diff --git a/base-files/files/lib/functions/system.sh b/base-files/files/lib/functions/system.sh index d06354b01..23484c8ad 100644 --- a/base-files/files/lib/functions/system.sh +++ b/base-files/files/lib/functions/system.sh @@ -61,11 +61,21 @@ find_mtd_chardev() { echo "${INDEX:+$PREFIX$INDEX}" } +get_mac_ascii() { + local part="$1" + local key="$2" + local mac_dirty + + mac_dirty=$(strings "$part" | sed -n 's/^'"$key"'=//p') + + # "canonicalize" mac + [ -n "$mac_dirty" ] && macaddr_canonicalize "$mac_dirty" +} + mtd_get_mac_ascii() { local mtdname="$1" local key="$2" local part - local mac_dirty part=$(find_mtd_part "$mtdname") if [ -z "$part" ]; then @@ -73,10 +83,7 @@ mtd_get_mac_ascii() { return fi - mac_dirty=$(strings "$part" | sed -n 's/^'"$key"'=//p') - - # "canonicalize" mac - [ -n "$mac_dirty" ] && macaddr_canonicalize "$mac_dirty" + get_mac_ascii "$part" "$key" } mtd_get_mac_encrypted_arcadyan() { @@ -190,6 +197,20 @@ mtd_get_part_size() { done < /proc/mtd } +mmc_get_mac_ascii() { + local part_name="$1" + local key="$2" + local part + + part=$(find_mmc_part "$part_name") + if [ -z "$part" ]; then + echo "mmc_get_mac_ascii: partition $part_name not found!" >&2 + return + fi + + get_mac_ascii "$part" "$key" +} + mmc_get_mac_binary() { local part_name="$1" local offset="$2" diff --git a/luci-app-store/API.md b/luci-app-store/API.md index 34fce1a5c..0ccbf9907 100644 --- a/luci-app-store/API.md +++ b/luci-app-store/API.md @@ -97,7 +97,7 @@ {"installed":false} ``` -7. 任务状态(日志) +7. 任务状态(日志),已废弃,使用luci-lib-taskd提供的封装 ``` GET /cgi-bin/luci/admin/store/log diff --git a/luci-app-store/Makefile b/luci-app-store/Makefile index 1ea7580ae..c1219c440 100644 --- a/luci-app-store/Makefile +++ b/luci-app-store/Makefile @@ -11,7 +11,7 @@ LUCI_DEPENDS:=+curl +opkg +luci-base +tar +coreutils +coreutils-stat +libuci-lua LUCI_EXTRA_DEPENDS:=luci-lib-taskd (>=1.0.19) LUCI_PKGARCH:=all -PKG_VERSION:=0.1.21-0 +PKG_VERSION:=0.1.22-0 # PKG_RELEASE MUST be empty for luci.mk PKG_RELEASE:= diff --git a/luci-app-store/luasrc/controller/store.lua b/luci-app-store/luasrc/controller/store.lua index ea6ac96ee..2f6dec978 100644 --- a/luci-app-store/luasrc/controller/store.lua +++ b/luci-app-store/luasrc/controller/store.lua @@ -30,12 +30,20 @@ function index() entry({"admin", "store", "configured"}, call("configured")) + -- docker + entry({"admin", "store", "docker_check_dir"}, call("docker_check_dir")) + entry({"admin", "store", "docker_check_migrate"}, call("docker_check_migrate")) + entry({"admin", "store", "docker_migrate"}, post("docker_migrate")) + + -- package for _, action in ipairs({"update", "install", "upgrade", "remove"}) do store_api(action, true) end for _, action in ipairs({"status", "installed"}) do store_api(action, false) end + + -- backup if nixio.fs.access("/usr/libexec/istore/backup") then entry({"admin", "store", "get_support_backup_features"}, call("get_support_backup_features")) entry({"admin", "store", "light_backup"}, post("light_backup")) @@ -408,6 +416,46 @@ function configured() luci.http.write_json({code=200, configured=configured}) end +function docker_check_dir() + local docker_on_system = luci.sys.call("/usr/libexec/istore/docker check_dir >/dev/null 2>&1") ~= 0 + luci.http.prepare_content("application/json") + luci.http.write_json({code=200, docker_on_system=docker_on_system}) +end + +function docker_check_migrate() + local path = luci.http.formvalue("path") + if path == nil or path == "" then + luci.http.status(400, "Bad Request") + return + end + local r,o,e = is_exec("/usr/libexec/istore/docker migrate_check " .. luci.util.shellquote(path)) + local result = "good" + if r == 1 then + result = "bad" + elseif r == 2 then + result = "existed" + end + luci.http.prepare_content("application/json") + luci.http.write_json({code=200, result=result, error=e}) +end + +function docker_migrate() + local path = luci.http.formvalue("path") + if path == nil or path == "" then + luci.http.status(400, "Bad Request") + return + end + + local action = "migrate" + local overwrite = luci.http.formvalue("overwrite") + if overwrite == "chdir" then + action = "change_dir" + end + local r,o,e = is_exec("/usr/libexec/istore/docker " .. action .. " " .. luci.util.shellquote(path), true) + luci.http.prepare_content("application/json") + luci.http.write_json({code=r, stdout=o, stderr=e}) +end + local function split(str,reps) local resultStrList = {} string.gsub(str,'[^'..reps..']+',function (w) diff --git a/luci-app-store/root/usr/libexec/istore/docker b/luci-app-store/root/usr/libexec/istore/docker new file mode 100755 index 000000000..69b51246d --- /dev/null +++ b/luci-app-store/root/usr/libexec/istore/docker @@ -0,0 +1,114 @@ +#!/bin/sh + +handle_part() { + case "$MOUNT" in + "/overlay") + return 1 + ;; + esac + return 0 +} + +check_dir() { + local data_root=$(uci -q get dockerd.globals.data_root) + [ -n "$data_root" ] || return 0 + local block_dev=$(findmnt -T "$data_root" -v -o SOURCE | sed -n 2p) + [ -n "$block_dev" ] || return 0 + [ "overlayfs:/overlay" = "$block_dev" ] && return 1 + local line=$(block info "$block_dev" 2>/dev/null) + [ -n "$line" ] || return 0 + eval "${line##*: } handle_part ${line%%: *}" +} + +migrate_check(){ + local dest="$1" + [ -n "$dest" ] || { + echo "dest dir not specified!" >&2 + return 1 + } + local data_root="$2" + [ -n "$data_root" ] || data_root=$(uci -q get dockerd.globals.data_root) + [ -n "$data_root" ] || { + echo "get docker data_root failed!" >&2 + return 1 + } + + [ "$data_root" = "/" ] || data_root="${data_root%%/}" + [ "$dest" = "/" ] || dest="${dest%%/}" + + [ "$data_root" = "$dest" ] && { + echo "dest dir is the same as data_root!" >&2 + return 1 + } + + [ 1 = "$FORCE" ] && return 0 + [ -e "$dest" ] || return 0 + [ -d "$dest" ] || { + echo "$dest is existed and it's not a dir, use FORCE=1 to overwrite it" >&2 + return 2 + } + [ 0 = "$(ls -A "$dest" | head -1 | wc -l)" ] || { + echo "$dest is existed and it's not empty, use FORCE=1 to overwrite it" >&2 + return 2 + } + return 0 +} + +migrate() { + local dest="$1" + local data_root=$(uci -q get dockerd.globals.data_root) + [ -n "$data_root" ] || { + echo "get docker data_root failed!" >&2 + return 1 + } + [ "$data_root" = "/" ] || data_root="${data_root%%/}" + [ "$dest" = "/" ] || dest="${dest%%/}" + + FORCE=1 migrate_check "$dest" "$data_root" + local check_result=$? + [ 0 = $check_result ] || return $check_result + + if [ "$UCI_ONLY" != 1 ]; then + rm -rf "$dest" + mkdir -p "$dest" + echo "Copy $data_root to $dest ..." + cp -a "$data_root/." "$dest/" || return 1 + fi + + echo "Change dockerd data_root to $dest and restart" + uci set dockerd.globals.data_root="$dest" + uci commit dockerd + /etc/init.d/dockerd restart + + echo "Done" +} + +action=${1} +shift + +usage() { + echo "usage: $1 sub-command [arguments...]" + echo "where sub-command is one of:" + echo " check_dir Check docker data_root is on extrnal disk" + echo " migrate_check {target_dir} Check target_dir is valid for migration, return 2 if target_dir existed and not empty" + echo " migrate {target_dir} Migrate docker data_root to target_dir" + echo " change_dir {target_dir} Migrate docker data_root to target_dir but change dir only (no data copy)" +} + +case $action in + "check_dir") + check_dir + ;; + "migrate") + migrate "$@" + ;; + "migrate_check") + migrate_check "$@" + ;; + "change_dir") + UCI_ONLY=1 migrate "$@" + ;; + *) + usage "$0" + ;; +esac diff --git a/luci-app-store/swagger.yaml b/luci-app-store/swagger.yaml index ac3c48099..bb7f69135 100644 --- a/luci-app-store/swagger.yaml +++ b/luci-app-store/swagger.yaml @@ -28,7 +28,7 @@ paths: post: tags: - install - summary: 安装插件 + summary: 安装插件。安装过程中使用taskd接口获取日志 parameters: - in: "query" name: "token" @@ -148,7 +148,7 @@ paths: get: tags: - log - summary: 任务日志 + summary: 任务日志,已废弃 responses: "200": description: OK @@ -194,11 +194,67 @@ paths: tags: - configured summary: 检查插件是否已经配置过 + parameters: + - in: "query" + name: "uci" + type: string + required: true + description: "要检查的uci配置文件名称" responses: "200": description: OK schema: $ref: "#/definitions/ResponseStoreConfigured" + /cgi-bin/luci/admin/store/docker_check_dir: + get: + tags: + - docker_check_dir + summary: 检查docker目录是否在系统盘 + responses: + "200": + description: OK + schema: + $ref: "#/definitions/ResponseDockerCheckDir" + /cgi-bin/luci/admin/store/docker_check_migrate: + get: + tags: + - docker_check_migrate + summary: 检查docker迁移目标目录是否有效 + parameters: + - in: "query" + name: "path" + type: string + required: true + description: "完整目标路径" + responses: + "200": + description: OK + schema: + $ref: "#/definitions/ResponseDockerCheckMigrate" + /cgi-bin/luci/admin/store/docker_migrate: + post: + tags: + - docker_migrate + summary: docker迁移到目标目录,异步,当返回code为0的时候,可以使用taskd接口展示日志,跟安装插件时一样 + parameters: + - in: "query" + name: "token" + type: string + required: true + - in: "query" + name: "path" + type: string + required: true + description: "完整目标路径" + - in: "query" + name: "overwrite" + type: string + description: "如果docker_check_migrate返回的result是existed,使用此参数指定解决方案:‘true’表示覆盖目标目录;‘chdir’表示只修改路径不复制文件" + responses: + "200": + description: OK + schema: + $ref: "#/definitions/ResponseStore" definitions: ResponseStoreToken: @@ -324,4 +380,26 @@ definitions: description: "为200时" configured: type: boolean + ResponseDockerCheckDir: + type: object + properties: + code: + type: integer + description: "为200时" + docker_on_system: + type: boolean + description: "docker数据在系统盘" + ResponseDockerCheckMigrate: + type: object + properties: + code: + type: integer + description: "为200时" + result: + type: string + description: "检查结果。可能值‘good’,‘bad’,‘existed’" + error: + type: string + description: "当result为bad时,此处返回错误日志" +