├── templ_discinfo ├── packages.txt ├── templ_media.repo ├── create_iso_in_container.sh ├── Dockerfile ├── templ_treeinfo ├── README.md ├── bootstrap.sh └── templ_comps.xml /templ_discinfo: -------------------------------------------------------------------------------- 1 | 1578087695.538488 2 | CentOS Linux 8 3 | x86_64 4 | ALL 5 | -------------------------------------------------------------------------------- /packages.txt: -------------------------------------------------------------------------------- 1 | net-tools 2 | ethtool 3 | # tcpdump 4 | # pciutils 5 | # wget 6 | # rsync 7 | -------------------------------------------------------------------------------- /templ_media.repo: -------------------------------------------------------------------------------- 1 | [InstallMedia] 2 | name=CentOS Linux 8 3 | mediaid=None 4 | metadata_expire=-1 5 | gpgcheck=0 6 | cost=500 7 | -------------------------------------------------------------------------------- /create_iso_in_container.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | ./bootstrap.sh clean 4 | ./bootstrap.sh step isomount 5 | ./bootstrap.sh step createtemplate 6 | ./bootstrap.sh step scandeps 7 | ./bootstrap.sh step createrepo 8 | ./bootstrap.sh step createiso 9 | ./bootstrap.sh step isounmount 10 | cp ./CentOS-8.1.1911-x86_64-minimal.iso /mnt/ 11 | -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | FROM centos:8 2 | MAINTAINER alex4108@live.com 3 | RUN dnf update -y 4 | RUN dnf install -y yum-utils createrepo syslinux genisoimage isomd5sum bzip2 curl file git wget unzip 5 | RUN curl -L -o /root/CentOS-8.1.1911-x86_64-boot.iso http://isoredirect.centos.org/centos/8/isos/x86_64/CentOS-8.1.1911-x86_64-boot.iso 6 | RUN echo $(sha256sum /root/CentOS-8.1.1911-x86_64-boot.iso) 7 | RUN curl -L -o /root/bootstrap.zip https://github.com/uboreas/centos-8-minimal/archive/ef31f862908af773c74c234353e6bbad48b1ef5e.zip 8 | RUN unzip /root/bootstrap.zip -d /root/ 9 | RUN mv /root/centos-8-minimal-ef31f862908af773c74c234353e6bbad48b1ef5e/* /root/ 10 | COPY create_iso_in_container.sh /root/ 11 | RUN chmod +x create_iso_in_container.sh && /root/create_iso_in_conatainer.sh 12 | CMD ["/bin/bash"] 13 | -------------------------------------------------------------------------------- /templ_treeinfo: -------------------------------------------------------------------------------- 1 | [checksums] 2 | images/efiboot.img = sha256: 3 | images/install.img = sha256: 4 | images/pxeboot/initrd.img = sha256: 5 | images/pxeboot/vmlinuz = sha256: 6 | 7 | [general] 8 | ; WARNING.0 = This section provides compatibility with pre-productmd treeinfos. 9 | ; WARNING.1 = Read productmd documentation for details about new format. 10 | arch = x86_64 11 | family = CentOS Linux 12 | name = CentOS Linux 8 13 | packagedir = BaseOS/Packages 14 | platforms = x86_64,xen 15 | repository = BaseOS 16 | timestamp = 1578087691 17 | variant = BaseOS 18 | variants = BaseOS 19 | version = 8 20 | 21 | [header] 22 | type = productmd.treeinfo 23 | version = 1.2 24 | 25 | [images-x86_64] 26 | efiboot.img = images/efiboot.img 27 | initrd = images/pxeboot/initrd.img 28 | kernel = images/pxeboot/vmlinuz 29 | 30 | [images-xen] 31 | initrd = images/pxeboot/initrd.img 32 | kernel = images/pxeboot/vmlinuz 33 | 34 | [media] 35 | discnum = 1 36 | totaldiscs = 1 37 | 38 | [release] 39 | name = CentOS Linux 40 | short = CentOS 41 | version = 8 42 | 43 | [stage2] 44 | mainimage = images/install.img 45 | 46 | [tree] 47 | arch = x86_64 48 | build_timestamp = 1578087691 49 | platforms = x86_64,xen 50 | variants = BaseOS 51 | 52 | [variant-BaseOS] 53 | id = BaseOS 54 | name = BaseOS 55 | packages = BaseOS/Packages 56 | repository = BaseOS 57 | type = variant 58 | uid = BaseOS 59 | 60 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # centos-8-minimal 2 | 3 | A pure bash script to create **minimal installation ISO** image together with **additional packages**, leveraging one of official CentOS 8 ISO distribution. 4 | 5 | This will create an ISO image in the order of workflow shown below: 6 | 7 | 1. Rerefence ISO will be mounted (to "mtemp/" in working folder). 8 | 2. An ISO template will be created (to "image/" in working folder). 9 | - Essentials (isolinux, EFI, boot images etc) will be copied from reference ISO as it is. 10 | - Template files will be re-constructed and (re)placed on ISO template. 11 | 3. Dependencies for "core" and "additional" packages will be scanned and a resulting package list will be created. 12 | - Required packages (RPM files) will be downloaded in this phase if it is not downloaded yet and will be added into ISO template. 13 | - A copy of newly downloaded RPM file will be added into "rpms/" in working folder for later use to avoid re-downloading. 14 | 4. A repository (and metadata information) will be created under ISO template to use packages added. 15 | 5. ISO image will be created using ISO template that prepared in above steps. 16 | 6. Reference ISO will be unmounted. 17 | 18 | Hence, there are two main parts of this project: 19 | 20 | - Script itself and template files as essentials (**bootstrap.sh** and templ\_\* files explained in Footnotes section) 21 | - An additional package list to install during OS installation (**packages.txt**). 22 | 23 | ### Running in Docker 24 | 25 | - Requires Docker & Internet Connection 26 | 27 | You can build the Dockerfile here and run it with a sample command. Note that to retrieve your ISO after the container has finished creation, you must specify a mount point at `/mnt` during the container's execution in order to retrieve the file. Additionally, privileged execution is required by the container to mount the ISO during the recreation process. 28 | 29 | Example: `mkdir ./iso-out && docker build -t centos-8-minimal && docker run --privileged -v ./iso-out:/mnt centos-8-minimal` 30 | 31 | ### Requirements 32 | 33 | - CentOS 8 34 | 35 | You can run script on CentOS 8 only (since it depends on CentOS 8 utilities). 36 | 37 | - Some additional packages needs to be installed in order to run the script. Those can be installed by using the command below: 38 | 39 | yum -y install yum-utils createrepo syslinux genisoimage isomd5sum bzip2 curl file 40 | 41 | - One of the official ISO image of CentOS 8 distribution. Place it to same folder with the script. 42 | 43 | CentOS-8.X.XXXX-x86_64-boot.iso 44 | CentOS-8.X.XXXX-x86_64-dvd1.iso 45 | 46 | ### Synopsis 47 | 48 | Basic usage of the script: 49 | 50 | # ./bootstrap.sh 51 | Usage: ./bootstrap.sh 52 | 53 | Alternative usage for each step (in same workflow order) and usage for some functions: 54 | 55 | # ./bootstrap.sh step 56 | Usage: ./bootstrap.sh step .. 57 | 58 | Workflow steps: 59 | isomount 60 | createtemplate 61 | scandeps 62 | createrepo 63 | createiso 64 | isounmount 65 | 66 | Some usefull functions: 67 | rpmname [package ..] 68 | rpmurl [package ..] 69 | rpmdownload [package ..] 70 | fulldeps 71 | 72 | ### Usage 73 | 74 | You can change the content of "packages.txt" if you wish and then simply run following command: 75 | 76 | # ./bootstrap.sh run 77 | 78 | Script will continue to use the ISO template resource (image/) created at the first run on consequent runs. If you did changes on anywhere, you should "force" it to start job from the scratch by issuing following command; 79 | 80 | # ./bootstrap.sh run force 81 | 82 | Above command is actually equal to following two commands: 83 | 84 | # ./bootstrap.sh clean 85 | # ./bootstrap.sh run 86 | 87 | The "clean" switch will do "isounmount" when necessary and will clean ISO template and temporary files created by the script. 88 | 89 | You can run particular workflow-step manually when needed. For example: 90 | 91 | # ./bootstrap.sh step createiso 92 | 93 | There are also some functions which will not do any effect on working data. 94 | 95 | For example you can get download links for given package(s): 96 | 97 | # ./bootstrap.sh step rpmurl httpd php 98 | 99 | Or, you can download RPM files directly by issuing; 100 | 101 | # ./bootstrap.sh step rpmdownload httpd php 102 | 103 | Dowloaded RPM files will be placed into "rpms/" folder for later use (will not be added into ISO template). 104 | 105 | If you want to add custom package(s) into the resulting ISO and you want it to be installed during OS installation, you can add package name(s) into **packages.txt** file. For example; 106 | 107 | **packages.txt**: 108 | 109 | # Some networking tools 110 | net-tools 111 | ethtool 112 | tcpdump 113 | 114 | # Some system tools 115 | pciutils 116 | 117 | # Apache - PHP related 118 | httpd 119 | mod_security 120 | mod_ssl 121 | php 122 | php-mbstring 123 | php-soap 124 | 125 | (Empty lines and lines starting with "#" will be ignored.) 126 | 127 | If you want to download package(s) together with it's full dependencies you can use "debug" main switch. This switch is also useful to track processing of given package(s) when something went wrong. 128 | 129 | # ./bootstrap.sh debug php 130 | 131 | This "debug" switch will create verbose output. 132 | Again, dowloaded RPM files will be placed into "rpms/" folder for later use (and will not be added into ISO template). 133 | 134 | ### Environent Variables 135 | 136 | - **CMVERBOSE**=\ 137 | 138 | It is possible to have verbose output by using this variable with any value. For example; 139 | 140 | # CMVERBOSE=1 ./bootstrap.sh run force 141 | 142 | It is not set by default (no verbose output). 143 | 144 | - **CMISO**="referece iso filename" 145 | 146 | You can specify the reference ISO to be used with this variable like; 147 | 148 | # CMISO="CentOS-8.1.1911-x86_64-boot.iso" ./bootstrap.sh run force 149 | 150 | Script will use "CentOS-8.1.1911-x86\_64-boot.iso" by default if such variable is not given. 151 | - **CMOUT**="resulting iso filename" 152 | 153 | You can specify the name of resulting ISO file. For example; 154 | 155 | # CMOUT="my-minimal-centos-8.iso" ./bootstrap.sh run force 156 | - **CMETH**=\<**fast** \| **deep**\> 157 | 158 | The "method" that will be used while resolving package dependencies. You can combine it with *CMVERBOSE* for debugging purposes. 159 | 160 | The script will use "fast" method by default. This will use system utilities to resolve dependencies (by issuing "repoquery --requires --resolve --recursive ") and will use what it returns as the list of packages. 161 | 162 | The "deep" method is a kind of custom implementation of dependency resolving process. It is really slow since it checks each package recursively with its dependencies. But, it will give an idea about the dependency resolving process and it may also useful for debugging if something went wrong. 163 | 164 | On the other hand, you can use "deep" method with "debug" switch together to do debugging on single package. For example; 165 | 166 | # CMETH="deep" ./bootstrap.sh debug php 167 | 168 | Above command will display each package checks recursively and will display a dependency tree when it finish resolving. 169 | 170 | ### Footnotes 171 | 172 | - Group file template 173 | 174 | The list of "core" packages which defined in "templ\_comps.xml" template file is obtained from the "BaseOS" group on official CentOS 8 DVD-ISO. 175 | 176 | First, its content will be used to collect required installation packages. Then, the list of packages specified in "package.txt" will be merged into it and will be used as group file to include in metadata of the resulting ISO. 177 | 178 | You can change such file if you want, but please keep a single empty line inside the "packagelist" of group "core" to merge additional packages defined in package.txt like; 179 | 180 | 181 | 182 | core 183 | Core 184 | 185 | tboot 186 | --- put single empty line here. --- 187 | 188 | 189 | 190 | - Other Template files 191 | 192 | The template file "tepl\_treeinfo" is obtained from official CentOS 8 DVD-ISO (".treeinfo" is the original name) . It's content will be re-constructed according to the reference ISO you are going to use and the resulting file will be added into ISO template. 193 | 194 | Other template files (templ\_discinfo, templ\_media.repo) are obtained from official CentOS 8 DVD-ISO and they will be added into resulting ISO as it is. 195 | 196 | 197 | -------------------------------------------------------------------------------- /bootstrap.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # 2019'10, gokhan@kylone.com 3 | 4 | # 5 | # Evironment variables 6 | # 7 | # CMVERBOSE : increase verbosity (set any value) 8 | # CMISO : official ISO to use (i.e. CentOS-8.1.1911-x86_64-boot.iso) 9 | # CMOUT : resultig ISO file name (i.e. CentOS-8.1.1911-x86_64-minimal.iso) 10 | # CMETH : dependency resolving method to use (deep or fast) 11 | # 12 | # Default values 13 | # 14 | # default official ISO to use 15 | iso="CentOS-8.1.1911-x86_64-boot.iso" 16 | # 17 | # resulting ISO file name and volume label 18 | # such values will be determined again according to source image during ISO mount 19 | out="CentOS-8.1.1911-x86_64-minimal.iso" 20 | lbl="CentOS-8-1-1911-x86_64" 21 | # 22 | # dependency resolving method 23 | # deep: check dependency of every package one by one 24 | # fast: check core package depedencies only 25 | met="fast" 26 | # 27 | # no need to change further 28 | 29 | pw="$(pwd)" 30 | dp="${pw}/image" 31 | md="${pw}/mtemp" 32 | bo="${dp}/BaseOS" 33 | 34 | function cmusage() { 35 | echo "Usage: ${0} " 36 | echo 37 | exit 1 38 | } 39 | 40 | function cmusagestep() { 41 | echo "Usage: ${0} step .." 42 | echo 43 | echo " Workflow steps:" 44 | echo " isomount" 45 | echo " createtemplate" 46 | echo " scandeps" 47 | echo " createrepo" 48 | echo " createiso" 49 | echo " isounmount" 50 | echo 51 | echo " Some usefull functions:" 52 | echo " rpmname [package ..]" 53 | echo " rpmurl [package ..]" 54 | echo " rpmdownload [package ..]" 55 | echo " fulldeps [package ..]" 56 | echo 57 | exit 1 58 | } 59 | 60 | function cmnotcentos() { 61 | echo 62 | echo " ! This script is not suitable to use in this platform" 63 | echo 64 | exit 1 65 | } 66 | 67 | function cmcheck() { 68 | if [ "${PIPESTATUS[0]}" != "0" ]; then 69 | exit 1 70 | fi 71 | } 72 | 73 | function cmpipe() { 74 | while read line; do 75 | echo " ${1}${line}" 76 | done 77 | } 78 | 79 | function cmdot() { 80 | if [ "${CMVERBOSE}" != "" ]; then 81 | cmpipe 82 | else 83 | echo -n " " 84 | while read line; do 85 | echo -n "." 86 | done 87 | echo " done" 88 | fi 89 | } 90 | 91 | function cmisounmount() { 92 | if [ -d "${md}" ]; then 93 | echo -n " ~ unmount ISO .." 94 | umount "${md}" 2>/dev/null 95 | rmdir "${md}" 96 | echo " done" 97 | fi 98 | } 99 | 100 | function cmisomount() { 101 | if [ ! -e "${iso}" ]; then 102 | echo 103 | echo " ! Reference ISO (${iso}) not found." 104 | echo 105 | echo " You can download CentOS 8 from following resource;" 106 | echo " http://isoredirect.centos.org/centos/8/isos/x86_64/" 107 | echo 108 | echo " If you want to use different minor release, please" 109 | echo " specify it like below;" 110 | echo 111 | echo " CMISO='/path/to/file.iso' ./bootstrap.sh .." 112 | echo 113 | exit 1 114 | fi 115 | cmisounmount 116 | echo " ~ mount ISO " 117 | if [ ! -d "${md}" ]; then 118 | mkdir -p "${md}" 119 | mount -o loop "${iso}" "${md}" 2>&1 | cmpipe 120 | cmcheck 121 | echo " ${md} mounted" 122 | if [ "$(cat "${md}/isolinux/isolinux.cfg" | grep "CentOS Linux 8")" == "" ]; then 123 | cmisounmount 124 | echo 125 | echo " ! Reference ISO should be one of the CentOS 8 distribution." 126 | echo 127 | exit 128 | fi 129 | fi 130 | } 131 | 132 | function cmclean() { 133 | cmisounmount 134 | rm -rf "${dp}" 135 | rm -f target_comps.xml "${out}" .[cpmrdtfu]* 136 | } 137 | 138 | function cmcreatetemplate() { 139 | if [ ! -d "${md}" ]; then 140 | if [ "${CMSTEP}" != "" ]; then 141 | echo " ! ISO not mounted, please run;" 142 | echo " ${0} step isomount" 143 | echo 144 | fi 145 | return 146 | fi 147 | echo -n " ~ Preparing image template " 148 | echo -n "." 149 | mkdir -p "${dp}" 150 | mkdir -p "${bo}/Packages" 151 | echo -n "." 152 | cp -r "${md}/EFI" "${dp}/" 153 | cmcheck 154 | echo -n "." 155 | 156 | cp "templ_discinfo" "${dp}/.discinfo" 157 | cp "templ_media.repo" "${dp}/media.repo" 158 | echo -n "." 159 | cp -r "${md}/isolinux" "${dp}/" 160 | echo -n "." 161 | cp -r "${md}/images" "${dp}/" 162 | cmcheck 163 | rm -f "${dp}/.treeinfo" 164 | touch "${dp}/.treeinfo" 165 | while IFS= read line; do 166 | imgf="$(echo "${line}" | grep "^images/" | awk -F" = " {'print $1'})" 167 | if [ "${imgf}" != "" ]; then 168 | if [ ! -e "${dp}/${imgf}" ]; then 169 | echo 170 | echo 171 | echo " ! Image '${imgf}' not found in base ISO" 172 | echo 173 | exit 1 174 | fi 175 | sum="$(sha256sum "${dp}/${imgf}" | awk {'print $1'})" 176 | echo "${imgf} = sha256:${sum}" >> "${dp}/.treeinfo" 177 | echo -n "." 178 | else 179 | echo "${line}" >> "${dp}/.treeinfo" 180 | fi 181 | done < "templ_treeinfo" 182 | if [ -e "${md}/.treeinfo" ]; then 183 | ts="$(cat ${md}/.treeinfo | grep "timestamp = " | head -1 | awk -F"= " {'print $2'} | tr -d "\n\r")" 184 | if [ "${ts}" != "" ]; then 185 | sed -i "s/\\(timestamp = \\)[0-9]\\+/\\1${ts}/g" "${dp}/.treeinfo" 186 | fi 187 | fi 188 | if [ -e "${md}/.discinfo" ]; then 189 | ts="$(head -1 ${md}/.discinfo | tr -d "\n\r")" 190 | if [ "${ts}" != "" ]; then 191 | sed -i "s/[0-9]\\+\.[0-9]\\+/${ts}/g" "${dp}/.discinfo" 192 | fi 193 | fi 194 | echo " done" 195 | } 196 | 197 | function resolvefast() { 198 | # input arguments 199 | # package [package ..] 200 | tf="${CMTEMP}" 201 | vb="${CMVERBOSE}" 202 | if [ "${vb}" != "" ]; then 203 | echo "${@}" | tr " " "\n" | cmpipe " " 204 | fi 205 | repoquery --requires --resolve --recursive "${@}" 2>/dev/null | \ 206 | awk -F":" {'print $1'} | \ 207 | sed 's/\-[0-9]\+$//g' | \ 208 | sort | uniq | \ 209 | grep -v "glibc-all-langpacks\|glibc-langpack-[a-z0-9]\+$" \ 210 | >> "${tf}" 211 | } 212 | 213 | function resolvedeep() { 214 | # input arguments 215 | # package [package ..] 216 | s="${CMSEP}-" 217 | tf="${CMTEMP}" 218 | vb="${CMVERBOSE}" 219 | repoquery --requires --resolve "${@}" 2>/dev/null | \ 220 | awk -F":" {'print $1'} | \ 221 | sed 's/\-[0-9]\+$//g' | \ 222 | sort | uniq | \ 223 | while read line; do 224 | if [ "${line}" == "glibc-all-langpacks" -o "$(echo "${line}" | grep "glibc-langpack-[a-z0-9]\+$")" != "" ]; then 225 | if [ "${vb}" != "" ]; then 226 | echo " skip: ${@} ${line}" 227 | fi 228 | continue 229 | fi 230 | if [ "$(cat "${tf}" | grep "^${line}$")" == "" ]; then 231 | echo "${s} ${line}" >> .tree 232 | echo "${line}" >> "${tf}" 233 | if [ "${vb}" != "" ]; then 234 | echo " package: ${@} ${line}" 235 | else 236 | echo -n "," 237 | fi 238 | CMSEP="${s}" resolvedeep "${line}" 239 | fi 240 | done 241 | } 242 | 243 | function cmfulldeps() { 244 | # input arguments 245 | # package [package ..] 246 | if [ "${1}" == "" ]; then 247 | echo "Usage: ${0} step fulldeps [package ..]" 248 | echo 249 | exit 250 | fi 251 | rm -f ".pkgs" ".tree" 252 | touch ".pkgs" ".tree" 253 | echo " ~ Resolving dependencies for ${@}" 254 | if [ "${met}" == "deep" ]; then 255 | CMVERBOSE=1 CSEP=" " CMTEMP=".pkgs" resolvedeep "${@}" 256 | else 257 | rm -f .fast 258 | CMVERBOSE=1 CMTEMP=".fast" resolvefast "${@}" 259 | cat .fast | sort | uniq > .pkgs 260 | rm -f .fast 261 | fi 262 | echo " ~ Full dependency list of ${1}" 263 | cat .pkgs | sort | cmpipe " " 264 | } 265 | 266 | function cmcreatelist() { 267 | echo -n " ~ Creating package list " 268 | rm -f .core 269 | echo -n "." 270 | cat templ_comps.xml | grep packagereq | awk -F">" {'print $2'} | awk -F"<" {'print $1'} > .core 271 | echo -n "." 272 | cat packages.txt | grep -v "^#" | grep -v "^$" >> .core 273 | echo " done" 274 | tp="$(cat .core | sort | uniq | wc -l)" 275 | echo " ~ Resolving dependencies for ${tp} package(s)" 276 | if [ "${CMVERBOSE}" == "" ]; then 277 | echo -n " " 278 | fi 279 | rm -f .tree .pkgs 280 | touch .pkgs 281 | cat .core | sort | uniq | while read line; do 282 | if [ "${met}" == "deep" ]; then 283 | if [ "${CMVERBOSE}" != "" ]; then 284 | CMTEMP=".pkgs" CMSEP=" " resolvedeep "${line}" 285 | fi 286 | fi 287 | echo "${line}" >> .pkgs 288 | if [ "${CMVERBOSE}" == "" ]; then 289 | echo -n "." 290 | fi 291 | done 292 | if [ "${met}" == "deep" ]; then 293 | if [ "${CMVERBOSE}" == "" ]; then 294 | CMTEMP=".pkgs" CMSEP=" " resolvedeep $(cat .core | sort | uniq | tr "\n" " ") 295 | fi 296 | else 297 | CMTEMP=".pkgs" resolvefast $(cat .core | sort | uniq | tr "\n" " ") 298 | fi 299 | rm -f .core 300 | cat .pkgs | sort | uniq > .pkgf 301 | mv .pkgf .pkgs 302 | if [ "${CMVERBOSE}" == "" ]; then 303 | echo " done" 304 | fi 305 | } 306 | 307 | function cmrpmdownload() { 308 | # input arguments 309 | # package [package ..] 310 | if [ "${1}" == "" ]; then 311 | echo "Usage: ${0} rpmdownload " 312 | echo 313 | exit 1 314 | fi 315 | mkdir -p rpms 316 | yumdownloader --urlprotocol http --urls "${@}" 2>/dev/null | \ 317 | grep "^http" | \ 318 | sort | uniq | \ 319 | while read u; do 320 | if [ "${u}" != "" ]; then 321 | f=`echo "${u}" | awk -F"/" {'print $NF'}` 322 | if [ -e "rpms/${f}" ]; then 323 | if [ "$(file "rpms/${f}" | grep "RPM ")" != "" ]; then 324 | echo " - exists (rpms/${f})" 325 | continue 326 | fi 327 | rm -f "rpms/${f}" 328 | fi 329 | echo " ${f} [${u}]" 330 | curl -s "${u}" -o "rpms/${f}" 331 | if [ "${?}" == "0" ]; then 332 | if [ "$(file "rpms/${f}" | grep "RPM ")" == "" ]; then 333 | rm -f "rpms/${f}" 334 | echo " ! failed" 335 | fi 336 | else 337 | rm -f "rpms/${f}" 338 | echo " ! failed" 339 | fi 340 | fi 341 | done 342 | } 343 | 344 | function rpmdownload() { 345 | # input arguments 346 | # package [package ..] 347 | if [ "${1}" == "" ]; then 348 | echo " ! Pacakge name required for rpmdownload" 349 | echo 350 | exit 1 351 | fi 352 | ul="${CMURL}" 353 | if [ "${ul}" == "" ]; then 354 | ul="$(yumdownloader --urlprotocol http --urls "${@}" 2>/dev/null | \ 355 | grep "^http" | \ 356 | sort | uniq)" 357 | fi 358 | mkdir -p rpms 359 | echo "${ul}" | while read u; do 360 | if [ "${u}" != "" ]; then 361 | f=`echo "${u}" | awk -F"/" {'print $NF'}` 362 | if [ -e "rpms/${f}" ]; then 363 | if [ "$(file "rpms/${f}" | grep "RPM ")" != "" ]; then 364 | echo "${f}" 365 | continue 366 | fi 367 | rm -f "rpms/${f}" 368 | fi 369 | if [ -e "cache/${f}" ]; then 370 | cp "cache/${f}" "rpms/" 371 | echo "${f}" 372 | continue 373 | fi 374 | curl -s "${u}" -o "rpms/${f}" 375 | if [ "${?}" == "0" ]; then 376 | if [ "$(file "rpms/${f}" | grep "RPM ")" != "" ]; then 377 | echo "${u}" >> .dlrpm 378 | echo "${f}" 379 | else 380 | rm -f "rpms/${f}" 381 | echo "${u} -> ${f}" > .dler 382 | fi 383 | else 384 | rm -f "rpms/${f}" 385 | echo "${u} -> ${f}" > .dler 386 | fi 387 | fi 388 | done 389 | } 390 | 391 | function cmrpmurl() { 392 | # input arguments 393 | # package [package ..] 394 | if [ "${CMSTEP}" != "" -a "${1}" == "" ]; then 395 | echo "Usage: ${0} step rpmurl [package ..]" 396 | echo 397 | exit 1 398 | fi 399 | yumdownloader --urlprotocol http --urls "${@}" | \ 400 | grep "^http" | \ 401 | sort | uniq > "${pw}/.urls" 402 | } 403 | 404 | function cmrpmname() { 405 | # input arguments 406 | # package [package ..] 407 | if [ "${CMSTEP}" != "" -a "${1}" == "" ]; then 408 | echo "Usage: ${0} step rpmname [package ..]" 409 | echo 410 | exit 1 411 | fi 412 | repoquery "${@}" 2>/dev/null | \ 413 | sed 's/\-[0-9]\+:/\-/g' | \ 414 | awk {'print $1".rpm"'} | \ 415 | sort | uniq 416 | } 417 | 418 | function cmcollectrpm() { 419 | # input arguments 420 | # package [package ..] 421 | vb="${CMVERBOSE}" 422 | if [ "${CMSTEP}" != "" -a "${1}" == "" ]; then 423 | echo "Usage: ${0} step collectrpm [package ..]" 424 | echo 425 | exit 1 426 | fi 427 | cmrpmurl "${@}" 428 | dl="$(cat "${pw}/.urls")" 429 | rr="$(echo "${dl}" | awk -F"/" {'print $NF'} | sort | uniq)" 430 | if [ "${rr}" != "" ]; then 431 | mkdir -p rpms 432 | echo "${rr}" | while read r; do 433 | if [ -e "rpms/${r}" ]; then 434 | if [ -d "${bo}/Packages" ]; then 435 | cp "rpms/${r}" "${bo}/Packages/" 436 | fi 437 | if [ "${vb}" != "" ]; then 438 | echo " cached: ${r}" 439 | else 440 | echo -n "." 441 | fi 442 | else 443 | pk="$(echo "${r}" | awk -F".el8" {'print $1'} | sed 's/\-[0-9\.\-]\+$//g')" 444 | fu="$(echo "${dl}" | grep "/${r}$")" 445 | if [ "${fu}" == "" ]; then 446 | ir="$(echo "${r}" | sed 's/\.x86_64/\.i686/g')" 447 | fu="$(echo "${dl}" | grep "/${ir}$")" 448 | fi 449 | if [ "$(echo "${fu}" | wc -l)" != "1" ]; then 450 | fu="" 451 | rp="$(echo "${r}" | awk -F".rpm" {'print $1'})" 452 | pk="$(dnf info "${rp}" | grep "^Name" | awk -F": " {'print $2'} | sort | uniq)" 453 | fi 454 | if [ "${vb}" != "" ]; then 455 | echo "downloading: ${pk}, ${r}" 456 | fi 457 | dd="$(CMURL="${fu}" rpmdownload "${pk}")" 458 | if [ "${dd}" == "" ]; then 459 | echo "${pk}:${r}:" >> .miss 460 | if [ "${vb}" != "" ]; then 461 | echo " not found: ${r} (${pk})" 462 | else 463 | echo -n "!" 464 | fi 465 | else 466 | echo "${dd}" | while read d; do 467 | if [ "${d}" != "${r}" ]; then 468 | if [ -d "${bo}/Packages" ]; then 469 | cp "rpms/${d}" "${bo}/Packages/" 470 | fi 471 | if [ "${vb}" != "" ]; then 472 | echo " dowmloaded: ${r} -> ${d}, ${pk}" 473 | else 474 | echo -n ":" 475 | fi 476 | else 477 | if [ -d "${bo}/Packages" ]; then 478 | cp "rpms/${d}" "${bo}/Packages/" 479 | fi 480 | if [ "${vb}" == "" ]; then 481 | echo -n ":" 482 | fi 483 | fi 484 | done 485 | fi 486 | fi 487 | done 488 | else 489 | echo "${@}" >> .rslv 490 | args="${@}" 491 | if [ "${args}" != "" ]; then 492 | echo " unresolved: ${args}" 493 | else 494 | echo -n "!" 495 | fi 496 | fi 497 | } 498 | 499 | function cmcollectrpms() { 500 | tp="$(cat .pkgs | sort | uniq | wc -l)" 501 | echo " ~ Searching RPMs for ${tp} package(s)" 502 | if [ "${CMVERBOSE}" == "" ]; then 503 | echo -n " " 504 | fi 505 | rm -f .miss .rslv .dler 506 | mkdir -p rpms 507 | cmcollectrpm $(cat .pkgs | sort | uniq | tr "\n" " ") 508 | if [ "${CMVERBOSE}" == "" ]; then 509 | echo " done" 510 | fi 511 | } 512 | 513 | function cmcreaterepo() { 514 | if [ ! -d "${bo}/Packages" ]; then 515 | echo " ! Image temmplate is not ready, please run;" 516 | echo " ${0} step createtemplate" 517 | echo " ${0} step scandeps" 518 | echo 519 | exit 1 520 | fi 521 | tp="$(cat "${pw}/packages.txt" | grep -v "^#" | grep -v "^$" | wc -l)" 522 | echo -n " ~ Creating component list for ${tp} add-on package " 523 | uc="${pw}/target_comps.xml" 524 | rm -f "${uc}" 525 | touch "${uc}" 526 | while IFS= read xl; do 527 | if [ "${xl}" == "" ]; then 528 | cat "${pw}/packages.txt" | grep -v "^#" | grep -v "^$" | while read line; do 529 | echo " ${line}" >> "${uc}" 530 | echo -n "." 531 | done 532 | else 533 | echo "${xl}" >> "${uc}" 534 | fi 535 | done < "${pw}/templ_comps.xml" 536 | echo " done" 537 | echo " ~ Creating repodata " 538 | cd "${bo}" 539 | cmcheck 540 | rm -rf repodata 541 | createrepo -g "${uc}" . 2>&1 | cmdot 542 | cmcheck 543 | cd "${pw}" 544 | rm -f "${uc}" 545 | } 546 | 547 | function cmcreateiso() { 548 | if [ ! -d "${bo}/repodata" ]; then 549 | echo " ! Repo is not ready, please run;" 550 | echo " ${0} step createrepo" 551 | echo 552 | exit 1 553 | fi 554 | lbl="$(cat "${dp}/isolinux/isolinux.cfg" | grep "LABEL=" | awk -F"LABEL=" {'print $2'} | awk {'print $1'} | grep -v "^$" | head -1 | tr -d "\n\r")" 555 | if [ "${CMOUT}" == "" ]; then 556 | ver="$(cat "${dp}/isolinux/isolinux.cfg" | grep "LABEL=CentOS" | head -1 | awk -F"LABEL=CentOS-" {'print $2'} | awk -F"-x86_64" {'print $1'} | sed 's/\-/\./g')" 557 | if [ "${ver}" == "8.BaseOS" ]; then 558 | ver="8.0.1905" 559 | elif [ "${ver}" == "Stream.8" ]; then 560 | ver="8.0.20191219" 561 | fi 562 | out="CentOS-${ver}-x86_64-minimal.iso" 563 | fi 564 | echo " ~ Creating ISO image" 565 | cd "${dp}" 566 | chmod 664 isolinux/isolinux.bin 567 | rm -f "${pw}/${out}" 568 | mkisofs \ 569 | -input-charset utf-8 \ 570 | -o "${pw}/${out}" \ 571 | -b isolinux/isolinux.bin \ 572 | -c isolinux/boot.cat \ 573 | -no-emul-boot \ 574 | -V "${lbl}" \ 575 | -boot-load-size 4 \ 576 | -boot-info-table \ 577 | -eltorito-alt-boot \ 578 | -e images/efiboot.img \ 579 | -no-emul-boot \ 580 | -R -J -v -T . 2>&1 | cmdot 581 | cmcheck 582 | if [ -e "/usr/bin/isohybrid" ]; then 583 | echo " ~ ISO hybrid" 584 | isohybrid --uefi "${pw}/${out}" | cmdot 585 | cmcheck 586 | fi 587 | if [ -e "/usr/bin/implantisomd5" ]; then 588 | echo " ~ Implant ISO MD5" 589 | implantisomd5 --force --supported-iso "${pw}/${out}" | cmdot 590 | cmcheck 591 | fi 592 | cd "${pw}" 593 | isz="$(du -h "${out}" | awk {'print $1'})" 594 | echo " ~ ISO image ready: ${out} (${isz})" 595 | } 596 | 597 | function cmjobsingle() { 598 | # input arguments 599 | # package [package ..] 600 | rm -f .[cpmrdtf]* 601 | touch .pkgs .tree 602 | echo " ~ Creating package list for ${@} " 603 | if [ "${met}" == "deep" ]; then 604 | CMVERBOSE=1 CMTEMP=".pkgs" CMSEP=" " resolvedeep "${@}" 605 | else 606 | rm -f .fast 607 | CMVERBOSE=1 CMTEMP=".fast" resolvefast "${@}" 608 | cat .fast | sort | uniq > .pkgs 609 | fi 610 | echo " ~ Package with dependencies" 611 | cat .pkgs | cmpipe " " 612 | if [ "${met}" == "deep" ]; then 613 | echo " ~ Dependency tree" 614 | cat .tree | cmpipe " " 615 | fi 616 | echo " ~ Searching RPMs" 617 | CMVERBOSE=1 cmcollectrpm $(cat .pkgs | sort | uniq | tr "\n" " ") 618 | } 619 | 620 | function cmscandeps() { 621 | cmcreatelist 622 | cmcollectrpms 623 | } 624 | 625 | function cmjobfull() { 626 | cmclean 627 | cmisomount 628 | cmcreatetemplate 629 | cmscandeps 630 | cmcreaterepo 631 | cmcreateiso 632 | cmisounmount 633 | } 634 | 635 | function cmjobquick() { 636 | if [ "${CMISO}" != "" ]; then 637 | cmisomount 638 | fi 639 | cmcreatetemplate 640 | cmcreaterepo 641 | cmcreateiso 642 | cmisounmount 643 | } 644 | 645 | if [ ! -e /etc/centos-release ]; then 646 | cmnotcentos 647 | fi 648 | if [ "$(cat /etc/centos-release | grep "CentOS Linux release 8")" == "" ]; then 649 | cmnotcentos 650 | fi 651 | if [ ! -e "/usr/bin/repoquery" -o ! -e "/usr/bin/createrepo" -o ! -e "/usr/bin/yumdownloader" -o ! -e "/usr/bin/curl" -o ! -e "/usr/bin/mkisofs" ]; then 652 | echo 653 | echo " ! Some additional packages needs to be installed." 654 | echo " Please run following command to have them all:" 655 | echo 656 | echo " yum -y install yum-utils createrepo syslinux genisoimage isomd5sum bzip2 curl file" 657 | echo 658 | exit 1 659 | fi 660 | if [ "${CMISO}" != "" ]; then 661 | iso="${CMISO}" 662 | fi 663 | if [ "${CMOUT}" != "" ]; then 664 | out="${CMOUT}" 665 | fi 666 | if [ "${CMETH}" != "" ]; then 667 | met="${CMETH}" 668 | fi 669 | if [ ! -e "packages.txt" ]; then 670 | touch "packages.txt" 671 | fi 672 | 673 | if [ "${1}" == "run" ]; then 674 | shift 675 | if [ "${1}" == "force" ]; then 676 | cmjobfull 677 | elif [ -d "${bo}/Packages" ]; then 678 | cmjobquick 679 | else 680 | cmjobfull 681 | fi 682 | elif [ "${1}" == "clean" ]; then 683 | cmclean 684 | elif [ "${1}" == "debug" ]; then 685 | shift 686 | if [ "${1}" == "" ]; then 687 | cmusage 688 | fi 689 | cmjobsingle "${@}" 690 | elif [ "${1}" == "step" ]; then 691 | shift 692 | if [ "${1}" == "" ]; then 693 | cmusagestep 694 | fi 695 | cmd="cm${1}" 696 | shift 697 | CMVERBOSE=1 CMSTEP=1 ${cmd} "${@}" 698 | else 699 | cmusage 700 | fi 701 | 702 | -------------------------------------------------------------------------------- /templ_comps.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | 7 | anaconda-tools 8 | Anaconda tools 9 | Anaconda-Werkzeuge 10 | Herramientas de anaconda 11 | Outils anaconda 12 | Strumenti Anaconda 13 | Strumenti Anaconda 14 | Anaconda ツール 15 | Anaconda ツール 16 | Anaconda 툴 17 | Ferramentas Anaconda 18 | Инструменты Anaconda 19 | Инструменты Anaconda 20 | Anaconda 工具 21 | 蟒蛇工具 22 | 23 | false 24 | false 25 | 26 | chrony 27 | cryptsetup 28 | device-mapper-multipath 29 | dosfstools 30 | dracut-network 31 | e2fsprogs 32 | efibootmgr 33 | fcoe-utils 34 | firewalld 35 | gfs2-utils 36 | grub2-efi-ia32 37 | grub2-efi-ia32-cdboot 38 | grub2-efi-x64 39 | grub2-efi-x64-cdboot 40 | grub2-pc 41 | grub2-tools 42 | grub2-tools-efi 43 | grub2-tools-extra 44 | iscsi-initiator-utils 45 | lvm2 46 | mdadm 47 | realmd 48 | shim-ia32 49 | shim-x64 50 | syslinux-extlinux 51 | teamd 52 | tmux 53 | xfsprogs 54 | 55 | 56 | 57 | core 58 | Core 59 | Kern 60 | Kern 61 | ማዕከላዊ ቦታ 62 | اللبّ 63 | ভিত্তি 64 | هستگ 65 | Падмурак 66 | Основа 67 | কোর 68 | কোর 69 | Jezgra 70 | Nucli 71 | Úplný základ 72 | Úplný základ 73 | Craidd 74 | Grundlæggende 75 | Kern 76 | Kern 77 | Πυρήνας 78 | Core 79 | Núcleo 80 | Núcleo 81 | Tuum 82 | اصل 83 | اصل 84 | Keskeiset 85 | Core 86 | Núcleo 87 | મૂળ 88 | ליבה 89 | कोर 90 | Jezgra 91 | Jezgra 92 | Mag 93 | Հիմք 94 | Nucleo 95 | Inti 96 | Bugas 97 | Lágmarkskerfi 98 | Principale 99 | Principale 100 | コア 101 | コア 102 | ბირთვი 103 | ಅಂತಸ್ಸಾರ 104 | 핵심 105 | Pamatsistēma 106 | कोर 107 | Основни 108 | കോറ്‍ 109 | कोर 110 | Teras 111 | Teras 112 | Kjerne 113 | कोर 114 | Kern 115 | Kjerne 116 | Bogare 117 | ପ୍ରମୂଖ 118 | ਮੂਲ 119 | Rdzeń 120 | Núcleo 121 | Núcleo 122 | Nucleu 123 | Основа 124 | Основа 125 | න්‍යෂ්ඨිය 126 | Jadro 127 | Jedro 128 | Bërthama 129 | Срж 130 | Srž 131 | Srž 132 | Grund 133 | கோர் 134 | கோர் 135 | అంతర్భాగం 136 | Система 137 | แกนหลัก 138 | Çekirdek 139 | Основа 140 | Основа 141 | مرکز 142 | Lõi 143 | 核心 144 | 核心 145 | 核心 146 | Okuyikhona 147 | Smallest possible installation 148 | Kleinste moontlike installasie 149 | Kleinste moontlike installasie 150 | ትንሹ የሚቻለው የማስገባት ሥራ 151 | أصغر تثبيت متاح 152 | ক্ষুদ্ৰতম সম্ভৱ ইনস্টল 153 | هوردترین نصب ممکن 154 | Найменшая з магчымых усталёўка 155 | Най-малката възможна инсталация 156 | ন্যূনতম ইনস্টলেশন 157 | ন্যূনতম ইনস্টলেশন 158 | Najmanja moguća instalacija 159 | La instal·lació més petita possible 160 | Nejmenší možná instalace 161 | Nejmenší možná instalace 162 | Arsefydliad lleiaf posib 163 | Mindst mulige installation 164 | Kleinstmögliche Installation 165 | Kleinstmögliche Installation 166 | Μικρότερη δυνατή εγκατάσταση 167 | Smallest possible installation 168 | Instalación lo más pequeña posible 169 | Instalación lo más pequeña posible 170 | Väikseim võimalik installatsioon 171 | کوچکترین نصب ممکن 172 | کوچکترین نصب ممکن 173 | Pienin mahdollinen asennus 174 | Installation minimale 175 | નાનામાં નાના શક્ય સ્થાપનો 176 | התקנה מזערית 177 | सबसे छोटा संभावित अधिष्ठापन 178 | Najmanja moguća instalacija 179 | Najmanja moguća instalacija 180 | Minimális telepítés 181 | Փոքրագույն հնարավոր տեղադրում 182 | Le plus parve installation possibile 183 | Instalasi sekecil mungkin 184 | Lágmarks uppsetning 185 | Installazione minima 186 | Installazione minima 187 | 最小限のインストール 188 | 最小限のインストール 189 | უმცირესი შესაძლო ჩადგმა 190 | ಕನಿಷ್ಟ ಸಾಧ್ಯವಿರುವ ಅನುಸ್ಥಾಪನೆ 191 | 가능한 최소 설치 192 | Vismazākā iespējamā instalācija 193 | सबसँ छोट संभावित संस्थापन 194 | Најмалата можна инсталација 195 | സാധ്യമായ ഏറ്റവും ചെറിയ ഇന്‍സ്റ്റലേഷന്‍ 196 | सर्वात लहान शक्य प्रतिष्ठापन 197 | Pemasangan sekecil mungkin 198 | Pemasangan sekecil mungkin 199 | Minste mulige installering 200 | सम्भावित सबैभन्दा सानो स्थापना 201 | Kleinst mogelijke installatie 202 | Minste mulige installering 203 | Go tsenya mo go kgonegago go gonyenyane kudu 204 | କ୍ଷୁଦ୍ରତମ ସମ୍ଭାବ୍ଯ ସ୍ଥାପନ 205 | ਘੱਟੋ-ਘੱਟ ਸੰਭਵ ਇੰਸਟਾਲੇਸ਼ਨ 206 | Najmniejsza możliwa instalacja 207 | A mais pequena instalação possível 208 | A mais pequena instalação possível 209 | Cea mai mică instalare posibilă 210 | Минимальная установка 211 | Минимальная установка 212 | සිදුකල හැකි ඉතාම කුඩා ස්ථාපනය 213 | Najmenšia možná inštalácia 214 | Najmanjša možna namestitev 215 | Instalimi më i vogël i mundshëm 216 | Најмања могућа инсталација 217 | Najmanja moguća instalacija 218 | Najmanja moguća instalacija 219 | Minsta möjliga installation 220 | மிக சிறிய நிறுவல் 221 | மிக சிறிய நிறுவல் 222 | సాధ్యమగు చిన్న సంస్థాపన 223 | Сабткунии оддӣ 224 | ติดตั้งให้มีขนาดเล็กที่สุดที่เป็นไปได้ 225 | Olası en küçük kurulum 226 | Мінімально можливе встановлення 227 | Мінімально можливе встановлення 228 | چھو ٹی ممکن انسٹللشن 229 | Cài đặt nhỏ tối thiểu 230 | 最小安裝 231 | 最小安装 232 | 最小安裝 233 | Ukufakwa okuncane 234 | false 235 | false 236 | 237 | audit 238 | basesystem 239 | bash 240 | coreutils 241 | cronie 242 | curl 243 | dnf 244 | e2fsprogs 245 | filesystem 246 | firewalld 247 | glibc 248 | grubby 249 | hostname 250 | initscripts 251 | iproute 252 | iprutils 253 | iputils 254 | irqbalance 255 | kbd 256 | kexec-tools 257 | less 258 | man-db 259 | ncurses 260 | openssh-clients 261 | openssh-server 262 | parted 263 | passwd 264 | policycoreutils 265 | procps-ng 266 | rng-tools 267 | rootfiles 268 | rpm 269 | selinux-policy-targeted 270 | setup 271 | shadow-utils 272 | sssd-common 273 | sssd-kcm 274 | sudo 275 | systemd 276 | tuned 277 | util-linux 278 | vim-minimal 279 | xfsprogs 280 | yum 281 | kernel 282 | NetworkManager 283 | NetworkManager-team 284 | NetworkManager-tui 285 | authselect 286 | biosdevname 287 | dnf-plugins-core 288 | dracut-config-rescue 289 | iwl100-firmware 290 | iwl1000-firmware 291 | iwl105-firmware 292 | iwl135-firmware 293 | iwl2000-firmware 294 | iwl2030-firmware 295 | iwl3160-firmware 296 | iwl3945-firmware 297 | iwl4965-firmware 298 | iwl5000-firmware 299 | iwl5150-firmware 300 | iwl6000-firmware 301 | iwl6000g2a-firmware 302 | iwl6050-firmware 303 | iwl7260-firmware 304 | kernel-tools 305 | libsysfs 306 | linux-firmware 307 | lshw 308 | lsscsi 309 | microcode_ctl 310 | prefixdevname 311 | sg3_utils 312 | sg3_utils-libs 313 | dracut-config-generic 314 | dracut-network 315 | rdma-core 316 | selinux-policy-mls 317 | tboot 318 | 319 | 320 | 321 | 322 | minimal-environment 323 | Minimal Install 324 | নূন্যতম ইনস্টল 325 | Minimální instalace 326 | Minimální instalace 327 | Minimale Installation 328 | Minimale Installation 329 | Instalación mínima 330 | Instalación mínima 331 | Installation minimale 332 | ન્યૂનતમ સ્થાપન 333 | न्यूनतम संस्थापन 334 | Installazione minima 335 | Installazione minima 336 | 最小限のインストール 337 | 最小限のインストール 338 | ಕನಿಷ್ಟ ಅನುಸ್ಥಾಪನೆ 339 | 최소 설치 340 | ഏറ്റവും കുറഞ്ഞ ഇന്‍സ്റ്റോള്‍ 341 | किमान इंस्टॉल 342 | ସର୍ବନିମ୍ନ ସ୍ଥାପନ 343 | ਘੱਟ ਤੋਂ ਘੱਟ ਇੰਸਟਾਲ 344 | Minimalna instalacja 345 | Instalações Mínimas 346 | Instalações Mínimas 347 | Минимальная установка 348 | Минимальная установка 349 | குறைந்தபட்ச நிறுவல் 350 | குறைந்தபட்ச நிறுவல் 351 | కనీసపు సంస్థాపన 352 | Мінімальна система 353 | Мінімальна система 354 | 最小型安裝 355 | 最小安装 356 | 最小型安裝 357 | Basic functionality. 358 | মৌলি কাৰ্য্যকৰীতা। 359 | Základní funkcionalita. 360 | Základní funkcionalita. 361 | Grundlegende Funktionalität. 362 | Grundlegende Funktionalität. 363 | Funcionalidad básica. 364 | Funcionalidad básica. 365 | Fonctionnalité de base. 366 | મૂળભૂત વિધેય. 367 | मौलिक प्रकार्यात्मकता. 368 | Funzione di base. 369 | Funzione di base. 370 | 基本的な機能です。 371 | 基本的な機能です。 372 | ಮೂಲಭೂತ ಕ್ರಿಯಾಶೀಲತೆ. 373 | 기본적인 기능입니다. 374 | അടിസ്ഥാന പ്രവൃത്തിവിശേഷണം. 375 | मूळ कार्यक्षमता. 376 | ସାଧାରଣ କାର୍ଯ୍ୟକାରିତା। 377 | ਮੁੱਢਲੀ ਕਾਰਜਸ਼ੀਲਤਾ। 378 | Podstawowa funkcjonalność. 379 | Função básica 380 | Função básica 381 | Базовая функциональность. 382 | Базовая функциональность. 383 | அடிப்படை செயலம்சம். 384 | அடிப்படை செயலம்சம். 385 | ప్రాథమిక ఫంక్షనాలిటి. 386 | Основні можливості. 387 | Основні можливості. 388 | 基本功能。 389 | 基本功能。 390 | 基本功能。 391 | 3 392 | 393 | core 394 | 395 | 396 | 397 | 398 | 399 | 400 | 401 | 402 | 403 | 404 | 405 | 406 | 407 | --------------------------------------------------------------------------------