1
/** Implementation of network port object based on unix domain sockets
2
Copyright (C) 2000 Free Software Foundation, Inc.
4
Written by: Richard Frith-Macdonald <richard@brainstorm.co.uk>
5
Based on code by: Andrew Kachites McCallum <mccallum@gnu.ai.mit.edu>
7
This file is part of the GNUstep Base Library.
9
This library is free software; you can redistribute it and/or
10
modify it under the terms of the GNU Library General Public
11
License as published by the Free Software Foundation; either
12
version 2 of the License, or (at your option) any later version.
14
This library is distributed in the hope that it will be useful,
15
but WITHOUT ANY WARRANTY; without even the implied warranty of
16
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
17
Library General Public License for more details.
19
You should have received a copy of the GNU Library General Public
20
License along with this library; if not, write to the Free
21
Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111 USA.
25
#include "GNUstepBase/preface.h"
26
#include "GNUstepBase/GSLock.h"
27
#include "Foundation/NSArray.h"
28
#include "Foundation/NSNotification.h"
29
#include "Foundation/NSException.h"
30
#include "Foundation/NSRunLoop.h"
31
#include "Foundation/NSByteOrder.h"
32
#include "Foundation/NSData.h"
33
#include "Foundation/NSDate.h"
34
#include "Foundation/NSMapTable.h"
35
#include "Foundation/NSPortMessage.h"
36
#include "Foundation/NSPortNameServer.h"
37
#include "Foundation/NSLock.h"
38
#include "Foundation/NSThread.h"
39
#include "Foundation/NSConnection.h"
40
#include "Foundation/NSDebug.h"
41
#include "Foundation/NSPathUtilities.h"
42
#include "Foundation/NSValue.h"
43
#include "Foundation/NSFileManager.h"
44
#include "Foundation/NSProcessInfo.h"
48
#include <unistd.h> /* for gethostname() */
52
#include <sys/param.h> /* for MAXHOSTNAMELEN */
53
#include <sys/types.h>
55
#include <arpa/inet.h> /* for inet_ntoa() */
56
#endif /* !__MINGW__ */
59
#include <string.h> /* for strchr() */
60
#include <ctype.h> /* for strchr() */
69
#include <sys/resource.h>
71
#include <sys/socket.h>
75
* Stuff for setting the sockets into non-blocking mode.
78
#define NBLK_OPT O_NONBLOCK
80
#define NBLK_OPT FNDELAY
83
#include <netinet/in.h>
85
#if !defined(SIOCGIFCONF) || defined(__CYGWIN__)
86
#include <sys/ioctl.h>
88
#include <sys/sockio.h>
93
#include <sys/stropts.h>
95
#endif /* !__MINGW__ */
98
#define close closesocket
101
/* Older systems (Solaris) compatibility */
103
#define AF_LOCAL AF_UNIX
104
#define PF_LOCAL PF_UNIX
107
#define SUN_LEN(su) \
108
(sizeof(*(su)) - sizeof((su)->sun_path) + strlen((su)->sun_path))
112
* Largest chunk of data possible in DO
114
static gsu32 maxDataLength = 10 * 1024 * 1024;
117
#define M_LOCK(X) {NSDebugMLLog(@"NSMessagePort",@"lock %@",X); [X lock];}
118
#define M_UNLOCK(X) {NSDebugMLLog(@"NSMessagePort",@"unlock %@",X); [X unlock];}
120
#define M_LOCK(X) {[X lock];}
121
#define M_UNLOCK(X) {[X unlock];}
124
#define GS_CONNECTION_MSG 0
125
#define NETBLOCK 8192
128
#define INADDR_NONE -1
132
* Theory of operation
138
/* Private interfaces */
141
* The GSPortItemType constant is used to identify the type of data in
142
* each packet read. All data transmitted is in a packet, each packet
143
* has an initial packet type and packet length.
147
GSP_PORT, /* Simple port item. */
148
GSP_DATA, /* Simple data item. */
149
GSP_HEAD /* Port message header + initial data. */
153
* The GSPortItemHeader structure defines the header for each item transmitted.
154
* Its contents are transmitted in network byte order.
157
gsu32 type; /* A GSPortItemType as a 4-byte number. */
158
gsu32 length; /* The length of the item (excluding header). */
162
* The GSPortMsgHeader structure is at the start of any item of type GSP_HEAD.
163
* Its contents are transmitted in network byte order.
164
* Any additional data in the item is an NSData object.
165
* NB. additional data counts as part of the same item.
168
gsu32 mId; /* The ID for the message starting with this. */
169
gsu32 nItems; /* Number of items (including this one). */
174
char addr[0]; /* name of the socket in the port directory */
178
* Here is how data is transmitted over a socket -
179
* Initially the process making the connection sends an item of type
180
* GSP_PORT to tell the remote end what port is connecting to it.
181
* Therafter, all communication is via port messages. Each port message
182
* consists of an item of type GSP_HEAD followed by zero or more items
183
* of type GSP_PORT or GSP_DATA. The number of items in a port message
184
* is encoded in the 'nItems' field of the header.
188
GS_H_UNCON = 0, // Currently idle and unconnected.
189
GS_H_TRYCON, // Trying connection (outgoing).
190
GS_H_ACCEPT, // Making initial connection (incoming).
191
GS_H_CONNECTED // Currently connected.
194
@interface GSMessageHandle : NSObject <GCFinalization, RunLoopEvents>
196
int desc; /* File descriptor for I/O. */
197
unsigned wItem; /* Index of item being written. */
198
NSMutableData *wData; /* Data object being written. */
199
unsigned wLength; /* Ammount written so far. */
200
NSMutableArray *wMsgs; /* Message in progress. */
201
NSMutableData *rData; /* Buffer for incoming data */
202
gsu32 rLength; /* Amount read so far. */
203
gsu32 rWant; /* Amount desired. */
204
NSMutableArray *rItems; /* Message in progress. */
205
GSPortItemType rType; /* Type of data being read. */
206
gsu32 rId; /* Id of incoming message. */
207
unsigned nItems; /* Number of items to be read. */
208
GSHandleState state; /* State of the handle. */
209
unsigned int addrNum; /* Address number within host. */
211
NSRecursiveLock *myLock; /* Lock for this handle. */
212
BOOL caller; /* Did we connect to other end? */
214
NSMessagePort *recvPort;
215
NSMessagePort *sendPort;
216
struct sockaddr_un sockAddr; /* Far end of connection. */
219
+ (GSMessageHandle*) handleWithDescriptor: (int)d;
220
- (BOOL) connectToPort: (NSMessagePort*)aPort beforeDate: (NSDate*)when;
224
- (void) receivedEvent: (void*)data
225
type: (RunLoopEventType)type
227
forMode: (NSString*)mode;
228
- (NSMessagePort*) recvPort;
229
- (BOOL) sendMessage: (NSArray*)components beforeDate: (NSDate*)when;
230
- (NSMessagePort*) sendPort;
231
- (void) setState: (GSHandleState)s;
232
- (GSHandleState) state;
233
- (NSDate*) timedOutEvent: (void*)data
234
type: (RunLoopEventType)type
235
forMode: (NSString*)mode;
240
* Utility functions for encoding and decoding ports.
242
static NSMessagePort*
243
decodePort(NSData *data)
245
GSPortItemHeader *pih;
248
pih = (GSPortItemHeader*)[data bytes];
249
NSCAssert(GSSwapBigI32ToHost(pih->type) == GSP_PORT,
250
NSInternalInconsistencyException);
251
pi = (GSPortInfo*)&pih[1];
252
if (pi->version != 0)
254
NSLog(@"Remote version of GNUstep is more recent than this one (%i)",
259
NSDebugFLLog(@"NSMessagePort", @"Decoded port as '%s'", pi->addr);
261
return [NSMessagePort _portWithName: pi->addr
266
newDataWithEncodedPort(NSMessagePort *port)
268
GSPortItemHeader *pih;
272
const unsigned char *name = [port _name];
274
plen = 2 + strlen(name);
276
data = [[NSMutableData alloc] initWithLength: sizeof(GSPortItemHeader)+plen];
277
pih = (GSPortItemHeader*)[data mutableBytes];
278
pih->type = GSSwapHostI32ToBig(GSP_PORT);
279
pih->length = GSSwapHostI32ToBig(plen);
280
pi = (GSPortInfo*)&pih[1];
281
strcpy(pi->addr, name);
283
NSDebugFLLog(@"NSMessagePort", @"Encoded port as '%s'", pi->addr);
290
@implementation GSMessageHandle
292
static Class mutableArrayClass;
293
static Class mutableDataClass;
294
static Class portMessageClass;
295
static Class runLoopClass;
298
+ (id) allocWithZone: (NSZone*)zone
300
[NSException raise: NSGenericException
301
format: @"attempt to alloc a GSMessageHandle!"];
305
+ (GSMessageHandle*) handleWithDescriptor: (int)d
307
GSMessageHandle *handle;
312
#endif /* __MINGW__ */
316
NSLog(@"illegal descriptor (%d) for message handle", d);
321
if (ioctlsocket(d, FIONBIO, &dummy) < 0)
323
NSLog(@"unable to set non-blocking mode on %d - %s",
324
d, GSLastErrorStr(errno));
327
#else /* !__MINGW__ */
328
if ((e = fcntl(d, F_GETFL, 0)) >= 0)
331
if (fcntl(d, F_SETFL, e) < 0)
333
NSLog(@"unable to set non-blocking mode on %d - %s",
334
d, GSLastErrorStr(errno));
340
NSLog(@"unable to get non-blocking mode on %d - %s",
341
d, GSLastErrorStr(errno));
345
handle = (GSMessageHandle*)NSAllocateObject(self, 0, NSDefaultMallocZone());
347
handle->wMsgs = [NSMutableArray new];
348
handle->myLock = [GSLazyRecursiveLock new];
350
return AUTORELEASE(handle);
355
if (self == [GSMessageHandle class])
358
WORD wVersionRequested;
361
wVersionRequested = MAKEWORD(2, 0);
362
WSAStartup(wVersionRequested, &wsaData);
364
mutableArrayClass = [NSMutableArray class];
365
mutableDataClass = [NSMutableData class];
366
portMessageClass = [NSPortMessage class];
367
runLoopClass = [NSRunLoop class];
371
- (BOOL) connectToPort: (NSMessagePort*)aPort beforeDate: (NSDate*)when
374
const unsigned char *name;
377
NSDebugMLLog(@"NSMessagePort", @"Connecting on 0x%x before %@", self, when);
378
if (state != GS_H_UNCON)
382
if (state == GS_H_CONNECTED) /* Already connected. */
384
NSLog(@"attempting connect on connected handle");
387
else if (state == GS_H_ACCEPT) /* Impossible. */
389
NSLog(@"attempting connect with accepting handle");
392
else /* Already connecting. */
394
NSLog(@"attempting connect while connecting");
401
if (recvPort == nil || aPort == nil)
403
NSLog(@"attempting connect with port(s) unset");
405
return NO; /* impossible. */
409
name = [aPort _name];
410
memset(&sockAddr, '\0', sizeof(sockAddr));
411
sockAddr.sun_family = AF_LOCAL;
412
strncpy(sockAddr.sun_path, name, sizeof(sockAddr.sun_path));
414
if (connect(desc, (struct sockaddr*)&sockAddr, SUN_LEN(&sockAddr)) < 0)
417
if (WSAGetLastError() != WSAEWOULDBLOCK)
419
if (errno != EINPROGRESS)
422
NSLog(@"unable to make connection to %s - %s",
424
GSLastErrorStr(errno));
431
l = [NSRunLoop currentRunLoop];
432
[l addEvent: (void*)(gsaddr)desc
435
forMode: NSConnectionReplyMode];
436
[l addEvent: (void*)(gsaddr)desc
439
forMode: NSConnectionReplyMode];
441
while (valid == YES && state == GS_H_TRYCON
442
&& [when timeIntervalSinceNow] > 0)
444
[l runMode: NSConnectionReplyMode beforeDate: when];
447
[l removeEvent: (void*)(gsaddr)desc
449
forMode: NSConnectionReplyMode
451
[l removeEvent: (void*)(gsaddr)desc
453
forMode: NSConnectionReplyMode
456
if (state == GS_H_TRYCON)
461
return NO; /* Timed out */
463
else if (state == GS_H_UNCON)
474
[aPort addHandle: self forSend: YES];
490
- (NSString*) description
492
return [NSString stringWithFormat: @"<GSMessageHandle %p (%d) to %s>",
493
self, desc, sockAddr.sun_path];
518
l = [runLoopClass currentRunLoop];
519
[l removeEvent: (void*)(gsaddr)desc
523
[l removeEvent: (void*)(gsaddr)desc
527
[l removeEvent: (void*)(gsaddr)desc
531
NSDebugMLLog(@"NSMessagePort", @"invalidated 0x%x", self);
532
[[self recvPort] removeHandle: self];
533
[[self sendPort] removeHandle: self];
544
- (NSMessagePort*) recvPort
549
return GS_GC_UNHIDE(recvPort);
552
- (void) receivedEvent: (void*)data
553
type: (RunLoopEventType)type
555
forMode: (NSString*)mode
557
NSDebugMLLog(@"NSMessagePort_details",
558
@"received %s event on 0x%x",
559
type == ET_RPORT ? "read" : "write", self);
561
* If we have been invalidated (desc < 0) then we should ignore this
562
* event and remove ourself from the runloop.
566
NSRunLoop *l = [runLoopClass currentRunLoop];
581
if (type == ET_RPORT)
588
* Make sure we have a buffer big enough to hold all the data we are
589
* expecting, or NETBLOCK bytes, whichever is greater.
593
rData = [[mutableDataClass alloc] initWithLength: NETBLOCK];
594
rWant = sizeof(GSPortItemHeader);
600
want = [rData length];
604
[rData setLength: want];
609
[rData setLength: want];
614
* Now try to fill the buffer with data.
616
bytes = [rData mutableBytes];
618
res = recv(desc, bytes + rLength, want - rLength, 0);
620
res = read(desc, bytes + rLength, want - rLength);
626
NSDebugMLLog(@"NSMessagePort", @"read eof on 0x%x", self);
631
else if (errno != EINTR && errno != EAGAIN)
633
NSDebugMLLog(@"NSMessagePort",
634
@"read failed - %s on 0x%x", GSLastErrorStr(errno), self);
639
res = 0; /* Interrupted - continue */
641
NSDebugMLLog(@"NSMessagePort_details",
642
@"read %d bytes on 0x%x", res, self);
645
while (valid == YES && rLength >= rWant)
647
BOOL shouldDispatch = NO;
657
* We have read an item header - set up to read the
658
* remainder of the item.
660
h = (GSPortItemHeader*)bytes;
661
rType = GSSwapBigI32ToHost(h->type);
662
l = GSSwapBigI32ToHost(h->length);
663
if (rType == GSP_PORT)
667
NSLog(@"%@ - unreasonable length (%u) for port",
674
* For a port, we leave the item header in the data
675
* so that our decode function can check length info.
679
else if (rType == GSP_DATA)
686
* For a zero-length data chunk, we create an empty
687
* data object and add it to the current message.
689
rType = GSP_NONE; /* ready for a new item */
693
memmove(bytes, bytes + rWant, rLength);
695
rWant = sizeof(GSPortItemHeader);
696
d = [mutableDataClass new];
697
[rItems addObject: d];
699
if (nItems == [rItems count])
701
shouldDispatch = YES;
706
if (l > maxDataLength)
708
NSLog(@"%@ - unreasonable length (%u) for data",
715
* If not a port or zero length data,
716
* we discard the data read so far and fill the
717
* data object with the data item from the msg.
722
memmove(bytes, bytes + rWant, rLength);
727
else if (rType == GSP_HEAD)
729
if (l > maxDataLength)
731
NSLog(@"%@ - unreasonable length (%u) for data",
738
* If not a port or zero length data,
739
* we discard the data read so far and fill the
740
* data object with the data item from the msg.
745
memmove(bytes, bytes + rWant, rLength);
751
NSLog(@"%@ - bad data received on port handle, rType=%i",
764
rType = GSP_NONE; /* ready for a new item */
766
* We have read a message header - set up to read the
767
* remainder of the message.
769
h = (GSPortMsgHeader*)bytes;
770
rId = GSSwapBigI32ToHost(h->mId);
771
nItems = GSSwapBigI32ToHost(h->nItems);
772
NSAssert(nItems >0, NSInternalInconsistencyException);
774
= [mutableArrayClass allocWithZone: NSDefaultMallocZone()];
775
rItems = [rItems initWithCapacity: nItems];
776
if (rWant > sizeof(GSPortMsgHeader))
781
* The first data item of the message was included in
782
* the header - so add it to the rItems array.
784
rWant -= sizeof(GSPortMsgHeader);
785
d = [mutableDataClass alloc];
786
d = [d initWithBytes: bytes + sizeof(GSPortMsgHeader)
788
[rItems addObject: d];
790
rWant += sizeof(GSPortMsgHeader);
794
memmove(bytes, bytes + rWant, rLength);
796
rWant = sizeof(GSPortItemHeader);
799
shouldDispatch = YES;
805
* want to read another item
810
memmove(bytes, bytes + rWant, rLength);
812
rWant = sizeof(GSPortItemHeader);
821
rType = GSP_NONE; /* ready for a new item */
822
d = [mutableDataClass allocWithZone: NSDefaultMallocZone()];
823
d = [d initWithBytes: bytes length: rWant];
824
[rItems addObject: d];
829
memmove(bytes, bytes + rWant, rLength);
831
rWant = sizeof(GSPortItemHeader);
832
if (nItems == [rItems count])
834
shouldDispatch = YES;
843
rType = GSP_NONE; /* ready for a new item */
844
p = decodePort(rData);
847
NSLog(@"%@ - unable to decode remote port", self);
853
* Set up to read another item header.
858
memmove(bytes, bytes + rWant, rLength);
860
rWant = sizeof(GSPortItemHeader);
862
if (state == GS_H_ACCEPT)
865
* This is the initial port information on a new
866
* connection - set up port relationships.
868
state = GS_H_CONNECTED;
869
[p addHandle: self forSend: YES];
874
* This is a port within a port message - add
875
* it to the message components.
877
[rItems addObject: p];
878
if (nItems == [rItems count])
880
shouldDispatch = YES;
887
if (shouldDispatch == YES)
890
NSMessagePort *rp = [self recvPort];
892
pm = [portMessageClass allocWithZone: NSDefaultMallocZone()];
893
pm = [pm initWithSendPort: [self sendPort]
899
NSDebugMLLog(@"NSMessagePort_details",
900
@"got message %@ on 0x%x", pm, self);
905
[rp handlePortMessage: pm];
912
[localException raise];
918
bytes = [rData mutableBytes];
924
if (state == GS_H_TRYCON) /* Connection attempt. */
927
int len = sizeof(res);
929
if (getsockopt(desc, SOL_SOCKET, SO_ERROR, (char*)&res, &len) == 0
933
NSLog(@"connect attempt failed - %s", GSLastErrorStr(res));
937
NSData *d = newDataWithEncodedPort([self recvPort]);
940
len = send(desc, [d bytes], [d length], 0);
942
len = write(desc, [d bytes], [d length]);
944
if (len == (int)[d length])
946
NSDebugMLLog(@"NSMessagePort_details",
947
@"wrote %d bytes on 0x%x", len, self);
948
state = GS_H_CONNECTED;
953
NSLog(@"connect write attempt failed - %s",
954
GSLastErrorStr(errno));
967
if ([wMsgs count] > 0)
969
NSArray *components = [wMsgs objectAtIndex: 0];
971
wData = [components objectAtIndex: wItem++];
976
// NSLog(@"No messages to write on 0x%x.", self);
984
res = send(desc, b + wLength, l - wLength, 0);
986
res = write(desc, b + wLength, l - wLength);
990
if (errno != EINTR && errno != EAGAIN)
992
NSLog(@"write attempt failed - %s", GSLastErrorStr(errno));
1000
NSDebugMLLog(@"NSMessagePort_details",
1001
@"wrote %d bytes on 0x%x", res, self);
1005
NSArray *components;
1008
* We have completed a data item so see what is
1009
* left of the message components.
1011
components = [wMsgs objectAtIndex: 0];
1013
if ([components count] > wItem)
1016
* More to write - get next item.
1018
wData = [components objectAtIndex: wItem++];
1023
* message completed - remove from list.
1025
NSDebugMLLog(@"NSMessagePort_details",
1026
@"completed 0x%x on 0x%x", components, self);
1029
[wMsgs removeObjectAtIndex: 0];
1039
- (BOOL) sendMessage: (NSArray*)components beforeDate: (NSDate*)when
1044
NSAssert([components count] > 0, NSInternalInconsistencyException);
1045
NSDebugMLLog(@"NSMessagePort_details",
1046
@"Sending message 0x%x %@ on 0x%x(%d) before %@",
1047
components, components, self, desc, when);
1049
[wMsgs addObject: components];
1051
l = [runLoopClass currentRunLoop];
1055
[l addEvent: (void*)(gsaddr)desc
1058
forMode: NSConnectionReplyMode];
1059
[l addEvent: (void*)(gsaddr)desc
1062
forMode: NSConnectionReplyMode];
1065
&& [wMsgs indexOfObjectIdenticalTo: components] != NSNotFound
1066
&& [when timeIntervalSinceNow] > 0)
1069
[l runMode: NSConnectionReplyMode beforeDate: when];
1073
[l removeEvent: (void*)(gsaddr)desc
1075
forMode: NSConnectionReplyMode
1077
[l removeEvent: (void*)(gsaddr)desc
1079
forMode: NSConnectionReplyMode
1082
if ([wMsgs indexOfObjectIdenticalTo: components] == NSNotFound)
1088
NSDebugMLLog(@"NSMessagePort_details",
1089
@"Message send 0x%x on 0x%x status %d", components, self, sent);
1093
- (NSMessagePort*) sendPort
1095
if (sendPort == nil)
1097
else if (caller == YES)
1098
return GS_GC_UNHIDE(sendPort); // We called, so port is not retained.
1100
return sendPort; // Retained port.
1103
- (void) setState: (GSHandleState)s
1108
- (GSHandleState) state
1113
- (NSDate*) timedOutEvent: (void*)data
1114
type: (RunLoopEventType)type
1115
forMode: (NSString*)mode
1124
@interface NSMessagePort (RunLoop) <RunLoopEvents>
1125
- (void) receivedEvent: (void*)data
1126
type: (RunLoopEventType)type
1128
forMode: (NSString*)mode;
1129
- (NSDate*) timedOutEvent: (void*)data
1130
type: (RunLoopEventType)type
1131
forMode: (NSString*)mode;
1135
@implementation NSMessagePort
1137
static NSRecursiveLock *messagePortLock = nil;
1140
Maps NSData objects with the socket name to NSMessagePort objects.
1142
static NSMapTable *messagePortMap = 0;
1143
static Class messagePortClass;
1146
static void clean_up_sockets(void)
1148
NSMessagePort *port;
1150
NSMapEnumerator mEnum;
1151
BOOL unknownThread = GSRegisterCurrentThread();
1152
CREATE_AUTORELEASE_POOL(arp);
1154
mEnum = NSEnumerateMapTable(messagePortMap);
1155
while (NSNextMapEnumeratorPair(&mEnum, (void *)&name, (void *)&port))
1157
if ([port _listener] != -1)
1158
unlink([name bytes]);
1160
NSEndMapTableEnumeration(&mEnum);
1162
if (unknownThread == YES)
1164
GSUnregisterCurrentThread();
1169
#if NEED_WORD_ALIGNMENT
1170
static unsigned wordAlign;
1175
if (self == [NSMessagePort class])
1177
#if NEED_WORD_ALIGNMENT
1178
wordAlign = objc_alignof_type(@encode(gsu32));
1180
messagePortClass = self;
1181
messagePortMap = NSCreateMapTable(NSNonRetainedObjectMapKeyCallBacks,
1182
NSNonOwnedPointerMapValueCallBacks, 0);
1184
messagePortLock = [GSLazyRecursiveLock new];
1185
atexit(clean_up_sockets);
1191
static int unique_index = 0;
1193
NSNumber *p = [NSNumber numberWithInt: 0700];
1196
attr = [NSDictionary dictionaryWithObject: p
1197
forKey: NSFilePosixPermissions];
1199
path = NSTemporaryDirectory();
1201
path = [path stringByAppendingPathComponent: @"NSMessagePort"];
1202
[[NSFileManager defaultManager] createDirectoryAtPath: path
1205
path = [path stringByAppendingPathComponent: @"ports"];
1206
[[NSFileManager defaultManager] createDirectoryAtPath: path
1209
M_LOCK(messagePortLock);
1210
path = [path stringByAppendingPathComponent:
1211
[NSString stringWithFormat: @"%i.%i",
1212
[[NSProcessInfo processInfo] processIdentifier], unique_index++]];
1213
M_UNLOCK(messagePortLock);
1215
return RETAIN([self _portWithName: [path fileSystemRepresentation]
1220
* This is the preferred initialisation method for NSMessagePort
1222
* 'socketName' is the name of the socket in the port directory
1224
+ (NSMessagePort*) _portWithName: (const unsigned char *)socketName
1225
listener: (BOOL)shouldListen
1228
NSMessagePort *port = nil;
1231
theName = [[NSData alloc] initWithBytes: socketName
1232
length: strlen(socketName)+1];
1234
M_LOCK(messagePortLock);
1237
* First try to find a pre-existing port.
1239
port = (NSMessagePort*)NSMapGet(messagePortMap, theName);
1243
port = (NSMessagePort*)NSAllocateObject(self, 0, NSDefaultMallocZone());
1244
port->name = theName;
1245
port->listener = -1;
1246
port->handles = NSCreateMapTable(NSIntMapKeyCallBacks,
1247
NSObjectMapValueCallBacks, 0);
1248
port->myLock = [GSLazyRecursiveLock new];
1249
port->_is_valid = YES;
1251
if (shouldListen == YES)
1254
struct sockaddr_un sockaddr;
1257
* Creating a new port on the local host - so we must create a
1258
* listener socket to accept incoming connections.
1260
memset(&sockaddr, '\0', sizeof(sockaddr));
1261
sockaddr.sun_family = AF_LOCAL;
1262
strncpy(sockaddr.sun_path, socketName, sizeof(sockaddr.sun_path));
1265
* Need size of buffer for getsockbyname() later.
1267
i = sizeof(sockaddr);
1269
if ((desc = socket(PF_LOCAL, SOCK_STREAM, PF_UNSPEC)) < 0)
1271
NSLog(@"unable to create socket - %s", GSLastErrorStr(errno));
1274
else if (bind(desc, (struct sockaddr *)&sockaddr,
1275
sizeof(sockaddr)) < 0)
1277
NSLog(@"unable to bind to %s - %s",
1278
sockaddr.sun_path, GSLastErrorStr(errno));
1282
else if (listen(desc, 5) < 0)
1284
NSLog(@"unable to listen on port - %s", GSLastErrorStr(errno));
1288
else if (getsockname(desc, (struct sockaddr*)&sockaddr, &i) < 0)
1290
NSLog(@"unable to get socket name - %s", GSLastErrorStr(errno));
1297
* Set up the listening descriptor and the actual message port
1298
* number (which will have been set to a real port number when
1299
* we did the 'bind' call.
1301
port->listener = desc;
1303
* Make sure we have the map table for this port.
1305
NSMapInsert(messagePortMap, (void*)theName, (void*)port);
1306
NSDebugMLLog(@"NSMessagePort", @"Created listening port: %@",
1313
* Make sure we have the map table for this port.
1315
NSMapInsert(messagePortMap, (void*)theName, (void*)port);
1316
NSDebugMLLog(@"NSMessagePort", @"Created speaking port: %@", port);
1323
NSDebugMLLog(@"NSMessagePort", @"Using pre-existing port: %@", port);
1325
IF_NO_GC(AUTORELEASE(port));
1327
M_UNLOCK(messagePortLock);
1331
- (void) addHandle: (GSMessageHandle*)handle forSend: (BOOL)send
1336
if (handle->caller == YES)
1337
handle->sendPort = GS_GC_HIDE(self);
1339
ASSIGN(handle->sendPort, self);
1343
handle->recvPort = GS_GC_HIDE(self);
1345
NSMapInsert(handles, (void*)(gsaddr)[handle descriptor], (void*)handle);
1349
- (id) copyWithZone: (NSZone*)zone
1351
return RETAIN(self);
1361
- (NSString*) description
1365
desc = [NSString stringWithFormat: @"<NSMessagePort %p with name %s>",
1366
self, [name bytes]];
1372
NSDebugMLLog(@"NSMessagePort", @"NSMessagePort 0x%x finalized", self);
1377
* This is a callback method used by the NSRunLoop class to determine which
1378
* descriptors to watch for the port.
1380
- (void) getFds: (int*)fds count: (int*)count
1384
GSMessageHandle *handle;
1390
* Make sure there is enough room in the provided array.
1392
NSAssert(*count > (int)NSCountMapTable(handles),
1393
NSInternalInconsistencyException);
1396
* Put in our listening socket.
1401
fds[(*count)++] = listener;
1405
* Enumerate all our socket handles, and put them in as long as they
1406
* are to be used for receiving.
1408
recvSelf = GS_GC_HIDE(self);
1409
me = NSEnumerateMapTable(handles);
1410
while (NSNextMapEnumeratorPair(&me, (void*)&sock, (void*)&handle))
1412
if (handle->recvPort == recvSelf)
1414
fds[(*count)++] = sock;
1417
NSEndMapTableEnumeration(&me);
1421
- (id) conversation: (NSPort*)recvPort
1425
GSMessageHandle *handle = nil;
1429
* Enumerate all our socket handles, and look for one with port.
1431
me = NSEnumerateMapTable(handles);
1432
while (NSNextMapEnumeratorPair(&me, (void*)&sock, (void*)&handle))
1434
if ([handle recvPort] == recvPort)
1437
NSEndMapTableEnumeration(&me);
1439
return AUTORELEASE(handle);
1442
NSEndMapTableEnumeration(&me);
1447
- (GSMessageHandle*) handleForPort: (NSMessagePort*)recvPort
1448
beforeDate: (NSDate*)when
1452
#ifndef BROKEN_SO_REUSEADDR
1455
GSMessageHandle *handle = nil;
1459
* Enumerate all our socket handles, and look for one with port.
1461
me = NSEnumerateMapTable(handles);
1462
while (NSNextMapEnumeratorPair(&me, (void*)&sock, (void*)&handle))
1464
if ([handle recvPort] == recvPort)
1467
NSEndMapTableEnumeration(&me);
1471
NSEndMapTableEnumeration(&me);
1474
* Not found ... create a new handle.
1477
if ((sock = socket(PF_LOCAL, SOCK_STREAM, PF_UNSPEC)) < 0)
1479
NSLog(@"unable to create socket - %s", GSLastErrorStr(errno));
1481
#ifndef BROKEN_SO_REUSEADDR
1483
* Under decent systems, SO_REUSEADDR means that the port can be reused
1484
* immediately that this process exits. Under some it means
1485
* that multiple processes can serve the same port simultaneously.
1486
* We don't want that broken behavior!
1488
else if (setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, (char*)&opt,
1492
NSLog(@"unable to set reuse on socket - %s", GSLastErrorStr(errno));
1495
else if ((handle = [GSMessageHandle handleWithDescriptor: sock]) == nil)
1498
NSLog(@"unable to create GSMessageHandle - %s", GSLastErrorStr(errno));
1502
[recvPort addHandle: handle forSend: NO];
1506
* If we succeeded in creating a new handle - connect to remote host.
1510
if ([handle connectToPort: self beforeDate: when] == NO)
1512
[handle invalidate];
1519
- (void) handlePortMessage: (NSPortMessage*)m
1521
id d = [self delegate];
1525
NSDebugMLLog(@"NSMessagePort",
1526
@"No delegate to handle incoming message", 0);
1529
if ([d respondsToSelector: @selector(handlePortMessage:)] == NO)
1531
NSDebugMLLog(@"NSMessagePort", @"delegate doesn't handle messages", 0);
1534
[d handlePortMessage: m];
1545
self = [messagePortClass new];
1551
if ([self isValid] == YES)
1555
if ([self isValid] == YES)
1557
NSArray *handleArray;
1560
M_LOCK(messagePortLock);
1563
(void) close(listener);
1564
unlink([name bytes]);
1567
NSMapRemove(messagePortMap, (void*)name);
1568
M_UNLOCK(messagePortLock);
1572
handleArray = NSAllMapTableValues(handles);
1573
i = [handleArray count];
1576
GSMessageHandle *handle;
1578
handle = [handleArray objectAtIndex: i];
1579
[handle invalidate];
1582
* We permit mutual recursive invalidation, so the handles map
1583
* may already have been destroyed.
1587
NSFreeMapTable(handles);
1591
[[NSMessagePortNameServer sharedInstance] removePort: self];
1598
- (BOOL) isEqual: (id)anObject
1600
if (anObject == self)
1604
if ([anObject class] == [self class])
1606
NSMessagePort *o = (NSMessagePort*)anObject;
1608
return [o->name isEqual: name];
1613
- (void) receivedEvent: (void*)data
1614
type: (RunLoopEventType)type
1616
forMode: (NSString*)mode
1618
int desc = (int)(gsaddr)extra;
1619
GSMessageHandle *handle;
1621
if (desc == listener)
1623
struct sockaddr_un sockAddr;
1624
int size = sizeof(sockAddr);
1626
desc = accept(listener, (struct sockaddr*)&sockAddr, &size);
1629
NSDebugMLLog(@"NSMessagePort",
1630
@"accept failed - handled in other thread?");
1635
* Create a handle for the socket and set it up so we are its
1636
* receiving port, and it's waiting to get the port name from
1639
handle = [GSMessageHandle handleWithDescriptor: desc];
1640
memcpy(&handle->sockAddr, &sockAddr, sizeof(sockAddr));
1642
[handle setState: GS_H_ACCEPT];
1643
[self addHandle: handle forSend: NO];
1649
handle = (GSMessageHandle*)NSMapGet(handles, (void*)(gsaddr)desc);
1650
IF_NO_GC(AUTORELEASE(RETAIN(handle)));
1656
if (type == ET_RDESC) t = "rdesc";
1657
else if (type == ET_WDESC) t = "wdesc";
1658
else if (type == ET_EDESC) t = "edesc";
1659
else if (type == ET_RPORT) t = "rport";
1661
NSLog(@"No handle for event %s on descriptor %d", t, desc);
1662
[[runLoopClass currentRunLoop] removeEvent: extra
1669
[handle receivedEvent: data type: type extra: extra forMode: mode];
1674
- (void) removeHandle: (GSMessageHandle*)handle
1677
if ([handle sendPort] == self)
1679
if (handle->caller != YES)
1682
* This is a handle for a send port, and the handle was not formed
1683
* by calling the remote process, so this port object must have
1684
* been created to deal with an incoming connection and will have
1685
* been retained - we must therefore release this port since the
1686
* handle no longer uses it.
1690
handle->sendPort = nil;
1692
if ([handle recvPort] == self)
1694
handle->recvPort = nil;
1696
NSMapRemove(handles, (void*)(gsaddr)[handle descriptor]);
1697
if (listener < 0 && NSCountMapTable(handles) == 0)
1705
* This returns the amount of space that a port coder should reserve at the
1706
* start of its encoded data so that the NSMessagePort can insert header info
1708
* The idea is that a message consisting of a single data item with space at
1709
* the start can be written directly without having to copy data to another
1712
- (unsigned int) reservedSpaceLength
1714
return sizeof(GSPortItemHeader) + sizeof(GSPortMsgHeader);
1717
- (BOOL) sendBeforeDate: (NSDate*)when
1719
components: (NSMutableArray*)components
1720
from: (NSPort*)receivingPort
1721
reserved: (unsigned)length
1727
if ([self isValid] == NO)
1731
if ([components count] == 0)
1733
NSLog(@"empty components sent");
1737
* If the reserved length in the first data object is wrong - we have to
1738
* fail, unless it's zero, in which case we can insert a data object for
1741
rl = [self reservedSpaceLength];
1742
if (length != 0 && length != rl)
1744
NSLog(@"bad reserved length - %u", length);
1747
if ([receivingPort isKindOfClass: messagePortClass] == NO)
1749
NSLog(@"woah there - receiving port is not the correct type");
1753
h = [self handleForPort: (NSMessagePort*)receivingPort beforeDate: when];
1756
NSMutableData *header;
1759
GSPortItemHeader *pih;
1760
GSPortMsgHeader *pmh;
1761
unsigned c = [components count];
1766
* Ok - ensure we have space to insert header info.
1768
if (length == 0 && rl != 0)
1770
header = [[mutableDataClass alloc] initWithCapacity: NETBLOCK];
1772
[header setLength: rl];
1773
[components insertObject: header atIndex: 0];
1777
header = [components objectAtIndex: 0];
1779
* The Item header contains the item type and the length of the
1780
* data in the item (excluding the item header itself).
1782
hLength = [header length];
1783
l = hLength - sizeof(GSPortItemHeader);
1784
pih = (GSPortItemHeader*)[header mutableBytes];
1785
pih->type = GSSwapHostI32ToBig(GSP_HEAD);
1786
pih->length = GSSwapHostI32ToBig(l);
1789
* The message header contains the message Id and the original count
1790
* of components in the message (excluding any extra component added
1791
* simply to hold the header).
1793
pmh = (GSPortMsgHeader*)&pih[1];
1794
pmh->mId = GSSwapHostI32ToBig(msgId);
1795
pmh->nItems = GSSwapHostI32ToBig(c);
1798
* Now insert item header information as required.
1799
* Pack as many items into the initial data object as possible, up to
1800
* a maximum of NETBLOCK bytes. This is to try to get a single,
1801
* efficient write operation if possible.
1803
for (i = 1; i < c; i++)
1805
id o = [components objectAtIndex: i];
1807
if ([o isKindOfClass: [NSData class]])
1809
GSPortItemHeader *pih;
1810
unsigned h = sizeof(GSPortItemHeader);
1811
unsigned l = [o length];
1814
if (pack == YES && hLength + l + h <= NETBLOCK)
1816
[header setLength: hLength + l + h];
1817
b = [header mutableBytes];
1819
#if NEED_WORD_ALIGNMENT
1821
* When packing data, an item may not be aligned on a
1822
* word boundary, so we work with an aligned buffer
1825
if ((hLength % wordAlign) != 0)
1827
GSPortItemHeader itemHeader;
1829
pih = (GSPortItemHeader*)&itemHeader;
1830
pih->type = GSSwapHostI32ToBig(GSP_DATA);
1831
pih->length = GSSwapHostI32ToBig(l);
1832
memcpy(b, (void*)pih, h);
1836
pih = (GSPortItemHeader*)b;
1837
pih->type = GSSwapHostI32ToBig(GSP_DATA);
1838
pih->length = GSSwapHostI32ToBig(l);
1841
pih = (GSPortItemHeader*)b;
1842
pih->type = GSSwapHostI32ToBig(GSP_DATA);
1843
pih->length = GSSwapHostI32ToBig(l);
1845
memcpy(b+h, [o bytes], l);
1846
[components removeObjectAtIndex: i--];
1855
d = [[NSMutableData alloc] initWithLength: l + h];
1856
b = [d mutableBytes];
1857
pih = (GSPortItemHeader*)b;
1858
memcpy(b+h, [o bytes], l);
1859
pih->type = GSSwapHostI32ToBig(GSP_DATA);
1860
pih->length = GSSwapHostI32ToBig(l);
1861
[components replaceObjectAtIndex: i
1866
else if ([o isKindOfClass: messagePortClass])
1868
NSData *d = newDataWithEncodedPort(o);
1869
unsigned dLength = [d length];
1871
if (pack == YES && hLength + dLength <= NETBLOCK)
1875
[header setLength: hLength + dLength];
1876
b = [header mutableBytes];
1879
memcpy(b, [d bytes], dLength);
1880
[components removeObjectAtIndex: i--];
1886
[components replaceObjectAtIndex: i withObject: d];
1893
* Now send the message.
1895
sent = [h sendMessage: components beforeDate: when];
1900
- (NSDate*) timedOutEvent: (void*)data
1901
type: (RunLoopEventType)type
1902
forMode: (NSString*)mode
1908
-(const unsigned char *) _name
1910
return [name bytes];