initial commit [141.14.140.180,mike]

This commit is contained in:
2026-08-10 16:12:09 +02:00
commit c534ea1c79
66 changed files with 9244 additions and 0 deletions
+3
View File
@@ -0,0 +1,3 @@
ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQC9kb7R4qAqRdPw90zxM9dg68bnskdgqAZLSaogAKSpcvsmVNDgz5XKAr22hg25VrhJdE4S8ptvAyDT3n18yYmqv0Bht7t/uHWUd+pzJHy5d763nII7ukQtKBZdLaLEqSGD1XkyOTj21MZiiCaNurU6pqbcx3mlfVGtH0SdBCs8Nz6tYVUC/jCn202LM5tmHakLgYBvU4QPb6nfNMGKvNVjxUCs1Vn+knPsUtiIi6ktkz8LHRSx8Qd9vlTLMccsnSm/FfaepVPLslQ2rA7fpixhWIH/sJizcTubB1ErA8BhDM7exD+UEfmN7CKvarFZxDf3WgckonIGuoS81wT+cZDT m
ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQCoqI8A+SES/kWp6ZqJ6agbk8lz7UhOIZZrSF1cnuv+9bCq2QtO5qkAalJSr5hNZXsEZl9OfQibcFk7GUWTaEHCg/vdTxfMUdWDQDkOyPNPQD2JRZ3NpcdtkteSEfYzPwyXFV3HSoQys6VQAib9SeMyDtTgFPN+xm2lQtVzGiFg73ny5Lq1PsaS40+6EwGZmMIfUQLVm5OjislDW1p+F1PyYUeM9wiK/KbJl99Pd5CECXN9uunAEOz/siDwpo1TXZ6od6ZKMCGDT3v742cc28HpHxK/GQ9ezXlaTZ4BldTtkoupK6KwxOZ8x/eyiGq00E/YdjtxhLi6ggmjlBk2Gk33 root@data
+49
View File
@@ -0,0 +1,49 @@
unsetenv LANG
setenv HN `hostname`
set prompt="%n@${HN}:%~ > "
if ( $USER == root ) then
set prompt="%n@${HN}:%~ >> "
endif
set history=128
set savehist=128
set watch=( any any )
set recexact
set autolist
set dunique
set matchbeep=nomatch
set pushdsilent
set fignore=( .o )
set echo_style=both
unset ignoreeof
unset mail
set notify
set time=( 1 " %E [%P] used." )
alias a 'alias'
alias h 'history'
alias L 'last -16'
alias ls 'ls -F'
alias ll 'ls -lsah'
alias lt 'ls -ltrah'
alias cd 'cd \!*;echo $cwd'
alias pwd 'echo $cwd'
alias s 'ssh'
alias sr 'ssh -l root'
alias rs 'rsync -va --delete --stats'
alias p 'ps -ef | grep ${LOGNAME}'
alias P 'ps -ef'
alias af 'apt-cache search'
alias ai 'apt-get install'
alias sd 'lsblk -o NAME,FSTYPE,SIZE,MOUNTPOINT,LABEL'
alias ga 'git add -v .'
alias gb 'git branch'
alias gs 'git status'
alias gl "git log -32 --reverse --date=local --format='%h %cd %s'"
alias gc 'git commit -m'
alias go 'git checkout'
alias gp 'git push'
setenv PATH "$PATH":/db/bin
+83
View File
@@ -0,0 +1,83 @@
# joe theme — Palette aus mwxcol.go
#
# white #F0F0EA pink #F5B2F7
# yellow #F9E2AF violet #CBA6F7
# orange #FAB387 grey #AAAABB
# red #F38BA8 dark #777788
# green #94E2D5 darker #444455
# blue #B4BEFE
#
# Ort: ~/.joe/colors/default.jcf (verdeckt die System-Version)
# Getestet mit joe 4.6.
.colors *
.set white $F0F0EA
.set yellow $F9E2AF
.set orange $FAB387
.set red $F38BA8
.set green $94E2D5
.set blue $B4BEFE
.set pink $F5B2F7
.set violet $CBA6F7
.set grey $AAAABB
.set dark $777788
.set darker $444455
-text [white]
# --- Syntax -----------------------------------------------------------
=Bad [red]
=Preproc [orange]
=Define [orange]
=String [red]
=Comment [grey]
=IncLocal [green]
=IncSystem [green] bold
=Constant [red]
=Escape [green] bold
=Type [violet]
=Keyword [pink]
=Variable [green]
=Brace [white] bold
=Tag [blue] bold
=TagName +Tag
=Title bold
=TODO [yellow]
=BUG [yellow]
# --- UI ---------------------------------------------------------------
-status [white]/[darker]
-selection /[darker]
-linum [dark]
-curlinum [yellow] bold
-menu [white]/[darker]
-menusel [darker]/[blue]
# --- diff -------------------------------------------------------------
=diff.AddLine [green]
=diff.DelLine [red]
=diff.ChgLine [yellow]
=diff.Garbage [darker]
=diff.FileOld [red] bold
=diff.FileNew [green] bold
=diff.Hunk [violet] bold
# Identitäts-Mapping wie bisher: numerische Farben aus alten Syntax-Files
# greifen auf die 16 Farben des Terminals durch.
-term 0 0
-term 1 1
-term 2 2
-term 3 3
-term 4 4
-term 5 5
-term 6 6
-term 7 7
-term 8 8
-term 9 9
-term 10 10
-term 11 11
-term 12 12
-term 13 13
-term 14 14
-term 15 15
Executable
+95
View File
@@ -0,0 +1,95 @@
#!/usr/bin/perl
######################################################################## count files and directories (mwx'2016)
use Getopt::Std;getopts('dnh');
$version='1.0.2';
if ($opt_h) {;&help;exit;}
if (-d $ARGV[0]) {
&dx($ARGV[0]);
} else {
&dx(".");
}
sub dx() {
my($dir)=@_;
$ml=length($dir);
opendir(DIR,$dir);
while($f=readdir(DIR)) {
if ($f ne '.' && $f ne '..') {
if (-d "$dir/$f") {
$n=0;$s=0;
open(IN,"find '$dir/$f' -type f |");
while(<IN>) {
chomp();
$s+=(stat($_))[7];
$ts+=(stat($_))[7];
$n++;
$tn++;
}
close(IN);
$N{"$f/"}=$n;
$S{"$f/"}=$s;
$ml=length($f) if (length($f)>$ml);
}
if (-f "$dir/$f" && !$opt_d) {
$S{$f}+=(stat("$dir/$f"))[7];
$ts+=(stat("$dir/$f"))[7];
$N{$f}++;
$tn++;
$ml=length($f) if (length($f)>$ml);
}
}
}
closedir(DIR);
$frm=sprintf "%%-%ds",$ml+1;
$out=sprintf "$frm %8d %8s\n",$dir,$tn,&formsize($ts);
if ($opt_n) {
for $f (sort { $N{$a} <=> $N{$b} } (keys(%N))) {
$out.=sprintf " $frm %8d %8s\n",$f,$N{$f},&formsize($S{$f});
}
} else {
for $f (sort { $S{$a} <=> $S{$b} } (keys(%S))) {
$out.=sprintf " $frm %8d %8s\n",$f,$N{$f},&formsize($S{$f});
}
}
print $out;
}
####################################################################################################### SUPPORT
sub formsize() {
my($b)=@_;
if ($b>1024*1024*1024*1024) {
$s=sprintf("%.0fT",$b/1024/1024/1024/1024)
} elsif ($b>1024*1024*1024) {
$s=sprintf("%.0fG",$b/1024/1024/1024)
} elsif ($b>1024*1024) {
$s=sprintf("%.0fM",$b/1024/1024)
} elsif ($b>1024) {
$s=sprintf("%.0fK",$b/1024)
} else {
$s=sprintf("%.0f",$b)
}
return $s;
}
sub help() {
print <<"ENDOFHELP";
dx ${version}, count files and directories, mwx'2016
usage: dx [-n] [-d] [path]
-n sort by number of files (default by size)
-d count only directories
-h this help
ENDOFHELP
}
########################################################################################################### END
Executable
+20
View File
@@ -0,0 +1,20 @@
#!/usr/bin/perl
use Getopt::Std;getopts('x');
use Sys::Hostname qw(hostname);use Socket;
my($ip) = inet_ntoa( (gethostbyname(hostname()))[4] );
my($user)=getpwuid( $< );
if ($opt_x) {
$comment="[$ip,$user] " . join(' ',@ARGV);
} else {
$comment=join(' ',@ARGV);
}
$comment='-' if ($comment=~/^\s*$/);
$comment=~s/\s*$//;
system("git add -v .");
system("git commit -m '$comment'");
system("git push");
Executable
+51
View File
@@ -0,0 +1,51 @@
#!/usr/bin/perl
$repo=$ARGV[0];
$repo=~s/\/\s*$//;
die if ($repo=~/^\s*$/);
$cgit='git://git.micw.org';
$pgit='ssh://git@git.micw.org:2234/home/git';
if (-d $repo) {
if (yesno('overwrite existing directory?',0)) {
&cmd("rm -rf $repo")
} else {
exit;
}
}
&cmd("git clone $cgit/$repo.git");
&cmd("cd $repo;git remote set-url origin $pgit/$repo.git");
sub cmd() {
my($cmd,$quiet)=@_;
print "$cmd\n" if (!$quiet);
system($cmd);
}
sub yesno {
my($prom,$def)=@_;
my($ans);
if ($def) {;$ans=&getkey("$prom Y/n ? ");}
else {;$ans=&getkey("$prom y/N ? ");}
if ($ans=~/^\s*$/) {
if ($def) {;return(1);}
else {;return(0);}
}
if ($ans=~/^\s*y\s*$/i || $ans=~/^\s*yes\s*$/i) {
return 1;
}
return 0;
}
sub getkey {
$|=1;
print "$_[0]";
system "stty", '-icanon', '-echo', 'eol', "\001";
$key = getc(STDIN);$key=~s/[\r\n\t]//g;
system "stty", 'icanon', 'echo', 'eol', "^@";
print "$key\n";
return $key;
}
Executable
+99
View File
@@ -0,0 +1,99 @@
-lmsg \i%k%T%W%I%X %n %m%y%R %x
-rmsg %S Row %r Col %c %p%% %u ^H help ^T menu
-nonotice
-noxon
-beep
-tab 2
-indentc 9
-nobackups
-pg 0
--autoindent
{Basic
^Y Save & Quit ^F Search/Replace ^A Begining of Line ^B Block Start
^S Save ^G Search Next ^E End of Line ^W Block Cut
^C Abort ^N Next Page ^U Begininig of File ^V Block Paste
^T Menu ^P Previous Page ^Q End of File ^H Help
^Q Undo ^K Delete Line ^L Goto Line
^R Redo ^D Delete Character mwx'2013 joerc v2.02
}
:main
type ^@ TO ÿ
rtn ^M
backs ^?
abort ^C
exsave ^Y
save ^S
undo ^Z
redo ^R
delch ^D
dellin ^K
help ^H
rtarw ^[ [ C
ltarw ^[ [ D
uparw ^[ [ A
dnarw ^[ [ B
pgdn ^N
pgup ^P
line ^L
ffirst ^F
fnext ^G
markb,msg,"Mark set",rtn ^B
markk,copy,blkdel,msg,"Block copy",rtn ^W
yank ^V
bol ^A
eol ^E
bof ^U
eof ^Q
menu,"root",rtn ^T
:prompt
:inherit main
:query
:inherit main
:querya
type ^@ TO ÿ
:querysr
:inherit main
:menu
:inherit main
abort ^Y
backsmenu ^?
bolmenu ^A
eolmenu ^E
rtarwmenu ^[ [ C
ltarwmenu ^[ [ D
uparwmenu ^[ [ A
dnarwmenu ^[ [ B
rtn ^M
:defmenu root
mode,"autoindent",rtn I Autoindent %Zautoindent%
mode,"overwrite",rtn T Overtype %Zoverwrite%
mode,"wordwrap",rtn W Word wrap %Zwordwrap%
mode,"hex",rtn % % Hex edit mode
mode,"tab",rtn D Tab width %Ztab%
mode,"crlf",rtn Z CR-LF (MS-DOS) %Zcrlf%
mode,"linums",rtn N Line numbers %Zlinums%
mode,"beep",rtn B Beep %Zbeep%
mode,"rdonly",rtn % % Read only %Zrdonly%
mode,"icase",rtn % % Search case %Zicase%
execmd C command
mode O option
Executable
+104
View File
@@ -0,0 +1,104 @@
-notite
-assume_color
-guess_non_utf8
-guess_utf16
-guess_crlf
-guess_indent
-menu_above
-transpose
-pastehack
-restore
-search_prompting
-nobackups
-nodeadjoe
-undo_keep 0
-noxon
-lmsg \i%k%T%W%I%X %n %m%y%R %x
-rmsg %S Row %r Col %c %p%% %u ^H help
-smsg ** Line %r Col %c Offset %o(0x%O) %e %a(0x%A) Width %w **
-zmsg ** Line %r Col %c Offset %o(0x%O) **
-highlight
-istep 2
-tab 2
:include ftyperc
{Basic
^Y Save & Quit ^F Search/Replace ^A Begining of Line ^B Block Start ^T^H Toggle Hex
^S Save ^G Search Next ^E End of Line ^W Block Cut ^T^O Overwrite Mode
^C Abort ^N Next Page ^U Begininig of File ^V Block Paste ^T^I Toggle Autoindent
^Z Undo ^P Previous Page ^Q End of File ^H Help ^T^T Set Tab Width
^R Redo ^K Delete Line ^L Goto Line ^T^C Execute Command
^D Delete Character mwx'2017 joerc v4.x.x
}
:windows Bindings common to all windows
type U+0 TO U+10FFFF
rtn ^M
backs ^?
abort ^C
exsave ^Y
save ^S
undo ^Z
redo ^R
delch ^D
dellin ^K
help ^H
rtarw ^[ [ C
ltarw ^[ [ D
uparw ^[ [ A
dnarw ^[ [ B
pgdn ^N
pgup ^P
line ^L
ffirst ^F
fnext ^G
markb,msg,"Mark set",rtn ^B
markk,copy,blkdel,msg,"Block copy",rtn ^W
yank ^V
bol ^A
eol ^E
bof ^U
eof ^Q
mode,"hex",rtn ^T ^H
mode,"overwrite",rtn ^T ^O
mode,"tab",rtn ^T ^T
mode,"autoindent",rtn ^T ^I
execmd ^T ^C
:main Text editing window
:inherit windows
:prompt
:inherit main
:menu
:inherit windows
:query
:inherit windows
:querya
type U+0 TO U+10FFFF
:querysr
type U+0 TO U+10FFFF
:shell Input to shell windows
:inherit main
:vtshell Input to ANSI shell windows
:inherit main
+1
View File
@@ -0,0 +1 @@
LANG="C.UTF-8"
Executable
+573
View File
@@ -0,0 +1,573 @@
#!/usr/bin/perl
############################################################################################# mbackup (mwx'2023)
$VERSION='3.2.0';
use Getopt::Std;getopts('vVDWMYhsfc:');$opt_v=1 if ($opt_V);
use Net::Ping;
use Fcntl ':flock';
chomp($HOST=`hostname`);
&help if($opt_h);
$NOW=time();
my(@z)=localtime($NOW);$z[4]+=1;$z[5]-=100;$z[6]+=1;
$z=sprintf("%02d.%02d-%02d:%02d",$z[3],$z[4],$z[5],$z[2],$z[1]);
$MAX{'daily'}=7; # default values
$MAX{'weekly'}=4;
$MAX{'monthly'}=12;
$MAX{'yearly'}=3;
$FLAT=0;
$RSYNCOPTS='--no-o --no-g --chmod=ugo=rwX';
$RSYNCRETRIES=3;
$CONFIG='';$REQUESTCONFIG=$ARGV[0]; # load configuration
for ($opt_c,"/db/etc/mbackup.conf",$ENV{'HOME'}."/etc/mbackup.conf",$ENV{'HOME'}."/.mbackup.conf") {
if (-f) {
$confile=$_;
last;
}
}
require $confile;
for (keys(%GROUPS)) { # build group list
if ($_ eq $REQUESTCONFIG) {
push(@GROUP,@{$GROUPS{$_}});
$CONFIG=$REQUESTCONFIG;
}
}
if ($CONFIG=~/^\s*$/) { # check for valid config
&log("ERROR: config '$REQUESTCONFIG' not found",1);
exit;
}
$REMOTE=0;$REMOTE=1 if ($TARGETDIR!~/^\//);
if ($opt_s) { # show configuration
if (@GROUP) {
print "\n";
print "mode: group\n\n";
print "group sleep: $GROUPSLEEP\n\n" if ($GROUPSLEEP>0);
for $job (@GROUP) {
print "/db/bin/mbackup -c $confile $job\n";
}
print "\n";
} else {
print "\n";
if ($FLAT==1) {
print "mode: flat\n";
} else {
print "mode: $MAX{'daily'} daily, $MAX{'weekly'} weekly, ";
print "$MAX{'monthly'} monthly, $MAX{'yearly'} yearly\n";
}
if ($#EXCLUDE>-1) {
print "\n";
$tmp="";$tmp.="$_, " for (@EXCLUDE);$tmp=~s/\,\s+$//;
print "exclude: $tmp\n";
}
print "\n";
$l=0;
for (keys(%SRC)) {
$l=length($_) if length($_)>$l;
}
printf "%-${l}s -> %s\/%s\n",$_,$TARGETDIR,$SRC{$_} for (keys(%SRC));
print "\n";
}
exit;
}
if (@GROUP) { # run backup group
for $job (@GROUP) {
$cmd="/db/bin/mbackup -c $confile $job";
&log("{$CONFIG} $cmd",1);
system($cmd);
if ($GROUPSLEEP>0 && $job ne $GROUP[-1]) {
&log("group loop sleep ($GROUPSLEEP seconds)",1);
sleep($GROUPSLEEP);
}
}
exit;
}
if ($MY_LOG) { # mysql loging
use DBI;
$dbh=DBI->connect("DBI:mysql:$MY_DB:$MY_HOST:",$MY_USER,$MY_PASSWD,{RaiseError=>1,PrintError=>1});
}
$mode=''; # set mode (daily,weekly,monthly,yearly)
my($sec,$min,$hour,$mday,$mon,$year,$wday,$yday)=localtime($NOW);
$mon++;$year+=1900;
$z=sprintf("%02d\.%02d\.%04d %02d\:%02d",$mday,$mon,$year,$hour,$min);
$mode='daily' if ( $MAX{'daily'}>0);
$mode='weekly' if ( $wday==0 && $MAX{'weekly'}>0);
$mode='monthly' if ( $mday==1 && $MAX{'monthly'}>0);
$mode='yearly' if ( $yday==0 && $MAX{'yearly'}>0);
$mode='daily' if ($opt_D); # set mode manual
$mode='weekly' if ($opt_W);
$mode='monthly' if ($opt_M);
$mode='yearly' if ($opt_Y);
if (defined($PING)) { # check if host is reachable
if (!Net::Ping->new("icmp", 15)->ping($PING)) {
&log("ERROR: host $PING not reachable",1);exit;
}
}
if (!-d "$TARGETDIR" && $REMOTE==0) { # check target dir
if ($opt_f) {
system("mkdir $TARGETDIR");
if (!-d "$TARGETDIR") {
&log("ERROR: target dir creation failed (mkdir $TARGETDIR)",1);
exit;
}
} else {
&log("ERROR: can not access target dir (mkdir $TARGETDIR)",1);
exit;
}
}
for $srcdir (keys(%SRC)) { # check source dir
if ($srcdir=~/^\// && !-d $srcdir) {
&log("ERROR: mising source dir '$srcdir'",1);
exit;
}
}
undef(%keycount); # check for duplicate source keys
for $srcdir (keys(%SRC)) {
$keycount{$SRC{$srcdir}}++;
if ($keycount{$SRC{$srcdir}}>1) {
&log("ERROR: duplicate source key '$SRC{$srcdir}'",1);
exit;
}
}
if ($FLAT==1 || $REMOTE==1) { # flat mode
$toterr=0;$totfiles=0;$tottfi=0;$totbytes=0;$tottby=0;$totrt=0;
for $srcdir (keys(%SRC)) {
$subname=$SRC{$srcdir};
$cmd="/usr/bin/rsync -av $RSYNCOPTS --stats --delete " .
&exf() . " --numeric-ids $srcdir/. $TARGETDIR/$subname";
($err,$fi,$tfi,$by,$tby,$rt,$try)=&rsync($cmd);
&log("[$CONFIG] $srcdir,$TARGETDIR ($err,$tfi,$fi,$tby,$by,$rt)",1);
if ($MY_LOG) { # mysql logging
$sql="INSERT INTO mbackup VALUES(0,now(),'$HOST','$VERSION','$CONFIG',".
"'$srcdir','$TARGETDIR',$fi,$tfi,$by,$tby,$rt,$err,$try)";
$dbh->do($sql);
}
if ($HTTP_LOG!~/^\s*$/) { # http logging
$tmp= "MBACKUP,$HOST,$VERSION,$CONFIG,$srcdir,$TARGETDIR,$fi,$tfi,$by,$tby,$rt,$err,$try";
use LWP::UserAgent;
my $ua = new LWP::UserAgent();
$response = $ua->post("http://$HTTP_LOG", Content => $tmp);
}
$toterr+=$err;$totfi+=$fi;$tottfi+=$tfi;$totby+=$by;$tottby+=$tby;$totrt+=$rt;
&log("rsync error $err in $srcdir/. -> $TARGETDIR/$subname") if ($err);
}
if ($toterr==0) {
&log("[$CONFIG] mbackup successful finished ($tottfi,$totfi,$tottby,$totby,$totrt)",1)
}
exit;
}
if ($mode eq '') { # check if mode is defiend otherwise exit
&log("ERROR: undefined mode",1);
exit;
}
$yweekly=0;$ymonthly=0;$yyearly=0; # search for missing snapshot
opendir(DIR,$TARGETDIR);
while ($file=readdir(DIR)) {
my(@stat)=stat("$TARGETDIR/$file");
if ($file=~/^weekly\.\d+$/) {
$yweekly=$stat[9] if ($stat[9]>$yweekly);
}
if ($file=~/^monthly\.\d+$/) {
$ymonthly=$stat[9] if ($stat[9]>$ymonthly);
}
if ($file=~/^yearly\.\d+$/) {
$yyearly=$stat[9] if ($stat[9]>$yyearly);
}
}
closedir(DIR);
if ($NOW-$yweekly>86400*10 && $yweekly!=0 && $MAX{'weekly'}>0) {
&log("WARNING: no weekly snapshot for more than 10 days",1);
}
if ($NOW-$ymonthly>86400*40 && $ymonthly!=0 && $MAX{'monthly'}>0) {
&log("WARNING: no monthly snapshot for more than 40 days",1);
}
if ($NOW-$yyearly>86400*400 && $yyearly!=0 && $MAX{'yearly'}>0) {
&log("WARNING: no yearly snapshot for more than 400 days",1);
}
undef(%SNAPLIST); # read snapshot list for current mode
opendir(DIR,$TARGETDIR);
while ($file=readdir(DIR)) {
my(@stat)=stat("$TARGETDIR/$file");
$SNAPLIST{$1}=$stat[9] if ($file=~/^$mode\.(\d+)/);
}
closedir(DIR);
for $n (sort {$b<=>$a} keys(%SNAPLIST)) { # snapshot cycling
$m=$n+1;
if (-d "$TARGETDIR/$mode.$n" && !-e "$TARGETDIR/$mode.$m") {
if ($m>=$MAX{$mode}) {
&cmd("rm -rf $TARGETDIR/$mode.$n");
} else {
&cmd("mv $TARGETDIR/$mode.$n $TARGETDIR/$mode.$m");
}
} else {
$tmp="";
$tmp="($TARGETDIR/$mode.$n not found)" if (!-d "$TARGETDIR/$mode.$n");
$tmp="($TARGETDIR/$mode.$m exists)" if (-e "$TARGETDIR/$mode.$m");
&log("ERROR: error while snapshot cycling $tmp",1);exit;
}
}
$youngest='';$y=0; # search for youngest snapshot
opendir(DIR,$TARGETDIR);
while ($file=readdir(DIR)) {
my(@stat)=stat("$TARGETDIR/$file");
if ($file=~/^((daily|weekly|monthly|yearly)\.\d+)/) {
if ($stat[9]>$y) {
$y=$stat[9];
$youngest=$file;
}
}
}
closedir(DIR);
&cmd("mkdir -m 0755 -p $TARGETDIR/$mode.tmp/"); # create new $mode.0
&cmd("touch $TARGETDIR/$mode.tmp/");
if (-d "$TARGETDIR/$mode.tmp") { # build new snapshot
$toterr=0;$totfi=0;$tottfi=0;$totbytes=0;$tottby=0;
for $srcdir (keys(%SRC)) {
$subname=$SRC{$srcdir};
$linkdest="";
if ($youngest!~/^\s*$/) {
if (-d "$TARGETDIR/$youngest" && -d "$TARGETDIR/$youngest/$subname") {
$linkdest="--link-dest=$TARGETDIR/$youngest/$subname"
}
}
$cmd="/usr/bin/rsync -av $RSYNCOPTS --no-o --no-g --chmod=ugo=rwX --stats --delete " .
&exf() . " --numeric-ids $linkdest $srcdir/. $TARGETDIR/$mode.tmp/$subname";
($err,$fi,$tfi,$by,$tby,$rt,$try)=&rsync($cmd);
&log("[$CONFIG] $srcdir,$TARGETDIR ($err,$tfi,$fi,$tby,$by,$rt)",1);
if ($MY_LOG) { # mysql logging
$sql="INSERT INTO mbackup VALUES(0,now(),'$HOST','$VERSION','$CONFIG',".
"'$srcdir','$TARGETDIR',$fi,$tfi,$by,$tby,$rt,$err,$try)";
$dbh->do($sql);
}
if ($HTTP_LOG!~/^\s*$/) { # http logging
$tmp= "MBACKUP,$HOST,$VERSION,$CONFIG,$srcdir,$TARGETDIR,$fi,$tfi,$by,$tby,$rt,$err,$try";
use LWP::UserAgent;
my $ua = new LWP::UserAgent();
$response = $ua->post("http://$HTTP_LOG", Content => $tmp);
}
$toterr+=$err;$totfi+=$fi;$tottfi+=$tfi;$totby+=$by;$tottby+=$tby;$totrt+=$rt;
}
if ($toterr==0) {
&log("[$CONFIG] mbackup successful finished ($tottfi,$totfi,$tby,$by,$totrt)",1);
&cmd("mv $TARGETDIR/$mode.tmp $TARGETDIR/$mode.0");
utime $NOW,$NOW,"$TARGETDIR/$mode.0";
}
} else {
&log("ERROR: can not access $mode.tmp",1);
}
######################################################################################################## SUPPORT
sub cmd() { # run command
my($cmd)=@_;
&log($cmd);
system($cmd);
}
sub log() { # write logs
my($str,$forceprint)=@_;
my(@z)=localtime(time());$z[4]+=1;$z[5]-=100;$z[6]+=1;
$ts=sprintf("%02d\.%02d\.%02d %02d\:%02d",$z[3],$z[4],$z[5],$z[2],$z[1]);
if ($LOGFILE!~/^\s+$/) {
open(LOG,">> $LOGFILE");
flock(LOG, LOCK_EX);
print LOG "$ts $$/$VERSION/$CONFIG $str\n";
close(LOG);
}
print "$str\n" if ($opt_v || $forceprint);
}
sub rsync() { # run rsync
my($rsynccmd)=@_;
my($files)=0;my($tfi)=0;my($bytes)=0;my($tby)=0;my($error)=0;my($try)=0;
$runtime=time();
$retries=0;
for $try (1..$RSYNCRETRIES) {
$rsynclog="";
open(RSYNC,"$rsynccmd |");while(<RSYNC>) {
print if ($opt_V);
if (/Number of files:\s*([\d\,]+)/) {;$files=$1;$files=~s/,//g;}
if (/Number of regular files transferred:\s*([\d\,]+)/) {;$tfi=$1;$tfi=~s/,//g;}
if (/Total file size:\s*([\d\,]*)\s*bytes/) {;$bytes=$1;$bytes=~s/,//g;}
if (/Total transferred file size:\s*([\d\,]*)\s*bytes/) {;$tby=$1;$tby=~s/,//g;}
}
close(RSYNC);
$error=($? >> 8);
$runtime=time()-$runtime;
if ($error==0) {
&log("$rsynccmd (successful)");
} else {
&log("$rsynccmd (error $error in try $try)")
}
last if ($error==0);
$retries++;
}
return ($error,$files,$tfi,$bytes,$tby,$runtime,$retries);
}
sub exf() { # exclude file
$ex='';
if ($#EXCLUDE>-1) {
$exfile="/tmp/mbackup.exclude.$CONFIG";
open(OUT,"> $exfile");
print OUT "$_\n" for (@EXCLUDE);
close(OUT);
$ex="--exclude-from $exfile --delete-excluded";
}
return $ex;
}
sub buildsrc() { # build source list (used in config)
my($base,@paths)=@_;
for $path (@paths) {
if ($path=~/\/([^\/]+)$/) {
$SRC{"$base$path"}=$1;
}
}
}
sub sc() { # build simple configuration (used in config)
my($uhd,$dirs,$exclude,$grp,$opt)=@_;
$mode=0;
if ($uhd=~/^([^\.]+)\.(.*)\:\:(.*)$/) {;$user=$3;$config=$1;$domain=$2;$mode=1;}
if ($uhd=~/^(\w+)\@([^\.]+)\.(.*)$/) {;$user=$1;$config=$2;$domain=$3;$mode=2;}
if ($mode>0) {
%opt=%$opt;
$config=$opt{name} if ($opt{name});
@grp=@$grp;
for $g (@grp) {
push(@{$GROUPS{$g}},$config)
}
if ($REQUESTCONFIG eq $config) {
$RSYNCOPTS=$opt{rsync} if ($opt{rsync});
$PING=$opt{ping} if ($opt{ping});
$TARGETBASE=$opt{target} if ($opt{target});
if ($opt{versions}=~/(\d+):(\d+):(\d+):(\d+)/) {
$MAX{'daily'}=$1;
$MAX{'weekly'}=$2;
$MAX{'monthly'}=$3;
$MAX{'yearly'}=$4;
$FLAT=0;
} else {
$FLAT=1;
}
@DIRS=@$dirs;
@EXCLUDE=@$exclude;
$TARGETDIR="$TARGETBASE/$config";
for $path (@DIRS) {
$tpath=$path;$tpath=~s/^\s*\///;$tpath=~s/\//\./g;
$SRC{"$uhd$path"}=$tpath if ($mode==1);
$SRC{"$uhd:$path"}=$tpath if ($mode==2);
}
$CONFIG=$REQUESTCONFIG;
}
}
}
sub help() { # help
print "mbackup v$VERSION\n";
print <<'ENDOFHELP';
USAGE:
------
mbackup [-v] [-V] [-DWMY] <backup job name>
mbackup -h
-D force daily mode
-W force weekly mode
-M force monthly mode
-Y force yearly mode
-v verbose output
-V very verbose output
-f force creation of target directory
-h this help
CONFIG FILE (/db/etc/mbackup.conf) GLOBAL OPTIONS:
--------------------------------------------------
$LOGFILE = "/var/log/mbackup.log"; # logfile location
$TARGETBASE = "/data/backup"; # targetbase for simple config
$HTTP_LOG = 'log.mxwx.org'; # http logging server
$MY_LOG = 1; # enable/disable mysql logging (deprecated)
$MY_HOST = 'localhost';
$MY_USER = '';
$MY_PASSWD = '';
$MY_DB = '';
SIMPLE BACKUP JOB CONFIGURATION (/db/etc/mbackup.conf):
-------------------------------------------------------
&sc(target,[paths],[exclude],[groups],{ option=>value, .. }
example:
&sc('root@host.domain',['/etc','/var','/home'],['/lib/lxcfs'],['all']);
&sc('host.domain::root',['/etc','/var','/home'],[],['all'], { rsync=>"-e 'ssh -p 2234'" });
&sc('root@host.domain',['/etc','/var','/home'],[],[] { versions=>'7:4:12:3' });
options:
name => 'name' # set configuration name
rsync => 'rsync options' # set additional rsync options
ping => 'ip address' # apply ping test before backup
version => 'daily:weekly:monthly:yearly' # define backup versioning
target => '/path/to/mbackup' # define backup destination
BACKUP JOB CONFIGURATION (/db/etc/mbackup.conf):
------------------------------------------------
if ($REQUESTCONFIG eq 'NAME') { # basic backup job config
<OPTIONS>
$CONFIG=$REQUESTCONFIG;
}
if ($REQUESTCONFIG eq 'NAME') { # backup group definition
@GROUP=('NAME1','NAME2',...);
$CONFIG=$REQUESTCONFIG;
}
push(@{$GROUPS{'all'}},'NAME'); # add config to group
BACKUP JOB OPTIONS:
-------------------
# backup target definition (use only once)
$TARGETDIR='/backup/NAME' # local traget directory
$TARGETDIR='10.0.1.1::root/backup/NAME' # rsync protocol target
$TARGETDIR='root@10.0.1.1:/backup/NAME' # ssh traget directory
# backup source definition (multiple allowd, different keys required)
$SRC{'/etc'} = 'etc1'; # local source
$SRC{'10.0.1.1::root/etc'} = 'etc2'; # remote source (rsync protocol)
$SRC{'root@10.0.1.1:/etc'} = 'etc3'; # remote source (ssh)
# define eculsions (absolut path definitions from source path)
@EXCLUDE=('/path1','/path2',...);
# define number of backups to keep, at least one value must be greater than 0
$MAX{'daily'} = 7; # keep n daily backup (default=7)
$MAX{'weekly'} = 4; # keep n weekly backup (default=4)
$MAX{'monthly'} = 12; # keep n monthly backup (default=12)
$MAX{'yearly'} = 3; # keep n yearly backup (default=3)
$FLAT=1; # only keep one backup (default=0)
$PING='10.0.1.1'; # perform ping test before backup run
$RSYNCOPTS="-e 'ssh -p 2234'"; # add extra options for rsync
$RSYNCRETRIES=3; # number of retries if rsync fails
MYSQL LOGGING TABLE:
--------------------
CREATE TABLE `mbackup` (
`id` int(11) unsigned NOT NULL AUTO_INCREMENT,
`ts` timestamp NULL DEFAULT NULL,
`host` varchar(255) DEFAULT NULL,
`version` varchar(255) DEFAULT NULL,
`config` varchar(255) DEFAULT NULL,
`source` varchar(255) DEFAULT NULL,
`target` varchar(255) DEFAULT NULL,
`files` bigint(11) DEFAULT NULL,
`transfiles` bigint(11) DEFAULT NULL,
`bytes` bigint(11) DEFAULT NULL,
`transbytes` bigint(11) DEFAULT NULL,
`runtime` int(11) DEFAULT NULL,
`error` int(11) DEFAULT NULL,
`retries` int(11) DEFAULT '0',
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1;
ENDOFHELP
exit;
}
############################################################################################################ END
Executable
+525
View File
@@ -0,0 +1,525 @@
#!/usr/bin/perl
################################################################################################ mgsh (mwx'2023)
use Sys::Hostname qw(hostname);use Socket;
use Config;
use Time::HiRes qw( usleep ualarm gettimeofday tv_interval );
use File::Compare;
$VERSION='3.1.5';
$INFO="mwx'2023";
$BASE="$ENV{HOME}/Source";
$BASE="/db/src" if ($Config{osname}=~/linux/i);
die "$BASE not found" if (!-d $BASE);
$PRJ=$ARGV[0] if (-d "$BASE/$ARGV[0]");
$GITHOST = '141.14.128.56';
$GITPORT = '2234';
$GITUSER = 'git';
$GITKEY = 'mgit_rsa';
$GITPATH ="/home/git";
$URL = "ssh://$GITUSER\@$GITHOST:$GITPORT$GITPATH";
$SSH = "ssh -p $GITPORT $GITUSER\@$GITHOST";
$IP = inet_ntoa( (gethostbyname(hostname()))[4] );
$HOST=hostname();$HOST=~s/([^\.]+)\..*$/$1/;
$USER=getpwuid( $< );
use Date::Parse; # date hashes
@D=('Sun','Mon','Tue','Wed','Thu','Fri','Sat');
@M=('Jan','Feb','Mar','Apr','May','Jun','Jul','Aug','Sep','Oct','Nov','Dec');
&cmd("git config --global user.name 'Michael Wesemann'",1); # git settings
&cmd("git config --global user.email mike\@pstbx.org",1);
&cmd("git config --global push.default matching",1);
print "mgsh v$VERSION $INFO, $BASE\n"; # ready to start
for (;;) { # main mgsh loop
if ($PRJ!~/^\s*$/ && -d "$BASE/$PRJ") { # set current working directory/project
$DIR="$BASE/$PRJ";
$BPLSTATE='';
if (-l "$DIR/build.pl") {
chomp($rlf=`readlink $DIR/build.pl`);
$BPLSTATE='/OFF' if ($rlf=~/\.off$/);
}
$p=$1 if ($BASE=~/\/([^\/]+)$/);$prompt="$p/$PRJ/$I$BPLSTATE";
} else {
$DIR="$BASE";
$p=$1 if ($BASE=~/\/([^\/]+)$/);$prompt="$p";
}
chomp($REMURL= `cd $DIR; git config remote.origin.url`);
$REPO=$1 if ($REMURL=~/$url\/(.*)\.git\/?$/);
$prompt=~s/\/\s*$//;$cmd=&readline("$prompt > ","",1); # read command from command line
@tmpcmd=split(' ',$cmd);undef(@cmd);undef(%OPT); # get commandline options
for (@tmpcmd) {if (/\-(\w)/) {$OPT{$1}="-$1";} else {push(@cmd,$_);}}
if ($cmd[0] eq 'dist') { ########################################################################### list [-a]
$CP='/bin/cp';
$CP='/opt/homebrew/bin/gcp' if (-f '/opt/homebrew/bin/gcp');
$DDIR="$BASE/dist/$PRJ";
$DDIR=$cmd[1] if ($cmd[1]!~/^\s*$/);
if (-d $DDIR) {
chdir($DIR);
$n=0;
open(LS,"cd $DIR;git ls-files |");
while(<LS>) {
chomp();
if (!/^private/) {
if (compare($_,"$DDIR/$_")!=0) {
print "$_\n";
cmd("$CP --parents $_ $DDIR",1);
$n++;
}
}
}
print "$n files copied to $DDIR\n";
} else {
print "dist path not found ($DDIR)\n";
}
close(LS);
}
elsif ($cmd[0] eq 'list') { ########################################################################### list [-a]
$path=".";$path="./archive" if ($OPT{a});
open(LS,"$SSH /bin/ls -ltr $path |");
while(<LS>) {
if ($cmd[1]=~/^\s*$/ || /$cmd[1]/i) {
if ($OPT{a}) {
print "$1\n" if (/git\s+git\s+\d+\s+(.*)\.git.tar.gz$/);
} else {
print "$1\n" if (/git\s+git\s+\d+\s+(.*)\.git$/);
}
}
}
close(LS);
}
elsif ($cmd[0] eq 'show') { ############################################################################# show
$prj=$PRJ if ($PRJ!~/^\s*$/);
$prj=$cmd[1] if ($cmd[1]!~/^\s*$/);
$path=".";
$found=0;
open(LS,"$SSH /bin/ls $path |");
while(<LS>) {
$found++ if (/^$prj\.git$/);
}
close(LS);
if ($found==1) {
open($FH,"$SSH \"cd $GITPATH/$prj\.git && git log --reverse --format='%h %cd %s'\" |");
&repolog($FH);
} else {
print "repository not found\n";
}
}
elsif ($cmd[0] eq 'log' && $PRJ!~/^\s*$/) { ############################################################## log
if (-d "$DIR/.git") {
open($FH,"cd $DIR;git log --reverse --format='%h %cd %s' |");
&repolog($FH);
} else {
print "repository not found";
}
}
elsif ($cmd[0] eq 'push' && $PRJ!~/^\s*$/) { ################################################## push [comment]
if (-f "$DIR/push.pl") {
&shell("perl $DIR/push.pl");
}
$comment='';$comment.="$cmd[$_] " for (1..$#cmd);$comment=~s/\s+$//;
$comment=~s/\s{2}/ /g;
if (-d "$DIR/.git") {
&shell("git add --all .");
$msg="[$USER\@$HOST] $comment";$msg=~s/^\s+//;$msg=~s/\s+$//;
&shell("git commit -m '$msg'");
&shell("git push");
&shell("$SSH touch $GITPATH/${PRJ}.git");
} else {
print "error, no repository (.git) found";
}
}
elsif ($cmd[0] eq 'edit' && $PRJ!~/^\s*$/) { ################################################### edit [number]
$num=10;$num=$cmd[1] if ($cmd[1]=~/^\d+$/);
if (-d "$DIR/.git") {
&shell("git rebase -i HEAD~$num");
&shell("git push --force");
} else {
print "error, no repository (.git) found";
}
}
elsif ($cmd[0] eq 'archive' && $PRJ!~/^\s*$/) { ############################################ archive [comment]
$comment='';$comment.="$cmd[$_] " for (1..$#cmd);$comment=~s/\s+$//;
$comment=~s/[\,\;\:\\\/\=\'\"\|\?\>\<\-]+/ /g;
$comment=~s/\s{2}/ /g;
$comment=~s/\s+/\_/g;
my(@z)=localtime(time());$z[4]+=1;$z[5]-=100;$z[6]+=1;
$z=sprintf("%02d%02d%02d.%02d%02d",$z[3],$z[4],$z[5],$z[2],$z[1]);
if ($comment=~/^\s*$/) {
&shell(sprintf("$SSH 'cp -r ${PRJ}.git archive/${PRJ}_$z.git'"));
&shell(sprintf("$SSH 'cd archive;tar cvzf ${PRJ}_$z.git.tar.gz ${PRJ}_$z.git'"));
&shell("$SSH 'rm -rf archive/${PRJ}_$z.git'");
} else {
&shell(sprintf("$SSH cp -r ${PRJ}.git archive/${PRJ}_${z}_$comment.git"));
&shell(sprintf("$SSH 'cd archive;tar cvzf ${PRJ}_${z}_$comment.git.tar.gz ${PRJ}_${z}_$comment.git'"));
&shell("$SSH 'rm -rf archive/${PRJ}_${z}_$comment.git'");
}
}
elsif ($cmd[0] eq 'init' && $PRJ!~/^\s*$/) { ############################################################ init
&shell("perl $DIR/push.pl") if (-f "$DIR/push.pl");
if ($REPO=~/^\s*$/ || yesno('overwrite existing repository?',0)) {
&shell("$SSH 'rm -rf $GITPATH/${PRJ}.git'");
&shell("$SSH 'mkdir $GITPATH/${PRJ}.git;chdir $GITPATH/${PRJ}.git;git --bare init'");
if (!-f ".gitignore" || yesno('overwrite existing .gitignore?',0)) {
open(OUT,"> .gitignore");
print OUT &gitignore();
close(OUT);
}
&shell("rm -rf .git");
&shell("git init");
&shell("git remote add origin $URL/${PRJ}.git/");
&shell("git add .");
&shell("git commit -m 'initial commit [$IP,$USER]'");
&shell("git push -u origin master");
}
}
elsif ($cmd[0] eq 'login') { ########################################################################### login
&cmd("$SSH");
}
elsif ($cmd[0] eq 'cd' && $cmd[1]!~/^\./) { ############################################################### cd
$PRJ='';
$PRJ=$cmd[1] if (-d "$BASE/$cmd[1]");
}
elsif ($cmd[0] eq 'checkout' && $PRJ!~/^\s*$/) { ###################################### checkout [git options]
&shell("git checkout $cmd[1]");
}
elsif ($cmd[0] eq 'clone') { ###################################################################### clone [-a]
$prj=$PRJ if ($PRJ!~/^\s*$/);
$prj=$cmd[1] if ($cmd[1]!~/^\s*$/);
$DIR="$BASE";chdir($DIR);
my($path)=".";my($path)="./archive" if ($OPT{a});
my($found)=0;
open(LS,"$SSH /bin/ls $path |");
while(<LS>) {
if ($OPT{a}) {
$found++ if (/^$prj\.git.tar.gz$/);
} else {
$found++ if (/^$prj\.git$/);
}
}
close(LS);
if ($found==1) {
if (-d "$DIR/$prj") {
if (yesno('overwrite existing directory?',0)) {
&cmd("rm -rf $DIR/$prj")
} else {
goto exitclone;
}
}
&shell("git clone $URL/$prj.git") if (!$OPT{a});
if ($OPT{a}) {
&cmd("$SSH 'cd archive;tar xvzf $prj.git.tar.gz'");
&cmd("git clone $URL/archive/$prj.git");
&cmd("$SSH 'rm -rf archive/$prj.git'");
}
} else {
print "repository not found, try 'list [-a]'";
}
$PRJ=$prj if (-d "$BASE/$prj");
exitclone:;
}
elsif ($cmd[0] eq 'cloneall') { ##################################################################### cloneall
$prj=$PRJ if ($PRJ!~/^\s*$/);
$prj=$cmd[1] if ($cmd[1]!~/^\s*$/);
$DIR="$BASE";chdir($DIR);
my($path)=".";my($path)="./archive" if ($OPT{a});
my($found)=0;
open(LS,"$SSH /bin/ls $path |");
while(<LS>) {
if (/^(.*)\.git$/) {
print "-$1-\n";
&shell("git clone $URL/$1.git");
}
}
close(LS);
}
elsif ($cmd[0] eq 'open' || $cmd[0] eq 'view'){ ######################################################### open
$prj=$PRJ if ($PRJ!~/^\s*$/);
$prj=$cmd[1] if ($cmd[1]!~/^\s*$/);
if (-d "$BASE/$prj") {
$DIR="$BASE/$prj";chdir($DIR);
$xws='';$xprj='';opendir(DIR,$DIR);
while($file=readdir(DIR)) {
$xws=$file if ($file=~/.xcworkspace$/);
$xprj=$file if ($file=~/.xcodeproj$/);
}
close(DIR);
$xprj='' if ($xws!~/^\s*$/ && $xprj!~/^\s*$/);
&shell("open $xws") if ($xws!~/^\s*$/ && -d "$DIR/$xws");
&shell("open $xprj") if ($xprj!~/^\s*$/ && -d "$DIR/$xprj");
&shell("coda $DIR") if ($xws=~/^\s*$/ && $xprj=~/^\s*$/);
$PRJ=$prj if ($cmd[0] eq 'open');
} else {
print "not found\n";
}
}
elsif ($cmd[0] eq 'count') { ##################################################################### count lines
&count($DIR);
}
elsif ($cmd[0] eq 'do') { ############################################################### execute extra script
&shell("perl $DIR/do.$cmd[1].pl") if (-f "$DIR/do.$cmd[1].pl");
}
elsif ($cmd[0] eq 'tag') { # manage tag
if ($cmd[1] eq 'add' && $cmd[2]!~/^\s*$/) {
&shell("git tag -a '$cmd[2]' -m '$cmd[2]'");
&shell("git push origin '$cmd[2]'");
} elsif ($cmd[1] eq 'checkout' && $cmd[2]!~/^\s*$/) {
&shell("git checkout 'tags/$cmd[2]'");
} elsif ($cmd[1] eq 'delete' && $cmd[2]!~/^\s*$/) {
&shell("git tag -d '$cmd[2]'");
&shell("git push origin ':refs/tags/$cmd[2]'");
} else {
if ($cmd[1]!~/^\s*$/) {
print "unkown tag subcommand\n";
} else {
&shell("git tag -l --format=\"%(taggerdate:short): %(refname:short)\"");
}
}
}
elsif ($cmd[0] eq 'help') { ############################################################################# help
print <<"ENDOFHELP";
mgsh (git shell) $VERSION $INFO, builtin commands:
cd [project] change project
open [project] open project
init make new repository from current directory
push [comment] push changes to git server
edit [number] edit last [number] commits (default is 10)
clone [-a] <repository> clone repository from git server (-a for archive)
cloneall> clone all repository from git server
checkout [git options] forward checkout to git
log show log
archive [comment] archive current repository
list [-a] [pattern] list repositories on git server (-a for archive)
show <repository> show repository log on git server
dist <path> cp changed files to other directory/repository
login connect to git server
do <name> execute extra script with name do.<name>.pl
count count lines in project
tag show tags
tag add <tag> add tag
tag checkout <tag> checkout tag
tag delete <tag> delete tag
ENDOFHELP
}
else { ###################################################################### forward unknown command to shell
&shell($cmd) if ($cmd!~/^\s*$/);
}
}
######################################################################################################## SUPPORT
sub shell() { # shell
my($cmd)=@_;
chdir $DIR;
system($cmd);
}
sub cmd() { # execute command
my($cmd,$quiet)=@_;
print "$cmd\n" if (!$quiet);
system($cmd);
}
sub readline { # readline
my($prompt,$input,$mode)=@_; use Term::ReadLine;
BEGIN{ $ENV{PERL_RL} = 'Perl o=0' };
if (!$term) {
$term = new Term::ReadLine 'term';
open(IN,"$BASE/.mgsh_history");
while(<IN>) {
chomp();
push(@history,$_);
}
close(IN);
$term->SetHistory(@history);
}
my($txt)=$term->readline($prompt,$input);
my(@history)=$term->GetHistory();
shift(@history) while($#history>=10);
open(OUT,"> $BASE/.mgsh_history");
print OUT "$_\n" for (@history);
close(OUT);
return $txt;
}
sub yesno { # request yes/no answer
my($prom,$def)=@_;
my($ans);
if ($def) {;$ans=&getkey("$prom Y/n ? ");}
else {;$ans=&getkey("$prom y/N ? ");}
if ($ans=~/^\s*$/) {
if ($def) {;return(1);}
else {;return(0);}
}
if ($ans=~/^\s*y\s*$/i || $ans=~/^\s*yes\s*$/i) {
return 1;
}
return 0;
}
sub getkey { # get key from keyboard
$|=1;
print "$_[0]";
system "stty", '-icanon', '-echo', 'eol', "\001";
$key = getc(STDIN);$key=~s/[\r\n\t]//g;
system "stty", 'icanon', 'echo', 'eol', "^@";
print "$key\n";
return $key;
}
sub count() { # count lines in project
my($dir)=@_;
use File::Find;
$lines=0;
find(\&counting,$dir);
sub counting {
$f=$File::Find::name;
if (-f $f) {
if ($f=~/\.[hmc]$/ || $f=~/\.xib$/ || $f=~/\.lm$/ || $f=~/\.ym$/ || $f=~/\.pl$/ || $f=~/\.php$/ ||
(-T $f && $f=~/\/bin\//)) {
open(WC,"/usr/bin/wc -l '$f' |");
while(<WC>) {;
$lines+=$1 if (/^\s*(\d*)\s+$f/);
}
close(WC);
}
}
}
print "$lines lines total in $PRJ\n";
}
sub repolog() { # show repository log
my($FH)=@_;
my($o)='';my($os)='';my($oss)='';my($n)=0;my($ns)=0;
my($z)='';my($zs)='';my($zss)='';
while(<$FH>) {
chomp();
@tmp=split(' ');
$t=str2time($tmp[1]);
my(@z)=localtime($t);$z[4]+=1;$z[5]+=1900;$z[6]+=1;
if (time()-$t<86400) {
$z=sprintf(" %02d\:%02d",$z[2],$z[1]);
$zs=sprintf(" %02d\:%02d",$z[2],$z[1]);
$zss=sprintf("%02d\:%02d",$z[2],$z[1]);
} elsif (time()-$t<86400*7) {
$z=sprintf(" %s, %02d\:%02d",$D[$z[6]-1],$z[2],$z[1]);
$zs=sprintf("%s, %02d\:%02d",$D[$z[6]-1],$z[2],$z[1]);
$ns++;
} else {
$z=sprintf("%s, %02d %s %4d, %02d\:%02d",$D[$z[6]-1],$z[3],$M[$z[4]-1],$z[5],$z[2],$z[1]);
$n++;
}
$o.="$tmp[0] $z $tmp[2]\n";
$os.="$tmp[0] $zs $tmp[2]\n";
$oss.="$tmp[0] $zss $tmp[2]\n";
}
if ($n==0 && $ns==0) {
print $oss;
} elsif ($n==0 && $ns>0) {
print $os;
} else {
print $o;
}
}
sub gitignore() {
return << "ENDOFGITIGNORE";
.DS_Store
.AppleDouble
.LSOverride
._*
.DocumentRevisions-V100
.fseventsd
.Spotlight-V100
.TemporaryItems
.Trashes
ENDOFGITIGNORE
}
############################################################################################################ END
Executable
+195
View File
@@ -0,0 +1,195 @@
#!/usr/bin/perl
################################################################################ mysql/mariadb backup (mwx'2021)
use Getopt::Std;getopts('bza');
use DBI;
use Term::ReadKey;
$VERSION='4.0.0';
$HOST = 'localhost'; # settings
$USER = 'mybackup';
$PASSWD = 'iexae3aitathoo4k';
$PATH = '/db/backup/mysql';
$KEEPDAILYBACKUPS = 21;
$TAR = "/bin/tar";
$RM = "/bin/rm";
$MYSQLDUMP = "/usr/bin/mysqldump";
$GZIP = "/bin/gzip";
if (!$opt_a && !$opt_b && !$opt_z) { # show usage
print <<"ENDOFHELP";
mybackup, MySQL/MariaDB backup, mwx'2021, v$VERSION
usage: mybackup -a add backup user to db (root password required)
mybackup -b run database backup
mybackup -z run database backup and compress output
ENDOFHELP
exit(0);
}
if ($opt_a) { # add backup user to db
use Term::ReadPassword;
$rootdbpw = read_password('Enter password: ');
$dbh=DBI->connect("DBI:mysql:mysql:localhost:","root",$rootdbpw,{RaiseError=>1,PrintError=>1});
$dbh->do("CREATE USER 'mybackup' IDENTIFIED BY '$PASSWD'");
$dbh->do("GRANT SELECT, SHOW VIEW, LOCK TABLES, RELOAD,REPLICATION CLIENT ON *.* TO 'mybackup'");
#
#
#
# CREATE USER 'mybackup' IDENTIFIED BY 'Yooch0esh9Ah';
#
#
# $sth=$dbh->prepare("SHOW COLUMNS FROM user");$sth->execute; # find authentication fields
# $HASPW=0;$HASAS=0;
# while ($dat = $sth->fetchrow_hashref) {
# $HASPW=1 if ($$dat{Field} eq 'Password');
# $HASAS=1 if ($$dat{Field} eq 'authentication_string');
# }
#
# $sql="INSERT INTO user SET User='$USER',Host='localhost',";
# $sql.="Password=password('$PASSWD')," if ($HASPW);
# $sql.="authentication_string=password('$PASSWD')," if ($HASAS);
# $sql.="ssl_cipher='',x509_issuer='',x509_subject='',".
# "Select_priv='Y',Reload_priv='Y',Lock_tables_priv='Y',Show_view_priv='Y',Process_priv='Y'";
#
# $dbh->do($sql);
$dbh->do("flush privileges");
if (&yesno('Write /root/.my.cnf file',1)) {
system("rm -f /root/.my.cnf");
open(OUT,"> /root/.my.cnf");
print OUT "[client]\nuser=root\npassword=$rootdbpw\n[mysqldump]\nuser=mybackup\npassword=$PASSWD\n";
close(OUT);
system("chmod 600 /root/.my.cnf");
}
&setupcron();
exit;
}
&cleanup();
@DBS=&autogetdbs();
my(@z)=localtime(time());$z[4]+=1;$z[6]+=1;$z[5]-=100;
$z=sprintf("%02d%02d%02d%02d%02d%02d",$z[3],$z[4],$z[5],$z[2],$z[1],$z[0]);
if (!-d "$PATH/$z") { # make target dir
mkdir("$PATH/$z",448);
}
for $db (@DBS) { # dump databaeses
if ($db ne 'information_schema' && $db ne 'performance_schema' && $db ne 'sys') {
my($fdb)=$db;$fdb=~s/\s/\./g;
if (!$IGNOREDB{$db}) {
if ($DBPARAM{$db}) {;$db=$DBPARAM{$db};}
&cmd("$MYSQLDUMP -h $HOST -u $USER -f -F -R --skip-extended-insert $db >$PATH/$z/$fdb.mysql");
}
}
}
$cmd="$MYSQLDUMP -h $HOST -u $USER -c -f -F -R --skip-extended-insert -A -v >$PATH/$z.mysql";
&cmd($cmd);
&cmd("$GZIP $PATH/$z.mysql");
chdir($PATH);
if (-d "$PATH/$z" && $opt_z) {
&cmd("$TAR czvf $z.tar.gz $z");
&cmd("$RM -rf $PATH/$z");
}
######################################################################################################## SUPPORT
sub cleanup() {
use Date::Parse;
opendir(DIR,"$PATH");
while($file=readdir(DIR)) {
if (-f "$PATH/$file" && $file=~/(\d\d)(\d\d)(\d\d)(\d\d)(\d\d)(\d\d)/) {
$m=$1;
$a=$3+2000;
$time = str2time("$a/$2/$1 $4:$5:$6");
my(@z)=localtime($time);$z[4]+=1;$z[5]-=100;$z[6]+=1;
$z=sprintf("%02d\.%02d\.%02d %02d\:%02d\:%02d",$z[3],$z[4],$z[5],$z[2],$z[1],$z[0]);
$t=(time()-$time)/86400;
if ($m!=1 && $t>$KEEPDAILYBACKUPS) {
&cmd("/bin/rm -f $PATH/$file") if (-f "$PATH/$file");
}
}
}
}
sub setupcron() {
open(CRON,"crontab -l |");
while(<CRON>) {
chomp();
push(@CRON,$_) if (!/\/db\/bin\/mybackup/);
}
push(@CRON,"0 12 * * * /db/bin/mybackup -z > /dev/null 2>&1");
open(OUT,"> /tmp/cron.$$");
for (@CRON) {
print OUT "$_\n";
}
close(OUT);
system("/usr/bin/crontab /tmp/cron.$$");
system("rm -f /tmp/cron.$$");
}
sub cmd() {
my($cmd)=@_;
print "$cmd\n";
system($cmd);
}
sub autogetdbs() {
my(@dbs);
$dbh=DBI->connect("DBI:mysql::$HOST:",$USER,$PASSWD,{RaiseError=>1,PrintError=>1});
$sth=$dbh->prepare("SHOW DATABASES");$sth->execute ;
while ($dat = $sth->fetchrow_hashref) {
push(@dbs,$$dat{Database});
}
return (@dbs);
}
sub yesno {
my($prom,$def)=@_;
my($ans);
if ($def) {;$ans=&getkey("$prom Y/n ? ");}
else {;$ans=&getkey("$prom y/N ? ");}
if ($ans=~/^\s*$/) {
if ($def) {;return(1);}
else {;return(0);}
}
if ($ans=~/^\s*y\s*$/i || $ans=~/^\s*yes\s*$/i) {
return 1;
}
return 0;
}
sub getkey {
$|=1;
print "$_[0]";
system "stty", '-icanon', '-echo', 'eol', "\001";
$key = getc(STDIN);$key=~s/[\r\n\t]//g;
system "stty", 'icanon', 'echo', 'eol', "^@";
print "$key\n";
return $key;
}
############################################################################################################ END
Executable
+43
View File
@@ -0,0 +1,43 @@
#!/usr/bin/perl
########################################################################## mysql/mariadb user manager (mwx'2021)
use DBI;
use Getopt::Std;getopts('hd:x');
$VERSION='4.0.0';
&help() if ($ARGV[0]=~/^\s*$/ || $opt_h);
$USER=$DB=$ARGV[0];
$DB=$opt_d if ($opt_d!~/^\s*$/);
$PW=sprintf("$USER%03d",int(rand(100000)));
my $dsn = "DBI:mysql:mysql;mysql_read_default_file=$ENV{HOME}/.my.cnf";
$dbh=DBI->connect($dsn,undef,undef,{PrintError=>1});
&sqldo("CREATE DATABASE IF NOT EXISTS $DB") if (!$opt_x);
&sqldo("DROP USER IF EXISTS '$USER'");
&sqldo("CREATE USER '$USER' IDENTIFIED BY '$PW'");
&sqldo("GRANT ALL PRIVILEGES ON $DB.* TO '$USER'");
sub sqldo() {
my($sql)=@_;
print "$sql\n";
$dbh->do($sql);
}
sub help() {
print <<"ENDOFHELP";
myuser - mariadb user manager (v$VERSION)
myuser <NAME> add user and database with the same name to mariadb
myuser -d <DBNAME> <NAME> add user and database to mariadb
myuser -x <NAME> add only user to mariadb
ENDOFHELP
exit();
}
############################################################################################################ END
Executable
+611
View File
@@ -0,0 +1,611 @@
#!/usr/bin/env python
# Try to determine how much RAM is currently being used per program.
# Note per _program_, not per process. So for example this script
# will report RAM used by all httpd process together. In detail it reports:
# sum(private RAM for program processes) + sum(Shared RAM for program processes)
# The shared RAM is problematic to calculate, and this script automatically
# selects the most accurate method available for your kernel.
# Licence: LGPLv2
# Author: P@draigBrady.com
# Source: http://www.pixelbeat.org/scripts/ps_mem.py
# V1.0 06 Jul 2005 Initial release
# V1.1 11 Aug 2006 root permission required for accuracy
# V1.2 08 Nov 2006 Add total to output
# Use KiB,MiB,... for units rather than K,M,...
# V1.3 22 Nov 2006 Ignore shared col from /proc/$pid/statm for
# 2.6 kernels up to and including 2.6.9.
# There it represented the total file backed extent
# V1.4 23 Nov 2006 Remove total from output as it's meaningless
# (the shared values overlap with other programs).
# Display the shared column. This extra info is
# useful, especially as it overlaps between programs.
# V1.5 26 Mar 2007 Remove redundant recursion from human()
# V1.6 05 Jun 2007 Also report number of processes with a given name.
# Patch from riccardo.murri@gmail.com
# V1.7 20 Sep 2007 Use PSS from /proc/$pid/smaps if available, which
# fixes some over-estimation and allows totalling.
# Enumerate the PIDs directly rather than using ps,
# which fixes the possible race between reading
# RSS with ps, and shared memory with this program.
# Also we can show non truncated command names.
# V1.8 28 Sep 2007 More accurate matching for stats in /proc/$pid/smaps
# as otherwise could match libraries causing a crash.
# Patch from patrice.bouchand.fedora@gmail.com
# V1.9 20 Feb 2008 Fix invalid values reported when PSS is available.
# Reported by Andrey Borzenkov <arvidjaar@mail.ru>
# V3.9 07 Mar 2017
# http://github.com/pixelb/scripts/commits/master/scripts/ps_mem.py
# Notes:
#
# All interpreted programs where the interpreter is started
# by the shell or with env, will be merged to the interpreter
# (as that's what's given to exec). For e.g. all python programs
# starting with "#!/usr/bin/env python" will be grouped under python.
# You can change this by using the full command line but that will
# have the undesirable affect of splitting up programs started with
# differing parameters (for e.g. mingetty tty[1-6]).
#
# For 2.6 kernels up to and including 2.6.13 and later 2.4 redhat kernels
# (rmap vm without smaps) it can not be accurately determined how many pages
# are shared between processes in general or within a program in our case:
# http://lkml.org/lkml/2005/7/6/250
# A warning is printed if overestimation is possible.
# In addition for 2.6 kernels up to 2.6.9 inclusive, the shared
# value in /proc/$pid/statm is the total file-backed extent of a process.
# We ignore that, introducing more overestimation, again printing a warning.
# Since kernel 2.6.23-rc8-mm1 PSS is available in smaps, which allows
# us to calculate a more accurate value for the total RAM used by programs.
#
# Programs that use CLONE_VM without CLONE_THREAD are discounted by assuming
# they're the only programs that have the same /proc/$PID/smaps file for
# each instance. This will fail if there are multiple real instances of a
# program that then use CLONE_VM without CLONE_THREAD, or if a clone changes
# its memory map while we're checksumming each /proc/$PID/smaps.
#
# I don't take account of memory allocated for a program
# by other programs. For e.g. memory used in the X server for
# a program could be determined, but is not.
#
# FreeBSD is supported if linprocfs is mounted at /compat/linux/proc/
# FreeBSD 8.0 supports up to a level of Linux 2.6.16
import getopt
import time
import errno
import os
import sys
# The following exits cleanly on Ctrl-C or EPIPE
# while treating other exceptions as before.
def std_exceptions(etype, value, tb):
sys.excepthook = sys.__excepthook__
if issubclass(etype, KeyboardInterrupt):
pass
elif issubclass(etype, IOError) and value.errno == errno.EPIPE:
pass
else:
sys.__excepthook__(etype, value, tb)
sys.excepthook = std_exceptions
#
# Define some global variables
#
PAGESIZE = os.sysconf("SC_PAGE_SIZE") / 1024 #KiB
our_pid = os.getpid()
have_pss = 0
have_swap_pss = 0
class Proc:
def __init__(self):
uname = os.uname()
if uname[0] == "FreeBSD":
self.proc = '/compat/linux/proc'
else:
self.proc = '/proc'
def path(self, *args):
return os.path.join(self.proc, *(str(a) for a in args))
def open(self, *args):
try:
if sys.version_info < (3,):
return open(self.path(*args))
else:
return open(self.path(*args), errors='ignore')
except (IOError, OSError):
val = sys.exc_info()[1]
if (val.errno == errno.ENOENT or # kernel thread or process gone
val.errno == errno.EPERM or
val.errno == errno.EACCES):
raise LookupError
raise
proc = Proc()
#
# Functions
#
def parse_options():
try:
long_options = [
'split-args',
'help',
'total',
'discriminate-by-pid',
'swap'
]
opts, args = getopt.getopt(sys.argv[1:], "shtdSp:w:", long_options)
except getopt.GetoptError:
sys.stderr.write(help())
sys.exit(3)
if len(args):
sys.stderr.write("Extraneous arguments: %s\n" % args)
sys.exit(3)
# ps_mem.py options
split_args = False
pids_to_show = None
discriminate_by_pid = False
show_swap = False
watch = None
only_total = False
for o, a in opts:
if o in ('-s', '--split-args'):
split_args = True
if o in ('-t', '--total'):
only_total = True
if o in ('-d', '--discriminate-by-pid'):
discriminate_by_pid = True
if o in ('-S', '--swap'):
show_swap = True
if o in ('-h', '--help'):
sys.stdout.write(help())
sys.exit(0)
if o in ('-p',):
try:
pids_to_show = [int(x) for x in a.split(',')]
except:
sys.stderr.write(help())
sys.exit(3)
if o in ('-w',):
try:
watch = int(a)
except:
sys.stderr.write(help())
sys.exit(3)
return (
split_args,
pids_to_show,
watch,
only_total,
discriminate_by_pid,
show_swap
)
def help():
help_msg = 'Usage: ps_mem [OPTION]...\n' \
'Show program core memory usage\n' \
'\n' \
' -h, -help Show this help\n' \
' -p <pid>[,pid2,...pidN] Only show memory usage PIDs in the '\
'specified list\n' \
' -s, --split-args Show and separate by, all command line'\
' arguments\n' \
' -t, --total Show only the total value\n' \
' -d, --discriminate-by-pid Show by process rather than by program\n' \
' -S, --swap Show swap information\n' \
' -w <N> Measure and show process memory every'\
' N seconds\n'
return help_msg
# (major,minor,release)
def kernel_ver():
kv = proc.open('sys/kernel/osrelease').readline().split(".")[:3]
last = len(kv)
if last == 2:
kv.append('0')
last -= 1
while last > 0:
for char in "-_":
kv[last] = kv[last].split(char)[0]
try:
int(kv[last])
except:
kv[last] = 0
last -= 1
return (int(kv[0]), int(kv[1]), int(kv[2]))
#return Private,Shared
#Note shared is always a subset of rss (trs is not always)
def getMemStats(pid):
global have_pss
global have_swap_pss
mem_id = pid #unique
Private_lines = []
Shared_lines = []
Pss_lines = []
Rss = (int(proc.open(pid, 'statm').readline().split()[1])
* PAGESIZE)
Swap_lines = []
Swap_pss_lines = []
Swap = 0
Swap_pss = 0
if os.path.exists(proc.path(pid, 'smaps')): # stat
lines = proc.open(pid, 'smaps').readlines() # open
# Note we checksum smaps as maps is usually but
# not always different for separate processes.
mem_id = hash(''.join(lines))
for line in lines:
if line.startswith("Shared"):
Shared_lines.append(line)
elif line.startswith("Private"):
Private_lines.append(line)
elif line.startswith("Pss"):
have_pss = 1
Pss_lines.append(line)
elif line.startswith("Swap:"):
Swap_lines.append(line)
elif line.startswith("SwapPss:"):
have_swap_pss = 1
Swap_pss_lines.append(line)
Shared = sum([int(line.split()[1]) for line in Shared_lines])
Private = sum([int(line.split()[1]) for line in Private_lines])
#Note Shared + Private = Rss above
#The Rss in smaps includes video card mem etc.
if have_pss:
pss_adjust = 0.5 # add 0.5KiB as this avg error due to truncation
Pss = sum([float(line.split()[1])+pss_adjust for line in Pss_lines])
Shared = Pss - Private
# Note that Swap = Private swap + Shared swap.
Swap = sum([int(line.split()[1]) for line in Swap_lines])
if have_swap_pss:
# The kernel supports SwapPss, that shows proportional swap share.
# Note that Swap - SwapPss is not Private Swap.
Swap_pss = sum([int(line.split()[1]) for line in Swap_pss_lines])
elif (2,6,1) <= kernel_ver() <= (2,6,9):
Shared = 0 #lots of overestimation, but what can we do?
Private = Rss
else:
Shared = int(proc.open(pid, 'statm').readline().split()[2])
Shared *= PAGESIZE
Private = Rss - Shared
return (Private, Shared, mem_id, Swap, Swap_pss)
def getCmdName(pid, split_args, discriminate_by_pid):
cmdline = proc.open(pid, 'cmdline').read().split("\0")
if cmdline[-1] == '' and len(cmdline) > 1:
cmdline = cmdline[:-1]
path = proc.path(pid, 'exe')
try:
path = os.readlink(path)
# Some symlink targets were seen to contain NULs on RHEL 5 at least
# https://github.com/pixelb/scripts/pull/10, so take string up to NUL
path = path.split('\0')[0]
except OSError:
val = sys.exc_info()[1]
if (val.errno == errno.ENOENT or # either kernel thread or process gone
val.errno == errno.EPERM or
val.errno == errno.EACCES):
raise LookupError
raise
if split_args:
return ' '.join(cmdline).replace('\n', ' ')
if path.endswith(" (deleted)"):
path = path[:-10]
if os.path.exists(path):
path += " [updated]"
else:
#The path could be have prelink stuff so try cmdline
#which might have the full path present. This helped for:
#/usr/libexec/notification-area-applet.#prelink#.fX7LCT (deleted)
if os.path.exists(cmdline[0]):
path = cmdline[0] + " [updated]"
else:
path += " [deleted]"
exe = os.path.basename(path)
cmd = proc.open(pid, 'status').readline()[6:-1]
if exe.startswith(cmd):
cmd = exe #show non truncated version
#Note because we show the non truncated name
#one can have separated programs as follows:
#584.0 KiB + 1.0 MiB = 1.6 MiB mozilla-thunder (exe -> bash)
# 56.0 MiB + 22.2 MiB = 78.2 MiB mozilla-thunderbird-bin
if sys.version_info >= (3,):
cmd = cmd.encode(errors='replace').decode()
if discriminate_by_pid:
cmd = '%s [%d]' % (cmd, pid)
return cmd
#The following matches "du -h" output
#see also human.py
def human(num, power="Ki", units=None):
if units is None:
powers = ["Ki", "Mi", "Gi", "Ti"]
while num >= 1000: #4 digits
num /= 1024.0
power = powers[powers.index(power)+1]
return "%.1f %sB" % (num, power)
else:
return "%.f" % ((num * 1024) / units)
def cmd_with_count(cmd, count):
if count > 1:
return "%s (%u)" % (cmd, count)
else:
return cmd
#Warn of possible inaccuracies
#2 = accurate & can total
#1 = accurate only considering each process in isolation
#0 = some shared mem not reported
#-1= all shared mem not reported
def shared_val_accuracy():
"""http://wiki.apache.org/spamassassin/TopSharedMemoryBug"""
kv = kernel_ver()
pid = os.getpid()
if kv[:2] == (2,4):
if proc.open('meminfo').read().find("Inact_") == -1:
return 1
return 0
elif kv[:2] == (2,6):
if os.path.exists(proc.path(pid, 'smaps')):
if proc.open(pid, 'smaps').read().find("Pss:")!=-1:
return 2
else:
return 1
if (2,6,1) <= kv <= (2,6,9):
return -1
return 0
elif kv[0] > 2 and os.path.exists(proc.path(pid, 'smaps')):
return 2
else:
return 1
def show_shared_val_accuracy( possible_inacc, only_total=False ):
level = ("Warning","Error")[only_total]
if possible_inacc == -1:
sys.stderr.write(
"%s: Shared memory is not reported by this system.\n" % level
)
sys.stderr.write(
"Values reported will be too large, and totals are not reported\n"
)
elif possible_inacc == 0:
sys.stderr.write(
"%s: Shared memory is not reported accurately by this system.\n" % level
)
sys.stderr.write(
"Values reported could be too large, and totals are not reported\n"
)
elif possible_inacc == 1:
sys.stderr.write(
"%s: Shared memory is slightly over-estimated by this system\n"
"for each program, so totals are not reported.\n" % level
)
sys.stderr.close()
if only_total and possible_inacc != 2:
sys.exit(1)
def get_memory_usage(pids_to_show, split_args, discriminate_by_pid,
include_self=False, only_self=False):
cmds = {}
shareds = {}
mem_ids = {}
count = {}
swaps = {}
shared_swaps = {}
for pid in os.listdir(proc.path('')):
if not pid.isdigit():
continue
pid = int(pid)
# Some filters
if only_self and pid != our_pid:
continue
if pid == our_pid and not include_self:
continue
if pids_to_show is not None and pid not in pids_to_show:
continue
try:
cmd = getCmdName(pid, split_args, discriminate_by_pid)
except LookupError:
#operation not permitted
#kernel threads don't have exe links or
#process gone
continue
try:
private, shared, mem_id, swap, swap_pss = getMemStats(pid)
except RuntimeError:
continue #process gone
if shareds.get(cmd):
if have_pss: #add shared portion of PSS together
shareds[cmd] += shared
elif shareds[cmd] < shared: #just take largest shared val
shareds[cmd] = shared
else:
shareds[cmd] = shared
cmds[cmd] = cmds.setdefault(cmd, 0) + private
if cmd in count:
count[cmd] += 1
else:
count[cmd] = 1
mem_ids.setdefault(cmd, {}).update({mem_id: None})
# Swap (overcounting for now...)
swaps[cmd] = swaps.setdefault(cmd, 0) + swap
if have_swap_pss:
shared_swaps[cmd] = shared_swaps.setdefault(cmd, 0) + swap_pss
else:
shared_swaps[cmd] = 0
# Total swaped mem for each program
total_swap = 0
# Total swaped shared mem for each program
total_shared_swap = 0
# Add shared mem for each program
total = 0
for cmd in cmds:
cmd_count = count[cmd]
if len(mem_ids[cmd]) == 1 and cmd_count > 1:
# Assume this program is using CLONE_VM without CLONE_THREAD
# so only account for one of the processes
cmds[cmd] /= cmd_count
if have_pss:
shareds[cmd] /= cmd_count
cmds[cmd] = cmds[cmd] + shareds[cmd]
total += cmds[cmd] # valid if PSS available
total_swap += swaps[cmd]
if have_swap_pss:
total_shared_swap += shared_swaps[cmd]
sorted_cmds = sorted(cmds.items(), key=lambda x:x[1])
sorted_cmds = [x for x in sorted_cmds if x[1]]
return sorted_cmds, shareds, count, total, swaps, shared_swaps, \
total_swap, total_shared_swap
def print_header(show_swap, discriminate_by_pid):
output_string = " Private + Shared = RAM used"
if show_swap:
if have_swap_pss:
output_string += " " * 5 + "Shared Swap"
output_string += " Swap used"
output_string += "\tProgram"
if discriminate_by_pid:
output_string += "[pid]"
output_string += "\n\n"
sys.stdout.write(output_string)
def print_memory_usage(sorted_cmds, shareds, count, total, swaps, total_swap,
shared_swaps, total_shared_swap, show_swap):
for cmd in sorted_cmds:
output_string = "%9s + %9s = %9s"
output_data = (human(cmd[1]-shareds[cmd[0]]),
human(shareds[cmd[0]]), human(cmd[1]))
if show_swap:
if have_swap_pss:
output_string += "\t%9s"
output_data += (human(shared_swaps[cmd[0]]),)
output_string += " %9s"
output_data += (human(swaps[cmd[0]]),)
output_string += "\t%s\n"
output_data += (cmd_with_count(cmd[0], count[cmd[0]]),)
sys.stdout.write(output_string % output_data)
if have_pss:
if show_swap:
if have_swap_pss:
sys.stdout.write("%s\n%s%9s%s%9s%s%9s\n%s\n" %
("-" * 61, " " * 24, human(total), " " * 7,
human(total_shared_swap), " " * 3,
human(total_swap), "=" * 61))
else:
sys.stdout.write("%s\n%s%9s%s%9s\n%s\n" %
("-" * 45, " " * 24, human(total), " " * 3,
human(total_swap), "=" * 45))
else:
sys.stdout.write("%s\n%s%9s\n%s\n" %
("-" * 33, " " * 24, human(total), "=" * 33))
def verify_environment(pids_to_show):
if os.geteuid() != 0 and not pids_to_show:
sys.stderr.write("Sorry, root permission required, or specify pids with -p\n")
sys.stderr.close()
sys.exit(1)
try:
kernel_ver()
except (IOError, OSError):
val = sys.exc_info()[1]
if val.errno == errno.ENOENT:
sys.stderr.write(
"Couldn't access " + proc.path('') + "\n"
"Only GNU/Linux and FreeBSD (with linprocfs) are supported\n")
sys.exit(2)
else:
raise
def main():
split_args, pids_to_show, watch, only_total, discriminate_by_pid, \
show_swap = parse_options()
verify_environment(pids_to_show)
if not only_total:
print_header(show_swap, discriminate_by_pid)
if watch is not None:
try:
sorted_cmds = True
while sorted_cmds:
sorted_cmds, shareds, count, total, swaps, shared_swaps, \
total_swap, total_shared_swap = \
get_memory_usage(pids_to_show, split_args,
discriminate_by_pid)
if only_total and have_pss:
sys.stdout.write(human(total, units=1)+'\n')
elif not only_total:
print_memory_usage(sorted_cmds, shareds, count, total,
swaps, total_swap, shared_swaps,
total_shared_swap, show_swap)
sys.stdout.flush()
time.sleep(watch)
else:
sys.stdout.write('Process does not exist anymore.\n')
except KeyboardInterrupt:
pass
else:
# This is the default behavior
sorted_cmds, shareds, count, total, swaps, shared_swaps, total_swap, \
total_shared_swap = get_memory_usage(pids_to_show, split_args,
discriminate_by_pid)
if only_total and have_pss:
sys.stdout.write(human(total, units=1)+'\n')
elif not only_total:
print_memory_usage(sorted_cmds, shareds, count, total, swaps,
total_swap, shared_swaps, total_shared_swap,
show_swap)
# We must close explicitly, so that any EPIPE exception
# is handled by our excepthook, rather than the default
# one which is reenabled after this script finishes.
sys.stdout.close()
vm_accuracy = shared_val_accuracy()
show_shared_val_accuracy( vm_accuracy, only_total )
if __name__ == '__main__': main()
+3
View File
@@ -0,0 +1,3 @@
#!/bin/sh -e
exit 0
+6
View File
@@ -0,0 +1,6 @@
#!/bin/sh -e
find /etc/update-motd.d -type f,l ! -name '98*' -delete
/bin/run-parts /etc/update-motd.d
exit 0
Executable
+289
View File
@@ -0,0 +1,289 @@
#!/usr/bin/perl
################################################################################ rt - restic wrapper (mwx'2026)
use File::Basename;use Term::ReadKey;use Getopt::Std;getopts('c:n:i:t:p:');
$VERSION='1.5.6';
$CONFIG='default';$CONFIG=$opt_c if ($opt_c!~/^\s*$/); # set deault options options
chomp($host=`hostname`);$host=~s/^([^\.]+).*/$1/;
$NAME=$host;
$POLICY='--keep-daily 14 --keep-weekly 12 --keep-monthly 12 --keep-yearly 100';
for ('/db/etc/rt.conf',$ENV{"HOME"}.'/.rt.conf') { $CONF=$_ if (-f $_);} # find/load/check configuration
die "Fatal: no config file found\n" if ($CONF=~/^\s*$/);
require($CONF);
$NAME = $opt_n if ($opt_n!~/^\s*$/); # overwrite options with commandline (name, id, tag, password)
$ID = $opt_i if ($opt_i!~/^\s*$/);
$TAG = $opt_t if ($opt_t!~/^\s*$/);
$PASSWORD = $opt_p if ($opt_p!~/^\s*$/);
for ('$NAME','$TARGET','$SRC','$RESTIC','$LOG') {
die "Fatal: missing $_ in config\n" if (eval($_)=~/^\s*$/);
}
$ID='0' if ($ID=~/^\s*$/); # set repository
$REPOSITORY="$TARGET/$NAME.$ID";
print "rt $VERSION, Name: $NAME, Repository: $REPOSITORY, Config: $CONFIG\n";
open(PW,"> $PWFILE");print PW "$PASSWORD\n";close(PW);
$ENV{'RESTIC_REPOSITORY'} = $REPOSITORY; # set environment
$ENV{'RESTIC_PASSWORD_FILE'} = $PWFILE;
$r=`$RESTIC version`; # find restic version
if ($r=~/restic\s+([\d\.]+)\s+compiled/) {
$RVERSION=$1;
@RV=split('\.',$1);$RVN=sprintf("%02d%02d%02d",$RV[0],$RV[1],$RV[2]);
}
$TMP="/tmp";
my(@z)=localtime(time());$z[4]+=1;$z[5]-=100;$z[6]+=1;
$z=sprintf("%02d\.%02d\.%02d %02d\:%02d",$z[3],$z[4],$z[5],$z[2],$z[1]);
if ($ARGV[0] eq 'cleanup') { # ----------------------------------------------------------- forget, prune, check
$ts=time();
print "$RESTIC -r $REPOSITORY forget $POLICY --prune\n"; # forget & prune
$rms=0;
open(IN,"$RESTIC forget $POLICY --prune |");
while(<IN>) {
print;
$rms=$1 if (/^(\d+)\s+snapshots\s+have\s+been\s+removed/)
}
print "$RESTIC -r $REPOSITORY check\n"; # check
open(IN,"$RESTIC check |");
while(<IN>) {
print;
chomp($lastline=$_);
}
$rt=time()-$ts;
if ($HTTP_LOG!~/^\s*$/) { # http loging
$tmp= "RTC,$VERSION/$RVERSION,$NAME,$ID,$CONFIG,$TAG,$REPOSITORY,$lastline,$rms,$rt";
use LWP::UserAgent;
my $ua = new LWP::UserAgent();
$response = $ua->post("https://$HTTP_LOG", Content => $tmp);
}
if ($MY_HOST!~/^\s*$/) { # mysql loging
use DBI;
$dbh=DBI->connect("DBI:mysql:$MY_DB:$MY_HOST:$MY_PORT",$MY_USER,$MY_PASSWD,{RaiseError=>1,PrintError=>1});
$sql="INSERT INTO resticcleanup VALUES(now(),'$VERSION/$RVERSION',
'$NAME','$ID','$CONFIG','$TAG','$REPOSITORY','$lastline',$rms,$rt)";
$dbh->do($sql);
}
} elsif ($ARGV[0] eq 'backup' || $ARGV[0] eq 'delaybackup') { # ----------------------------------- run backup
$snap='';$added='';$total='';
if ($ARGV[0] eq 'delaybackup') {
printf "$z --- delaybackup (%d sec) ---\n",&dfn($NAME);
sleep(&dfn($NAME))
} else {
printf "$z --- backup ---\n";
}
print "$RESTIC -r $REPOSITORY -vv backup $EXCLUDE $SRC\n";
$ts=time();
open(IN,"$RESTIC -vv backup $EXCLUDE $SRC |");
while(<IN>) {
print if (!/^unchanged/);
if (/Files:\s*(\d+)\s*new,\s*(\d+)\s*changed,\s*(\d+)\s*unmodified/) {
$fn+=$1;$fc+=$2;$fu+=$3;
}
if (/Dirs:\s*(\d+)\s*new,\s*(\d+)\s*changed,\s*(\d+)\s*unmodified/) {
$fn+=$1;$fc+=$2;$fu+=$3;
}
$snap=$1 if (/snapshot\s+([^\s]+)\s+saved/);
$added=&deform($1) if (/Added\s+to\s+the\s+repo:\s+(.*)/);
$added=&deform($1) if (/Added\s+to\s+the\s+repository:\s+([^\(]*)/);
$total=&deform($1) if (/processed\s+\d+\s+files,\s+(.*)\s+in/);
$files=$1 if (/processed\s+(\d+)\s+files,\s+.*\s+in/);
}
if ($snap!~/^\s*$/ && $added!~/^\s*$/ && $total!~/^\s*$/) {
$rt=time()-$ts;
open(LOG,">> $LOG"); # write logfile
print LOG "$z [$NAME/$snap], Files: $files, New: $fn, Changed: $fc, ".
"Unmodified: $fu, Added: $added, Total: $total, Runtime: $rt\n";
close(LOG);
if ($HTTP_LOG!~/^\s*$/) { # http loging
$tmp= "RT,$VERSION/$RVERSION,$NAME,$ID,$CONFIG,$TAG,$REPOSITORY,".
"$snap,$files,$fn,$fc,$fu,$added,$total,$rt";
use LWP::UserAgent;
my $ua = new LWP::UserAgent();
$response = $ua->post("https://$HTTP_LOG", Content => $tmp);
}
if ($MY_HOST!~/^\s*$/) { # mysql loging
use DBI;
$dbh=DBI->connect("DBI:mysql:$MY_DB:$MY_HOST:$MY_PORT",$MY_USER,$MY_PASSWD,{RaiseError=>1,PrintError=>1});
$sql="INSERT INTO restic VALUES(now(),'$VERSION/$RVERSION',
'$NAME','$ID','$CONFIG','$TAG','$REPOSITORY','$snap',$files,$fn,$fc,$fu,$added,$total,$rt)";
$dbh->do($sql);
}
}
} elsif ($ARGV[0] eq 'password') { # ----------------------------------------------- setup restic password file
system("mkdir -p ".&dirname($PWFILE)) if (!-d &dirname($PWFILE));
print "new repository password: ";
chomp($pwd = &ReadLine(0));
if ($pwd!~/^\s*$/) {
open(OUT,"> $PWFILE");print OUT "$pwd\n";close(OUT);
}
} elsif ($ARGV[0] eq 'help' || $ARGV[0]=~/^\s*$/) { # ----------------------------------------------- show help
&help();
&advhelp() if ($ARGV[0] eq 'help');
} else { # ------------------------------------------------------------------------------ other restic commands
system("$RESTIC @ARGV");
if ($ARGV[0] eq 'init') {
system("rm -f /var/log/restic.log /var/log/rt.log");
}
}
####################################################################################################### SUPPORT
sub deform() { # transform sizes
my($s)=@_;
return $1<<40 if ($s=~/([\d\.]+)\s*TiB/i);
return $1<<30 if ($s=~/([\d\.]+)\s*GiB/i);
return $1<<20 if ($s=~/([\d\.]+)\s*MiB/i);
return $1<<10 if ($s=~/([\d\.]+)\s*KiB/i);
return $1 if ($s=~/([\d\.]+)\s*B/i);
}
sub dfn() { # calculate delay from string
my($s)=@_;my $v=0;my $n=1;
$v+=ord($_)*$n++ for (split('',$s));
return ($v%24)*5*60;
}
sub help() { # help
print "\nrt v$VERSION - restic wrapper (mwx'2022)\n";
print <<'ENDOFHELP';
USAGE:
------
rt [options] backup run restic backup
rt [options] delaybackup run delayed restic backup
rt [options] cleanup run forget, prune and check
rt [options] password setup restic password file
rt [options] init initialize restic repository
rt help advanced help
rt <command> other commands will be forwarded directly to restic
options: -c <config> sets $CONFIG (default is 'default')
-n <name> overwrite $NAME
-i <id> overwrite $ID
-t <tag> overwrite $TAG
-p <password> overwrite $PASSWORD
ENDOFHELP
}
sub advhelp() { # advanced help
print <<'ENDOFHELP';
CONFIG FILE (/db/etc/rt.conf)
-----------------------------
$NAME = 'NAME'; # backup name
$ID = '0'; # repository id (optional, default is 0)
$TAG = 'TAG'; # custom tag for logging (optional)
$TARGET = "/backup/restic"; # restic target
= "s3:http://188.68.34.116:9234/restic";
= "b2:mwx002:restic/server";
$SRC = '/etc /db /var /root /home /usr/local /opt'; # source paths
$EXCLUDE = '-e /var/lib/lxcfs -e /var/log/journal'; # exclude options
$RESTIC = '/db/bin/restic'; # restic binary
$LOG = '/var/log/restic.log'; # log file
$PWFILE = '/db/etc/restic.pw'; # restic password file
$HTTP_LOG = 'HOST'; # host for logging via https
$MY_HOST = 'HOST'; # mysql host
$MY_PORT = 'PORT'; # mysql port
$MY_USER = 'USER'; # mysql user
$MY_PASSWD = 'PASSWORD'; # mysql password
$MY_DB = 'DATABASE'; # mysql database
$ENV{'AWS_ACCESS_KEY_ID'} = '123456'; # S3 token example
$ENV{'AWS_SECRET_ACCESS_KEY'} = 'abcdefg';
$ENV{'B2_ACCOUNT_ID'} = '123456'; # B2 token example
$ENV{'B2_ACCOUNT_KEY'} = 'abcdefg';
MYSQL LOGGING TABLES:
---------------------
CREATE TABLE `restic` (
`ts` timestamp NULL DEFAULT NULL,
`version` varchar(255) DEFAULT NULL,
`name` varchar(255) DEFAULT NULL,
`id` int(11) DEFAULT NULL,
`config` varchar(255) DEFAULT NULL,
`tag` varchar(255) DEFAULT NULL,
`repository` varchar(255) DEFAULT NULL,
`snapshot` varchar(255) DEFAULT NULL,
`files` bigint(11) DEFAULT NULL,
`new` bigint(11) DEFAULT NULL,
`changed` bigint(11) DEFAULT NULL,
`unmodified` bigint(11) DEFAULT NULL,
`added` bigint(11) DEFAULT NULL,
`total` bigint(11) DEFAULT NULL,
`runtime` int(11) DEFAULT NULL
) ENGINE=InnoDB DEFAULT CHARSET=latin1;
CREATE TABLE `resticcleanup` (
`ts` timestamp NULL DEFAULT NULL,
`version` varchar(255) DEFAULT NULL,
`name` varchar(255) DEFAULT NULL,
`id` int(11) DEFAULT NULL,
`config` varchar(255) DEFAULT NULL,
`tag` varchar(255) DEFAULT NULL,
`repository` varchar(255) DEFAULT NULL,
`txt` varchar(255) DEFAULT NULL,
`rmsnap` int(11) DEFAULT NULL,
`runtime` int(11) DEFAULT NULL
) ENGINE=InnoDB DEFAULT CHARSET=latin1;
CRONTAB ENTRIES:
---------------
0 2 * * * /db/bin/rt delaybackup >> /var/log/rt.log 2>&1
0 7 * * 6 /db/bin/rt cleanup >> /var/log/rt.log 2>&1
ENDOFHELP
}
########################################################################################################### END
+43
View File
@@ -0,0 +1,43 @@
#!/usr/bin/perl
####################################################################### telemetry collector - v1.0.0 (mwx'2020)
use LWP::UserAgent;
chomp($host=`hostname`);$host=~s/\..*$//i;
$url = 'http://monitor.rz-berlin.mpg.de/telemetry.php';
############################################################################################ collect disk usage
$data = "df,$host,";
open(IN,"/bin/df -k -T |");
while(<IN>) {
@df=split(' ');
if ($df[1]=~/^ext/ || $df[1]=~/^nfs/) {
$total=($df[2]/1024/1024);
$avail=($df[4]/1024/1024);
$used=$total - $avail;
$per=$used*100/$total;
$data.=sprintf("$df[6],%.3f,%.3f,%.3f,%.2f,",$total,$avail,$used,$per);
}
}
$data .= time();
print "$data\n";
my $ua = new LWP::UserAgent();
$response = $ua->post($url, Content => $data);
########################################################################################### collect system load
open(UPTIME,"/usr/bin/uptime |");chomp($_=<UPTIME>);
if (/load\s*averages*\:\s*[\d\.]+\,\s*[\d\.]+\,\s*([\d\.]+)/) {
$load=$1;
}
$data = "load,$host,$load,".time();
print "$data\n";
my $ua = new LWP::UserAgent();
$response = $ua->post($url, Content => $data);
########################################################################################################### END
+26
View File
@@ -0,0 +1,26 @@
# This file is part of systemd.
#
# systemd is free software; you can redistribute it and/or modify it under the
# terms of the GNU Lesser General Public License as published by the Free
# Software Foundation; either version 2.1 of the License, or (at your option)
# any later version.
#
# Entries in this file show the compile time defaults. Local configuration
# should be created by either modifying this file (or a copy of it placed in
# /etc/ if the original file is shipped in /usr/), or by creating "drop-ins" in
# the /etc/systemd/timesyncd.conf.d/ directory. The latter is generally
# recommended. Defaults can be restored by simply deleting the main
# configuration file and all drop-ins located in /etc/.
#
# Use 'systemd-analyze cat-config systemd/timesyncd.conf' to display the full config.
#
# See timesyncd.conf(5) for details.
[Time]
NTP=de.pool.ntp.org
FallbackNTP=ptbtime1.ptb.de 0.debian.pool.ntp.org 1.debian.pool.ntp.org 2.debian.pool.ntp.org 3.debian.pool.ntp.org
#RootDistanceMaxSec=5
#PollIntervalMinSec=32
#PollIntervalMaxSec=2048
#ConnectionRetrySec=30
#SaveIntervalSec=60
+96
View File
@@ -0,0 +1,96 @@
unsetopt always_last_prompt
autoload -U colors && colors
zle_highlight+=(paste:none)
unsetopt BEEP
autoload -U compinit
compinit
bindkey "\e[3~" delete-char
bindkey '^[[F' end-of-line
bindkey '^[[H' beginning-of-line
bindkey '^[[6~' end-of-line
bindkey '^[[5~' beginning-of-line
HISTFILE=~/.zsh_history
HISTSIZE=10000
SAVEHIST=10000
setopt HIST_REDUCE_BLANKS
setopt EXTENDED_HISTORY
setopt INC_APPEND_HISTORY
setopt HIST_EXPIRE_DUPS_FIRST
setopt HIST_IGNORE_DUPS
setopt HIST_IGNORE_ALL_DUPS
setopt HIST_IGNORE_SPACE
setopt HIST_SAVE_NO_DUPS
setopt HIST_REDUCE_BLANKS
setopt HIST_VERIFY
setopt HIST_BEEP
setopt SHARE_HISTORY
HN=`hostname -s`
PP=">"; if [[ $USER == "root" ]] then PP=">>" fi
PROMPT="%n@${HN}:%{$fg[white]%}%~%{$reset_color%} $PP "
alias a='alias'
alias h='history -d -10000'
alias L='last -16'
alias ls='ls -F'
alias ll='ls -lsh'
alias lt='ls -ltrh'
alias lla='ls -lsah'
alias lta='ls -ltrah'
alias s='ssh'
alias sr='ssh -l root'
alias rs='rsync -va --delete --stats'
alias p='ps -ef | grep ${LOGNAME}'
alias P='ps -ef'
alias af='apt-cache search'
alias ai='apt-get install'
alias sd='lsblk -o NAME,FSTYPE,SIZE,MOUNTPOINT,LABEL'
alias ga='git add -v .'
alias gb='git branch'
alias gs='git status'
alias gl="git log -32 --reverse --date=local --format='%h %cd %s'"
alias gc='git commit -m'
alias gu='git checkout'
alias gp='git push'
alias y='yazi'
alias f='fzf'
export TERM=xterm-256color
export COLORTERM=truecolor
PATH=/db/bin:"$PATH"
if [ -f /db/go/bin/go ]; then
PATH="$PATH":/db/go/bin:$(/db/go/bin/go env GOPATH)/bin
export CGO_ENABLED=0
fi
tcsh_autolist() { if [[ -z ${LBUFFER// } ]]
then BUFFER="ls " CURSOR=3 zle list-choices
else zle expand-or-complete-prefix; fi }
zle -N tcsh_autolist
bindkey '^I' tcsh_autolist
if [ -f /etc/zshcomp ]; then
source /etc/zshcom
fi
[ -f /etc/zsh/zshcomp ] && source /etc/zsh/zshcomp
if [ -f /db/etc/omp.json ]; then
if (( $+commands[oh-my-posh] )); then
eval "$(oh-my-posh init zsh --config /db/etc/omp.json )";
fi
fi
if (( $+commands[lsd] )); then
alias lx='lsd --icon never --tree -la'
alias ll='lsd --icon never -la'
alias lt='lsd --icon never -latr'
alias ls='lsd --icon never -a'
fi