1  /*
   2   * \brief  Service management framework
   3   * \author Norman Feske
   4   * \date   2006-07-12
   5   */

   6  
   7  /*
   8   * Copyright (C) 2006-2013 Genode Labs GmbH
   9   *
  10   * This file is part of the Genode OS framework, which is distributed
  11   * under the terms of the GNU General Public License version 2.
  12   */

  13  
  14  #ifndef _INCLUDE__BASE__SERVICE_H_
  15  #define _INCLUDE__BASE__SERVICE_H_
  16  
  17  #include <root/client.h>
  18  #include <base/printf.h>
  19  #include <util/list.h>
  20  #include <ram_session/client.h>
  21  #include <base/env.h>
  22  
  23  namespace Genode {
  24  
  25     class Client;
  26     class Server;
  27     class Service;
  28     class Local_service;
  29     class Parent_service;
  30     class Child_service;
  31     class Service_registry;
  32  }

  33  
  34  
  35  /**
  36   * Client role
  37   *
  38   * A client is someone who applies for a service. If the service is not
  39   * available yet, we enqueue the client into a wait queue and wake him up
  40   * as soon as the requested service gets available.
  41   */

  42  class Genode::Client : public List<Client>::Element
  43  {
  44     private:
  45  
  46        Cancelable_lock _service_apply_lock;

  47        const char     *_apply_for;
  48  
  49     public:
  50  
  51        /**
  52         * Constructor
  53         */

  54        Client(): _service_apply_lock(Lock::LOCKED), _apply_for(0) { }

  55  
  56        virtual ~Client() { }
  57  
  58        /**
  59         * Set/Request service name that we are currently applying for
  60         */

  61        void apply_for(const char *apply_for) { _apply_for = apply_for; }

  62        const char *apply_for() { return _apply_for; }
  63  
  64        /**
  65         * Service wait queue support
  66         */

  67        void sleep()  { _service_apply_lock.lock(); }

  68        void wakeup() { _service_apply_lock.unlock(); }

  69  }
;

  70  
  71  
  72  /**
  73   * Server role
  74   *
  75   * A server is a process that provides one or multiple services. For the
  76   * most part, this class is used as an opaque key to represent the server
  77   * role.
  78   */

  79  class Genode::Server
  80  {
  81     private:
  82  
  83        Ram_session_capability _ram;

  84  
  85     public:
  86  
  87        /**
  88         * Constructor
  89         *
  90         * \param ram  RAM session capability of the server process used,
  91         *             for quota transfers from/to the server
  92         */

  93        Server(Ram_session_capability ram): _ram(ram) { }

  94  
  95        /**
  96         * Return RAM session capability of the server process
  97         */

  98        Ram_session_capability ram_session_cap() const { return _ram; }

  99  }
;

 100  
 101  
 102  class Genode::Service : public List<Service>::Element
 103  {
 104     public:
 105  
 106        enum { MAX_NAME_LEN = 32 };

 107  
 108     private:
 109  
 110        char _name[MAX_NAME_LEN];

 111  
 112     public:
 113  
 114        /*********************
 115         ** Exception types **
 116         *********************/

 117  
 118        class Invalid_args   { };
 119        class Unavailable    { };
 120        class Quota_exceeded { };
 121  
 122        /**
 123         * Constructor
 124         *
 125         * \param name  service name
 126         */

 127        Service(const char *name) { strncpy(_name, name, sizeof(_name)); }

 128  
 129        virtual ~Service() { }
 130  
 131        /**
 132         * Return service name
 133         */

 134        const char *name() const { return _name; }

 135  
 136        /**
 137         * Create session
 138         *
 139         * \param args      session-construction arguments
 140         * \param affinity  preferred CPU affinity of session
 141         *
 142         * \throw Invalid_args
 143         * \throw Unavailable
 144         * \throw Quota_exceeded
 145         */

 146        virtual Session_capability session(char const *args,
 147                                           Affinity const &affinity)
 = 0;

 148  
 149        /**
 150         * Extend resource donation to an existing session
 151         */

 152        virtual void upgrade(Session_capability session, const char *args) = 0;

 153  
 154        /**
 155         * Close session
 156         */

 157        virtual void close(Session_capability /*session*/) { }

 158  
 159        /**
 160         * Return server providing the service
 161         */

 162        virtual Server *server() const { return 0; }

 163  
 164        /**
 165         * Return the RAM session to be used for trading resources
 166         */

 167        Ram_session_capability ram_session_cap()
 168        {
 169           if (server())
 170              return server()->ram_session_cap();

 171           return Ram_session_capability();

 172        }

 173  }
;

 174  
 175  
 176  /**
 177   * Representation of a locally implemented service
 178   */

 179  class Genode::Local_service : public Service
 180  {
 181     private:
 182  
 183        Root *_root;

 184  
 185     public:
 186  
 187        Local_service(const char *name, Root *root)
 188        : Service(name), _root(root) { }

 189  
 190        Session_capability session(const char *args, Affinity const &affinity) override
 191        {
 192           try { return _root->session(args, affinity); }
 193           catch (Root::Invalid_args)   { throw Invalid_args(); }
 194           catch (Root::Unavailable)    { throw Unavailable(); }
 195           catch (Root::Quota_exceeded) { throw Quota_exceeded(); }
 196           catch (Genode::Ipc_error)    { throw Unavailable();  }

 197        }

 198  
 199        void upgrade(Session_capability session, const char *args) override
 200        {
 201           try { _root->upgrade(session, args); }
 202           catch (Genode::Ipc_error)      { throw Unavailable();    }

 203        }

 204  
 205        void close(Session_capability session) override
 206        {
 207           try { _root->close(session); }
 208           catch (Genode::Ipc_error)    { throw Blocking_canceled(); }

 209        }

 210  }
;

 211  
 212  
 213  /**
 214   * Representation of a service provided by our parent
 215   */

 216  class Genode::Parent_service : public Service
 217  {
 218     public:
 219  
 220        Parent_service(const char *name) : Service(name) { }
 221  
 222        Session_capability session(const char *args, Affinity const &affinity) override
 223        {
 224           try { return env()->parent()->session(name(), args, affinity); }
 225           catch (Parent::Unavailable) {
 226              PWRN("parent has no service \"%s\"", name());
 227              throw Unavailable();

 228           }

 229           catch (Parent::Quota_exceeded) { throw Quota_exceeded(); }
 230           catch (Genode::Ipc_error)      { throw Unavailable();    }

 231        }

 232  
 233        void upgrade(Session_capability session, const char *args) override
 234        {
 235           try { env()->parent()->upgrade(session, args); }
 236           catch (Genode::Ipc_error)    { throw Unavailable();    }

 237        }

 238  
 239        void close(Session_capability session) override
 240        {
 241           try { env()->parent()->close(session); }
 242           catch (Genode::Ipc_error)    { throw Blocking_canceled(); }

 243        }

 244  }
;

 245  
 246  
 247  /**
 248   * Representation of a service that is implemented in a child
 249   */

 250  class Genode::Child_service : public Service
 251  {
 252     private:
 253  
 254        Root_capability _root_cap;
 255        Root_client     _root;
 256        Server         *_server;

 257  
 258     public:
 259  
 260        /**
 261         * Constructor
 262         *
 263         * \param name    name of service
 264         * \param root    capability to root interface
 265         * \param server  server process providing the service
 266         */

 267        Child_service(const char     *name,
 268                      Root_capability root,
 269                      Server         *server)

 270        : Service(name), _root_cap(root), _root(root), _server(server) { }

 271  
 272        Server *server() const override { return _server; }
 273  
 274        Session_capability session(const char *args, Affinity const &affinity) override
 275        {
 276           if (!_root_cap.valid())
 277              throw Unavailable();

 278  
 279           try { return _root.session(args, affinity); }
 280           catch (Root::Invalid_args)   { throw Invalid_args();   }
 281           catch (Root::Unavailable)    { throw Unavailable();    }
 282           catch (Root::Quota_exceeded) { throw Quota_exceeded(); }
 283           catch (Genode::Ipc_error)    { throw Unavailable();    }

 284        }

 285  
 286        void upgrade(Session_capability sc, const char *args) override
 287        {
 288           if (!_root_cap.valid())
 289              throw Unavailable();

 290  
 291           try { _root.upgrade(sc, args); }
 292           catch (Root::Invalid_args)   { throw Invalid_args();   }
 293           catch (Root::Unavailable)    { throw Unavailable();    }
 294           catch (Root::Quota_exceeded) { throw Quota_exceeded(); }
 295           catch (Genode::Ipc_error)    { throw Unavailable();    }

 296        }

 297  
 298        void close(Session_capability sc) override
 299        {
 300           try { _root.close(sc); }
 301           catch (Genode::Ipc_error)    { throw Blocking_canceled(); }

 302        }

 303  }
;

 304  
 305  
 306  /**
 307   * Container for holding service representations
 308   */

 309  class Genode::Service_registry
 310  {
 311     protected:
 312  
 313        Lock          _service_wait_queue_lock;
 314        List<Client>  _service_wait_queue;
 315        List<Service> _services;

 316  
 317     public:
 318  
 319        /**
 320         * Probe for service with specified name
 321         *
 322         * \param name    service name
 323         * \param server  server providing the service,
 324         *                default (0) for any server
 325         */

 326        Service *find(const char *name, Server *server = 0)
 327        {
 328           if (!name) return 0;
 329  
 330           Lock::Guard lock_guard(_service_wait_queue_lock);
 331  
 332           for (Service *= _services.first(); s; s = s->next())
 333              if (strcmp(s->name(), name) == 0
 334               && (!server || s->server() == server))
 return s;

 335  
 336           return 0;

 337        }

 338  
 339        /**
 340         * Check if service name is ambiguous
 341         *
 342         * \return true  if the same service is provided multiple
 343         *               times
 344         */

 345        bool is_ambiguous(const char *name)
 346        {
 347           Lock::Guard lock_guard(_service_wait_queue_lock);
 348  
 349           /* count number of services with the specified name */
 350           unsigned cnt = 0;
 351           for (Service *= _services.first(); s; s = s->next())
 352              cnt += (strcmp(s->name(), name) == 0);

 353           return cnt > 1;

 354        }

 355  
 356        /**
 357         * Return first service provided by specified server
 358         */

 359        Service *find_by_server(Server *server)
 360        {
 361           Lock::Guard lock_guard(_service_wait_queue_lock);
 362  
 363           for (Service *= _services.first(); s; s = s->next())
 364              if (s->server() == server)
 365                 return s;

 366  
 367           return 0;

 368        }

 369  
 370        /**
 371         * Wait for service
 372         *
 373         * This method is called by the clients`s thread when requesting a
 374         * session creation. It blocks if the requested service is not
 375         * available.
 376         *
 377         * \return  service structure that matches the request or
 378         *          0 if the waiting was canceled.
 379         */

 380        Service *wait_for_service(const char *name, Client *client, const char *client_name)
 381        {
 382           Service *service;
 383  
 384           client->apply_for(name);
 385  
 386           _service_wait_queue_lock.lock();
 387           _service_wait_queue.insert(client);
 388           _service_wait_queue_lock.unlock();

 389  
 390           do {
 391              service = find(name);
 392  
 393              /*
 394               * The service that we are seeking is not available today.
 395               * Lets sleep a night over it.
 396               */

 397              if (!service) {
 398                 printf("%s: service %s not yet available - sleeping\n",
 399                        client_name, name)
;

 400  
 401                 try {
 402                    client->sleep();
 403                    printf("%s: service %s got available\n", client_name, name);

 404                 }
 catch (Blocking_canceled) {
 405                    printf("%s: cancel waiting for service\n", client_name);
 406                    break;

 407                 }

 408              }

 409  
 410           }
 while (!service);
 411  
 412           /* we got what we needed, stop applying */
 413           _service_wait_queue_lock.lock();
 414           _service_wait_queue.remove(client);
 415           _service_wait_queue_lock.unlock();
 416  
 417           client->apply_for(0);
 418  
 419           return service;

 420        }

 421  
 422        /**
 423         * Register service
 424         *
 425         * This method is called by the server`s thread.
 426         */

 427        void insert(Service *service)
 428        {
 429           /* make new service known */
 430           _services.insert(service);
 431  
 432           /* wake up applicants waiting for the service */

 433           Lock::Guard lock_guard(_service_wait_queue_lock);
 434           for (Client *= _service_wait_queue.first(); c; c = c->next())
 435              if (strcmp(service->name(), c->apply_for()) == 0)
 436                 c->wakeup();

 437        }

 438  
 439        /**
 440         * Unregister service
 441         */

 442        void remove(Service *service) { _services.remove(service); }

 443  
 444        /**
 445         * Unregister all services
 446         */

 447        void remove_all()
 448        {
 449           while (_services.first())
 450              remove(_services.first());

 451        }

 452  }
;

 453  
 454  #endif /* _INCLUDE__BASE__SERVICE_H_ */