1// UserlandFSDispatcher.cpp 2 3#include <new> 4 5#include <Application.h> 6#include <Clipboard.h> 7#include <Locker.h> 8#include <Message.h> 9#include <Roster.h> 10 11#include "AutoDeleter.h" 12#include "AutoLocker.h" 13#include "Compatibility.h" 14#include "Debug.h" 15#include "DispatcherDefs.h" 16#include "FileSystem.h" 17#include "FSInfo.h" 18#include "RequestAllocator.h" 19#include "RequestPort.h" 20#include "Requests.h" 21#include "ServerDefs.h" 22#include "String.h" 23#include "UserlandFSDispatcher.h" 24 25// constructor 26UserlandFSDispatcher::UserlandFSDispatcher(const char* signature) 27 : BApplication(signature), 28 fTerminating(false), 29 fRequestProcessor(-1), 30 fConnectionPort(-1), 31 fConnectionReplyPort(-1), 32 fRequestLock(), 33 fRequestPort(NULL) 34{ 35} 36 37// destructor 38UserlandFSDispatcher::~UserlandFSDispatcher() 39{ 40 fTerminating = true; 41 // stop roster watching 42 be_roster->StopWatching(this); 43 // close/delete the ports 44 fRequestLock.Lock(); 45 if (fRequestPort) 46 fRequestPort->Close(); 47 fRequestLock.Unlock(); 48 if (fConnectionPort >= 0) 49 delete_port(fConnectionPort); 50 if (fConnectionReplyPort >= 0) 51 delete_port(fConnectionReplyPort); 52 // wait for the request processor 53 if (fRequestProcessor >= 0) { 54 int32 result; 55 wait_for_thread(fRequestProcessor, &result); 56 } 57} 58 59// Init 60status_t 61UserlandFSDispatcher::Init() 62{ 63 // ensure that we are the only dispatcher 64 BClipboard clipboard(kUserlandFSDispatcherClipboardName); 65 if (!clipboard.Lock()) { 66 ERROR(("Failed to lock the clipboard.\n")); 67 return B_ERROR; 68 } 69 status_t error = B_OK; 70 if (BMessage* data = clipboard.Data()) { 71 // check the old value in the clipboard 72 BMessenger messenger; 73 if (data->FindMessenger("messenger", &messenger) == B_OK) { 74 if (messenger.IsValid()) { 75 PRINT(("There's already a dispatcher running.\n")); 76 error = B_ERROR; 77 } 78 } 79 // clear the clipboard 80 if (error == B_OK) { 81 clipboard.Clear(); 82 data = clipboard.Data(); 83 if (!data) 84 error = B_ERROR; 85 } 86 // add our messenger 87 if (error == B_OK) { 88 SET_ERROR(error, data->AddMessenger("messenger", be_app_messenger)); 89 if (error == B_OK) 90 SET_ERROR(error, clipboard.Commit()); 91 // work-around for BeOS R5: The very first commit to a clipboard 92 // (i.e. the one that creates the clipboard) seems to be ignored. 93 if (error == B_OK) 94 SET_ERROR(error, clipboard.Commit()); 95 if (error != B_OK) 96 ERROR(("Failed to set clipboard messenger.\n")); 97 } 98 } else { 99 ERROR(("Failed to get clipboard data container\n")); 100 error = B_ERROR; 101 } 102 clipboard.Unlock(); 103 if (error != B_OK) 104 return error; 105 // create the connection port and connection reply port 106 fConnectionPort = create_port(1, kUserlandFSDispatcherPortName); 107 if (fConnectionPort < 0) 108 return fConnectionPort; 109 fConnectionReplyPort = create_port(1, kUserlandFSDispatcherReplyPortName); 110 if (fConnectionReplyPort < 0) 111 return fConnectionReplyPort; 112 // start watching for terminated applications 113 error = be_roster->StartWatching(this, B_REQUEST_QUIT); 114 if (error != B_OK) 115 return error ; 116 // spawn request processor thread 117 fRequestProcessor = spawn_thread(_RequestProcessorEntry, 118 "main request processor", B_NORMAL_PRIORITY, this); 119 if (fRequestProcessor < 0) 120 return fRequestProcessor; 121 resume_thread(fRequestProcessor); 122 return B_OK; 123} 124 125// MessageReceived 126void 127UserlandFSDispatcher::MessageReceived(BMessage* message) 128{ 129 switch (message->what) { 130 case UFS_REGISTER_FS: 131 { 132 // get the team 133 team_id team; 134 status_t error = message->FindInt32("team", &team); 135 if (error != B_OK) 136 PRINT(("UFS_REGISTER_FS failed: no team\n")); 137 // get the FS info 138 FSInfo* info = NULL; 139 if (error == B_OK) { 140 info = new(nothrow) FSInfo; 141 if (info) { 142 error = info->SetTo(message); 143 } else { 144 error = B_NO_MEMORY; 145 PRINT(("UFS_REGISTER_FS failed: failed to allocate " 146 "FSInfo\n")); 147 } 148 } 149 ObjectDeleter<FSInfo> infoDeleter(info); 150 // find the FileSystem 151 FileSystem* fileSystem = NULL; 152 if (error == B_OK) { 153 AutoLocker<FileSystemMap> _(fFileSystems); 154 fileSystem = _GetFileSystemNoInit(team); 155 if (fileSystem) { 156 fileSystem->CompleteInit(info); 157 infoDeleter.Detach(); 158 } else { 159 PRINT(("UFS_REGISTER_FS: no FileSystem found for " 160 "team %ld, trying to register anyway\n", team)); 161 // try to find by name 162 fileSystem = fFileSystems.Get(info->GetName()); 163 if (fileSystem) { 164 // there's already an FS with that name registered 165 PRINT(("UFS_REGISTER_FS failed: FileSystem with " 166 "name %s does already exist.\n", info->GetName())); 167 fileSystem = NULL; 168 error = B_ERROR; 169 } else { 170 // the FS is not known yet: create one 171 fileSystem = new FileSystem(team, info, &error); 172 if (fileSystem) { 173 infoDeleter.Detach(); 174 } else { 175 error = B_NO_MEMORY; 176 PRINT(("UFS_REGISTER_FS failed: failed to allocate " 177 "FileSystem\n")); 178 } 179 // add it 180 if (error == B_OK) { 181 error = fFileSystems.Put(info->GetName(), 182 fileSystem); 183 if (error != B_OK) { 184 PRINT(("UFS_REGISTER_FS failed: failed to " 185 "add FileSystem\n")); 186 delete fileSystem; 187 } 188 } 189 } 190 } 191 } 192 // send the reply 193 if (error == B_OK) 194 message->SendReply(UFS_REGISTER_FS_ACK); 195 else 196 message->SendReply(UFS_REGISTER_FS_DENIED); 197 if (fileSystem) 198 _PutFileSystem(fileSystem); 199 break; 200 } 201 case B_SOME_APP_QUIT: 202 { 203 // get the team 204 team_id team; 205 status_t error = message->FindInt32("be:team", &team); 206 if (error != B_OK) 207 return; 208 // find the FileSystem 209 FileSystem* fileSystem = _GetFileSystemNoInit(team); 210 if (!fileSystem) 211 return; 212 // abort the initialization 213 fileSystem->AbortInit(); 214 _PutFileSystem(fileSystem); 215 } 216 default: 217 BApplication::MessageReceived(message); 218 } 219} 220 221// _GetFileSystem 222status_t 223UserlandFSDispatcher::_GetFileSystem(const char* name, FileSystem** _fileSystem) 224{ 225 if (!name || !_fileSystem) 226 RETURN_ERROR(B_BAD_VALUE); 227 // get the file system 228 FileSystem* fileSystem; 229 { 230 AutoLocker<FileSystemMap> _(fFileSystems); 231 fileSystem = fFileSystems.Get(name); 232 if (fileSystem) { 233 fileSystem->AddReference(); 234 } else { 235 // doesn't exists yet: create 236 status_t error; 237 fileSystem = new(nothrow) FileSystem(name, &error); 238 if (!fileSystem) 239 RETURN_ERROR(B_NO_MEMORY); 240 if (error == B_OK) 241 error = fFileSystems.Put(fileSystem->GetName(), fileSystem); 242 if (error != B_OK) { 243 delete fileSystem; 244 RETURN_ERROR(error); 245 } 246 } 247 } 248 // prepare access 249 status_t error = fileSystem->Access(); 250 if (error != B_OK) { 251 _PutFileSystem(fileSystem); 252 RETURN_ERROR(error); 253 } 254 *_fileSystem = fileSystem; 255 return B_OK; 256} 257 258// _GetFileSystemNoInit 259FileSystem* 260UserlandFSDispatcher::_GetFileSystemNoInit(team_id team) 261{ 262 AutoLocker<FileSystemMap> _(fFileSystems); 263 for (FileSystemMap::Iterator it = fFileSystems.GetIterator(); 264 it.HasNext();) { 265 FileSystem* fileSystem = it.Next().value; 266 if (fileSystem->GetTeam() == team) { 267 // found it 268 if (fileSystem) 269 fileSystem->AddReference(); 270 return fileSystem; 271 } 272 } 273 return NULL; 274} 275 276// _PutFileSystem 277status_t 278UserlandFSDispatcher::_PutFileSystem(FileSystem* fileSystem) 279{ 280 if (!fileSystem) 281 RETURN_ERROR(B_BAD_VALUE); 282 AutoLocker<FileSystemMap> _(fFileSystems); 283 if (fFileSystems.Get(fileSystem->GetName()) != fileSystem) 284 RETURN_ERROR(B_BAD_VALUE); 285 if (fileSystem->RemoveReference() && fileSystem->InitCheck() != B_OK) { 286PRINT(("removing FileSystem `%s'\n", fileSystem->GetName())); 287 fFileSystems.Remove(fileSystem->GetName()); 288 delete fileSystem; 289 } 290 return B_OK; 291} 292 293// _WaitForConnection 294bool 295UserlandFSDispatcher::_WaitForConnection() 296{ 297 while (!fTerminating) { 298 int32 code; 299 char buffer; 300 size_t bytesRead = read_port(fConnectionPort, &code, &buffer, 0); 301 if (bytesRead >= 0 && code == UFS_DISPATCHER_CONNECT) { 302 const Port::Info* info = fRequestPort->GetPortInfo(); 303 size_t bytesWritten = write_port(fConnectionReplyPort, 304 UFS_DISPATCHER_CONNECT_ACK, info, sizeof(Port::Info)); 305 if (bytesWritten >= 0) 306 return true; 307 } 308 } 309 return false; 310} 311 312// _ProcessRequests 313status_t 314UserlandFSDispatcher::_ProcessRequests() 315{ 316 while (!fTerminating) { 317 Request* request; 318 status_t error = fRequestPort->ReceiveRequest(&request); 319 if (error != B_OK) 320 RETURN_ERROR(error); 321 RequestReleaser _(fRequestPort, request); 322 // check the request type 323 if (request->GetType() == UFS_DISCONNECT_REQUEST) 324 return B_OK; 325 if (request->GetType() != FS_CONNECT_REQUEST) 326 RETURN_ERROR(B_BAD_VALUE); 327 PRINT(("UserlandFSDispatcher::_ProcessRequests(): received FS connect " 328 "request\n")); 329 // it's an FS connect request 330 FSConnectRequest* connectRequest = (FSConnectRequest*)request; 331 // get the FS name 332 int32 len = connectRequest->fsName.GetSize(); 333 status_t result = B_OK; 334 if (len <= 0) 335 result = B_BAD_DATA; 336 String fsName; 337 if (result == B_OK) 338 fsName.SetTo((const char*)connectRequest->fsName.GetData(), len); 339 if (result == B_OK && fsName.GetLength() == 0) 340 result = B_BAD_DATA; 341 // prepare the reply 342 RequestAllocator allocator(fRequestPort->GetPort()); 343 FSConnectReply* reply; 344 error = AllocateRequest(allocator, &reply); 345 if (error != B_OK) 346 RETURN_ERROR(error); 347 FileSystem* fileSystem = NULL; 348 if (result == B_OK) 349 result = _GetFileSystem(fsName.GetString(), &fileSystem); 350 if (result == B_OK) { 351 const FSInfo* info = fileSystem->GetInfo(); 352 result = allocator.AllocateData(reply->portInfos, 353 info->GetInfos(), info->GetSize(), sizeof(Port::Info)); 354 if (result == B_OK) 355 reply->portInfoCount = info->CountInfos(); 356 _PutFileSystem(fileSystem); 357 } 358 reply->error = result; 359 // send it 360 error = fRequestPort->SendRequest(&allocator); 361 if (error != B_OK) 362 RETURN_ERROR(error); 363 } 364 return B_OK; 365} 366 367// _RequestProcessorEntry 368int32 369UserlandFSDispatcher::_RequestProcessorEntry(void* data) 370{ 371 return ((UserlandFSDispatcher*)data)->_RequestProcessor(); 372} 373 374// _RequestProcessor 375int32 376UserlandFSDispatcher::_RequestProcessor() 377{ 378PRINT(("UserlandFSDispatcher::_RequestProcessor()\n")); 379 while (!fTerminating) { 380 // allocate a request port 381 status_t error = B_OK; 382 { 383 fRequestLock.Lock(); 384 fRequestPort = new(nothrow) RequestPort(kRequestPortSize); 385 if (fRequestPort) 386 error = fRequestPort->InitCheck(); 387 else 388 error = B_NO_MEMORY; 389 if (error != B_OK) { 390 delete fRequestPort; 391 fRequestPort = NULL; 392 } 393 fRequestLock.Unlock(); 394 } 395 if (error != B_OK) { 396 be_app->PostMessage(B_QUIT_REQUESTED); 397PRINT((" failed to allocate request port: %s\n", strerror(error))); 398 return error; 399 } 400 // wait for a connection and process the requests 401 if (_WaitForConnection()) { 402 PRINT(("UserlandFSDispatcher::_RequestProcessor(): connected\n")); 403 _ProcessRequests(); 404 PRINT(("UserlandFSDispatcher::_RequestProcessor(): " 405 "disconnected\n")); 406 } 407 // delete the request port 408 fRequestLock.Lock(); 409 delete fRequestPort; 410 fRequestPort = NULL; 411 fRequestLock.Unlock(); 412 } 413PRINT(("UserlandFSDispatcher::_RequestProcessor() done\n")); 414 return B_OK; 415} 416 417