Backup with Hanoi Strategy
$begingroup$
I want your suggestions about this script. I use it to backup configuration of softwares installed on my home media server.
What improvements would you do?
thanks you for any inputs.
#!/bin/bash
# ==================================================================================
# Description: BackUp script following a Hanoi scheme using rsync.
# Author: Hugo Lapointe Di Giacomo
# ==================================================================================
set -o errexit
set -o nounset
#set -o xtrace
# ----------------------------------------------------------------------------------
# Options
# ----------------------------------------------------------------------------------
setsMax=16 # ................................ Maximum number of sets bakcup.
cmdPrefix="" # .............................. "echo" if dry-run enable, "" otherwise.
srcDir="/appdata" # ......................... Source directory to backup.
dstDir="/mnt/backup/snapshots" # ............ Destination directory to backup in.
logDir="/mnt/backup/logs" # ................. Logs directory.
# ----------------------------------------------------------------------------------
# Constants
# ----------------------------------------------------------------------------------
SEC_PER_DAY=86400; # ........................ Number of seconds per day.
LATEST_SNAP="$dstDir/latest"; # ............. The "latest" snap.
LATEST_LOG="$logDir/latest"; # .............. The "latest" log.
DATE_FORMAT="%Y-%m-%d"; # ................... The format of a date.
HOUR_FORMAT="%H:%M"; # ...................... The format of a hour.
# ----------------------------------------------------------------------------------
# Show usage of this script.
# ----------------------------------------------------------------------------------
function showUsage() {
echo "Usage: $0 [OPTIONS]..."
echo ""
echo "Options available:"
echo " -h, --help Show usage of the script."
echo " -m, --max Maximum sets of backup."
echo " -s, --src Source directory to backup."
echo " -d. --dst Destination directory to backup in."
echo " -l, --log Log directory."
echo " -r, --dry Enable dry-run."
}
# ----------------------------------------------------------------------------------
# Parse arguments.
# ----------------------------------------------------------------------------------
function parseArgs() {
while [ $# -gt 0 ]; do
case $1 in
-h|--help)
showUsage $@
exit 0
;;
-m|--max)
setsMax=${2:-$setsMax}
shift 2
;;
-s|--src)
if [ -d "$2" ]; then
srcDir="$2"
fi
shift 2
;;
-d|--dst)
if [ -d "$2" ]; then
dstDir="$2"
fi
shift 2
;;
-l|--log)
if [ -d "$2" ]; then
logDir="$2"
fi
shift 2
;;
-r|--dry)
cmdPrefix="echo"
shift 1
;;
*)
echo "Unknown option or value: $1"
showUsage $@
exit 1
;;
esac
done
}
# ----------------------------------------------------------------------------------
# Create a backup with rsync.
# ----------------------------------------------------------------------------------
function createBackUp() {
snapDir="$dstDir/$1" # ................. Name of the snap dir.
logFile="$logDir/$1.log" # ............. Name of the log file.
RSYNC_CMD="rsync " # ................... Rsync Command.
RSYNC_CMD+="--archive " # .............. Enable recursion and preserve infos.
RSYNC_CMD+="--verbose " # .............. Increase amount of infos printed.
RSYNC_CMD+="--human-readable " # ....... Output number in readable format.
RSYNC_CMD+="--progress " # ............. Show progress during transfer.
RSYNC_CMD+="--delete " # ............... Delete files from receiving side.
RSYNC_CMD+="--link-dest=$LATEST_SNAP " # "latest" symbolic link to hardlink.
RSYNC_CMD+="$srcDir " # ................ Source directory to backup.
RSYNC_CMD+="$snapDir " # ............... Destination directory to backup in.
# Create backup and save the output in a log file.
$cmdPrefix $RSYNC_CMD 2>&1 | tee "$logFile"
}
# ----------------------------------------------------------------------------------
# Update the latest links.
# ----------------------------------------------------------------------------------
function updateLatestLinks() {
$cmdPrefix rm -f "$LATEST_SNAP"
$cmdPrefix ln -s "$snapDir" "$LATEST_SNAP"
$cmdPrefix rm -f "$LATEST_LOG"
$cmdPrefix ln -s "$logFile" "$LATEST_LOG"
}
# ----------------------------------------------------------------------------------
# Calculate the previous move in the cycle.
# https://en.wikipedia.org/wiki/Backup_rotation_scheme
# ----------------------------------------------------------------------------------
function calculateDaysFromBackupOfSameSet() {
local todayInSec=$(date +%s)
local todayInDay=$(($todayInSec / $SEC_PER_DAY))
local daysToBackup=$((2 ** ($setsMax - 1)))
local daysElapsed=0
local i=1
while [ $i -lt $(($daysToBackup / 2)) ]; do
local rotation=$(($todayInDay & $i))
if [ $rotation -eq 0 ]; then
daysElapsed=$(($i * 2))
break
fi
daysElapsed=$(($daysToBackup / 2))
i=$((2 * $i))
done
echo $daysElapsed
}
# ----------------------------------------------------------------------------------
# Main function.
# ----------------------------------------------------------------------------------
function main() {
parseArgs $@
local todayDatetime=$(date +$DATE_FORMAT.$HOUR_FORMAT)
if createBackUp $todayDatetime; then
updateLatestLinks
local daysElapsed=$(calculateDaysFromBackupOfSameSet)
local expiredDay=$(date -d "$daysElapsed days ago" +$DATE_FORMAT)
$cmdPrefix rm -frv "$dstDir/$expiredDay."*
fi
}
main $@
sh
$endgroup$
add a comment |
$begingroup$
I want your suggestions about this script. I use it to backup configuration of softwares installed on my home media server.
What improvements would you do?
thanks you for any inputs.
#!/bin/bash
# ==================================================================================
# Description: BackUp script following a Hanoi scheme using rsync.
# Author: Hugo Lapointe Di Giacomo
# ==================================================================================
set -o errexit
set -o nounset
#set -o xtrace
# ----------------------------------------------------------------------------------
# Options
# ----------------------------------------------------------------------------------
setsMax=16 # ................................ Maximum number of sets bakcup.
cmdPrefix="" # .............................. "echo" if dry-run enable, "" otherwise.
srcDir="/appdata" # ......................... Source directory to backup.
dstDir="/mnt/backup/snapshots" # ............ Destination directory to backup in.
logDir="/mnt/backup/logs" # ................. Logs directory.
# ----------------------------------------------------------------------------------
# Constants
# ----------------------------------------------------------------------------------
SEC_PER_DAY=86400; # ........................ Number of seconds per day.
LATEST_SNAP="$dstDir/latest"; # ............. The "latest" snap.
LATEST_LOG="$logDir/latest"; # .............. The "latest" log.
DATE_FORMAT="%Y-%m-%d"; # ................... The format of a date.
HOUR_FORMAT="%H:%M"; # ...................... The format of a hour.
# ----------------------------------------------------------------------------------
# Show usage of this script.
# ----------------------------------------------------------------------------------
function showUsage() {
echo "Usage: $0 [OPTIONS]..."
echo ""
echo "Options available:"
echo " -h, --help Show usage of the script."
echo " -m, --max Maximum sets of backup."
echo " -s, --src Source directory to backup."
echo " -d. --dst Destination directory to backup in."
echo " -l, --log Log directory."
echo " -r, --dry Enable dry-run."
}
# ----------------------------------------------------------------------------------
# Parse arguments.
# ----------------------------------------------------------------------------------
function parseArgs() {
while [ $# -gt 0 ]; do
case $1 in
-h|--help)
showUsage $@
exit 0
;;
-m|--max)
setsMax=${2:-$setsMax}
shift 2
;;
-s|--src)
if [ -d "$2" ]; then
srcDir="$2"
fi
shift 2
;;
-d|--dst)
if [ -d "$2" ]; then
dstDir="$2"
fi
shift 2
;;
-l|--log)
if [ -d "$2" ]; then
logDir="$2"
fi
shift 2
;;
-r|--dry)
cmdPrefix="echo"
shift 1
;;
*)
echo "Unknown option or value: $1"
showUsage $@
exit 1
;;
esac
done
}
# ----------------------------------------------------------------------------------
# Create a backup with rsync.
# ----------------------------------------------------------------------------------
function createBackUp() {
snapDir="$dstDir/$1" # ................. Name of the snap dir.
logFile="$logDir/$1.log" # ............. Name of the log file.
RSYNC_CMD="rsync " # ................... Rsync Command.
RSYNC_CMD+="--archive " # .............. Enable recursion and preserve infos.
RSYNC_CMD+="--verbose " # .............. Increase amount of infos printed.
RSYNC_CMD+="--human-readable " # ....... Output number in readable format.
RSYNC_CMD+="--progress " # ............. Show progress during transfer.
RSYNC_CMD+="--delete " # ............... Delete files from receiving side.
RSYNC_CMD+="--link-dest=$LATEST_SNAP " # "latest" symbolic link to hardlink.
RSYNC_CMD+="$srcDir " # ................ Source directory to backup.
RSYNC_CMD+="$snapDir " # ............... Destination directory to backup in.
# Create backup and save the output in a log file.
$cmdPrefix $RSYNC_CMD 2>&1 | tee "$logFile"
}
# ----------------------------------------------------------------------------------
# Update the latest links.
# ----------------------------------------------------------------------------------
function updateLatestLinks() {
$cmdPrefix rm -f "$LATEST_SNAP"
$cmdPrefix ln -s "$snapDir" "$LATEST_SNAP"
$cmdPrefix rm -f "$LATEST_LOG"
$cmdPrefix ln -s "$logFile" "$LATEST_LOG"
}
# ----------------------------------------------------------------------------------
# Calculate the previous move in the cycle.
# https://en.wikipedia.org/wiki/Backup_rotation_scheme
# ----------------------------------------------------------------------------------
function calculateDaysFromBackupOfSameSet() {
local todayInSec=$(date +%s)
local todayInDay=$(($todayInSec / $SEC_PER_DAY))
local daysToBackup=$((2 ** ($setsMax - 1)))
local daysElapsed=0
local i=1
while [ $i -lt $(($daysToBackup / 2)) ]; do
local rotation=$(($todayInDay & $i))
if [ $rotation -eq 0 ]; then
daysElapsed=$(($i * 2))
break
fi
daysElapsed=$(($daysToBackup / 2))
i=$((2 * $i))
done
echo $daysElapsed
}
# ----------------------------------------------------------------------------------
# Main function.
# ----------------------------------------------------------------------------------
function main() {
parseArgs $@
local todayDatetime=$(date +$DATE_FORMAT.$HOUR_FORMAT)
if createBackUp $todayDatetime; then
updateLatestLinks
local daysElapsed=$(calculateDaysFromBackupOfSameSet)
local expiredDay=$(date -d "$daysElapsed days ago" +$DATE_FORMAT)
$cmdPrefix rm -frv "$dstDir/$expiredDay."*
fi
}
main $@
sh
$endgroup$
add a comment |
$begingroup$
I want your suggestions about this script. I use it to backup configuration of softwares installed on my home media server.
What improvements would you do?
thanks you for any inputs.
#!/bin/bash
# ==================================================================================
# Description: BackUp script following a Hanoi scheme using rsync.
# Author: Hugo Lapointe Di Giacomo
# ==================================================================================
set -o errexit
set -o nounset
#set -o xtrace
# ----------------------------------------------------------------------------------
# Options
# ----------------------------------------------------------------------------------
setsMax=16 # ................................ Maximum number of sets bakcup.
cmdPrefix="" # .............................. "echo" if dry-run enable, "" otherwise.
srcDir="/appdata" # ......................... Source directory to backup.
dstDir="/mnt/backup/snapshots" # ............ Destination directory to backup in.
logDir="/mnt/backup/logs" # ................. Logs directory.
# ----------------------------------------------------------------------------------
# Constants
# ----------------------------------------------------------------------------------
SEC_PER_DAY=86400; # ........................ Number of seconds per day.
LATEST_SNAP="$dstDir/latest"; # ............. The "latest" snap.
LATEST_LOG="$logDir/latest"; # .............. The "latest" log.
DATE_FORMAT="%Y-%m-%d"; # ................... The format of a date.
HOUR_FORMAT="%H:%M"; # ...................... The format of a hour.
# ----------------------------------------------------------------------------------
# Show usage of this script.
# ----------------------------------------------------------------------------------
function showUsage() {
echo "Usage: $0 [OPTIONS]..."
echo ""
echo "Options available:"
echo " -h, --help Show usage of the script."
echo " -m, --max Maximum sets of backup."
echo " -s, --src Source directory to backup."
echo " -d. --dst Destination directory to backup in."
echo " -l, --log Log directory."
echo " -r, --dry Enable dry-run."
}
# ----------------------------------------------------------------------------------
# Parse arguments.
# ----------------------------------------------------------------------------------
function parseArgs() {
while [ $# -gt 0 ]; do
case $1 in
-h|--help)
showUsage $@
exit 0
;;
-m|--max)
setsMax=${2:-$setsMax}
shift 2
;;
-s|--src)
if [ -d "$2" ]; then
srcDir="$2"
fi
shift 2
;;
-d|--dst)
if [ -d "$2" ]; then
dstDir="$2"
fi
shift 2
;;
-l|--log)
if [ -d "$2" ]; then
logDir="$2"
fi
shift 2
;;
-r|--dry)
cmdPrefix="echo"
shift 1
;;
*)
echo "Unknown option or value: $1"
showUsage $@
exit 1
;;
esac
done
}
# ----------------------------------------------------------------------------------
# Create a backup with rsync.
# ----------------------------------------------------------------------------------
function createBackUp() {
snapDir="$dstDir/$1" # ................. Name of the snap dir.
logFile="$logDir/$1.log" # ............. Name of the log file.
RSYNC_CMD="rsync " # ................... Rsync Command.
RSYNC_CMD+="--archive " # .............. Enable recursion and preserve infos.
RSYNC_CMD+="--verbose " # .............. Increase amount of infos printed.
RSYNC_CMD+="--human-readable " # ....... Output number in readable format.
RSYNC_CMD+="--progress " # ............. Show progress during transfer.
RSYNC_CMD+="--delete " # ............... Delete files from receiving side.
RSYNC_CMD+="--link-dest=$LATEST_SNAP " # "latest" symbolic link to hardlink.
RSYNC_CMD+="$srcDir " # ................ Source directory to backup.
RSYNC_CMD+="$snapDir " # ............... Destination directory to backup in.
# Create backup and save the output in a log file.
$cmdPrefix $RSYNC_CMD 2>&1 | tee "$logFile"
}
# ----------------------------------------------------------------------------------
# Update the latest links.
# ----------------------------------------------------------------------------------
function updateLatestLinks() {
$cmdPrefix rm -f "$LATEST_SNAP"
$cmdPrefix ln -s "$snapDir" "$LATEST_SNAP"
$cmdPrefix rm -f "$LATEST_LOG"
$cmdPrefix ln -s "$logFile" "$LATEST_LOG"
}
# ----------------------------------------------------------------------------------
# Calculate the previous move in the cycle.
# https://en.wikipedia.org/wiki/Backup_rotation_scheme
# ----------------------------------------------------------------------------------
function calculateDaysFromBackupOfSameSet() {
local todayInSec=$(date +%s)
local todayInDay=$(($todayInSec / $SEC_PER_DAY))
local daysToBackup=$((2 ** ($setsMax - 1)))
local daysElapsed=0
local i=1
while [ $i -lt $(($daysToBackup / 2)) ]; do
local rotation=$(($todayInDay & $i))
if [ $rotation -eq 0 ]; then
daysElapsed=$(($i * 2))
break
fi
daysElapsed=$(($daysToBackup / 2))
i=$((2 * $i))
done
echo $daysElapsed
}
# ----------------------------------------------------------------------------------
# Main function.
# ----------------------------------------------------------------------------------
function main() {
parseArgs $@
local todayDatetime=$(date +$DATE_FORMAT.$HOUR_FORMAT)
if createBackUp $todayDatetime; then
updateLatestLinks
local daysElapsed=$(calculateDaysFromBackupOfSameSet)
local expiredDay=$(date -d "$daysElapsed days ago" +$DATE_FORMAT)
$cmdPrefix rm -frv "$dstDir/$expiredDay."*
fi
}
main $@
sh
$endgroup$
I want your suggestions about this script. I use it to backup configuration of softwares installed on my home media server.
What improvements would you do?
thanks you for any inputs.
#!/bin/bash
# ==================================================================================
# Description: BackUp script following a Hanoi scheme using rsync.
# Author: Hugo Lapointe Di Giacomo
# ==================================================================================
set -o errexit
set -o nounset
#set -o xtrace
# ----------------------------------------------------------------------------------
# Options
# ----------------------------------------------------------------------------------
setsMax=16 # ................................ Maximum number of sets bakcup.
cmdPrefix="" # .............................. "echo" if dry-run enable, "" otherwise.
srcDir="/appdata" # ......................... Source directory to backup.
dstDir="/mnt/backup/snapshots" # ............ Destination directory to backup in.
logDir="/mnt/backup/logs" # ................. Logs directory.
# ----------------------------------------------------------------------------------
# Constants
# ----------------------------------------------------------------------------------
SEC_PER_DAY=86400; # ........................ Number of seconds per day.
LATEST_SNAP="$dstDir/latest"; # ............. The "latest" snap.
LATEST_LOG="$logDir/latest"; # .............. The "latest" log.
DATE_FORMAT="%Y-%m-%d"; # ................... The format of a date.
HOUR_FORMAT="%H:%M"; # ...................... The format of a hour.
# ----------------------------------------------------------------------------------
# Show usage of this script.
# ----------------------------------------------------------------------------------
function showUsage() {
echo "Usage: $0 [OPTIONS]..."
echo ""
echo "Options available:"
echo " -h, --help Show usage of the script."
echo " -m, --max Maximum sets of backup."
echo " -s, --src Source directory to backup."
echo " -d. --dst Destination directory to backup in."
echo " -l, --log Log directory."
echo " -r, --dry Enable dry-run."
}
# ----------------------------------------------------------------------------------
# Parse arguments.
# ----------------------------------------------------------------------------------
function parseArgs() {
while [ $# -gt 0 ]; do
case $1 in
-h|--help)
showUsage $@
exit 0
;;
-m|--max)
setsMax=${2:-$setsMax}
shift 2
;;
-s|--src)
if [ -d "$2" ]; then
srcDir="$2"
fi
shift 2
;;
-d|--dst)
if [ -d "$2" ]; then
dstDir="$2"
fi
shift 2
;;
-l|--log)
if [ -d "$2" ]; then
logDir="$2"
fi
shift 2
;;
-r|--dry)
cmdPrefix="echo"
shift 1
;;
*)
echo "Unknown option or value: $1"
showUsage $@
exit 1
;;
esac
done
}
# ----------------------------------------------------------------------------------
# Create a backup with rsync.
# ----------------------------------------------------------------------------------
function createBackUp() {
snapDir="$dstDir/$1" # ................. Name of the snap dir.
logFile="$logDir/$1.log" # ............. Name of the log file.
RSYNC_CMD="rsync " # ................... Rsync Command.
RSYNC_CMD+="--archive " # .............. Enable recursion and preserve infos.
RSYNC_CMD+="--verbose " # .............. Increase amount of infos printed.
RSYNC_CMD+="--human-readable " # ....... Output number in readable format.
RSYNC_CMD+="--progress " # ............. Show progress during transfer.
RSYNC_CMD+="--delete " # ............... Delete files from receiving side.
RSYNC_CMD+="--link-dest=$LATEST_SNAP " # "latest" symbolic link to hardlink.
RSYNC_CMD+="$srcDir " # ................ Source directory to backup.
RSYNC_CMD+="$snapDir " # ............... Destination directory to backup in.
# Create backup and save the output in a log file.
$cmdPrefix $RSYNC_CMD 2>&1 | tee "$logFile"
}
# ----------------------------------------------------------------------------------
# Update the latest links.
# ----------------------------------------------------------------------------------
function updateLatestLinks() {
$cmdPrefix rm -f "$LATEST_SNAP"
$cmdPrefix ln -s "$snapDir" "$LATEST_SNAP"
$cmdPrefix rm -f "$LATEST_LOG"
$cmdPrefix ln -s "$logFile" "$LATEST_LOG"
}
# ----------------------------------------------------------------------------------
# Calculate the previous move in the cycle.
# https://en.wikipedia.org/wiki/Backup_rotation_scheme
# ----------------------------------------------------------------------------------
function calculateDaysFromBackupOfSameSet() {
local todayInSec=$(date +%s)
local todayInDay=$(($todayInSec / $SEC_PER_DAY))
local daysToBackup=$((2 ** ($setsMax - 1)))
local daysElapsed=0
local i=1
while [ $i -lt $(($daysToBackup / 2)) ]; do
local rotation=$(($todayInDay & $i))
if [ $rotation -eq 0 ]; then
daysElapsed=$(($i * 2))
break
fi
daysElapsed=$(($daysToBackup / 2))
i=$((2 * $i))
done
echo $daysElapsed
}
# ----------------------------------------------------------------------------------
# Main function.
# ----------------------------------------------------------------------------------
function main() {
parseArgs $@
local todayDatetime=$(date +$DATE_FORMAT.$HOUR_FORMAT)
if createBackUp $todayDatetime; then
updateLatestLinks
local daysElapsed=$(calculateDaysFromBackupOfSameSet)
local expiredDay=$(date -d "$daysElapsed days ago" +$DATE_FORMAT)
$cmdPrefix rm -frv "$dstDir/$expiredDay."*
fi
}
main $@
sh
sh
asked 8 mins ago
hlapointehlapointe
148216
148216
add a comment |
add a comment |
0
active
oldest
votes
Your Answer
StackExchange.ifUsing("editor", function () {
return StackExchange.using("mathjaxEditing", function () {
StackExchange.MarkdownEditor.creationCallbacks.add(function (editor, postfix) {
StackExchange.mathjaxEditing.prepareWmdForMathJax(editor, postfix, [["\$", "\$"]]);
});
});
}, "mathjax-editing");
StackExchange.ifUsing("editor", function () {
StackExchange.using("externalEditor", function () {
StackExchange.using("snippets", function () {
StackExchange.snippets.init();
});
});
}, "code-snippets");
StackExchange.ready(function() {
var channelOptions = {
tags: "".split(" "),
id: "196"
};
initTagRenderer("".split(" "), "".split(" "), channelOptions);
StackExchange.using("externalEditor", function() {
// Have to fire editor after snippets, if snippets enabled
if (StackExchange.settings.snippets.snippetsEnabled) {
StackExchange.using("snippets", function() {
createEditor();
});
}
else {
createEditor();
}
});
function createEditor() {
StackExchange.prepareEditor({
heartbeatType: 'answer',
autoActivateHeartbeat: false,
convertImagesToLinks: false,
noModals: true,
showLowRepImageUploadWarning: true,
reputationToPostImages: null,
bindNavPrevention: true,
postfix: "",
imageUploader: {
brandingHtml: "Powered by u003ca class="icon-imgur-white" href="https://imgur.com/"u003eu003c/au003e",
contentPolicyHtml: "User contributions licensed under u003ca href="https://creativecommons.org/licenses/by-sa/3.0/"u003ecc by-sa 3.0 with attribution requiredu003c/au003e u003ca href="https://stackoverflow.com/legal/content-policy"u003e(content policy)u003c/au003e",
allowUrls: true
},
onDemand: true,
discardSelector: ".discard-answer"
,immediatelyShowMarkdownHelp:true
});
}
});
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
StackExchange.ready(
function () {
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fcodereview.stackexchange.com%2fquestions%2f215473%2fbackup-with-hanoi-strategy%23new-answer', 'question_page');
}
);
Post as a guest
Required, but never shown
0
active
oldest
votes
0
active
oldest
votes
active
oldest
votes
active
oldest
votes
Thanks for contributing an answer to Code Review Stack Exchange!
- Please be sure to answer the question. Provide details and share your research!
But avoid …
- Asking for help, clarification, or responding to other answers.
- Making statements based on opinion; back them up with references or personal experience.
Use MathJax to format equations. MathJax reference.
To learn more, see our tips on writing great answers.
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
StackExchange.ready(
function () {
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fcodereview.stackexchange.com%2fquestions%2f215473%2fbackup-with-hanoi-strategy%23new-answer', 'question_page');
}
);
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown