LiMaL Development Guide

Edited by

Server Technologies Team

SUSE LINUX Products GmbH

$LastChangedDate: 2007-04-27 13:08:13 +0200 (Fr, 27 Apr 2007) $

Abstract

This document describes the concept of a flexible system management library for Linux. Everything which is described here, can change on the way to it's implementation.

Unpublished Work of SUSE LINUX AG. All Rights Reserved.

This work is an unpublished work and contains confidential, proprietary, and trade secret information of SUSE LINUX AG. Access to this work is restricted to SUSE employees who have a need to know to perform tasks within the scope of their assignments. No part of this work may be practiced, performed, copied, distributed, revised, modified, translated, abridged, condensed, expanded, collected, or adapted without the prior written consent of SUSE LINUX AG. Any use or exploitation of this work without authorization could subject the perpetrator to criminal and civil liability.

General Disclaimer

This document is not to be construed as a promise by any participating company to develop, deliver, or market a feature or a product. SUSE LINUX AG makes no representations or warranties with respect to the contents of this document, and specifically disclaims any express or implied warranties of merchantability or fitness or any particular purpose. Further, SUSE LINUX AG reserves the right to revise this document and to make changes to its content, at any time, without obligation to notify any person or entity of such revisions or changes.


Table of Contents

1. Why do we need LiMaL?
2. What is LiMaL?
2.1. What should a pluglib provide
3. How to write a LiMaL pluglib
3.1. Package Handling
3.1.1. Requirements
3.1.2. Files and Directories of a LiMaL package
3.1.2.1. package/
3.1.2.2. package/doc/
3.1.2.3. package/doc/autodocs/
3.1.2.4. package/package/
3.1.2.5. package/src/
3.1.2.6. package/src/limal/<package-name>/
3.1.2.7. package/testsuite
3.1.2.8. package/po
3.1.3. Create a New Package
3.1.4. Toplevel make Targets in Detail
3.1.4.1. make -f Makefile.cvs
3.1.4.2. make
3.1.4.3. make install
3.1.4.4. make package-local
3.1.4.5. make package
3.1.4.6. make checkin-stable
3.1.4.7. make stable
3.1.4.8. make check
3.1.5. Using pkg-config for Metainformation about Installed Libraries
3.1.6. Using SWIG
3.1.7. Language Translation
3.1.7.1. Language Translations with C/C++
3.2. The external interface
3.2.1. Namespace
3.2.2. Basic types
3.2.3. BloCxx String
3.2.4. BloCxx List
3.2.5. BloCxx Array
3.2.6. BloCxx Map
3.2.7. Binary Data - LiMaL ByteBuffer
3.2.8. References
3.2.8.1. Simple C++ References
3.2.8.2. BloCxx COWReference
3.2.9. Exceptions
3.2.10. Callbacks
3.2.11. LiMaL Pluglib naming Guide
3.2.11.1. A factory method to create a new instance of an entity
3.2.11.2. Methods to manipulate instances of entities
3.2.11.3. Initialization and flushing the caches (making the change permanent)
3.2.11.4. Manipulating the entity's values
3.3. Utilities for internal use
3.3.1. Logging
3.3.2. Locking
3.3.3. BloCxx Exec
3.3.4. File Utitlities
3.3.5. String Utilities
3.3.6. Network
3.4. Documentation
3.4.1. Introduction
3.4.2. General Documentation
3.4.3. Inline Documentation
3.5. Testsuite
3.5.1. Introduction
3.5.2. Requirements
3.5.3. Environment
3.5.4. Creating Tests
3.5.5. Single Tests
3.5.6. Multiple Tests
3.5.7. Running Tests
3.6. Programming Guideline
4. LiMaL License
5. What is BloCxx ?

Chapter 1. Why do we need LiMaL?

Managing an operating system requires deep knowledge of specific system details. Even such a seemingly easy task like restarting a system service via a so called init script requires at least the knowledge of the specific LSB conform return codes such a script must return. Also configuration file locations within the file system can change as to some extend can semantics. And that's one goal of LiMaL:

Provide a system library style, objectoriented way of access to the operating system.

When the underlying system or configuration files changes, the library is going to stay the same. The current and first consumer of LiMaL is YaST. Potential future consumers are going to be some CIM providers for OpenWBEM to manage the SUSE LINUX operating system.

LiMaL is also not meant to be restricted to run on SUSE LINUX. Future versions might support other LINUX versions or even other operating systems.

Chapter 2. What is LiMaL?

LiMaL is a library written in C++, which provides a documented and unique programming interface to the operation system. It is designed to be able to add interfaces to other programming languages later on. Each feature, that LiMaL provides, must be implemented by a so called pluglib (see Chapter 3, How to write a LiMaL pluglib). Pluglibs developers have to follow some defined rules like e.g. how to handle errors or how to provide system logging and LiMaL provides some basic functionality for pluglib developers to help archiving that. LiMaL is designed that way, that both, pluglib developer and system management developer will have a documented and unique way to use LiMaL.

LiMaL uses BloCxx (see Chapter 5, What is BloCxx ?) as a generic C++ framework to implement the services it provides. BloCxx provides an system independent interface for file access, locking, logging, etc.

If there are another low-level requests, LiMaL has to fulfill these requirements.

2.1. What should a pluglib provide

  • A pluglib provides low-level access to the system.

  • A very detailed interface with the possibility to change every single value is required (This is a CIM requirement).

  • A little bit higher level API which provide a more abstract view to the configured service should also be implemented.

  • Parameter checks. If it is possible for an application to call a function with wrong paramters, these parameters have to be checked. (E.g. the application should provide an email address, an IP address checks if the function parameter is a string)

  • Script language bindings (using SWIG) to at least perl.

Chapter 3. How to write a LiMaL pluglib

3.1. Package Handling

In order to develop and handle source code of LiMaL in a very simple and most uniform way, we provide templates of a package. This chapter explains the contents of a LiMaL package and how to handle it.

3.1.1. Requirements

Besides the general development tools e.g. compilers a LiMaL package needs following packages installed:

  • BloCxx packages currently located in http://forge.novell.com/modules/xfmod/project/?limal

  • limal-devtools, limal, limal-devel

    These packages are currently located in http://forge.novell.com/modules/xfmod/project/?limal

  • automake, autoconf, perl, perl-XML-Writer, libxml2, libxslt, docbook-xsl-stylesheets, docbook_4, libgcrypt, libgpg-error sgml-skel, dejagnu, expect, tcl, doxygen , openslp, openslp-devel, libpng, gpp, libgpp, libgcrypt, libgpg-error, pkgconfig, gettext, gettext-devel, swig. These packages are needed for the YaST developping environment too.

3.1.2. Files and Directories of a LiMaL package

The general directory layout is

Table 3.1. directories of a package

directorydescription
package/toplevel dir
package/doc/documentation (manually edited)
package/doc/autodocs/documentation (doxygen generated)
package/src/sources
package/src/limal/<package-name>/include files
package/packagepackage files for autobuild
package/testsuitetestsuite
package/swigDescription of the SWIG interface.
package/poTranslation files.

3.1.2.1. package/

Table 3.2. file description of package/

filedescription
configure.in.inConfiguration file for autoconf/automake
package.spec.inDefinition of the RPM .spec file.
MAINTAINERPerson who is responsible for this package.
POTFILESFiles which inlcude text for translation.
Makefile.cvsConfiguration file for automake/autoconf
RPMNAMERPM package name
SUBDIRSSubdirectories of the package, the make has to go through.
VERSIONVersion in a X.Y.Z format.

3.1.2.2. package/doc/

Each GENERAL package documentation will be written in DocBook-XML format. The layout stylesheet of LiMaL will be used in order to support a equal layout. The generated html files can be found in the html directory.

Table 3.3. file description of package/doc/

filedescription
Makefile.amConfiguration file automake. For more information have a look to the template package.

3.1.2.3. package/doc/autodocs/

In order to document internal functions, variables,... of C/C++/Perl code we are using doxygen. This means that the documentation has to be written within the header files of source code. The format of this "inline" documentation will be explained shortly in a special chapter. While compiling the source code the concerning ducumentation will be generated automatically by doxygen and will be stored in package/doc/autodocs.

Table 3.4. file description of package/doc/autodocs

filedescription
Makefile.amConfiguration file automake. For more information have a look to the template package.

3.1.2.4. package/package/

This is the directory in which the package files for autobuild will be generated with the call "make package" or "make package-local".

Table 3.5. file description of package/package/

filedescription
package.changesThe documentation file for logging all package changes for the autobuild.

3.1.2.5. package/src/

The actual directory for your source code.

Table 3.6. file description of package/src/

filedescription
Makefile.amConfiguration file automake. For more information have a look to the template package.

3.1.2.6. package/src/limal/<package-name>/

The directory for all include files of a package. Only limal core packages (liblimal) install their include files directly into the src/limal/ directory.

Table 3.7. file description of package/src/limal/<package-name>/

filedescription
Makefile.amConfiguration file automake. For more information have a look to the template package.

3.1.2.7. package/testsuite

The directory for the package testsuite. We are using dejagnu for testing packages. There is an own chapter how tests has to generated and handled within a LiMaL package

3.1.2.8. package/po

The directory for the translation files. During "make" a pot-file with the name <package>.pot is created. If you get back translated .po files you can check-in them into this directory with the name <lang>.po.

The LINGUAS file includes all languages to build in one line seperated by space.

With the Makevars file you can control the behaviour of gettext.

POTFILES.in has a list of all files where gettext should search for strings to translate.

3.1.3. Create a New Package

If you want to create a new LiMaL package you can generate a template with the following commands:

  • Check out the LiMaL SVN head with the command:

    svn co https://svn.suse.de/svn/limal/limal-head. The svn password is the same as the YaST svn login.

  • Go to directory "source":

    cd source.

  • Generate a new package with the call:

    /usr/share/limal/devtools/bin/create-new-package <skeleton> <new-package-name> <maintainer> <email>

    /usr/share/limal/devtools/bin/create-new-package -s shows a list of available skeletons.

  • You have to put your source code to <new-package-package>/src and your include files into <new-package-name>/src/limal/<new-package-name>/.

  • If you need a define in a C++ file you have to pass it to the compiler. Simply use AM_CXXFLAGS = -DEXAMPLE=\"example\" in your Makefile.am.

  • Create the configure files, makefiles and package files with the command described in the next chapter.

3.1.4. Toplevel make Targets in Detail

In oder to create configure files, makefiles and package files there are some predinfed make targets available which are very useful and unify the developping process of a LiMaL module.

3.1.4.1. make -f Makefile.cvs

Generates all configure files and Makefiles by using autoconf and automake.

3.1.4.2. make

Compiles your code.

3.1.4.3. make install

Install your compiled code into the predefined directories. You can set the environment variable DESTDIR in order to change the destination.

Plaese take care that you have to call the command with root permissions.

3.1.4.4. make package-local

Create a tarball from your module and put it into the package/ directory. This also creates a spec file from the .spec.in file.

3.1.4.5. make package

This checks for cvs consistency (see below) and whether or not you correctly tagged that version (don't forget to increase the version number in VERSION and call limaltool tagversion!), then does everything "make package-local" did.

3.1.4.6. make checkin-stable

This makes a package (i.e. it does everything "make package" does and checks it into the correct SuSE Linux distribution.

This requires /work/src/done to be mounted via NFS.

3.1.4.7. make stable

Just an alias for "make checkin-stable".

3.1.4.8. make check

Running testsuite.

3.1.5. Using pkg-config for Metainformation about Installed Libraries

The pkg-config program is used to retrieve information about installed libraries in the system. pkg-config retrieves information about packages from special metadata files. These files are named after the package, with the extension .pc. By default, pkg-config looks in the directory /usr/lib/pkgconfig for these files. It will also look in the colon-separated list of directories specified by the PKG_CON-FIG_PATH environment variable.

The LiMaL package environment delivers a pkg-config mechanism which is activated (on default) in the configure.in.in file:

## lib configure.in.in

## initialize
@LIMAL-INIT-COMMON@
@LIMAL-INIT-PROGRAM@

CREATE_PKGCONFIG=true

LIMAL_APACHE_LIB_VERSION=${VERSION}
LIMAL_APACHE_API_VERSION=${VERSION%%.*}
AC_SUBST(LIMAL_APACHE_LIB_VERSION)
AC_SUBST(LIMAL_APACHE_API_VERSION)

micro=${VERSION##*.}
minor=${VERSION#*.}
minor=${minor%.*}
cur=${LIMAL_APACHE_API_VERSION}
rev=$(( $minor * 100 + $micro ))
LIB_VERSION_INFO=${cur}:${rev}
AC_SUBST(LIB_VERSION_INFO)

## some common checks
@LIMAL-CHECKS-COMMON@
@LIMAL-CHECKS-PROGRAM@
@LIMAL-CHECKS-LIMAL@
@LIMAL-CHECKS-SWIG@
@LIMAL-CHECKS-PERL@

#AC_ARG_ENABLE(debug,[  --enable-debug          debug msg for Rep ],[DEBUG_FLAGS="-DAPACHE_DEBUG"])
#AC_SUBST(DEBUG_FLAGS)


## and generate the output
@LIMAL-OUTPUT@

              

If there is no <packagename>.pc.in file defined in the package root directory, the make command will generate this file automatically with following entries:

prefix=@prefix@
exec_prefix=@exec_prefix@
bindir=@bindir@
libdir=@libdir@
includedir=@includedir@
limalbindir=@limalbindir@
limallibdir=@limallibdir@
limaldatadir=@limaldatadir@
limalincluledir=@limalincludedir@

Name: @RPMNAME@
Version: @VERSION@
Description: LiMaL package "@RPMNAME@"
Requires: limal
Cflags: -I${includedir}
Libs: -L${limallibdir} -l@RPMNAME@
              

3.1.6. Using SWIG

SWIG reads annotated C/C++ header files and creates wrapper code (glue code) in order to make the corresponding C/C++ libraries available to other languages, or to extend C/C++ programs with a scripting language.

In order to use SWIG in your package you have to check the SWIG environment by calling the check rule @LIMAL-CHECKS-SWIG@ defined in configure.in.in:

.
..
...
@LIMAL-CHECKS-SWIG@
...
..
.
              

This function checks the useability of SWIG and returns the library path of SWIG (e.g. /usr/lib/swig1.3). You can use the variable @SWIG_LIB@ in your Makefile.am files in order to get the path of the SWIG library.

3.1.7. Language Translation

LiMaL is using the well known gettext mechanism for translating messages like errors, warnings,....

The concerning translation files ( *.mo files ) will be stored in /usr/share/locale.

With the make call in the package root directory it extracts all translation strings from the source code. The result will be a po/*.pot file with which you can handle your translation. Translated pot files get the name <lang>.po and are checked-in into the po/ directory. By enhancing the LINGUAS file the po-files are compiled to mo-files.

In order to handle language translations in your source code you have to take care about few points which depend on the selected programming language:

3.1.7.1. Language Translations with C/C++

If you are using the LiMaL package template almost everyting will be defined for handling translations. Please have a look into the package template for internal information. Otherwise you have to take care about only few things:

Strings which have to be translated are marked with __("<string>") in the C/C++ source code.

3.2. The external interface

This section describes how the external interface of a LiMaL pluglib should look like. It defines the types which are allowed, the namespaces we have to use, the error handling and a naming guide for classes and methods.

If there is a specific need to support additional types, please keep in mind, that for every additional type a SWIG interface must be provided.

3.2.1. Namespace

LiMaL (as well as BloCxx) is using a version in its namespace, that is defined in the limal/config.h header file:

  namespace LIMAL_NAMESPACE
  {}
  namespace limal = LIMAL_NAMESPACE;
      

The LIMAL_NAMESPACE is a macro, that appends the limal library version to limal, resulting in a namespace followed by a version number, for example limal1. This special library namespace version is a simple integer, that is incremented when the interface is changed.

Since limal is a alias to LIMAL_NAMESPACE, you can simply use the limal shortcut, except in namespace declarations (see bellow):

  #include <blocxx/String.hpp>
  #include <limal/Logger.hpp>

  namespace // anonymous
  {

  void doSomething(const blocxx::String &str)
  {
  	limal::Logger logger("TheNameOfCurrentComponent");
  	LIMAL_SLOG(logger, "DEBUG", "doing something with '"
  	                            << str << "'");
  	
  	// ...
  }
  } // End of anonymous namespace
      

In namespace declarations, the LIMAL_NAMESPACE macro should be used. For example, if you are writing a MyLib pluglib and want to declare a sub-namespace below of the limal namespace, you have to include the limal/config.h header file and use the namespaces as follows:

  #include <blocxx/String.hpp>
  #include <limal/config.h>
  
  // declare namespace with version:
  namespace LIMAL_NAMESPACE {
    #define   MYLIB_NAMESPACE MyLib1
    namespace MYLIB_NAMESPACE {}
    namespace MyLib = MYLIB_NAMESPACE;
  }

  // use namespace-macro in declarations:
  namespace LIMAL_NAMESPACE {
  namespace MYLIB_NAMESPACE {
  
  	class MyClass
  	{
  	public:
  		MyClass(const blocxx::String &str = ""): myStr(str)
  		{}
  
  		~MyClass()
  		{}
  
  		void doit()
  		{
  			imal::Logger logger("MyLib1");
  			LIMAL_SLOG_DEBUG(logger, "doing something with '"
  			                         <<  str << "'");
  			// ...
  		}
  	private:
  		blocxx::String myStr;
  	};
  
  } // End of MYLIB_NAMESPACE
  } // End of LIMAL_NAMESPACE
      

Each pluglib has to use a namespace. Of course, it is strongly recommended to use namespaces with versions in all pluglibs!

To declare own namespace, the helper macro LIMAL_DEFINE_NAMESPACE(name,version) can be used. See also the configure.in.in and limal/config.h in limal core library, how to declare a namespace with appended version for your package.

The skeleton in limal-devtools provides all required functionality.

3.2.2. Basic types

This is the list of basic types that are allowed to be used in the interface. These types are defined in BloCxx.


  #include <blocxx/Types.hpp>

  blocxx::Int32      = signed integer 32bit
  blocxx::UInt32     = unsigned integer 32bit
  blocxx::Int64      = signed integer 64bit
  blocxx::UInt64     = unsigned integer 64bit


  bool               = (normal C++ bool)

   or

  #include <blocxx/Bool.hpp>

  blocxx::Bool       = boolean

  

Some other basic types are also allowed:


  #include <sys/types.h>

  size_t     = size_t (architecture dependent)


  #include <time.h>

  time_t     = datetime datatype (architecture dependent)

      

BloCxx has a DateTime class which internaly uses time_t. You have a constructor with time_t as parameter and and a get() method which returns time_t.

It is also allowed to use


  enum       = enumerations
  struct     = structures
      

in the external interface.

3.2.3. BloCxx String

As string in the external interface we use


  #include <blocxx/String.hpp>

  blocxx::String
      

More information can be found here.

3.2.4. BloCxx List

As list in the external interface we use


  #include <blocxx/List.hpp>
        
  blocxx::List<ValueType>
      

The BloCxx List is mainly a wrapper around std::list. It stores the elements in a linked list, allowing fast insertions and deletions.

More information can be found here.

3.2.5. BloCxx Array

Alternatively to a BloCxx List, a BloCxx Array can be used as well, if you want contiguous elements stored as an array.


  #include <blocxx/Array.hpp>
        
  blocxx::Array<ValueType>
      

The BloCxx Array is mainly a wrapper around std::vector. Accessing its members or appending elements can be done in constant time, whereas locating a specific value or inserting elements into the vector takes linear time.

More information can be found here.

As a specialized Array, BloCxx defines a StringArray type, that is nothing else than Array<String>. It is used very often in diverse BloCxx classes.

3.2.6. BloCxx Map

As map in the external interface we use


  #include <blocxx/Map.hpp>

  blocxx::Map<KeyType,ValueType>
      

The BloCxx Map is mainly a wrapper around std::map.

More information can be found here.

3.2.7. Binary Data - LiMaL ByteBuffer

For binary data we have implemented the limal::ByteBuffer class.


  #include <limal/ByteBuffer.hpp>

  limal::ByteBuffer b;
      

More information can be found here.

3.2.8. References

3.2.8.1. Simple C++ References

Script languages like YCP or Perl are using the LiMaL library via a SWIG wrapper, that automatically wraps all objects and its public methods and embeds them into objects of the destination language. This works fine also for methods using references.

  class Foo
  {
  };

  class Bar {
  	Foo  getFoo();
  	void setFoo(const Foo &foo);
  };
	    

3.2.8.2. BloCxx COWReference

If you want to implement reference counting, you can just derive your class from blocxx::COWReferenceBase and typedef using the blocxx::COWReference. The blocxx::COWReference classes provides a smart pointer with non-intrusive reference counting and support of a 'copy on write' functionality. To provide the 'copy on write' mechanism, the base class should provide a clone() method creating a copy of itself. For example:

  class Foo: public blocxx::COWReferenceBase
  {
	Foo * clone()
	{
	    return new Foo(*this);
	}
  };
  typedef blocxx::COWReference<Foo> FooRef;

  class Bar {
  	FooRef  getFoo();
  	void    setFoo(const FooRef &fooRef);
  };
	    

There are also other Reference classes in blocxx, for example blocxx::COWIntrusiveReference. Choose one of them or use blocxx::RefCount to write a class providing the required functionality. But make sure, the SWIG interface works propelly.

3.2.9. Exceptions

LiMaL uses exceptions to handle errors.

Due to the fact LiMaL uses BloCxx types and BloCxx itself uses the STL very often we have three types of exceptions in the external interface - LiMaL, BloCxx and STL exceptions.

The LiMaL Exceptions are derived from the BloCxx exceptions which are derived from the STL exceptions.

The LiMaL exceptions are only a very small set of common exception types which are not part of BloCxx. A pluglib could throw LiMaL exceptions as well as BloCxx exceptions via the BLOCXX_THROW() marco. You have only to take care for the correct namespace.

Here is a list of the LiMaL exceptions

  • RuntimeException

  • OverflowException

  • SyntaxException

  • ValueException

  • SystemException

If a pluglib writer wants a special exception he is able to create them with some macros which BloCxx provides.


  // declaration in the header file

  namespace MYPLUGLIB_NAMESPACE {

      BLOCXX_DECLARE_EXCEPTION(<NAME>); 

  }

  // implementation in a .cpp file

  namespace MYPLUGLIB_NAMESPACE {
  
      BLOCXX_DEFINE_EXCEPTION(<NAME>);

  }
      

3.2.10. Callbacks

LiMaL provides callback support, but it is strongly recomended to avoid callbacks in the interface at all.

The main problem with callbacks are different signatures (the argument list and return value) of the callback functions, that requires a manually wrapping of each callback function signature and for each supported destination language (see Section 2.1, “What should a pluglib provide”).

LiMaL supports callbacks, providing a template class limal::CallbackBase<Request,Result>. The template allows to specialize the callback using two classes Request and Result, representing the arguments and the return value of the callback method. As result, the callback class makes use of only one callback function (method) signature that is embedded in a callback object. For example:


  //
  // declare your Request (argument) and Result (return value) classes
  //
  class MyCBRequest { /* ... */ };
  class MyCBResult  { /* ... */ };

  //
  // specialize the callback interface for MyCBRequest/MyCBResult
  //
  typedef limal::CallbackBase<MyCBRequest,MyCBResult> MyCallback;

  //
  // declare and implement the function calling the specialized callback
  //
  int doit_using_a_callback(MyCallback *cb);
      

Using this interface for callbacks enables us to provide a generic SWIG wrapper (currently implemented for perl only). For more informations, see the documentation of the limal::CallbackBase and example in the limal_Callback.i providing macros to specialize and instantiate a wrapped callback with SWIG.

3.2.11. LiMaL Pluglib naming Guide

This is a naming guide for LiMaL pluglibs. It defines standardized naming in the public APIs of pluglibs.

The function name consists of a several words. Only the first one is lowercase, every other word must start with an uppercase letter, for example createClientCertificate.

Class names begin with a uppercase character.

3.2.11.1. A factory method to create a new instance of an entity

  • create<What>(...) To create a new, non-existent ... entity

  • get<What>(...) To read, get, extract, ... an existing entity.

3.2.11.2. Methods to manipulate instances of entities

  • set<What>(...) Commit the changes done via the entity to the configuration cache.

  • destroy<What>(...) Frees the entity, throwing away all changes done via it if not called Set before.

  • remove<What>(...) To delete, remove ... an entity.

3.2.11.3. Initialization and flushing the caches (making the change permanent)

  • init<What>(...) To read/initialize the data in a pluglib

  • commit<What>(...) To write/perform the changes for pluglibs with caches.

3.2.11.4. Manipulating the entity's values

  • get<What>(...) To read, get, extract, ... as specific value of the entity.

  • is<What>(...) To read, get, extract, ... a specific boolean value of the entity.

  • enable<What>(...) To enable, set as true ... a specific boolean value of the entity.

  • disable<What>(...) To disable, set as false ... a specific boolean value of the entity.

  • set<What>(...) To set, write, ... the specific value of the entity or an existing entity.

If there is good reason, other function prefixes are allowed. For example in CA-Management we have revokeCertificate or revokeCA. This is because one does not delete a certificate, it is revoked. This is a habitual language use and quite a good reason to use the Revoke prefix.

3.3. Utilities for internal use

3.3.1. Logging

BloCxx provides very flexible configureable logging mechanisms with a log4j like interface. It provides implementations of common Loggers allowing to log to syslog, file, stderr.

For more informations about the BloCxx Logger implementation, see BloCxx documentation, logging.txt and the examples directory in the blocxx source package.

1. Logging in a PlugLib

Additionally to the BloCxx Logger, LiMaL provides a limal::Logger (helper) class, that allows a simplified usage of the blocxx::Logger inside of a PlugLib. The PlugLib has only to create a limal::Logger instance and pass its name to the constructor. The name will be used as a local component name for all log messages. A PlugLib with the name "xXx" may use the logger as in the following example:

  #include <limal/Logger.hpp>
  #include <blocxx/Format.hpp>

  // create Logger instance
  limal::Logger logger("xXx");

  // and use it via generic macro:
  LIMAL_SLOG(logger, "DEBUG", "doing something with '"
                              << str << "'");

  // or a more specific one:
  LIMAL_SLOG_DEBUG(logger, "doing something with '"
                           << str << "'");

  // alternatively using the blocxx::Format class:
  LIMAL_LOG(logger, "INFO", blocxx::Format("Formating %1 with %2.",
                                           "/dev/hda1", "reiserfs"));

  // or also using a temoporary object
  LIMAL_LOG(limal::Logger("yYy"), "ERROR", "What's this?!");
            

As you can see in above example, there are LIMAL_LOG and LIMAL_SLOG macros. The difference is that LIMAL_LOG expects an expression that evaluates into a String object. The LIMAL_SLOG variant allows to use the stream output operator << to construct the message.

Each pluglib generated from limal-XXxx skeleton, provides also a pluglib private Utils.hpp header file, that contains a once more simplifing macro LOGIT(level,message), initializing the limal::Logger instance with the name of the pluglib as local component.

2. Logger in Application

The Application using BloCxx or LiMaL libraries has to register either a default or a per thread Logger instance to BloCxx using the Logger::setDefaultLogger() or Logger::setThreadLogger() functions provided in blocxx and limal namespaces. If the Application forgets to register a logger, the messages from LiMaL and BloCxx libraries are discarded (sent to the NullLogger, that will be created on demand).

  #include <limal/Logger.hpp>
  
  // component and category filters
  blocxx::Array<blocxx::String> components(1, "*");
  blocxx::Array<blocxx::String> categories(1, "*");
  
  // create a Logger instance
  blocxx::LoggerRef logger( limal::Logger::createSyslogLogger(
                            // default component name, filters
                            "MyApp", components, categories,
                            // log message format definition
                            "%r [%d] %p %c - %m",
                            // syslog identity and facility
                            "myapp", "user"
                          );
  
  // register it as default logger
  limal::Logger::setDefaultLogger(logger);
  
  // or for each thread:
  limal::Logger::setThreadLogger(logger);
	

Alternatively the Application can use the BloCxx Appender interface directly, for example:

  #include <blocxx/Logger.hpp>
  #include <blocxx/AppenderLogger.hpp>
  #include <blocxx/FileAppender.hpp>
  
  // create a LogAppenderRef to a FileAppender
  blocxx::LogAppenderRef appender(new FileAppender(
      blocxx::LogAppender::ALL_COMPONENTS,
      blocxx::LogAppender::ALL_CATEGORIES,
      "/path/to/log.file",
      FileAppender::STR_DEFAULT_MESSAGE_PATTERN,
      0, // maximal file size in kb
      0  // number of backup files
  ));
  
  // create a LoggerRef using the appender
  blocxx::LoggerRef       logger(new AppenderLogger(
                                 "MyApp",
                                  E_ALL_LEVEL,
                                  appender
                          ));
  
  // register it as default logger
  blocxx::Logger::setDefaultLogger(logger);

  // or for each thread:
  blocxx::Logger::setThreadLogger(logger);
	    

Or also implement own Logger or LogAppender and let blocxx use it. See also limal/src/Logger.cpp in the limal repository - the above example code is stolen from the limal::Logger::createFileLogger() function.

3.3.2. Locking

1. Thread Locking

BloCxx provides a Mutex class implementing a recursive mutex and a MutexLock helper class that can be used to acquire the Mutex in a scope, for example:

  #include <blocxx/Mutex.hpp>
  #include <blocxx/MutexLock.hpp>
  
  namespace {
	blocxx::Mutex	    mutex;

	doitOne()
	{
	    blocxx::MutexLock lock(mutex);

	    // Just do it, we are in locked state in the
	    // current thread as long as lock variable
	    // doesn't leave its scope.

	    // It is also fine to call doitTwo, since the
	    // mutex is recursive:
	    doitTwo();

	    // go on..
	}

	doitTwo()
	{
	    blocxx::MutexLock lock(mutex);

	    // do something, we are in locked state now
	}
  }
	    

Of course BloCxx provides also a NonRecursiveMutex class with its a NonRecursiveMutexLock helper. Synchronization using a conditional variable is provided by the Condition, ConditionResource and ConditionLock classes.

2. Interprocess Locking

BloCxx provides an Interprocess Locking Mechanism which is described here.

3. File Locking

BloCxx has an own file handling class which includes a fctnl based locking mechanism:

  • trylock

    Acquire a kernel lock on the file. It returns immediately if the file has been already locked.

  • getLock

    Acquire a kernel lock on the file. If the file has been already locked the function will wait until the file will be unlocked by the other process.

  • unlock

    Release a lock on the file.

3.3.3. BloCxx Exec

In order to manage system commands on the local machine, do NOT use calls like the "system" command of C. For security reasons please use the BloCxx calls which are provided in the Exec namespace.

3.3.4. File Utitlities

1. File Informations

The limal package provides classes for getting file information and manipulate files ( location, attributes,...):

2. File Parser

This class provides a set of functionality for reading and writing INI files. The parser can be configured for almost each entry/value/comment format by using regular expressions. For more information have a look into the online docu of the limal package. Meanwhile there is an example for reading/writing a sysconfig file:

    #include <limal/INIParser.hpp>

    using namespace blocxx;
    using namespace limal;
    using namespace limal::INI;

    blocxx::Array<Options>  		options; // Options like NO_NESTED_SECTIONS, LINE_CAN_CONTINUE, ...
    blocxx::StringArray 		commentsDescr; // Regular expression of the comments description
    blocxx::Array<SectionDescr> 	sectionDescr; // Regular expression of a section description
    blocxx::Array<EntryDescr> 		entryDescr; // Regular expressions for entries (keys/values).
    blocxx::Array<IoPatternDescr> 	rewrites; // rules for writing key/value

    options.append (GLOBAL_VALUES); // Values at the top level(not in section) are allowed
    options.append (LINE_CAN_CONTINUE); // if there is \ at the end of line,
    options.append (COMMENTS_LAST); // Lines are parsed for comments after they are parsed for values 

    // comment description

    commentsDescr.append("^[ \t]*#.*$"); 
    commentsDescr.append("#.*");
    commentsDescr.append("^[ \t]*$");

    // Entry (key/value) description

    IoPatternDescr pattern = { "([a-zA-Z0-9_]+)[ \t]*=[ \t]*\"([^\"]*)\"", "%s=\"%s\""};
    EntryDescr eDescr =  {pattern, "([a-zA-Z0-9_]+)[ \t]*=[ \t]*\"([^\"]*)", "([^\"]*)\"" , true};    
    entryDescr.append (eDescr);

    IoPatternDescr pattern2 = {"([a-zA-Z0-9_]+)[ \t]*=[ \t]*([^\"]*[^ \t\"]|)[ \t]*$", "%s=\"%s\""};
    EntryDescr eDescr2 =  {pattern2, "", "" , false};
    entryDescr.append (eDescr2);

    IoPatternDescr pattern3 = {"^[ \t]*([a-zA-Z_][a-zA-Z0-9_]*)[ \t]*=[ \t]*'([^']*)'", "%s='%s'" };
    EntryDescr eDescr3 =  {pattern3, "([a-zA-Z_][a-zA-Z0-9_]*)[ \t]*=[ \t]*'([^']*)", "([^']*)'" , true};
    entryDescr.append (eDescr3);

    INIParser descParser;        

    // which file has to be parsed ?

    descParser.initFiles ("/etc/sysconfig/mail");

    // init the rest of parser

    if (!INIParser::initMachine (options, commentsDescr, sectionDescr,
				 entryDescr, rewrites))
    {
	return "ERROR";
    }

    // parsing file

    if (!descParser.parse ())
    {
        return "ERROR";
    }

    return descParser.iniFile.getValue("SMTPD_LISTEN_REMOTE");


	

The example has been taken from the additional class "SysConfig" which is derived from INIParser:

	SysConfig : public INIParser
	{
	  public:
	    SysConfig() : INIParser() {};

	    bool initMachine ();
	};
	

So the part of initialisation of sysconfig files is much more comfortable.

3.3.5. String Utilities

The BloCxx string is a UTF8 String implementation that includes utility functions like toUpperCase, toLowerCase, compareToIgnoreCase and trimming of strings. Where required, it automaticaly converts the string into a Wide-Character to perform operations on single characters using its own converters in the UTF8Utils namespace.

1. Encoding Conversions

BloCxx provides an optional IConv_t class wrapping the the POSIX iconv converter supporting conversion between nearly all character encondings (at least on Linux). Further it provides some utility functions in the IConv namespace.

2. Regular Expressions

BloCxx provides simple PosixRegEx and PerlRegEx classes, that wrapps the POSIX/Perl regular expression interface (provided by the glibc and the pcre libraries). The classes provides also most common utilities methods like capture, replace, grep and split.

3.3.6. Network

BloCxx provides the classes Socket and ServerSocket for network communication.

3.4. Documentation

3.4.1. Introduction

In order to ensure a unified look in our documentation we will use the templates of the package limal-devtools. Please ensure that this package will be installed on your developing system.

There are two kinds of documentation:

  • General Documentation

    This documentation can be found in <package>/doc and is written in DocBook-XML.

  • Inline Documentation

    Modules, functions and variables are described in the source code. We will use doxygen and special stylesheets. The documentation will be generated automatically by calling make

    The documentation will be generated (in HTML format) in <package>/doc/autodocs.

3.4.2. General Documentation

As decribed above this kind of documentation is located in <package>/doc and is written in DocBook-XML.

A short description of DocBook-XML can be found here.

The source XML files and the stylesheets (given by package limal-devtools) are defined in the accordingly <package>/doc/Makefile.am:

#
# Makefile.am for limal-utils/doc
#

SUBDIRS = autodocs

htmldir = $(docdir)
xml_files =  $(wildcard *.xml) 

html_DATA = $(wildcard html/*.html) \
	    html/index.html \
	    html/limaldocs.css

pdf: limal-utils.pdf

html/index.html: $(xml_files)
	XML_CATALOG_FILES=@XML_CATALOG@\
	@XSLTPROC@ @XSLTPROC_FLAGS@ --xinclude \
	@STYLESHEET_HTML@ limal-utils.xml

limal-utils.fo: $(xml_files)
	XML_CATALOG_FILES=@XML_CATALOG@ \
	@XSLTPROC@ @XSLTPROC_FLAGS@ --xinclude \
	-o $@ @STYLESHEET_PDF@ limal-utils.xml

limal-utils.pdf: limal-utils.fo
	fop -q $< $@


html/limaldocs.css: html/index.html
	cp @STYLESHEET_CSS@ html
	cp -a $(limaldatadir)/docbook/images html


EXTRA_DIST = $(xml_files)

CLEANFILES =  $(html_DATA)

clean-local:
	rm -rf html

install-data-local:
	mkdir -p $(DESTDIR)$(htmldir)
	cp -a $(srcdir)/html/images $(DESTDIR)$(htmldir)
      

This is an example for a source XML file. Every XML file should have following attributes at least:


<?xml version="1.0" encoding='ISO-8859-1'?>
<!DOCTYPE book PUBLIC "-//OASIS//DTD DocBook XML V4.1.2//EN"
	"/usr/share/xml/docbook/schema/dtd/4.2/docbookx.dtd"
[
<!ENTITY version "2.10.0">
<!ENTITY dx "DocBook-XML">

<!ENTITY date "Januar 2005">
]>

<book id="limal">
  <bookinfo>
    <title>LiMaL Packages</title>
    <subtitle>LiMaL package description.</subtitle>
    <author>
       <firstname>Stefan</firstname>
       <surname>Schubert</surname>
    </author>
    <copyright><year>2005</year><holder>SUSE LINUX GmbH</holder></copyright>
    <abstract>
      <para>LiMaL Package Description. Version 0.0.1 - Januar 2005</para>
    </abstract>
  </bookinfo>
    <chapter id="main">
.
..
...
..
.
    </chapter>
</book>

      

3.4.3. Inline Documentation

Modules, functions and variables are described in the source code. We will use doxygen and special stylesheets. The documentation will be generated automatically by calling make

The documentation will be generated (HTML format) in <package>/doc/autodocs.

First of all you have to configure the concerning <package>/doc/autodocs/Makefile.am:

#
# Makefile.am for limal-utils/doc/autodocs
#

htmldir = $(docdir)/autodocs

html_DATA = index.html $(wildcard *.html *.png) doxygen.css @RPMNAME@.tag

CLEANFILES = $(html_DATA) doxygen.log doxygen.conf installdox

doxygen.css @RPMNAME@.tag: index.html

header_dir = $(srcdir)/../../src/limal/utils/

index.html: $(header_dir)/*.hpp 
        ${LIMALDOXYGEN} --prefix=$(prefix) PROJECT_NAME=@RPMNAME@ TAGFILES= INPUT=$(header_dir)

        

You have to declare which files ( e.G *.h ) have to be parsed by doxygen in order to generate the documentation of modules, functions, variables, ...

Lets have a look to a short example in order to show the available tags:


   File:       SysConfig.h

   Author:     Cornelius Schumacher <cschum@suse.de>
   Maintainer: Cornelius Schumacher <cschum@suse.de>

/-*/
#ifndef SysConfig_h
#define SysConfig_h

#include <string>
#include <map>

/**
@brief Reading and Writing Configuration files.
*/

/**
  This class provides access to the configuration files under /etc/sysconfig.
  
  A sysconfig file consists of lines of key/value pairs. It can also contain
  empty lines and comments (lines starting with "#").
  
  Keys are case-sensitive. Values can be quoted like 'value' or "value".
  Whitesspace at the beginning or end of keys and values (outside of quotes) is
  removed.
*/
class SysConfig
{
  public:
    /**
      Construct a sysconfig object. Reads the given file.
      
      @path Path to sysconfig file. This can either be an absolute path or a
            path relative to /etc/sysconfig.
    */
    SysConfig( const char *path );

    ~SysConfig();
        
    /**
      @return string entry

      @param key          Key of entry.
      @param defaultValue Default return value, if key doesn't exit.
    */
    std::string readEntry( const std::string &key,
                           const std::string &defaultValue = std::string() );


};

#endif

        

After creating the Makefile with make -f Makefile.cvs in the package root directory, the document will be generated automatically by every calling of make.

The documentation will be generated (HTML format) in <package>/doc/autodocs.

For the example (described above) the output file classSysConfig.html would be generated.

Here is a list of common used parameters in doxygen:

  • brief

    Starts a paragraph that serves as a brief description. For classes and files the brief description will be used in lists and at the start of the documentation page. For class and file members, the brief description will be placed at the declaration of the member and prepended to the detailed description. A brief description may span several lines (although it is advised to keep it brief!). A brief description ends when a blank line or another sectioning command is encountered. If multiple @brief commands are present they will be joined.

  • return

    Starts a return value description for a function. The text of the paragraph has no special internal structure. All visual enhancement commands may be used inside the paragraph. Multiple adjacent @return commands will be joined into a single paragraph. The \return description ends when a blank line or some other sectioning command is encountered.

  • param <parameter-name>

    Starts a parameter description for a function parameter with name <parameter-name>. Followed by a description of the parameter. The existence of the parameter is checked and a warning is given if the documentation of this (or any other) parameter is missing or not present in the function declaration or definition.

    The @param command has an optional attribute specifying the direction of the attribute. Possible values are "in" and "out". Here is an example for the function memcpy:

    /*!
     Copies bytes from a source memory area to a destination memory area,
     where both areas may not overlap.
     @param[out] dest The memory area to copy to.
     @param[in]  src  The memory area to copy from.
     @param[in]  n    The number of bytes to copy
     */
    void memcpy(void *dest, const void *src, size_t n);
    	        

    If a parameter is both input and output, use [in,out] as an attribute.

    The parameter description is a paragraph with no special internal structure. All visual enhancement commands may be used inside the paragraph.

    Multiple adjacent @param commands will be joined into a single paragraph. Each parameter description will start on a new line. The @param description ends when a blank line or some other sectioning command is encountered.

  • exception <exception-object>

    Starts an exception description for an exception object with name <exception-object>. Followed by a description of the exception. The existence of the exception object is not checked. The text of the paragraph has no special internal structure. All visual enhancement commands may be used inside the paragraph. Multiple adjacent @exception commands will be joined into a single paragraph. Each parameter description will start on a new line. The @exception description ends when a blank line or some other sectioning command is encountered.

    Example:

    
        /**
          @brief  Reading Entries
          @return string entry
    
          @param key          Key of entry.
          @param defaultValue Default return value, if key doesn't exit.
          @exception std::out_of_range parameter is out of range.
        */
        std::string readEntry( const std::string &key,
                               const std::string &defaultValue = std::string() ) throw(std::out_of_range) {};
    	        

  • code

    Starts a block of code. A code block is treated differently from ordinary text. It is interpreted as C/C++ code. The names of the classes and members that are documented are automatically replaced by links to the documentation.

  • endcode

    Ends a block of code.

  • todo

    Starts a paragraph where a TODO item is described. The description will also add an item to a separate TODO list. The two instances of the description will be cross-referenced. Each item in the TODO list will be preceded by a header that indicates the origin of the item.

  • warning

    Starts a paragraph where one or more warning messages may be entered. The paragraph will be indented. The text of the paragraph has no special internal structure. All visual enhancement commands may be used inside the paragraph. Multiple adjacent @warning commands will be joined into a single paragraph. Each warning description will start on a new line. Alternatively, one @warning command may mention several warnings. The @warning command ends when a blank line or some other sectioning command is encountered.

  • note

    Starts a paragraph where a note can be entered. The paragraph will be indented. The text of the paragraph has no special internal structure. All visual enhancement commands may be used inside the paragraph. Multiple adjacent @note commands will be joined into a single paragraph. Each note description will start on a new line. Alternatively, one @note command may mention several notes. The @note command ends when a blank line or some other sectioning command is encountered.

  • see or sa

    Starts a paragraph where one or more cross-references to classes, functions, methods, variables, files or URL may be specified. Two names joined by either :: or # are understood as referring to a class and one of its members. One of several overloaded methods or constructors may be selected by including a parenthesized list of argument types after the method name.

  • b <word>

    Displays the argument <word> using a bold font. Equivalent to <b>word</b>. To put multiple words in bold use <b>multiple words</b>.

3.5. Testsuite

This is a description of how to test your LiMaL source code.

3.5.1. Introduction

Main task of testing your source code is to ensure same functionality after any change you do. There is usually some code depending on your source, so if you do a change, you must be sure the outer behavior remains a same.

The testsuite can be also used for testing functionality on other machines and architectures you don't have access to or on any strange hardware configurations.

And at last, with a well written testsuite you can test your code in a random circumstances, with random input and for long time like nobody alive can ever do.

3.5.2. Requirements

The testsuite is based on dejagnu. So following packages have to be installed:

  • dejagnu.rpm

  • expect.rpm

  • tk.rpm

  • tcl.rpm

3.5.3. Environment

Test will be run with NO root rights. So you have to keep this in mind if you want to access resources with root permissions only.

In order to solve this problem it is useful to define functions in your lib,plugin,... for setting an testsuite environment. e.g.: setEtcDirectory(<etc-testsuite-directory>) for changing the path of the /etc directory.

3.5.4. Creating Tests

The simplest way to generate testcases is to use the testsuite envirnoment of the LiMaL package template.

There are two kinds of testcases defined in this testsuite which are passed through a test run:

  • Single tests are called one time.

  • Multiple tests are called several times with several parameters.

These two testcases are defined in the directory testsuite/<program>.test:

  • single_test.exp

  • multi_test.exp

If these kinds of testcases do not fullfill your requirements you can define your own kind of testcase by adding a new file in this directory:

testsuite/<program>.test/<new-name>.exp:

3.5.5. Single Tests

While a test run single tests are called one time.

  • A testprogram with the name testsuite/<programname>.single will be called without parameters.

    This program has been created by yourself. The program returns 0 if it has been finished successfully. The output of the program is stdout and stderr.

  • The output (stderr,stdout) of this testprogram will be compared with output described in testsuite/single.out/<programname>.out and testsuite/single.out/<programname>.err.

  • The test has been successful if the testprogram has returned 0 and the output is the same as in testsuite/single.out/<programname>.out and testsuite/single.out/<programname>.err described.

3.5.6. Multiple Tests

Multiple tests are called several times with several parameters.

So all programs with the extention "multi" will be execute for several times.

Each testprogram testsuite/<programname>.multi has an own subdirectory testsuite/<programname> in which several testcases are defined for this program.

Each testcase has 3 files:

  • <testcasename>.in

  • <testcasename>.out

  • <testcasename>.err

The program "<programname>.multi" will be called with the argument "<testcasename>.in". The standard output will be compared with the files "<testcasename>.out" and "<testcasename>.err". The testcase is successful if the program returns 0 AND the outputs are identically equal to the files "<testcasename>.out" and "<testcasename>.err".

3.5.7. Running Tests

When you have written the testprograms and have compiled it with "make -f Makefile.cvs; make" you can run it with "make check".

The standard output will be written to file testsuite/single.out/out/<programname>.out ( tests which are called one time ) or testsuite/<programname>/out/<testcase>.out ( tests which are called several time ).

The error output of multiple tests will be written to file testsuite/<programname>/out/<testcase>.err. It should be an empty file.

If the output files are OK, copy them to testsuite/single.out/ ( tests which are called one time ) or testsuite/<programname>/ ( tests which are called several time ).

Now, you have a (one) testsuite ready. The output files will be used during the next check run ( with make check ) for comparison with real output to decide if the test passed.

3.6. Programming Guideline

In order to ensure a unified look in the source code we will have to come to an agreement using the same coding style. We basically suggest the LiMaL Coding Guide:

1. General LiMaL Coding Guidelines

This guide describes the LiMaL Coding Guide, it rests upon the Blocxx Coding Guide. So in this document you will only find the details which are changed towards the Blocxx Coding Guide. Or said the other way around, if something isn't explained here you will find it in the Blocxx Coding Guide. We like to mention again that in blocxx the default indention is by tabs NOT by spaces. If someone would like to change the spacing he is free to change the tabwidth in his editor. But this doesn't change the fact that only tabs are allowed to indent the code.

2. Return Values

Return values of functions have to be placed one line above the function itself. This has to be done in the declaration as well as in the definition.

It looks like:


	Yes:	blocxx::String
		getText() const;
		
	No:	blocxx::String getText() const;
	 
	Yes:	blocxx::String
		TestNamespace::getText() const
		{
		}
		
	No:	blocxx::String TestNamespace::getText() const
		{
		}
	

[Note]Justification:
Because return values can consist of long terms especially if they have a namespace in front. This leads into badly readable code, and you can't see the return value at first sight.

3. Line Wrapping

Lines should be wrapped after 80 characters. So e.g. long conditions in if- clauses, have to be wrappend. The wrapping should take place after the last operator, so that the next line starts with a condition. Also wrapped lines have to be indented, so that the following line starts after the bracket in the line before. this indention in difference to the normal indention in blocxx, has to be made with spaces.

Otherwise you won't be able to set the beginning of the conditions to the same column.

An example is given below:

		
		if (functA == true || functB != true ||
		    functC == false)
		{
		}
	^^^^^^^^
	   Tab  ^^^^
	        Space
	
	

4. Variablename indention

Names of variables should be indent in relation to their type. That means that the name of the variable can be clearly distinguished from the type. Furthermore if there are many declarations the name of all variables should be in the same column like shown below:
		
	No:	blocxx::String   test;
		blocxx::StringArray  test2;
       
	Yes:	blocxx::String       test;
		blocxx::StringArray  test2;
       
[Note]Justification:
Indention helps to split up the variable name from the rest of the declaration. So the name itself is clearly readable. The same indention for all declarations helps thereby, because the eyes can stay in the same column.

5. Basis of LiMaL Coding Guide

The LiMaL Coding Guide is mainly based on BloCxx Coding Guide because:

  • LiMaL based on BloCxx.

  • The BloCxx guideline is similar to the old YaST guideline.

The complete text of BloCxx Coding Guide can be found here.

6. Tools and editor preferences

Furthermore there exist 3 tools which help you to format old code to the new LiMaL Coding Guidelines. A desciption of these tools and some helpful editor preferences can be found here. The config files, the shell script and the editor prefereces described are available here.

Chapter 4. LiMaL License

LiMaL is licensed under the GPL as YaST is.

Chapter 5. What is BloCxx ?

BloCxx is derived from the OpenWBEM project, where it has been also used as a common set of base classes. The advantage of BloCxx is, that it is a robust set of code which has been used since a long time. Additionally it is running on all kinds of operating systems, which even includes Win32.

BloCxx is hosted on http://forge.novell.com/modules/xfmod/project/?blocxx

Similar to sourceforge, forge.novell.com hosts open source projects, which is, what BloCxx already is.

There's also a mailinglist at http://forge.novell.com/modules/xfmod/maillist/?group_id=1634

Feel free to subscribe. Discussions regarding the system management library must still take place on the list limal-devel@suse.de, however.