[InitKit] InitKit

Ryan Lortie desrt at desrt.ca
Tue Dec 11 21:44:00 PST 2007


WARNING: significant brain-dump contained within.  Proceed with caution.

WARNING2: don't think for a second that I have any free time to actually
write code or anything :)


> First up: terminology.  What do we call what InitKit manages?

Just call them 'services'.  This is obviously want you -want- to call
them and none of the other names really fit.  More on this in a moment.

> D-Bus names:

What's in a name?

Separated/Names Are.Crack.

Anything else is fine.  I like "Init" but only because it has tradition
behind it.  It's actually not a particularly descriptive name in the
context of what we're asking for it to do for us over the bus
((obviously it's not called "init" because it "init"s programs)).

> What about the method form?
>
>
> Start (String service_name)
> Stop (String service_name)

No.  Or, at least, not really.  More in a moment on what "not really"
means.

> Or do we build for extension?
> 
> FindServiceByName (String name)
> 
> which returns an object that implements org.freedesktop.Service, which
> has the following methods:
> 
> Start ()
> Stop ()

Yes.  Absolutely.  You want each service as a first-order dbus object.


My thoughts:



== Roles ==

We should have a list of "roles" on each service (think also:
"provides").  Services provide many roles.  The roles should be
extensible within a custom namespace.  An example role might be
something like

  dbus:org.freedesktop.DeviceKit

to mean "Provides the org.freedesktop.DeviceKit bus destination".

Then in addition to 'FindServiceByName' we can have:

  FindServicesByRole ('dbus:org.freedesktop.DeviceKit')

             ^ note "s".  returns a list.
or maybe

  FindServicesByRole ('dbus', 'org.freedesktop.DeviceKit')

but in any case it should be possible to do some sort of

  FindServicesByRole ('dbus:*')

to get them all.  We may want to be name-space-crazy about the 'dbus:'
part.  Maybe we want a full 'org.freedesktop.DBus.SystemService:' or
some junk like that.  If we want to encourage people to make their own
role namespaces (and I think we do) then this certainly might be a sane
choice to make.

Then you can do .Start() on one of the returned objects.  Or you can
do .GetInfo() or many other fun things.  This is possible because we
separate the query step from the action step.  If we try to mix the two
together then we get this awful explosion:

StartByName ()
StopByName ()
GetInfoByName ()
StartByRole ()
StopByRole ()
GetInfoByRole ()
....[many many more]


That said, this design introduces an additional round-trip.  Maybe it
makes sense to have some very special "optimised" calls like

  StartServiceByRole ('dbus', 'org.freedesktop.DeviceKit')

              ^ no "s" here.

that queries the list of services and in the expected case (exactly 1
matched service found) starts it.  In exceptional cases (no match, more
than one match) it can throw an exception (but see below about David's
"affinity" idea).

I really don't think you want a call like this, though.  In general, I
don't think you ever want to request that a service is started or
stopped (even when the service in question is the result of a role-based
query).  More on this in a moment.

(btw: about the 'service' name:) What you are really doing is a
_service_.  These programs are services in the same sense of the word
that the programs that dbus starts are services.  The only exception is
that the '.service' files that dbus has are only providing a single
specific sort of role (ie: acting as a dbus destination).  You are
merely expanding on this idea by allowing services to provide a wide
variety of different (and possibly multiple) roles.



== Requirements and Requests ==

As alternatives to my (bad) StartServiceByRole () call suggested above
are generic top-level

  RequireRole ('dbus', 'org.freedesktop.DeviceKit')

and matching

  UnrequireRole ().

(think: ref/unref).

This allows a member of the bus to require that a given role is being
provided by some running service (a service is started if need be).
This is also the beginnings of inter-service dependency management; a
service can list 'required roles' in its service file or it can manually
(and possibly conditionally) request them for itself as it is starting
up.  The requirement would hold for as long as the service runs.

UnrequireRole () indicates that the previously required service may be
stopped (but InitKit is under no obligation to do so immediately --
there may still be other users).  A program disconnecting from the bus
automatically 'unrequires' everything that it had required while
connected.

Additionally there could be a persistent "user has requested" list of
roles.  The user here is the system administrator and the request means
that they have explicitly indicated that they want the service to be
running.  We might want to record, instead of "requested roles", just
"requested services" directly, but this ruins our ability to implement a
nice aspect of David's affinities (again: see below).

It might be worth saving these binary requests in a file.  I'm not sure
if you want this to be InitKit's job or not, but it seems reasonable for
it to handle it.


There are two important 'role' classes that I can think of now (but
there will obviously be very many -- anyone can make their own).

The first is the 'dbus' role.  Lots of people are already shipping
programs with dbus service files included.  We should automatically scan
the dbus 'services' directory on startup and treat all of the found
services as if they were special-cased service files of our own that
provide the single role of 'dbus:org.example.whatever' (according to the
file).  Then we are only a dbus patch away from everything working like
magic (and slowly people will migrate to our new service files.  Or
not...).

The second is the 'interesting to the user' role.  Confusingly and
disorientingly, I believe the best namespace for this role to be
'service:'.  (We would speak of 'services' providing a specific 'service
role').

This namespace is for services that the user might be interested in
manually starting.  Examples include network-facing servers.

The idea for this class is that a graphical user interface can perform a
query:

  FindServicesByRole ('service', '*')

and get
-->
  [
   (service:ssh-server,   '/org/fdo/Init/openssh'),
   (service:web-server,   '/org/fdo/Init/apache'),
   etc...
  ]

They can then use this list to toggle the "I want this running" bit
(either way we decide: on the role or directly on the service itself).



== Affinity ==

David Zeuthen was explaining this awesome idea that he had to me.  It
goes something like this (David, please correct me if I stray too far):

Some set of things (be they dbus services, mime-type handlers, or in
this case InitKit services) grows the ability to be tagged.  The tags
have arbitrary and conceptually partially-overlapping names.  In context
of the desktop this looks something like "GNOME", "gtk", "KDE", "qt",
"Epiphany", "Firefox", "Pidgin", "Empathy", "Gossip", etc.

Note that some of the tags cover wide ranges of things and others cover
much tighter ideas (like specific applications).

The user then specifies their "affinity list".  This is a list of things
that they like.  I'd specify "Empathy", "Epiphany", "GNOME", "gtk".

On the desktop if I am asked "please start me a web browser" I have a
list of possible candidates.  I use my affinity list to break the tie.
Specifically, I check which of the possible web browsers matches my
first affinity -- "Empathy".  None of them.  Let's move on to
"Epiphany".  The choice is clear.

But maybe Epiphany isn't installed on this machine.  Next is GNOME.
Well, no GNOME web browser either... How about "gtk"?  Well, at least
Firefox is GTK, so we use that instead of konq.  It generally works like
this.

For the StartServiceByRole (), RequireRole () or RequestRole () calls
we're always faced with the possibility that several services may offer
to provide a given role.  Affinities allow us to "break the tie" in the
same way as they do on the desktop.

In the 'services' space there is somewhat less choice than there is in
the desktop application space, but I still think that this could be a
valuable feature to implement (and it's probably quite simple to
implement and certainly very simple to understand).


== Other Things ==

Not touched in this email are still some very hard questions.  Points
for discussion:

  * conflicting services (two different ssh servers?)

  * startup notification protocol

        How does your newly-started service notify InitKit that it is 
        "ready"?

  * blocking semantics

        If you Require () a role do you block until that role is ready
        or do you get an immediate "ok, I will try now..." reply?

  * shutdown protocol

        We can always kill -9 stuff, but maybe we should have a
        dbus-based "please go away now" signal.  This could fit in
        nicely with the startup-notification point mentioned above. 
        There is certainly room for a scripts-based approach but I guess
        that ultimately we want to move away from scripts.


Cheers :)



More information about the InitKit mailing list