#include <errno.h>
#include <netdb.h>
#include <new>
#include <stdio.h>
#include <string.h>
#ifdef HAIKU_TARGET_PLATFORM_BEOS
# include <socket.h>
#else
# include <unistd.h>
# include <netinet/in.h>
# include <sys/socket.h>
#endif
#include <AutoDeleter.h>
#include <AutoLocker.h>
#include <Directory.h>
#include <File.h>
#include <FindDirectory.h>
#include <Node.h>
#include <Path.h>
#include <util/DoublyLinkedList.h>
#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";
static const char* kUsage =
"Usage: netfs_server <options>\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"
;
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()
{
User* user = NULL;
status_t error = fConnectionListener->FinishInitialization(
fConnection, fServer->GetSecurityContext(), &user);
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;
error = clientConnection->Init();
}
if (error == B_OK)
error = fServer->_AddClientConnection(clientConnection);
if (error != B_OK)
delete clientConnection;
delete this;
return 0;
}
private:
NetFSServer* fServer;
ConnectionListener* fConnectionListener;
Connection* fConnection;
thread_id fThread;
};
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);
ServerInfoRequest request;
request.serverInfo = fServerInfo;
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::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)
{
}
NetFSServer::~NetFSServer()
{
fTerminating = true;
if (fConnectionListener)
fConnectionListener->StopListening();
if (fConnectionListenerThread >= 0) {
int32 result;
wait_for_thread(fConnectionListenerThread, &result);
}
delete fConnectionListener;
if (fBroadcasterSemaphore >= 0)
delete_sem(fBroadcasterSemaphore);
if (fBroadcaster >= 0) {
safe_closesocket(fBroadcastingSocket);
suspend_thread(fBroadcaster);
int32 result;
wait_for_thread(fBroadcaster, &result);
}
_ExitServerInfoConnectionListener();
if (fClosedConnectionsSemaphore >= 0)
delete_sem(fClosedConnectionsSemaphore);
if (fConnectionDeleter >= 0) {
int32 result;
wait_for_thread(fConnectionDeleter, &result);
}
AutoLocker<Locker> _(fLock);
for (int32 i = 0;
ClientConnection* connection
= (ClientConnection*)fClientConnections.ItemAt(i);
i++) {
connection->Close();
delete connection;
}
for (int32 i = 0;
ClientConnection* connection
= (ClientConnection*)fClosedConnections.ItemAt(i);
i++) {
delete connection;
}
VolumeManager::DeleteDefault();
FDManager::DeleteDefault();
delete fSecurityContext;
}
status_t
NetFSServer::Init()
{
status_t error = _InitSettings();
if (error != B_OK)
return error;
error = FDManager::CreateDefault();
if (error != B_OK)
return error;
error = VolumeManager::CreateDefault();
if (error != B_OK)
return error;
fVolumeManager = VolumeManager::GetDefault();
error = fConnectionListenerFactory.CreateConnectionListener(
"insecure", NULL, &fConnectionListener);
if (error != B_OK)
return error;
fConnectionListenerThread = spawn_thread(&_ConnectionListenerEntry,
"connection listener", B_NORMAL_PRIORITY, this);
if (fConnectionListenerThread < 0)
return fConnectionListenerThread;
fClosedConnectionsSemaphore = create_sem(0, "closed connections");
if (fClosedConnectionsSemaphore < 0)
return fClosedConnectionsSemaphore;
fConnectionDeleter = spawn_thread(&_ConnectionDeleterEntry,
"connection deleter", B_NORMAL_PRIORITY, this);
if (fConnectionDeleter < 0)
return fConnectionDeleter;
error = _InitServerInfoConnectionListener();
if (error != B_OK)
return error;
fBroadcasterSemaphore = create_sem(0, "broadcaster snooze");
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;
}
thread_id
NetFSServer::Run()
{
resume_thread(fConnectionListenerThread);
resume_thread(fConnectionDeleter);
resume_thread(fServerInfoConnectionListener);
resume_thread(fBroadcaster);
return BApplication::Run();
}
void
NetFSServer::MessageReceived(BMessage* message)
{
switch (message->what) {
case NETFS_REQUEST_GET_MESSENGER:
{
BMessage reply;
reply.AddMessenger("messenger", be_app_messenger);
_SendReply(message, &reply);
break;
}
case NETFS_REQUEST_ADD_USER:
{
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;
status_t error = fSecurityContext->AddUser(user, password);
_SendReply(message, error);
break;
}
case NETFS_REQUEST_REMOVE_USER:
{
const char* userName;
if (message->FindString("user", &userName) != B_OK) {
_SendReply(message, B_BAD_VALUE);
break;
}
User* user;
status_t error = fSecurityContext->RemoveUser(userName, &user);
if (error == B_OK) {
AutoLocker<Locker> _(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:
{
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:
{
const char* userName;
if (message->FindString("user", &userName) != B_OK) {
_SendReply(message, B_BAD_VALUE);
break;
}
User* user = fSecurityContext->FindUser(userName);
if (!user) {
_SendReply(message, B_ENTRY_NOT_FOUND);
break;
}
BReference<User> userReference(user, true);
BMessage statistics;
status_t error = StatisticsManager::GetDefault()
->GetUserStatistics(user, &statistics);
BMessage reply;
if (error == B_OK)
error = reply.AddMessage("statistics", &statistics);
if (error == B_OK)
_SendReply(message, &reply);
else
_SendReply(message, error);
break;
}
case NETFS_REQUEST_ADD_SHARE:
{
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;
}
status_t error = fSecurityContext->AddShare(share, path);
if (error == B_OK)
_ServerInfoUpdated();
_SendReply(message, error);
break;
}
case NETFS_REQUEST_REMOVE_SHARE:
{
const char* shareName;
if (message->FindString("share", &shareName) != B_OK) {
_SendReply(message, B_BAD_VALUE);
break;
}
Share* share;
status_t error = fSecurityContext->RemoveShare(shareName, &share);
if (error == B_OK) {
AutoLocker<Locker> _(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:
{
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:
{
const char* shareName;
if (message->FindString("share", &shareName) != B_OK) {
_SendReply(message, B_BAD_VALUE);
break;
}
AutoLocker<Locker> securityContextLocker(fSecurityContext);
Share* share = fSecurityContext->FindShare(shareName);
if (!share) {
_SendReply(message, B_ENTRY_NOT_FOUND);
break;
}
BReference<Share> shareReference(share, true);
BMessage allUsers;
status_t error = fSecurityContext->GetUsers(&allUsers);
if (error != B_OK) {
_SendReply(message, error);
break;
}
BMessage users;
const char* userName;
for (int32 i = 0;
allUsers.FindString("users", i, &userName) == B_OK;
i++) {
if (User* user = fSecurityContext->FindUser(userName)) {
Permissions permissions = fSecurityContext
->GetNodePermissions(share->GetPath(), user);
user->ReleaseReference();
if (permissions.ImpliesMountSharePermission()) {
error = users.AddString("users", userName);
if (error != B_OK) {
_SendReply(message, error);
break;
}
}
}
}
securityContextLocker.Unlock();
BMessage reply;
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_SHARE_STATISTICS:
{
const char* shareName;
if (message->FindString("share", &shareName) != B_OK) {
_SendReply(message, B_BAD_VALUE);
break;
}
Share* share = fSecurityContext->FindShare(shareName);
if (!share) {
_SendReply(message, B_ENTRY_NOT_FOUND);
break;
}
BReference<Share> shareReference(share, true);
BMessage statistics;
status_t error = StatisticsManager::GetDefault()
->GetShareStatistics(share, &statistics);
BMessage reply;
if (error == B_OK)
error = reply.AddMessage("statistics", &statistics);
if (error == B_OK)
_SendReply(message, &reply);
else
_SendReply(message, error);
break;
}
case NETFS_REQUEST_SET_USER_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;
}
Share* share = fSecurityContext->FindShare(shareName);
User* user = fSecurityContext->FindUser(userName);
BReference<Share> shareReference(share);
BReference<User> userReference(user);
if (!share || !user) {
_SendReply(message, B_ENTRY_NOT_FOUND);
break;
}
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) {
AutoLocker<Locker> _(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:
{
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;
}
Share* share = fSecurityContext->FindShare(shareName);
User* user = fSecurityContext->FindUser(userName);
BReference<Share> shareReference(share);
BReference<User> userReference(user);
if (!share || !user) {
_SendReply(message, B_ENTRY_NOT_FOUND);
break;
}
Permissions permissions = fSecurityContext->GetNodePermissions(
share->GetPath(), user);
BMessage reply;
status_t error = reply.AddInt32("permissions",
(int32)permissions.GetPermissions());
if (error == B_OK)
_SendReply(message, &reply);
else
_SendReply(message, error);
break;
}
case NETFS_REQUEST_SAVE_SETTINGS:
{
status_t error = _SaveSettings();
_SendReply(message, error);
break;
}
}
}
VolumeManager*
NetFSServer::GetVolumeManager() const
{
return fVolumeManager;
}
SecurityContext*
NetFSServer::GetSecurityContext() const
{
return fSecurityContext;
}
status_t
NetFSServer::_AddClientConnection(ClientConnection* clientConnection)
{
if (!clientConnection)
return B_BAD_VALUE;
AutoLocker<Locker> locker(fLock);
if (!fClientConnections.AddItem(clientConnection))
return B_NO_MEMORY;
return B_OK;
}
void
NetFSServer::ClientConnectionClosed(ClientConnection* connection, bool broken)
{
PRINT("NetFSServer::ClientConnectionClosed(%d)\n", broken);
if (!connection)
return;
AutoLocker<Locker> locker(fLock);
if (!fClientConnections.RemoveItem(connection))
return;
if (!fClosedConnections.AddItem(connection)) {
locker.Unlock();
delete connection;
return;
}
release_sem(fClosedConnectionsSemaphore);
}
status_t
NetFSServer::_LoadSecurityContext(SecurityContext** _securityContext)
{
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<SecurityContext> securityContextDeleter(securityContext);
BPath path;
DriverSettings settings;
if (_GetSettingsDirPath(&path, false) == B_OK
&& path.Append(kFallbackSettingsFileName) == B_OK
&& settings.Load(path.Path()) == B_OK) {
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;
}
error = securityContext->AddUser(userName, password);
if (error != B_OK)
ERROR("ERROR: Failed to add user `%s'\n", userName);
}
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;
}
Share* share;
error = securityContext->AddShare(shareName, path, &share);
if (error != B_OK) {
ERROR("ERROR: Failed to add share `%s'\n", shareName);
continue;
}
BReference<Share> shareReference(share, true);
DriverParameter userParameter;
for (DriverParameterIterator userIt
= parameter.GetParameterIterator("user");
userIt.GetNext(&userParameter);) {
const char* userName = userParameter.ValueAt(0);
User* user = securityContext->FindUser(userName);
if (!user) {
ERROR("ERROR: Undefined user `%s'.\n", userName);
continue;
}
BReference<User> 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);
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;
}
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));
error = _LoadSecurityContext(&fSecurityContext);
if (error != B_OK) {
WARN("NetFSServer::_InitSettings(): WARNING: Failed to load "
"settings from driver settings: %s\n", strerror(error));
fSecurityContext = new(std::nothrow) SecurityContext;
if (!fSecurityContext)
return B_NO_MEMORY;
error = fSecurityContext->InitCheck();
if (error != B_OK)
return error;
}
}
return B_OK;
}
status_t
NetFSServer::_LoadSettings()
{
BPath filePath;
status_t error = _GetSettingsFilePath(&filePath, false);
if (error != B_OK)
RETURN_ERROR(error);
BEntry bEntry;
if (FDManager::SetEntry(&bEntry, filePath.Path()) != B_OK
|| !bEntry.Exists()) {
return B_ENTRY_NOT_FOUND;
}
BFile file;
error = FDManager::SetFile(&file, filePath.Path(), B_READ_ONLY);
if (error != B_OK)
RETURN_ERROR(error);
BMessage settings;
error = settings.Unflatten(&file);
if (error != B_OK)
RETURN_ERROR(error);
BMessage securityContextArchive;
error = settings.FindMessage("security context",
&securityContextArchive);
if (error != B_OK)
RETURN_ERROR(error);
SecurityContext* securityContext
= new(std::nothrow) SecurityContext(&securityContextArchive);
if (!securityContext)
RETURN_ERROR(B_NO_MEMORY);
ObjectDeleter<SecurityContext> securityContextDeleter(securityContext);
error = securityContext->InitCheck();
if (error != B_OK)
RETURN_ERROR(error);
delete fSecurityContext;
fSecurityContext = securityContext;
securityContextDeleter.Detach();
return B_OK;
}
status_t
NetFSServer::_SaveSettings()
{
AutoLocker<Locker> locker(fSecurityContext);
BMessage settings;
BMessage securityContextArchive;
status_t error = fSecurityContext->Archive(&securityContextArchive, true);
if (error != B_OK)
RETURN_ERROR(error);
error = settings.AddMessage("security context", &securityContextArchive);
if (error != B_OK)
RETURN_ERROR(error);
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);
error = settings.Flatten(&file);
if (error != B_OK)
RETURN_ERROR(error);
return B_OK;
}
status_t
NetFSServer::_GetSettingsDirPath(BPath* path, bool create)
{
BPath settingsDir;
status_t error = find_directory(B_USER_SETTINGS_DIRECTORY, &settingsDir,
create);
if (error != B_OK)
RETURN_ERROR(error);
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;
}
status_t
NetFSServer::_GetSettingsFilePath(BPath* path, bool createDir)
{
BPath dirPath;
status_t error = _GetSettingsDirPath(&dirPath, createDir);
if (error != B_OK)
return error;
return path->SetTo(dirPath.Path(), kSettingsFileName);
}
status_t
NetFSServer::_InitServerInfoConnectionListener()
{
fServerInfoConnectionListener = spawn_thread(
&_ServerInfoConnectionListenerEntry,
"server info connection listener", B_NORMAL_PRIORITY, this);
if (fServerInfoConnectionListener < 0)
return fServerInfoConnectionListener;
fServerInfoConnectionListenerSocket = socket(AF_INET, SOCK_STREAM, 0);
if (fServerInfoConnectionListenerSocket < 0)
return errno;
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;
}
if (listen(fServerInfoConnectionListenerSocket, 5) < 0)
return errno;
return B_OK;
}
void
NetFSServer::_ExitServerInfoConnectionListener()
{
safe_closesocket(fServerInfoConnectionListenerSocket);
if (fServerInfoConnectionListener >= 0) {
int32 result;
wait_for_thread(fServerInfoConnectionListener, &result);
}
}
int32
NetFSServer::_ConnectionListenerEntry(void* data)
{
return ((NetFSServer*)data)->_ConnectionListener();
}
int32
NetFSServer::_ConnectionListener()
{
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;
}
int32
NetFSServer::_ConnectionDeleterEntry(void* data)
{
return ((NetFSServer*)data)->_ConnectionDeleter();
}
int32
NetFSServer::_ConnectionDeleter()
{
while (!fTerminating) {
status_t error = acquire_sem(fClosedConnectionsSemaphore);
ClientConnection* connection = NULL;
if (error == B_OK) {
AutoLocker<Locker> _(fLock);
connection = (ClientConnection*)fClosedConnections.RemoveItem((int32)0);
}
if (connection)
delete connection;
}
return 0;
}
int32
NetFSServer::_BroadcasterEntry(void* data)
{
return ((NetFSServer*)data)->_Broadcaster();
}
int32
NetFSServer::_Broadcaster()
{
fBroadcastingSocket = socket(AF_INET, SOCK_DGRAM, 0);
if (fBroadcastingSocket < 0) {
WARN("NetFSServer::_Broadcaster(): WARN: Failed to init broadcasting: "
"%s.\n", strerror(errno));
return errno;
}
#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
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) {
uint32 messageCode = (update ? BROADCAST_MESSAGE_SERVER_UPDATE
: BROADCAST_MESSAGE_SERVER_TICK);
message.message = B_HOST_TO_BENDIAN_INT32(messageCode);
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(kMinBroadcastingInterval);
bigtime_t remainingTime = kBroadcastingInterval
- kMinBroadcastingInterval;
if (fBroadcasterSemaphore >= 0) {
status_t snoozeError = acquire_sem_etc(fBroadcasterSemaphore, 1,
B_RELATIVE_TIMEOUT, remainingTime);
while (snoozeError == B_OK) {
snoozeError = acquire_sem_etc(fBroadcasterSemaphore, 1,
B_RELATIVE_TIMEOUT, 0);
}
} else
snooze(remainingTime);
update = atomic_and(&fServerInfoUpdated, 0);
}
safe_closesocket(fBroadcastingSocket);
return B_OK;
}
int32
NetFSServer::_ServerInfoConnectionListenerEntry(void* data)
{
return ((NetFSServer*)data)->_ServerInfoConnectionListener();
}
int32
NetFSServer::_ServerInfoConnectionListener()
{
if (fServerInfoConnectionListenerSocket < 0)
return B_BAD_VALUE;
TaskManager taskManager;
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);
ServerInfo info;
status_t error = _GetServerInfo(info);
if (error != B_OK) {
closesocket(fd);
return error;
}
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;
}
status_t
NetFSServer::_GetServerInfo(ServerInfo& serverInfo)
{
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);
if (error == B_OK)
error = serverInfo.SetConnectionMethod("insecure");
if (error != B_OK)
return error;
BMessage shares;
error = fSecurityContext->GetShares(&shares);
if (error != B_OK)
return error;
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;
}
void
NetFSServer::_ServerInfoUpdated()
{
atomic_or(&fServerInfoUpdated, 1);
release_sem(fBroadcasterSemaphore);
}
void
NetFSServer::_SendReply(BMessage* message, BMessage* reply, status_t error)
{
BMessage stackReply;
if (!reply)
reply = &stackReply;
reply->AddInt32("error", error);
message->SendReply(reply, (BHandler*)NULL, 0LL);
}
void
NetFSServer::_SendReply(BMessage* message, status_t error)
{
_SendReply(message, NULL, error);
}
static
void
print_usage(bool error)
{
fputs(kUsage, (error ? stderr : stdout));
}
int
main(int argc, char** argv)
{
#ifdef DEBUG_OBJECT_TRACKING
ObjectTracker::InitDefault();
#endif
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;
}
}
status_t error = StatisticsManager::CreateDefault();
if (error != B_OK) {
fprintf(stderr, "Failed to create statistics manager: %s\n",
strerror(error));
return 1;
}
{
NetFSServer server(broadcast);
error = server.Init();
if (error != B_OK) {
fprintf(stderr, "Failed to initialize server: %s\n",
strerror(error));
return 1;
}
server.Run();
}
StatisticsManager::DeleteDefault();
#ifdef DEBUG_OBJECT_TRACKING
ObjectTracker::ExitDefault();
#endif
return 0;
}