1#!/bin/sh
2
3# Created on April 13, 2012
4#
5# Copyright (c) 2012, NETGEAR, Inc.
6# 350 East Plumeria, San Jose California, 95134, U.S.A.
7# All rights reserved.
8#
9# This software is the confidential and proprietary information of
10# NETGEAR, Inc. ("Confidential Information").  You shall not
11# disclose such Confidential Information and shall use it only in
12# accordance with the terms of the license agreement you entered into
13# with NETGEAR.
14
15# cp_installer for all devices
16# 
17#   usage:
18#       cp_installer <server-url> <cp-install-dir> <path-to-eco.env> <certificate>
19#   e.g.
20#       ./cp_installer.sh  http://updates.netgear.com/ecosystem/pkg-repo  .  .
21#       ./cp_installer.sh  https://updates.netgear.com/ecosystem/pkg-repo /media/nand .
22#       ./cp_installer.sh  https://updates.netgear.com/ecosystem/pkg-repo /media/nand . /etc/ca/CAs.txt
23#       ./cp_installer.sh  updates.netgear.com/ecosystem/pkg-repo /media/nand
24#
25#
26# This script file installs the Control Point package as
27#       <cp-install-dir>/cp.d
28
29REPO_URL=${1}
30LOCAL_DIR=${2}
31CP_INSTALL_DIR=${LOCAL_DIR}/cp.d
32
33PATH_ECO_ENV=${3}
34if [ -z ${PATH_ECO_ENV} ] || [ ${PATH_ECO_ENV} = "." ]; then
35    PATH_ECO_ENV=$PWD
36else
37    # Check if PATH_ECO_ENV is an absolute path, get the first char
38    ABSOLUTE_PATH=`echo "${PATH_ECO_ENV}" | cut -c1`
39    if [ "${ABSOLUTE_PATH}" != "/" ]; then
40        PATH_ECO_ENV=${PWD}/${PATH_ECO_ENV}
41    fi
42fi
43
44CA_FILE=${4}
45HTTPS_FLAGS=""
46
47#. ./cp.d/cpinst/cp_dbg.sh
48cp_timing_debug() {
49    if [ -n "${TIMING_DEBUG}" ] && [ ${TIMING_DEBUG} -eq 1 ]; then
50	CURR_TIME=`uptime | cut -d " " -f2`
51	echo " ___ ${CURR_TIME} ___ ${1}"
52    fi
53    return 0
54}
55
56ENV_EXISTS=0		# eco.env file exists: 0=no, 1=yes
57# source the env file, if it's in the same directory
58# otherwise the caller must do it before calling this script
59if [ -r ${PATH_ECO_ENV}/eco.env ]; then
60  echo "sourcing  ${PATH_ECO_ENV}/eco.env ..."
61  . ${PATH_ECO_ENV}/eco.env
62  ENV_EXISTS=1
63fi
64
65if [ "X${DEVICE_MODEL_NAME}" = "X" ]; then
66  echo "eco env var DEVICE_MODEL_NAME not defined. exiting ${0}"
67  exit 1
68fi
69echo "DEVICE_MODEL_NAME is $DEVICE_MODEL_NAME"
70TARGET_ID=${DEVICE_MODEL_NAME}
71
72if [ "X${FIRMWARE_VERSION}" = "X" ]; then
73  echo "        -- eco env var FIRMWARE_VERSION not defined. exiting ${0}"
74  exit 1
75fi
76
77
78# wait_mount_nand()
79#   wait for the nand partition $APPS_MOUNT_POINT 
80#   to be mounted in the system.
81#   The environment variable APPS_MOUNT_POINT must be 
82#   defined and exported before this script is called.
83
84wait_mount_nand()
85{
86    OUTFILE=/tmp/mtab.txt
87    MTABLE=/proc/mounts
88    NAND=$APPS_MOUNT_POINT
89    
90    if [ "X${NAND}" = "X" ]; then
91      echo "        -- eco env var APPS_MOUNT_POINT not defined. exiting ${0}"
92      exit 1
93    fi
94    
95    echo "APPS_MOUNT_POINT is $NAND"
96    
97    # let's put 60 seconds timeout
98    count=60
99    
100    while [ $count -ne 0 ] ;
101    do
102        # dump mtab to file for pipe
103        awk '{ print $2 }' $MTABLE > $OUTFILE
104
105        while read MOUNT
106        do
107            if [ "$MOUNT" = "$NAND" ]
108            then
109                echo "$NAND mounted ok."
110                /bin/rm $OUTFILE
111                return 0
112            fi
113        done < $OUTFILE
114        sleep 1
115        echo "wait_mount, retry"
116        count=`expr $count - 1`
117    done
118
119    echo "        -- ${0} timedout"
120    return 1
121}
122
123echo "	-- ${0}, `uptime`"
124if [ -z ${INITIAL_WAIT} ]; then
125    if [ ${ENV_EXISTS} -eq 0 ]; then
126	# eco.env file does not exist, do not sleep
127	INITIAL_WAIT=0
128    else
129	INITIAL_WAIT=75
130    fi
131fi
132echo "  -- ${0}, sleep INITIAL_WAIT=${INITIAL_WAIT} seconds"
133sleep ${INITIAL_WAIT}
134wait_mount_nand
135echo "	-- ${0}, `uptime`"
136
137RESULT=${?}
138if [ ${RESULT} -ne 0 ]; then 
139    echo "        -- wait_mount_nand failed ... exiting ${0}"
140    exit ${RESULT}
141fi
142
143normalized_string=""
144normalize_version() 
145{
146    NUMSIZE=5
147    substring=""
148    normalized_string=""
149
150    startnum=-1
151    chars=`echo ${1} | awk -v ORS="" '{ gsub(/./,"&\n") ; print }'`
152    for char in $chars; do 
153        case $char in
154            "1" | "2" | "3" | "4" | "5" | "6" | "7" | "8" | "9" | "0")
155                substring="${substring}${char}"
156                ;;
157            *)
158                if [ ${#substring} -gt 0 ]; then
159                    pack_accum=${#substring}
160                    while [ ${pack_accum} -lt ${NUMSIZE} ]; do
161                        #pack_accum=$((${pack_accum}+1))
162                        pack_accum=`expr ${pack_accum} + 1`
163                        normalized_string="${normalized_string}0"
164                    done
165                    normalized_string="${normalized_string}${substring}"
166                    substring=""
167                fi
168                normalized_string="${normalized_string}${char}"
169                ;;
170        esac
171    done
172
173    if [ ${#substring} -gt 0 ]; then
174        pack_accum=${#substring}
175        while [ ${pack_accum} -lt ${NUMSIZE} ]; do
176            #pack_accum=$((${pack_accum}+1))
177            pack_accum=`expr ${pack_accum} + 1`
178            normalized_string="${normalized_string}0"
179        done
180        normalized_string="${normalized_string}${substring}"
181    fi
182}
183
184
185#   determine the best fit upgrade version
186UPDATE_FIRMWARE_VERSION=""
187TEMP_FILE=/tmp/tmp_pkglists
188
189
190get_https_flags()
191{
192    # get the URL scheme (http or https)
193    SCHEME=`echo ${REPO_URL} | cut -d ':' -f1`
194    # if no scheme in the URL, prepend "https://"
195    if [ "${SCHEME}" != "http" ] && [ "${SCHEME}" != "https" ]; then
196        REPO_URL="https://${REPO_URL}"
197        SCHEME=https
198    fi
199    if [ "${SCHEME}" != "http" ]; then
200        if [ "${CA_FILE}" != "" ]; then
201            CERTIFICATE=${CA_FILE}
202            if [ "${CERTIFICATE}" = "" ]; then
203                CERTIFICATE=/etc/ca/CAs.txt
204            fi
205        fi
206        HTTPS_FLAGS="--secure-protocol=auto  --ca-certificate=${CERTIFICATE}"
207    fi
208}
209
210
211check_update_server()
212{
213    # each ping = sleep 10 sec (if internet is down), or
214    #		= default deadline 20 sec + sleep 10 sec
215    # max no. of pings = 15
216    # max ping time = 150 sec (internet down), or
217    #		    = 450 sec (internet up)
218    # max wait time = initial wait (75 sec) + total ping
219    #		    = 225 sec (internet down), or
220    #		    = 525 sec (internet up)
221    SLEEP_TIME=10
222    ping_count=15
223
224    SERVER_NAME=`echo ${REPO_URL} | cut -d "/" -f3`
225    echo "      -- checking server ${SERVER_NAME}"
226    while [ ${ping_count} -ne 0 ] ;
227    do
228        PING_STAT=`((ping -c1 updates.netgear.com) > /dev/null 2>&1) && echo "up" || echo "down"`
229        if [ ${PING_STAT} = "up" ]; then
230            return 0
231        fi
232        ping_count=`expr $ping_count - 1`
233        sleep $SLEEP_TIME
234        echo -n "#"
235    done
236
237    # Server is not reachable.
238    echo " -- Unable to ping server ${SERVER_NAME}"
239    return 1
240}
241
242
243validate_cp_pkg()
244{
245    PACKAGE_DB=${CP_INSTALL_DIR}/"pkgdb"
246    PKG_LIST=${PACKAGE_DB}/"pkg_list"
247
248    if [ ! -f ${PKG_LIST} ]; then 
249        echo "  -- ${PKG_LIST} not found"
250        return 1
251    fi
252    #   iterate through the packages in the pkg_list file
253    while read line; do
254        PKG_NAME=`echo ${line} | cut -d " " -f1`
255        PKG_STATE_FILE=${PACKAGE_DB}/${PKG_NAME}/"state"
256        if [ ! -f ${PKG_STATE_FILE} ]; then 
257            echo "      -- ${PKG_STATE_FILE} not found"
258            return 1
259        fi
260        PKG_STATE=`cat ${PKG_STATE_FILE}`
261        echo "  -- ${PKG_STATE_FILE} = ${PKG_STATE}"
262        if [ ${PKG_STATE} != "validated" ]; then
263            return 1
264        fi
265    done < ${PKG_LIST}
266    # All packages validated OK
267    return 0
268}
269
270
271get_update_version()
272{
273    check_update_server
274    # result: 0=up / 1=down
275    SERVER_STAT=${?}
276    CP_VALIDATE_RESULT=0
277
278    DONE=0
279    while [ $DONE -eq 0 ]; do
280        if [ -f ${TEMP_FILE} ]; then
281            rm -f ${TEMP_FILE}
282        fi
283        
284	cp_timing_debug "${0} get_update_version wget ### > > >"
285        wget -4 ${HTTPS_FLAGS} ${REPO_URL}/${TARGET_ID} -T 30 --tries=1 -O ${TEMP_FILE}
286	cp_timing_debug "get_update_version wget < < < ###"
287        RESULT=${?}
288        if [ ${RESULT} -ne 0 ]; then 
289            # If CP was downloaded before, but the server is down,
290            # then validate the existing CP package.
291            # The validation will need to be run once.
292            if [ -d ${CP_INSTALL_DIR}/cpinst ] && [ ${SERVER_STAT} -eq 1 ]; then
293                if [ ${CP_VALIDATE_RESULT} -eq 0 ]; then
294                    validate_cp_pkg
295                    CP_VALIDATE_RESULT=${?}
296                    # result: 0=validated / 1=not
297                fi
298                if [ ${CP_VALIDATE_RESULT} -eq 0 ]; then
299                    echo "        -- update server offline, but a cp is present!"
300                    return 1
301                fi
302            fi
303            echo "        -- Unable to connect to package server. ... retrying in 1 minute ${0}"
304            sleep 60
305        else
306            DONE=1
307        fi
308    done
309    
310    #   always update cpinst if can contact the update server
311    if [ -d ./cpinst ]; then
312        rm -rf ./cpinst
313    fi
314    
315    # get a normalized version of the firmware version
316    normalize_version ${FIRMWARE_VERSION}
317    normalized_firmware_version=${normalized_string}
318    normalized_update_version=""
319
320    for repo_dir in `grep "pkg_cont-" $TEMP_FILE | sed -e 's/.*href=\"//' -e 's/\/\">.*$//'`; do
321        #   extract version
322        repo_version=`echo ${repo_dir} | sed -e 's/.*pkg_cont\-//'`
323        normalize_version ${repo_version}
324        normalized_repo_version=${normalized_string}
325        
326        normalize_version `echo ${repo_version} | sed 's/-[^-]*$//'`
327        min_target_version=${normalized_string}
328        if [ ! "${min_target_version}" \> "${normalized_firmware_version}" ]; then
329            if [ -z "${UPDATE_FIRMWARE_VERSION}" -o ! "${normalized_repo_version}" \< "${normalized_update_version}" ]; then
330                UPDATE_FIRMWARE_VERSION=${repo_version}
331                normalized_update_version=${normalized_repo_version}
332            fi
333        fi
334    done 
335
336    #   clean up the temp file
337    if [ -f ${TEMP_FILE} ]; then
338        rm -f ${TEMP_FILE}
339    fi
340
341    echo "INSTALLED_FIRMWARE_VERSION = \"${FIRMWARE_VERSION}\""
342    if [ -z "${UPDATE_FIRMWARE_VERSION}" ]; then
343        echo "no corresponding version, exiting ... !"
344        exit 1
345    fi
346    echo "UPDATE_FIRMWARE_VERSION = \"${UPDATE_FIRMWARE_VERSION}\""
347    
348    return 0
349}
350
351
352install_cpinst()
353{
354    get_https_flags
355    get_update_version
356    RESULT=${?}
357    if [ $RESULT -ne 0 ]; then
358        echo "        -- revert to installed cpinst!"
359        return 0
360    fi
361
362    #   fetch the cp startup scripts from the repository
363    DONE=0
364    while [ $DONE -eq 0 ]; do
365        # delete partially downloaded file just in case
366        rm -f /tmp/cpinst.tar.gz
367
368	cp_timing_debug "${0} install_cpinst wget === > > >"
369        wget -4 ${HTTPS_FLAGS} ${REPO_URL}/${TARGET_ID}/pkg_cont-${UPDATE_FIRMWARE_VERSION}/packages/cpinst.tar.gz -O /tmp/cpinst.tar.gz
370	cp_timing_debug "install_cpinst wget < < < ==="
371        RESULT=${?}
372        if [ ${RESULT} -ne 0 ]; then 
373            echo "        -- Unable to connect to package server. ... retrying in 1 minute ${0}"
374            sleep 60
375        else
376            DONE=1
377        fi
378    done
379
380    tar -zxf /tmp/cpinst.tar.gz
381    rm -rf ./META-INF
382    rm -rf /tmp/cpinst.tar.gz
383}
384
385#   call the core startup script
386if [ -d ${LOCAL_DIR} ]; then
387    if [ ! -d ${CP_INSTALL_DIR} ]; then
388        mkdir ${CP_INSTALL_DIR}
389    fi
390    cd ${CP_INSTALL_DIR}
391    
392    install_cpinst
393
394    export HTTPS_FLAGS
395
396    if [ -x ./cpinst/cp_startup.sh ]; then
397        ./cpinst/cp_startup.sh ${TARGET_ID} ${FIRMWARE_VERSION} ${REPO_URL} ${PATH_ECO_ENV}
398    else
399        echo "        -- startup script doesn't exist ... exiting ${0}"
400        exit 1
401    fi
402fi
403
404exit 0
405
406