source: src-sh/libsh/functions.sh @ 4c06449

9.1-release9.2-releasereleng/10.0releng/10.0.1releng/10.0.2releng/10.0.3
Last change on this file since 4c06449 was 4c06449, checked in by Kris Moore <kris@…>, 17 months ago

Add all the new pkg conflict logic to libsh

Will detect if something is going to fail during pkg process and
warn the user either graphically or via CLI

In GUI add detection of the GUI hooks from conflict detection

When we find conflicts, remove them for now. (More advanced logic
may be incoming as I play with it)

  • Property mode set to 100755
File size: 13.6 KB
Line 
1#!/bin/sh
2# Functions we can source for pc-bsd scripts
3# Author: Kris Moore
4# Copyright: 2012
5# License: BSD
6##############################################################
7
8PCBSD_ETCCONF="/usr/local/etc/pcbsd.conf"
9
10download_cache_packages()
11{
12  if [ ! -e "/usr/local/etc/pkg.conf" ] ; then
13    exit_err "No /usr/local/etc/pkg.conf!"
14  fi
15
16  # Tickle pkg update first
17  pkg-static update
18  local ARCH="`uname -m`"
19
20  ${1} > /tmp/.pkgUpList.$$
21
22  while read line
23  do
24     lTag=`echo $line | awk '{print $1}'` 
25     case $lTag in
26    Upgrading|Downgrading) pkgList="`echo $line | awk '{print $2}' | sed 's|:||g'`-`echo $line | awk '{print $5}'`.txz $pkgList" ;;
27 Reinstalling) pkgList="`echo $line | awk '{print $2}'`.txz $pkgList" ;;
28   Installing) pkgList="`echo $line | awk '{print $2}' | sed 's|:||g'`-`echo $line | awk '{print $3}'`.txz $pkgList" ;;
29                    *) continue ;;
30     esac
31
32  done < /tmp/.pkgUpList.$$
33  rm /tmp/.pkgUpList.$$
34
35  # Get the PKG_CACHEDIR
36  PKG_CACHEDIR="/var/cache/pkg"
37  cat /usr/local/etc/pkg.conf | grep -q "^PKG_CACHEDIR:"
38  if [ $? -eq 0 ] ; then
39    PKG_CACHEDIR="`grep '^PKG_CACHEDIR:' /usr/local/etc/pkg.conf | awk '{print $2}'`"
40  fi
41  if [ -z "$PKG_CACHEDIR" ] ; then
42     exit_err "Failed getting PKG_CACHEDIR"
43  fi
44  export PKG_CACHEDIR
45
46  PKGREL=`uname -r | cut -d '-' -f 1-2`
47
48  # Where are the packages on our mirrors?
49  pkgUrl="/packages/${PKGREL}/${ARCH}"
50
51  if [ ! -d "$PKG_CACHEDIR/All" ] ; then
52     mkdir -p ${PKG_CACHEDIR}/All
53  fi
54
55  for i in $pkgList
56  do
57    if [ -e "${PKG_CACHEDIR}/All/${i}" ] ; then rm ${PKG_CACHEDIR}/All/${i} ; fi
58    get_file_from_mirrors "${pkgUrl}/All/${i}" "${PKG_CACHEDIR}/All/${i}"
59    if [ $? -ne 0 ] ; then
60      exit_err "Failed downloading: /${pkgUrl}/All/${i}"
61    fi
62  done
63}
64
65get_mirror() {
66
67  # Check if we already looked up a mirror we can keep using
68  if [ -n "$CACHED_PCBSD_MIRROR" ] ; then
69     VAL="$CACHED_PCBSD_MIRROR"
70     export VAL
71     return
72  fi
73
74  # Set the mirror URL
75  VAL="`cat ${PCBSD_ETCCONF} 2>/dev/null | grep 'PCBSD_MIRROR: ' | sed 's|PCBSD_MIRROR: ||g'`"
76  if [ -n "$VAL" ] ; then
77     echo "Using mirror: $VAL"
78     CACHED_PCBSD_MIRROR="$VAL"
79     export VAL CACHED_PCBSD_MIRROR
80     return
81  fi
82
83  echo "Getting regional mirror..."
84  . /etc/profile
85
86  # No URL? Lets get one from the master server
87  local mFile="${HOME}/.mirrorUrl.$$"
88  touch $mFile
89  fetch -o $mFile http://getmirror.pcbsd.org >/dev/null 2>/dev/null
90  VAL="`cat $mFile | grep 'URL: ' | sed 's|URL: ||g'`"
91  rm $mFile
92  if [ -n "$VAL" ] ; then
93     echo "Using mirror: $VAL"
94     CACHED_PCBSD_MIRROR="$VAL"
95     export VAL CACHED_PCBSD_MIRROR
96     return
97  fi
98
99  # Still no mirror? Lets try the PC-BSD FTP server...
100  VAL="ftp://ftp.pcbsd.org/pub/mirror"
101  CACHED_PCBSD_MIRROR="$VAL"
102  export VAL CACHED_PCBSD_MIRROR
103  echo "Using mirror: $VAL"
104  return 
105}
106
107# Function which returns the installed list of PC-BSD mirrors for use
108# with the aria2c command
109# Will return just a single mirror, if the user has manually specified one
110# in /usr/local/etc/pcbsd.conf
111get_aria_mirror_list()
112{
113  if [ -z $1 ] ; then
114     exit_err "Need to supply file to grab from mirrors..."
115  fi
116
117  # Set the mirror URL
118  local VAL="`cat ${PCBSD_ETCCONF} 2>/dev/null | grep 'PCBSD_MIRROR: ' | sed 's|PCBSD_MIRROR: ||g'`"
119  if [ -n "$VAL" ] ; then
120     echo "${VAL}${1}"
121     return
122  fi
123
124  if [ ! -e "/usr/local/share/pcbsd/conf/pcbsd-mirrors" ] ; then
125     exit_err "Missing mirror list: /usr/local/share/pcbsd/conf/pcbsd-mirrors"
126  fi
127
128  # Build the mirror list
129  while read line
130  do
131    VAL="$VAL ${line}${1}"
132  done < /usr/local/share/pcbsd/conf/pcbsd-mirrors
133  echo ${VAL}
134}
135
136# Function to download a file from the pcbsd mirrors
137# Arg1 = Remote File URL
138# Arg2 = Where to save file
139get_file_from_mirrors()
140{
141   _rf="${1}"
142   _lf="${2}"
143
144   # Get any proxy information
145   . /etc/profile
146
147   # Split up the dir / file name
148   local aDir=`dirname $_lf`
149   local aFile=`basename $_lf`
150
151   # Server status flag
152   local aStatFile=${HOME}/.pcbsd-aria-stat
153   if [ -e "$aStatFile" ] ; then
154     local aStat="--server-stat-of=$aStatFile --server-stat-if=$aStatFile --uri-selector=adaptive --server-stat-timeout=864000"
155   else
156     local aStat="--server-stat-of=$aStatFile --uri-selector=adaptive"
157   fi
158   touch $aStatFile
159
160   # Get mirror list
161   local mirrorList="$(get_aria_mirror_list $1)"
162   
163   # Running from a non GUI?
164   if [ "$GUI_FETCH_PARSING" != "YES" -a "$PBI_FETCH_PARSING" != "YES" -a -z "$PCFETCHGUI" ] ; then
165      aria2c -k 5M ${aStat} --check-certificate=false --file-allocation=none -d "${aDir}" -o "${aFile}" ${mirrorList}
166      return $?
167   fi
168
169   echo "FETCH: ${_rf}"
170
171   # Doing a front-end download, parse the output of fetch
172   _eFile="/tmp/.fetch-exit.$$"
173   fetch -s "`echo ${mirrorList} | awk '{print $1}'`" > /tmp/.fetch-size.$$ 2>/dev/null
174   _fSize=`cat /tmp/.fetch-size.$$ 2>/dev/null`
175   _fSize="`expr ${_fSize} / 1024 2>/dev/null`"
176   rm "/tmp/.fetch-size.$$" 2>/dev/null
177   _time=1
178   if [ -z "$_fSize" ] ; then _fSize=0; fi
179
180   ( aria2c -o ${aFile} -d ${aDir} -k 5M ${aStat} --check-certificate=false --file-allocation=none ${mirrorList} >/dev/null 2>/dev/null ; echo "$?" > ${_eFile} ) &
181   FETCH_PID=`ps -auwwwx | grep -v grep | grep "aria2c -o ${aFile}" | awk '{print $2}'`
182   while :
183   do
184      if [ -e "${_lf}" ] ; then
185         _dSize=`du -k ${_lf} | tr -d '\t' | cut -d '/' -f 1`
186         if [ $(is_num "$_dSize") ] ; then
187            if [ ${_fSize} -lt ${_dSize} ] ; then _dSize="$_fSize" ; fi
188            _kbs=`expr ${_dSize} \/ $_time`
189            echo "SIZE: ${_fSize} DOWNLOADED: ${_dSize} SPEED: ${_kbs} KB/s"
190         fi
191      fi
192
193      # Make sure download isn't finished
194      ps -p $FETCH_PID >/dev/null 2>/dev/null
195      if [ "$?" != "0" ] ; then break ; fi
196      sleep 2
197      _time=`expr $_time + 2`
198   done
199
200   _err="`cat ${_eFile}`"
201   rm ${_eFile} 2>/dev/null
202   if [ "$_err" = "0" ]; then echo "FETCHDONE" ; fi
203   unset FETCH_PID
204   return $_err
205
206}
207
208# Function to download a file from remote using fetch
209# Arg1 = Remote File URL
210# Arg2 = Where to save file
211# Arg3 = Number of attempts to make before failing
212get_file() {
213
214        _rf="${1}"
215        _lf="${2}"
216        _ftries=${3}
217        if [ -z "$_ftries" ] ; then _ftries=3; fi
218
219        # Get any proxy information
220        . /etc/profile
221
222        if [ -e "${_lf}" ] ; then
223                echo "Resuming download of: ${_lf}"
224        fi
225
226        if [ "$GUI_FETCH_PARSING" != "YES" -a -z "$PCFETCHGUI" ] ; then
227                fetch -r -o "${_lf}" "${_rf}"
228                _err=$?
229        else
230                echo "FETCH: ${_rf}"
231
232                # Doing a front-end download, parse the output of fetch
233                _eFile="/tmp/.fetch-exit.$$"
234                fetch -s "${_rf}" > /tmp/.fetch-size.$$ 2>/dev/null
235                _fSize=`cat /tmp/.fetch-size.$$ 2>/dev/null`
236                _fSize="`expr ${_fSize} / 1024 2>/dev/null`"
237                rm "/tmp/.fetch-size.$$" 2>/dev/null
238                _time=1
239
240                ( fetch -r -o "${_lf}" "${_rf}" >/dev/null 2>/dev/null ; echo "$?" > ${_eFile} ) &
241                FETCH_PID=`ps -auwwwx | grep -v grep | grep "fetch -r -o ${_lf}" | awk '{print $2}'`
242                while :
243                do
244                        if [ -e "${_lf}" ] ; then
245                                _dSize=`du -k ${_lf} | tr -d '\t' | cut -d '/' -f 1`
246                                if [ $(is_num "$_dSize") ] ; then
247                                        if [ ${_fSize} -lt ${_dSize} ] ; then _dSize="$_fSize" ; fi
248                                        _kbs=`expr ${_dSize} \/ $_time`
249                                        echo "SIZE: ${_fSize} DOWNLOADED: ${_dSize} SPEED: ${_kbs} KB/s"
250                                fi
251                        fi
252
253                        # Make sure download isn't finished
254                        ps -p $FETCH_PID >/dev/null 2>/dev/null
255                        if [ "$?" != "0" ] ; then break ; fi
256                        sleep 2
257                        _time=`expr $_time + 2`
258                done
259
260                _err="`cat ${_eFile}`"
261                rm ${_eFile} 2>/dev/null
262                if [ "$_err" = "0" ]; then echo "FETCHDONE" ; fi
263                unset FETCH_PID
264        fi
265
266        echo ""
267        if [ $_err -ne 0 -a $_ftries -gt 0 ] ; then
268                sleep 30
269                _ftries=`expr $_ftries - 1`
270
271                # Remove the local file if we failed
272                if [ -e "${_lf}" ]; then rm "${_lf}"; fi
273
274                get_file "${_rf}" "${_lf}" $_ftries     
275                _err=$?
276        fi
277        return $_err
278}
279
280# Check if a value is a number
281is_num()
282{
283        expr $1 + 1 2>/dev/null
284        return $?
285}
286
287# Exit with a error message
288exit_err() {
289        if [ -n "${LOGFILE}" ] ; then
290           echo "ERROR: $*" >> ${LOGFILE}
291        fi
292        echo >&2 "ERROR: $*"
293        exit 1
294}
295
296
297### Print an error on STDERR and bail out
298printerror() {
299  exit_err $*
300}
301
302
303# Check if the target directory is on ZFS
304# Arg1 = The dir to check
305# Arg2 = If set to 1, don't dig down to lower level directory
306isDirZFS() {
307  local _chkDir="$1"
308  while :
309  do
310     # Is this dir a ZFS mount
311     mount | grep -w "on $_chkDir " | grep -qw "(zfs," && return 0
312
313     # If this directory is mounted, but NOT ZFS
314     if [ "$2" != "1" ] ; then
315       mount | grep -qw "on $_chkDir " && return 1
316     fi
317     
318     # Quit if not walking down
319     if [ "$2" = "1" ] ; then return 1 ; fi
320 
321     if [ "$_chkDir" = "/" ] ; then break ; fi
322     _chkDir=`dirname $_chkDir`
323  done
324 
325  return 1
326}
327
328# Gets the mount-point of a particular zpool / dataset
329# Arg1 = zpool to check
330getZFSMount() {
331  local zpool="$1"
332  local mnt=`mount | grep "^${zpool} on" | grep "(zfs," | awk '{print $3}'`
333  if [ -n "$mnt" ] ; then
334     echo "$mnt"
335     return 0
336  fi
337  return 1
338}
339
340# Get the ZFS dataset of a particular directory
341getZFSDataset() {
342  local _chkDir="$1"
343  while :
344  do
345    local zData=`mount | grep " on ${_chkDir} " | grep "(zfs," | awk '{print $1}'`
346    if [ -n "$zData" ] ; then
347       echo "$zData"
348       return 0
349    fi
350    if [ "$2" != "rec" ] ; then return 1 ; fi
351    if [ "$_chkDir" = "/" ] ; then return 1 ; fi
352    _chkDir=`dirname $_chkDir`
353  done
354  return 1
355}
356
357# Get the ZFS tank name for a directory
358# Arg1 = Directory to check
359getZFSTank() {
360  local _chkDir="$1"
361
362  _chkdir=${_chkDir%/}
363  while :
364  do
365     zpath=`zfs list | awk -v path="${_chkDir}" '$5 == path { print $1 }'`
366     if [ -n "${zpath}" ] ; then
367        echo $zpath | cut -f1 -d '/'
368        return 0
369     fi
370
371     if [ "$_chkDir" = "/" ] ; then return 1 ; fi
372     _chkDir=`dirname $_chkDir`
373  done
374
375  return 1
376}
377
378# Get the mountpoint for a ZFS name
379# Arg1 = name
380getZFSMountpoint() {
381   local _chkName="${1}"
382   if [ -z "${_chkName}" ]; then return 1 ; fi
383
384   zfs list "${_chkName}" | tail -1 | awk '{ print $5 }'
385}
386
387# Get the ZFS relative path for a path
388# Arg1 = Path
389getZFSRelativePath() {
390   local _chkDir="${1}"
391   local _tank=`getZFSTank "$_chkDir"`
392   local _mp=`getZFSMountpoint "${_tank}"`
393
394   if [ -z "${_tank}" ] ; then return 1 ; fi
395
396   local _name="${_chkDir#${_mp}}"
397   echo "${_name}"
398   return 0
399}
400
401# Check if an address is IPv6
402isV6() {
403  echo ${1} | grep -q ":"
404  return $?
405}
406   
407# Is a mount point, or any of its parent directories, a symlink?
408is_symlinked_mountpoint()
409{
410        local _dir
411        _dir=$1
412        [ -L "$_dir" ] && return 0
413        [ "$_dir" = "/" ] && return 1
414        is_symlinked_mountpoint `dirname $_dir`
415        return $?
416}
417
418# Function to ask the user to press Return to continue
419rtn()
420{
421  echo -e "Press ENTER to continue\c";
422  read garbage
423};
424
425# Function to check if an IP address passes a basic sanity test
426check_ip()
427{
428  ip="$1"
429 
430  # If this is a V6 address, skip validation for now
431  isV6 "${ip}"
432  if [ $? -eq 0 ] ; then return ; fi
433
434  # Check if we can cut this IP into the right segments
435  SEG="`echo $ip | cut -d '.' -f 1 2>/dev/null`"
436  echo $SEG | grep -E "^[0-9]+$" >/dev/null 2>/dev/null
437  if [ "$?" != "0" ]
438  then
439     return 1
440  fi
441  if [ $SEG -gt 255 -o $SEG -lt 0 ]
442  then
443     return 1
444  fi
445 
446  # Second segment
447  SEG="`echo $ip | cut -d '.' -f 2 2>/dev/null`"
448  echo $SEG | grep -E "^[0-9]+$" >/dev/null 2>/dev/null
449  if [ "$?" != "0" ]
450  then
451     return 1
452  fi
453  if [ $SEG -gt 255 -o $SEG -lt 0 ]
454  then
455     return 1
456  fi
457
458  # Third segment
459  SEG="`echo $ip | cut -d '.' -f 3 2>/dev/null`"
460  echo $SEG | grep -E "^[0-9]+$" >/dev/null 2>/dev/null
461  if [ "$?" != "0" ]
462  then
463     return 1
464  fi
465  if [ $SEG -gt 255 -o $SEG -lt 0 ]
466  then
467     return 1
468  fi
469 
470  # Fourth segment
471  SEG="`echo $ip | cut -d '.' -f 4 2>/dev/null`"
472  echo $SEG | grep -E "^[0-9]+$" >/dev/null 2>/dev/null
473  if [ "$?" != "0" ]
474  then
475     return 1
476  fi
477  if [ $SEG -gt 255 -o $SEG -lt 0 ]
478  then
479     return 1
480  fi
481
482  return 0
483};
484
485check_pkg_conflicts()
486{
487  # Lets test if we have any conflicts
488  pkg-static ${1} 2>/tmp/.pkgConflicts.$$ >/tmp/.pkgConflicts.$$
489  if [ $? -eq 0 ] ; then rm /tmp/.pkgConflicts.$$ ; return ; fi
490 
491  # Found conflicts, suprise suprise, yet another reason I hate packages
492  # Lets start building a list of the old packages we can prompt to remove
493
494  # Nice ugly sed line, sure this can be neater
495  cat /tmp/.pkgConflicts.$$ | grep 'WARNING: locally installed' \
496        | sed 's|.*installed ||g' | sed 's| conflicts.*||g' | sort | uniq \
497        > /tmp/.pkgConflicts.$$.2
498  while read line
499  do
500    cList="$line $cList"
501  done < /tmp/.pkgConflicts.$$.2
502  rm /tmp/.pkgConflicts.$$.2
503  rm /tmp/.pkgConflicts.$$
504
505  if [ "$GUI_FETCH_PARSING" != "YES" -a "$PBI_FETCH_PARSING" != "YES" -a -z "$PCFETCHGUI" ] ; then
506        echo "The following packages will conflict with your pkg command:"
507        echo "-------------------------------------"
508        echo "$cList" | more
509        echo "Do you wish to remove them automatically?"
510        echo -e "Default yes: (y/n)\c"
511        read tmp
512        if [ "$tmp" != "y" -a "$tmp" != "Y" ] ; then return 1 ; fi
513  else
514        echo "PKGCONFLICTS: $cList"
515        echo "PKGREPLY: /tmp/pkgans.$$"
516        while :
517        do
518          if [ -e "/tmp/pkgans.$$" ] ; then
519            ans=`cat /tmp/pkgans.$$`
520            if [ "$ans" = "yes" ] ; then
521               break
522            else
523               return 1
524            fi
525          fi
526          sleep 3
527        done
528  fi
529
530  # Lets auto-resolve these bad-boys
531  # Right now the logic is pretty simple, you conflict, you die
532  for bPkg in $cList
533  do
534     # Nuked!
535     echo "Removing conflicting package: $bPkg"
536     pkg delete -q -y -f ${bPkg}
537  done
538
539  # Lets test if we still have any conflicts
540  pkg-static ${1} 2>/dev/null >/dev/null
541  if [ $? -eq 0 ] ; then return 0; fi
542
543  # Crapola, we still have conflicts, lets warn and bail
544  echo "ERROR: pkg ${1} is still reporting conflicts... Resolve these manually and try again"
545  return 1
546}
Note: See TracBrowser for help on using the repository browser.