summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorPeter Schiffer <pschiffe@redhat.com>2013-04-26 17:02:26 +0200
committerPeter Schiffer <pschiffe@redhat.com>2013-04-26 17:02:26 +0200
commit8de6807e5017fd5f8be76d6b70d1eb1387c24a62 (patch)
tree9f81978d61ae9a7f160537221a806d665f1c00a5
parent6c60ed63585024726dd43e35130d6cf2679e16fc (diff)
parent338851a8fe5b9dfec1e9695602d501e1fe918912 (diff)
downloadopenlmi-providers-8de6807e5017fd5f8be76d6b70d1eb1387c24a62.tar.gz
openlmi-providers-8de6807e5017fd5f8be76d6b70d1eb1387c24a62.tar.xz
openlmi-providers-8de6807e5017fd5f8be76d6b70d1eb1387c24a62.zip
Merge branch 'master' of ssh://git.fedorahosted.org/git/openlmi-providers
-rw-r--r--CMakeLists.txt2
-rw-r--r--README111
-rw-r--r--doc/cim-provider-howto.md1213
-rw-r--r--mof/60_LMI_Realmd.mof519
-rw-r--r--mof/70_LMI_SoftwareIndicationFilters.mof24
-rwxr-xr-xopenlmi-cimmof340
-rwxr-xr-xopenlmi-mof-register78
-rw-r--r--src/CMakeLists.txt4
-rw-r--r--src/python/openlmi/common/IndicationManager.py60
-rw-r--r--src/python/openlmi/common/JobManager.py134
-rw-r--r--src/realmd/CMakeLists.txt50
-rw-r--r--src/realmd/LMI_HostedRealmdServiceProvider.c233
-rw-r--r--src/realmd/LMI_RealmdKerberosRealmProvider.c627
-rw-r--r--src/realmd/LMI_RealmdRealmProvider.c326
-rw-r--r--src/realmd/LMI_RealmdServiceProvider.c631
-rw-r--r--src/realmd/LMI_ServiceAffectsRealmdRealmProvider.c252
-rw-r--r--src/realmd/README221
-rw-r--r--src/realmd/VERSION1
-rw-r--r--src/realmd/doc/class_diagram.svg273
-rw-r--r--src/realmd/doc/examples/realmd-cim245
-rw-r--r--src/realmd/rdcp_dbus.c2050
-rw-r--r--src/realmd/rdcp_dbus.h74
-rw-r--r--src/realmd/rdcp_error.c123
-rw-r--r--src/realmd/rdcp_error.h38
-rw-r--r--src/realmd/rdcp_realmdrealm.h310
-rw-r--r--src/realmd/rdcp_util.c311
-rw-r--r--src/realmd/rdcp_util.h116
-rw-r--r--src/realmd/realm-dbus-constants.h66
-rw-r--r--src/software/openlmi/software/LMI_SoftwareInstallationJob.py2
-rw-r--r--src/software/openlmi/software/cimom_entry.py73
-rw-r--r--src/software/openlmi/software/yumdb/jobmanager.py81
31 files changed, 8416 insertions, 172 deletions
diff --git a/CMakeLists.txt b/CMakeLists.txt
index b42debc..f25850e 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -18,6 +18,7 @@ option(WITH-SERVICE "Build service provider" ON)
option(WITH-ACCOUNT "Build account provider" ON)
option(WITH-HARDWARE "Build hardware provider" ON)
option(WITH-LOGICALFILE "Build logical file provider" ON)
+option(WITH-REALMD "Build RealmD provider" ON)
# Set path to custom cmake modules
set(CMAKE_MODULE_PATH ${CMAKE_CURRENT_SOURCE_DIR}/cmake/modules ${CMAKE_MODULE_PATH})
@@ -36,6 +37,7 @@ add_subdirectory(tools)
install(PROGRAMS openlmi-mof-register DESTINATION bin)
install(PROGRAMS openlmi-register-pegasus DESTINATION libexec)
+install(PROGRAMS openlmi-cimmof DESTINATION libexec)
install(FILES cmake/modules/OpenLMIMacros.cmake DESTINATION share/cmake/Modules)
install(FILES cmake/modules/FindCMPI.cmake DESTINATION share/cmake/Modules)
install(FILES cmake/modules/FindKonkretCMPI.cmake DESTINATION share/cmake/Modules)
diff --git a/README b/README
index 7e17e45..f1cf2a8 100644
--- a/README
+++ b/README
@@ -51,10 +51,15 @@ Following providers are part of this sub-project:
Allows to install, remove, list and update packages.
Requires python 2.6+ and yum.
+* RealmD
+ This is a CIM interface for the RealmD daemon which allows for the Kerberos
+ and Active Directory realms enrollment
+
*******************************************************************************
* Build Dependencies *
*******************************************************************************
For all providers:
+ - cmake
- konkretcmpi-devel
- sblim-cmpi-devel
@@ -71,7 +76,9 @@ Provider specific dependencies:
* Service
- chkconfig
- systemd or upstart or SysVinit
-
+* RealmD
+ - glib2-devel
+ - dbus-devel
*******************************************************************************
* Compilation and installation *
@@ -87,3 +94,105 @@ $ make
You can disable specific provider by adding -DWITH-<PROVIDER>=0 to cmake line.
+
+*******************************************************************************
+* Development Tips *
+*******************************************************************************
+
+Understanding konkret code generation issues:
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+konkret is a tool that reads in a mof file and generates C code. For
+every XXX class it will generate a XXX.h and XXXProvider.c file. The
+code generation occurs due to CMake macros provided by the
+openlmi-providers-devel package. konkret needs to run any time you
+modify the mof file. It *always* generates a new XXX.h file because
+that's where definitions based on the contents of the mof file are
+located. If there is no XXXProvider.c file it will also generate
+it. This is a "stub" file in which you will fill in with your
+implementation. If XXXProvider.c exits it will not overwrite it,
+however it always overwrites the XXX.h file.
+
+Do not put anything into the XXX.h file you'll need to retain.
+
+After editing the mof file the make targets will cause konkret to run
+again. You'll get brand new XXX.h files. But your old XXXProvider.c
+files may no longer have the correct definitions (e.g. prototypes)
+found in the XXX.h file so you may need to hand edit by copying the
+function prototype from the XXX.h file into your XXXProvider.c file.
+
+If you've written definitions that logically belong in XXX.h but don't
+want them nuked the next time konkret runs my solution was to put them
+in someother .h file that's included by the XXXProvider.c file.
+
+Initializing class instances:
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+The way konkret works is to emit specialized inline functions to
+initialize each member of a class. If the class is subclassed you get
+different initializers depending on whether the property is in the
+parent class or the subclass. You cannot call a parent class property
+initializer in a subclass (yuck), you have to use the subclass
+initializer for the property inherited from the parent class. This
+creates a maintenance problem if the parent class changes, you have
+find every place parent class properties are inialized and make
+changes. To solve this problem I defined macros that initialize class
+properties. The macro takes a "klass" parameter and token pastes it to
+generate the class specific property manipulation function call. Using
+these macros means anytime a class changes due to a change in the mof
+file there is only one place where you need to change the code. These
+macros are a good example of what logically belongs in the XXX.h file
+but are separated out into a different .h file because konkret will
+nuke anything you've added to a XXX.h file.
+
+Modifications to the provider:
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+During development if the mof file changes you have to make Pegasus
+reload the mof. It's not sufficient to retart cimserver, you have to
+unregister the provider and register it again for Pegasus to see the
+mof changes. Thus you would use the openlmi-mof-register command above
+except pass the unregister option, followed by the above command, e.g.
+
+% openlmi-mof-register unregister /usr/share/openlmi-providers/LMI_Realmd.mof /usr/share/openlmi-providers/LMI_Realmd.reg
+% openlmi-mof-register register /usr/share/openlmi-providers/LMI_Realmd.mof /usr/share/openlmi-providers/LMI_Realmd.reg
+
+If all you've done during devopment is modify the provider (not it's
+mof definition) then all you need to do is:
+
+% sudo cimserver -s
+% sudo make install
+% sudo cimserver
+
+How do I run the Pegasus CIMOM so I can see debug statements?
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+% sudo cimserver daemon=false forceProviderProcesses=false
+
+How do I use GDB to debug my provider?
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+Create the following .gdbinit file where XXX is where you want to break.
+
+<<<<<<<<<<
+set breakpoint pending on
+b XXX
+r daemon=false forceProviderProcesses=false
+>>>>>>>>>>
+
+then run gdb like this:
+
+% sudo gdb cimserver
+
+How do I trace what Pegasus is doing?
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+% cimserver daemon=false forceProviderProcesses=false logLevel=TRACE traceLevel=5 traceFacility=File traceComponents=All
+
+The trace file is written to:
+
+/var/lib/Pegasus/cache/trace/cimserver.trc
+
+More information about cimserver tracing can be found in the
+"OpenPegasus Tracing User Guide" PDF. Google the title to get a
+current URL.
diff --git a/doc/cim-provider-howto.md b/doc/cim-provider-howto.md
new file mode 100644
index 0000000..da7fb0e
--- /dev/null
+++ b/doc/cim-provider-howto.md
@@ -0,0 +1,1213 @@
+% OpenLMI CIM Provider HOWTO
+% John Dennis <jdennis@redhat.com>
+% 3/30/2013
+
+# License #
+
+This document is licensed under the [Create Commons ShareAlike
+license](http://creativecommons.org/licenses/by-sa/3.0/)
+
+You are free:
+
+To Share
+ : To copy, distribute and transmit the work
+
+To Remix
+ : To adapt the work
+
+Use commercially
+
+ : To make commercial use of the work
+
+Under the following conditions:
+
+Attribution
+
+ : You must attribute the work in the manner specified by the author
+ or licensor (but not in any way that suggests that they endorse
+ you or your use of the work).
+
+Share Alike
+
+ : If you alter, transform, or build upon this work, you may
+ distribute the resulting work only under the same or similar
+ license to this one.
+
+
+# Introduction #
+
+CIM stands for Common Information Model. CIM is one component of WBEM
+(Web-Based Enterprise Management). WBEM is a technology suite allowing
+one to remotely enumerate the computing resources in an enterprise,
+query their state, modify their configuration and otherwise act upon
+those resources. People often use the term CIM when they are actually
+discussing WBEM. Technically CIM is only a schema and
+specification. The suite of specifications and technologies providing
+enterprise computer management based on the CIM model is WBEM.
+
+CIM is an open standard under the auspices of DMTF ([Distributed
+Management Task Force](http://www.dmtf.org/)). WBEM Management tools
+based on CIM allow IT administrators to manage diverse computing
+resources in a heterogeneous environment. In addtion to CIM DMTF also
+manages many of the specifications related to WBEM.
+
+If you've been asked to write a CIM provider this document will
+introduce you to the relevant technologies and guide you through the
+development process.
+
+## WBEM Components ##
+
+Although technically CIM is only a schema definition the common usage
+of the term CIM is often taken to mean the collection of tools and
+technologies enabling computer management, i.e. WBEM. Without the
+overarching technology ecosystem that WBEM provides CIM would just be
+a paper abstraction. Lets briefly explore how these independent
+components fit together to form a WBEM management solution.
+
+A manged system runs a service called the CIMOM, sometimes referred to
+as a broker. CIMOM stands for Common Information Model Object
+Manager. Typically the CIMOM is connected to the internet so that it
+can provide remote administration of the computer. The CIMOM will load
+a set of providers. Each provider is a software module dedicated to
+managing one type (i.e. class) of resource on the computer, for
+example network interfaces. There may be multiple instances of that
+resource class. The provider is responsible for managing all instances
+of that resource class. The provider in addition to providing
+information about a resource instance may optionally allow the
+resource instance to be configured or acted upon. It is the CIMOM
+which organizes all the providers on the system and grants a CIM
+client access to those providers. There are many CIMOM implementations
+available. Since all CIMOM's are supposed to follow the collection of
+WBEM standards all CIM clients should be able to inter-operate with
+each CIMOM.
+
+A CIM provider can be loaded into different CIMOM implementations if
+both the CIMOM and the provider utilize a common programming API. CMPI
+(Common Manageability Programming Interface) is the standardized API
+all CIMOM's and provider's should be coded to.
+
+A CIM client is able to connect to a CIMOM and interact with the
+resources (i.e. objects) being managed by the CIMOM. It is important
+to note *a CIM client does not interact directly with a provider*
+running in the CIMOM. Rather the CIMOM will expose to the CIM
+client the objects made available to the CIMOM via it's set of loaded
+providers.
+
+Communication between a CIM client and a CIMOM occurs via
+standardized protocols. At the time of this writing only one protocol
+is in wide use, [CIM Operations over
+HTTP](http://www.dmtf.org/standards/wbem). This protocol establishes
+HTTP headers and then passes the CIM payload as an XML document to
+the CIMOM. Responses from the CIMOM are also encoded in XML. The
+definition of the XML documents are defined in [Representation of CIM
+in XML](http://www.dmtf.org/standards/wbem). The collection of
+standards used by CIM clients and CIMOM brokers are known as WBEM
+(Web-Based Enterprise Management).
+
+In a typical scenario a CIM client will make an authenticated
+connection to a CIMOM and ask it to enumerate the set of objects
+it's interested in. The CIM client may choose to enumerate all instances
+of a specific class or it may utilize a query language to refine the
+results. CIM objects are often related to one another via
+Associations. Querying via associations is very similar to
+performing a join in SQL.
+
+CIM objects have properties and methods. A property is a piece of data
+and a method is a function you can call. A CIM client may examine the
+properties of a returned object to determine it's health state,
+configuration, status, etc. Configuration is an advanced topic
+explained in greater depth in [Resource
+Configuration](#resource-configuration).
+
+A lot of information has been presented in a short time, to help
+clarify here is simple break down of how the components fit together.
+
+1. CIM client makes authenticated connection to a CIMOM.
+
+2. Using the HTTP protocol an XML document is passed from the CIM
+ client to the CIMOM. The XML document describes the requested CIM
+ operation. Formation of the XML document is performed by CIM client
+ API library routines.
+
+3. The CIMOM interprets the XML document and calls routines in it's
+ providers to service the request.
+
+4. The CIMOM communicates with it's providers using the CMPI C
+ language API.
+
+5. The CIMOM coalesces the information supplied by it's providers
+ and forms an XML document which will then be returned as the HTTP
+ response to the CIM client.
+
+6. Library routines in the CIM client parse the XML document and
+ return the information via a CIM client API.
+
+# What do I need to know to write a CIM provider? #
+
+CIM is an extraordinarily complex topic. Without some guidance one can
+easily get lost in the wealth of material resulting in spinning your
+wheels without making a lot of progress. In this section we try to
+summarize some key aspects of CIM and direct you to information that
+will help you complete your task while helping you stay clear of
+material which is not relevant.
+
+Material deemed to be critical will be highlighted
+
+**In a single sentence like this.**
+
+## CIM Schema, MOF and Profiles ##
+
+### CIM Schema and MOF Syntax ###
+
+CIM models real world objects and their relationships. Those objects
+are modeled via CIM classes. A CIM class has properties and
+methods. The DMTF has defined a set of CIM classes which are meant to
+be the building blocks for a CIM model, this is the [CIM
+Schema](http://www.dmtf.org/standards/CIM). The CIM Schema is
+expressed in the MOF [Managed Object
+Format](http://www.dmtf.org/standards/cim) syntax. MOF files are used
+to define provider interfaces.
+
+**You must be fluent in MOF, it is the language of CIM.**
+
+### Models and Profiles ###
+
+A model is a collection of schema elements designed to model a
+computer system component which is to be managed. It defines the
+schema classes used to represent the managed elements and their
+relationships. At it's heart a model is pure CIM Schema but schema
+alone is not sufficient to explain intended usages nor the rules for
+how the schema elements interact. This expository material is
+collected into a document called a profile. Profiles follow a
+standardized format called the [Management Profile Specification
+Template](http://www.dmtf.org/standards/profiles). DMTF has already
+defined numerous profiles to cover common computer system elements,
+these are collected in the [Management
+Profiles](http://www.dmtf.org/standards/profiles) web page. For some
+reason the primary CIM page on the DMTF website does not link to the
+management profiles. This curious omission might cause you to miss the
+critical aspect of CIM profiles.
+
+**Prior to starting a CIM provider peruse the [Management
+Profiles](http://www.dmtf.org/standards/profiles) to determine if one
+or more profiles already exist, if so you should implement that model.**
+
+## Are you creating a model or implementing an existing profile? ##
+
+If a profile already exists your job is tremendously simplified. The
+profile lays out the exact classes you have to implement along with
+the rules for how they interact. Chapter 9 of the profile is
+especially useful because it illustrates expected use cases with
+examples, this can greatly aid your comprehension of the model and
+it's profile. If a profile exists it is not necessary to understand
+the breath and depth of the CIM Schema, the necessary schema elements
+have already been assembled for you. At this point you can move on to
+the provider implementation tasks at this point using the profile as a
+recipe.
+
+However if a profile does not yet exist for your provider you must
+define one. Unfortunately this is a very challenging task, it demands
+an in-depth understanding the CIM Schema as well as a working knowledge
+of the existing profiles. You need a familiarity with the existing profiles
+in order to understand the design patterns of CIM, otherwise your
+provider will not function as expected.
+
+If you do find yourself in the position of having to author a
+model/profile then you should read the [Using the
+Schema](http://www2.informatik.hu-berlin.de/~xing/Lib/cim-tutorial/using/index.html)
+and [Extending the
+Schema](http://www2.informatik.hu-berlin.de/~xing/Lib/cim-tutorial/extend/index.html)
+sections in this [tutorial
+section](http://www2.informatik.hu-berlin.de/~xing/Lib/cim-tutorial/intro/components.html). It
+will provide the conceptual framework for schema design decisions.
+
+
+## CMPI and KonkretCMPI ##
+
+OpenLMI encourages the use of KonkretCMPI to aid provider
+development.
+
+**You will want to read the** [KonkretCMPI
+Documentation](http://konkretcmpi.org/KonkretCMPI.html) **to
+understand the basic development process with KonkretCMPI.**
+
+You might be tempted after reading the KonkretCMPI documentation to
+dive and begin writing a provider believing KonkretCMPI given you
+all you need to know to complete the task. But the truth is you're not
+writing to a KonkretCMPI API, instead you're writing your provider
+using the CMPI API. Essentially what KonkretCMPI does is insulate you
+from CMPI. KonkretCMPI gives you a layer over CMPI that provides a
+nice level of abstraction and other utility support. The other primary
+advantage of KonkretCMPI is it automatically generates all the
+necessary stub functions needed to comply with CMPI. After KonkretCMPI
+runs you need to add your implementation to the function stubs
+KonkretCMPI generated for you. Overall this simplifies the development
+process.
+
+However, if you don't have an understanding of CMPI from the outset
+you'll likely find what KonkretCMPI generates confusing because it
+will seem to exist in a vacuum. You won't necessarily understand how
+or why all the code pieces generated by KonkretCMPI fit together. You
+might find yourself asking were certain initialization is performed or
+in what order, how to manage life cycle, how are errors handled,
+etc. All of this is clearly spelled out in the CMPI spec. You'll
+probably also discover what KonkretCMPI gives to is incomplete, not
+everything you may need to do in your provider is covered by
+KonkretCMPI. There is an excellent chance you'll need to call the CMPI
+API directly for services not provided by KonkretCMPI. It's best to
+think of KonkretCMPI as a CMPI helper, it is not a CMPI replacement
+(i.e. wrapper around CMPI).
+
+**Therefore you should also read the** [CMPI
+Specification](https://www2.opengroup.org/ogsys/catalog/C061)
+
+The CMPI specification is long, here is my suggestion for reading it
+to get started. Read chapters 1-5, those chapters lay out the basic
+model and groundwork. If you understand those concepts you're in good
+shape. The remaining chapters list the function tables and detailed
+descriptions of each function in it's respective table. You don't need
+to know the details of each function, but it helps to know what is
+available. I would suggest perusing the beginning of each of these
+remaining chapters just to see what's available in table, you can skip
+the per function detail.
+
+The CMPI specification describes the CMPI API at the raw C level. It
+can be very verbose code which is ripe for simplification through
+pre-processor macros. Standard macros are defined in
+`/usr/include/cmpi/cmpimacs.h`. Most code will use those macros and as
+such those macros are the *effective* CMPI API. You should be using
+these standard CMPI macros and be familiar with them for those cases
+where KonkretCMPI does not provide an alternative.
+
+**You should be familiar with the contents of `cmpimacs.h`.**
+
+## Tutorials ##
+
+At this point you now have enough background information to dive into
+the [DMTF CIM
+Tutorial](http://www.wbemsolutions.com/tutorials/DMTF/). This tutorial
+will help clarify the concepts already presented and round out
+material we have glossed over. The tutorial is brief and a bit
+superficial, it may or may not satisfy your needs for the
+comprehension of CIM.
+
+**Read the** [DMTF CIM
+Tutorial](http://www.wbemsolutions.com/tutorials/DMTF/).
+
+On the other hand the [Learn
+CIM](http://www2.informatik.hu-berlin.de/~xing/Lib/cim-tutorial/start.html)
+is much more complete and goes into much more depth. Not everyone will
+need the material here but many will find it helpful. A good strategy
+is to skim the tutorial making note of the material it covers and then
+later when confronted with a gap in your comprehension return to the
+tutorial.
+
+## Tools and Packages ##
+
+### CIMOM's ###
+
+ * [OpenPegasus](https://collaboration.opengroup.org/pegasus/)
+ OpenPegasus is an open-source implementation of the DMTF CIM and
+ WBEM standards. OpenPegasus is written in C++ and is designed to
+ be portable. It builds and runs on most versions of UNIX,
+ Linux, OpenVMS, and Microsoft Windows.
+
+ RPM package name: tog-pegasus
+
+ You may find the [OpenPegasus Administrator's Guide](http://cvs.opengroup.org/cgi-bin/viewcvs.cgi/*checkout*/pegasus/doc/Admin_Guide_Release.pdf)
+ useful for topics concerning installation, configuration,
+ authentication, etc. of OpenPegasus.
+
+ * [SFCB](http://sourceforge.net/apps/mediawiki/sblim/index.php?title=Sfcb)
+ Small Footprint CIM Broker. SFCB is a CIM server for
+ resource-constrained and embedded environments. It is written in C
+ and designed to be modular and lightweight.
+
+ RPM package name: sblim-sfcb
+
+
+### Development Tools ###
+
+ * [KonkretCMPI](http://konkretcmpi.org/) is used to generate C source code
+ for providers from a mof specification.
+
+ RPM package name: konkretcmpi
+
+
+ * [CMake](http://www.cmake.org/) is the required build tool. Various
+ aspects of OpenLMI provider development and deployment are
+ automatically handled via custom CMake macros provided by OpenLMI.
+
+ RPM package name: cmake
+
+ * [PyWBEM](http://sourceforge.net/apps/mediawiki/pywbem/index.php?title=Main_Page)
+ PyWBEM is a Python library supporting CIM operations over HTTP using
+ the WBEM CIM-XML protocol. It is easy to use and master. PyWBEM also
+ provides a Python provider interface suitable for rapid development
+ of CIM providers.
+
+ RPM package name: pywbem
+
+ * [OpenLMI](https://fedorahosted.org/openlmi/)
+ OpenLMI maintains numerous development tools to ease the task of
+ CIM provider development, deployement and WBEM operations.
+
+ RPM package name: openlmi-providers-devel
+
+
+### Client Tools ###
+
+ * [PyWBEM](http://sourceforge.net/apps/mediawiki/pywbem/index.php?title=Main_Page)
+ can be used for client scripting in Python.
+
+ * [YAWN](http://sourceforge.net/apps/mediawiki/pywbem/index.php?title=YAWN)
+ Yet Another WBEM Navigator. YAWN runs in the Apache web server, it
+ is Python based and utilizes Apache's mod-python. It is a CIM
+ client tool in that it provides a way to browse CIM Schema and
+ exercise CIM providers from within your local web browser.
+
+ RPM package name: yawn
+
+ * [OpenLMI](https://fedorahosted.org/openlmi/)
+ OpenLMI maintain several client tools
+
+ RPM package name: openlmi-tools
+
+# Writing a CIM provider for OpenLMI #
+
+## OpenLMI Development Conventions ##
+
+OpenLMI has established a number of development conventions which you will
+want to observe.
+
+* The preferred CIMOM is
+ [OpenPegasus](https://collaboration.opengroup.org/pegasus/)
+
+* The preferred source code language for providers is C.
+
+* [KonkretCMPI](http://konkretcmpi.org/) is used to generate C source code
+ for providers.
+
+* [CMake](http://www.cmake.org/) is the build tool. Various aspects of
+ provider development and deployment are automatically handled via
+ custom CMake macros provided by OpenLMI.
+
+* [PyWBEM](http://sourceforge.net/apps/mediawiki/pywbem/index.php?title=Main_Page)
+ is used for client scripting in Python and implementing providers
+ when Python is the language of choice.
+
+
+## Set-Up Your Environment ##
+
+This assumes you are working on a Fedora/RHEL/CentOS RPM based Linux
+distribution, although the basic concepts remain the same some package
+names and commands may differ slightly for other Linux distributions.
+
+### Install CIMOM ###
+
+Install the OpenPegasus package
+
+~~~~~~
+sudo yum install tog-pegasus
+~~~~~~
+
+Make sure OpenPegasus is running. **Note:** provider registration only
+works when OpenPegasus is running.
+
+~~~~~~
+sudo systemctl start tog-pegasus.service
+~~~~~~
+
+If you want the OpenPegasus CIMOM service to automatically start after
+a reboot:
+
+~~~~~~
+sudo systemctl enable tog-pegasus.service
+~~~~~~
+
+**Tip:** pegasus can easily be controlled via the `cimserver` command,
+in fact the systemd service control mechanisms simply calls the
+`cimserver` command. During development you may find it easier to
+start, stop, and check the status of the OpenPegasus CIMOM service via
+`cimserver` rather than by the `systemctl` infrastructure.
+
+### Install YAWN ###
+
+YAWN is not a requirement but being able to browse the CIM class
+hierarchy and invoke your provider from within your web browser is so
+handy most developers will want this. YAWN runs in the Apache web
+server therefore you will need to have Apache (e.g. httpd) installed
+and running to be able to browse at the following URL http://host/yawn
+where host is the host name you've installed YAWN on.
+
+~~~~~~
+sudo yum install httpd yawn
+~~~~~~
+
+Then make sure the Apache httpd service is running.
+
+~~~~~~
+sudo systemctl start httpd.service
+~~~~~~
+
+Optionally enable Apache httpd to start after booting.
+
+~~~~~~
+sudo systemctl enable httpd.service
+~~~~~~
+
+See the section on [OpenPegasus authentication](#openpegasus-authentication) to understand the
+username and password prompts required by YAWN when you first connect.
+
+### Install Client Tools ###
+
+OpenLMI Tools provides a CIM shell and a few other handy utilities.
+
+~~~~~~
+sudo yum install openlmi-tools
+~~~~~~
+
+Various WBEM command line utilities are provided by sblim-wbemcli
+
+~~~~~~
+sudo yum install sblim-wbemcli
+~~~~~~
+
+Python libraries which allow you to write simple Python scripts for
+WBEM operations are provided by pywbem.
+
+~~~~~~
+sudo yum install pywbem
+~~~~~~
+
+## Install Provider Development tools ##
+
+You will need KonkretCMPI, CMake and tools provided by OpenLMI.
+
+~~~~~~
+sudo yum install cmake konkretcmpi openlmi-providers-devel
+~~~~~~
+
+## Begin Provider Development (C Language) ##
+
+During this discussion we're going to use `XXX` as the name of
+your provider, you will need to substitute `XXX` for your provider
+name.
+
+You must use CMake to take advantage of the OpenLMI development
+support. It expects the following structure in your development
+tree.
+
+~~~~~~
+CMakeLists.txt
+mof/LMI_XXX.mof
+~~~~~~
+
+CMake will read the contents of CMakeLists.txt and produce a set of
+native Makefiles. CMake macros provided by openlmi-providers-devel
+will also set things up to invoke konkretcmpi to translate your
+LMI_XXX.mof file into a set of C source code files.
+
+The defaults for CMake do not correspond to the defaults when CMake is
+invoked when producing OpenLMI RPM's. The goal here is not so much to
+match RPM but rather to produce a build that matches the expected
+system conventions and the OpenLMI conventions.
+
+Since you probably won't be producing an RPM for your provider
+initially it's best to make sure CMake is configured to generate
+Makefiles that will build using the same conventions for building and
+installing as is done in RPM. There are various ways to do this but
+one simple technique is to define a shell script which invokes CMake
+with the matching RPM configuration, for example:
+
+~~~~~~
+#!/bin/sh
+
+/usr/bin/cmake -DCMAKE_VERBOSE_MAKEFILE=ON \
+ -DCMAKE_INSTALL_PREFIX:PATH=/usr \
+ -DINCLUDE_INSTALL_DIR:PATH=/usr/include \
+ -DLIB_INSTALL_DIR:PATH=/usr/lib \
+ -DSYSCONF_INSTALL_DIR:PATH=/etc \
+ -DSHARE_INSTALL_PREFIX:PATH=/usr/share \
+ -DBUILD_SHARED_LIBS:BOOL=ON .
+
+if [ $? -eq 0 ]; then
+ make
+fi
+~~~~~~
+
+During development it is useful to turn on debugging symbols and turn
+off optimization which complicate debugging using `gdb`. If you add
+
+~~~~~~
+export CFLAGS='-g -O0'
+~~~~~~
+
+to the above script before invoking cmake it will add these options to
+the Makefile. CMake has other mechanisms for producing debug builds
+(i.e. build targets). If you prefer those CMake mechanisms that's fine
+too but I found the above to be simple and expedient.
+
+You will need a valid CMakeLists.txt file that follows the OpenLMI
+CMake conventions. Since OpenLMI is under active development the
+contents of the example CMakeLists.txt file may evolve or a
+CMakeLists.txt template may be included in the future but for the time
+being the example CMakeLists.txt represents a viable starting point.
+
+**Note:** If you aren't familiar with CMake it can be difficult to
+figure out how to do a few simple things. For example if your provider
+is dependent upon another library how do you get things set up such
+that the include files are found and the right libraries are added
+when linking? In the following CMakeLists.txt example I've added a
+dependency on `glib2` only for the purposes of illustration, if your
+provider does not use `glib2` you won't need any of the items in the
+CMakeLists.txt file with the string "glib" in it. But you can use
+"glib" items as a model for what needs to be added for any dependency
+you do have. Also note that `glib2` installs pkgconfig files which
+define how to compile and link against `glib2`. Most library packages ship
+with pkgconfig files, the `glib2` example assumes pkgconfig files are
+available for the dependency, if your dependency does not provide
+pkgconfig files you'll have to adjust accordingly.
+
+~~~~~~
+cmake_minimum_required(VERSION 2.6)
+include(OpenLMIMacros)
+find_package(CMPI REQUIRED)
+find_package(KonkretCMPI REQUIRED)
+
+find_package(PkgConfig QUIET)
+pkg_check_modules(GLIB2 glib-2.0 REQUIRED)
+
+add_subdirectory(mof)
+
+set(PROVIDER_NAME XXX)
+set(LIBRARY_NAME cmpiLMI_${PROVIDER_NAME})
+set(MOF LMI_XXX.mof)
+
+
+# Add all your .c source files here
+set(provider_SRCS
+ LMI_XXXProvider.c
+)
+
+set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wall")
+
+konkretcmpi_generate(${MOF}
+ CIM_PROVIDERS
+ CIM_HEADERS
+)
+
+add_library(${LIBRARY_NAME} SHARED
+ ${provider_SRCS}
+ ${CIM_PROVIDERS}
+ ${CIM_HEADERS}
+)
+
+# FIXME - /usr/include/openlmi shouldn't be hardcoded, needed for globals.h
+# OpenLMI should provide a pkgconfig file
+include_directories(${CMAKE_CURRENT_BINARY_DIR}
+ ${CMPI_INCLUDE_DIR}
+ ${GLIB2_INCLUDE_DIRS}
+ /usr/include/openlmi
+ )
+
+target_link_libraries(${LIBRARY_NAME}
+ openlmicommon
+ ${KONKRETCMPI_LIBRARIES}
+ ${GLIB2_LIBRARIES}
+ )
+
+# Create registration file
+cim_registration(${PROVIDER_NAME} ${LIBRARY_NAME} ${MOF} share/openlmi-providers)
+
+install(TARGETS ${LIBRARY_NAME} DESTINATION lib${LIB_SUFFIX}/cmpi/)
+~~~~~~
+
+## Perform a Build ##
+
+Your mof/LMI_XXX.mof file will need to have been populated with
+something to start with. At this point you may want to refer to the
+[KonkretCMPI Documentation](http://konkretcmpi.org/KonkretCMPI.html)
+to understand the basic development process with KonkretCMPI and
+follow it's example tutorial.
+
+### Understanding KonkretCMPI Behavior ###
+
+The role of KonkretCMPI is to read a MOF file and generate C code
+definitions and code stubs necessary to implement your
+provider. KonkretCMPI generates 3 distinct sets of files:
+
+* .h C header files
+* .c C implementation files
+* .reg CIMOM registration files
+
+Anytime your mof file changes KonkretCMPI needs to run again to make
+sure the generated files are in sync with the mof file contents.
+If KonkretCMPI sees that you are missing any of the generated .c files it
+will generate the .c files for you. However if KonkretCMPI finds an
+existing .c file it will **not overwrite** the .c file. This is because
+you will have likely added your custom provider implementation code to
+these files and you don't want to lose your work.
+
+The header files are directly tied to the definitions found in the mof
+file. It is essential those definitions be correct and reflect the
+current contents of the mof file. Therefore KonkretCMPI **always
+overwrites generated .h files**.
+
+**Tip:** Do not add any custom content to any of the generated .h
+files, your content will be lost every time KonkretCMPI runs due to a
+mof file change. If you did add any custom content to a generated .h
+file you'll have to merge it back in, this is a pain. A better
+approach is to put custom header style information in an independent
+header file and include it in your .c file.
+
+**Note:** Because KonkretCMPI will not overwrite an existing .c file
+you may need to merge function prototype information back into your .c
+file. You typically only need to do this when a CIM method signature
+is modified in the mof file or you've added a new CIM method. It's
+much easier to merge function prototypes back into your .c file than
+it is to move the .c file aside to allow KonkretCMPI to regenerate the
+.c file and then subsequently needing to merge all your custom code
+back into the newly generated .c file.
+
+**Tip:** It's easier to do your provider development if your mof file
+is complete and exactly as you want it. This way you'll have fewer
+issues with KonkretCMPI regenerating files, needing to unregister and
+re-register your provider, or wondering why you can't see your updated
+mof (because you forget to redo the registration). But sometimes it's
+easier to develop your mof incrementally, you'll have to decide which
+development strategy better suits your style and situation.
+
+## Installing and Registering Your Provider ##
+
+If all has gone well after typing `make` you will have compiled your
+.c files and linked them into a provider module. Now you need to
+install your provider so your CIMOM can load it.
+
+In order for your CIMOM to expose your provider it must be registered
+with your CIMOM. This requires the following 3 files to have been
+installed in the expected location in your file system.
+
+* provider module (i.e. your provider .so file)
+* provider mof file
+* provider registration file
+
+The following command will install these files
+
+~~~~~~
+sudo make install
+~~~~~~
+
+**Note:** This is one reason it's critical to invoke CMake with the
+overridden defaults otherwise the installation directories will not
+default to the system defaults.
+
+Now that the files are installed you must register your
+provider. OpenLMI provides a utility script `openlmi-mof-register` to
+simplify this task. **Note:** the `openlmi-mof-register` script is
+also used to unregister a provider, more on that in a moment.
+
+Substitute `XXX` for the name of your provider.
+
+~~~~~~
+sudo openlmi-mof-register register \
+ /usr/share/openlmi-providers/XXX.mof \
+ /usr/share/openlmi-providers/XXX.reg
+~~~~~~
+
+**Note:** OpenPegasus must be running when you register or unregister
+a provider.
+
+**Tip:** During your provider development cycle of edit/build/test you
+do not need to re-register your provider if your mof file did not
+change. It's sufficient to just install your updated provider .so
+(e.g. `sudo make install`) and restart the CIMOM. However if your mof
+file changed your CIMOM won't know about the mof changes because one
+of the things registration does is import the mof definitions into the
+CIMOM. After editing your mof you will need to unregister your old
+provider and re-register it again. In summary only after a mof
+modification you will need to do the following:
+
+~~~~~~
+sudo openlmi-mof-register unregister \
+ /usr/share/openlmi-providers/XXX.mof \
+ /usr/share/openlmi-providers/XXX.reg
+sudo make install
+sudo openlmi-mof-register register \
+ /usr/share/openlmi-providers/XXX.mof \
+ /usr/share/openlmi-providers/XXX.reg
+~~~~~~
+
+## Testing Your Provider ##
+
+Once your provider is installed and registered you will want to
+exercise it. Here are some simple ways you can do that, which one you
+choose is up to you, all boil down to invoking a CIM client against
+your CIMOM.
+
+* Use YAWN in your web browser, open a URL
+(e.g. `http://localhost/yawn) and navigate to one of your provider
+classes.
+
+* Write a Python script using PyWBEM and run that script.
+
+* Use the OpenLMI shell
+
+## Development Debugging Tricks and Techniques ##
+
+### Starting, Stopping and Controling OpenPegasus ###
+
+Don't use the `systemctl` service command to stop and start
+OpenPegasus, instead use the `cimserver` command, it's much easier,
+plus you can specify one time configuration parameters useful for
+debugging (see below).
+
+### Run OpenPegasus In The Foreground ###
+
+You can insert printf debugging statements in your code but you won't
+see them on the console unless you run OpenPegasus in a special
+way. Also, you will want to run OpenPegasus in the foreground, not
+allow it to fork a daemon process or spawn child processes. Running
+OpenPegasus in the following way is much friendlier during the
+development cycle.
+
+~~~~~~
+sudo cimserver daemon=false forceProviderProcesses=false
+~~~~~~
+
+When run this way you'll see any printf's you've added, they will
+appear in your console. When you're done with the current testing
+cycle simply control-c and OpenPegasus will exit. A rapid development
+cycle might look like this:
+
+~~~~~~
+# Edit your provider source code
+make
+sudo make install
+sudo cimserver daemon=false forceProviderProcesses=false
+# Test via YAWN or a script
+~~~~~~
+
+### Using the Debugger ###
+
+It's easy to set a breakpoint in your provider and have `gdb` break
+there for you. Of course you'll want to have compiled with -g to turn
+debugging symbols on and you'll probably also want to disable
+optimization with -O0 so that single stepping in the debugger
+follows your source code instead of jumping around.
+
+Create a .gdbinit file in your local directory. Let's say you want to
+break on LMI_foobar
+
+~~~~~~
+set breakpoint pending on
+b LMI_foobar
+r daemon=false forceProviderProcesses=false
+~~~~~~
+
+For security reasons current versions of `gdb` require you to enable
+reading the the local .gdbinit file, one solution is to add this to
+your `~/.gdbinit` file:
+
+~~~~~~
+add-auto-load-safe-path .
+set auto-load local-gdbinit on
+~~~~~~
+
+Then run OpenPegasus under `gdb`
+
+~~~~~~
+sudo gdb /usr/sbin/cimserver
+~~~~~~
+
+Exercise your provide in your preferred fashion and you should
+break. Then debug in `gdb` as you would normally do.
+
+Of course like most things in life there are multiple ways of doing
+things, the above is just one suggestion, you could use gdb to attach
+to the running cimserver process or any number of other mechanisms, use
+your programming skills and knowledge to find a methodology that works
+best for you.
+
+### Controlling OpenPegasus Behavior ###
+
+[Advanced startup properties for CIMOM](http://publib.boulder.ibm.com/infocenter/iseries/v6r1m0/topic/rzatl/rzatladvstartup.htm)
+provides useful information about available options to control
+OpenPegasus behavior.
+
+### OpenPegasus Logging and Tracing ###
+
+OpenPegasus has a tracing facility. You can utilize the CMPI logging
+commands to record debug and/or informational messages to the
+OpenPegasus trace file instead of printf statements. This is a cleaner
+solution once your provider more stable and you can dispense with
+temporary printf statements.
+
+The CMPI logging and trace functions are `CMLogMessage` and
+`CMTraceMessage` and are defined in `/usr/include/cmpi/cmpimacs.h`,
+refer to that file for their usage.
+
+But more importantly when you're baffled about what OpenPegasus is
+doing it can be invaluable to have full logging and trace information
+at your disposal to peruse. The trace file is:
+
+~~~~~~
+/var/lib/Pegasus/cache/trace/cimserver.trc
+~~~~~~
+
+To ratchet up the verbosity of the trace information you may want to
+run OpenPegasus like this:
+
+~~~~~~
+sudo cimserver daemon=false forceProviderProcesses=false \
+ logLevel=TRACE traceLevel=5 traceFacility=File \
+ traceComponents=All
+~~~~~~
+
+Detailed information about OpenPegasus tracing can be found in
+[OpenPegasus Tracing User Guide](http://cvs.opengroup.org/cgi-bin/viewcvs.cgi/*checkout*/pegasus/doc/TracingUserGuide.pdf?rev=1.3)
+
+
+### Dumping Method Parameters To the Console ###
+
+Sometimes it's nice to be able to see the CIM method parameters being
+passed to your CIM method by dumping them to the console (of course
+you could also run under `gdb` too and break on the
+method). KonkretCMPI generates a Args_Print function in the generated
+.h file you can call.
+
+To print method YYY args in konkret:
+
+In XXX_DispatchMethod() in XXX.h
+
+Add
+
+XXX_YYY_Args_Print(&args, stdout);
+
+*after* XXX_YYY_Args_InitFromArgs (otherwise args won't be initialized.)
+
+## Provider Development Tips ##
+
+## MOF Development Issues ##
+
+### Structures and Array of Structures as CIM Method Parameters ###
+
+There is no way to pass a complex object in a CIM method call (i.e. a
+structure or class). CIM arrays are limited to simple scalar base
+types (int, string, etc.). Thus there is no way to pass things like
+(key,value) pairs directly. Instead one needs to define an array for
+the key names, and an array for the values (of a specific base
+type). To find the value of a key look up it's value at the same index
+in the as it appears in the key array. The same holds true for any
+array of structures, you have to decompose the structure members into
+individual arrays and recombine them back together by indexing into
+each array using the same index. Don't forget you'll need to declare
+the array with the `ArrayType ( "Indexed" )` qualifier in the MOF
+file. This is very reminiscent of programming in FORTRAN, ugh!
+
+## KonkretCMPI Oddities ##
+
+KonkretCMPI doc uses this example invocation (note KonkretCMPI
+invocation is normally done via CMake macros)
+
+~~~~~~
+konkret -s KC_Widget -m Widget.mof KC_Widget=Widget
+~~~~~~
+
+But there is no man page describing what the args do in detail and the
+-h option is very terse and omits describing the final arg and for a
+long it was not clear to me what that arg was doing. The CMake macro
+konkretcmpi_generate does not use the same arg list as what is
+documented above which is also confusing. Apparently the form used in
+the Konkret doc of `KC_Widget=Widget` is an alias mechanism which
+modifies the class name as found in the MOF file (lhs) to an alternate
+name (rhs) used in the generated C code. The type names, function
+names, generated file names etc. will all use the rhs alias, otherwise
+they will use the class name as found in the MOF.
+
+## OpenPegasus Authentication ##
+
+The [OpenPegasus Administrator's Guide](http://cvs.opengroup.org/cgi-bin/viewcvs.cgi/*checkout*/pegasus/doc/Admin_Guide_Release.pdf)
+gives a brief overview of how OpenPegasus handles authentication. But
+the following document which is installed along with the tog-pegasus
+package on Red Hat systems gives a more comprehensive overview.
+
+~~~~~~
+/usr/share/doc/tog-pegasus-*/README.RedHat.Security
+~~~~~~
+
+The short story is root user authentication works for local
+connections but is denied for network connections. If you've installed
+YAWN then the user authentication prompt issued by YAWN appears to
+OpenPegasus as a local user and root will work. However this is quite
+insecure and should be avoided. Root authentication is possibly
+justified in constrained cases such as during development where the
+target machine is on an isolated local network (i.e. virtual
+machines used for test and development).
+
+The preferred mechanism is to use the `pegasus` user account which is
+created when tog-pegasus is installed. However there is no password
+established for the `pegasus` user during install (this is a security
+precaution) and you will need to set the `pegasus` user password
+(requires root privileges)
+
+~~~~~~
+sudo passwd pegasus XXX
+~~~~~~
+
+where XXX is the `pegasus` password. After this is done you can
+authenticate to OpenPegasus with the username `pegasus` and the
+password you created.
+
+# Advanced CIM Topics #
+
+## Resource Configuration ##
+
+How one handles configuration of CIM elements is a surprisingly
+complex topic and woefully under documented. If you're developing your
+own profile you'll need to understand these topics. The best way to
+learn about configuration approaches is to study the existing CIM
+profiles and see how they are handled in the example profiles.
+
+One naive approach would be to provide a CIM method to set
+configuration parameters on your CIM object. This is a traditional
+approach in many programming API's. However the CIM Schema and
+existing models often take a different approach utilizing the
+following CIM classes and associations. This is probably worth entire
+tutorial on it's own. Here is a brief introduction:
+
+The following classes are used as base classes to contain
+configuration parameters.
+
+* `CIM_SettingData`
+* `CIM_Capabilities`
+
+The following *association* classes are used to form links between
+the `SettingData` and `Capability` derived classes.
+
+* `CIM_ElementSettingData`
+* `CIM_ElementCapabilities`
+* `CIM_SettingsDefineCapabilities`
+* `CIM_SettingsDefineState`
+
+The way these can be combined is many fold. Naively you may assume you
+would have only one `SettingData` instance where the entirety of the
+configuration parameters are stored. But in fact you may have many
+such instances joined in a web by associations, some indicating
+current values, defaults values to be applied next, minimum values,
+maximum values, etc.
+
+#### CIM_SettingData ####
+
+ChangeableType
+ : Has the following possible values
+
+ * Not Changeable - Persistent
+ * Changeable - Transient
+ * Changeable - Persistent
+ * Not Changeable - Transient
+
+CIM_SettingData is linked via CIM_SettingsDefineState and
+CIM_SettingsDefineCapabilities associations.
+
+
+#### CIM_ElementSettingData ####
+
+CIM_ElementSettingData has the following properties:
+
+IsDefault
+ : Has the following possible values
+
+ * Unknown
+ * Is Default
+ * Is Not Default
+
+IsCurrent
+ : Has the following possible values
+
+ * Unknown
+ * Is Current
+ * Is Not Current
+
+IsNext
+ : Has the following possible values
+
+ * Unknown
+ * Is Next
+ * Is Not Next
+ * Is Next For Single Use
+
+#### CIM_ElementCapabilities ####
+
+Characteristics[]
+ : Has the following possible simultaneous values
+
+ * Default
+ * Current
+
+#### CIM_SettingsDefineCapabilities ####
+
+PropertyPolicy
+ : Has the following possible values
+
+ * Independent
+ * Correlated
+
+ValueRole
+ : Has the following possible values
+
+ * Default
+ * Optimal
+ * Mean
+ * Supported
+
+ValueRange
+ : Has the following possible values
+
+ * Point
+ * Minimums
+ * Maximums
+ * Increments
+
+### Tying the Configuration Classes Together ###
+
+If you're looking at a CIM_ElementSettingData association the `IsDefault`
+property will tell you if the group of configuration parameters
+pointed by the SettingData reference are the default values. Likewise
+the `IsCurrent` property tells you if the configuration parameters
+pointed by the SettingData reference are current values or not. The
+`IsNext` property tells you if the the configuration parameters
+pointed by the SettingData reference will be applied the next time
+configuration is applied and whether those parameters will permanently
+persist.
+
+The CIM_ElementCapabilities association tells you if the
+CIM_Capabilities pointed to by the association for a CIM_ManagedElement
+(i.e. an object) are the defaults or the current values.
+
+The CIM_SettingsDefineCapabilities association tells you how to
+interpret the SettingData being pointed to. There may be many
+SettingData objects needed to fully specify the configuration. The
+`PropertyPolicy` property tells you if you have to correlate the
+SettingData values or if you can treat them independently. The
+`ValueRole` property tells you what role the pointed to SettingData
+plays, i.e. defaults, optimal, average, etc. The `ValueRange` property
+tells you if the pointed to SettingData are a single set of values,
+just the minimum values, just the maximum values, or represent the
+increments each property value can be stepped by.
+
+In practice what the `ValueRole` property does is force you to have
+many SettingData objects to specify the configuration for an
+element. Let's say your CIM element has some properties that can only
+be specified within a minimum and maximum range. You would then create
+a SettingData containing the valid minimums and point to it via a
+CIM_SettingsDefineCapabilities association. Likewise you would create
+a SettingData containing the valid maximums and point to it via a
+CIM_SettingsDefineCapabilities association. To ascertain the valid
+range you have to query for CIM_SettingsDefineCapabilities where the
+`ValueRange` property is Minimums, query for the Maximums and then
+follow the association pointers to each respective Capabilities to form
+the min/max range. By the same token a CIM_SettingsDefineCapabilities
+whose `ValueRange` property is Point indicates a single set of values
+rather than a range. Ultimately you have to find all the
+CIM_SettingsDefineCapabilities objects bound to the element you want
+to configure and interpret them.
+
+Are you confused yet? It's very convoluted and the possible
+combinations are large. Don't you wish you could just call a method
+and set the configuration parameters or query them? The best way to
+wrap your head around all this is to study the various profiles
+utilizing these classes, especially study the use case examples in
+Chapter 9 of the profile, that will help solidify your understanding.
+
+# FAQ #
+
+**Q:** How do I make a CIM method a class method as opposed to a instance
+method?
+
+**A:** An instance method is bound to the instance it is called from, in
+object oriented languages the instance is often called "self" or
+"this". This is the default method binding in CIM. However you can
+specify class methods as well which are not bound to an
+instance, to do this add the `Static` qualifier to the list of
+qualifiers belonging to the method.
+
+# Vocabulary #
+
+CIM
+
+ : [Common Information Model](http://www.dmtf.org/standards/cim) is
+ schema and associated specification which details how to represent
+ the elements of a computer system in order to manage those
+ elements. This yeilds a common and portable mechanism by which IT
+ administrators can manage their computing resources. CIM is
+ defined by the DMTF.
+
+CIM Schema
+
+ : The [CIM Schema](http://www.dmtf.org/standards/CIM) is a
+ collection of predefined CIM classes which forms the building
+ blocks for modeling in CIM. The CIM Schema is expressed in MOF
+ (Managed Object Format) syntax.
+
+CIMOM
+
+ : Common Information Model Object Manager. Sometimes referred to as a
+ broker the CIMOM is a network connected service running on a
+ managed computer which grants access to the CIM providers on
+ the managed computer. A CIM client connects to the CIMOM in
+ order to manage a specific resource on the managed computer. Those
+ resource instances are made available to the CIMOM by the
+ providers loaded by the CIMOM.
+
+CMPI
+
+ : [Common Manageability Programming
+ Interface](http://www.opengroup.org/standards/enterprise-management). CMPI
+ is an open standard defined by the Open Group which defines the
+ programming API between a CIMOM and a CIM provider. In the
+ absence of CMPI each CIM provider would need to be coded to
+ the API of the CIMOM it was loaded into. CMPI allows a CIM
+ provider to be written once and utilized by different CIMOM
+ implementations.
+
+DMTF
+
+ : [Distributed Management Task Force](http://www.dmtf.org/) is an
+ industry consortium defining open standards for computer system
+ management.
+
+KonkretCMPI
+
+ : A tool used to aid development of CIM
+ providers. [KonkretCMPI](http://konkretcmpi.org/) reads a MOF
+ specification file and generates a set of C header files, C
+ program files and provider registration files. The primary purpose
+ of KonkretCMPI is to insulate a provider author from the CMPI API
+ by providing all the necessary "glue code" needed to adhere to the
+ CMPI specification. This allows the programmer to focus on the
+ particulars of the provider.
+
+MOF
+
+ : [Managed Object Format](http://www.dmtf.org/standards/CIM) is the
+ syntax used to describe the CIM Schema. MOF files are used to
+ define provider interfaces.
+
+Provider
+
+ : A software module which is loaded by the CIMOM broker running
+ locally on a managed system which provides information about a
+ type (i.e. class) of resource, for example network
+ interfaces. There may be multiple instances of that resource
+ class. The Provider is responsible for managing all instances
+ of that resource class. The Provider in addition to providing
+ information about a resource instance may optionally allow the
+ resource instance to be configured or acted upon.
+
+WBEM
+
+ : [Web-Based Enterprise
+ Management](http://www.dmtf.org/standards/wbem). A collection of
+ standardized technologies providing unified management of
+ distributed computing environments based on CIM concepts.
diff --git a/mof/60_LMI_Realmd.mof b/mof/60_LMI_Realmd.mof
new file mode 100644
index 0000000..2063fc6
--- /dev/null
+++ b/mof/60_LMI_Realmd.mof
@@ -0,0 +1,519 @@
+[ Description (
+ "Access to the Realmd Service. "
+ "Realmd is used to discover realms available for joining as well as "
+ "providing a mechanism for joining and leaving a realm."),
+ Provider("cmpi:cmpiLMI_Realmd") ]
+class LMI_RealmdService : CIM_Service
+{
+ [Description (
+ "The name of the provider. This is not normally displayed "
+ "to the user, but may be useful for diagnostics or debugging.")]
+ string RealmdName;
+
+ [Description (
+ "The version of the provider. This is not normally used in "
+ "logic, but may be useful for diagnostics or debugging.")]
+ string RealmdVersion;
+
+ [Description (
+ "The locale used for messages.")]
+ // FIXME: we should support CIM_LocalizationCapabilities but there is no way query supported locales.
+ string Locale;
+
+ [Description (
+ "A list of known, enrolled or discovered realms. All realms "
+ "that this provider knows about are listed here. As realms "
+ "are discovered they are added to this list.")]
+ string Realms[];
+
+ [Description (
+
+ "Discover realms for the given target. The input target is "
+ "usually a domain or realm name, perhaps typed by a user. If an "
+ "empty target string is provided the realm provider should try "
+ "to discover a default realm if possible (eg: from DHCP).\n "
+ "\n"
+ "The behavior of the method may be modified via optional "
+ "<name,value> pairs called \"options\" passed an array of "
+ "option names and option values. The <name,value> pair is "
+ "formed by indexing into the name array and finding it's value "
+ "at the same index in the value array.\n "
+ "\n"
+ "The currently defined options are:\n "
+ "\n"
+ "\"client-software\": a string containing the client software "
+ "identifier that the returned realms should match.\n"
+ "\n"
+ "\"server-software\": a string containing the client software "
+ "identifier that the returned realms should match.\n"
+ )]
+
+ uint32 Discover(
+ [In, Description (
+ "What realms to discover")]
+ string Target,
+ [In, ArrayType ( "Indexed" ), Description (
+ "This array is correlated with the OptionValues array. "
+ "Each entry is related to the entries in the other array "
+ "located at the same index. In this way a (name,value) tuple "
+ "can be constructed.")]
+ string OptionNames[],
+ [In, ArrayType ( "Indexed" ), Description (
+ "This array is correlated with the OptionNames array. "
+ "Each entry is related to the entries in the other array "
+ "located at the same index. In this way a (name,value) tuple "
+ "can be constructed.")]
+ string OptionValues[],
+ [In ( false ), Out, Description (
+ "Array of references to discovered realms")]
+ LMI_RealmdRealm REF DiscoveredRealms[]);
+
+ // Proof of concept simplfied API starts here
+
+ [Description (
+ "The name of the domain that this computer is a member of "
+ "or NULL if not a member of any domain.")]
+ string Domain;
+
+ [Description (
+ "Join the computer to a domain.")]
+ uint32 JoinDomain(
+ [In, Description (
+ "The name of the domain to join.")]
+ string Domain,
+ [In, Description (
+ "The administrative user who is authorizing joining the domain. "
+ "Or NULL for a one time password based join.")]
+ string User,
+ [In, Description (
+ "Either NULL for an automatic join, a one time password, or the "
+ "password for the administrative user in the User parameter.")]
+ string Password,
+ [In, ArrayType ( "Indexed" ), Description (
+ "This array is correlated with the OptionValues array. "
+ "Each entry is related to the entries in the other array "
+ "located at the same index. In this way a (name,value) tuple "
+ "can be constructed.")]
+ string OptionNames[],
+ [In, ArrayType ( "Indexed" ), Description (
+ "This array is correlated with the OptionNames array. "
+ "Each entry is related to the entries in the other array "
+ "located at the same index. In this way a (name,value) tuple "
+ "can be constructed.")]
+ string OptionValues[]);
+
+ [Description (
+ "Make the computer leave its joined domain.")]
+ uint32 LeaveDomain(
+ [In, Description (
+ "The name of the domain to join.")]
+ string Domain,
+ [In, Description (
+ "The administrative user who is authorizing joining the domain. "
+ "Or NULL for a one time password based join.")]
+ string User,
+ [In, Description (
+ "Either NULL for an automatic join, a one time password, or the "
+ "password for the administrative user in the User parameter.")]
+ string Password,
+ [In, ArrayType ( "Indexed" ), Description (
+ "This array is correlated with the OptionValues array. "
+ "Each entry is related to the entries in the other array "
+ "located at the same index. In this way a (name,value) tuple "
+ "can be constructed.")]
+ string OptionNames[],
+ [In, ArrayType ( "Indexed" ), Description (
+ "This array is correlated with the OptionNames array. "
+ "Each entry is related to the entries in the other array "
+ "located at the same index. In this way a (name,value) tuple "
+ "can be constructed.")]
+ string OptionValues[]);
+};
+
+[ Description (
+ "Represents one realm. "
+
+ "Contains generic information about a realm, and useful properties "
+ "for introspecting what kind of realm this is and how to work with "
+ "the realm. "
+
+ "Use LMI_RealmdService.Discover() to get access to help populate the "
+ "LMI_RealmdService.Realms property. "
+
+ "Different realms support various ways to configure them on the "
+ "system. LMI_RealmdRealm.Configured property to determine if a realm "
+ "is configured. If it is configured the property will be set to class "
+ "used to configure it. "
+
+ "To configure a realm use the method on the LMIRealmdRealm subclass "
+ "designed for that purpose, for example the "
+ "LMI_RealmdKerberosRealm.Join() method. "
+
+ "To deconfigure a realm from the current system, you can use the "
+ "Deconfigure() method. "),
+ Provider("cmpi:cmpiLMI_Realmd") ]
+class LMI_RealmdRealm : CIM_LogicalElement
+{
+
+ [Key, Override ( "InstanceID" ),
+ Description (
+ "Within the scope of the instantiating Namespace, "
+ "InstanceID opaquely and uniquely identifies an instance "
+ "of this class. In order to ensure uniqueness within the "
+ "NameSpace, the value of InstanceID shall be constructed "
+ "using the following \'preferred\' algorithm: \n"
+ "<OrgID>:<LocalID> \n"
+ "<LocalID> will be DBus object path correlated to this instance.")]
+ string InstanceID;
+
+ [Key, Description ( "The scoping System\'s CCN." ),
+ MaxLen ( 256 ),
+ Propagated ( "CIM_System.CreationClassName" )]
+ string SystemCreationClassName;
+
+ [Key, Description ( "The scoping System\'s Name." ),
+ MaxLen ( 256 ),
+ Propagated ( "CIM_System.Name" )]
+ string SystemName;
+
+ [Description (
+ "Name of the realm, "
+ "appropriate for display to end users where necessary.")]
+ string RealmName;
+
+ [Description (
+ "If this property is NULL then the realm is not configured."
+ "Otherwise the realm is configured and the property contains "
+ "a string which is the interface that represents how it was "
+ "configured, e.g. \"KerberosMembership\".")]
+ string Configured;
+
+ [Description (
+ "Indicates the types of operations this realm is capable of."
+ "Current possible values are: \"Kerberos\", \"KerberosMembership\".")]
+ string SupportedInterfaces[];
+
+ [Description (
+ "Extra detail information expressed as (name,value) pairs. "
+ "This array is correlated with the DetailValues array. "
+ "Each entry is related to the entries in the other array "
+ "located at the same index. In this way a (name,value) tuple "
+ "can be constructed."),
+ ArrayType ( "Indexed" )]
+ string DetailNames[];
+ [Description (
+ "Extra detail information expressed as (name,value) pairs. "
+ "This array is correlated with the DetailNames array. "
+ "Each entry is related to the entries in the other array "
+ "located at the same index. In this way a (name,value) tuple "
+ "can be constructed."),
+ ArrayType ( "Indexed" )]
+ string DetailValues[];
+
+ [Description (
+ "Software packages that are required in order for a join to "
+ "succeed. These are either simple strings like \"sssd\" "
+ "or strings with an operator and version number like \"sssd >= 1.9.0\" "
+ "These values are specific to the packaging system that is being run.")]
+ string RequiredPackages[];
+
+ [Description (
+ "Supported formats for login to this realm. This is only "
+ "relevant once the realm has been enrolled. The formats "
+ "will contain a \"%U\" in the string, which indicates where the "
+ "user name should be placed. The formats may contain a \"%D\" in "
+ "the string which indicates where a domain name should be placed. "
+ "The first format in the list is the preferred format for login names.")]
+ string LoginFormats[];
+
+ [Description (
+ "The policy for logging into this computer using this realm. "
+ "The policy can be changed using the ChangeLoginPolicy() method. "
+ "The following policies are predefined. Not all providers support "
+ "all these policies and there may be provider specific policies or "
+ "multiple policies represented in the string: "
+ "\"allow-any-login\": allow login by any authenticated user present in this realm. "
+ "\"allow-permitted-logins\": only allow the logins permitted in the PermittedLogins property. "
+ "\"deny-any-login\": don't allow any logins via authenticated users of this realm.")]
+ string LoginPolicy;
+
+ [Description (
+ "The list of permitted authenticated users allowed to login "
+ "into this computer. This is only relevant if the LoginPolicy property "
+ "contains the \"allow-permitted-logins\" string.")]
+ string PermittedLogins[];
+
+ [Description (
+ "Change the login policy and/or permitted logins for this realm. "
+ "Not all realms support the all the various login policies. An "
+ "error will be returned if the new login policy is not supported. "
+ "You may specify a NULL value for the login_policy argument which "
+ "will cause no change in the policy itself. If the policy is changed, "
+ "it will be reflected in the LoginPolicy property. "
+ "The permitted_add and permitted_remove arguments represent lists of "
+ "login names that should be added and removed from the PermittedLogins property.")]
+ uint32 ChangeLoginPolicy(
+ [In, Description (
+ "the new login policy or NULL")]
+ string LoginPolicy,
+ [In, Description (
+ "a list of logins to permit")]
+ string PermittedAdd[],
+ [In, Description (
+ "a list of logins to not permit")]
+ string PermittedRemove[]);
+
+ [Description (
+ "Deconfigure: deconfigure this realm"
+ "\n"
+ "Deconfigure this realm from the local machine with standard "
+ "default behavior. "
+ "\n"
+ "The behavior of this method depends on the which configuration "
+ "interface is present in the Configured property. It does not "
+ "always delete membership accounts in the realm, but just "
+ "reconfigures the local machine so it no longer is configured "
+ "for the given realm. In some cases the implementation may try "
+ "to update membership accounts, but this is not guaranteed."
+ "\n"
+ "Various configuration interfaces may support more specific ways "
+ "to deconfigure a realm in a specific way, such as the "
+ "KerberosMembership.Leave() method.")]
+ uint32 Deconfigure();
+
+};
+
+
+[ Description (
+ "Credentials supported for joining. "
+ "\n"
+ "Various kinds of credentials that are supported when calling the "
+ "Join() method. "
+ "\n"
+ "Each credential is represented by a type, and an owner. The type "
+ "denotes which kind of credential is passed to the method. The "
+ "owner indicates to the client how to prompt the user or obtain "
+ "the credential, and to the service how to use the credential. "
+ "\n"
+
+ "The various types are: "
+ "\"ccache\": "
+ "The credentials should contain an array of octets containing"
+ "the data from a kerberos credential cache file. "
+ "The data must be passed in the Data parameter, the Name & Password parameters must be NULL. "
+ "\n"
+ "\"password\": "
+ "The credentials should contain a pair of strings representing "
+ "a name and password. The name may contain a realm in the "
+ "standard kerberos format. If a realm is missing, it will "
+ "default to this realm. "
+ "The name must be passed in the Name parameter, the password must be passed "
+ "in the Password parameter, the Data parameter must be NULL. "
+ "\n"
+ "\"secret\": "
+ "The credentials should contain a string secret. This is "
+ "usually used for one time passwords. "
+ "The data must be passed in the Data parameter, the Name & Password parameters must be NULL. "
+ "\n"
+ "\"automatic\": "
+ "The credentials should contain an empty string. Using "
+ "\"automatic\" indicates that default or system credentials are "
+ "to be used. "
+ "The Name, Password & Data parameters must be NULL. "
+ "\n"
+ "The various owners are: "
+ "\n"
+ "\"administrator\": "
+ "The credentials belong to a kerberos user principal. "
+ "The caller may use this as a hint to prompt the user "
+ "for administrative credentials. "
+ "\n"
+ "\"user\": "
+ "The credentials belong to a kerberos user principal. The "
+ "caller may use this as a hint to prompt the user for his "
+ "(possibly non-administrative) credentials. "
+ "\n"
+ "\"computer\": "
+ "The credentials belong to a computer account. "
+ "\n"
+ "\"none\": "
+ "The credentials have an unspecified owner, such as a one time "
+ "secret."),
+ Provider("cmpi:cmpiLMI_Realmd") ]
+class LMI_RealmdKerberosRealm : LMI_RealmdRealm
+{
+ [Description (
+ "The kerberos name for this realm. This is usually in upper "
+ "case.")]
+ string RealmName;
+
+ [Description (
+ "The DNS domain name for this realm.")]
+ string DomainName;
+
+ [Description (
+ "The common administrator name for this type of realm. This "
+ "can be used by clients as a hint when prompting the user for "
+ "administrative authentication.")]
+ string SuggestedAdministrator;
+
+ [Description (
+ "This array is correlated with the SupportedJoinCredentialOwners array. "
+
+ "Each entry is related to the entries in the other array "
+ "located at the same index. In this way a (type,owner) tuple "
+ "can be constructed. The set of tuples formed by correlating "
+ "the two arrays define the supported combinations for the Join "
+ "method."),
+ ValueMap { "1", "2", "3", "4"},
+ Values { "ccache", "password", "secrect", "automatic" },
+ ArrayType ( "Indexed" )]
+ uint32 SupportedJoinCredentialTypes[];
+
+ [Description (
+ "This array is correlated with the SupportedJoinCredentialTypes array. "
+
+ "Each entry is related to the entries in the other array "
+ "located at the same index. In this way a (type,owner) tuple "
+ "can be constructed. The set of tuples formed by correlating "
+ "the two arrays define the supported combinations for the Join "
+ "method."),
+ ValueMap { "1", "2", "3", "4"},
+ Values { "administrator", "user", "computer", "none" },
+ ArrayType ( "Indexed" )]
+ uint32 SupportedJoinCredentialOwners[];
+
+ [Description (
+ "This array is correlated with the SupportedLeaveCredentialOwners array. "
+
+ "Each entry is related to the entries in the other array "
+ "located at the same index. In this way a (type,owner) tuple "
+ "can be constructed. The set of tuples formed by correlating "
+ "the two arrays define the supported combinations for the Leave "
+ "method."),
+ ValueMap { "1", "2", "3", "4"},
+ Values { "ccache", "password", "secrect", "automatic" },
+ ArrayType ( "Indexed" )]
+ uint32 SupportedLeaveCredentialTypes[];
+
+ [Description (
+ "This array is correlated with the SupportedLeaveCredentialTypes array. "
+
+ "Each entry is related to the entries in the other array "
+ "located at the same index. In this way a (type,owner) tuple "
+ "can be constructed. The set of tuples formed by correlating "
+ "the two arrays define the supported combinations for the Leave "
+ "method."),
+ ValueMap { "1", "2", "3", "4"},
+ Values { "administrator", "user", "computer", "none" },
+ ArrayType ( "Indexed" )]
+ uint32 SupportedLeaveCredentialOwners[];
+
+ // FIXME - The Data parameter should be uint8 array with the octetstring qualifier
+ // but the octetstring qualier doesn't seem to do anything and you end up with
+ // an array of CMPIValue's with one octet in each, this is highly inefficent and awkward.
+
+ [Description (
+ "")]
+ uint32 Join(
+ [In, Description (
+ "Credential type, see LMI_RealmdKerberosRealm description"),
+ ValueMap { "1", "2", "3", "4"},
+ Values { "ccache", "password", "secrect", "automatic" }]
+ uint32 Type,
+ [In, Description (
+ "Credential owner, see LMI_RealmdKerberosRealm description"),
+ ValueMap { "1", "2", "3", "4"},
+ Values { "administrator", "user", "computer", "none" }]
+ uint32 Owner,
+ [In, Description (
+ "The name may contain a realm in the standard kerberos format. "
+ "If a realm is missing, it will default to this realm. "
+ "Used when the Type is password.")]
+ string Name,
+ [In, Description (
+ "Authentication password. "
+ "Used when the Type is password.")]
+ string Password,
+ [In, Description (
+ "Binary data when the Type is ccache or secret"),
+ OctetString]
+ uint8 Data[],
+ [In, ArrayType ( "Indexed" ), Description (
+ "This array is correlated with the OptionValues array. "
+ "Each entry is related to the entries in the other array "
+ "located at the same index. In this way a (name,value) tuple "
+ "can be constructed.")]
+ string OptionNames[],
+ [In, ArrayType ( "Indexed" ), Description (
+ "This array is correlated with the OptionNames array. "
+ "Each entry is related to the entries in the other array "
+ "located at the same index. In this way a (name,value) tuple "
+ "can be constructed.")]
+ string OptionValues[]);
+
+ [Description (
+ "")]
+ uint32 Leave(
+ [In, Description (
+ "Credential type, see LMI_RealmdKerberosRealm description"),
+ ValueMap { "1", "2", "3", "4"},
+ Values { "ccache", "password", "secrect", "automatic" }]
+ uint32 Type,
+ [In, Description (
+ "Credential owner, see LMI_RealmdKerberosRealm description"),
+ ValueMap { "1", "2", "3", "4"},
+ Values { "administrator", "user", "computer", "none" }]
+ uint32 Owner,
+ [In, Description (
+ "The name may contain a realm in the standard kerberos format. "
+ "If a realm is missing, it will default to this realm. "
+ "Used when the Type is password.")]
+ string Name,
+ [In, Description (
+ "Authentication password. "
+ "Used when the Type is password.")]
+ string Password,
+ [In, Description (
+ "Binary data when the Type is ccache or secret"),
+ OctetString]
+ uint8 Data[],
+ [In, ArrayType ( "Indexed" ), Description (
+ "This array is correlated with the OptionValues array. "
+ "Each entry is related to the entries in the other array "
+ "located at the same index. In this way a (name,value) tuple "
+ "can be constructed.")]
+ string OptionNames[],
+ [In, ArrayType ( "Indexed" ), Description (
+ "This array is correlated with the OptionNames array. "
+ "Each entry is related to the entries in the other array "
+ "located at the same index. In this way a (name,value) tuple "
+ "can be constructed.")]
+ string OptionValues[]);
+};
+
+[ Association,
+ Provider("cmpi:cmpiLMI_Realmd") ]
+class LMI_HostedRealmdService: CIM_HostedService
+{
+ [ Override("Antecedent"),
+ Description("The hosting System") ]
+ CIM_ComputerSystem REF Antecedent;
+
+ [ Override("Dependent"),
+ Description("The Central Instance of realm management") ]
+ LMI_RealmdService REF Dependent;
+};
+
+[ Association,
+ Provider("cmpi:cmpiLMI_Realmd") ]
+class LMI_ServiceAffectsRealmdRealm: CIM_ServiceAffectsElement
+{
+ [ Override("AffectingElement"),
+ Description("The Central Instance of realm management") ]
+ LMI_RealmdService REF AffectingElement;
+
+ [ Override("AffectedElement"),
+ Description("The managed Identity") ]
+ LMI_RealmdRealm REF AffectedElement;
+};
diff --git a/mof/70_LMI_SoftwareIndicationFilters.mof b/mof/70_LMI_SoftwareIndicationFilters.mof
index e3af599..b79917d 100644
--- a/mof/70_LMI_SoftwareIndicationFilters.mof
+++ b/mof/70_LMI_SoftwareIndicationFilters.mof
@@ -28,7 +28,7 @@ instance of CIM_IndicationFilter {
SystemCreationClassName = "CIM_ComputerSystem";
SystemName = "kvm-fedora18";
Name = "LMI:LMI_SoftwareInstallationJob:PercentUpdated";
- Query = "SELECT * FROM CIM_InstModification WHERE "
+ Query = "SELECT * FROM LMI_SoftwareInstModification WHERE "
"SourceInstance ISA LMI_SoftwareInstallationJob AND "
"SourceInstance.CIM_ConcreteJob::PercentComplete <> "
"PreviousInstance.CIM_ConcreteJob::PercentComplete";
@@ -47,12 +47,11 @@ instance of CIM_IndicationFilter {
SystemName = "kvm-fedora18";
Name = "LMI:LMI_SoftwareInstallationJob:Succeeded";
QueryLanguage = "CIM:CQL";
- Query = "SELECT * FROM CIM_InstModification WHERE "
+ Query = "SELECT * FROM LMI_SoftwareInstModification WHERE "
"SourceInstance ISA LMI_SoftwareInstallationJob AND "
- "SourceInstance.CIM_ConcreteJob::JobState"
- " = 17"
- /* This is not supported by sfcb:
- * " = CIM_ConcreteJob.JobState#'Completed'" */;
+ "SourceInstance.CIM_ConcreteJob::JobState = 17";
+ /* This is not supported by sfcb:
+ * " = CIM_ConcreteJob.JobState#'Completed'" */
SourceNamespace = "root/cimv2";
SourceNamespaces = {"root/cimv2"};
};
@@ -62,12 +61,11 @@ instance of CIM_IndicationFilter {
SystemCreationClassName = "CIM_ComputerSystem";
SystemName = "kvm-fedora18";
Name = "LMI:LMI_SoftwareInstallationJob:Failed";
- Query = "SELECT * FROM CIM_InstModification WHERE "
+ Query = "SELECT * FROM LMI_SoftwareInstModification WHERE "
"SourceInstance ISA LMI_SoftwareInstallationJob AND "
- "SourceInstance.CIM_ConcreteJob::JobState"
- " = 10"
- /* This is not supported by sfcb:
- * "CIM_ConcreteJob.JobState#'Exception'" */;
+ "SourceInstance.CIM_ConcreteJob::JobState = 10";
+ /* This is not supported by sfcb:
+ * "CIM_ConcreteJob.JobState#'Exception'" */
QueryLanguage = "CIM:CQL";
Description = "Modification of Operational Status for a "
"Concrete Job to 'Complete' and 'OK'.";
@@ -80,7 +78,7 @@ instance of CIM_IndicationFilter {
SystemCreationClassName = "CIM_ComputerSystem";
SystemName = "kvm-fedora18";
Name = "LMI:LMI_SoftwareInstallationJob:Changed";
- Query = "SELECT * FROM CIM_InstModification WHERE "
+ Query = "SELECT * FROM LMI_SoftwareInstModification WHERE "
"SourceInstance ISA LMI_SoftwareInstallationJob AND "
"SourceInstance.CIM_ConcreteJob::JobState <> "
"PreviousInstance.CIM_ConcreteJob::JobState";
@@ -95,7 +93,7 @@ instance of CIM_IndicationFilter {
SystemCreationClassName = "CIM_ComputerSystem";
SystemName = "kvm-fedora18";
Name = "LMI:LMI_SoftwareInstallationJob:Created";
- Query = "SELECT * FROM CIM_InstCreation WHERE"
+ Query = "SELECT * FROM LMI_SoftwareInstCreation WHERE"
" SourceInstance ISA LMI_SoftwareInstallationJob";
QueryLanguage = "CIM:CQL";
Description = "Creation of a ConcreteJob.";
diff --git a/openlmi-cimmof b/openlmi-cimmof
new file mode 100755
index 0000000..cbec8d4
--- /dev/null
+++ b/openlmi-cimmof
@@ -0,0 +1,340 @@
+#!/usr/bin/env python
+# Copyright (C) 2012-2013 Red Hat, Inc. All rights reserved.
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+
+"""
+Allows to modify Pegasus repository with declarations in mof files.
+Pegasus must be running for this script to work. It depends on cimmof
+binary, which is online compilator of MOF files for pegasus.
+
+It works in this way:
+ 1. cimmof is called on input mof files
+ 2. its output (xml) is then parsed by pywbem functions producing
+ CIM objects (instances of ``pywbem.CIMClass`` and
+ ``pywbem.CIMInstance``)
+ 3. these objects are then used in calls to
+ ``{Create,Modify}{Instance,Class}``
+
+*Note* that only Class and Instance declarations are supported.
+ - This is due to limitations in pywbem parser.
+ - Although this could be avoided by calling wbemexec on generated XML.
+"""
+
+import argparse
+import re
+import logging
+import subprocess
+import sys
+import pywbem
+import xml.dom.minidom as dom
+
+DEFAULT_NAMESPACE = "root/cimv2"
+DEFAULT_CIMMOF = "cimmof"
+
+RE_COMMENT = re.compile(r'\s*<!--.*?-->\s*', re.DOTALL)
+
+logging.basicConfig(level=logging.ERROR,
+ format="%(levelname)s - %(message)s")
+LOG = logging.getLogger(__name__)
+
+def die(msg, *args, **kwargs):
+ """
+ Exit with error printed to stderr.
+ """
+ LOG.error(msg, *args, **kwargs)
+ sys.exit(1)
+
+def xml_cleanup(xml_str):
+ """
+ Return xml string without comments and whitespaces.
+ """
+ # remove comments
+ without_comments = "".join(RE_COMMENT.split(xml_str))
+ # remove whitespaces
+ return "".join(l.strip() for l in without_comments.split("\n"))
+
+def get_objects_from_mofs(cimmof, namespace, *mofs):
+ """
+ Call cimmof binary with mofs as input and obtain class/instance
+ declarations in XML.
+
+ Return list of pywbem CIM abstractions for each declaration.
+ """
+ cmd = [cimmof, '--xml', '-n', namespace]
+ objects = []
+ for mof in mofs:
+ process = subprocess.Popen(cmd, stdin=subprocess.PIPE,
+ stdout=subprocess.PIPE, stderr=sys.stderr)
+ (out, _) = process.communicate(mof.read())
+ parsed_dom = dom.parseString(xml_cleanup(out))
+ # we cannot use pywbem.parse_cim because it does not support
+ # DECLARATION element, but we can parse individual values
+ for decl in parsed_dom.getElementsByTagName('VALUE.OBJECT'):
+ # pywbem first makes tupletree from dom:
+ # (name, attributes, children)
+ # and from it generates pywbem CIM abstractions
+ (_name, _attrs, obj) = pywbem.tupleparse.parse_value_object(
+ pywbem.dom_to_tupletree(decl))
+ objects.append(obj)
+ return objects
+
+def get_instance_path(conn, namespace, instance, classes):
+ """
+ Obtains a class declaration from cimom for given instance and builds
+ a path from it.
+
+ :param conn is a wbem connection
+ :param namespace (``str``) is a target namespace of instance
+ :param instance (``CIMInstance``) contains class name and is used to get
+ values of key properties. Is modified by adding path to it.
+ :param classes (``dict``) is a cache of classes obtained from cimom.
+ Its items are in form: ``(classname, CIMClass)``.
+
+ Return ``CIMInstanceName``.
+ """
+ if not instance.classname in classes:
+ classes[instance.classname] = conn.GetClass(instance.classname,
+ IncludeQualifiers=True,
+ namespace=namespace)
+ cls = classes[instance.classname]
+ keys = [p.name for p in cls.properties.values() if "Key" in p.qualifiers]
+ path = pywbem.CIMInstanceName(instance.classname, namespace=namespace)
+ for key in keys:
+ if not key in instance:
+ die("instance of %s is missing key property \"%s\"",
+ instance.classname, key)
+ path[key] = instance[key]
+ instance.path = path
+ return path
+
+def create_class(conn, cls, namespace, classes, allow_update=False):
+ """
+ Create class or modify it if already present.
+
+ :param classes: (``dict``) a cache of classes obtained from cimom.
+ :param allow_update: (``bool``) whether to modify existing class.
+ """
+ try:
+ if not cls.classname in classes:
+ classes[cls.classname] = conn.GetClass(cls.classname,
+ IncludeQualifiers=True,
+ namespace=namespace)
+ except pywbem.CIMError as err:
+ if err.args[0] != pywbem.CIM_ERR_NOT_FOUND:
+ raise
+ if cls.classname in classes:
+ if not allow_update:
+ LOG.error("class %s already exists", cls.classname)
+ else:
+ conn.ModifyClass(cls, namespace=namespace)
+ LOG.info("modified class %s", cls.classname)
+ else:
+ conn.CreateClass(cls, namespace=namespace)
+ LOG.info("created class %s", cls.classname)
+
+def create_instance(conn, inst, namespace, classes, allow_update=False):
+ """
+ Create instance or modify it if already present.
+
+ :param classes: (``dict``) a cache of classes obtained from cimom.
+ :param allow_update: (``bool``) whether to modify existing instance.
+ """
+ path = get_instance_path(conn, namespace, inst, classes)
+ present = None
+ try:
+ present = conn.GetInstance(path)
+ except pywbem.CIMError as err:
+ if err.args[0] != pywbem.CIM_ERR_NOT_FOUND:
+ raise
+ if present is not None:
+ try:
+ if allow_update:
+ conn.ModifyInstance(inst)
+ LOG.info("modified instance for path %s", path)
+ else:
+ LOG.error("instance %s already exists", path)
+ except pywbem.CIMError as err:
+ if err.args[0] == pywbem.CIM_ERR_NOT_SUPPORTED:
+ LOG.error("ModifyInstance() is not supported for class %s,"
+ " please remove the instance first",
+ inst.classname)
+ else:
+ raise
+
+ else:
+ conn.CreateInstance(inst)
+ LOG.info("created instance for path %s", path)
+
+def reorder_objects(cmd, objects):
+ """
+ Reorder classes and instances so that dependent objects are handled
+ later.
+
+ Classes can depend between each other in two ways:
+ 1. one inherits from another
+ 2. one refers to another (associations)
+
+ The first case can be solved by counting number of parents, that are
+ also to be removed. Class with highest number will be created as last.
+
+ The second one applies only to associations, which can not refer
+ to each other. Let's just append them after non-associations.
+
+ :param cmd: (``str``) can be "create" or "delete". In latter case the
+ result is reversed, so the dependent classes/instances are removed
+ as first.
+
+ *Note* this does not handle dependencies between instances.
+ """
+ cls_list = []
+ assoc_list = []
+ cls_dict = set( c.classname.lower()
+ for c in objects if isinstance(c, pywbem.CIMClass))
+ # (class name, number of superclasses in cls_dict)
+ cls_deps = pywbem.NocaseDict()
+ inst_list = []
+ for obj in objects:
+ if isinstance(obj, pywbem.CIMClass):
+ if 'association' in obj.qualifiers:
+ assoc_list.append(obj)
+ else:
+ cls_list.append(obj)
+ cls_deps[obj.classname] = 0
+ parent = obj.superclass
+ while parent in cls_dict:
+ cls_deps[obj.classname] += 1
+ parent = cls_dict[parent].classname
+ else: # no specific reordering of instances
+ inst_list.append(obj)
+ key_func = lambda c: (cls_deps[c.classname], c.classname)
+ cls_list = sorted(cls_list, key=key_func)
+ assoc_list = sorted(assoc_list, key=key_func)
+
+ result = cls_list + assoc_list + inst_list
+ if cmd == "delete":
+ result.reverse()
+ return result
+
+def push_to_repo(namespace, cmd, objects, allow_update=False):
+ """
+ Create or delete desired objects in Pegasus repository.
+
+ :param cmd: (``string``) is one of { 'create' | 'delete' }
+ :param objects: (``list``) is a list of pywbem CIM abstractions
+ created from mofs. They will be operated upon.
+ """
+ if not isinstance(namespace, basestring):
+ raise TypeError("namespace must be string")
+ if not cmd in ('create', 'delete'):
+ raise ValueError('cmd must be either "create" or "delete"')
+ classes = pywbem.NocaseDict()
+ conn = pywbem.PegasusUDSConnection()
+ objects = reorder_objects(cmd, objects)
+ for obj in objects:
+ try:
+ if cmd == "create":
+ if isinstance(obj, pywbem.CIMClass):
+ create_class(conn, obj, namespace, classes, allow_update)
+ elif isinstance(obj, pywbem.CIMInstance):
+ create_instance(conn, obj, namespace, classes,
+ allow_update)
+ else:
+ LOG.error("unsupported object for creation: %s",
+ obj.__class__.__name__)
+
+ else:
+ try:
+ if isinstance(obj, pywbem.CIMClass):
+ conn.DeleteClass(obj.classname, namespace=namespace)
+ LOG.info("deleted class %s", obj.classname)
+ elif isinstance(obj, pywbem.CIMInstance):
+ path = get_instance_path(conn, namespace, obj, classes)
+ conn.DeleteInstance(path)
+ LOG.info("deleted instance %s", path)
+ else:
+ LOG.error("unsupported object for deletion: %s",
+ obj.__class__.__name__)
+ except pywbem.CIMError as err:
+ if err.args[0] == pywbem.CIM_ERR_NOT_FOUND:
+ LOG.warn("%s not present in repository",
+ obj if isinstance(obj, pywbem.CIMClass)
+ else path)
+ else:
+ raise
+
+ except pywbem.CIMError as err:
+ if err.args[0] in (pywbem.CIM_ERR_INVALID_PARAMETER, ):
+ LOG.warn("failed to %s %s: %s", cmd, obj, err)
+ else:
+ raise
+
+def parse_cmd_line():
+ """
+ Parse command line and return options.
+ """
+ parser = argparse.ArgumentParser(
+ usage="%(prog)s [options] {create,delete} mof [mof ...]",
+ description="Allows to create/delete instances and classes"
+ " declared in MOF files. It operates only on Pegasus broker"
+ " that needs to be up and running.")
+ parser.add_argument('--cimmof', default=DEFAULT_CIMMOF,
+ help="Path to cimmof binary to use.")
+ #parser.add_argument('--xml', action='store_true', default=False,
+ #help="Do not execute any action on cimom, just print the"
+ #" xml to stdout.")
+ parser.add_argument('-n', '--namespace', default=DEFAULT_NAMESPACE,
+ help="Target CIM Repository namespace.")
+ parser.add_argument('-v', '--verbose', action='store_true',
+ default=False, help="Be more verbosive on output.")
+
+ mof_parser = argparse.ArgumentParser(add_help=False)
+ mof_parser.add_argument('mof', nargs='+',
+ type=argparse.FileType('r'),
+ default=sys.stdin,
+ help="Mof files containing declarations of classes and instances"
+ " to be installed or removed from Pegasus broker.")
+
+ command = parser.add_subparsers(title="Operation commands",
+ dest="command",
+ help="Operation on declarations.")
+ create_cmd = command.add_parser('create', parents=[mof_parser],
+ help='Create instances and classes listed in mof files.')
+ create_cmd.add_argument('-u', '--allow-update',
+ action="store_true", default=False,
+ help="Allow update of class declaration if it already exists.")
+ command.add_parser('delete', parents=[mof_parser],
+ help="Delete instances and classes listed in mof files.")
+
+ args = parser.parse_args()
+ return args
+
+def main():
+ """
+ The main functionality of script.
+ """
+ args = parse_cmd_line()
+ if args.verbose:
+ LOG.setLevel(logging.INFO)
+ # parse mofs and build list of pywbem objects
+ objs = get_objects_from_mofs(args.cimmof, args.namespace, *args.mof)
+ if not objs:
+ die("no declarations found!")
+ push_to_repo(args.namespace, args.command, objs,
+ getattr(args, 'allow_update', False))
+
+if __name__ == '__main__':
+ main()
+
diff --git a/openlmi-mof-register b/openlmi-mof-register
index d7918e0..b2b1790 100755
--- a/openlmi-mof-register
+++ b/openlmi-mof-register
@@ -24,11 +24,15 @@ default_namespace="root/cimv2"
function usage()
{
- printf "Usage: $0 [ --just-mofs ] [ -n namespace ] CMD <mof> [mof] [...] [reg]
+ printf "Usage: $0 [ --just-mofs ] [ -n namespace ] [ -c cimom ]
+ CMD <mof> [mof] [...] [reg]
CMD is one of [ register, unregister ]
Default namespace is $default_namespace, which can be changed with '-n' option.
+ Supported cimoms are sfcbd and tog-pegasus. Without \"-c\" argument, the
+ operation is processed for any cimom present on system (all of them).
+
--just-mofs option causes that all arguments after CMD will be
treated as mof files - no registration file is expected.
@@ -46,14 +50,14 @@ function register()
shift
fi
mofs="$@"
- if [ $HAS_SFCBD -eq 1 ];
+ if [ $HAS_SFCBD -eq 1 ] && echo $cimom | grep -q 'all\|sfcbd';
then
- /usr/bin/sfcbstage ${reg:+-r} $reg $mofs
+ /usr/bin/sfcbstage -n $namespace ${reg:+-r} $reg $mofs
/usr/bin/sfcbrepos -f
/usr/bin/systemctl reload-or-try-restart sblim-sfcb.service
fi
- if [ $HAS_PEGASUS -eq 1 ];
+ if [ $HAS_PEGASUS -eq 1 ] && echo $cimom | grep -q 'all\|tog-pegasus';
then
/usr/sbin/cimserver --status > /dev/null 2>&1
if [ $? -eq 0 ];
@@ -64,7 +68,7 @@ function register()
fi
$CIMMOF -uc $mofs
- if [ $JUST_MOFS = 0 ]; then
+ if [ $JUST_MOFS -eq 0 ]; then
if [ -x $(dirname $0)/openlmi-register-pegasus ];
then
cat "$reg" | $(dirname $0)/openlmi-register-pegasus | $CIMMOF -uc -n root/PG_Interop
@@ -81,30 +85,39 @@ function unregister()
reg=$1
shift
fi
- # convert mofs to `basename mof`
- declare -a mofs=("$@")
- for ((i=0; i<${#mofs[@]}; i++)); do
- mofs[$i]=$(basename "${mofs[$i]}")
- done
- if [ $HAS_SFCBD -eq 1 ];
+ declare -a mofs=("$@")
+ if [ $HAS_SFCBD -eq 1 ] && echo $cimom | grep -q 'all\|sfcbd';
then
- /usr/bin/sfcbunstage ${reg:+-r} $(basename "$reg") ${mofs[@]}
+ # convert mofs to `basename mof`
+ declare -a bmofs
+ for ((i=0; i<${#mofs[@]}; i++)); do
+ bmofs[$i]=$(basename "${mofs[$i]}")
+ done
+ /usr/bin/sfcbunstage -n $namespace ${reg:+-r} $(basename "$reg") ${bmofs[@]}
/usr/bin/sfcbrepos -f
/usr/bin/systemctl reload-or-try-restart sblim-sfcb.service
fi
- if [ -n "$reg" -a $HAS_PEGASUS -eq 1 ];
+ if [ $HAS_PEGASUS -eq 1 ] && echo $cimom | grep -q 'all\|tog-pegasus';
then
- for provider in $(sed -n 's/ *location: *//p' "$reg" | sort | uniq);
- do
- /usr/bin/cimprovider -d -m ${provider} && /usr/bin/cimprovider -r -m ${provider}
- done
+ if [ -x $(dirname $0)/openlmi-cimmof ]; then
+ $(dirname $0)/openlmi-cimmof -n $namespace delete ${mofs[@]}
+ else
+ /usr/libexec/openlmi-cimmof -n $namespace delete ${mofs[@]}
+ fi
+ if [ -n "$reg" ];
+ then
+ for provider in $(sed -n 's/ *location: *//p' "$reg" | sort | uniq);
+ do
+ /usr/bin/cimprovider -d -m ${provider} && /usr/bin/cimprovider -r -m ${provider}
+ done
+ fi
fi
}
JUST_MOFS=0
-optspec=":hn:-:"
+optspec=":hn:c:-:"
while getopts "$optspec" optchar; do
case "$optchar" in
@@ -116,6 +129,7 @@ while getopts "$optspec" optchar; do
*)
if [ "$OPTERR" = 1 ] && [ "${optspec:0:1}" != ":" ]; then
echo "Unknown option --${OPTARG}" >&2
+ exit 1
fi
;;
esac
@@ -123,6 +137,13 @@ while getopts "$optspec" optchar; do
n)
namespace="$OPTARG"
;;
+ c)
+ cimom="$OPTARG"
+ if [ "$cimom" != "sfcbd" -a "$cimom" != "tog-pegasus" ]; then
+ echo "Not supported cimom: $cimom" >&2
+ exit 1
+ fi
+ ;;
h)
usage;
exit 0;
@@ -130,6 +151,7 @@ while getopts "$optspec" optchar; do
*)
if [ "$OPTERR" != 1 ] || [ "${optspec:0:1}" = ":" ]; then
echo "Non-option argument: '-${OPTARG}'" >&2
+ exit 1
fi
;;
esac
@@ -137,6 +159,7 @@ done
shift $(($OPTIND - 1))
namespace=${namespace:-$default_namespace}
+cimom=${cimom:-all}
if [ $# -lt 2 ];
then
@@ -149,6 +172,10 @@ then
HAS_SFCBD=1
else
HAS_SFCBD=0
+ if [ $cimom != "sfcbd" ]; then
+ echo "Sfcbd not detected on system!" >&2
+ exit 1
+ fi
fi
if [ -e /usr/sbin/cimserver ];
@@ -156,6 +183,10 @@ then
HAS_PEGASUS=1
else
HAS_PEGASUS=0
+ if [ $cimom != "tog-pegasus" ]; then
+ echo "Pegasus not detected on system!" >&2
+ exit 1
+ fi
fi
# TODO: check if at least one server is installed
@@ -164,9 +195,14 @@ shift
# parse the reg and mofs - use $@ and remove the last item
declare -a ARGS=("$@")
-LEN=$(( ${#ARGS[@]} -1 ))
-REG=${ARGS[$LEN]}
-MOFS=(${ARGS[@]:0:$(($LEN))})
+if [ $JUST_MOFS -eq 0 ];
+then
+ LEN=$(( ${#ARGS[@]} -1 ))
+ REG=${ARGS[$LEN]}
+ MOFS=(${ARGS[@]:0:$(($LEN))})
+else
+ MOFS=("$@")
+fi
case $CMD in
register)
diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt
index 6bf4b85..0cd11d9 100644
--- a/src/CMakeLists.txt
+++ b/src/CMakeLists.txt
@@ -40,3 +40,7 @@ endif (WITH-HARDWARE)
if (WITH-LOGICALFILE)
add_subdirectory(logicalfile)
endif (WITH-LOGICALFILE)
+
+if (WITH-REALMD)
+ add_subdirectory(realmd)
+endif (WITH-REALMD)
diff --git a/src/python/openlmi/common/IndicationManager.py b/src/python/openlmi/common/IndicationManager.py
index ba0919d..a17703f 100644
--- a/src/python/openlmi/common/IndicationManager.py
+++ b/src/python/openlmi/common/IndicationManager.py
@@ -35,8 +35,9 @@ RE_FILTER_NAME = re.compile(r'^(?P<prefix>lmi:'
r'(?P<class_name>[a-z0-9_]+):)(?P<filter_id>.*)$', re.IGNORECASE)
FILTER_DEFAULTS = {
+ "SourceNamespace" : "root/cimv2",
"SourceNamespaces" : ["root/cimv2"],
- "QueryLanguage" : "CIM:CQL"
+ "QueryLanguage" : "CIM:CQL"
}
@cmpi_logging.trace_function
@@ -511,10 +512,19 @@ class IndicationManager(singletonmixin.Singleton):
self.ensure_filters_installed(class_name=class_name)
@cmpi_logging.trace_method
- def authorize_filter(self, _env, fltr, _ns, _classes, _owner):
+ def authorize_filter(self, _env, fltr, _class_name, _op, _owner):
"""
AuthorizeFilter callback from CIMOM. Call this method from appropriate
- CIMOM callback.
+ CIMOM callback
+
+ It asks us to verify whether this filter is allowed.
+
+ :param fltr: Contains the filter that must be authorized.
+ :param _class_name: (``String``) Contains the class name extracted
+ from the filter FROM clause.
+ :param _op: The name of the class for which monitoring is required.
+ Only the namespace part is set if className is a process indication.
+ :param _owner The owner argument is the destination owner.
"""
with self._access_lock:
res = self._get_matching_filter(fltr)
@@ -526,10 +536,24 @@ class IndicationManager(singletonmixin.Singleton):
return False
@cmpi_logging.trace_method
- def activate_filter(self, _env, fltr, _ns, _classes, first_activation):
+ def activate_filter(self, _env, fltr, _class_name, _class_path,
+ first_activation):
"""
ActivateFilter callback from CIMOM. Call this method from appropriate
CIMOM callback.
+
+ It ask us to begin monitoring a resource. The function shall begin
+ monitoring the resource according to the filter express only.
+
+ :param fltr: The filter argument contains the filter specification
+ for this subscription to become active.
+ :param _class_name: (``String``) The class name extracted from the filter
+ FROM clause.
+ :param _class_path: (``CIMInstanceName``) The name of the class for
+ which monitoring is required. Only the namespace part is set if
+ eventType is a process indication.
+ :param first_activation: (``bool``) Set to true if this is the first
+ filter for className.
"""
with self._access_lock:
if not first_activation:
@@ -541,10 +565,23 @@ class IndicationManager(singletonmixin.Singleton):
make_filter_name(res[0], res[1]), fltr)
@cmpi_logging.trace_method
- def deactivate_filter(self, _env, fltr, _ns, _classes, last_activation):
+ def deactivate_filter(self, _env, fltr, _class_name, _class_path,
+ last_activation):
"""
DeactivateFilter callback from CIMOM. Call this method from appropriate
CIMOM callback.
+
+ Informs us that monitoring using this filter should stop.
+
+ :param fltr: The filter argument contains the filter specification for
+ this subscription to become active.
+ :param class_name: (``String``) The class name extracted from the filter
+ FROM clause.
+ :param class_path: (``CIMInstanceName``) class_path The name of the
+ class for which monitoring is required. Only the namespace part is
+ set if className is a process indication.
+ :last_activation: (``bool``) Set to true if this is the last filter for
+ className.
"""
with self._access_lock:
if not last_activation:
@@ -560,6 +597,11 @@ class IndicationManager(singletonmixin.Singleton):
"""
EnableIndications callback from CIMOM. Call this method from
appropriate CIMOM callback.
+
+ Tells us that indications can now be generated. The MB is now prepared
+ to process indications. The function is normally called by the MB after
+ having done its intialization and processing of persistent subscription
+ requests.
"""
with self._access_lock:
self._enabled = True
@@ -570,6 +612,11 @@ class IndicationManager(singletonmixin.Singleton):
"""
EnableIndications callback from CIMOM. Call this method from
appropriate CIMOM callback.
+
+ Tells us that we should stop generating indications. MB will not accept
+ any indications until enabled again. The function is normally called
+ when the MB is shutting down indication services either temporarily or
+ permanently.
"""
with self._access_lock:
self._enabled = False
@@ -603,7 +650,8 @@ class IndicationManager(singletonmixin.Singleton):
ind['SourceInstance'] = instance
ind['SourceInstanceHost'] = socket.gethostname()
ind['SourceInstanceModelPath'] = str(instance.path)
- ind['IndicationFilterName'] = make_filter_name(instance.classname, filter_id)
+ ind['IndicationFilterName'] = make_filter_name(
+ instance.classname, filter_id)
ind['PerceivedSeverity'] = self.SEVERITY_INFO
cmpi_logging.logger.info("Sending indication %s for %s" %
diff --git a/src/python/openlmi/common/JobManager.py b/src/python/openlmi/common/JobManager.py
index 5ed7830..e78967c 100644
--- a/src/python/openlmi/common/JobManager.py
+++ b/src/python/openlmi/common/JobManager.py
@@ -47,10 +47,33 @@ import threading
from Queue import Queue
import pywbem
import openlmi.common.cmpi_logging as cmpi_logging
+from openlmi.common.IndicationManager import IndicationManager
from pywbem.cim_provider2 import CIMProvider2
import socket
import traceback
+@cmpi_logging.trace_function
+def register_filters(job_clsname, indication_manager=None):
+ """
+ This function registers static indication filters at IndicationManager.
+ It should be called upon provider's initialization.
+
+ :param job_clsname: (``String``) CIM class name for asynchonous jobs.
+ Will be part of filter queries.
+ :param indication_manager: If not given, global instance will be obtained.
+ """
+ if indication_manager is None:
+ ind_manager = IndicationManager.get_instance()
+ filters = {}
+ query_args = {
+ "classname" : job_clsname,
+ "prefix" : indication_manager.nameprefix
+ }
+ for fltr_id, fltr_props in JobManager.IND_FILTERS.items():
+ filters[fltr_id] = fltr_props.copy()
+ filters[fltr_id]['Query'] = fltr_props['Query'] % query_args
+ indication_manager.add_filters(job_clsname, filters)
+
# Too many instance attributes
# pylint: disable-msg=R0902
class Job(object):
@@ -495,6 +518,23 @@ class Job(object):
return inst
@cmpi_logging.trace_method
+ def get_cim_error(self):
+ """
+ Return job error as CIMInstance of CIM_Error.
+ :returns: CIMInstance of CIM_Error
+ """
+ path = pywbem.CIMInstanceName(
+ classname="CIM_Error",
+ host=socket.gethostname(),
+ namespace=self.job_manager.namespace)
+ err = pywbem.CIMInstance(
+ classname="CIM_Error",
+ path=path)
+ err['CIMStatusCode'] = pywbem.Uint32(self.error[0])
+ err['Message'] = self.error[1]
+ return err
+
+ @cmpi_logging.trace_method
def get_post_call(self):
"""
Return indication that describes the post-execution values of the
@@ -524,15 +564,7 @@ class Job(object):
if self.return_value is not None:
inst['ReturnValue'] = str(self.return_value)
if self.error is not None:
- path = pywbem.CIMInstanceName(
- classname="CIM_Error",
- host=socket.gethostname(),
- namespace=self.job_manager.namespace)
- err = pywbem.CIMInstance(
- classname="CIM_Error",
- path=path)
- err['CIMStatusCode'] = pywbem.Uint32(self.error[0])
- err['Message'] = self.error[1]
+ err = self.get_cim_error()
inst['Error'] = [err, ]
return inst
@@ -651,6 +683,43 @@ class JobManager(object):
IND_JOB_CHANGED = "Changed"
IND_JOB_CREATED = "Created"
+ IND_FILTERS = {
+ IND_JOB_PERCENT_UPDATED: {
+ "Query" : "SELECT * FROM LMI_%(prefix)sInstModification WHERE "
+ "SourceInstance ISA %(classname)s AND "
+ "SourceInstance.CIM_ConcreteJob::PercentComplete <> "
+ "PreviousInstance.CIM_ConcreteJob::PercentComplete",
+ "Description" : "Modification of Percentage Complete for a "
+ "Concrete Job.",
+ },
+ IND_JOB_SUCCEEDED: {
+ "Query" : "SELECT * FROM LMI_%(prefix)sInstModification WHERE "
+ "SourceInstance ISA %(classname)s AND "
+ "SourceInstance.CIM_ConcreteJob::JobState = 17",
+ "Description": "Modification of Job State for a "
+ "Concrete Job to 'Complete'.",
+ },
+ IND_JOB_FAILED: {
+ "Query" : "SELECT * FROM LMI_%(prefix)sInstModification WHERE "
+ "SourceInstance ISA %(classname)s AND "
+ "SourceInstance.CIM_ConcreteJob::JobState = 10",
+ "Description": "Modification of Job State for a "
+ "Concrete Job to 'Exception'.",
+ },
+ IND_JOB_CHANGED: {
+ "Query" : "SELECT * FROM LMI_%(prefix)sInstModification WHERE "
+ "SourceInstance ISA %(classname)s AND "
+ "SourceInstance.CIM_ConcreteJob::JobState <> "
+ "PreviousInstance.CIM_ConcreteJob::JobState",
+ "Description": "Modification of Job State for a ConcreteJob.",
+ },
+ IND_JOB_CREATED: {
+ "Query" : "SELECT * FROM LMI_%(prefix)sInstCreation WHERE "
+ "SourceInstance ISA %(classname)s",
+ "Description": "Creation of a ConcreteJob.",
+ },
+ }
+
@cmpi_logging.trace_method
def __init__(self, name, namespace, indication_manager, timer_manager):
"""
@@ -701,46 +770,7 @@ class JobManager(object):
"""
Add all job-related ``IndicationFilters`` to indication manager.
"""
- filters = {
- self.IND_JOB_PERCENT_UPDATED: {
- "Query" : "SELECT * FROM CIM_InstModification WHERE "
- "SourceInstance ISA %(classname)s AND "
- "SourceInstance.CIM_ConcreteJob::PercentComplete <> "
- "PreviousInstance.CIM_ConcreteJob::PercentComplete",
- "Description" : "Modification of Percentage Complete for a "
- "Concrete Job.",
- },
- self.IND_JOB_SUCCEEDED: {
- "Query" : "SELECT * FROM CIM_InstModification WHERE "
- "SourceInstance ISA %(classname)s AND "
- "SourceInstance.CIM_ConcreteJob::JobState = 17",
- "Description": "Modification of Job State for a "
- "Concrete Job to 'Complete'.",
- },
- self.IND_JOB_FAILED: {
- "Query" : "SELECT * FROM CIM_InstModification WHERE "
- "SourceInstance ISA %(classname)s AND "
- "SourceInstance.CIM_ConcreteJob::JobState = 10",
- "Description": "Modification of Job State for a "
- "Concrete Job to 'Exception'.",
- },
- self.IND_JOB_CHANGED: {
- "Query" : "SELECT * FROM CIM_InstModification WHERE "
- "SourceInstance ISA %(classname)s AND "
- "SourceInstance.CIM_ConcreteJob::JobState <> "
- "PreviousInstance.CIM_ConcreteJob::JobState",
- "Description": "Modification of Job State for a ConcreteJob.",
- },
- self.IND_JOB_CREATED: {
- "Query" : "SELECT * FROM CIM_InstCreation WHERE "
- "SourceInstance ISA %(classname)s",
- "Description": "Creation of a ConcreteJob.",
- },
- }
- # add class name
- for f in filters.itervalues():
- f['Query'] = f['Query'] % {"classname" : self.job_classname }
- self.indication_manager.add_filters(self.job_classname, filters)
+ register_filters(self.job_classname, self.indication_manager)
@cmpi_logging.trace_method
def get_providers(self):
@@ -1139,7 +1169,8 @@ class LMI_ConcreteJob(CIMProvider2):
if job.error is None:
errors = []
else:
- errors = [job.error, ]
+ err = job.get_cim_error()
+ errors = [err, ]
out_params = [
pywbem.CIMParameter(
name='errors',
@@ -1283,9 +1314,10 @@ class LMI_ConcreteJob(CIMProvider2):
type='instance',
is_array=False)
else:
+ err = job.get_cim_error()
error = pywbem.CIMParameter(
name='error',
- value=job.error,
+ value=err,
type='instance')
rval = self.Values.GetError.Success
return (rval, [error])
diff --git a/src/realmd/CMakeLists.txt b/src/realmd/CMakeLists.txt
new file mode 100644
index 0000000..cd6f6c8
--- /dev/null
+++ b/src/realmd/CMakeLists.txt
@@ -0,0 +1,50 @@
+pkg_check_modules(DBUS1 dbus-1 REQUIRED)
+pkg_check_modules(GLIB2 glib-2.0 REQUIRED)
+
+set(PROVIDER_NAME Realmd)
+set(LIBRARY_NAME cmpiLMI_${PROVIDER_NAME})
+set(MOF 60_LMI_Realmd.mof)
+
+
+set(provider_SRCS
+ LMI_HostedRealmdServiceProvider.c
+ LMI_RealmdKerberosRealmProvider.c
+ LMI_RealmdRealmProvider.c
+ LMI_RealmdServiceProvider.c
+ LMI_ServiceAffectsRealmdRealmProvider.c
+ rdcp_dbus.c
+ rdcp_error.c
+ rdcp_util.c
+)
+
+set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wall")
+
+konkretcmpi_generate(${MOF}
+ CIM_PROVIDERS
+ CIM_HEADERS
+)
+
+add_library(${LIBRARY_NAME} SHARED
+ ${provider_SRCS}
+ ${CIM_PROVIDERS}
+ ${CIM_HEADERS}
+)
+
+# FIXME - /usr/include/openlmi shouldn't be hardcoded, needed for globals.h
+include_directories(${CMAKE_CURRENT_BINARY_DIR}
+ ${CMPI_INCLUDE_DIR}
+ ${DBUS1_INCLUDE_DIRS}
+ ${GLIB2_INCLUDE_DIRS}
+ )
+
+target_link_libraries(${LIBRARY_NAME}
+ openlmicommon
+ ${KONKRETCMPI_LIBRARIES}
+ ${DBUS1_LIBRARIES}
+ ${GLIB2_LIBRARIES}
+ )
+
+# Create registration file
+cim_registration(${PROVIDER_NAME} ${LIBRARY_NAME} ${MOF} share/openlmi-providers)
+
+install(TARGETS ${LIBRARY_NAME} DESTINATION lib${LIB_SUFFIX}/cmpi/)
diff --git a/src/realmd/LMI_HostedRealmdServiceProvider.c b/src/realmd/LMI_HostedRealmdServiceProvider.c
new file mode 100644
index 0000000..08732c1
--- /dev/null
+++ b/src/realmd/LMI_HostedRealmdServiceProvider.c
@@ -0,0 +1,233 @@
+#include <konkret/konkret.h>
+#include "LMI_HostedRealmdService.h"
+#include "CIM_ComputerSystem.h"
+#include "rdcp_util.h"
+#include "globals.h"
+
+static const CMPIBroker* _cb;
+
+static void LMI_HostedRealmdServiceInitialize()
+{
+}
+
+static CMPIStatus LMI_HostedRealmdServiceCleanup(
+ CMPIInstanceMI* mi,
+ const CMPIContext* cc,
+ CMPIBoolean term)
+{
+ CMReturn(CMPI_RC_OK);
+}
+
+static CMPIStatus LMI_HostedRealmdServiceEnumInstanceNames(
+ CMPIInstanceMI* mi,
+ const CMPIContext* cc,
+ const CMPIResult* cr,
+ const CMPIObjectPath* cop)
+{
+ return KDefaultEnumerateInstanceNames(
+ _cb, mi, cc, cr, cop);
+}
+
+static CMPIStatus LMI_HostedRealmdServiceEnumInstances(
+ CMPIInstanceMI* mi,
+ const CMPIContext* cc,
+ const CMPIResult* cr,
+ const CMPIObjectPath* cop,
+ const char** properties)
+{
+ CMPIStatus status;
+ CIM_ComputerSystemRef computer_sys_ref;
+ LMI_RealmdServiceRef realmd_service_ref;
+ LMI_HostedRealmdService hosted_realmd_service;
+ CMPIObjectPath *computer_sys_op = NULL;
+
+ const char *name_space = KNameSpace(cop);
+ const char *host_name = get_system_name();
+
+ CMSetStatus(&status, CMPI_RC_OK);
+
+ LMI_InitComputerSystemKeys(CIM_ComputerSystemRef, &computer_sys_ref,
+ name_space, host_name);
+
+ LMI_InitRealmdServiceKeys(LMI_RealmdServiceRef, &realmd_service_ref, name_space, host_name);
+
+ computer_sys_op = LMI_RealmdServiceRef_ToObjectPath(&realmd_service_ref, &status);
+ computer_sys_op->ft->setClassName(computer_sys_op,
+ get_system_creation_class_name());
+
+ LMI_HostedRealmdService_Init(&hosted_realmd_service, _cb, name_space);
+ LMI_HostedRealmdService_SetObjectPath_Antecedent(&hosted_realmd_service,
+ computer_sys_op);
+ LMI_HostedRealmdService_Set_Dependent(&hosted_realmd_service,
+ &realmd_service_ref);
+
+ KReturnInstance(cr, hosted_realmd_service);
+
+ return status;
+}
+
+static CMPIStatus LMI_HostedRealmdServiceGetInstance(
+ CMPIInstanceMI* mi,
+ const CMPIContext* cc,
+ const CMPIResult* cr,
+ const CMPIObjectPath* cop,
+ const char** properties)
+{
+ return KDefaultGetInstance(
+ _cb, mi, cc, cr, cop, properties);
+}
+
+static CMPIStatus LMI_HostedRealmdServiceCreateInstance(
+ CMPIInstanceMI* mi,
+ const CMPIContext* cc,
+ const CMPIResult* cr,
+ const CMPIObjectPath* cop,
+ const CMPIInstance* ci)
+{
+ CMReturn(CMPI_RC_ERR_NOT_SUPPORTED);
+}
+
+static CMPIStatus LMI_HostedRealmdServiceModifyInstance(
+ CMPIInstanceMI* mi,
+ const CMPIContext* cc,
+ const CMPIResult* cr,
+ const CMPIObjectPath* cop,
+ const CMPIInstance* ci,
+ const char**properties)
+{
+ CMReturn(CMPI_RC_ERR_NOT_SUPPORTED);
+}
+
+static CMPIStatus LMI_HostedRealmdServiceDeleteInstance(
+ CMPIInstanceMI* mi,
+ const CMPIContext* cc,
+ const CMPIResult* cr,
+ const CMPIObjectPath* cop)
+{
+ CMReturn(CMPI_RC_ERR_NOT_SUPPORTED);
+}
+
+static CMPIStatus LMI_HostedRealmdServiceExecQuery(
+ CMPIInstanceMI* mi,
+ const CMPIContext* cc,
+ const CMPIResult* cr,
+ const CMPIObjectPath* cop,
+ const char* lang,
+ const char* query)
+{
+ CMReturn(CMPI_RC_ERR_NOT_SUPPORTED);
+}
+
+static CMPIStatus LMI_HostedRealmdServiceAssociationCleanup(
+ CMPIAssociationMI* mi,
+ const CMPIContext* cc,
+ CMPIBoolean term)
+{
+ CMReturn(CMPI_RC_OK);
+}
+
+static CMPIStatus LMI_HostedRealmdServiceAssociators(
+ CMPIAssociationMI* mi,
+ const CMPIContext* cc,
+ const CMPIResult* cr,
+ const CMPIObjectPath* cop,
+ const char* assocClass,
+ const char* resultClass,
+ const char* role,
+ const char* resultRole,
+ const char** properties)
+{
+ return KDefaultAssociators(
+ _cb,
+ mi,
+ cc,
+ cr,
+ cop,
+ LMI_HostedRealmdService_ClassName,
+ assocClass,
+ resultClass,
+ role,
+ resultRole,
+ properties);
+}
+
+static CMPIStatus LMI_HostedRealmdServiceAssociatorNames(
+ CMPIAssociationMI* mi,
+ const CMPIContext* cc,
+ const CMPIResult* cr,
+ const CMPIObjectPath* cop,
+ const char* assocClass,
+ const char* resultClass,
+ const char* role,
+ const char* resultRole)
+{
+ return KDefaultAssociatorNames(
+ _cb,
+ mi,
+ cc,
+ cr,
+ cop,
+ LMI_HostedRealmdService_ClassName,
+ assocClass,
+ resultClass,
+ role,
+ resultRole);
+}
+
+static CMPIStatus LMI_HostedRealmdServiceReferences(
+ CMPIAssociationMI* mi,
+ const CMPIContext* cc,
+ const CMPIResult* cr,
+ const CMPIObjectPath* cop,
+ const char* assocClass,
+ const char* role,
+ const char** properties)
+{
+ return KDefaultReferences(
+ _cb,
+ mi,
+ cc,
+ cr,
+ cop,
+ LMI_HostedRealmdService_ClassName,
+ assocClass,
+ role,
+ properties);
+}
+
+static CMPIStatus LMI_HostedRealmdServiceReferenceNames(
+ CMPIAssociationMI* mi,
+ const CMPIContext* cc,
+ const CMPIResult* cr,
+ const CMPIObjectPath* cop,
+ const char* assocClass,
+ const char* role)
+{
+ return KDefaultReferenceNames(
+ _cb,
+ mi,
+ cc,
+ cr,
+ cop,
+ LMI_HostedRealmdService_ClassName,
+ assocClass,
+ role);
+}
+
+CMInstanceMIStub(
+ LMI_HostedRealmdService,
+ LMI_HostedRealmdService,
+ _cb,
+ LMI_HostedRealmdServiceInitialize())
+
+CMAssociationMIStub(
+ LMI_HostedRealmdService,
+ LMI_HostedRealmdService,
+ _cb,
+ LMI_HostedRealmdServiceInitialize())
+
+KONKRET_REGISTRATION(
+ "root/cimv2",
+ "LMI_HostedRealmdService",
+ "LMI_HostedRealmdService",
+ "instance association");
diff --git a/src/realmd/LMI_RealmdKerberosRealmProvider.c b/src/realmd/LMI_RealmdKerberosRealmProvider.c
new file mode 100644
index 0000000..5dc90d2
--- /dev/null
+++ b/src/realmd/LMI_RealmdKerberosRealmProvider.c
@@ -0,0 +1,627 @@
+#include <konkret/konkret.h>
+#include "LMI_RealmdKerberosRealm.h"
+#include "globals.h"
+#include "rdcp_error.h"
+#include "rdcp_dbus.h"
+#include "rdcp_util.h"
+#include "rdcp_realmdrealm.h"
+
+static const CMPIBroker* _cb = NULL;
+
+CMPIStatus LMI_RealmdKerberosRealmRef_InitFromDBusPath(
+ LMI_RealmdKerberosRealmRef* self,
+ const CMPIBroker* cb,
+ const char* ns,
+ const char* dbus_path)
+{
+ CMPIStatus status;
+
+ CMSetStatus(&status, CMPI_RC_OK);
+
+ LMI_RealmdRealmInitKeys(LMI_RealmdKerberosRealmRef, self, dbus_path);
+
+ return status;
+}
+
+CMPIStatus LMI_RealmdKerberosRealm_InitFromDBusPath(
+ LMI_RealmdKerberosRealm* self,
+ const CMPIBroker* cb,
+ const char* ns,
+ const char* dbus_path)
+{
+ CMPIStatus status;
+ GError *g_error = NULL;
+ GVariant *realm_props = NULL;
+ GVariant *kerberos_props = NULL;
+ GVariant *kerberos_membership_props = NULL;
+
+ CMSetStatus(&status, CMPI_RC_OK);
+
+ if (!rdcp_dbus_initialize(&g_error)) {
+ return handle_g_error(&g_error, _cb, &status, CMPI_RC_ERR_FAILED, "rdcp_dbus_initialize failed");
+ }
+
+ GET_DBUS_PROPERIES_OR_EXIT(realm_props, dbus_path,
+ REALM_DBUS_REALM_INTERFACE, &status);
+ GET_DBUS_PROPERIES_OR_EXIT(kerberos_props, dbus_path,
+ REALM_DBUS_KERBEROS_INTERFACE, &status);
+
+ LMI_RealmdRealmInitKeys(LMI_RealmdKerberosRealm, self, dbus_path);
+ LMI_InitFromDBusRealmProps(LMI_RealmdKerberosRealm, self, realm_props);
+ LMI_InitFromDBusKerberosRealmProps(LMI_RealmdKerberosRealm, self, kerberos_props);
+
+ if (SupportsDBusInterface(realm_props, REALM_DBUS_KERBEROS_MEMBERSHIP_INTERFACE)) {
+ GET_DBUS_PROPERIES_OR_EXIT(kerberos_membership_props, dbus_path,
+ REALM_DBUS_KERBEROS_MEMBERSHIP_INTERFACE, &status);
+
+ LMI_InitFromDBusKerberosMembershipProps(LMI_RealmdKerberosRealm, self,
+ kerberos_membership_props);
+ }
+
+ exit:
+ G_VARIANT_FREE(realm_props);
+ G_VARIANT_FREE(kerberos_props);
+ G_VARIANT_FREE(kerberos_membership_props);
+
+ return status;
+}
+
+
+static void LMI_RealmdKerberosRealmInitialize()
+{
+}
+
+static CMPIStatus LMI_RealmdKerberosRealmCleanup(
+ CMPIInstanceMI* mi,
+ const CMPIContext* cc,
+ CMPIBoolean term)
+{
+ CMReturn(CMPI_RC_OK);
+}
+
+static CMPIStatus LMI_RealmdKerberosRealmEnumInstanceNames(
+ CMPIInstanceMI* mi,
+ const CMPIContext* cc,
+ const CMPIResult* cr,
+ const CMPIObjectPath* cop)
+{
+ return KDefaultEnumerateInstanceNames(
+ _cb, mi, cc, cr, cop);
+}
+
+static CMPIStatus LMI_RealmdKerberosRealmEnumInstances(
+ CMPIInstanceMI* mi,
+ const CMPIContext* cc,
+ const CMPIResult* cr,
+ const CMPIObjectPath* cop,
+ const char** properties)
+{
+ CMPIStatus status;
+ GError *g_error = NULL;
+ GVariant *provider_props = NULL;
+ GVariant *realm_props = NULL;
+ GVariantIter *iter = NULL;
+ gchar *realm_obj_path;
+ const char *name_space = KNameSpace(cop);
+
+ CMSetStatus(&status, CMPI_RC_OK);
+
+ if (!rdcp_dbus_initialize(&g_error)) {
+ return handle_g_error(&g_error, _cb, &status, CMPI_RC_ERR_FAILED,
+ "rdcp_dbus_initialize failed");
+ }
+
+ GET_DBUS_PROPERIES_OR_EXIT(provider_props, REALM_DBUS_SERVICE_PATH,
+ REALM_DBUS_PROVIDER_INTERFACE, &status);
+
+ g_variant_lookup(provider_props, "Realms", "ao", &iter);
+ while (g_variant_iter_next(iter, "&o", &realm_obj_path)) {
+ LMI_RealmdKerberosRealm realmd_realm;
+
+ GET_DBUS_PROPERIES_OR_EXIT(realm_props, realm_obj_path,
+ REALM_DBUS_REALM_INTERFACE, &status);
+ if (!SupportsDBusInterface(realm_props, REALM_DBUS_KERBEROS_INTERFACE)) {
+ G_VARIANT_FREE(realm_props);
+ continue;
+ }
+ G_VARIANT_FREE(realm_props);
+
+ status = LMI_RealmdKerberosRealm_InitFromDBusPath(&realmd_realm, _cb,
+ name_space, realm_obj_path);
+ if (status.rc != CMPI_RC_OK) {
+ goto exit;
+ }
+
+ KReturnInstance(cr, realmd_realm);
+ }
+
+ exit:
+ G_VARIANT_ITER_FREE(iter);
+ G_VARIANT_FREE(provider_props);
+ G_VARIANT_FREE(realm_props);
+
+ return status;
+}
+
+static CMPIStatus LMI_RealmdKerberosRealmGetInstance(
+ CMPIInstanceMI* mi,
+ const CMPIContext* cc,
+ const CMPIResult* cr,
+ const CMPIObjectPath* cop,
+ const char** properties)
+{
+ return KDefaultGetInstance(
+ _cb, mi, cc, cr, cop, properties);
+}
+
+static CMPIStatus LMI_RealmdKerberosRealmCreateInstance(
+ CMPIInstanceMI* mi,
+ const CMPIContext* cc,
+ const CMPIResult* cr,
+ const CMPIObjectPath* cop,
+ const CMPIInstance* ci)
+{
+ CMReturn(CMPI_RC_ERR_NOT_SUPPORTED);
+}
+
+static CMPIStatus LMI_RealmdKerberosRealmModifyInstance(
+ CMPIInstanceMI* mi,
+ const CMPIContext* cc,
+ const CMPIResult* cr,
+ const CMPIObjectPath* cop,
+ const CMPIInstance* ci,
+ const char** properties)
+{
+ CMReturn(CMPI_RC_ERR_NOT_SUPPORTED);
+}
+
+static CMPIStatus LMI_RealmdKerberosRealmDeleteInstance(
+ CMPIInstanceMI* mi,
+ const CMPIContext* cc,
+ const CMPIResult* cr,
+ const CMPIObjectPath* cop)
+{
+ CMReturn(CMPI_RC_ERR_NOT_SUPPORTED);
+}
+
+static CMPIStatus LMI_RealmdKerberosRealmExecQuery(
+ CMPIInstanceMI* mi,
+ const CMPIContext* cc,
+ const CMPIResult* cr,
+ const CMPIObjectPath* cop,
+ const char* lang,
+ const char* query)
+{
+ CMReturn(CMPI_RC_ERR_NOT_SUPPORTED);
+}
+
+CMInstanceMIStub(
+ LMI_RealmdKerberosRealm,
+ LMI_RealmdKerberosRealm,
+ _cb,
+ LMI_RealmdKerberosRealmInitialize())
+
+static CMPIStatus LMI_RealmdKerberosRealmMethodCleanup(
+ CMPIMethodMI* mi,
+ const CMPIContext* cc,
+ CMPIBoolean term)
+{
+ CMReturn(CMPI_RC_OK);
+}
+
+static CMPIStatus LMI_RealmdKerberosRealmInvokeMethod(
+ CMPIMethodMI* mi,
+ const CMPIContext* cc,
+ const CMPIResult* cr,
+ const CMPIObjectPath* cop,
+ const char* meth,
+ const CMPIArgs* in,
+ CMPIArgs* out)
+{
+ return LMI_RealmdKerberosRealm_DispatchMethod(
+ _cb, mi, cc, cr, cop, meth, in, out);
+}
+
+CMMethodMIStub(
+ LMI_RealmdKerberosRealm,
+ LMI_RealmdKerberosRealm,
+ _cb,
+ LMI_RealmdKerberosRealmInitialize())
+
+KUint32 LMI_RealmdKerberosRealm_ChangeLoginPolicy(
+ const CMPIBroker* cb,
+ CMPIMethodMI* mi,
+ const CMPIContext* context,
+ const LMI_RealmdKerberosRealmRef* self,
+ const KString* LoginPolicy,
+ const KStringA* PermittedAdd,
+ const KStringA* PermittedRemove,
+ CMPIStatus* status)
+{
+ KUint32 result = KUINT32_INIT;
+
+ KSetStatus(status, ERR_NOT_SUPPORTED);
+ return result;
+}
+
+KUint32 LMI_RealmdKerberosRealm_Deconfigure(
+ const CMPIBroker* cb,
+ CMPIMethodMI* mi,
+ const CMPIContext* context,
+ const LMI_RealmdKerberosRealmRef* self,
+ CMPIStatus* status)
+{
+ KUint32 result = KUINT32_INIT;
+
+ KSetStatus(status, ERR_NOT_SUPPORTED);
+ return result;
+}
+
+KEXTERN KUint32 LMI_RealmdKerberosRealm_Join(
+ const CMPIBroker* cb,
+ CMPIMethodMI* mi,
+ const CMPIContext* context,
+ const LMI_RealmdKerberosRealmRef* self,
+ const KUint32* Type,
+ const KUint32* Owner,
+ const KString* Name,
+ const KString* Password,
+ const KUint8A* Data,
+ const KStringA* OptionNames,
+ const KStringA* OptionValues,
+ CMPIStatus* status)
+{
+ GError *g_error = NULL;
+ KUint32 result = KUINT32_INIT;
+ const char *cred_type = NULL;
+ const char *cred_owner = NULL;
+ gchar *data = NULL;
+ gsize data_len;
+ gchar *dbus_path = NULL;
+ GVariant *credentials = NULL;
+ GVariant *data_variant = NULL;
+ GVariant *options = NULL;
+
+ KUint32_Set(&result, LMI_REALMD_RESULT_SUCCESS);
+ CMSetStatus(status, CMPI_RC_OK);
+
+ if (!rdcp_dbus_initialize(&g_error)) {
+ handle_g_error(&g_error, _cb, status, CMPI_RC_ERR_FAILED, "rdcp_dbus_initialize failed");
+ KUint32_Set(&result, LMI_REALMD_RESULT_FAILED);
+ goto exit;
+ }
+
+ if (!Type->exists || Type->null) {
+ CMSetStatusWithChars(cb, status, CMPI_RC_ERR_INVALID_PARAMETER, "Type parameter absent");
+ KUint32_Set(&result, LMI_REALMD_RESULT_FAILED);
+ goto exit;
+ }
+
+ if (!Owner->exists || Owner->null) {
+ CMSetStatusWithChars(cb, status, CMPI_RC_ERR_INVALID_PARAMETER, "Owner parameter absent");
+ KUint32_Set(&result, LMI_REALMD_RESULT_FAILED);
+ goto exit;
+ }
+
+ if (!dbus_path_from_instance_id(self->InstanceID.chars, &dbus_path, &g_error)) {
+ handle_g_error(&g_error, cb, status, CMPI_RC_ERR_FAILED,
+ "dbus_path_from_instance_id() failed");
+ KUint32_Set(&result, LMI_REALMD_RESULT_FAILED);
+ goto exit;
+ }
+
+ switch(Owner->value) {
+ case LMI_RealmdKerberosRealm_SupportedJoinCredentialOwners_administrator:
+ case LMI_RealmdKerberosRealm_SupportedJoinCredentialOwners_user:
+ case LMI_RealmdKerberosRealm_SupportedJoinCredentialOwners_computer:
+ case LMI_RealmdKerberosRealm_SupportedJoinCredentialOwners_none:
+ break;
+ default:
+ CMSetStatusWithChars(cb, status, CMPI_RC_ERR_INVALID_PARAMETER, "Invalid Owner parameter");
+ KUint32_Set(&result, LMI_REALMD_RESULT_FAILED);
+ goto exit;
+ }
+
+ cred_type = SupportedJoinCredentialTypes_enum_to_name(Type->value);
+ cred_owner = SupportedJoinCredentialOwners_enum_to_name(Owner->value);
+
+ switch(Type->value) {
+ case LMI_RealmdKerberosRealm_SupportedJoinCredentialTypes_ccache:
+ if ((Name->exists && !Name->null) || (Password->exists && !Password->null)) {
+ CMSetStatusWithChars(cb, status, CMPI_RC_ERR_INVALID_PARAMETER,
+ "Name & Password parameters must be NULL when Type is ccache");
+ KUint32_Set(&result, LMI_REALMD_RESULT_FAILED);
+ goto exit;
+ }
+ if (!Data->exists || Data->null) {
+ CMSetStatusWithChars(cb, status, CMPI_RC_ERR_INVALID_PARAMETER,
+ "Data parameter must be provided when Type is ccache");
+ KUint32_Set(&result, LMI_REALMD_RESULT_FAILED);
+ goto exit;
+ }
+
+ if ((data = get_data_from_KUint8A(Data, &data_len)) == NULL) {
+ CMSetStatusWithChars(cb, status, CMPI_RC_ERR_FAILED,
+ "unabled to allocate memory");
+ KUint32_Set(&result, LMI_REALMD_RESULT_FAILED);
+ goto exit;
+ }
+ data_variant = g_variant_new_from_data(G_VARIANT_TYPE ("ay"),
+ data, data_len,
+ TRUE, g_free, (gpointer) data);
+
+ credentials = g_variant_new("(ssv)", cred_type, cred_owner, data_variant);
+ break;
+ case LMI_RealmdKerberosRealm_SupportedJoinCredentialTypes_password:
+ if (!Name->exists || Name->null || !Password->exists || Password->null) {
+ CMSetStatusWithChars(cb, status, CMPI_RC_ERR_INVALID_PARAMETER,
+ "Name & Password parameters must be provided when Type is password");
+ KUint32_Set(&result, LMI_REALMD_RESULT_FAILED);
+ goto exit;
+ }
+ if (Data->exists && !Data->null) {
+ CMSetStatusWithChars(cb, status, CMPI_RC_ERR_INVALID_PARAMETER,
+ "Data parameter must be NULL when Type is password");
+ KUint32_Set(&result, LMI_REALMD_RESULT_FAILED);
+ goto exit;
+ }
+
+ credentials = g_variant_new("(ssv)", cred_type, cred_owner,
+ g_variant_new("(ss)", Name->chars, Password->chars));
+
+ break;
+ case LMI_RealmdKerberosRealm_SupportedJoinCredentialTypes_secrect:
+ if ((Name->exists && !Name->null) || (Password->exists && !Password->null)) {
+ CMSetStatusWithChars(cb, status, CMPI_RC_ERR_INVALID_PARAMETER,
+ "Name & Password parameters must be NULL when Type is secret");
+ KUint32_Set(&result, LMI_REALMD_RESULT_FAILED);
+ goto exit;
+ }
+ if (!Data->exists || Data->null) {
+ CMSetStatusWithChars(cb, status, CMPI_RC_ERR_INVALID_PARAMETER,
+ "Data parameter must be provided when Type is secret");
+ KUint32_Set(&result, LMI_REALMD_RESULT_FAILED);
+ goto exit;
+ }
+
+ if ((data = get_data_from_KUint8A(Data, &data_len)) == NULL) {
+ CMSetStatusWithChars(cb, status, CMPI_RC_ERR_FAILED,
+ "unabled to allocate memory");
+ KUint32_Set(&result, LMI_REALMD_RESULT_FAILED);
+ goto exit;
+ }
+ credentials = g_variant_new("(ssv)", cred_type, cred_owner,
+ g_variant_new_fixed_array(G_VARIANT_TYPE_BYTE,
+ data, data_len, 1));
+ break;
+ case LMI_RealmdKerberosRealm_SupportedJoinCredentialTypes_automatic:
+ if ((Name->exists && !Name->null) || (Password->exists && !Password->null) ||
+ (Data->exists && !Data->null)) {
+ CMSetStatusWithChars(cb, status, CMPI_RC_ERR_INVALID_PARAMETER,
+ "Name, Password & Data parameters must be NULL when Type is secret");
+ KUint32_Set(&result, LMI_REALMD_RESULT_FAILED);
+ goto exit;
+ }
+
+ credentials = g_variant_new ("(ssv)", cred_type, cred_owner,
+ g_variant_new_string (""));
+
+ break;
+ default:
+ CMSetStatusWithChars(cb, status, CMPI_RC_ERR_INVALID_PARAMETER, "Invalid Type parameter");
+ KUint32_Set(&result, LMI_REALMD_RESULT_FAILED);
+ goto exit;
+ }
+
+
+ if (!build_g_variant_options_from_KStringA(OptionNames, OptionValues, &options, &g_error)) {
+ handle_g_error(&g_error, cb, status, CMPI_RC_ERR_FAILED,
+ "failed to convert options to gvariant");
+ KUint32_Set(&result, LMI_REALMD_RESULT_FAILED);
+ goto exit;
+ }
+
+ if (!dbus_join_call(system_bus, dbus_path, credentials, options, &g_error)) {
+ handle_g_error(&g_error, cb, status, CMPI_RC_ERR_FAILED, "dbus_join_call() failed");
+ KUint32_Set(&result, LMI_REALMD_RESULT_FAILED);
+ goto exit;
+ }
+
+
+ exit:
+
+ g_free(data);
+ G_VARIANT_FREE(credentials);
+ G_VARIANT_FREE(data_variant);
+ G_VARIANT_FREE(options);
+
+ return result;
+}
+
+KEXTERN KUint32 LMI_RealmdKerberosRealm_Leave(
+ const CMPIBroker* cb,
+ CMPIMethodMI* mi,
+ const CMPIContext* context,
+ const LMI_RealmdKerberosRealmRef* self,
+ const KUint32* Type,
+ const KUint32* Owner,
+ const KString* Name,
+ const KString* Password,
+ const KUint8A* Data,
+ const KStringA* OptionNames,
+ const KStringA* OptionValues,
+ CMPIStatus* status)
+{
+ GError *g_error = NULL;
+ KUint32 result = KUINT32_INIT;
+ const char *cred_type = NULL;
+ const char *cred_owner = NULL;
+ gchar *data = NULL;
+ gsize data_len;
+ gchar *dbus_path = NULL;
+ GVariant *credentials = NULL;
+ GVariant *data_variant = NULL;
+ GVariant *options = NULL;
+
+ KUint32_Set(&result, LMI_REALMD_RESULT_SUCCESS);
+ CMSetStatus(status, CMPI_RC_OK);
+
+ if (!rdcp_dbus_initialize(&g_error)) {
+ handle_g_error(&g_error, _cb, status, CMPI_RC_ERR_FAILED, "rdcp_dbus_initialize failed");
+ KUint32_Set(&result, LMI_REALMD_RESULT_FAILED);
+ goto exit;
+ }
+
+ if (!Type->exists || Type->null) {
+ CMSetStatusWithChars(cb, status, CMPI_RC_ERR_INVALID_PARAMETER, "Type parameter absent");
+ KUint32_Set(&result, LMI_REALMD_RESULT_FAILED);
+ goto exit;
+ }
+
+ if (!Owner->exists || Owner->null) {
+ CMSetStatusWithChars(cb, status, CMPI_RC_ERR_INVALID_PARAMETER, "Owner parameter absent");
+ KUint32_Set(&result, LMI_REALMD_RESULT_FAILED);
+ goto exit;
+ }
+
+ if (!dbus_path_from_instance_id(self->InstanceID.chars, &dbus_path, &g_error)) {
+ handle_g_error(&g_error, cb, status, CMPI_RC_ERR_FAILED,
+ "dbus_path_from_instance_id() failed");
+ KUint32_Set(&result, LMI_REALMD_RESULT_FAILED);
+ goto exit;
+ }
+
+ switch(Owner->value) {
+ case LMI_RealmdKerberosRealm_SupportedLeaveCredentialOwners_administrator:
+ case LMI_RealmdKerberosRealm_SupportedLeaveCredentialOwners_user:
+ case LMI_RealmdKerberosRealm_SupportedLeaveCredentialOwners_computer:
+ case LMI_RealmdKerberosRealm_SupportedLeaveCredentialOwners_none:
+ break;
+ default:
+ CMSetStatusWithChars(cb, status, CMPI_RC_ERR_INVALID_PARAMETER, "Invalid Owner parameter");
+ KUint32_Set(&result, LMI_REALMD_RESULT_FAILED);
+ goto exit;
+ }
+
+ cred_type = SupportedLeaveCredentialTypes_enum_to_name(Type->value);
+ cred_owner = SupportedLeaveCredentialOwners_enum_to_name(Owner->value);
+
+ switch(Type->value) {
+ case LMI_RealmdKerberosRealm_SupportedLeaveCredentialTypes_ccache:
+ if ((Name->exists && !Name->null) || (Password->exists && !Password->null)) {
+ CMSetStatusWithChars(cb, status, CMPI_RC_ERR_INVALID_PARAMETER,
+ "Name & Password parameters must be NULL when Type is ccache");
+ KUint32_Set(&result, LMI_REALMD_RESULT_FAILED);
+ goto exit;
+ }
+ if (!Data->exists || Data->null) {
+ CMSetStatusWithChars(cb, status, CMPI_RC_ERR_INVALID_PARAMETER,
+ "Data parameter must be provided when Type is ccache");
+ KUint32_Set(&result, LMI_REALMD_RESULT_FAILED);
+ goto exit;
+ }
+
+ if ((data = get_data_from_KUint8A(Data, &data_len)) == NULL) {
+ CMSetStatusWithChars(cb, status, CMPI_RC_ERR_FAILED,
+ "unabled to allocate memory");
+ KUint32_Set(&result, LMI_REALMD_RESULT_FAILED);
+ goto exit;
+ }
+ data_variant = g_variant_new_from_data(G_VARIANT_TYPE ("ay"),
+ data, data_len,
+ TRUE, g_free, (gpointer) data);
+
+ credentials = g_variant_new("(ssv)", cred_type, cred_owner, data_variant);
+ break;
+ case LMI_RealmdKerberosRealm_SupportedLeaveCredentialTypes_password:
+ if (!Name->exists || Name->null || !Password->exists || Password->null) {
+ CMSetStatusWithChars(cb, status, CMPI_RC_ERR_INVALID_PARAMETER,
+ "Name & Password parameters must be provided when Type is password");
+ KUint32_Set(&result, LMI_REALMD_RESULT_FAILED);
+ goto exit;
+ }
+ if (Data->exists && !Data->null) {
+ CMSetStatusWithChars(cb, status, CMPI_RC_ERR_INVALID_PARAMETER,
+ "Data parameter must be NULL when Type is password");
+ KUint32_Set(&result, LMI_REALMD_RESULT_FAILED);
+ goto exit;
+ }
+
+ credentials = g_variant_new("(ssv)", cred_type, cred_owner,
+ g_variant_new("(ss)", Name->chars, Password->chars));
+
+ break;
+ case LMI_RealmdKerberosRealm_SupportedLeaveCredentialTypes_secrect:
+ if ((Name->exists && !Name->null) || (Password->exists && !Password->null)) {
+ CMSetStatusWithChars(cb, status, CMPI_RC_ERR_INVALID_PARAMETER,
+ "Name & Password parameters must be NULL when Type is secret");
+ KUint32_Set(&result, LMI_REALMD_RESULT_FAILED);
+ goto exit;
+ }
+ if (!Data->exists || Data->null) {
+ CMSetStatusWithChars(cb, status, CMPI_RC_ERR_INVALID_PARAMETER,
+ "Data parameter must be provided when Type is secret");
+ KUint32_Set(&result, LMI_REALMD_RESULT_FAILED);
+ goto exit;
+ }
+
+ if ((data = get_data_from_KUint8A(Data, &data_len)) == NULL) {
+ CMSetStatusWithChars(cb, status, CMPI_RC_ERR_FAILED,
+ "unabled to allocate memory");
+ KUint32_Set(&result, LMI_REALMD_RESULT_FAILED);
+ goto exit;
+ }
+ credentials = g_variant_new("(ssv)", cred_type, cred_owner,
+ g_variant_new_fixed_array(G_VARIANT_TYPE_BYTE,
+ data, data_len, 1));
+ break;
+ case LMI_RealmdKerberosRealm_SupportedLeaveCredentialTypes_automatic:
+ if ((Name->exists && !Name->null) || (Password->exists && !Password->null) ||
+ (Data->exists && !Data->null)) {
+ CMSetStatusWithChars(cb, status, CMPI_RC_ERR_INVALID_PARAMETER,
+ "Name, Password & Data parameters must be NULL when Type is secret");
+ KUint32_Set(&result, LMI_REALMD_RESULT_FAILED);
+ goto exit;
+ }
+
+ credentials = g_variant_new ("(ssv)", cred_type, cred_owner,
+ g_variant_new_string (""));
+
+ break;
+ default:
+ CMSetStatusWithChars(cb, status, CMPI_RC_ERR_INVALID_PARAMETER, "Invalid Type parameter");
+ KUint32_Set(&result, LMI_REALMD_RESULT_FAILED);
+ goto exit;
+ }
+
+
+ if (!build_g_variant_options_from_KStringA(OptionNames, OptionValues, &options, &g_error)) {
+ handle_g_error(&g_error, cb, status, CMPI_RC_ERR_FAILED,
+ "failed to convert options to gvariant");
+ KUint32_Set(&result, LMI_REALMD_RESULT_FAILED);
+ goto exit;
+ }
+
+ if (!dbus_leave_call(system_bus, dbus_path, credentials, options, &g_error)) {
+ handle_g_error(&g_error, cb, status, CMPI_RC_ERR_FAILED, "dbus_leave_call() failed");
+ KUint32_Set(&result, LMI_REALMD_RESULT_FAILED);
+ goto exit;
+ }
+
+
+ exit:
+
+ g_free(data);
+ G_VARIANT_FREE(credentials);
+ G_VARIANT_FREE(data_variant);
+ G_VARIANT_FREE(options);
+
+ return result;
+}
+
+
+KONKRET_REGISTRATION(
+ "root/cimv2",
+ "LMI_RealmdKerberosRealm",
+ "LMI_RealmdKerberosRealm",
+ "instance method");
diff --git a/src/realmd/LMI_RealmdRealmProvider.c b/src/realmd/LMI_RealmdRealmProvider.c
new file mode 100644
index 0000000..d8e0a18
--- /dev/null
+++ b/src/realmd/LMI_RealmdRealmProvider.c
@@ -0,0 +1,326 @@
+#include <konkret/konkret.h>
+#include <string.h>
+#include "LMI_RealmdRealm.h"
+#include "globals.h"
+#include "rdcp_error.h"
+#include "rdcp_dbus.h"
+#include "rdcp_util.h"
+#include "rdcp_realmdrealm.h"
+
+static const CMPIBroker* _cb = NULL;
+
+CMPIStatus LMI_RealmdRealmRef_InitFromDBusPath(
+ LMI_RealmdRealmRef* self,
+ const CMPIBroker* cb,
+ const char* ns,
+ const char* dbus_path)
+{
+ CMPIStatus status;
+
+ CMSetStatus(&status, CMPI_RC_OK);
+
+ LMI_RealmdRealmInitKeys(LMI_RealmdRealmRef, self, dbus_path);
+
+ return status;
+}
+
+CMPIStatus LMI_RealmdRealm_InitFromDBusPath(
+ LMI_RealmdRealm* self,
+ const CMPIBroker* cb,
+ const char* ns,
+ const char* dbus_path)
+{
+ CMPIStatus status;
+ GError *g_error = NULL;
+ GVariant *realm_props = NULL;
+
+ CMSetStatus(&status, CMPI_RC_OK);
+
+ if (!rdcp_dbus_initialize(&g_error)) {
+ return handle_g_error(&g_error, _cb, &status, CMPI_RC_ERR_FAILED, "rdcp_dbus_initialize failed");
+ }
+
+ GET_DBUS_PROPERIES_OR_EXIT(realm_props, dbus_path,
+ REALM_DBUS_REALM_INTERFACE, &status);
+
+ LMI_RealmdRealmInitKeys(LMI_RealmdRealm, self, dbus_path);
+ LMI_InitFromDBusRealmProps(LMI_RealmdRealm, self, realm_props);
+
+ exit:
+ G_VARIANT_FREE(realm_props);
+
+ return status;
+}
+
+static void LMI_RealmdRealmInitialize()
+{
+}
+
+static CMPIStatus LMI_RealmdRealmCleanup(
+ CMPIInstanceMI* mi,
+ const CMPIContext* cc,
+ CMPIBoolean term)
+{
+ CMReturn(CMPI_RC_OK);
+}
+
+static CMPIStatus LMI_RealmdRealmEnumInstanceNames(
+ CMPIInstanceMI* mi,
+ const CMPIContext* cc,
+ const CMPIResult* cr,
+ const CMPIObjectPath* cop)
+{
+ return KDefaultEnumerateInstanceNames(
+ _cb, mi, cc, cr, cop);
+}
+
+static CMPIStatus LMI_RealmdRealmEnumInstances(
+ CMPIInstanceMI* mi,
+ const CMPIContext* cc,
+ const CMPIResult* cr,
+ const CMPIObjectPath* cop,
+ const char** properties)
+{
+ CMPIStatus status;
+ GError *g_error = NULL;
+ GVariant *provider_props = NULL;
+ GVariantIter *iter = NULL;
+ gchar *realm_obj_path;
+ const char *name_space = KNameSpace(cop);
+
+ CMSetStatus(&status, CMPI_RC_OK);
+
+ if (!rdcp_dbus_initialize(&g_error)) {
+ return handle_g_error(&g_error, _cb, &status, CMPI_RC_ERR_FAILED, "rdcp_dbus_initialize failed");
+ }
+
+ GET_DBUS_PROPERIES_OR_EXIT(provider_props, REALM_DBUS_SERVICE_PATH,
+ REALM_DBUS_PROVIDER_INTERFACE, &status);
+
+ g_variant_lookup(provider_props, "Realms", "ao", &iter);
+ while (g_variant_iter_next(iter, "&o", &realm_obj_path)) {
+ LMI_RealmdRealm realmd_realm;
+
+ status = LMI_RealmdRealm_InitFromDBusPath(&realmd_realm, _cb,
+ name_space, realm_obj_path);
+ if (status.rc != CMPI_RC_OK) {
+ goto exit;
+ }
+ KReturnInstance(cr, realmd_realm);
+ }
+
+ exit:
+ G_VARIANT_ITER_FREE(iter);
+ G_VARIANT_FREE(provider_props);
+
+ return status;
+}
+
+static CMPIStatus LMI_RealmdRealmGetInstance(
+ CMPIInstanceMI* mi,
+ const CMPIContext* cc,
+ const CMPIResult* cr,
+ const CMPIObjectPath* cop,
+ const char** properties)
+{
+ CMPIStatus status;
+ GError *g_error = NULL;
+ LMI_RealmdRealmRef realmdrealm_ref;
+ gchar *dbus_path = NULL;
+ LMI_RealmdRealm realmd_realm;
+
+ CMSetStatus(&status, CMPI_RC_OK);
+
+ KReturnIf(LMI_RealmdRealmRef_InitFromObjectPath(&realmdrealm_ref, _cb, cop));
+
+ if (!dbus_path_from_instance_id(realmdrealm_ref.InstanceID.chars, &dbus_path, &g_error)) {
+ return handle_g_error(&g_error, _cb, &status, CMPI_RC_ERR_FAILED,
+ "dbus_path_from_instance_id() failed");
+ }
+
+ KReturnIf(LMI_RealmdRealm_InitFromDBusPath(&realmd_realm, _cb, KNameSpace(cop), dbus_path));
+
+ KReturnInstance(cr, realmd_realm);
+
+ return status;
+}
+
+static CMPIStatus LMI_RealmdRealmCreateInstance(
+ CMPIInstanceMI* mi,
+ const CMPIContext* cc,
+ const CMPIResult* cr,
+ const CMPIObjectPath* cop,
+ const CMPIInstance* ci)
+{
+ CMReturn(CMPI_RC_ERR_NOT_SUPPORTED);
+}
+
+static CMPIStatus LMI_RealmdRealmModifyInstance(
+ CMPIInstanceMI* mi,
+ const CMPIContext* cc,
+ const CMPIResult* cr,
+ const CMPIObjectPath* cop,
+ const CMPIInstance* ci,
+ const char** properties)
+{
+ CMReturn(CMPI_RC_ERR_NOT_SUPPORTED);
+}
+
+static CMPIStatus LMI_RealmdRealmDeleteInstance(
+ CMPIInstanceMI* mi,
+ const CMPIContext* cc,
+ const CMPIResult* cr,
+ const CMPIObjectPath* cop)
+{
+ CMReturn(CMPI_RC_ERR_NOT_SUPPORTED);
+}
+
+static CMPIStatus LMI_RealmdRealmExecQuery(
+ CMPIInstanceMI* mi,
+ const CMPIContext* cc,
+ const CMPIResult* cr,
+ const CMPIObjectPath* cop,
+ const char* lang,
+ const char* query)
+{
+ CMReturn(CMPI_RC_ERR_NOT_SUPPORTED);
+}
+
+CMInstanceMIStub(
+ LMI_RealmdRealm,
+ LMI_RealmdRealm,
+ _cb,
+ LMI_RealmdRealmInitialize())
+
+static CMPIStatus LMI_RealmdRealmMethodCleanup(
+ CMPIMethodMI* mi,
+ const CMPIContext* cc,
+ CMPIBoolean term)
+{
+ CMReturn(CMPI_RC_OK);
+}
+
+static CMPIStatus LMI_RealmdRealmInvokeMethod(
+ CMPIMethodMI* mi,
+ const CMPIContext* cc,
+ const CMPIResult* cr,
+ const CMPIObjectPath* cop,
+ const char* meth,
+ const CMPIArgs* in,
+ CMPIArgs* out)
+{
+ return LMI_RealmdRealm_DispatchMethod(
+ _cb, mi, cc, cr, cop, meth, in, out);
+}
+
+CMMethodMIStub(
+ LMI_RealmdRealm,
+ LMI_RealmdRealm,
+ _cb,
+ LMI_RealmdRealmInitialize())
+
+KUint32 LMI_RealmdRealm_ChangeLoginPolicy(
+ const CMPIBroker* cb,
+ CMPIMethodMI* mi,
+ const CMPIContext* context,
+ const LMI_RealmdRealmRef* self,
+ const KString* LoginPolicy,
+ const KStringA* PermittedAdd,
+ const KStringA* PermittedRemove,
+ CMPIStatus* status)
+{
+ GError *g_error = NULL;
+ KUint32 result = KUINT32_INIT;
+ gchar *dbus_path = NULL;
+ const gchar *login_policy = NULL;
+ GVariant *permitted_add = NULL;
+ GVariant *permitted_remove = NULL;
+ GVariant *options = NULL;
+
+ KUint32_Set(&result, LMI_REALMD_RESULT_SUCCESS);
+ CMSetStatus(status, CMPI_RC_OK);
+
+ if (!rdcp_dbus_initialize(&g_error)) {
+ handle_g_error(&g_error, _cb, status, CMPI_RC_ERR_FAILED, "rdcp_dbus_initialize failed");
+ KUint32_Set(&result, LMI_REALMD_RESULT_FAILED);
+ goto exit;
+ }
+
+ if (!LoginPolicy->exists || LoginPolicy->null) {
+ login_policy = "";
+ } else {
+ login_policy = LoginPolicy->chars;
+ }
+
+ if (!PermittedAdd->exists || PermittedAdd->null) {
+ CMSetStatusWithChars(cb, status, CMPI_RC_ERR_INVALID_PARAMETER, "PermittedAdd parameter absent");
+ KUint32_Set(&result, LMI_REALMD_RESULT_FAILED);
+ goto exit;
+ }
+
+ if (!PermittedRemove->exists || PermittedRemove->null) {
+ CMSetStatusWithChars(cb, status, CMPI_RC_ERR_INVALID_PARAMETER, "PermittedRemove parameter absent");
+ KUint32_Set(&result, LMI_REALMD_RESULT_FAILED);
+ goto exit;
+ }
+
+ if (!dbus_path_from_instance_id(self->InstanceID.chars, &dbus_path, &g_error)) {
+ handle_g_error(&g_error, cb, status, CMPI_RC_ERR_FAILED,
+ "dbus_path_from_instance_id() failed");
+ KUint32_Set(&result, LMI_REALMD_RESULT_FAILED);
+ goto exit;
+ }
+
+ if (!build_g_variant_string_array_from_KStringA(PermittedAdd, &permitted_add, &g_error)) {
+ handle_g_error(&g_error, cb, status, CMPI_RC_ERR_FAILED,
+ "failed to convert PermittedAdd to gvariant array");
+ KUint32_Set(&result, LMI_REALMD_RESULT_FAILED);
+ goto exit;
+ }
+
+ if (!build_g_variant_string_array_from_KStringA(PermittedRemove, &permitted_remove, &g_error)) {
+ handle_g_error(&g_error, cb, status, CMPI_RC_ERR_FAILED,
+ "failed to convert PermittedRemove to gvariant array");
+ KUint32_Set(&result, LMI_REALMD_RESULT_FAILED);
+ goto exit;
+ }
+
+ /* For now we don't pass any options so just create an empty dictionary */
+ options = g_variant_new_array(G_VARIANT_TYPE ("{sv}"), NULL, 0);
+
+ if (!dbus_change_login_policy_call(system_bus, dbus_path, login_policy,
+ permitted_add, permitted_remove,
+ options, &g_error)) {
+ handle_g_error(&g_error, cb, status, CMPI_RC_ERR_FAILED, "dbus_change_login_policy_call() failed");
+ KUint32_Set(&result, LMI_REALMD_RESULT_FAILED);
+ goto exit;
+ }
+
+
+ exit:
+
+ G_VARIANT_FREE(permitted_add);
+ G_VARIANT_FREE(permitted_remove);
+ G_VARIANT_FREE(options);
+
+ return result;
+}
+
+KUint32 LMI_RealmdRealm_Deconfigure(
+ const CMPIBroker* cb,
+ CMPIMethodMI* mi,
+ const CMPIContext* context,
+ const LMI_RealmdRealmRef* self,
+ CMPIStatus* status)
+{
+ KUint32 result = KUINT32_INIT;
+
+ KSetStatus(status, ERR_NOT_SUPPORTED);
+ return result;
+}
+
+KONKRET_REGISTRATION(
+ "root/cimv2",
+ "LMI_RealmdRealm",
+ "LMI_RealmdRealm",
+ "instance method");
diff --git a/src/realmd/LMI_RealmdServiceProvider.c b/src/realmd/LMI_RealmdServiceProvider.c
new file mode 100644
index 0000000..01fc0b8
--- /dev/null
+++ b/src/realmd/LMI_RealmdServiceProvider.c
@@ -0,0 +1,631 @@
+#include <konkret/konkret.h>
+#include "LMI_RealmdService.h"
+#include "globals.h"
+#include "rdcp_error.h"
+#include "rdcp_dbus.h"
+#include "rdcp_util.h"
+#include "rdcp_realmdrealm.h"
+
+static const CMPIBroker* _cb = NULL;
+
+/**
+ * get_joined_domain:
+ *
+ * @provider_props Realmd service provider properties
+ *
+ * Determine if the host is joined to a domain and if so return the domain name.
+ *
+ * Returns: domain name if found, NULL otherwise. Must be freed with g_free.
+ */
+static gchar *
+get_joined_domain(GVariant *provider_props)
+{
+ CMPIStatus status;
+ GError *g_error = NULL;
+ GVariant *realm_props = NULL;
+ GVariant *kerberos_props = NULL;
+ GVariantIter *iter = NULL;
+ gchar *realm_obj_path = NULL;
+ gchar *configured_interface = NULL;
+ gchar *domain_name = NULL;
+
+ CMSetStatus(&status, CMPI_RC_OK);
+
+ g_variant_lookup(provider_props, "Realms", "ao", &iter);
+ while (g_variant_iter_next(iter, "&o", &realm_obj_path)) {
+ GET_DBUS_PROPERIES_OR_EXIT(realm_props, realm_obj_path,
+ REALM_DBUS_REALM_INTERFACE, &status);
+ if (g_variant_lookup(realm_props, "Configured", "&s", &configured_interface)) {
+ if (strlen(configured_interface)) {
+ if (strcmp(configured_interface, REALM_DBUS_KERBEROS_MEMBERSHIP_INTERFACE) == 0) {
+ GET_DBUS_PROPERIES_OR_EXIT(kerberos_props, realm_obj_path,
+ REALM_DBUS_KERBEROS_INTERFACE, &status);
+ if (g_variant_lookup(kerberos_props, "DomainName", "&s", &domain_name)) {
+ goto exit;
+ }
+ G_VARIANT_FREE(kerberos_props);
+ }
+ }
+ }
+ G_VARIANT_FREE(realm_props);
+ }
+
+ exit:
+ G_VARIANT_ITER_FREE(iter);
+ G_VARIANT_FREE(realm_props);
+ G_VARIANT_FREE(kerberos_props);
+
+ return domain_name ? g_strdup(domain_name) : NULL;
+}
+
+
+
+static void LMI_RealmdServiceInitialize()
+{
+}
+
+static CMPIStatus LMI_RealmdServiceCleanup(
+ CMPIInstanceMI* mi,
+ const CMPIContext* cc,
+ CMPIBoolean term)
+{
+ CMReturn(CMPI_RC_OK);
+}
+
+static CMPIStatus LMI_RealmdServiceEnumInstanceNames(
+ CMPIInstanceMI* mi,
+ const CMPIContext* cc,
+ const CMPIResult* cr,
+ const CMPIObjectPath* cop)
+{
+ return KDefaultEnumerateInstanceNames(
+ _cb, mi, cc, cr, cop);
+}
+
+static CMPIStatus LMI_RealmdServiceEnumInstances(
+ CMPIInstanceMI* mi,
+ const CMPIContext* cc,
+ const CMPIResult* cr,
+ const CMPIObjectPath* cop,
+ const char** properties)
+{
+ CMPIStatus status;
+ GError *g_error = NULL;
+ LMI_RealmdService lmi_realmd_service;
+ const char *name_space = KNameSpace(cop);
+ const char *host_name = get_system_name();
+ CMPICount i;
+ GVariant *provider_props = NULL;
+ GVariantIter *iter;
+ gsize n_items;
+ gchar *realm_obj_path;
+ gchar *name = NULL;
+ gchar *version = NULL;
+ gchar *joined_domain = NULL;
+
+ CMSetStatus(&status, CMPI_RC_OK);
+
+ if (!rdcp_dbus_initialize(&g_error)) {
+ return handle_g_error(&g_error, _cb, &status, CMPI_RC_ERR_FAILED, "rdcp_dbus_initialize failed");
+ }
+
+ LMI_InitRealmdServiceKeys(LMI_RealmdService, &lmi_realmd_service, name_space, host_name);
+
+ GET_DBUS_PROPERIES_OR_EXIT(provider_props, REALM_DBUS_SERVICE_PATH,
+ REALM_DBUS_PROVIDER_INTERFACE, &status);
+
+ g_variant_lookup(provider_props, "Realms", "ao", &iter);
+ n_items = g_variant_iter_n_children(iter);
+ LMI_RealmdService_Init_Realms(&lmi_realmd_service, n_items);
+ for (i = 0; g_variant_iter_next(iter, "&o", &realm_obj_path); i++) {
+#ifdef RDCP_DEBUG
+ printf("path[%d]=%s\n", i, realm_obj_path);
+#endif
+ LMI_RealmdService_Set_Realms(&lmi_realmd_service, i, realm_obj_path);
+ }
+
+ if (g_variant_lookup(provider_props, "Name", "&s", &name)) {
+ LMI_RealmdService_Set_RealmdName(&lmi_realmd_service, name);
+ }
+
+ if (g_variant_lookup(provider_props, "Version", "&s", &version)) {
+ LMI_RealmdService_Set_RealmdVersion(&lmi_realmd_service, version);
+ }
+
+ if ((joined_domain = get_joined_domain(provider_props))) {
+ LMI_RealmdService_Set_Domain(&lmi_realmd_service, joined_domain);
+ }
+
+ KReturnInstance(cr, lmi_realmd_service);
+
+ exit:
+ G_VARIANT_ITER_FREE(iter);
+ G_VARIANT_FREE(provider_props);
+ g_free(joined_domain);
+
+ return status;
+}
+
+static CMPIStatus LMI_RealmdServiceGetInstance(
+ CMPIInstanceMI* mi,
+ const CMPIContext* cc,
+ const CMPIResult* cr,
+ const CMPIObjectPath* cop,
+ const char** properties)
+{
+ return KDefaultGetInstance(
+ _cb, mi, cc, cr, cop, properties);
+}
+
+static CMPIStatus LMI_RealmdServiceCreateInstance(
+ CMPIInstanceMI* mi,
+ const CMPIContext* cc,
+ const CMPIResult* cr,
+ const CMPIObjectPath* cop,
+ const CMPIInstance* ci)
+{
+ CMReturn(CMPI_RC_ERR_NOT_SUPPORTED);
+}
+
+static CMPIStatus LMI_RealmdServiceModifyInstance(
+ CMPIInstanceMI* mi,
+ const CMPIContext* cc,
+ const CMPIResult* cr,
+ const CMPIObjectPath* cop,
+ const CMPIInstance* ci,
+ const char** properties)
+{
+ CMReturn(CMPI_RC_ERR_NOT_SUPPORTED);
+}
+
+static CMPIStatus LMI_RealmdServiceDeleteInstance(
+ CMPIInstanceMI* mi,
+ const CMPIContext* cc,
+ const CMPIResult* cr,
+ const CMPIObjectPath* cop)
+{
+ CMReturn(CMPI_RC_ERR_NOT_SUPPORTED);
+}
+
+static CMPIStatus LMI_RealmdServiceExecQuery(
+ CMPIInstanceMI* mi,
+ const CMPIContext* cc,
+ const CMPIResult* cr,
+ const CMPIObjectPath* cop,
+ const char* lang,
+ const char* query)
+{
+ CMReturn(CMPI_RC_ERR_NOT_SUPPORTED);
+}
+
+CMInstanceMIStub(
+ LMI_RealmdService,
+ LMI_RealmdService,
+ _cb,
+ LMI_RealmdServiceInitialize())
+
+static CMPIStatus LMI_RealmdServiceMethodCleanup(
+ CMPIMethodMI* mi,
+ const CMPIContext* cc,
+ CMPIBoolean term)
+{
+ CMReturn(CMPI_RC_OK);
+}
+
+static CMPIStatus LMI_RealmdServiceInvokeMethod(
+ CMPIMethodMI* mi,
+ const CMPIContext* cc,
+ const CMPIResult* cr,
+ const CMPIObjectPath* cop,
+ const char* meth,
+ const CMPIArgs* in,
+ CMPIArgs* out)
+{
+ return LMI_RealmdService_DispatchMethod(
+ _cb, mi, cc, cr, cop, meth, in, out);
+}
+
+CMMethodMIStub(
+ LMI_RealmdService,
+ LMI_RealmdService,
+ _cb,
+ LMI_RealmdServiceInitialize())
+
+KUint32 LMI_RealmdService_RequestStateChange(
+ const CMPIBroker* cb,
+ CMPIMethodMI* mi,
+ const CMPIContext* context,
+ const LMI_RealmdServiceRef* self,
+ const KUint16* RequestedState,
+ KRef* Job,
+ const KDateTime* TimeoutPeriod,
+ CMPIStatus* status)
+{
+ KUint32 result = KUINT32_INIT;
+
+ KSetStatus(status, ERR_NOT_SUPPORTED);
+ return result;
+}
+
+KUint32 LMI_RealmdService_StartService(
+ const CMPIBroker* cb,
+ CMPIMethodMI* mi,
+ const CMPIContext* context,
+ const LMI_RealmdServiceRef* self,
+ CMPIStatus* status)
+{
+ KUint32 result = KUINT32_INIT;
+
+ KSetStatus(status, ERR_NOT_SUPPORTED);
+ return result;
+}
+
+KUint32 LMI_RealmdService_StopService(
+ const CMPIBroker* cb,
+ CMPIMethodMI* mi,
+ const CMPIContext* context,
+ const LMI_RealmdServiceRef* self,
+ CMPIStatus* status)
+{
+ KUint32 result = KUINT32_INIT;
+
+ KSetStatus(status, ERR_NOT_SUPPORTED);
+ return result;
+}
+
+KUint32 LMI_RealmdService_ChangeAffectedElementsAssignedSequence(
+ const CMPIBroker* cb,
+ CMPIMethodMI* mi,
+ const CMPIContext* context,
+ const LMI_RealmdServiceRef* self,
+ const KRefA* ManagedElements,
+ const KUint16A* AssignedSequence,
+ KRef* Job,
+ CMPIStatus* status)
+{
+ KUint32 result = KUINT32_INIT;
+
+ KSetStatus(status, ERR_NOT_SUPPORTED);
+ return result;
+}
+
+KUint32 LMI_RealmdService_Discover(
+ const CMPIBroker* cb,
+ CMPIMethodMI* mi,
+ const CMPIContext* context,
+ const LMI_RealmdServiceRef* self,
+ const KString* Target,
+ const KStringA* OptionNames,
+ const KStringA* OptionValues,
+ KRefA* DiscoveredRealms,
+ CMPIStatus* status)
+{
+ GError *g_error = NULL;
+ KUint32 result = KUINT32_INIT;
+ GVariant *options = NULL;
+ gint32 relevance = 0;
+ gchar **paths = NULL;
+ gchar *path, **pp;
+ CMPICount i, n_paths;
+
+ KUint32_Set(&result, LMI_REALMD_RESULT_SUCCESS);
+ CMSetStatus(status, CMPI_RC_OK);
+
+ if (!rdcp_dbus_initialize(&g_error)) {
+ handle_g_error(&g_error, _cb, status, CMPI_RC_ERR_FAILED, "rdcp_dbus_initialize failed");
+ KUint32_Set(&result, LMI_REALMD_RESULT_FAILED);
+ goto exit;
+ }
+
+ if (!build_g_variant_options_from_KStringA(OptionNames, OptionValues, &options, &g_error)) {
+ handle_g_error(&g_error, _cb, status, CMPI_RC_ERR_FAILED,
+ "failed to convert options to gvariant");
+ KUint32_Set(&result, LMI_REALMD_RESULT_FAILED);
+ goto exit;
+ }
+
+ if (!dbus_discover_call(system_bus, Target->chars, options,
+ &relevance, &paths, &g_error)) {
+ handle_g_error(&g_error, _cb, status, CMPI_RC_ERR_FAILED, "dbus_discover_call() failed");
+ KUint32_Set(&result, LMI_REALMD_RESULT_FAILED);
+ goto exit;
+ }
+
+#ifdef RDCP_DEBUG
+ print_paths(paths, "%s: target=%s, paths:", __FUNCTION__, Target->chars);
+#endif
+
+ for (pp = paths, path = *pp++, n_paths = 0; path; path = *pp++, n_paths++);
+
+ if (!KRefA_Init(DiscoveredRealms, cb, n_paths)) {
+ KUint32_Set(&result, LMI_REALMD_RESULT_FAILED);
+ goto exit;
+ }
+
+ for (pp = paths, path = *pp++, i = 0; path; path = *pp++, i++) {
+ LMI_RealmdRealmRef realmdrealm_ref;
+ CMPIObjectPath *realmdrealm_op;
+
+
+ *status = LMI_RealmdRealmRef_InitFromDBusPath(&realmdrealm_ref, cb,
+ LMI_RealmdServiceRef_NameSpace((LMI_RealmdServiceRef*)self), path);
+ if (status->rc != CMPI_RC_OK) {
+ KUint32_Set(&result, LMI_REALMD_RESULT_FAILED);
+ goto exit;
+ }
+
+ if ((realmdrealm_op = LMI_RealmdRealmRef_ToObjectPath(&realmdrealm_ref, status)) == NULL) {
+ goto exit;
+ }
+ if (!KRefA_Set(DiscoveredRealms, i, realmdrealm_op)) {
+ KUint32_Set(&result, LMI_REALMD_RESULT_FAILED);
+ goto exit;
+ }
+ }
+
+ exit:
+
+ G_VARIANT_FREE(options);
+ g_strfreev(paths);
+
+ return result;
+}
+
+// FIXME
+static gboolean
+get_credential_supported_owner(GVariant *supported, const gchar *cred_type, const gchar **cred_owner_return)
+{
+ GVariantIter iter;
+ const gchar *type;
+ const gchar *owner;
+
+ g_variant_iter_init (&iter, supported);
+ while (g_variant_iter_loop (&iter, "(&s&s)", &type, &owner)) {
+ if (g_str_equal (cred_type, type)) {
+ *cred_owner_return = owner;
+ return TRUE;
+ }
+ }
+
+ return FALSE;
+}
+
+static gboolean
+is_credential_supported (GVariant *supported, const gchar *cred_type, const gchar *cred_owner)
+{
+ GVariantIter iter;
+ const gchar *type;
+ const gchar *owner;
+
+ g_variant_iter_init(&iter, supported);
+ while (g_variant_iter_loop (&iter, "(&s&s)", &type, &owner)) {
+ if (g_str_equal(cred_type, type) &&
+ g_str_equal(cred_owner, owner)) {
+ return TRUE;
+ }
+ }
+
+ return FALSE;
+}
+
+KUint32 LMI_RealmdService_Join_Leave_Domain(
+ bool join,
+ const CMPIBroker* cb,
+ CMPIMethodMI* mi,
+ const CMPIContext* context,
+ const LMI_RealmdServiceRef* self,
+ const KString* Domain,
+ const KString* User,
+ const KString* Password,
+ const KStringA* OptionNames,
+ const KStringA* OptionValues,
+ CMPIStatus* status)
+{
+ const gchar *method_name = NULL;
+ const gchar *supported_credentials_property = NULL;
+ GError *g_error = NULL;
+ KUint32 result = KUINT32_INIT;
+ gint32 relevance = 0;
+ gchar **paths = NULL;
+ gchar *dbus_path, **pp;
+ CMPICount n_paths;
+ const gchar *cred_type = NULL;
+ const gchar *cred_owner = NULL;
+ GVariant *supported_creds = NULL;
+ GVariant *realm_props = NULL;
+ GVariant *kerberos_membership_props = NULL;
+ GVariant *credentials = NULL;
+ GVariant *options = NULL;
+
+ KUint32_Set(&result, LMI_REALMD_RESULT_SUCCESS);
+ CMSetStatus(status, CMPI_RC_OK);
+
+ /* Assure we can communicate with DBus */
+ if (!rdcp_dbus_initialize(&g_error)) {
+ handle_g_error(&g_error, _cb, status, CMPI_RC_ERR_FAILED, "rdcp_dbus_initialize failed");
+ KUint32_Set(&result, LMI_REALMD_RESULT_FAILED);
+ goto exit;
+ }
+
+ if (join) {
+ method_name = "Join";
+ supported_credentials_property = "SupportedJoinCredentials";
+ } else {
+ method_name = "Leave";
+ supported_credentials_property = "SupportedLeaveCredentials";
+ }
+
+ /* Call Discover to obtain list of DBus object paths for domain */
+ if (!build_g_variant_options_from_KStringA(OptionNames, OptionValues, &options, &g_error)) {
+ handle_g_error(&g_error, _cb, status, CMPI_RC_ERR_FAILED,
+ "failed to convert options to gvariant");
+ KUint32_Set(&result, LMI_REALMD_RESULT_FAILED);
+ goto exit;
+ }
+
+ if (!dbus_discover_call(system_bus, Domain->chars, options,
+ &relevance, &paths, &g_error)) {
+ handle_g_error(&g_error, _cb, status, CMPI_RC_ERR_FAILED, "dbus_discover_call() failed");
+ KUint32_Set(&result, LMI_REALMD_RESULT_FAILED);
+ goto exit;
+ }
+
+#ifdef RDCP_DEBUG
+ print_paths(paths, "%s: target=%s, paths:", __FUNCTION__, Domain->chars);
+#endif
+
+ for (pp = paths, dbus_path = *pp++, n_paths = 0; dbus_path; dbus_path = *pp++, n_paths++);
+
+ if (n_paths < 1) {
+ SetCMPIStatus(cb, status, CMPI_RC_ERR_FAILED, "Domain (%s) does not exist", Domain->chars);
+ KUint32_Set(&result, LMI_REALMD_RESULT_NO_SUCH_DOMAIN);
+ goto exit;
+ }
+
+ dbus_path = paths[0];
+
+ /* Lookup the realm properties so we can determine the supported DBus interfaces */
+ GET_DBUS_PROPERIES_OR_EXIT(realm_props, dbus_path,
+ REALM_DBUS_REALM_INTERFACE, status);
+ if (!SupportsDBusInterface(realm_props, REALM_DBUS_KERBEROS_MEMBERSHIP_INTERFACE)) {
+ SetCMPIStatus(cb, status, CMPI_RC_ERR_FAILED, "Domain (%s) does not support joining or leaving",
+ Domain->chars);
+ KUint32_Set(&result, LMI_REALMD_RESULT_DOMAIN_DOES_NOT_SUPPORT_JOINING);
+ goto exit;
+ }
+
+ GET_DBUS_PROPERIES_OR_EXIT(kerberos_membership_props, dbus_path,
+ REALM_DBUS_KERBEROS_MEMBERSHIP_INTERFACE, status);
+
+ if (!g_variant_lookup(kerberos_membership_props, supported_credentials_property, "@a(ss)",
+ &supported_creds)) {
+ SetCMPIStatus(cb, status, CMPI_RC_ERR_FAILED,
+ "Domain (%s) did not supply supported %s credentials",
+ Domain->chars, method_name);
+ KUint32_Set(&result, LMI_REALMD_RESULT_FAILED);
+ goto exit;
+ }
+
+ if (!User->exists || User->null) {
+ /* No User */
+ if (!Password->exists || Password->null) {
+ /* No User, No Password: automatic */
+ cred_type = "automatic";
+ if (!get_credential_supported_owner(supported_creds, cred_type, &cred_owner)) {
+ SetCMPIStatus(cb, status, CMPI_RC_ERR_FAILED,
+ "Domain (%s) does not support automatic %s credentials",
+ Domain->chars, method_name);
+ KUint32_Set(&result, LMI_REALMD_RESULT_DOMAIN_DOES_NOT_SUPPORT_PROVIDED_CREDENTIALS);
+ goto exit;
+ }
+
+ credentials = g_variant_new ("(ssv)", cred_type, cred_owner,
+ g_variant_new_string (""));
+
+ } else {
+ /* No User, Password: one time password using secret */
+ cred_type = "secret";
+ if (!get_credential_supported_owner(supported_creds, cred_type, &cred_owner)) {
+ SetCMPIStatus(cb, status, CMPI_RC_ERR_FAILED,
+ "Domain (%s) does not support secret %s credentials",
+ Domain->chars, method_name);
+ KUint32_Set(&result, LMI_REALMD_RESULT_DOMAIN_DOES_NOT_SUPPORT_PROVIDED_CREDENTIALS);
+ goto exit;
+ }
+ credentials = g_variant_new("(ssv)", cred_type, cred_owner,
+ g_variant_new_fixed_array (G_VARIANT_TYPE_BYTE,
+ Password->chars,
+ strlen(Password->chars), 1));
+ }
+ } else {
+ /* User */
+ if (!Password->exists || Password->null) {
+ /* User, No Password: invalid combination */
+ SetCMPIStatus(cb, status, CMPI_RC_ERR_INVALID_PARAMETER,
+ "Must provide a password when User is provided");
+ KUint32_Set(&result, LMI_REALMD_RESULT_FAILED);
+ goto exit;
+ } else {
+ /* User, Password: password auth */
+ cred_type = "password";
+ cred_owner = "administrator";
+ if (!is_credential_supported(supported_creds, cred_type, cred_owner)) {
+ SetCMPIStatus(cb, status, CMPI_RC_ERR_FAILED,
+ "Domain (%s) does not support password with administrator ownership credentials",
+ Domain->chars);
+ KUint32_Set(&result, LMI_REALMD_RESULT_DOMAIN_DOES_NOT_SUPPORT_PROVIDED_CREDENTIALS);
+ goto exit;
+ }
+ credentials = g_variant_new("(ssv)", cred_type, cred_owner,
+ g_variant_new("(ss)", User->chars, Password->chars));
+
+ }
+ }
+
+ if (join) {
+ if (!dbus_join_call(system_bus, dbus_path, credentials, options, &g_error)) {
+ handle_g_error(&g_error, cb, status, CMPI_RC_ERR_FAILED, "dbus_join_call() failed");
+ KUint32_Set(&result, LMI_REALMD_RESULT_FAILED);
+ goto exit;
+ }
+ } else {
+ if (!dbus_leave_call(system_bus, dbus_path, credentials, options, &g_error)) {
+ handle_g_error(&g_error, cb, status, CMPI_RC_ERR_FAILED, "dbus_leave_call() failed");
+ KUint32_Set(&result, LMI_REALMD_RESULT_FAILED);
+ goto exit;
+ }
+ }
+
+
+ exit:
+
+ G_VARIANT_FREE(supported_creds);
+ G_VARIANT_FREE(realm_props);
+ G_VARIANT_FREE(kerberos_membership_props);
+ G_VARIANT_FREE(credentials);
+ G_VARIANT_FREE(options);
+ g_strfreev(paths);
+
+ return result;
+}
+KEXTERN KUint32 LMI_RealmdService_JoinDomain(
+ const CMPIBroker* cb,
+ CMPIMethodMI* mi,
+ const CMPIContext* context,
+ const LMI_RealmdServiceRef* self,
+ const KString* Domain,
+ const KString* User,
+ const KString* Password,
+ const KStringA* OptionNames,
+ const KStringA* OptionValues,
+ CMPIStatus* status)
+{
+ return LMI_RealmdService_Join_Leave_Domain(true, cb, mi, context, self,
+ Domain, User, Password,
+ OptionNames, OptionValues,
+ status);
+}
+
+KEXTERN KUint32 LMI_RealmdService_LeaveDomain(
+ const CMPIBroker* cb,
+ CMPIMethodMI* mi,
+ const CMPIContext* context,
+ const LMI_RealmdServiceRef* self,
+ const KString* Domain,
+ const KString* User,
+ const KString* Password,
+ const KStringA* OptionNames,
+ const KStringA* OptionValues,
+ CMPIStatus* status)
+{
+ return LMI_RealmdService_Join_Leave_Domain(false, cb, mi, context, self,
+ Domain, User, Password,
+ OptionNames, OptionValues,
+ status);
+}
+
+KONKRET_REGISTRATION(
+ "root/cimv2",
+ "LMI_RealmdService",
+ "LMI_RealmdService",
+ "instance method");
diff --git a/src/realmd/LMI_ServiceAffectsRealmdRealmProvider.c b/src/realmd/LMI_ServiceAffectsRealmdRealmProvider.c
new file mode 100644
index 0000000..70d65f4
--- /dev/null
+++ b/src/realmd/LMI_ServiceAffectsRealmdRealmProvider.c
@@ -0,0 +1,252 @@
+#include <konkret/konkret.h>
+#include "LMI_ServiceAffectsRealmdRealm.h"
+#include "rdcp_util.h"
+#include "globals.h"
+#include "rdcp_error.h"
+#include "rdcp_dbus.h"
+#include "rdcp_util.h"
+#include "rdcp_realmdrealm.h"
+
+static const CMPIBroker* _cb;
+
+static void LMI_ServiceAffectsRealmdRealmInitialize()
+{
+}
+
+static CMPIStatus LMI_ServiceAffectsRealmdRealmCleanup(
+ CMPIInstanceMI* mi,
+ const CMPIContext* cc,
+ CMPIBoolean term)
+{
+ CMReturn(CMPI_RC_OK);
+}
+
+static CMPIStatus LMI_ServiceAffectsRealmdRealmEnumInstanceNames(
+ CMPIInstanceMI* mi,
+ const CMPIContext* cc,
+ const CMPIResult* cr,
+ const CMPIObjectPath* cop)
+{
+ return KDefaultEnumerateInstanceNames(
+ _cb, mi, cc, cr, cop);
+}
+
+static CMPIStatus LMI_ServiceAffectsRealmdRealmEnumInstances(
+ CMPIInstanceMI* mi,
+ const CMPIContext* cc,
+ const CMPIResult* cr,
+ const CMPIObjectPath* cop,
+ const char** properties)
+{
+ CMPIStatus status;
+ GError *g_error = NULL;
+ GVariant *provider_props = NULL;
+ GVariantIter *iter = NULL;
+ gchar *realm_obj_path;
+ LMI_RealmdServiceRef realmd_service_ref;
+ LMI_ServiceAffectsRealmdRealm service_affects;
+ const char *name_space = KNameSpace(cop);
+ const char *host_name = get_system_name();
+
+ CMSetStatus(&status, CMPI_RC_OK);
+
+ LMI_InitRealmdServiceKeys(LMI_RealmdServiceRef, &realmd_service_ref, name_space, host_name);
+
+ if (!rdcp_dbus_initialize(&g_error)) {
+ return handle_g_error(&g_error, _cb, &status, CMPI_RC_ERR_FAILED, "rdcp_dbus_initialize failed");
+ }
+
+ GET_DBUS_PROPERIES_OR_EXIT(provider_props, REALM_DBUS_SERVICE_PATH,
+ REALM_DBUS_PROVIDER_INTERFACE, &status);
+
+ g_variant_lookup(provider_props, "Realms", "ao", &iter);
+ while (g_variant_iter_next(iter, "&o", &realm_obj_path)) {
+ LMI_RealmdRealmRef realmd_realm_ref;
+
+ status = LMI_RealmdRealmRef_InitFromDBusPath(&realmd_realm_ref, _cb, name_space, realm_obj_path);
+ if (status.rc != CMPI_RC_OK) {
+ goto exit;
+ }
+
+ LMI_ServiceAffectsRealmdRealm_Init(&service_affects, _cb, name_space);
+ LMI_ServiceAffectsRealmdRealm_Set_AffectedElement(&service_affects, &realmd_realm_ref);
+ LMI_ServiceAffectsRealmdRealm_Set_AffectingElement(&service_affects, &realmd_service_ref);
+ LMI_ServiceAffectsRealmdRealm_Init_ElementEffects(&service_affects, 1);
+ LMI_ServiceAffectsRealmdRealm_Set_ElementEffects(&service_affects, 0,
+ LMI_ServiceAffectsRealmdRealm_ElementEffects_Manages);
+
+ KReturnInstance(cr, service_affects);
+ }
+
+ exit:
+ G_VARIANT_ITER_FREE(iter);
+ G_VARIANT_FREE(provider_props);
+
+ return status;
+}
+
+static CMPIStatus LMI_ServiceAffectsRealmdRealmGetInstance(
+ CMPIInstanceMI* mi,
+ const CMPIContext* cc,
+ const CMPIResult* cr,
+ const CMPIObjectPath* cop,
+ const char** properties)
+{
+ return KDefaultGetInstance(
+ _cb, mi, cc, cr, cop, properties);
+}
+
+static CMPIStatus LMI_ServiceAffectsRealmdRealmCreateInstance(
+ CMPIInstanceMI* mi,
+ const CMPIContext* cc,
+ const CMPIResult* cr,
+ const CMPIObjectPath* cop,
+ const CMPIInstance* ci)
+{
+ CMReturn(CMPI_RC_ERR_NOT_SUPPORTED);
+}
+
+static CMPIStatus LMI_ServiceAffectsRealmdRealmModifyInstance(
+ CMPIInstanceMI* mi,
+ const CMPIContext* cc,
+ const CMPIResult* cr,
+ const CMPIObjectPath* cop,
+ const CMPIInstance* ci,
+ const char**properties)
+{
+ CMReturn(CMPI_RC_ERR_NOT_SUPPORTED);
+}
+
+static CMPIStatus LMI_ServiceAffectsRealmdRealmDeleteInstance(
+ CMPIInstanceMI* mi,
+ const CMPIContext* cc,
+ const CMPIResult* cr,
+ const CMPIObjectPath* cop)
+{
+ CMReturn(CMPI_RC_ERR_NOT_SUPPORTED);
+}
+
+static CMPIStatus LMI_ServiceAffectsRealmdRealmExecQuery(
+ CMPIInstanceMI* mi,
+ const CMPIContext* cc,
+ const CMPIResult* cr,
+ const CMPIObjectPath* cop,
+ const char* lang,
+ const char* query)
+{
+ CMReturn(CMPI_RC_ERR_NOT_SUPPORTED);
+}
+
+static CMPIStatus LMI_ServiceAffectsRealmdRealmAssociationCleanup(
+ CMPIAssociationMI* mi,
+ const CMPIContext* cc,
+ CMPIBoolean term)
+{
+ CMReturn(CMPI_RC_OK);
+}
+
+static CMPIStatus LMI_ServiceAffectsRealmdRealmAssociators(
+ CMPIAssociationMI* mi,
+ const CMPIContext* cc,
+ const CMPIResult* cr,
+ const CMPIObjectPath* cop,
+ const char* assocClass,
+ const char* resultClass,
+ const char* role,
+ const char* resultRole,
+ const char** properties)
+{
+ return KDefaultAssociators(
+ _cb,
+ mi,
+ cc,
+ cr,
+ cop,
+ LMI_ServiceAffectsRealmdRealm_ClassName,
+ assocClass,
+ resultClass,
+ role,
+ resultRole,
+ properties);
+}
+
+static CMPIStatus LMI_ServiceAffectsRealmdRealmAssociatorNames(
+ CMPIAssociationMI* mi,
+ const CMPIContext* cc,
+ const CMPIResult* cr,
+ const CMPIObjectPath* cop,
+ const char* assocClass,
+ const char* resultClass,
+ const char* role,
+ const char* resultRole)
+{
+ return KDefaultAssociatorNames(
+ _cb,
+ mi,
+ cc,
+ cr,
+ cop,
+ LMI_ServiceAffectsRealmdRealm_ClassName,
+ assocClass,
+ resultClass,
+ role,
+ resultRole);
+}
+
+static CMPIStatus LMI_ServiceAffectsRealmdRealmReferences(
+ CMPIAssociationMI* mi,
+ const CMPIContext* cc,
+ const CMPIResult* cr,
+ const CMPIObjectPath* cop,
+ const char* assocClass,
+ const char* role,
+ const char** properties)
+{
+ return KDefaultReferences(
+ _cb,
+ mi,
+ cc,
+ cr,
+ cop,
+ LMI_ServiceAffectsRealmdRealm_ClassName,
+ assocClass,
+ role,
+ properties);
+}
+
+static CMPIStatus LMI_ServiceAffectsRealmdRealmReferenceNames(
+ CMPIAssociationMI* mi,
+ const CMPIContext* cc,
+ const CMPIResult* cr,
+ const CMPIObjectPath* cop,
+ const char* assocClass,
+ const char* role)
+{
+ return KDefaultReferenceNames(
+ _cb,
+ mi,
+ cc,
+ cr,
+ cop,
+ LMI_ServiceAffectsRealmdRealm_ClassName,
+ assocClass,
+ role);
+}
+
+CMInstanceMIStub(
+ LMI_ServiceAffectsRealmdRealm,
+ LMI_ServiceAffectsRealmdRealm,
+ _cb,
+ LMI_ServiceAffectsRealmdRealmInitialize())
+
+CMAssociationMIStub(
+ LMI_ServiceAffectsRealmdRealm,
+ LMI_ServiceAffectsRealmdRealm,
+ _cb,
+ LMI_ServiceAffectsRealmdRealmInitialize())
+
+KONKRET_REGISTRATION(
+ "root/cimv2",
+ "LMI_ServiceAffectsRealmdRealm",
+ "LMI_ServiceAffectsRealmdRealm",
+ "instance association");
diff --git a/src/realmd/README b/src/realmd/README
new file mode 100644
index 0000000..dec6866
--- /dev/null
+++ b/src/realmd/README
@@ -0,0 +1,221 @@
+Realmd CIM Provider
+
+Building
+========
+
+Prerequisites:
+--------------
+
+To build you'll need these packages installed:
+
+cmake
+openlmi-providers-devel
+konkretcmpi
+
+
+To install and run you'll need at a minimum:
+--------------------------------------------
+
+tog-pegasus
+openlmi-providers
+
+
+This project uses the same build mechanism as openlmi-provider which
+is based on cmake. It's also important specify the same cmake
+configuration parameters enforced by RPM.
+
+My short term solution is to use the following shell script. I add the
+CFLAGS override to turn on options useful for debugging during
+development, you may wish to omit that.
+
+<<<<<<<<<<
+#!/bin/sh
+
+export CFLAGS='-g -O0 -DRDCP_DEBUG'
+
+/usr/bin/cmake -DCMAKE_VERBOSE_MAKEFILE=ON -DCMAKE_INSTALL_PREFIX:PATH=/usr -DINCLUDE_INSTALL_DIR:PATH=/usr/include -DLIB_INSTALL_DIR:PATH=/usr/lib -DSYSCONF_INSTALL_DIR:PATH=/etc -DSHARE_INSTALL_PREFIX:PATH=/usr/share -DBUILD_SHARED_LIBS:BOOL=ON .
+
+if [ $? -eq 0 ]; then
+ make
+fi
+>>>>>>>>>>
+
+Installing:
+-----------
+
+% sudo make install
+
+This copies the mof file, the registration file and the loadable
+module to their destination. Then you must register your module with
+the Pegasus CIMOM. Note: pegasus MUST be running!
+
+% openlmi-mof-register register /usr/share/openlmi-providers/LMI_Realmd.mof /usr/share/openlmi-providers/LMI_Realmd.reg
+
+Development Tips:
+-----------------
+
+Understanding konkret code generation issues:
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+konkret is a tool that reads in a mof file and generates C code. For
+every XXX class it will generate a XXX.h and XXXProvider.c file. The
+code generation occurs due to CMake macros provided by the
+openlmi-providers-devel package. konkret needs to run any time you
+modify the mof file. It *always* generates a new XXX.h file because
+that's where definitions based on the contents of the mof file are
+located. If there is no XXXProvider.c file it will also generate
+it. This is a "stub" file in which you will fill in with your
+implementation. If XXXProvider.c exits it will not overwrite it,
+however it always overwrites the XXX.h file.
+
+Do not put anything into the XXX.h file you'll need to retain.
+
+After editing the mof file the make targets will cause konkret to run
+again. You'll get brand new XXX.h files. But your old XXXProvider.c
+files may no longer have the correct definitions (e.g. prototypes)
+found in the XXX.h file so you may need to hand edit by copying the
+function prototype from the XXX.h file into your XXXProvider.c file.
+
+If you've written definitions that logically belong in XXX.h but don't
+want them nuked the next time konkret runs my solution was to put them
+in someother .h file that's included by the XXXProvider.c file.
+
+Initializing class instances:
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+The way konkret works is to emit specialized inline functions to
+initialize each member of a class. If the class is subclassed you get
+different initializers depending on whether the property is in the
+parent class or the subclass. You cannot call a parent class property
+initializer in a subclass (yuck), you have to use the subclass
+initializer for the property inherited from the parent class. This
+creates a maintenance problem if the parent class changes, you have
+find every place parent class properties are inialized and make
+changes. To solve this problem I defined macros that initialize class
+properties. The macro takes a "klass" parameter and token pastes it to
+generate the class specific property manipulation function call. Using
+these macros means anytime a class changes due to a change in the mof
+file there is only one place where you need to change the code. These
+macros are a good example of what logically belongs in the XXX.h file
+but are separated out into a different .h file because konkret will
+nuke anything you've added to a XXX.h file.
+
+Modifications to the provider:
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+During development if the mof file changes you have to make Pegasus
+reload the mof. It's not sufficient to retart cimserver, you have to
+unregister the provider and register it again for Pegasus to see the
+mof changes. Thus you would use the openlmi-mof-register command above
+except pass the unregister option, followed by the above command, e.g.
+
+% openlmi-mof-register unregister /usr/share/openlmi-providers/LMI_Realmd.mof /usr/share/openlmi-providers/LMI_Realmd.reg
+% openlmi-mof-register register /usr/share/openlmi-providers/LMI_Realmd.mof /usr/share/openlmi-providers/LMI_Realmd.reg
+
+If all you've done during devopment is modify the provider (not it's
+mof definition) then all you need to do is:
+
+% sudo cimserver -s
+% sudo make install
+% sudo cimserver
+
+How do I run the Pegasus CIMOM so I can see debug statements?
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+% sudo cimserver daemon=false forceProviderProcesses=false
+
+How do I use GDB to debug my provider?
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+Create the following .gdbinit file where XXX is where you want to break.
+
+<<<<<<<<<<
+set breakpoint pending on
+b XXX
+r daemon=false forceProviderProcesses=false
+>>>>>>>>>>
+
+then run gdb like this:
+
+% sudo gdb cimserver
+
+How do I trace what Pegasus is doing?
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+% cimserver daemon=false forceProviderProcesses=false logLevel=TRACE traceLevel=5 traceFacility=File traceComponents=All
+
+The trace file is written to:
+
+/var/lib/Pegasus/cache/trace/cimserver.trc
+
+More information about cimserver tracing can be found in the
+"OpenPegasus Tracing User Guide" PDF. Google the title to get a
+current URL.
+
+FAQ
+===
+
+Q: What does the rdcp acronym stand for?
+
+A: Realmd CIM Provider. The "rd" is for Realmd, the "c" is for CIM and
+ the "p" is for Provider
+
+Q: What decisions influenced your DBus implementation strategy?
+
+A: There are essentially two supported DBus API's. A very high level
+ GDBus and a very low level libdbus.
+
+ GDBus requires you to utilize Gnome's GObject pseudo
+ object-orientated framework. If you're not familar with it it's a
+ fairly steep learning curve to become proficient. Also if you're
+ not proficient in GObject programming it's hard to comprehend code
+ which utilizes it thus putting normal C developers at a
+ disadvantage. However GDBus gives you a lot of nice support, one of
+ the nice features is everything is based on GVariants, a powerful
+ data structure.
+
+ On the other hand libdbus is very low level, there is very little
+ support for the necessary DBus operations. However it is used
+ extensively by other projects so it's not an aberration to use it,
+ it's pure C code making it easier to understand and it doesn't pull
+ in the whole GObject system. But it's a lot of work to use.
+
+ I took a compromise approach. I didn't have the time to become
+ proficient with GObject and I felt the GObject based code was
+ difficult for C programmers without GObject experience to read
+ and modify. However I recognized the value of expressing most
+ things in terms of GVariants, a hallmark of GDBus. So I wrote some
+ utility code that supports serializing GVariants into and out of
+ libdbus. This allowed me to use the powerful GVariant without
+ having to get involved with GObjects and GDBus. If we ever decide
+ to port the code to GDBus it shold be fairly straight forward
+ because we're already using GVariant's as a fundamental type. This
+ seemed to represent a reasonable compromise between libdus and
+ GDBus, it avoids the pseudo object-orientated framework of GObject
+ in favor of vanilla C code but retains the powerful use of
+ GVariants. I guess only time will tell if it was a smart choice or
+ not.
+
+ToDo
+====
+
+Implement locale in RealmdService.
+
+Utilize CMPI Logging instead of debug printf statements (currently
+controlled by the RDCP_DEBUG compile time flag).
+
+Generate indications when realms are added or removed from the Realms
+property of the LMI_RealmdService.
+
+Any blocking operations should not block the CIMOM.
+(e.g. communicating with DBus). I think the right way to do this is
+via the CMPI threading support, but this needs further
+investigation. Other openlmi developers would be a good resource for
+this issue.
+
+We call DBus to get object properties a lot. There is I believe
+support for DBus clients which caches object properties and listens on
+the properties change signal to refresh the properties cache. Access
+to the properties are then performed via the local properties cache
+rather than the via RPC. Obviously this is much more efficient, we
+should support it.
diff --git a/src/realmd/VERSION b/src/realmd/VERSION
new file mode 100644
index 0000000..8acdd82
--- /dev/null
+++ b/src/realmd/VERSION
@@ -0,0 +1 @@
+0.0.1
diff --git a/src/realmd/doc/class_diagram.svg b/src/realmd/doc/class_diagram.svg
new file mode 100644
index 0000000..1b962d3
--- /dev/null
+++ b/src/realmd/doc/class_diagram.svg
@@ -0,0 +1,273 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!-- Created with Inkscape (http://www.inkscape.org/) -->
+
+<svg
+ xmlns:dc="http://purl.org/dc/elements/1.1/"
+ xmlns:cc="http://creativecommons.org/ns#"
+ xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+ xmlns:svg="http://www.w3.org/2000/svg"
+ xmlns="http://www.w3.org/2000/svg"
+ xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+ xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+ width="744.09448819"
+ height="1052.3622047"
+ id="svg2"
+ version="1.1"
+ inkscape:version="0.48.4 r9939"
+ sodipodi:docname="class_diagram.svg">
+ <defs
+ id="defs4">
+ <marker
+ inkscape:stockid="Arrow2Mend"
+ orient="auto"
+ refY="0.0"
+ refX="0.0"
+ id="Arrow2Mend"
+ style="overflow:visible;">
+ <path
+ id="path3871"
+ style="fill-rule:evenodd;stroke-width:0.62500000;stroke-linejoin:round;"
+ d="M 8.7185878,4.0337352 L -2.2072895,0.016013256 L 8.7185884,-4.0017078 C 6.9730900,-1.6296469 6.9831476,1.6157441 8.7185878,4.0337352 z "
+ transform="scale(0.6) rotate(180) translate(0,0)" />
+ </marker>
+ <marker
+ inkscape:stockid="Arrow2Lend"
+ orient="auto"
+ refY="0.0"
+ refX="0.0"
+ id="Arrow2Lend"
+ style="overflow:visible;">
+ <path
+ id="path3865"
+ style="fill-rule:evenodd;stroke-width:0.62500000;stroke-linejoin:round;"
+ d="M 8.7185878,4.0337352 L -2.2072895,0.016013256 L 8.7185884,-4.0017078 C 6.9730900,-1.6296469 6.9831476,1.6157441 8.7185878,4.0337352 z "
+ transform="scale(1.1) rotate(180) translate(1,0)" />
+ </marker>
+ </defs>
+ <sodipodi:namedview
+ id="base"
+ pagecolor="#ffffff"
+ bordercolor="#666666"
+ borderopacity="1.0"
+ inkscape:pageopacity="0.0"
+ inkscape:pageshadow="2"
+ inkscape:zoom="2.8"
+ inkscape:cx="235.3264"
+ inkscape:cy="611.39239"
+ inkscape:document-units="px"
+ inkscape:current-layer="layer1"
+ showgrid="true"
+ inkscape:window-width="1920"
+ inkscape:window-height="1002"
+ inkscape:window-x="0"
+ inkscape:window-y="27"
+ inkscape:window-maximized="1"
+ inkscape:snap-grids="true"
+ inkscape:snap-bbox="true">
+ <inkscape:grid
+ type="xygrid"
+ id="grid3006"
+ empspacing="5"
+ visible="true"
+ enabled="true"
+ snapvisiblegridlinesonly="true" />
+ </sodipodi:namedview>
+ <metadata
+ id="metadata7">
+ <rdf:RDF>
+ <cc:Work
+ rdf:about="">
+ <dc:format>image/svg+xml</dc:format>
+ <dc:type
+ rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
+ </cc:Work>
+ </rdf:RDF>
+ </metadata>
+ <g
+ inkscape:label="Layer 1"
+ inkscape:groupmode="layer"
+ id="layer1">
+ <g
+ id="g3035">
+ <g
+ id="g3030">
+ <g
+ id="g3022"
+ transform="matrix(1,0,0,0.97395887,0,10.489247)">
+ <rect
+ y="402.36218"
+ x="350.39706"
+ height="25"
+ width="120"
+ id="rect3008"
+ style="fill:#ffffff;fill-opacity:1;stroke:#000000;stroke-width:1.41732287;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none" />
+ </g>
+ <path
+ style="fill:none;stroke:#000000;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+ d="m 350.39707,417.36218 120,0"
+ id="path3014"
+ inkscape:connector-curvature="0" />
+ </g>
+ <text
+ sodipodi:linespacing="125%"
+ id="text3010"
+ y="412.41025"
+ x="375.69394"
+ style="font-size:8px;font-style:normal;font-weight:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;font-family:Bitstream Vera Sans"
+ xml:space="preserve"><tspan
+ y="412.41025"
+ x="375.69394"
+ id="tspan3012"
+ sodipodi:role="line">ComputerSystem</tspan></text>
+ </g>
+ <g
+ id="g3085"
+ transform="translate(-0.3488455,0)">
+ <g
+ transform="translate(-221,2e-6)"
+ id="g3030-5">
+ <g
+ id="g3022-6"
+ transform="matrix(1,0,0,0.97395887,0,10.489247)">
+ <rect
+ y="402.36218"
+ x="350.39706"
+ height="25"
+ width="120"
+ id="rect3008-5"
+ style="fill:#ffffff;fill-opacity:1;stroke:#000000;stroke-width:1.41732287;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none" />
+ </g>
+ <path
+ style="fill:none;stroke:#000000;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+ d="m 350.39707,417.36218 120,0"
+ id="path3014-2"
+ inkscape:connector-curvature="0" />
+ </g>
+ <text
+ sodipodi:linespacing="125%"
+ id="text3010-2"
+ y="412.41025"
+ x="189.21738"
+ style="font-size:8px;font-style:normal;font-weight:normal;text-align:center;line-height:125%;letter-spacing:0px;word-spacing:0px;text-anchor:middle;fill:#000000;fill-opacity:1;stroke:none;font-family:Bitstream Vera Sans"
+ xml:space="preserve"><tspan
+ y="412.41025"
+ x="189.21738"
+ id="tspan3012-9"
+ sodipodi:role="line">RealmdService</tspan></text>
+ </g>
+ <path
+ style="fill:#000000;fill-opacity:1;stroke:#ff0000;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+ d="m 350,417.36218 -100,0"
+ id="path3093"
+ inkscape:connector-type="polyline"
+ inkscape:connector-curvature="0" />
+ <text
+ xml:space="preserve"
+ style="font-size:8px;font-style:normal;font-weight:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;font-family:Bitstream Vera Sans"
+ x="270.92188"
+ y="412.43054"
+ id="text3863"
+ sodipodi:linespacing="125%"><tspan
+ sodipodi:role="line"
+ id="tspan3865"
+ x="270.92188"
+ y="412.43054">HostedService</tspan></text>
+ <g
+ id="g3018"
+ transform="translate(-0.3488455,69.844081)">
+ <g
+ transform="translate(-221,2e-6)"
+ id="g3030-5-6">
+ <g
+ id="g3022-6-4"
+ transform="matrix(1,0,0,0.97395887,0,10.489247)">
+ <rect
+ y="402.36218"
+ x="350.39706"
+ height="25"
+ width="120"
+ id="rect3008-5-7"
+ style="fill:#ffffff;fill-opacity:1;stroke:#000000;stroke-width:1.41732287;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none" />
+ </g>
+ <path
+ style="fill:none;stroke:#000000;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+ d="m 350.39707,417.36218 120,0"
+ id="path3014-2-0"
+ inkscape:connector-curvature="0" />
+ </g>
+ <text
+ sodipodi:linespacing="125%"
+ id="text3010-2-2"
+ y="412.41025"
+ x="189.21738"
+ style="font-size:8px;font-style:normal;font-weight:normal;text-align:center;line-height:125%;letter-spacing:0px;word-spacing:0px;text-anchor:middle;fill:#000000;fill-opacity:1;stroke:none;font-family:Bitstream Vera Sans"
+ xml:space="preserve"><tspan
+ y="412.41025"
+ x="189.21738"
+ id="tspan3012-9-8"
+ sodipodi:role="line">RealmdRealm</tspan></text>
+ </g>
+ <g
+ id="g3062"
+ transform="translate(-0.3488455,139.68809)">
+ <g
+ transform="translate(-221,2e-6)"
+ id="g3030-5-6-3">
+ <g
+ id="g3022-6-4-4"
+ transform="matrix(1,0,0,0.97395887,0,10.489247)">
+ <rect
+ y="402.36218"
+ x="350.39706"
+ height="25"
+ width="120"
+ id="rect3008-5-7-3"
+ style="fill:#ffffff;fill-opacity:1;stroke:#000000;stroke-width:1.41732287;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none" />
+ </g>
+ <path
+ style="fill:none;stroke:#000000;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+ d="m 350.39707,417.36218 120,0"
+ id="path3014-2-0-8"
+ inkscape:connector-curvature="0" />
+ </g>
+ <text
+ sodipodi:linespacing="125%"
+ id="text3010-2-2-5"
+ y="412.41025"
+ x="189.21738"
+ style="font-size:8px;font-style:normal;font-weight:normal;text-align:center;line-height:125%;letter-spacing:0px;word-spacing:0px;text-anchor:middle;fill:#000000;fill-opacity:1;stroke:none;font-family:Bitstream Vera Sans"
+ xml:space="preserve"><tspan
+ y="412.41025"
+ x="189.21738"
+ id="tspan3012-9-8-4"
+ sodipodi:role="line">RealmdKerberosRealm</tspan></text>
+ </g>
+ <text
+ xml:space="preserve"
+ style="font-size:8px;font-style:normal;font-weight:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;font-family:Bitstream Vera Sans"
+ x="199.47266"
+ y="447.2489"
+ id="text3961"
+ sodipodi:linespacing="125%"><tspan
+ sodipodi:role="line"
+ id="tspan3963"
+ x="199.47266"
+ y="447.2489">ServiceAffectsElement</tspan></text>
+ <path
+ style="fill:none;stroke:#ff0000;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+ d="m 189.04822,470.39873 0,-43.67629"
+ id="path4498"
+ inkscape:connector-type="polyline"
+ inkscape:connector-curvature="3"
+ inkscape:connection-start="#g3018"
+ inkscape:connection-start-point="d4"
+ inkscape:connection-end="#g3085"
+ inkscape:connection-end-point="d4" />
+ <path
+ style="fill:none;stroke:#0000ff;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;marker-end:url(#Arrow2Mend)"
+ d="m 189.04822,542.42392 0,-43.67622"
+ id="path4500"
+ inkscape:connector-type="polyline"
+ inkscape:connector-curvature="3" />
+ </g>
+</svg>
diff --git a/src/realmd/doc/examples/realmd-cim b/src/realmd/doc/examples/realmd-cim
new file mode 100644
index 0000000..90ae9d5
--- /dev/null
+++ b/src/realmd/doc/examples/realmd-cim
@@ -0,0 +1,245 @@
+#!/usr/bin/python
+
+import sys
+import os
+import optparse
+import urlparse
+import pywbem
+
+#----------------------------------------------------------------------
+
+def do_list(conn, options, args):
+ realms = conn.EnumerateInstances('LMI_RealmdKerberosRealm')
+
+ print "%d realms" % (len(realms))
+
+ for realm in realms:
+ if options.verbose > 1:
+ # Very verbose, dump all properties
+ property_names = sorted(realm.keys())
+ for name in property_names:
+ value = realm[name]
+ print " %s: %s" % (name, value)
+
+ print realm['RealmName']
+ print " type: kerberos"
+ print " realm-name: %s" % realm['RealmName']
+ print " domain-name: %s" % realm['DomainName']
+
+ is_configured = True
+ configured = realm['Configured']
+ if not configured:
+ configured = "no"
+ is_configured = False
+ elif configured == "KerberosMembership":
+ configured = "kerberos-member"
+
+ print " configured: %s" % configured
+
+ for detail in zip(realm['DetailNames'], realm['DetailValues']):
+ print " %s: %s" % (detail[0], detail[1])
+
+ if is_configured:
+ print " login-formats: %s" % ", ".join(realm['LoginFormats'])
+ print " login-policy: %s" % realm['LoginPolicy']
+ print " permitted-logins: %s" % ", ".join(realm['PermittedLogins'])
+
+ print
+
+def do_join(conn, options, args):
+ # Validate arguments
+ if len(args) != 3:
+ raise ValueError("You must supply exacly 3 arguments (user, password, domain)")
+
+ user, password, domain = args
+
+ if (options.verbose):
+ print "Joining domain: %s" % domain
+
+ try:
+ realmd_service_instance_name = conn.EnumerateInstanceNames('LMI_RealmdService')[0]
+ except Exception, e:
+ raise ValueError("could not obtain realmd service")
+
+ try:
+ retval, outparams = conn.InvokeMethod("JoinDomain", realmd_service_instance_name,
+ Domain=domain,
+ User=user,
+ Password=password)
+ except Exception, e:
+ raise ValueError("Join failed (%s)" % (e))
+
+
+
+def do_leave(conn, options, args):
+ # Validate arguments
+ if len(args) != 3:
+ raise ValueError("You must supply exacly 3 arguments (user, password, domain)")
+
+ user, password, domain = args
+
+ if (options.verbose):
+ print "Leave domain: %s" % domain
+
+ try:
+ realmd_service_instance_name = conn.EnumerateInstanceNames('LMI_RealmdService')[0]
+ except Exception, e:
+ raise ValueError("could not obtain realmd service")
+
+ try:
+ retval, outparams = conn.InvokeMethod("LeaveDomain", realmd_service_instance_name,
+ Domain=domain,
+ User=user,
+ Password=password)
+ except Exception, e:
+ raise ValueError("Leave failed (%s)" % (e))
+
+
+
+def do_discover(conn, options, args):
+ # Validate arguments
+ if len(args) != 1:
+ raise ValueError("You must supply exactly 1 domain.")
+
+ domain = args[0]
+
+ if (options.verbose):
+ print "Discovering domain: %s" % domain
+
+ try:
+ realmd_service_instance_name = conn.EnumerateInstanceNames('LMI_RealmdService')[0]
+ except Exception, e:
+ raise ValueError("could not obtain realmd service")
+
+ try:
+ retval, outparams = conn.InvokeMethod("Discover", realmd_service_instance_name,
+ Target=domain)
+ except Exception, e:
+ raise ValueError("Join failed (%s)" % (e))
+
+ realm_refs = outparams['DiscoveredRealms']
+
+ print "%d Discovered" % len(realm_refs)
+ for realm_ref in realm_refs:
+ #print realm_ref
+ realm = conn.GetInstance(realm_ref)
+ print realm['RealmName']
+ print " Name: %s" % realm['RealmName']
+ print " Configured: %s" % realm['Configured']
+ print " Supported Interfaces: %s" % ", ".join(realm['SupportedInterfaces'])
+ for detail in zip(realm['DetailNames'], realm['DetailValues']):
+ print " %s: %s" % (detail[0], detail[1])
+ print " login-formats: %s" % ", ".join(realm['LoginFormats'])
+ print " login-policy: %s" % realm['LoginPolicy']
+ print " permitted-logins: %s" % ", ".join(realm['PermittedLogins'])
+
+
+
+#----------------------------------------------------------------------
+
+def main():
+
+ actions = {'list': do_list,
+ 'discover': do_discover,
+ 'join': do_join,
+ 'leave': do_leave,
+ }
+
+ usage ='''
+ %%prog [options] <action> <arg> ...
+
+ %%prog [options] list
+ %%prog [options] discover domain
+ %%prog [options] join user password domain
+ %%prog [options] leave user password domain
+
+ Available Actions: %(actions)s
+ ''' % {'actions': ", ".join(sorted(actions.keys()))}
+
+ # Set-up defaults
+
+ default_cimom_port = 5989
+ default_url = os.environ.get("LMI_CIMOM_URL", "https://localhost:5989")
+
+ parsed_default_url = urlparse.urlparse(default_url)
+
+ if parsed_default_url.port:
+ default_port = parsed_default_url.port or default_cimom_port
+
+ default_username = os.environ.get("LMI_CIMOM_USERNAME", "root")
+ default_password = os.environ.get("LMI_CIMOM_PASSWORD", "")
+
+ # Set-up arg parser
+ parser = optparse.OptionParser(usage=usage)
+
+ parser.add_option('-c', '--url', dest='url', default=default_url,
+ help='CIMOM URL or hostname to connect to')
+
+ parser.add_option('-u', '--username', dest='username', default=default_username,
+ help='Username for CIMOM authentication')
+
+ parser.add_option('-p', '--password', dest='password', default=default_password,
+ help='Password for CIMOM authentication')
+
+ parser.add_option('-v', '--verbose', dest='verbose', default=0,
+ action='count',
+ help='Turn on verbose output, increases verbosity level by one each time specified')
+
+ options, args = parser.parse_args()
+
+ # Validate arguments
+
+ try:
+ action = args.pop(0)
+ except IndexError:
+ print >>sys.stderr, "You must supply an action to execute"
+ parser.print_help()
+ return 1
+
+ try:
+ action_func = actions[action]
+ except KeyError:
+ print >>sys.stderr, "Unknown action (%s)" % (action)
+ parser.print_help()
+ return 1
+
+ # Get CIMOM URL
+
+ parsed_url = urlparse.urlparse(options.url)
+
+ if not parsed_url.netloc:
+ # Handle case where URL was bare hostname
+ parsed_url = urlparse.urlparse('//' + options.url)
+
+ scheme = 'https'
+ hostname = parsed_url.hostname
+ port = parsed_url.port or default_port
+
+ url = urlparse.urlunparse((scheme, "%s:%d" % (hostname, port), '', None, None, None))
+
+ # Connect to CIMOM
+
+ if (options.verbose):
+ print "Connecting to: %s" % url
+
+ try:
+ conn = pywbem.WBEMConnection(url, (options.username, options.password))
+ except Exception, e:
+ print >>sys.stderr, "Unable to connect to %s (%s)" % (options.url, e)
+ return 1
+
+
+ # Execute action
+
+ try:
+ action_func(conn, options, args)
+ except Exception, e:
+ print >>sys.stderr, "%s failed: %s" % (action, e)
+ return 1
+
+ return 0
+
+#----------------------------------------------------------------------
+
+if __name__ == "__main__":
+ sys.exit(main())
diff --git a/src/realmd/rdcp_dbus.c b/src/realmd/rdcp_dbus.c
new file mode 100644
index 0000000..b05e5c9
--- /dev/null
+++ b/src/realmd/rdcp_dbus.c
@@ -0,0 +1,2050 @@
+#include <konkret/konkret.h>
+
+#include "rdcp_error.h"
+#include "realm-dbus-constants.h"
+
+#include "rdcp_dbus.h"
+#include "rdcp_util.h"
+
+/*----------------------------------------------------------------------------*/
+
+DBusConnection* system_bus = NULL;
+
+/*----------------------------------------------------------------------------*/
+#ifdef TRACE_VARIANT
+static const char *
+dbus_type_to_string(int type);
+#endif
+
+static GError *
+dbus_error_to_gerror(DBusError *dbus_error);
+
+static const char*
+dbus_msg_type_to_string (int message_type);
+
+static void
+dbus_message_print_indent(GString *string, int depth);
+
+static void
+dbus_message_print_hex(GString *string, unsigned char *bytes, unsigned int len, int depth);
+
+static void
+dbus_message_print_byte_array(GString *string, DBusMessageIter *iter, int depth);
+
+static void
+dbus_message_print_iter(GString *string, DBusMessageIter *iter, int depth);
+
+static GString *
+dbus_message_print_string(DBusMessage *message, GString *string, dbus_bool_t show_header);
+
+static gchar *
+dbus_message_print(DBusMessage *message, GString *string, dbus_bool_t show_header);
+
+static gboolean
+append_g_variant_to_dbus_msg_iter(DBusMessageIter *iter, GVariant *value, GError **g_error);
+
+static gboolean
+append_g_variant_to_dbus_message(DBusMessage *message, GVariant *g_variant, GError **g_error);
+
+static gboolean
+dbus_method_append_args_tuple(DBusMessage *message, GVariant *args, GError **g_error);
+
+static gboolean
+marshal_dbus_string_variant(DBusMessageIter *iter, const char *value, GError **g_error);
+
+static gboolean
+marshal_dbus_dict_string_entry(DBusMessageIter *array, const char *name, const char *value, GError **g_error);
+
+static gboolean
+dbus_iter_to_variant(DBusMessageIter *iter, GVariant **g_variant_return, GError **g_error);
+
+static gboolean
+dbus_message_to_g_variant(DBusMessage *msg, GVariant **g_variant_return, GError **g_error);
+
+static gboolean
+dbus_method_reply_to_g_variant_tuple(DBusMessage *msg, GVariant **g_variant_return, GError **g_error);
+
+gboolean
+get_dbus_string_property(DBusConnection *bus, const char *object_path,
+ const char* interface, const char *property,
+ char **value_return, GError **g_error);
+gboolean
+get_dbus_properties(DBusConnection *bus, const char *object_path,
+ const char* interface, GVariant **properties_return,
+ GError **g_error);
+
+static gboolean
+dbus_discover_marshal(const char* target, GVariant *options,
+ DBusMessage **msg_return, GError **g_error);
+
+static gboolean
+dbus_discover_unmarshal(DBusMessage *reply, gint32 *relevance_return, gchar ***paths_return, GError **g_error);
+
+/*----------------------------------------------------------------------------*/
+
+#define RETURN_DBUS_ERROR(gerror, dbus_error) \
+{ \
+ if (gerror) { \
+ *gerror = dbus_error_to_gerror(&dbus_error); \
+ } \
+ dbus_error_free(&dbus_error); \
+ return FALSE; \
+}
+
+GError *
+dbus_error_to_gerror(DBusError *dbus_error)
+{
+ GError *gerror = NULL;
+
+ if (dbus_error == NULL) {
+ g_set_error(&gerror, RDCP_ERROR, RDCP_ERROR_DBUS,
+ "dbus error not provided");
+ } else {
+ g_set_error(&gerror, RDCP_ERROR, RDCP_ERROR_DBUS,
+ "dbus error (%s): %s", dbus_error->name, dbus_error->message);
+ }
+
+ return gerror;
+}
+
+/*----------------------------------------------------------------------------*/
+
+/*
+ * The code to print a DBus message is based upon DBUS code in
+ * dbus/tools/dbus-print-message.c which is GPL.
+ */
+
+static const char*
+dbus_msg_type_to_string (int message_type)
+{
+ switch (message_type)
+ {
+ case DBUS_MESSAGE_TYPE_SIGNAL:
+ return "signal";
+ case DBUS_MESSAGE_TYPE_METHOD_CALL:
+ return "method call";
+ case DBUS_MESSAGE_TYPE_METHOD_RETURN:
+ return "method return";
+ case DBUS_MESSAGE_TYPE_ERROR:
+ return "error";
+ default:
+ return "(unknown message type)";
+ }
+}
+
+#define INDENT_STRING " "
+#define INDENT_WIDTH sizeof(INDENT_STRING)
+#define PAGE_WIDTH 80
+
+static void
+dbus_message_print_indent(GString *string, int depth)
+{
+ while (depth-- > 0)
+ g_string_append(string, INDENT_STRING);
+}
+
+static void
+dbus_message_print_hex(GString *string, unsigned char *bytes, unsigned int len, int depth)
+{
+ unsigned int i, columns;
+
+ g_string_append_printf(string, "array of bytes [\n");
+
+ dbus_message_print_indent (string, depth + 1);
+
+ /* Each byte takes 3 cells (two hexits, and a space), except the last one. */
+ columns = (PAGE_WIDTH - ((depth + 1) * INDENT_WIDTH)) / 3;
+
+ if (columns < 8)
+ columns = 8;
+
+ i = 0;
+
+ while (i < len) {
+ g_string_append_printf(string, "%02x", bytes[i]);
+ i++;
+
+ if (i != len) {
+ if (i % columns == 0) {
+ g_string_append(string, "\n");
+ dbus_message_print_indent(string, depth + 1);
+ } else {
+ g_string_append(string, " ");
+ }
+ }
+ }
+
+ g_string_append(string, "\n");
+ dbus_message_print_indent(string, depth);
+ g_string_append(string, "]\n");
+}
+
+#define DEFAULT_SIZE 100
+
+static void
+dbus_message_print_byte_array(GString *string, DBusMessageIter *iter, int depth)
+{
+ unsigned char *bytes = malloc (DEFAULT_SIZE + 1);
+ unsigned int len = 0;
+ unsigned int max = DEFAULT_SIZE;
+ dbus_bool_t all_ascii = TRUE;
+ int current_type;
+
+ while ((current_type = dbus_message_iter_get_arg_type (iter)) != DBUS_TYPE_INVALID) {
+ unsigned char val;
+
+ dbus_message_iter_get_basic (iter, &val);
+ bytes[len] = val;
+ len++;
+
+ if (val < 32 || val > 126)
+ all_ascii = FALSE;
+
+ if (len == max) {
+ max *= 2;
+ bytes = realloc(bytes, max + 1);
+ }
+
+ dbus_message_iter_next (iter);
+ }
+
+ if (all_ascii) {
+ bytes[len] = '\0';
+ g_string_append_printf(string, "array of bytes \"%s\"\n", bytes);
+ } else {
+ dbus_message_print_hex(string, bytes, len, depth);
+ }
+
+ free (bytes);
+}
+
+static void
+dbus_message_print_iter(GString *string, DBusMessageIter *iter, int depth)
+{
+ int type;
+
+ while ((type = dbus_message_iter_get_arg_type (iter)) != DBUS_TYPE_INVALID) {
+
+ dbus_message_print_indent(string, depth);
+
+ switch (type) {
+ case DBUS_TYPE_BOOLEAN: {
+ dbus_bool_t val;
+ dbus_message_iter_get_basic (iter, &val);
+ g_string_append_printf(string, "boolean %s\n", val ? "true" : "false");
+ } break;
+
+ case DBUS_TYPE_BYTE: {
+ unsigned char val;
+ dbus_message_iter_get_basic (iter, &val);
+ g_string_append_printf(string, "byte %d\n", val);
+ } break;
+
+ case DBUS_TYPE_INT16: {
+ dbus_int16_t val;
+ dbus_message_iter_get_basic (iter, &val);
+ g_string_append_printf(string, "int16 %" G_GINT16_FORMAT "\n", val);
+ } break;
+
+ case DBUS_TYPE_UINT16: {
+ dbus_uint16_t val;
+ dbus_message_iter_get_basic (iter, &val);
+ g_string_append_printf(string, "uint16 %" G_GUINT16_FORMAT "\n", val);
+ } break;
+
+ case DBUS_TYPE_INT32: {
+ dbus_int32_t val;
+ dbus_message_iter_get_basic (iter, &val);
+ g_string_append_printf(string, "int32 %" G_GINT32_FORMAT "\n", val);
+ } break;
+
+ case DBUS_TYPE_UINT32: {
+ dbus_uint32_t val;
+ dbus_message_iter_get_basic (iter, &val);
+ g_string_append_printf(string, "uint32 %" G_GUINT32_FORMAT "\n", val);
+ } break;
+
+ case DBUS_TYPE_INT64: {
+ dbus_int64_t val;
+ dbus_message_iter_get_basic (iter, &val);
+ g_string_append_printf(string, "int64 %" G_GINT64_FORMAT "\n", val);
+ } break;
+
+ case DBUS_TYPE_UINT64: {
+ dbus_uint64_t val;
+ dbus_message_iter_get_basic (iter, &val);
+ g_string_append_printf(string, "uint64 %" G_GUINT64_FORMAT "\n", val);
+ } break;
+
+ case DBUS_TYPE_DOUBLE: {
+ double val;
+ dbus_message_iter_get_basic (iter, &val);
+ g_string_append_printf(string, "double %g\n", val);
+ } break;
+
+ case DBUS_TYPE_STRING: {
+ char *val;
+ dbus_message_iter_get_basic (iter, &val);
+ g_string_append_printf(string, "string \"%s\"\n", val);
+ } break;
+
+ case DBUS_TYPE_OBJECT_PATH: {
+ char *val;
+ dbus_message_iter_get_basic (iter, &val);
+ g_string_append_printf(string, "object path \"%s\"\n", val);
+ } break;
+
+ case DBUS_TYPE_SIGNATURE: {
+ char *val;
+ dbus_message_iter_get_basic (iter, &val);
+ g_string_append_printf(string, "signature \"%s\"\n", val);
+ } break;
+
+ case DBUS_TYPE_UNIX_FD: {
+ dbus_uint32_t val;
+ dbus_message_iter_get_basic (iter, &val);
+ g_string_append_printf(string, "Unix FD %" G_GUINT32_FORMAT "\n", val);
+ } break;
+
+ case DBUS_TYPE_ARRAY: {
+ int current_type;
+ DBusMessageIter subiter;
+
+ dbus_message_iter_recurse (iter, &subiter);
+
+ current_type = dbus_message_iter_get_arg_type (&subiter);
+
+ if (current_type == DBUS_TYPE_BYTE) {
+ dbus_message_print_byte_array(string, &subiter, depth);
+ break;
+ }
+
+ g_string_append(string, "array [\n");
+ while (current_type != DBUS_TYPE_INVALID) {
+ dbus_message_print_iter(string, &subiter, depth+1);
+
+ dbus_message_iter_next (&subiter);
+ current_type = dbus_message_iter_get_arg_type (&subiter);
+
+ if (current_type != DBUS_TYPE_INVALID)
+ g_string_append(string, ",");
+ }
+ dbus_message_print_indent(string, depth);
+ g_string_append(string, "]\n");
+ } break;
+
+ case DBUS_TYPE_VARIANT: {
+ DBusMessageIter subiter;
+
+ dbus_message_iter_recurse (iter, &subiter);
+
+ g_string_append(string, "variant ");
+ dbus_message_print_iter(string, &subiter, depth+1);
+ } break;
+
+ case DBUS_TYPE_STRUCT: {
+ int current_type;
+ DBusMessageIter subiter;
+
+ dbus_message_iter_recurse (iter, &subiter);
+
+ g_string_append(string, "struct {\n");
+ while ((current_type = dbus_message_iter_get_arg_type (&subiter)) != DBUS_TYPE_INVALID) {
+ dbus_message_print_iter(string, &subiter, depth+1);
+ dbus_message_iter_next (&subiter);
+ if (dbus_message_iter_get_arg_type (&subiter) != DBUS_TYPE_INVALID)
+ g_string_append(string, ",");
+ }
+ dbus_message_print_indent(string, depth);
+ g_string_append(string, "}\n");
+ } break;
+
+ case DBUS_TYPE_DICT_ENTRY: {
+ DBusMessageIter subiter;
+
+ dbus_message_iter_recurse (iter, &subiter);
+
+ g_string_append(string, "dict entry(\n");
+ dbus_message_print_iter(string, &subiter, depth+1);
+ dbus_message_iter_next (&subiter);
+ dbus_message_print_iter(string, &subiter, depth+1);
+ dbus_message_print_indent(string, depth);
+ g_string_append(string, ")\n");
+ } break;
+
+ default:
+ g_string_append_printf(string, " (unknown arg type '%c')\n", type);
+ break;
+ }
+ dbus_message_iter_next(iter);
+ }
+}
+
+/**
+ * dbus_message_print_string:
+ * @message The DBusMessage to format into a string.
+ * @string If non-NULL appends to this GString.
+ * @show_header If #TRUE the message header will be included.
+ *
+ * Formats a DBusMessage into a string.
+ *
+ * Returns: A GString which must be freed with g_string_free()
+ */
+static GString *
+dbus_message_print_string(DBusMessage *message, GString *string, dbus_bool_t show_header)
+{
+ DBusMessageIter iter;
+ const char *sender;
+ const char *destination;
+ int message_type;
+
+ g_return_val_if_fail (message != NULL, NULL);
+
+ if (string == NULL) {
+ string = g_string_new(NULL);
+ }
+
+ message_type = dbus_message_get_type (message);
+ sender = dbus_message_get_sender (message);
+ destination = dbus_message_get_destination (message);
+
+ if (show_header) {
+ g_string_append_printf(string, "%s sender=%s -> dest=%s",
+ dbus_msg_type_to_string (message_type),
+ sender ? sender : "(null sender)",
+ destination ? destination : "(null destination)");
+
+ switch (message_type) {
+ case DBUS_MESSAGE_TYPE_METHOD_CALL:
+ case DBUS_MESSAGE_TYPE_SIGNAL:
+ g_string_append_printf(string, " serial=%u path=%s; interface=%s; member=%s\n",
+ dbus_message_get_serial (message),
+ dbus_message_get_path (message),
+ dbus_message_get_interface (message),
+ dbus_message_get_member (message));
+ break;
+
+ case DBUS_MESSAGE_TYPE_METHOD_RETURN:
+ g_string_append_printf(string, " reply_serial=%u\n",
+ dbus_message_get_reply_serial (message));
+ break;
+
+ case DBUS_MESSAGE_TYPE_ERROR:
+ g_string_append_printf(string, " error_name=%s reply_serial=%u\n",
+ dbus_message_get_error_name (message),
+ dbus_message_get_reply_serial (message));
+ break;
+
+ default:
+ g_string_append(string, "\n");
+ break;
+ }
+ }
+
+ dbus_message_iter_init(message, &iter);
+ dbus_message_print_iter(string, &iter, 1);
+
+ return string;
+}
+
+/**
+ * dbus_message_print_string:
+ * @message The DBusMessage to format into a string.
+ * @string If non-NULL appends to this GString.
+ * @show_header If #TRUE the message header will be included.
+ *
+ * Formats a DBusMessage into a string.
+ *
+ * Returns: A simple malloc'ed string which must be freed with g_free().
+ */
+static gchar *
+dbus_message_print(DBusMessage *message, GString *string, dbus_bool_t show_header)
+{
+ g_return_val_if_fail (message != NULL, NULL);
+
+ return g_string_free(dbus_message_print_string(message, NULL, show_header), FALSE);
+}
+
+/*----------------------------------------------------------------------------*/
+
+/**
+ * append_g_variant_to_dbus_msg_iter:
+ * @g_error initialized to error info when FALSE is returned.
+ *
+ * Helper routine for append_g_variant_to_dbus_message().
+ * Performs the recusive descent into the GVariant appending
+ * values to the DBusMessage as it goes.
+ *
+ * Returns: return TRUE if successful, FALSE if error with @g_error initialized.
+ */
+static gboolean
+append_g_variant_to_dbus_msg_iter(DBusMessageIter *iter, GVariant *value, GError **g_error)
+{
+ GVariantClass class;
+
+ g_return_val_if_fail (iter != NULL, FALSE);
+ g_return_val_if_fail (value != NULL, FALSE);
+ g_return_val_if_fail (g_error == NULL || *g_error == NULL, FALSE);
+
+ class = g_variant_classify(value);
+
+ switch (class) {
+ case G_VARIANT_CLASS_BOOLEAN: {
+ dbus_bool_t v = g_variant_get_boolean(value);
+ dbus_message_iter_append_basic(iter, DBUS_TYPE_BOOLEAN, &v);
+ } break;
+ case G_VARIANT_CLASS_BYTE: {
+ guint8 v = g_variant_get_byte(value);
+ dbus_message_iter_append_basic(iter, DBUS_TYPE_BYTE, &v);
+ } break;
+ case G_VARIANT_CLASS_INT16: {
+ gint16 v = g_variant_get_int16 (value);
+ dbus_message_iter_append_basic(iter, DBUS_TYPE_INT16, &v);
+ } break;
+ case G_VARIANT_CLASS_UINT16: {
+ guint16 v = g_variant_get_uint16(value);
+ dbus_message_iter_append_basic(iter, DBUS_TYPE_UINT16, &v);
+ } break;
+ case G_VARIANT_CLASS_INT32: {
+ gint32 v = g_variant_get_int32(value);
+ dbus_message_iter_append_basic(iter, DBUS_TYPE_INT32, &v);
+ } break;
+ case G_VARIANT_CLASS_UINT32: {
+ guint32 v = g_variant_get_uint32(value);
+ dbus_message_iter_append_basic(iter, DBUS_TYPE_UINT32, &v);
+ } break;
+ case G_VARIANT_CLASS_INT64: {
+ gint64 v = g_variant_get_int64(value);
+ dbus_message_iter_append_basic(iter, DBUS_TYPE_INT64, &v);
+ } break;
+ case G_VARIANT_CLASS_UINT64: {
+ guint64 v = g_variant_get_uint64(value);
+ dbus_message_iter_append_basic(iter, DBUS_TYPE_UINT64, &v);
+ } break;
+ case G_VARIANT_CLASS_HANDLE: {
+ gint32 v = g_variant_get_handle(value);
+ dbus_message_iter_append_basic(iter, DBUS_TYPE_INT32, &v);
+ } break;
+ case G_VARIANT_CLASS_DOUBLE: {
+ gdouble v = g_variant_get_double(value);
+ dbus_message_iter_append_basic(iter, DBUS_TYPE_DOUBLE, &v);
+ } break;
+ case G_VARIANT_CLASS_STRING: {
+ const gchar *v = g_variant_get_string(value, NULL);
+ dbus_message_iter_append_basic(iter, DBUS_TYPE_STRING, &v);
+ } break;
+ case G_VARIANT_CLASS_OBJECT_PATH: {
+ const gchar *v = g_variant_get_string(value, NULL);
+ dbus_message_iter_append_basic(iter, DBUS_TYPE_OBJECT_PATH, &v);
+ } break;
+ case G_VARIANT_CLASS_SIGNATURE: {
+ const gchar *v = g_variant_get_string(value, NULL);
+ dbus_message_iter_append_basic(iter, DBUS_TYPE_SIGNATURE, &v);
+ } break;
+ case G_VARIANT_CLASS_VARIANT: {
+ DBusMessageIter sub;
+ GVariant *child;
+
+ child = g_variant_get_child_value(value, 0);
+ dbus_message_iter_open_container(iter, DBUS_TYPE_VARIANT,
+ g_variant_get_type_string(child),
+ &sub);
+ if (!append_g_variant_to_dbus_msg_iter(&sub, child, g_error)) {
+ G_VARIANT_FREE(child);
+ goto fail;
+ }
+ dbus_message_iter_close_container(iter, &sub);
+ G_VARIANT_FREE(child);
+ } break;
+ case G_VARIANT_CLASS_MAYBE: {
+ GVariant *child;
+
+ if (!g_variant_n_children(value)) {
+ g_set_error(g_error, RDCP_ERROR, RDCP_ERROR_DBUS,
+ "cannot serialize an empty GVariant MAYBE");
+ goto fail;
+ } else {
+ child = g_variant_get_child_value(value, 0);
+ if (!append_g_variant_to_dbus_msg_iter(iter, child, g_error)) {
+ G_VARIANT_FREE(child);
+ goto fail;
+ }
+ G_VARIANT_FREE(child);
+ }
+ } break;
+ case G_VARIANT_CLASS_ARRAY: {
+ DBusMessageIter dbus_iter;
+ const gchar *type_string;
+ gsize n, i;
+ GVariant *child;
+
+ type_string = g_variant_get_type_string(value);
+ type_string++; /* skip the 'a' */
+
+ dbus_message_iter_open_container(iter, DBUS_TYPE_ARRAY,
+ type_string, &dbus_iter);
+
+ n = g_variant_n_children(value);
+
+ for (i = 0; i < n; i++) {
+ child = g_variant_get_child_value(value, i);
+ if (!append_g_variant_to_dbus_msg_iter(&dbus_iter, child, g_error)) {
+ G_VARIANT_FREE(child);
+ goto fail;
+ }
+ G_VARIANT_FREE(child);
+ }
+
+ dbus_message_iter_close_container(iter, &dbus_iter);
+ } break;
+ case G_VARIANT_CLASS_TUPLE: {
+ DBusMessageIter dbus_iter;
+ gsize n, i;
+ GVariant *child;
+
+ dbus_message_iter_open_container(iter, DBUS_TYPE_STRUCT,
+ NULL, &dbus_iter);
+
+ n = g_variant_n_children(value);
+
+ for (i = 0; i < n; i++) {
+ child = g_variant_get_child_value(value, i);
+ if (!append_g_variant_to_dbus_msg_iter(&dbus_iter, child, g_error)) {
+ G_VARIANT_FREE(child);
+ goto fail;
+ }
+ G_VARIANT_FREE(child);
+ }
+
+ dbus_message_iter_close_container(iter, &dbus_iter);
+
+ } break;
+ case G_VARIANT_CLASS_DICT_ENTRY: {
+ DBusMessageIter dbus_iter;
+ GVariant *key, *val;
+
+ dbus_message_iter_open_container(iter, DBUS_TYPE_DICT_ENTRY,
+ NULL, &dbus_iter);
+ key = g_variant_get_child_value(value, 0);
+ if (!append_g_variant_to_dbus_msg_iter(&dbus_iter, key, g_error)) {
+ G_VARIANT_FREE(key);
+ goto fail;
+ }
+ G_VARIANT_FREE(key);
+
+ val = g_variant_get_child_value(value, 1);
+ if (!append_g_variant_to_dbus_msg_iter(&dbus_iter, val, g_error)) {
+ G_VARIANT_FREE(val);
+ goto fail;
+ }
+ G_VARIANT_FREE(val);
+
+ dbus_message_iter_close_container(iter, &dbus_iter);
+ } break;
+ default: {
+ g_set_error(g_error, RDCP_ERROR, RDCP_ERROR_DBUS,
+ "Error serializing GVariant with class '%c' to a D-Bus message",
+ class);
+ goto fail;
+ } break;
+ }
+
+ return TRUE;
+
+ fail:
+ return FALSE;
+}
+
+/**
+ * append_g_variant_to_dbus_message:
+ * @message DBus message currently being built
+ * @g_variant The GVariant item to be appended to @message
+ * @g_error initialized to error info when FALSE is returned.
+ *
+ * Given a DBusMessage append the contents of the provied @g_variant to the message.
+ *
+ * Returns: return TRUE if successful, FALSE if error with @g_error initialized.
+ */
+static gboolean
+append_g_variant_to_dbus_message(DBusMessage *message, GVariant *g_variant, GError **g_error)
+{
+ DBusMessageIter iter;
+
+ g_return_val_if_fail (message != NULL, FALSE);
+ g_return_val_if_fail (g_variant != NULL, FALSE);
+ g_return_val_if_fail (g_error == NULL || *g_error == NULL, FALSE);
+
+ dbus_message_iter_init_append(message, &iter);
+ if (!append_g_variant_to_dbus_msg_iter(&iter, g_variant, g_error)) {
+ return FALSE;
+ }
+ return TRUE;
+}
+
+/**
+ * dbus_method_append_args_tuple:
+ * @message DBus message currently being built
+ * @args A GVariant tuple containing the method parameters to
+ * be appended to @message
+ * @g_error initialized to error info when FALSE is returned.
+ *
+ * Append the method parameters to a DBus method message. @args
+ * is a GVariant tuple representing the parameter list.
+ *
+ * Returns: return TRUE if successful, FALSE if error with @g_error initialized.
+ */
+static gboolean
+dbus_method_append_args_tuple(DBusMessage *message, GVariant *args, GError **g_error)
+{
+ DBusMessageIter iter;
+ gsize n, i;
+ GVariant *arg;
+
+ g_return_val_if_fail (message != NULL, FALSE);
+ g_return_val_if_fail (args != NULL && g_variant_is_of_type(args, G_VARIANT_TYPE_TUPLE), FALSE);
+ g_return_val_if_fail (g_error == NULL || *g_error == NULL, FALSE);
+
+ if ((n = g_variant_n_children(args))) {
+
+ dbus_message_iter_init_append(message, &iter);
+
+ for (i = 0; i < n; i++) {
+ arg = g_variant_get_child_value(args, i);
+ if (!append_g_variant_to_dbus_msg_iter(&iter, arg, g_error)) {
+ G_VARIANT_FREE(arg);
+ return FALSE;
+ }
+ G_VARIANT_FREE(arg);
+ }
+ }
+
+ return TRUE;
+}
+
+/**
+ * marshal_dbus_string_variant:
+ * @iter iterator into which the string variant will be inserted
+ * @value string value to insert as variant
+ * @g_error initialized to error info when FALSE is returned.
+ *
+ * Add a string variant while marshaling DBus protocol.
+ *
+ * Returns: return TRUE if successful, FALSE if error with @g_error initialized.
+ */
+static gboolean
+marshal_dbus_string_variant(DBusMessageIter *iter, const char *value, GError **g_error)
+{
+ DBusMessageIter variant;
+
+ g_return_val_if_fail (iter != NULL, FALSE);
+ g_return_val_if_fail (value != NULL, FALSE);
+ g_return_val_if_fail (g_error == NULL || *g_error == NULL, FALSE);
+
+ if (!dbus_message_iter_open_container(iter, DBUS_TYPE_VARIANT, "s", &variant)) {
+ g_set_error(g_error, RDCP_ERROR, RDCP_ERROR_DBUS,
+ "cannot open dbus variant string container, value=\"%s\"", value);
+ return FALSE;
+ }
+
+ if (!dbus_message_iter_append_basic(&variant, DBUS_TYPE_STRING, &value)) {
+ g_set_error(g_error, RDCP_ERROR, RDCP_ERROR_DBUS,
+ "cannot append dbus variant string value, value=\"%s\"", value);
+ return FALSE;
+ }
+
+ if (!dbus_message_iter_close_container(iter, &variant)) {
+ g_set_error(g_error, RDCP_ERROR, RDCP_ERROR_DBUS,
+ "cannot close dbus variant container, value=\"%s\"", value);
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+/**
+ * marshal_dbus_dict_string_entry:
+ * @array dictionary array into which entry is inserted
+ * @name entry's key
+ * @value entry's value
+ * @g_error initialized to error info when FALSE is returned.
+ *
+ * Adds a dictionary entry into an dictionary array whose key is a
+ * string and whose value is also a string while marshaling DBus protocol.
+ *
+ * Returns: return TRUE if successful, FALSE if error with @g_error initialized.
+ */
+static gboolean
+marshal_dbus_dict_string_entry(DBusMessageIter *array, const char *name, const char *value, GError **g_error)
+{
+ DBusMessageIter entry;
+
+ g_return_val_if_fail (array != NULL, FALSE);
+ g_return_val_if_fail (name != NULL, FALSE);
+ g_return_val_if_fail (value != NULL, FALSE);
+ g_return_val_if_fail (g_error == NULL || *g_error == NULL, FALSE);
+
+ if (!dbus_message_iter_open_container(array, DBUS_TYPE_DICT_ENTRY, NULL, &entry)) {
+ g_set_error(g_error, RDCP_ERROR, RDCP_ERROR_DBUS,
+ "cannot open dbus dict entry container for option <%s=%s>", name, value);
+ return FALSE;
+ }
+
+ if (!dbus_message_iter_append_basic(&entry, DBUS_TYPE_STRING, &name)) {
+ g_set_error(g_error, RDCP_ERROR, RDCP_ERROR_DBUS,
+ "cannot append option name for option <%s=%s>", name, value);
+ return FALSE;
+ }
+
+ if (!marshal_dbus_string_variant(&entry, value, g_error)) {
+ return FALSE;
+ }
+
+ if (!dbus_message_iter_close_container(array, &entry)) {
+ g_set_error(g_error, RDCP_ERROR, RDCP_ERROR_DBUS,
+ "cannot close dbus dict entry container for option <%s=%s>", name, value);
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+
+/*----------------------------------------------------------------------------*/
+
+
+/**
+ * dbus_iter_to_variant:
+ * @msg DBusMessage which will be converted to a GVariant
+ * @g_variant_return Pointer to location where GVariant will be returned,
+ * will be NULL if error occurs
+ * @g_error initialized to error info when FALSE is returned.
+ *
+ * Helper routine for dbus_message_to_g_variant(),
+ * Performs the recusive descent into the DBusMessage appending
+ * values to the GVariant as it goes.
+ *
+ * Returns: return TRUE if successful, @g_variant_return will be non-NULL.
+ * FALSE if error with @g_error initialized, @g_variant_return will be NULL.
+ */
+static gboolean
+dbus_iter_to_variant(DBusMessageIter *iter, GVariant **g_variant_return, GError **g_error)
+{
+ gboolean result = TRUE;
+ int arg_type;
+ GVariant *g_variant = NULL;
+ char *signature = NULL;
+ GVariantBuilder builder;
+ DBusMessageIter sub;
+
+ g_return_val_if_fail (iter != NULL, FALSE);
+ g_return_val_if_fail (g_variant_return != NULL, FALSE);
+ g_return_val_if_fail (g_error == NULL || *g_error == NULL, FALSE);
+
+ *g_variant_return = NULL;
+
+ arg_type = dbus_message_iter_get_arg_type(iter);
+
+#ifdef TRACE_VARIANT
+ signature = dbus_message_iter_get_signature(iter);
+ printf("dbus_iter_to_variant enter: type=%s signature=%s\n",
+ dbus_type_to_string(arg_type), signature);
+ g_free(signature);
+ signature = NULL;
+#endif
+
+ switch (arg_type) {
+ case DBUS_TYPE_BOOLEAN: {
+ dbus_bool_t value;
+
+ dbus_message_iter_get_basic(iter, &value);
+ if ((g_variant = g_variant_new_boolean(value)) == NULL) {
+ g_set_error(g_error, RDCP_ERROR, RDCP_ERROR_DBUS,
+ "unable to create GVariant boolean value=%d", value);
+ result = FALSE;
+ }
+
+ } break;
+ case DBUS_TYPE_BYTE: {
+ guint8 value;
+
+ dbus_message_iter_get_basic(iter, &value);
+ if ((g_variant = g_variant_new_byte(value)) == NULL) {
+ g_set_error(g_error, RDCP_ERROR, RDCP_ERROR_DBUS,
+ "unable to create GVariant byte value=%uc", value);
+ result = FALSE;
+ }
+
+ } break;
+ case DBUS_TYPE_INT16: {
+ gint16 value;
+
+ dbus_message_iter_get_basic(iter, &value);
+ if ((g_variant = g_variant_new_int16(value)) == NULL) {
+ g_set_error(g_error, RDCP_ERROR, RDCP_ERROR_DBUS,
+ "unable to create GVariant int16 value=%" G_GINT16_FORMAT, value);
+ result = FALSE;
+ }
+
+ } break;
+ case DBUS_TYPE_UINT16: {
+ guint16 value;
+
+ dbus_message_iter_get_basic(iter, &value);
+ if ((g_variant = g_variant_new_uint16(value)) == NULL) {
+ g_set_error(g_error, RDCP_ERROR, RDCP_ERROR_DBUS,
+ "unable to create GVariant uint16 value=%" G_GUINT16_FORMAT, value);
+ result = FALSE;
+ }
+
+ } break;
+ case DBUS_TYPE_INT32: {
+ gint32 value;
+
+ dbus_message_iter_get_basic(iter, &value);
+ if ((g_variant = g_variant_new_int32(value)) == NULL) {
+ g_set_error(g_error, RDCP_ERROR, RDCP_ERROR_DBUS,
+ "unable to create GVariant int32 value=%" G_GINT32_FORMAT, value);
+ result = FALSE;
+ }
+
+ } break;
+ case DBUS_TYPE_UINT32: {
+ guint32 value;
+
+ dbus_message_iter_get_basic(iter, &value);
+ if ((g_variant = g_variant_new_uint32(value)) == NULL) {
+ g_set_error(g_error, RDCP_ERROR, RDCP_ERROR_DBUS,
+ "unable to create GVariant uint32 value=%" G_GUINT32_FORMAT, value);
+ result = FALSE;
+ }
+
+ } break;
+ case DBUS_TYPE_INT64: {
+ gint64 value;
+
+ dbus_message_iter_get_basic(iter, &value);
+ if ((g_variant = g_variant_new_int64(value)) == NULL) {
+ g_set_error(g_error, RDCP_ERROR, RDCP_ERROR_DBUS,
+ "unable to create GVariant int64 value=%" G_GINT64_FORMAT, value);
+ result = FALSE;
+ }
+
+ } break;
+ case DBUS_TYPE_UINT64: {
+ guint64 value;
+
+ dbus_message_iter_get_basic(iter, &value);
+ if ((g_variant = g_variant_new_uint64(value)) == NULL) {
+ g_set_error(g_error, RDCP_ERROR, RDCP_ERROR_DBUS,
+ "unable to create GVariant uint64 value=%" G_GUINT64_FORMAT, value);
+ result = FALSE;
+ }
+
+ } break;
+ case DBUS_TYPE_DOUBLE: {
+ gdouble value;
+
+ dbus_message_iter_get_basic(iter, &value);
+ if ((g_variant = g_variant_new_double(value)) == NULL) {
+ g_set_error(g_error, RDCP_ERROR, RDCP_ERROR_DBUS,
+ "unable to create GVariant double value=%f", value);
+ result = FALSE;
+ }
+
+ } break;
+ case DBUS_TYPE_STRING: {
+ gchar *value;
+
+ dbus_message_iter_get_basic(iter, &value);
+ if ((g_variant = g_variant_new_string(value)) == NULL) {
+ g_set_error(g_error, RDCP_ERROR, RDCP_ERROR_DBUS,
+ "unable to create GVariant string value=\"%s\"", value);
+ result = FALSE;
+ }
+
+ } break;
+ case DBUS_TYPE_OBJECT_PATH: {
+ gchar *value;
+
+ dbus_message_iter_get_basic(iter, &value);
+ if ((g_variant = g_variant_new_object_path(value)) == NULL) {
+ g_set_error(g_error, RDCP_ERROR, RDCP_ERROR_DBUS,
+ "unable to create GVariant object path value=\"%s\"", value);
+ result = FALSE;
+ }
+
+ } break;
+ case DBUS_TYPE_SIGNATURE: {
+ gchar *value;
+
+ dbus_message_iter_get_basic(iter, &value);
+ if ((g_variant = g_variant_new_signature(value)) == NULL) {
+ g_set_error(g_error, RDCP_ERROR, RDCP_ERROR_DBUS,
+ "unable to create GVariant signature value=\"%s\"", value);
+ result = FALSE;
+ }
+
+ } break;
+ case DBUS_TYPE_UNIX_FD: {
+ guint32 value;
+
+ dbus_message_iter_get_basic(iter, &value);
+ if ((g_variant = g_variant_new_uint32(value)) == NULL) {
+ g_set_error(g_error, RDCP_ERROR, RDCP_ERROR_DBUS,
+ "unable to create GVariant file descriptor value=%u", value);
+ result = FALSE;
+ }
+
+ } break;
+ case DBUS_TYPE_ARRAY: {
+ GVariant *item;
+
+ signature = dbus_message_iter_get_signature(iter);
+
+ g_variant_builder_init(&builder, G_VARIANT_TYPE(signature));
+ dbus_message_iter_recurse(iter, &sub);
+
+ while (dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_INVALID) {
+ if (!dbus_iter_to_variant(&sub, &item, g_error)) {
+ g_variant_builder_clear(&builder);
+ result = FALSE;
+ goto exit;
+ }
+#ifdef TRACE_VARIANT
+ {
+ gchar *variant_as_string = g_variant_print(item, TRUE);
+ printf("array item=%s\n", variant_as_string);
+ g_free(variant_as_string);
+ }
+#endif
+ g_variant_builder_add_value(&builder, item);
+ dbus_message_iter_next(&sub);
+ }
+
+ if ((g_variant = g_variant_builder_end(&builder)) == NULL) {
+ g_set_error(g_error, RDCP_ERROR, RDCP_ERROR_DBUS,
+ "unable to create GVariant array");
+ result = FALSE;
+ }
+
+ } break;
+ case DBUS_TYPE_VARIANT: {
+ GVariant *item;
+
+ signature = dbus_message_iter_get_signature(iter);
+ g_variant_builder_init(&builder, G_VARIANT_TYPE(signature));
+ dbus_message_iter_recurse(iter, &sub);
+
+ if (!dbus_iter_to_variant(&sub, &item, g_error)) {
+ g_variant_builder_clear(&builder);
+ result = FALSE;
+ goto exit;
+ }
+#ifdef TRACE_VARIANT
+ {
+ gchar *variant_as_string = g_variant_print(item, TRUE);
+ printf("variant item=%s\n", variant_as_string);
+ g_free(variant_as_string);
+ }
+#endif
+
+ g_variant_builder_add_value(&builder, item);
+
+ if ((g_variant = g_variant_builder_end(&builder)) == NULL) {
+ gchar *variant_as_string = g_variant_print(item, FALSE);
+
+ g_set_error(g_error, RDCP_ERROR, RDCP_ERROR_DBUS,
+ "unable to create GVariant variant for value=%s", variant_as_string);
+ g_free(variant_as_string);
+ result = FALSE;
+ }
+ } break;
+ case DBUS_TYPE_STRUCT: {
+ GVariant *item;
+
+ signature = dbus_message_iter_get_signature(iter);
+ g_variant_builder_init(&builder, G_VARIANT_TYPE(signature));
+ dbus_message_iter_recurse(iter, &sub);
+
+ while (dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_INVALID) {
+ if (!dbus_iter_to_variant(&sub, &item, g_error)) {
+ g_variant_builder_clear(&builder);
+ result = FALSE;
+ goto exit;
+ }
+#ifdef TRACE_VARIANT
+ {
+ gchar *variant_as_string = g_variant_print(item, TRUE);
+ printf("struct item=%s\n", variant_as_string);
+ g_free(variant_as_string);
+ }
+#endif
+ g_variant_builder_add_value(&builder, item);
+ dbus_message_iter_next(&sub);
+ }
+
+ if ((g_variant = g_variant_builder_end(&builder)) == NULL) {
+ g_set_error(g_error, RDCP_ERROR, RDCP_ERROR_DBUS,
+ "unable to create GVariant struct");
+ result = FALSE;
+ }
+
+ } break;
+ case DBUS_TYPE_DICT_ENTRY: {
+ GVariant *key, *value;
+
+ dbus_message_iter_recurse(iter, &sub);
+ signature = dbus_message_iter_get_signature(iter);
+
+ if (!dbus_iter_to_variant(&sub, &key, g_error)) {
+ g_prefix_error(g_error, "unable to create GVariant dict_entry key: ");
+ result = FALSE;
+ goto exit;
+ }
+
+ dbus_message_iter_next(&sub);
+
+ if (!dbus_iter_to_variant(&sub, &value, g_error)) {
+ g_prefix_error(g_error, "unable to create GVariant dict_entry value: ");
+ result = FALSE;
+ goto exit;
+ }
+#ifdef TRACE_VARIANT
+ {
+ gchar *key_variant_as_string = g_variant_print(key, TRUE);
+ gchar *value_variant_as_string = g_variant_print(key, TRUE);
+ printf("dict_entry key=%s value=%s\n", g_variant_print(key, TRUE), g_variant_print(value, TRUE));
+ g_free(key_variant_as_string);
+ g_free(value_variant_as_string);
+ }
+#endif
+
+ g_variant = g_variant_new_dict_entry(key, value);
+
+ } break;
+ default: {
+ signature = dbus_message_iter_get_signature(iter);
+ g_set_error(g_error, RDCP_ERROR, RDCP_ERROR_DBUS,
+ "unknown DBus type=%d, signature=%s", arg_type, signature);
+ result = FALSE;
+ break;
+ }
+ }
+
+ exit:
+ if (signature) dbus_free(signature);
+ *g_variant_return = g_variant;
+
+#ifdef TRACE_VARIANT
+ {
+ gchar *variant_as_string = NULL;
+ if (g_variant) {
+ variant_as_string = g_variant_print(g_variant, TRUE);
+ } else {
+ variant_as_string = "NULL";
+ }
+ printf("dbus_iter_to_variant returns %s, variant=%s\n",
+ result ? "TRUE" : "FALSE", variant_as_string);
+ if (g_variant) {
+ g_free(variant_as_string);
+ }
+ }
+#endif
+
+ return result;
+}
+
+/**
+ * dbus_message_to_g_variant:
+ * @msg DBusMessage which will be converted to a GVariant
+ * @g_variant_return Pointer to location where GVariant will be returned,
+ * will be NULL if error occurs
+ * @g_error initialized to error info when FALSE is returned.
+ *
+ * Converts a DBusMessage to a GVariant.
+ *
+ * Returns: return TRUE if successful, @g_variant_return will be non-NULL.
+ * FALSE if error with @g_error initialized, @g_variant_return will be NULL.
+ */
+static gboolean
+dbus_message_to_g_variant(DBusMessage *msg, GVariant **g_variant_return, GError **g_error)
+{
+ DBusMessageIter iter;
+
+ g_return_val_if_fail (msg != NULL, FALSE);
+ g_return_val_if_fail (g_variant_return != NULL, FALSE);
+ g_return_val_if_fail (g_error == NULL || *g_error == NULL, FALSE);
+
+ *g_variant_return = NULL;
+
+#ifdef RDCP_DBUS_DEBUG
+ {
+ gchar *msg_as_string = dbus_message_print(msg, NULL, FALSE);
+ printf("dbus_message_to_g_variant: msg=\n%s", msg_as_string);
+ g_free(msg_as_string);
+ }
+#endif
+
+ if (!dbus_message_iter_init(msg, &iter)) {
+ g_set_error(g_error, RDCP_ERROR, RDCP_ERROR_DBUS,
+ "could not create iterator to parse DBus message");
+ return FALSE;
+ }
+
+ if (!dbus_iter_to_variant(&iter, g_variant_return, g_error)) {
+ g_prefix_error(g_error, "unable to convert dbus_message to GVariant: ");
+ return FALSE;
+ }
+
+#ifdef RDCP_DBUS_DEBUG
+ {
+ gchar *variant_as_string = NULL;
+ if (*g_variant_return) {
+ variant_as_string = g_variant_print(*g_variant_return, TRUE);
+ } else {
+ variant_as_string = "NULL";
+ }
+ printf("dbus_message_to_g_variant returns variant=%s\n", variant_as_string);
+ if (*g_variant_return) {
+ g_free(variant_as_string);
+ }
+ }
+#endif
+
+ return TRUE;
+}
+
+/**
+ * dbus_method_reply_to_g_variant_tuple:
+ * @msg DBus message reply which will be converted to a tuple of GVariant's
+ * @g_variant_return Pointer to location where GVariant will be returned,
+ * will be NULL if error occurs
+ * @g_error initialized to error info when FALSE is returned.
+ *
+ * A DBus method reply contains a sequence of zero or more OUT parameters.
+ * Parse the method reply and build a GVariant tuple whose members are
+ * the OUT parameters. Each tuple member will also be a GVariant.
+ *
+ * Returns: return TRUE if successful, @g_variant_return will be non-NULL.
+ * FALSE if error with @g_error initialized, @g_variant_return will be NULL.
+ */
+static gboolean
+dbus_method_reply_to_g_variant_tuple(DBusMessage *msg, GVariant **g_variant_return, GError **g_error)
+{
+ DBusMessageIter iter;
+ GVariantBuilder builder;
+
+ g_return_val_if_fail (msg != NULL, FALSE);
+ g_return_val_if_fail (g_variant_return != NULL, FALSE);
+ g_return_val_if_fail (g_error == NULL || *g_error == NULL, FALSE);
+
+ *g_variant_return = NULL;
+
+#ifdef RDCP_DBUS_DEBUG
+ {
+ gchar *msg_as_string = dbus_message_print(msg, NULL, FALSE);
+ printf("dbus_method_reply_to_g_variant_tuple: msg=\n%s", msg_as_string);
+ g_free(msg_as_string);
+ }
+#endif
+
+ g_variant_builder_init(&builder, G_VARIANT_TYPE_TUPLE);
+
+ if (!dbus_message_iter_init(msg, &iter)) {
+ g_set_error(g_error, RDCP_ERROR, RDCP_ERROR_DBUS,
+ "could not create iterator to parse DBus message");
+ return FALSE;
+ }
+
+ while (dbus_message_iter_get_arg_type (&iter) != DBUS_TYPE_INVALID) {
+ GVariant *g_variant = NULL;
+
+ if (!dbus_iter_to_variant(&iter, &g_variant, g_error)) {
+ g_prefix_error(g_error, "unable to convert dbus_message to GVariant: ");
+ return FALSE;
+ }
+
+ g_variant_builder_add_value(&builder, g_variant);
+
+ dbus_message_iter_next (&iter);
+ }
+
+ if ((*g_variant_return = g_variant_builder_end(&builder)) == NULL) {
+ g_set_error(g_error, RDCP_ERROR, RDCP_ERROR_DBUS,
+ "unable to build GVariant options array");
+ return FALSE;
+ }
+
+
+#ifdef RDCP_DBUS_DEBUG
+ {
+ gchar *variant_as_string = NULL;
+ if (*g_variant_return) {
+ variant_as_string = g_variant_print(*g_variant_return, TRUE);
+ } else {
+ variant_as_string = "NULL";
+ }
+ printf("dbus_method_reply_to_g_variant_tuple returns variant=%s\n", variant_as_string);
+ if (*g_variant_return) {
+ g_free(variant_as_string);
+ }
+ }
+#endif
+
+ return TRUE;
+}
+
+/*----------------------------------------------------------------------------*/
+
+/**
+ * get_dbus_string_property:
+ * @bus The DBus connection on which the query will be performed
+ * @object_path The DBus object path identifying the object
+ * @interface The DBus interface which provides the property
+ * @property The name of the proptery on the interface
+ * @value_return Pointer to where string value will be returned.
+ * Must be freed with g_free(), will be NULL if error occurs.
+ * @g_error initialized to error info when FALSE is returned.
+ *
+ * Retrieve a string valued property from an DBus object.
+ *
+ * Returns: return TRUE if successful, @value_return will be non-NULL.
+ * FALSE if error with @g_error initialized, @value_return will be NULL.
+ */
+gboolean
+get_dbus_string_property(DBusConnection *bus, const char *object_path,
+ const char* interface, const char *property,
+ char **value_return, GError **g_error)
+{
+ const char *method = "Get";
+ DBusMessage* msg = NULL;
+ DBusMessage* reply = NULL;
+ DBusError dbus_error;
+ const char *interface_ptr = interface;
+ const char *property_ptr = property;
+ DBusMessageIter iter, variant;
+ char *value = NULL;
+ char *signature;
+
+ g_return_val_if_fail (bus != NULL, FALSE);
+ g_return_val_if_fail (object_path != NULL, FALSE);
+ g_return_val_if_fail (interface != NULL, FALSE);
+ g_return_val_if_fail (property != NULL, FALSE);
+ g_return_val_if_fail (value_return != NULL, FALSE);
+ g_return_val_if_fail (g_error == NULL || *g_error == NULL, FALSE);
+
+ *value_return = NULL;
+ dbus_error_init(&dbus_error);
+
+ if ((msg = dbus_message_new_method_call(REALM_DBUS_BUS_NAME, object_path,
+ DBUS_INTERFACE_PROPERTIES, method)) == NULL) {
+ g_set_error(g_error, RDCP_ERROR, RDCP_ERROR_DBUS,
+ "failed to create"
+ "DBus %s.%s() message, object_path=%s, interface=%s, property=%s",
+ DBUS_INTERFACE_PROPERTIES, method, object_path, interface, property);
+ return FALSE;
+ }
+
+ if (!dbus_message_append_args(msg,
+ DBUS_TYPE_STRING, &interface_ptr,
+ DBUS_TYPE_STRING, &property_ptr,
+ DBUS_TYPE_INVALID)) {
+ g_set_error(g_error, RDCP_ERROR, RDCP_ERROR_DBUS,
+ "failed to add args to "
+ "DBus %s.%s() message, object_path=%s, interface=%s, property=%s",
+ DBUS_INTERFACE_PROPERTIES, method, object_path, interface, property);
+ dbus_message_unref(msg);
+ return FALSE;
+ }
+
+ if ((reply = dbus_connection_send_with_reply_and_block(bus, msg, -1, &dbus_error)) == NULL) {
+ dbus_message_unref(msg);
+ RETURN_DBUS_ERROR(g_error, dbus_error);
+ }
+ dbus_message_unref(msg);
+
+ if (!dbus_message_has_signature(reply, "v")) {
+ g_set_error(g_error, RDCP_ERROR, RDCP_ERROR_DBUS,
+ "expected variant in DBus %s.%s() reply, object_path=%s, interface=%s, property=%s",
+ DBUS_INTERFACE_PROPERTIES, method, object_path, interface, property);
+ dbus_message_unref(reply);
+ return FALSE;
+ }
+
+ if (!dbus_message_iter_init(reply, &iter)) {
+ g_set_error(g_error, RDCP_ERROR, RDCP_ERROR_DBUS,
+ "could not create iterator to parse "
+ "DBus %s.%s() reply, object_path=%s, interface=%s, property=%s",
+ DBUS_INTERFACE_PROPERTIES, method, object_path, interface, property);
+ dbus_message_unref(reply);
+ return FALSE;
+ }
+
+ dbus_message_iter_recurse(&iter, &variant);
+ signature = dbus_message_iter_get_signature(&variant);
+ if (!g_str_equal(signature, DBUS_TYPE_STRING_AS_STRING)) {
+ g_set_error(g_error, RDCP_ERROR, RDCP_ERROR_DBUS,
+ "expected string type variant but got \"%s\" signature instead for "
+ "DBus %s.%s() reply, object_path=%s, interface=%s, property=%s",
+ signature, DBUS_INTERFACE_PROPERTIES, method, object_path, interface, property);
+ dbus_free(signature);
+ dbus_message_unref(reply);
+ return FALSE;
+ }
+ dbus_free(signature);
+ dbus_message_iter_get_basic(&variant, &value);
+ *value_return = g_strdup(value);
+
+ dbus_message_unref(reply);
+ return TRUE;
+}
+
+/**
+ * get_dbus_properties:
+ * @bus The DBus connection on which the query will be performed
+ * @object_path The DBus object path identifying the object
+ * @interface The DBus interface which provides the property
+ * @properties_return Pointer to where a GVariant containing all
+ * the interface's properties for the object will be returned.
+ * Must be freed with g_variant_unref(), will be NULL if error occurs.
+ * @g_error initialized to error info when FALSE is returned.
+ *
+ * Retrieve all the interface properties from an DBus object.
+ * Returned as a GVariant dictionary. Use g_variant_lookup() to
+ * obtain a specific property in the dictionary.
+ *
+ * Returns: return TRUE if successful, @value_return will be non-NULL.
+ * FALSE if error with @g_error initialized, @value_return will be NULL.
+ */
+gboolean
+get_dbus_properties(DBusConnection *bus, const char *object_path,
+ const char* interface, GVariant **properties_return,
+ GError **g_error)
+{
+ const char *method = "GetAll";
+ DBusMessage* msg = NULL;
+ DBusMessage* reply = NULL;
+ DBusError dbus_error;
+ const char *interface_ptr = interface;
+
+ g_return_val_if_fail (bus != NULL, FALSE);
+ g_return_val_if_fail (object_path != NULL, FALSE);
+ g_return_val_if_fail (interface != NULL, FALSE);
+ g_return_val_if_fail (properties_return != NULL, FALSE);
+ g_return_val_if_fail (g_error == NULL || *g_error == NULL, FALSE);
+
+ *properties_return = NULL;
+ dbus_error_init(&dbus_error);
+
+ if ((msg = dbus_message_new_method_call(REALM_DBUS_BUS_NAME, object_path,
+ DBUS_INTERFACE_PROPERTIES, method)) == NULL) {
+ g_set_error(g_error, RDCP_ERROR, RDCP_ERROR_DBUS,
+ "failed to create"
+ "DBus %s.%s() message, object_path=%s, interface=%s",
+ DBUS_INTERFACE_PROPERTIES, method, object_path, interface);
+ return FALSE;
+ }
+
+ if (!dbus_message_append_args(msg,
+ DBUS_TYPE_STRING, &interface_ptr,
+ DBUS_TYPE_INVALID)) {
+ g_set_error(g_error, RDCP_ERROR, RDCP_ERROR_DBUS,
+ "failed to add args to "
+ "DBus %s.%s() message, object_path=%s, interface=%s",
+ DBUS_INTERFACE_PROPERTIES, method, object_path, interface);
+ dbus_message_unref(msg);
+ return FALSE;
+ }
+
+ if ((reply = dbus_connection_send_with_reply_and_block(bus, msg, -1, &dbus_error)) == NULL) {
+ dbus_message_unref(msg);
+ RETURN_DBUS_ERROR(g_error, dbus_error);
+ }
+ dbus_message_unref(msg);
+
+ if (!dbus_message_to_g_variant(reply, properties_return, g_error)) {
+ dbus_message_unref(reply);
+ return FALSE;
+ }
+
+
+ dbus_message_unref(reply);
+ return TRUE;
+}
+
+/*----------------------------------------------------------------------------*/
+
+/**
+ * dbus_discover_marshal:
+ * @target what to discover
+ * @options dictionary of option {key,values}
+ * @msg_return if successful DBus message returned here,
+ * if error then this will be NULL.
+ * @g_error initialized to error info when FALSE is returned.
+ *
+ * Marshal a realm Discover method call.
+ *
+ * Returns: return TRUE if successful, @msg_return will point to DBusMessage,
+ * FALSE if error with @g_error initialized. @msg_return will be NULL.
+ */
+static gboolean
+dbus_discover_marshal(const char* target, GVariant *options,
+ DBusMessage **msg_return, GError **g_error)
+{
+ const char *method = "Discover";
+ DBusMessage *msg = NULL;
+ DBusMessageIter iter;
+
+ g_return_val_if_fail (target != NULL, FALSE);
+ g_return_val_if_fail (options != NULL, FALSE);
+ g_return_val_if_fail (msg_return != NULL, FALSE);
+ g_return_val_if_fail (g_error == NULL || *g_error == NULL, FALSE);
+
+ *msg_return = NULL;
+
+ if ((msg = dbus_message_new_method_call(REALM_DBUS_BUS_NAME, REALM_DBUS_SERVICE_PATH,
+ REALM_DBUS_PROVIDER_INTERFACE, method)) == NULL) {
+ g_set_error(g_error, RDCP_ERROR, RDCP_ERROR_DBUS,
+ "failed to create dbus method call %s.%s() message, object_path=%s",
+ REALM_DBUS_PROVIDER_INTERFACE, method, REALM_DBUS_SERVICE_PATH);
+ return FALSE;
+ }
+
+ dbus_message_iter_init_append (msg, &iter); /* void return */
+
+ if (!dbus_message_iter_append_basic(&iter, DBUS_TYPE_STRING, &target)) {
+ g_set_error(g_error, RDCP_ERROR, RDCP_ERROR_DBUS,
+ "failed to add target parameter (%s)", target);
+ dbus_message_unref(msg);
+ return FALSE;
+ }
+
+ if (!append_g_variant_to_dbus_message(msg, options, g_error)) {
+ g_prefix_error(g_error, "unable to append GVariant options dictionary into %s.%s() message",
+ REALM_DBUS_PROVIDER_INTERFACE, method);
+ dbus_message_unref(msg);
+ return FALSE;
+ }
+
+ *msg_return = msg;
+ return TRUE;
+}
+
+/**
+ * dbus_discover_unmarshal:
+ * @reply DBus method reply from Discover call
+ * @relevance_return Pointer to returned relevance value
+ * @paths_return Pointer to an array of object path strings,
+ * must be freed with g_strfreev().
+ * @g_error initialized to error info when FALSE is returned.
+ *
+ * Parses the DBus reply message from the Discover call and unpacks
+ * the output paramters in the *_return parameters of this function.
+ *
+ * Returns: return TRUE if successful, FALSE if error with @g_error initialized.
+ */
+
+static gboolean
+dbus_discover_unmarshal(DBusMessage *reply, gint32 *relevance_return, gchar ***paths_return, GError **g_error)
+{
+ GVariant *g_variant_reply = NULL;
+
+ g_return_val_if_fail (reply != NULL, FALSE);
+ g_return_val_if_fail (relevance_return != NULL, FALSE);
+ g_return_val_if_fail (paths_return != NULL, FALSE);
+ g_return_val_if_fail (g_error == NULL || *g_error == NULL, FALSE);
+
+ if (!dbus_method_reply_to_g_variant_tuple(reply, &g_variant_reply, g_error)) {
+ gchar *reply_string = dbus_message_print(reply, NULL, FALSE);
+
+ g_prefix_error(g_error, "unable convert reply (%s) to GVariant tuple: ", reply_string);
+ g_free(reply_string);
+ dbus_message_unref(reply);
+ return FALSE;
+ }
+
+ g_variant_get(g_variant_reply, "(i^ao)", relevance_return, paths_return);
+ G_VARIANT_FREE(g_variant_reply);
+
+ return TRUE;
+}
+
+/**
+ * dbus_discover_call:
+ * @target what to discover
+ * @options dictionary of option {key,values}
+ * @relevance_return Pointer to returned relevance value
+ * @paths_return Pointer to an array of object path strings,
+ * must be freed with g_strfreev().
+ * @g_error initialized to error info when FALSE is returned.
+ *
+ * Marshal a realm Discover method call, call it synchronously,
+ * unmarsh it's reply and return the OUT parameters.
+ *
+ * Returns: return TRUE if successful, FALSE if error with @g_error initialized
+ */
+gboolean
+dbus_discover_call(DBusConnection *bus, const char *target, GVariant *options,
+ gint32 *relevance_return, gchar ***paths_return, GError **g_error)
+{
+ DBusError dbus_error;
+ DBusMessage *msg = NULL;
+ DBusMessage* reply = NULL;
+
+ g_return_val_if_fail (bus != NULL, FALSE);
+ g_return_val_if_fail (target != NULL, FALSE);
+ g_return_val_if_fail (options != NULL, FALSE);
+ g_return_val_if_fail (relevance_return != NULL, FALSE);
+ g_return_val_if_fail (paths_return != NULL, FALSE);
+ g_return_val_if_fail (g_error == NULL || *g_error == NULL, FALSE);
+
+ dbus_error_init(&dbus_error);
+ if (!dbus_discover_marshal(target, options, &msg, g_error)) {
+ RETURN_DBUS_ERROR(g_error, dbus_error);
+ }
+
+ if ((reply = dbus_connection_send_with_reply_and_block(bus, msg, -1, &dbus_error)) == NULL) {
+ dbus_message_unref(msg);
+ RETURN_DBUS_ERROR(g_error, dbus_error);
+ }
+ dbus_message_unref(msg);
+
+ if (!dbus_discover_unmarshal(reply, relevance_return, paths_return, g_error)) {
+ dbus_message_unref(reply);
+ return FALSE;
+ }
+ dbus_message_unref(reply);
+ return TRUE;
+}
+
+/*----------------------------------------------------------------------------*/
+
+/**
+ * dbus_change_login_policy_marshal:
+ * @dbus_path The DBus object path of the object supporting the Realm
+ * interface on which the call will be made.
+ * @login_policy The new login policy, or an empty string.
+ * @permitted_add: An array of logins to permit.
+ * @permitted_remove: An array of logins to not permit.
+ * @options dictionary of option {key,values}
+ * @msg_return if successful DBus message returned here,
+ * if error then this will be NULL.
+ * @g_error initialized to error info when FALSE is returned.
+ *
+ * Marshal a realm ChangeLoginPolicy method call.
+ *
+ * Returns: return TRUE if successful, @msg_return will point to DBusMessage,
+ * FALSE if error with @g_error initialized. @msg_return will be NULL.
+ */
+static gboolean
+dbus_change_login_policy_marshal(const gchar *dbus_path, const char *login_policy,
+ GVariant *permitted_add, GVariant *permitted_remove,
+ GVariant *options, DBusMessage **msg_return, GError **g_error)
+{
+ const char *method = "ChangeLoginPolicy";
+ DBusMessage *msg = NULL;
+ DBusMessageIter iter;
+
+ g_return_val_if_fail (dbus_path != NULL, FALSE);
+ g_return_val_if_fail (login_policy != NULL, FALSE);
+ g_return_val_if_fail (permitted_add != NULL, FALSE);
+ g_return_val_if_fail (permitted_remove != NULL, FALSE);
+ g_return_val_if_fail (options != NULL, FALSE);
+ g_return_val_if_fail (msg_return != NULL, FALSE);
+ g_return_val_if_fail (g_error == NULL || *g_error == NULL, FALSE);
+
+ *msg_return = NULL;
+
+ if ((msg = dbus_message_new_method_call(REALM_DBUS_BUS_NAME, dbus_path,
+ REALM_DBUS_REALM_INTERFACE, method)) == NULL) {
+ g_set_error(g_error, RDCP_ERROR, RDCP_ERROR_DBUS,
+ "failed to create dbus method call %s.%s() message, object_path=%s",
+ REALM_DBUS_PROVIDER_INTERFACE, method, REALM_DBUS_SERVICE_PATH);
+ return FALSE;
+ }
+
+ dbus_message_iter_init_append (msg, &iter); /* void return */
+
+ if (!dbus_message_iter_append_basic(&iter, DBUS_TYPE_STRING, &login_policy)) {
+ g_set_error(g_error, RDCP_ERROR, RDCP_ERROR_DBUS,
+ "failed to add login_policy parameter (%s)", login_policy);
+ dbus_message_unref(msg);
+ return FALSE;
+ }
+
+ if (!append_g_variant_to_dbus_message(msg, permitted_add, g_error)) {
+ g_prefix_error(g_error, "unable to append GVariant permitted_add dictionary into %s.%s() message",
+ REALM_DBUS_PROVIDER_INTERFACE, method);
+ dbus_message_unref(msg);
+ return FALSE;
+ }
+
+ if (!append_g_variant_to_dbus_message(msg, permitted_remove, g_error)) {
+ g_prefix_error(g_error, "unable to append GVariant permitted_remove dictionary into %s.%s() message",
+ REALM_DBUS_PROVIDER_INTERFACE, method);
+ dbus_message_unref(msg);
+ return FALSE;
+ }
+
+ if (!append_g_variant_to_dbus_message(msg, options, g_error)) {
+ g_prefix_error(g_error, "unable to append GVariant options dictionary into %s.%s() message",
+ REALM_DBUS_PROVIDER_INTERFACE, method);
+ dbus_message_unref(msg);
+ return FALSE;
+ }
+
+ *msg_return = msg;
+ return TRUE;
+}
+
+/**
+ * dbus_change_login_policy_unmarshal:
+ * @reply DBus method reply from ChangeLoginPolicy call
+ * @g_error initialized to error info when FALSE is returned.
+ *
+ * Parses the DBus reply message from the ChangeLoginPolicy call.
+ *
+ * Returns: return TRUE if successful, FALSE if error with @g_error initialized.
+ */
+
+static gboolean
+dbus_change_login_policy_unmarshal(DBusMessage *reply, GError **g_error)
+{
+ g_return_val_if_fail (reply != NULL, FALSE);
+ g_return_val_if_fail (g_error == NULL || *g_error == NULL, FALSE);
+
+ return TRUE;
+}
+
+/**
+ * dbus_change_login_policy_call:
+ * @dbus_path The DBus object path of the object supporting the Realm
+ * interface on which the call will be made.
+ * @login_policy The new login policy, or an empty string.
+ * @permitted_add: An array of logins to permit.
+ * @permitted_remove: An array of logins to not permit.
+ * @options dictionary of option {key,values}
+ * @g_error initialized to error info when FALSE is returned.
+ *
+ * Marshal a realm ChangeLoginPolicy method call, call it synchronously,
+ * unmarsh it's reply and return the OUT parameters.
+ *
+ * Returns: return TRUE if successful, FALSE if error with @g_error initialized
+ */
+gboolean
+dbus_change_login_policy_call(DBusConnection *bus, const gchar *dbus_path, const char *login_policy,
+ GVariant *permitted_add, GVariant *permitted_remove,
+ GVariant *options, GError **g_error)
+{
+ DBusError dbus_error;
+ DBusMessage *msg = NULL;
+ DBusMessage* reply = NULL;
+
+ g_return_val_if_fail (bus != NULL, FALSE);
+ g_return_val_if_fail (dbus_path != NULL, FALSE);
+ g_return_val_if_fail (login_policy != NULL, FALSE);
+ g_return_val_if_fail (permitted_add != NULL, FALSE);
+ g_return_val_if_fail (permitted_remove != NULL, FALSE);
+ g_return_val_if_fail (options != NULL, FALSE);
+ g_return_val_if_fail (g_error == NULL || *g_error == NULL, FALSE);
+
+ dbus_error_init(&dbus_error);
+ if (!dbus_change_login_policy_marshal(dbus_path, login_policy,
+ permitted_add, permitted_remove,
+ options, &msg, g_error)) {
+ RETURN_DBUS_ERROR(g_error, dbus_error);
+ }
+
+ if ((reply = dbus_connection_send_with_reply_and_block(bus, msg, -1, &dbus_error)) == NULL) {
+ dbus_message_unref(msg);
+ RETURN_DBUS_ERROR(g_error, dbus_error);
+ }
+ dbus_message_unref(msg);
+
+ if (!dbus_change_login_policy_unmarshal(reply, g_error)) {
+ dbus_message_unref(reply);
+ return FALSE;
+ }
+ dbus_message_unref(reply);
+ return TRUE;
+}
+
+/*----------------------------------------------------------------------------*/
+
+/**
+ * dbus_join_leave_marshal:
+ * @dbus_path The DBus object path of the object supporting the kerberos
+ * membership interface on which the Join/Leave call will be made.
+ * @credentials A GVariant encoding the credentials according the the credential
+ * type. See the Realmd DBus interface for specifics.
+ * @options dictionary of option {key,values}
+ * @msg_return if successful DBus message returned here,
+ * if error then this will be NULL.
+ * @g_error initialized to error info when FALSE is returned.
+ *
+ * Since the Join() & Leave() methods share identical signatures (differing
+ * only in their method name) we use a common routine.
+ *
+ * Returns: return TRUE if successful, @msg_return will point to DBusMessage,
+ * FALSE if error with @g_error initialized. @msg_return will be NULL.
+ */
+static gboolean
+dbus_join_leave_marshal(const char *method, const gchar* dbus_path,
+ GVariant *credentials, GVariant *options,
+ DBusMessage **msg_return, GError **g_error)
+{
+ DBusMessage *msg = NULL;
+ DBusMessageIter iter;
+
+ g_return_val_if_fail (method != NULL, FALSE);
+ g_return_val_if_fail (dbus_path != NULL, FALSE);
+ g_return_val_if_fail (credentials != NULL, FALSE);
+ g_return_val_if_fail (options != NULL, FALSE);
+ g_return_val_if_fail (msg_return != NULL, FALSE);
+ g_return_val_if_fail (g_error == NULL || *g_error == NULL, FALSE);
+
+ *msg_return = NULL;
+
+ if ((msg = dbus_message_new_method_call(REALM_DBUS_BUS_NAME, dbus_path,
+ REALM_DBUS_KERBEROS_MEMBERSHIP_INTERFACE, method)) == NULL) {
+ g_set_error(g_error, RDCP_ERROR, RDCP_ERROR_DBUS,
+ "failed to create dbus method call %s.%s() message, object_path=%s",
+ REALM_DBUS_PROVIDER_INTERFACE, method, REALM_DBUS_SERVICE_PATH);
+ return FALSE;
+ }
+
+ dbus_message_iter_init_append (msg, &iter); /* void return */
+
+ if (!append_g_variant_to_dbus_message(msg, credentials, g_error)) {
+ g_prefix_error(g_error, "unable to append GVariant credentials into %s.%s() message",
+ REALM_DBUS_PROVIDER_INTERFACE, method);
+ dbus_message_unref(msg);
+ return FALSE;
+ }
+
+ if (!append_g_variant_to_dbus_message(msg, options, g_error)) {
+ g_prefix_error(g_error, "unable to append GVariant options dictionary into %s.%s() message",
+ REALM_DBUS_PROVIDER_INTERFACE, method);
+ dbus_message_unref(msg);
+ return FALSE;
+ }
+
+ *msg_return = msg;
+ return TRUE;
+}
+
+/**
+ * dbus_join_leave_unmarshal:
+ * @reply DBus method reply from Join/Leave call
+ * @g_error initialized to error info when FALSE is returned.
+ *
+ * Since the Join() & Leave() methods share identical signatures (differing
+ * only in their method name) we use a common routine.
+ *
+ * Parses the DBus reply message from the Join/Leave call.
+ *
+ * Returns: return TRUE if successful, FALSE if error with @g_error initialized.
+ */
+
+static gboolean
+dbus_join_leave_unmarshal(DBusMessage *reply, GError **g_error)
+{
+
+ g_return_val_if_fail (reply != NULL, FALSE);
+ g_return_val_if_fail (g_error == NULL || *g_error == NULL, FALSE);
+
+ return TRUE;
+}
+
+/**
+ * dbus_join_leave_call:
+ * @dbus_path The DBus object path of the object supporting the kerberos
+ * membership interface on which the Join/Leave call will be made.
+ * @credentials A GVariant encoding the credentials according the the credential
+ * type. See the Realmd DBus interface for specifics.
+ * @options dictionary of option {key,values}
+ * @g_error initialized to error info when FALSE is returned.
+ *
+ * Since the Join() & Leave() methods share identical signatures (differing
+ * only in their method name) we use a common routine.
+ *
+ * Marshal a realm Join/Leave method call, call it synchronously,
+ * unmarsh it's reply and return the OUT parameters.
+ *
+ * Returns: return TRUE if successful, FALSE if error with @g_error initialized
+ */
+gboolean
+dbus_join_leave_call(const char *method, DBusConnection *bus, const gchar *dbus_path,
+ GVariant *credentials, GVariant *options, GError **g_error)
+{
+ DBusError dbus_error;
+ DBusMessage *msg = NULL;
+ DBusMessage* reply = NULL;
+
+ g_return_val_if_fail (method != NULL, FALSE);
+ g_return_val_if_fail (bus != NULL, FALSE);
+ g_return_val_if_fail (dbus_path != NULL, FALSE);
+ g_return_val_if_fail (credentials != NULL, FALSE);
+ g_return_val_if_fail (options != NULL, FALSE);
+ g_return_val_if_fail (g_error == NULL || *g_error == NULL, FALSE);
+
+ dbus_error_init(&dbus_error);
+ if (!dbus_join_leave_marshal(method, dbus_path, credentials, options, &msg, g_error)) {
+ RETURN_DBUS_ERROR(g_error, dbus_error);
+ }
+
+ if ((reply = dbus_connection_send_with_reply_and_block(bus, msg, -1, &dbus_error)) == NULL) {
+ dbus_message_unref(msg);
+ RETURN_DBUS_ERROR(g_error, dbus_error);
+ }
+ dbus_message_unref(msg);
+
+ if (!dbus_join_leave_unmarshal(reply, g_error)) {
+ dbus_message_unref(reply);
+ return FALSE;
+ }
+ dbus_message_unref(reply);
+ return TRUE;
+}
+
+
+/*----------------------------------------------------------------------------*/
+
+/**
+ * dbus_join_call:
+ * @dbus_path The DBus object path of the object supporting the kerberos
+ * membership interface on which the Join call will be made.
+ * @credentials A GVariant encoding the credentials according the the credential
+ * type. See the Realmd DBus interface for specifics.
+ * @options dictionary of option {key,values}
+ * @g_error initialized to error info when FALSE is returned.
+ *
+ * Marshal a realm Join method call, call it synchronously,
+ * unmarsh it's reply and return the OUT parameters.
+ *
+ * Returns: return TRUE if successful, FALSE if error with @g_error initialized
+ */
+gboolean
+dbus_join_call(DBusConnection *bus, const gchar *dbus_path,
+ GVariant *credentials, GVariant *options, GError **g_error)
+{
+
+ return dbus_join_leave_call("Join", bus, dbus_path, credentials,
+ options, g_error);
+
+}
+
+
+/**
+ * dbus_leave_call:
+ * @dbus_path The DBus object path of the object supporting the kerberos
+ * membership interface on which the Leave call will be made.
+ * @credentials A GVariant encoding the credentials according the the credential
+ * type. See the Realmd DBus interface for specifics.
+ * @options dictionary of option {key,values}
+ * @g_error initialized to error info when FALSE is returned.
+ *
+ * Marshal a realm Leave method call, call it synchronously,
+ * unmarsh it's reply and return the OUT parameters.
+ *
+ * Returns: return TRUE if successful, FALSE if error with @g_error initialized
+ */
+gboolean
+dbus_leave_call(DBusConnection *bus, const gchar *dbus_path,
+ GVariant *credentials, GVariant *options, GError **g_error)
+{
+
+ return dbus_join_leave_call("Leave", bus, dbus_path, credentials,
+ options, g_error);
+
+}
+
+
+/*----------------------------------------------------------------------------*/
+/**
+ * get_short_dbus_interface_name
+ * @interface fully qualified DBus interface name
+ *
+ * Given a DBus interface name return a friendly short name
+ * appropriate for users to see. Currently the known short names are:
+ *
+ * * "Kerberos"
+ * * "KerberosMembership"
+ * * "Realm"
+ * * "Provider"
+ * * "Service"
+ *
+ * If the interface is not recognized the portion of the interface
+ * following the last period (".") will be returned. If there is no
+ * period in the interface name then the entire interface string is returned.
+ * If the interface is NULL then "(null)" is returned.
+ *
+ * Returns: pointer to string, must free with g_free()
+ */
+
+char *
+get_short_dbus_interface_name(const char *interface)
+{
+ char *token = NULL;
+
+ if (interface == NULL) {
+ return g_strdup("(null)");
+ }
+
+ if (strcmp(interface, REALM_DBUS_KERBEROS_INTERFACE) == 0) {
+ return g_strdup("Kerberos");
+ }
+ if (strcmp(interface, REALM_DBUS_KERBEROS_MEMBERSHIP_INTERFACE) == 0) {
+ return g_strdup("KerberosMembership");
+ }
+ if (strcmp(interface, REALM_DBUS_REALM_INTERFACE) == 0) {
+ return g_strdup("Realm");
+ }
+ if (strcmp(interface, REALM_DBUS_PROVIDER_INTERFACE) == 0) {
+ return g_strdup("Provider");
+ }
+ if (strcmp(interface, REALM_DBUS_SERVICE_INTERFACE) == 0) {
+ return g_strdup("Service");
+ }
+ /* Return string which begins after last period */
+ if ((token = rindex(interface, '.'))) {
+ token++; /* skip "." char */
+ return g_strdup(token);
+ } else {
+ return g_strdup(interface);
+ }
+
+}
+
+/*----------------------------------------------------------------------------*/
+
+/**
+ * rdcp_dbus_initialize:
+ * @g_error initialized to error info when FALSE is returned.
+ *
+ * Initializes the dbus module.
+ *
+ * - opens a connection the System Bus, exported as #system_bus
+ *
+ * Returns: return TRUE if successful, FALSE if error with @g_error initialized.
+ */
+
+gboolean
+rdcp_dbus_initialize(GError **g_error)
+{
+ DBusError dbus_error = DBUS_ERROR_INIT;
+
+ dbus_error_init(&dbus_error);
+
+ g_return_val_if_fail (g_error == NULL || *g_error == NULL, FALSE);
+
+ if (!system_bus) {
+ if ((system_bus = dbus_bus_get(DBUS_BUS_SYSTEM, &dbus_error)) == NULL) {
+ *g_error = dbus_error_to_gerror(&dbus_error);
+ g_prefix_error(g_error, "could not connect to System DBus");
+ return FALSE;
+ }
+ }
+
+ return TRUE;
+}
diff --git a/src/realmd/rdcp_dbus.h b/src/realmd/rdcp_dbus.h
new file mode 100644
index 0000000..d050071
--- /dev/null
+++ b/src/realmd/rdcp_dbus.h
@@ -0,0 +1,74 @@
+#ifndef __RDCP_DBUS_H__
+#define __RDCP_DBUS_H__
+
+#include <glib.h>
+#include <dbus/dbus.h>
+#include "realm-dbus-constants.h"
+
+#define VERBOSE
+//#define TRACE_VARIANT
+
+/*----------------------------------------------------------------------------*/
+
+extern DBusConnection* system_bus;
+
+/*----------------------------------------------------------------------------*/
+
+gboolean
+get_dbus_string_property(DBusConnection *bus, const char *object_path,
+ const char* interface, const char *property,
+ char **value_return, GError **g_error);
+gboolean
+get_dbus_properties(DBusConnection *bus, const char *object_path,
+ const char* interface, GVariant **properties_return,
+ GError **g_error);
+
+gboolean
+dbus_discover_call(DBusConnection *bus, const char *target, GVariant *options,
+ gint32 *relevance_return, gchar ***paths_return, GError **g_error);
+
+gboolean
+dbus_change_login_policy_call(DBusConnection *bus, const gchar *dbus_path, const char *login_policy,
+ GVariant *permitted_add, GVariant *permitted_remove,
+ GVariant *options, GError **g_error);
+gboolean
+dbus_join_call(DBusConnection *bus, const gchar *dbus_path,
+ GVariant *credentials, GVariant *options, GError **g_error);
+
+gboolean
+dbus_leave_call(DBusConnection *bus, const gchar *dbus_path,
+ GVariant *credentials, GVariant *options, GError **g_error);
+
+char *
+get_short_dbus_interface_name(const char *interface);
+
+gboolean
+rdcp_dbus_initialize(GError **g_error);
+
+#ifdef RDCP_DEBUG
+#define PRINT_DBUS_PROPERTIES(dbus_props, dbus_path, dbus_interface) \
+ print_properties(dbus_props, "%s: Properties for %s, interface=%s", \
+ __FUNCTION__, dbus_path, dbus_interface);
+#else
+#define PRINT_DBUS_PROPERTIES(dbus_properties, dbus_path, dbus_interface)
+#endif
+
+#define GET_DBUS_PROPERIES_OR_EXIT(dbus_props, dbus_path, dbus_interface, status) \
+{ \
+ if (dbus_props != NULL) { \
+ handle_g_error(&g_error, _cb, status, CMPI_RC_ERR_FAILED, \
+ "get_dbus_properties failed, dbus_props was non-NULL (%s:%d)", \
+ __FILE__, __LINE__); \
+ goto exit; \
+ } \
+ if (!get_dbus_properties(system_bus, dbus_path, dbus_interface, &dbus_props, &g_error)) { \
+ handle_g_error(&g_error, _cb, status, CMPI_RC_ERR_FAILED, \
+ "get_dbus_properties failed, path=%s interface=%s", \
+ dbus_path, dbus_interface); \
+ goto exit; \
+ } \
+ \
+ PRINT_DBUS_PROPERTIES(dbus_props, dbus_path, dbus_interface); \
+}
+
+#endif /* __RDCP_DBUS_H__ */
diff --git a/src/realmd/rdcp_error.c b/src/realmd/rdcp_error.c
new file mode 100644
index 0000000..93d7ac3
--- /dev/null
+++ b/src/realmd/rdcp_error.c
@@ -0,0 +1,123 @@
+#include <stdio.h>
+#include "rdcp_util.h"
+#include "rdcp_error.h"
+
+GQuark
+rdcp_error_quark (void)
+{
+ static volatile gsize once = 0;
+ static GQuark quark = 0;
+
+ if (g_once_init_enter(&once)) {
+ quark = g_quark_from_static_string("rdcp-error");
+ g_once_init_leave(&once, 1);
+ }
+
+ return quark;
+}
+
+const char *
+rdcp_error_code_to_string(rdcp_error_codes ec)
+{
+ switch(ec) {
+ case RDCP_ERROR_INTERNAL: return "RDCP_ERROR_INTERNAL";
+ case RDCP_ERROR_INVALID_ARG: return "RDCP_ERROR_INVALID_ARG";
+ case RDCP_ERROR_INVALID_INSTANCE_ID: return "RDCP_ERROR_INVALID_INSTANCE_ID";
+ case RDCP_ERROR_DBUS: return "RDCP_ERROR_DBUS";
+ default: return "unknown error code";
+ }
+}
+
+/*----------------------------------------------------------------------------*/
+
+/**
+ * handle_g_error:
+ * @g_error pointer to non-NULL GError pointer describing problem
+ * @mb CMPI message broker
+ * @st CMPI status result
+ * @rc CMPI return code
+ * @format printf-style format string, may be #NULL if no additional
+ * message is desired.
+ *
+ * Sets @st status to the @rc return code and an optional printf
+ * styles formatted message which is prepended to the error message
+ * contained in the g_error. It frees the g_error.
+ *
+ * Returns: the @st status passed in.
+ */
+CMPIStatus
+handle_g_error(GError **g_error, const CMPIBroker* mb, CMPIStatus* st, CMPIrc rc,
+ const gchar *format, ...)
+{
+ CMPIStatus failsafe_status;
+ GString *message;
+ va_list va;
+
+ CMSetStatus(&failsafe_status, CMPI_RC_ERR_FAILED);
+ g_return_val_if_fail (g_error != NULL && *g_error != NULL, failsafe_status);
+ g_return_val_if_fail (st != NULL, failsafe_status);
+
+ message = g_string_sized_new(DEFAULT_STATUS_MSG_SIZE);
+ g_string_append_printf(message, "%s: ", ORGID);
+
+ if (format) {
+ va_start(va, format);
+ g_string_append_vprintf(message, format, va);
+ va_end(va);
+ g_string_append(message, ": ");
+ }
+
+ g_string_append_printf(message, "(%s(%d)) ",
+ rdcp_error_code_to_string((*g_error)->code),
+ (*g_error)->code);
+ g_string_append(message, (*g_error)->message);
+ g_error_free(*g_error);
+ *g_error = NULL;
+
+ CMSetStatusWithChars(mb, st, rc, message->str);
+ g_string_free(message, TRUE);
+
+ return *st;
+}
+
+
+/**
+ * SetCMPIStatus:
+ * @mb CMPI message broker
+ * @st CMPI status result
+ * @rc CMPI return code
+ * @format printf-style format string, may be #NULL if no additional
+ * message is desired.
+ *
+ * Sets @st status to the @rc return code and an optional printf
+ * style formatted message.
+ *
+ * Returns: the @st status passed in.
+ */
+CMPIStatus
+SetCMPIStatus(const CMPIBroker* mb, CMPIStatus* st, CMPIrc rc,
+ const gchar *format, ...)
+{
+ CMPIStatus failsafe_status;
+ GString *message = NULL;
+ va_list va;
+
+ CMSetStatus(&failsafe_status, CMPI_RC_ERR_FAILED);
+ g_return_val_if_fail (st != NULL, failsafe_status);
+
+ if (format) {
+ message = g_string_sized_new(DEFAULT_STATUS_MSG_SIZE);
+ g_string_append_printf(message, "%s: ", ORGID);
+
+ va_start(va, format);
+ g_string_append_vprintf(message, format, va);
+ va_end(va);
+
+ CMSetStatusWithChars(mb, st, rc, message->str);
+ g_string_free(message, TRUE);
+ } else {
+ CMSetStatus(st, rc);
+ }
+
+ return *st;
+}
diff --git a/src/realmd/rdcp_error.h b/src/realmd/rdcp_error.h
new file mode 100644
index 0000000..ff60d5b
--- /dev/null
+++ b/src/realmd/rdcp_error.h
@@ -0,0 +1,38 @@
+#ifndef __RDCP_ERROR_H__
+#define __RDCP_ERROR_H__
+
+#include <glib.h>
+
+#define RDCP_ERROR (rdcp_error_quark ())
+
+GQuark rdcp_error_quark (void) G_GNUC_CONST;
+
+typedef enum {
+ RDCP_ERROR_INTERNAL = 1,
+ RDCP_ERROR_INVALID_ARG,
+ RDCP_ERROR_INVALID_INSTANCE_ID,
+ RDCP_ERROR_DBUS,
+} rdcp_error_codes;
+
+const char *
+rdcp_error_code_to_string(rdcp_error_codes ec);
+
+#define LMI_REALMD_RESULT_SUCCESS 0
+#define LMI_REALMD_RESULT_FAILED 1
+#define LMI_REALMD_RESULT_NO_SUCH_DOMAIN 2
+#define LMI_REALMD_RESULT_DOMAIN_DOES_NOT_SUPPORT_PROVIDED_CREDENTIALS 3
+#define LMI_REALMD_RESULT_DOMAIN_DOES_NOT_SUPPORT_JOINING 4
+
+#define DEFAULT_STATUS_MSG_SIZE 128
+
+CMPIStatus
+handle_g_error(GError **g_error, const CMPIBroker* cb, CMPIStatus* status, CMPIrc rc,
+ const gchar *format, ...)
+ __attribute__ ((format (printf, 5, 6)));
+
+CMPIStatus
+SetCMPIStatus(const CMPIBroker* mb, CMPIStatus* st, CMPIrc rc,
+ const gchar *format, ...)
+ __attribute__ ((format (printf, 4, 5)));
+
+#endif /* __RDCP_ERROR_H__ */
diff --git a/src/realmd/rdcp_realmdrealm.h b/src/realmd/rdcp_realmdrealm.h
new file mode 100644
index 0000000..491bc73
--- /dev/null
+++ b/src/realmd/rdcp_realmdrealm.h
@@ -0,0 +1,310 @@
+#ifndef __RDCP_REALMDREALM_H__
+#define __RDCP_REALMDREALM_H__
+
+#include <strings.h>
+#include "LMI_RealmdRealm.h"
+#include "LMI_RealmdKerberosRealm.h"
+
+
+KINLINE LMI_RealmdKerberosRealm_SupportedJoinCredentialTypes_Enum
+SupportedJoinCredentialTypes_name_to_enum(const char *name)
+{
+ if (strcasecmp(name, "ccache"))
+ return LMI_RealmdKerberosRealm_SupportedJoinCredentialTypes_ccache;
+ if (strcasecmp(name, "password"))
+ return LMI_RealmdKerberosRealm_SupportedJoinCredentialTypes_password;
+ if (strcasecmp(name, "secrect"))
+ return LMI_RealmdKerberosRealm_SupportedJoinCredentialTypes_secrect;
+ if (strcasecmp(name, "automatic"))
+ return LMI_RealmdKerberosRealm_SupportedJoinCredentialTypes_automatic;
+ return 0;
+}
+
+KINLINE const char *
+SupportedJoinCredentialTypes_enum_to_name(LMI_RealmdKerberosRealm_SupportedJoinCredentialTypes_Enum value)
+{
+ switch(value) {
+ case LMI_RealmdKerberosRealm_SupportedJoinCredentialTypes_ccache:
+ return "ccache";
+ case LMI_RealmdKerberosRealm_SupportedJoinCredentialTypes_password:
+ return "password";
+ case LMI_RealmdKerberosRealm_SupportedJoinCredentialTypes_secrect:
+ return "secrect";
+ case LMI_RealmdKerberosRealm_SupportedJoinCredentialTypes_automatic:
+ return "automatic";
+ default:
+ return NULL;
+ }
+}
+
+KINLINE LMI_RealmdKerberosRealm_SupportedJoinCredentialOwners_Enum
+SupportedJoinCredentialOwners_name_to_enum(const char *name)
+{
+ if (strcasecmp(name, "administrator"))
+ return LMI_RealmdKerberosRealm_SupportedJoinCredentialOwners_administrator;
+ if (strcasecmp(name, "user"))
+ return LMI_RealmdKerberosRealm_SupportedJoinCredentialOwners_user;
+ if (strcasecmp(name, "computer"))
+ return LMI_RealmdKerberosRealm_SupportedJoinCredentialOwners_computer;
+ if (strcasecmp(name, "none"))
+ return LMI_RealmdKerberosRealm_SupportedJoinCredentialOwners_none;
+ return 0;
+}
+
+KINLINE const char *
+SupportedJoinCredentialOwners_enum_to_name(LMI_RealmdKerberosRealm_SupportedJoinCredentialOwners_Enum value)
+{
+ switch(value) {
+ case LMI_RealmdKerberosRealm_SupportedJoinCredentialOwners_administrator:
+ return "administrator";
+ case LMI_RealmdKerberosRealm_SupportedJoinCredentialOwners_user:
+ return "user";
+ case LMI_RealmdKerberosRealm_SupportedJoinCredentialOwners_computer:
+ return "computer";
+ case LMI_RealmdKerberosRealm_SupportedJoinCredentialOwners_none:
+ return "none";
+ default:
+ return NULL;
+ }
+}
+
+KINLINE LMI_RealmdKerberosRealm_SupportedLeaveCredentialTypes_Enum
+SupportedLeaveCredentialTypes_name_to_enum(const char *name)
+{
+ if (strcasecmp(name, "ccache"))
+ return LMI_RealmdKerberosRealm_SupportedLeaveCredentialTypes_ccache;
+ if (strcasecmp(name, "password"))
+ return LMI_RealmdKerberosRealm_SupportedLeaveCredentialTypes_password;
+ if (strcasecmp(name, "secrect"))
+ return LMI_RealmdKerberosRealm_SupportedLeaveCredentialTypes_secrect;
+ if (strcasecmp(name, "automatic"))
+ return LMI_RealmdKerberosRealm_SupportedLeaveCredentialTypes_automatic;
+ return 0;
+}
+
+KINLINE const char *SupportedLeaveCredentialTypes_enum_to_name(LMI_RealmdKerberosRealm_SupportedLeaveCredentialTypes_Enum value)
+{
+ switch(value) {
+ case LMI_RealmdKerberosRealm_SupportedLeaveCredentialTypes_ccache:
+ return "ccache";
+ case LMI_RealmdKerberosRealm_SupportedLeaveCredentialTypes_password:
+ return "password";
+ case LMI_RealmdKerberosRealm_SupportedLeaveCredentialTypes_secrect:
+ return "secrect";
+ case LMI_RealmdKerberosRealm_SupportedLeaveCredentialTypes_automatic:
+ return "automatic";
+ default:
+ return NULL;
+ }
+}
+
+KINLINE LMI_RealmdKerberosRealm_SupportedLeaveCredentialOwners_Enum
+SupportedLeaveCredentialOwners_name_to_enum(const char *name)
+{
+ if (strcasecmp(name, "administrator"))
+ return LMI_RealmdKerberosRealm_SupportedLeaveCredentialOwners_administrator;
+ if (strcasecmp(name, "user"))
+ return LMI_RealmdKerberosRealm_SupportedLeaveCredentialOwners_user;
+ if (strcasecmp(name, "computer"))
+ return LMI_RealmdKerberosRealm_SupportedLeaveCredentialOwners_computer;
+ if (strcasecmp(name, "none"))
+ return LMI_RealmdKerberosRealm_SupportedLeaveCredentialOwners_none;
+ return 0;
+}
+
+KINLINE const char *
+SupportedLeaveCredentialOwners_enum_to_name(LMI_RealmdKerberosRealm_SupportedLeaveCredentialOwners_Enum value)
+{
+ switch(value) {
+ case LMI_RealmdKerberosRealm_SupportedLeaveCredentialOwners_administrator:
+ return "administrator";
+ case LMI_RealmdKerberosRealm_SupportedLeaveCredentialOwners_user:
+ return "user";
+ case LMI_RealmdKerberosRealm_SupportedLeaveCredentialOwners_computer:
+ return "computer";
+ case LMI_RealmdKerberosRealm_SupportedLeaveCredentialOwners_none:
+ return "none";
+ default:
+ return NULL;
+ }
+}
+
+#define LMI_RealmdRealmInitKeys(klass, obj, dbus_path) \
+{ \
+ gchar *instance_id = NULL; \
+ const char *host_name = get_system_name(); \
+ \
+ klass##_Init(obj, cb, ns); \
+ \
+ instance_id = instance_id_from_dbus_path(dbus_path); \
+ klass##_Set_InstanceID(obj, instance_id); \
+ g_free(instance_id); \
+ \
+ klass##_Set_SystemCreationClassName(obj, get_system_creation_class_name()); \
+ klass##_Set_SystemName(obj, host_name); \
+}
+
+KINLINE bool SupportsDBusInterface(GVariant *dbus_props, const char *dbus_interface)
+{
+ bool result = false;
+ GVariantIter *iter = NULL;
+ gchar *value;
+
+ if (g_variant_lookup(dbus_props, "SupportedInterfaces", "as", &iter)) {
+ while (g_variant_iter_next(iter, "&s", &value)) {
+ if (strcmp(value, dbus_interface) == 0) {
+ result = true;
+ break;
+ }
+ }
+ G_VARIANT_ITER_FREE(iter);
+ }
+ return result;
+}
+
+#define LMI_InitFromDBusRealmProps(klass, obj, dbus_props) \
+{ \
+ gchar *value = NULL; \
+ gchar *name = NULL; \
+ gsize n_items; \
+ GVariantIter *iter; \
+ CMPICount i; \
+ \
+ if (g_variant_lookup(dbus_props, "Name", "&s", &value)) { \
+ klass##_Set_RealmName(obj, value); \
+ } \
+ \
+ if (g_variant_lookup(dbus_props, "Configured", "&s", &value)) { \
+ if (strlen(value) == 0) { \
+ klass##_Null_Configured(obj); \
+ } else { \
+ char *interface_name = get_short_dbus_interface_name(value); \
+ klass##_Set_Configured(obj, interface_name); \
+ g_free(interface_name); \
+ } \
+ } \
+ \
+ if (g_variant_lookup(dbus_props, "SupportedInterfaces", "as", &iter)) { \
+ n_items = g_variant_iter_n_children(iter); \
+ klass##_Init_SupportedInterfaces(obj, n_items); \
+ for (i = 0; g_variant_iter_next(iter, "&s", &value); i++) { \
+ char *interface_name = get_short_dbus_interface_name(value); \
+ klass##_Set_SupportedInterfaces(obj, i, interface_name); \
+ g_free(interface_name); \
+ } \
+ G_VARIANT_ITER_FREE(iter); \
+ } \
+ \
+ if (g_variant_lookup(dbus_props, "Details", "a(ss)", &iter)) { \
+ n_items = g_variant_iter_n_children(iter); \
+ klass##_Init_DetailNames(obj, n_items); \
+ klass##_Init_DetailValues(obj, n_items); \
+ for (i = 0; g_variant_iter_next(iter, "(&s&s)", &name, &value); i++) { \
+ klass##_Set_DetailNames(obj, i, name); \
+ klass##_Set_DetailValues(obj, i, value); \
+ } \
+ G_VARIANT_ITER_FREE(iter); \
+ } \
+ \
+ if (g_variant_lookup(dbus_props, "LoginFormats", "as", &iter)) { \
+ n_items = g_variant_iter_n_children(iter); \
+ klass##_Init_LoginFormats(obj, n_items); \
+ for (i = 0; g_variant_iter_next(iter, "&s", &value); i++) { \
+ klass##_Set_LoginFormats(obj, i, value); \
+ } \
+ G_VARIANT_ITER_FREE(iter); \
+ } \
+ \
+ if (g_variant_lookup(dbus_props, "LoginPolicy", "&s", &value)) { \
+ klass##_Set_LoginPolicy(obj, value); \
+ } \
+ \
+ if (g_variant_lookup(dbus_props, "PermittedLogins", "as", &iter)) { \
+ n_items = g_variant_iter_n_children(iter); \
+ klass##_Init_PermittedLogins(obj, n_items); \
+ for (i = 0; g_variant_iter_next(iter, "&s", &value); i++) { \
+ klass##_Set_PermittedLogins(obj, i, value); \
+ } \
+ G_VARIANT_ITER_FREE(iter); \
+ } \
+}
+
+#define LMI_InitFromDBusKerberosRealmProps(klass, obj, dbus_props) \
+{ \
+ gchar *value = NULL; \
+ \
+ if (g_variant_lookup(dbus_props, "RealmName", "&s", &value)) { \
+ klass##_Set_RealmName(obj, value); \
+ } \
+ \
+ if (g_variant_lookup(dbus_props, "DomainName", "&s", &value)) { \
+ klass##_Set_DomainName(obj, value); \
+ } \
+}
+
+#define LMI_InitFromDBusKerberosMembershipProps(klass, obj, dbus_props) \
+{ \
+ gchar *value = NULL; \
+ gchar *type = NULL; \
+ gchar *owner = NULL; \
+ gsize n_items; \
+ GVariantIter *iter; \
+ CMPICount i; \
+ \
+ if (g_variant_lookup(dbus_props, "SuggestedAdministrator", "&s", &value)) { \
+ klass##_Set_SuggestedAdministrator(obj, value); \
+ } \
+ \
+ if (g_variant_lookup(dbus_props, "SupportedJoinCredentials", "a(ss)", &iter)) { \
+ n_items = g_variant_iter_n_children(iter); \
+ klass##_Init_SupportedJoinCredentialTypes(obj, n_items); \
+ klass##_Init_SupportedJoinCredentialOwners(obj, n_items); \
+ for (i = 0; g_variant_iter_next(iter, "(&s&s)", &type, &owner); i++) { \
+ klass##_Set_SupportedJoinCredentialTypes(obj, i, \
+ SupportedJoinCredentialTypes_name_to_enum(type)); \
+ klass##_Set_SupportedJoinCredentialOwners(obj, i, \
+ SupportedJoinCredentialOwners_name_to_enum(owner)); \
+ } \
+ G_VARIANT_ITER_FREE(iter); \
+ } \
+ \
+ if (g_variant_lookup(dbus_props, "SupportedLeaveCredentials", "a(ss)", &iter)) { \
+ n_items = g_variant_iter_n_children(iter); \
+ klass##_Init_SupportedLeaveCredentialTypes(obj, n_items); \
+ klass##_Init_SupportedLeaveCredentialOwners(obj, n_items); \
+ for (i = 0; g_variant_iter_next(iter, "(&s&s)", &type, &owner); i++) { \
+ klass##_Set_SupportedLeaveCredentialTypes(obj, i, \
+ SupportedLeaveCredentialTypes_name_to_enum(type)); \
+ klass##_Set_SupportedLeaveCredentialOwners(obj, i, \
+ SupportedLeaveCredentialOwners_name_to_enum(owner)); \
+ } \
+ G_VARIANT_ITER_FREE(iter); \
+ } \
+}
+
+CMPIStatus LMI_RealmdRealmRef_InitFromDBusPath(
+ LMI_RealmdRealmRef* self,
+ const CMPIBroker* cb,
+ const char* ns,
+ const char* dbus_path);
+
+
+CMPIStatus LMI_RealmdRealm_InitFromDBusPath(
+ LMI_RealmdRealm* self,
+ const CMPIBroker* cb,
+ const char* ns,
+ const char* dbus_path);
+
+CMPIStatus LMI_RealmdKerberosRealmRef_InitFromDBusPath(
+ LMI_RealmdKerberosRealmRef* self,
+ const CMPIBroker* cb,
+ const char* ns,
+ const char* dbus_path);
+
+CMPIStatus LMI_RealmdKerberosRealm_InitFromDBusPath(
+ LMI_RealmdKerberosRealm* self,
+ const CMPIBroker* cb,
+ const char* ns,
+ const char* dbus_path);
+
+#endif /* __RDCP_REALMDREALM_H__ */
diff --git a/src/realmd/rdcp_util.c b/src/realmd/rdcp_util.c
new file mode 100644
index 0000000..73ba17b
--- /dev/null
+++ b/src/realmd/rdcp_util.c
@@ -0,0 +1,311 @@
+#include "rdcp_util.h"
+#include "rdcp_dbus.h"
+
+#define VERBOSE
+
+void
+print_properties (GVariant *properties, gchar *format, ...)
+{
+ va_list args;
+ GVariantClass class;
+ GVariantIter iter;
+ GVariant *value;
+ gchar *key;
+ gchar *value_as_string;
+ gsize n_children, i;
+
+ if (format) {
+ va_start(args, format);
+ vprintf(format, args);
+ va_end(args);
+ printf("\n");
+ }
+
+ g_variant_iter_init (&iter, properties);
+ while (g_variant_iter_loop (&iter, "{sv}", &key, &value)) {
+ class = g_variant_classify(value);
+
+ if (class == G_VARIANT_CLASS_ARRAY) {
+ n_children = g_variant_n_children(value);
+ if (n_children == 0) {
+ printf(" %s: []\n", key);
+
+ } else {
+ GVariant *child;
+
+ printf(" %s: [\n", key);
+ for (i = 0; i < n_children; i++) {
+ child = g_variant_get_child_value(value, i);
+ value_as_string = g_variant_print(child, TRUE);
+ printf(" %s", value_as_string);
+ g_free(value_as_string);
+ G_VARIANT_FREE(child);
+ if (i < n_children-1) {
+ printf("\n");
+ } else {
+ printf("]\n");
+ }
+ }
+ }
+ } else {
+ value_as_string = g_variant_print(value, TRUE);
+ printf(" %s: %s\n", key, value_as_string);
+ g_free(value_as_string);
+ }
+ }
+ printf("\n");
+}
+
+void
+print_paths(gchar **paths, gchar *format, ...) {
+ va_list args;
+ gchar *path, **pp;
+ int i, n_items;
+
+ pp = paths;
+ for (path = *pp++, n_items = 0; path; path = *pp++, n_items++);
+
+ if (format) {
+ va_start(args, format);
+ vprintf(format, args);
+ va_end(args);
+ }
+
+ printf(" [%d paths:]\n", n_items);
+
+ pp = paths;
+ for (path = *pp++, i = 0; path; path = *pp++, i++) {
+ printf(" path[%d]: %s\n", i, path);
+ }
+}
+
+/*----------------------------------------------------------------------------*/
+
+/**
+ * build_g_variant_options_from_KStringA
+ * @keys An array of dictionary keys.
+ * @values An array of dictionary values.
+ * @g_variant_return Pointer to location where GVariant will be returned,
+ * will be NULL if error.
+ * @g_error initialized to error info when FALSE is returned.
+ *
+ * Builds a GVariant dictionay of Realmd options.
+ *
+ * The keys and values in the dict are strings. The keys and values
+ * are passed as independent arrays, each value is paired with it's
+ * key by using the same index into keys and values array. It is an
+ * error if the length of the two arrays are not equal. If the length
+ * of the keys & values array is zero then the dictionary will be
+ * empty.
+ *
+ * If the option is not a string you must initialize the value for it
+ * as a string. The string value will be parsed and converted to the
+ * appropriate type for the option. Boolean values must be either
+ * "TRUE" or "FALSE" (case insensitive).
+ *
+ * Returns: return TRUE if successful, @g_variant_return will be non-NULL.
+ * FALSE if error with @g_error initialized, @g_variant_return will be NULL.
+ */
+gboolean
+build_g_variant_options_from_KStringA(const KStringA *keys, const KStringA *values,
+ GVariant **g_variant_return, GError **g_error)
+{
+ GVariantBuilder builder;
+ GVariant *g_variant;
+ CMPICount i, count;
+
+ g_return_val_if_fail (keys != NULL, FALSE);
+ g_return_val_if_fail (values != NULL, FALSE);
+ g_return_val_if_fail (g_variant_return != NULL, FALSE);
+ g_return_val_if_fail (g_error == NULL || *g_error == NULL, FALSE);
+
+ *g_variant_return = NULL;
+
+ if (keys->count != values->count) {
+ g_set_error(g_error, RDCP_ERROR, RDCP_ERROR_INVALID_ARG,
+ "length of keys array (%d) != length of values array (%d)",
+ keys->count, values->count);
+ return FALSE;
+ }
+
+ count = keys->count;
+
+ g_variant_builder_init(&builder, G_VARIANT_TYPE_VARDICT);
+
+ for (i = 0; i < count; i++) {
+ const char *key;
+ const char *value;
+
+ key = KStringA_Get((KStringA *)keys, i);
+ value = KStringA_Get((KStringA *)values, i);
+
+ if (g_str_equal(key, "assume-packages")) {
+ gboolean g_boolean;
+
+ if (g_ascii_strcasecmp(value, "true") == 0) {
+ g_boolean = TRUE;
+ } else if (g_ascii_strcasecmp(value, "false") == 0) {
+ g_boolean = FALSE;
+ } else {
+ g_set_error(g_error, RDCP_ERROR, RDCP_ERROR_INVALID_ARG,
+ "invalid value for assume-packages option (%s), must be TRUE or FALSE", value);
+ g_variant_builder_clear(&builder);
+ return FALSE;
+ }
+ g_variant_builder_add_parsed (&builder, "{%s, <%b>}", key, g_boolean);
+ } else {
+ g_variant_builder_add_parsed (&builder, "{%s, <%s>}", key, value);
+ }
+ }
+
+ if ((g_variant = g_variant_builder_end(&builder)) == NULL) {
+ g_set_error(g_error, RDCP_ERROR, RDCP_ERROR_DBUS,
+ "unable to build GVariant options array");
+ return FALSE;
+ }
+
+ *g_variant_return = g_variant;
+ return TRUE;
+}
+
+/**
+ * build_g_variant_string_array_from_KStringA:
+ * @values The KStringA source array
+ * @g_variant_return Pointer to location where GVariant will be returned,
+ * will be NULL if error.
+ * @g_error initialized to error info when FALSE is returned.
+ *
+ * Builds a GVariant array of strings "as" from a KStringA.
+ *
+ * Returns: return TRUE if successful, @g_variant_return will be non-NULL.
+ * FALSE if error with @g_error initialized, @g_variant_return will be NULL.
+ */
+gboolean
+build_g_variant_string_array_from_KStringA(const KStringA *values,
+ GVariant **g_variant_return, GError **g_error)
+{
+ GVariantBuilder builder;
+ GVariant *g_variant;
+ CMPICount i, count;
+
+ g_return_val_if_fail (values != NULL, FALSE);
+ g_return_val_if_fail (g_variant_return != NULL, FALSE);
+ g_return_val_if_fail (g_error == NULL || *g_error == NULL, FALSE);
+
+ *g_variant_return = NULL;
+
+ count = values->count;
+
+ g_variant_builder_init(&builder, G_VARIANT_TYPE_STRING_ARRAY);
+
+ for (i = 0; i < count; i++) {
+ const char *value;
+
+ value = KStringA_Get((KStringA *)values, i);
+ g_variant_builder_add (&builder, "s", value);
+ }
+
+ if ((g_variant = g_variant_builder_end(&builder)) == NULL) {
+ g_set_error(g_error, RDCP_ERROR, RDCP_ERROR_DBUS,
+ "unable to build GVariant options array");
+ return FALSE;
+ }
+
+ *g_variant_return = g_variant;
+ return TRUE;
+}
+
+/*----------------------------------------------------------------------------*/
+
+/**
+ * dbus_path_from_instance_id
+ * @instance_id A CIM Class InstanceID with a dbus_path encapsulated inside
+ * @dbus_path_return Pointer to location where DBus object path will be returned,
+ * will be NULL if error occurs. Must be freed with g_free()
+ * @g_error initialized to error info when FALSE is returned.
+ *
+ * CIM class instances bound to a DBus object embed the DBus object
+ * path into their InstanceID. This is one element contributing to the
+ * InstanceID's uniqueness. It also allows us to retrieve the DBus
+ * object path in order to operate on the matching DBus object.
+ *
+ * Returns: return TRUE if successful, FALSE if error with @g_error initialized
+ */
+gboolean
+dbus_path_from_instance_id(const char *instance_id, gchar **dbus_path_return, GError **g_error)
+{
+ gchar *dbus_path = NULL;
+
+ g_return_val_if_fail (instance_id != NULL, FALSE);
+ g_return_val_if_fail (dbus_path_return != NULL, FALSE);
+ g_return_val_if_fail (g_error == NULL || *g_error == NULL, FALSE);
+
+ *dbus_path_return = NULL;
+
+ if ((dbus_path = strchr(instance_id, ':')) != NULL) {
+ dbus_path++; /* skip ':' */
+ *dbus_path_return = g_strdup(dbus_path);
+ return TRUE;
+ } else {
+ g_set_error(g_error, RDCP_ERROR, RDCP_ERROR_INVALID_INSTANCE_ID,
+ "could not locate DBus path in CIM InstanceID = \"%s\"",
+ instance_id);
+ return FALSE;
+ }
+}
+
+/**
+ * instance_id_from_dbus_path
+ * @dbus_path The DBus object path to encode in the CIM InstanceID
+ *
+ * Returns: InstanceID, must be freed with g_free()
+ */
+gchar *
+instance_id_from_dbus_path(const char *dbus_path)
+{
+ GString *g_instance_id = NULL;
+
+ g_instance_id = g_string_new(NULL);
+ g_string_printf(g_instance_id, "%s:%s", ORGID, dbus_path);
+ return g_string_free(g_instance_id, FALSE);
+}
+
+/**
+ * get_data_from_KUint8A
+ *
+ * @ka KUint8 Array containing binary data
+ * @size_return Length of the returned buffer is returned here if non-NULL.
+ *
+ * Binary data passed in a CMPI Uint8 array cannot be accessed as a
+ * pointer to the data. Instead each element of the array must be read
+ * and inserted into a contiguous buffer.
+ *
+ * Returns: data buffer, must be freed with g_free()
+ */
+gchar *
+get_data_from_KUint8A(const KUint8A *ka, gsize *size_return)
+{
+ gsize n_octets;
+ gchar *buffer, *p;
+ CMPICount count, i;
+ KUint8 octet;
+
+ count = ka->count;
+ n_octets = count;
+
+ if ((buffer = g_malloc(n_octets)) == NULL) {
+ if (size_return)
+ *size_return = 0;
+ return NULL;
+ }
+
+ for (i = 0, p = buffer; i < count; i++) {
+ octet = KUint8A_Get(ka, i);
+ *p++ = octet.value;
+ }
+
+ if (size_return)
+ *size_return = n_octets;
+
+ return buffer;
+}
diff --git a/src/realmd/rdcp_util.h b/src/realmd/rdcp_util.h
new file mode 100644
index 0000000..aef255a
--- /dev/null
+++ b/src/realmd/rdcp_util.h
@@ -0,0 +1,116 @@
+#ifndef __RDCP_UTIL_H__
+#define __RDCP_UTIL_H__
+
+#include <stdarg.h>
+#include <stdbool.h>
+
+#include <konkret/konkret.h>
+#include <glib.h>
+
+#include "rdcp_error.h"
+
+#define ORGID "LMI_Realmd"
+#define REALMD_SERVICE_NAME "OpenLMI Realmd Service"
+
+
+#define G_VARIANT_FREE(variant) \
+{ \
+ if (variant) { \
+ g_variant_unref(variant); \
+ variant = NULL; \
+ } \
+}
+
+#define G_VARIANT_ITER_FREE(iter) \
+{ \
+ if (iter) { \
+ g_variant_iter_free(iter); \
+ iter = NULL; \
+ } \
+}
+
+/**
+ * octetstring_parse
+ * @octetstring Pointer to octetstring data
+ * @data_len_return Pointer to uint32 value which receives the number
+ * of octets in the octetstring buffer.
+ *
+ * Given an octetstring, extract it's length and set the pointer to
+ * the data.
+ *
+ * When specified on elements of type array of uint8, the OctetString
+ * qualifier indicates that the entire array represents a single octet
+ * string. The first four array entries shall represent a length
+ * field, and any subsequent entries shall represent the octets in the
+ * octet string. The four uint8 values in the length field shall be
+ * interpreted as a 32-bit unsigned number where the first array entry
+ * is the most significant byte. The number represented by the length
+ * field shall be the number of octets in the octet string plus
+ * four. For example, the empty octet string is represented as { 0x00,
+ * 0x00, 0x00, 0x04 }.
+
+ *
+ */
+
+KINLINE unsigned char *octetstring_parse(unsigned char *octetstring, CMPIUint32 *data_len)
+{
+ unsigned char *data = NULL;
+ *data_len = 0;
+ if (octetstring) {
+ *data_len = (octetstring[0] << 24) |
+ (octetstring[1] << 16) |
+ (octetstring[2] << 8) |
+ (octetstring[3]);
+ *data_len -= 4;
+ data = octetstring + 4;
+ }
+ return data;
+}
+
+#define LMI_InitRealmdServiceKeys(klass, obj, name_space, host_name) \
+{ \
+ klass##_Init(obj, _cb, name_space); \
+ klass##_Set_Name(obj, REALMD_SERVICE_NAME); \
+ klass##_Set_SystemCreationClassName(obj, \
+ get_system_creation_class_name()); \
+ klass##_Set_SystemName(obj, host_name); \
+ klass##_Set_CreationClassName(obj, \
+ LMI_RealmdService_ClassName); \
+ \
+}
+
+
+#define LMI_InitComputerSystemKeys(klass, obj, name_space, host_name) \
+{ \
+ klass##_Init(obj, _cb, name_space); \
+ klass##_Set_Name(obj, host_name); \
+ klass##_Set_CreationClassName(obj, \
+ get_system_creation_class_name()); \
+}
+
+void
+print_properties (GVariant *properties, gchar *format, ...)
+__attribute__ ((format (printf, 2, 3)));
+
+void
+print_paths(gchar **paths, gchar *format, ...)
+__attribute__ ((format (printf, 2, 3)));
+
+gboolean
+build_g_variant_options_from_KStringA(const KStringA *keys, const KStringA *values,
+ GVariant **g_variant_return, GError **g_error);
+
+gboolean
+build_g_variant_string_array_from_KStringA(const KStringA *values,
+ GVariant **g_variant_return, GError **g_error);
+
+gboolean
+dbus_path_from_instance_id(const char *instance_id, gchar **dbus_path_return, GError **g_error);
+
+gchar *
+instance_id_from_dbus_path(const char *dbus_path);
+
+gchar *
+get_data_from_KUint8A(const KUint8A *ka, gsize *size_return);
+
+#endif /* __RDCP_UTIL_H__ */
diff --git a/src/realmd/realm-dbus-constants.h b/src/realmd/realm-dbus-constants.h
new file mode 100644
index 0000000..555c59b
--- /dev/null
+++ b/src/realmd/realm-dbus-constants.h
@@ -0,0 +1,66 @@
+/* realmd -- Realm configuration service
+ *
+ * Copyright 2012 Red Hat Inc
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published
+ * by the Free Software Foundation; either version 2 of the licence or (at
+ * your option) any later version.
+ *
+ * See the included COPYING file for more information.
+ *
+ * Author: Stef Walter <stefw@gnome.org>
+ */
+
+#ifndef __REALM_DBUS_CONSTANTS_H__
+#define __REALM_DBUS_CONSTANTS_H__
+
+#define REALM_DBUS_BUS_NAME "org.freedesktop.realmd"
+#define REALM_DBUS_SERVICE_PATH "/org/freedesktop/realmd"
+
+#define DBUS_PEER_INTERFACE "org.freedesktop.DBus.Peer"
+#define DBUS_PROPERTIES_INTERFACE "org.freedesktop.DBus.Properties"
+#define DBUS_INTROSPECTABLE_INTERFACE "org.freedesktop.DBus.Introspectable"
+
+#define REALM_DBUS_PROVIDER_INTERFACE "org.freedesktop.realmd.Provider"
+#define REALM_DBUS_REALM_INTERFACE "org.freedesktop.realmd.Realm"
+#define REALM_DBUS_KERBEROS_INTERFACE "org.freedesktop.realmd.Kerberos"
+#define REALM_DBUS_KERBEROS_MEMBERSHIP_INTERFACE "org.freedesktop.realmd.KerberosMembership"
+#define REALM_DBUS_SERVICE_INTERFACE "org.freedesktop.realmd.Service"
+
+#define REALM_DBUS_DIAGNOSTICS_SIGNAL "Diagnostics"
+
+#define REALM_DBUS_ERROR_INTERNAL "org.freedesktop.realmd.Error.Internal"
+#define REALM_DBUS_ERROR_FAILED "org.freedesktop.realmd.Error.Failed"
+#define REALM_DBUS_ERROR_BUSY "org.freedesktop.realmd.Error.Busy"
+#define REALM_DBUS_ERROR_NOT_AUTHORIZED "org.freedesktop.realmd.Error.NotAuthorized"
+#define REALM_DBUS_ERROR_CANCELLED "org.freedesktop.realmd.Error.Cancelled"
+#define REALM_DBUS_ERROR_ALREADY_CONFIGURED "org.freedesktop.realmd.Error.AlreadyConfigured"
+#define REALM_DBUS_ERROR_NOT_CONFIGURED "org.freedesktop.realmd.Error.NotConfigured"
+#define REALM_DBUS_ERROR_AUTH_FAILED "org.freedesktop.realmd.Error.AuthenticationFailed"
+
+#define REALM_DBUS_DISCOVERY_DOMAIN "domain"
+#define REALM_DBUS_DISCOVERY_KDCS "kerberos-kdcs"
+#define REALM_DBUS_DISCOVERY_REALM "kerberos-realm"
+
+#define REALM_DBUS_NAME_CHARS "abcdefghijklnmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_"
+
+#define REALM_DBUS_LOGIN_POLICY_ANY "allow-any-login"
+#define REALM_DBUS_LOGIN_POLICY_PERMITTED "allow-permitted-logins"
+#define REALM_DBUS_LOGIN_POLICY_DENY "deny-any-login"
+
+#define REALM_DBUS_OPTION_OPERATION "operation"
+#define REALM_DBUS_OPTION_COMPUTER_OU "computer-ou"
+#define REALM_DBUS_OPTION_SERVER_SOFTWARE "server-software"
+#define REALM_DBUS_OPTION_CLIENT_SOFTWARE "client-software"
+#define REALM_DBUS_OPTION_MEMBERSHIP_SOFTWARE "membership-software"
+#define REALM_DBUS_OPTION_ASSUME_PACKAGES "assume-packages"
+
+#define REALM_DBUS_IDENTIFIER_ACTIVE_DIRECTORY "active-directory"
+#define REALM_DBUS_IDENTIFIER_WINBIND "winbind"
+#define REALM_DBUS_IDENTIFIER_FREEIPA "freeipa"
+#define REALM_DBUS_IDENTIFIER_SSSD "sssd"
+#define REALM_DBUS_IDENTIFIER_SAMBA "samba"
+#define REALM_DBUS_IDENTIFIER_ADCLI "adcli"
+
+#endif /* __REALM_DBUS_CONSTANTS_H__ */
diff --git a/src/software/openlmi/software/LMI_SoftwareInstallationJob.py b/src/software/openlmi/software/LMI_SoftwareInstallationJob.py
index ba0867f..a5fc269 100644
--- a/src/software/openlmi/software/LMI_SoftwareInstallationJob.py
+++ b/src/software/openlmi/software/LMI_SoftwareInstallationJob.py
@@ -375,6 +375,6 @@ class LMI_SoftwareInstallationJob(CIMProvider2):
error = InstallationJob.job2error(job)
out_params = []
if error is not None:
- param = pywbem.CIMParameter('Errors', type='instance', value=error)
+ param = pywbem.CIMParameter('Error', type='instance', value=error)
out_params.append(param)
return (self.values.GetErrors.Success, out_params)
diff --git a/src/software/openlmi/software/cimom_entry.py b/src/software/openlmi/software/cimom_entry.py
index ad944c8..70df071 100644
--- a/src/software/openlmi/software/cimom_entry.py
+++ b/src/software/openlmi/software/cimom_entry.py
@@ -26,6 +26,7 @@ Entry module for OpenLMI Software providers.
from multiprocessing import Queue
from openlmi.common import cmpi_logging
+from openlmi.common import JobManager
from openlmi.common.IndicationManager import IndicationManager
from openlmi.software.core import InstallationJob
from openlmi.software.LMI_SoftwareIdentity import LMI_SoftwareIdentity
@@ -113,26 +114,84 @@ def get_providers(env):
# daemon. That means it does not have to be cleaned up.
im = IndicationManager.get_instance(
env, "Software", "root/cimv2", queue=Queue())
- jobmanager.register_filters(im)
+ JobManager.register_filters("LMI_SoftwareInstallationJob", im)
return providers
-def authorize_filter(env, fltr, ns, classes, owner):
+def authorize_filter(env, fltr, class_name, op, owner):
+ """
+ CIMOM callback.
+
+ It asks us to verify whether this filter is allowed.
+
+ :param fltr: (``String``) Contains the filter that must be authorized.
+ :param class_name: (``String``) Contains the class name extracted
+ from the filter FROM clause.
+ :param op: The name of the class for which monitoring is required.
+ Only the namespace part is set if className is a process indication.
+ :param owner The owner argument is the destination owner.
+ """
IndicationManager.get_instance().authorize_filter(
- env, fltr, ns, classes, owner)
+ env, fltr, class_name, op, owner)
-def activate_filter (env, fltr, ns, classes, first_activation):
+def activate_filter(env, fltr, class_name, class_path, first_activation):
+ """
+ CIMOM callback.
+
+ It ask us to begin monitoring a resource. The function shall begin
+ monitoring the resource according to the filter express only.
+
+ :param fltr: (``String``) The filter argument contains the filter
+ specification for this subscription to become active.
+ :param class_name: (``String``) The class name extracted from the filter
+ FROM clause.
+ :param class_path: (``CIMInstanceName``) The name of the class for which
+ monitoring is required. Only the namespace part is set if eventType
+ is a process indication.
+ :param first_activation: (``bool``) Set to true if this is the first
+ filter for className.
+ """
IndicationManager.get_instance().activate_filter(
- env, fltr, ns, classes, first_activation)
+ env, fltr, class_name, class_path, first_activation)
+
+def deactivate_filter(env, fltr, class_name, class_path, last_activation):
+ """
+ CIMOM callback.
-def deactivate_filter(env, fltr, ns, classes, last_activation):
+ Informs us that monitoring using this filter should stop.
+
+ :param fltr: (``String``) The filter argument contains the filter
+ specification for this subscription to become active.
+ :param class_name: (``String``) The class name extracted from the filter
+ FROM clause.
+ :param class_path: (``CIMInstanceName``) class_path The name of the class
+ for which monitoring is required. Only the namespace part is set
+ if className is a process indication.
+ :last_activation: (``bool``) Set to true if this is the last filter for
+ className.
+ """
IndicationManager.get_instance().deactivate_filter(
- env, fltr, ns, classes, last_activation)
+ env, fltr, class_name, class_path, last_activation)
def enable_indications(env):
+ """
+ CIMOM callback.
+
+ Tells us that indications can now be generated. The MB is now prepared
+ to process indications. The function is normally called by the MB after
+ having done its intialization and processing of persistent subscription
+ requests.
+ """
IndicationManager.get_instance().enable_indications(env)
def disable_indications(env):
+ """
+ CIMOM callback.
+
+ Tells us that we should stop generating indications. MB will not accept any
+ indications until enabled again. The function is normally called when the
+ MB is shutting down indication services either temporarily or permanently.
+ """
IndicationManager.get_instance().disable_indications(env)
def can_unload(_env):
diff --git a/src/software/openlmi/software/yumdb/jobmanager.py b/src/software/openlmi/software/yumdb/jobmanager.py
index 7283d05..977b38f 100644
--- a/src/software/openlmi/software/yumdb/jobmanager.py
+++ b/src/software/openlmi/software/yumdb/jobmanager.py
@@ -39,8 +39,8 @@ import threading
import time
import traceback
-from openlmi.common import cmpi_logging
from openlmi.common.IndicationManager import IndicationManager
+from openlmi.common.JobManager import JobManager as JM
from openlmi.software.yumdb import errors, jobs
from openlmi.software.yumdb.util import trace_function
@@ -56,69 +56,6 @@ MINIMUM_TIME_BEFORE_REMOVAL = 10
# replacement for cmpi_logging.logger
LOG = None
-IND_JOB_PERCENT_UPDATED = "PercentUpdated"
-IND_JOB_SUCCEEDED = "Succeeded"
-IND_JOB_FAILED = "Failed"
-IND_JOB_CHANGED = "Changed"
-IND_JOB_CREATED = "Created"
-
-IND_FILTERS = {
- IND_JOB_PERCENT_UPDATED: {
- "Query" : "SELECT * FROM CIM_InstModification WHERE "
- "SourceInstance ISA %(classname)s AND "
- "SourceInstance.CIM_ConcreteJob::PercentComplete <> "
- "PreviousInstance.CIM_ConcreteJob::PercentComplete",
- "Description" : "Modification of Percentage Complete for a "
- "Concrete Job.",
- },
- IND_JOB_SUCCEEDED: {
- "Query" : "SELECT * FROM CIM_InstModification WHERE "
- "SourceInstance ISA %(classname)s AND "
- "SourceInstance ISA LMI_SoftwareInstallationJob AND "
- "SourceInstance.CIM_ConcreteJob::JobState = 17",
- # symbolic constants not supported by sfcb
- #"CIM_ConcreteJob.JobState#'Completed'"
- "Description": "Modification of Operational Status for a "
- "Concrete Job to 'Complete' and 'OK'.",
- },
- IND_JOB_FAILED: {
- "Query" : "SELECT * FROM CIM_InstModification WHERE "
- "SourceInstance ISA %(classname)s AND "
- "SourceInstance.CIM_ConcreteJob::JobState = 10",
- # symbolic constants not supported by sfcb
- #"CIM_ConcreteJob.JobState#'Exception'",
- "Description": "Modification of Operational Status for a "
- "Concrete Job to 'Complete' and 'Error'.",
- },
- IND_JOB_CHANGED: {
- "Query" : "SELECT * FROM CIM_InstModification WHERE "
- "SourceInstance ISA %(classname)s AND "
- "SourceInstance.CIM_ConcreteJob::JobState <> "
- "PreviousInstance.CIM_ConcreteJob::JobState",
- "Description": "Modification of Job State for a ConcreteJob.",
- },
- IND_JOB_CREATED: {
- "Query" : "SELECT * FROM CIM_InstCreation WHERE "
- "SourceInstance ISA %(classname)s",
- "Description": "Creation of a ConcreteJob.",
- },
-}
-
-@cmpi_logging.trace_function
-def register_filters(indication_manager):
- """
- This function registers static indication filters at IndicationManager.
- It should be called upon provider's initialization.
- """
- to_register = {}
- for fltr_id, fltr in IND_FILTERS.items():
- if not indication_manager.is_registered(JOB_CLASSNAME, fltr_id):
- fltr["Query"] = fltr["Query"] % {"classname" : JOB_CLASSNAME }
- to_register[fltr_id] = fltr
- if to_register:
- indication_manager.add_filters(JOB_CLASSNAME,
- to_register, ensure_installed=False)
-
# *****************************************************************************
# Decorators
# *****************************************************************************
@@ -183,7 +120,7 @@ class JobIndicationSender(object):
"""
def __init__(self, indication_manager, job,
- indications=IND_JOB_CHANGED, new=None):
+ indications=JM.IND_JOB_CHANGED, new=None):
"""
:param job (``YumJob``) Is job instance, which will be immediately
snapshoted as old instance and later as a new one.
@@ -258,11 +195,11 @@ class JobIndicationSender(object):
"can not send any indication without id")
if make_snapshot:
self.snapshot()
- if ( IND_JOB_CHANGED in self._indications
+ if ( JM.IND_JOB_CHANGED in self._indications
and self._new_instance is None):
raise errors.IndicationError("no snapshot made for modified job")
for fltr_id in self._indications:
- if fltr_id == IND_JOB_CREATED:
+ if fltr_id == JM.IND_JOB_CREATED:
LOG.debug("sending instance creation indication for job %s",
self._job)
self._indication_manager.send_instcreation(
@@ -368,7 +305,7 @@ class JobManager(threading.Thread):
LOG.debug('job %s enqued for YumWorker to handle', job)
heapq.heappush(self._job_queue, job)
if getattr(job, 'async', False) is True:
- ind = self._prepare_indication_for(job, IND_JOB_CREATED)
+ ind = self._prepare_indication_for(job, JM.IND_JOB_CREATED)
self._async_jobs[job.jobid] = job
ind.send()
self._job_enqueued.notify()
@@ -557,7 +494,7 @@ class JobManager(threading.Thread):
'can not finish not started job "%s"' % job)
if getattr(job, 'async', False):
ind = self._prepare_indication_for(job,
- (IND_JOB_CHANGED, IND_JOB_PERCENT_UPDATED))
+ (JM.IND_JOB_CHANGED, JM.IND_JOB_PERCENT_UPDATED))
job.finish(result, result_data)
if getattr(job, 'async', False):
if job.delete_on_completion:
@@ -566,9 +503,9 @@ class JobManager(threading.Thread):
self._schedule_event(schedule_at, job.jobid,
self.ACTION_REMOVE)
if result == job.RESULT_SUCCESS:
- ind.add_indication_ids(IND_JOB_SUCCEEDED)
+ ind.add_indication_ids(JM.IND_JOB_SUCCEEDED)
elif result == job.RESULT_ERROR:
- ind.add_indication_ids(IND_JOB_FAILED)
+ ind.add_indication_ids(JM.IND_JOB_FAILED)
ind.send(True)
else:
LOG.debug("sending reply for %s: (%s, %s)", job,
@@ -598,7 +535,7 @@ class JobManager(threading.Thread):
if job is not None:
if getattr(job, "async", False):
ind = self._prepare_indication_for(job,
- (IND_JOB_CHANGED, IND_JOB_PERCENT_UPDATED))
+ (JM.IND_JOB_CHANGED, JM.IND_JOB_PERCENT_UPDATED))
job.start()
ind.send(True)
else: