Addons API - XIYO/asuswrt-merlin.ng-kr GitHub Wiki

제3자 μ• λ“œμ˜¨

μ†Œκ°œ

384.15 버전뢀터 Asuswrt-Merlin은 제3자 μ• λ“œμ˜¨μ˜ μ›Ή 톡합을 μ§€μ›ν•©λ‹ˆλ‹€. 기쑴의 webui μ„Ήμ…˜μ— μƒˆ νƒ­μœΌλ‘œ μ΅œλŒ€ μ—΄ 개의 μ‚¬μš©μž μ •μ˜ νŽ˜μ΄μ§€λ₯Ό μΆ”κ°€ν•  수 μžˆμŠ΅λ‹ˆλ‹€.

λ˜ν•œ nvramκ³Ό λ³„λ„λ‘œ μ• λ“œμ˜¨μ„ μœ„ν•œ μ „μš© μ„€μ • μ €μž₯μ†Œκ°€ 있으며, λ”°λΌμ„œ κ·Έ μ œν•œμ— ꡬ속받지 μ•ŠμŠ΅λ‹ˆλ‹€(i.e. νŽŒμ›¨μ–΄ 이미지λ₯Ό λ‹€μ‹œ μ»΄νŒŒμΌν•˜μ§€ μ•Šκ³ λ„ μƒˆ 섀정을 λ§Œλ“€ 수 μžˆμŠ΅λ‹ˆλ‹€).

지원은 rc_support nvram 값에 am_addons ν”Œλž˜κ·Έμ˜ 쑴재둜 감지할 수 μžˆμŠ΅λ‹ˆλ‹€.

μœ„μΉ˜

/jffs/addons/ 디렉토리가 ν‘œμ€€ μœ„μΉ˜μž…λ‹ˆλ‹€. 여기에 λͺ¨λ“  νŒŒμΌμ„ μ €μž₯ν•  디렉토리λ₯Ό 생성해야 ν•©λ‹ˆλ‹€. 이 λ””λ ‰ν† λ¦¬μ˜ λ£¨νŠΈμ— νŒŒμΌμ„ μΆ”κ°€ν•˜μ§€ λ§ˆμ‹­μ‹œμ˜€.

λͺ¨λ“  μ• λ“œμ˜¨μ— λŒ€ν•œ μ‚¬μš©μž μ •μ˜ 섀정은 /jffs/addons/custom_settings.txt에 μ €μž₯λ©λ‹ˆλ‹€.

μ‚¬μš©μž μ •μ˜ νŽ˜μ΄μ§€

νŽŒμ›¨μ–΄λŠ” μ΅œλŒ€ 20개의 μ‚¬μš©μž μ •μ˜ νŽ˜μ΄μ§€λ₯Ό μ§€μ›ν•©λ‹ˆλ‹€(386.1 μ΄μ „μ—λŠ” 10κ°œμ˜€μŠ΅λ‹ˆλ‹€). μ΄λŸ¬ν•œ νŽ˜μ΄μ§€λŠ” λΆ€νŒ… μ‹œμ— 슀크립트λ₯Ό 톡해 λ§ˆμš΄νŠΈν•΄μ•Ό ν•˜λ©°, μ΄λŠ” ideally services-startμ—μ„œ ν˜ΈμΆœν•΄μ•Ό ν•©λ‹ˆλ‹€. μ‚¬μš©μž μ •μ˜ νŽ˜μ΄μ§€λ₯Ό /jffs/addons/my_addon/ 폴더에 μ„€μΉ˜ μŠ€ν¬λ¦½νŠΈμ™€ ν•¨κ»˜ 두어야 ν•©λ‹ˆλ‹€. λ‹€μŒμ€ Tools μ„Ήμ…˜μ— μƒˆ νƒ­μœΌλ‘œ νŽ˜μ΄μ§€λ₯Ό μ‚½μž…ν•  μ„€μΉ˜ 슀크립트의 μ˜ˆμž…λ‹ˆλ‹€.

#!/bin/sh

source /usr/sbin/helper.sh

# Does the firmware support addons?
nvram get rc_support | grep -q am_addons
if [ $? != 0 ]
then
    logger "MyPage" "This firmware does not support addons!"
    exit 5
fi

# Obtain the first available mount point in $am_webui_page
am_get_webui_page /jffs/addons/my_addon/MyPage.asp

if [ "$am_webui_page" = "none" ]
then
    logger "MyPage" "Unable to install Mypage"
    exit 5
fi
logger "MyPage" "Mounting MyPage as $am_webui_page"

# Copy custom page
cp /jffs/addons/my_addon/MyPage.asp /www/user/$am_webui_page

# Copy menuTree (if no other script has done it yet) so we can modify it
if [ ! -f /tmp/menuTree.js ]
then
    cp /www/require/modules/menuTree.js /tmp/
    mount -o bind /tmp/menuTree.js /www/require/modules/menuTree.js
fi

# Insert link at the end of the Tools menu.  Match partial string, since tabname can change between builds (if using an AS tag)
sed -i "/url: \"Tools_OtherSettings.asp\", tabName:/a {url: \"$am_webui_page\", tabName: \"My Page\"}," /tmp/menuTree.js

# sed and binding mounts don't work well together, so remount modified file
umount /www/require/modules/menuTree.js && mount -o bind /tmp/menuTree.js /www/require/modules/menuTree.js

μ‚¬μš©μž μ •μ˜ μ„€μ •

μƒˆλ‘œμš΄ nvram 섀정은 νŽŒμ›¨μ–΄ μž¬λΉŒλ“œ 없이 λ™μ μœΌλ‘œ μΆ”κ°€ν•  수 μ—†μœΌλ©°, λ˜ν•œ μ‹ κ·œ Broadcom HND ν”Œλž«νΌμ˜ 크기 μ œν•œ(μƒˆ λ³€μˆ˜λŠ” μ΅œλŒ€ 100λ°”μ΄νŠΈμ˜ 값을 가짐) λ•Œλ¬Έμ— μ• λ“œμ˜¨μ„ μœ„ν•œ μƒˆλ‘œμš΄ μ„€μ • μ €μž₯μ†Œκ°€ κ΅¬ν˜„λ˜μ—ˆμŠ΅λ‹ˆλ‹€. /jffs/addons/custom_settings.txt νŒŒμΌμ—λŠ” λͺ¨λ“  제3자 섀정이 ν¬ν•¨λ©λ‹ˆλ‹€. 각 μ€„μ—λŠ” λ‹€μŒκ³Ό 같이 μ •μ˜λœ ν•˜λ‚˜μ˜ 섀정이 ν¬ν•¨λ©λ‹ˆλ‹€:

setting1 My First Setting
setting2 My Second Setting

λ³€μˆ˜ 이름은 영숫자, λŒ€μ‹œ(-) 및 밑쀄(_) 문자둜 μ œν•œν•΄μ•Ό ν•©λ‹ˆλ‹€. μ΄λ¦„μ˜ μ΅œλŒ€ κΈΈμ΄λŠ” 29μžμž…λ‹ˆλ‹€.

λ³€μˆ˜ λ‚΄μš©μ€ 이둠적으둜 λͺ¨λ“  7λΉ„νŠΈ ASCII 인쇄 κ°€λŠ₯ 문자λ₯Ό ν—ˆμš©ν•΄μ•Ό ν•©λ‹ˆλ‹€. λ‚΄μš©μ˜ 크기 μ œν•œμ€ 2999μžμž…λ‹ˆλ‹€. 더 λ³΅μž‘ν•œ 값이 ν•„μš”ν•œ 경우(예: 쀄 λ°”κΏˆμ΄ μžˆλŠ” λ¬Έμžμ—΄), base64 인코딩을 μ‚¬μš©ν•˜κ±°λ‚˜ 데이터λ₯Ό λ³„λ„μ˜ 파일둜 μ΄λ™ν•˜λŠ” 것이 μ’‹μŠ΅λ‹ˆλ‹€(λ„ˆλ¬΄ ν¬κ±°λ‚˜ λ³΅μž‘ν•œ 경우). μ›Ή νŽ˜μ΄μ§€μ—μ„œ 읽기 μ•‘μ„ΈμŠ€κ°€ ν•„μš”ν•œ 경우 /www/user/ 폴더에 μ΄λŸ¬ν•œ νŒŒμΌμ„ μž‘μ„±ν•  수 μžˆλ‹€λŠ” 점에 μœ μ˜ν•˜μ„Έμš”. 이 경우 κ³ μœ ν•œ 이름을 μ‚¬μš©ν•˜κ±°λ‚˜(예: 기쑴의 λ„€μž„μŠ€νŽ˜μ΄μŠ€λ₯Ό μž¬μ‚¬μš©) 데이터λ₯Ό ν•˜μœ„ 디렉토리에 μ €μž₯ν•˜λ„λ‘ ν•˜μ‹­μ‹œμ˜€.

전체 μ €μž₯μ†Œμ˜ μ΅œλŒ€ 총 ν¬κΈ°λŠ” 8KBμž…λ‹ˆλ‹€. μ΄λŸ¬ν•œ 크기 μ œν•œμœΌλ‘œ 인해, 거기에 λ§Žμ€ μ–‘μ˜ 데이터λ₯Ό μ €μž₯ν•˜μ§€ μ•Šλ„λ‘ λ…Έλ ₯ν•˜μ‹­μ‹œμ˜€ - ν•„μš”ν•œ 경우 λ³„λ„μ˜ νŒŒμΌμ„ μ‚¬μš©ν•˜μ‹­μ‹œμ˜€. 더 컀지면, μ›Ή μ„œλ²„μ—μ„œ μ²˜λ¦¬ν•  λ•Œ 섀정이 잘릴 κ²ƒμž…λ‹ˆλ‹€.

λͺ¨λ“  μ• λ“œμ˜¨μ΄ λ™μΌν•œ μ €μž₯μ†Œλ₯Ό κ³΅μœ ν•˜κ²Œ λ˜λ―€λ‘œ, 섀정을 μ‰½κ²Œ 식별할 수 μžˆλ„λ‘ λ„€μž„μŠ€νŽ˜μ΄μŠ€λ₯Ό μ •μ˜ν•΄μ•Ό ν•©λ‹ˆλ‹€. μ΄λŠ” λ‹€μŒκ³Ό 같이 보일 κ²ƒμž…λ‹ˆλ‹€:

my_addon_version 3.0.2
my_addon_state enabled
diversion_version 2.0
diversion_whitelist /jffs/addons/diversion/my-whitelist.txt

μ‰˜ μ‚¬μš©

μ‰˜ 슀크립트 μ‘°μž‘μ„ μœ„ν•΄, helper.shλŠ” λ‚΄μš©μ„ κ²€μƒ‰ν•˜κ³  μ €μž₯ν•˜λŠ” ν•¨μˆ˜ μŒμ„ μ œκ³΅ν•©λ‹ˆλ‹€.

# set a value
am_settings_set addon_title Cool Addon 1.0
# retrieve it, in the $TITLE variable
TITLE=$(am_settings_get addon_tittle)

μ›Ή μ‚¬μš©λ²•

μ›Ή 톡합을 μœ„ν•΄ νŽ˜μ΄μ§€μ˜ μ‹œμž‘ 뢀근에 λ‹€μŒμ„ μ‚½μž…ν•˜μ—¬ νŽ˜μ΄μ§€ λ‚΄μ—μ„œ μ΄λŸ¬ν•œ 섀정에 μžλ°”μŠ€ν¬λ¦½νŠΈ 객체둜 μ•‘μ„ΈμŠ€ν•  수 μžˆμŠ΅λ‹ˆλ‹€:

var custom_settings = <% get_custom_settings(); %>;

HTML μ„Ήμ…˜μ—μ„œ nvram 섀정을 μ‚¬μš©ν•˜λŠ” ν•„λ“œμ²˜λŸΌ μž…λ ₯ ν•„λ“œλ₯Ό μ •μ˜ν•©λ‹ˆλ‹€:

<tr>
   <th>Diversion path</th>
   <td>
      <input type="text" maxlength="100" class="input_25_table" id="diversion_whitelist" autocorrect="off" autocapitalize="off">
   </td>
</tr>

λ˜ν•œ 섀정을 μ μš©ν•  λ•Œ 섀정을 λ‹€μ‹œ μ „μ†‘ν•˜λŠ” 데 μ‚¬μš©λ˜λŠ” μˆ¨κ²¨μ§„ amng_custom μž…λ ₯ ν•„λ“œλ₯Ό μ •μ˜ν•©λ‹ˆλ‹€:

<input type="hidden" name="amng_custom" id="amng_custom" value="">

그런 λ‹€μŒ initial() ν•¨μˆ˜μ—μ„œ κ°μ²΄μ—μ„œ 값을 κ²€μƒ‰ν•˜κ³  ν•„λ“œμ— ν• λ‹Ήν•©λ‹ˆλ‹€:

    if (custom_settings.diversion_whitelist == undefined)
        document.getElementById('diversion_whitelist').value = "/jffs/addons/diversion/diversion-white.txt";
    else
       document.getElementById('diversion_whitelist').value = custom_settings.diversion_whitelist;

μ‚¬μš©μžκ°€ 적용 λ²„νŠΌμ„ 클릭할 λ•Œ ν˜ΈμΆœλ˜λŠ” apply() ν•¨μˆ˜μ—μ„œ ν•„λ“œ 값을 κ²€μƒ‰ν•˜κ³ , 객체에 λ‹€μ‹œ μ €μž₯ν•˜κ³ , 객체λ₯Ό λ¬Έμžμ—΄λ‘œ λ³€ν™˜ν•˜κ³  μˆ¨κ²¨μ§„ amng_custom ν•„λ“œμ— ν• λ‹Ήν•œ λ‹€μŒ 양식을 μ œμΆœν•©λ‹ˆλ‹€:

function applySettings(){
        /* Retrieve value from input fields, and store in object */
        custom_settings.diversion_whitelist = document.getElementById('diversion_whitelist').value;

        /* Store object as a string in the amng_custom hidden input field */
        document.getElementById('amng_custom').value = JSON.stringify(custom_settings);

        /* Apply */
        showLoading();
        document.form.submit();
}

μ‚¬μš©μž μ •μ˜ μ„œλΉ„μŠ€

μ΄λŸ¬ν•œ μ„€μ • 변경에 λŒ€ν•œ μž‘μ—…μ„ μˆ˜ν–‰ν•˜κΈ° μœ„ν•΄ λΌμš°ν„°μ—μ„œ 일뢀 슀크립트λ₯Ό μ‹€ν–‰ν•΄μ•Ό ν•  κ²ƒμž…λ‹ˆλ‹€. service-event μŠ€ν¬λ¦½νŠΈλŠ” 이λ₯Ό μˆ˜ν–‰ν•  수 μžˆλŠ” μž₯μ†Œμž…λ‹ˆλ‹€. λΌμš°ν„°μ—μ„œ μΈμ‹ν•˜μ§€ λͺ»ν•˜λŠ” μ΄λ²€νŠΈμ— λŒ€ν•΄μ„œλ„ μ‹€ν–‰λ˜λ―€λ‘œ 자체 μ„œλΉ„μŠ€ ν•Έλ“€λŸ¬λ₯Ό 섀계할 수 μžˆμŠ΅λ‹ˆλ‹€.

νŽ˜μ΄μ§€μ—μ„œ μ„œλΉ„μŠ€λ₯Ό λ‹€μ‹œ μ‹œμž‘ν•˜λ„λ‘ κ΅¬μ„±ν•©λ‹ˆλ‹€. 예λ₯Ό λ“€μ–΄ "myservice"라고 ν˜ΈμΆœν•΄ λ΄…μ‹œλ‹€:

<input type="hidden" name="action_script" value="restart_myservice">

/jffs/addons/my_addon/myservice.sh 슀크립트λ₯Ό λ§Œλ“€κ³  이와 같은 λ‚΄μš©μ„ ν¬ν•¨μ‹œν‚΅λ‹ˆλ‹€:

#!/bin/sh

TYPE=$1
EVENT=$2

if [ "$EVENT" = "myservice" -a "$TYPE" = "restart" ]
then
        logger -t "myservice" "Restarting my service..."
        # do your stuff here, access /jffs/addons/custom_settings.txt, etc...
fi

그리고 _/jffs/scripts/service-event_μ—μ„œ 슀크립트λ₯Ό ν˜ΈμΆœν•©λ‹ˆλ‹€:

#!/bin/sh

### MyService start
/jffs/addons/my_addon/myservice.sh $*
### MyService end

μ°Έκ³ : λ³΅μž‘ν•œ 슀크립트λ₯Ό μ‹œμŠ€ν…œ /jffs/scripts/* μŠ€ν¬λ¦½νŠΈμ— μΆ”κ°€ν•˜μ§€ λ§ˆμ‹­μ‹œμ˜€. λŒ€μ‹  슀크립트λ₯Ό /jffs/addons/my_addon/ 폴더에 λ„£κ³  κ΄€λ ¨ μ‹œμŠ€ν…œ μŠ€ν¬λ¦½νŠΈμ—μ„œ ν•΄λ‹Ή 슀크립트λ₯Ό ν˜ΈμΆœν•©λ‹ˆλ‹€. μ΄λ ‡κ²Œν•˜λ©΄ μ‹œμž‘/μ’…λ£Œ 쀄을 μ°Ύμ•„ 전체 블둝을 μ œκ±°ν•˜κ³  λ‹€λ₯Έ μ„€μΉ˜λœ μ• λ“œμ˜¨μ— 영ν–₯을 μ£Όμ§€ μ•ŠλŠ” 제거 ν”„λ‘œκ·Έλž¨μ„ μ‰½κ²Œ κ°œλ°œν•  수 μžˆμŠ΅λ‹ˆλ‹€.

예제 μ‚¬μš©μž μ •μ˜ νŽ˜μ΄μ§€

λ‹€μŒμ€ μ‹œμž‘μ μœΌλ‘œ μ‚¬μš©ν•  수 μžˆλŠ” κ°„λ‹¨ν•œ 예제 νŽ˜μ΄μ§€μž…λ‹ˆλ‹€.

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="X-UA-Compatible" content="IE=Edge">
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<meta HTTP-EQUIV="Pragma" CONTENT="no-cache">
<meta HTTP-EQUIV="Expires" CONTENT="-1">
<link rel="shortcut icon" href="images/favicon.png">
<link rel="icon" href="images/favicon.png">
<title>ν…ŒμŠ€νŠΈ νŽ˜μ΄μ§€</title>
<link rel="stylesheet" type="text/css" href="index_style.css">
<link rel="stylesheet" type="text/css" href="form_style.css">
<script language="JavaScript" type="text/javascript" src="/state.js"></script>
<script language="JavaScript" type="text/javascript" src="/general.js"></script>
<script language="JavaScript" type="text/javascript" src="/popup.js"></script>
<script language="JavaScript" type="text/javascript" src="/help.js"></script>
<script type="text/javascript" language="JavaScript" src="/validator.js"></script>
<script>

var custom_settings = <% get_custom_settings(); %>;

function initial(){
        SetCurrentPage();
        show_menu();

        if (custom_settings.diversion_path == undefined)
                document.getElementById('diversion_path').value = "/tmp/default";
        else
                document.getElementById('diversion_path').value = custom_settings.diversion_path;
}


function SetCurrentPage() {
    /* Set the proper return pages */
    document.form.next_page.value = window.location.pathname.substring(1);
    document.form.current_page.value = window.location.pathname.substring(1);
}

function applySettings(){
        /* Retrieve value from input fields, and store in object */
        custom_settings.diversion_path = document.getElementById('diversion_path').value;

        /* Store object as a string in the amng_custom hidden input field */
        document.getElementById('amng_custom').value = JSON.stringify(custom_settings);

        /* Apply */
        showLoading();
        document.form.submit();
}
</script>

</head>
<body onload="initial();"  class="bg">
<div id="TopBanner"></div>
<div id="Loading" class="popup_bg"></div>
<iframe name="hidden_frame" id="hidden_frame" src="" width="0" height="0" frameborder="0"></iframe>
<form method="post" name="form" action="start_apply.htm" target="hidden_frame">
<input type="hidden" name="current_page" value="MyPage.asp">
<input type="hidden" name="next_page" value="MyPage.asp">
<input type="hidden" name="group_id" value="">
<input type="hidden" name="modified" value="0">
<input type="hidden" name="action_mode" value="apply">
<input type="hidden" name="action_wait" value="5">
<input type="hidden" name="first_time" value="">
<input type="hidden" name="action_script" value="">
<input type="hidden" name="preferred_lang" id="preferred_lang" value="<% nvram_get("preferred_lang"); %>">
<input type="hidden" name="firmver" value="<% nvram_get("firmver"); %>">
<input type="hidden" name="amng_custom" id="amng_custom" value="">

<table class="content" align="center" cellpadding="0" cellspacing="0">
<tr>
<td width="17">&nbsp;</td>
<td valign="top" width="202">
<div id="mainMenu"></div>
<div id="subMenu"></div>
</td>
<td valign="top">
<div id="tabMenu" class="submenuBlock"></div>
<table width="98%" border="0" align="left" cellpadding="0" cellspacing="0">
<tr>
<td align="left" valign="top">
<table width="760px" border="0" cellpadding="5" cellspacing="0" bordercolor="#6b8fa3" class="FormTitle" id="FormTitle">
<tr>
<td bgcolor="#4D595D" colspan="3" valign="top">
<div>&nbsp;</div>
<div class="formfonttitle">ν…ŒμŠ€νŠΈ νŽ˜μ΄μ§€ - μ‚¬μš©μž μ •μ˜ μ„€μ •</div>
<div style="margin:10px 0 10px 5px;" class="splitLine"></div>
<div class="formfontdesc"><#1838#></div>

<table width="100%" border="1" align="center" cellpadding="4" cellspacing="0" bordercolor="#6b8fa3" class="FormTable">

        <tr>
                <th>Diversion path</th>
                <td>
                        <input type="text" maxlength="100" class="input_25_table" id="diversion_path" autocorrect="off" autocapitalize="off">
                </td>
        </tr>
</table>
<div class="apply_gen">
        <input name="button" type="button" class="button_gen" onclick="applySettings();" value="적

용"/>
</div>
</form>

<div>
<table class="apply_gen">
<tr class="apply_gen" valign="top">
</tr>
</table>
</div>
</td>
</tr>
</table>
</td>
</tr>
</table>
</td>
<td width="10" align="center" valign="top"></td>
</tr>
</table>
<div id="footer"></div>
</body>
</html>
⚠️ **GitHub.com Fallback** ⚠️