kqchan.hpp
  1  #ifndef _DARLINGSERVER_KQCHAN_HPP_
  2  #define _DARLINGSERVER_KQCHAN_HPP_
  3  
  4  #include <memory>
  5  #include <optional>
  6  #include <deque>
  7  
  8  #include <darlingserver/monitor.hpp>
  9  #include <darlingserver/message.hpp>
 10  #include <darlingserver/duct-tape.h>
 11  #include <darlingserver/logging.hpp>
 12  
 13  #define NOTE_EXIT        0x80000000U
 14  #define NOTE_FORK        0x40000000U
 15  #define NOTE_EXEC        0x20000000U
 16  #define NOTE_REAP        0x10000000U
 17  #define NOTE_SIGNAL      0x08000000U
 18  #define NOTE_EXITSTATUS  0x04000000U
 19  #define NOTE_EXIT_DETAIL 0x02000000U
 20  #define NOTE_TRACK       0x00000001U
 21  #define NOTE_TRACKERR    0x00000002U
 22  #define NOTE_CHILD       0x00000004U
 23  #define NOTE_PDATAMASK   0x000fffffU
 24  #define NOTE_PCTRLMASK  (~(NOTE_PDATAMASK))
 25  
 26  namespace DarlingServer {
 27  	class Process;
 28  	class Thread;
 29  
 30  	class Kqchan: public Loggable {
 31  		friend class DarlingServer::Process;
 32  
 33  	protected:
 34  		uint64_t _debugID;
 35  		std::weak_ptr<DarlingServer::Process> _process;
 36  		std::shared_ptr<FD> _socket;
 37  		std::shared_ptr<Monitor> _monitor;
 38  		MessageQueue _inbox;
 39  		MessageQueue _outbox;
 40  		bool _canSend = false;
 41  		std::mutex _notificationMutex;
 42  		bool _canSendNotification = true;
 43  		bool _deferNotification = false;
 44  		std::optional<Message> _deferredNotification = std::nullopt;
 45  		std::mutex _sendingMutex;
 46  		uint64_t _notificationCount = 0;
 47  
 48  		Kqchan(std::shared_ptr<DarlingServer::Process> process);
 49  
 50  		virtual uintptr_t _idForProcess() const;
 51  
 52  		virtual void _processMessages();
 53  
 54  		virtual std::shared_ptr<Kqchan> sharedFromRoot();
 55  
 56  		void _sendNotification();
 57  		void _sendDeferredNotification();
 58  
 59  	public:
 60  		virtual ~Kqchan();
 61  
 62  		virtual int setup();
 63  
 64  		void logToStream(Log::Stream& stream) const;
 65  
 66  		class MachPort;
 67  		class Process;
 68  	};
 69  
 70  
 71  	class Kqchan::MachPort: public Kqchan, public std::enable_shared_from_this<MachPort> {
 72  	private:
 73  		uint32_t _port;
 74  		uint64_t _receiveBuffer;
 75  		uint64_t _receiveBufferSize;
 76  		uint64_t _savedFilterFlags;
 77  		dtape_kqchan_mach_port_t* _dtapeKqchan = nullptr;
 78  
 79  		void _modify(uint64_t receiveBuffer, uint64_t receiveBufferSize, uint64_t savedFilterFlags, pid_t nstid);
 80  		void _read(uint64_t defaultBuffer, uint64_t defaultBufferSize, pid_t nstid);
 81  
 82  		void _notify();
 83  
 84  		virtual void _processMessages();
 85  
 86  		/**
 87  		 * This method is used to check for events in an async, lock-safe manner.
 88  		 * This is because the caller may be holding a lock on the kqchan or may even be outside a microthread,
 89  		 * so the actual check needs to be scheduled in a kernel microthread.
 90  		 */
 91  		void _checkForEventsAsync();
 92  		std::function<void()> _checkForEventsAsyncFactory();
 93  
 94  	protected:
 95  		virtual uintptr_t _idForProcess() const;
 96  
 97  		virtual std::shared_ptr<Kqchan> sharedFromRoot();
 98  
 99  	public:
100  		MachPort(std::shared_ptr<DarlingServer::Process> process, uint32_t port, uint64_t receiveBuffer, uint64_t receiveBufferSize, uint64_t savedFilterFlags);
101  		~MachPort();
102  
103  		MachPort(const MachPort&) = delete;
104  		MachPort& operator=(const MachPort&) = delete;
105  
106  		MachPort(MachPort&&) = delete;
107  		MachPort& operator=(MachPort&&) = delete;
108  
109  		virtual int setup();
110  	};
111  
112  	class Kqchan::Process: public Kqchan, public std::enable_shared_from_this<Process> {
113  		friend class DarlingServer::Process;
114  
115  	private:
116  		// some events can be coalesced, but ones like NOTE_FORK and NOTE_EXIT can't be sent in a single event
117  		struct Event {
118  		public:
119  			uint32_t events;
120  			int64_t data;
121  			std::shared_ptr<Process> newKqchan;
122  		};
123  
124  		pid_t _nspid;
125  		uint32_t _flags;
126  		std::mutex _mutex;
127  		std::deque<Event> _events;
128  		std::weak_ptr<DarlingServer::Process> _targetProcess;
129  		bool _attached = false;
130  
131  		void _modify(uint32_t flags);
132  		void _read();
133  
134  		void _notify(uint32_t event, int64_t data);
135  
136  		virtual void _processMessages();
137  
138  		/**
139  		 * See Kqchan::MachPort::_checkForEventsAsync(); this does the same thing for process kqchannels.
140  		 */
141  		void _checkForEventsAsync();
142  		std::function<void()> _checkForEventsAsyncFactory();
143  
144  	protected:
145  		virtual uintptr_t _idForProcess() const;
146  
147  		virtual std::shared_ptr<Kqchan> sharedFromRoot();
148  
149  	public:
150  		Process(std::shared_ptr<DarlingServer::Process> process, pid_t nspid, uint32_t flags);
151  		~Process();
152  
153  		Process(const Process&) = delete;
154  		Process& operator=(const Process&) = delete;
155  
156  		Process(Process&&) = delete;
157  		Process& operator=(Process&&) = delete;
158  
159  		virtual int setup();
160  
161  		virtual void logToStream(Log::Stream& stream) const;
162  	};
163  };
164  
165  #endif // _DARLINGSERVER_KQCHAN_HPP_