functions.sh 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555
  1. #!/bin/sh
  2. # Copyright (C) 2006-2013 OpenWrt.org
  3. # Copyright (C) 2006 Fokus Fraunhofer <[email protected]>
  4. # Copyright (C) 2010 Vertical Communications
  5. debug () {
  6. ${DEBUG:-:} "$@"
  7. }
  8. # newline
  9. N="
  10. "
  11. _C=0
  12. NO_EXPORT=1
  13. LOAD_STATE=1
  14. LIST_SEP=" "
  15. hotplug_dev() {
  16. env -i ACTION=$1 INTERFACE=$2 /sbin/hotplug-call net
  17. }
  18. append() {
  19. local var="$1"
  20. local value="$2"
  21. local sep="${3:- }"
  22. eval "export ${NO_EXPORT:+-n} -- \"$var=\${$var:+\${$var}\${value:+\$sep}}\$value\""
  23. }
  24. list_contains() {
  25. local var="$1"
  26. local str="$2"
  27. local val
  28. eval "val=\" \${$var} \""
  29. [ "${val%% $str *}" != "$val" ]
  30. }
  31. list_remove() {
  32. local var="$1"
  33. local remove="$2"
  34. local val
  35. eval "val=\" \${$var} \""
  36. val1="${val%% $remove *}"
  37. [ "$val1" = "$val" ] && return
  38. val2="${val##* $remove }"
  39. [ "$val2" = "$val" ] && return
  40. val="${val1## } ${val2%% }"
  41. val="${val%% }"
  42. eval "export ${NO_EXPORT:+-n} -- \"$var=\$val\""
  43. }
  44. config_load() {
  45. [ -n "$IPKG_INSTROOT" ] && return 0
  46. uci_load "$@"
  47. }
  48. reset_cb() {
  49. config_cb() { return 0; }
  50. option_cb() { return 0; }
  51. list_cb() { return 0; }
  52. }
  53. reset_cb
  54. package() {
  55. return 0
  56. }
  57. config () {
  58. local cfgtype="$1"
  59. local name="$2"
  60. export ${NO_EXPORT:+-n} CONFIG_NUM_SECTIONS=$(($CONFIG_NUM_SECTIONS + 1))
  61. name="${name:-cfg$CONFIG_NUM_SECTIONS}"
  62. append CONFIG_SECTIONS "$name"
  63. [ -n "$NO_CALLBACK" ] || config_cb "$cfgtype" "$name"
  64. export ${NO_EXPORT:+-n} CONFIG_SECTION="$name"
  65. export ${NO_EXPORT:+-n} "CONFIG_${CONFIG_SECTION}_TYPE=$cfgtype"
  66. }
  67. option () {
  68. local varname="$1"; shift
  69. local value="$*"
  70. export ${NO_EXPORT:+-n} "CONFIG_${CONFIG_SECTION}_${varname}=$value"
  71. [ -n "$NO_CALLBACK" ] || option_cb "$varname" "$*"
  72. }
  73. list() {
  74. local varname="$1"; shift
  75. local value="$*"
  76. local len
  77. config_get len "$CONFIG_SECTION" "${varname}_LENGTH" 0
  78. [ $len = 0 ] && append CONFIG_LIST_STATE "${CONFIG_SECTION}_${varname}"
  79. len=$(($len + 1))
  80. config_set "$CONFIG_SECTION" "${varname}_ITEM$len" "$value"
  81. config_set "$CONFIG_SECTION" "${varname}_LENGTH" "$len"
  82. append "CONFIG_${CONFIG_SECTION}_${varname}" "$value" "$LIST_SEP"
  83. list_cb "$varname" "$*"
  84. }
  85. config_rename() {
  86. local OLD="$1"
  87. local NEW="$2"
  88. local oldvar
  89. local newvar
  90. [ -n "$OLD" -a -n "$NEW" ] || return
  91. for oldvar in `set | grep ^CONFIG_${OLD}_ | \
  92. sed -e 's/\(.*\)=.*$/\1/'` ; do
  93. newvar="CONFIG_${NEW}_${oldvar##CONFIG_${OLD}_}"
  94. eval "export ${NO_EXPORT:+-n} \"$newvar=\${$oldvar}\""
  95. unset "$oldvar"
  96. done
  97. export ${NO_EXPORT:+-n} CONFIG_SECTIONS="$(echo " $CONFIG_SECTIONS " | sed -e "s, $OLD , $NEW ,")"
  98. [ "$CONFIG_SECTION" = "$OLD" ] && export ${NO_EXPORT:+-n} CONFIG_SECTION="$NEW"
  99. }
  100. config_unset() {
  101. config_set "$1" "$2" ""
  102. }
  103. config_clear() {
  104. local SECTION="$1"
  105. local oldvar
  106. list_remove CONFIG_SECTIONS "$SECTION"
  107. export ${NO_EXPORT:+-n} CONFIG_SECTIONS="${SECTION:+$CONFIG_SECTIONS}"
  108. for oldvar in `set | grep ^CONFIG_${SECTION:+${SECTION}_} | \
  109. sed -e 's/\(.*\)=.*$/\1/'` ; do
  110. unset $oldvar
  111. done
  112. }
  113. # config_get <variable> <section> <option> [<default>]
  114. # config_get <section> <option>
  115. config_get() {
  116. case "$3" in
  117. "") eval echo "\${CONFIG_${1}_${2}:-\${4}}";;
  118. *) eval export ${NO_EXPORT:+-n} -- "${1}=\${CONFIG_${2}_${3}:-\${4}}";;
  119. esac
  120. }
  121. # config_get_bool <variable> <section> <option> [<default>]
  122. config_get_bool() {
  123. local _tmp
  124. config_get _tmp "$2" "$3" "$4"
  125. case "$_tmp" in
  126. 1|on|true|enabled) _tmp=1;;
  127. 0|off|false|disabled) _tmp=0;;
  128. *) _tmp="$4";;
  129. esac
  130. export ${NO_EXPORT:+-n} "$1=$_tmp"
  131. }
  132. config_set() {
  133. local section="$1"
  134. local option="$2"
  135. local value="$3"
  136. local old_section="$CONFIG_SECTION"
  137. CONFIG_SECTION="$section"
  138. option "$option" "$value"
  139. CONFIG_SECTION="$old_section"
  140. }
  141. config_foreach() {
  142. local ___function="$1"
  143. [ "$#" -ge 1 ] && shift
  144. local ___type="$1"
  145. [ "$#" -ge 1 ] && shift
  146. local section cfgtype
  147. [ -z "$CONFIG_SECTIONS" ] && return 0
  148. for section in ${CONFIG_SECTIONS}; do
  149. config_get cfgtype "$section" TYPE
  150. [ -n "$___type" -a "x$cfgtype" != "x$___type" ] && continue
  151. eval "$___function \"\$section\" \"\$@\""
  152. done
  153. }
  154. config_list_foreach() {
  155. [ "$#" -ge 3 ] || return 0
  156. local section="$1"; shift
  157. local option="$1"; shift
  158. local function="$1"; shift
  159. local val
  160. local len
  161. local c=1
  162. config_get len "${section}" "${option}_LENGTH"
  163. [ -z "$len" ] && return 0
  164. while [ $c -le "$len" ]; do
  165. config_get val "${section}" "${option}_ITEM$c"
  166. eval "$function \"\$val\" \"\$@\""
  167. c="$(($c + 1))"
  168. done
  169. }
  170. insert_modules() {
  171. [ -d /etc/modules.d ] && {
  172. cd /etc/modules.d
  173. sed 's/^[^#]/insmod &/' $* | ash 2>&- || :
  174. }
  175. }
  176. include() {
  177. local file
  178. for file in $(ls $1/*.sh 2>/dev/null); do
  179. . $file
  180. done
  181. }
  182. find_mtd_index() {
  183. local PART="$(grep "\"$1\"" /proc/mtd | awk -F: '{print $1}')"
  184. local INDEX="${PART##mtd}"
  185. echo ${INDEX}
  186. }
  187. find_mtd_part() {
  188. local INDEX=$(find_mtd_index "$1")
  189. local PREFIX=/dev/mtdblock
  190. [ -d /dev/mtdblock ] && PREFIX=/dev/mtdblock/
  191. echo "${INDEX:+$PREFIX$INDEX}"
  192. }
  193. find_mtd_chardev() {
  194. local INDEX=$(find_mtd_index "$1")
  195. local PREFIX=/dev/mtd
  196. [ -d /dev/mtd ] && PREFIX=/dev/mtd/
  197. echo "${INDEX:+$PREFIX$INDEX}"
  198. }
  199. mtd_get_mac_ascii()
  200. {
  201. local mtdname="$1"
  202. local key="$2"
  203. local part
  204. local mac_dirty
  205. . /lib/functions.sh
  206. part=$(find_mtd_part "$mtdname")
  207. if [ -z "$part" ]; then
  208. echo "mtd_get_mac_ascii: partition $mtdname not found!" >&2
  209. return
  210. fi
  211. mac_dirty=$(strings "$part" | sed -n 's/'"$key"'=//p')
  212. # "canonicalize" mac
  213. printf "%02x:%02x:%02x:%02x:%02x:%02x" 0x${mac_dirty//:/ 0x}
  214. }
  215. mtd_get_mac_binary() {
  216. local mtdname="$1"
  217. local offset="$2"
  218. local part
  219. part=$(find_mtd_part "$mtdname")
  220. if [ -z "$part" ]; then
  221. echo "mtd_get_mac_binary: partition $mtdname not found!" >&2
  222. return
  223. fi
  224. dd bs=1 skip=$offset count=6 if=$part 2>/dev/null | hexdump -v -n 6 -e '5/1 "%02x:" 1/1 "%02x"'
  225. }
  226. mtd_get_part_size() {
  227. local part_name=$1
  228. local first dev size erasesize name
  229. while read dev size erasesize name; do
  230. name=${name#'"'}; name=${name%'"'}
  231. if [ "$name" = "$part_name" ]; then
  232. echo $((0x$size))
  233. break
  234. fi
  235. done < /proc/mtd
  236. }
  237. macaddr_add() {
  238. local mac=$1
  239. local val=$2
  240. local oui=${mac%:*:*:*}
  241. local nic=${mac#*:*:*:}
  242. nic=$(printf "%06x" $((0x${nic//:/} + $val & 0xffffff)) | sed 's/^\(.\{2\}\)\(.\{2\}\)\(.\{2\}\)/\1:\2:\3/')
  243. echo $oui:$nic
  244. }
  245. macaddr_setbit_la()
  246. {
  247. local mac=$1
  248. printf "%02x:%s" $((0x${mac%%:*} | 0x02)) ${mac#*:}
  249. }
  250. macaddr_2bin()
  251. {
  252. local mac=$1
  253. echo -ne \\x${mac//:/\\x}
  254. }
  255. strtok() { # <string> { <variable> [<separator>] ... }
  256. local tmp
  257. local val="$1"
  258. local count=0
  259. shift
  260. while [ $# -gt 1 ]; do
  261. tmp="${val%%$2*}"
  262. [ "$tmp" = "$val" ] && break
  263. val="${val#$tmp$2}"
  264. export ${NO_EXPORT:+-n} "$1=$tmp"; count=$((count+1))
  265. shift 2
  266. done
  267. if [ $# -gt 0 -a -n "$val" ]; then
  268. export ${NO_EXPORT:+-n} "$1=$val"; count=$((count+1))
  269. fi
  270. return $count
  271. }
  272. jffs2_mark_erase() {
  273. local part="$(find_mtd_part "$1")"
  274. [ -z "$part" ] && {
  275. echo Partition not found.
  276. return 1
  277. }
  278. echo -e "\xde\xad\xc0\xde" | mtd -qq write - "$1"
  279. }
  280. uci_apply_defaults() {
  281. cd /etc/uci-defaults || return 0
  282. files="$(ls)"
  283. [ -z "$files" ] && return 0
  284. mkdir -p /tmp/.uci
  285. for file in $files; do
  286. ( . "./$(basename $file)" ) && rm -f "$file"
  287. done
  288. uci commit
  289. }
  290. group_add() {
  291. local name="$1"
  292. local gid="$2"
  293. local rc
  294. [ -f "${IPKG_INSTROOT}/etc/group" ] || return 1
  295. [ -n "$IPKG_INSTROOT" ] || lock /var/lock/group
  296. echo "${name}:x:${gid}:" >> ${IPKG_INSTROOT}/etc/group
  297. rc=$?
  298. [ -n "$IPKG_INSTROOT" ] || lock -u /var/lock/group
  299. return $rc
  300. }
  301. group_exists() {
  302. grep -qs "^${1}:" ${IPKG_INSTROOT}/etc/group
  303. }
  304. user_add() {
  305. local name="${1}"
  306. local uid="${2}"
  307. local gid="${3:-$2}"
  308. local desc="${4:-$1}"
  309. local home="${5:-/var/run/$1}"
  310. local shell="${6:-/bin/false}"
  311. local rc
  312. [ -f "${IPKG_INSTROOT}/etc/passwd" ] || return 1
  313. [ -n "$IPKG_INSTROOT" ] || lock /var/lock/passwd
  314. echo "${name}:x:${uid}:${gid}:${desc}:${home}:${shell}" >> ${IPKG_INSTROOT}/etc/passwd
  315. echo "${name}:x:0:0:99999:7:::" >> ${IPKG_INSTROOT}/etc/shadow
  316. rc=$?
  317. [ -n "$IPKG_INSTROOT" ] || lock -u /var/lock/passwd
  318. return $rc
  319. }
  320. user_exists() {
  321. grep -qs "^${1}:" ${IPKG_INSTROOT}/etc/passwd
  322. }
  323. pi_include() {
  324. if [ -f "/tmp/overlay/$1" ]; then
  325. . "/tmp/overlay/$1"
  326. elif [ -f "$1" ]; then
  327. . "$1"
  328. elif [ -d "/tmp/overlay/$1" ]; then
  329. if [ -n "$(ls /tmp/overlay/$1/*.sh 2>/dev/null)" ]; then
  330. for src_script in /tmp/overlay/$1/*.sh; do
  331. . "$src_script"
  332. done
  333. fi
  334. elif [ -d "$1" ]; then
  335. if [ -n "$(ls $1/*.sh 2>/dev/null)" ]; then
  336. for src_script in $1/*.sh; do
  337. . "$src_script"
  338. done
  339. fi
  340. else
  341. echo "WARNING: $1 not found"
  342. return 1
  343. fi
  344. return 0
  345. }
  346. boot_hook_splice_start() {
  347. export -n PI_HOOK_SPLICE=1
  348. }
  349. boot_hook_splice_finish() {
  350. local hook
  351. for hook in $PI_STACK_LIST; do
  352. local v; eval "v=\${${hook}_splice:+\$${hook}_splice }$hook"
  353. export -n "${hook}=${v% }"
  354. export -n "${hook}_splice="
  355. done
  356. export -n PI_HOOK_SPLICE=
  357. }
  358. boot_hook_init() {
  359. local hook="${1}_hook"
  360. export -n "PI_STACK_LIST=${PI_STACK_LIST:+$PI_STACK_LIST }$hook"
  361. export -n "$hook="
  362. }
  363. boot_hook_add() {
  364. local hook="${1}_hook${PI_HOOK_SPLICE:+_splice}"
  365. local func="${2}"
  366. [ -n "$func" ] && {
  367. local v; eval "v=\$$hook"
  368. export -n "$hook=${v:+$v }$func"
  369. }
  370. }
  371. boot_hook_shift() {
  372. local hook="${1}_hook"
  373. local rvar="${2}"
  374. local v; eval "v=\$$hook"
  375. [ -n "$v" ] && {
  376. local first="${v%% *}"
  377. [ "$v" != "${v#* }" ] && \
  378. export -n "$hook=${v#* }" || \
  379. export -n "$hook="
  380. export -n "$rvar=$first"
  381. return 0
  382. }
  383. return 1
  384. }
  385. boot_run_hook() {
  386. local hook="$1"
  387. local func
  388. while boot_hook_shift "$hook" func; do
  389. local ran; eval "ran=\$PI_RAN_$func"
  390. [ -n "$ran" ] || {
  391. export -n "PI_RAN_$func=1"
  392. $func "$1" "$2"
  393. }
  394. done
  395. }
  396. jffs2_ready() {
  397. mtdpart="$(find_mtd_part rootfs_data)"
  398. [ -z "$mtdpart" ] && return 1
  399. magic=$(hexdump $mtdpart -n 4 -e '4/1 "%02x"')
  400. [ "$magic" != "deadc0de" ]
  401. }
  402. dupe() { # <new_root> <old_root>
  403. cd $1
  404. echo -n "creating directories... "
  405. {
  406. cd $2
  407. find . -xdev -type d
  408. echo "./dev ./overlay ./mnt ./proc ./tmp"
  409. # xdev skips mounted directories
  410. cd $1
  411. } | xargs mkdir -p
  412. echo "done"
  413. echo -n "setting up symlinks... "
  414. for file in $(cd $2; find . -xdev -type f;); do
  415. case "$file" in
  416. ./rom/note) ;; #nothing
  417. ./etc/config*|\
  418. ./usr/lib/opkg/info/*) cp -af $2/$file $file;;
  419. *) ln -sf /rom/${file#./*} $file;;
  420. esac
  421. done
  422. for file in $(cd $2; find . -xdev -type l;); do
  423. cp -af $2/${file#./*} $file
  424. done
  425. echo "done"
  426. }
  427. pivot() { # <new_root> <old_root>
  428. mount -o noatime,move /proc $1/proc && \
  429. pivot_root $1 $1$2 && {
  430. mount -o noatime,move $2/dev /dev
  431. mount -o noatime,move $2/tmp /tmp
  432. mount -o noatime,move $2/sys /sys 2>&-
  433. mount -o noatime,move $2/overlay /overlay 2>&-
  434. return 0
  435. }
  436. }
  437. fopivot() { # <rw_root> <ro_root> <dupe?>
  438. root=$1
  439. {
  440. if grep -q overlay /proc/filesystems; then
  441. mount -o noatime,lowerdir=/,upperdir=$1 -t overlayfs "overlayfs:$1" /mnt && root=/mnt
  442. elif grep -q mini_fo /proc/filesystems; then
  443. mount -t mini_fo -o noatime,base=/,sto=$1 "mini_fo:$1" /mnt 2>&- && root=/mnt
  444. else
  445. mount --bind -o noatime / /mnt
  446. mount --bind -o noatime,union "$1" /mnt && root=/mnt
  447. fi
  448. } || {
  449. [ "$3" = "1" ] && {
  450. mount | grep "on $1 type" 2>&- 1>&- || mount -o noatime,bind $1 $1
  451. dupe $1 $rom
  452. }
  453. }
  454. pivot $root $2
  455. }
  456. ramoverlay() {
  457. mkdir -p /tmp/root
  458. mount -t tmpfs -o noatime,mode=0755 root /tmp/root
  459. fopivot /tmp/root /rom 1
  460. }
  461. [ -z "$IPKG_INSTROOT" -a -f /lib/config/uci.sh ] && . /lib/config/uci.sh