OSDN Git Service

Unmounting failure is changed from error to warning.
[portsreinstall/current.git] / lib / libmain.sh
1 #!/bin/sh -e
2 # ==============================================================================
3 # portsreinstall library script
4 # - Common functions of main programs -
5 # Copyright (C) 2018 Mamoru Sakaue, MwGhennndo, All Rights Reserved.
6 # This software is distributed under the 2-Clause BSD License.
7 # ==============================================================================
8
9 # ============= Define the software version =============
10 main_set_version ()
11 {
12         MYVERSION=4.0.1
13         COMPATIBLE_VERSIONS='^(4\.[0]\.[0-1])$'
14         # Template for development versions
15         MYVERSION=4.0.0+toward_4.0.1_20180714214038
16         COMPATIBLE_VERSIONS='^(4\.[0]\.[0]|4\.[0]\.[0]+(|\+toward_4\.[0]\.[1]+_[0-9]+))$'
17 }
18
19 # ============= Parse options, arguments and control parameters =============
20 # All arguments/options of the main program must be passed.
21 main_parse_options_arguments ()
22 {
23         # ============= Save arguments for upgraded restart =============
24         options_dump_args "$@" > ${TMPDIR}/restart_command.sh
25
26         # ============= Option check =============
27         options_set_default
28
29         options_getopts "$@" || :
30         if [ $OPTIONS_ERRNO -eq 2 ]
31         then
32                 message_echo "INTERNAL ERROR: In parsing options" >&2
33                 exit 1
34         fi
35         shift "${OPTIONS_SHIFT}"
36         
37         options_regularize
38
39         # ============= Argument check for no-command options =============
40         if [ $opt_help_mode -ne 0 -o $opt_show_version = yes ]
41         then
42                 if [ $# -gt 0 ]
43                 then
44                         OPTIONS_ERRNO=1
45                 fi
46         fi
47
48         # ============= Output usage if the case of a help mode or option/argument errors =============
49         if [ $OPTIONS_ERRNO -ne 0 ]
50         then
51                 exit $OPTIONS_ERRNO
52         elif [ $opt_help_mode -eq 1 ]
53         then
54                 usage_short
55                 exit
56         elif [ $opt_help_mode -eq 2 ]
57         then
58                 usage_long | less -r
59                 exit
60         fi
61
62         # ============= Output version number =============
63         if [ $opt_show_version = yes ]
64         then
65                 message_version
66                 exit
67         fi
68
69         # ============= Set up variables for environment of ports and packages =============
70         conf_setup_ports_envs
71         conf_setup_packages_envs
72
73         # ============= Execute command operations before getting the temporary database ready =============
74         command_all_exec_before_db_creation "$@"
75
76         # ============= Creation of temporary database directory =============
77         database_maintain_create
78
79         # ============= Argument check for conventional runs =============
80         command_all_parse_args "$@"
81         shift "${COMMAND_SHIFT}"
82 }
83
84 # ============= Define the common termination messages =============
85 main_define_common_termination_messages ()
86 {
87         temp_terminate_process_common ()
88         {
89                 local errno msg_where
90                 errno=${1:-0}
91                 [ $opt_batch_mode = yes -o $errno -eq 0 ] && return
92                 msg_where=`temp_get_msg_current_stage`
93                 [ -n "$msg_where" ] && msg_where=" during $msg_where"
94                 message_echo
95                 if [ $errno -eq 130 ]
96                 then
97                         message_echo "INFO: Terminated at `message_timestamp`$msg_where."
98                         message_echo
99                         message_echo " You can restart this process from the terminated point by"
100                 else
101                         message_echo "INFO: Aborted at `message_timestamp`$msg_where."
102                         message_echo
103                         message_echo " You may restart this process from the aborted point by"
104                 fi
105                 message_echo "executing without options or arguments as:"
106                 if [ -n "$COMMAND_RESTART" ]
107                 then
108                         message_echo "  ${APPNAME} $COMMAND_RESTART"
109                 else
110                         message_echo "  ${APPNAME}"
111                 fi
112         }
113 }
114
115 # ============= Set termination messages for special commands =============
116 main_set_termination_messages_special ()
117 {
118         temp_terminate_process ()
119         {
120                 local errno tmp_msg
121                 errno=${1:-0}
122                 tmp_msg=${TMPDIR}/temp_terminate_process:msg
123                 [ $errno -eq 0 -o $opt_batch_mode = yes ] && return
124                 temp_terminate_process_common "$errno"
125                 [ -n "$COMMAND_RESTART" ] && return
126                 cat > $tmp_msg << eof
127 Instead, if you only want to construct the temporary database so as to stop before the actual reinstallation, execute as:
128 ${APPNAME} prepare
129 eof
130                 message_cat "$tmp_msg"
131         }
132 }
133
134 # ============= Option settings =============
135 main_option_settings ()
136 {
137         local optcomb_err
138         # Load, renew and save option values
139         optcomb_err=0
140         if [ \( "x$opt_reload_conf" = xyes -o "x$opt_reset_targets" = xyes \) -a "x$COMMAND_MODE" != xredo ]
141         then
142                 message_echo "ERROR: Options -L and -N are available only in the initial run of redo command." >&2
143                 message_echo >&2
144                 optcomb_err=1
145         fi
146         if [ "x$opt_batch_ports_only" = xyes -a "x$opt_interactive_ports_only" = xyes ]
147         then
148                 message_echo "ERROR: Options -A and -I conflict with each other." >&2
149                 message_echo >&2
150                 optcomb_err=1
151         fi
152         if [ -e "${DBDIR}/saved_options.sh" ]
153         then
154                 options_chk_invalid_optvals_renewal non_renewable || optcomb_err=$?
155                 {
156                         options_renewed_optvals M renewable_anytime || optcomb_err=$?
157                         options_renewed_optvals N renewable_in_redo_on_target || optcomb_err=$?
158                         options_renewed_optvals L renewable_in_redo_on_conf || optcomb_err=$?
159                 } > ${TMPDIR}/renewed_optvals.sh
160                 [ $optcomb_err -eq 0 ] || exit $optcomb_err
161                 . "${DBDIR}/saved_options.sh"
162                 . "${TMPDIR}/renewed_optvals.sh"
163         fi
164         misc_is_superuser_privilege && misc_get_all_vardefs | options_filter saved > ${DBDIR}/saved_options.sh
165         :
166 }
167
168 # ============= Save the previous configuration if exists =============
169 main_save_prev_conf ()
170 {
171         local PROGRAM_DEPENDS
172         PROGRAM_DEPENDS=''
173         _program_exec_and_record_completion__operation ()
174         {
175                 rm -rf "${DBDIR}/conf.prev"
176                 [ -d "${DBDIR}/conf" ] && \
177                         cp -Rp "${DBDIR}/conf" "${DBDIR}/conf.prev"
178                 :
179         }
180         program_exec_and_record_completion SAVE_PREV_CONF
181 }
182
183 # ============= Load the saved configuration =============
184 main_load_conf ()
185 {
186         . "${DBDIR}/conf/setenv.sh"
187 }
188
189 # ============= Get complete configuration variable definitions by importing pkgtools.conf(5) if available =============
190 main_get_complete_conf ()
191 {
192         local PROGRAM_DEPENDS
193         PROGRAM_DEPENDS='SAVE_PREV_CONF'
194         _program_exec_and_record_completion__operation ()
195         {
196                 local need_msg
197                 need_msg=no
198                 rm -rf "${DBDIR}/conf"
199                 mkdir -p "${DBDIR}/conf"
200                 [ "x`options_get_effective_opt_load_pkgtoolsconf 2> /dev/null`" != xno -a $opt_batch_mode = no ] \
201                         && need_msg=yes
202                 [ $need_msg = yes ] && \
203                         message_section_title "Parsing pkgtools.conf (by using installed portupgrade)"
204                 conf_get_complete_var_defs > ${DBDIR}/conf/complete_setup.sh
205                 [ $need_msg = yes ] &&  { message_echo "===> ok"; message_echo; }
206                 :
207         }
208         program_exec_and_record_completion GET_COMPLETE_CONF_VAR_DEF
209 }
210
211 # ============= Parse the configuration =============
212 main_parse_conf ()
213 {
214         local PROGRAM_DEPENDS
215         PROGRAM_DEPENDS='GET_COMPLETE_CONF_VAR_DEF'
216         _program_exec_and_record_completion__operation ()
217         {
218                 message_section_title "Parsing the configuration"
219                 conf_manipulate_available_var_defs
220                 . "${DBDIR}/conf/manipulated_defs.sh"
221                 # ALT_MOVED_*
222                 conf_build_effective_MOVED
223                 # Environmental variables
224                 conf_setup_effective_env > ${DBDIR}/conf/setenv.sh
225                 . "${DBDIR}/conf/setenv.sh"
226                 # HOLD_*
227                 conf_parse_vars_for_each_port_glob HOLD
228                 # TABOO_*
229                 conf_parse_vars_for_each_port_glob TABOO
230                 fileedit_combine_lists "${DBDIR}/conf/TABOO:PORTS.parsed" "${DBDIR}/taboo.list" \
231                         > ${DBDIR}/taboo.all.list
232                 # FREEZE_*
233                 conf_parse_vars_for_each_port_glob FREEZE
234                 fileedit_combine_lists "${DBDIR}/conf/FREEZE:PORTS.parsed" "${DBDIR}/freeze.list" \
235                         > ${DBDIR}/freeze.all.list
236                 # NOPKG_*
237                 conf_parse_vars_for_each_port_glob NOPKG
238                 # REPLACE_*
239                 conf_build_replacement_patterns_from_REPLACE
240                 # CONFLICT_*
241                 conf_parse_vars_for_each_port_glob_with_bound_val CONFLICT TARGET DEF
242                 # MARG_*
243                 conf_parse_vars_for_each_port_glob_with_bound_val MARG TARGET DEF
244                 # MENV_*
245                 conf_parse_vars_for_each_port_glob_with_bound_val MENV TARGET DEF
246                 # BEFOREBUILD_*
247                 conf_parse_vars_for_each_port_glob_with_bound_val BEFOREBUILD TARGET COMMAND
248                 # BEFOREDEINSTALL_*
249                 conf_parse_vars_for_each_port_glob_with_bound_val BEFOREDEINSTALL TARGET COMMAND
250                 # AFTERINSTALL_*
251                 conf_parse_vars_for_each_port_glob_with_bound_val AFTERINSTALL TARGET COMMAND
252                 message_echo
253         }
254         program_exec_and_record_completion PARSE_CONF
255 }
256
257 # ============= Set up parameters based on options, arguments, environment =============
258 main_setup_parameters ()
259 {
260         # ============= Termination messages during construction of the temporary database =============
261         main_define_common_termination_messages
262         temp_reset_termination_messages_common
263         main_set_termination_messages_special
264
265         # ============= Opening title =============
266
267         message_credit
268         command_all_chk_need_opening_notice && message_opening_notice
269         message_echo
270
271         # ============= Execute command operations which do not need package tools =============
272
273         command_all_exec_without_pkgtools "$@"
274         misc_is_superuser_privilege && database_maintain_mark_use
275         # ============= Definition of environment dependent functions =============
276
277         pkgsys_def_pkgtools
278
279         # ============= Option settings =============
280
281         # Execute command operations which are not affected by saved option settings
282         command_all_exec_irrespective_of_saved_options "$@"
283
284         # Load, renew and save option values
285         main_option_settings
286
287         # Show option values
288         message_show_option_settings
289
290         # ============= Configurations =============
291
292         # Save the previous configuration if exists
293         main_save_prev_conf
294
295         # Get complete configuration variable definitions by importing pkgtools.conf(5) if available
296         main_get_complete_conf
297
298         # Parse the configuration
299         main_parse_conf
300
301         # Load the saved configuration
302         main_load_conf
303 }
304
305 # ============= Operation without packages management tools =============
306 main_operation_without_pkg_management_tools ()
307 {
308         # Execute command operations which should be done without upgrade of tools
309         command_all_exec_before_tools_upgrade "$@"
310
311         # Check whether the temporary database is newer than the ports tree and refresh if so
312         database_maintain_refresh_if_obsolete
313 }
314
315 # ============= Collect all installed packages =============
316 main_collect_all_installed_packages ()
317 {
318         local PROGRAM_DEPENDS
319         PROGRAM_DEPENDS=''
320         _program_exec_and_record_completion__operation ()
321         {
322                 local tmp_installed_ports tmp_installed_pkgs
323                 message_section_title "Collecting all installed packages"
324                 if [ ! -e "${DBDIR}/installed_ports" ]
325                 then
326                         pkg_info_all_flavored_origins > ${DBDIR}/installed_ports.tmp
327                         pkg_info_gen_pkg_origin_table
328                         mv "${DBDIR}/installed_ports.tmp" "${DBDIR}/installed_ports"
329                 fi
330                 message_echo
331         }
332         program_exec_and_record_completion COLLECT_ALL_INSTALLED_PACKAGES
333 }
334
335 # ============= Preliminary inspection of tools which have to be up-to-date =============
336 # (No need depend on PARSE_CONF because INSPECT_ALL_DEPENDENCIES will take the task.)
337 main_preliminary_inspection_of_tools ()
338 {
339         local PROGRAM_DEPENDS
340         PROGRAM_DEPENDS=
341         _program_exec_restartable_loop_operation__routine ()
342         {
343                 local origin
344                 origin=$1
345                 database_build_inspect_dependencies "$origin"
346         }
347         _program_exec_and_record_completion__operation ()
348         {
349                 local DEPTH_INDEX
350                 message_section_title "Preliminary inspection of tools which have to be up-to-date"
351                 {
352                         [ "$PKGSYS_USE_PKGNG" = yes ] && pkgsys_portsmgmt_pkg
353                         pkgsys_is_dialog4ports_used && pkgsys_portsmgmt_dialog4ports
354                         [ -n "$MYPORTORIGIN" ] && echo "$MYPORTORIGIN"
355                 } 2> /dev/null > ${DBDIR}/stage.loop_list/tools_to_inspect
356                 cp /dev/null "${DBDIR}/done_required_ports_to_inspect"
357                 DEPTH_INDEX='--'
358                 program_exec_restartable_loop_operation tools_to_inspect
359                 database_build_post_inspect_dependencies
360                 message_echo
361         }
362         program_exec_and_record_completion PRELIMINARY_INSPECTION_OF_TOOLS
363 }
364
365 # ============= Upgrade of pkg(8) if new =============
366 # (No need depend on PARSE_CONF because REINSTALLATION will take the task.)
367 main_upgrade_pkg8_if_new ()
368 {
369         local PROGRAM_DEPENDS
370         if [ \( "$COMMAND_MODE" = do -o "$COMMAND_MODE" = redo \) \
371                 -a $opt_dry_run = no -a $opt_suppress_pkgtools_upadte = no \
372                 -a "$PKGSYS_USE_PKGNG" = yes ]
373         then
374                 PROGRAM_DEPENDS='PRELIMINARY_INSPECTION_OF_TOOLS'
375                 _program_exec_and_record_completion__operation ()
376                 {
377                         local _MSG_CURRENT_STAGE_general
378                         _MSG_CURRENT_STAGE_general="pkg(8) upgrade"
379                         temp_set_msg_current_stage "${_MSG_CURRENT_STAGE_general}"
380                         message_section_title "Upgrade of `pkgsys_portsmgmt_pkg` if new"
381                         touch "${DBDIR}/target_all"
382                         reinstall_exec `pkgsys_portsmgmt_pkg`
383                         reinstall_restore_conflicts
384                         rm -f "${DBDIR}/target_all"
385                         temp_set_msg_current_stage
386                         message_echo
387                 }
388                 program_exec_and_record_completion UPGRADE_PKGNG
389         fi
390 }
391
392 # ============= Upgrade of dialog4ports(1) if new =============
393 # (No need depend on PARSE_CONF because REINSTALLATION will take the task.)
394 main_upgrade_dialog4ports1_if_new ()
395 {
396         local PROGRAM_DEPENDS
397         if [ \( "$COMMAND_MODE" = do -o "$COMMAND_MODE" = redo \) \
398                 -a $opt_dry_run = no -a $opt_suppress_pkgtools_upadte = no ] \
399                 && pkgsys_is_dialog4ports_used
400         then
401                 PROGRAM_DEPENDS='PRELIMINARY_INSPECTION_OF_TOOLS'
402                 _program_exec_and_record_completion__operation ()
403                 {
404                         local _MSG_CURRENT_STAGE_general
405                         _MSG_CURRENT_STAGE_general="dialog4ports(1) upgrade"
406                         temp_set_msg_current_stage "${_MSG_CURRENT_STAGE_general}"
407                         message_section_title "Upgrade of `pkgsys_portsmgmt_dialog4ports` if new"
408                         touch "${DBDIR}/target_all"
409                         reinstall_exec `pkgsys_portsmgmt_dialog4ports`
410                         reinstall_restore_conflicts
411                         rm -f "${DBDIR}/target_all"
412                         temp_set_msg_current_stage
413                         message_echo
414                 }
415                 program_exec_and_record_completion UPGRADE_DIALOG4PORTS
416         fi
417 }
418
419 # ============= Upgrade of this utility if new =============
420 # (No need depend on PARSE_CONF because REINSTALLATION will take the task.)
421 main_self_upgrade ()
422 {
423         local PROGRAM_DEPENDS
424         if [ \( "$COMMAND_MODE" = do -o "$COMMAND_MODE" = redo \) \
425                 -a $opt_dry_run = no -a $opt_suppress_self_upadte = no \
426                 -a -n "$MYPORTORIGIN" ]
427         then
428                 PROGRAM_DEPENDS='PRELIMINARY_INSPECTION_OF_TOOLS'
429                 _program_exec_and_record_completion__operation ()
430                 {
431                         local _MSG_CURRENT_STAGE_general
432                         _MSG_CURRENT_STAGE_general="pkgng upgrade"
433                         temp_set_msg_current_stage "${_MSG_CURRENT_STAGE_general}"
434                         message_section_title "Upgrade of this utility if new"
435                         touch "${DBDIR}/target_all"
436                         reinstall_exec "$MYPORTORIGIN"
437                         reinstall_restore_conflicts
438                         rm -f "${DBDIR}/target_all"
439                         temp_set_msg_current_stage
440                         message_echo
441                 }
442                 program_exec_and_record_completion UPGRADE_SELF
443         fi
444         if [ "x`${APPNAME} -aV 2> /dev/null`" != "x$MYVERSION" ]
445         then
446                 message_echo "INFO: ${APPNAME} is upgraded and the temporary database needs refresh."
447                 database_maintain_clean_for_self_upgrade || :
448                 message_echo "INFO: Restarting with the new version."
449                 message_echo
450                 temp_trap_for_invoking_new_version
451                 . "${TMPDIR}"/restart_command.sh
452                 exit
453         fi
454 }
455
456 # ============= Overlay onto the temporary database by reflecting changes in configuration =============
457 main_reflect_conf_changes ()
458 {
459         local PROGRAM_DEPENDS
460         PROGRAM_DEPENDS='PARSE_CONF'
461         _program_exec_and_record_completion__operation ()
462         {
463                 local tmpfile_diff tmpfile_old tmpfile_new key
464                 [ -d "${DBDIR}/conf.prev" ] || return 0
465                 message_section_title "Overlay onto the temporary database by reflecting changes in configuration"
466                 tmpfile_old=${TMPDIR}/PATCH_TO_TMPDB_REFLECT_CONF_CHANGES::old
467                 tmpfile_new=${TMPDIR}/PATCH_TO_TMPDB_REFLECT_CONF_CHANGES::new
468                 tmpfile_updated_ports=${TMPDIR}/PATCH_TO_TMPDB_REFLECT_CONF_CHANGES::updated_ports
469                 if fileedit_manipulate_old_new_lines \
470                         "${DBDIR}/conf.prev/setenv.sh" "${DBDIR}/conf/setenv.sh" "$tmpfile_old" "$tmpfile_new"
471                 then
472                         if grep -q -e ^LOCALBASE= -e ^LINUXBASE= -e ^PORTSDIR= "$tmpfile_old" "$tmpfile_new"
473                         then
474                                 message_echo "ERROR: Migration of the temporary database is unavailable because LOCALBASE, LINUXBASE or PORTSDIR was changed." >&2
475                                 message_echo "        ${APPNAME} clean" >&2
476                                 message_echo "must be executed in advance." >&2
477                                 exit 1
478                         fi
479                 fi
480                 cut -s -d '|' -f 1,2 "${DBDIR}/conf.prev/MOVED_ALT.parsed" | tr '|' '\t' > ${TMPDIR}/MOVED_ALT.old
481                 cut -s -d '|' -f 1,2 "${DBDIR}/conf/MOVED_ALT.parsed" | tr '|' '\t' > ${TMPDIR}/MOVED_ALT.new
482                 if fileedit_manipulate_old_new_lines \
483                         "${TMPDIR}/MOVED_ALT.old" "${TMPDIR}/MOVED_ALT.new" "$tmpfile_old" "$tmpfile_new"
484                 then
485                         cat "$tmpfile_old" "$tmpfile_new" | while read from to
486                         do
487                                 echo "$from"
488                                 [ -n "$to" ] && echo "$to"
489                         done
490                 fi > $tmpfile_updated_ports
491                 sort -u "${DBDIR}/conf/NOPKG:PORTS.parsed" 2> /dev/null > ${TMPDIR}/NOPKG:PORTS.parsed.new || :
492                 sort -u "${DBDIR}/conf.prev/NOPKG:PORTS.parsed" 2> /dev/null > ${TMPDIR}/NOPKG:PORTS.parsed.old || :
493                 diff "${TMPDIR}/NOPKG:PORTS.parsed.old" "${TMPDIR}/NOPKG:PORTS.parsed.new" | sed -n 's/^[<>] //p' >> $tmpfile_updated_ports
494                 if fileedit_manipulate_old_new_lines \
495                         "${DBDIR}/conf.prev/REPLACE.csv" "${DBDIR}/conf/REPLACE.csv" "$tmpfile_old" "$tmpfile_new"
496                 then
497                         cat "$tmpfile_old" "$tmpfile_new" | while read from to
498                         do
499                                 echo "$from"
500                                 [ -n "$to" ] && echo "$to"
501                         done
502                 fi >> $tmpfile_updated_ports
503                 [ `wc -l < $tmpfile_updated_ports` -gt 0 ] && rm -f "${DBDIR}/REPLACE.complete_sed_pattern"
504                 [ -d "${DBDIR}/conf/each_port" ] && find "${DBDIR}/conf/each_port" -depth 2 \
505                         | while read dbpath
506                 do
507                         origin=`str_dirpath_to_origin "$dbpath"`
508                         dbpath_prev=${DBDIR}/conf.prev/each_port/$origin
509                         diff -r "$dbpath_prev" "$dbpath" > /dev/null 2>&1 && continue
510                         echo "$origin"
511                 done >> $tmpfile_updated_ports
512                 [ -d "${DBDIR}/conf.prev/each_port" ] && find "${DBDIR}/conf.prev/each_port" -depth 2 \
513                         | while read dbpath_prev
514                 do
515                         origin=`str_dirpath_to_origin "$dbpath_prev"`
516                         dbpath=${DBDIR}/conf/each_port/$origin
517                         [ -d "$dbpath" ] && continue
518                         echo "$origin"
519                 done >> $tmpfile_updated_ports
520                 if [ `wc -l < $tmpfile_updated_ports` -gt 0 ]
521                 then
522                         sort -u "$tmpfile_updated_ports" | while read origin
523                         do
524                                 message_echo "Reset for $origin"
525                                 database_build_patch_reconf "$origin"
526                         done
527                         program_deregister_stage_complete PREPARE_FOR_INSPECT_ALL_DEPENDENCIES
528                         program_deregister_stage_complete ALL_COMPLETE
529                 fi
530                 message_echo
531         }
532         program_exec_and_record_completion PATCH_TO_TMPDB_REFLECT_CONF_CHANGES
533 }