1/*
2 * Copyright (c) 2000-2004,2007 Apple Inc. All Rights Reserved.
3 *
4 * @APPLE_LICENSE_HEADER_START@
5 *
6 * This file contains Original Code and/or Modifications of Original Code
7 * as defined in and that are subject to the Apple Public Source License
8 * Version 2.0 (the 'License'). You may not use this file except in
9 * compliance with the License. Please obtain a copy of the License at
10 * http://www.opensource.apple.com/apsl/ and read it before using this
11 * file.
12 *
13 * The Original Code and all software distributed under the License are
14 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
15 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
16 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
17 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
18 * Please see the License for the specific language governing rights and
19 * limitations under the License.
20 *
21 * @APPLE_LICENSE_HEADER_END@
22 */
23
24
25//
26// machserver - C++ shell for writing Mach 3 servers
27//
28#ifndef _H_MACHSERVER
29#define _H_MACHSERVER
30
31#include <security_utilities/mach++.h>
32#include <security_utilities/timeflow.h>
33#include <security_utilities/threading.h>
34#include <security_utilities/globalizer.h>
35#include <security_utilities/alloc.h>
36#include <security_utilities/tqueue.h>
37#include <set>
38
39namespace Security {
40namespace MachPlusPlus {
41
42
43extern "C" {
44	void cdsa_mach_notify_dead_name(mach_port_t, mach_port_name_t port);
45	void cdsa_mach_notify_port_destroyed(mach_port_t, mach_port_name_t port);
46	void cdsa_mach_notify_port_deleted(mach_port_t, mach_port_name_t port);
47	void cdsa_mach_notify_send_once(mach_port_t);
48	void cdsa_mach_notify_no_senders(mach_port_t, mach_port_mscount_t);
49};
50
51
52//
53// Mach server object
54//
55class MachServer {
56protected:
57	class LoadThread; friend class LoadThread;
58
59	struct Allocation {
60		void *addr;
61		Allocator *allocator;
62		Allocation(void *p, Allocator &alloc) : addr(p), allocator(&alloc) { }
63		bool operator < (const Allocation &other) const
64		{ return addr < other.addr || (addr == other.addr && allocator < other.allocator); }
65	};
66
67protected:
68    struct PerThread {
69        MachServer *server;
70        set<Allocation> deferredAllocations;
71
72        PerThread() : server(NULL) { }
73    };
74    static ModuleNexus< ThreadNexus<PerThread> > thread;
75    static PerThread &perThread()	{ return thread()(); }
76
77public:
78	MachServer();
79    MachServer(const char *name);
80	MachServer(const char *name, const Bootstrap &bootstrap);
81	virtual ~MachServer();
82
83	void run(mach_msg_size_t maxSize = 4096, mach_msg_options_t options = 0);
84
85	Time::Interval timeout() const { return workerTimeout; }
86	void timeout(Time::Interval t)	{ workerTimeout = t; }
87	UInt32 maxThreads() const		{ return maxWorkerCount; }
88	void maxThreads(UInt32 n)		{ maxWorkerCount = n; }
89	bool floatingThread() const		{ return useFloatingThread; }
90	void floatingThread(bool t)		{ useFloatingThread = t; }
91
92	Port primaryServicePort() const	{ return mServerPort; }
93
94	// listen on additional ports (dispatching to the main handler)
95	void add(Port receiver);
96	void remove(Port receiver);
97
98	// the currently active server in this thread (there can only be one)
99	static MachServer &active()
100	{ assert(perThread().server); return *perThread().server; }
101
102	// request port status notifications (override virtual methods below to receive)
103	virtual void notifyIfDead(Port port, bool doNotify = true) const;
104	virtual void notifyIfUnused(Port port, bool doNotify = true) const;
105
106	// register (Allocator-derived) memory to be released after reply is sent
107	void releaseWhenDone(Allocator &alloc, void *memory);
108
109	// call if you realize that your server method will take a long time
110	void longTermActivity();
111
112public:
113	class Timer : private ScheduleQueue<Time::Absolute>::Event {
114		friend class MachServer;
115	protected:
116		Timer(bool longTerm = false) { mLongTerm = longTerm; }
117		virtual ~Timer();
118
119		bool longTerm() const		{ return mLongTerm; }
120		void longTerm(bool lt)		{ mLongTerm = lt; }
121
122	public:
123		virtual void action() = 0;
124
125		Time::Absolute when() const	{ return Event::when(); }
126		bool scheduled() const		{ return Event::scheduled(); }
127
128		// lifetime management hooks (default does nothing)
129		virtual void select();
130		virtual void unselect();
131
132	private:
133		bool mLongTerm;				// long-term activity (count as worker thread)
134	};
135
136	virtual void setTimer(Timer *timer, Time::Absolute when);
137	void setTimer(Timer *timer, Time::Interval offset)
138	{ setTimer(timer, Time::now() + offset); }
139
140	virtual void clearTimer(Timer *timer);
141
142public:
143    class Handler {
144    public:
145        Handler(mach_port_t p) : mPort(p) { }
146        Handler() : mPort(MACH_PORT_NULL) { }
147		virtual ~Handler();
148
149        mach_port_t port() const	{ return mPort; }
150
151        virtual boolean_t handle(mach_msg_header_t *in, mach_msg_header_t *out) = 0;
152
153    protected:
154        void port(mach_port_t p)	{ assert(mPort == MACH_PORT_NULL); mPort = p; }
155
156    private:
157        mach_port_t mPort;
158    };
159
160    class NoReplyHandler : public Handler {
161    public:
162        virtual boolean_t handle(mach_msg_header_t *in) = 0;
163
164    private:
165        boolean_t handle(mach_msg_header_t *in, mach_msg_header_t *out);
166    };
167
168    void add(Handler &handler);
169    void remove(Handler &handler);
170
171protected:
172	// your server dispatch function
173	virtual boolean_t handle(mach_msg_header_t *in, mach_msg_header_t *out) = 0;
174
175	// override these to receive Mach-style port notifications about your clients
176	virtual void notifyDeadName(Port port);
177	virtual void notifyPortDeleted(Port port);
178	virtual void notifyPortDestroyed(Port port);
179	virtual void notifySendOnce(Port port);
180	virtual void notifyNoSenders(Port port, mach_port_mscount_t);
181
182	// this will be called if the server wants a new thread but has hit its limit
183	virtual void threadLimitReached(UInt32 limit);
184
185	// this gets called every time the server finishes an action (any action)
186	virtual void eventDone();
187
188	// don't mess with this unless you know what you're doing
189    Bootstrap bootstrap;			// bootstrap port we registered with
190	ReceivePort mServerPort;		// registered/primary server port
191    PortSet mPortSet;				// joint receiver port set
192
193	mach_msg_size_t mMaxSize;				// maximum message size
194	mach_msg_options_t mMsgOptions;	// kernel call options
195
196    typedef set<Handler *> HandlerSet;
197    HandlerSet mHandlers;			// subsidiary message port handlers
198
199protected:
200	void releaseDeferredAllocations();
201
202protected:
203	void busy();
204	void idle();
205	void ensureReadyThread();
206
207protected:
208	class LoadThread : public Thread {
209	public:
210		LoadThread(MachServer &srv) : server(srv) { }
211
212		MachServer &server;
213
214		void action();		// code implementation
215	};
216
217	Mutex managerLock;		// lock for thread-global management info below
218	set<Thread *> workers;	// threads running for this server
219	UInt32 workerCount;		// number of worker threads (including primary)
220	UInt32 maxWorkerCount;	// administrative limit to workerCount
221	bool useFloatingThread;	// keep a "floating" idle thread (instead of using longTermActivity)
222
223	UInt32 highestWorkerCount; // high water mark for workerCount
224	UInt32 idleCount;		// number of threads waiting for work
225	Time::Interval workerTimeout; // seconds of idle time before a worker retires
226	Time::Absolute nextCheckTime; // next time to check for excess threads
227	UInt32 leastIdleWorkers; // max(idleCount) since last checkpoint
228	ScheduleQueue<Time::Absolute> timers;
229
230	void addThread(Thread *thread); // add thread to worker pool
231	void removeThread(Thread *thread); // remove thread from worker pool
232	bool processTimer();	// handle one due timer object, if any (return true if there was one)
233
234private:
235	static boolean_t handler(mach_msg_header_t *in, mach_msg_header_t *out);
236    void setup(const char *name);
237	void runServerThread(bool doTimeout = false);
238
239	friend void cdsa_mach_notify_dead_name(mach_port_t, mach_port_name_t port);
240	friend void cdsa_mach_notify_port_destroyed(mach_port_t, mach_port_name_t port);
241	friend void cdsa_mach_notify_port_deleted(mach_port_t, mach_port_name_t port);
242	friend void cdsa_mach_notify_send_once(mach_port_t);
243	friend void cdsa_mach_notify_no_senders(mach_port_t, mach_port_mscount_t);
244};
245
246
247} // end namespace MachPlusPlus
248} // end namespace Security
249
250#endif //_H_MACHSERVER
251