Title: Updating pool settings via CGI on S19Pro
Post by: Morescratch on May 24, 2021, 05:25:49 PM
Hi all,
Just wondering if anyone has been able to update the pool settings on an S19Pro via CGI. I wrote a script that used to work on S9's but it doesn't work on S19Pro units. It also worked on the S17's. Any insights would be appreciated!
Title: Re: Updating pool settings via CGI on S19Pro
Post by: Morescratch on May 24, 2021, 06:39:45 PM
SOLVED. I had to POST the following to set_miner_conf.cgi:
data = { "pools": [ { "url": "antpool.com:3333", "user": "antminer1, "pass": "" }, { "url": "antpool.com:3333", "user": "antminer1", "pass": "" }, { "url": "antpool.com:3333", "user": "antminer1", "pass": "" } ] }
Title: Re: Updating pool settings via CGI on S19Pro
Post by: s3binator on August 06, 2021, 02:59:22 PM
I have previously always sent a payload using urllib.parse.urlencode through request for S9 generation miners, I am getting an illegal request error using the same where the set_miner_conf.cgi file I unpacked from a 2021 April firmware is : #!/bin/sh
function cgi_get_POST_vars() { [ "${CONTENT_TYPE}" != "application/x-www-form-urlencoded" ] && \ echo "Warning: you should probably use MIME type "\ "application/x-www-form-urlencoded!" 1>&2 [ -z "$QUERY_STRING_POST" \ -a "$REQUEST_METHOD" = "POST" -a ! -z "$CONTENT_LENGTH" ] && \ read -n $CONTENT_LENGTH QUERY_STRING_POST return }
function update_config_file() { config_file="/config/cgminer.conf" echo "{" > $config_file echo "\"pools\" : [" >> $config_file echo "{" >> $config_file echo "\"url\" : \"$ant_pool1url\"," >> $config_file echo "\"user\" : \"$ant_pool1user\"," >> $config_file echo "\"pass\" : \"$ant_pool1pw\"" >> $config_file echo "}," >> $config_file echo "{" >> $config_file echo "\"url\" : \"$ant_pool2url\"," >> $config_file echo "\"user\" : \"$ant_pool2user\"," >> $config_file echo "\"pass\" : \"$ant_pool2pw\"" >> $config_file echo "}," >> $config_file echo "{" >> $config_file echo "\"url\" : \"$ant_pool3url\"," >> $config_file echo "\"user\" : \"$ant_pool3user\"," >> $config_file echo "\"pass\" : \"$ant_pool3pw\"" >> $config_file echo "}" >> $config_file echo "]" >> $config_file echo "," >> $config_file echo "\"api-listen\" : true," >> $config_file echo "\"api-network\" : true," >> $config_file echo "\"api-groups\" : \"A:stats:pools:devs:summary:version\"," >> $config_file echo "\"api-allow\" : \"A:0/0,W:*\"," >> $config_file echo "\"bitmain-fan-ctrl\" : $ant_fan_customize_value," >> $config_file echo "\"bitmain-fan-pwm\" : \"$ant_fan_customize_switch\"," >> $config_file echo "\"bitmain-use-vil\" : true," >> $config_file echo "\"bitmain-freq\" : \"$ant_freq\"," >> $config_file echo "\"bitmain-voltage\" : \"$ant_voltage\"," >> $config_file echo "\"bitmain-ccdelay\" : \"$ant_ccdelay\"," >> $config_file echo "\"bitmain-pwth\" : \"$ant_pwth\"," >> $config_file echo "\"bitmain-work-mode\" : \"$ant_miner_mode\"," >> $config_file echo "\"bitmain-freq-level\" : \"$ant_freq_level\"" >> $config_file #echo "\"miner-mode\" : \"$ant_work_mode\"," >> $config_file #echo "\"freq-level\" : \"$ant_freq_level\"" >> $config_file echo "}" >> $config_file }
function parse_configs() { echo $1 | jq -r $2 }
function old_configs() { cat /config/cgminer.conf | jq -r $1 } reload=false cgi_get_POST_vars
#echo $QUERY_STRING_POST > /tmp/aaa #QUERY_STRING_POST=`cat /tmp/aaa`
echo $QUERY_STRING_POST | jq > /dev/null
if [[ $? -eq 0 ]]&&[[ ! "$QUERY_STRING_POST"x == ""x ]]; then QUERY_STRING_POST=`echo $QUERY_STRING_POST | jq -c` ret=`parse_configs $QUERY_STRING_POST ".pools[0].url"` if [ "$ret"x == "null"x ];then ant_pool1url=`old_configs ".pools[0].url"` else ant_pool1url=$ret fi ret=`parse_configs $QUERY_STRING_POST ".pools[0].user"` if [ "$ret"x == "null"x ];then ant_pool1user=`old_configs ".pools[0].user"` else ant_pool1user=$ret fi ret=`parse_configs $QUERY_STRING_POST ".pools[0].pass"` if [ "$ret"x == "null"x ];then ant_pool1pw=`old_configs ".pools[0].pass"` else ant_pool1pw=$ret fi ret=`parse_configs $QUERY_STRING_POST ".pools[1].url"` if [ "$ret"x == "null"x ];then ant_pool2url=`old_configs ".pools[1].url"` else ant_pool2url=$ret fi ret=`parse_configs $QUERY_STRING_POST ".pools[1].user"` if [ "$ret"x == "null"x ];then ant_pool2user=`old_configs ".pools[1].user"` else ant_pool2user=$ret fi ret=`parse_configs $QUERY_STRING_POST ".pools[1].pass"` if [ "$ret"x == "null"x ];then ant_pool2pw=`old_configs ".pools[1].pass"` else ant_pool2pw=$ret fi ret=`parse_configs $QUERY_STRING_POST ".pools[2].url"` if [ "$ret"x == "null"x ];then ant_pool3url=`old_configs ".pools[2].url"` else ant_pool3url=$ret fi ret=`parse_configs $QUERY_STRING_POST ".pools[2].user"` if [ "$ret"x == "null"x ];then ant_pool3user=`old_configs ".pools[2].user"` else ant_pool3user=$ret fi ret=`parse_configs $QUERY_STRING_POST ".pools[2].pass"` if [ "$ret"x == "null"x ];then ant_pool3pw=`old_configs ".pools[2].pass"` else ant_pool3pw=$ret fi ret=`parse_configs $QUERY_STRING_POST '."bitmain-fan-ctrl"'` if [ "$ret"x == "null"x ];then ant_fan_customize_value=`old_configs '."bitmain-fan-ctrl"'` else ant_fan_customize_value=$ret fi ret=`parse_configs $QUERY_STRING_POST '."bitmain-fan-pwm"'` if [ "$ret"x == "null"x ];then ant_fan_customize_switch=`old_configs '."bitmain-fan-pwm"'` else ant_fan_customize_switch=$ret fi ret=`parse_configs $QUERY_STRING_POST '."miner-mode"'` if [[ "$ret"x == "null"x ]]||([[ $ret -ne 0 ]]&&[[ $ret -ne 1 ]]);then ant_miner_mode=`old_configs '."bitmain-work-mode"'` reload=true else old_ant_miner_mode=`old_configs '."bitmain-work-mode"'` ant_miner_mode=$ret if [ $old_ant_miner_mode == $ret ];then reload=true fi fi ret=`parse_configs $QUERY_STRING_POST '."freq-level"'` if [ "$ret"x == "null"x ];then ant_freq_level=`old_configs '."bitmain-freq-level"'` else ant_freq_level=$ret fi ant_ccdelay=`old_configs '."bitmain-ccdelay"'` ant_pwth=`old_configs '."bitmain-pwth"'` ant_freq=`old_configs '."bitmain-freq"'` ant_voltage=`old_configs '."bitmain-voltage"'` # ret=`parse_configs $QUERY_STRING_POST '."bitmain-freq"'` # if [ "$ret"x == "null"x ];then # ant_freq=`old_configs '."bitmain-freq"'` # else # ant_freq=$ret # fi # ret=`parse_configs $QUERY_STRING_POST '."bitmain-voltage"'` # if [ "$ret"x == "null"x ];then # ant_voltage=`old_configs '."bitmain-voltage"'` # else # ant_voltage=$ret # fi # ret=`parse_configs $QUERY_STRING_POST '."bitmain-ccdelay"'` # if [ "$ret"x == "null"x ];then # ant_ccdelay=`old_configs '."bitmain-ccdelay"'` # else # ant_ccdelay=$ret # fi # ret=`parse_configs $QUERY_STRING_POST '."bitmain-pwth"'` # if [ "$ret"x == "null"x ];then # ant_pwth=`old_configs '."bitmain-pwth"'` # else # ant_pwth=$ret # fi
update_config_file sync sleep 1s if [ $reload == true ];then cgminer-api {\"command\":\"reload\",\"new_api\":true} > /dev/null & else sudo /etc/init.d/S70cgminer restart > /dev/null & fi
echo "{\"stats\":\"success\",\"code\":\"M000\",\"msg\":\"OK!\"}" else echo "{\"stats\":\"error\",\"code\":\"M001\",\"msg\":\"Illegal request!\"}" fi Are you saying to just literally post what you provided in your own response to set_miner_conf.cgi instead? I also need to use set_network_conf to update hostname, so would like to replicate exactly what the web interface sends to its own cgi files when manually submitting a manual input. Anyone had any luck with this? I get the following response <?xml version="1.0" encoding="iso-8859-1"?> <!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" xml:lang="en" lang="en"> <head> <title>500 - Internal Server Error</title> </head> <body> <h1>500 - Internal Server Error</h1> </body> </html> when sending what I always had to S9 (and equivalent), to the set_network_conf,cgi file as well. Any more insite would be great! Thanks!
Title: Re: Updating pool settings via CGI on S19Pro
Post by: s3binator on August 06, 2021, 03:21:13 PM
I guess the the new cgi_get_POST_vars function accepts POST message in a different way than before, I am no bash expert and cant exactly decipher what is accepted by the cgi script. The set_network_conf file from the unpacked firmware is: #!/bin/sh
function cgi_get_POST_vars() { # check content type # FIXME: not sure if we could handle uploads with this.. [ "${CONTENT_TYPE}" != "application/x-www-form-urlencoded" ] && \ echo "Warning: you should probably use MIME type "\ "application/x-www-form-urlencoded!" 1>&2 # save POST variables (only first time this is called) [ -z "$QUERY_STRING_POST" \ -a "$REQUEST_METHOD" = "POST" -a ! -z "$CONTENT_LENGTH" ] && \ read -n $CONTENT_LENGTH QUERY_STRING_POST return }
function update_network_conf_file() { config_file="/config/network.conf" if [ "${ant_conf_nettype}" == "1" ]; then echo "dhcp=true" > $config_file echo "hostname=${ant_conf_hostname}" >> $config_file else echo "hostname=${ant_conf_hostname}" > $config_file echo "ipaddress=${ant_conf_ipaddress}" >> $config_file echo "netmask=${ant_conf_netmask}" >> $config_file echo "gateway=${ant_conf_gateway}" >> $config_file echo "dnsservers=${ant_conf_dnsservers}" >> $config_file fi }
function check_param() { if ! echo "$ant_conf_hostname" | egrep -q "$regex_hostname"; then return 1 fi
if [ "${ant_conf_nettype}" == "2" ]; then if ! echo "${ant_conf_ipaddress}" | egrep -q "$regex_ip"; then return 2 fi if ! echo "${ant_conf_netmask}" | egrep -q "$regex_ip" ; then return 3 fi if ! echo "${ant_conf_gateway}" | egrep -q "$regex_ip"; then return 4 fi if ! echo "${ant_conf_dnsservers}" | egrep -q "$regex_ip"; then return 5 fi fi return 0 }
ant_conf_nettype= ant_conf_hostname= ant_conf_ipaddress= ant_conf_netmask= ant_conf_gateway= ant_conf_dnsservers= stats="error" err_flag=0 msg=
regex_ip="^[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}$" regex_hostname="^(([a-zA-Z]|[a-zA-Z][a-zA-Z0-9-]*[a-zA-Z0-9])\.)*([A-Za-z]|[A-Za-z][A-Za-z0-9-]*[A-Za-z0-9])$"
cgi_get_POST_vars #echo $QUERY_STRING_POST > /tmp/a #QUERY_STRING_POST=`cat /tmp/a` ant_conf_nettype=`echo $QUERY_STRING_POST | jq -r ".ipPro"` ant_conf_hostname=`echo $QUERY_STRING_POST | jq -r ".ipHost"` if [ "${ant_conf_nettype}" == "2" ];then ant_conf_ipaddress=`echo $QUERY_STRING_POST | jq -r ".ipAddress"` ant_conf_netmask=`echo $QUERY_STRING_POST | jq -r ".ipSub"` ant_conf_gateway=`echo $QUERY_STRING_POST | jq -r ".ipGateway"` ant_conf_dnsservers=`echo $QUERY_STRING_POST | jq -r ".ipDns"` fi
check_param err_flag=$?
if [ $err_flag -eq 0 ]; then msg="OK!" stats="success" fi if [ $err_flag -eq 1 ]; then msg="Hostname invalid!" fi if [ $err_flag -eq 2 ]; then msg="IP invalid!" fi if [ $err_flag -eq 3 ]; then msg="Netmask invalid!" fi if [ $err_flag -eq 4 ]; then msg="Gateway invalid!" fi if [ $err_flag -eq 5 ]; then msg="DNS invalid!" fi
echo "{\"stats\":\"$stats\",\"code\":\"N00$err_flag\",\"msg\":\"$msg\"}"
if [ $err_flag -eq 0 ];then update_network_conf_file sync sleep 1s sudo /etc/init.d/S38network restart > /dev/null 2>&1 & fi
for completeness. Is the get POST variables function specifying to the exact JSON styled message as OP suggests?
Title: Re: Updating pool settings via CGI on S19Pro
Post by: s3binator on August 06, 2021, 07:35:22 PM
Hi Everyone, cant post the full js code snippets as they are too long and I get empty body error. I got things to work like OP suggested. I found the following js file miner.b0d88d.js in the www/js folder. It handles the request to set_miner_conf.cgi, only showing the relavent snippet: 68: function(e, n, t) { "use strict"; t.r(n); var r = t(0), o = (t(5), t(3), r.a); o.loadProperties(), new Vue({ el: "#MinerSet", data: () => ({ showLoading: !1, minerForm: { "bitmain-fan-ctrl": !1, "bitmain-fan-pwm": "100", "miner-mode": 0, "freq-level": 100, pools: [{ url: "", user: "", pass: "" }, { url: "", user: "", pass: "" }, { url: "", user: "", pass: "" }] }, modeList: [{ text: "", id: 0 }, { text: "", id: 1 }] }), created() { this.initData(); let e = window.ee; e && (e.removeEvent("lang-changed"), e.addListener("lang-changed", this.updateModeList)) }, mounted() { this.doMinerType() }, computed: { trShow() { return 2 === this.minerForm["miner-mode"] } }, methods: { doMinerType() { $.ajax({ url: "/cgi-bin/get_system_info.cgi", dataType: "json", type: "GET", timeout: 3e3, async: !0, processData: !1, contentType: !1, success: function(e) { -1 != e.minertype.indexOf("Hydro") && (document.querySelector(".miner-machine-form table.table-form tbody tr:first-of-type").style.display = "none") } }) }, initData() { var e = this; this.updateModeList(), $.ajax({ url: "/cgi-bin/get_miner_conf.cgi", dataType: "json", type: "GET", async: !1, processData: !1, contentType: !1, success: function(n) { if (n) { for (i = 0; i < 3; i++) e.minerForm.pools[i].url = n.pools[i].url, e.minerForm.pools[i].user = n.pools[i].user, e.minerForm.pools[i].pass = n.pools[i].pass; e.minerForm["bitmain-fan-ctrl"] = n["bitmain-fan-ctrl"], e.minerForm["bitmain-fan-pwm"] = n["bitmain-fan-pwm"], e.minerForm["miner-mode"] = n["bitmain-work-mode"], e.minerForm["freq-level"] = n["bitmain-freq-level"] } }, error: function() { o.showWarn("error", "commonCode") } }) }, updateModeList() { this.modeList[0].text = $.i18[Suspicious link removed]op("modeNormal"), this.modeList[1].text = $.i18[Suspicious link removed]op("modeSleep") }, modeSelectChange(e) { this.minerForm["miner-mode"] = e }, checkFun(e) { this.$set(this.minerForm, e, !this.minerForm[e]) }, saveMineSet() { var e = JSON.stringify(this.minerForm), n = this; n.showLoading = !0, $.ajax({ url: "/cgi-bin/set_miner_conf.cgi", dataType: "json", type: "POST", async: !0, data: e, processData: !1, contentType: !1, success: function(e) { n.showLoading = !1; var t = e; "success" === t.stats ? o.showWarn("success",[Suspicious link removed]de) : o.showWarn("error",[Suspicious link removed]de), r.a.loadInner("miner") }, error: function(e) { n.showLoading = !1, o.showWarn("error", "commonCode"), r.a.loadInner("miner") }
where the function saveMineSet(), does just post a JSON formatted string to set_miner_conf.cgi. I ended up using something like: import requests from requests.auth import HTTPDigestAuth import json
confData = {} confData['pools']= [] confData['pools'].append({"url": configInfo['url1'], "user": configInfo['user1'], "pass": configInfo['pass1']}) confData['pools'].append({"url": configInfo['url2'], "user": configInfo['user2'], "pass": configInfo['pass2']}) confData['pools'].append({"url": configInfo['url3'], "user": configInfo['user3'], "pass": configInfo['pass3']})
confPayload = json.dumps(confData) setConfAddr = '/cgi-bin/set_miner_conf.cgi' try: r = requests.post('http://' + self.ip + setConfAddr, auth=HTTPDigestAuth(self.webUserName, self.webPassword), data=confPayload) except: self.error("post ant miner configuration failed.")
in python. The js file should give you insight on how to change some of the other configuration variables if needed. Setting network configuration is similar, and how to format the JSON that is posted to set_network_conf.cgi can be seen in the ip.1cc6a.js file (also in www/js/), again only showing relevant snippet: ! 69: function(n, e, t) { "use strict"; t.r(e); var r = t(0), o = (t(5), t(3), r.a); o.loadProperties(), new Vue({ el: "#ipSet", data: () => ({ showLoading: !1, ipForm: { ipHost: "", ipPro: 1, ipAddress: "", ipSub: "", ipGateway: "", ipDns: "" }, ipInfo: { ip: "", mac: "", mask: "" }, sltList: [{ text: "DHCP", id: 1 }, { text: "Static", id: 2 }] }), created() { this.initData() }, computed: { trShow() { return 2 === this.ipForm.ipPro } }, mounted() {}, methods: { initData() { var n = this; $.ajax({ url: "/cgi-bin/get_network_info.cgi", dataType: "json", type: "GET", async: !1, processData: !1, contentType: !1, success: function(e) { e && (n.ipForm.ipHost = e.conf_hostname, n.ipForm.ipPro = "DHCP" == e.conf_nettype ? 1 : 2, n.ipForm.ipAddress = e.conf_ipaddress, n.ipForm.ipSub = e.conf_netmask, n.ipForm.ipGateway = e.conf_gateway, n.ipForm.ipDns = e.conf_dnsservers, n.ipInfo.ip = e.ipaddress, n.ipInfo.mac = e.macaddr, n.ipInfo.mask = e.netmask) }, error: function() { r.a.showWarn("error") } }) }, proSelectChange(n) { this.ipForm.ipPro = n }, saveIpSet() { var n = JSON.stringify(this.ipForm), e = this; e.showLoading = !0, $.ajax({ url: "/cgi-bin/set_network_conf.cgi", dataType: "json", type: "POST", async: !0, data: n, processData: !1, contentType: !1, success: function(n) { e.showLoading = !1; var t = n; "success" === t.stats ? o.showWarn("success",[Suspicious link removed]de) : o.showWarn("error",[Suspicious link removed]de), r.a.loadInner("ip") }, error: function(n) { e.showLoading = !1, o.showWarn("error", "commonCode"), r.a.loadInner("ip") }
I hope this helps anyone in the future from having to unpack the firmware and figure out how the web interface communicates with the cgi-bin files.
|