1/*
2 * Copyright (c) 2000-2004 Apple Computer, 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// mach++ - C++ bindings for useful Mach primitives
27//
28#ifndef _H_MACHPP
29#define _H_MACHPP
30
31#include <security_utilities/utilities.h>
32#include <security_utilities/errors.h>
33#include <security_utilities/threading.h>
34#include <security_utilities/globalizer.h>
35#include <mach/mach.h>
36#include <servers/bootstrap.h>
37#include <set>
38
39// yes, we use some UNIX (non-mach) headers...
40#include <sys/types.h>
41#include <unistd.h>
42
43namespace Security {
44namespace MachPlusPlus {
45
46
47//
48// Exceptions thrown by the mach++ interface.
49//
50class Error : public CommonError {
51protected:
52	// actually, kern_return_t can be just about any subsystem type return code
53	Error(kern_return_t err);
54public:
55	virtual ~Error() throw();
56
57    virtual OSStatus osStatus() const;
58	virtual int unixError() const;
59
60	const kern_return_t error;
61
62	static void check(kern_return_t err);
63    static void throwMe(kern_return_t err) __attribute__((noreturn));
64};
65
66// generic return code checker
67inline void check(kern_return_t status)
68{ Error::check(status); }
69
70
71//
72// Simple vm_allocate/deallocate glue
73//
74void *allocate(size_t size);
75void deallocate(vm_address_t addr, size_t size);
76
77inline void deallocate(const void *addr, size_t size)
78{ deallocate(reinterpret_cast<vm_address_t>(addr), size); }
79
80
81//
82// An encapsulation of a Mach 3 port
83//
84class Port {
85protected:
86	static mach_port_t self() { return mach_task_self(); }
87
88public:
89	Port() { mPort = MACH_PORT_NULL; }
90	Port(mach_port_t port) { mPort = port; }
91
92	// devolve to Mach primitive type
93	operator mach_port_t () const	{ return mPort; }
94
95	// access reference (for primitives storing into &mach_port_t)
96	mach_port_t &port ()			{ return mPort; }
97	const mach_port_t &port () const { return mPort; }
98
99	// status checks
100	mach_port_type_t type() const
101	{ mach_port_type_t typ; check(mach_port_type(self(), mPort, &typ)); return typ; }
102
103	bool isType(mach_port_type_t typ) const	{ return type() & typ; }
104	bool isDead() const		{ return isType(MACH_PORT_TYPE_DEAD_NAME); }
105
106	// port allocation and management
107	void allocate(mach_port_right_t right = MACH_PORT_RIGHT_RECEIVE)
108	{ check(mach_port_allocate(self(), right, &mPort)); }
109	void deallocate()	{ check(mach_port_deallocate(self(), mPort)); }
110	void destroy()		{ check(mach_port_destroy(self(), mPort)); }
111
112	void insertRight(mach_msg_type_name_t type)
113	{ check(mach_port_insert_right(self(), mPort, mPort, type)); }
114
115	void modRefs(mach_port_right_t right, mach_port_delta_t delta = 1)
116	{ check(mach_port_mod_refs(self(), mPort, right, delta)); }
117
118	mach_port_urefs_t getRefs(mach_port_right_t right);
119
120	// port notification interface
121	mach_port_t requestNotify(mach_port_t notify,
122		mach_msg_id_t type = MACH_NOTIFY_DEAD_NAME, mach_port_mscount_t sync = 1);
123    mach_port_t cancelNotify(mach_msg_id_t type = MACH_NOTIFY_DEAD_NAME);
124
125	// queue state management
126	mach_port_msgcount_t qlimit() const;
127	void qlimit(mach_port_msgcount_t limit);
128
129    IFDUMP(void dump(const char *name = NULL));
130
131protected:
132	mach_port_t mPort;
133};
134
135
136//
137// A simple Port that deallocates itself on destruction.
138// If you need a subclass of Port, just assign it to a separate AutoPort.
139//
140class AutoPort : public Port {
141public:
142	AutoPort()	{ }
143	AutoPort(mach_port_t port) : Port(port) { }
144	~AutoPort()	{ if (mPort != MACH_PORT_NULL) deallocate(); }
145};
146
147
148//
149// Ports representing PortSets
150//
151class PortSet : public Port {
152public:
153	PortSet() { allocate(MACH_PORT_RIGHT_PORT_SET); }
154	~PortSet() { destroy(); }
155
156	void operator += (const Port &port)
157	{ check(mach_port_move_member(self(), port, mPort)); }
158
159    void operator -= (const Port &port)
160    { check(mach_port_move_member(self(), port, MACH_PORT_NULL)); }
161
162	set<Port> members() const;
163	bool contains(Port member) const;	// relatively slow
164};
165
166
167//
168// Ports that are bootstrap ports
169//
170class Bootstrap : public Port {
171public:
172    Bootstrap() { check(task_get_bootstrap_port(mach_task_self(), &mPort)); }
173    Bootstrap(mach_port_t bootp) : Port(bootp) { }
174
175	mach_port_t checkIn(const char *name) const;
176	mach_port_t checkInOptional(const char *name) const;
177
178	void registerAs(mach_port_t port, const char *name) const;
179
180	mach_port_t lookup(const char *name) const;
181	mach_port_t lookup2(const char *name) const;
182    mach_port_t lookupOptional(const char *name) const;
183
184    Bootstrap subset(Port requestor);
185
186    IFDUMP(void dump());
187
188private:
189	// officially, the register/lookup IPCs take an array of 128 characters (not a zero-end string)
190	mutable char nameBuffer[BOOTSTRAP_MAX_NAME_LEN];
191
192protected:
193	char *makeName(const char *s) const
194	{ return strncpy(nameBuffer, s, BOOTSTRAP_MAX_NAME_LEN); }
195};
196
197
198//
199// Ports that are Task Ports
200//
201class TaskPort : public Port {
202public:
203    TaskPort() { mPort = self(); }
204	TaskPort(mach_port_t p) : Port(p) { }
205    TaskPort(const Port &p) : Port(p) { }
206	TaskPort(pid_t pid);
207
208    Bootstrap bootstrap() const
209    { mach_port_t boot; check(task_get_bootstrap_port(mPort, &boot)); return boot; }
210    void bootstrap(Bootstrap boot)
211    { check(task_set_bootstrap_port(mPort, boot)); }
212
213    pid_t pid() const;
214};
215
216
217//
218// Ports that are are self-allocated and have receive rights
219//
220class ReceivePort : public Port {
221public:
222	ReceivePort()	{ allocate(); }
223	ReceivePort(const char *name, const Bootstrap &bootstrap, bool tryCheckin = true);
224	~ReceivePort()	{ destroy(); }
225};
226
227
228//
229// A little stack utility for temporarily switching your bootstrap around.
230// Essentially, it restores your bootstrap port when it dies. Since the
231// "current bootstrap port" is a process-global item, this uses a global
232// zone of exclusion (aka critical region). There's no protection against
233// someone else calling the underlying system service, of course.
234//
235class StBootstrap {
236public:
237    StBootstrap(const Bootstrap &boot, const TaskPort &task = TaskPort());
238    ~StBootstrap();
239
240private:
241    Bootstrap mOldBoot;
242    TaskPort mTask;
243    StLock<Mutex> locker;
244    static ModuleNexus<Mutex> critical; // critical region guard (of a sort)
245};
246
247
248//
249// A Mach-level memory guard.
250// This will vm_deallocate its argument when it gets destroyed.
251//
252class VMGuard {
253public:
254	VMGuard(void *addr, size_t length) : mAddr(addr), mLength(length) { }
255	~VMGuard()	{ deallocate(mAddr, mLength); }
256
257private:
258	void *mAddr;
259	size_t mLength;
260};
261
262
263//
264// Message buffers for Mach messages.
265// The logic here is somewhat inverted from the usual: send/receive
266// are methods on the buffers (rather than buffers being arguments to send/receive).
267// It's rather handy once you get used to that view.
268//
269class Message {
270public:
271    Message(void *buffer, mach_msg_size_t size);		// use buffer with size
272    Message(mach_msg_size_t size);					// allocate buffer with size
273    Message();								// set buffer later
274    virtual ~Message();
275
276    void setBuffer(void *buffer, mach_msg_size_t size); // use buffer with size
277    void setBuffer(mach_msg_size_t size);			// allocate buffer with size
278    void release();							// discard buffer (if any)
279
280    operator mig_reply_error_t & () const	{ return *mBuffer; }
281    operator mach_msg_header_t & () const	{ return mBuffer->Head; }
282    operator mig_reply_error_t * () const	{ return mBuffer; }
283    operator mach_msg_header_t * () const	{ return &mBuffer->Head; }
284    operator NDR_record_t & () const		{ return mBuffer->NDR; }
285
286    void *data() const						{ return mBuffer; }
287    mach_msg_size_t length() const			{ return mBuffer->Head.msgh_size; }
288    Port localPort() const					{ return mBuffer->Head.msgh_local_port; }
289    Port remotePort() const					{ return mBuffer->Head.msgh_remote_port; }
290    mach_msg_id_t msgId() const				{ return mBuffer->Head.msgh_id; }
291    mach_msg_bits_t bits() const			{ return mBuffer->Head.msgh_bits; }
292    kern_return_t returnCode() const		{ return mBuffer->RetCode; }
293
294    void localPort(mach_port_t p)			{ mBuffer->Head.msgh_local_port = p; }
295    void remotePort(mach_port_t p)			{ mBuffer->Head.msgh_remote_port = p; }
296
297public:
298    bool send(mach_msg_option_t options = 0,
299        mach_msg_timeout_t timeout = MACH_MSG_TIMEOUT_NONE,
300        mach_port_name_t notify = MACH_PORT_NULL);
301    bool receive(mach_port_t receivePort,
302        mach_msg_option_t options = 0,
303        mach_msg_timeout_t timeout = MACH_MSG_TIMEOUT_NONE,
304        mach_port_name_t notify = MACH_PORT_NULL);
305    bool sendReceive(mach_port_t receivePort,
306        mach_msg_option_t options = 0,
307        mach_msg_timeout_t timeout = MACH_MSG_TIMEOUT_NONE,
308        mach_port_name_t notify = MACH_PORT_NULL);
309
310    void destroy()		{ mach_msg_destroy(*this); }
311
312protected:
313    bool check(kern_return_t status);
314
315private:
316    mig_reply_error_t *mBuffer;
317    mach_msg_size_t mSize;
318    bool mRelease;
319};
320
321
322} // end namespace MachPlusPlus
323} // end namespace Security
324
325#endif //_H_MACHPP
326