Contents
Processes
Sabayon's code is split among three processes: the sysadmin-level application, and two internal helpers. The main administration tool is the sabayon process: it runs as root, and it is the little window that shows you the list of user profiles. It uses two helper processes. The first helper is sabayon-session, which runs as a temporary user and shows the live Xephyr session. The second helper is sabayon-apply, which is used to apply the settings from the user profile being edited into a temporary home directory.
sabayon - main tool used by admins
sabayon-session - helper to run the nested session in a window
sabayon-apply - system tool and helper to apply a user profile to a home directory
Whitelist of environment variables
Sabayon sanitizes the environment before launching its child processes, so that extraneous environment variables don't sneak in. There is a whitelist of environment variables that will be passed through to the child processes in sabayon/lib/config.py.in:PASSTHROUGH_ENVIRONMENT. If you need to pass an environment variable to the child processes or to the child session, you'll need to modify PASSTHROUGH_ENVIRONMENT.
Error logging
The helper processes must be able to keep detailed logs of their operation to aid debugging. These logs must be accessible from the main "sabayon" tool which admins use: when the tool encounters an error in itself or in any of its helpers, it must be able to give the admin useful information for debugging (either by the admin himself or by a Sabayon hacker).
While each helper process could maintain its own log, this would be cumbersome for admins to gather and send to a developer. So, Sabayon automatically aggregates all the logs into a single one which can be sent "as-is" to someone for debugging purposes.
Turning on logging
By default, all user actions are logged. That is, Sabayon and its helpers keep logs of the actions which the admin does while directly using them. Sabayon will automatically present these logs to the admin when an error occurs.
Additionally, the admin can enable more detailed logging. To do this, create a file called sabayon-debug-log.conf in root's home directory. This file has a syntax similar to Samba's smb.conf or to Windows .ini files. Put the following text in the ~root/sabayon-debug-log.conf file:
[debug log] max lines = 1000 enable domains = <list of domain names separated by ";">
For example, to enable logs from the storage and files-source modules, you would use a line like this:
enable domains = storage;files-source
These are the available domain names. Note that by default they are not logged unless you list them in the sabayon-debug-log.conf file:
user-profile - Main module to handle user profiles.
storage - Module to save user profiles to a .zip bundle.
panel-delegate - Understands changes which affect the GNOME Panel and its applets.
gconf-source - Monitoring changes in GConf data and logging/saving/restoring them.
files-source - Monitoring of changes in the temporary home directory and logging/saving/restoring them.
mozilla-source - Understands changes in the configuration of Mozilla Firefox.
proto-session - Low-level setup of the nested session (X authority, X displays, etc.).
usermod - Low-level utilities to create a temporary home directory.
dir-monitor - Low-level monitoring of the file system.
user-db - Maintains an association between users and profiles.
cache - Retrieves and caches the contents of remote URIs.
admin-tool - Toplevel operations in the sabayon program (not normally needed).
sabayon-apply - Toplevel operations in the sabayon-apply helper program (not normally needed).
sabayon-session - Toplevel operations in the sabayon-session helper program (not normally needed).
deprecated - Turns on warnings about using deprecated Python or pygtk features (not normally needed).
Exceptions in callbacks
Often, the code flow in Sabayon looks like this:
python code: (1)
gtk_main (): (2)
python code inside a callback: (3)
... (4)If the code inside the callback (3) throws an exception in (4), then Python will go to (2) and the main loop will keep spinning happily. That is, (1) will never know about that exception! It will seem as if the program kept running normally.
If a callback gets an exception due to a bug, we want the toplevel program to notice that as soon as possible so that it can log the appropriate message to the debug log, and terminate the program. So, we use the errors.checked_callback decorator. This decorator catches all unhandled exceptions in a callback, logs the exception to the debug log, and calls gtk.main_quit(). You must use the decorator in all GObject or GSource callbacks (this includes idle handlers, GIO watches, etc.):
import errors
@errors.checked_callback
def my_callback (...):
... body ...And finally, the toplevel program (or whatever calls gtk.main()) should look like this:
import errors
gtk.main ()
if errors.errors_have_fatal_error ():
debuglog.debug_log (True, "domain", "a fatal error occurred; exiting the program")
debuglog.debug_log_dump_to_dated_file (...)
sys.exit (1)
Configuration sources, changes, and delegates
Sabayon monitors different types of data sources. For example, it monitors which files are created in the session's home directory, or which GConf keys are modified.
The core of Sabayon doesn't know about particular data sources. There is an abstract class, ProfileSource in lib/userprofile.py, which all source types must implement. All the sources we have are in the lib/sources directory. The two major sources are FilesSource and GConfSource. Respectively, they monitor changes in files and changes in the GConf keys.
Each ProfileSource basically monitors some kind of data and creates a ProfileChange object every time something happens in that data. ProfileChange, in lib/userprofile.py, is an abstract class for changes in the user's configuration data.
Each major ProfileSource defines its own kind of ProfileChange. For example, FilesSource generates FileChange objects, and GConfSource generates GConfChange objects. A FileChange has information like "$filename got created/deleted/modified". A GConfChange has information like "$gconf_key_name changed to $value".
The major sources are useful for most configuration data. However, sometimes Sabayon would like to present a higher-level view of changes than just "this file got modified" or "that GConf key changed". For example, Firefox tweaks many files under ~/.mozilla, and gnome-panel tweaks many GConf keys under the /apps/panel namespace.
For each type of ProfileSource, we can specialize the way in which changes are handled through the use of a delegate.
For example, the MozillaDelegate (in lib/sources/mozillasource.py) says that every change detected by FilesSource, which is also under .mozilla, should be handled by the delegate instead of the generic code. MozillaDelegate has special knowledge about how to parse Firefox's preferences, bookmarks, etc.
Similarly, PanelDelegate (in lib/sources/paneldelegate.py) knows how to understand changes under the /apps/panel namespace in GConf. The panel sets a bunch of GConf keys when an applet is added. Instead of presenting the user with a meaningless list of GConf changes ("OAFIID for the applet, panel toplevel ID, blah blah"), the PanelDelegate parses that and instead presents human-readable information like "Workspace Switcher Applet got added to the bottom panel".
Each kind of delegate gets triggered by a a namespace section from the configuration space which its parent ProfileSource is monitoring. The namespace section of MozillaDelegate is thus ".mozilla", relative to the FilesSource which monitors the session's home directory, as that is the directory under ~ which it wants to "own". Similarly, the namespace section of PanelDelegate is "/apps/panel", relative to the GConfSource.
