upnpc to add/delete/show port mapping entry

1. discover
wirshark filter expression : tcp.port == 12470 && tcp.len > 0 || ( http && udp && (ip.addr==192.168.0.4 || ip.addr == 239.255.255.250) )
ip.addr == 239.255.255.250 is the default multicast address, http use udp protocol(not tcp at all)

[zhang@localhost bin]$ ./upnpc -s
List of UPNP devices found on the network :
 desc: http://192.168.0.1:12470/rootDesc.xml
 st: urn:schemas-upnp-org:device:InternetGatewayDevice:1

Found valid IGD : http://192.168.0.1:12470/ctl/IPConn
Local LAN ip address : 192.168.0.4
Connection Type : IP_Routed
Status : Connected, uptime=61831s, LastConnectionError : ERROR_NONE
  Time started : Fri May 11 21:17:45 2012
MaxBitRateDown : 4200000 bps (4.2 Mbps)   MaxBitRateUp 4200000 bps (4.2 Mbps)
ExternalIPAddress = 192.168.0.1
Bytes:   Sent: 453116748    Recv: 63475745
Packets: Sent:   484040    Recv:   521903
[zhang@localhost bin]$

////////////////////////////////////////////////////////////////
M-SEARCH * HTTP/1.1
HOST: 239.255.255.250:1900
ST: urn:schemas-upnp-org:device:InternetGatewayDevice:1
MAN: "ssdp:discover"
MX: 2

================================================================
HTTP/1.1 200 OK
CACHE-CONTROL: max-age=120
ST: urn:schemas-upnp-org:device:InternetGatewayDevice:1
USN: uuid:00000000-0000-0000-0000-000000000000::urn:schemas-upnp-org:device:InternetGatewayDevice:1
EXT:
SERVER: LINUX-2.6 UPnP/1.0 MiniUPnPd/1.5
LOCATION: http://192.168.0.1:12470/rootDesc.xml

/////////////////////////////////////////////////////////////////
GET /rootDesc.xml HTTP/1.1
Host: 192.168.0.1:12470
Connection: Close
User-Agent: Fedora/10, UPnP/1.0, MiniUPnPc/1.6


HTTP/1.1 200 OK
Content-Type: text/xml
Connection: close
Content-Length: 2574
Server: LINUX-2.6 UPnP/1.0 MiniUPnPd/1.5

=================================================================
<?xml version="1.0"?>
<root xmlns="urn:schemas-upnp-org:device-1-0">
  <specVersion>
    <major>1</major>
    <minor>0</minor>
  </specVersion>
  <device>
    <deviceType>urn:schemas-upnp-org:device:InternetGatewayDevice:1</deviceType>
    <friendlyName>NETCORE NR235W</friendlyName>
    <manufacturer>NETCORE</manufacturer>
    <manufacturerURL>http://www.netcore.com.cn/</manufacturerURL>
    <modelDescription>NETCORE NR235W</modelDescription>
    <modelName>NR235W</modelName>
    <modelNumber>NR235W</modelNumber>
    <modelURL>http://www.netcore.com.cn/</modelURL>
    <serialNumber>00000000</serialNumber>
    <UDN>uuid:00000000-0000-0000-0000-000000000000</UDN>
    <serviceList>
      <service>
      <serviceType>urn:schemas-upnp-org:service:Layer3Forwarding:1</serviceType>
      <serviceId>urn:upnporg:serviceId:Layer3Forwarding1</serviceId>
      <controlURL>/ctl/L3F</controlURL>
      <eventSubURL>/evt/L3F</eventSubURL>
      <SCPDURL>/L3F.xml</SCPDURL>
      </service>
    </serviceList>
    <deviceList>
      <device>
    <deviceType>urn:schemas-upnp-org:device:WANDevice:1</deviceType>
    <friendlyName>WANDevice</friendlyName>
    <manufacturer>MiniUPnP</manufacturer>
    <manufacturerURL>http://www.netcore.com.cn/</manufacturerURL>
    <modelDescription>WAN Device</modelDescription>
    <modelName>WANDevice</modelName>
    <modelNumber>20110126</modelNumber>
    <modelURL>http://www.netcore.com.cn/</modelURL>
    <serialNumber>00000000</serialNumber>
    <UDN>uuid:00000000-0000-0000-0000-000000000000</UDN>
    <UPC>MINIUPNPD</UPC>
      <serviceList>
        <service>
          <serviceType>urn:schemas-upnp-org:service:WANCommonInterfaceConfig:1</serviceType>
          <serviceId>urn:upnp-org:serviceId:WANCommonIFC1</serviceId>
          <controlURL>/ctl/CmnIfCfg</controlURL>
          <eventSubURL>/evt/CmnIfCfg</eventSubURL>
          <SCPDURL>/WANCfg.xml</SCPDURL>
        </service>
      </serviceList>
    <deviceList>
    <device>
      <deviceType>urn:schemas-upnp-org:device:WANConnectionDevice:1</deviceType>
      <friendlyName>WANConnectionDevice</friendlyName>
      <manufacturer>MiniUPnP</manufacturer>
      <manufacturerURL>http://www.netcore.com.cn/</manufacturerURL>
      <modelDescription>MiniUPnP daemon</modelDescription>
      <modelName>MiniUPnPd</modelName>
      <modelNumber>20110126</modelNumber>
      <modelURL>http://www.netcore.com.cn/</modelURL>
      <serialNumber>00000000</serialNumber>
      <UDN>uuid:00000000-0000-0000-0000-000000000000</UDN>
      <UPC>MINIUPNPD</UPC>
      <serviceList>
        <service>
          <serviceType>urn:schemas-upnp-org:service:WANIPConnection:1</serviceType>
          <serviceId>urn:upnp-org:serviceId:WANIPConn1</serviceId>
          <controlURL>/ctl/IPConn</controlURL>
          <eventSubURL>/evt/IPConn</eventSubURL>
          <SCPDURL>/WANIPCn.xml</SCPDURL>
        </service>
      </serviceList>
    </device>
      </deviceList>
      </device>
    </deviceList>
    <presentationURL>http://192.168.0.1/</presentationURL>
  </device>
</root>


2.
wirshark filter expression : tcp.len > 0 && tcp.port == 12470 && (ip.addr==192.168.0.4 || ip.addr == 239.255.255.250)

[zhang@localhost bin]$ ./upnpc -l
List of UPNP devices found on the network :
 desc: http://192.168.0.1:12470/rootDesc.xml
 st: urn:schemas-upnp-org:device:InternetGatewayDevice:1

Found valid IGD : http://192.168.0.1:12470/ctl/IPConn
Local LAN ip address : 192.168.0.4
Connection Type : IP_Routed
Status : Connected, uptime=62685s, LastConnectionError : ERROR_NONE
  Time started : Fri May 11 21:17:45 2012
MaxBitRateDown : 4200000 bps (4.2 Mbps)   MaxBitRateUp 4200000 bps (4.2 Mbps)
ExternalIPAddress = 192.168.0.1
 i protocol exPort->inAddr:inPort description remoteHost leaseTime
 0 TCP    23->192.168.0.1:23    'libminiupnpc' '' 0
 1 UDP  5060->192.168.0.4:5060  'libminiupnpc' '' 0
 2 UDP 59131->192.168.0.3:59131 'Teredo' '' 0
GetGenericPortMappingEntry() returned 713 (SpecifiedArrayIndexInvalid)
[zhang@localhost bin]$

//////////////////////////////////////////////////////////////////
POST /ctl/IPConn HTTP/1.1
Host: 192.168.0.1:12470
User-Agent: Fedora/10, UPnP/1.0, MiniUPnPc/1.6
Content-Length: 341
Content-Type: text/xml
SOAPAction: "urn:schemas-upnp-org:service:WANIPConnection:1#GetGenericPortMappingEntry"
Connection: Close
Cache-Control: no-cache
Pragma: no-cache

<?xml version="1.0"?>
<s:Envelope xmlns:s="http://schemas.xmlsoap.org/soap/envelope/" s:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/"><s:Body><u:GetGenericPortMappingEntry xmlns:u="urn:schemas-upnp-org:service:WANIPConnection:1"><NewPortMappingIndex>1</NewPortMappingIndex></u:GetGenericPortMappingEntry></s:Body></s:Envelope>

=================================================================
HTTP/1.1 200 OK
Content-Type: text/xml
Connection: close
Content-Length: 633
Server: LINUX-2.6 UPnP/1.0 MiniUPnPd/1.5

<?xml version="1.0"?>
<s:Envelope xmlns:s="http://schemas.xmlsoap.org/soap/envelope/" s:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/"><s:Body><u:GetGenericPortMappingEntryResponse xmlns:u="urn:schemas-upnp-org:service:WANIPConnection:1"><NewRemoteHost></NewRemoteHost><NewExternalPort>5060</NewExternalPort><NewProtocol>UDP</NewProtocol><NewInternalPort>5060</NewInternalPort><NewInternalClient>192.168.0.4</NewInternalClient><NewEnabled>1</NewEnabled><NewPortMappingDescription>libminiupnpc</NewPortMappingDescription><NewLeaseDuration>0</NewLeaseDuration></u:GetGenericPortMappingEntryResponse></s:Body></s:Envelope>

//////////////////////////////////////////////////////////////////
POST /ctl/IPConn HTTP/1.1
Host: 192.168.0.1:12470
User-Agent: Fedora/10, UPnP/1.0, MiniUPnPc/1.6
Content-Length: 341
Content-Type: text/xml
SOAPAction: "urn:schemas-upnp-org:service:WANIPConnection:1#GetGenericPortMappingEntry"
Connection: Close
Cache-Control: no-cache
Pragma: no-cache

<?xml version="1.0"?>
<s:Envelope xmlns:s="http://schemas.xmlsoap.org/soap/envelope/" s:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/">
  <s:Body>
    <u:GetGenericPortMappingEntry xmlns:u="urn:schemas-upnp-org:service:WANIPConnection:1">
    <NewPortMappingIndex>2</NewPortMappingIndex>
    </u:GetGenericPortMappingEntry>
  </s:Body>
</s:Envelope>

=================================================================
HTTP/1.1 200 OK
Content-Type: text/xml
Connection: close
Content-Length: 629
Server: LINUX-2.6 UPnP/1.0 MiniUPnPd/1.5

<?xml version="1.0"?>
<s:Envelope xmlns:s="http://schemas.xmlsoap.org/soap/envelope/" s:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/">
  <s:Body>
    <u:GetGenericPortMappingEntryResponse xmlns:u="urn:schemas-upnp-org:service:WANIPConnection:1">
    <NewRemoteHost></NewRemoteHost>
    <NewExternalPort>59131</NewExternalPort>
    <NewProtocol>UDP</NewProtocol>
    <NewInternalPort>59131</NewInternalPort>
    <NewInternalClient>192.168.0.3</NewInternalClient>
    <NewEnabled>1</NewEnabled>
    <NewPortMappingDescription>Teredo</NewPortMappingDescription>
    <NewLeaseDuration>0</NewLeaseDuration>
    </u:GetGenericPortMappingEntryResponse>
  </s:Body>
</s:Envelope>


//////////////////////////////////////////////////////////////////
POST /ctl/IPConn HTTP/1.1
Host: 192.168.0.1:12470
User-Agent: Fedora/10, UPnP/1.0, MiniUPnPc/1.6
Content-Length: 341
Content-Type: text/xml
SOAPAction: "urn:schemas-upnp-org:service:WANIPConnection:1#GetGenericPortMappingEntry"
Connection: Close
Cache-Control: no-cache
Pragma: no-cache

<?xml version="1.0"?>
<s:Envelope xmlns:s="http://schemas.xmlsoap.org/soap/envelope/" s:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/">
  <s:Body>
    <u:GetGenericPortMappingEntry xmlns:u="urn:schemas-upnp-org:service:WANIPConnection:1">
    <NewPortMappingIndex>3</NewPortMappingIndex>
    </u:GetGenericPortMappingEntry>
  </s:Body>
</s:Envelope>

=================================================================
HTTP/1.1 500 Internal Server Error
Content-Type: text/xml
Connection: close
Content-Length: 410
Server: LINUX-2.6 UPnP/1.0 MiniUPnPd/1.5

<s:Envelope xmlns:s="http://schemas.xmlsoap.org/soap/envelope/" s:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/">
  <s:Body>
    <s:Fault>
    <faultcode>s:Client</faultcode>
    <faultstring>UPnPError</faultstring>
    <detail>
      <UPnPError xmlns="urn:schemas-upnp-org:control-1-0">
      <errorCode>713</errorCode>
      <errorDescription>SpecifiedArrayIndexInvalid</errorDescription>
      </UPnPError>
    </detail>
    </s:Fault>
  </s:Body>
</s:Envelope>

3.AddPortMapping
wirshark filter expression :
tcp.len > 0 && tcp.port == 12470 && (ip.addr==192.168.0.4 || ip.addr == 239.255.255.250) || http

[zhang@localhost bin]$ ./upnpc -a 192.168.0.4 5070 5070 udp
List of UPNP devices found on the network :
 desc: http://192.168.0.1:12470/rootDesc.xml
 st: urn:schemas-upnp-org:device:InternetGatewayDevice:1

Found valid IGD : http://192.168.0.1:12470/ctl/IPConn
Local LAN ip address : 192.168.0.4
ExternalIPAddress = 192.168.0.1
InternalIP:Port = 192.168.0.4:5070
external 192.168.0.1:5070 UDP is redirected to internal 192.168.0.4:5070 (duration=0)
[zhang@localhost bin]$
[zhang@localhost bin]$
[zhang@localhost bin]$
[zhang@localhost bin]$ ./upnpc -l
List of UPNP devices found on the network :
 desc: http://192.168.0.1:12470/rootDesc.xml
 st: urn:schemas-upnp-org:device:InternetGatewayDevice:1

Found valid IGD : http://192.168.0.1:12470/ctl/IPConn
Local LAN ip address : 192.168.0.4
Connection Type : IP_Routed
Status : Connected, uptime=97618s, LastConnectionError : ERROR_NONE
  Time started : Fri May 11 21:17:45 2012
MaxBitRateDown : 4200000 bps (4.2 Mbps)   MaxBitRateUp 4200000 bps (4.2 Mbps)
ExternalIPAddress = 192.168.0.1
 i protocol exPort->inAddr:inPort description remoteHost leaseTime
 0 UDP  5070->192.168.0.4:5070  'libminiupnpc' '' 0
 1 UDP 51084->192.168.0.3:51084 'Teredo' '' 0
 2 TCP    23->192.168.0.1:23    'libminiupnpc' '' 0
 3 UDP  5060->192.168.0.4:5060  'libminiupnpc' '' 0
 4 UDP 59131->192.168.0.3:59131 'Teredo' '' 0
GetGenericPortMappingEntry() returned 713 (SpecifiedArrayIndexInvalid)
[zhang@localhost bin]$

//////////////////////////////////////////////////////////////////////
POST /ctl/IPConn HTTP/1.1
Host: 192.168.0.1:12470
User-Agent: Fedora/10, UPnP/1.0, MiniUPnPc/1.6
Content-Length: 593
Content-Type: text/xml
SOAPAction: "urn:schemas-upnp-org:service:WANIPConnection:1#AddPortMapping"
Connection: Close
Cache-Control: no-cache
Pragma: no-cache

<?xml version="1.0"?>
<s:Envelope xmlns:s="http://schemas.xmlsoap.org/soap/envelope/" s:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/">
  <s:Body>
    <u:AddPortMapping xmlns:u="urn:schemas-upnp-org:service:WANIPConnection:1">
    <NewRemoteHost></NewRemoteHost>
    <NewExternalPort>5070</NewExternalPort>
    <NewProtocol>UDP</NewProtocol>
    <NewInternalPort>5070</NewInternalPort>
    <NewInternalClient>192.168.0.4</NewInternalClient>
    <NewEnabled>1</NewEnabled>
    <NewPortMappingDescription>libminiupnpc</NewPortMappingDescription>
    <NewLeaseDuration>0</NewLeaseDuration>
    </u:AddPortMapping>
  </s:Body>
</s:Envelope>

===============================================================
HTTP/1.1 200 OK
Content-Type: text/xml
Connection: close
Content-Length: 263
Server: LINUX-2.6 UPnP/1.0 MiniUPnPd/1.5

<?xml version="1.0"?>
<s:Envelope xmlns:s="http://schemas.xmlsoap.org/soap/envelope/" s:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/">
  <s:Body>
  <u:AddPortMappingResponse xmlns:u="urn:schemas-upnp-org:service:WANIPConnection:1"/>
  </s:Body>
</s:Envelope>

4.DeletePortMapping
wirshark filter expression :
tcp.len > 0 && tcp.port == 12470 && (ip.addr==192.168.0.4 || ip.addr == 239.255.255.250) || http

[zhang@localhost bin]$ ./upnpc -d 51084 udp
List of UPNP devices found on the network :
 desc: http://192.168.0.1:12470/rootDesc.xml
 st: urn:schemas-upnp-org:device:InternetGatewayDevice:1

Found valid IGD : http://192.168.0.1:12470/ctl/IPConn
Local LAN ip address : 192.168.0.4
UPNP_DeletePortMapping() returned : 0
[zhang@localhost bin]$
[zhang@localhost bin]$
[zhang@localhost bin]$ ./upnpc -l
List of UPNP devices found on the network :
 desc: http://192.168.0.1:12470/rootDesc.xml
 st: urn:schemas-upnp-org:device:InternetGatewayDevice:1

Found valid IGD : http://192.168.0.1:12470/ctl/IPConn
Local LAN ip address : 192.168.0.4
Connection Type : IP_Routed
Status : Connected, uptime=98070s, LastConnectionError : ERROR_NONE
  Time started : Fri May 11 21:17:45 2012
MaxBitRateDown : 4200000 bps (4.2 Mbps)   MaxBitRateUp 4200000 bps (4.2 Mbps)
ExternalIPAddress = 192.168.0.1
 i protocol exPort->inAddr:inPort description remoteHost leaseTime
 0 UDP  5070->192.168.0.4:5070  'libminiupnpc' '' 0
 1 TCP    23->192.168.0.1:23    'libminiupnpc' '' 0
 2 UDP  5060->192.168.0.4:5060  'libminiupnpc' '' 0
 3 UDP 59131->192.168.0.3:59131 'Teredo' '' 0
GetGenericPortMappingEntry() returned 713 (SpecifiedArrayIndexInvalid)

//////////////////////////////////////////////////////////////////
POST /ctl/IPConn HTTP/1.1
Host: 192.168.0.1:12470
User-Agent: Fedora/10, UPnP/1.0, MiniUPnPc/1.6
Content-Length: 380
Content-Type: text/xml
SOAPAction: "urn:schemas-upnp-org:service:WANIPConnection:1#DeletePortMapping"
Connection: Close
Cache-Control: no-cache
Pragma: no-cache

<?xml version="1.0"?>
<s:Envelope xmlns:s="http://schemas.xmlsoap.org/soap/envelope/" s:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/">
  <s:Body>
    <u:DeletePortMapping xmlns:u="urn:schemas-upnp-org:service:WANIPConnection:1">
      <NewRemoteHost></NewRemoteHost>
      <NewExternalPort>51084</NewExternalPort>
      <NewProtocol>UDP</NewProtocol>
    </u:DeletePortMapping>
  </s:Body>
</s:Envelope>

===================================================================
HTTP/1.1 200 OK
Content-Type: text/xml
Connection: close
Content-Length: 295
Server: LINUX-2.6 UPnP/1.0 MiniUPnPd/1.5

<?xml version="1.0"?>
<s:Envelope xmlns:s="http://schemas.xmlsoap.org/soap/envelope/" s:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/">
  <s:Body>
    <u:DeletePortMappingResponse xmlns:u="urn:schemas-upnp-org:service:WANIPConnection:1">
    </u:DeletePortMappingResponse>
  </s:Body>
</s:Envelope>


root@H500:/# cat /etc/config/upnpc config on_off 'upnpc_info' option mode 'auto' option enabled 'on' config entry 'vhttpd' option proto 'TCP' option ext_port '8800' option desc 'vhttpd' option status 'off' config config 'pub_ip' option ip 'null' config config 'communicate' option status 'failed' option timestamp '1630479342' config entry 'tp_live' option proto 'TCP' option desc 'tp_live' option status 'off' option ext_port '8800' option comm_status 'success' option timestamp '1761113129' config entry 'tp_vod' option proto 'TCP' option desc 'tp_vod' option status 'off' option ext_port '8800' option comm_status 'failed' option timestamp '1630479342' config entry 'tp_speaker' option proto 'TCP' option desc 'tp_speaker' option status 'off' option ext_port '8800' option comm_status 'failed' option timestamp '1630479342' config entry 'uhttpd' option proto 'TCP' option desc 'uhttpd' option status 'off' option ext_port '18443' option comm_status 'failed' option timestamp '1761115038' config entry 'tp_stream' option proto 'TCP' option desc 'tp_stream' option status 'off' option ext_port '21443' option comm_status 'failed' option timestamp '1761115038' 我的upnpc.sh是这样的:#!/bin/sh RUN_ONCE_FILE=/var/run/upnpc_running RUN_ONCE_FILE_RESTART=/var/run/upnpc_running_restart RUN_ONCE_FILE_STOP=/var/run/upnpc_running_stop if [ -e $RUN_ONCE_FILE ]; then exit 0 fi touch $RUN_ONCE_FILE . /lib/functions.sh . /usr/share/libubox/jshn.sh __network_ipaddr() { local __var="$1" local __family="$2" local __prefix="${3:-0}" local __tmp="$(ubus -t 2 call network wan_status 2>/dev/null)" json_load "${__tmp:-{}}" json_get_type __tmp "ipv${__family}_address" if [ "$__tmp" = array ]; then json_select "ipv${__family}_address" json_get_type __tmp 1 if [ "$__tmp" = object ]; then json_select 1 json_get_var $__var address [ $__prefix -gt 0 ] && { json_get_var __tmp mask eval "export -- \"$__var=\${$__var}/$__tmp\"" } return 0 fi fi return 1 } # IP_ADDR=$(echo $(ifconfig) |cut -d ' ' -f7|cut -d ':' -f2) # IP_ADDR=$(cat /tmp/udhcpc/dhcpip) # network_get_ipaddr() { __network_ipaddr $1 4 0; } BIND_STATUS="" PUB_IP="" COMM_STATUS="" IP_ADDR="" COMPARE_RESULT=0 DELETE_STRING="" ADD_STRING="" # random method RANDOM_INDEX="" RANDOM_MIN=10000 RANDOM_MAX=30000 upnpc_mode=$(uci_get "upnpc" "upnpc_info" "mode") upnpc_enabled=$(uci_get "upnpc" "upnpc_info" "enabled") uhttpd_inner_port=$(uci_get "uhttpd" "main" "listen_https" "443") #vhttpd_inner_port=$(uci_get "cet" "vhttpd" "port" "8080") rtsp_inner_port=$(uci_get "cet" "rtsp" "port" "554") onvif_service_inner_port="2020" tp_live_inner_port="19443" tp_vod_inner_port="18443" tp_speaker_inner_port="17443" tp_stream_inner_port="21443" uhttpd_status=$(uci_get "upnpc" "uhttpd" "status") uhttpd_ext_port=$(uci_get "upnpc" "uhttpd" "ext_port") uhttpd_proto=$(uci_get "upnpc" "uhttpd" "proto") uhttpd_desc=$(uci_get "upnpc" "uhttpd" "desc") uhttpd_flag=1 uhttpd_mapping=0 rtsp_status=$(uci_get "upnpc" "rtsp" "status") rtsp_ext_port=$(uci_get "upnpc" "rtsp" "ext_port") rtsp_proto=$(uci_get "upnpc" "rtsp" "proto") rtsp_desc=$(uci_get "upnpc" "rtsp" "desc") rtsp_flag=2 rtsp_mapping=0 onvif_service_status=$(uci_get "upnpc" "onvif_service" "status") onvif_service_ext_port=$(uci_get "upnpc" "onvif_service" "ext_port") onvif_service_proto=$(uci_get "upnpc" "onvif_service" "proto") onvif_service_desc=$(uci_get "upnpc" "onvif_service" "desc") onvif_service_flag=4 onvif_service_mapping=0 tp_live_status=$(uci_get "upnpc" "tp_live" "status") tp_live_ext_port=$(uci_get "upnpc" "tp_live" "ext_port") tp_live_proto=$(uci_get "upnpc" "tp_live" "proto") tp_live_desc=$(uci_get "upnpc" "tp_live" "desc") tp_live_flag=8 tp_live_mapping=0 tp_vod_status=$(uci_get "upnpc" "tp_vod" "status") tp_vod_ext_port=$(uci_get "upnpc" "tp_vod" "ext_port") tp_vod_proto=$(uci_get "upnpc" "tp_vod" "proto") tp_vod_desc=$(uci_get "upnpc" "tp_vod" "desc") tp_vod_flag=16 tp_vod_mapping=0 tp_speaker_status=$(uci_get "upnpc" "tp_speaker" "status") tp_speaker_ext_port=$(uci_get "upnpc" "tp_speaker" "ext_port") tp_speaker_proto=$(uci_get "upnpc" "tp_speaker" "proto") tp_speaker_desc=$(uci_get "upnpc" "tp_speaker" "desc") tp_speaker_flag=32 tp_speaker_mapping=0 tp_stream_status=$(uci_get "upnpc" "tp_stream" "status") tp_stream_ext_port=$(uci_get "upnpc" "tp_stream" "ext_port") tp_stream_proto=$(uci_get "upnpc" "tp_stream" "proto") tp_stream_desc=$(uci_get "upnpc" "tp_stream" "desc") tp_stream_flag=1 tp_stream_mapping=0 upnpc_get_mapping() { local compare_times=0 # we will try three times at most to get port mapping while [ "$compare_times" -lt 3 ] do upnpc -l > "/tmp/upnpc_output" if [ -s "/tmp/upnpc_output" ] then break; else let compare_times=$compare_times+1 sleep 1 fi done } upnpc_update_comm() { local port_status=$1 local secname=$2 local port=$3 # check status local config_status local config_ext_port local config_comm_status eval config_get config_status "${secname}" status eval config_get config_ext_port "${secname}" ext_port eval config_get config_comm_status "${secname}" comm_status if [ $config_status == $port_status ] then if [ -z $port ] then return 0 elif [ $config_ext_port == $port ] then return 0 fi fi if [ $port_status == "on" ] then if [ $PUB_IP == "null" ] then if [ $config_comm_status != "failed" ] then uci set upnpc."${secname}".comm_status='failed' timestamp=`date +%s` uci set upnpc."${secname}".timestamp=$timestamp fi else if [ $config_comm_status != "unknown" ] then uci set upnpc."${secname}".comm_status='unknown' timestamp=`date +%s` uci set upnpc."${secname}".timestamp=$timestamp fi fi elif [ $port_status == "off" ] then if [ $config_comm_status != "failed" ] then uci set upnpc."${secname}".comm_status='failed' timestamp=`date +%s` uci set upnpc."${secname}".timestamp=$timestamp fi fi } # get config values from the upnpc config file upnpc_get_config() { local secname="$1" config_get "${secname}_status" "$secname" status config_get "${secname}_ext_port" "$secname" ext_port config_get "${secname}_proto" "$secname" proto config_get "${secname}_desc" "$secname" desc } # set config values base on the "upnpc -l" results upnpc_set_config_from_real() { local secname="$1" local file_path="$2" local ip local inner_port local flag=0 while read proto ext_port ip_port desc do # divide ip_port such as 192.168.0.60:8000 into 192.168.0.60 8000 ip=$(echo $ip_port | sed 's/:.*//g') inner_port=$(echo $ip_port | sed 's/.*://g') local config_inner_port local config_proto local config_desc local config_ext_port eval config_inner_port="\${${secname}_inner_port}" eval config_proto="\${${secname}_proto}" eval config_desc="\${${secname}_desc}" eval config_ext_port="\${${secname}_ext_port}" if [ "$IP_ADDR" == "$ip" ] \ && [ "$config_inner_port" == "$inner_port" ] \ && [ "$config_proto" == "$proto" ] \ && [ "$config_desc" == "$desc" ]; then if [ "manual" == "$upnpc_mode" ] then # upnp works in manual mode # we need to keep user`s config to do port mapping again uci set upnpc."$secname".status='on' flag=1 break fi # 处理一下port变化的情况 if [ $secname == "tp_live" ] \ || [ $secname == "tp_vod" ] \ || [ $secname == "tp_speaker" ]; then upnpc_update_comm "on" $secname $ext_port fi uci set upnpc."$secname".status='on' uci set upnpc."$secname".ext_port=$ext_port flag=1 break fi done < "$file_path" if [ $flag == 0 ] then if [ $secname == "tp_live" ] \ || [ $secname == "tp_vod" ] \ || [ $secname == "tp_speaker" ]; then upnpc_update_comm "off" $secname fi uci set upnpc."$secname".status='off' fi } # compare config values with the "upnpc -l" results upnpc_compare_real_config() { local secname="$1" local file_path="$2" local mode="$3" local flag=1 local ip local inner_port while read proto ext_port ip_port desc do # divide ip_port such as 192.168.0.60:8000 into 192.168.0.60 8000 ip=$(echo $ip_port | sed 's/:.*//g') inner_port=$(echo $ip_port | sed 's/.*://g') local config_inner_port local config_ext_port local config_proto local config_desc eval config_inner_port="\${${secname}_inner_port}" eval config_ext_port="\${${secname}_ext_port}" eval config_proto="\${${secname}_proto}" eval config_desc="\${${secname}_desc}" if [ "$IP_ADDR" == "$ip" ] \ && [ "$config_inner_port" == "$inner_port" ] \ && [ "$config_ext_port" == "$ext_port" ] \ && [ "$config_proto" == "$proto" ] \ && [ "$config_desc" == "$desc" ]; then if [ "auto" == "$mode" ] \ && [ "$ext_port" -le "$RANDOM_MIN" ]; then break fi if [ $secname == "tp_live" ] \ || [ $secname == "tp_vod" ] \ || [ $secname == "tp_speaker" ]; then upnpc_update_comm "on" $secname $ext_port fi uci set upnpc."$secname".status='on' flag=0 break else # delete port, if not in config if [ "$config_desc" == "$desc" ] then upnpc -d $ext_port $proto fi fi done < "$file_path" if [ "$flag" == "1" ] then local config_flag eval config_flag="\${${secname}_flag}" let COMPARE_RESULT=$COMPARE_RESULT+$config_flag fi } # decide whether to delete certain port mapping upnpc_decide_delete_string() { local secname="$1" local proto="$2" local ext_port="$3" local ip_port="$4" local desc="$5" local mode="$6" local inner_port # divide ip_port such as 192.168.0.60:8000 into 192.168.0.60 8000 inner_port=$(echo $ip_port | sed 's/.*://g') local config_inner_port local config_ext_port local config_proto local config_desc eval config_inner_port="\${${secname}_inner_port}" eval config_ext_port="\${${secname}_ext_port}" eval config_proto="\${${secname}_proto}" eval config_desc="\${${secname}_desc}" if [ "$config_inner_port" == "$inner_port" ] \ && [ "$config_ext_port" == "$ext_port" ] \ && [ "$config_proto" == "$proto" ] \ && [ "$config_desc" == "$desc" ]; then if [ "auto" == "$mode" ] \ && [ "$ext_port" -lt "$RANDOM_MIN" ]; then # need to delete this port mapping DELETE_FLAG=1 let ${secname}_mapping=0 else # do not need to delete this port mapping DELETE_FLAG=0 # mark that the port mapping of this secname has already been done let ${secname}_mapping=1 fi fi } # build ADD_STRING upnpc_build_add_string() { local secname="$1" local mode="$2" local config_ext_port local config_proto local config_desc local config_inner_port local port_mapping eval config_inner_port="\${${secname}_inner_port}" eval config_ext_port="\${${secname}_ext_port}" eval config_proto="\${${secname}_proto}" eval config_desc="\${${secname}_desc}" eval port_mapping="\${${secname}_mapping}" if [ "0" == "$port_mapping" ] then # the port mapping of this secname has not already been done # we need to do port mapping of this secname, so add to ADD_STRING if [ "auto" != "$mode" ] \ || [ "$config_ext_port" -ge "$RANDOM_MIN" ]; then ADD_STRING=$ADD_STRING" "$config_inner_port" "$config_ext_port" "$config_proto" "$config_desc fi fi } # reconfigure port mapping in auto mode upnpc_auto_reconfigure() { #RANDOM_INDEX=$(lua -e 'math.randomseed(tostring(os.time()):reverse():sub(1, 6)); print(math.random(10000, 30000))') myrand() { if [ -z "$RANDOM" ] ; then SEED=`tr -cd 0-9 </dev/urandom | head -c 8` else SEED=$RANDOM fi RND_NUM=`echo $SEED $1 $2|awk '{srand($1);printf "%d",rand()*10000%($3-$2)+$2}'` echo $RND_NUM } RANDOM_INDEX=$(myrand 20000 30000) local compare_times=0 # we will try three times at most to do port mapping while [ "$compare_times" -lt 3 ] do COMPARE_RESULT=0 config_foreach upnpc_compare_real_config entry /tmp/upnpc_output auto if [ "0" == "$COMPARE_RESULT" ] then # the port mappings have been all successful break else # the port mappings have not been all successful # we need to build ADD_STRING based on COMPARE_RESULT ADD_STRING=$IP_ADDR local flag # randomly set the external port by +1 let flag="$COMPARE_RESULT"\&"$uhttpd_flag" if [ "$flag" -ne "0" ] then uhttpd_ext_port=$RANDOM_INDEX let RANDOM_INDEX=$RANDOM_INDEX+1 ADD_STRING=$ADD_STRING" "$uhttpd_inner_port" "$uhttpd_ext_port" "$uhttpd_proto" "$uhttpd_desc fi let flag="$COMPARE_RESULT"\&"$rtsp_flag" if [ "$flag" -ne "0" ] then rtsp_ext_port=$RANDOM_INDEX let RANDOM_INDEX=$RANDOM_INDEX+1 ADD_STRING=$ADD_STRING" "$rtsp_inner_port" "$rtsp_ext_port" "$rtsp_proto" "$rtsp_desc fi let flag="$COMPARE_RESULT"\&"$onvif_service_flag" if [ "$flag" -ne "0" ] then onvif_service_ext_port=$RANDOM_INDEX let RANDOM_INDEX=$RANDOM_INDEX+1 ADD_STRING=$ADD_STRING" "$onvif_service_inner_port" "$onvif_service_ext_port" "$onvif_service_proto" "$onvif_service_desc fi let flag="$COMPARE_RESULT"\&"$tp_live_flag" if [ "$flag" -ne "0" ] then tp_live_ext_port=$RANDOM_INDEX let RANDOM_INDEX=$RANDOM_INDEX+1 ADD_STRING=$ADD_STRING" "$tp_live_inner_port" "$tp_live_ext_port" "$tp_live_proto" "$tp_live_desc fi let flag="$COMPARE_RESULT"\&"$tp_vod_flag" if [ "$flag" -ne "0" ] then tp_vod_ext_port=$RANDOM_INDEX let RANDOM_INDEX=$RANDOM_INDEX+1 ADD_STRING=$ADD_STRING" "$tp_vod_inner_port" "$tp_vod_ext_port" "$tp_vod_proto" "$tp_vod_desc fi let flag="$COMPARE_RESULT"\&"$tp_speaker_flag" if [ "$flag" -ne "0" ] then tp_speaker_ext_port=$RANDOM_INDEX let RANDOM_INDEX=$RANDOM_INDEX+1 ADD_STRING=$ADD_STRING" "$tp_speaker_inner_port" "$tp_speaker_ext_port" "$tp_speaker_proto" "$tp_speaker_desc fi let flag="$COMPARE_RESULT"\&"$tp_stream_flag" if [ "$flag" -ne "0" ] then tp_stream_ext_port=$RANDOM_INDEX let RANDOM_INDEX=$RANDOM_INDEX+1 ADD_STRING=$ADD_STRING" "$tp_stream_inner_port" "$tp_stream_ext_port" "$tp_stream_proto" "$tp_stream_desc fi # execute the upnpc comand to do the port mapping # grep the "upnpc -l" results to get only map values that are belonged to current_ip upnpc -a $ADD_STRING \ -l | grep $IP_ADDR > /tmp/upnpc_output let compare_times=$compare_times+1 fi done } # reconfigure port mapping upnpc_reconfigure() { config_load upnpc config_get upnpc_mode upnpc_info mode config_get PUB_IP pub_ip ip IP_ADDR=$(echo $(ifconfig) |cut -d ' ' -f7|cut -d ':' -f2) if [ -e /tmp/upnpc_pre_ip ] then PRE_IP=`cat /tmp/upnpc_pre_ip` else PRE_IP="" fi if [ -z "$IP_ADDR" ] \ && [ -z "$PRE_IP" ]; then # current ip and preview ip are all empty cat /tmp/upnpc_output > /tmp/upnpc_output1 elif [ "$IP_ADDR" == "$PRE_IP" ] \ || [ -z "$PRE_IP" ] then # preview_ip is equal to current_ip # so we do not need to check PRE_IP PRE_IP="" # grep the "upnpc -l" results to get only map values that are belonged to current_ip cat /tmp/upnpc_output | grep "$IP_ADDR" > /tmp/upnpc_output1 elif [ -z "$IP_ADDR" ] then # grep the "upnpc -l" results to get only map values that are belonged to preview_ip cat /tmp/upnpc_output | grep "$PRE_IP" > /tmp/upnpc_output1 else # grep the "upnpc -l" results to get only map values that are belonged to preview_ip and current_ip cat /tmp/upnpc_output | grep -E "$IP_ADDR|$PRE_IP" > /tmp/upnpc_output1 fi # grep the "upnpc -l" results to get only map values that are belonged to preview_ip and current_ip cat /tmp/upnpc_output | grep -E "$IP_ADDR|$PRE_IP" > /tmp/upnpc_output1 config_foreach upnpc_get_config entry local ip while read proto ext_port ip_port desc do # divide ip_port such as 192.168.0.60:8000 into 192.168.0.60 8000 # and get the ip value such as "192.168.0.60" ip=$(echo $ip_port | sed 's/:.*//g') if [ "$IP_ADDR" == "$ip" ] then # this port mapping is belonged to IP_ADDR DELETE_FLAG=1 config_foreach upnpc_decide_delete_string entry $proto $ext_port $ip_port $desc $upnpc_mode if [ "1" == "$DELETE_FLAG" ] then # this port mapping is not useful, delete it # package the DELETE_STRING with the form: ext_port proto DELETE_STRING=$DELETE_STRING" "$ext_port" "$proto fi fi if [ "$PRE_IP" == "$ip" ] then # this port mapping is belonged to PRE_IP, delete it # package the DELETE_STRING with the form: ext_port proto DELETE_STRING=$DELETE_STRING" "$ext_port" "$proto fi done < "/tmp/upnpc_output1" # begin to build ADD_STRING ADD_STRING=$IP_ADDR config_foreach upnpc_build_add_string entry $upnpc_mode # begin to build upnpc_string local upnpc_string if [ -n "$DELETE_STRING" ] then # DELETE_STRING is not empty, add to upnpc_string upnpc_string=$upnpc_string" -d "$DELETE_STRING fi if [ "$IP_ADDR" != "$ADD_STRING" ] then # ADD_STRING is not default, add to upnpc_string upnpc_string=$upnpc_string" -a "$ADD_STRING fi # add -l to upnpc_string to list all the port mappings upnpc_string=$upnpc_string" -l" # execute the upnpc comand to do the port mapping # grep the "upnpc -l" results to get only map values that are belonged to current_ip upnpc $upnpc_string | grep $IP_ADDR > /tmp/upnpc_output # save current ip into /tmp/upnpc_pre_ip echo $IP_ADDR > /tmp/upnpc_pre_ip if [ "auto" == "$upnpc_mode" ] then # upnp works in auto mode, if failed # we need to use randomly port to do port mapping again upnpc_auto_reconfigure fi # set upnpc config file based on the "upnpc -l" results config_foreach upnpc_set_config_from_real entry /tmp/upnpc_output uci commit upnpc rm -f "/tmp/upnpc_output1" rm -f "/tmp/upnpc_output" } # the following function will be called outside asynchronously # so we need to add lock to prevent them from being called at the same time # maintain port mapping upnpc_maintain() { config_load cloud_brd config_get BIND_STATUS bind status config_load upnpc config_get upnpc_enabled upnpc_info enabled config_get upnpc_mode upnpc_info mode config_get PUB_IP pub_ip ip timestamp=`date +%s` echo $timestamp >/dev/console if [ "$BIND_STATUS" == "1" ] then if [ "$upnpc_enabled" == "on" ] then IP_ADDR=$(echo $(ifconfig) |cut -d ' ' -f7|cut -d ':' -f2) if [ -n "$IP_ADDR" ] then lock "/var/run/upnpc" #upnpc -l > "/tmp/upnpc_output" upnpc_get_mapping # grep the "upnpc -l" results to get only map values that are belonged to current_ip cat /tmp/upnpc_output | grep $IP_ADDR > /tmp/upnpc_output1 COMPARE_RESULT=0 config_foreach upnpc_compare_real_config entry /tmp/upnpc_output1 $upnpc_mode if [ $COMPARE_RESULT == 0 ] then uci commit upnpc # do not need to maintain # save current ip into /tmp/upnpc_pre_ip echo $IP_ADDR > /tmp/upnpc_pre_ip rm -f "/tmp/upnpc_output1" rm -f "/tmp/upnpc_output" else # need to maintain, reconfigure upnpc upnpc_reconfigure fi lock -u "/var/run/upnpc" fi fi else upnpc_stop fi } # stop port mapping upnpc_stop() { if [ -e $RUN_ONCE_FILE_STOP ]; then exit 0 fi touch $RUN_ONCE_FILE_STOP lock "/var/run/upnpc" config_load upnpc config_get PUB_IP pub_ip ip # network_get_ipaddr IP_ADDR IP_ADDR=$(echo $(ifconfig) |cut -d ' ' -f7|cut -d ':' -f2) local DELETE_STRING local PRE_IP local ip if [ -e /tmp/upnpc_pre_ip ] then PRE_IP=`cat /tmp/upnpc_pre_ip` else PRE_IP="" fi # grep the "upnpc -l" results to get only map values that are belonged to preview_ip and current_ip upnpc -l | grep -E "$IP_ADDR|$PRE_IP" > "/tmp/upnpc_output" while read proto ext_port ip_port desc do # divide ip_port such as 192.168.0.60:8000 into 192.168.0.60 8000 # and get the ip value such as "192.168.0.60" ip=$(echo $ip_port | sed 's/:.*//g') if [ "$ip" == "$IP_ADDR" ] \ || [ "$ip" == "$PRE_IP" ]; then DELETE_STRING=$DELETE_STRING" "$ext_port" "$proto fi done < "/tmp/upnpc_output" if [ -n "$DELETE_STRING" ] then upnpc -d $DELETE_STRING \ -l | grep $IP_ADDR > "/tmp/upnpc_output" fi config_foreach upnpc_set_config_from_real entry /tmp/upnpc_output uci commit upnpc rm -f /tmp/upnpc_output lock -u "/var/run/upnpc" rm $RUN_ONCE_FILE_STOP } # restart port mapping upnpc_restart() { if [ -e $RUN_ONCE_FILE_RESTART ]; then exit 0 fi touch $RUN_ONCE_FILE_RESTART config_load cloud_brd config_get BIND_STATUS bind status config_load upnpc config_get upnpc_enabled upnpc_info enabled if [ "$BIND_STATUS" == "1" ] then if [ "$upnpc_enabled" == "on" ] then lock "/var/run/upnpc" upnpc -l > "/tmp/upnpc_output" upnpc_reconfigure lock -u "/var/run/upnpc" else upnpc_stop fi else upnpc_stop fi rm $RUN_ONCE_FILE_RESTART } case "$1" in maintain) upnpc_maintain ;; esac rm $RUN_ONCE_FILE,upnp.c代码:/***************************************************************************** * Copyright@ 2019-2021 TP-LINK TECHNOLOGIES CO., LTD. * File Name: ulinkied_upnp.c * Author: Mengmingzhan * Version: 1.0 * Description: * * information. * * History: * 2021-01-06: File created. *****************************************************************************/ #include "upnp.h" #include "udsd.h" #include <time.h> #include <lib-tpcom/lib_tpcom.h> #include <lib-tpcom/err_code.h> #include <stdio.h> #include <unistd.h> #include <sys/socket.h> #include <arpa/inet.h> #include <string.h> #include <sys/ioctl.h> #include <net/if.h> typedef struct _UPNPC_ENTRY_INFO { char proto[16]; char desc[16]; char ip[32]; char inner_port[8]; char ext_port[8]; } UPNPC_ENTRY_INFO; static int get_local_ip(char *ip) { FILE *fp = NULL; // char ipaddr[20] = {0}; if ((fp = fopen("/tmp/udhcpc/dhcpip", "r")) == NULL) { DBG_ERR( "open dhcpip file fail.\n"); goto END; } fseek(fp, 0L, SEEK_END); int len = ftell(fp); fseek(fp, 0L, SEEK_SET); fread(ip, 1, len-1, fp); fclose(fp); return 1; END: if (fp) fclose(fp); return 0; } void udsd_get_upnp_info(struct ubus_app *svr_ubus_app, struct blob_buf* bBuf, struct blob_attr *msg) { int ret = 0; int result = 0; char enabled[10] = {0}; char mode[10] = {0}; struct uci_context *uci_ctx = tpuci_open(); if(!uci_ctx) { result = UPNP_OTHER_ERROR; goto failed; } /* enabled */ ret = tpuci_get_val_string(uci_ctx, enabled, sizeof(enabled), "upnpc", "upnpc_info", "enabled"); if (0 != ret){ DBG_ERR( "get upnpc enabled failed.\n"); result = UPNP_PARAM_ENABLED_ERROR; goto failed; } if(strcmp(enabled, "on") && strcmp(enabled, "off")){ DBG_ERR( "upnp enabled config arg failed.\n"); result = UPNP_PARAM_ENABLED_ERROR; goto failed; } blobmsg_add_string(bBuf, "enabled", enabled); /* mode */ ret = tpuci_get_val_string(uci_ctx, mode, sizeof(mode), "upnpc", "upnpc_info", "mode"); if (0 != ret){ DBG_ERR( "get upnpc mode failed.\n"); result = UPNP_PARAM_MODE_ERROR; goto failed; } if(strcmp(mode, "auto")){ DBG_ERR( "upnp enabled config arg failed.\n"); result = UPNP_PARAM_MODE_ERROR; goto failed; } blobmsg_add_string(bBuf, "mode", "auto"); failed: tpuci_close(uci_ctx); common_end(bBuf, result); return; } void udsd_set_upnp_info(struct ubus_app *svr_ubus_app, struct blob_buf* bBuf, struct blob_attr *msg) { int ret = 0; int result = 0; char *enabled_str = NULL; char *mode_str = NULL; if (!bBuf) { DBG_ERR( "arg err\n"); return; } struct uci_context *uci_ctx = tpuci_open(); if (!msg || !uci_ctx) { DBG_ERR( "msg || uci_ctx err\n"); result = UPNP_OTHER_ERROR; goto failed; } struct blobmsg ( blobmsg_string enabled, blobmsg_string mode ) (req, msg, false); char *json_str = blobmsg_format_json(msg, true); if (json_str) { DBG_ERR("Received params: %s\n", json_str); free(json_str); } if (!req.enabled) { DBG_ERR( "set upnpc blobmsg err\n"); result = UPNP_PARAM_ENABLED_ERROR; goto failed; } if (!req.mode) { DBG_ERR( "set upnpc blobmsg err\n"); result = UPNP_PARAM_MODE_ERROR; goto failed; } enabled_str = blobmsg_get_string(req.enabled); mode_str = blobmsg_get_string(req.mode); /* enabled */ if(strcmp(enabled_str, "on") && strcmp(enabled_str, "off")){ DBG_ERR( "upnpc enabled config arg failed.\n"); result = UPNP_PARAM_ENABLED_ERROR; goto failed; } ret = tpuci_set_val(uci_ctx, enabled_str, "upnpc", "upnpc_info", "enabled", 1); if (0 != ret){ DBG_ERR( "set upnp enabled format uci err\n"); result = UPNP_PARAM_ENABLED_ERROR; goto failed; } /* mode */ if(strcmp(mode_str, "auto")){ DBG_ERR( "upnpc mode config arg failed.\n"); result = UPNP_PARAM_MODE_ERROR; goto failed; } ret = tpuci_set_val(uci_ctx, mode_str, "upnpc", "upnpc_info", "mode", 1); if (0 != ret){ DBG_ERR( "set upnp mode format uci err\n"); result = UPNP_PARAM_MODE_ERROR; goto failed; } system(". /lib/upnpc/upnpc.sh; upnpc_restart"); failed: tpuci_close(uci_ctx); common_end(bBuf, result); return; } void udsd_get_upnp_status(struct ubus_app *svr_ubus_app, struct blob_buf* bBuf, struct blob_attr *msg) { int ret = 0; int i = 0; int result = 0; char ipaddr[20] = {0}; char status[20] = {0}; char proto[20] = {0}; char desc[20] = {0}; int inner_port = 0; int ext_port = 0; // char mode[8] = {0}; FILE *fp = NULL; char buf[128] = {0}; char *lines[LINES_MAX] = {0}; char *read_buf = NULL; int lines_cnt = 0; int flag[7] = {0}; UPNPC_ENTRY_INFO entries_info; int part_num = 0; char *entry[] = {"uhttpd", "rtsp", "onvif_service", "tp_live", "tp_vod", "tp_speaker", "tp_stream"}; int fix_port[] = {443, 554, 2020, 19443, 18443, 17443, 21443}; struct uci_context *uci_ctx = tpuci_open(); if(!uci_ctx) { result = UPNP_OTHER_ERROR; goto failed; } /* ipaddr */ if ( 0 == get_local_ip(ipaddr) || NULL == ipaddr) { DBG_ERR( "get upnpc local ip failed.\n"); result = UPNP_PARAM_IPADDR_ERROR; goto failed; } fp = popen("upnpc -l", "r"); if (!fp) { goto failed; } while (NULL != (read_buf = fgets(buf, 128, fp)) && strlen(read_buf) != 0) { lines[i] = strdup(read_buf); DBG_ERR("line: %s\n", lines[i]); i++; /* 最大支持16行数据,超过则做截断处理 */ if (i >= LINES_MAX) { break; } } lines_cnt = i; struct blob_buf b_tmp = {NULL}; blobmsg_buf_init(&b_tmp); for (i = 0; i < lines_cnt; i++) { if (strstr(lines[i], ipaddr)) { part_num = sscanf(lines[i], "%15[^ ]%*[ ]%7[^ ]%*[ ]%31[^:]:%7[^ ]%*[ ]%15s", entries_info.proto, entries_info.ext_port, entries_info.ip, entries_info.inner_port, entries_info.desc); if (part_num == 5) { if (0 == strlen(ipaddr)) { sscanf(entries_info.ip, "%s", ipaddr); } void *table = NULL; if (NULL != (table = blobmsg_open_table(&b_tmp, entries_info.desc))) { //DBG_ERR("desc: %s\n", entries_info.desc); blobmsg_add_u32(&b_tmp, "ext_port", atoi(entries_info.ext_port)); blobmsg_add_u32(&b_tmp, "inner_port", atoi(entries_info.inner_port)); blobmsg_close_table(&b_tmp, table); //DBG_DBG_BLOB(b_tmp.head); } } } } blobmsg_add_string(bBuf, "ipaddr", ipaddr); struct blobmsg ( blobmsg_table uhttpd, blobmsg_table rtsp, blobmsg_table onvif_service, blobmsg_table tp_live, blobmsg_table tp_vod, blobmsg_table tp_speaker, blobmsg_table tp_stream, ) (info, b_tmp.head, false); if (info.uhttpd) flag[0] = 1; if (info.rtsp) flag[1] = 1; if (info.onvif_service) flag[2] = 1; if (info.tp_live) flag[3] = 1; if (info.tp_vod) flag[4] = 1; if (info.tp_speaker) flag[5] = 1; if (info.tp_stream) flag[6] = 1; //DBG_ERR( "flag: %d, %d, %d\n", flag[0],flag[1],flag[2]); void *array; void *table; if (NULL != (array = blobmsg_open_array(bBuf, "entry_list"))) { for (i = 0; i < 6; i++) { /* proto */ ret = tpuci_get_val_string(uci_ctx, proto, sizeof(proto), "upnpc", entry[i], "proto"); if (0 != ret){ DBG_ERR( "get upnpc proto failed.\n"); result = UPNP_PARAM_PROTO_ERROR; goto failed; } /* desc */ ret = tpuci_get_val_string(uci_ctx, desc, sizeof(desc), "upnpc", entry[i], "desc"); if (0 != ret){ DBG_ERR( "get upnpc desc failed.\n"); result = UPNP_PARAM_DESC_ERROR; goto failed; } /* status */ if (0 == flag[i]) { strcpy(status, "off"); }else { strcpy(status, "on"); } /* ext_port */ if (0 == flag[i]) { ext_port = 0; }else { if (0 == i) { struct blobmsg ( blobmsg_int32 ext_port, ) (src, info.uhttpd, false); if (src.ext_port) ext_port = blobmsg_get_u32(src.ext_port); } if (1 == i) { struct blobmsg ( blobmsg_int32 ext_port, ) (src, info.rtsp, false); if (src.ext_port) ext_port = blobmsg_get_u32(src.ext_port); } if (2 == i) { struct blobmsg ( blobmsg_int32 ext_port, ) (src, info.onvif_service, false); if (src.ext_port) ext_port = blobmsg_get_u32(src.ext_port); } if (3 == i) { struct blobmsg ( blobmsg_int32 ext_port, ) (src, info.tp_live, false); if (src.ext_port) ext_port = blobmsg_get_u32(src.ext_port); } if (4 == i) { struct blobmsg ( blobmsg_int32 ext_port, ) (src, info.tp_vod, false); if (src.ext_port) ext_port = blobmsg_get_u32(src.ext_port); } if (5 == i) { struct blobmsg ( blobmsg_int32 ext_port, ) (src, info.tp_speaker, false); if (src.ext_port) ext_port = blobmsg_get_u32(src.ext_port); } if (6 == i) { struct blobmsg ( blobmsg_int32 ext_port, ) (src, info.tp_stream, false); if (src.ext_port) ext_port = blobmsg_get_u32(src.ext_port); } } /* inner_port */ inner_port = fix_port[i]; /* add info */ if (NULL != (table = blobmsg_open_table(bBuf, NULL))) { blobmsg_add_u32(bBuf, "inner_port", inner_port); blobmsg_add_string(bBuf, "status", status); blobmsg_add_string(bBuf, "proto", proto); blobmsg_add_string(bBuf, "desc", desc); blobmsg_add_u32(bBuf, "ext_port", ext_port); blobmsg_close_table(bBuf, table); } else { DBG_ERR("blobmsg_open_table failed.\n"); result = UPNP_OTHER_ERROR; } } blobmsg_close_array(bBuf, array); } else { DBG_ERR("blobmsg_open_array failed.\n"); result = UPNP_OTHER_ERROR; } failed: tpuci_close(uci_ctx); common_end(bBuf, result); blob_buf_free(&b_tmp); pclose(fp); for (i = 0; i < lines_cnt; i++) { SAFE_FREE(lines[i]); } return; } void udsd_get_pub_ip(struct ubus_app *svr_ubus_app, struct blob_buf* bBuf, struct blob_attr *msg) { int ret = 0; int result = 0; char pubip[20] = {0}; struct uci_context *uci_ctx = tpuci_open(); if(!uci_ctx) { result = UPNP_OTHER_ERROR; goto failed; } ret = tpuci_get_val_string(uci_ctx, pubip, sizeof(pubip), "upnpc", "pub_ip", "ip"); if (0 != ret){ DBG_ERR( "get upnpc pub_ip failed.\n"); result = UPNP_PARAM_PUBIP_ERROR; goto failed; } blobmsg_add_string(bBuf, "ip", pubip); failed: tpuci_close(uci_ctx); common_end(bBuf, result); return; } void udsd_get_upnp_commstatus(struct ubus_app *svr_ubus_app, struct blob_buf* bBuf, struct blob_attr *msg) { int ret = 0; int result = 0; int timestamp = 0; char desc[15] = {0}; char status_str[10] = {0}; char *comm[] = {"uhttpd", "rtsp", "onvif_service", "tp_live", "tp_vod", "tp_speaker", "tp_stream"}; struct uci_context *uci_ctx = tpuci_open(); if(!uci_ctx) { result = UPNP_OTHER_ERROR; goto failed; } void *array; void *table; int i = 0; if (NULL != (array = blobmsg_open_array(bBuf, "comm_list"))) { for (i = 0; i < 6; i++) { /* desc */ ret = tpuci_get_val_string(uci_ctx, desc, sizeof(desc), "upnpc", comm[i], "desc"); if (0 != ret){ DBG_ERR( "get upnpc desc failed.\n"); result = UPNP_PARAM_DESC_ERROR; goto failed; } /* comm_status */ ret = tpuci_get_val_string(uci_ctx, status_str, sizeof(status_str), "upnpc", comm[i], "comm_status"); if (0 != ret){ DBG_ERR( "get upnpc comm_status failed.\n"); result = UPNP_PARAM_COMM_STATUS_ERROR; goto failed; } /* timestamp */ ret = tpuci_get_val_int(uci_ctx, &timestamp, "upnpc", comm[i], "timestamp"); if (0 != ret){ DBG_ERR( "get upnpc timestamp failed.\n"); result = UPNP_PARAM_COMM_TIMESTAMP_ERROR; goto failed; } /* add info */ if (NULL != (table = blobmsg_open_table(bBuf, NULL))) { blobmsg_add_string(bBuf, "desc", desc); blobmsg_add_string(bBuf, "comm_status", status_str); blobmsg_add_u32(bBuf, "timestamp", timestamp); blobmsg_close_table(bBuf, table); } else { DBG_ERR("blobmsg_open_table failed.\n"); result = UPNP_OTHER_ERROR; } } blobmsg_close_array(bBuf, array); } else { DBG_ERR("blobmsg_open_array failed.\n"); result = UPNP_OTHER_ERROR; } failed: tpuci_close(uci_ctx); common_end(bBuf, result); return; } void udsd_set_upnp_commstatus(struct ubus_app *svr_ubus_app, struct blob_buf* bBuf, struct blob_attr *msg) { if (!bBuf) { DBG_ERR( "arg err\n"); return; } int ret = 0; int result = 0; char *status_str = NULL; char *desc = NULL; int timestamp = 0; time_t time_now = 0; struct uci_context *uci_ctx = tpuci_open(); if (!msg || !uci_ctx) { DBG_ERR( "msg || uci_ctx err\n"); result = UPNP_OTHER_ERROR; goto failed; } struct blobmsg ( blobmsg_string comm_status, blobmsg_string desc, blobmsg_int32 timestamp ) (req, msg, false); char *json_str = blobmsg_format_json(msg, true); if (json_str) { DBG_ERR("Received params: %s\n", json_str); free(json_str); } if (!req.comm_status) { DBG_ERR( "set upnpc blobmsg err\n"); result = UPNP_PARAM_COMM_STATUS_ERROR; goto failed; } if (!req.timestamp) { DBG_ERR( "set upnpc blobmsg err\n"); result = UPNP_PARAM_COMM_TIMESTAMP_ERROR; goto failed; } if (!req.desc) { DBG_ERR( "set upnpc blobmsg err\n"); result = UPNP_PARAM_DESC_ERROR; goto failed; } status_str = blobmsg_get_string(req.comm_status); desc = blobmsg_get_string(req.desc); timestamp = blobmsg_get_u32(req.timestamp); DBG_ERR( "status_str:%s\n",status_str); DBG_ERR( "desc:%s\n",desc); DBG_ERR( "timestamp:%d\n",timestamp); /* comm_status */ if(strcmp(status_str, "failed") && strcmp(status_str, "success") && strcmp(status_str, "unknown")){ DBG_ERR( "upnpc config arg failed.\n"); result = UPNP_PARAM_COMM_STATUS_ERROR; goto failed; } ret = tpuci_set_val(uci_ctx, status_str, "upnpc", desc, "comm_status", 1); if (0 != ret){ DBG_ERR( "set upnp comm_status format uci err\n"); result = UPNP_PARAM_COMM_STATUS_ERROR; goto failed; } /* timestamp */ time_now = time(NULL); if (timestamp - time_now > 86400 || time_now - timestamp > 86400) { DBG_ERR("timestamp too large or small."); result = UPNP_PARAM_COMM_TIMESTAMP_ERROR; goto failed; } ret = tpuci_set_val_int(uci_ctx, timestamp, "upnpc", desc, "timestamp", 1); if (0 != ret){ DBG_ERR( "set upnp timestamp format uci err\n"); result = UPNP_PARAM_COMM_TIMESTAMP_ERROR; goto failed; } failed: tpuci_close(uci_ctx); common_end(bBuf, result); return; } /**************************************************************************************************/ /* PUBLIC_FUNCTIONS */ /**************************************************************************************************/ static const UDSD_MOD_CMD g_udsd_cmds_upnp[] = { {UDSD_CMD_R, "get_upnp_info", udsd_get_upnp_info}, {UDSD_CMD_W, "set_upnp_info", udsd_set_upnp_info}, {UDSD_CMD_R, "get_upnp_status", udsd_get_upnp_status}, {UDSD_CMD_R, "get_pub_ip", udsd_get_pub_ip}, {UDSD_CMD_R, "get_upnp_commstatus", udsd_get_upnp_commstatus}, {UDSD_CMD_W, "set_upnp_commstatus", udsd_set_upnp_commstatus}, {UDSD_CMD_N, NULL, NULL} }; UDSD_CMD_ADD(upnp, "upnp", g_udsd_cmds_upnp); upnpc.config:config on_off 'upnpc_info' option enabled 'on' option mode 'auto' config entry 'uhttpd' option proto 'TCP' option desc 'uhttpd' option status 'off' option ext_port '18443' option comm_status 'failed' option timestamp '1761115038' config entry 'tp_stream' option proto 'TCP' option desc 'tp_stream' option status 'off' option ext_port '21443' option comm_status 'failed' option timestamp '1761115038' config entry 'tp_live' option proto 'TCP' option desc 'tp_live' option status 'off' option ext_port '18445' option comm_status 'failed' option timestamp '1761115038' config entry 'tp_vod' option proto 'TCP' option desc 'tp_vod' option status 'off' option ext_port '18446' option comm_status 'failed' option timestamp '1761115038' config entry 'tp_speaker' option proto 'TCP' option desc 'tp_speaker' option status 'off' option ext_port '18447' option comm_status 'failed' option timestamp '1761115038' config config 'pub_ip' option ip 'null' 分析下为什么cat /etc/config/upnpc得到的结果不是预期的。
最新发布
10-23
import tkinter as tk from tkinter import scrolledtext, ttk, filedialog, messagebox import subprocess import threading import os import datetime class ADBBackupTool: def __init__(self, root): self.root = root root.title("晶晨/海思ADB备份工具") def center_window(width, height, y_offset=0): screen_width = root.winfo_screenwidth() screen_height = root.winfo_screenheight() x = (screen_width - width) // 2 y = (screen_height - height) // 2 + y_offset return f"{width}x{height}+{x}+{y}" root.geometry(center_window(1200, 700, -50)) self.context_menu = tk.Menu(root, tearoff=0) self.context_menu.add_command(label="复制", command=lambda: self.copy_text()) self.context_menu.add_command(label="粘贴", command=lambda: self.paste_text()) self.context_menu.add_separator() self.context_menu.add_command(label="全选", command=lambda: self.select_all()) self.main_frame = tk.Frame(root) self.main_frame.pack(fill=tk.BOTH, expand=True, padx=5, pady=5) self.main_frame.columnconfigure(0, weight=1) self.main_frame.rowconfigure(0, weight=1) RIGHT_PANE_WIDTH = 300 self.cmd_frame = tk.Frame(self.main_frame) self.cmd_frame.grid(row=0, column=0, sticky="nsew") self.cmd_label = tk.Label(self.cmd_frame, text="命令窗口", font=('Arial', 10, 'bold')) self.cmd_label.pack(anchor='w') self.cmd_text = scrolledtext.ScrolledText( self.cmd_frame, wrap=tk.WORD, font=('Consolas', 10), bg='black', fg='white', insertbackground='white' ) self.cmd_text.pack(fill=tk.BOTH, expand=True) self.cmd_text.bind("<Button-3>", self.show_context_menu) self.function_frame = tk.Frame(self.main_frame, width=RIGHT_PANE_WIDTH) self.function_frame.grid(row=0, column=1, sticky="nsew", padx=(5, 0)) self.function_frame.pack_propagate(False) self.conn_frame = tk.LabelFrame(self.function_frame, text="连接设置", padx=5, pady=5) self.conn_frame.pack(fill=tk.X, pady=(0, 5)) ip_port_frame = tk.Frame(self.conn_frame) ip_port_frame.pack(fill=tk.X, pady=2) tk.Label(ip_port_frame, text="IP地址:").pack(side=tk.LEFT, padx=(0, 5)) self.ip_entry = tk.Entry(ip_port_frame, width=15) self.ip_entry.pack(side=tk.LEFT, padx=(0, 10)) self.ip_entry.insert(0, "192.168.31.200") self.ip_entry.bind("<Button-3>", self.show_context_menu) tk.Label(ip_port_frame, text="端口号:").pack(side=tk.LEFT, padx=(0, 5)) self.port_entry = tk.Entry(ip_port_frame, width=9) self.port_entry.pack(side=tk.LEFT) self.port_entry.insert(0, "5555") self.port_entry.bind("<Button-3>", self.show_context_menu) self.connect_btn = tk.Button( self.conn_frame, text="连接设备", command=self.connect_device, bg="#4CAF50", fg="white" ) self.connect_btn.pack(fill=tk.X, pady=(5, 0)) self.query_frame = tk.LabelFrame(self.function_frame, text="快速查询", padx=5, pady=5) self.query_frame.pack(fill=tk.X, pady=(0, 5)) self.cmd_var = tk.StringVar() self.cmd_combobox = ttk.Combobox( self.query_frame, textvariable=self.cmd_var, values=["请选择命令", "晶晨分区获取", "晶晨dtb备份到桌面", "海思分区获取","海思分区获取2", "保存海思分区表", "U盘路径查询"], state="readonly", height=5, width=25 ) self.cmd_combobox.pack(fill=tk.X, pady=2) self.cmd_combobox.current(0) # 默认选中"请选择命令" self.cmd_combobox.bind("<<ComboboxSelected>>", self.update_cmd_entry) self.cmd_mapping = { "晶晨分区获取": "adb shell ls /dev/block", "晶晨dtb备份到桌面": 'adb pull "/dev/dtb" "C:\\Users\\Administrator\\Desktop\\dtb"', "海思分区获取": 'adb shell "cd /dev/block/platform/soc/by-name && ls -l"', "海思分区获取2": "adb shell cat /proc/partitions ", "保存海思分区表": "save_hisilicon_partitions", "U盘路径查询": "adb shell df" } self.cmd_entry = tk.Entry(self.query_frame) self.cmd_entry.pack(fill=tk.X, pady=2) self.cmd_entry.insert(0, "可手动输入") self.cmd_entry.bind("<Button-3>", self.show_context_menu) self.cmd_entry.bind("<FocusIn>", self.on_entry_focus_in) self.cmd_entry.bind("<Key>", self.on_entry_key_press) self.run_cmd_btn = tk.Button( self.query_frame, text="执行命令", command=self.execute_custom_cmd, bg="#2196F3", fg="white" ) self.run_cmd_btn.pack(fill=tk.X, pady=(0, 5)) self.partition_select_frame = tk.LabelFrame(self.function_frame, text="选择要备份的分区", padx=5, pady=5) self.partition_select_frame.pack(fill=tk.BOTH, expand=True) self.partition_container = tk.Frame(self.partition_select_frame) self.partition_container.pack(fill=tk.BOTH, expand=True) self.partition_scrollbar = ttk.Scrollbar(self.partition_container) self.partition_scrollbar.pack(side=tk.RIGHT, fill=tk.Y) self.partition_canvas = tk.Canvas( self.partition_container, yscrollcommand=self.partition_scrollbar.set, bg='#f0f0f0', highlightthickness=0 ) self.partition_canvas.pack(side=tk.LEFT, fill=tk.BOTH, expand=True) self.partition_scrollbar.config(command=self.partition_canvas.yview) self.partition_list_frame = tk.Frame(self.partition_canvas, bg='#f0f0f0') self.partition_canvas.create_window((0, 0), window=self.partition_list_frame, anchor="nw") self.partition_list_frame.bind( "<Configure>", lambda e: self.partition_canvas.configure( scrollregion=self.partition_canvas.bbox("all") ) ) self.partition_vars = {} self.chip_type = None self.select_all_var = tk.BooleanVar() self.select_all_check = tk.Checkbutton( self.partition_list_frame, text="全选", variable=self.select_all_var, command=self.toggle_select_all, bg='#f0f0f0', anchor='w', font=('Arial', 10) ) self.select_all_check.pack(fill=tk.X, pady=2, padx=5) self.backup_btn = tk.Button( self.function_frame, text="备份选中分区", command=self.start_backup, bg="#9C27B0", fg="white" ) self.backup_btn.pack(fill=tk.X, pady=(5, 0)) self.status_bar = tk.Label(root, text="就绪", bd=1, relief=tk.SUNKEN, anchor=tk.W) self.status_bar.pack(side=tk.BOTTOM, fill=tk.X) self.check_adb() def save_hisilicon_partitions(self): """保存海思分区表到桌面""" if not hasattr(self, 'chip_type') or self.chip_type != "海思": messagebox.showerror("错误", "请先获取海思分区表") return desktop_path = "C:\\Users\\Administrator\\Desktop" save_path = os.path.join(desktop_path, "海思分区表.txt") try: content = self.cmd_text.get("1.0", tk.END) lines = content.split('\n') partition_lines = [line for line in lines if "->" in line] if not partition_lines: messagebox.showerror("错误", "未找到分区信息") return with open(save_path, 'w', encoding='utf-8') as f: f.write("海思设备分区表\n") f.write(f"生成时间: {datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S')}\n") f.write("="*50 + "\n") for line in partition_lines: f.write(line + "\n") self.append_cmd_output(f"分区表已保存到桌面: 海思分区表.txt") messagebox.showinfo("成功", "分区表已保存到桌面:\n海思分区表.txt") except Exception as e: self.append_cmd_output(f"保存分区表出错: {str(e)}") messagebox.showerror("错误", f"保存分区表出错:\n{str(e)}") def on_entry_focus_in(self, event): if self.cmd_entry.get() == "请选择命令或手动输入": self.cmd_entry.delete(0, tk.END) def on_entry_key_press(self, event): if self.cmd_entry.get() == "请选择命令或手动输入": self.cmd_entry.delete(0, tk.END) def update_cmd_entry(self, event): selected = self.cmd_combobox.get() self.cmd_entry.delete(0, tk.END) self.cmd_entry.insert(0, self.cmd_mapping.get(selected, "")) def show_context_menu(self, event): self.current_widget = event.widget try: self.current_widget.focus_set() self.context_menu.post(event.x_root, event.y_root) except Exception as e: print(f"显示右键菜单出错: {e}") return "break" def copy_text(self): try: if isinstance(self.current_widget, (tk.Entry, scrolledtext.ScrolledText)): self.current_widget.event_generate("<<Copy>>") except Exception as e: print(f"复制出错: {e}") def paste_text(self): try: if isinstance(self.current_widget, (tk.Entry, scrolledtext.ScrolledText)): self.current_widget.event_generate("<<Paste>>") except Exception as e: print(f"粘贴出错: {e}") def select_all(self): try: if isinstance(self.current_widget, tk.Entry): self.current_widget.select_range(0, tk.END) self.current_widget.icursor(tk.END) elif isinstance(self.current_widget, scrolledtext.ScrolledText): self.current_widget.tag_add(tk.SEL, "1.0", tk.END) self.current_widget.mark_set(tk.INSERT, "1.0") self.current_widget.see(tk.INSERT) except Exception as e: print(f"全选出错: {e}") def check_adb(self): try: result = subprocess.run("adb version", shell=True, capture_output=True, text=True) if "Android Debug Bridge" in result.stdout: self.append_cmd_output("ADB已就绪\n" + result.stdout.split('\n')[0]) else: self.append_cmd_output("错误: ADB未正确安装") messagebox.showerror("错误", "ADB未正确安装,请先安装ADB工具") except Exception as e: self.append_cmd_output(f"检查ADB出错: {str(e)}") messagebox.showerror("错误", f"检查ADB出错: {str(e)}") def execute_custom_cmd(self): cmd = self.cmd_entry.get() if not cmd: self.append_cmd_output("错误: 请输入要执行的命令") return if cmd == "save_hisilicon_partitions": self.save_hisilicon_partitions() return self.append_cmd_output(f"执行命令: {cmd}") threading.Thread(target=self._execute_cmd, args=(cmd,), daemon=True).start() def _execute_cmd(self, cmd): try: process = subprocess.Popen( cmd, shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True, bufsize=1, universal_newlines=True ) while True: output = process.stdout.readline() if output == '' and process.poll() is not None: break if output: self.root.after(0, self.append_cmd_output, output.strip()) stderr = process.stderr.read() if stderr: self.root.after(0, self.append_cmd_output, f"错误:\n{stderr.strip()}") self.root.after(0, self.append_cmd_output, f"命令执行完成,返回值: {process.returncode}") if "ls /dev/block" in cmd or "by-name" in cmd: self._parse_partitions(cmd) except Exception as e: self.root.after(0, self.append_cmd_output, f"执行命令出错: {str(e)}") def _parse_partitions(self, cmd): for widget in self.partition_list_frame.winfo_children()[1:]: widget.destroy() self.partition_vars.clear() partitions = [] if "by-name" in cmd: self.chip_type = "海思" self.append_cmd_output("检测到海思设备分区表") result = subprocess.run(cmd, shell=True, capture_output=True, text=True) for line in result.stdout.split('\n'): if "->" in line: part_name = line.split("->")[-1].split("/")[-1].strip() if part_name: partitions.append(part_name) else: self.chip_type = "晶晨" self.append_cmd_output("检测到晶晨设备分区表") result = subprocess.run(cmd, shell=True, capture_output=True, text=True) for line in result.stdout.split('\n'): if line and not line.startswith("total") and not line.startswith("ls:"): part_name = line.split()[-1] if part_name and part_name not in ["", "by-name", "platform"]: partitions.append(part_name) if partitions: self._display_partitions(partitions) self.append_cmd_output("分区列表已更新,请在右侧选择要备份的分区") else: self.append_cmd_output("未找到分区信息") def connect_device(self): ip = self.ip_entry.get() port = self.port_entry.get() if not ip or not port: self.append_cmd_output("错误: 请输入IP地址和端口号") return self.append_cmd_output(f"尝试连接设备: {ip}:{port}") self.connect_btn.config(state=tk.DISABLED, text="连接中...") threading.Thread(target=self._adb_connect, args=(ip, port), daemon=True).start() def _adb_connect(self, ip, port): try: cmd = f"adb connect {ip}:{port}" self.append_cmd_output(f"执行: {cmd}") process = subprocess.Popen( cmd, shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True, bufsize=1, universal_newlines=True ) while True: output = process.stdout.readline() if output == '' and process.poll() is not None: break if output: self.root.after(0, self.append_cmd_output, output.strip()) stderr = process.stderr.read() if stderr: self.root.after(0, self.append_cmd_output, f"错误:\n{stderr.strip()}") if process.returncode == 0: self.root.after(0, self.status_bar.config, {"text": f"已连接: {ip}:{port}"}) else: self.root.after(0, self.append_cmd_output, "连接失败!") except Exception as e: self.root.after(0, self.append_cmd_output, f"连接出错: {str(e)}") finally: self.root.after(0, self.connect_btn.config, {"state": tk.NORMAL, "text": "连接设备"}) def _display_partitions(self, partitions): for part in partitions: self.partition_vars[part] = tk.BooleanVar() cb = tk.Checkbutton( self.partition_list_frame, text=part, variable=self.partition_vars[part], anchor='w', bg='#f0f0f0', font=('Arial', 10) ) cb.pack(fill=tk.X, pady=2, padx=5) self.partition_list_frame.update_idletasks() self.partition_canvas.config(scrollregion=self.partition_canvas.bbox("all")) self.partition_canvas.bind_all("<MouseWheel>", lambda event: self.partition_canvas.yview_scroll(int(-1 * (event.delta / 120)), "units")) def toggle_select_all(self): select_all = self.select_all_var.get() for var in self.partition_vars.values(): var.set(select_all) def start_backup(self): selected_partitions = [part for part, var in self.partition_vars.items() if var.get()] if not selected_partitions: messagebox.showwarning("警告", "请至少选择一个分区进行备份") return backup_dir = filedialog.askdirectory(title="选择备份保存目录") if not backup_dir: self.append_cmd_output("备份已取消") return self.append_cmd_output(f"将备份保存到: {backup_dir}") self.backup_btn.config(state=tk.DISABLED, text="备份中,请稍等...") threading.Thread( target=self._perform_backup, args=(selected_partitions, backup_dir), daemon=True ).start() def _perform_backup(self, partitions, backup_dir): try: self.append_cmd_output("准备备份环境...") self._execute_adb_command("adb shell \"rm -rf /sdcard/backup\"") self._execute_adb_command("adb shell \"mkdir -p /sdcard/backup\"") for i, partition in enumerate(partitions): progress = (i + 1) / len(partitions) * 100 self.append_cmd_output(f"正在备份 {partition} ({i + 1}/{len(partitions)}) - {progress:.1f}%") backup_cmd = f"adb shell \"dd if=/dev/block/{partition} | gzip -c > /sdcard/backup/{partition}.img.gz\"" self._execute_adb_command(backup_cmd) self.append_cmd_output("正在从设备下载备份文件...") pull_cmd = f"adb pull /sdcard/backup \"{backup_dir}\"" self._execute_adb_command(pull_cmd) self._execute_adb_command("adb shell \"rm -rf /sdcard/backup\"") self.append_cmd_output(f"备份完成! 文件已保存到: {backup_dir}") messagebox.showinfo("完成", f"备份已完成,文件保存在:\n{backup_dir}") except Exception as e: self.append_cmd_output(f"备份出错: {str(e)}") messagebox.showerror("错误", f"备份过程中出错:\n{str(e)}") finally: self.root.after(0, self.backup_btn.config, {"state": tk.NORMAL, "text": "备份选中分区"}) def _execute_adb_command(self, cmd): self.append_cmd_output(f"执行: {cmd}") process = subprocess.Popen( cmd, shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True, bufsize=1, universal_newlines=True ) while True: output = process.stdout.readline() if output == '' and process.poll() is not None: break if output: self.root.after(0, self.append_cmd_output, output.strip()) stderr = process.stderr.read() if stderr: self.root.after(0, self.append_cmd_output, f"错误:\n{stderr.strip()}") if process.returncode != 0: raise Exception(f"命令执行失败: {cmd}") def append_cmd_output(self, text): self.cmd_text.insert(tk.END, text + "\n") self.cmd_text.see(tk.END) if __name__ == "__main__": root = tk.Tk() app = ADBBackupTool(root) root.mainloop() 在这个基础上曾加http下载 sparsege格式 这个先取消
07-24
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值