PCストレージ規格を解説! 現行の主流から旧規格まで
PCストレージ規格の基本を整理します。SATAとNVMeの違い、PCIe世代、M.2や2.5インチなどの形状、外付けストレージ規格、HDDとSSDの違い をまとめています。
今回は、OpenWrtのLuaスクリプト用UCI操作関数の使い方について紹介します。
このUCI操作関数は、WebUIのLuCIまたはLuCIと連携するCGIシステムがUCIコンフィグレーションファイルを操作するために使用しているものです。 luci.model.uciモジュールに収録されています。
これら関数の使い方を理解することで、UCIコンフィグレーションファイルを操作するCGIシステムの開発が容易になります。
関数仕様の詳細については既に公式ドキュメントに記載されています。 そのため、この記事ではサンプルコードとその実行結果を掲載することで使い方を説明します。
この記事は以下の環境で実施した結果を元に作成しています。
掲載サンプルコードの操作対象ファイルは、この記事のために作成したUCIコンフィグレーションファイル(/etc/config/sample)です。
このUCIコンフィグレーションファイルはUCIコマンドを使用して作成できます。 読者ご自身の環境で、サンプルコードを実行したい場合は、お手持ちのOpenWrtデバイスのコンソール上で以下のUCIコマンドを実行してください。
root@OpenWrt:~# touch /etc/config/sample root@OpenWrt:~# uci batch << EOF > set sample.sectionA=type > set sample.sectionA.flag1=on > set sample.sectionA.flag2=off > set sample.sectionA.flag3=true > set sample.sectionA.flag4=false > set sample.sectionA.flag5=1 > set sample.sectionA.flag6=0 > set sample.sectionB=type > set sample.sectionB.value=100 > set sample.sectionC=type > add_list sample.sectionC.items=1 > add_list sample.sectionC.items=2 > add_list sample.sectionC.items=3 > add_list sample.sectionC.items=4 > add_list sample.sectionC.items=5 > add sample unname > set sample.@unname[0].value=100 > add sample unname > set sample.@unname[1].value=200 > add sample unname > set sample.@unname[2].value=300 > EOF root@OpenWrt:~# uci commit sample
上記のコマンド実行後、catコマンドでsampleファイルの中身を見てみると、次のように表示されるはずです。
root@OpenWrt:~# cat /etc/config/sample
config confirm 'sectionA'
option flag1 'on'
option flag2 'off'
option flag3 'true'
option flag4 'false'
option flag5 '1'
option flag6 '0'
config confirm 'sectionB'
option value '100'
config confirm 'sectionC'
list items '1'
list items '2'
list items '3'
list items '4'
list items '5'
config uname
option value '100'
config uname
option value '200'
config uname
option value '300'
以上より、この記事では、/etc/config/sampleに対してUCI操作をするコードを掲載します。 これを通してLuaスクリプト用UCI操作関数について、その使い方を理解して頂ければ幸いです。 それでは、次節から説明に入ります。
Luaスクリプト用UCI操作関数を使用する上で重要なことの1つにステージング領域の選択があります。
Luaスクリプト用UCI操作関数は元々、LuCIで利用されることを目的に実装されています。そのため、ユーザーの通信セッションに 紐づくメモリ領域をステージング領域として使用するか否かでその後の設定データの保存先が変わる特徴を持っています。
もし、実装者の方がステージング領域を/tmp/.uciとする通常のUCIコマンド(/usr/bin/uci)の完全等価処理として これらのUCI操作関数を使用したい場合、必須モジュールのluci.model.uciをインポートした後にそのままset関数など 呼び出せば設定変更は/tmp/.uciに保存されます。
逆に通信セッションに紐づくメモリ領域/tmp/run/rpcd/uci-<ubus_rpc_session>を指定したい場合、 必須モジュールluci.model.uciをインポートした後にset_session_id関数を実行してメモリ領域(/tmp/run/rpcd/-<ubus_rpc_session>)作成する必要があります。
set_session_id関数によってメモリ領域を確保した後は、set関数などのUCI操作関数による設定変更は全てそのメモリ領域にコピーされます。
このように、Luaスクリプト用UCI操作関数にはステージング領域の決定方法に特徴がありますので、そのことは理解していた方が良いと思います。 次節ではこのことを踏まえて説明します。
今回のLuaスクリプト用UCI操作関数を使用するためには、事前準備として luci.model.uciモジュールをロードし、その中から関数の情報が格納されたテーブルを取得する必要があります。 モジュールをロードして、テーブルを取得するには以下の2つの方法があります。
local uci = require("luci.model.uci").cursor()
local uci = require("luci").cursor_state()
各UCI操作関数についてサンプルコードとその実行結果を掲載し、説明します。
-- add_test1.lua
local uci = require("luci.model.uci").cursor()
uci:add("sample", "test")
root@OpenWrt:~# lua add_test1.lua root@OpenWrt:~# cat /tmp/.uci/sample --catの代わりにuci changesでも同様の結果が表示されます。 +sample.cfg046865='test'
-- add_test2.lua
local uci = require("luci.model.uci").cursor()
local result = uci:set_session_id("0479472194661836394729210")
if result then
local id = uci:get_session_id()
print("success : id =" .. id)
else
print("failed")
return
end
uci:add("sample", "test")
root@OpenWrt:~# ubus monitor & root@OpenWrt:~# lua add_test2.lua
-- add_apply.lua
local uci = require("luci.model.uci").cursor()
-- changes_test.lua local uci = require("luci.model.uci").cursor() -- uci:set関数で以下のsampleコンフィグ設定を変更 -- 新規データとして、sample.sectionB.value='hello'をステージング領域に保存します。 uci:set("sample", "sectionB", "value", "hello") local config_data = uci:changes("sample") -- ステージング領域のsampleコンフィグを表示 for _, table in ipairs(config_data) do for _, val in ipairs(table) do io.write(val .. " ") end io.write("\n") end
root@OpenWrt:~# lua changes_test.lua
set sectionB value hello
-- commit_test.lua
local uci = require("luci.model.uci").cursor()
uci:set("sample", "sectionB", "value", "hello")
uci:commit("sample")
root@OpenWrt:~# lua commit_test.lua root@OpenWrt:~# cat /etc/config/sample config type 'sectionA' option flag1 'on' option flag2 'off' option flag3 'true' option flag4 'false' option flag5 '1' option flag6 '0' config type 'sectionB' option value 'hello' config type 'sectionC' list items '1' list items '2' list items '3' list items '4' list items '5'
-- confirm_test.lua
local uci = require("luci.model.uci").cursor()
-- delete_section_test.lua
local uci = require("luci.model.uci").cursor()
uci:delete("sample", "sectionB")
root@OpenWrt:~# lua delete_section_test.lua root@OpenWrt:~# uci changes -sample.sectionB
-- delete_option_test.lua
local uci = require("luci.model.uci").cursor()
uci:delete("sample", "sectionB", "value")
root@OpenWrt:~# lua delete_option_test.lua root@OpenWrt:~# uci changes -sample.sectionB.value
-- delete_all_test.lua local uci = require("luci.model.uci").cursor() local call_cnt = 0 function output_msg(data, message) if type(data) == "table" then for key, val in pairs(data) do local _message = message .. "[" .. key .. "]" output_msg(val, _message) end elseif type(data) == "boolean" then if data == true then io.write(message .. " = true\n") elseif data == false then io.write(message .. " = false\n") end elseif type(data) == "string" or type(data) == "number" then io.write(message .. " = " .. data .. "\n") end end function comparator(table) local delete_flg = false call_cnt = call_cnt + 1 print("---------[CALLED FUNC No." .. call_cnt .. "]----------") for key, val in pairs(table) do local message = "table[" .. key .. "]" output_msg(val, message) io.flush() end -- sampleからセクション名「sectionB」、「sectionC」のデータを削除します if table[".name"] == "sectionB" then delete_flg = true elseif table[".name"] == "sectionC" then delete_flg = true end -- 返却値がtrueならセクションを削除します return delete_flg end uci:delete_all("sample", "type", comparator)
root@OpenWrt:~# lua delete_all_test.lua ---------[CALLED FUNC No.1]---------- table[.name] = sectionB table[.type] = type table[value] = hello table[.anonymous] = false table[.index] = 1 ---------[CALLED FUNC No.2]---------- table[flag2] = off table[.anonymous] = false table[flag3] = true table[.index] = 0 table[flag4] = false table[flag6] = 0 table[flag5] = 1 table[flag1] = on table[.name] = sectionA table[.type] = type ---------[CALLED FUNC No.3]---------- table[.name] = sectionC table[.type] = type table[items][1] = 1 table[items][2] = 2 table[items][3] = 3 table[items][4] = 4 table[items][5] = 5 table[.anonymous] = false table[.index] = 2 root@OpenWrt:~# uci changes -sample.sectionB -sample.sectionC
-- foreach_test.lua
local uci = require("luci.model.uci").cursor()
local call_cnt = 0
function output_msg(data, message)
if type(data) == "table" then
for key, val in pairs(data) do
local _message = message .. "[" .. key .. "]"
output_msg(val, _message)
end
elseif type(data) == "boolean" then
if data == true then
io.write(message .. " = true\n")
elseif data == false then
io.write(message .. " = false\n")
end
elseif type(data) == "string" or type(data) == "number" then
io.write(message .. " = " .. data .. "\n")
end
end
function callback(table)
call_cnt = call_cnt + 1
print("---------[CALLED FUNC No." .. call_cnt .. "]----------")
for key, val in pairs(table) do
local message = "table[" .. key .. "]"
output_msg(val, message)
io.flush()
end
end
uci:foreach("sample", "type", callback)
root@OpenWrt:~# lua foreach_test.lua
---------[CALLED FUNC No.1]----------
table[flag2] = off
table[.anonymous] = false
table[flag3] = true
table[.index] = 0
table[flag4] = false
table[flag6] = 0
table[flag5] = 1
table[flag1] = on
table[.name] = sectionA
table[.type] = type
---------[CALLED FUNC No.2]----------
table[.name] = sectionB
table[.type] = type
table[value] = hello
table[.anonymous] = false
table[.index] = 1
---------[CALLED FUNC No.3]----------
table[.name] = sectionC
table[.type] = type
table[items][1] = 1
table[items][2] = 2
table[items][3] = 3
table[items][4] = 4
table[items][5] = 5
table[.anonymous] = false
table[.index] = 2
-- get_test.lua
local uci = require("luci.model.uci").cursor()
local val = uci:get("sample", "sectionB", "value")
print("sample.sectionB.value = " .. val)
root@OpenWrt:~# lua get_test.lua
sample.sectionB.value = 100
-- get_all_test.lua
local uci = require("luci.model.uci").cursor()
local sample = uci:get_all("sample")
for key, val in pairs(sample) do
for k, v in pairs(val) do
if type(v) == "string" then
print("sample[" .. key .. "][" .. k .. "] = " .. v)
elseif type(v) == "boolean" then
if v == true then
print("sample[" .. key .. "][" .. k .. "] = true")
else
print("sample[" .. key .. "][" .. k .. "] = false")
end
end
end
end
root@OpenWrt:~# lua get_all_test.lua
sample[option][.name] = sectionB
sample[option][.type] = type
sample[option][value] = hello
sample[option][.anonymous] = false
sample[bool][flag2] = off
sample[bool][.anonymous] = false
sample[bool][flag3] = true
sample[bool][flag4] = false
sample[bool][flag6] = 0
sample[bool][flag5] = 1
sample[bool][flag1] = on
sample[bool][.name] = sectionA
sample[bool][.type] = type
sample[list][.name] = sectionC
sample[list][.type] = type
sample[list][.anonymous] = false
| 真(true) | 偽(false) |
|---|---|
| 'on' | 左記以外 |
| 'true' | |
| 'yes' | |
| '1' |
-- get_bool_test.lua local uci = require("luci.model.uci").cursor() local flg = {} flg.result1 = uci:get_bool("sample", "sectionA", "flag1") -- 'on' flg.result2 = uci:get_bool("sample", "sectionA", "flag2") -- 'off' flg.result3 = uci:get_bool("sample", "sectionA", "flag3") -- 'true' flg.result4 = uci:get_bool("sample", "sectionA", "flag4") -- 'false' flg.result5 = uci:get_bool("sample", "sectionA", "flag5") -- '1' flg.result6 = uci:get_bool("sample", "sectionA", "flag6") -- '0' for key, val in pairs(flg) do io.write("flg." .. key .. " = ") if val == true then io.write("true\n") else io.write("false\n") end end
root@OpenWrt:~# lua get_bool_test.lua
flg.result6 = false
flg.result4 = false
flg.result1 = true
flg.result5 = true
flg.result3 = true
flg.result2 = false
-- get_confdir_test.lua
local uci = require("luci.model.uci").cursor()
local flash_dir = uci:get_confdir()
print("flash dir: " .. flash_dir)
root@OpenWrt:~# lua get_confdir_test.lua
flash dir: /etc/config
-- get_first_test.lua
local uci = require("luci.model.uci").cursor()
local value = uci:get_first("sample", "unname", "value", "not found")
print(value)
root@OpenWrt:~# lua get_first_test.lua
100
なお、この関数の存在から分かるように、OpenWrt専用アプリケーションは数ある
匿名セクションの中から一番先頭のセクション情報をプライオリティ設定として認識します。
-- get_list_test.lua
local uci = require("luci.model.uci").cursor()
local table = uci:get_list("sample", "sectionC", "items")
for idx, val in ipairs(table) do
print("table[" .. idx .. "] = " .. table[idx])
end
root@OpenWrt:~# lua get_list_test.lua
table[1] = 1
table[2] = 2
table[3] = 3
table[4] = 4
table[5] = 5
-- get_savedir_test.lua
local uci = require("luci.model.uci").cursor()
local staging_dir = uci:get_savedir()
print("staging dir = " .. staging_dir)
root@OpenWrt:~# lua get_savedir_test.lua staging dir: /tmp/.uci
-- get_session_id_test.lua
local uci = require("luci.model.uci").cursor()
-- /usr/lib/lua/luci/model/uci.luaのload関数定義の抜粋部
function load(self, config)
return true
end
-- load_test.lua
local uci = require("luci.model.uci").cursor()
io.write("Input config name :")
io.flush()
local config = io.read()
local result = uci:load(config)
if result then
print("loaded! (true)")
else
print("not found! (false)")
end
-- revert_test.lua
local uci = require("luci.model.uci").cursor()
uci:set("sample", "sectionB", "value", "hello")
uci:revert("sample")
root@OpenWrt:~# lua revert_test.lua root@OpenWrt:~# uci changes sample root@Openwrt:~# #変更内容は消去されたので、何も表示されません
-- rollback_test.lua
local uci = require("luci.model.uci").cursor()
-- rollback_pending_test.lua
local uci = require("luci.model.uci").cursor()
uci:set_session_id("test-session-001")
local pending = uci:rollback_pending()
print("rollback pending:", tostring(pending))
-- save_test.lua
function save(self, config)
return true
end
-- section_test.lua
local uci = require("luci.model.uci").cursor()
local option = {
data1 = "created!",
data2 = "created!",
}
uci:section("sample", "new", "sectionD", option)
root@OpenWrt:~# lua section_test.lua root@OpenWrt:~# uci changes sample.sectionD='new' sample.sectionD.data1='created!' sample.sectionD.data2='created!'
-- set_test1.lua
local uci = require("luci.model.uci").cursor()
uci:set("sample", "sectionB", "value", "hello")
root@OpenWrt:~# lua set_test1.lua root@OpenWrt:~# uci changes sample.sectionB.value='hello'
-- set_test2.lua
local uci = require("luci.model.uci").cursor()
local result = uci:set_session_id("0479472194661836394729210")
if result then
local id = uci:get_session_id()
print("success : id =" .. id)
else
print("failed")
return
end
uci:set("sample", "sectionB", "value", "hello")
root@OpenWrt:~# ubus monitor & root@OpenWrt:~# lua set_test2.lua
-- ubus monitorコマンドの出力結果の抜粋です。実際はもっと大量のメッセージが出力されます。
-> 6f38264a #00000000 status: {"status":0}
<- 6f38264a #4b12e34c invoke: {"objid":1259529036,"method":"set","data":{"ubus_rpc_session":"0479472194661836394729210","config":"sample","values":{"value":"hello"},"section":"sectionB"}}
-> 72395126 #6f38264a invoke: {"objid":1259529036,"method":"set","data":{"ubus_rpc_session":"0479472194661836394729210","config":"sample","values":{"value":"hello"},"section":"sectionB"},"user":"root","group":"root"}
-- set_confdir_test.lua
local uci = require("luci.model.uci").cursor()
-- set_list_test.lua
local uci = require("luci.model.uci").cursor()
local words = {
"good morning",
"hello",
"good bye",
}
uci:set_list("sample", "sectionC", "greetings", words)
root@OpenWrt:~# lua set_list_test.lua root@OpenWrt:~# uci changes sample.sectionC.greetings+='good morning' sample.sectionC.greetings+='hello' sample.sectionC.greetings+='good bye'
-- set_session_id_test.lua
local uci = require("luci.model.uci").cursor()
local result = uci:set_session_id("0479472194661836394729210")
if result then
local id = uci:get_session_id()
print("success : id =" .. id)
else
print("failed")
end
-- substate_test.lua
local uci = require("luci.model.uci").cursor()
-- tset_test.lua
local uci = require("luci.model.uci").cursor()
local new_options = {
greeting1 = "good morning",
greeting2 = "hello",
greeting3 = "good bye",
}
uci:tset("sample", "sectionB", new_options)
root@OpenWrt:~# lua tset_test.lua root@OpenWrt:~# uci changes sample.sectionB.greeting3='good bye' sample.sectionB.greeting1='good morning' sample.sectionB.greeting2='hello'
local uci = require("luci.model.uci").cursor()
-- reorder_test.lua local uci = require("luci.model.uci").cursor() local section = "" local index = 0 uci:foreach("sample", "unname", function(tbl) index = index + 1 -- unameタイプで2番目の匿名セクション@unname[2]のUIDを取得 if index == 2 then section = tbl[".name"] end end) if #section == 0 then print("Not Found.") end uci:reorder("sample", section, 1) -- 一番先頭に配置変更する
root@OpenWrt:~# lua reorder_test.lua root@OpenWrt:~# uci show sample
PCストレージ規格を解説! 現行の主流から旧規格まで
PCストレージ規格の基本を整理します。SATAとNVMeの違い、PCIe世代、M.2や2.5インチなどの形状、外付けストレージ規格、HDDとSSDの違い をまとめています。
超格安な日本発のマイコンボード UIAPduinoの紹介
290円という価格が目を引く日本発のマイコンボードについて、主な仕様、安さの背景、向 く用途、定番ボードとの違いをまとめて紹介します。
2026年版 スマートウォッチの選び方 iPhone・Android別におすすめモデルを紹介
スマートウォッチを初めて選ぶ人向けに、対応スマホ、健康管理、通知、決済、バッテリー、スポーツ機能の違いを整理し、Amazonで購入しやすい代表製品を紹介します。
2026年版 ドローンの選び方 基礎知識とおすすめモデル5選
ドローンを初めて購入する人向けに、基礎知識について整理し、DJI・HOVERAir・Potensicの代表モデルを紹介します。
レーザー彫刻機の選び方を紹介! 家庭向けで見たい方式や確認ポイントを整理
今回はレーザー彫刻機の選び方を紹介します。ダイオード、CO2、ファイバーの違い、家庭用で最初に見たい確認ポイント、代表的な メーカーと製品、家庭で使うときの注意点を整理して紹介します。
3Dプリンターの選び方! 造形方式や素材、確認ポイントを整理して紹介
3Dプリンターの選び方を紹介します。FDMと光造形の違い、PLAやPETG、ABSなどの素材、造形サイズ、家庭で使うときの注意点、代表的なメーカーと製品を整理しています。