Skip to content

Instantly share code, notes, and snippets.

@mspreij
Last active August 4, 2023 12:28
Show Gist options
  • Save mspreij/fd02565929daccb8f6799e926ca319db to your computer and use it in GitHub Desktop.
Save mspreij/fd02565929daccb8f6799e926ca319db to your computer and use it in GitHub Desktop.
########
# Edit Functions live. often I wanted to tweak a function but didn't want to go to the bother of finding out where in my
# home dir it was defined (yeah I should clean my room)
########
ef ()
{
if [[ -z $1 || "$1" == "-?" ]]; then
echo " ef will let you edit already defined functions, or executable bash/php scripts /somewhere/ in \$PATH.";
echo " Function changes will only remain during the session, scripts are overwritten.";
echo " Usage: ef <thing>";
echo " Try 'ef ef' :-)";
return;
fi;
local t=$(type -t $1);
if [[ $t != 'function' && $t != 'file' ]]; then
if [[ -z $t ]]; then
t='(none)';
fi;
echo " '$1' is type: $t; I'm either unwilling or unable to edit that." 1>&2;
return 1;
fi;
if [[ $t == 'function' ]]; then
local func="# '$1' is a function, changes will only be valid for this session
$(type $1 | tail -n+2)";
else
local path="$(type -P $1)";
local func="$(cat "$path")";
fi;
local tmp=$(mktemp);
echo "$func" > $tmp;
while true; do
${EDITOR:-nano} "$tmp";
if [[ "$(cat "$tmp")" != "$func" ]]; then
if [[ $(head -n1 "$tmp") =~ php ]]; then
php -l "$tmp";
else
bash -n "$tmp";
fi;
if [[ $? == 0 ]]; then
if [[ -n $path ]]; then
echo \ \ Writing script to $path ...;
cat "$tmp" > "$path";
else
echo \ \ Storing and sourcing function ...;
cat "$tmp" > ~/src/ef/$1; # so yeah, I'm stashing them here and sourcing these in .bashrc so they DO stick around for next session.
. "$tmp";
fi;
break;
else
echo " The shell did not approve of your modifications :-(";
if [[ -n $path ]]; then
echo " The file I was going to write it back to is: ${path}";
fi;
echo -n " Edit again (y)? ";
read a;
if [[ "$a" != "y" ]]; then
break;
fi;
fi;
else
break;
fi;
done;
rm $tmp
}
########
# EditJson - supply a json file to edit it pretty-printed, it'll be written back compact.
########
#!/usr/bin/env php
<?php
$file = @$argv[1];
if (! $file or $file == '-?' or $file == '-h') {
echo " EditJson, supply a json file to edit it pretty-printed, it'll be written back compact.\n";
return;
}
if (! (file_exists($file) and is_readable($file) and is_writable($file))) {
echo " E: file not found, or not readable, or not writeable.\n"; // you doofus
exit(1);
}
// read the file, check it's valid json
$json = file_get_contents($file);
$data = '';
if (strlen($json) > 0) {
$data = json_decode($json, 1);
if ($error = json_last_error()) {
echo " \e[31mE: json_decode error: ".json_last_error_msg()."\e[0m\n";
exit(2);
}
}
// To avoid overwriting (and changing the timestamp of) the file if it's not needed, we want to compare before and after. Since before could have whitespace in weird places,
// and the prettified before/after can also just differ by linebreaks, use our own json-encoded versions to compare.
$json_orig = json_encode($data);
// put the pretty-printed json in a temp file
$fname = tempnam('/tmp', '');
file_put_contents($fname, json_encode($data, JSON_PRETTY_PRINT));
// edit the temp file, loop back if the saved content is not valid json.
while (1) {
system("nano $fname > $(tty)");
$content = file_get_contents($fname);
if (strlen($content) > 0) {
$data = json_decode($content, 1);
if ($error = json_last_error()) {
echo " \e[31mE: json_decode error: ".json_last_error_msg()."\e[0m\n";
$edit = input(" Edit again (y/n)? ");
if (! in_array($edit, ['y', 'yes', 'ok', 'ja', 'j', 'oui', 'da', 'jawohl'])) {
break;
}
}else{
$json_new = json_encode($data);
if ($json_new !== $json_orig) file_put_contents($file, $json_new); // write back compact json
break;
}
}
}
unlink($fname);
function input(string $prompt = null): string
{
echo $prompt;
$handle = fopen ("php://stdin","r");
$output = fgets ($handle);
return trim ($output);
}
########
# Shortcut wrapper for sshcrypt, https://github.com/leighmcculloch/sshcrypt, which uses the ssh keys from ssh-agent to
# encrypt / decrypt data. You can symlink this file to "decrypt" to make it decrypt.
########
#!/usr/bin/env bash
me=`basename "$0"` # used to check whether we're encrypting or decrypting
if [[ $# -eq 0 ]]; then
# If no arguments were passed, read input from standard input
if [[ ! -t 0 ]]; then
input="$(cat)"
else
echo " ${me} accepts STDIN, or a filename as argument. Output goes to STDOUT."
exit
fi
else
# If one or more arguments were passed, use the first argument as the filename and read the input from that file
filename="$1"
if [[ ! -f "$filename" || ! -r "$filename" ]]; then
echo >&2 " E: file not found or unreadable: '$filename'"
exit 3
fi
input="$(cat "$filename")"
fi
if [[ -z $input ]]; then
echo >&2 " E: no input"
exit 4
fi
if [[ "$me" = encrypt ]]; then
echo "$input" | sshcrypt agent-encrypt
else
echo "$input" | sshcrypt agent-decrypt
fi
########
# a simple basic-info thing for servers that you can run to get a quick overview of possible problems when troubleshooting
########
#!/usr/bin/env bash
header() { echo -e "\e[1;36;4m$1\e[0m"; }
delimiter() { echo ------------------------------; }
run_section() {
header "$1"
eval "$2"
delimiter
}
run_section "Disk" "df -h | grep -v tmp"
run_section "Memory" "free -h"
run_section "Load" "PROCPS_USERLEN=16 PROCPS_FROMLEN=20 w"
run_section "Top processes" "ps aux --sort=-%cpu,-%mem | grep -v 'ps aux --sort=-%cpu,-%mem' | head"
run_section "Diagnostic msg" "dmesg -T | grep -i error"
run_section "Failed services" "[[ \$(systemctl --failed | grep -c 'loaded failed failed') -eq 0 ]] && echo 'No failed services' || systemctl --failed"
header Fail2Ban
if [[ -f /var/log/fail2ban.log ]]; then
fail2bans=$(cat /var/log/fail2ban.log | grep $(date +%F) | grep -e Ban -e Unban)
if [[ -n $fail2bans ]]; then
echo -e " \e[33mFail2bans of today:\e[0m"
echo -n "$fail2bans" | sed 's/^/ /'
echo
else
echo "Clear"
fi;
else
echo "No logs"
fi;
# delimiter
# show ip location info, IF jq is installed that is (not by default)
# curl -s ipinfo.io/213.126.10.250 | jq -r '"(\(.hostname); Location: \(.city), \(.region), \(.country))"'
phplint ()
{
if [[ $1 = '-?' ]]; then
echo " phplint runs php's linter (php -l <file>) on php files in the current and child directories.";
echo " The vendor/ directory is skipped.";
echo " If you need an older version of php:";
echo -e " for file in \$(find . -name '*php' -not -path './vendor/*'); do \e[35;1myourphp\e[0m -l \"\$file\" > /dev/null; done";
echo " That runs the thing for all the php files and discards non-error output.";
return;
fi;
local errors=0
local errMsg=''
local file=''
for file in $(find . -name '*php' -not -path './vendor/*');
do
errMsg=$(php -l "$file" 2>&1);
x=$?;
if [[ "$x" != '0' ]]; then
((errors++));
errMsg=$(echo "$errMsg" | head -n1);
case $errMsg in
*"Parse error"*)
echo -e " \e[31;1m${errMsg}\e[0m"
;;
*"Deprecated"*)
echo -e " \e[38;5;208m${errMsg}\e[0m"
;;
# add more???
esac;
fi;
done;
if [[ $errors > 0 ]]; then
echo ' '$(php -v | head -n1);
echo " Found $errors error(s).";
return 1;
else
echo ' Looks good to me!';
fi
}
########
# our network is shitty :-( this logs outages, another script then reads those logs to tell me why the backups failed.
########
#!/usr/bin/env bash
# stuff this in crontab, erry minute, give a hostname as argument to ping, then check ~/log/pingbox_<host>.log the next day
# if it's a longterm thing, add a logrotate entry with daily, nocompress, rotate 14 or whatever
if [[ -z $1 ]]; then
echo " pingbox requires a box to ping. result will be logged in ~/log/pingbox.<box>.log"
exit
fi
if [[ ! -d ~/log ]]; then
mkdir ~/log
fi
function ts {
date '+[%Y-%m-%d %H:%M:%S] '
}
result=`ping -c1 -W1 "$1"`
match='1 received'
if [[ $result =~ $match ]]; then
echo `ts` up: $1 >> ~/log/pingbox_${1}.log
else
echo `ts` DOWN $1 >> ~/log/pingbox_${1}.log
fi
########
# pre-commit hook that checks for the string 'NO_COMMIT', which I comment lines that shouldn't be committed with.
########
#!/usr/bin/env php
<?php
$diff = `git diff --cached -w`; // --cached, because git stages files before committing them
// We're looking for NO_COMMIT lines that were added (start with '+'), but then want to know the files they're in, so simply grepping won't work.
// Instead we'll loop over the diff checking each line, and remembering filenames
$found = 0;
$linenr = 0;
$file = '';
$old_file = '';
$out = '';
foreach(explode("\n", trim($diff)) as $line) {
$linenr++;
if (substr($line, 0, 6) === '+++ b/') {
$file = substr($line, 6);
}elseif (substr($line, 0, 2) === '@@') {
preg_match('/@@ -(\d+),/', $line, $matches);
$linenr = $matches[1] - 2; // -1 because the next line is this line number, and -1 because the extra '-' line sits before the '+'
}elseif (preg_match('/^\+.*NO_COMMIT/', $line)) {
$found = 1;
if ($file != $old_file) {
$out .= " \e[38;5;208m$file\e[0m\n";
}
$out .= " $linenr: ".substr(str_replace('NO_COMMIT', "\e[31mNO_COMMIT\e[0m", $line), 1)."\n";
$old_file = $file;
}
}
if ($found) {
echo "\e[31;1mCommit aborted:\e[0m\n";
echo $out;
}
exit($found);
########
# RemoteRun, run a local script on a remote host as root
# requires .ssh/config hosts, agent forwarding and remote root needs to be able to ssh in from localhost (on the remote that is)
########
#!/usr/bin/env bash
# if no file was given, or the first arg was -?/h, show help and exit
if [[ -z "$2" || "$1" == '-?' || "$1" == '-h' ]]; then
echo ' r[emote] run: run a local script on a remote host.'
echo ' Usage: rrun <host> <scriptfile> [args..]'
echo -e ' $scriptfile will be copied to $host:~/tmp/, made executable, and run \e[31;4mas root\e[0m, with args if given.'
echo ' SSH config needs to be correct, and allow agent-forwarding'.
exit
fi;
if [[ ! -r "$2" || ! -f "$2" ]]; then
echo " E: script file not readable or not found"
exit 1
fi;
host=$1
file=$2
# get rid of the first two args so the rest, if used, can be passed to the script
shift 2
# the remote filename gets a timestamp prepended (to avoid writing over other files or post-mortems etc)
rscript=/home/myuser/tmp/$(date '+%Y%m%d.%H%M%S')_"$(basename "$file")"
# scp is not perfect, so make checksums to compare, just in case..
sum=$(md5sum "$file" | awk '{print $1}')
# copy the file to host:~/tmp/
echo copying $file to $host..
scp "$file" $host:"$rscript"
# compare remote checksum with local
sum2=$(ssh -n $host md5sum "$rscript" | awk '{print $1}')
if [[ "$sum" != "$sum2" ]]; then
echo "md5 for the files differ, scp/network issue? Bailing."
exit 2
fi;
# make script executable, and run it as root user
ssh -n $host chmod +x "$rscript"
ssh -n -A $host ssh root@localhost "$rscript" $@;
# remove script
ssh -n $host rm "$rscript"
########
# rclone wrapper to mount/unmount remotes
########
#!/usr/bin/env bash
me=$(basename "$0") # up or down (make a symlink for down to up)
if [[ -z $1 ]]; then
echo " up/down wrap rclone to [un]mount remotes in ~/mnt. Pass an existing configuration name as parameter.";
exit;
fi;
server="$1";
# check we have a config for it
grep -F "[$server]" ~/.config/rclone/rclone.conf > /dev/null;
if [[ $? -gt 0 ]]; then
echo "No config found for '$server'";
exit;
fi
targetdir="/home/user/mnt/$server";
# up
if [[ "$me" == up ]]; then
if [[ -d "$targetdir" ]]; then
content="$(ls "$targetdir")"
if [[ -n "$content" ]]; then
echo " Warning: existing mount point '$targetdir' has content";
fi;
else
mkdir "$targetdir";
fi;
rclone mount "$server":/ ~/mnt/"$server" &
if [[ $? = 0 ]]; then
echo " Mounted $server in ~/mnt/$server"
else
echo " rclone exit status was not zero"
fi
# down
else
if [[ ! -d "$targetdir" ]]; then
echo " '$server' is not mounted."
exit
fi
sudo umount "$targetdir"
if [[ -z "$(ls "$targetdir")" ]]; then
rmdir "$targetdir"
fi
fi
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment