Using the library
This chapter shows how a program reads the data through the libopendmi
library. Every function is described in full in the part of this manual about
the C library.
1. Finding the library
The installed library is found with CMake, by the package it installs:
find_package(OpenDMI 0.7 REQUIRED)
Binary compatibility is not kept between the minor versions before 1.0, so the package is compatible with the same minor version only.
The library is found with pkg-config too, by the opendmi package.
A library installed outside the directories CMake and pkg-config look in,
such as the default /opt/opendmi prefix of build.sh, is found through the
prefix given to CMake, and the directory of the package given to pkg-config:
$ cmake -B build -DCMAKE_PREFIX_PATH=/opt/opendmi
$ export PKG_CONFIG_PATH=/opt/opendmi/lib/pkgconfig
The directory of the libraries is lib in the examples of this chapter, and
is lib64 on some systems.
2. Linking
The library is built both as a shared library and as a static one, and a program is linked with either.
2.1. Linking with the shared library
The shared library is what a program is linked with by default:
target_link_libraries(example PRIVATE OpenDMI::opendmi)
OpenDMI::opendmi and OpenDMI::shared are the same library. Without CMake,
the flags come from pkg-config:
$ cc example.c $(pkg-config --cflags --libs opendmi)
The library is looked up again when the program starts. A library installed outside the directories the system looks in is found by the path the program is linked with:
$ cc example.c $(pkg-config --cflags --libs opendmi) \
-Wl,-rpath,/opt/opendmi/lib
CMake gives that path to the programs in its build tree by itself, and to the
installed ones through the INSTALL_RPATH property. The shared library is named
after the minor version before 1.0, e.g. libopendmi.so.0.7 on Linux
and libopendmi.0.7.dylib on macOS, so that a program is not started
with a library it is incompatible with.
2.2. Linking with the static library
The static library makes a program which needs no OpenDMI installed to run:
target_link_libraries(example PRIVATE OpenDMI::static)
The target brings along everything the static library needs, which the other ways of linking give by hand.
The static library is libopendmi.a, and opendmi-static.lib with MSVC. It is
linked by its path, because -lopendmi is resolved to the shared library
whenever one is installed next to it. The libraries it depends on are linked
along with it: none on Linux and the BSDs, the CoreFoundation and IOKit
frameworks on macOS, and ntdll on Windows, along with ICU4C when the library
is built with the translations. pkg-config --static --libs opendmi lists them,
after the -lopendmi the path stands for:
$ cc $(pkg-config --cflags opendmi) example.c /opt/opendmi/lib/libopendmi.a
$ cc $(pkg-config --cflags opendmi) example.c /opt/opendmi/lib/libopendmi.a \
-framework CoreFoundation -framework IOKit
The first command links the program on Linux, and the second on macOS.
On Windows, the program linked with the static library defines DMI_STATIC,
so that the functions of the library are not taken for the ones a DLL imports.
OpenDMI::static defines it by itself, and pkg-config --static --cflags
opendmi gives it there too.
3. Reading the data
A program creates a context, which holds the data and everything decoded from it, opens the data of the system, and looks up the structures it needs in the registry of the context. Decoded structures are plain C types, one for each structure type:
#include <stdio.h>
#include <opendmi/context.h>
#include <opendmi/registry.h>
#include <opendmi/entity.h>
#include <opendmi/entity/system.h>
int main(void)
{
dmi_context_t *context = dmi_create(DMI_CONTEXT_FLAG_LINK);
if (context == nullptr)
return 1;
if (dmi_open(context, nullptr)) {
dmi_registry_t *registry = dmi_get_registry(context);
dmi_entity_t *entity = dmi_registry_lookup_first(registry,
DMI_TYPE(system),
false);
dmi_system_t *system = dmi_entity_info(entity, DMI_TYPE(system));
if (system != nullptr)
printf("%s %s\n", system->vendor, system->product);
}
dmi_destroy(context);
return 0;
}
dmi_open reads the data of the system, and requires the same privileges
the command line tool does. dmi_load reads a dump file instead, which is
how a program is tried out on the data of another machine:
if (dmi_load(context, "smbios.bin")) {
...
}
4. Handling errors
Functions which fail return false or nullptr, and leave the reason in the
error queue of the context, where dmi_error_get_last takes it from:
if (!dmi_open(context, nullptr)) {
const dmi_error_t *error = dmi_error_get_last(context);
if (error != nullptr)
fprintf(stderr, "%s\n", dmi_error_message(error->reason));
}
The last error is the one the failed call has raised itself. The errors raised
on the way to it, such as the file which could not be opened, precede it in
the queue, and are taken from its beginning with dmi_error_get_first.