mirror of
https://github.com/darlinghq/darling.git
synced 2024-11-27 06:10:36 +00:00
More work on darlingserver
dyld now progresses to sigexc_setup (and then dies when calling the old LKM started_suspended trap). It *seems* like Mach ports are being created properly in darlingserver, but given that there are no RPC calls that actually *use* the Mach ports yet, that's not certain yet. Additionally, the microthreading code in darlingserver *appears* to be working, but no code that actually needs to suspend and resume has been tested yet.
This commit is contained in:
parent
2bb784ed91
commit
81cada9b56
3
.gitmodules
vendored
3
.gitmodules
vendored
@ -372,3 +372,6 @@
|
||||
[submodule "src/external/pyobjc"]
|
||||
path = src/external/pyobjc
|
||||
url = ../darling-pyobjc.git
|
||||
[submodule "src/external/darlingserver"]
|
||||
path = src/external/darlingserver
|
||||
url = ../darlingserver.git
|
||||
|
@ -68,7 +68,10 @@ function(mig defFileName)
|
||||
-sheader ${CMAKE_CURRENT_BINARY_DIR}/${relativeName}${MIG_ARCH_SUFFIX}${MIG_SERVER_HEADER_SUFFIX}
|
||||
-xtracemig ${CMAKE_CURRENT_BINARY_DIR}/${relativeName}${MIG_ARCH_SUFFIX}${MIG_XTRACE_SUFFIX}
|
||||
${MIG_FLAGS}
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/${defFileName}
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/${defFileName} \;
|
||||
# this is so that the xtrace file is always produced so that the command is not constantly re-run
|
||||
# for MIG definitions that produce no xtrace files
|
||||
touch ${CMAKE_CURRENT_BINARY_DIR}/${relativeName}${MIG_ARCH_SUFFIX}${MIG_XTRACE_SUFFIX}
|
||||
DEPENDS
|
||||
migexe migcom
|
||||
)
|
||||
|
@ -56,7 +56,8 @@ add_subdirectory(external/cctools-port/cctools/misc)
|
||||
#add_subdirectory(libdyld)
|
||||
add_subdirectory(buildtools)
|
||||
add_subdirectory(libelfloader/wrapgen)
|
||||
add_subdirectory(darlingserver)
|
||||
add_subdirectory(libsimple)
|
||||
add_subdirectory(external/darlingserver)
|
||||
add_subdirectory(startup)
|
||||
|
||||
include_directories(${CMAKE_SOURCE_DIR}/basic-headers)
|
||||
|
@ -1,60 +0,0 @@
|
||||
project(darlingserver)
|
||||
|
||||
cmake_minimum_required(VERSION 3.10)
|
||||
|
||||
include_directories(
|
||||
include
|
||||
internal-include
|
||||
${CMAKE_CURRENT_BINARY_DIR}/include
|
||||
${CMAKE_CURRENT_BINARY_DIR}/internal-include
|
||||
)
|
||||
|
||||
add_custom_command(
|
||||
OUTPUT
|
||||
${CMAKE_CURRENT_BINARY_DIR}/include/darlingserver/rpc.h
|
||||
${CMAKE_CURRENT_BINARY_DIR}/internal-include/darlingserver/rpc.internal.h
|
||||
${CMAKE_CURRENT_BINARY_DIR}/src/rpc.c
|
||||
COMMAND
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/scripts/generate-rpc-wrappers.py
|
||||
${CMAKE_CURRENT_BINARY_DIR}/include/darlingserver/rpc.h
|
||||
${CMAKE_CURRENT_BINARY_DIR}/internal-include/darlingserver/rpc.internal.h
|
||||
${CMAKE_CURRENT_BINARY_DIR}/src/rpc.c
|
||||
\"../include/darlingserver/rpc.h\"
|
||||
VERBATIM
|
||||
MAIN_DEPENDENCY
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/scripts/generate-rpc-wrappers.py
|
||||
)
|
||||
|
||||
add_custom_target(generate_dserver_rpc_wrappers
|
||||
ALL
|
||||
DEPENDS
|
||||
${CMAKE_CURRENT_BINARY_DIR}/include/darlingserver/rpc.h
|
||||
${CMAKE_CURRENT_BINARY_DIR}/internal-include/darlingserver/rpc.internal.h
|
||||
${CMAKE_CURRENT_BINARY_DIR}/src/rpc.c
|
||||
)
|
||||
|
||||
add_executable(darlingserver
|
||||
src/darlingserver.cpp
|
||||
src/server.cpp
|
||||
src/message.cpp
|
||||
src/call.cpp
|
||||
src/registry.cpp
|
||||
src/logging.cpp
|
||||
src/process.cpp
|
||||
src/thread.cpp
|
||||
)
|
||||
|
||||
add_dependencies(darlingserver
|
||||
generate_dserver_rpc_wrappers
|
||||
)
|
||||
|
||||
target_compile_options(darlingserver PRIVATE -pthread -std=c++17)
|
||||
target_link_options(darlingserver PRIVATE -pthread)
|
||||
|
||||
install(TARGETS darlingserver DESTINATION bin)
|
||||
|
||||
#file(MAKE_DIRECTORY "${CMAKE_BINARY_DIR}/${DARLING_SDK_RELATIVE_PATH}/usr/include/darlingserver")
|
||||
#create_symlink(
|
||||
# "${DARLING_ROOT_RELATIVE_TO_SDK}/../../../src/darlingserver/include/darlingserver/rpc.h"
|
||||
# "${CMAKE_BINARY_DIR}/${DARLING_SDK_RELATIVE_PATH}/usr/include/darlingserver/rpc.h"
|
||||
#)
|
@ -1,65 +0,0 @@
|
||||
/**
|
||||
* This file is part of Darling.
|
||||
*
|
||||
* Copyright (C) 2021 Darling developers
|
||||
*
|
||||
* Darling is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Darling is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with Darling. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef _DARLINGSERVER_CALL_HPP_
|
||||
#define _DARLINGSERVER_CALL_HPP_
|
||||
|
||||
#include <darlingserver/rpc.h>
|
||||
#include <darlingserver/rpc.internal.h>
|
||||
|
||||
#include <darlingserver/message.hpp>
|
||||
#include <darlingserver/registry.hpp>
|
||||
|
||||
#include <memory>
|
||||
|
||||
namespace DarlingServer {
|
||||
class CallWithReply;
|
||||
|
||||
class Call {
|
||||
public:
|
||||
enum class Number {
|
||||
Invalid = dserver_callnum_invalid,
|
||||
DSERVER_ENUM_VALUES
|
||||
};
|
||||
|
||||
protected:
|
||||
std::weak_ptr<Thread> _thread;
|
||||
MessageQueue& _replyQueue;
|
||||
Address _replyAddress;
|
||||
|
||||
void _stopPending();
|
||||
|
||||
public:
|
||||
Call(MessageQueue& replyQueue, std::shared_ptr<Thread> thread, Address replyAddress);
|
||||
virtual ~Call();
|
||||
|
||||
static std::shared_ptr<Call> callFromMessage(Message&& requestMessage, MessageQueue& replyQueue);
|
||||
|
||||
virtual Number number() const = 0;
|
||||
std::shared_ptr<Thread> thread() const;
|
||||
|
||||
virtual void processCall() = 0;
|
||||
|
||||
DSERVER_CLASS_DECLS;
|
||||
};
|
||||
|
||||
DSERVER_CLASS_DEFS;
|
||||
};
|
||||
|
||||
#endif // _DARLINGSERVER_CALL_HPP_
|
@ -1,88 +0,0 @@
|
||||
/**
|
||||
* This file is part of Darling.
|
||||
*
|
||||
* Copyright (C) 2021 Darling developers
|
||||
*
|
||||
* Darling is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Darling is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with Darling. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef _DARLINGSERVER_LOGGING_HPP_
|
||||
#define _DARLINGSERVER_LOGGING_HPP_
|
||||
|
||||
#include <fstream>
|
||||
#include <string>
|
||||
#include <sstream>
|
||||
|
||||
namespace DarlingServer {
|
||||
static struct EndLog {} endLog;
|
||||
|
||||
class Log {
|
||||
private:
|
||||
std::string _category;
|
||||
|
||||
enum class Type {
|
||||
Debug,
|
||||
Info,
|
||||
Warning,
|
||||
Error,
|
||||
};
|
||||
|
||||
void _log(Type type, std::string message);
|
||||
static std::string _typeToString(Type type);
|
||||
|
||||
public:
|
||||
Log(std::string category);
|
||||
|
||||
Log(const Log&) = delete;
|
||||
Log& operator=(const Log&) = delete;
|
||||
Log(Log&&) = delete;
|
||||
Log& operator=(Log&&) = delete;
|
||||
|
||||
class Stream;
|
||||
friend class Stream;
|
||||
|
||||
Stream debug();
|
||||
Stream info();
|
||||
Stream warning();
|
||||
Stream error();
|
||||
};
|
||||
|
||||
class Log::Stream {
|
||||
friend class Log;
|
||||
|
||||
private:
|
||||
Type _type;
|
||||
Log& _log;
|
||||
std::ostringstream _buffer;
|
||||
|
||||
Stream(Type type, Log& log);
|
||||
|
||||
public:
|
||||
Stream(const Stream&) = delete;
|
||||
Stream& operator=(const Stream&) = delete;
|
||||
Stream(Stream&&) = delete;
|
||||
Stream& operator=(Stream&&) = delete;
|
||||
|
||||
template<class T>
|
||||
Stream& operator<<(T value) {
|
||||
_buffer << value;
|
||||
return *this;
|
||||
};
|
||||
|
||||
template<>
|
||||
Stream& operator<<<EndLog>(EndLog value);
|
||||
};
|
||||
};
|
||||
|
||||
#endif // _DARLINGSERVER_LOGGING_HPP_
|
@ -1,174 +0,0 @@
|
||||
/**
|
||||
* This file is part of Darling.
|
||||
*
|
||||
* Copyright (C) 2021 Darling developers
|
||||
*
|
||||
* Darling is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Darling is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with Darling. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef _DARLINGSERVER_MESSAGE_HPP_
|
||||
#define _DARLINGSERVER_MESSAGE_HPP_
|
||||
|
||||
#include <sys/socket.h>
|
||||
#include <sys/un.h>
|
||||
#include <vector>
|
||||
#include <cstdint>
|
||||
#include <deque>
|
||||
#include <mutex>
|
||||
#include <optional>
|
||||
#include <functional>
|
||||
|
||||
namespace DarlingServer {
|
||||
class Address {
|
||||
private:
|
||||
struct sockaddr_un _address;
|
||||
size_t _size;
|
||||
|
||||
public:
|
||||
Address();
|
||||
Address(const struct sockaddr_un& rawAddress, size_t addressSize = sizeof(struct sockaddr_un));
|
||||
|
||||
struct sockaddr_un& raw();
|
||||
const struct sockaddr_un& raw() const;
|
||||
|
||||
size_t rawSize() const;
|
||||
void setRawSize(size_t newRawSize);
|
||||
};
|
||||
|
||||
/**
|
||||
* A C++ wrapper for local (Unix) SOCK_DGRAM/SOCK_SEQPACKET socket messages, with support for file descriptors (SCM_RIGHTS) and credentials (SCM_CREDENTIALS).
|
||||
*/
|
||||
class Message {
|
||||
private:
|
||||
struct msghdr _header;
|
||||
struct iovec _dataDescriptor;
|
||||
std::vector<uint8_t> _buffer;
|
||||
struct cmsghdr* _controlHeader = nullptr;
|
||||
Address _socketAddress;
|
||||
|
||||
void _initWithOther(Message&&);
|
||||
void _cleanupSelf();
|
||||
|
||||
struct cmsghdr* _credentialsHeader();
|
||||
const struct cmsghdr* _credentialsHeader() const;
|
||||
struct cmsghdr* _descriptorHeader();
|
||||
const struct cmsghdr* _descriptorHeader() const;
|
||||
size_t _descriptorSpace() const;
|
||||
|
||||
void _ensureCredentialsHeader();
|
||||
void _ensureDescriptorHeader(size_t descriptorSpace);
|
||||
|
||||
public:
|
||||
/**
|
||||
* Default-initializes a Message.
|
||||
*
|
||||
* By default, the data buffer holds 256 bytes and the control data buffer has space for 4 descriptors.
|
||||
*
|
||||
* If this Message is being used to receive a message from a socket, you must preallocate
|
||||
* the buffers for the recvmsg/recvmmsg call. The buffers are already preallocated to the
|
||||
* sizes specified by in this constructor, however, if you wish to grow the data buffer,
|
||||
* you can call pushData() with a null buffer pointer and a non-zero size. If you wish to
|
||||
* grow the control data buffer, you can call pushDescriptor() with a descriptor value of `-1`.
|
||||
*/
|
||||
Message(size_t bufferSpace = 256, size_t descriptorSpace = 4);
|
||||
~Message();
|
||||
|
||||
Message(Message&&);
|
||||
Message& operator=(Message&&);
|
||||
|
||||
Message(const Message&) = delete;
|
||||
Message& operator=(const Message&) = delete;
|
||||
|
||||
struct msghdr& rawHeader();
|
||||
const struct msghdr& rawHeader() const;
|
||||
|
||||
std::vector<uint8_t>& data();
|
||||
const std::vector<uint8_t>& data() const;
|
||||
|
||||
Address address() const;
|
||||
void setAddress(Address address);
|
||||
|
||||
bool copyCredentialsOut(struct ucred& outputCredentials) const;
|
||||
void copyCredentialsIn(const struct ucred& inputCredentials);
|
||||
|
||||
pid_t pid() const;
|
||||
void setPID(pid_t pid);
|
||||
|
||||
uid_t uid() const;
|
||||
void setUID(uid_t uid);
|
||||
|
||||
gid_t gid() const;
|
||||
void setGID(gid_t gid);
|
||||
|
||||
/**
|
||||
* Returns an array of all the descritors currently owned by this Message.
|
||||
*
|
||||
* Note that the descriptors are still owned by this Message during and after this call.
|
||||
* Callers should NOT close the descriptors they receive in the returned vector.
|
||||
*
|
||||
* See extractDescriptor() for a method that will transfer ownership of a descriptor to the caller.
|
||||
*/
|
||||
std::vector<int> descriptors() const;
|
||||
|
||||
/**
|
||||
* Acquires ownership of the given descriptor.
|
||||
*
|
||||
* Note that callers should NOT close the descriptor after a call to this method.
|
||||
* This Message becomes the owner of the descriptor and will take care of closing it.
|
||||
*/
|
||||
void pushDescriptor(int descriptor);
|
||||
|
||||
/**
|
||||
* Extracts a single descriptor from this Message, transferring ownership of it to the caller.
|
||||
*
|
||||
* @returns The descriptor that was extracted, or `-1` if it was not found.
|
||||
*/
|
||||
int extractDescriptor(int descriptor);
|
||||
|
||||
/**
|
||||
* Like extractDescriptor(), but extracts it based on its index in the vector returned by descriptors().
|
||||
*/
|
||||
int extractDescriptorAtIndex(size_t index);
|
||||
|
||||
/**
|
||||
* Gives up ownership of the descriptors previously held by this Message and acquires ownership
|
||||
* of the descriptors passed in the given vector.
|
||||
*
|
||||
* Note that this method does not return the old descriptors in any way,
|
||||
* so if the caller wishes to know what they were, they must call descriptors() before calling this method.
|
||||
*/
|
||||
void replaceDescriptors(const std::vector<int>& newDescriptors);
|
||||
};
|
||||
|
||||
/**
|
||||
* A thread-safe Message queue with methods for sending and receiving messages.
|
||||
*/
|
||||
class MessageQueue {
|
||||
private:
|
||||
std::deque<Message> _messages;
|
||||
std::mutex _lock;
|
||||
std::function<void()> _messageArrivalNotificationCallback = nullptr;
|
||||
|
||||
public:
|
||||
void setMessageArrivalNotificationCallback(std::function<void()> messageArrivalNotificationCallback);
|
||||
|
||||
void push(Message&& message);
|
||||
std::optional<Message> pop();
|
||||
|
||||
bool sendMany(int socket);
|
||||
bool receiveMany(int socket);
|
||||
};
|
||||
};
|
||||
|
||||
#endif // _DARLINGSERVER_MESSAGE_HPP_
|
@ -1,76 +0,0 @@
|
||||
/**
|
||||
* This file is part of Darling.
|
||||
*
|
||||
* Copyright (C) 2021 Darling developers
|
||||
*
|
||||
* Darling is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Darling is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with Darling. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef _DARLINGSERVER_PROCESS_HPP_
|
||||
#define _DARLINGSERVER_PROCESS_HPP_
|
||||
|
||||
#include <sys/types.h>
|
||||
#include <memory>
|
||||
#include <vector>
|
||||
#include <mutex>
|
||||
#include <shared_mutex>
|
||||
|
||||
namespace DarlingServer {
|
||||
class Thread;
|
||||
class Server;
|
||||
|
||||
class Process {
|
||||
friend class Thread;
|
||||
friend class Server;
|
||||
|
||||
private:
|
||||
pid_t _pid;
|
||||
pid_t _nspid;
|
||||
int _pidfd;
|
||||
mutable std::shared_mutex _rwlock;
|
||||
std::vector<std::weak_ptr<Thread>> _threads;
|
||||
std::string _vchrootPath;
|
||||
|
||||
void _unregisterThreads();
|
||||
|
||||
public:
|
||||
using ID = pid_t;
|
||||
using NSID = ID;
|
||||
|
||||
Process(ID id, NSID nsid);
|
||||
~Process();
|
||||
|
||||
Process(const Process&) = delete;
|
||||
Process& operator=(const Process&) = delete;
|
||||
Process(Process&&) = delete;
|
||||
Process& operator=(Process&&) = delete;
|
||||
|
||||
/**
|
||||
* The PID of this Process as seen from darlingserver's namespace.
|
||||
*/
|
||||
ID id() const;
|
||||
|
||||
/**
|
||||
* The PID of this Process as seen from within the container (i.e. launchd's namespace).
|
||||
*/
|
||||
NSID nsid() const;
|
||||
|
||||
std::vector<std::shared_ptr<Thread>> threads() const;
|
||||
|
||||
std::string vchrootPath() const;
|
||||
void setVchrootPath(std::string path);
|
||||
};
|
||||
};
|
||||
|
||||
#endif // _DARLINGSERVER_PROCESS_HPP_
|
@ -1,171 +0,0 @@
|
||||
/**
|
||||
* This file is part of Darling.
|
||||
*
|
||||
* Copyright (C) 2021 Darling developers
|
||||
*
|
||||
* Darling is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Darling is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with Darling. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef _DARLINGSERVER_REGISTRY_HPP_
|
||||
#define _DARLINGSERVER_REGISTRY_HPP_
|
||||
|
||||
#include <vector>
|
||||
#include <memory>
|
||||
#include <mutex>
|
||||
#include <unordered_map>
|
||||
#include <shared_mutex>
|
||||
#include <optional>
|
||||
#include <functional>
|
||||
|
||||
#include <sys/types.h>
|
||||
|
||||
#include <darlingserver/message.hpp>
|
||||
#include <darlingserver/process.hpp>
|
||||
#include <darlingserver/thread.hpp>
|
||||
|
||||
namespace DarlingServer {
|
||||
template<class Entry>
|
||||
class Registry {
|
||||
private:
|
||||
std::unordered_map<typename Entry::ID, std::shared_ptr<Entry>> _map;
|
||||
std::unordered_map<typename Entry::NSID, std::shared_ptr<Entry>> _nsmap;
|
||||
std::shared_mutex _rwlock;
|
||||
|
||||
public:
|
||||
using ID = typename Entry::ID;
|
||||
using NSID = typename Entry::NSID;
|
||||
|
||||
std::shared_ptr<Entry> registerIfAbsent(NSID nsid, std::function<std::shared_ptr<Entry>()> entryFactory) {
|
||||
std::unique_lock lock(_rwlock);
|
||||
|
||||
auto it2 = _nsmap.find(nsid);
|
||||
if (it2 != _nsmap.end()) {
|
||||
return (*it2).second;
|
||||
}
|
||||
|
||||
auto entry = entryFactory();
|
||||
_map[entry->id()] = entry;
|
||||
_nsmap[entry->nsid()] = entry;
|
||||
return entry;
|
||||
};
|
||||
|
||||
bool registerEntry(std::shared_ptr<Entry> entry, bool replace = false) {
|
||||
std::unique_lock lock(_rwlock);
|
||||
|
||||
if (!replace && (_map.find(entry->id()) != _map.end() || _nsmap.find(entry->nsid()) != _nsmap.end())) {
|
||||
return false;
|
||||
}
|
||||
|
||||
_map[entry->id()] = entry;
|
||||
_nsmap[entry->nsid()] = entry;
|
||||
return true;
|
||||
};
|
||||
|
||||
bool unregisterEntryByID(ID id) {
|
||||
std::unique_lock lock(_rwlock);
|
||||
|
||||
auto it = _map.find(id);
|
||||
|
||||
if (it == _map.end()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
std::shared_ptr<Entry> entry = (*it).second;
|
||||
auto it2 = _nsmap.find(entry->nsid());
|
||||
|
||||
if (it2 == _nsmap.end()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
_map.erase(it);
|
||||
_nsmap.erase(it2);
|
||||
return true;
|
||||
};
|
||||
|
||||
bool unregisterEntryByNSID(NSID nsid) {
|
||||
std::unique_lock lock(_rwlock);
|
||||
|
||||
auto it2 = _nsmap.find(nsid);
|
||||
|
||||
if (it2 == _nsmap.end()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
std::shared_ptr<Entry> entry = (*it2).second;
|
||||
auto it = _map.find(entry->id());
|
||||
|
||||
if (it == _map.end()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
_map.erase(it);
|
||||
_nsmap.erase(it2);
|
||||
return true;
|
||||
};
|
||||
|
||||
/**
|
||||
* Unregisters the given entry from this Registry.
|
||||
*
|
||||
* This is the recommended method for unregistering entries as it will
|
||||
* actually compare pointers to ensure the entry being unregistered is
|
||||
* the same as the one currently registered with the same IDs.
|
||||
*/
|
||||
bool unregisterEntry(std::shared_ptr<Entry> entry) {
|
||||
std::unique_lock lock(_rwlock);
|
||||
|
||||
auto it = _map.find(entry->id());
|
||||
auto it2 = _nsmap.find(entry->nsid());
|
||||
|
||||
if (it == _map.end() || it2 == _nsmap.end()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// note that we *want* pointer-to-pointer comparison
|
||||
if ((*it).second != entry || (*it2).second != entry) {
|
||||
return false;
|
||||
}
|
||||
|
||||
_map.erase(it);
|
||||
_nsmap.erase(it2);
|
||||
return true;
|
||||
};
|
||||
|
||||
std::optional<std::shared_ptr<Entry>> lookupEntryByID(ID id) {
|
||||
std::shared_lock lock(_rwlock);
|
||||
|
||||
auto it = _map.find(id);
|
||||
if (it == _map.end()) {
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
return *it;
|
||||
};
|
||||
|
||||
std::optional<std::shared_ptr<Entry>> lookupEntryByNSID(ID nsid) {
|
||||
std::shared_lock lock(_rwlock);
|
||||
|
||||
auto it2 = _nsmap.find(nsid);
|
||||
if (it2 == _nsmap.end()) {
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
return *it2;
|
||||
};
|
||||
};
|
||||
|
||||
Registry<Process>& processRegistry();
|
||||
Registry<Thread>& threadRegistry();
|
||||
};
|
||||
|
||||
#endif // _DARLINGSERVER_REGISTRY_HPP_
|
@ -1,68 +0,0 @@
|
||||
/**
|
||||
* This file is part of Darling.
|
||||
*
|
||||
* Copyright (C) 2021 Darling developers
|
||||
*
|
||||
* Darling is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Darling is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with Darling. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef _DARLINGSERVER_SERVER_HPP_
|
||||
#define _DARLINGSERVER_SERVER_HPP_
|
||||
|
||||
#include <string>
|
||||
#include <sys/epoll.h>
|
||||
#include <thread>
|
||||
|
||||
#include <darlingserver/message.hpp>
|
||||
#include <darlingserver/workers.hpp>
|
||||
#include <darlingserver/call.hpp>
|
||||
#include <darlingserver/registry.hpp>
|
||||
|
||||
namespace DarlingServer {
|
||||
// NOTE: server instances MUST be created with `new` rather than as a normal local/stack variable
|
||||
class Server {
|
||||
private:
|
||||
int _listenerSocket;
|
||||
std::string _prefix;
|
||||
std::string _socketPath;
|
||||
int _epollFD;
|
||||
MessageQueue _inbox;
|
||||
MessageQueue _outbox;
|
||||
WorkQueue<Message> _workQueue;
|
||||
bool _canRead = false;
|
||||
bool _canWrite = true;
|
||||
int _wakeupFD;
|
||||
|
||||
void _worker(DarlingServer::Message message);
|
||||
|
||||
public:
|
||||
Server(std::string prefix);
|
||||
~Server();
|
||||
|
||||
Server(const Server&) = delete;
|
||||
Server& operator=(const Server&) = delete;
|
||||
Server(Server&&) = delete;
|
||||
Server& operator=(Server&&) = delete;
|
||||
|
||||
void start();
|
||||
|
||||
void monitorProcess(std::shared_ptr<Process> process);
|
||||
|
||||
std::string prefix() const;
|
||||
|
||||
static Server& sharedInstance();
|
||||
};
|
||||
};
|
||||
|
||||
#endif // _DARLINGSERVER_SERVER_HPP_
|
@ -1,79 +0,0 @@
|
||||
/**
|
||||
* This file is part of Darling.
|
||||
*
|
||||
* Copyright (C) 2021 Darling developers
|
||||
*
|
||||
* Darling is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Darling is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with Darling. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef _DARLINGSERVER_THREAD_HPP_
|
||||
#define _DARLINGSERVER_THREAD_HPP_
|
||||
|
||||
#include <memory>
|
||||
#include <sys/types.h>
|
||||
#include <mutex>
|
||||
#include <shared_mutex>
|
||||
|
||||
#include <darlingserver/message.hpp>
|
||||
|
||||
namespace DarlingServer {
|
||||
class Process;
|
||||
class Call;
|
||||
|
||||
class Thread: public std::enable_shared_from_this<Thread> {
|
||||
friend class Process;
|
||||
|
||||
private:
|
||||
pid_t _tid;
|
||||
pid_t _nstid;
|
||||
std::weak_ptr<Process> _process;
|
||||
std::shared_ptr<Call> _pendingCall;
|
||||
Address _address;
|
||||
mutable std::shared_mutex _rwlock;
|
||||
|
||||
public:
|
||||
using ID = pid_t;
|
||||
using NSID = ID;
|
||||
|
||||
Thread(std::shared_ptr<Process> process, NSID nsid);
|
||||
~Thread() noexcept(false);
|
||||
|
||||
void registerWithProcess();
|
||||
|
||||
Thread(const Thread&) = delete;
|
||||
Thread& operator=(const Thread&) = delete;
|
||||
Thread(Thread&&) = delete;
|
||||
Thread& operator=(Thread&&) = delete;
|
||||
|
||||
std::shared_ptr<Process> process() const;
|
||||
|
||||
std::shared_ptr<Call> pendingCall() const;
|
||||
void setPendingCall(std::shared_ptr<Call> newPendingCall);
|
||||
|
||||
/**
|
||||
* The TID of this Thread as seen from darlingserver's namespace.
|
||||
*/
|
||||
ID id() const;
|
||||
|
||||
/**
|
||||
* The TID of this Thread as seen from within the container (i.e. launchd's namespace).
|
||||
*/
|
||||
NSID nsid() const;
|
||||
|
||||
Address address() const;
|
||||
void setAddress(Address address);
|
||||
};
|
||||
};
|
||||
|
||||
#endif // _DARLINGSERVER_THREAD_HPP_
|
@ -1,176 +0,0 @@
|
||||
/**
|
||||
* This file is part of Darling.
|
||||
*
|
||||
* Copyright (C) 2021 Darling developers
|
||||
*
|
||||
* Darling is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Darling is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with Darling. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef _DARLINGSERVER_WORKERS_HPP_
|
||||
#define _DARLINGSERVER_WORKERS_HPP_
|
||||
|
||||
#include <queue>
|
||||
#include <functional>
|
||||
#include <thread>
|
||||
#include <vector>
|
||||
#include <mutex>
|
||||
#include <condition_variable>
|
||||
#include <unordered_map>
|
||||
#include <chrono>
|
||||
#include <optional>
|
||||
|
||||
namespace DarlingServer {
|
||||
template<class WorkItem>
|
||||
class WorkQueue {
|
||||
private:
|
||||
std::mutex _queueMutex;
|
||||
std::queue<WorkItem> _workItems;
|
||||
std::function<void(WorkItem)> _workerFunction;
|
||||
std::unordered_map<std::thread::id, std::thread> _workerThreads;
|
||||
size_t _threadsAvailable = 0;
|
||||
std::condition_variable _cv;
|
||||
bool _dying = false;
|
||||
|
||||
static constexpr unsigned int minimumThreads() {
|
||||
return 2;
|
||||
};
|
||||
|
||||
static unsigned int maximumThreads() {
|
||||
unsigned int hw = std::thread::hardware_concurrency();
|
||||
if (hw > 0 && hw - 1 > minimumThreads()) {
|
||||
// minus one for the main thread
|
||||
return hw - 1;
|
||||
} else {
|
||||
return minimumThreads();
|
||||
}
|
||||
};
|
||||
|
||||
static constexpr std::chrono::seconds temporaryWorkerWaitPeriod() {
|
||||
return std::chrono::seconds(15);
|
||||
};
|
||||
|
||||
void worker(bool permanent) {
|
||||
std::optional<WorkItem> workItem = std::nullopt;
|
||||
std::unique_lock lock(_queueMutex, std::defer_lock);
|
||||
|
||||
while (true) {
|
||||
// acquire the lock for the condition variable
|
||||
lock.lock();
|
||||
|
||||
// we're going to wait for work, so we're available
|
||||
++_threadsAvailable;
|
||||
|
||||
bool waitResult;
|
||||
auto predicate = [&, this]() {
|
||||
// if we're dying, stop waiting
|
||||
if (_dying) {
|
||||
--_threadsAvailable;
|
||||
return true;
|
||||
}
|
||||
|
||||
// if we have a work item ready, take it and stop waiting
|
||||
if (_workItems.size() > 0) {
|
||||
workItem = std::move(_workItems.front());
|
||||
_workItems.pop();
|
||||
--_threadsAvailable;
|
||||
return true;
|
||||
}
|
||||
|
||||
// otherwise, let's keep waiting
|
||||
return false;
|
||||
};
|
||||
|
||||
if (permanent) {
|
||||
// permanent workers just wait until there's work available (or the queue is being destroyed)
|
||||
_cv.wait(lock, predicate);
|
||||
waitResult = true;
|
||||
} else {
|
||||
// temporary workers wait for a certain period of time for work; if there's no work available after that period, they die
|
||||
waitResult = _cv.wait_for(lock, temporaryWorkerWaitPeriod(), predicate);
|
||||
}
|
||||
|
||||
// if the queue is being destroyed, let the thread die
|
||||
if (_dying) {
|
||||
return;
|
||||
}
|
||||
|
||||
// if we failed to find any work items, let's die
|
||||
if (!waitResult) {
|
||||
auto thisThread = std::move(_workerThreads[std::this_thread::get_id()]);
|
||||
_workerThreads.erase(thisThread.get_id());
|
||||
thisThread.detach();
|
||||
return;
|
||||
}
|
||||
|
||||
// drop the lock to perform the work
|
||||
lock.unlock();
|
||||
|
||||
// perform the work
|
||||
_workerFunction(std::move(workItem.value()));
|
||||
workItem = std::nullopt;
|
||||
}
|
||||
};
|
||||
|
||||
public:
|
||||
WorkQueue(std::function<void(WorkItem)> workerFunction):
|
||||
_workerFunction(workerFunction)
|
||||
{
|
||||
std::scoped_lock lock(_queueMutex);
|
||||
|
||||
// start the permanent worker threads
|
||||
for (size_t i = 0; i < minimumThreads(); ++i) {
|
||||
std::thread worker = std::thread(&WorkQueue::worker, this, true);
|
||||
_workerThreads[worker.get_id()] = std::move(worker);
|
||||
}
|
||||
};
|
||||
~WorkQueue() {
|
||||
// tell all the threads that we're being destroyed
|
||||
std::unique_lock lock(_queueMutex);
|
||||
_dying = true;
|
||||
lock.unlock();
|
||||
_cv.notify_all();
|
||||
|
||||
// now wait for all of the threads to die
|
||||
for (auto& [id, thread]: _workerThreads) {
|
||||
thread.join();
|
||||
}
|
||||
};
|
||||
|
||||
WorkQueue(const WorkQueue&) = delete;
|
||||
WorkQueue& operator=(const WorkQueue&) = delete;
|
||||
WorkQueue(WorkQueue&&) = delete;
|
||||
WorkQueue& operator=(WorkQueue&&) = delete;
|
||||
|
||||
void push(WorkItem workItem) {
|
||||
std::unique_lock lock(_queueMutex);
|
||||
_workItems.push(std::move(workItem));
|
||||
|
||||
// if there are no threads available to perform the work AND we have less than the
|
||||
// maximum number of worker threads currently active, spawn a temporary worker thread.
|
||||
if (_threadsAvailable == 0 && _workerThreads.size() < maximumThreads()) {
|
||||
std::thread worker = std::thread(&WorkQueue::worker, this, false);
|
||||
_workerThreads[worker.get_id()] = std::move(worker);
|
||||
|
||||
// note that we don't need to notify anyone in this case, since we want the new worker thread
|
||||
// to take care of the new work item (it's not a problem if another thread gets to it first, though).
|
||||
} else {
|
||||
// otherwise, notify one of the available threads that there's work available
|
||||
lock.unlock();
|
||||
_cv.notify_one();
|
||||
}
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
#endif // _DARLINGSERVER_WORKERS_HPP_
|
@ -1,563 +0,0 @@
|
||||
#!/usr/bin/env python3
|
||||
|
||||
import os
|
||||
import sys
|
||||
from collections import OrderedDict
|
||||
import textwrap
|
||||
from datetime import datetime
|
||||
|
||||
# NOTE: in Python 3.7+, we can rely on dictionaries having their items in insertion order.
|
||||
# unfortunately, we can't expect everyone building Darling to have Python 3.7+ installed.
|
||||
calls = [
|
||||
('checkin', [], []),
|
||||
|
||||
('checkout', [], []),
|
||||
|
||||
('vchroot_path', [
|
||||
('buffer', 'char*'),
|
||||
('buffer_size', 'size_t'),
|
||||
], [
|
||||
('length', 'size_t'),
|
||||
]),
|
||||
]
|
||||
|
||||
if len(sys.argv) < 5:
|
||||
sys.exit("Usage: " + sys.argv[0] + " <public-header-path> <internal-header-path> <library-source-path> <library-import>")
|
||||
|
||||
os.makedirs(os.path.dirname(sys.argv[1]), exist_ok=True)
|
||||
os.makedirs(os.path.dirname(sys.argv[2]), exist_ok=True)
|
||||
os.makedirs(os.path.dirname(sys.argv[3]), exist_ok=True)
|
||||
|
||||
def to_camel_case(snake_str):
|
||||
components = snake_str.split('_')
|
||||
return ''.join(x.title() for x in components)
|
||||
|
||||
public_header = open(sys.argv[1], "w")
|
||||
internal_header = open(sys.argv[2], "w")
|
||||
library_source = open(sys.argv[3], "w")
|
||||
library_import = sys.argv[4]
|
||||
|
||||
license_header = """\
|
||||
// This file has been auto-generated by generate-rpc-wrappers.py for use with darlingserver
|
||||
|
||||
/**
|
||||
* This file is part of Darling.
|
||||
*
|
||||
* Copyright (C) {} Darling developers
|
||||
*
|
||||
* Darling is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Darling is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with Darling. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
""".format(datetime.now().year)
|
||||
|
||||
public_header.write(license_header)
|
||||
library_source.write(license_header)
|
||||
internal_header.write(license_header)
|
||||
|
||||
public_header.write("""\
|
||||
#ifndef _DARLINGSERVER_API_H_
|
||||
#define _DARLINGSERVER_API_H_
|
||||
|
||||
#include <sys/types.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
""")
|
||||
|
||||
public_header.write("enum dserver_callnum {\n")
|
||||
public_header.write("\tdserver_callnum_invalid,\n")
|
||||
for call in calls:
|
||||
call_name = call[0]
|
||||
call_parameters = call[1]
|
||||
reply_parameters = call[2]
|
||||
|
||||
public_header.write("\tdserver_callnum_" + call_name + ",\n")
|
||||
public_header.write("};\n")
|
||||
|
||||
public_header.write("""\
|
||||
|
||||
typedef enum dserver_callnum dserver_callnum_t;
|
||||
|
||||
typedef struct dserver_rpc_callhdr {
|
||||
pid_t pid;
|
||||
pid_t tid;
|
||||
dserver_callnum_t number;
|
||||
} dserver_rpc_callhdr_t;
|
||||
|
||||
typedef struct dserver_rpc_replyhdr {
|
||||
dserver_callnum_t number;
|
||||
int code;
|
||||
} dserver_rpc_replyhdr_t;
|
||||
|
||||
""")
|
||||
|
||||
library_source.write("""\
|
||||
#include {}
|
||||
|
||||
#if !defined(dserver_rpc_hooks_msghdr_t) || !defined(dserver_rpc_hooks_iovec_t) || !defined(dserver_rpc_hooks_cmsghdr_t) || !defined(DSERVER_RPC_HOOKS_CMSG_SPACE) || !defined(DSERVER_RPC_HOOKS_CMSG_FIRSTHDR) || !defined(DSERVER_RPC_HOOKS_SOL_SOCKET) || !defined(DSERVER_RPC_HOOKS_SCM_RIGHTS) || !defined(DSERVER_RPC_HOOKS_CMSG_LEN) || !defined(DSERVER_RPC_HOOKS_CMSG_DATA) || !defined(DSERVER_RPC_HOOKS_ATTRIBUTE)
|
||||
#error Missing definitions
|
||||
#endif
|
||||
|
||||
#ifndef dserver_rpc_hooks_get_pid
|
||||
DSERVER_RPC_HOOKS_ATTRIBUTE pid_t dserver_rpc_hooks_get_pid(void);
|
||||
#endif
|
||||
|
||||
#ifndef dserver_rpc_hooks_get_tid
|
||||
DSERVER_RPC_HOOKS_ATTRIBUTE pid_t dserver_rpc_hooks_get_tid(void);
|
||||
#endif
|
||||
|
||||
#ifndef dserver_rpc_hooks_get_server_address
|
||||
DSERVER_RPC_HOOKS_ATTRIBUTE void* dserver_rpc_hooks_get_server_address(void);
|
||||
#endif
|
||||
|
||||
#ifndef dserver_rpc_hooks_get_server_address_length
|
||||
DSERVER_RPC_HOOKS_ATTRIBUTE size_t dserver_rpc_hooks_get_server_address_length(void);
|
||||
#endif
|
||||
|
||||
#ifndef dserver_rpc_hooks_memcpy
|
||||
DSERVER_RPC_HOOKS_ATTRIBUTE void* dserver_rpc_hooks_memcpy(void* destination, const void* source, size_t length);
|
||||
#endif
|
||||
|
||||
#ifndef dserver_rpc_hooks_send_message
|
||||
DSERVER_RPC_HOOKS_ATTRIBUTE long int dserver_rpc_hooks_send_message(int socket, const dserver_rpc_hooks_msghdr_t* message);
|
||||
#endif
|
||||
|
||||
#ifndef dserver_rpc_hooks_receive_message
|
||||
DSERVER_RPC_HOOKS_ATTRIBUTE long int dserver_rpc_hooks_receive_message(int socket, dserver_rpc_hooks_msghdr_t* out_message);
|
||||
#endif
|
||||
|
||||
#ifndef dserver_rpc_hooks_get_bad_message_status
|
||||
DSERVER_RPC_HOOKS_ATTRIBUTE int dserver_rpc_hooks_get_bad_message_status(void);
|
||||
#endif
|
||||
|
||||
#ifndef dserver_rpc_hooks_get_communication_error_status
|
||||
DSERVER_RPC_HOOKS_ATTRIBUTE int dserver_rpc_hooks_get_communication_error_status(void);
|
||||
#endif
|
||||
|
||||
#ifndef dserver_rpc_hooks_get_broken_pipe_status
|
||||
DSERVER_RPC_HOOKS_ATTRIBUTE int dserver_rpc_hooks_get_broken_pipe_status(void);
|
||||
#endif
|
||||
|
||||
#ifndef dserver_rpc_hooks_close_fd
|
||||
DSERVER_RPC_HOOKS_ATTRIBUTE void dserver_rpc_hooks_close_fd(int fd);
|
||||
#endif
|
||||
|
||||
#ifndef dserver_rpc_hooks_get_socket
|
||||
DSERVER_RPC_HOOKS_ATTRIBUTE int dserver_rpc_hooks_get_socket(void);
|
||||
#endif
|
||||
|
||||
""".format(library_import))
|
||||
|
||||
internal_header.write("#define DSERVER_VALID_CALLNUM_CASES \\\n")
|
||||
for call in calls:
|
||||
call_name = call[0]
|
||||
call_parameters = call[1]
|
||||
reply_parameters = call[2]
|
||||
|
||||
internal_header.write("\tcase dserver_callnum_" + call_name + ": \\\n")
|
||||
internal_header.write("\n")
|
||||
|
||||
internal_header.write("#define DSERVER_CONSTRUCT_CASES \\\n")
|
||||
for call in calls:
|
||||
call_name = call[0]
|
||||
call_parameters = call[1]
|
||||
reply_parameters = call[2]
|
||||
camel_name = to_camel_case(call_name)
|
||||
|
||||
internal_header.write("\tCALL_CASE(" + call_name + ", " + camel_name + "); \\\n")
|
||||
internal_header.write("\n")
|
||||
|
||||
internal_header.write("#define DSERVER_ENUM_VALUES \\\n")
|
||||
for call in calls:
|
||||
call_name = call[0]
|
||||
call_parameters = call[1]
|
||||
reply_parameters = call[2]
|
||||
camel_name = to_camel_case(call_name)
|
||||
|
||||
internal_header.write("\t" + camel_name + " = dserver_callnum_" + call_name + ", \\\n")
|
||||
internal_header.write("\n")
|
||||
|
||||
internal_header.write("#define DSERVER_CLASS_DECLS \\\n")
|
||||
for call in calls:
|
||||
call_name = call[0]
|
||||
call_parameters = call[1]
|
||||
reply_parameters = call[2]
|
||||
camel_name = to_camel_case(call_name)
|
||||
|
||||
internal_header.write("\tclass " + camel_name + "; \\\n")
|
||||
internal_header.write("\n")
|
||||
|
||||
internal_header.write("#define DSERVER_CLASS_DEFS \\\n")
|
||||
for call in calls:
|
||||
call_name = call[0]
|
||||
call_parameters = call[1]
|
||||
reply_parameters = call[2]
|
||||
camel_name = to_camel_case(call_name)
|
||||
fd_count_in_reply = 0
|
||||
|
||||
internal_header.write(textwrap.indent(textwrap.dedent("""\
|
||||
class Call::{1}: public Call {{ \\
|
||||
friend class Call; \\
|
||||
private: \\
|
||||
{2}
|
||||
public: \\
|
||||
{1}(MessageQueue& replyQueue, std::shared_ptr<Thread> thread, dserver_rpc_call_{0}_t* data, Message&& requestMessage): \\
|
||||
Call(replyQueue, thread, requestMessage.address()){3} \\
|
||||
{4}
|
||||
{{ \\
|
||||
"""), '\t').format(
|
||||
call_name,
|
||||
camel_name,
|
||||
("dserver_call_" + call_name + "_t _body; \\") if len(call_parameters) > 0 else "\\",
|
||||
"," if len(call_parameters) > 0 else "",
|
||||
"_body(data->body) \\" if len(call_parameters) > 0 else "\\"
|
||||
)
|
||||
)
|
||||
|
||||
for param in call_parameters:
|
||||
param_name = param[0]
|
||||
param_type = param[1]
|
||||
|
||||
if param_type != "@fd":
|
||||
continue
|
||||
|
||||
param_type = "int"
|
||||
|
||||
internal_header.write("\t\t\t_body." + param_name + " = requestMessage.extractDescriptorAtIndex(_body." + param_name + "); \\\n")
|
||||
internal_header.write("\t\t}; \\\n")
|
||||
|
||||
internal_header.write("\t\t~" + camel_name + "() { \\\n")
|
||||
for param in call_parameters:
|
||||
param_name = param[0]
|
||||
param_type = param[1]
|
||||
|
||||
if param_type != "@fd":
|
||||
continue
|
||||
|
||||
param_type = "int"
|
||||
|
||||
internal_header.write("\t\t\tif (_body." + param_name + " != -1) { \\\n")
|
||||
internal_header.write("\t\t\t\tclose(_body." + param_name + "); \\\n")
|
||||
internal_header.write("\t\t} \\\n")
|
||||
|
||||
internal_header.write(textwrap.indent(textwrap.dedent("""\
|
||||
}}; \\
|
||||
virtual Call::Number number() const {{ \\
|
||||
return Call::Number::{0}; \\
|
||||
}}; \\
|
||||
virtual void processCall(); \\
|
||||
private: \\
|
||||
"""), '\t').format(camel_name))
|
||||
|
||||
internal_header.write("\t\tvoid _sendReply(int resultCode")
|
||||
for param in reply_parameters:
|
||||
param_name = param[0]
|
||||
param_type = param[1]
|
||||
|
||||
if param_type == "@fd":
|
||||
param_type = "int"
|
||||
fd_count_in_reply += 1
|
||||
|
||||
internal_header.write(", " + param_type + " " + param_name)
|
||||
internal_header.write(") { \\\n")
|
||||
|
||||
internal_header.write(textwrap.indent(textwrap.dedent("""\
|
||||
Message reply(sizeof(dserver_rpc_reply_{0}_t), {1}); \\
|
||||
reply.setAddress(_replyAddress); \\
|
||||
auto replyStruct = reinterpret_cast<dserver_rpc_reply_{0}_t*>(reply.data().data()); \\
|
||||
replyStruct->header.number = dserver_callnum_{0}; \\
|
||||
replyStruct->header.code = resultCode; \\
|
||||
"""), '\t\t\t').format(call_name, fd_count_in_reply))
|
||||
|
||||
fd_index = 0
|
||||
for param in reply_parameters:
|
||||
param_name = param[0]
|
||||
param_type = param[1]
|
||||
val = param_name
|
||||
|
||||
if param_type == "@fd":
|
||||
param_type = "int"
|
||||
val = str(fd_index)
|
||||
internal_header.write("\t\t\treply.pushDescriptor(" + param_name + "); \\\n")
|
||||
|
||||
internal_header.write("\t\t\treplyStruct->body." + param_name + " = " + val + "; \\\n")
|
||||
internal_header.write("\t\t\t_replyQueue.push(std::move(reply)); \\\n")
|
||||
internal_header.write("\t\t}; \\\n")
|
||||
|
||||
internal_header.write("\t}; \\\n")
|
||||
internal_header.write("\n")
|
||||
|
||||
for call in calls:
|
||||
call_name = call[0]
|
||||
call_parameters = call[1]
|
||||
reply_parameters = call[2]
|
||||
fd_count_in_call = 0
|
||||
fd_count_in_reply = 0
|
||||
|
||||
# define the RPC call body structure
|
||||
if len(call_parameters) > 0:
|
||||
public_header.write("typedef struct dserver_call_" + call_name + " dserver_call_" + call_name + "_t;\n")
|
||||
public_header.write("struct dserver_call_" + call_name + " {\n")
|
||||
for param in call_parameters:
|
||||
param_name = param[0]
|
||||
param_type = param[1]
|
||||
|
||||
if param_type == "@fd":
|
||||
param_type = "int"
|
||||
fd_count_in_call += 1
|
||||
|
||||
public_header.write("\t" + param_type + " " + param_name + ";\n")
|
||||
public_header.write("};\n")
|
||||
|
||||
# define the RPC call structure
|
||||
public_header.write(textwrap.dedent("""\
|
||||
typedef struct dserver_rpc_call_{0} dserver_rpc_call_{0}_t;
|
||||
struct dserver_rpc_call_{0} {{
|
||||
dserver_rpc_callhdr_t header;
|
||||
""").format(call_name))
|
||||
if len(call_parameters) > 0:
|
||||
public_header.write("\tdserver_call_" + call_name + "_t body;\n")
|
||||
public_header.write("};\n")
|
||||
|
||||
# define the RPC reply body structure
|
||||
if len(reply_parameters) > 0:
|
||||
public_header.write("typedef struct dserver_reply_" + call_name + " dserver_reply_" + call_name + "_t;\n")
|
||||
public_header.write("struct dserver_reply_" + call_name + " {\n")
|
||||
for param in reply_parameters:
|
||||
param_name = param[0]
|
||||
param_type = param[1]
|
||||
|
||||
if param_type == "@fd":
|
||||
param_type = "int"
|
||||
fd_count_in_reply += 1
|
||||
|
||||
public_header.write("\t" + param_type + " " + param_name + ";\n")
|
||||
public_header.write("};\n")
|
||||
|
||||
# define the RPC reply structure
|
||||
public_header.write(textwrap.dedent("""\
|
||||
typedef struct dserver_rpc_reply_{0} dserver_rpc_reply_{0}_t;
|
||||
struct dserver_rpc_reply_{0} {{
|
||||
dserver_rpc_replyhdr_t header;
|
||||
""").format(call_name))
|
||||
if len(reply_parameters) > 0:
|
||||
public_header.write("\tdserver_reply_" + call_name + "_t body;\n")
|
||||
public_header.write("};\n")
|
||||
|
||||
# declare the RPC call wrapper function
|
||||
# (and output the prototype part of the function definition)
|
||||
tmp = "int dserver_rpc_" + call_name + "("
|
||||
public_header.write(tmp)
|
||||
library_source.write(tmp)
|
||||
is_first = True
|
||||
for param in call_parameters:
|
||||
param_name = param[0]
|
||||
param_type = param[1]
|
||||
|
||||
if param_type == "@fd":
|
||||
param_type = "int"
|
||||
|
||||
if is_first:
|
||||
is_first = False
|
||||
tmp = ""
|
||||
else:
|
||||
tmp = ", "
|
||||
tmp += param_type + " " + param_name
|
||||
public_header.write(tmp)
|
||||
library_source.write(tmp)
|
||||
|
||||
for param in reply_parameters:
|
||||
param_name = param[0]
|
||||
param_type = param[1]
|
||||
|
||||
if param_type == "@fd":
|
||||
param_type = "int"
|
||||
|
||||
if is_first:
|
||||
is_first = False
|
||||
tmp = ""
|
||||
else:
|
||||
tmp = ", "
|
||||
tmp += param_type + "* out_" + param_name
|
||||
public_header.write(tmp)
|
||||
library_source.write(tmp)
|
||||
public_header.write(");\n\n")
|
||||
library_source.write(") {\n")
|
||||
|
||||
# define the RPC call wrapper function
|
||||
library_source.write(textwrap.indent(textwrap.dedent("""\
|
||||
int status = 0;
|
||||
dserver_rpc_call_{0}_t call = {{
|
||||
.header = {{
|
||||
.pid = dserver_rpc_hooks_get_pid(),
|
||||
.tid = dserver_rpc_hooks_get_tid(),
|
||||
.number = dserver_callnum_{0},
|
||||
}},
|
||||
"""), '\t').format(call_name))
|
||||
|
||||
if len(call_parameters) > 0:
|
||||
library_source.write("\t\t.body = {\n")
|
||||
fd_index = 0
|
||||
for param in call_parameters:
|
||||
param_name = param[0]
|
||||
param_type = param[1]
|
||||
val = param_name
|
||||
|
||||
if param_type == "@fd":
|
||||
param_type = "int"
|
||||
val = "(" + param_name + " < 0) ? -1 : " + str(fd_index)
|
||||
fd_index += 1
|
||||
|
||||
library_source.write("\t\t\t." + param_name + " = " + val + ",\n")
|
||||
library_source.write("\t\t},\n")
|
||||
|
||||
library_source.write("\t};\n")
|
||||
library_source.write("\tdserver_rpc_reply_" + call_name + "_t reply;\n")
|
||||
|
||||
if fd_count_in_call > 0 or fd_count_in_reply > 0:
|
||||
library_source.write("\tint fds[" + max(fd_count_in_call, fd_count_in_reply) + "]")
|
||||
if fd_count_in_call > 0:
|
||||
library_source.write(" = { ")
|
||||
is_first = True
|
||||
for param in call_parameters:
|
||||
param_name = param[0]
|
||||
param_type = param[1]
|
||||
|
||||
if param_type != "@fd":
|
||||
continue
|
||||
|
||||
if is_first:
|
||||
is_first = False
|
||||
else:
|
||||
library_source.write(", ")
|
||||
library_source.write(param_name)
|
||||
library_source.write(" }")
|
||||
library_source.write(";\n")
|
||||
library_source.write("\tchar controlbuf[DSERVER_RPC_HOOKS_CMSG_SPACE(sizeof(fds))];\n")
|
||||
|
||||
library_source.write(textwrap.indent(textwrap.dedent("""\
|
||||
dserver_rpc_hooks_iovec_t call_data = {
|
||||
.iov_base = &call,
|
||||
.iov_len = sizeof(call),
|
||||
};
|
||||
dserver_rpc_hooks_msghdr_t callmsg = {
|
||||
.msg_name = dserver_rpc_hooks_get_server_address(),
|
||||
.msg_namelen = dserver_rpc_hooks_get_server_address_length(),
|
||||
.msg_iov = &call_data,
|
||||
.msg_iovlen = 1,
|
||||
"""), '\t'))
|
||||
|
||||
if fd_count_in_call == 0:
|
||||
library_source.write("\t\t.msg_control = NULL,\n")
|
||||
library_source.write("\t\t.msg_controllen = 0,\n")
|
||||
else:
|
||||
library_source.write("\t\t.msg_control = controlbuf,\n")
|
||||
library_source.write("\t\t.msg_controllen = sizeof(controlbuf),\n")
|
||||
|
||||
library_source.write("\t};\n")
|
||||
|
||||
if fd_count_in_call > 0:
|
||||
library_source.write(textwrap.indent(textwrap.dedent("""\
|
||||
dserver_rpc_hooks_cmsghdr_t* call_cmsg = DSERVER_RPC_HOOKS_CMSG_FIRSTHDR(&callmsg);
|
||||
call_cmsg->cmsg_level = DSERVER_RPC_HOOKS_SOL_SOCKET;
|
||||
call_cmsg->cmsg_type = DSERVER_RPC_HOOKS_SCM_RIGHTS;
|
||||
call_cmsg->cmsg_len = DSERVER_RPC_HOOKS_CMSG_LEN(sizeof(int) * {0});
|
||||
dserver_rpc_hooks_memcpy(DSERVER_RPC_HOOKS_CMSG_DATA(call_cmsg), fds, sizeof(int) * {0});
|
||||
"""), '\t').format(fd_count_in_call))
|
||||
|
||||
library_source.write(textwrap.indent(textwrap.dedent("""\
|
||||
dserver_rpc_hooks_iovec_t reply_data = {
|
||||
.iov_base = &reply,
|
||||
.iov_len = sizeof(reply),
|
||||
};
|
||||
dserver_rpc_hooks_msghdr_t replymsg = {
|
||||
.msg_name = NULL,
|
||||
.msg_namelen = 0,
|
||||
.msg_iov = &reply_data,
|
||||
.msg_iovlen = 1,
|
||||
"""), '\t'))
|
||||
|
||||
if fd_count_in_reply == 0:
|
||||
library_source.write("\t\t.msg_control = NULL,\n")
|
||||
library_source.write("\t\t.msg_controllen = 0,\n")
|
||||
else:
|
||||
library_source.write("\t\t.msg_control = controlbuf,\n")
|
||||
library_source.write("\t\t.msg_controllen = sizeof(controlbuf),\n")
|
||||
|
||||
library_source.write("\t};\n\n")
|
||||
|
||||
library_source.write("\tint socket = dserver_rpc_hooks_get_socket();\n")
|
||||
library_source.write("\tif (socket < 0) {")
|
||||
library_source.write("\t\treturn dserver_rpc_hooks_get_broken_pipe_status();\n")
|
||||
library_source.write("\t}\n\n")
|
||||
|
||||
library_source.write("\tlong int long_status = dserver_rpc_hooks_send_message(socket, &callmsg);\n")
|
||||
library_source.write("\tif (long_status < 0) {\n")
|
||||
library_source.write("\t\treturn (int)long_status;\n")
|
||||
library_source.write("\t}\n\n")
|
||||
library_source.write("\tif (long_status != sizeof(call)) {\n")
|
||||
library_source.write("\t\treturn dserver_rpc_hooks_get_communication_error_status();\n")
|
||||
library_source.write("\t}\n\n")
|
||||
|
||||
library_source.write("\tlong_status = dserver_rpc_hooks_receive_message(socket, &replymsg);\n")
|
||||
library_source.write("\tif (long_status < 0) {\n")
|
||||
library_source.write("\t\treturn (int)long_status;\n")
|
||||
library_source.write("\t}\n\n")
|
||||
library_source.write("\tif (long_status != sizeof(reply)) {\n")
|
||||
library_source.write("\t\treturn dserver_rpc_hooks_get_communication_error_status();\n")
|
||||
library_source.write("\t}\n\n")
|
||||
|
||||
if fd_count_in_reply != 0:
|
||||
library_source.write(textwrap.indent(textwrap.dedent("""\
|
||||
dserver_rpc_hooks_cmsghdr_t* reply_cmsg = DSERVER_RPC_HOOKS_CMSG_FIRSTHDR(&replymsg);
|
||||
if (!reply_cmsg || reply_cmsg->cmsg_level != DSERVER_RPC_HOOKS_SOL_SOCKET || reply_cmsg->cmsg_type != DSERVER_RPC_HOOKS_SCM_RIGHTS || reply_cmsg->cmsg_len != DSERVER_RPC_HOOKS_CMSG_LEN(sizeof(int) * {0})) {{
|
||||
status = dserver_rpc_hooks_get_bad_message_status();
|
||||
return status;
|
||||
}}
|
||||
dserver_rpc_hooks_memcpy(fds, DSERVER_RPC_HOOKS_CMSG_DATA(reply_cmsg), sizeof(int) * {0});
|
||||
"""), '\t').format(fd_count_in_reply))
|
||||
|
||||
for param in reply_parameters:
|
||||
param_name = param[0]
|
||||
param_type = param[1]
|
||||
|
||||
if param_type == "@fd":
|
||||
param_type = "int"
|
||||
library_source.write("\tif (out_" + param_name + ") {\n")
|
||||
library_source.write("\t\t*out_" + param_name + " = fds[reply.body." + param_name + "];\n")
|
||||
library_source.write("\t} else {\n")
|
||||
library_source.write("\t\tdserver_rpc_hooks_close_fd(fds[reply.body." + param_name + "]);\n")
|
||||
library_source.write("\t}\n")
|
||||
else:
|
||||
library_source.write("\tif (out_" + param_name + ") {\n")
|
||||
library_source.write("\t\t*out_" + param_name + " = reply.body." + param_name + ";\n")
|
||||
library_source.write("\t}\n")
|
||||
|
||||
library_source.write("\treturn status;\n")
|
||||
|
||||
library_source.write("};\n\n")
|
||||
|
||||
public_header.write("""\
|
||||
#ifdef __cplusplus
|
||||
};
|
||||
#endif
|
||||
|
||||
#endif // _DARLINGSERVER_API_H_
|
||||
""")
|
||||
|
||||
public_header.close()
|
||||
internal_header.close()
|
||||
library_source.close()
|
@ -1,162 +0,0 @@
|
||||
/**
|
||||
* This file is part of Darling.
|
||||
*
|
||||
* Copyright (C) 2021 Darling developers
|
||||
*
|
||||
* Darling is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Darling is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with Darling. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#define _GNU_SOURCE 1
|
||||
#include <darlingserver/call.hpp>
|
||||
#include <darlingserver/server.hpp>
|
||||
#include <sys/uio.h>
|
||||
|
||||
std::shared_ptr<DarlingServer::Call> DarlingServer::Call::callFromMessage(Message&& requestMessage, MessageQueue& replyQueue) {
|
||||
if (requestMessage.data().size() < sizeof(dserver_rpc_callhdr_t)) {
|
||||
throw std::invalid_argument("Message buffer was too small for call header");
|
||||
}
|
||||
|
||||
dserver_rpc_callhdr_t* header = reinterpret_cast<dserver_rpc_callhdr_t*>(requestMessage.data().data());
|
||||
std::shared_ptr<Call> result = nullptr;
|
||||
std::shared_ptr<Process> process = nullptr;
|
||||
std::shared_ptr<Thread> thread = nullptr;
|
||||
|
||||
// first, make sure we know this call number
|
||||
switch (header->number) {
|
||||
DSERVER_VALID_CALLNUM_CASES
|
||||
break;
|
||||
|
||||
default:
|
||||
throw std::invalid_argument("Invalid call number");
|
||||
}
|
||||
|
||||
// now let's lookup (and possibly create) the process and thread making this call
|
||||
process = processRegistry().registerIfAbsent(header->pid, [&]() {
|
||||
auto tmp = std::make_shared<Process>(requestMessage.pid(), header->pid);
|
||||
Server::sharedInstance().monitorProcess(tmp);
|
||||
return tmp;
|
||||
});
|
||||
thread = threadRegistry().registerIfAbsent(header->tid, [&]() {
|
||||
auto tmp = std::make_shared<Thread>(process, header->tid);
|
||||
tmp->setAddress(requestMessage.address());
|
||||
tmp->registerWithProcess();
|
||||
return tmp;
|
||||
});
|
||||
|
||||
// finally, let's construct the call class
|
||||
|
||||
#define CALL_CASE(_callName, _className) \
|
||||
case dserver_callnum_ ## _callName: { \
|
||||
if (requestMessage.data().size() < sizeof(dserver_rpc_call_ ## _callName ## _t)) { \
|
||||
throw std::invalid_argument("Message buffer was too small for dserver_call_" #_callName "_t"); \
|
||||
} \
|
||||
result = std::make_shared<_className>(replyQueue, thread, reinterpret_cast<dserver_rpc_call_ ## _callName ## _t*>(header), std::move(requestMessage)); \
|
||||
} break;
|
||||
|
||||
switch (header->number) {
|
||||
DSERVER_CONSTRUCT_CASES
|
||||
|
||||
default:
|
||||
throw std::invalid_argument("Invalid call number");
|
||||
}
|
||||
|
||||
#undef CALL_CASE
|
||||
|
||||
thread->setPendingCall(result);
|
||||
|
||||
return result;
|
||||
};
|
||||
|
||||
DarlingServer::Call::Call(MessageQueue& replyQueue, std::shared_ptr<Thread> thread, Address replyAddress):
|
||||
_replyQueue(replyQueue),
|
||||
_thread(thread),
|
||||
_replyAddress(replyAddress)
|
||||
{};
|
||||
|
||||
DarlingServer::Call::~Call() {};
|
||||
|
||||
/**
|
||||
* Note that you MUST NOT have any local variables referencing `this` when this method is called.
|
||||
*
|
||||
* This Call object MAY be destroyed upon the return of this method.
|
||||
*/
|
||||
void DarlingServer::Call::_stopPending() {
|
||||
// we're done processing this call; dump it
|
||||
if (auto thread = _thread.lock()) {
|
||||
thread->setPendingCall(nullptr);
|
||||
}
|
||||
};
|
||||
|
||||
void DarlingServer::Call::Checkin::processCall() {
|
||||
// the Call creation already took care of registering the process and thread
|
||||
_sendReply(0);
|
||||
_stopPending();
|
||||
};
|
||||
|
||||
void DarlingServer::Call::Checkout::processCall() {
|
||||
int code = 0;
|
||||
|
||||
if (auto thread = _thread.lock()) {
|
||||
if (auto process = thread->process()) {
|
||||
threadRegistry().unregisterEntry(thread);
|
||||
|
||||
if (thread->id() == process->id()) {
|
||||
processRegistry().unregisterEntry(process);
|
||||
}
|
||||
} else {
|
||||
code = -ESRCH;
|
||||
}
|
||||
} else {
|
||||
code = -ESRCH;
|
||||
}
|
||||
|
||||
_sendReply(code);
|
||||
_stopPending();
|
||||
};
|
||||
|
||||
void DarlingServer::Call::VchrootPath::processCall() {
|
||||
int code = 0;
|
||||
size_t fullLength = 0;
|
||||
|
||||
if (auto thread = _thread.lock()) {
|
||||
if (auto process = thread->process()) {
|
||||
if (_body.buffer_size > 0) {
|
||||
struct iovec local;
|
||||
struct iovec remote;
|
||||
auto tmpstr = process->vchrootPath().substr(0, _body.buffer_size - 1);
|
||||
auto len = std::min(tmpstr.length() + 1, _body.buffer_size);
|
||||
|
||||
fullLength = process->vchrootPath().length();
|
||||
|
||||
// note that, despite the const cast, this is safe because the local iovec data is not modified by the call
|
||||
local.iov_base = const_cast<char*>(tmpstr.c_str());
|
||||
local.iov_len = len;
|
||||
|
||||
remote.iov_base = _body.buffer;
|
||||
remote.iov_len = len;
|
||||
|
||||
if (process_vm_writev(process->id(), &local, 1, &remote, 1, 0) < 0) {
|
||||
code = -errno;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
code = -ESRCH;
|
||||
}
|
||||
} else {
|
||||
code = -ESRCH;
|
||||
}
|
||||
|
||||
_sendReply(code, fullLength);
|
||||
_stopPending();
|
||||
};
|
@ -1,532 +0,0 @@
|
||||
/**
|
||||
* This file is part of Darling.
|
||||
*
|
||||
* Copyright (C) 2021 Darling developers
|
||||
*
|
||||
* Darling is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Darling is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with Darling. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#define _GNU_SOURCE 1
|
||||
#include <stdio.h>
|
||||
#include <unistd.h>
|
||||
#include <sched.h>
|
||||
#include <stdlib.h>
|
||||
#include <sys/mount.h>
|
||||
#include <errno.h>
|
||||
#include <string.h>
|
||||
#include <stdbool.h>
|
||||
#include <sys/prctl.h>
|
||||
#include <dirent.h>
|
||||
#include <sys/stat.h>
|
||||
#include <pwd.h>
|
||||
#include <sys/mman.h>
|
||||
#include <fcntl.h>
|
||||
#include <sys/resource.h>
|
||||
#include <iostream>
|
||||
#include <linux/sched.h>
|
||||
#include <sys/syscall.h>
|
||||
#include <sys/signal.h>
|
||||
|
||||
#include <darling-config.h>
|
||||
|
||||
#include <darlingserver/server.hpp>
|
||||
|
||||
// TODO: most of the code here was ported over from startup/darling.c; we should C++-ify it.
|
||||
|
||||
#define MLDR_PATH LIBEXEC_PATH "/usr/libexec/darling/mldr"
|
||||
|
||||
void fixPermissionsRecursive(const char* path, uid_t originalUID, gid_t originalGID)
|
||||
{
|
||||
DIR* dir;
|
||||
struct dirent* ent;
|
||||
|
||||
if (chown(path, originalUID, originalGID) == -1)
|
||||
fprintf(stderr, "Cannot chown %s: %s\n", path, strerror(errno));
|
||||
|
||||
dir = opendir(path);
|
||||
if (!dir)
|
||||
return;
|
||||
|
||||
while ((ent = readdir(dir)) != NULL)
|
||||
{
|
||||
if (ent->d_type == DT_DIR)
|
||||
{
|
||||
char* subdir;
|
||||
|
||||
if (strcmp(ent->d_name, ".") == 0 || strcmp(ent->d_name, "..") == 0)
|
||||
continue;
|
||||
|
||||
subdir = (char*) malloc(strlen(path) + 2 + strlen(ent->d_name));
|
||||
sprintf(subdir, "%s/%s", path, ent->d_name);
|
||||
|
||||
fixPermissionsRecursive(subdir, originalUID, originalGID);
|
||||
|
||||
free(subdir);
|
||||
}
|
||||
}
|
||||
|
||||
closedir(dir);
|
||||
}
|
||||
|
||||
const char* xdgDirectory(const char* name)
|
||||
{
|
||||
static char dir[4096];
|
||||
char* cmd = (char*) malloc(16 + strlen(name));
|
||||
|
||||
sprintf(cmd, "xdg-user-dir %s", name);
|
||||
|
||||
FILE* proc = popen(cmd, "r");
|
||||
|
||||
free(cmd);
|
||||
|
||||
if (!proc)
|
||||
return NULL;
|
||||
|
||||
fgets(dir, sizeof(dir)-1, proc);
|
||||
|
||||
pclose(proc);
|
||||
|
||||
size_t len = strlen(dir);
|
||||
if (len <= 1)
|
||||
return NULL;
|
||||
|
||||
if (dir[len-1] == '\n')
|
||||
dir[len-1] = '\0';
|
||||
return dir;
|
||||
}
|
||||
|
||||
void setupUserHome(const char* prefix, uid_t originalUID)
|
||||
{
|
||||
char buf[4096], buf2[4096];
|
||||
|
||||
snprintf(buf, sizeof(buf), "%s/Users", prefix);
|
||||
|
||||
// Remove the old /Users symlink that may exist
|
||||
unlink(buf);
|
||||
|
||||
// mkdir /Users
|
||||
mkdir(buf, 0777);
|
||||
|
||||
// mkdir /Users/Shared
|
||||
strcat(buf, "/Shared");
|
||||
mkdir(buf, 0777);
|
||||
|
||||
const char* home = getenv("HOME");
|
||||
|
||||
const char* login = NULL;
|
||||
struct passwd* pw = getpwuid(originalUID);
|
||||
|
||||
if (pw != NULL)
|
||||
login = pw->pw_name;
|
||||
|
||||
if (!login)
|
||||
login = getlogin();
|
||||
|
||||
if (!login)
|
||||
{
|
||||
fprintf(stderr, "Cannot determine your user name\n");
|
||||
exit(1);
|
||||
}
|
||||
if (!home)
|
||||
{
|
||||
fprintf(stderr, "Cannot determine your home directory\n");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
snprintf(buf, sizeof(buf), "%s/Users/%s", prefix, login);
|
||||
|
||||
// mkdir /Users/$LOGIN
|
||||
mkdir(buf, 0755);
|
||||
|
||||
snprintf(buf2, sizeof(buf2), "/Volumes/SystemRoot%s", home);
|
||||
|
||||
strcat(buf, "/LinuxHome");
|
||||
unlink(buf);
|
||||
|
||||
// symlink /Users/$LOGIN/LinuxHome -> $HOME
|
||||
symlink(buf2, buf);
|
||||
|
||||
static const char* xdgmap[][2] = {
|
||||
{ "DESKTOP", "Desktop" },
|
||||
{ "DOWNLOAD", "Downloads" },
|
||||
{ "PUBLICSHARE", "Public" },
|
||||
{ "DOCUMENTS", "Documents" },
|
||||
{ "MUSIC", "Music" },
|
||||
{ "PICTURES", "Pictures" },
|
||||
{ "VIDEOS", "Movies" },
|
||||
};
|
||||
|
||||
for (int i = 0; i < sizeof(xdgmap) / sizeof(xdgmap[0]); i++)
|
||||
{
|
||||
const char* dir = xdgDirectory(xdgmap[i][0]);
|
||||
if (!dir)
|
||||
continue;
|
||||
|
||||
snprintf(buf2, sizeof(buf2), "/Volumes/SystemRoot%s", dir);
|
||||
snprintf(buf, sizeof(buf), "%s/Users/%s/%s", prefix, login, xdgmap[i][1]);
|
||||
|
||||
unlink(buf);
|
||||
symlink(buf2, buf);
|
||||
}
|
||||
}
|
||||
|
||||
void setupCoredumpPattern(void)
|
||||
{
|
||||
FILE* f = fopen("/proc/sys/kernel/core_pattern", "w");
|
||||
if (f != NULL)
|
||||
{
|
||||
// This is how macOS saves core dumps
|
||||
fputs("/cores/core.%p\n", f);
|
||||
fclose(f);
|
||||
}
|
||||
}
|
||||
|
||||
static void wipeDir(const char* dirpath)
|
||||
{
|
||||
char path[4096];
|
||||
struct dirent* ent;
|
||||
DIR* dir = opendir(dirpath);
|
||||
|
||||
if (!dir)
|
||||
return;
|
||||
|
||||
while ((ent = readdir(dir)) != NULL)
|
||||
{
|
||||
if (strcmp(ent->d_name, ".") == 0 || strcmp(ent->d_name, "..") == 0)
|
||||
continue;
|
||||
|
||||
snprintf(path, sizeof(path), "%s/%s", dirpath, ent->d_name);
|
||||
|
||||
if (ent->d_type == DT_DIR)
|
||||
{
|
||||
wipeDir(path);
|
||||
rmdir(path);
|
||||
}
|
||||
else
|
||||
unlink(path);
|
||||
}
|
||||
|
||||
closedir(dir);
|
||||
}
|
||||
|
||||
void darlingPreInit(const char* prefix)
|
||||
{
|
||||
// TODO: Run /usr/libexec/makewhatis
|
||||
const char* dirs[] = {
|
||||
"/var/tmp",
|
||||
"/var/run"
|
||||
};
|
||||
|
||||
char fullpath[4096];
|
||||
strcpy(fullpath, prefix);
|
||||
const size_t prefixLen = strlen(fullpath);
|
||||
|
||||
for (size_t i = 0; i < sizeof(dirs)/sizeof(dirs[0]); i++)
|
||||
{
|
||||
fullpath[prefixLen] = 0;
|
||||
strcat(fullpath, dirs[i]);
|
||||
wipeDir(fullpath);
|
||||
}
|
||||
}
|
||||
|
||||
void spawnLaunchd(const char* prefix)
|
||||
{
|
||||
puts("Bootstrapping the container with launchd...");
|
||||
|
||||
// putenv("KQUEUE_DEBUG=1");
|
||||
|
||||
auto tmp = (std::string(prefix) + "/var/run/darlingserver.sock");
|
||||
|
||||
setenv("DYLD_ROOT_PATH", LIBEXEC_PATH, 1);
|
||||
setenv("__mldr_sockpath", tmp.c_str(), 1);
|
||||
execl(MLDR_PATH, "mldr!" LIBEXEC_PATH "/usr/libexec/darling/vchroot", "vchroot", prefix, "/sbin/launchd", NULL);
|
||||
|
||||
fprintf(stderr, "Failed to exec launchd: %s\n", strerror(errno));
|
||||
abort();
|
||||
}
|
||||
|
||||
static bool testEnvVar(const char* var_name) {
|
||||
const char* var = getenv(var_name);
|
||||
|
||||
if (!var) {
|
||||
return false;
|
||||
}
|
||||
|
||||
auto len = strlen(var);
|
||||
|
||||
if (len > 0 && (var[0] == '1' || var[0] == 't' || var[0] == 'T')) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
};
|
||||
|
||||
static void temp_drop_privileges(uid_t uid, gid_t gid) {
|
||||
// it's important to drop GID first, because non-root users can't change their GID
|
||||
if (setresgid(gid, gid, 0) < 0) {
|
||||
fprintf(stderr, "Failed to temporarily drop group privileges\n");
|
||||
exit(1);
|
||||
}
|
||||
if (setresuid(uid, uid, 0) < 0) {
|
||||
fprintf(stderr, "Failed to temporarily drop user privileges\n");
|
||||
exit(1);
|
||||
}
|
||||
};
|
||||
|
||||
static void perma_drop_privileges(uid_t uid, gid_t gid) {
|
||||
if (setresgid(gid, gid, gid) < 0) {
|
||||
fprintf(stderr, "Failed to drop group privileges\n");
|
||||
exit(1);
|
||||
}
|
||||
if (setresuid(uid, uid, uid) < 0) {
|
||||
fprintf(stderr, "Failed to drop user privileges\n");
|
||||
exit(1);
|
||||
}
|
||||
};
|
||||
|
||||
static void regain_privileges() {
|
||||
if (seteuid(0) < 0) {
|
||||
fprintf(stderr, "Failed to regain root EUID\n");
|
||||
exit(1);
|
||||
}
|
||||
if (setegid(0) < 0) {
|
||||
fprintf(stderr, "Failed to regain root EGID\n");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
if (setresuid(0, 0, 0) < 0) {
|
||||
fprintf(stderr, "Failed to regain root privileges\n");
|
||||
exit(1);
|
||||
}
|
||||
if (setresgid(0, 0, 0) < 0) {
|
||||
fprintf(stderr, "Failed to regain root privileges\n");
|
||||
exit(1);
|
||||
}
|
||||
};
|
||||
|
||||
int main(int argc, char** argv) {
|
||||
const char* prefix = NULL;
|
||||
uid_t originalUID = -1;
|
||||
gid_t originalGID = -1;
|
||||
int pipefd = -1;
|
||||
bool fix_permissions = false;
|
||||
pid_t launchdGlobalPID = -1;
|
||||
size_t prefix_length = 0;
|
||||
struct rlimit default_limit;
|
||||
struct rlimit increased_limit;
|
||||
FILE* nr_open_file = NULL;
|
||||
int childWaitFDs[2];
|
||||
|
||||
char *opts;
|
||||
char putOld[4096];
|
||||
char *p;
|
||||
|
||||
if (argc < 6) {
|
||||
fprintf(stderr, "darlingserver is not meant to be started manually\n");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
prefix = argv[1];
|
||||
sscanf(argv[2], "%d", &originalUID);
|
||||
sscanf(argv[3], "%d", &originalGID);
|
||||
sscanf(argv[4], "%d", &pipefd);
|
||||
|
||||
if (argv[5][0] == '1') {
|
||||
fix_permissions = true;
|
||||
}
|
||||
|
||||
prefix_length = strlen(prefix);
|
||||
|
||||
if (getuid() != 0 || getgid() != 0) {
|
||||
fprintf(stderr, "darlingserver needs to start as root\n");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
// temporarily drop privileges to perform some prefix work
|
||||
temp_drop_privileges(originalUID, originalGID);
|
||||
setupUserHome(prefix, originalUID);
|
||||
//setupCoredumpPattern();
|
||||
regain_privileges();
|
||||
|
||||
// read the default rlimit so we can restore it for our children
|
||||
if (getrlimit(RLIMIT_NOFILE, &default_limit) != 0) {
|
||||
fprintf(stderr, "Failed to read default FD rlimit: %s\n", strerror(errno));
|
||||
exit(1);
|
||||
}
|
||||
|
||||
// read the system maximum
|
||||
nr_open_file = fopen("/proc/sys/fs/nr_open", "r");
|
||||
if (nr_open_file == NULL) {
|
||||
fprintf(stderr, "Failed to open /proc/sys/fs/nr_open: %s\n", strerror(errno));
|
||||
exit(1);
|
||||
}
|
||||
if (fscanf(nr_open_file, "%lu", &increased_limit.rlim_max) != 1) {
|
||||
fprintf(stderr, "Failed to read /proc/sys/fs/nr_open: %s\n", strerror(errno));
|
||||
exit(1);
|
||||
}
|
||||
increased_limit.rlim_cur = increased_limit.rlim_max;
|
||||
if (fclose(nr_open_file) != 0) {
|
||||
fprintf(stderr, "Failed to close /proc/sys/fs/nr_open: %s\n", strerror(errno));
|
||||
exit(1);
|
||||
}
|
||||
|
||||
// now set our increased rlimit
|
||||
if (setrlimit(RLIMIT_NOFILE, &increased_limit) != 0) {
|
||||
fprintf(stderr, "Failed to increase FD rlimit: %s\n", strerror(errno));
|
||||
exit(1);
|
||||
}
|
||||
|
||||
// Since overlay cannot be mounted inside user namespaces, we have to setup a new mount namespace
|
||||
// and do the mount while we can be root
|
||||
if (unshare(CLONE_NEWNS) != 0)
|
||||
{
|
||||
fprintf(stderr, "Cannot unshare PID and mount namespaces: %s\n", strerror(errno));
|
||||
exit(1);
|
||||
}
|
||||
|
||||
// Because systemd marks / as MS_SHARED and we would inherit this into the overlay mount,
|
||||
// causing it not to be unmounted once the init process dies.
|
||||
if (mount(NULL, "/", NULL, MS_REC | MS_SLAVE, NULL) != 0)
|
||||
{
|
||||
fprintf(stderr, "Cannot remount / as slave: %s\n", strerror(errno));
|
||||
exit(1);
|
||||
}
|
||||
|
||||
umount("/dev/shm");
|
||||
if (mount("tmpfs", "/dev/shm", "tmpfs", MS_NOSUID | MS_NOEXEC | MS_NODEV, NULL) != 0)
|
||||
{
|
||||
fprintf(stderr, "Cannot mount new /dev/shm: %s\n", strerror(errno));
|
||||
exit(1);
|
||||
}
|
||||
|
||||
opts = (char*) malloc(strlen(prefix)*2 + sizeof(LIBEXEC_PATH) + 100);
|
||||
|
||||
const char* opts_fmt = "lowerdir=%s,upperdir=%s,workdir=%s.workdir,index=off";
|
||||
|
||||
sprintf(opts, opts_fmt, LIBEXEC_PATH, prefix, prefix);
|
||||
|
||||
// Mount overlay onto our prefix
|
||||
if (mount("overlay", prefix, "overlay", 0, opts) != 0)
|
||||
{
|
||||
fprintf(stderr, "Cannot mount overlay: %s\n", strerror(errno));
|
||||
exit(1);
|
||||
}
|
||||
|
||||
free(opts);
|
||||
|
||||
// This is executed once at prefix creation
|
||||
if (fix_permissions) {
|
||||
const char* extra_paths[] = {
|
||||
"/private/etc/passwd",
|
||||
"/private/etc/master.passwd",
|
||||
"/private/etc/group",
|
||||
};
|
||||
char path[4096];
|
||||
|
||||
fixPermissionsRecursive(prefix, originalUID, originalGID);
|
||||
|
||||
path[sizeof(path) - 1] = '\0';
|
||||
strncpy(path, prefix, sizeof(path) - 1);
|
||||
for (size_t i = 0; i < sizeof(extra_paths) / sizeof(*extra_paths); ++i) {
|
||||
path[prefix_length] = '\0';
|
||||
strncat(path, extra_paths[i], sizeof(path) - 1);
|
||||
fixPermissionsRecursive(path, originalUID, originalGID);
|
||||
}
|
||||
}
|
||||
|
||||
snprintf(putOld, sizeof(putOld), "%s/proc", prefix);
|
||||
|
||||
// mount procfs for our new PID namespace
|
||||
if (mount("proc", putOld, "proc", 0, "") != 0)
|
||||
{
|
||||
fprintf(stderr, "Cannot mount procfs: %s\n", strerror(errno));
|
||||
exit(1);
|
||||
}
|
||||
|
||||
// temporarily drop privileges and do some prefix work
|
||||
temp_drop_privileges(originalUID, originalGID);
|
||||
darlingPreInit(prefix);
|
||||
regain_privileges();
|
||||
|
||||
// Tell the parent we're ready
|
||||
write(pipefd, ".", 1);
|
||||
close(pipefd);
|
||||
|
||||
if (pipe(childWaitFDs) != 0) {
|
||||
std::cerr << "Failed to create child waiting pipe: " << strerror(errno) << std::endl;
|
||||
exit(1);
|
||||
}
|
||||
|
||||
// we have to use `clone` rather than `fork` to create the process in its own PID namespace
|
||||
// and still be able to spawn new processes and threads of our own
|
||||
struct clone_args launchdCloneArgs;
|
||||
launchdCloneArgs.flags = CLONE_NEWPID;
|
||||
launchdCloneArgs.pidfd = 0;
|
||||
launchdCloneArgs.child_tid = 0;
|
||||
launchdCloneArgs.parent_tid = 0;
|
||||
launchdCloneArgs.exit_signal = SIGCHLD;
|
||||
launchdCloneArgs.stack = 0;
|
||||
launchdCloneArgs.stack_size = 0;
|
||||
launchdCloneArgs.tls = 0;
|
||||
launchdCloneArgs.set_tid = 0;
|
||||
launchdCloneArgs.set_tid_size = 0;
|
||||
launchdCloneArgs.cgroup = 0;
|
||||
launchdGlobalPID = syscall(SYS_clone3, &launchdCloneArgs, sizeof(launchdCloneArgs));
|
||||
|
||||
// drop privileges in both parent and child
|
||||
perma_drop_privileges(originalUID, originalGID);
|
||||
prctl(PR_SET_DUMPABLE, 1, 0, 0, 0);
|
||||
|
||||
if (launchdGlobalPID < 0) {
|
||||
fprintf(stderr, "Failed to fork to start launchd: %s\n", strerror(errno));
|
||||
exit(1);
|
||||
} else if (launchdGlobalPID == 0) {
|
||||
// this is the child
|
||||
char buf[1];
|
||||
|
||||
close(childWaitFDs[1]);
|
||||
|
||||
// decrease the FD limit back to the default
|
||||
if (setrlimit(RLIMIT_NOFILE, &default_limit) != 0) {
|
||||
fprintf(stderr, "Failed to decrease FD limit back down for launchd: %s\n", strerror(errno));
|
||||
exit(1);
|
||||
}
|
||||
|
||||
// wait for the parent to give us the green light
|
||||
read(childWaitFDs[0], buf, 1);
|
||||
close(childWaitFDs[0]);
|
||||
|
||||
spawnLaunchd(prefix);
|
||||
__builtin_unreachable();
|
||||
}
|
||||
|
||||
// this is the parent
|
||||
close(childWaitFDs[0]);
|
||||
|
||||
// create the server
|
||||
auto server = new DarlingServer::Server(prefix);
|
||||
|
||||
// tell the child to go ahead; the socket has been created
|
||||
write(childWaitFDs[1], ".", 1);
|
||||
close(childWaitFDs[1]);
|
||||
|
||||
// start the main loop
|
||||
server->start();
|
||||
|
||||
// this should never happen
|
||||
std::cerr << "Server exited main loop!" << std::endl;
|
||||
delete server;
|
||||
|
||||
return 1;
|
||||
};
|
@ -1,87 +0,0 @@
|
||||
/**
|
||||
* This file is part of Darling.
|
||||
*
|
||||
* Copyright (C) 2021 Darling developers
|
||||
*
|
||||
* Darling is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Darling is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with Darling. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <darlingserver/logging.hpp>
|
||||
#include <darlingserver/server.hpp>
|
||||
#include <filesystem>
|
||||
#include <fcntl.h>
|
||||
#include <unistd.h>
|
||||
|
||||
DarlingServer::Log::Log(std::string category):
|
||||
_category(category)
|
||||
{};
|
||||
|
||||
DarlingServer::Log::Stream::Stream(Type type, Log& log):
|
||||
_type(type),
|
||||
_log(log)
|
||||
{};
|
||||
template<>
|
||||
DarlingServer::Log::Stream& DarlingServer::Log::Stream::operator<<<EndLog>(EndLog value) {
|
||||
_log._log(_type, _buffer.str());
|
||||
return *this;
|
||||
};
|
||||
|
||||
DarlingServer::Log::Stream DarlingServer::Log::debug() {
|
||||
return Stream(Type::Debug, *this);
|
||||
};
|
||||
|
||||
DarlingServer::Log::Stream DarlingServer::Log::info() {
|
||||
return Stream(Type::Info, *this);
|
||||
};
|
||||
|
||||
DarlingServer::Log::Stream DarlingServer::Log::warning() {
|
||||
return Stream(Type::Warning, *this);
|
||||
};
|
||||
|
||||
DarlingServer::Log::Stream DarlingServer::Log::error() {
|
||||
return Stream(Type::Error, *this);
|
||||
};
|
||||
|
||||
std::string DarlingServer::Log::_typeToString(Type type) {
|
||||
switch (type) {
|
||||
case Type::Debug:
|
||||
return "Debug";
|
||||
case Type::Info:
|
||||
return "Info";
|
||||
case Type::Warning:
|
||||
return "Warning";
|
||||
case Type::Error:
|
||||
return "Error";
|
||||
default:
|
||||
return "Unknown";
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
void DarlingServer::Log::_log(Type type, std::string message) {
|
||||
// NOTE: we use POSIX file APIs because we want to append each message to the log file atomically,
|
||||
// and as far as i can tell, C++ fstreams provide no such guarantee (that they won't write in chunks).
|
||||
static int logFile = []() {
|
||||
std::filesystem::path path(Server::sharedInstance().prefix() + "/private/var/log/");
|
||||
std::filesystem::create_directories(path.parent_path());
|
||||
return open(path.c_str(), O_WRONLY | O_APPEND);
|
||||
}();
|
||||
|
||||
struct timespec time;
|
||||
clock_gettime(CLOCK_REALTIME, &time);
|
||||
double secs = (double)time.tv_sec + ((double)time.tv_nsec / 1.0e9);
|
||||
std::string messageToLog = "[" + std::to_string(secs) + "](" + _category + ", " + _typeToString(type) + ") " + message + "\n";
|
||||
|
||||
write(logFile, messageToLog.c_str(), messageToLog.size());
|
||||
};
|
@ -1,568 +0,0 @@
|
||||
/**
|
||||
* This file is part of Darling.
|
||||
*
|
||||
* Copyright (C) 2021 Darling developers
|
||||
*
|
||||
* Darling is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Darling is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with Darling. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <darlingserver/message.hpp>
|
||||
#include <unistd.h>
|
||||
#include <cstdlib>
|
||||
#include <stdexcept>
|
||||
#include <new>
|
||||
#include <mutex>
|
||||
|
||||
DarlingServer::Address::Address() {
|
||||
_address.sun_family = AF_UNIX;
|
||||
memset(_address.sun_path, 0, sizeof(_address.sun_path));
|
||||
_size = sizeof(_address);
|
||||
};
|
||||
|
||||
DarlingServer::Address::Address(const struct sockaddr_un& rawAddress, size_t addressLength) {
|
||||
_address = rawAddress;
|
||||
_size = addressLength;
|
||||
};
|
||||
|
||||
struct sockaddr_un& DarlingServer::Address::raw() {
|
||||
return _address;
|
||||
};
|
||||
|
||||
const struct sockaddr_un& DarlingServer::Address::raw() const {
|
||||
return _address;
|
||||
};
|
||||
|
||||
size_t DarlingServer::Address::rawSize() const {
|
||||
return _size;
|
||||
};
|
||||
|
||||
void DarlingServer::Address::setRawSize(size_t newRawSize) {
|
||||
_size = newRawSize;
|
||||
};
|
||||
|
||||
DarlingServer::Message::Message(size_t bufferSpace, size_t descriptorSpace) {
|
||||
size_t controlLen = CMSG_SPACE(sizeof(struct ucred)) + (descriptorSpace > 0 ? CMSG_SPACE(sizeof(int) * descriptorSpace) : 0);
|
||||
_controlHeader = static_cast<decltype(_controlHeader)>(malloc(controlLen));
|
||||
|
||||
if (!_controlHeader) {
|
||||
throw std::bad_alloc();
|
||||
}
|
||||
|
||||
_controlHeader->cmsg_len = CMSG_LEN(sizeof(struct ucred));
|
||||
_controlHeader->cmsg_level = SOL_SOCKET;
|
||||
_controlHeader->cmsg_type = SCM_CREDENTIALS;
|
||||
struct ucred ourCreds;
|
||||
ourCreds.pid = getpid();
|
||||
ourCreds.uid = getuid();
|
||||
ourCreds.gid = getgid();
|
||||
// the cmsg man page says memcpy should be used with CMSG_DATA instead of direct pointer access
|
||||
memcpy(CMSG_DATA(_controlHeader), &ourCreds, sizeof(ourCreds));
|
||||
|
||||
if (descriptorSpace > 0) {
|
||||
auto fdHeader = reinterpret_cast<decltype(_controlHeader)>(reinterpret_cast<char*>(_controlHeader) + CMSG_SPACE(sizeof(struct ucred)));
|
||||
fdHeader->cmsg_len = CMSG_LEN(sizeof(int) * descriptorSpace);
|
||||
fdHeader->cmsg_level = SOL_SOCKET;
|
||||
fdHeader->cmsg_type = SCM_RIGHTS;
|
||||
for (size_t i = 0; i < descriptorSpace; ++i) {
|
||||
const int fd = -1;
|
||||
memcpy(CMSG_DATA(fdHeader) + (sizeof(int) * i), &fd, sizeof(int));
|
||||
}
|
||||
}
|
||||
|
||||
_buffer.resize(bufferSpace);
|
||||
|
||||
_header.msg_name = &_socketAddress.raw();
|
||||
_header.msg_namelen = _socketAddress.rawSize();
|
||||
_header.msg_iov = &_dataDescriptor;
|
||||
_header.msg_iovlen = 1;
|
||||
_header.msg_control = _controlHeader;
|
||||
_header.msg_controllen = controlLen;
|
||||
_header.msg_flags = 0;
|
||||
|
||||
_dataDescriptor.iov_base = _buffer.data();
|
||||
_dataDescriptor.iov_len = _buffer.capacity();
|
||||
};
|
||||
|
||||
void DarlingServer::Message::_initWithOther(Message&& other) {
|
||||
_buffer = std::move(other._buffer);
|
||||
_socketAddress = std::move(other._socketAddress);
|
||||
_controlHeader = std::move(other._controlHeader);
|
||||
|
||||
other._controlHeader = nullptr;
|
||||
|
||||
_header = std::move(other._header);
|
||||
_header.msg_name = &_socketAddress.raw();
|
||||
_header.msg_iov = &_dataDescriptor;
|
||||
|
||||
_dataDescriptor.iov_base = _buffer.data();
|
||||
_dataDescriptor.iov_len = _buffer.capacity();
|
||||
};
|
||||
|
||||
void DarlingServer::Message::_cleanupSelf() {
|
||||
if (_controlHeader) {
|
||||
auto fdHeader = _descriptorHeader();
|
||||
if (fdHeader) {
|
||||
for (size_t i = 0; i < _descriptorSpace(); ++i) {
|
||||
int fd = -1;
|
||||
memcpy(&fd, CMSG_DATA(fdHeader) + (sizeof(int) * i), sizeof(int));
|
||||
if (fd != -1) {
|
||||
close(fd);
|
||||
}
|
||||
}
|
||||
}
|
||||
free(_controlHeader);
|
||||
_controlHeader = nullptr;
|
||||
}
|
||||
};
|
||||
|
||||
struct cmsghdr* DarlingServer::Message::_credentialsHeader() {
|
||||
return const_cast<struct cmsghdr*>(const_cast<const Message*>(this)->_credentialsHeader());
|
||||
};
|
||||
|
||||
const struct cmsghdr* DarlingServer::Message::_credentialsHeader() const {
|
||||
const struct cmsghdr* hdr = CMSG_FIRSTHDR(&_header);
|
||||
|
||||
while (hdr) {
|
||||
if (hdr->cmsg_level == SOL_SOCKET && hdr->cmsg_type == SCM_CREDENTIALS) {
|
||||
return hdr;
|
||||
}
|
||||
|
||||
hdr = CMSG_NXTHDR(const_cast<struct msghdr*>(&_header), const_cast<struct cmsghdr*>(hdr));
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
};
|
||||
|
||||
struct cmsghdr* DarlingServer::Message::_descriptorHeader() {
|
||||
return const_cast<struct cmsghdr*>(const_cast<const Message*>(this)->_descriptorHeader());
|
||||
};
|
||||
|
||||
const struct cmsghdr* DarlingServer::Message::_descriptorHeader() const {
|
||||
const struct cmsghdr* hdr = CMSG_FIRSTHDR(&_header);
|
||||
|
||||
while (hdr) {
|
||||
if (hdr->cmsg_level == SOL_SOCKET && hdr->cmsg_type == SCM_RIGHTS) {
|
||||
return hdr;
|
||||
}
|
||||
|
||||
hdr = CMSG_NXTHDR(const_cast<struct msghdr*>(&_header), const_cast<struct cmsghdr*>(hdr));
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
};
|
||||
|
||||
size_t DarlingServer::Message::_descriptorSpace() const {
|
||||
auto hdr = _descriptorHeader();
|
||||
|
||||
if (!hdr) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
return (hdr->cmsg_len - (reinterpret_cast<uintptr_t>(CMSG_DATA(hdr)) - reinterpret_cast<uintptr_t>(hdr))) / sizeof(int);
|
||||
};
|
||||
|
||||
DarlingServer::Message::~Message() {
|
||||
_cleanupSelf();
|
||||
};
|
||||
|
||||
DarlingServer::Message::Message(Message&& other) {
|
||||
_initWithOther(std::move(other));
|
||||
};
|
||||
|
||||
DarlingServer::Message& DarlingServer::Message::operator=(Message&& other) {
|
||||
_cleanupSelf();
|
||||
_initWithOther(std::move(other));
|
||||
return *this;
|
||||
};
|
||||
|
||||
struct msghdr& DarlingServer::Message::rawHeader() {
|
||||
return _header;
|
||||
};
|
||||
|
||||
const struct msghdr& DarlingServer::Message::rawHeader() const {
|
||||
return _header;
|
||||
};
|
||||
|
||||
std::vector<uint8_t>& DarlingServer::Message::data() {
|
||||
return _buffer;
|
||||
};
|
||||
|
||||
const std::vector<uint8_t>& DarlingServer::Message::data() const {
|
||||
return _buffer;
|
||||
};
|
||||
|
||||
DarlingServer::Address DarlingServer::Message::address() const {
|
||||
return _socketAddress;
|
||||
};
|
||||
|
||||
void DarlingServer::Message::setAddress(Address address) {
|
||||
_socketAddress = address;
|
||||
_header.msg_namelen = _socketAddress.rawSize();
|
||||
};
|
||||
|
||||
std::vector<int> DarlingServer::Message::descriptors() const {
|
||||
std::vector<int> descriptors;
|
||||
auto fdHeader = _descriptorHeader();
|
||||
|
||||
if (fdHeader) {
|
||||
for (size_t i = 0; i < _descriptorSpace(); ++i) {
|
||||
int fd = -1;
|
||||
memcpy(&fd, CMSG_DATA(fdHeader) + (sizeof(int) * i), sizeof(int));
|
||||
if (fd != -1) {
|
||||
descriptors.push_back(fd);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return descriptors;
|
||||
};
|
||||
|
||||
void DarlingServer::Message::pushDescriptor(int descriptor) {
|
||||
if (auto fdHeader = _descriptorHeader()) {
|
||||
for (size_t i = 0; i < _descriptorSpace(); ++i) {
|
||||
int fd = -1;
|
||||
memcpy(&fd, CMSG_DATA(fdHeader) + (sizeof(int) * i), sizeof(int));
|
||||
if (fd != -1) {
|
||||
continue;
|
||||
}
|
||||
|
||||
memcpy(CMSG_DATA(fdHeader) + (sizeof(int) * i), &descriptor, sizeof(int));
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
auto oldSpace = _descriptorSpace();
|
||||
_ensureDescriptorHeader(oldSpace + 1);
|
||||
memcpy(CMSG_DATA(_descriptorHeader()) + (sizeof(int) * oldSpace), &descriptor, sizeof(int));
|
||||
};
|
||||
|
||||
int DarlingServer::Message::extractDescriptor(int descriptor) {
|
||||
if (auto fdHeader = _descriptorHeader()) {
|
||||
for (size_t i = 0; i < _descriptorSpace(); ++i) {
|
||||
int fd = -1;
|
||||
memcpy(&fd, CMSG_DATA(fdHeader) + (sizeof(int) * i), sizeof(int));
|
||||
if (fd != descriptor) {
|
||||
continue;
|
||||
}
|
||||
|
||||
fd = -1;
|
||||
memcpy(CMSG_DATA(fdHeader) + (sizeof(int) * i), &fd, sizeof(int));
|
||||
return descriptor;
|
||||
}
|
||||
}
|
||||
|
||||
return -1;
|
||||
};
|
||||
|
||||
int DarlingServer::Message::extractDescriptorAtIndex(size_t index) {
|
||||
if (auto fdHeader = _descriptorHeader()) {
|
||||
for (size_t i = 0, presentIndex = 0; i < _descriptorSpace(); ++i) {
|
||||
int fd = -1;
|
||||
memcpy(&fd, CMSG_DATA(fdHeader) + (sizeof(int) * i), sizeof(int));
|
||||
if (fd == -1) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (presentIndex++ != index) {
|
||||
continue;
|
||||
}
|
||||
|
||||
int tmp = -1;
|
||||
memcpy(CMSG_DATA(fdHeader) + (sizeof(int) * i), &tmp, sizeof(int));
|
||||
return fd;
|
||||
}
|
||||
}
|
||||
|
||||
return -1;
|
||||
};
|
||||
|
||||
void DarlingServer::Message::replaceDescriptors(const std::vector<int>& newDescriptors) {
|
||||
_ensureDescriptorHeader(newDescriptors.size());
|
||||
memcpy(CMSG_DATA(_descriptorHeader()), newDescriptors.data(), sizeof(int) * newDescriptors.size());
|
||||
};
|
||||
|
||||
bool DarlingServer::Message::copyCredentialsOut(struct ucred& outputCredentials) const {
|
||||
auto credHeader = _credentialsHeader();
|
||||
|
||||
if (credHeader) {
|
||||
memcpy(&outputCredentials, CMSG_DATA(credHeader), sizeof(outputCredentials));
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
void DarlingServer::Message::copyCredentialsIn(const struct ucred& inputCredentials) {
|
||||
_ensureCredentialsHeader();
|
||||
memcpy(CMSG_DATA(_credentialsHeader()), &inputCredentials, sizeof(inputCredentials));
|
||||
};
|
||||
|
||||
void DarlingServer::Message::_ensureCredentialsHeader() {
|
||||
if (_credentialsHeader()) {
|
||||
return;
|
||||
}
|
||||
|
||||
auto fdHeader = _descriptorHeader();
|
||||
auto descSpace = _descriptorSpace();
|
||||
size_t controlLen = CMSG_SPACE(sizeof(struct ucred)) + (descSpace > 0 ? CMSG_SPACE(sizeof(int) * descSpace) : 0);
|
||||
auto tmp = static_cast<decltype(_controlHeader)>(malloc(controlLen));
|
||||
auto old = _controlHeader;
|
||||
struct cmsghdr* credHeader = nullptr;
|
||||
|
||||
if (!tmp) {
|
||||
throw std::bad_alloc();
|
||||
}
|
||||
|
||||
_controlHeader = tmp;
|
||||
_header.msg_control = _controlHeader;
|
||||
|
||||
credHeader = _controlHeader;
|
||||
|
||||
if (descSpace > 0) {
|
||||
auto newFdHeader = reinterpret_cast<decltype(_controlHeader)>(reinterpret_cast<char*>(_controlHeader) + CMSG_SPACE(sizeof(struct ucred)));
|
||||
memcpy(newFdHeader, fdHeader, CMSG_SPACE(sizeof(int) * descSpace));
|
||||
}
|
||||
|
||||
free(old);
|
||||
|
||||
_header.msg_controllen = controlLen;
|
||||
|
||||
credHeader->cmsg_len = CMSG_LEN(sizeof(struct ucred));
|
||||
credHeader->cmsg_level = SOL_SOCKET;
|
||||
credHeader->cmsg_type = SCM_CREDENTIALS;
|
||||
|
||||
struct ucred creds;
|
||||
creds.pid = getpid();
|
||||
creds.uid = getuid();
|
||||
creds.gid = getgid();
|
||||
memcpy(CMSG_DATA(credHeader), &creds, sizeof(struct ucred));
|
||||
};
|
||||
|
||||
void DarlingServer::Message::_ensureDescriptorHeader(size_t newSpace) {
|
||||
if (_descriptorSpace() == newSpace) {
|
||||
return;
|
||||
}
|
||||
|
||||
auto credHeader = _credentialsHeader();
|
||||
size_t oldSpace = _descriptorSpace();
|
||||
size_t controlLen = (credHeader ? CMSG_SPACE(sizeof(struct ucred)) : 0) + (newSpace == 0 ? 0 : CMSG_SPACE(sizeof(int) * newSpace));
|
||||
|
||||
if (controlLen == 0) {
|
||||
free(_controlHeader);
|
||||
_controlHeader = nullptr;
|
||||
_header.msg_control = nullptr;
|
||||
_header.msg_controllen = 0;
|
||||
return;
|
||||
}
|
||||
|
||||
auto tmp = static_cast<decltype(_controlHeader)>(malloc(controlLen));
|
||||
auto old = _controlHeader;
|
||||
struct cmsghdr* fdHeader = nullptr;
|
||||
|
||||
if (!tmp) {
|
||||
throw std::bad_alloc();
|
||||
}
|
||||
|
||||
_controlHeader = tmp;
|
||||
_header.msg_control = _controlHeader;
|
||||
|
||||
if (credHeader) {
|
||||
memcpy(_controlHeader, credHeader, CMSG_SPACE(sizeof(struct ucred)));
|
||||
fdHeader = reinterpret_cast<decltype(_controlHeader)>(reinterpret_cast<char*>(_controlHeader) + CMSG_SPACE(sizeof(struct ucred)));
|
||||
} else {
|
||||
fdHeader = _controlHeader;
|
||||
}
|
||||
|
||||
free(old);
|
||||
|
||||
_header.msg_controllen = controlLen;
|
||||
|
||||
fdHeader->cmsg_len = CMSG_LEN(sizeof(int) * newSpace);
|
||||
fdHeader->cmsg_level = SOL_SOCKET;
|
||||
fdHeader->cmsg_type = SCM_RIGHTS;
|
||||
|
||||
for (size_t i = oldSpace; i < newSpace; ++i) {
|
||||
int fd = -1;
|
||||
memcpy(CMSG_DATA(fdHeader) + (sizeof(int) * i), &fd, sizeof(int));
|
||||
}
|
||||
};
|
||||
|
||||
pid_t DarlingServer::Message::pid() const {
|
||||
struct ucred creds;
|
||||
|
||||
if (copyCredentialsOut(creds)) {
|
||||
return creds.pid;
|
||||
} else {
|
||||
return -1;
|
||||
}
|
||||
};
|
||||
|
||||
void DarlingServer::Message::setPID(pid_t pid) {
|
||||
_ensureCredentialsHeader();
|
||||
struct ucred creds;
|
||||
copyCredentialsOut(creds);
|
||||
creds.pid = pid;
|
||||
copyCredentialsIn(creds);
|
||||
};
|
||||
|
||||
uid_t DarlingServer::Message::uid() const {
|
||||
struct ucred creds;
|
||||
|
||||
if (copyCredentialsOut(creds)) {
|
||||
return creds.uid;
|
||||
} else {
|
||||
return -1;
|
||||
}
|
||||
};
|
||||
|
||||
void DarlingServer::Message::setUID(uid_t uid) {
|
||||
_ensureCredentialsHeader();
|
||||
struct ucred creds;
|
||||
copyCredentialsOut(creds);
|
||||
creds.uid = uid;
|
||||
copyCredentialsIn(creds);
|
||||
};
|
||||
|
||||
gid_t DarlingServer::Message::gid() const {
|
||||
struct ucred creds;
|
||||
|
||||
if (copyCredentialsOut(creds)) {
|
||||
return creds.gid;
|
||||
} else {
|
||||
return -1;
|
||||
}
|
||||
};
|
||||
|
||||
void DarlingServer::Message::setGID(gid_t gid) {
|
||||
_ensureCredentialsHeader();
|
||||
struct ucred creds;
|
||||
copyCredentialsOut(creds);
|
||||
creds.gid = gid;
|
||||
copyCredentialsIn(creds);
|
||||
};
|
||||
|
||||
void DarlingServer::MessageQueue::push(Message&& message) {
|
||||
std::unique_lock lock(_lock);
|
||||
_messages.push_back(std::move(message));
|
||||
|
||||
if (_messages.size() > 0) {
|
||||
auto callback = _messageArrivalNotificationCallback;
|
||||
lock.unlock();
|
||||
if (callback) {
|
||||
callback();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
std::optional<DarlingServer::Message> DarlingServer::MessageQueue::pop() {
|
||||
std::scoped_lock lock(_lock);
|
||||
if (_messages.size() > 0) {
|
||||
Message message = std::move(_messages.front());
|
||||
_messages.pop_front();
|
||||
return message;
|
||||
} else {
|
||||
return std::nullopt;
|
||||
}
|
||||
};
|
||||
|
||||
bool DarlingServer::MessageQueue::sendMany(int socket) {
|
||||
bool canSendMore = true;
|
||||
std::scoped_lock lock(_lock);
|
||||
struct mmsghdr mmsgs[16];
|
||||
size_t len = 0;
|
||||
int ret = 0;
|
||||
|
||||
while (ret >= 0) {
|
||||
len = 0;
|
||||
for (size_t i = 0; i < _messages.size() && i < sizeof(mmsgs) / sizeof(*mmsgs); ++i) {
|
||||
mmsgs[i].msg_hdr = _messages[i].rawHeader();
|
||||
mmsgs[i].msg_len = 0;
|
||||
++len;
|
||||
}
|
||||
|
||||
if (len == 0) {
|
||||
break;
|
||||
}
|
||||
|
||||
ret = sendmmsg(socket, mmsgs, len, MSG_DONTWAIT);
|
||||
|
||||
if (ret < 0) {
|
||||
if (errno == EAGAIN) {
|
||||
canSendMore = false;
|
||||
break;
|
||||
} else if (errno == EINTR) {
|
||||
ret = 0;
|
||||
} else {
|
||||
throw std::system_error(errno, std::generic_category(), "Failed to send messages through socket");
|
||||
}
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < ret; ++i) {
|
||||
_messages.pop_front();
|
||||
}
|
||||
}
|
||||
|
||||
return canSendMore;
|
||||
};
|
||||
|
||||
bool DarlingServer::MessageQueue::receiveMany(int socket) {
|
||||
bool canReadMore = true;
|
||||
std::unique_lock lock(_lock);
|
||||
struct mmsghdr mmsgs[16];
|
||||
int ret = 0;
|
||||
|
||||
while (ret >= 0) {
|
||||
std::array<Message, sizeof(mmsgs) / sizeof(*mmsgs)> messages;
|
||||
|
||||
for (size_t i = 0; i < messages.size(); ++i) {
|
||||
mmsgs[i].msg_hdr = messages[i].rawHeader();
|
||||
mmsgs[i].msg_len = 0;
|
||||
}
|
||||
|
||||
ret = recvmmsg(socket, mmsgs, sizeof(mmsgs) / sizeof(*mmsgs), MSG_CMSG_CLOEXEC | MSG_DONTWAIT, nullptr);
|
||||
|
||||
if (ret < 0) {
|
||||
if (errno == EAGAIN) {
|
||||
canReadMore = false;
|
||||
break;
|
||||
} else if (errno == EINTR) {
|
||||
ret = 0;
|
||||
} else {
|
||||
throw std::system_error(errno, std::generic_category(), "Failed to receive messages through socket");
|
||||
}
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < ret; ++i) {
|
||||
messages[i].rawHeader() = mmsgs[i].msg_hdr;
|
||||
messages[i].data().resize(mmsgs[i].msg_len);
|
||||
messages[i].setAddress(Address(*(const struct sockaddr_un*)mmsgs[i].msg_hdr.msg_name, mmsgs[i].msg_hdr.msg_namelen));
|
||||
_messages.push_back(std::move(messages[i]));
|
||||
}
|
||||
}
|
||||
|
||||
if (_messages.size() > 0) {
|
||||
auto callback = _messageArrivalNotificationCallback;
|
||||
lock.unlock();
|
||||
if (callback) {
|
||||
callback();
|
||||
}
|
||||
}
|
||||
|
||||
return canReadMore;
|
||||
};
|
||||
|
||||
void DarlingServer::MessageQueue::setMessageArrivalNotificationCallback(std::function<void()> messageArrivalNotificationCallback) {
|
||||
std::unique_lock lock(_lock);
|
||||
_messageArrivalNotificationCallback = messageArrivalNotificationCallback;
|
||||
};
|
@ -1,84 +0,0 @@
|
||||
/**
|
||||
* This file is part of Darling.
|
||||
*
|
||||
* Copyright (C) 2021 Darling developers
|
||||
*
|
||||
* Darling is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Darling is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with Darling. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <darlingserver/process.hpp>
|
||||
#include <darlingserver/registry.hpp>
|
||||
#include <sys/syscall.h>
|
||||
#include <unistd.h>
|
||||
|
||||
DarlingServer::Process::Process(ID id, NSID nsid):
|
||||
_pid(id),
|
||||
_nspid(nsid)
|
||||
{
|
||||
_pidfd = syscall(SYS_pidfd_open, _pid, 0);
|
||||
if (_pidfd < 0) {
|
||||
throw std::system_error(errno, std::generic_category(), "Failed to open pidfd for process");
|
||||
}
|
||||
};
|
||||
|
||||
DarlingServer::Process::~Process() {
|
||||
close(_pidfd);
|
||||
|
||||
_unregisterThreads();
|
||||
};
|
||||
|
||||
void DarlingServer::Process::_unregisterThreads() {
|
||||
std::unique_lock lock(_rwlock);
|
||||
while (!_threads.empty()) {
|
||||
auto thread = _threads.back().lock();
|
||||
lock.unlock();
|
||||
if (thread) {
|
||||
thread->_process = std::weak_ptr<Process>();
|
||||
threadRegistry().unregisterEntry(thread);
|
||||
}
|
||||
lock.lock();
|
||||
_threads.pop_back();
|
||||
}
|
||||
};
|
||||
|
||||
DarlingServer::Process::ID DarlingServer::Process::id() const {
|
||||
return _pid;
|
||||
};
|
||||
|
||||
DarlingServer::Process::NSID DarlingServer::Process::nsid() const {
|
||||
return _nspid;
|
||||
};
|
||||
|
||||
std::vector<std::shared_ptr<DarlingServer::Thread>> DarlingServer::Process::threads() const {
|
||||
std::vector<std::shared_ptr<DarlingServer::Thread>> result;
|
||||
std::shared_lock lock(_rwlock);
|
||||
|
||||
for (auto& maybeThread: _threads) {
|
||||
if (auto thread = maybeThread.lock()) {
|
||||
result.push_back(thread);
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
};
|
||||
|
||||
std::string DarlingServer::Process::vchrootPath() const {
|
||||
std::shared_lock lock(_rwlock);
|
||||
return _vchrootPath;
|
||||
};
|
||||
|
||||
void DarlingServer::Process::setVchrootPath(std::string path) {
|
||||
std::unique_lock lock(_rwlock);
|
||||
_vchrootPath = path;
|
||||
};
|
@ -1,30 +0,0 @@
|
||||
/**
|
||||
* This file is part of Darling.
|
||||
*
|
||||
* Copyright (C) 2021 Darling developers
|
||||
*
|
||||
* Darling is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Darling is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with Darling. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <darlingserver/registry.hpp>
|
||||
|
||||
DarlingServer::Registry<DarlingServer::Process>& DarlingServer::processRegistry() {
|
||||
static Registry<Process> registry;
|
||||
return registry;
|
||||
};
|
||||
|
||||
DarlingServer::Registry<DarlingServer::Thread>& DarlingServer::threadRegistry() {
|
||||
static Registry<Thread> registry;
|
||||
return registry;
|
||||
};
|
@ -1,196 +0,0 @@
|
||||
/**
|
||||
* This file is part of Darling.
|
||||
*
|
||||
* Copyright (C) 2021 Darling developers
|
||||
*
|
||||
* Darling is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Darling is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with Darling. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <darlingserver/server.hpp>
|
||||
#include <sys/socket.h>
|
||||
#include <stdexcept>
|
||||
#include <errno.h>
|
||||
#include <cstring>
|
||||
#include <unistd.h>
|
||||
#include <sys/un.h>
|
||||
#include <sys/epoll.h>
|
||||
#include <fcntl.h>
|
||||
#include <system_error>
|
||||
#include <thread>
|
||||
#include <array>
|
||||
#include <darlingserver/registry.hpp>
|
||||
#include <sys/eventfd.h>
|
||||
|
||||
static DarlingServer::Server* sharedInstancePointer = nullptr;
|
||||
|
||||
DarlingServer::Server::Server(std::string prefix):
|
||||
_prefix(prefix),
|
||||
_socketPath(_prefix + "/var/run/darlingserver.sock"),
|
||||
_workQueue(std::bind(&Server::_worker, this, std::placeholders::_1))
|
||||
{
|
||||
sharedInstancePointer = this;
|
||||
|
||||
// remove the old socket (if it exists)
|
||||
unlink(_socketPath.c_str());
|
||||
|
||||
// create the socket
|
||||
_listenerSocket = socket(AF_UNIX, SOCK_DGRAM | SOCK_NONBLOCK | SOCK_CLOEXEC, 0);
|
||||
if (_listenerSocket < 0) {
|
||||
throw std::system_error(errno, std::generic_category(), "Failed to create socket");
|
||||
}
|
||||
|
||||
int passCred = 1;
|
||||
if (setsockopt(_listenerSocket, SOL_SOCKET, SO_PASSCRED, &passCred, sizeof(passCred)) < 0) {
|
||||
throw std::system_error(errno, std::generic_category(), "Failed to set SO_PASSCRED on socket");
|
||||
}
|
||||
|
||||
struct sockaddr_un addr;
|
||||
addr.sun_family = AF_UNIX;
|
||||
addr.sun_path[sizeof(addr.sun_path) - 1] = '\0';
|
||||
strncpy(addr.sun_path, _socketPath.c_str(), sizeof(addr.sun_path) - 1);
|
||||
|
||||
if (bind(_listenerSocket, (struct sockaddr*)&addr, sizeof(addr)) != 0) {
|
||||
throw std::system_error(errno, std::generic_category(), "Failed to bind socket");
|
||||
}
|
||||
|
||||
_wakeupFD = eventfd(0, EFD_CLOEXEC | EFD_NONBLOCK);
|
||||
if (_wakeupFD < 0) {
|
||||
throw std::system_error(errno, std::generic_category(), "Failed to create eventfd for on-demand epoll wakeups");
|
||||
}
|
||||
|
||||
_epollFD = epoll_create1(EPOLL_CLOEXEC);
|
||||
if (_epollFD < 0) {
|
||||
throw std::system_error(errno, std::generic_category(), "Failed to create epoll context");
|
||||
}
|
||||
|
||||
struct epoll_event settings;
|
||||
settings.data.ptr = this;
|
||||
settings.events = EPOLLIN | EPOLLOUT | EPOLLET;
|
||||
|
||||
if (epoll_ctl(_epollFD, EPOLL_CTL_ADD, _listenerSocket, &settings) < 0) {
|
||||
throw std::system_error(errno, std::generic_category(), "Failed to add listener socket to epoll context");
|
||||
}
|
||||
|
||||
settings.data.ptr = &_wakeupFD;
|
||||
settings.events = EPOLLIN | EPOLLONESHOT;
|
||||
|
||||
if (epoll_ctl(_epollFD, EPOLL_CTL_ADD, _wakeupFD, &settings) < 0) {
|
||||
throw std::system_error(errno, std::generic_category(), "Failed to add eventfd to epoll context");
|
||||
}
|
||||
|
||||
_outbox.setMessageArrivalNotificationCallback([this]() {
|
||||
// we don't really have to worry about the eventfd overflowing;
|
||||
// if it does, that means the main loop has been waiting a LONG time for the listener socket to become writable again.
|
||||
// in that case, we don't really care if the eventfd is being incremented; we can't send anything anyways.
|
||||
// once the socket becomes writable again, the eventfd will be monitored again.
|
||||
eventfd_write(_wakeupFD, 1);
|
||||
});
|
||||
};
|
||||
|
||||
DarlingServer::Server::~Server() {
|
||||
close(_epollFD);
|
||||
close(_wakeupFD);
|
||||
close(_listenerSocket);
|
||||
unlink(_socketPath.c_str());
|
||||
};
|
||||
|
||||
void DarlingServer::Server::start() {
|
||||
while (true) {
|
||||
if (_canRead) {
|
||||
_canRead = _inbox.receiveMany(_listenerSocket);
|
||||
|
||||
// TODO: receive messages directly onto the work queue
|
||||
while (auto msg = _inbox.pop()) {
|
||||
_workQueue.push(std::move(msg.value()));
|
||||
}
|
||||
}
|
||||
|
||||
if (_canWrite) {
|
||||
// reset the eventfd by reading from it
|
||||
eventfd_t value;
|
||||
eventfd_read(_wakeupFD, &value);
|
||||
_canWrite = _outbox.sendMany(_listenerSocket);
|
||||
}
|
||||
|
||||
struct epoll_event settings;
|
||||
settings.data.ptr = &_wakeupFD;
|
||||
settings.events = (_canWrite) ? (EPOLLIN | EPOLLONESHOT) : 0;
|
||||
|
||||
if (epoll_ctl(_epollFD, EPOLL_CTL_MOD, _wakeupFD, &settings) < 0) {
|
||||
throw std::system_error(errno, std::generic_category(), "Failed to modify eventfd in epoll context");
|
||||
}
|
||||
|
||||
struct epoll_event events[16];
|
||||
int ret = epoll_wait(_epollFD, events, 16, -1);
|
||||
|
||||
if (ret < 0) {
|
||||
if (errno == EINTR) {
|
||||
continue;
|
||||
}
|
||||
|
||||
throw std::system_error(errno, std::generic_category(), "Failed to wait on epoll context");
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < ret; ++i) {
|
||||
struct epoll_event* event = &events[i];
|
||||
|
||||
if (event->data.ptr == this) {
|
||||
if (event->events & EPOLLIN) {
|
||||
_canRead = true;
|
||||
}
|
||||
|
||||
if (event->events & EPOLLOUT) {
|
||||
_canWrite = true;
|
||||
}
|
||||
} else if (event->data.ptr == &_wakeupFD) {
|
||||
// we allow the loop to go back to the top and try to send some messages
|
||||
// (if _canWrite is true, the eventfd will be reset; otherwise, there's no point in resetting it)
|
||||
} else if (event->events & EPOLLIN) {
|
||||
std::shared_ptr<Process>& process = *reinterpret_cast<std::shared_ptr<Process>*>(event->data.ptr);
|
||||
|
||||
if (epoll_ctl(_epollFD, EPOLL_CTL_DEL, process->_pidfd, NULL) < 0) {
|
||||
throw std::system_error(errno, std::generic_category(), "Failed to remove process handle from epoll context");
|
||||
}
|
||||
|
||||
process->_unregisterThreads();
|
||||
processRegistry().unregisterEntry(process);
|
||||
|
||||
delete &process;
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
void DarlingServer::Server::monitorProcess(std::shared_ptr<Process> process) {
|
||||
struct epoll_event settings;
|
||||
settings.data.ptr = new std::shared_ptr<Process>(process);
|
||||
settings.events = EPOLLIN;
|
||||
|
||||
if (epoll_ctl(_epollFD, EPOLL_CTL_ADD, process->_pidfd, &settings) < 0) {
|
||||
throw std::system_error(errno, std::generic_category(), "Failed to add process descriptor to epoll context");
|
||||
}
|
||||
};
|
||||
|
||||
DarlingServer::Server& DarlingServer::Server::sharedInstance() {
|
||||
return *sharedInstancePointer;
|
||||
};
|
||||
|
||||
std::string DarlingServer::Server::prefix() const {
|
||||
return _prefix;
|
||||
};
|
||||
|
||||
void DarlingServer::Server::_worker(DarlingServer::Message message) {
|
||||
auto call = DarlingServer::Call::callFromMessage(std::move(message), _outbox);
|
||||
call->processCall();
|
||||
};
|
@ -1,122 +0,0 @@
|
||||
/**
|
||||
* This file is part of Darling.
|
||||
*
|
||||
* Copyright (C) 2021 Darling developers
|
||||
*
|
||||
* Darling is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Darling is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with Darling. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <darlingserver/thread.hpp>
|
||||
#include <darlingserver/process.hpp>
|
||||
#include <filesystem>
|
||||
#include <fstream>
|
||||
|
||||
DarlingServer::Thread::Thread(std::shared_ptr<Process> process, NSID nsid):
|
||||
_nstid(nsid),
|
||||
_process(process)
|
||||
{
|
||||
_tid = -1;
|
||||
|
||||
for (const auto& entry: std::filesystem::directory_iterator("/proc/" + std::to_string(process->id()) + "/task")) {
|
||||
std::ifstream statusFile(entry.path() / "status");
|
||||
std::string line;
|
||||
|
||||
while (std::getline(statusFile, line)) {
|
||||
if (line.substr(0, sizeof("NSpid") - 1) == "NSpid") {
|
||||
auto pos = line.find_last_of('\t');
|
||||
std::string id;
|
||||
|
||||
if (pos != line.npos) {
|
||||
id = line.substr(pos + 1);
|
||||
}
|
||||
|
||||
if (id.empty()) {
|
||||
throw std::runtime_error("Failed to parse thread ID");
|
||||
}
|
||||
|
||||
if (std::stoi(id) != _nstid) {
|
||||
continue;
|
||||
}
|
||||
|
||||
_tid = std::stoi(entry.path().filename().string());
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (_tid == -1) {
|
||||
throw std::runtime_error("Failed to find thread ID within darlingserver's namespace");
|
||||
}
|
||||
};
|
||||
|
||||
void DarlingServer::Thread::registerWithProcess() {
|
||||
auto process = _process.lock();
|
||||
std::unique_lock lock(process->_rwlock);
|
||||
process->_threads.push_back(shared_from_this());
|
||||
};
|
||||
|
||||
DarlingServer::Thread::~Thread() noexcept(false) {
|
||||
auto process = _process.lock();
|
||||
if (!process) {
|
||||
// the process is unregistering us
|
||||
return;
|
||||
}
|
||||
|
||||
std::unique_lock lock(process->_rwlock);
|
||||
auto it = process->_threads.begin();
|
||||
while (it != process->_threads.end()) {
|
||||
if (auto thread = it->lock()) {
|
||||
if (thread.get() == this) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (it == process->_threads.end()) {
|
||||
throw std::runtime_error("Thread was not registered with Process");
|
||||
}
|
||||
process->_threads.erase(it);
|
||||
};
|
||||
|
||||
DarlingServer::Thread::ID DarlingServer::Thread::id() const {
|
||||
return _tid;
|
||||
};
|
||||
|
||||
DarlingServer::Thread::NSID DarlingServer::Thread::nsid() const {
|
||||
return _nstid;
|
||||
};
|
||||
|
||||
std::shared_ptr<DarlingServer::Process> DarlingServer::Thread::process() const {
|
||||
return _process.lock();
|
||||
};
|
||||
|
||||
std::shared_ptr<DarlingServer::Call> DarlingServer::Thread::pendingCall() const {
|
||||
std::shared_lock lock(_rwlock);
|
||||
return _pendingCall;
|
||||
};
|
||||
|
||||
void DarlingServer::Thread::setPendingCall(std::shared_ptr<Call> newPendingCall) {
|
||||
std::unique_lock lock(_rwlock);
|
||||
_pendingCall = newPendingCall;
|
||||
};
|
||||
|
||||
DarlingServer::Address DarlingServer::Thread::address() const {
|
||||
std::shared_lock lock(_rwlock);
|
||||
return _address;
|
||||
};
|
||||
|
||||
void DarlingServer::Thread::setAddress(Address address) {
|
||||
std::unique_lock lock(_rwlock);
|
||||
_address = address;
|
||||
};
|
@ -124,7 +124,8 @@ static int sLastErrorNo;
|
||||
#endif
|
||||
|
||||
#ifdef DARLING
|
||||
extern "C" int mach_driver_get_fd(void);
|
||||
extern "C" int mach_driver_get_dyld_fd(void);
|
||||
extern "C" void* elfcalls_get_pointer(void);
|
||||
#endif
|
||||
|
||||
// In 10.3.x and earlier all the NSObjectFileImage API's were implemeneted in libSystem.dylib
|
||||
@ -259,7 +260,8 @@ static const struct dyld_func dyld_funcs[] = {
|
||||
{"__dyld_link_module", (void*)_dyld_link_module },
|
||||
#endif
|
||||
#ifdef DARLING
|
||||
{"__dyld_get_mach_driver_fd", (void*)mach_driver_get_fd },
|
||||
{"__dyld_get_mach_driver_fd", (void*)mach_driver_get_dyld_fd },
|
||||
{"__dyld_get_elfcalls", (void*)elfcalls_get_pointer },
|
||||
#endif
|
||||
#pragma clang diagnostic pop
|
||||
#endif //DEPRECATED_APIS_SUPPORTED
|
||||
@ -1283,7 +1285,7 @@ bool _dyld_launched_prebound()
|
||||
if ( dyld::gLogAPIs )
|
||||
dyld::log("%s()\n", __func__);
|
||||
|
||||
// ¥¥¥Êif we deprecate prebinding, we may want to consider always returning true or false here
|
||||
// <EFBFBD><EFBFBD><EFBFBD><EFBFBD>if we deprecate prebinding, we may want to consider always returning true or false here
|
||||
return dyld::mainExecutablePrebound();
|
||||
}
|
||||
|
||||
|
@ -84,7 +84,7 @@ static int sLastErrorNo;
|
||||
#endif
|
||||
|
||||
#ifdef DARLING
|
||||
extern "C" int mach_driver_get_fd(void);
|
||||
extern "C" int mach_driver_get_dyld_fd(void);
|
||||
extern "C" bool darling_am_i_ptraced(void);
|
||||
#endif
|
||||
|
||||
@ -214,7 +214,7 @@ static struct dyld_func dyld_funcs[] = {
|
||||
{"__dyld_link_module", (void*)_dyld_link_module },
|
||||
#endif
|
||||
#ifdef DARLING
|
||||
{"__dyld_get_mach_driver_fd", (void*)mach_driver_get_fd },
|
||||
{"__dyld_get_mach_driver_fd", (void*)mach_driver_get_dyld_fd },
|
||||
{"__dyld_am_i_ptraced", (void*)darling_am_i_ptraced },
|
||||
#endif
|
||||
#endif //DEPRECATED_APIS_SUPPORTED
|
||||
@ -1233,7 +1233,7 @@ bool _dyld_launched_prebound()
|
||||
if ( dyld::gLogAPIs )
|
||||
dyld::log("%s()\n", __func__);
|
||||
|
||||
// ¥¥¥Êif we deprecate prebinding, we may want to consider always returning true or false here
|
||||
// <EFBFBD><EFBFBD><EFBFBD><EFBFBD>if we deprecate prebinding, we may want to consider always returning true or false here
|
||||
return dyld::mainExecutablePrebound();
|
||||
}
|
||||
|
||||
|
1
src/external/darlingserver
vendored
Submodule
1
src/external/darlingserver
vendored
Submodule
@ -0,0 +1 @@
|
||||
Subproject commit 3bfc652c9b307ababa4f4d72fddd876704e6395b
|
@ -21,6 +21,7 @@ include_directories(${CMAKE_CURRENT_SOURCE_DIR}
|
||||
${CMAKE_BINARY_DIR}/src/startup
|
||||
${CMAKE_BINARY_DIR}/src/kernel/libsyscall
|
||||
${CMAKE_CURRENT_BINARY_DIR}
|
||||
${CMAKE_BINARY_DIR}/src/external/darlingserver/include
|
||||
)
|
||||
|
||||
mig(signal/mach_exc.defs)
|
||||
@ -301,16 +302,22 @@ set(emulation_sources
|
||||
vchroot_userspace.c
|
||||
syscalls-table.S
|
||||
linux-syscall.S
|
||||
|
||||
${CMAKE_BINARY_DIR}/src/external/darlingserver/src/rpc.c
|
||||
)
|
||||
|
||||
set_source_files_properties(signal/duct_signals.c PROPERTIES COMPILE_FLAGS "-nostdinc")
|
||||
set_source_files_properties(signal/sigexc.c PROPERTIES OBJECT_DEPENDS ${CMAKE_CURRENT_BINARY_DIR}/signal/exc.h)
|
||||
set_source_files_properties(${CMAKE_BINARY_DIR}/src/external/darlingserver/src/rpc.c PROPERTIES
|
||||
GENERATED TRUE
|
||||
COMPILE_FLAGS "-include ${CMAKE_CURRENT_SOURCE_DIR}/resources/dserver-rpc-defs.h"
|
||||
)
|
||||
|
||||
add_darling_object_library(emulation ${emulation_sources})
|
||||
add_darling_object_library(emulation_dyld ${emulation_sources})
|
||||
set_target_properties(emulation_dyld PROPERTIES COMPILE_FLAGS "-ffunction-sections -DVARIANT_DYLD")
|
||||
add_dependencies(emulation rtsig_h)
|
||||
add_dependencies(emulation_dyld rtsig_h)
|
||||
add_dependencies(emulation rtsig_h generate_dserver_rpc_wrappers)
|
||||
add_dependencies(emulation_dyld rtsig_h generate_dserver_rpc_wrappers)
|
||||
|
||||
make_fat(emulation)
|
||||
make_fat(emulation_dyld)
|
||||
|
@ -76,3 +76,6 @@ int native_dlclose(void* module)
|
||||
return elfcalls()->dlclose(module);
|
||||
}
|
||||
|
||||
const void* __dserver_socket_address(void) {
|
||||
return elfcalls()->dserver_socket_address();
|
||||
};
|
||||
|
@ -28,6 +28,7 @@ int __darling_thread_terminate(void* stackaddr,
|
||||
|
||||
void* __darling_thread_get_stack(void);
|
||||
|
||||
const void* __dserver_socket_address(void);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
|
@ -8,6 +8,7 @@ include_directories(
|
||||
${CMAKE_SOURCE_DIR}/src/kernel/emulation/linux/ext
|
||||
${CMAKE_SOURCE_DIR}/src/startup
|
||||
${CMAKE_BINARY_DIR}/src/startup
|
||||
${CMAKE_BINARY_DIR}/src/external/darlingserver/include
|
||||
)
|
||||
|
||||
set(mach_server_client_sources
|
||||
|
@ -26,12 +26,10 @@ static int driver_fd = -1;
|
||||
VISIBLE
|
||||
struct elf_calls* _elfcalls;
|
||||
|
||||
static bool use_per_thread_driver_fd = false;
|
||||
|
||||
void mach_driver_init(const char** applep)
|
||||
{
|
||||
// DARLINGSERVER/MLDR TESTING
|
||||
__simple_printf("We're being initialized...\n");
|
||||
__builtin_unreachable();
|
||||
|
||||
#ifdef VARIANT_DYLD
|
||||
if (applep != NULL)
|
||||
{
|
||||
@ -55,8 +53,15 @@ void mach_driver_init(const char** applep)
|
||||
_libkernel_functions->dyld_func_lookup("__dyld_get_mach_driver_fd", (void**) &p);
|
||||
|
||||
driver_fd = (*p)();
|
||||
|
||||
// ask for elfcalls already set up by dyld
|
||||
void* (*p2)(void);
|
||||
_libkernel_functions->dyld_func_lookup("__dyld_get_elfcalls", (void**)&p2);
|
||||
|
||||
_elfcalls = p2();
|
||||
#endif
|
||||
|
||||
#if 0
|
||||
// If mach_driver_init() is being called in the fork child, the LKM will now
|
||||
// swap out driver_fd for a new one.
|
||||
if (__real_ioctl(driver_fd, NR_get_api_version, 0) != DARLING_MACH_API_VERSION)
|
||||
@ -65,11 +70,17 @@ void mach_driver_init(const char** applep)
|
||||
sys_write(2, msg, strlen(msg));
|
||||
sys_kill(0, 6);
|
||||
}
|
||||
#endif
|
||||
|
||||
// mach_driver_init() gets called at several points in application's lifetime:
|
||||
// 1) When dyld loads
|
||||
// 2) When libc initializes
|
||||
// 3) After forking
|
||||
|
||||
// this was previously done when the LKM was in-use.
|
||||
// now with per-thread dserver FDs, this is more complicated to do.
|
||||
// TODO: find a suitable way of doing this.
|
||||
#if 0
|
||||
#ifdef VARIANT_DYLD
|
||||
struct rlimit lim;
|
||||
if (sys_getrlimit(RLIMIT_NOFILE, &lim) == 0 && driver_fd != lim.rlim_cur)
|
||||
@ -84,23 +95,45 @@ void mach_driver_init(const char** applep)
|
||||
driver_fd = d;
|
||||
}
|
||||
#endif
|
||||
#endif
|
||||
}
|
||||
|
||||
void mach_driver_init_pthread(void) {
|
||||
_os_tsd_set_direct(__TSD_DSERVER_RPC_FD, (void*)(intptr_t)driver_fd);
|
||||
use_per_thread_driver_fd = true;
|
||||
};
|
||||
|
||||
__attribute__((visibility("default")))
|
||||
int lkm_call(int call_nr, void* arg)
|
||||
{
|
||||
return __real_ioctl(driver_fd, call_nr, arg);
|
||||
__simple_printf("Something called the old LKM API (nr = %d)\n", call_nr);
|
||||
__builtin_unreachable();
|
||||
}
|
||||
|
||||
__attribute__((visibility("default")))
|
||||
int lkm_call_raw(int call_nr, void* arg)
|
||||
{
|
||||
return __real_ioctl_raw(driver_fd, call_nr, arg);
|
||||
__simple_printf("Something called the old LKM API (nr = %d)\n", call_nr);
|
||||
__builtin_unreachable();
|
||||
}
|
||||
|
||||
__attribute__((visibility("default")))
|
||||
int mach_driver_get_fd(void)
|
||||
int mach_driver_get_dyld_fd(void)
|
||||
{
|
||||
return driver_fd;
|
||||
}
|
||||
|
||||
VISIBLE
|
||||
int mach_driver_get_fd(void) {
|
||||
if (use_per_thread_driver_fd) {
|
||||
return (int)(intptr_t)_os_tsd_get_direct(__TSD_DSERVER_RPC_FD);
|
||||
} else {
|
||||
return driver_fd;
|
||||
}
|
||||
};
|
||||
|
||||
VISIBLE
|
||||
void* elfcalls_get_pointer(void) {
|
||||
return _elfcalls;
|
||||
};
|
||||
|
||||
|
@ -1,10 +1,18 @@
|
||||
#ifndef _LKM_H
|
||||
#define _LKM_H
|
||||
|
||||
#include <os/tsd.h>
|
||||
|
||||
// TSD slot 6 is reserved by Apple for Windows/WINE compatibility.
|
||||
// Since WINE can never run under Darling, we can use that slot for our own purposes.
|
||||
// Namely, we use it to store the the per-thread darlingserver socket.
|
||||
#define __TSD_DSERVER_RPC_FD 6
|
||||
|
||||
void mach_driver_init(const char** applep);
|
||||
int lkm_call(int call_nr, void* arg);
|
||||
int lkm_call_raw(int call_nr, void* arg); // w/o errno translation
|
||||
int mach_driver_get_fd(void);
|
||||
int mach_driver_get_dyld_fd(void);
|
||||
|
||||
#endif
|
||||
|
||||
|
@ -10,12 +10,22 @@
|
||||
#include "mach_traps.h"
|
||||
#include <mach/mach_init.h>
|
||||
#include "../ext/mremap.h"
|
||||
#include <darlingserver/rpc.h>
|
||||
#include "../simple.h"
|
||||
|
||||
#define UNIMPLEMENTED_TRAP() { char msg[] = "Called unimplemented Mach trap: "; write(2, msg, sizeof(msg)-1); write(2, __FUNCTION__, sizeof(__FUNCTION__)-1); write(2, "\n", 1); }
|
||||
|
||||
mach_port_name_t mach_reply_port_impl(void)
|
||||
{
|
||||
return lkm_call(NR_mach_reply_port, 0);
|
||||
__simple_printf("Got to mach_reply_port_impl\n");
|
||||
unsigned int port_name;
|
||||
if (dserver_rpc_mach_reply_port(&port_name) != 0) {
|
||||
__simple_printf("mach_reply_port_impl RPC failed\n");
|
||||
port_name = MACH_PORT_NULL;
|
||||
}
|
||||
__simple_printf("Returning from mach_reply_port_impl: %d\n", port_name);
|
||||
//__simple_abort();
|
||||
return port_name;
|
||||
}
|
||||
|
||||
mach_port_name_t thread_self_trap_impl(void)
|
||||
@ -649,7 +659,15 @@ kern_return_t syscall_thread_switch_impl(
|
||||
|
||||
mach_port_name_t task_self_trap_impl(void)
|
||||
{
|
||||
return lkm_call(NR_task_self_trap, 0);
|
||||
__simple_printf("Got to task_self_trap_impl\n");
|
||||
unsigned int port_name;
|
||||
if (dserver_rpc_task_self_trap(&port_name) != 0) {
|
||||
__simple_printf("task_self_trap_impl RPC failed\n");
|
||||
port_name = MACH_PORT_NULL;
|
||||
}
|
||||
__simple_printf("Returning from task_self_trap_impl: %d\n", port_name);
|
||||
//__simple_abort();
|
||||
return port_name;
|
||||
}
|
||||
|
||||
/*
|
||||
|
68
src/kernel/emulation/linux/resources/dserver-rpc-defs.h
Normal file
68
src/kernel/emulation/linux/resources/dserver-rpc-defs.h
Normal file
@ -0,0 +1,68 @@
|
||||
#include "../network/recvmsg.h"
|
||||
#include "../network/sendmsg.h"
|
||||
#include "../network/getsockopt.h"
|
||||
#include <stddef.h>
|
||||
#include <sys/_types/_iovec_t.h>
|
||||
#include <linux-syscalls/linux.h>
|
||||
#include "../base.h"
|
||||
#include "../duct_errno.h"
|
||||
#include "../mach/lkm.h"
|
||||
#include "../elfcalls_wrapper.h"
|
||||
|
||||
extern void* memcpy(void* dest, const void* src, __SIZE_TYPE__ n);
|
||||
|
||||
struct linux_sockaddr_un {
|
||||
unsigned short int sun_family;
|
||||
char sun_path[108];
|
||||
};
|
||||
|
||||
#define dserver_rpc_hooks_msghdr_t struct linux_msghdr
|
||||
#define dserver_rpc_hooks_iovec_t struct iovec
|
||||
#define dserver_rpc_hooks_cmsghdr_t struct linux_cmsghdr
|
||||
#define DSERVER_RPC_HOOKS_CMSG_SPACE LINUX_CMSG_SPACE
|
||||
#define DSERVER_RPC_HOOKS_CMSG_FIRSTHDR(msghdr) ({ \
|
||||
struct linux_msghdr* _msghdr = (msghdr); \
|
||||
(_msghdr->msg_controllen >= sizeof(struct linux_cmsghdr)) ? ((struct linux_cmsghdr*)(_msghdr->msg_control)) : NULL; \
|
||||
})
|
||||
#define DSERVER_RPC_HOOKS_SOL_SOCKET LINUX_SOL_SOCKET
|
||||
#define DSERVER_RPC_HOOKS_SCM_RIGHTS 1
|
||||
#define DSERVER_RPC_HOOKS_CMSG_LEN LINUX_CMSG_LEN
|
||||
#define DSERVER_RPC_HOOKS_CMSG_DATA(cmsghdr) (&(cmsghdr)->cmsg_data[0])
|
||||
#define DSERVER_RPC_HOOKS_ATTRIBUTE static
|
||||
|
||||
#define dserver_rpc_hooks_get_pid() ((pid_t)LINUX_SYSCALL0(__NR_getpid))
|
||||
|
||||
#define dserver_rpc_hooks_get_tid() ((pid_t)LINUX_SYSCALL(__NR_gettid))
|
||||
|
||||
|
||||
#define dserver_rpc_hooks_get_server_address() ((void*)__dserver_socket_address())
|
||||
|
||||
#define dserver_rpc_hooks_get_server_address_length() sizeof(struct linux_sockaddr_un)
|
||||
|
||||
#define dserver_rpc_hooks_memcpy memcpy
|
||||
|
||||
static long int dserver_rpc_hooks_send_message(int socket, const dserver_rpc_hooks_msghdr_t* message) {
|
||||
#ifdef __NR_socketcall
|
||||
return LINUX_SYSCALL(__NR_socketcall, LINUX_SYS_SENDMSG, ((long[6]) { socket, message, 0 }));
|
||||
#else
|
||||
return LINUX_SYSCALL(__NR_sendmsg, socket, message, 0);
|
||||
#endif
|
||||
};
|
||||
|
||||
static long int dserver_rpc_hooks_receive_message(int socket, dserver_rpc_hooks_msghdr_t* out_message) {
|
||||
#ifdef __NR_socketcall
|
||||
return LINUX_SYSCALL(__NR_socketcall, LINUX_SYS_RECVMSG, ((long[6]) { socket, out_message, 0 }));
|
||||
#else
|
||||
return LINUX_SYSCALL(__NR_recvmsg, socket, out_message, 0);
|
||||
#endif
|
||||
};
|
||||
|
||||
#define dserver_rpc_hooks_get_bad_message_status() LINUX_EBADMSG
|
||||
|
||||
#define dserver_rpc_hooks_get_communication_error_status() LINUX_ECOMM
|
||||
|
||||
#define dserver_rpc_hooks_get_broken_pipe_status() LINUX_EPIPE
|
||||
|
||||
#define dserver_rpc_hooks_close_fd(fd) ((int)LINUX_SYSCALL1(__NR_close, fd))
|
||||
|
||||
#define dserver_rpc_hooks_get_socket mach_driver_get_fd
|
@ -7,6 +7,7 @@
|
||||
#include "mach/lkm.h"
|
||||
#include "signal/kill.h"
|
||||
#include <sys/signal.h>
|
||||
#include <darlingserver/rpc.h>
|
||||
|
||||
extern char* memchr(char* buf, int c, __SIZE_TYPE__ n);
|
||||
|
||||
@ -374,14 +375,10 @@ void __simple_printf(const char* format, ...)
|
||||
__attribute__ ((visibility ("default")))
|
||||
void __simple_kprintf(const char* format, ...)
|
||||
{
|
||||
char buffer[512];
|
||||
va_list vl;
|
||||
|
||||
va_start(vl, format);
|
||||
__simple_vsnprintf(buffer, sizeof(buffer), format, vl);
|
||||
__simple_vkprintf(format, vl);
|
||||
va_end(vl);
|
||||
|
||||
lkm_call(NR_kernel_printk, buffer);
|
||||
}
|
||||
|
||||
__attribute__ ((visibility ("default")))
|
||||
@ -410,7 +407,7 @@ void __simple_vkprintf(const char* format, va_list args)
|
||||
{
|
||||
char buffer[512];
|
||||
__simple_vsnprintf(buffer, sizeof(buffer), format, args);
|
||||
lkm_call(NR_kernel_printk, buffer);
|
||||
dserver_rpc_kprintf(buffer, __simple_strlen(buffer));
|
||||
}
|
||||
|
||||
__attribute__ ((visibility ("default")))
|
||||
|
@ -17,7 +17,7 @@ long sys_close_nocancel(int fd)
|
||||
{
|
||||
int ret;
|
||||
|
||||
if (fd == mach_driver_get_fd()) {
|
||||
if (fd == mach_driver_get_dyld_fd()) {
|
||||
__simple_kprintf("*** Someone tried to close the special LKM fd! ***");
|
||||
return 0;
|
||||
}
|
||||
|
@ -164,6 +164,11 @@ int _mach_fork_parent(void) {
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#ifdef DARLING
|
||||
extern void mach_driver_init(const char** applep);
|
||||
extern void mach_driver_init_pthread(void);
|
||||
#endif
|
||||
|
||||
void
|
||||
#ifdef DARLING
|
||||
mach_init_doit(const char** applep)
|
||||
@ -205,4 +210,8 @@ mach_init_doit(void)
|
||||
|
||||
_init_cpu_capabilities();
|
||||
_pthread_set_self(0);
|
||||
|
||||
#ifdef DARLING
|
||||
mach_driver_init_pthread();
|
||||
#endif
|
||||
}
|
||||
|
13
src/libsimple/CMakeLists.txt
Normal file
13
src/libsimple/CMakeLists.txt
Normal file
@ -0,0 +1,13 @@
|
||||
project(libsimple)
|
||||
|
||||
add_library(libsimple_darlingserver
|
||||
src/lock.c
|
||||
)
|
||||
|
||||
target_include_directories(libsimple_darlingserver PUBLIC
|
||||
include
|
||||
)
|
||||
|
||||
target_compile_definitions(libsimple_darlingserver PRIVATE
|
||||
LIBSIMPLE_LINUX=1
|
||||
)
|
4
src/libsimple/README.md
Normal file
4
src/libsimple/README.md
Normal file
@ -0,0 +1,4 @@
|
||||
# libsimple
|
||||
|
||||
A library that provides basic functions and utilities for code that runs under Darling.
|
||||
Some of these utilities are also available outside of a Darling environment (for use in darlingserver).
|
14
src/libsimple/include/libsimple/base.h
Normal file
14
src/libsimple/include/libsimple/base.h
Normal file
@ -0,0 +1,14 @@
|
||||
#ifndef _LIBSIMPLE_BASE_H_
|
||||
#define _LIBSIMPLE_BASE_H_
|
||||
|
||||
#if __cplusplus
|
||||
#define LIBSIMPLE_DECLARATIONS_BEGIN extern "C" {
|
||||
#define LIBSIMPLE_DECLARATIONS_END }
|
||||
#else
|
||||
#define LIBSIMPLE_DECLARATIONS_BEGIN
|
||||
#define LIBSIMPLE_DECLARATIONS_END
|
||||
#endif
|
||||
|
||||
#define LIBSIMPLE_INLINE __attribute__((always_inline))
|
||||
|
||||
#endif // _LIBSIMPLE_BASE_H_
|
49
src/libsimple/include/libsimple/lock.h
Normal file
49
src/libsimple/include/libsimple/lock.h
Normal file
@ -0,0 +1,49 @@
|
||||
#ifndef _LIBSIMPLE_LOCK_H_
|
||||
#define _LIBSIMPLE_LOCK_H_
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#include "base.h"
|
||||
|
||||
LIBSIMPLE_DECLARATIONS_BEGIN;
|
||||
|
||||
//
|
||||
// lock
|
||||
//
|
||||
|
||||
typedef struct libsimple_lock {
|
||||
uint32_t state;
|
||||
} libsimple_lock_t;
|
||||
|
||||
#define LIBSIMPLE_LOCK_INITIALIZER {0}
|
||||
|
||||
LIBSIMPLE_INLINE
|
||||
static void libsimple_lock_init(libsimple_lock_t* lock) {
|
||||
lock->state = 0;
|
||||
};
|
||||
|
||||
void libsimple_lock_lock(libsimple_lock_t* lock);
|
||||
void libsimple_lock_unlock(libsimple_lock_t* lock);
|
||||
|
||||
//
|
||||
// once
|
||||
//
|
||||
|
||||
typedef struct libsimple_once {
|
||||
uint32_t state;
|
||||
} libsimple_once_t;
|
||||
|
||||
typedef void (*libsimple_once_callback)(void);
|
||||
|
||||
#define LIBSIMPLE_ONCE_INITIALIZER {0}
|
||||
|
||||
LIBSIMPLE_INLINE
|
||||
static void libsimple_once_init(libsimple_once_t* once) {
|
||||
once->state = 0;
|
||||
};
|
||||
|
||||
void libsimple_once(libsimple_once_t* once, libsimple_once_callback callback);
|
||||
|
||||
LIBSIMPLE_DECLARATIONS_END;
|
||||
|
||||
#endif // _LIBSIMPLE_LOCK_H_
|
216
src/libsimple/src/lock.c
Normal file
216
src/libsimple/src/lock.c
Normal file
@ -0,0 +1,216 @@
|
||||
#include <stdatomic.h>
|
||||
#include <limits.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
#include <libsimple/lock.h>
|
||||
|
||||
#ifndef LIBSIMPLE_LOCK_DEBUG
|
||||
#define LIBSIMPLE_LOCK_DEBUG 0
|
||||
#endif
|
||||
|
||||
#if LIBSIMPLE_LOCK_DEBUG
|
||||
#define libsimple_lock_debug_internal(x, ...) libsimple_log(x "\n", ## __VA_ARGS__)
|
||||
#undef LIBSIMPLE_INLINE
|
||||
#define LIBSIMPLE_INLINE
|
||||
#else
|
||||
#define libsimple_lock_debug_internal(x, ...)
|
||||
#endif
|
||||
|
||||
#define libsimple_lock_debug(x, ...) libsimple_lock_debug_internal("lock %p: " x, lock, ## __VA_ARGS__)
|
||||
#define libsimple_once_debug(x, ...) libsimple_lock_debug_internal("once %p: " x, once, ## __VA_ARGS__)
|
||||
|
||||
#define FUTEX_WAIT 0
|
||||
#define FUTEX_WAKE 1
|
||||
|
||||
struct timespec;
|
||||
|
||||
#if LIBSIMPLE_LINUX
|
||||
#include <sys/syscall.h>
|
||||
#include <unistd.h>
|
||||
#endif
|
||||
|
||||
static int linux_futex(int* uaddr, int op, int val, const struct timespec* timeout, int* uaddr2, int val3) {
|
||||
#if LIBSIMPLE_LINUX
|
||||
return syscall(SYS_futex, uaddr, op, val, timeout, uaddr2, val3);
|
||||
#else
|
||||
#error linux_futex not implemented for this platform
|
||||
#endif
|
||||
};
|
||||
|
||||
//
|
||||
// libsimple_lock
|
||||
//
|
||||
// simple lock implementation on top of futexes, based on https://github.com/eliben/code-for-blog/blob/master/2018/futex-basics/mutex-using-futex.cpp
|
||||
//
|
||||
|
||||
// used to hide the use of C11 atomics from external code (e.g. C++ code, which can't use C11 atomics).
|
||||
// it's *technically* undefined behavior to cast the external one to this one, but that's okay;
|
||||
// we know our exact environment and it works in it
|
||||
typedef struct libsimple_lock_internal {
|
||||
_Atomic uint32_t state;
|
||||
} libsimple_lock_internal_t;
|
||||
|
||||
enum libsimple_lock_state {
|
||||
// the lock is unlocked (duh)
|
||||
libsimple_lock_state_unlocked = 0,
|
||||
|
||||
// the lock is locked, but no one is waiting for it
|
||||
libsimple_lock_state_locked_uncontended = 1,
|
||||
|
||||
// the locked is locked and there are waiters
|
||||
libsimple_lock_state_locked_contended = 2,
|
||||
};
|
||||
|
||||
LIBSIMPLE_INLINE
|
||||
uint32_t cmpxchg_wrapper_u32(_Atomic uint32_t* atom, uint32_t expected, uint32_t desired) {
|
||||
atomic_compare_exchange_strong_explicit(atom, &expected, desired, memory_order_acq_rel, memory_order_acquire);
|
||||
return expected;
|
||||
};
|
||||
|
||||
void libsimple_lock_lock(libsimple_lock_t* _lock) {
|
||||
libsimple_lock_internal_t* lock = (libsimple_lock_internal_t*)_lock;
|
||||
|
||||
libsimple_lock_debug("locking");
|
||||
|
||||
// we do the initial exchange using `libsimple_lock_state_locked_uncontended` because
|
||||
// if it was previously unlocked, it is now locked but nobody is waiting for it
|
||||
uint32_t prev = cmpxchg_wrapper_u32(&lock->state, libsimple_lock_state_unlocked, libsimple_lock_state_locked_uncontended);
|
||||
|
||||
libsimple_lock_debug("previous state was %u", prev);
|
||||
|
||||
// if it was not unlocked, we need to do some waiting
|
||||
if (prev != libsimple_lock_state_unlocked) {
|
||||
do {
|
||||
// we can skip the cmpxchg if the lock was already contended
|
||||
// also, when we do the cmpxchg, we need to make sure it's still locked
|
||||
// we use `libsimple_lock_state_locked_contended` because it was either uncontended (but it is now) or it was already contended
|
||||
if (prev == libsimple_lock_state_locked_contended || cmpxchg_wrapper_u32(&lock->state, libsimple_lock_state_locked_uncontended, libsimple_lock_state_locked_contended) != libsimple_lock_state_unlocked) {
|
||||
libsimple_lock_debug("going to wait");
|
||||
// do the actual sleeping
|
||||
// we expect `libsimple_lock_state_locked_contended` because we don't want to sleep if it's not contended
|
||||
linux_futex((int*)&lock->state, FUTEX_WAIT, libsimple_lock_state_locked_contended, NULL, 0, 0);
|
||||
libsimple_lock_debug("awoken");
|
||||
}
|
||||
|
||||
// loop as long as the lock was not unlocked
|
||||
// we use `libsimple_lock_state_locked_contended` for the same reason as before
|
||||
} while ((prev = cmpxchg_wrapper_u32(&lock->state, libsimple_lock_state_unlocked, libsimple_lock_state_locked_contended)) != libsimple_lock_state_unlocked);
|
||||
libsimple_lock_debug("lock acquired after sleeping");
|
||||
}
|
||||
|
||||
libsimple_lock_debug("lock acquired");
|
||||
};
|
||||
|
||||
void libsimple_lock_unlock(libsimple_lock_t* _lock) {
|
||||
libsimple_lock_internal_t* lock = (libsimple_lock_internal_t*)_lock;
|
||||
|
||||
libsimple_lock_debug("unlocking");
|
||||
|
||||
uint32_t prev = atomic_exchange_explicit(&lock->state, libsimple_lock_state_unlocked, memory_order_acq_rel);
|
||||
|
||||
libsimple_lock_debug("previous state was %u", prev);
|
||||
|
||||
// if it was previously contended, then we need to wake someone up
|
||||
if (prev == libsimple_lock_state_locked_contended) {
|
||||
libsimple_lock_debug("waking someone up");
|
||||
linux_futex((int*)&lock->state, FUTEX_WAKE, 1, NULL, 0, 0);
|
||||
}
|
||||
};
|
||||
|
||||
//
|
||||
// libsimple_once
|
||||
//
|
||||
// see https://github.com/bugaevc/serenity/blob/1fc3f2ba84d9c75c6c30e62014dabe4fcd62aae1/Libraries/LibPthread/pthread_once.cpp
|
||||
//
|
||||
|
||||
typedef struct libsimple_once_internal {
|
||||
_Atomic uint32_t state;
|
||||
} libsimple_once_internal_t;
|
||||
|
||||
enum libsimple_once_state {
|
||||
// the once hasn't been triggered yet
|
||||
libsimple_once_state_untriggered = 0,
|
||||
|
||||
// the once has been triggered, but no one is waiting for it
|
||||
libsimple_once_state_triggered_uncontended = 1,
|
||||
|
||||
// the once has been triggered and there are waiters
|
||||
libsimple_once_state_triggered_contended = 2,
|
||||
|
||||
// the once has been triggered and has completed execution
|
||||
libsimple_once_state_completed = 3,
|
||||
};
|
||||
|
||||
void libsimple_once(libsimple_once_t* _once, libsimple_once_callback callback) {
|
||||
libsimple_once_internal_t* once = (libsimple_once_internal_t*)_once;
|
||||
|
||||
libsimple_once_debug("evaluating...");
|
||||
|
||||
uint32_t prev = libsimple_once_state_untriggered;
|
||||
bool exchanged = atomic_compare_exchange_strong_explicit(&once->state, &prev, libsimple_once_state_triggered_uncontended, memory_order_acq_rel, memory_order_acquire);
|
||||
|
||||
libsimple_once_debug("previous state was %u", prev);
|
||||
|
||||
if (exchanged) {
|
||||
libsimple_once_debug("performing callback...");
|
||||
|
||||
// we had `libsimple_once_state_untriggered` and now we have `libsimple_once_state_triggered_uncontended`, so let's do the callback
|
||||
callback();
|
||||
|
||||
libsimple_once_debug("callback done; updating state...");
|
||||
|
||||
// now let's update the state to tell everyone we're done
|
||||
prev = atomic_exchange_explicit(&once->state, libsimple_once_state_completed, memory_order_acq_rel);
|
||||
|
||||
libsimple_once_debug("previous state was %u", prev);
|
||||
|
||||
switch (prev) {
|
||||
// no one was waiting, so we have no one to wake up
|
||||
case libsimple_once_state_triggered_uncontended: break;
|
||||
case libsimple_once_state_triggered_contended: {
|
||||
libsimple_once_debug("waking up all waiters...");
|
||||
// otherwise, we have to wake someone up
|
||||
linux_futex((int*)&once->state, FUTEX_WAKE, INT_MAX, NULL, 0, 0);
|
||||
} break;
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
libsimple_once_debug("someone else is performing the callback");
|
||||
|
||||
while (true) {
|
||||
switch (prev) {
|
||||
case libsimple_once_state_completed: {
|
||||
// we're done
|
||||
libsimple_once_debug("callback completed by someone else; returning...");
|
||||
return;
|
||||
}; // not reached
|
||||
case libsimple_once_state_triggered_uncontended: {
|
||||
// somebody is already perfoming the callback;
|
||||
// now we're waiting, so let's update the state for that
|
||||
libsimple_once_debug("someone is already performing the callback with no waiters; becoming first waiter...");
|
||||
exchanged = atomic_compare_exchange_strong_explicit(&once->state, &prev, libsimple_once_state_triggered_contended, memory_order_acq_rel, memory_order_acquire);
|
||||
if (!exchanged) {
|
||||
libsimple_once_debug("state changed from uncontended; re-evaluating...");
|
||||
// something changed, let's re-evaluate
|
||||
continue;
|
||||
}
|
||||
prev = libsimple_once_state_triggered_contended;
|
||||
}; // fall through
|
||||
case libsimple_once_state_triggered_contended: {
|
||||
// somebody is already performing the callback and there are already waiters;
|
||||
// let's wait
|
||||
libsimple_once_debug("someone is already performing the callback with waiters; going to wait...");
|
||||
linux_futex((int*)&once->state, FUTEX_WAIT, prev, NULL, 0, 0);
|
||||
|
||||
libsimple_once_debug("woken up");
|
||||
|
||||
// we got woken up, but that may have been spurious;
|
||||
// let's loop again to re-evaluate
|
||||
prev = atomic_load_explicit(&once->state, memory_order_acquire);
|
||||
libsimple_once_debug("previous state was %u; going to re-evaluate...", prev);
|
||||
} break;
|
||||
}
|
||||
}
|
||||
};
|
@ -18,7 +18,7 @@ add_executable(darling darling.c)
|
||||
|
||||
target_link_libraries(darling -lutil)
|
||||
|
||||
include_directories(${CMAKE_CURRENT_SOURCE_DIR} ${CMAKE_BINARY_DIR}/src/darlingserver/include)
|
||||
include_directories(${CMAKE_CURRENT_SOURCE_DIR} ${CMAKE_BINARY_DIR}/src/external/darlingserver/include)
|
||||
|
||||
install(TARGETS darling DESTINATION bin
|
||||
PERMISSIONS
|
||||
|
@ -4,12 +4,12 @@ cmake_minimum_required(VERSION 3.10)
|
||||
|
||||
include_directories(include)
|
||||
|
||||
set_source_files_properties(${CMAKE_BINARY_DIR}/src/darlingserver/src/rpc.c PROPERTIES
|
||||
set_source_files_properties(${CMAKE_BINARY_DIR}/src/external/darlingserver/src/rpc.c PROPERTIES
|
||||
GENERATED TRUE
|
||||
)
|
||||
|
||||
add_library(mldr_dserver_rpc ${CMAKE_BINARY_DIR}/src/darlingserver/src/rpc.c)
|
||||
add_library(mldr32_dserver_rpc ${CMAKE_BINARY_DIR}/src/darlingserver/src/rpc.c)
|
||||
add_library(mldr_dserver_rpc ${CMAKE_BINARY_DIR}/src/external/darlingserver/src/rpc.c)
|
||||
add_library(mldr32_dserver_rpc ${CMAKE_BINARY_DIR}/src/external/darlingserver/src/rpc.c)
|
||||
|
||||
add_dependencies(mldr_dserver_rpc generate_dserver_rpc_wrappers)
|
||||
add_dependencies(mldr32_dserver_rpc generate_dserver_rpc_wrappers)
|
||||
|
@ -480,15 +480,19 @@ static void setup_space(struct load_results* lr, bool is_64_bit) {
|
||||
}
|
||||
|
||||
static char vchroot_buffer[4096];
|
||||
size_t vchroot_path_length = 0;
|
||||
uint64_t vchroot_path_length = 0;
|
||||
|
||||
if (dserver_rpc_vchroot_path(vchroot_buffer, sizeof(vchroot_buffer), &vchroot_path_length) < 0) {
|
||||
fprintf(stderr, "Failed to retrieve vchroot path from darlingserver\n");
|
||||
int code = dserver_rpc_vchroot_path(vchroot_buffer, sizeof(vchroot_buffer), &vchroot_path_length);
|
||||
if (code < 0) {
|
||||
fprintf(stderr, "Failed to retrieve vchroot path from darlingserver: %d\n", code);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
if (vchroot_path_length > 0) {
|
||||
lr->root_path = vchroot_buffer;
|
||||
} else if (vchroot_path_length >= sizeof(vchroot_buffer)) {
|
||||
fprintf(stderr, "Vchroot path is too large for buffer\n");
|
||||
exit(1);
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -3,7 +3,6 @@
|
||||
#include <stdbool.h>
|
||||
|
||||
#include "lock.h"
|
||||
#include <darling/emulation/simple.h>
|
||||
|
||||
#ifndef XTRACE_LOCK_DEBUG
|
||||
#define XTRACE_LOCK_DEBUG 0
|
||||
|
Loading…
Reference in New Issue
Block a user