Creating and Using Components
更新时间: 2025/10/11
在Gitcode上查看源码

In openUBMC, all components share a unified code structure. The BMC Studio development tool provides the bingo new command to quickly create a component that follows the standard openUBMC format.

Command Description

You can view the description of each subcommand in BMC Studio by using the -h or --help flag:

shell
bingo new -h

Result:

shell
Creat a component.

optional arguments:
  -h, --help            show this help message and exit
  --n NAME, --name NAME  Specify the component name.
  --t TYPE, --type TYPE  Specify the component type. Options: application.
                        Default: application.
  -l LANGUAGE, --language LANGUAGE
                        Specify the component programming language. Options: lua.
                        Default: lua.

By providing the necessary parameters, you can quickly create a component repository with basic code. This repository includes features such as service startup and package building. Once you generate the component repository, you only need to focus on writing the service code. The following is an example of creating a component named my_app:

shell
bingo new -n my_app

After you run this command, follow the prompts in the terminal to enter the corresponding values. The tool generates a component repository named my_app in the current directory. Then, you can navigate to the component repository directory and use the bingo build command to build the Conan package for my_app. Run the following commands:

shell
cd my_app
git init
bingo build

After the command is executed, the console outputs a build artifact tag in the package_name/version@user/channel format, such as my_app/0.0.1@openubmc.dev/dev. The name and version properties in the mds/service.json file define the package name and version, respectively. You should increment the version property value with each modification and submission. Record the modification information for the new version in the CHANGELOG.md file. In openUBMC, the default user is associated with the channel. There are three build channels:

  1. dev: The developer channel and the default channel for local builds.
  2. stable: Used for release packages.

You can specify a channel in the build command by passing the --stage parameter. For example, run the following command to build the stable package for the my_app component:

shell
bingo build --stage stable

If the build channel is dev, the default user is openubmc.dev. If the channel is stable, the default user is openubmc. You can also specify a user by using the --user parameter, in which case the user does not change based on the channel. For more information about component build commands, refer to Component Build and Release.

After the build is complete, you can run conan cache path <component_name> to view the storage path of the build products.

Component Repository Architecture

The following section introduces the file architecture and provides a brief description of the automatically generated my_app component. You can write your own component features within this repository.

shell
├── .clang-format
├── .gitignore
├── CHANGELOG.md                                     -- Modification logs
├── README.md                                        -- Component introduction file
├── CMakeLists.txt                                   -- CMake build script, typically covering compilation and packaging
├── Makefile
├── conanfile.py                                     -- Conan script that inherits from conanbase.py. You can override methods from conanbase.
├── permissions.ini                                  -- Permission configuration file
├── config.cfg                                       -- Component startup configuration file
├── mds
   ├── model.json                                   -- Configuration for properties, methods, and signals for resource collaboration interfaces
   └── service.json                                 -- Configuration for component repository version information, required dependencies, and paths
├── src                                              -- Source code folder
   ├── lualib
   └── my_app_app.lua                          -- Component implementation file
   └── service
       └── main.lua                                 -- Skynet service definition, which is the entry service for the component
├── test
   ├── integration                                  -- Directory for IT files
   ├── test_my_app.conf
   └── test_my_app.lua                         -- Entry file for IT
   └── unit                                         -- Directory for UT files
       └── test.lua                                 -- Entry file for UT
└── user_conf                                        -- User configurations. The build system installs files in the rootfs directory to their corresponding locations in the root file system.
    └── rootfs
        └── etc
            └── systemd
                └── system
                    ├── multi-user.target.wants
   └── my_app.service          -- Systemd service startup configuration file
                    └── my_app.service

In this component architecture, different files and directories have specific responsibilities:

  • The CHANGELOG.md and README.md files record modification logs and component introductions, respectively. They are not involved in component execution.

  • CMakeLists.txt and conanfile.py are compilation and build scripts and are independent of the component service code. For details, see Component Build and Release.

  • Makefile is a portable build system file that the CMake build system generates through CMakeLists.txt.

  • permissions.ini: Permission configuration file.

  • user_conf: User configuration directory. The my_app.service file is the component startup configuration file. It defines how Skynet reads the /opt/bmc/apps/bmc_time/config.cfg configuration file to start the service.

  • config.cfg: The startup configuration file for the Skynet service. It defines my_app/service/main.lua as the service entry file.

  • mds: Defines the model description for the component. This includes the resource collaboration interfaces that the component implements, persistent descriptions of component data, and component dependencies. It stores component model definition source files in a unified format, with different JSON files for different models. The model.json file describes the component data model and configures the properties, methods, and signals of the resource collaboration interface. The service.json file describes the component service model and configures the component repository version information, dependencies, and paths. The bingo gen command of BMC Studio uses these model and interface definitions to generate code automatically. For details, see MDS.

  • The src directory stores source code files. The src/lualib/my_app_app.lua file defines the component implementation code, while the src/service/main.lua file is the entry function for the component app that starts the component service. You can write your service code in the src directory. For details, see Component Development and Coding.

  • The test directory stores DT code. In openUBMC, DT ensures the quality of component code development. Therefore, you must complete developer tests while developing service code. DT uses integration tests and unit tests to verify component interfaces and functional functions. The corresponding code is stored in the test/integration and test/unit directories. For details, see Independent Component Testing.

Service components in openUBMC use the module description source (MDS) file to configure resource collaboration interfaces and dependencies. Code related to these model definitions does not appear in the preceding architecture. You can run the following commands to obtain the full code for the component:

shell
cd <component_repository_directory>
bingo gen

These commands automatically generate the gen directory in the component repository directory. The gen directory contains code for calling and subscribing to resource collaboration interfaces configured in the model description file, along with other model-related code. For details, see Automatic Code Generation.

Component Startup Process

openUBMC uses systemd, the service management tool of Linux, to manage services and resources. openUBMC uses systemd, the service management tool of Linux, to manage services and resources. In the systemd daemon, each component is treated as a unit. A unit file named <component_name>.service defines the description, attributes, and commands for the service. This file is stored in the user_conf/rootfs/etc/systemd/system/ directory of the component repository.

For example, the my_app.service unit file for the new my_app component defines the default configuration information:

ini
[Unit]
Description=my_app service
After=dbus.service
Requires=dbus.service

[Service]
User=root
Restart=always
RestartSec=1
StartLimitInterval=0
EnvironmentFile=/dev/shm/dbus/.dbus
Environment="ROOT_DIR="
Environment="PROJECT_DIR="
WorkingDirectory=/opt/bmc/apps/my_app
ExecStart=/opt/bmc/skynet/skynet /opt/bmc/apps/my_app/config.cfg

[Install]
WantedBy=multi-user.target

This file consists of three configuration sections:

  • Unit section: configures the description and dependencies of the service, and specifies how it starts with the system. This service starts after the dbus service.
  • Service section: defines specific management and operation methods for the service. By default, the service runs in the /opt/bmc/apps/my_app working directory. It starts by reading the /opt/bmc/apps/my_app/config.cfg configuration file through Skynet.
  • Install section: specifies multi-user.target as the default target for systemd. This directory stores symbolic links to systemd configurations in the parent directory to support automatic startup of services.

According to the Unit file definition of the new my_app component, the my_app service starts by launching Skynet. The default Skynet startup configuration file /opt/bmc/apps/my_app/config.cfg is as follows:

lua
include "/opt/bmc/libmc/config.cfg"

config:set_root("/")
config:set_start("my_app/service/main")
config:include_app("my_app")
config:done()

This configuration file is the config.cfg file in the component repository directory, which is essentially a Lua script. openUBMC defines basic configuration modules in opt/bmc/libmc/config.cfg. By importing this module and calling its methods, the Skynet configuration file defines the root directory for file execution, the Skynet startup script, and the Lua directories accessible within the Skynet process. The Skynet startup script is defined as the my_app/service/main.lua file:

lua
require 'skynet.manager'
local skynet = require 'skynet'
local logging = require 'mc.logging'
local my_app_app = require 'my_app_app'

local CMD = {}

function CMD.exit()
    logging:notice('my_app service exit')
end

skynet.start(function()
    skynet.uniqueservice('sd_bus')
    skynet.register('my_app')
    local ok, err = pcall(my_app_app.new)
    if not ok then
        logging:error('my_app start failed, err: %s', err)
    end
    skynet.dispatch('lua', function(_, _, cmd, ...)
        local f = assert(CMD[cmd])
        skynet.ret(skynet.pack(f(...)))
    end)
end)

Each component repository uses the src/lualib directory to store Lua code. The xxx_app.lua file (where xxx is the component name) serves as the entry point for the component. In the preceding Skynet startup script, importing the my_app_app.lua entry file registers the my_app component in Skynet and creates a my_app object. (The my_app_app class defined in my_app_app.lua is the my_app class.) Note: When you instantiate the class, the constructor ctor() and the initialization function init() run automatically.

The my_app_app.lua file is defined as follows:

lua
local class = require 'mc.class'
local service = require 'my_app.service'

local my_app = class(service)

function my_app:ctor()

end

function my_app:init()
    self.super.init(self)
end

return my_app

The my_app class inherits from the my_app.service class of the component. The my_app.service class is automatically generated based on the MDS model definition files (model.json and service.json) and the mdb_interface component repository. It serves as the entry point for the entire service and defines component initialization, registration of service resource collaboration interface methods, and object creation.

Custom Component Startup

Configuring Custom Components

In addition to starting common components, you may need to start custom components. The configuration for these scenarios differs slightly. Taking the configuration of the custom custom_app component as an example, you must configure it in the component repository. The custom_app.service configuration is as follows:

ini
[Unit]
Description=custom_app service
After=framework.service
Requires=dbus.service framework.service

[Service]
StandardOutput=tty
TTYPath=/dev/ttyS0
User=root
Restart=always
RestartSec=1
StartLimitInterval=0
EnvironmentFile=/dev/shm/dbus/.dbus
Environment="ROOT_DIR="
Environment="PROJECT_DIR="
WorkingDirectory=/opt/bmc/extend/custom_app/apps/custom_app
ExecStartPre=/bin/bash -c "uptime=`/bin/cat /proc/uptime |awk -F '.' '&#123;print $1&#125;'`; if [ $uptime -lt 100 ]; then /bin/sleep 30; fi"
ExecStart=/opt/bmc/skynet/skynet /opt/bmc/extend/custom_app/apps/custom_app/config.cfg
ExecStartPost=/bin/bash -c 'echo 0x21 > /proc/$MAINPID/coredump_filter'
KillMode=process
MemoryHigh=400M
MemoryMax=512M
LimitCORE=100000000
LimitSTACK=100000000

[Install]

Based on the Unit file definition of the custom custom_app component, the custom_app service starts by launching Skynet. The default Skynet startup configuration file /opt/bmc/extend/custom_app/apps/custom_app/config.cfg is as follows:

lua
include "/opt/bmc/libmc/config.cfg"

app_root = "/opt/bmc/extend/custom_app/apps/"
config:set_root("/")
config:set_start("custom_app/service/main")
config:include_app("persistence")
config:include_app("maca")
config:include_app("custom_app")
config:done()

MODULE_NAME = "custom_app"  -- Service name

Similar to common components, custom components require a startup script. The following is an example of the custom_app/service/main.lua file:

lua
require 'skynet.manager'
local skynet = require 'skynet'
local logging = require 'mc.logging'
local custom_app = require 'custom_app'

local CMD = {}

function CMD.exit()
    logging:notice('custom_app service exit')
end

skynet.start(function()
    skynet.uniqueservice('sd_bus')
    skynet.register('custom_app')
    local ok, err = pcall(custom_app.new)
    if not ok then
        logging:error('custom_app start failed, err: %s', err)
    end
    skynet.dispatch('lua', function(_, _, cmd, ...)
        local f = assert(CMD[cmd])
        skynet.ret(skynet.pack(f(...)))
    end)
end)

The custom_app.lua file is defined as follows:

lua
local class = require 'mc.class'
local service = require 'custom_app.service'

local custom_app = class(service)

function custom_app:ctor()

end

function custom_app:init()
    self.super.init(self)
end

return custom_app

After configuring the component repository code, you must also configure the manifest to ensure that the custom component is included during the full package build. Add the custom component dependency to the manifest file build/product/BMC/openUBMC/manifest.yml:

yml
dependencies:
  - conan: custom_app

Configure the component version in build/subsys/rc and build/subsys/stable:

yml
dependencies:
  - conan: "custom_app/1.1.0@openubmc/stable"

The build process for custom components is the same as for common components. For details, see Component Build and Release.

Using Custom Components

Custom components differ from common components in that you must customize them after building the package to use the corresponding services. Change the Customer property of the maca component resource collaboration interface to the name of the custom component. Then, restart the BMC.

shell
busctl --user introspect bmc.kepler.maca /bmc/kepler/Managers/1/Package bmc.kepler.Managers.Package
shell
NAME                                   TYPE      SIGNATURE   RESULT/VALUE                   FLAGS
bmc.kepler.Managers.Package            interface -           -                              -
.Customer                              property  s           ""                             -
.Provider                              property  s           ""                             const
.Version                               property  s           ""                             const