OSDN Git Service

fixed bind
[alterlinux/LFBS.git] / lfbs
1 #!/usr/bin/env bash
2
3 # SPDX-License-Identifier: GPL-3.0
4 #
5 # kokkiemouse
6 # Twitter: @kokkiemouse
7 # Email  : kokkiemouse@gmail.com
8 #
9 # lfbs
10 #
11
12 set -e
13 # set -u
14
15 export LANG=C
16
17 script_path=$(readlink -f "${0%/*}")
18 work_dir="${script_path}/work"
19 channels_dir="${script_path}/channels"
20 codename="32"
21 os_name="Fedora"
22 iso_name="Fedora"
23
24 arch=x86_64
25
26 out_dir="${script_path}/out"
27 iso_label="${os_name}_${codename}_${arch}"
28 iso_publisher='Fascode Network <https://fascode.net>'
29 iso_application="${os_name} Live/Rescue CD"
30 iso_version="${codename}-$(date +%Y.%m.%d)"
31 iso_filename="${iso_name}-${iso_version}-${arch}.iso"
32
33 username="liveuser"
34 usershell="/bin/bash"
35
36 debug=false
37 cache_only=false
38
39
40 start_time="$(date +%s)"
41
42 _msg_common() {
43     if [[ "${debug}" = true ]]; then
44         local _current_time
45         local _time
46         _current_time="$(date +%s)"
47         _time="$(("${_current_time}"-"${start_time}"))"
48
49         if [[ "${_time}" -ge 3600 ]]; then
50             echo "[$(date -d @${_time} +%H:%M.%S)]$("${script_path}/echo_color" -t 6 "[LFBS Core]")"
51         elif [[ "${_time}" -ge 60 ]]; then
52             echo "[00:$(date -d @${_time} +%M.%S)]$("${script_path}/echo_color" -t 6 "[LFBS Core]")"
53         else
54             echo "[00:00.$(date -d @${_time} +%S)] $("${script_path}/echo_color" -t 6 "[LFBS Core]")"
55         fi
56     else
57         "${script_path}/echo_color" -t 6 "[LFBS Core]"
58     fi
59 }
60
61 # Show an INFO message
62 # _msg_info <message>
63 _msg_info() {
64     local _msg
65     _msg="${@}"
66     echo "$(_msg_common)  $("${script_path}/echo_color" -t 2 "Info:") ${_msg}"
67 }
68
69 # Show an debug message
70 # _msg_debug <message>
71 _msg_debug() {
72     if [[ "${debug}" = true ]]; then
73         local _msg
74         _msg="${@}"
75         echo "$(_msg_common)  $("${script_path}/echo_color" -t 3 "Debug:") ${_msg}"
76     fi
77 }
78
79 # Show an ERROR message then exit with status
80 # _msg_error <message> <exit code>
81 _msg_error() {
82     local _msg
83     local _error
84     _msg="${1}"
85     _error=${2}
86     echo "$(_msg_common)  $("${script_path}/echo_color" -t 1 "Error:") ${_msg}"
87
88     if [[ ! ${_error} = 0 ]]; then
89         exit ${_error}
90     fi
91 }
92
93 # Unmount chroot dir
94 umount_chroot () {
95     local mount
96
97     for mount in $(mount | awk '{print $3}' | grep "$(realpath "${work_dir}")" | sort -r); do
98         _msg_info "Unmounting ${mount}"
99         umount -fl "${mount}"
100     done
101 }
102
103 # Helper function to run make_*() only one time.
104 run_once() {
105     local name
106     umount_chroot
107     name="$1"
108
109     if [[ ! -e "${work_dir}/build.${name}" ]]; then
110         _msg_info "$(echo $name | sed "s@_@ @g") is starting."
111         "${1}"
112         _msg_info "$(echo $name | sed "s@_@ @g") was done!"
113         touch "${work_dir}/build.${name}"
114     fi
115 }
116
117 run_cmd() {
118     local mount
119
120     for mount in "dev" "dev/pts" "proc" "sys" "etc/resolv.conf"; do
121     #for mount in "dev" "dev/pts" "proc" "sys" ; do
122         if [[ "${mount}" == "etc/resolv.conf" ]]; then
123             cp /etc/resolv.conf "${work_dir}/airootfs/${mount}"
124         else
125             mount --bind /${mount} "${work_dir}/airootfs/${mount}"
126         fi
127     done
128     
129     chroot "${work_dir}/airootfs" "${@}"
130
131     for mount in $(mount | awk '{print $3}' | grep "$(realpath "${work_dir}")" | sort -r); do
132         umount -fl "${mount}"
133     done
134 }
135
136 _dnf_install() {
137     run_cmd dnf install -y ${@}
138 }
139
140 # rm helper
141 # Delete the file if it exists.
142 # For directories, rm -rf is used.
143 # If the file does not exist, skip it.
144 # remove <file> <file> ...
145 remove() {
146     local _list
147     local _file
148     _list=($(echo "$@"))
149
150     for _file in "${_list[@]}"; do
151         _msg_debug "Removeing ${_file}"
152
153         if [[ -f ${_file} ]]; then
154             rm -f "${_file}"
155         elif [[ -d ${_file} ]]; then
156             rm -rf "${_file}"
157         fi
158     done
159 }
160
161 # Show help
162 _usage () {
163     echo "usage ${0} [options] [channel]"
164     echo
165     echo " General options:"
166     echo
167     echo "    -a | --arch <str>      Set architecture"
168     echo "                           Default: ${arch}"
169     echo "    -c | --codename <str>  Set ubuntu codename"
170     echo "                           Default: ${codename}"
171     echo "    -m | --mirror <url>    Set apt mirror server."
172     echo "                           Default: ${mirror}"
173     echo "    -o | --out <out_dir>   Set the output directory"
174     echo "                           Default: ${out_dir}"
175     echo "    -w | --work <work_dir> Set the working directory"
176     echo "                           Default: ${work_dir}"
177     echo
178     echo "    -d | --debug           "
179     echo "    -h | --help            This help message and exit"
180     echo
181     echo "You can switch between installed packages, files included in images, etc. by channel."
182     echo
183     echo " Channel:"
184     
185     local _channel
186     local channel_list
187     local description
188
189     for _channel in $(ls -l "${channels_dir}" | awk '$1 ~ /d/ {print $9 }'); do
190         if [[ -n $(ls "${channels_dir}/${_channel}") ]] && [[ ! "${_channel}" = "share" ]]; then
191             channel_list+=( "${_channel}" )
192         fi
193     done
194
195     for _channel in ${channel_list[@]}; do
196         if [[ -f "${channels_dir}/${_channel}/description.txt" ]]; then
197             description=$(cat "${channels_dir}/${_channel}/description.txt")
198         else
199             description="This channel does not have a description.txt."
200         fi
201
202         echo -ne "    ${_channel}"
203
204         for i in $( seq 1 $(( 23 - ${#_channel} )) ); do
205             echo -ne " "
206         done
207         
208         echo -ne "${description}\n"
209     done
210 }
211
212
213 prepare_build() {
214     if [[ ${EUID} -ne 0 ]]; then
215         _msg_error "This script must be run as root." 1
216     fi
217
218     [[ ! -d "${work_dir}" ]] && mkdir -p "${work_dir}"
219     [[ ! -d "${out_dir}" ]] && mkdir -p "${out_dir}"
220     umount_chroot
221
222     # Check codename
223     if [[ -z $(grep -h -v ^'#' ${channels_dir}/${channel_name}/codename.${arch} | grep -x ${codename}) ]]; then
224         _msg_error "This codename (${channel_name}) is not supported on this channel (${codename})."
225     fi
226
227 }
228
229 make_basefs() {
230     remove "${work_dir}/${codename}"
231     _msg_info "Installing Fedora to '${work_dir}/airootfs'..."
232     mkdir -p "${work_dir}/airootfs"
233     dnf --installroot="${work_dir}/airootfs" $(${script_path}/system/repository-json-parser.py ${script_path}/system/repository.json) groupinstall core -y
234     _msg_info "${codename} installed successfully!"
235     
236     echo 'PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:${PATH}' > "${work_dir}/airootfs/etc/bash.bashrc"
237     run_cmd dnf upgrade -y
238     # run_cmd apt-get upgrade
239 }
240
241
242 prepare_build() {
243     if [[ ${EUID} -ne 0 ]]; then
244         _msg_error "This script must be run as root." 1
245     fi
246
247     [[ ! -d "${work_dir}" ]] && mkdir -p "${work_dir}"
248     [[ ! -d "${out_dir}" ]] && mkdir -p "${out_dir}"
249     umount_chroot
250
251     # Check codename
252     if [[ -z $(grep -h -v ^'#' ${channels_dir}/${channel_name}/codename.${arch} | grep -x ${codename}) ]]; then
253         _msg_error "This codename (${channel_name}) is not supported on this channel (${codename})."
254     fi
255
256 }
257
258 make_systemd() {
259     _dnf_install dbus-tools
260     run_cmd dbus-uuidgen --ensure=/etc/machine-id
261     run_cmd mkdir /var/lib/dbus
262     run_cmd ln -sf /etc/machine-id /var/lib/dbus/machine-id
263 }
264 # 引数解析()
265 # 参考記事:https://0e0.pw/ci83 https://0e0.pw/VJlg
266
267 _opt_short="w:o:ha:-:m:c:d"
268 _opt_long="help,arch:,codename:,debug,help,mirror:,out:,work,cache-only"
269 OPT=$(getopt -o ${_opt_short} -l ${_opt_long} -- "${@}")
270
271 if [[ ${?} != 0 ]]; then
272     exit 1
273 fi
274
275 eval set -- "${OPT}"
276
277 while :; do
278     case ${1} in
279         -a | --arch)
280             if [[ -z ${2} ]]; then
281                 _msg_error "Please specify the architecture."
282                 exit 1
283             else
284                 arch="${2}"
285             fi
286             shift 2
287             ;;
288         -c | --codename)
289             if [[ -z ${2} ]]; then
290                 _msg_error "Please specify the codename."
291                 exit 1
292             else
293                 codename="${2}"
294             fi
295             shift 2
296             ;;
297         -d | --debug)
298             debug=true
299             shift 1
300             ;;
301         -h | --help)
302             _usage
303             exit 0
304             ;;
305         -m | --mirror)
306             if [[ -z ${2} ]]; then
307                 _msg_error "Please specify the mirror server."
308                 exit 1
309             else
310                 mirror="${2}"
311             fi
312
313             shift 2
314             ;;
315         -o | --out)
316             if [[ -z ${2} ]]; then
317                 _msg_error "Please specify the out dir."
318                 exit 1
319             else
320                 out_dir="${2}"
321             fi
322
323             shift 2
324             ;;
325         -w | --work)
326             if [[ -z ${2} ]]; then
327                 _msg_error "Please specify the out dir."
328                 exit 1
329             else
330                 work_dir="${2}"
331             fi
332
333             shift 2
334             ;;
335         --cache-only)
336             cache_only=true
337             shift 1
338             ;;
339         --)
340             shift
341             break
342             ;;
343         *)
344             _msg_error "Invalid argument '${1}'"
345             _usage 1
346             ;;
347     esac
348 done
349
350 bootfiles_dir="${work_dir}/bootfiles"
351 trap  umount_chroot 0 2 15
352
353 if [[ -n "${1}" ]]; then
354     channel_name="${1}"
355
356     check_channel() {
357         local channel_list
358         local i
359         channel_list=()
360
361         for _channel in $(ls -l "${channels_dir}" | awk '$1 ~ /d/ {print $9 }'); do
362             if [[ -n $(ls "${channels_dir}/${_channel}") ]] && [[ ! "${_channel}" = "share" ]]; then
363                 channel_list+=( "${_channel}" )
364             fi
365         done
366
367         for i in ${channel_list[@]}; do
368             if [[ "${i}" = "${channel_name}" ]]; then
369                 echo -n "true"
370                 return 0
371             fi
372         done
373
374         echo -n "false"
375         return 1
376     }
377
378     if [[ $(check_channel ${channel_name}) = false ]]; then
379         _msg_error "Invalid channel ${channel_name}"
380         exit 1
381     fi
382 fi
383
384
385 prepare_build
386 run_once make_basefs
387 run_once make_systemd
388 run_once make_apt_packages
389 run_once make_config
390 run_once make_add_user
391 run_once make_clean
392 run_once make_squashfs
393 run_once make_nfb
394 run_once make_efi
395 run_once make_iso
396 run_once make_checksum