Skip to content
GitLab
Explore
Sign in
Primary navigation
Search or go to…
Project
I
iml-backup
Manage
Activity
Members
Labels
Plan
Issues
Issue boards
Milestones
Wiki
Code
Merge requests
Repository
Branches
Commits
Tags
Repository graph
Compare revisions
Snippets
Build
Pipelines
Jobs
Pipeline schedules
Artifacts
Deploy
Releases
Container registry
Model registry
Operate
Environments
Monitor
Incidents
Analyze
Value stream analytics
Contributor analytics
CI/CD analytics
Repository analytics
Model experiments
Help
Help
Support
GitLab documentation
Compare GitLab plans
Community forum
Contribute to GitLab
Provide feedback
Keyboard shortcuts
?
Snippets
Groups
Projects
Show more breadcrumbs
IML Open Source
iml-backup
Commits
1008ddf9
Commit
1008ddf9
authored
1 year ago
by
Hahn Axel (hahn)
Browse files
Options
Downloads
Patches
Plain Diff
Sqlite Handle profiles; restore into existing db file; restore file owner and perms
parent
96823fd0
Branches
Branches containing commit
No related tags found
1 merge request
!129
Db Profiles
Changes
2
Hide whitespace changes
Inline
Side-by-side
Showing
2 changed files
localdump.sh
+113
-55
113 additions, 55 deletions
localdump.sh
plugins/localdump/sqlite.sh
+32
-51
32 additions, 51 deletions
plugins/localdump/sqlite.sh
with
145 additions
and
106 deletions
localdump.sh
+
113
−
55
View file @
1008ddf9
...
@@ -66,22 +66,32 @@
...
@@ -66,22 +66,32 @@
# ---------- GETTER
# ---------- GETTER
# write debug output if DBD_DEBUG is enabled
# param string text to show
function
dbdetect._wd
(){
function
dbdetect._wd
(){
test
"
$DBD_DEBUG
"
-eq
"1"
&&
echo
"DEBUG:
$*
"
>
&2
test
"
$DBD_DEBUG
"
-eq
"1"
&&
echo
"DEBUG:
$*
"
>
&2
}
}
# get a list of all config files
# param string full path of ini file
function
dbdetect.getConfigs
(){
function
dbdetect.getConfigs
(){
find
${
DBD_BASEDIR
}
-type
f
-name
"*.ini"
find
${
DBD_BASEDIR
}
-type
f
-name
"*.ini"
}
}
# get type from given ini file
# param string full path of ini file
function
dbdetect.getType
(){
function
dbdetect.getType
(){
basename
"
$1
"
|
cut
-d
"_"
-f
1 |
sed
"s,
\.
ini
$,
,"
basename
"
$1
"
|
cut
-d
"_"
-f
1 |
sed
"s,
\.
ini
$,
,"
}
}
# get profile from given ini file
# param string full path of ini file
function
dbdetect.getProfile
(){
function
dbdetect.getProfile
(){
basename
"
$1
"
|
cut
-d
"_"
-f
1- |
sed
"s,
\.
ini
$,
,"
basename
"
$1
"
|
cut
-d
"_"
-f
1- |
sed
"s,
\.
ini
$,
,"
}
}
# check if the requirements for a database match
# param string full path of ini file to check
function
dbdetect.exists
(){
function
dbdetect.exists
(){
local
_config
=
"
$1
"
local
_config
=
"
$1
"
...
@@ -93,14 +103,17 @@ function dbdetect.exists(){
...
@@ -93,14 +103,17 @@ function dbdetect.exists(){
ini.set
"
$_config
"
"detect"
ini.set
"
$_config
"
"detect"
# --- check tcp
# --- check tcp
local
tcpport
;
tcpport
=
$(
ini.value
"tcp"
)
local
tcpport
;
tcpport
=
$(
ini.value
"tcp"
)
local
tcptarget
;
tcptarget
=
$(
ini.value
"tcp-target"
)
if
[
-n
"
$tcpport
"
]
;
then
if
{
!
>
/dev/tcp/
$tcptarget
/
$tcpport
;
}
>
/dev/null 2>&1
;
then
local
tcptarget
;
tcptarget
=
$(
ini.value
"tcp-target"
)
# echo "No port tcp $tcpport available"
tcptarget
=
${
tcptarget
:-
localhost
}
dbdetect._wd
"... No port tcp
$tcpport
available on
$tcptarget
"
if
{
!
>
/dev/tcp/
$tcptarget
/
$tcpport
;
}
>
/dev/null 2>&1
;
then
return
1
# echo "No port tcp $tcpport available"
dbdetect._wd
"... No port tcp
$tcpport
available on
$tcptarget
"
return
1
fi
dbdetect._wd
"... Found tcp
$tcpport
on
$tcptarget
."
fi
fi
dbdetect._wd
"... Found tcp
$tcpport
on
$tcptarget
."
# --- check tcp process
# --- check tcp process
local
tcpprocess
;
tcpprocess
=
$(
ini.value
"tcp-process"
)
local
tcpprocess
;
tcpprocess
=
$(
ini.value
"tcp-process"
)
...
@@ -139,21 +152,45 @@ function dbdetect.exists(){
...
@@ -139,21 +152,45 @@ function dbdetect.exists(){
dbdetect._wd
"... Process
${
process
}
was found"
dbdetect._wd
"... Process
${
process
}
was found"
fi
fi
# --- check db files
local
filetype
;
filetype
=
$(
ini.value
"type"
)
if
[
-n
"
${
filetype
}
"
]
;
then
local
myfiles
;
declare
-a
myfiles
for
myfile
in
$(
ini.value
"file[]"
)
do
if
!
file
-b
"
${
myfile
}
"
|
grep
-i
"
$filetype
"
>
/dev/null
;
then
dbdetect._wd
"... File
${
myfile
}
is no type
$filetype
"
return
1
fi
dbdetect._wd
"... File
${
myfile
}
is type
$filetype
"
myfiles+
=
"
${
myfile
}
|"
done
fi
# --- OK, everything was found ... we initialize it
dbdetect._wd
"OK, match:
$_config
"
ini.set
"
$_config
"
"set"
ini.set
"
$_config
"
"set"
local
value
local
value
local
dbuser
=
$(
ini.value
"dbuser"
)
local
dbuser
=
$(
ini.value
"dbuser"
)
local
dbpassword
=
$(
ini.value
"dbpassword"
)
local
dbpassword
=
$(
ini.value
"dbpassword"
)
for
mykey
in
$(
ini.keys
)
for
mykey
in
su
env
params
do
do
# params = '--port={{tcp}} --password={{dbpassword}} --user={{dbuser}} --host={{tcp-target}}'
value
=
"
$(
ini.value
"
$mykey
"
)
"
value
=
"
$(
ini.value
"
$mykey
"
)
"
value
=
"
${
value
//\{\{tcp\
}
\}
/
$tcpport
}"
value
=
"
${
value
//\{\{tcp\
}
\}
/
$tcpport
}"
value
=
"
${
value
//\{\{tcp-target\
}
\}
/
$tcptarget
}"
value
=
"
${
value
//\{\{tcp-target\
}
\}
/
$tcptarget
}"
value
=
"
${
value
//\{\{dbuser\
}
\}
/
$dbuser
}"
value
=
"
${
value
//\{\{dbuser\
}
\}
/
$dbuser
}"
value
=
"
${
value
//\{\{dbpassword\
}
\}
/
$dbpassword
}"
value
=
"
${
value
//\{\{dbpassword\
}
\}
/
$dbpassword
}"
value
=
"
${
value
//\{\{file\[\]\
}
\}
/
$myfiles
}"
DBD_PARAMS[
$mykey
]=
"
$value
"
DBD_PARAMS[
$mykey
]=
"
$value
"
dbdetect._wd
">>>
$mykey
=
$value
"
done
done
dbdetect._wd
">>> files =
$myfiles
"
DBD_PARAMS[files]
=
"
$myfiles
"
return
0
return
0
}
}
...
@@ -167,6 +204,13 @@ function dbdetect.getParams(){
...
@@ -167,6 +204,13 @@ function dbdetect.getParams(){
echo
"
${
DBD_PARAMS
[
'params'
]
}
"
echo
"
${
DBD_PARAMS
[
'params'
]
}
"
}
}
# for backup scripts: get checked files from [detect] -> file[]
#
function
dbdetect.getFiles
(){
echo
"
${
DBD_PARAMS
[
'files'
]
}
"
|
tr
"|"
"
\n
"
}
# set variables in [set] -> env = ...
# set variables in [set] -> env = ...
# USAGE:
# USAGE:
# eval $( dbdetect.setenv )
# eval $( dbdetect.setenv )
...
@@ -189,26 +233,6 @@ function dbdetect.runas(){
...
@@ -189,26 +233,6 @@ function dbdetect.runas(){
# FUNCTIONS 4 DB-WRAPPER
# FUNCTIONS 4 DB-WRAPPER
# ----------------------------------------------------------------------
# ----------------------------------------------------------------------
function
db.init
(){
BACKUP_BASEDIR
=
$(
_j_getvar
${
JOBFILE
}
"dir-localdumps"
)
# check
if
[
-z
"
$BACKUP_BASEDIR
"
]
;
then
color error
echo
ERROR: missing config
for
backup target.
echo
There must be an entry dir-localdumps
in
${
JOBFILE
}
color reset
exit
1
fi
BACKUP_PLUGINDIR
=
$(
dirname
$0
)
/plugins/localdump
BACKUP_KEEP_DAYS
=
$(
_j_getvar
${
JOBFILE
}
"keep-days"
)
if
[
$BACKUP_KEEP_DAYS
-eq
0
]
;
then
BACKUP_KEEP_DAYS
=
7
fi
BACKUP_DATE
=
$(
/bin/date +%Y%m%d-%H%M
)
}
# helpfer function for SERVICENAME.backup
# helpfer function for SERVICENAME.backup
# it is called after the service specific dump was done.
# it is called after the service specific dump was done.
...
@@ -346,7 +370,7 @@ function dbdetect.runas(){
...
@@ -346,7 +370,7 @@ function dbdetect.runas(){
if
[
-z
$1
]
;
then
if
[
-z
$1
]
;
then
find
-type
f |
sed
"s#__[0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9]-[0-9][0-9].*##g"
|
grep
-v
"
\.
meta"
|
sort
-ud
|
sed
"s#^
\.
/##g"
find
-type
f |
sed
"s#__[0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9]-[0-9][0-9].*##g"
|
grep
-v
"
\.
meta"
|
sort
-ud
|
sed
"s#^
\.
/##g"
else
else
ls
-ltr
"
$*
__"
*
|
sed
"s#^
\.
/##g"
ls
-ltr
"
$*
__"
*
|
sed
"s#^
\.
/##g"
|
grep
-v
"
\.
meta"
fi
fi
cd
-
>
/dev/null
cd
-
>
/dev/null
else
else
...
@@ -376,7 +400,7 @@ function dbdetect.runas(){
...
@@ -376,7 +400,7 @@ function dbdetect.runas(){
# the metafile is written in sqlite backup to store full path
# the metafile is written in sqlite backup to store full path
metafile
=
${
BACKUP_TARGETDIR
}
/
${
dumpfile
}
.meta
metafile
=
${
BACKUP_TARGETDIR
}
/
${
dumpfile
}
.meta
if
[
-f
$metafile
]
;
then
if
[
-f
$metafile
]
;
then
cat
$metafile
grep
"^/"
"
$metafile
"
||
grep
"^ File: "
"
$metafile
"
|
cut
-c
9-
else
else
sBasename
=
$(
basename
$1
)
sBasename
=
$(
basename
$1
)
sDb
=
$(
echo
${
sBasename
}
|
sed
"s#__[0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9]-[0-9][0-9].*##g"
)
sDb
=
$(
echo
${
sBasename
}
|
sed
"s#__[0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9]-[0-9][0-9].*##g"
)
...
@@ -391,6 +415,29 @@ function dbdetect.runas(){
...
@@ -391,6 +415,29 @@ function dbdetect.runas(){
}
}
# read .meta file (that contains output of stats) and restore last owner and file permissions
# param string filename of db dump
# param string restored database file
function
restorePermissions
(){
local
sMyMeta
=
"
${
1
}
.meta"
local
sTargetfile
=
"
$2
"
if
[
-f
"
${
sMyMeta
}
"
]
;
then
# Access: (0674/-rw-rwxr--) Uid: ( 1000/ axel) Gid: ( 1000/ axel)
# ^ ^ ^
# _sPerm _sUser _sGroup
local
_sPerm
=
$(
grep
"^Access: ("
"
${
sMyMeta
}
"
|
cut
-f
2
-d
'('
|
cut
-f
1
-d
'/'
)
if
[
-n
"
$_sPerm
"
]
;
then
local
_sUser
=
$(
grep
"^Access: ("
"
${
sMyMeta
}
"
|
cut
-f
3
-d
'/'
|
cut
-f
1
-d
')'
|
tr
-d
' '
)
local
_sGroup
=
$(
grep
"^Access: ("
"
${
sMyMeta
}
"
|
cut
-f
4
-d
'/'
|
cut
-f
1
-d
')'
|
tr
-d
' '
)
echo
-n
"Restoring file owner
$_sUser
:
$_sGroup
and permissions
$_sPerm
... "
chown
"
$_sUser
:
$_sGroup
"
"
${
sTargetfile
}
"
&&
chmod
"
$_sPerm
"
"
${
sTargetfile
}
"
fetchrc
fi
fi
}
# ------------------------------------------------------------
# ------------------------------------------------------------
# show help
# show help
# ------------------------------------------------------------
# ------------------------------------------------------------
...
@@ -419,7 +466,26 @@ function dbdetect.runas(){
...
@@ -419,7 +466,26 @@ function dbdetect.runas(){
# INIT
# INIT
# ----------------------------------------------------------------------
# ----------------------------------------------------------------------
db.init
# ----- db init
BACKUP_BASEDIR
=
$(
_j_getvar
${
JOBFILE
}
"dir-localdumps"
)
# check
if
[
-z
"
$BACKUP_BASEDIR
"
]
;
then
color error
echo
ERROR: missing config
for
backup target.
echo
There must be an entry dir-localdumps
in
${
JOBFILE
}
color reset
exit
1
fi
BACKUP_PLUGINDIR
=
$(
dirname
$0
)
/plugins/localdump
DBD_BASEDIR
=
$BACKUP_PLUGINDIR
/profiles
BACKUP_KEEP_DAYS
=
$(
_j_getvar
${
JOBFILE
}
"keep-days"
)
if
[
$BACKUP_KEEP_DAYS
-eq
0
]
;
then
BACKUP_KEEP_DAYS
=
7
fi
BACKUP_DATE
=
$(
/bin/date +%Y%m%d-%H%M
)
# ----- checks
# ----- checks
...
@@ -455,7 +521,14 @@ function dbdetect.runas(){
...
@@ -455,7 +521,14 @@ function dbdetect.runas(){
case
"
$mode
"
in
case
"
$mode
"
in
# ------------------------------------------------------------
# ------------------------------------------------------------
check
)
check
)
.
$BACKUP_SCRIPT
$mode
DBD_DEBUG
=
1
for
PROFILENAME
in
$(
dbdetect.getConfigs
)
do
echo
"-----
$PROFILENAME
"
dbdetect.exists
"
${
PROFILENAME
}
"
echo
done
# . $BACKUP_SCRIPT $mode
;;
;;
# ------------------------------------------------------------
# ------------------------------------------------------------
backup
)
backup
)
...
@@ -469,23 +542,6 @@ function dbdetect.runas(){
...
@@ -469,23 +542,6 @@ function dbdetect.runas(){
services
=
$*
services
=
$*
fi
fi
# ----- check all params
for
SERVICENAME
in
$services
do
BACKUP_SCRIPT
=
$(
get_service_script
${
SERVICENAME
}
)
if
[
!
-f
$BACKUP_SCRIPT
]
;
then
color error
echo
ERROR: parameter
$SERVICENAME
seems to be wrong.
echo
The backup script does not exist:
$BACKUP_SCRIPT
color reset
echo
echo
services
in
this folder are:
get_services
exit
2
fi
done
# ----- GO
# ----- GO
# PROFILENAME mysql_localhost_13306
# PROFILENAME mysql_localhost_13306
# SERVICENAME mysql
# SERVICENAME mysql
...
@@ -505,23 +561,25 @@ function dbdetect.runas(){
...
@@ -505,23 +561,25 @@ function dbdetect.runas(){
h2
"START SCRIPT FOR [
${
PROFILENAME
}
] ->
${
SERVICENAME
}
"
h2
"START SCRIPT FOR [
${
PROFILENAME
}
] ->
${
SERVICENAME
}
"
# ------ set env
# ------ set env
# echo "BACKUP_PARAMS = $BACKUP_PARAMS"
# dbdetect.setenv
# dbdetect.setenv
eval
$(
dbdetect.setenv
)
eval
$(
dbdetect.setenv
)
# echo "BACKUP_PARAMS = $BACKUP_PARAMS"
_j_runHooks
"200-before-db-service"
_j_runHooks
"200-before-db-service"
.
$BACKUP_SCRIPT
$mode
h3
"BACKUP [
${
PROFILENAME
}
] ->
${
SERVICENAME
}
"
.
$BACKUP_SCRIPT
$mode
test
$rc
-gt
0
&&
j_notify
"db
${
SERVICENAME
}
"
"
$BACKUP_SCRIPT
$mode
was finished with rc=
$rc
"
$rc
test
$rc
-gt
0
&&
j_notify
"db
${
SERVICENAME
}
"
"
$BACKUP_SCRIPT
$mode
was finished with rc=
$rc
"
$rc
_j_runHooks
"230-after-db-service"
"
$rc
"
_j_runHooks
"230-after-db-service"
"
$rc
"
# ------ unset env
eval
$(
dbdetect.unssetenv
)
# ----- post jobs: cleanup
# ----- post jobs: cleanup
cleanup_backup_target
cleanup_backup_target
show_info_backup_target
show_info_backup_target
# ------ unset env
eval
$(
dbdetect.unssetenv
)
else
else
...
...
This diff is collapsed.
Click to expand it.
plugins/localdump/sqlite.sh
+
32
−
51
View file @
1008ddf9
...
@@ -12,6 +12,7 @@
...
@@ -12,6 +12,7 @@
# 2018-02-02 ah,ds v1.0 first lines
# 2018-02-02 ah,ds v1.0 first lines
# 2018-02-09 ah,ds v1.1 write a .meta file after successful backup
# 2018-02-09 ah,ds v1.1 write a .meta file after successful backup
# 2022-03-17 v1.2 WIP: add lines with prefix __DB__
# 2022-03-17 v1.2 WIP: add lines with prefix __DB__
# 2024-02-20 ah v1.3 Handle profiles; restore into existing db file; restore file owner and perms
# ================================================================================
# ================================================================================
if
[
-z
"
$BACKUP_TARGETDIR
"
]
;
then
if
[
-z
"
$BACKUP_TARGETDIR
"
]
;
then
...
@@ -23,22 +24,17 @@ fi
...
@@ -23,22 +24,17 @@ fi
# CONFIG
# CONFIG
# --------------------------------------------------------------------------------
# --------------------------------------------------------------------------------
FILEDEFS
=
${
DIR_JOBS
}
/backup-dbfiles.job
# --------------------------------------------------------------------------------
# --------------------------------------------------------------------------------
# FUNCTIONS
# FUNCTIONS
# --------------------------------------------------------------------------------
# --------------------------------------------------------------------------------
# make sqlite3 backups of all sqlite = ... in backup-dbfiles.job
# start multiple sqlite3 backups
# files are taken from loaded profiles/sqlite*.ini - section [detect] -> files[]
function
doSqliteBackup
(){
function
doSqliteBackup
(){
if
!
_j_getvar
${
FILEDEFS
}
"sqlite"
|
grep
.
;
then
echo
"__DB__
$SERVICENAME
SKIP: no entries found for sqlite."
return
0
fi
create_targetdir
create_targetdir
for
DATABASE_FILE
in
$(
_j_getvar
${
FILEDEFS
}
"sqlite"
)
for
DATABASE_FILE
in
$(
dbdetect.getFiles
)
do
do
echo
-n
"__DB__
${
SERVICENAME
}
backup
$DATABASE_FILE
"
echo
-n
"__DB__
${
SERVICENAME
}
backup
$DATABASE_FILE
"
if
[
!
-f
"
$DATABASE_FILE
"
]
;
then
if
[
!
-f
"
$DATABASE_FILE
"
]
;
then
...
@@ -47,24 +43,17 @@ function doSqliteBackup(){
...
@@ -47,24 +43,17 @@ function doSqliteBackup(){
color reset
color reset
rc
=
$rc
+1
rc
=
$rc
+1
else
else
file
"
$DATABASE_FILE
"
|
cut
-f
2
-d
":"
|
grep
-i
"sqlite"
>
/dev/null
if
[
$?
-ne
0
]
;
then
TARGET
=
$(
get_outfile
${
DATABASE_FILE
}
)
color error
TARGET
=
${
BACKUP_TARGETDIR
}
/
$(
echo
${
TARGET
}
|
sed
"s#/#_#g"
)
.sql
echo
"ERROR: given database file is not a sqlite database"
META
=
${
TARGET
}
.gz.meta
color reset
# echo -n " to $TARGET "
rc
=
$rc
+1
sqlite3
"
$DATABASE_FILE
"
.dump
>
"
${
TARGET
}
"
else
fetchrc
>
/dev/null
TARGET
=
$(
get_outfile
${
DATABASE_FILE
}
)
db._compressDumpfile
"
${
TARGET
}
"
&&
stat
"
$DATABASE_FILE
"
>
"
${
META
}
"
TARGET
=
${
BACKUP_TARGETDIR
}
/
$(
echo
${
TARGET
}
|
sed
"s#/#_#g"
)
.sql
META
=
${
TARGET
}
.gz.meta
ls
-l
${
TARGET
}*
# echo -n " to $TARGET "
sqlite3
"
$DATABASE_FILE
"
.dump
>
"
${
TARGET
}
"
fetchrc
>
/dev/null
db._compressDumpfile
"
${
TARGET
}
"
&&
echo
"
$DATABASE_FILE
"
>
"
${
META
}
"
ls
-l
${
TARGET
}*
fi
fi
fi
done
done
}
}
...
@@ -73,29 +62,27 @@ function doSqliteBackup(){
...
@@ -73,29 +62,27 @@ function doSqliteBackup(){
# param string database dump file (gzipped)
# param string database dump file (gzipped)
# param string optional: database to import; default: database is parsed from file
# param string optional: database to import; default: database is parsed from file
function
restoreByFile
(){
function
restoreByFile
(){
sMyfile
=
$1
local
sMyfile
=
$1
sMyDb
=
$2
local
sMyDb
=
$2
if
[
-f
"
${
sMyDb
}
"
]
;
then
if
[
-f
"
${
sMyDb
}
"
]
;
then
color error
# echo -n "" > "${sMyDb}"
echo
ERROR: target file already exists. Remove or rename it first.
echo
"Deleting
${
sMyDb
}
before restore..."
rm
-f
"
${
sMyDb
}
"
fi
echo
-n
"Restore
${
sMyfile
}
... "
color cmd
zcat
"
${
sMyfile
}
"
| sqlite3
"
${
sMyDb
}
"
fetchrc
if
[
$myrc
-eq
0
]
;
then
restorePermissions
"
${
sMyfile
}
"
"
${
sMyDb
}
"
ls
-l
"
${
sMyDb
}
"
ls
-l
"
${
sMyDb
}
"
color reset
echo
rc
=
$rc
+1
else
else
color cmd
color error
zcat
"
${
sMyfile
}
"
| sqlite3
"
${
sMyDb
}
"
echo
ERROR
while
restoring backup.
fetchrc
color reset
if
[
$myrc
-eq
0
]
;
then
color ok
echo
OK, restore was successful
color reset
ls
-l
"
${
sMyDb
}
"
else
color error
echo
ERROR
while
restoring backup.
color reset
fi
fi
fi
}
}
...
@@ -108,12 +95,6 @@ function restoreByFile(){
...
@@ -108,12 +95,6 @@ function restoreByFile(){
# ----- check requirements
# ----- check requirements
j_requireBinary
"sqlite3"
1
j_requireBinary
"sqlite3"
1
if
[
!
-f
"
$FILEDEFS
"
]
;
then
echo
"INFO: file definitions
$FILEDEFS
do not exist."
rc
=
$rc
+1
fi
if
[
$rc
-ne
0
]
;
then
if
[
$rc
-ne
0
]
;
then
rc
=
0
rc
=
0
echo
"SKIP: sqlite seems not to be here"
echo
"SKIP: sqlite seems not to be here"
...
...
This diff is collapsed.
Click to expand it.
Preview
0%
Loading
Try again
or
attach a new file
.
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Save comment
Cancel
Please
register
or
sign in
to comment