1*635e0e46SAndrew Geissler#
2*635e0e46SAndrew Geissler# SPDX-License-Identifier: GPL-2.0-only
3*635e0e46SAndrew Geissler#
4*635e0e46SAndrew Geissler
5*635e0e46SAndrew Geisslerimport re
6*635e0e46SAndrew Geisslerimport shutil
7*635e0e46SAndrew Geisslerimport subprocess
8*635e0e46SAndrew Geisslerfrom oe.package_manager import *
9*635e0e46SAndrew Geissler
10*635e0e46SAndrew Geisslerclass OpkgIndexer(Indexer):
11*635e0e46SAndrew Geissler    def write_index(self):
12*635e0e46SAndrew Geissler        arch_vars = ["ALL_MULTILIB_PACKAGE_ARCHS",
13*635e0e46SAndrew Geissler                     "SDK_PACKAGE_ARCHS",
14*635e0e46SAndrew Geissler                     ]
15*635e0e46SAndrew Geissler
16*635e0e46SAndrew Geissler        opkg_index_cmd = bb.utils.which(os.getenv('PATH'), "opkg-make-index")
17*635e0e46SAndrew Geissler        if self.d.getVar('PACKAGE_FEED_SIGN') == '1':
18*635e0e46SAndrew Geissler            signer = get_signer(self.d, self.d.getVar('PACKAGE_FEED_GPG_BACKEND'))
19*635e0e46SAndrew Geissler        else:
20*635e0e46SAndrew Geissler            signer = None
21*635e0e46SAndrew Geissler
22*635e0e46SAndrew Geissler        if not os.path.exists(os.path.join(self.deploy_dir, "Packages")):
23*635e0e46SAndrew Geissler            open(os.path.join(self.deploy_dir, "Packages"), "w").close()
24*635e0e46SAndrew Geissler
25*635e0e46SAndrew Geissler        index_cmds = set()
26*635e0e46SAndrew Geissler        index_sign_files = set()
27*635e0e46SAndrew Geissler        for arch_var in arch_vars:
28*635e0e46SAndrew Geissler            archs = self.d.getVar(arch_var)
29*635e0e46SAndrew Geissler            if archs is None:
30*635e0e46SAndrew Geissler                continue
31*635e0e46SAndrew Geissler
32*635e0e46SAndrew Geissler            for arch in archs.split():
33*635e0e46SAndrew Geissler                pkgs_dir = os.path.join(self.deploy_dir, arch)
34*635e0e46SAndrew Geissler                pkgs_file = os.path.join(pkgs_dir, "Packages")
35*635e0e46SAndrew Geissler
36*635e0e46SAndrew Geissler                if not os.path.isdir(pkgs_dir):
37*635e0e46SAndrew Geissler                    continue
38*635e0e46SAndrew Geissler
39*635e0e46SAndrew Geissler                if not os.path.exists(pkgs_file):
40*635e0e46SAndrew Geissler                    open(pkgs_file, "w").close()
41*635e0e46SAndrew Geissler
42*635e0e46SAndrew Geissler                index_cmds.add('%s --checksum md5 --checksum sha256 -r %s -p %s -m %s' %
43*635e0e46SAndrew Geissler                                  (opkg_index_cmd, pkgs_file, pkgs_file, pkgs_dir))
44*635e0e46SAndrew Geissler
45*635e0e46SAndrew Geissler                index_sign_files.add(pkgs_file)
46*635e0e46SAndrew Geissler
47*635e0e46SAndrew Geissler        if len(index_cmds) == 0:
48*635e0e46SAndrew Geissler            bb.note("There are no packages in %s!" % self.deploy_dir)
49*635e0e46SAndrew Geissler            return
50*635e0e46SAndrew Geissler
51*635e0e46SAndrew Geissler        oe.utils.multiprocess_launch(create_index, index_cmds, self.d)
52*635e0e46SAndrew Geissler
53*635e0e46SAndrew Geissler        if signer:
54*635e0e46SAndrew Geissler            feed_sig_type = self.d.getVar('PACKAGE_FEED_GPG_SIGNATURE_TYPE')
55*635e0e46SAndrew Geissler            is_ascii_sig = (feed_sig_type.upper() != "BIN")
56*635e0e46SAndrew Geissler            for f in index_sign_files:
57*635e0e46SAndrew Geissler                signer.detach_sign(f,
58*635e0e46SAndrew Geissler                                   self.d.getVar('PACKAGE_FEED_GPG_NAME'),
59*635e0e46SAndrew Geissler                                   self.d.getVar('PACKAGE_FEED_GPG_PASSPHRASE_FILE'),
60*635e0e46SAndrew Geissler                                   armor=is_ascii_sig)
61*635e0e46SAndrew Geissler
62*635e0e46SAndrew Geisslerclass OpkgPkgsList(PkgsList):
63*635e0e46SAndrew Geissler    def __init__(self, d, rootfs_dir, config_file):
64*635e0e46SAndrew Geissler        super(OpkgPkgsList, self).__init__(d, rootfs_dir)
65*635e0e46SAndrew Geissler
66*635e0e46SAndrew Geissler        self.opkg_cmd = bb.utils.which(os.getenv('PATH'), "opkg")
67*635e0e46SAndrew Geissler        self.opkg_args = "-f %s -o %s " % (config_file, rootfs_dir)
68*635e0e46SAndrew Geissler        self.opkg_args += self.d.getVar("OPKG_ARGS")
69*635e0e46SAndrew Geissler
70*635e0e46SAndrew Geissler    def list_pkgs(self, format=None):
71*635e0e46SAndrew Geissler        cmd = "%s %s status" % (self.opkg_cmd, self.opkg_args)
72*635e0e46SAndrew Geissler
73*635e0e46SAndrew Geissler        # opkg returns success even when it printed some
74*635e0e46SAndrew Geissler        # "Collected errors:" report to stderr. Mixing stderr into
75*635e0e46SAndrew Geissler        # stdout then leads to random failures later on when
76*635e0e46SAndrew Geissler        # parsing the output. To avoid this we need to collect both
77*635e0e46SAndrew Geissler        # output streams separately and check for empty stderr.
78*635e0e46SAndrew Geissler        p = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE, shell=True)
79*635e0e46SAndrew Geissler        cmd_output, cmd_stderr = p.communicate()
80*635e0e46SAndrew Geissler        cmd_output = cmd_output.decode("utf-8")
81*635e0e46SAndrew Geissler        cmd_stderr = cmd_stderr.decode("utf-8")
82*635e0e46SAndrew Geissler        if p.returncode or cmd_stderr:
83*635e0e46SAndrew Geissler            bb.fatal("Cannot get the installed packages list. Command '%s' "
84*635e0e46SAndrew Geissler                     "returned %d and stderr:\n%s" % (cmd, p.returncode, cmd_stderr))
85*635e0e46SAndrew Geissler
86*635e0e46SAndrew Geissler        return opkg_query(cmd_output)
87*635e0e46SAndrew Geissler
88*635e0e46SAndrew Geissler
89*635e0e46SAndrew Geissler
90*635e0e46SAndrew Geisslerclass OpkgDpkgPM(PackageManager):
91*635e0e46SAndrew Geissler    def __init__(self, d, target_rootfs):
92*635e0e46SAndrew Geissler        """
93*635e0e46SAndrew Geissler        This is an abstract class. Do not instantiate this directly.
94*635e0e46SAndrew Geissler        """
95*635e0e46SAndrew Geissler        super(OpkgDpkgPM, self).__init__(d, target_rootfs)
96*635e0e46SAndrew Geissler
97*635e0e46SAndrew Geissler    def package_info(self, pkg, cmd):
98*635e0e46SAndrew Geissler        """
99*635e0e46SAndrew Geissler        Returns a dictionary with the package info.
100*635e0e46SAndrew Geissler
101*635e0e46SAndrew Geissler        This method extracts the common parts for Opkg and Dpkg
102*635e0e46SAndrew Geissler        """
103*635e0e46SAndrew Geissler
104*635e0e46SAndrew Geissler        try:
105*635e0e46SAndrew Geissler            output = subprocess.check_output(cmd, stderr=subprocess.STDOUT, shell=True).decode("utf-8")
106*635e0e46SAndrew Geissler        except subprocess.CalledProcessError as e:
107*635e0e46SAndrew Geissler            bb.fatal("Unable to list available packages. Command '%s' "
108*635e0e46SAndrew Geissler                     "returned %d:\n%s" % (cmd, e.returncode, e.output.decode("utf-8")))
109*635e0e46SAndrew Geissler        return opkg_query(output)
110*635e0e46SAndrew Geissler
111*635e0e46SAndrew Geissler    def extract(self, pkg, pkg_info):
112*635e0e46SAndrew Geissler        """
113*635e0e46SAndrew Geissler        Returns the path to a tmpdir where resides the contents of a package.
114*635e0e46SAndrew Geissler
115*635e0e46SAndrew Geissler        Deleting the tmpdir is responsability of the caller.
116*635e0e46SAndrew Geissler
117*635e0e46SAndrew Geissler        This method extracts the common parts for Opkg and Dpkg
118*635e0e46SAndrew Geissler        """
119*635e0e46SAndrew Geissler
120*635e0e46SAndrew Geissler        ar_cmd = bb.utils.which(os.getenv("PATH"), "ar")
121*635e0e46SAndrew Geissler        tar_cmd = bb.utils.which(os.getenv("PATH"), "tar")
122*635e0e46SAndrew Geissler        pkg_path = pkg_info[pkg]["filepath"]
123*635e0e46SAndrew Geissler
124*635e0e46SAndrew Geissler        if not os.path.isfile(pkg_path):
125*635e0e46SAndrew Geissler            bb.fatal("Unable to extract package for '%s'."
126*635e0e46SAndrew Geissler                     "File %s doesn't exists" % (pkg, pkg_path))
127*635e0e46SAndrew Geissler
128*635e0e46SAndrew Geissler        tmp_dir = tempfile.mkdtemp()
129*635e0e46SAndrew Geissler        current_dir = os.getcwd()
130*635e0e46SAndrew Geissler        os.chdir(tmp_dir)
131*635e0e46SAndrew Geissler        data_tar = 'data.tar.xz'
132*635e0e46SAndrew Geissler
133*635e0e46SAndrew Geissler        try:
134*635e0e46SAndrew Geissler            cmd = [ar_cmd, 'x', pkg_path]
135*635e0e46SAndrew Geissler            output = subprocess.check_output(cmd, stderr=subprocess.STDOUT)
136*635e0e46SAndrew Geissler            cmd = [tar_cmd, 'xf', data_tar]
137*635e0e46SAndrew Geissler            output = subprocess.check_output(cmd, stderr=subprocess.STDOUT)
138*635e0e46SAndrew Geissler        except subprocess.CalledProcessError as e:
139*635e0e46SAndrew Geissler            bb.utils.remove(tmp_dir, recurse=True)
140*635e0e46SAndrew Geissler            bb.fatal("Unable to extract %s package. Command '%s' "
141*635e0e46SAndrew Geissler                     "returned %d:\n%s" % (pkg_path, ' '.join(cmd), e.returncode, e.output.decode("utf-8")))
142*635e0e46SAndrew Geissler        except OSError as e:
143*635e0e46SAndrew Geissler            bb.utils.remove(tmp_dir, recurse=True)
144*635e0e46SAndrew Geissler            bb.fatal("Unable to extract %s package. Command '%s' "
145*635e0e46SAndrew Geissler                     "returned %d:\n%s at %s" % (pkg_path, ' '.join(cmd), e.errno, e.strerror, e.filename))
146*635e0e46SAndrew Geissler
147*635e0e46SAndrew Geissler        bb.note("Extracted %s to %s" % (pkg_path, tmp_dir))
148*635e0e46SAndrew Geissler        bb.utils.remove(os.path.join(tmp_dir, "debian-binary"))
149*635e0e46SAndrew Geissler        bb.utils.remove(os.path.join(tmp_dir, "control.tar.gz"))
150*635e0e46SAndrew Geissler        os.chdir(current_dir)
151*635e0e46SAndrew Geissler
152*635e0e46SAndrew Geissler        return tmp_dir
153*635e0e46SAndrew Geissler
154*635e0e46SAndrew Geissler    def _handle_intercept_failure(self, registered_pkgs):
155*635e0e46SAndrew Geissler        self.mark_packages("unpacked", registered_pkgs.split())
156*635e0e46SAndrew Geissler
157*635e0e46SAndrew Geisslerclass OpkgPM(OpkgDpkgPM):
158*635e0e46SAndrew Geissler    def __init__(self, d, target_rootfs, config_file, archs, task_name='target', ipk_repo_workdir="oe-rootfs-repo", filterbydependencies=True, prepare_index=True):
159*635e0e46SAndrew Geissler        super(OpkgPM, self).__init__(d, target_rootfs)
160*635e0e46SAndrew Geissler
161*635e0e46SAndrew Geissler        self.config_file = config_file
162*635e0e46SAndrew Geissler        self.pkg_archs = archs
163*635e0e46SAndrew Geissler        self.task_name = task_name
164*635e0e46SAndrew Geissler
165*635e0e46SAndrew Geissler        self.deploy_dir = oe.path.join(self.d.getVar('WORKDIR'), ipk_repo_workdir)
166*635e0e46SAndrew Geissler        self.deploy_lock_file = os.path.join(self.deploy_dir, "deploy.lock")
167*635e0e46SAndrew Geissler        self.opkg_cmd = bb.utils.which(os.getenv('PATH'), "opkg")
168*635e0e46SAndrew Geissler        self.opkg_args = "--volatile-cache -f %s -t %s -o %s " % (self.config_file, self.d.expand('${T}/ipktemp/') ,target_rootfs)
169*635e0e46SAndrew Geissler        self.opkg_args += self.d.getVar("OPKG_ARGS")
170*635e0e46SAndrew Geissler
171*635e0e46SAndrew Geissler        if prepare_index:
172*635e0e46SAndrew Geissler            create_packages_dir(self.d, self.deploy_dir, d.getVar("DEPLOY_DIR_IPK"), "package_write_ipk", filterbydependencies)
173*635e0e46SAndrew Geissler
174*635e0e46SAndrew Geissler        opkg_lib_dir = self.d.getVar('OPKGLIBDIR')
175*635e0e46SAndrew Geissler        if opkg_lib_dir[0] == "/":
176*635e0e46SAndrew Geissler            opkg_lib_dir = opkg_lib_dir[1:]
177*635e0e46SAndrew Geissler
178*635e0e46SAndrew Geissler        self.opkg_dir = os.path.join(target_rootfs, opkg_lib_dir, "opkg")
179*635e0e46SAndrew Geissler
180*635e0e46SAndrew Geissler        bb.utils.mkdirhier(self.opkg_dir)
181*635e0e46SAndrew Geissler
182*635e0e46SAndrew Geissler        self.saved_opkg_dir = self.d.expand('${T}/saved/%s' % self.task_name)
183*635e0e46SAndrew Geissler        if not os.path.exists(self.d.expand('${T}/saved')):
184*635e0e46SAndrew Geissler            bb.utils.mkdirhier(self.d.expand('${T}/saved'))
185*635e0e46SAndrew Geissler
186*635e0e46SAndrew Geissler        self.from_feeds = (self.d.getVar('BUILD_IMAGES_FROM_FEEDS') or "") == "1"
187*635e0e46SAndrew Geissler        if self.from_feeds:
188*635e0e46SAndrew Geissler            self._create_custom_config()
189*635e0e46SAndrew Geissler        else:
190*635e0e46SAndrew Geissler            self._create_config()
191*635e0e46SAndrew Geissler
192*635e0e46SAndrew Geissler        self.indexer = OpkgIndexer(self.d, self.deploy_dir)
193*635e0e46SAndrew Geissler
194*635e0e46SAndrew Geissler    def mark_packages(self, status_tag, packages=None):
195*635e0e46SAndrew Geissler        """
196*635e0e46SAndrew Geissler        This function will change a package's status in /var/lib/opkg/status file.
197*635e0e46SAndrew Geissler        If 'packages' is None then the new_status will be applied to all
198*635e0e46SAndrew Geissler        packages
199*635e0e46SAndrew Geissler        """
200*635e0e46SAndrew Geissler        status_file = os.path.join(self.opkg_dir, "status")
201*635e0e46SAndrew Geissler
202*635e0e46SAndrew Geissler        with open(status_file, "r") as sf:
203*635e0e46SAndrew Geissler            with open(status_file + ".tmp", "w+") as tmp_sf:
204*635e0e46SAndrew Geissler                if packages is None:
205*635e0e46SAndrew Geissler                    tmp_sf.write(re.sub(r"Package: (.*?)\n((?:[^\n]+\n)*?)Status: (.*)(?:unpacked|installed)",
206*635e0e46SAndrew Geissler                                        r"Package: \1\n\2Status: \3%s" % status_tag,
207*635e0e46SAndrew Geissler                                        sf.read()))
208*635e0e46SAndrew Geissler                else:
209*635e0e46SAndrew Geissler                    if type(packages).__name__ != "list":
210*635e0e46SAndrew Geissler                        raise TypeError("'packages' should be a list object")
211*635e0e46SAndrew Geissler
212*635e0e46SAndrew Geissler                    status = sf.read()
213*635e0e46SAndrew Geissler                    for pkg in packages:
214*635e0e46SAndrew Geissler                        status = re.sub(r"Package: %s\n((?:[^\n]+\n)*?)Status: (.*)(?:unpacked|installed)" % pkg,
215*635e0e46SAndrew Geissler                                        r"Package: %s\n\1Status: \2%s" % (pkg, status_tag),
216*635e0e46SAndrew Geissler                                        status)
217*635e0e46SAndrew Geissler
218*635e0e46SAndrew Geissler                    tmp_sf.write(status)
219*635e0e46SAndrew Geissler
220*635e0e46SAndrew Geissler        os.rename(status_file + ".tmp", status_file)
221*635e0e46SAndrew Geissler
222*635e0e46SAndrew Geissler    def _create_custom_config(self):
223*635e0e46SAndrew Geissler        bb.note("Building from feeds activated!")
224*635e0e46SAndrew Geissler
225*635e0e46SAndrew Geissler        with open(self.config_file, "w+") as config_file:
226*635e0e46SAndrew Geissler            priority = 1
227*635e0e46SAndrew Geissler            for arch in self.pkg_archs.split():
228*635e0e46SAndrew Geissler                config_file.write("arch %s %d\n" % (arch, priority))
229*635e0e46SAndrew Geissler                priority += 5
230*635e0e46SAndrew Geissler
231*635e0e46SAndrew Geissler            for line in (self.d.getVar('IPK_FEED_URIS') or "").split():
232*635e0e46SAndrew Geissler                feed_match = re.match(r"^[ \t]*(.*)##([^ \t]*)[ \t]*$", line)
233*635e0e46SAndrew Geissler
234*635e0e46SAndrew Geissler                if feed_match is not None:
235*635e0e46SAndrew Geissler                    feed_name = feed_match.group(1)
236*635e0e46SAndrew Geissler                    feed_uri = feed_match.group(2)
237*635e0e46SAndrew Geissler
238*635e0e46SAndrew Geissler                    bb.note("Add %s feed with URL %s" % (feed_name, feed_uri))
239*635e0e46SAndrew Geissler
240*635e0e46SAndrew Geissler                    config_file.write("src/gz %s %s\n" % (feed_name, feed_uri))
241*635e0e46SAndrew Geissler
242*635e0e46SAndrew Geissler            """
243*635e0e46SAndrew Geissler            Allow to use package deploy directory contents as quick devel-testing
244*635e0e46SAndrew Geissler            feed. This creates individual feed configs for each arch subdir of those
245*635e0e46SAndrew Geissler            specified as compatible for the current machine.
246*635e0e46SAndrew Geissler            NOTE: Development-helper feature, NOT a full-fledged feed.
247*635e0e46SAndrew Geissler            """
248*635e0e46SAndrew Geissler            if (self.d.getVar('FEED_DEPLOYDIR_BASE_URI') or "") != "":
249*635e0e46SAndrew Geissler                for arch in self.pkg_archs.split():
250*635e0e46SAndrew Geissler                    cfg_file_name = os.path.join(self.target_rootfs,
251*635e0e46SAndrew Geissler                                                 self.d.getVar("sysconfdir"),
252*635e0e46SAndrew Geissler                                                 "opkg",
253*635e0e46SAndrew Geissler                                                 "local-%s-feed.conf" % arch)
254*635e0e46SAndrew Geissler
255*635e0e46SAndrew Geissler                    with open(cfg_file_name, "w+") as cfg_file:
256*635e0e46SAndrew Geissler                        cfg_file.write("src/gz local-%s %s/%s" %
257*635e0e46SAndrew Geissler                                       (arch,
258*635e0e46SAndrew Geissler                                        self.d.getVar('FEED_DEPLOYDIR_BASE_URI'),
259*635e0e46SAndrew Geissler                                        arch))
260*635e0e46SAndrew Geissler
261*635e0e46SAndrew Geissler                        if self.d.getVar('OPKGLIBDIR') != '/var/lib':
262*635e0e46SAndrew Geissler                            # There is no command line option for this anymore, we need to add
263*635e0e46SAndrew Geissler                            # info_dir and status_file to config file, if OPKGLIBDIR doesn't have
264*635e0e46SAndrew Geissler                            # the default value of "/var/lib" as defined in opkg:
265*635e0e46SAndrew Geissler                            # libopkg/opkg_conf.h:#define OPKG_CONF_DEFAULT_LISTS_DIR     VARDIR "/lib/opkg/lists"
266*635e0e46SAndrew Geissler                            # libopkg/opkg_conf.h:#define OPKG_CONF_DEFAULT_INFO_DIR      VARDIR "/lib/opkg/info"
267*635e0e46SAndrew Geissler                            # libopkg/opkg_conf.h:#define OPKG_CONF_DEFAULT_STATUS_FILE   VARDIR "/lib/opkg/status"
268*635e0e46SAndrew Geissler                            cfg_file.write("option info_dir     %s\n" % os.path.join(self.d.getVar('OPKGLIBDIR'), 'opkg', 'info'))
269*635e0e46SAndrew Geissler                            cfg_file.write("option lists_dir    %s\n" % os.path.join(self.d.getVar('OPKGLIBDIR'), 'opkg', 'lists'))
270*635e0e46SAndrew Geissler                            cfg_file.write("option status_file  %s\n" % os.path.join(self.d.getVar('OPKGLIBDIR'), 'opkg', 'status'))
271*635e0e46SAndrew Geissler
272*635e0e46SAndrew Geissler
273*635e0e46SAndrew Geissler    def _create_config(self):
274*635e0e46SAndrew Geissler        with open(self.config_file, "w+") as config_file:
275*635e0e46SAndrew Geissler            priority = 1
276*635e0e46SAndrew Geissler            for arch in self.pkg_archs.split():
277*635e0e46SAndrew Geissler                config_file.write("arch %s %d\n" % (arch, priority))
278*635e0e46SAndrew Geissler                priority += 5
279*635e0e46SAndrew Geissler
280*635e0e46SAndrew Geissler            config_file.write("src oe file:%s\n" % self.deploy_dir)
281*635e0e46SAndrew Geissler
282*635e0e46SAndrew Geissler            for arch in self.pkg_archs.split():
283*635e0e46SAndrew Geissler                pkgs_dir = os.path.join(self.deploy_dir, arch)
284*635e0e46SAndrew Geissler                if os.path.isdir(pkgs_dir):
285*635e0e46SAndrew Geissler                    config_file.write("src oe-%s file:%s\n" %
286*635e0e46SAndrew Geissler                                      (arch, pkgs_dir))
287*635e0e46SAndrew Geissler
288*635e0e46SAndrew Geissler            if self.d.getVar('OPKGLIBDIR') != '/var/lib':
289*635e0e46SAndrew Geissler                # There is no command line option for this anymore, we need to add
290*635e0e46SAndrew Geissler                # info_dir and status_file to config file, if OPKGLIBDIR doesn't have
291*635e0e46SAndrew Geissler                # the default value of "/var/lib" as defined in opkg:
292*635e0e46SAndrew Geissler                # libopkg/opkg_conf.h:#define OPKG_CONF_DEFAULT_LISTS_DIR     VARDIR "/lib/opkg/lists"
293*635e0e46SAndrew Geissler                # libopkg/opkg_conf.h:#define OPKG_CONF_DEFAULT_INFO_DIR      VARDIR "/lib/opkg/info"
294*635e0e46SAndrew Geissler                # libopkg/opkg_conf.h:#define OPKG_CONF_DEFAULT_STATUS_FILE   VARDIR "/lib/opkg/status"
295*635e0e46SAndrew Geissler                config_file.write("option info_dir     %s\n" % os.path.join(self.d.getVar('OPKGLIBDIR'), 'opkg', 'info'))
296*635e0e46SAndrew Geissler                config_file.write("option lists_dir    %s\n" % os.path.join(self.d.getVar('OPKGLIBDIR'), 'opkg', 'lists'))
297*635e0e46SAndrew Geissler                config_file.write("option status_file  %s\n" % os.path.join(self.d.getVar('OPKGLIBDIR'), 'opkg', 'status'))
298*635e0e46SAndrew Geissler
299*635e0e46SAndrew Geissler    def insert_feeds_uris(self, feed_uris, feed_base_paths, feed_archs):
300*635e0e46SAndrew Geissler        if feed_uris == "":
301*635e0e46SAndrew Geissler            return
302*635e0e46SAndrew Geissler
303*635e0e46SAndrew Geissler        rootfs_config = os.path.join('%s/etc/opkg/base-feeds.conf'
304*635e0e46SAndrew Geissler                                  % self.target_rootfs)
305*635e0e46SAndrew Geissler
306*635e0e46SAndrew Geissler        os.makedirs('%s/etc/opkg' % self.target_rootfs, exist_ok=True)
307*635e0e46SAndrew Geissler
308*635e0e46SAndrew Geissler        feed_uris = self.construct_uris(feed_uris.split(), feed_base_paths.split())
309*635e0e46SAndrew Geissler        archs = self.pkg_archs.split() if feed_archs is None else feed_archs.split()
310*635e0e46SAndrew Geissler
311*635e0e46SAndrew Geissler        with open(rootfs_config, "w+") as config_file:
312*635e0e46SAndrew Geissler            uri_iterator = 0
313*635e0e46SAndrew Geissler            for uri in feed_uris:
314*635e0e46SAndrew Geissler                if archs:
315*635e0e46SAndrew Geissler                    for arch in archs:
316*635e0e46SAndrew Geissler                        if (feed_archs is None) and (not os.path.exists(oe.path.join(self.deploy_dir, arch))):
317*635e0e46SAndrew Geissler                            continue
318*635e0e46SAndrew Geissler                        bb.note('Adding opkg feed url-%s-%d (%s)' %
319*635e0e46SAndrew Geissler                            (arch, uri_iterator, uri))
320*635e0e46SAndrew Geissler                        config_file.write("src/gz uri-%s-%d %s/%s\n" %
321*635e0e46SAndrew Geissler                                          (arch, uri_iterator, uri, arch))
322*635e0e46SAndrew Geissler                else:
323*635e0e46SAndrew Geissler                    bb.note('Adding opkg feed url-%d (%s)' %
324*635e0e46SAndrew Geissler                        (uri_iterator, uri))
325*635e0e46SAndrew Geissler                    config_file.write("src/gz uri-%d %s\n" %
326*635e0e46SAndrew Geissler                                      (uri_iterator, uri))
327*635e0e46SAndrew Geissler
328*635e0e46SAndrew Geissler                uri_iterator += 1
329*635e0e46SAndrew Geissler
330*635e0e46SAndrew Geissler    def update(self):
331*635e0e46SAndrew Geissler        self.deploy_dir_lock()
332*635e0e46SAndrew Geissler
333*635e0e46SAndrew Geissler        cmd = "%s %s update" % (self.opkg_cmd, self.opkg_args)
334*635e0e46SAndrew Geissler
335*635e0e46SAndrew Geissler        try:
336*635e0e46SAndrew Geissler            subprocess.check_output(cmd.split(), stderr=subprocess.STDOUT)
337*635e0e46SAndrew Geissler        except subprocess.CalledProcessError as e:
338*635e0e46SAndrew Geissler            self.deploy_dir_unlock()
339*635e0e46SAndrew Geissler            bb.fatal("Unable to update the package index files. Command '%s' "
340*635e0e46SAndrew Geissler                     "returned %d:\n%s" % (cmd, e.returncode, e.output.decode("utf-8")))
341*635e0e46SAndrew Geissler
342*635e0e46SAndrew Geissler        self.deploy_dir_unlock()
343*635e0e46SAndrew Geissler
344*635e0e46SAndrew Geissler    def install(self, pkgs, attempt_only=False):
345*635e0e46SAndrew Geissler        if not pkgs:
346*635e0e46SAndrew Geissler            return
347*635e0e46SAndrew Geissler
348*635e0e46SAndrew Geissler        cmd = "%s %s" % (self.opkg_cmd, self.opkg_args)
349*635e0e46SAndrew Geissler        for exclude in (self.d.getVar("PACKAGE_EXCLUDE") or "").split():
350*635e0e46SAndrew Geissler            cmd += " --add-exclude %s" % exclude
351*635e0e46SAndrew Geissler        for bad_recommendation in (self.d.getVar("BAD_RECOMMENDATIONS") or "").split():
352*635e0e46SAndrew Geissler            cmd += " --add-ignore-recommends %s" % bad_recommendation
353*635e0e46SAndrew Geissler        cmd += " install "
354*635e0e46SAndrew Geissler        cmd += " ".join(pkgs)
355*635e0e46SAndrew Geissler
356*635e0e46SAndrew Geissler        os.environ['D'] = self.target_rootfs
357*635e0e46SAndrew Geissler        os.environ['OFFLINE_ROOT'] = self.target_rootfs
358*635e0e46SAndrew Geissler        os.environ['IPKG_OFFLINE_ROOT'] = self.target_rootfs
359*635e0e46SAndrew Geissler        os.environ['OPKG_OFFLINE_ROOT'] = self.target_rootfs
360*635e0e46SAndrew Geissler        os.environ['INTERCEPT_DIR'] = self.intercepts_dir
361*635e0e46SAndrew Geissler        os.environ['NATIVE_ROOT'] = self.d.getVar('STAGING_DIR_NATIVE')
362*635e0e46SAndrew Geissler
363*635e0e46SAndrew Geissler        try:
364*635e0e46SAndrew Geissler            bb.note("Installing the following packages: %s" % ' '.join(pkgs))
365*635e0e46SAndrew Geissler            bb.note(cmd)
366*635e0e46SAndrew Geissler            output = subprocess.check_output(cmd.split(), stderr=subprocess.STDOUT).decode("utf-8")
367*635e0e46SAndrew Geissler            bb.note(output)
368*635e0e46SAndrew Geissler            failed_pkgs = []
369*635e0e46SAndrew Geissler            for line in output.split('\n'):
370*635e0e46SAndrew Geissler                if line.endswith("configuration required on target."):
371*635e0e46SAndrew Geissler                    bb.warn(line)
372*635e0e46SAndrew Geissler                    failed_pkgs.append(line.split(".")[0])
373*635e0e46SAndrew Geissler            if failed_pkgs:
374*635e0e46SAndrew Geissler                failed_postinsts_abort(failed_pkgs, self.d.expand("${T}/log.do_${BB_CURRENTTASK}"))
375*635e0e46SAndrew Geissler        except subprocess.CalledProcessError as e:
376*635e0e46SAndrew Geissler            (bb.fatal, bb.warn)[attempt_only]("Unable to install packages. "
377*635e0e46SAndrew Geissler                                              "Command '%s' returned %d:\n%s" %
378*635e0e46SAndrew Geissler                                              (cmd, e.returncode, e.output.decode("utf-8")))
379*635e0e46SAndrew Geissler
380*635e0e46SAndrew Geissler    def remove(self, pkgs, with_dependencies=True):
381*635e0e46SAndrew Geissler        if not pkgs:
382*635e0e46SAndrew Geissler            return
383*635e0e46SAndrew Geissler
384*635e0e46SAndrew Geissler        if with_dependencies:
385*635e0e46SAndrew Geissler            cmd = "%s %s --force-remove --force-removal-of-dependent-packages remove %s" % \
386*635e0e46SAndrew Geissler                (self.opkg_cmd, self.opkg_args, ' '.join(pkgs))
387*635e0e46SAndrew Geissler        else:
388*635e0e46SAndrew Geissler            cmd = "%s %s --force-depends remove %s" % \
389*635e0e46SAndrew Geissler                (self.opkg_cmd, self.opkg_args, ' '.join(pkgs))
390*635e0e46SAndrew Geissler
391*635e0e46SAndrew Geissler        try:
392*635e0e46SAndrew Geissler            bb.note(cmd)
393*635e0e46SAndrew Geissler            output = subprocess.check_output(cmd.split(), stderr=subprocess.STDOUT).decode("utf-8")
394*635e0e46SAndrew Geissler            bb.note(output)
395*635e0e46SAndrew Geissler        except subprocess.CalledProcessError as e:
396*635e0e46SAndrew Geissler            bb.fatal("Unable to remove packages. Command '%s' "
397*635e0e46SAndrew Geissler                     "returned %d:\n%s" % (e.cmd, e.returncode, e.output.decode("utf-8")))
398*635e0e46SAndrew Geissler
399*635e0e46SAndrew Geissler    def write_index(self):
400*635e0e46SAndrew Geissler        self.deploy_dir_lock()
401*635e0e46SAndrew Geissler
402*635e0e46SAndrew Geissler        result = self.indexer.write_index()
403*635e0e46SAndrew Geissler
404*635e0e46SAndrew Geissler        self.deploy_dir_unlock()
405*635e0e46SAndrew Geissler
406*635e0e46SAndrew Geissler        if result is not None:
407*635e0e46SAndrew Geissler            bb.fatal(result)
408*635e0e46SAndrew Geissler
409*635e0e46SAndrew Geissler    def remove_packaging_data(self):
410*635e0e46SAndrew Geissler        bb.utils.remove(self.opkg_dir, True)
411*635e0e46SAndrew Geissler        # create the directory back, it's needed by PM lock
412*635e0e46SAndrew Geissler        bb.utils.mkdirhier(self.opkg_dir)
413*635e0e46SAndrew Geissler
414*635e0e46SAndrew Geissler    def remove_lists(self):
415*635e0e46SAndrew Geissler        if not self.from_feeds:
416*635e0e46SAndrew Geissler            bb.utils.remove(os.path.join(self.opkg_dir, "lists"), True)
417*635e0e46SAndrew Geissler
418*635e0e46SAndrew Geissler    def list_installed(self):
419*635e0e46SAndrew Geissler        return OpkgPkgsList(self.d, self.target_rootfs, self.config_file).list_pkgs()
420*635e0e46SAndrew Geissler
421*635e0e46SAndrew Geissler    def dummy_install(self, pkgs):
422*635e0e46SAndrew Geissler        """
423*635e0e46SAndrew Geissler        The following function dummy installs pkgs and returns the log of output.
424*635e0e46SAndrew Geissler        """
425*635e0e46SAndrew Geissler        if len(pkgs) == 0:
426*635e0e46SAndrew Geissler            return
427*635e0e46SAndrew Geissler
428*635e0e46SAndrew Geissler        # Create an temp dir as opkg root for dummy installation
429*635e0e46SAndrew Geissler        temp_rootfs = self.d.expand('${T}/opkg')
430*635e0e46SAndrew Geissler        opkg_lib_dir = self.d.getVar('OPKGLIBDIR')
431*635e0e46SAndrew Geissler        if opkg_lib_dir[0] == "/":
432*635e0e46SAndrew Geissler            opkg_lib_dir = opkg_lib_dir[1:]
433*635e0e46SAndrew Geissler        temp_opkg_dir = os.path.join(temp_rootfs, opkg_lib_dir, 'opkg')
434*635e0e46SAndrew Geissler        bb.utils.mkdirhier(temp_opkg_dir)
435*635e0e46SAndrew Geissler
436*635e0e46SAndrew Geissler        opkg_args = "-f %s -o %s " % (self.config_file, temp_rootfs)
437*635e0e46SAndrew Geissler        opkg_args += self.d.getVar("OPKG_ARGS")
438*635e0e46SAndrew Geissler
439*635e0e46SAndrew Geissler        cmd = "%s %s update" % (self.opkg_cmd, opkg_args)
440*635e0e46SAndrew Geissler        try:
441*635e0e46SAndrew Geissler            subprocess.check_output(cmd, stderr=subprocess.STDOUT, shell=True)
442*635e0e46SAndrew Geissler        except subprocess.CalledProcessError as e:
443*635e0e46SAndrew Geissler            bb.fatal("Unable to update. Command '%s' "
444*635e0e46SAndrew Geissler                     "returned %d:\n%s" % (cmd, e.returncode, e.output.decode("utf-8")))
445*635e0e46SAndrew Geissler
446*635e0e46SAndrew Geissler        # Dummy installation
447*635e0e46SAndrew Geissler        cmd = "%s %s --noaction install %s " % (self.opkg_cmd,
448*635e0e46SAndrew Geissler                                                opkg_args,
449*635e0e46SAndrew Geissler                                                ' '.join(pkgs))
450*635e0e46SAndrew Geissler        try:
451*635e0e46SAndrew Geissler            output = subprocess.check_output(cmd, stderr=subprocess.STDOUT, shell=True)
452*635e0e46SAndrew Geissler        except subprocess.CalledProcessError as e:
453*635e0e46SAndrew Geissler            bb.fatal("Unable to dummy install packages. Command '%s' "
454*635e0e46SAndrew Geissler                     "returned %d:\n%s" % (cmd, e.returncode, e.output.decode("utf-8")))
455*635e0e46SAndrew Geissler
456*635e0e46SAndrew Geissler        bb.utils.remove(temp_rootfs, True)
457*635e0e46SAndrew Geissler
458*635e0e46SAndrew Geissler        return output
459*635e0e46SAndrew Geissler
460*635e0e46SAndrew Geissler    def backup_packaging_data(self):
461*635e0e46SAndrew Geissler        # Save the opkglib for increment ipk image generation
462*635e0e46SAndrew Geissler        if os.path.exists(self.saved_opkg_dir):
463*635e0e46SAndrew Geissler            bb.utils.remove(self.saved_opkg_dir, True)
464*635e0e46SAndrew Geissler        shutil.copytree(self.opkg_dir,
465*635e0e46SAndrew Geissler                        self.saved_opkg_dir,
466*635e0e46SAndrew Geissler                        symlinks=True)
467*635e0e46SAndrew Geissler
468*635e0e46SAndrew Geissler    def recover_packaging_data(self):
469*635e0e46SAndrew Geissler        # Move the opkglib back
470*635e0e46SAndrew Geissler        if os.path.exists(self.saved_opkg_dir):
471*635e0e46SAndrew Geissler            if os.path.exists(self.opkg_dir):
472*635e0e46SAndrew Geissler                bb.utils.remove(self.opkg_dir, True)
473*635e0e46SAndrew Geissler
474*635e0e46SAndrew Geissler            bb.note('Recover packaging data')
475*635e0e46SAndrew Geissler            shutil.copytree(self.saved_opkg_dir,
476*635e0e46SAndrew Geissler                            self.opkg_dir,
477*635e0e46SAndrew Geissler                            symlinks=True)
478*635e0e46SAndrew Geissler
479*635e0e46SAndrew Geissler    def package_info(self, pkg):
480*635e0e46SAndrew Geissler        """
481*635e0e46SAndrew Geissler        Returns a dictionary with the package info.
482*635e0e46SAndrew Geissler        """
483*635e0e46SAndrew Geissler        cmd = "%s %s info %s" % (self.opkg_cmd, self.opkg_args, pkg)
484*635e0e46SAndrew Geissler        pkg_info = super(OpkgPM, self).package_info(pkg, cmd)
485*635e0e46SAndrew Geissler
486*635e0e46SAndrew Geissler        pkg_arch = pkg_info[pkg]["arch"]
487*635e0e46SAndrew Geissler        pkg_filename = pkg_info[pkg]["filename"]
488*635e0e46SAndrew Geissler        pkg_info[pkg]["filepath"] = \
489*635e0e46SAndrew Geissler                os.path.join(self.deploy_dir, pkg_arch, pkg_filename)
490*635e0e46SAndrew Geissler
491*635e0e46SAndrew Geissler        return pkg_info
492*635e0e46SAndrew Geissler
493*635e0e46SAndrew Geissler    def extract(self, pkg):
494*635e0e46SAndrew Geissler        """
495*635e0e46SAndrew Geissler        Returns the path to a tmpdir where resides the contents of a package.
496*635e0e46SAndrew Geissler
497*635e0e46SAndrew Geissler        Deleting the tmpdir is responsability of the caller.
498*635e0e46SAndrew Geissler        """
499*635e0e46SAndrew Geissler        pkg_info = self.package_info(pkg)
500*635e0e46SAndrew Geissler        if not pkg_info:
501*635e0e46SAndrew Geissler            bb.fatal("Unable to get information for package '%s' while "
502*635e0e46SAndrew Geissler                     "trying to extract the package."  % pkg)
503*635e0e46SAndrew Geissler
504*635e0e46SAndrew Geissler        tmp_dir = super(OpkgPM, self).extract(pkg, pkg_info)
505*635e0e46SAndrew Geissler        bb.utils.remove(os.path.join(tmp_dir, "data.tar.xz"))
506*635e0e46SAndrew Geissler
507*635e0e46SAndrew Geissler        return tmp_dir
508