epm-sh-functions 13.1 KB
Newer Older
Vitaly Lipatov's avatar
Vitaly Lipatov committed
1 2
#!/bin/sh
#
3 4
# Copyright (C) 2012, 2014  Etersoft
# Copyright (C) 2012, 2014  Vitaly Lipatov <lav@etersoft.ru>
Vitaly Lipatov's avatar
Vitaly Lipatov committed
5
#
6 7 8
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
Vitaly Lipatov's avatar
Vitaly Lipatov committed
9 10 11 12 13
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14
# GNU Affero General Public License for more details.
Vitaly Lipatov's avatar
Vitaly Lipatov committed
15
#
16 17
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
Vitaly Lipatov's avatar
Vitaly Lipatov committed
18 19
#

20
# copied from /etc/init.d/outformat (ALT Linux)
21

22 23
check_core_commands()
{
24 25 26 27 28
	#which --help >/dev/null || fatal "Can't find which command (which package is missed?)"
	# broken which on Debian systems
	which which >/dev/null || fatal "Can't find which command (which or debianutils package is missed?)"
	which grep >/dev/null || fatal "Can't find grep command (coreutils package is missed?)"
	which sed >/dev/null || fatal "Can't find sed command (sed package is missed?)"
29 30 31
}


32
# FIXME on Android: FIX ME! implement ttyname_r() bionic/libc/bionic/stubs.c:366
33 34 35
inputisatty()
{
	# check stdin
36 37
	#tty -s 2>/dev/null
	test -t 0
38 39
}

40 41
isatty()
{
42 43 44 45 46 47
	# check stdout
	test -t 1
}

isatty2()
{
Vitaly Lipatov's avatar
Vitaly Lipatov committed
48 49
	# check stderr
	test -t 2
50 51
}

52 53
check_tty()
{
54 55 56 57 58 59
	isatty2 || return

	# Set a sane TERM required for tput
	[ -n "$TERM" ] || TERM=dumb
	export TERM

60 61
	check_core_commands

Vitaly Lipatov's avatar
Vitaly Lipatov committed
62 63
	# grep -E from busybox may not --color
	# grep -E from MacOS print help to stderr
Vitaly Lipatov's avatar
Vitaly Lipatov committed
64
	if grep -E --help 2>&1 | grep -q -- "--color" ; then
65
		export EGREPCOLOR="--color"
66 67
	fi

68 69 70 71
	which tput >/dev/null 2>/dev/null || return
	# FreeBSD does not support tput -S
	echo | tput -S >/dev/null 2>/dev/null || return
	[ -z "$USETTY" ] || return
72 73 74
	export USETTY=1
}

75 76 77 78
: ${BLACK:=0} ${RED:=1} ${GREEN:=2} ${YELLOW:=3} ${BLUE:=4} ${MAGENTA:=5} ${CYAN:=6} ${WHITE:=7}

set_boldcolor()
{
79
	[ "$USETTY" = "1" ] || return
80 81 82 83 84 85 86 87
	{
		echo bold
		echo setaf $1
	} |tput -S
}

restore_color()
{
88
	[ "$USETTY" = "1" ] || return
89 90 91 92 93 94
	{
		echo op; # set Original color Pair.
		echo sgr0; # turn off all special graphics mode (bold in our case).
	} |tput -S
}

Vitaly Lipatov's avatar
Vitaly Lipatov committed
95 96
echover()
{
Vitaly Lipatov's avatar
Vitaly Lipatov committed
97
    [ -z "$verbose" ] && return
98
    echo "$*" >&2
Vitaly Lipatov's avatar
Vitaly Lipatov committed
99 100
}

101 102 103
# echo string without EOL
echon()
{
Vitaly Lipatov's avatar
Vitaly Lipatov committed
104
	# default /bin/sh on MacOS does not recognize -n
Vitaly Lipatov's avatar
Vitaly Lipatov committed
105
	/bin/echo -n "$*"
106 107 108
}


Vitaly Lipatov's avatar
Vitaly Lipatov committed
109 110 111
# Used DISTRNAME
set_target_pkg_env()
{
Vitaly Lipatov's avatar
Vitaly Lipatov committed
112
	[ -n "$DISTRNAME" ] || fatal "Missing DISTRNAME in set_target_pkg_env."
113 114 115 116 117
	local ver="$DISTRVERSION"
	[ -n "$ver" ] && ver="/$ver"
	PKGFORMAT=$($DISTRVENDOR -p "$DISTRNAME$ver")
	PKGVENDOR=$($DISTRVENDOR -s "$DISTRNAME$ver")
	RPMVENDOR=$($DISTRVENDOR -n "$DISTRNAME$ver")
Vitaly Lipatov's avatar
Vitaly Lipatov committed
118 119 120
}

# Print command line and run command line
121
showcmd()
Vitaly Lipatov's avatar
Vitaly Lipatov committed
122
{
123 124 125
	if [ -z "$quiet" ] ; then
		set_boldcolor $GREEN
		local PROMTSIG="\$"
126
		is_root && PROMTSIG="#"
Vitaly Lipatov's avatar
Vitaly Lipatov committed
127
		echo " $PROMTSIG $*"
128
		restore_color
129
	fi >&2
130 131 132 133 134
}

# Print command line and run command line
docmd()
{
Vitaly Lipatov's avatar
Vitaly Lipatov committed
135
	showcmd "$*$EXTRA_SHOWDOCMD"
136 137
#FIXME
	$@
Vitaly Lipatov's avatar
Vitaly Lipatov committed
138 139
}

140
# Run every arg with docmd
141 142
docmd_foreach()
{
143
	local cmd pkg
144 145 146 147
	cmd="$1"
	#showcmd "$@"
	shift
	for pkg in "$@" ; do
148
		docmd "$cmd" $pkg
149 150 151
	done
}

152 153 154 155
# run command line with SUDO
sudorun()
{
	set_sudo
Vitaly Lipatov's avatar
Vitaly Lipatov committed
156 157 158 159 160
	if [ -z "$SUDO" ] ; then
		"$@"
		return
	fi
	$SUDO "$@"
161 162
}

163
# Print command line and run command line with SUDO
164
sudocmd()
165
{
166
	set_sudo
167
	[ -n "$SUDO" ] && showcmd "$SUDO $*" || showcmd "$*"
168
	sudorun "$@"
169 170
}

171
# Run every arg with sudocmd
172
# Returns on any error
173 174
sudocmd_foreach()
{
175
	local cmd pkg
176 177 178 179
	cmd="$1"
	#showcmd "$@"
	shift
	for pkg in "$@" ; do
180 181
		# don't quote $cmd here: it can be a command with an args
		sudocmd $cmd $pkg || return
182 183 184
	done
}

185 186 187 188 189 190 191 192
# add realpath if missed
if ! which realpath 2>/dev/null >/dev/null ; then
realpath()
{
	readlink -f "$@"
}
fi

193 194 195 196 197 198 199 200 201 202 203
# print full path to files
make_filepath()
{
	local i
	for i in "$@" ; do
		[ -f "$i" ] || continue
		echo "$i" | grep -q "/" && echo "$i" && continue
		echo "./$i"
	done
}

204 205
get_firstarg()
{
206
	echon "$1"
207 208 209 210 211
}

get_lastarg()
{
	local lastarg
Vitaly Lipatov's avatar
Vitaly Lipatov committed
212
	eval "lastarg=\${$#}"
213
	echon "$lastarg"
214 215
}

216 217 218 219 220 221
# TODO: see etersoft-build-utils/tests/test_isnumber.sh
isnumber()
{
	echo "$*" | filter_strip_spaces | grep -q "^[0-9]\+$"
}

Vitaly Lipatov's avatar
Vitaly Lipatov committed
222 223
# copied from strings
# CHECKME: the same like estrlist has ?
Vitaly Lipatov's avatar
Vitaly Lipatov committed
224
# Note: used grep -E! write '[0-9]+(first|two)', not '[0-9]\+...'
Vitaly Lipatov's avatar
Vitaly Lipatov committed
225 226 227 228 229 230 231 232 233 234 235 236
rhas()
{
	echo "$1" | grep -E -q -- "$2"
}

# copied from strings
is_dirpath()
{
    [ "$1" = "." ] && return $?
    rhas "$1" "/"
}

Vitaly Lipatov's avatar
Vitaly Lipatov committed
237 238 239 240 241 242 243 244 245 246 247 248 249
filter_strip_spaces()
{
        # possible use just
        #xargs echo
        sed -e "s| \+| |g" | \
                sed -e "s|^ ||" | sed -e "s| \$||"
}

strip_spaces()
{
        echo "$*" | filter_strip_spaces
}

250 251 252 253 254 255
# param true false
subst_option()
{
	eval "[ -n \"\$$1\" ]" && echo "$2" || echo "$3"
}

256 257 258 259
store_output()
{
    # use make_temp_file from etersoft-build-utils
    RC_STDOUT=$(mktemp)
260 261
    local CMDSTATUS=$RC_STDOUT.pipestatus
    echo 1 >$CMDSTATUS
262
    #RC_STDERR=$(mktemp)
263
    ( LANG=C $@ 2>&1 ; echo $? >$CMDSTATUS ) | tee $RC_STDOUT
Vitaly Lipatov's avatar
Vitaly Lipatov committed
264
    return "$(cat $CMDSTATUS)"
265
    # bashism
266
    # http://tldp.org/LDP/abs/html/bashver3.html#PIPEFAILREF
267
    #return $PIPESTATUS
268 269
}

270 271 272 273 274 275
showcmd_store_output()
{
    showcmd "$@"
    store_output "$@"
}

276 277
clean_store_output()
{
278
    rm -f $RC_STDOUT $RC_STDOUT.pipestatus
279 280
}

281
# run epm, possible from side repo
282 283
epm()
{
284
	[ -n "$PROGNAME" ] || fatal "Can't use epm call from the piped script"
285 286 287 288 289 290 291 292
	$PROGDIR/$PROGNAME --inscript "$@"
}

# run $SUDO epm, possible from side repo
sudoepm()
{
	[ -n "$PROGNAME" ] || fatal "Can't use epm call from the piped script"
	sudorun $PROGDIR/$PROGNAME --inscript "$@"
293
}
Vitaly Lipatov's avatar
Vitaly Lipatov committed
294 295 296 297 298

# Print error message and stop the program
fatal()
{
	if [ -z "$TEXTDOMAIN" ] ; then
Vitaly Lipatov's avatar
Vitaly Lipatov committed
299
		echo "Error: $*" >&2
Vitaly Lipatov's avatar
Vitaly Lipatov committed
300 301
#	else
#		echog "Error in $0: $@" >&2
Vitaly Lipatov's avatar
Vitaly Lipatov committed
302
	fi
303
#	[ "$TERM" = "screen" ] && echo "(screen detected: waiting ten seconds to exit ...)" >&2 && sleep 10
Vitaly Lipatov's avatar
Vitaly Lipatov committed
304 305
	exit 1
}
306

Vitaly Lipatov's avatar
Vitaly Lipatov committed
307 308 309 310
# Print warning message
warning()
{
	if [ -z "$TEXTDOMAIN" ] ; then
Vitaly Lipatov's avatar
Vitaly Lipatov committed
311
		echo "Warning: $*" >&2
Vitaly Lipatov's avatar
Vitaly Lipatov committed
312 313 314 315 316
#	else
#		echog "Error in $0: $@" >&2
	fi
}

317 318 319 320
info()
{
	[ -n "$quiet" ] && return

321 322 323
	# print message to stderr if stderr forwarded to (a file)
	if isatty2 ; then
		isatty || return 0
Vitaly Lipatov's avatar
Vitaly Lipatov committed
324
		echo "$*"
325
	else
Vitaly Lipatov's avatar
Vitaly Lipatov committed
326
		echo "$*" >&2
327
	fi
328 329
}

330 331 332
# if we have not sudo, returns 1 and set SUDO variable to fatal
SUDO_TESTED=''
SUDO_CMD='sudo'
333 334
set_sudo()
{
335 336 337 338 339 340
	local nofail="$1"

	# cache the result
	[ -n "$SUDO_TESTED" ] && return "$SUDO_TESTED"
	SUDO_TESTED="0"

341 342 343
	SUDO=""
	# skip SUDO if disabled
	[ -n "$EPMNOSUDO" ] && return
344 345 346 347
	if [ "$DISTRNAME" = "Cygwin" ] || [ "$DISTRNAME" = "Windows" ] ; then
		# skip sudo using on Windows
		return
	fi
348

349
	# if we are root, do not need sudo
350
	is_root && return
351

352 353 354
	# start error section
	SUDO_TESTED="1"

355
	if ! which $SUDO_CMD >/dev/null 2>/dev/null ; then
356 357
		[ "$nofail" = "nofail" ] || SUDO="fatal 'Can't find sudo. Please install and tune sudo ('# epm install sudo') or run epm under root.'"
		return "$SUDO_TESTED"
358
	fi
359

360 361
	# if input is a console
	if inputisatty && isatty && isatty2 ; then
362
		if ! $SUDO_CMD -l >/dev/null ; then
363
			[ "$nofail" = "nofail" ] || SUDO="fatal 'Can't use sudo (only passwordless sudo is supported in non interactive using). Please run epm under root.'"
364
			return "$SUDO_TESTED"
365 366 367
		fi
	else
		# use sudo if one is tuned and tuned without password
368
		if ! $SUDO_CMD -l -n >/dev/null 2>/dev/null ; then
369
			[ "$nofail" = "nofail" ] || SUDO="fatal 'Can't use sudo (only passwordless sudo is supported). Please run epm under root or check http://altlinux.org/sudo.'"
370
			return "$SUDO_TESTED"
371
		fi
372 373
	fi

374
	SUDO_TESTED="0"
375
	SUDO="$SUDO_CMD --"
376
	# check for < 1.7 version which do not support -- (and --help possible too)
377
	$SUDO_CMD -h 2>/dev/null | grep -q "  --" || SUDO="$SUDO_CMD"
378

379 380
}

381 382 383 384 385 386
# return TRUE if we can run privileged command
sudo_allowed()
{
	set_sudo nofail
}

387 388
# wait for n seconds (if possible) during executing command
# args: seconds command
389 390 391
withtimeout()
{
	local TO=$(which timeout 2>/dev/null || which gtimeout 2>/dev/null)
Vitaly Lipatov's avatar
Vitaly Lipatov committed
392
	if [ -x "$TO" ] ; then
393
		$TO "$@"
Vitaly Lipatov's avatar
Vitaly Lipatov committed
394 395
		return
	fi
396
	fatal "Possible indefinite wait due timeout command is missed"
Vitaly Lipatov's avatar
Vitaly Lipatov committed
397
	# fallback: drop time arg and run without timeout
398 399
	#shift
	#"$@"
400 401
}

402 403
set_eatmydata()
{
404 405
	# don't use eatmydata (useless)
	return 0
406 407 408 409
	# skip if disabled
	[ -n "$EPMNOEATMYDATA" ] && return
	# use if possible
	which eatmydata >/dev/null 2>/dev/null || return
410
	set_sudo
411
	# FIXME: check if SUDO already has eatmydata
412
	[ -n "$SUDO" ] && SUDO="$SUDO eatmydata" || SUDO="eatmydata"
413
	[ -n "$verbose" ] && info "Uwaga! eatmydata is installed, we will use it for disable all sync operations."
Vitaly Lipatov's avatar
Vitaly Lipatov committed
414
	return 0
415 416
}

417 418
# 
__get_package_for_command()
419
{
420
	case "$1" in
421
		equery|revdep-rebuild)
422
			echo 'gentoolkit'
423
			;;
424
		update-kernel|remove-old-kernels)
425
			echo 'update-kernel'
426 427 428 429
			;;
	esac
}

430 431 432 433 434 435 436 437 438 439 440 441 442 443 444
# TODO:
confirm() {
    local response
    # call with a prompt string or use a default
    read -r -p "${1:-Are you sure? [y/N]} " response
    case $response in
        [yY][eE][sS]|[yY])
            true
            ;;
        *)
            false
            ;;
    esac
}

445 446 447 448 449 450 451 452 453 454 455

confirm_info()
{
	info "$*"
	if [ -z "$non_interactive" ] ; then
		confirm "Are you sure? [y/N]" || fatal "Exiting"
	fi

}


456 457 458 459 460 461
is_root()
{
	local EFFUID="$(id -u)"
	[ "$EFFUID" = "0" ]
}

462 463
assure_root()
{
464
	is_root || fatal "run me only under root"
465 466 467 468 469 470 471 472 473
}

regexp_subst()
{
	local expression="$1"
	shift
	sed -i -r -e "$expression" "$@"
}

Vitaly Lipatov's avatar
Vitaly Lipatov committed
474
# TODO: we we can't use epm directly?
475 476 477
assure_exists()
{
	load_helper epm-assure
Vitaly Lipatov's avatar
Vitaly Lipatov committed
478
	local package="$2"
479
	local textpackage=
Vitaly Lipatov's avatar
Vitaly Lipatov committed
480
	[ -n "$package" ] || package="$(__get_package_for_command "$1")"
481
	[ -n "$3" ] && textpackage=" >= $3"
482
	( direct='' epm_assure "$1" $package $3 ) || fatal "Can't assure in '$1' command from $package$textpackage package"
483 484
}

485
# will replaced within disabled_eget in packaged version
Vitaly Lipatov's avatar
Vitaly Lipatov committed
486
eget()
487
{
488
	local EGET
489 490 491 492 493
	# use internal eget only if exists
	if [ -s $SHAREDIR/tools_eget ] ; then
		$SHAREDIR/tools_eget "$@"
		return
	fi
494
	fatal "Internal error: missed tools_eget"
495

496
	# FIXME: we need disable output here, eget can be used for get output
497
	assure_exists eget eget 3.3 >/dev/null
498 499 500 501 502
	# run external command, not the function
	EGET=$(which eget) || fatal "Missed command eget from installed package eget"
	$EGET "$@"
}

Vitaly Lipatov's avatar
Vitaly Lipatov committed
503 504 505 506 507 508 509 510 511 512 513 514 515 516
estrlist()
{
	if [ -s $SHAREDIR/tools_estrlist ] ; then
		$SHAREDIR/tools_estrlist "$@"
		return
	fi
	fatal "missed tools_estrlist"
}

onefile_estrlist()
{
	internal_tools_estrlist "$@"
}

517 518
# will replaced within eget() in packed version
onefile_eget()
Vitaly Lipatov's avatar
Vitaly Lipatov committed
519
{
Vitaly Lipatov's avatar
Vitaly Lipatov committed
520
	assure_exists wget
521
	internal_tools_eget "$@"
Vitaly Lipatov's avatar
Vitaly Lipatov committed
522 523
}

524
# TODO: improve and drop!
525 526 527
get_package_type()
{
	local i
528 529 530 531 532 533 534 535 536
	case $1 in
		*.deb)
			echo "deb"
			return
			;;
		*.rpm)
			echo "rpm"
			return
			;;
537 538 539 540 541 542 543 544
		*.txz)
			echo "txz"
			return
			;;
		*.tbz)
			echo "tbz"
			return
			;;
545 546 547 548
		*.exe)
			echo "exe"
			return
			;;
549 550 551 552
		*.msi)
			echo "msi"
			return
			;;
553 554 555 556
		*.AppImage)
			echo "AppImage"
			return
			;;
557 558 559 560 561 562 563
		*)
			#fatal "Don't know type of $1"
			# return package name for info
			echo "$1"
			return 1
			;;
	esac
564 565 566
}


567
# print options description from HELPCMD/HELPOPT lines in the code
568
# args: section_name, [file with code]
569 570
get_help()
{
571 572 573
    if [ "$0" = "/dev/stdin" ] || [ "$0" = "sh" ] ; then
        return
    fi
574
    local F="$0"
Vitaly Lipatov's avatar
Vitaly Lipatov committed
575 576 577
    if [ -n "$2" ] ; then
        is_dirpath "$2" && F="$2" || F="$(dirname $0)/$2"
    fi
578

579
    cat "$F" | grep -- "# $1" | while read -r n ; do
Vitaly Lipatov's avatar
Vitaly Lipatov committed
580 581 582 583 584 585
        if echo "$n" | grep -q "# $1: PART: " ; then
            echo
            echo "$n" | sed -e "s|# $1: PART: ||"
            continue
        fi
        echo "$n" | grep -q "^ *#" && continue
586 587 588
        opt=`echo $n | sed -e "s|) # $1:.*||g" -e 's|"||g' -e 's@^|@@'`
        desc=`echo $n | sed -e "s|.*) # $1:||g"`
        printf "    %-20s %s\n" "$opt" "$desc"
589 590 591
    done
}

592 593
# TODO: get all info by one request (too slow)
set_distro_info()
594
{
595
	# use external distro_info if internal one is missed
596
	DISTRVENDOR=$PROGDIR/distr_info
597
	[ -x $DISTRVENDOR ] || DISTRVENDOR=distro_info
598
	export DISTRVENDOR
599

600
	[ -n "$DISTRNAME" ] || DISTRNAME=$($DISTRVENDOR -d) || fatal "Can't get distro name."
601
	[ -n "$DISTRVERSION" ] || DISTRVERSION=$($DISTRVENDOR -v)
602
	if [ -z "$DISTRARCH" ] ; then
Vitaly Lipatov's avatar
Vitaly Lipatov committed
603
		DISTRARCH=$($DISTRVENDOR --distro-arch)
604
	fi
605
	DISTRCONTROL="$($DISTRVENDOR -y)"
606 607 608 609 610 611

	# TODO: improve BIGTMPDIR conception
	# https://bugzilla.mozilla.org/show_bug.cgi?id=69938
	# https://refspecs.linuxfoundation.org/FHS_3.0/fhs/ch05s15.html
	# https://geekpeach.net/ru/%D0%BA%D0%B0%D0%BA-systemd-tmpfiles-%D0%BE%D1%87%D0%B8%D1%89%D0%B0%D0%B5%D1%82-tmp-%D0%B8%D0%BB%D0%B8-var-tmp-%D0%B7%D0%B0%D0%BC%D0%B5%D0%BD%D0%B0-tmpwatch-%D0%B2-centos-rhel-7
	[ -n "$BIGTMPDIR" ] || [ -d "/var/tmp" ] && BIGTMPDIR="/var/tmp" || BIGTMPDIR="/tmp"
612 613 614 615 616 617 618
}

# FIXME: detect if not recognized
set_pm_type()
{
	local CMD
	set_distro_info
619 620
	set_target_pkg_env

621 622 623 624 625 626
# override package manager detection result
if [ -n "$FORCEPM" ] ; then
	PMTYPE=$FORCEPM
	return
fi

627
	PMTYPE="$($DISTRVENDOR -g $DISTRNAME/$DISTRVERSION)"
628
}
629

630 631
is_active_systemd()
{
632
	[ "$DISTRCONTROL" = "systemd" ]
633
}
634 635 636 637 638 639 640

assure_distr()
{
	local TEXT="this option"
	[ -n "$2" ] && TEXT="$2"
	[ "$DISTRNAME" = "$1" ] || fatal "$TEXT supported only for $1 distro"
}
641 642 643 644 645 646 647 648 649 650

# return delimiter sign in depend of package type
get_pkg_name_delimiter()
{
   local pkgtype="$1"
   [ -n "$pkgtype" ] || pkgtype="$($DISTRVENDOR -p)"

   [ "$pkgtype" = "deb" ] && echo "_" && return
   echo "-"
}
651 652 653

has_space()
{
654
    estrlist -- has_space "$@"
655
}