* Copyright 2013, Haiku, Inc. All Rights Reserved.
* Distributed under the terms of the MIT License.
*
* Authors:
* Ingo Weinhold <ingo_weinhold@gmx.de>
*/
#include "PackageDaemon.h"
#include <errno.h>
#include <string.h>
#include <Directory.h>
#include <NodeMonitor.h>
#include <AutoDeleter.h>
#include <package/DaemonDefs.h>
#include "DebugSupport.h"
#include "Root.h"
#include "Volume.h"
using namespace BPackageKit::BPrivate;
PackageDaemon::PackageDaemon(status_t* _error)
:
BServer(B_PACKAGE_DAEMON_APP_SIGNATURE, false, _error),
fSystemRoot(NULL),
fRoots(10),
fVolumeWatcher()
{
}
PackageDaemon::~PackageDaemon()
{
for (int32 i = 0; Root* root = fRoots.ItemAt(i); i++)
root->ReleaseReference();
fRoots.MakeEmpty(false);
}
status_t
PackageDaemon::Init()
{
status_t error = fVolumeWatcher.StartWatching(BMessenger(this, this));
if (error != B_OK) {
ERROR("PackageDaemon::Init(): failed to start volume watching: %s\n",
strerror(error));
}
for (int32 cookie = 0;;) {
dev_t device = next_dev(&cookie);
if (device < 0)
break;
_RegisterVolume(device);
}
return B_OK;
}
void
PackageDaemon::MessageReceived(BMessage* message)
{
switch (message->what) {
case B_NODE_MONITOR:
{
int32 opcode;
if (message->FindInt32("opcode", &opcode) != B_OK)
break;
if (opcode == B_DEVICE_MOUNTED)
_HandleVolumeMounted(message);
else if (opcode == B_DEVICE_UNMOUNTED)
_HandleVolumeUnmounted(message);
break;
}
case B_MESSAGE_GET_INSTALLATION_LOCATION_INFO:
case B_MESSAGE_COMMIT_TRANSACTION:
{
status_t error;
node_ref nodeRef;
error = message->FindInt32("volume", &nodeRef.device);
if (error == B_OK)
error = message->FindInt64("root", &nodeRef.node);
if (fSystemRoot != NULL && (error != B_OK
|| fSystemRoot->NodeRef() == nodeRef)) {
fSystemRoot->HandleRequest(DetachCurrentMessage());
} else if (error == B_OK) {
Root* root = _FindRoot(nodeRef);
if (root != NULL) {
root->HandleRequest(DetachCurrentMessage());
}
}
break;
}
default:
BServer::MessageReceived(message);
break;
}
}
status_t
PackageDaemon::_RegisterVolume(dev_t deviceID)
{
fs_info info;
status_t error = fs_stat_dev(deviceID, &info);
if (error != 0)
RETURN_ERROR(error);
if (strcmp(info.fsh_name, "packagefs") != 0)
RETURN_ERROR(B_BAD_VALUE);
Volume* volume = new(std::nothrow) Volume(this);
if (volume == NULL)
RETURN_ERROR(B_NO_MEMORY);
ObjectDeleter<Volume> volumeDeleter(volume);
node_ref rootRef;
error = volume->Init(node_ref(info.dev, info.root), rootRef);
if (error != B_OK)
RETURN_ERROR(error);
if (volume->MountType() == PACKAGE_FS_MOUNT_TYPE_CUSTOM) {
INFORM("skipping custom mounted volume at \"%s\"\n",
volume->Path().String());
return B_OK;
}
Root* root;
error = _GetOrCreateRoot(rootRef, root);
if (error != B_OK)
RETURN_ERROR(error);
error = root->RegisterVolume(volume);
if (error != B_OK) {
_PutRoot(root);
RETURN_ERROR(error);
}
volumeDeleter.Detach();
INFORM("volume at \"%s\" registered\n", volume->Path().String());
return B_OK;
}
void
PackageDaemon::_UnregisterVolume(Volume* volume)
{
volume->Unmounted();
INFORM("volume at \"%s\" unregistered\n", volume->Path().String());
Root* root = volume->GetRoot();
root->UnregisterVolume(volume);
_PutRoot(root);
}
status_t
PackageDaemon::_GetOrCreateRoot(const node_ref& nodeRef, Root*& _root)
{
Root* root = _FindRoot(nodeRef);
if (root != NULL) {
root->AcquireReference();
} else {
root = new(std::nothrow) Root;
if (root == NULL)
RETURN_ERROR(B_NO_MEMORY);
ObjectDeleter<Root> rootDeleter(root);
bool isSystemRoot = false;
if (fSystemRoot == NULL) {
struct stat st;
isSystemRoot = stat("/boot", &st) == 0
&& node_ref(st.st_dev, st.st_ino) == nodeRef;
}
status_t error = root->Init(nodeRef, isSystemRoot);
if (error != B_OK)
RETURN_ERROR(error);
if (!fRoots.AddItem(root))
RETURN_ERROR(B_NO_MEMORY);
rootDeleter.Detach();
if (isSystemRoot)
fSystemRoot = root;
INFORM("root at \"%s\" (device: %" B_PRIdDEV ", node: %" B_PRIdINO ") "
"registered\n", root->Path().String(), nodeRef.device,
nodeRef.node);
}
_root = root;
return B_OK;
}
Root*
PackageDaemon::_FindRoot(const node_ref& nodeRef) const
{
for (int32 i = 0; Root* root = fRoots.ItemAt(i); i++) {
if (root->NodeRef() == nodeRef)
return root;
}
return NULL;
}
void
PackageDaemon::_PutRoot(Root* root)
{
if (root->ReleaseReference() == 1) {
INFORM("root at \"%s\" unregistered\n", root->Path().String());
fRoots.RemoveItem(root, true);
}
}
Volume*
PackageDaemon::_FindVolume(dev_t deviceID) const
{
for (int32 i = 0; Root* root = fRoots.ItemAt(i); i++) {
if (Volume* volume = root->FindVolume(deviceID))
return volume;
}
return NULL;
}
void
PackageDaemon::_HandleVolumeMounted(const BMessage* message)
{
int32 device;
if (message->FindInt32("new device", &device) != B_OK)
return;
_RegisterVolume(device);
}
void
PackageDaemon::_HandleVolumeUnmounted(const BMessage* message)
{
int32 device;
if (message->FindInt32("device", &device) != B_OK)
return;
if (Volume* volume = _FindVolume(device))
_UnregisterVolume(volume);
}
int
main(int argc, const char* const* argv)
{
status_t error;
PackageDaemon daemon(&error);
if (error == B_OK)
error = daemon.Init();
if (error != B_OK) {
FATAL("failed to init server application: %s\n", strerror(error));
return 1;
}
daemon.Run();
return 0;
}