root/src/servers/package/Volume.h
/*
 * Copyright 2013-2021, Haiku, Inc. All Rights Reserved.
 * Distributed under the terms of the MIT License.
 *
 * Authors:
 *              Ingo Weinhold <ingo_weinhold@gmx.de>
 *              Andrew Lindesay <apl@lindesay.co.nz>
 */
#ifndef VOLUME_H
#define VOLUME_H


#include <Handler.h>
#include <Locker.h>
#include <Message.h>
#include <String.h>

#include <package/ActivationTransaction.h>
#include <package/DaemonClient.h>
#include <package/packagefs.h>
#include <util/DoublyLinkedList.h>

#include "FSUtils.h"
#include "Package.h"


// Locking Policy
// ==============
//
// A Volume object is accessed by two threads:
// 1. The application thread: initially (c'tor and Init()) and when handling a
//    location info request (HandleGetLocationInfoRequest()).
// 2. The corresponding Root object's job thread (any other operation).
//
// The only thread synchronization needed is for the status information accessed
// by HandleGetLocationInfoRequest() and modified by the job thread. The data
// are encapsulated in a VolumeState, which is protected by Volume::fLock. The
// lock must be held by the app thread when accessing the data (it reads only)
// and by the job thread when modifying the data (not needed when reading).


using BPackageKit::BPrivate::BActivationTransaction;
using BPackageKit::BPrivate::BDaemonClient;

class BDirectory;

class CommitTransactionHandler;
class PackageFileManager;
class Root;
class VolumeState;

namespace BPackageKit {
        class BSolver;
        class BSolverRepository;
}

using BPackageKit::BPackageInstallationLocation;
using BPackageKit::BSolver;
using BPackageKit::BSolverRepository;


class Volume : public BHandler {
public:
                        class Listener;

public:
                                                                Volume(BLooper* looper);
        virtual                                         ~Volume();

                        status_t                        Init(const node_ref& rootDirectoryRef,
                                                                        node_ref& _packageRootRef);
                        status_t                        InitPackages(Listener* listener);

                        status_t                        AddPackagesToRepository(
                                                                        BSolverRepository& repository,
                                                                        bool activeOnly);
                        void                            InitialVerify(Volume* nextVolume,
                                                                        Volume* nextNextVolume);
                        void                            HandleGetLocationInfoRequest(BMessage* message);
                        void                            HandleCommitTransactionRequest(
                                                                        BMessage* message);

                        void                            PackageJobPending();
                        void                            PackageJobFinished();
                        bool                            IsPackageJobPending() const;

                        void                            Unmounted();

        virtual void                            MessageReceived(BMessage* message);

                        const BString&          Path() const
                                                                        { return fPath; }
                        PackageFSMountType      MountType() const
                                                                        { return fMountType; }
                        BPackageInstallationLocation Location() const;

                        const node_ref&         RootDirectoryRef() const
                                                                        { return fRootDirectoryRef; }
                        dev_t                           DeviceID() const
                                                                        { return fRootDirectoryRef.device; }
                        ino_t                           RootDirectoryID() const
                                                                        { return fRootDirectoryRef.node; }

                        const node_ref&         PackagesDirectoryRef() const;
                        dev_t                           PackagesDeviceID() const
                                                                        { return PackagesDirectoryRef().device; }
                        ino_t                           PackagesDirectoryID() const
                                                                        { return PackagesDirectoryRef().node; }

                        Root*                           GetRoot() const
                                                                        { return fRoot; }
                        void                            SetRoot(Root* root)
                                                                        { fRoot = root; }

                        int64                           ChangeCount() const
                                                                        { return fChangeCount; }

                        PackageFileNameHashTable::Iterator PackagesByFileNameIterator()
                                                                        const;

                        int                                     OpenRootDirectory() const;

                        void                            ProcessPendingNodeMonitorEvents();

                        bool                            HasPendingPackageActivationChanges() const;
                        void                            ProcessPendingPackageActivationChanges();
                        void                            ClearPackageActivationChanges();
                        const PackageSet&       PackagesToBeActivated() const
                                                                        { return fPackagesToBeActivated; }
                        const PackageSet&       PackagesToBeDeactivated() const
                                                                        { return fPackagesToBeDeactivated; }

                        status_t                        CreateTransaction(
                                                                        BPackageInstallationLocation location,
                                                                        BActivationTransaction& _transaction,
                                                                        BDirectory& _transactionDirectory);
                        void                            CommitTransaction(
                                                                        const BActivationTransaction& transaction,
                                                                        const PackageSet& packagesAlreadyAdded,
                                                                        const PackageSet& packagesAlreadyRemoved,
                                                                        BCommitTransactionResult& _result);

private:
                        struct NodeMonitorEvent;
                        struct PackagesDirectory;

                        typedef FSUtils::RelativePath RelativePath;
                        typedef DoublyLinkedList<NodeMonitorEvent> NodeMonitorEventList;

private:
                        void                            _HandleEntryCreatedOrRemoved(
                                                                        const BMessage* message, bool created);
                        void                            _HandleEntryMoved(const BMessage* message);
                        void                            _QueueNodeMonitorEvent(const BString& name,
                                                                        bool wasCreated);

                        void                            _PackagesEntryCreated(const char* name);
                        void                            _PackagesEntryRemoved(const char* name);

                        status_t                        _ReadPackagesDirectory();
                        status_t                        _InitLatestState();
                        status_t                        _InitLatestStateFromActivatedPackages();
                        status_t                        _GetActivePackages(int fd);
                        void                            _RunQueuedScripts(); // TODO: Never called, fix?
                        bool                            _CheckActivePackagesMatchLatestState(
                                                                        PackageFSGetPackageInfosRequest* request);
                        void                            _SetLatestState(VolumeState* state,
                                                                        bool isActive);
                        void                            _DumpState(VolumeState* state);

                        status_t                        _AddRepository(BSolver* solver,
                                                                        BSolverRepository& repository,
                                                                        bool activeOnly, bool installed);

                        status_t                        _OpenPackagesSubDirectory(
                                                                        const RelativePath& path, bool create,
                                                                        BDirectory& _directory);

                        void                            _CommitTransaction(BMessage* message,
                                                                        const BActivationTransaction* transaction,
                                                                        const PackageSet& packagesAlreadyAdded,
                                                                        const PackageSet& packagesAlreadyRemoved,
                                                                        BCommitTransactionResult& _result);

        static  void                            _CollectPackageNamesAdded(
                                                                        const VolumeState* oldState,
                                                                        const VolumeState* newState,
                                                                        BStringList& addedPackageNames);

private:
                        BString                         fPath;
                        PackageFSMountType      fMountType;
                        node_ref                        fRootDirectoryRef;
                        PackagesDirectory*      fPackagesDirectories;
                        uint32                          fPackagesDirectoryCount;
                        Root*                           fRoot;
                        Listener*                       fListener;
                        PackageFileManager*     fPackageFileManager;
                        VolumeState*            fLatestState;
                        VolumeState*            fActiveState;
                        int64                           fChangeCount;
                        BLocker                         fLock;
                        BLocker                         fPendingNodeMonitorEventsLock;
                        NodeMonitorEventList fPendingNodeMonitorEvents;
                        bigtime_t                       fNodeMonitorEventHandleTime;
                        PackageSet                      fPackagesToBeActivated;
                        PackageSet                      fPackagesToBeDeactivated;
                        BMessage                        fLocationInfoReply;
                                                                        // only accessed in the application thread
                        int32                           fPendingPackageJobCount;
};


class Volume::Listener {
public:
        virtual                                         ~Listener();

        virtual void                            VolumeNodeMonitorEventOccurred(Volume* volume)
                                                                        = 0;
};


#endif  // VOLUME_H