]> git.ipfire.org Git - pakfire.git/blame - src/pakfire/util.py
libpakfire: Allow direct parsing of XZ compressed SOLV files
[pakfire.git] / src / pakfire / util.py
CommitLineData
964aa579 1#!/usr/bin/python3
b792d887
MT
2###############################################################################
3# #
4# Pakfire - The IPFire package management system #
5# Copyright (C) 2011 Pakfire development team #
6# #
7# This program is free software: you can redistribute it and/or modify #
8# it under the terms of the GNU General Public License as published by #
9# the Free Software Foundation, either version 3 of the License, or #
10# (at your option) any later version. #
11# #
12# This program is distributed in the hope that it will be useful, #
13# but WITHOUT ANY WARRANTY; without even the implied warranty of #
14# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the #
15# GNU General Public License for more details. #
16# #
17# You should have received a copy of the GNU General Public License #
18# along with this program. If not, see <http://www.gnu.org/licenses/>. #
19# #
20###############################################################################
47a4cb89 21
102c5ee7 22import hashlib
47a4cb89
MT
23import os
24import random
47a4cb89 25import shutil
aa7f15fe 26import signal
47a4cb89 27import string
47a4cb89
MT
28import sys
29import time
30
8b6bc023
MT
31import logging
32log = logging.getLogger("pakfire")
33
964aa579
MT
34from .constants import *
35from .i18n import _
47a4cb89 36
3c0e282f 37# Import binary version of version_compare and capability functions
964aa579 38from ._pakfire import version_compare, get_capabilities, set_capabilities, personality
102c5ee7 39
e9c20259
MT
40def cli_is_interactive():
41 """
42 Say weather a shell is interactive or not.
43 """
44 if sys.stdin.isatty() and sys.stdout.isatty() and sys.stderr.isatty():
45 return True
46
47 return False
48
c0fd807c
MT
49def ask_user(question):
50 """
51 Ask the user the question, he or she can answer with yes or no.
52
53 This function returns True for "yes" and False for "no".
54
55 If the software is running in a non-inteactive shell, no question
56 is asked at all and the answer is always "yes".
57 """
58 if not cli_is_interactive():
59 return True
60
964aa579
MT
61 print(_("%s [y/N]") % question, end=' ')
62 ret = input()
63 print() # Just an empty line.
c0fd807c
MT
64
65 return ret in ("y", "Y", "z", "Z", "j", "J")
66
7c8f2953
MT
67def random_string(length=20):
68 s = ""
69
70 for i in range(length):
964aa579 71 s += random.choice(string.ascii_letters)
7c8f2953
MT
72
73 return s
74
38ba21f9 75def make_progress(message, maxval, eta=True, speed=False):
964aa579
MT
76 # XXX delay importing the progressbar module
77 # (because of a circular dependency)
10458aa0 78 from .ui import progressbar
964aa579 79
4496b160
MT
80 # Return nothing if stdout is not a terminal.
81 if not sys.stdout.isatty():
82 return
83
dccd65ea
MT
84 if not maxval:
85 maxval = 1
4496b160 86
dccd65ea
MT
87 pb = progressbar.ProgressBar(maxval)
88 pb.add("%-50s" % message)
38ba21f9 89
dccd65ea
MT
90 bar = progressbar.WidgetBar()
91 pb.add(bar)
1e80d5d7 92
dccd65ea
MT
93 if speed:
94 percentage = progressbar.WidgetPercentage()
95 pb.add(percentage)
4496b160 96
dccd65ea
MT
97 filetransfer = progressbar.WidgetFileTransferSpeed()
98 pb.add(filetransfer)
4496b160 99
dccd65ea
MT
100 if eta:
101 eta = progressbar.WidgetETA()
102 pb.add(eta)
103
104 return pb.start()
4496b160 105
47a4cb89
MT
106def rm(path, *args, **kargs):
107 """
108 version of shutil.rmtree that ignores no-such-file-or-directory errors,
109 and tries harder if it finds immutable files
110 """
111 tryAgain = 1
112 failedFilename = None
113 while tryAgain:
114 tryAgain = 0
115 try:
116 shutil.rmtree(path, *args, **kargs)
964aa579 117 except OSError as e:
47a4cb89
MT
118 if e.errno == 2: # no such file or directory
119 pass
120 elif e.errno==1 or e.errno==13:
121 tryAgain = 1
122 if failedFilename == e.filename:
123 raise
124 failedFilename = e.filename
125 os.system("chattr -R -i %s" % path)
126 else:
127 raise
0a9a2371 128
102c5ee7 129def calc_hash1(filename=None, data=None):
e3bcfd23 130 h = hashlib.new("sha1")
102c5ee7
MT
131
132 if filename:
133 f = open(filename)
134 buf = f.read(BUFFER_SIZE)
135 while buf:
136 h.update(buf)
137 buf = f.read(BUFFER_SIZE)
138
139 f.close()
140
141 elif data:
142 h.update(data)
143
144 return h.hexdigest()
145
146def text_wrap(s, length=65):
c07a3ca7
MT
147 if not s:
148 return ""
149
150 lines = []
151
152 words = []
153 for line in s.splitlines():
154 if not line:
155 words.append("")
156 else:
157 words += line.split()
158
159 line = []
160 while words:
161 word = words.pop(0)
162
163 # An empty words means a line break.
164 if not word:
165 if line:
166 lines.append(" ".join(line))
167 lines.append("")
168 line = []
169
170 else:
171 if len(" ".join(line)) + len(word) >= length:
172 lines.append(" ".join(line))
173 line = []
174 words.insert(0, word)
175 else:
176 line.append(word)
102c5ee7 177
c07a3ca7
MT
178 if line:
179 lines.append(" ".join(line))
102c5ee7 180
c07a3ca7 181 assert not words
102c5ee7 182
c07a3ca7
MT
183 #return "\n".join(lines)
184 return lines
aa7f15fe
MT
185
186def orphans_kill(root, killsig=signal.SIGTERM):
187 """
188 kill off anything that is still chrooted.
189 """
8b6bc023 190 log.debug(_("Killing orphans..."))
aa7f15fe 191
536438d9 192 killed = False
aa7f15fe
MT
193 for fn in [d for d in os.listdir("/proc") if d.isdigit()]:
194 try:
195 r = os.readlink("/proc/%s/root" % fn)
196 if os.path.realpath(root) == os.path.realpath(r):
8b6bc023 197 log.warning(_("Process ID %s is still running in chroot. Killing...") % fn)
536438d9 198 killed = True
aa7f15fe
MT
199
200 pid = int(fn, 10)
201 os.kill(pid, killsig)
202 os.waitpid(pid, 0)
964aa579 203 except OSError as e:
aa7f15fe 204 pass
c07a3ca7 205
536438d9
MT
206 # If something was killed, wait a couple of seconds to make sure all file descriptors
207 # are closed and we can proceed with umounting the filesystems.
208 if killed:
8b6bc023 209 log.warning(_("Waiting for processes to terminate..."))
536438d9
MT
210 time.sleep(3)
211
7814c06d
MT
212 # Calling ourself again to make sure all processes were killed.
213 orphans_kill(root, killsig=killsig)
214
c07a3ca7
MT
215def scriptlet_interpreter(scriptlet):
216 """
217 This function returns the interpreter of a scriptlet.
218 """
219 # XXX handle ELF?
220 interpreter = None
221
222 for line in scriptlet.splitlines():
223 if line.startswith("#!/"):
224 interpreter = line[2:]
225 interpreter = interpreter.split()[0]
226 break
227
228 return interpreter