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:
bingo new -hResult:
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:
bingo new -n my_appAfter 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:
cd my_app
git init
bingo buildAfter 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:
dev: The developer channel and the default channel for local builds.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:
bingo build --stage stableIf 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.
├── .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.serviceIn this component architecture, different files and directories have specific responsibilities:
The
CHANGELOG.mdandREADME.mdfiles record modification logs and component introductions, respectively. They are not involved in component execution.CMakeLists.txtandconanfile.pyare compilation and build scripts and are independent of the component service code. For details, see Component Build and Release.Makefileis a portable build system file that the CMake build system generates throughCMakeLists.txt.permissions.ini: Permission configuration file.user_conf: User configuration directory. Themy_app.servicefile is the component startup configuration file. It defines how Skynet reads the/opt/bmc/apps/bmc_time/config.cfgconfiguration file to start the service.config.cfg: The startup configuration file for theSkynetservice. It definesmy_app/service/main.luaas 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. Themodel.jsonfile describes the component data model and configures the properties, methods, and signals of the resource collaboration interface. Theservice.jsonfile describes the component service model and configures the component repository version information, dependencies, and paths. Thebingo gencommand of BMC Studio uses these model and interface definitions to generate code automatically. For details, see MDS.The
srcdirectory stores source code files. Thesrc/lualib/my_app_app.luafile defines the component implementation code, while thesrc/service/main.luafile is the entry function for the component app that starts the component service. You can write your service code in thesrcdirectory. For details, see Component Development and Coding.The
testdirectory 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 thetest/integrationandtest/unitdirectories. 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:
cd <component_repository_directory>
bingo genThese 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:
[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.targetThis file consists of three configuration sections:
Unitsection: configures the description and dependencies of the service, and specifies how it starts with the system. This service starts after thedbusservice.Servicesection: defines specific management and operation methods for the service. By default, the service runs in the/opt/bmc/apps/my_appworking directory. It starts by reading the/opt/bmc/apps/my_app/config.cfgconfiguration file through Skynet.Installsection: specifiesmulti-user.targetas 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:
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:
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:
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_appThe 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:
[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 '.' '{print $1}'`; 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:
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 nameSimilar to common components, custom components require a startup script. The following is an example of the custom_app/service/main.lua file:
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:
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_appAfter 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:
dependencies:
- conan: custom_appConfigure the component version in build/subsys/rc and build/subsys/stable:
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.
busctl --user introspect bmc.kepler.maca /bmc/kepler/Managers/1/Package bmc.kepler.Managers.PackageNAME TYPE SIGNATURE RESULT/VALUE FLAGS
bmc.kepler.Managers.Package interface - - -
.Customer property s "" -
.Provider property s "" const
.Version property s "" const