aboutsummaryrefslogtreecommitdiffstats
path: root/System/apt-reinstall-bin-packages.sh
blob: 1f2e9fe56325cd0d93bf4f90d92e0207c69942cd (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
#!/bin/bash
########################################################################################################################
# apt-reinstall-bin-packages.sh
########################################################################################################################
#
# All rights reserved Ⓒ 2017-2023 sdxlive.com
#
# Written by Jean-Christophe Manciot <jcmanciot@sdxlive.com>
#
# Licensed under a GPLv3 License.
# You may not use this file except in compliance with the License. You may obtain a copy of the License at
#
#    https://www.gnu.org/licenses/gpl-3.0.md
#
# The licensor cannot revoke these freedoms as long as you follow the license terms.
#
# Attribution — You must give appropriate credit, provide a link to the license, and indicate if changes were made. 
# You may do so in any reasonable manner, but not in any way that suggests the licensor endorses you or your use.
#
########################################################################################################################

########################################################################################################################
#
# Reinstalling all installed binary packages, i.e. all packages which have an installation candidate.
# It is possible to pass a perl regular expression to narrow the search for installed binary packages which need to be 
# reinstalled.
# No manual user's consent will be asked before each package reinstallation.
#
########################################################################################################################
#
# Requirements:
# ------------
#
#  - We assume that apt alongside all its dependencies is already installed.
#
########################################################################################################################
#
# Parameters:
# ----------
#
# - <etc_enabled>: 
#       * true|yes: the binary packages will be reinstalled only if they contain at least one configuration file (in /etc)
#                   For advices about how to set related dpkg options in /etc/apt/apt.conf.d/local (for instance) to 
#                   control how the conffiles will be automatically managed during the reinstallation (without manual 
#                   user's consent'): 
#                       + --force-confold
#                       + --force-confnew
#                       + --force-confdef
#                       + --force-confmiss
#                   Cf. https://raphaelhertzog.com/2010/09/21/debian-conffile-configuration-file-managed-by-dpkg/
#                   The following message generated by apt is of interest:
#                   "Package distributor has shipped an updated version" 
#       * false|no: the binary packages won't be reinstalled if they contain at least one configuration file (in /etc)
#       * neutral:  the binary packages will be reinstalled whether or not they contain at least one configuration file (in /etc)    
#                   (default)
# - <perl_regex_include_pattern>: optional perl regular expression used with 'grep -P --' to narrow the search for the  
#                                 matched installed binary packages. Some special characters may need to be escaped.
#                                 Use with caution: setting <auto> to false is strongly advised when using 
#                                 <perl_regex_include_pattern>.
# - <perl_regex_exclude_pattern>: optional perl regular expression used with 'grep -Pv --' to exclude some binary packages
#                                 from the search. Some special characters may need to be escaped.
#                                 Use with caution: setting <auto> to false is strongly advised when using 
#                                 <perl_regex_exclude_pattern>.
# - <apt_non_interactive_enabled>: boolean
#       * true: 
#               + debconf/frontend is set to Noninteractive at the beginning of the script then
#               + debconf/frontend is set to Dialog at the end of the script
#       * false: debconf/frontend is not modified
#                (default)
#
#######################################################################################################################
#
# Usage example:
# -------------
# 
# Reinstalling all binary python packages
# apt-reinstall-bin-packages.sh neutral python
#
########################################################################################################################
# set -x
# Expanding aliases
shopt -s expand_aliases

etc_enabled=$1
perl_regex_include_pattern=$2
perl_regex_exclude_pattern=$3
apt_non_interactive_enabled=$4
PARAMETERS_NUMBER=1

case "$#" in
        0)
                etc_enabled=neutral
                ;;
        $PARAMETERS_NUMBER)
                case "$1" in
                        -h|--help)
                                end_of_help_message_line_number=$(awk '/^# set -x/{ print NR; exit }' $(which apt-reinstall-bin-packages.sh))
                                ((end_of_help_message_line_number--))
                                if [[ (-n $(apt-cache -q=0 2>&1 policy zenity | grep Installed)) && (! ($(apt-cache -q=0 2>&1 policy zenity | grep Installed) =~ none)) ]]; then
                                        awk -v var=$end_of_help_message_line_number 'NR >= 2 && NR <= var' $(which apt-reinstall-bin-packages.sh) | zenity --text-info --title "apt-reinstall-bin-packages.sh help" --width 1350 --height 1000
                                else
                                        awk -v var=$end_of_help_message_line_number 'NR >= 2 && NR <= var' $(which apt-reinstall-bin-packages.sh)
                                fi
                                exit 1
                                ;;
                        *)
                                perl_regex_include_pattern=''
                                perl_regex_exclude_pattern=''
                                apt_non_interactive_enabled=false
                                ;;
                esac
                ;;
        $((PARAMETERS_NUMBER+1)))
                perl_regex_exclude_pattern=''
                apt_non_interactive_enabled=false
                ;;
        $((PARAMETERS_NUMBER+2)))
                apt_non_interactive_enabled=false
                ;;
        $((PARAMETERS_NUMBER+3)))
                ;;
        *)
                echo -e ''$_{1..180}'\b-'
                echo Wrong number of parameters: $(basename $0) $@
                echo -e ''$_{1..180}'\b-'
                exit 1
                ;;
esac

# Escaping the '$.*?/|\^(){}+@[]' characters for later grep -P searched string
# Letters, digits and '-' must not be escaped
alias escape_grep_pcre_search_string=$'sed -e \'s|[]$.*?/|\^(){}+@[]|\\\&|g\''
# '

dpkg-lock.sh apt update

if [[ $apt_non_interactive_enabled == true ]]; then
        echo 'debconf debconf/frontend select Noninteractive' | debconf-set-selections
fi        

# Installing dependencies needed to play an alert or a completion sound when necessary
dpkg-lock.sh apt -y install alsa-utils emacspeak pidgin-data

# Installed binary packages
sorted_bin_package_names=()
if [[ -n "$perl_regex_exclude_pattern" ]]; then
        readarray -t sorted_bin_package_names < <(apt list --installed 2>/dev/null | grep -v ^Listing\.\.\.$ | grep -Pv -- "$perl_regex_exclude_pattern" | grep -P -- "$perl_regex_include_pattern" | cut -d '/' -f 1 | sort -u)
else
        readarray -t sorted_bin_package_names < <(apt list --installed 2>/dev/null | grep -v ^Listing\.\.\.$ | grep -P -- "$perl_regex_include_pattern" | cut -d '/' -f 1 | sort -u)
fi

reinstalled_bin_package_names_versions=()
reinstallation_failed_bin_package_names_versions=()
no_installation_candidate_bin_package_names_versions=()

for bin_package_name in "${sorted_bin_package_names[@]}"
do
        grep_esc_bin_package_name="$(echo "$bin_package_name" | escape_grep_pcre_search_string)"

        bin_package_installation_candidates=()
        bin_package_installed_version=$(apt-cache -q=0 2>&1 policy "$bin_package_name" | grep Installed | sed -E 's|^.*?: (.*)$|\1|g')
        readarray -t bin_package_installation_candidates < <(apt-cache -q=0 2>&1 madison "$bin_package_name" | grep -P "$grep_esc_bin_package_name" | grep amd64)

        if [[ ${#bin_package_installation_candidates[@]} -ne 0 ]]; then
                etc_presence=()
                case $etc_enabled in
                        true|yes)
                                readarray -t etc_presence< <(apt-file list ${bin_package_name} | grep /etc)
                                if [[ ${#etc_presence[@]} -eq 0 ]]; then
                                        continue
                                fi
                                ;;
                        false|no)
                                readarray -t etc_presence< <(apt-file list ${bin_package_name} | grep /etc)
                                if [[ ${#etc_presence[@]} -ne 0 ]]; then
                                        continue
                                fi
                                ;;
                        *)
                                # Neutral
                                ;;
                esac

                printf "\n"
                echo -e ''$_{1..180}'\b='
                echo "Reinstalling: ${bin_package_name}=${bin_package_installed_version}"
                echo -e ''$_{1..180}'\b='
                # Passing package version so that held packages can be reinstalled without upgrade
                dpkg-lock.sh apt --show-progress --reinstall 2>&1 install "${bin_package_name}=${bin_package_installed_version}"
                return_code=$?
                if [[ $return_code -eq 0 ]]; then
                        reinstalled_bin_package_names_versions=("${reinstalled_bin_package_names_versions[@]}" "${bin_package_name}=${bin_package_installed_version}")
                else
                        reinstallation_failed_bin_package_names_versions=("${reinstallation_failed_bin_package_names_versions[@]}" "${bin_package_name}=${bin_package_installed_version}")
                fi
        else
                no_installation_candidate_bin_package_names_versions=("${no_installation_candidate_bin_package_names_versions[@]}" "${bin_package_name}=${bin_package_installed_version}")
        fi
done

if [[ ${#reinstalled_bin_package_names_versions[@]} -ne 0 ]]; then
        printf "\n"
        echo -e ''$_{1..180}'\b='
        echo "The following binary packages have been reinstalled:"
        echo -e ''$_{1..180}'\b='
        echo "bin_package_name <--> bin_package_installed_version:"
        echo -e ''$_{1..180}'\b='
        printf "%s\n" "${reinstalled_bin_package_names_versions[@]}" | sort -uV | sed -E 's|=| <--> |g'
fi

if [[ ${#reinstallation_failed_bin_package_names_versions[@]} -ne 0 ]]; then
        printf "\n"
        echo -e ''$_{1..180}'\b='
        echo "The following binary packages couldn't be reinstalled due to an issue:"
        echo -e ''$_{1..180}'\b='
        echo "bin_package_name <--> bin_package_installed_version:"
        echo -e ''$_{1..180}'\b='
        printf "%s\n" "${reinstallation_failed_bin_package_names_versions[@]}" | sort -uV | sed -E 's|=| <--> |g'
fi

if [[ ${#no_installation_candidate_bin_package_names_versions[@]} -ne 0 ]]; then
        printf "\n"
        echo -e ''$_{1..180}'\b='
        echo "The following binary packages couldn't be reinstalled due to the lack of installation candidate:"
        echo -e ''$_{1..180}'\b='
        echo "bin_package_name <--> bin_package_installed_version:"
        echo -e ''$_{1..180}'\b='
        printf "%s\n" "${no_installation_candidate_bin_package_names_versions[@]}" | sort -uV | sed -E 's|=| <--> |g'
fi

aplay /usr/share/sounds/purple/login.wav
if [[ $apt_non_interactive_enabled == true ]]; then
        echo 'debconf debconf/frontend select Dialog' | debconf-set-selections
fi        
exit 0