Wie verwende ich pm-suspend-hybrid standardmäßig anstelle von pm-suspend?


41

Ich möchte die hybride Suspend-Methode verwenden, anstatt beim Schließen des Deckels oder Auswählen von "Suspend" aus dem Menü zu suspendieren.

Ich kann mir vorstellen, das pm-suspend-Skript so zu ändern, dass es automatisch ausgeführt wird, aber es gibt möglicherweise einen wartbareren / einfacheren Weg.

Antworten:


44

Indirekter Hybridschlaf

Dies ist die ältere Methode: zuerst anhalten und dann nach einer Verzögerung (standardmäßig 15 Minuten) in den Ruhezustand versetzen. Verwenden Sie dies mit einem Linux-Kernel vor 3.6 oder wenn Sie möchten, verbraucht er nach 15 Minuten keinen Strom mehr.

Füge die Datei hinzu /etc/pm/config.d/00-use-suspend-hybrid:

# Always use suspend_hybrid instead of suspend
if [ "$METHOD" = "suspend" ]; then
  METHOD=suspend_hybrid
fi
# The delay after which hibernation gets triggered (default: 900 seconds, 15 minutes):
PM_HIBERNATE_DELAY=900

Möglicherweise möchten Sie sicherstellen, dass die Hybridmethode auf Ihrem System über den folgenden Code unterstützt wird. Wenn es "0" sagt, sollte es funktionieren:

sudo pm-is-supported --suspend-hybrid && echo $?

Echtes Hybrid-Suspending mit Linux 3.6+

Wenn Sie einen Linux 3.6-Kernel haben, können Sie den folgenden verwenden, der von Anfang an sowohl für die Festplatte als auch für den Arbeitsspeicher inaktiviert wird.

Füge die Datei hinzu /etc/pm/config.d/00-use-suspend-hybrid:

# WORKAROUND: always set the default hibernate mode first (normal mode)
# (not required if you have the patch mentioned by Rohan below (http://askubuntu.com/a/344879/169))
HIBERNATE_MODE=platform

# Always use hibernate instead of suspend, but with "suspend to both"
if [ "$METHOD" = "suspend" ]; then
  METHOD=hibernate
  HIBERNATE_MODE=suspend
fi

# Make sure to use the kernel's method, in case uswsusp is installed etc.
SLEEP_MODULE=kernel

Dadurch wird das Image immer auf die Festplatte geschrieben und anschließend in den RAM-Speicher verschoben. Dies hat den Vorteil, dass die Wiederherstellung immer schnell erfolgt (solange der Akku nicht leer ist) und der Computer für kurze Zeit nicht aufwacht (nach PM_HIBERNATE_DELAY). für echt überwintern.

Der Nachteil ist, dass der Vorgang länger dauert (weil er immer auf der Festplatte im Ruhezustand ist) und dass der Akku auf lange Sicht leer wird (z. B. nach 12 Stunden).


2
Eine kleine Anmerkung: Anstelle von 'sudo pm-is-supported --suspend-hybrid && echo $?' verwenden Sie 'sudo pm-is-supported --suspend-hybrid; echo $? ' als Rückgabewert von pm-is-supported wird 0 für unterstützt und 1 für nicht.
James Caccese

1
@JamesCaccese: In der Shellscript-Welt bedeutet 0 "wahr" und alles andere bedeutet "falsch". Ihr Scriptlet würde funktionieren, aber das Scriptlet des ursprünglichen Posters würde auch wie vorgesehen funktionieren und eine '0' auf "unterstützt" und nichts auf "nicht unterstützt" ausgeben. Wenn Sie etwas wollen, das immer unterstützt oder nicht unterstützt wird, versuchen Sie 'sudo pm-is-supported --suspend-hybrid && echo "supported" || echo "nicht unterstützt" '
zanfur

@zanfur - Obwohl mir Ihre nachfolgend bereitgestellte Lösung zum Drucken beider Zustände gefällt (und nichts aus irgendeinem unerwarteten Grund, aus dem pm-unterstützt wird, nicht erwartungsgemäß ausgeführt wird, was den Fehlerzustand nicht kennt ), schätze ich die Erwähnung von James Caccese dieser Vorbehalt, aus dem oben genannten Grund.
user66001

Wenn Sie 16.04 verwenden, lesen Sie diese Antwort unten.
Kapad

Das einzige fehlende Stück für mich war das resumeArgument in /etc/default/grub.conf. Auch als nvidia user musste ich einstellen nomodeset. So resultierende gräbt Eintrag in meinem Fall ist: GRUB_CMDLINE_LINUX_DEFAULT="nomodeset resume=UUID=uuidofswappartition". Vergessen Sie nicht grub-update. Und auch einige Module müssen entladen werden, also erstellte Datei /etc/pm/config.d/00-unload_modulesmit Zeile SUSPEND_MODULES="ath5k r8169"und nur um sicherzugehen, dass ich auch 00-use-suspend-hybrid in10-use-suspend-hybrid
mauron85

31

Ubuntu 18.04 eine zeitgesteuerte Option

In Ubuntu 18.04 gibt es eine neue zeitgesteuerte Option. In systemdist ein neuer Modus verfügbar suspend-then-hibernate. Dies beginnt mit dem Ruhemodus und geht nach einer festgelegten Zeit in den Ruhezustand über.

Im hybrid-sleepModus wird der Ruhezustand nur dann wirksam, wenn der Akku sehr schwach ist und das System heruntergefahren wird.

Um diese Funktion zu verwenden, müssen Sie eine Datei /etc/systemd/sleep.confmit dem nächsten Inhalt erstellen :

[Sleep]
HibernateDelaySec=3600

Dies wird nach 1 Stunde Schlaf aus dem Schlaf in den Winterschlaf gehen. Sie können bearbeiten HibernateDelaySec, um die Verzögerung in den Ruhezustand zu ändern.

Testen Sie zunächst mit systemd, ob Suspend-Then-Hibernate funktioniert

Öffnen Sie ein Terminal, indem Sie Ctrl+ Alt+ drücken Tund Folgendes eingeben:

sudo systemctl suspend-then-hibernate

Wenn es funktioniert, mach es dauerhaft.

  • Folgendes funktioniert, wenn ich den Deckel schließe .

Öffnen Sie die Datei /etc/systemd/logind.confmit Ihrem bevorzugten Editor. Sie müssen Ihre administrative Macht durch aufzurufen sudo, gksudooder pkexecdiese Datei bearbeiten.

Finde die zwei Zeilen:

#HandleSuspendKey=suspend
#HandleLidSwitch=suspend

Beachten Sie, dass diese Zeilen mit #vor ihnen auskommentiert sind . Dies suspendist die Standardaktion. Entfernen Sie die #und Veränderung suspendzu suspend-then-hibernatein diesen beiden Linien , so dass sie wie folgt aussehen:

HandleSuspendKey=suspend-then-hibernate
HandleLidSwitch=suspend-then-hibernate

Speicher die Datei. Melden Sie sich logindmit dem folgenden Befehl ab und wieder an oder starten Sie den Dienst neu :

systemctl restart systemd-logind.service

Warnung! Ihre Benutzersitzung wird neu gestartet

Quelle: Lid Closed Suspend, dann Hibernate

Ubuntu 16.04 und höher

Die Lösung von blueyed für Real Hybrid Suspending mit Linux 3.6+ hat bei mir nicht funktioniert. Ich vermute, das liegt daran, dass Ubuntu 16.04 systemddie Datei verwendet und nicht verwendet /etc/pm/config.d/00-use-suspend-hybrid.

Testen Sie zunächst mit systemd, ob der Ruhezustand und der Hybridschlaf funktionieren

Öffnen Sie ein Terminal, indem Sie Ctrl+ Alt+ drücken Tund Folgendes eingeben:

sudo systemctl hibernate

Dadurch sollte Ihr Computer in den Ruhezustand versetzt werden. Geben Sie Folgendes ein, um den Hybridschlaf zu versuchen:

sudo systemctl hybrid-sleep

Wenn es funktioniert, mach es dauerhaft.

  • Folgendes funktioniert, wenn ich den Deckel schließe .

Öffnen Sie die Datei /etc/systemd/logind.confmit Ihrem bevorzugten Editor. Sie müssen Ihre administrative Macht durch aufzurufen sudo, gksudooder pkexecdiese Datei bearbeiten.

Finde die zwei Zeilen:

#HandleSuspendKey=suspend
#HandleLidSwitch=suspend

Beachten Sie, dass diese Zeilen mit #vor ihnen auskommentiert sind . Dies suspendist die Standardaktion. Entfernen Sie die #und Veränderung suspendzu hybrid-sleepin diesen beiden Linien , so dass sie wie folgt aussehen:

HandleSuspendKey=hybrid-sleep
HandleLidSwitch=hybrid-sleep

Speicher die Datei. Melden Sie sich ab und wieder an.

Hinweis:

  • Abgesehen von suspendoder hybrid-sleepgibt es eine dritte Option hibernate.
  • Mein Laptop verfügt nicht über eine physische Standby-Taste. Also konnte ich es nicht testen.
  • Durch Klicken auf im SuspendZahnradmenü wird der Computer in den normalen Energiesparmodus und nicht in den Hybrid-Energiesparmodus versetzt.

Quelle: https://superuser.com/questions/719447/how-to-use-systemd-hybrid-sleep-instead-of-suspend-under-gnome-in-linux

ich hoffe das hilft


2
Diese Antwort braucht mehr Gegenstimmen. Behobene Probleme für mich im 16.04. Vielen Dank.
Kapad

Bitte schön. Nachdem ich vom 14.04. Auf den 16.04. Umgezogen bin, finde ich mit der Zeit langsam die neue Art, Dinge zu tun.
user68186

1
Funktioniert auch mit Ubuntu GNOME 16.04.1
HarlemSquirrel

4

In 12.04 habe ich festgestellt, dass beim Auslösen des Ruhezustands (mithilfe von PM_HIBERNATE_DELAY=XX) das Fortsetzen / Auftauen der Shell-Skripte die Variable grub recordfail nicht deaktiviert. Daher bootet grub nicht automatisch.

Das Zeitlimit ist auf -1 festgelegt und wartet auf die Benutzerauswahl. Ich vermute, dies erfordert einige Bearbeitung von Skripten in /etc/pm/sleep.d/10_grub-common. Ich bin ein Neuling, daher habe ich mich leider nicht daran gemacht, die genaue Veränderung herauszufinden.


1
Wäre wahrscheinlich einen Fehlerbericht wert und / oder würde testen, ob es in 12.10+ funktioniert.
Blueyed

Ich sehe das gleiche Problem in 12.10
MDCore

3

Diese Frage taucht bei Google so oft auf, dass ich finde, dass es sich lohnt, sie zu beantworten. Die hier beschriebene Methode ist (imo) kein Hybrid-Suspend. Es ist "Ruhezustand nach X Minuten in Suspend". True Hybrid Suspend schreibt Ihren RAM auf die Festplatte und wechselt dann in den Energiesparmodus (Sleep-Modus). Während es länger dauert, wird der Vorgang sofort fortgesetzt, solange der Akku des Geräts noch vorhanden ist. Andernfalls wird der Vorgang von der Festplatte fortgesetzt. Dieses Verhalten wird von den meisten als hybrider Ruhezustand bezeichnet und standardmäßig in neueren Windows- und Mac-Laptops verwendet.

So aktivieren Sie das echte Hybrid-Suspend:

  • Folgen Sie dem ersten Teil der oberen Antwort. Dies überschreibt den "suspend" -Aufruf, um einen "hybrid_suspend" in pm-utils durchzuführen.
    % cat /etc/pm/config.d/00-use-suspend-hybrid
    # Verwenden Sie immer suspend_hybrid anstelle von suspend
    if ["$ METHOD" = "suspend"]; dann
        METHODE = suspend_hybrid
    fi
  • Erstellen Sie ein Backup von / usr / lib / pm-utils / pm-functions
  • Laden Sie den Patch hier herunter : https://bugs.freedesktop.org/attachment.cgi?id=68712
    • Dieser Patch aktiviert Hybrid-Suspend, falls verfügbar (dh auf Kernels 3.6+)
  • Wenden Sie es entweder mit 'patch -p0' an oder führen Sie es manuell zusammen, wenn dies fehlschlägt

Diese Methode funktioniert bei meinem Sony Vaio SVS.

PS: Den Patch hier reproduzieren, falls die Datei in Zukunft gelöscht wird:

diff --git a / pm / pm-functions.in b / pm / pm-functions.in
--- a / pm / pm-functions.in
+++ b / pm / pm-functions.in
@@ -316,8 +316,28 @@ if [-z "$ HIBERNATE_MODULE"] && \
    {
        [-n "$ {HIBERNATE_MODE}"] && \
        grep -qw "$ {HIBERNATE_MODE}" / sys / power / disk && \
+ HIBERNATE_MODE_SAVE = $ (cat / sys / power / disk) && \
+ HIBERNATE_MODE_SAVE = "$ {HIBERNATE_MODE_SAVE ## * [}" && \
+ HIBERNATE_MODE_SAVE = "$ {HIBERNATE_MODE_SAVE %%] *}" && \
        echo -n "$ {HIBERNATE_MODE}"> / sys / power / disk
        echo -n "disk"> / sys / power / state
+ RET = $?
+ echo -n "$ HIBERNATE_MODE_SAVE"> / sys / power / disk
+ "$ RET" zurückgeben
+}
+ fi
+
+ # für Kernel, die Suspend für beide unterstützen (dh Hybrid-Suspend)
+ # seit Kernel 3.6
+ if [-z "$ SUSPEND_HYBRID_MODULE"] && \
+ [-f / sys / power / disk] && \
+ grep -q disk / sys / power / state && \
+ grep -q suspend / sys / power / disk; dann
+ SUSPEND_HYBRID_MODULE = "Kernel"
+ do_suspend_hybrid ()
+ {
+ HIBERNATE_MODE = "Aussetzen"
+ do_hibernate
    }
 fi

Quellen:


1
Sie haben Recht mit Hybrid-Suspend. Ich habe vor kurzem mein Snippet selbst geändert. Sie können (ziemlich) das gleiche Ergebnis erzielen, indem Sie METHOD = hibernate und HIBERNATE_MODE = suspend verwenden. Ich setze HIBERNATE_MODE = platform oben in der Datei, anstatt die vorherige Version zu speichern und wiederherzustellen (was der Patch bewirkt). Ich werde meine Antwort oben aktualisieren.
bläulich

Das sieht gut aus, danke für die Bearbeitung, @blueyed
Rohan Dhruva

1

Es gibt eine andere Lösung, bei der keine Datei in config.d hinzugefügt wird, sondern nur wakealarm in / sys / class / rtc / rtc0. Verwenden Sie veralteten Code in pm-Funktionen (/ usr / lib / pm-utils), nachdem die Kommentare # nicht direkt vom Kernel unterstützt werden ... (weil der aktuelle Kernel (nach 3.6 etwas) dies direkt unterstützt). Setzen Sie diesen Code zurück und geben Sie do_suspend () anstelle von do_suspend_hybrid () ein und verwenden Sie den Patch für pm-Funktionen (bis sie ihn reparieren).

Veralteter Code (aussetzen und in den Ruhezustand versetzen, wenn suspend_hybrid aufgerufen wird):

# since the kernel does not directly support hybrid sleep, we do
# something else -- suspend and schedule an alarm to go into
# hibernate if we have slept long enough.
# Only do this if we do not need to do any special video hackery on resume
# from hibernate, though.
if [ -z "$SUSPEND_HYBRID_MODULE" -a -w "$PM_RTC/wakealarm" ] && \
    check_suspend && check_hibernate && ! is_set $HIBERNATE_RESUME_POST_VIDEO; \
    then
    SUSPEND_HYBRID_MODULE="kernel"
    do_suspend_hybrid() {
    WAKETIME=$(( $(cat "$PM_RTC/since_epoch") + PM_HIBERNATE_DELAY))
    echo >"$PM_RTC/wakealarm"
    echo $WAKETIME > "$PM_RTC/wakealarm"
    if do_suspend; then
        NOW=$(cat "$PM_RTC/since_epoch")
        if [ "$NOW" -ge "$WAKETIME" -a "$NOW" -lt $((WAKETIME + 30)) ]; then
        log "Woken by RTC alarm, hibernating."
        # if hibernate fails for any reason, go back to suspend.
        do_hibernate || do_suspend
        else
        echo > "$PM_RTC/wakealarm"
        fi
    else
        # if we cannot suspend, just try to hibernate.
        do_hibernate
    fi
    }
fi

Empfohlen. Noch einfacher ist es, uswsusp zu verwenden und gleichzeitig den Nutzen von s2both zu maximieren, dh s2both beim Suspendieren. Fügen Sie den wiederhergestellten Code in den Teil do_suspend () des Moduls uswsusp (/usr/lib/pm-utils/module.d) ein.

Zurückgesetzter Code (suspend_hybrid, wenn suspend aufgerufen wird):

        WAKETIME=$(( $(cat "$PM_RTC/since_epoch") + PM_HIBERNATE_DELAY))
        echo >"$PM_RTC/wakealarm"
        echo $WAKETIME > "$PM_RTC/wakealarm"
        if do_suspend_hybrid; then
            NOW=$(cat "$PM_RTC/since_epoch")
            if [ "$NOW" -ge "$WAKETIME" -a "$NOW" -lt $((WAKETIME + 30)) ];             then
            log "Woken by RTC alarm, hibernating."
            # if hibernate fails for any reason, go back to suspend_hybrid.
            do_hibernate || do_suspend_hybrid
            else
            echo > "$PM_RTC/wakealarm"
            fi
        else
            # when do_suspend is being called, convert to suspend_hybrid.
            do_suspend_hybrid
        fi      

Mit uswsusp können wir den Fortschritt von Suspend / Hibernate und den umgekehrten Prozess im Text sehen, selbst wenn wir die Rücktaste drücken, um ihn abzubrechen. Ohne uswsusp wird Suspend / Hibernate nur ärgerlich angezeigt bzw. ausgeblendet, insbesondere wenn Wakealarm ausgelöst wird und Hibernate ausgeführt wird (s2disk in uswsusp). Stellen Sie die Ruhezeit vor dem Ruhezustand wie gewohnt in der pm-functions-Datei ein.

# variables to handle hibernate after suspend support
PM_HIBERNATE_DELAY=900  # 15 minutes
PM_RTC=/sys/class/rtc/rtc0

Hier ist der uswsusp-Mod: (Denken Sie daran, dass dieses Modul von pm-Funktionen aufgerufen wird, damit die eingefügten Variablen identisch sind.)

#!/bin/sh

# disable processing of 90chvt and 99video.
# s2ram and s2disk handle all this stuff internally.
uswsusp_hooks()
{
    disablehook 99video "disabled by uswsusp"
}

# Since we disabled 99video, we need to take responsibility for proper
# quirk handling.  s2ram handles all common video quirks internally,
# so all we have to do is translate the HAL standard options to s2ram options.
uswsusp_get_quirks()
{
    OPTS=""
    ACPI_SLEEP=0
    for opt in $PM_CMDLINE; do
        case "${opt##--quirk-}" in # just quirks, please
            dpms-on)       ;; # no-op
            dpms-suspend)      ;; # no-op
            radeon-off)        OPTS="$OPTS --radeontool" ;;
            reset-brightness)  ;; # no-op
            s3-bios)       ACPI_SLEEP=$(($ACPI_SLEEP + 1)) ;;
            s3-mode)       ACPI_SLEEP=$(($ACPI_SLEEP + 2)) ;;
            vbe-post)      OPTS="$OPTS --vbe_post" ;;
            vbemode-restore)   OPTS="$OPTS --vbe_mode" ;;
            vbestate-restore)  OPTS="$OPTS --vbe_save" ;;
            vga-mode-3)        ;; # no-op
            save-pci)          OPTS="$OPTS --pci_save" ;;
            none)          QUIRK_NONE="true" ;;
            *) continue ;;
        esac
    done
    [ $ACPI_SLEEP -ne 0 ] && OPTS="$OPTS --acpi_sleep $ACPI_SLEEP"
    # if we were told to ignore quirks, do so.
    # This is arguably not the best way to do things, but...
    [ "$QUIRK_NONE" = "true" ] && OPTS=""
}

# Since we disabled 99video, we also need to handle displaying
# help info for the quirks we handle.
uswsusp_help()
{
    echo  # first echo makes it look nicer.
    echo "s2ram video quirk handler options:"
    echo
    echo "  --quirk-radeon-off"
    echo "  --quirk-s3-bios"
    echo "  --quirk-s3-mode"
    echo "  --quirk-vbe-post"
    echo "  --quirk-vbemode-restore"
    echo "  --quirk-vbestate-restore"
    echo "  --quirk-save-pci"
    echo "  --quirk-none"
}

# This idiom is used for all sleep methods.  Only declare the actual
# do_ method if:
# 1: some other sleep module has not already done so, and
# 2: this sleep method can actually work on this system.
#
# For suspend, if SUSPEND_MODULE is set then something else has already
# implemented do_suspend.  We could just check to see of do_suspend was
# already declared using command_exists, but using a dedicated environment
# variable makes it easier to debug when we have to know what sleep module
# ended up claiming ownership of a given sleep method.
if [ -z "$SUSPEND_MODULE" ] && command_exists s2ram && \
    ( grep -q mem /sys/power/state || \
        ( [ -c /dev/pmu ] && check_suspend_pmu; ); ); then
    SUSPEND_MODULE="uswsusp"
    do_suspend()
    {
        WAKETIME=$(( $(cat "$PM_RTC/since_epoch") + PM_HIBERNATE_DELAY))
        echo >"$PM_RTC/wakealarm"
        echo $WAKETIME > "$PM_RTC/wakealarm"
        if do_suspend_hybrid; then
            NOW=$(cat "$PM_RTC/since_epoch")
            if [ "$NOW" -ge "$WAKETIME" -a "$NOW" -lt $((WAKETIME + 30)) ];             then
            log "Woken by RTC alarm, hibernating."
            # if hibernate fails for any reason, go back to suspend_hybrid.
            do_hibernate || do_suspend_hybrid
            else
            echo > "$PM_RTC/wakealarm"
            fi
        else
            # when do_suspend is being called, convert to suspend_hybrid.
            do_suspend_hybrid
        fi      
    }
fi

if [ -z "$HIBERNATE_MODULE" ] && \
    [ -f /sys/power/disk ] && \
    grep -q disk /sys/power/state && \
    [ -c /dev/snapshot ] &&
    command_exists s2disk; then
    HIBERNATE_MODULE="uswsusp"
    do_hibernate()
    {
        s2disk
    }
fi

if [ -z "$SUSPEND_HYBRID_MODULE" ] && 
    grep -q mem /sys/power/state && \
    command_exists s2both && \
    check_hibernate; then
    SUSPEND_HYBRID_MODULE="uswsusp"
    do_suspend_hybrid()
    {   
        uswsusp_get_quirks
        s2both --force $OPTS 
    }
    if [ "$METHOD" = "suspend_hybrid" ]; then
        add_before_hooks uswsusp_hooks
        add_module_help uswsusp_help
    fi
fi  

0

Die Antwort von user68186 für Ubuntu 16.04 hat bei mir nicht funktioniert. Die Lösung hier war jedoch.

Stellen Sie zunächst sicher, dass der Ruhezustand funktioniert. Dann

Suchen und installieren Sie den dconf Editor in Ubuntu Software. Starten Sie es dann und navigieren Sie zu org -> gnome -> settings daemon -> plugins -> power.

Ändern Sie den Wert von "lid-close-ac-action" und "lid-close-battery-action".

In meinen Energieeinstellungen werden diese Optionen als leer angezeigt, funktionieren jedoch wie beabsichtigt.


0

In Ubuntu 18.04 ist es viel einfacher. In systemd gibt es einen neuen Modus Suspend- Then -Hibernate . Um diese Funktion zu verwenden, müssen Sie eine Datei /etc/systemd/sleep.conf mit dem nächsten Inhalt erstellen :

[Sleep]
HibernateDelaySec=3600

Dann können Sie es per Befehl testen:

sudo systemctl suspend-then-hibernate

(Sie können HibernateDelaySec bearbeiten, um die Verzögerung für den Ruhezustand zu verringern.) Wenn alles funktioniert, können Sie die Lid Close Action ändern. Dazu müssen Sie die Datei /etc/systemd/logind.conf bearbeiten

Sie müssen die Option HandleLidSwitch = finden, das Kommentarzeichen entfernen und zu HandleLidSwitch = suspend-then-hibernate wechseln . Dann müssen Sie den logind-Dienst mit dem nächsten Befehl neu starten (wirning! Ihre Benutzersitzung wird neu gestartet):

systemctl restart systemd-logind.service

Das ist alles! Jetzt können Sie diese schöne Funktion verwenden.

Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.