96e4f6db55335e3263e59aa9d702d87bb28bfa8d
[distro-functions] / src / package-manager-abstractions
1 #!/bin/bash
2 # Copyright (C) 2014 Ian Kelling
3
4 # Licensed under the Apache License, Version 2.0 (the "License");
5 # you may not use this file except in compliance with the License.
6 # You may obtain a copy of the License at
7
8 # http://www.apache.org/licenses/LICENSE-2.0
9
10 # Unless required by applicable law or agreed to in writing, software
11 # distributed under the License is distributed on an "AS IS" BASIS,
12 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 # See the License for the specific language governing permissions and
14 # limitations under the License.
15
16 # basic yum/apt package manager abstraction, plus a few minor conveniences
17 if command -v yum &> /dev/null; then
18 # package manager
19 p() {
20 local s; [[ $EUID != 0 ]] && s=sudo
21 $s yum "$@"
22 }
23 # package install
24 pi() {
25 local s; [[ $EUID != 0 ]] && s=sudo
26 $s yum -y install "$@"
27 }
28 # package find
29 pfd() {
30 local s; [[ $EUID != 0 ]] && s=sudo
31 $s yum search "$@"
32 }
33 # package remove/uninstall
34 pu() {
35 local s; [[ $EUID != 0 ]] && s=sudo
36 $s yum autoremove "$@"
37 }
38 pup() { # upgrade
39 local s; [[ $EUID != 0 ]] && s=sudo
40 $s yum -y distro-sync full "$@"
41 }
42 # package list info
43 pl() {
44 yum info "$@"
45 }
46 pfile() {
47 yum whatprovides \*/$1
48 }
49
50 elif command -v apt-get &>/dev/null; then
51 plock-wait() {
52 local i
53 i=0
54 while fuser /var/lib/dpkg/lock &>/dev/null; do
55 sleep 1
56 i=$(( i+1 ))
57 if (( i > 300 )); then
58 echo "error: timed out waiting for /var/lib/dpkg/lock" >&2
59 return 1
60 fi
61 done
62 }
63 pcheck() {
64 for arg; do
65 if [[ $1 == -* ]]; then
66 shift
67 else
68 break
69 fi
70 done
71 if dpkg -s -- "$@" |& grep -Fx "Status: install ok installed" &>/dev/null; then
72 return 1
73 fi
74 return 0
75 }
76 pp() { # package policy
77 apt-cache policy $@
78 }
79 p() {
80 local s; [[ $EUID != 0 ]] && s=sudo
81 case $1 in
82 install)
83 $s apt-get --purge --auto-remove "$@"
84 ;;
85 *)
86 $s apt-get "$@"
87 ;;
88 esac
89 }
90 pupdate() {
91 local now t s f cachetime limittime; [[ $EUID != 0 ]] && s=sudo
92 # update package list if its more than an 2 hours old
93 f=/var/cache/apt/pkgcache.bin
94 if [[ -r $f ]]; then
95 cachetime=$(stat -c %Y $f )
96 else
97 cachetime=0
98 fi
99 now=$(date +%s)
100 limittime=$(( now - 60*60*2 ))
101 for f in /etc/apt/sources.list /etc/apt/sources.list.d/*.list; do
102 if [[ -r $f ]]; then
103 t=$(stat -c %Y $f )
104 if (( t > limittime )); then
105 limittime=$t
106 fi
107 fi
108 done
109 if (( cachtime > limittime )); then
110 $s apt-get update
111 fi
112 }
113 pi() {
114 pcheck "$@" || return 0
115 pupdate
116 if [[ $- != *i* ]]; then
117 echo pi "$*"
118 fi
119 if [[ $EUID == 0 ]]; then
120 DEBIAN_FRONTEND=noninteractive apt-get -y install --purge --auto-remove "$@"
121 else
122 sudo DEBIAN_FRONTEND=noninteractive apt-get -y install --purge --auto-remove "$@"
123 fi
124
125 }
126
127 pi-nostart() {
128 local ret=
129 pcheck "$@" || return 0
130 plock-wait
131 pupdate
132 local f=/usr/sbin/policy-rc.d
133 if [[ $- != *i* ]]; then
134 echo pi-nostart "$@"
135 fi
136 if [[ $EUID == 0 ]]; then
137 dd of=$f status=none <<EOF
138 #!/bin/sh
139 exit 101
140 EOF
141 chmod +x $f
142 DEBIAN_FRONTEND=noninteractive apt-get -y install --purge --auto-remove "$@" || ret=$?
143 rm $f
144 else
145 sudo dd of=$f status=none <<EOF
146 #!/bin/sh
147 exit 101
148 EOF
149 sudo chmod +x $f
150 sudo DEBIAN_FRONTEND=noninteractive apt-get -y install --purge --auto-remove "$@" || ret=$?
151 sudo rm $f
152 fi
153 return $ret
154 }
155 # package find description
156 pfd() {
157 # package name and descriptions
158 apt-cache search "$@"
159 }
160 # package find file
161 pff() {
162 local s; [[ $EUID != 0 ]] && s=sudo
163 # nice aptitude search from emacs shell. package description width as
164 # wide as the screen, and package name field small aptitude
165 # manual can't figure out how wide emacs terminal is, of course
166 # it doesn't consult the $COLUMNS variable... and in a normal
167 # terminal, it makes the package name field ridiculously big
168 # also, remove that useless dash before the description
169 aptitude -F "%c%a%M %p %$((COLUMNS - 30))d" -w $COLUMNS search "$@"
170 }
171 pu() {
172 local s; [[ $EUID != 0 ]] && s=sudo
173 local needed=false
174 for arg; do
175 if dpkg -s -- "$arg" &>/dev/null; then
176 needed=true
177 break
178 fi
179 done
180 $needed || return 0
181 plock-wait
182 $s apt-get -y remove --purge --auto-remove "$@"
183 # seems slightly redundant, but it removes more stuff sometimes.
184 $s apt-get -y autoremove
185 }
186 pup() { # upgrade
187 plock-wait
188 pupdate
189 local s; [[ $EUID != 0 ]] && s=sudo
190 $s apt-get -y dist-upgrade --purge --auto-remove "$@"
191 $s apt-get -y autoremove
192 }
193 # package info
194 pl() {
195 if type -p aptitude &>/dev/null; then
196 aptitude show "$@"
197 else
198 apt-cache show "$@"
199 fi
200 }
201 pfile() {
202 # -a = search all repos
203 local -a arg all
204 all=false
205 case $1 in
206 -a)
207 all=true
208 shift
209 ;;
210 esac
211 local file=$1
212 # ucfq can tell us about config files which are not tracked
213 # with apt-file. but, for at least a few files I tested
214 # which are tracked with apt-file, ucfq doesn't show their
215 # package name. So, commenting this, waiting to find
216 # a config file only tracked by ucfq to see if it gives the
217 # package name and if I can identify this kind of file.
218 # if [[ $file == /* ]] && ! ucfq -w $file | grep ::: &>/dev/null; then
219 # ucfq $file
220
221 if [[ $file == /* ]]; then
222 dpkg -S "$file"
223 else
224 if ! $all; then
225 arg=(--filter-origins "$(positive-origins)")
226 fi
227 if [[ $file == /* ]]; then
228 apt-file "${arg[@]}" find -x /"$file"\$
229 update-alternatives --list "$file" 2>/dev/null
230 else
231 apt-file "${arg[@]}" find -x "$file"\$
232 fi
233 fi
234 }
235 pkgfiles() {
236 if dpkg -s "$1" &>/dev/null; then
237 dpkg-query -L $1
238 else
239 apt-file -x list "^$1$"
240 fi
241 }
242
243 elif command -v pacman &>/dev/null; then
244 p() {
245 pacaur "$@"
246 }
247 pi() {
248 pacaur -S --noconfirm --needed --noedit "$@"
249 }
250 pfd() {
251 pacaur -Ss "$@"
252 }
253 pu() {
254 pacaur -Rs --noconfirm "$@"
255 if p=$(pacaur -Qdtq); then
256 pacaur -Rs $p
257 fi
258 }
259 aurex() {
260 p="$1"
261 aur='https://aur.archlinux.org'
262 curl -s $aur/$(curl -s "$aur/rpc.php?type=info&arg=$p" \
263 | jq -r .results.URLPath) | tar xz
264 cd "$p"
265
266 }
267 pmirror() {
268 local s; [[ $EUID != 0 ]] && s=sudo
269 local x=$(mktemp)
270 curl -s "https://www.archlinux.org/mirrorlist/\
271 ?country=US&protocol=https&ip_version=4&ip_version=6&use_mirror_status=on" \
272 | sed -r 's/^[ #]*(Server *=)/\1/' > $x
273 if (( $(stat -c %s $x ) > 10 )); then
274 $s cp $x /etc/pacman.d/mirrorlist
275 rm $x
276 fi
277 }
278 pup() { # upgrade
279 local s; [[ $EUID != 0 ]] && s=sudo
280 # file_time + 24 hours > current_time
281 if ! (( $(stat -c%Y /etc/pacman.d/mirrorlist) + 60*60*24 > $(date +%s) ))
282 then
283 pmirror
284 fi
285 pacaur -Syu --noconfirm "$@"
286 }
287 # package info
288 pl() {
289 pacaur -Si "$@"
290 }
291 pfile() {
292 pkgfile "$1"
293 }
294 pkgfiles() {
295 if pacaur -Qs "^$1$" &>/dev/null; then
296 pacman -Ql $1
297 else
298 pkgfile -l $1
299 fi
300 }
301 fi