// NetFSServer.cpp #include #include #include #include #include #ifdef HAIKU_TARGET_PLATFORM_BEOS # include #else # include # include # include #endif #include #include #include #include #include #include #include #include #include "Connection.h" #include "ConnectionListener.h" #include "DebugSupport.h" #include "DriverSettings.h" #include "FDManager.h" #include "InsecureChannel.h" #include "NetFSDefs.h" #include "NetFSServer.h" #include "NetFSServerRosterDefs.h" #include "RequestChannel.h" #include "Requests.h" #include "SecurityContext.h" #include "StatisticsManager.h" #include "TaskManager.h" #include "Utils.h" #include "VolumeManager.h" static const char* kSettingsDirName = "netfs"; static const char* kSettingsFileName = "netfs_server"; static const char* kFallbackSettingsFileName = "netfs_server_fallback"; // usage static const char* kUsage = "Usage: netfs_server \n" "options:\n" " --dont-broadcast - don't use broadcasting to announce the server's\n" " availability to clients\n" " -h, --help - print this text\n" ; // ConnectionInitializer class NetFSServer::ConnectionInitializer { public: ConnectionInitializer(NetFSServer* server, ConnectionListener* connectionListener, Connection* connection) : fServer(server), fConnectionListener(connectionListener), fConnection(connection), fThread(-1) { } ~ConnectionInitializer() { delete fConnection; } status_t Run() { fThread = spawn_thread(&_ThreadEntry, "connection initializer", B_NORMAL_PRIORITY, this); if (fThread < 0) return fThread; resume_thread(fThread); return B_OK; } private: static int32 _ThreadEntry(void* data) { return ((ConnectionInitializer*)data)->_Thread(); } int32 _Thread() { // finish connection initialization User* user = NULL; status_t error = fConnectionListener->FinishInitialization( fConnection, fServer->GetSecurityContext(), &user); // create a client connection ClientConnection* clientConnection = NULL; if (error == B_OK) { clientConnection = new(std::nothrow) ClientConnection(fConnection, fServer->GetSecurityContext(), user, fServer); if (!clientConnection) error = B_NO_MEMORY; } if (error == B_OK) { fConnection = NULL; // connection belongs to client connection now error = clientConnection->Init(); } // add the client connection to the server if (error == B_OK) error = fServer->_AddClientConnection(clientConnection); // cleanup on error if (error != B_OK) delete clientConnection; delete this; return 0; } private: NetFSServer* fServer; ConnectionListener* fConnectionListener; Connection* fConnection; thread_id fThread; }; // ServerInfoSender class NetFSServer::ServerInfoSender : public Task { public: ServerInfoSender(int socket, const ServerInfo& serverInfo) : Task("server info sender"), fChannel(new(std::nothrow) InsecureChannel(socket)), fServerInfo(serverInfo) { if (!fChannel) closesocket(socket); } ~ServerInfoSender() { delete fChannel; } status_t Init() { if (!fChannel) return B_NO_MEMORY; return B_OK; } virtual void Stop() { if (fChannel) fChannel->Close(); } virtual status_t Execute() { if (!fChannel) { SetDone(true); return B_NO_INIT; } RequestChannel requestChannel(fChannel); // create the server info request ServerInfoRequest request; request.serverInfo = fServerInfo; // send the request status_t error = requestChannel.SendRequest(&request); if (error != B_OK) { ERROR("ServerInfoSender: ERROR: Failed to send request: %s\n", strerror(error)); } SetDone(true); return B_OK; } private: Channel* fChannel; ServerInfo fServerInfo; }; // NetFSServer // constructor NetFSServer::NetFSServer(bool useBroadcasting) : BApplication(kNetFSServerSignature), fSecurityContext(NULL), fConnectionListenerFactory(), fConnectionListener(NULL), fLock("netfs server"), fClientConnections(), fVolumeManager(NULL), fClosedConnections(), fClosedConnectionsSemaphore(-1), fConnectionListenerThread(-1), fConnectionDeleter(-1), fBroadcaster(-1), fBroadcastingSocket(-1), fBroadcasterSemaphore(-1), fServerInfoConnectionListener(-1), fServerInfoConnectionListenerSocket(-1), fServerInfoUpdated(0), fUseBroadcasting(useBroadcasting), fTerminating(false) { } // destructor NetFSServer::~NetFSServer() { fTerminating = true; // stop the connection listener if (fConnectionListener) fConnectionListener->StopListening(); if (fConnectionListenerThread >= 0) { int32 result; wait_for_thread(fConnectionListenerThread, &result); } delete fConnectionListener; // delete the broadcaster semaphore if (fBroadcasterSemaphore >= 0) delete_sem(fBroadcasterSemaphore); // terminate the broadcaster if (fBroadcaster >= 0) { safe_closesocket(fBroadcastingSocket); // interrupt the thread in case it is currently snoozing suspend_thread(fBroadcaster); int32 result; wait_for_thread(fBroadcaster, &result); } // terminate the server info connection listener _ExitServerInfoConnectionListener(); // terminate the connection deleter if (fClosedConnectionsSemaphore >= 0) delete_sem(fClosedConnectionsSemaphore); if (fConnectionDeleter >= 0) { int32 result; wait_for_thread(fConnectionDeleter, &result); } // blow away all remaining connections AutoLocker _(fLock); // open connections for (int32 i = 0; ClientConnection* connection = (ClientConnection*)fClientConnections.ItemAt(i); i++) { connection->Close(); delete connection; } // closed connections for (int32 i = 0; ClientConnection* connection = (ClientConnection*)fClosedConnections.ItemAt(i); i++) { delete connection; } VolumeManager::DeleteDefault(); FDManager::DeleteDefault(); delete fSecurityContext; } // Init status_t NetFSServer::Init() { // init the settings status_t error = _InitSettings(); if (error != B_OK) return error; // create the FD manager error = FDManager::CreateDefault(); if (error != B_OK) return error; // create the volume manager error = VolumeManager::CreateDefault(); if (error != B_OK) return error; fVolumeManager = VolumeManager::GetDefault(); // create a connection listener // error = fConnectionListenerFactory.CreateConnectionListener( // "port", NULL, &fConnectionListener); error = fConnectionListenerFactory.CreateConnectionListener( "insecure", NULL, &fConnectionListener); if (error != B_OK) return error; // spawn the connection listener thread fConnectionListenerThread = spawn_thread(&_ConnectionListenerEntry, "connection listener", B_NORMAL_PRIORITY, this); if (fConnectionListenerThread < 0) return fConnectionListenerThread; // create the closed connections semaphore fClosedConnectionsSemaphore = create_sem(0, "closed connections"); if (fClosedConnectionsSemaphore < 0) return fClosedConnectionsSemaphore; // spawn the connection deleter fConnectionDeleter = spawn_thread(&_ConnectionDeleterEntry, "connection deleter", B_NORMAL_PRIORITY, this); if (fConnectionDeleter < 0) return fConnectionDeleter; // init the server info connection listener error = _InitServerInfoConnectionListener(); if (error != B_OK) return error; // create the broadcaster semaphore fBroadcasterSemaphore = create_sem(0, "broadcaster snooze"); // spawn the broadcaster if (fUseBroadcasting) { fBroadcaster = spawn_thread(&_BroadcasterEntry, "broadcaster", B_NORMAL_PRIORITY, this); if (fBroadcaster < 0) { WARN("NetFSServer::Init(): Failed to spawn broadcaster thread " "(%s). Continuing anyway.\n", strerror(fBroadcaster)); } } return B_OK; } // Run thread_id NetFSServer::Run() { // start the connection listener resume_thread(fConnectionListenerThread); // start the connection deleter resume_thread(fConnectionDeleter); // start the server info connection listener resume_thread(fServerInfoConnectionListener); // start the broadcaster resume_thread(fBroadcaster); return BApplication::Run(); } // MessageReceived void NetFSServer::MessageReceived(BMessage* message) { switch (message->what) { case NETFS_REQUEST_GET_MESSENGER: { // for the time being we process all requests here BMessage reply; reply.AddMessenger("messenger", be_app_messenger); _SendReply(message, &reply); break; } case NETFS_REQUEST_ADD_USER: { // get user name and password const char* user; const char* password; if (message->FindString("user", &user) != B_OK) { _SendReply(message, B_BAD_VALUE); break; } if (message->FindString("password", &password) != B_OK) password = NULL; // add the user status_t error = fSecurityContext->AddUser(user, password); _SendReply(message, error); break; } case NETFS_REQUEST_REMOVE_USER: { // get user name const char* userName; if (message->FindString("user", &userName) != B_OK) { _SendReply(message, B_BAD_VALUE); break; } // remove the user User* user; status_t error = fSecurityContext->RemoveUser(userName, &user); if (error == B_OK) { // propagate the information to the client connections AutoLocker _(fLock); for (int32 i = 0; ClientConnection* connection = (ClientConnection*)fClientConnections.ItemAt(i); i++) { connection->UserRemoved(user); } user->ReleaseReference(); } _SendReply(message, error); break; } case NETFS_REQUEST_GET_USERS: { // get the users BMessage reply; BMessage users; status_t error = fSecurityContext->GetUsers(&users); if (error == B_OK) error = reply.AddMessage("users", &users); if (error == B_OK) _SendReply(message, &reply); else _SendReply(message, error); break; } case NETFS_REQUEST_GET_USER_STATISTICS: { // get user name const char* userName; if (message->FindString("user", &userName) != B_OK) { _SendReply(message, B_BAD_VALUE); break; } // get the user User* user = fSecurityContext->FindUser(userName); if (!user) { _SendReply(message, B_ENTRY_NOT_FOUND); break; } BReference userReference(user, true); // get the statistics BMessage statistics; status_t error = StatisticsManager::GetDefault() ->GetUserStatistics(user, &statistics); // prepare the reply BMessage reply; if (error == B_OK) error = reply.AddMessage("statistics", &statistics); // send the reply if (error == B_OK) _SendReply(message, &reply); else _SendReply(message, error); break; } case NETFS_REQUEST_ADD_SHARE: { // get share name and path const char* share; const char* path; if (message->FindString("share", &share) != B_OK || message->FindString("path", &path) != B_OK) { _SendReply(message, B_BAD_VALUE); break; } // add the share status_t error = fSecurityContext->AddShare(share, path); if (error == B_OK) _ServerInfoUpdated(); _SendReply(message, error); break; } case NETFS_REQUEST_REMOVE_SHARE: { // get share name const char* shareName; if (message->FindString("share", &shareName) != B_OK) { _SendReply(message, B_BAD_VALUE); break; } // remove the share Share* share; status_t error = fSecurityContext->RemoveShare(shareName, &share); if (error == B_OK) { // propagate the information to the client connections AutoLocker _(fLock); for (int32 i = 0; ClientConnection* connection = (ClientConnection*)fClientConnections.ItemAt(i); i++) { connection->ShareRemoved(share); } share->ReleaseReference(); } if (error == B_OK) _ServerInfoUpdated(); _SendReply(message, error); break; } case NETFS_REQUEST_GET_SHARES: { // get the shares BMessage reply; BMessage shares; status_t error = fSecurityContext->GetShares(&shares); if (error == B_OK) error = reply.AddMessage("shares", &shares); if (error == B_OK) _SendReply(message, &reply); else _SendReply(message, error); break; } case NETFS_REQUEST_GET_SHARE_USERS: { // get share name const char* shareName; if (message->FindString("share", &shareName) != B_OK) { _SendReply(message, B_BAD_VALUE); break; } AutoLocker securityContextLocker(fSecurityContext); // get the share Share* share = fSecurityContext->FindShare(shareName); if (!share) { _SendReply(message, B_ENTRY_NOT_FOUND); break; } BReference shareReference(share, true); // get all users BMessage allUsers; status_t error = fSecurityContext->GetUsers(&allUsers); if (error != B_OK) { _SendReply(message, error); break; } // filter the users with mount permission BMessage users; const char* userName; for (int32 i = 0; allUsers.FindString("users", i, &userName) == B_OK; i++) { if (User* user = fSecurityContext->FindUser(userName)) { // get the user's permissions Permissions permissions = fSecurityContext ->GetNodePermissions(share->GetPath(), user); user->ReleaseReference(); // add the user, if they have the permission to mount the // share if (permissions.ImpliesMountSharePermission()) { error = users.AddString("users", userName); if (error != B_OK) { _SendReply(message, error); break; } } } } securityContextLocker.Unlock(); // prepare the reply BMessage reply; if (error == B_OK) error = reply.AddMessage("users", &users); // send the reply if (error == B_OK) _SendReply(message, &reply); else _SendReply(message, error); break; } case NETFS_REQUEST_GET_SHARE_STATISTICS: { // get share name const char* shareName; if (message->FindString("share", &shareName) != B_OK) { _SendReply(message, B_BAD_VALUE); break; } // get the share Share* share = fSecurityContext->FindShare(shareName); if (!share) { _SendReply(message, B_ENTRY_NOT_FOUND); break; } BReference shareReference(share, true); // get the statistics BMessage statistics; status_t error = StatisticsManager::GetDefault() ->GetShareStatistics(share, &statistics); // prepare the reply BMessage reply; if (error == B_OK) error = reply.AddMessage("statistics", &statistics); // send the reply if (error == B_OK) _SendReply(message, &reply); else _SendReply(message, error); break; } case NETFS_REQUEST_SET_USER_PERMISSIONS: { // get share and user name, and the permissions const char* shareName; const char* userName; uint32 permissions; if (message->FindString("share", &shareName) != B_OK || message->FindString("user", &userName) != B_OK || message->FindInt32("permissions", (int32*)&permissions) != B_OK) { _SendReply(message, B_BAD_VALUE); break; } // get the share and the user Share* share = fSecurityContext->FindShare(shareName); User* user = fSecurityContext->FindUser(userName); BReference shareReference(share); BReference userReference(user); if (!share || !user) { _SendReply(message, B_ENTRY_NOT_FOUND); break; } // set the permissions status_t error = B_OK; if (permissions == 0) { fSecurityContext->ClearNodePermissions(share->GetPath(), user); } else { error = fSecurityContext->SetNodePermissions(share->GetPath(), user, permissions); } if (error == B_OK) { // propagate the information to the client connections AutoLocker _(fLock); for (int32 i = 0; ClientConnection* connection = (ClientConnection*)fClientConnections.ItemAt(i); i++) { connection->UserPermissionsChanged(share, user, permissions); } } _SendReply(message, error); break; } case NETFS_REQUEST_GET_USER_PERMISSIONS: { // get share and user name const char* shareName; const char* userName; if (message->FindString("share", &shareName) != B_OK || message->FindString("user", &userName) != B_OK) { _SendReply(message, B_BAD_VALUE); break; } // get the share and the user Share* share = fSecurityContext->FindShare(shareName); User* user = fSecurityContext->FindUser(userName); BReference shareReference(share); BReference userReference(user); if (!share || !user) { _SendReply(message, B_ENTRY_NOT_FOUND); break; } // get the permissions Permissions permissions = fSecurityContext->GetNodePermissions( share->GetPath(), user); // prepare the reply BMessage reply; status_t error = reply.AddInt32("permissions", (int32)permissions.GetPermissions()); // send it if (error == B_OK) _SendReply(message, &reply); else _SendReply(message, error); break; } case NETFS_REQUEST_SAVE_SETTINGS: { status_t error = _SaveSettings(); // send a reply _SendReply(message, error); break; } } } // GetVolumeManager VolumeManager* NetFSServer::GetVolumeManager() const { return fVolumeManager; } // GetSecurityContext SecurityContext* NetFSServer::GetSecurityContext() const { return fSecurityContext; } // _AddClientConnection status_t NetFSServer::_AddClientConnection(ClientConnection* clientConnection) { if (!clientConnection) return B_BAD_VALUE; AutoLocker locker(fLock); if (!fClientConnections.AddItem(clientConnection)) return B_NO_MEMORY; return B_OK; } // ClientConnectionClosed void NetFSServer::ClientConnectionClosed(ClientConnection* connection, bool broken) { PRINT("NetFSServer::ClientConnectionClosed(%d)\n", broken); if (!connection) return; AutoLocker locker(fLock); if (!fClientConnections.RemoveItem(connection)) return; if (!fClosedConnections.AddItem(connection)) { // out of memory: Try to delete the connection right now., // There's a certain chance that we'll access free()d memory in the // process, but things are apparently bad enough, anyway. locker.Unlock(); delete connection; return; } release_sem(fClosedConnectionsSemaphore); } // _LoadSecurityContext status_t NetFSServer::_LoadSecurityContext(SecurityContext** _securityContext) { // create a security context SecurityContext* securityContext = new(std::nothrow) SecurityContext; if (!securityContext) return B_NO_MEMORY; status_t error = securityContext->InitCheck(); if (error != B_OK) { delete securityContext; return error; } ObjectDeleter securityContextDeleter(securityContext); // load the fallback settings, if present BPath path; DriverSettings settings; if (_GetSettingsDirPath(&path, false) == B_OK && path.Append(kFallbackSettingsFileName) == B_OK && settings.Load(path.Path()) == B_OK) { // load users DriverParameter parameter; for (DriverParameterIterator it = settings.GetParameterIterator("user"); it.GetNext(¶meter);) { const char* userName = parameter.ValueAt(0); const char* password = parameter.GetParameterValue("password"); if (!userName) { WARN("Skipping nameless user settings entry.\n"); continue; } // PRINT(("user: %s, password: %s\n", parameter.ValueAt(0), // parameter.GetParameterValue("password"))); error = securityContext->AddUser(userName, password); if (error != B_OK) ERROR("ERROR: Failed to add user `%s'\n", userName); } // load shares for (DriverParameterIterator it = settings.GetParameterIterator("share"); it.GetNext(¶meter);) { const char* shareName = parameter.ValueAt(0); const char* path = parameter.GetParameterValue("path"); if (!shareName || !path) { WARN("settings: Skipping invalid share settings entry (no name" " or no path).\n"); continue; } // PRINT(("share: %s, path: %s\n", parameter.ValueAt(0), // parameter.GetParameterValue("path"))); Share* share; error = securityContext->AddShare(shareName, path, &share); if (error != B_OK) { ERROR("ERROR: Failed to add share `%s'\n", shareName); continue; } BReference shareReference(share, true); DriverParameter userParameter; // iterate through the share users for (DriverParameterIterator userIt = parameter.GetParameterIterator("user"); userIt.GetNext(&userParameter);) { const char* userName = userParameter.ValueAt(0); // PRINT((" user: %s\n", userName)); User* user = securityContext->FindUser(userName); if (!user) { ERROR("ERROR: Undefined user `%s'.\n", userName); continue; } BReference userReference(user, true); DriverParameter permissionsParameter; if (!userParameter.FindParameter("permissions", &permissionsParameter)) { continue; } Permissions permissions; for (int32 i = 0; i < permissionsParameter.CountValues(); i++) { const char* permission = permissionsParameter.ValueAt(i); // PRINT((" permission: %s\n", permission)); if (strcmp(permission, "mount") == 0) { permissions.AddPermissions(MOUNT_SHARE_PERMISSION); } else if (strcmp(permission, "query") == 0) { permissions.AddPermissions(QUERY_SHARE_PERMISSION); } else if (strcmp(permission, "read") == 0) { permissions.AddPermissions(READ_PERMISSION | READ_DIR_PERMISSION | RESOLVE_DIR_ENTRY_PERMISSION); } else if (strcmp(permission, "write") == 0) { permissions.AddPermissions(WRITE_PERMISSION | WRITE_DIR_PERMISSION); } else if (strcmp(permission, "all") == 0) { permissions.AddPermissions(ALL_PERMISSIONS); } } error = securityContext->SetNodePermissions(share->GetPath(), user, permissions); if (error != B_OK) { ERROR("ERROR: Failed to set permissions for share `%s'\n", share->GetName()); } } } } securityContextDeleter.Detach(); *_securityContext = securityContext; return B_OK; } // _InitSettings status_t NetFSServer::_InitSettings() { status_t error = _LoadSettings(); if (error != B_OK) { WARN("NetFSServer::_InitSettings(): WARNING: Failed to load settings " "file: %s - falling back to driver settings.\n", strerror(error)); // fall back to the driver settings file error = _LoadSecurityContext(&fSecurityContext); if (error != B_OK) { WARN("NetFSServer::_InitSettings(): WARNING: Failed to load " "settings from driver settings: %s\n", strerror(error)); // use defaults // create a security context fSecurityContext = new(std::nothrow) SecurityContext; if (!fSecurityContext) return B_NO_MEMORY; error = fSecurityContext->InitCheck(); if (error != B_OK) return error; } } return B_OK; } // _LoadSettings status_t NetFSServer::_LoadSettings() { // get the settings file path BPath filePath; status_t error = _GetSettingsFilePath(&filePath, false); if (error != B_OK) RETURN_ERROR(error); // if existing load the settings BEntry bEntry; if (FDManager::SetEntry(&bEntry, filePath.Path()) != B_OK || !bEntry.Exists()) { return B_ENTRY_NOT_FOUND; } // open the settings file BFile file; error = FDManager::SetFile(&file, filePath.Path(), B_READ_ONLY); if (error != B_OK) RETURN_ERROR(error); // read the settings BMessage settings; error = settings.Unflatten(&file); if (error != B_OK) RETURN_ERROR(error); // get the security context archive BMessage securityContextArchive; error = settings.FindMessage("security context", &securityContextArchive); if (error != B_OK) RETURN_ERROR(error); // create a security context SecurityContext* securityContext = new(std::nothrow) SecurityContext(&securityContextArchive); if (!securityContext) RETURN_ERROR(B_NO_MEMORY); ObjectDeleter securityContextDeleter(securityContext); error = securityContext->InitCheck(); if (error != B_OK) RETURN_ERROR(error); // set it delete fSecurityContext; fSecurityContext = securityContext; securityContextDeleter.Detach(); return B_OK; } // _SaveSettings status_t NetFSServer::_SaveSettings() { AutoLocker locker(fSecurityContext); // create the settings archive BMessage settings; // archive the security context BMessage securityContextArchive; status_t error = fSecurityContext->Archive(&securityContextArchive, true); if (error != B_OK) RETURN_ERROR(error); // add it to the settings archive error = settings.AddMessage("security context", &securityContextArchive); if (error != B_OK) RETURN_ERROR(error); // open the settings file BPath filePath; error = _GetSettingsFilePath(&filePath, true); if (error != B_OK) RETURN_ERROR(error); BFile file; error = FDManager::SetFile(&file, filePath.Path(), B_READ_WRITE | B_CREATE_FILE | B_ERASE_FILE); if (error != B_OK) RETURN_ERROR(error); // write to the settings file error = settings.Flatten(&file); if (error != B_OK) RETURN_ERROR(error); return B_OK; } // _GetSettingsDirPath status_t NetFSServer::_GetSettingsDirPath(BPath* path, bool create) { // get the user settings directory BPath settingsDir; status_t error = find_directory(B_USER_SETTINGS_DIRECTORY, &settingsDir, create); if (error != B_OK) RETURN_ERROR(error); // create our subdir, if not existing and desired error = path->SetTo(settingsDir.Path(), kSettingsDirName); if (error != B_OK) RETURN_ERROR(error); BEntry bEntry; if (create && (FDManager::SetEntry(&bEntry, settingsDir.Path()) != B_OK || !bEntry.Exists())) { error = create_directory(path->Path(), S_IRWXU | S_IRWXG | S_IRWXO); if (error != B_OK) RETURN_ERROR(error); } return B_OK; } // _GetSettingsFilePath status_t NetFSServer::_GetSettingsFilePath(BPath* path, bool createDir) { // get settings dir BPath dirPath; status_t error = _GetSettingsDirPath(&dirPath, createDir); if (error != B_OK) return error; // construct the file path return path->SetTo(dirPath.Path(), kSettingsFileName); } // _InitServerInfoConnectionListener status_t NetFSServer::_InitServerInfoConnectionListener() { // spawn the listener thread fServerInfoConnectionListener = spawn_thread( &_ServerInfoConnectionListenerEntry, "server info connection listener", B_NORMAL_PRIORITY, this); if (fServerInfoConnectionListener < 0) return fServerInfoConnectionListener; // create a listener socket fServerInfoConnectionListenerSocket = socket(AF_INET, SOCK_STREAM, 0); if (fServerInfoConnectionListenerSocket < 0) return errno; // bind it to the port sockaddr_in addr; addr.sin_family = AF_INET; addr.sin_port = htons(kDefaultServerInfoPort); addr.sin_addr.s_addr = INADDR_ANY; if (bind(fServerInfoConnectionListenerSocket, (sockaddr*)&addr, sizeof(addr)) < 0) { return errno; } // start listening if (listen(fServerInfoConnectionListenerSocket, 5) < 0) return errno; return B_OK; } // _ExitServerInfoConnectionListener void NetFSServer::_ExitServerInfoConnectionListener() { // close the socket safe_closesocket(fServerInfoConnectionListenerSocket); // wait for the listener if (fServerInfoConnectionListener >= 0) { int32 result; wait_for_thread(fServerInfoConnectionListener, &result); } } // _ConnectionListenerEntry int32 NetFSServer::_ConnectionListenerEntry(void* data) { return ((NetFSServer*)data)->_ConnectionListener(); } // _ConnectionListener int32 NetFSServer::_ConnectionListener() { // start listening for connections Connection* connection = NULL; status_t error = B_OK; do { error = fConnectionListener->Listen(&connection); if (error == B_OK) { ConnectionInitializer* initializer = new(std::nothrow) ConnectionInitializer(this, fConnectionListener, connection); if (initializer) { if (initializer->Run() != B_OK) { ERROR("Failed to run connection initializer.\n") delete initializer; } } else { ERROR("Failed to create connection initializer.\n") delete connection; } } } while (error == B_OK && !fTerminating); return 0; } // _ConnectionDeleterEntry int32 NetFSServer::_ConnectionDeleterEntry(void* data) { return ((NetFSServer*)data)->_ConnectionDeleter(); } // _ConnectionDeleter int32 NetFSServer::_ConnectionDeleter() { while (!fTerminating) { status_t error = acquire_sem(fClosedConnectionsSemaphore); ClientConnection* connection = NULL; if (error == B_OK) { AutoLocker _(fLock); connection = (ClientConnection*)fClosedConnections.RemoveItem((int32)0); } if (connection) delete connection; } return 0; } // _BroadcasterEntry int32 NetFSServer::_BroadcasterEntry(void* data) { return ((NetFSServer*)data)->_Broadcaster(); } // _Broadcaster int32 NetFSServer::_Broadcaster() { // create the socket fBroadcastingSocket = socket(AF_INET, SOCK_DGRAM, 0); if (fBroadcastingSocket < 0) { WARN("NetFSServer::_Broadcaster(): WARN: Failed to init broadcasting: " "%s.\n", strerror(errno)); return errno; } // set the socket broadcast option #ifndef HAIKU_TARGET_PLATFORM_BEOS int soBroadcastValue = 1; if (setsockopt(fBroadcastingSocket, SOL_SOCKET, SO_BROADCAST, &soBroadcastValue, sizeof(soBroadcastValue)) < 0) { WARN("NetFSServer::_Broadcaster(): WARN: Failed to set " "SO_BROADCAST on socket: %s.\n", strerror(errno)); } #endif // prepare the broadcast message BroadcastMessage message; message.magic = B_HOST_TO_BENDIAN_INT32(BROADCAST_MESSAGE_MAGIC); message.protocolVersion = B_HOST_TO_BENDIAN_INT32(NETFS_PROTOCOL_VERSION); bool update = false; while (!fTerminating) { // set tick/update uint32 messageCode = (update ? BROADCAST_MESSAGE_SERVER_UPDATE : BROADCAST_MESSAGE_SERVER_TICK); message.message = B_HOST_TO_BENDIAN_INT32(messageCode); // send broadcasting message sockaddr_in addr; addr.sin_family = AF_INET; addr.sin_port = htons(kDefaultBroadcastPort); addr.sin_addr.s_addr = INADDR_BROADCAST; int addrSize = sizeof(addr); ssize_t bytesSent = sendto(fBroadcastingSocket, &message, sizeof(message), 0, (sockaddr*)&addr, addrSize); if (bytesSent < 0) { WARN("NetFSServer::_Broadcaster(): WARN: sending failed: %s.\n", strerror(errno)); return errno; } // snooze a bit // we snooze a minimal interval to avoid shooting updates like a // machine gun snooze(kMinBroadcastingInterval); bigtime_t remainingTime = kBroadcastingInterval - kMinBroadcastingInterval; // snooze the rest blocking on our semaphore (if it exists) if (fBroadcasterSemaphore >= 0) { status_t snoozeError = acquire_sem_etc(fBroadcasterSemaphore, 1, B_RELATIVE_TIMEOUT, remainingTime); // set the semaphore count back to zero while (snoozeError == B_OK) { snoozeError = acquire_sem_etc(fBroadcasterSemaphore, 1, B_RELATIVE_TIMEOUT, 0); } } else snooze(remainingTime); update = atomic_and(&fServerInfoUpdated, 0); } // close the socket safe_closesocket(fBroadcastingSocket); return B_OK; } // _ServerInfoConnectionListenerEntry int32 NetFSServer::_ServerInfoConnectionListenerEntry(void* data) { return ((NetFSServer*)data)->_ServerInfoConnectionListener(); } // _ServerInfoConnectionListener int32 NetFSServer::_ServerInfoConnectionListener() { if (fServerInfoConnectionListenerSocket < 0) return B_BAD_VALUE; TaskManager taskManager; // accept a incoming connection while (!fTerminating) { int fd = -1; do { taskManager.RemoveDoneTasks(); fd = accept(fServerInfoConnectionListenerSocket, NULL, 0); if (fd < 0) { status_t error = errno; if (error != B_INTERRUPTED) return error; if (fTerminating) return B_OK; } } while (fd < 0); // get a fresh server info ServerInfo info; status_t error = _GetServerInfo(info); if (error != B_OK) { closesocket(fd); return error; } // create a server info sender thread ServerInfoSender* sender = new(std::nothrow) ServerInfoSender(fd, info); if (sender == NULL) { closesocket(fd); delete sender; return B_NO_MEMORY; } if ((error = sender->Init()) != B_OK) { closesocket(fd); delete sender; return error; } taskManager.RunTask(sender); } return B_OK; } // _GetServerInfo status_t NetFSServer::_GetServerInfo(ServerInfo& serverInfo) { // set the server name and the connection method char hostName[1024]; if (gethostname(hostName, sizeof(hostName)) < 0) { ERROR("NetFSServer::_GetServerInfo(): ERROR: Failed to get host " "name."); return B_ERROR; } status_t error = serverInfo.SetServerName(hostName); // TODO: Set the actually used connection method! if (error == B_OK) error = serverInfo.SetConnectionMethod("insecure"); if (error != B_OK) return error; // get the shares from the security context BMessage shares; error = fSecurityContext->GetShares(&shares); if (error != B_OK) return error; // add the shares const char* shareName; for (int32 i = 0; shares.FindString("shares", i, &shareName) == B_OK; i++) { error = serverInfo.AddShare(shareName); if (error != B_OK) return error; } return B_OK; } // _ServerInfoUpdated void NetFSServer::_ServerInfoUpdated() { atomic_or(&fServerInfoUpdated, 1); release_sem(fBroadcasterSemaphore); } // _SendReply void NetFSServer::_SendReply(BMessage* message, BMessage* reply, status_t error) { // no reply is specified, if no data have to be sent BMessage stackReply; if (!reply) reply = &stackReply; reply->AddInt32("error", error); message->SendReply(reply, (BHandler*)NULL, 0LL); } // _SendReply void NetFSServer::_SendReply(BMessage* message, status_t error) { _SendReply(message, NULL, error); } // #pragma mark - // print_usage static void print_usage(bool error) { fputs(kUsage, (error ? stderr : stdout)); } // main int main(int argc, char** argv) { #ifdef DEBUG_OBJECT_TRACKING ObjectTracker::InitDefault(); #endif // parse the arguments bool broadcast = true; for (int argi = 1; argi < argc; argi++) { const char* arg = argv[argi]; if (strcmp(arg, "--dont-broadcast") == 0) { broadcast = false; } else if (strcmp(arg, "--help") == 0 || strcmp(arg, "-h") == 0) { print_usage(false); return 0; } else { print_usage(true); return 1; } } // create the statistics manager status_t error = StatisticsManager::CreateDefault(); if (error != B_OK) { fprintf(stderr, "Failed to create statistics manager: %s\n", strerror(error)); return 1; } // init and run the server { NetFSServer server(broadcast); error = server.Init(); if (error != B_OK) { fprintf(stderr, "Failed to initialize server: %s\n", strerror(error)); return 1; } server.Run(); } // delete the statistics manager StatisticsManager::DeleteDefault(); #ifdef DEBUG_OBJECT_TRACKING ObjectTracker::ExitDefault(); #endif return 0; }