├── Authors ├── ChangeLog ├── Makefile ├── VERSION ├── data ├── Makefile └── time-slider.desktop.in ├── etc ├── dbus-1 │ └── system.d │ │ └── time-slider.conf └── xdg │ └── autostart │ └── time-slider-notify.desktop ├── lib └── svc │ ├── manifest │ ├── application │ │ ├── time-slider-plugin.xml │ │ └── time-slider.xml │ └── system │ │ └── filesystem │ │ └── auto-snapshot.xml │ └── method │ ├── time-slider │ ├── time-slider-plugin │ └── time-slider-rsync ├── po ├── ChangeLog ├── LINGUAS ├── Makefile ├── POTFILES.in ├── ar.po ├── ca.po ├── cs.po ├── de.po ├── es.po ├── fr.po ├── hu.po ├── id.po ├── it.po ├── ja.po ├── ko.po ├── nl.po ├── pl.po ├── pt_BR.po ├── ru.po ├── sv.po ├── zh_CN.po ├── zh_HK.po └── zh_TW.po ├── py-compile.py └── usr ├── bin └── time-slider-setup ├── lib ├── time-slider-delete ├── time-slider-notify ├── time-slider-snapshot ├── time-slider-version ├── time-slider │ └── plugins │ │ ├── rsync │ │ ├── rsync-backup │ │ └── rsync-trigger │ │ └── zfssend │ │ └── zfssend └── time-sliderd └── share ├── applications └── time-slider.desktop ├── icons └── hicolor │ ├── 16x16 │ └── apps │ │ └── time-slider-setup.png │ ├── 24x24 │ └── apps │ │ └── time-slider-setup.png │ ├── 32x32 │ └── apps │ │ └── time-slider-setup.png │ ├── 36x36 │ └── apps │ │ └── time-slider-setup.png │ ├── 48x48 │ └── apps │ │ └── time-slider-setup.png │ ├── 72x72 │ └── apps │ │ └── time-slider-setup.png │ └── 96x96 │ └── apps │ └── time-slider-setup.png └── time-slider ├── lib ├── plugin │ ├── __init__.py │ ├── plugin.py │ ├── pluginsmf.py │ ├── rsync │ │ ├── __init__.py │ │ ├── backup.py │ │ ├── rsyncsmf.py │ │ └── trigger.py │ └── zfssend │ │ ├── __init__.py │ │ └── zfssend.py └── time_slider │ ├── __init__.py │ ├── applet.py │ ├── autosnapsmf.py │ ├── dbussvc.py │ ├── deletegui.py │ ├── fileversion.py │ ├── rbac.py │ ├── setupgui.py │ ├── smf.py │ ├── snapnowui.py │ ├── timesliderd.py │ ├── timeslidersmf.py │ ├── util.py │ └── zfs.py └── ui ├── time-slider-delete.ui ├── time-slider-setup.ui ├── time-slider-snapshot.ui └── time-slider-version.ui /Authors: -------------------------------------------------------------------------------- 1 | 2008-09-11 Niall Power - 2 | 3 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | include VERSION 2 | 3 | mkinstalldirs = /usr/bin/mkdir -p 4 | INSTALL = /usr/sbin/install 5 | INSTALL_DATA = ${INSTALL} -u root -g bin -m 644 -f 6 | INSTALL_PROGRAM = ${INSTALL} -u root -g bin -f 7 | INSTALL_SCRIPT = ${INSTALL} -f 8 | RM = /usr/bin/rm -f 9 | RMRF = /usr/bin/rm -Rf 10 | RMDIR = /usr/bin/rmdir 11 | # Use python 3.5 if PYTHON environent is not set 12 | ifeq ($(strip $(PYTHON)),) 13 | PYTHON = /usr/bin/python3.9 14 | endif 15 | 16 | SUBDIRS = po data 17 | 18 | DISTFILES = Authors \ 19 | VERSION \ 20 | ChangeLog \ 21 | Makefile \ 22 | py-compile.py \ 23 | $(SUBDIRS) \ 24 | lib \ 25 | usr \ 26 | etc 27 | 28 | clean: 29 | $(RM) usr/share/time-slider/lib/time_slider/*.pyc 30 | $(RM) usr/share/time-slider/lib/plugin/*.pyc 31 | $(RM) usr/share/time-slider/lib/plugin/rsync/*.pyc 32 | $(RM) usr/share/time-slider/lib/plugin/zfssend/*.pyc 33 | 34 | all: 35 | for subdir in $(SUBDIRS); do \ 36 | cd $$subdir; make; cd ..;\ 37 | done 38 | echo $(VERSION) 39 | 40 | dist: clean all 41 | $(RMRF) time-slider-$(VERSION) 42 | mkdir time-slider-$(VERSION) 43 | cp -pR $(DISTFILES) time-slider-$(VERSION) 44 | /usr/bin/tar cf - time-slider-$(VERSION) | bzip2 > time-slider-$(VERSION).tar.bz2 45 | $(RMRF) time-slider-$(VERSION) 46 | 47 | install: 48 | for subdir in $(SUBDIRS); do \ 49 | cd $$subdir; \ 50 | make DESTDIR=$(DESTDIR) GETTEXT_PACKAGE=time-slider install; \ 51 | cd ..;\ 52 | done 53 | $(mkinstalldirs) $(DESTDIR)/etc/dbus-1/system.d 54 | $(INSTALL_DATA) $(DESTDIR)/etc/dbus-1/system.d etc/dbus-1/system.d/time-slider.conf 55 | $(mkinstalldirs) $(DESTDIR)/etc/xdg/autostart 56 | $(INSTALL_DATA) $(DESTDIR)/etc/xdg/autostart etc/xdg/autostart/*.desktop 57 | $(mkinstalldirs) $(DESTDIR)/lib/svc/method 58 | $(INSTALL_SCRIPT) $(DESTDIR)/lib/svc/method lib/svc/method/time-slider 59 | $(INSTALL_SCRIPT) $(DESTDIR)/lib/svc/method lib/svc/method/time-slider-plugin 60 | $(INSTALL_SCRIPT) $(DESTDIR)/lib/svc/method lib/svc/method/time-slider-rsync 61 | $(mkinstalldirs) $(DESTDIR)/usr/bin 62 | $(INSTALL_PROGRAM) $(DESTDIR)/usr/bin usr/bin/time-slider-setup 63 | $(mkinstalldirs) $(DESTDIR)/usr/lib/time-slider/plugins/rsync 64 | $(mkinstalldirs) $(DESTDIR)/usr/lib/time-slider/plugins/zfssend 65 | $(INSTALL_PROGRAM) $(DESTDIR)/usr/lib usr/lib/time-sliderd 66 | $(INSTALL_PROGRAM) $(DESTDIR)/usr/lib usr/lib/time-slider-delete 67 | $(INSTALL_PROGRAM) $(DESTDIR)/usr/lib usr/lib/time-slider-notify 68 | $(INSTALL_PROGRAM) $(DESTDIR)/usr/lib usr/lib/time-slider-snapshot 69 | $(INSTALL_PROGRAM) $(DESTDIR)/usr/lib usr/lib/time-slider-version 70 | $(INSTALL_PROGRAM) $(DESTDIR)/usr/lib/time-slider/plugins/zfssend usr/lib/time-slider/plugins/zfssend/zfssend 71 | $(INSTALL_PROGRAM) $(DESTDIR)/usr/lib/time-slider/plugins/rsync usr/lib/time-slider/plugins/rsync/rsync-trigger 72 | $(INSTALL_PROGRAM) $(DESTDIR)/usr/lib/time-slider/plugins/rsync usr/lib/time-slider/plugins/rsync/rsync-backup 73 | $(mkinstalldirs) $(DESTDIR)/usr/share/applications 74 | $(INSTALL_DATA) $(DESTDIR)/usr/share/applications usr/share/applications/time-slider.desktop 75 | $(mkinstalldirs) $(DESTDIR)/usr/share/icons/hicolor/16x16/apps 76 | $(INSTALL_DATA) $(DESTDIR)/usr/share/icons/hicolor/16x16/apps usr/share/icons/hicolor/16x16/apps/time-slider-setup.png 77 | $(mkinstalldirs) $(DESTDIR)/usr/share/icons/hicolor/24x24/apps 78 | $(INSTALL_DATA) $(DESTDIR)/usr/share/icons/hicolor/24x24/apps usr/share/icons/hicolor/24x24/apps/time-slider-setup.png 79 | $(mkinstalldirs) $(DESTDIR)/usr/share/icons/hicolor/32x32/apps 80 | $(INSTALL_DATA) $(DESTDIR)/usr/share/icons/hicolor/32x32/apps usr/share/icons/hicolor/32x32/apps/time-slider-setup.png 81 | $(mkinstalldirs) $(DESTDIR)/usr/share/icons/hicolor/36x36/apps 82 | $(INSTALL_DATA) $(DESTDIR)/usr/share/icons/hicolor/36x36/apps usr/share/icons/hicolor/36x36/apps/time-slider-setup.png 83 | $(mkinstalldirs) $(DESTDIR)/usr/share/icons/hicolor/48x48/apps 84 | $(INSTALL_DATA) $(DESTDIR)/usr/share/icons/hicolor/48x48/apps usr/share/icons/hicolor/48x48/apps/time-slider-setup.png 85 | $(mkinstalldirs) $(DESTDIR)/usr/share/icons/hicolor/72x72/apps 86 | $(INSTALL_DATA) $(DESTDIR)/usr/share/icons/hicolor/72x72/apps usr/share/icons/hicolor/72x72/apps/time-slider-setup.png 87 | $(mkinstalldirs) $(DESTDIR)/usr/share/icons/hicolor/96x96/apps 88 | $(INSTALL_DATA) $(DESTDIR)/usr/share/icons/hicolor/96x96/apps usr/share/icons/hicolor/96x96/apps/time-slider-setup.png 89 | $(mkinstalldirs) $(DESTDIR)/usr/share/time-slider/ui 90 | $(INSTALL_DATA) $(DESTDIR)/usr/share/time-slider/ui usr/share/time-slider/ui/time-slider-delete.ui 91 | $(INSTALL_DATA) $(DESTDIR)/usr/share/time-slider/ui usr/share/time-slider/ui/time-slider-setup.ui 92 | $(INSTALL_DATA) $(DESTDIR)/usr/share/time-slider/ui usr/share/time-slider/ui/time-slider-snapshot.ui 93 | $(INSTALL_DATA) $(DESTDIR)/usr/share/time-slider/ui usr/share/time-slider/ui/time-slider-version.ui 94 | $(mkinstalldirs) $(DESTDIR)/usr/share/time-slider/lib/time_slider 95 | for file in usr/share/time-slider/lib/time_slider/*.py; do \ 96 | if test -f $$file ; then \ 97 | $(INSTALL_DATA) $(DESTDIR)/usr/share/time-slider/lib/time_slider $$file; \ 98 | fi; \ 99 | done 100 | $(mkinstalldirs) $(DESTDIR)/usr/share/time-slider/lib/plugin 101 | for file in usr/share/time-slider/lib/plugin/*.py; do \ 102 | if test -f $$file ; then \ 103 | $(INSTALL_DATA) $(DESTDIR)/usr/share/time-slider/lib/plugin $$file; \ 104 | fi; \ 105 | done 106 | $(mkinstalldirs) $(DESTDIR)/usr/share/time-slider/lib/plugin/rsync 107 | for file in usr/share/time-slider/lib/plugin/rsync/*.py; do \ 108 | if test -f $$file ; then \ 109 | $(INSTALL_DATA) $(DESTDIR)/usr/share/time-slider/lib/plugin/rsync $$file; \ 110 | fi; \ 111 | done 112 | $(mkinstalldirs) $(DESTDIR)/usr/share/time-slider/lib/plugin/zfssend 113 | for file in usr/share/time-slider/lib/plugin/zfssend/*.py; do \ 114 | if test -f $$file ; then \ 115 | $(INSTALL_DATA) $(DESTDIR)/usr/share/time-slider/lib/plugin/zfssend $$file; \ 116 | fi; \ 117 | done 118 | $(mkinstalldirs) $(DESTDIR)/lib/svc/manifest/application 119 | $(INSTALL_DATA) $(DESTDIR)/lib/svc/manifest/application lib/svc/manifest/application/time-slider.xml 120 | $(INSTALL_DATA) $(DESTDIR)/lib/svc/manifest/application lib/svc/manifest/application/time-slider-plugin.xml 121 | $(mkinstalldirs) $(DESTDIR)/lib/svc/manifest/system/filesystem 122 | $(INSTALL_DATA) $(DESTDIR)/lib/svc/manifest/system/filesystem lib/svc/manifest/system/filesystem/auto-snapshot.xml 123 | $(PYTHON) py-compile.py 124 | 125 | uninstall: 126 | for subdir in $(SUBDIRS); do \ 127 | cd $$subdir; \ 128 | make DESTDIR=$(DESTDIR) GETTEXT_PACKAGE=time-slider uninstall; \ 129 | cd ..;\ 130 | done 131 | $(RM) $(DESTDIR)/etc/dbus-1/system.d/time-slider.conf 132 | $(RM) $(DESTDIR)/etc/xdg/autostart/time-slider-notify.desktop 133 | $(RM) $(DESTDIR)/lib/svc/method/time-slider 134 | $(RM) $(DESTDIR)/lib/svc/method/time-slider-plugin 135 | $(RM) $(DESTDIR)/lib/svc/method/time-slider-rsync 136 | $(RM) $(DESTDIR)/usr/bin/time-slider-setup 137 | $(RM) $(DESTDIR)/usr/lib/time-sliderd 138 | $(RM) $(DESTDIR)/usr/lib/time-slider-delete 139 | $(RM) $(DESTDIR)/usr/lib/time-slider-notify 140 | $(RM) $(DESTDIR)/usr/lib/time-slider-snapshot 141 | $(RM) $(DESTDIR)/usr/lib/time-slider-version 142 | $(RM) $(DESTDIR)/usr/lib/time-slider-zfssend 143 | $(RM) $(DESTDIR)/usr/lib/time-slider-rsync 144 | $(RMRF) $(DESTDIR)/usr/lib/time-slider/plugins/rsync 145 | $(RMRF) $(DESTDIR)/usr/lib/time-slider/plugins/zfssend 146 | $(RM) $(DESTDIR)/usr/share/applications/time-slider.desktop 147 | $(RM) $(DESTDIR)/usr/share/icons/hicolor/*/apps/time-slider-setup.png 148 | $(RMRF) $(DESTDIR)/usr/share/time-slider 149 | $(RM) $(DESTDIR)/lib/svc/manifest/application/time-slider.xml 150 | $(RM) $(DESTDIR)/lib/svc/manifest/application/time-slider-plugin.xml 151 | $(RM) $(DESTDIR)/lib/svc/manifest/system/filesystem/auto-snapshot.xml 152 | -------------------------------------------------------------------------------- /VERSION: -------------------------------------------------------------------------------- 1 | VERSION = 0.2.101 2 | -------------------------------------------------------------------------------- /data/Makefile: -------------------------------------------------------------------------------- 1 | 2 | top_builddir = .. 3 | top_srcdir = .. 4 | mkinstalldirs = /usr/bin/mkdir -p 5 | INSTALL = /usr/sbin/install 6 | INSTALL_DATA = ${INSTALL} -u root -g bin -m 644 -f 7 | RM = /usr/bin/rm -f 8 | RMDIR = /usr/bin/rmdir 9 | INTLTOOL_MERGE = /usr/bin/intltool-merge 10 | desktop_in_files = time-slider.desktop.in 11 | desktop_DATA = $(desktop_in_files:.desktop.in=.desktop) 12 | 13 | all: $(desktop_DATA) 14 | 15 | install: 16 | $(mkinstalldirs) $(DESTDIR)/usr/share/applications 17 | $(INSTALL_DATA) $(DESTDIR)/usr/share/applications time-slider.desktop 18 | 19 | uninstall: 20 | $(RM) $(DESTDIR)/usr/share/applications/time-slider.desktop 21 | 22 | %.desktop: %.desktop.in $(INTLTOOL_MERGE) $(wildcard $(top_srcdir)/po/*.po) ; LC_ALL=C $(INTLTOOL_MERGE) -d -u -c $(top_builddir)/po/.intltool-merge-cache $(top_srcdir)/po $< $@ 23 | 24 | -------------------------------------------------------------------------------- /data/time-slider.desktop.in: -------------------------------------------------------------------------------- 1 | [Desktop Entry] 2 | Encoding=UTF-8 3 | _Name=Time Slider 4 | _Comment=Configure the system to take automatic snapshots of your data 5 | Terminal=false 6 | Type=Application 7 | StartupNotify=true 8 | Exec=time-slider-setup 9 | Icon=time-slider-setup 10 | Categories=Application;System;Settings 11 | -------------------------------------------------------------------------------- /etc/dbus-1/system.d/time-slider.conf: -------------------------------------------------------------------------------- 1 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | -------------------------------------------------------------------------------- /etc/xdg/autostart/time-slider-notify.desktop: -------------------------------------------------------------------------------- 1 | [Desktop Entry] 2 | Encoding=UTF-8 3 | Name=Time Slider notification applet 4 | Comment=Show Time Slider status and notifications 5 | Icon=time-slider-setup 6 | Exec=/usr/lib/time-slider-notify 7 | Terminal=false 8 | Type=Application 9 | Categories= 10 | X-GNOME-Autostart-enabled=true 11 | -------------------------------------------------------------------------------- /lib/svc/manifest/application/time-slider-plugin.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 29 | 30 | 31 | 32 | 36 | 37 | 38 | 43 | 44 | 45 | 46 | 51 | 56 | 57 | 58 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 72 | 74 | 75 | 76 | 77 | 96 | 97 | 98 | 100 | 102 | 104 | 105 | 106 | 107 | 109 | 110 | 139 | 140 | 141 | 142 | 144 | 146 | 147 | 148 | 149 | 151 | 152 | 153 | 161 | 162 | 163 | 165 | 166 | 167 | 168 | 169 | 170 | 171 | 176 | 177 | 178 | 183 | 184 | 185 | 186 | 188 | 190 | 191 | 192 | 193 | 195 | 196 | 197 | 198 | 200 | 202 | 208 | 210 | 219 | 221 | 227 | 229 | 230 | 231 | 232 | 233 | 234 | 255 | 256 | 257 | -------------------------------------------------------------------------------- /lib/svc/manifest/application/time-slider.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 25 | 26 | 27 | 28 | 32 | 33 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 51 | 52 | 53 | 54 | 69 | 74 | 75 | 76 | 77 | 78 | 79 | 84 | 85 | 86 | 87 | 88 | 89 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 102 | 103 | 104 | 105 | 107 | 109 | 110 | 111 | 112 | 114 | 116 | 117 | 118 | 119 | 121 | 123 | 127 | 129 | 131 | 132 | 133 | 134 | 136 | 138 | 140 | 142 | 144 | 145 | 146 | 147 | 148 | 161 | 162 | 163 | 164 | -------------------------------------------------------------------------------- /lib/svc/manifest/system/filesystem/auto-snapshot.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 29 | 30 | 31 | 32 | 36 | 37 | 38 | 43 | 44 | 45 | 46 | 51 | 52 | 53 | 58 | 59 | 60 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 74 | 76 | 77 | 78 | 79 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 97 | 98 | 99 | 104 | 105 | 106 | 107 | 108 | 110 | 112 | 113 | 114 | 115 | 116 | 118 | 120 | 122 | 123 | 124 | 125 | 126 | 127 | 132 | 133 | 134 | 135 | 136 | 138 | 140 | 141 | 142 | 143 | 144 | 146 | 148 | 150 | 151 | 152 | 153 | 154 | 155 | 156 | 161 | 162 | 163 | 164 | 165 | 167 | 169 | 170 | 171 | 172 | 173 | 175 | 177 | 179 | 180 | 181 | 182 | 183 | 184 | 189 | 190 | 191 | 192 | 193 | 195 | 197 | 198 | 199 | 200 | 201 | 203 | 205 | 207 | 208 | 209 | 210 | 211 | 212 | 213 | 218 | 219 | 220 | 221 | 222 | 224 | 226 | 227 | 228 | 229 | 230 | 232 | 234 | 236 | 237 | 238 | 239 | 240 | 241 | 242 | 243 | 261 | 262 | 263 | -------------------------------------------------------------------------------- /lib/svc/method/time-slider: -------------------------------------------------------------------------------- 1 | #!/bin/ksh 2 | # 3 | # CDDL HEADER START 4 | # 5 | # The contents of this file are subject to the terms of the 6 | # Common Development and Distribution License (the "License"). 7 | # You may not use this file except in compliance with the License. 8 | # 9 | # You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 10 | # or http://www.opensolaris.org/os/licensing. 11 | # See the License for the specific language governing permissions 12 | # and limitations under the License. 13 | # 14 | # When distributing Covered Code, include this CDDL HEADER in each 15 | # file and include the License file at usr/src/OPENSOLARIS.LICENSE. 16 | # If applicable, add the following below this CDDL HEADER, with the 17 | # fields enclosed by brackets "[]" replaced with your own identifying 18 | # information: Portions Copyright [yyyy] [name of copyright owner] 19 | # 20 | # CDDL HEADER END 21 | # 22 | # 23 | # Copyright 2006 Sun Microsystems, Inc. All rights reserved. 24 | # Use is subject to license terms. 25 | # 26 | # ident "%Z%%M% %I% %E% SMI" 27 | 28 | . /lib/svc/share/smf_include.sh 29 | 30 | 31 | # Given the exit status of a command, an integer, 0 if the command completed 32 | # without errors. If the command exited with errors we degrade the 33 | # state of this service into maintenance mode. If a 3rd argument is presented 34 | # we don't degrade the service. We also log an error message as passed into 35 | # this function. 36 | # 37 | function check_failure { # integer exit status, error message, non-fatal flag 38 | 39 | typeset RESULT=$1 40 | typeset ERR_MSG=$2 41 | typeset NON_FATAL=$3 42 | 43 | if [ $RESULT -ne 0 ] ; then 44 | print_log "Error: $ERR_MSG" 45 | if [ -z "${NON_FATAL}" ] ; then 46 | print_log "Moving service $SMF_FMRI to maintenance mode." 47 | svcadm mark maintenance $SMF_FMRI 48 | fi 49 | fi 50 | 51 | } 52 | 53 | 54 | # A function we use to emit output. Right now, this goes to syslog via logger(1) 55 | # as well as being echoed to stdout which will result in it being picked up by 56 | # SMF if the $LOG variable is null. 57 | # 58 | function print_log { # message to display 59 | logger -t time-slider -p daemon.notice $* 60 | echo $* 61 | } 62 | 63 | # this function removes any left over cron jobs belonging to 64 | # legacy zfs-auto-snapshot 65 | # 66 | function remove_legacy_cronjobs { 67 | 68 | crontab -l | grep -v "/lib/svc/method/zfs-auto-snapshot" \ 69 | > /tmp/saved-crontab.$$ 70 | 71 | crontab /tmp/saved-crontab.$$ 72 | check_failure $? "Unable to remove legacy zfs-auto-snaphot cron jobs" "NON_FATAL" 73 | 74 | rm /tmp/saved-crontab.$$ 75 | 76 | } 77 | 78 | case "$1" in 79 | 'start') 80 | remove_legacy_cronjobs 81 | 82 | [ ! -x /usr/lib/time-sliderd ] && exit $SMF_EXIT_ERR_CONFIG 83 | 84 | /usr/lib/time-sliderd 85 | err=$? 86 | if [ $err -ne 0 ]; then 87 | echo "Time Slider failed to start: error $err" 88 | exit $SMF_EXIT_ERR_FATAL 89 | fi 90 | ;; 91 | *) 92 | echo "Usage: $0 { start }" 93 | exit $SMF_EXIT_ERR_FATAL 94 | ;; 95 | esac 96 | 97 | exit $SMF_EXIT_OK 98 | -------------------------------------------------------------------------------- /lib/svc/method/time-slider-plugin: -------------------------------------------------------------------------------- 1 | #!/bin/ksh 2 | # 3 | # CDDL HEADER START 4 | # 5 | # The contents of this file are subject to the terms of the 6 | # Common Development and Distribution License (the "License"). 7 | # You may not use this file except in compliance with the License. 8 | # 9 | # You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 10 | # or http://www.opensolaris.org/os/licensing. 11 | # See the License for the specific language governing permissions 12 | # and limitations under the License. 13 | # 14 | # When distributing Covered Code, include this CDDL HEADER in each 15 | # file and include the License file at usr/src/OPENSOLARIS.LICENSE. 16 | # If applicable, add the following below this CDDL HEADER, with the 17 | # fields enclosed by brackets "[]" replaced with your own identifying 18 | # information: Portions Copyright [yyyy] [name of copyright owner] 19 | # 20 | # CDDL HEADER END 21 | # 22 | # 23 | # Copyright 2010 Sun Microsystems, Inc. All rights reserved. 24 | # Use is subject to license terms. 25 | # 26 | 27 | . /lib/svc/share/smf_include.sh 28 | 29 | case "$1" in 30 | 'start') 31 | PLUGIN_CMD="$(svcprop -p plugin/trigger_command $SMF_FMRI)" 32 | if [ $? -ne 0 ] ; then 33 | echo "Unable to obtain plugin invocation command" 34 | exit $SMF_EXIT_ERR_CONFIG 35 | fi 36 | 37 | if [ ! -x $PLUGIN_CMD ] ; then 38 | echo "\"$PLUGIN_CMD\" is not an executable path" 39 | exit $SMF_EXIT_ERR_CONFIG 40 | fi 41 | ;; 42 | *) 43 | echo "Usage: $0 { start }" 44 | exit $SMF_EXIT_ERR_FATAL 45 | ;; 46 | esac 47 | 48 | exit $SMF_EXIT_OK 49 | 50 | -------------------------------------------------------------------------------- /lib/svc/method/time-slider-rsync: -------------------------------------------------------------------------------- 1 | #!/bin/ksh 2 | # 3 | # CDDL HEADER START 4 | # 5 | # The contents of this file are subject to the terms of the 6 | # Common Development and Distribution License (the "License"). 7 | # You may not use this file except in compliance with the License. 8 | # 9 | # You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 10 | # or http://www.opensolaris.org/os/licensing. 11 | # See the License for the specific language governing permissions 12 | # and limitations under the License. 13 | # 14 | # When distributing Covered Code, include this CDDL HEADER in each 15 | # file and include the License file at usr/src/OPENSOLARIS.LICENSE. 16 | # If applicable, add the following below this CDDL HEADER, with the 17 | # fields enclosed by brackets "[]" replaced with your own identifying 18 | # information: Portions Copyright [yyyy] [name of copyright owner] 19 | # 20 | # CDDL HEADER END 21 | # 22 | # 23 | # Copyright 2006 Sun Microsystems, Inc. All rights reserved. 24 | # Use is subject to license terms. 25 | # A rather simple SMF method script for 26 | # svc:/application/time-slider:default 27 | # It's sole purpose is to set the correct SMF property value of it's 28 | # direct "auto-snapshot:" dependencies: zfs/fs-name = '//' 29 | 30 | . /lib/svc/share/smf_include.sh 31 | 32 | RSYNC_PROG="/usr/lib/time-slider/plugins/rsync/rsync-backup" 33 | 34 | # This function sets the appropriate svc configuration property for all 35 | # dependent auto-snapshot: instances if necessary 36 | function update_service_props { 37 | DEPENDENCIES=$(svcs -d -H -o fmri $SMF_FMRI|grep auto-snapshot) 38 | for dependency in ${DEPENDENCIES} ; do 39 | if [ "$(svcprop -p zfs/fs-name $dependency)" != "//" ] ; then 40 | svccfg -s $dependency setprop zfs/fs-name = astring: "//" 41 | svcadm refresh $dependency 42 | fi 43 | done 44 | } 45 | 46 | # Given the exit status of a command, an integer, 0 if the command completed 47 | # without errors. If the command exited with errors we degrade the 48 | # state of this service into maintenance mode. If a 3rd argument is presented 49 | # we don't degrade the service. We also log an error message as passed into 50 | # this function. 51 | # 52 | function check_failure { # integer exit status, error message to display, be fatal 53 | 54 | typeset RESULT=$1 55 | typeset ERR_MSG=$2 56 | typeset NON_FATAL=$3 57 | 58 | if [ $RESULT -ne 0 ] ; then 59 | print_log "Error: $ERR_MSG" 60 | print_log "Moving service $SMF_FMRI to maintenance mode." 61 | if [ -z "${NON_FATAL}" ] ; then 62 | print_log "Moving service $SMF_FMRI to maintenance mode." 63 | svcadm mark maintenance $SMF_FMRI 64 | fi 65 | fi 66 | 67 | } 68 | 69 | # A function we use to emit output. Right now, this goes to syslog via logger(1) 70 | function print_log { # message to display 71 | logger -t time-slider -p daemon.notice $* 72 | } 73 | 74 | 75 | function add_rsync_cronjob { 76 | # Call every 30 minutes, 15 and 45 minutes after the hour. 77 | SCHEDULE="15,45 * * * *" 78 | 79 | # adding a cron job is essentially just looking for an existing entry, 80 | # removing it, and appending a new one. Neato. 81 | crontab -l | grep -v "${RSYNC_PROG} $SMF_FMRI" \ 82 | > /tmp/saved-crontab.$$ 83 | 84 | echo "${SCHEDULE} ${RSYNC_PROG} $SMF_FMRI" \ 85 | >> /tmp/saved-crontab.$$ 86 | 87 | crontab /tmp/saved-crontab.$$ 88 | check_failure $? "Unable to add cron job!" 89 | 90 | rm /tmp/saved-crontab.$$ 91 | return 0 92 | } 93 | 94 | function remove_rsync_cronjob { 95 | 96 | crontab -l | grep -v "${RSYNC_PROG} $SMF_FMRI$" \ 97 | > /tmp/saved-crontab.$$ 98 | crontab /tmp/saved-crontab.$$ 99 | check_failure $? "Unable to unschedule cron job for $SMF_FMRI" 100 | 101 | rm /tmp/saved-crontab.$$ 102 | 103 | # finally, check our status before we return 104 | STATE=$(svcprop -p restarter/state $SMF_FMRI) 105 | if [ "${STATE}" == "maintenance" ] ; then 106 | STATE=1 107 | else 108 | STATE=0 109 | fi 110 | } 111 | 112 | case $SMF_METHOD in 113 | "start") 114 | #FIXME - This HAS to be == rsync-trigger program 115 | PLUGIN_CMD="$(svcprop -p plugin/trigger_command $SMF_FMRI)" 116 | if [ $? -ne 0 ] ; then 117 | echo "Unable to obtain plugin invocation command" 118 | exit $SMF_EXIT_ERR_CONFIG 119 | fi 120 | 121 | if [ ! -x $PLUGIN_CMD ] ; then 122 | echo "\"$PLUGIN_CMD\" is not an executable path" 123 | exit $SMF_EXIT_ERR_CONFIG 124 | fi 125 | add_rsync_cronjob 126 | ;; 127 | "stop") 128 | remove_rsync_cronjob 129 | ;; 130 | *) 131 | echo "Command line invocation of ${0} unsupported." 132 | echo "This script is intended for smf(5) invocation only" 133 | exit $SMF_EXIT_ERR_NOSMF 134 | ;; 135 | esac 136 | exit $SMF_EXIT_OK 137 | -------------------------------------------------------------------------------- /po/ChangeLog: -------------------------------------------------------------------------------- 1 | 2010-01-21 Harry Fu 7 | 8 | * LINGUAS: new languages are added - ar,id,nl 9 | * *.po: Updated cs/hu/pl with the latest translations. 10 | 11 | 2009-02-19 Takao Fujiwara 12 | 13 | * POTFILES.in: Added time-slider-version.glade and fileversion.py 14 | * *.po: Updated with the latest strings. 15 | 16 | 2008-11-04 Takao Fujiwara 17 | 18 | * *.po: Updated with the latest strings. 19 | 20 | 2008-10-20 Takao Fujiwara 21 | 22 | * LINGUAS: Added to compile .mo files. 23 | * Makefile: Added. 24 | * POTFILES.in: Added for intltool. 25 | * cs.po de.po es.po fr.po hu.po it.po ja.po ko.po pl.po pt_BR.po ru.po 26 | sv.po zh_CN.po zh_HK.po zh_TW.po: Added .po files. 27 | 28 | -------------------------------------------------------------------------------- /po/LINGUAS: -------------------------------------------------------------------------------- 1 | # please keep this list sorted alphabetically 2 | # 3 | ar 4 | ca 5 | cs 6 | de 7 | es 8 | fr 9 | hu 10 | id 11 | it 12 | ja 13 | ko 14 | nl 15 | pl 16 | pt_BR 17 | ru 18 | sv 19 | zh_CN 20 | zh_HK 21 | zh_TW 22 | -------------------------------------------------------------------------------- /po/Makefile: -------------------------------------------------------------------------------- 1 | srcdir = . 2 | top_srcdir = .. 3 | top_builddir = .. 4 | 5 | subdir = po 6 | prefix = /usr 7 | DATADIRNAME = share 8 | itlocaledir = $(prefix)/$(DATADIRNAME)/locale 9 | LOCALE_OWNER = root 10 | LOCALE_GROUP = root 11 | #mkdir_p = install -d -m 755 -o $(LOCALE_OWNER) -g $(LOCALE_GROUP) 12 | mkdir_p = install -d -m 755 13 | 14 | PACKAGE = $(firstword $(patsubst PACKAGE=%, %, $(shell grep "^PACKAGE=" $(top_srcdir)/Makefile))) 15 | VERSION = $(firstword $(patsubst VERSION=%, %, $(shell grep "^VERSION=" $(top_srcdir)/Makefile))) 16 | GETTEXT_PACKAGE = $(PACKAGE) 17 | 18 | INSTALL = install -c 19 | INSTALL_DATA = ${INSTALL} -m 644 20 | 21 | GMSGFMT = msgfmt 22 | MSGFMT = msgfmt 23 | 24 | ALL_LINGUAS = 25 | 26 | PO_LINGUAS=$(shell if test -r $(srcdir)/LINGUAS; then grep -v "^\#" $(srcdir)/LINGUAS; fi) 27 | 28 | USER_LINGUAS=$(shell if test -n "$(LINGUAS)"; then LLINGUAS="$(LINGUAS)"; ALINGUAS="$(ALL_LINGUAS)"; for lang in $$LLINGUAS; do if test -n "`grep ^$$lang$$ $(srcdir)/LINGUAS`" -o -n "`echo $$ALINGUAS|grep ' ?$$lang ?'`"; then printf "$$lang "; fi; done; fi) 29 | 30 | USE_LINGUAS=$(shell if test -n "$(USER_LINGUAS)"; then LLINGUAS="$(USER_LINGUAS)"; else if test -n "$(PO_LINGUAS)"; then LLINGUAS="$(PO_LINGUAS)"; else LLINGUAS="$(ALL_LINGUAS)"; fi; fi; for lang in $$LLINGUAS; do printf "$$lang "; done) 31 | 32 | POFILES=$(shell LINGUAS="$(USE_LINGUAS)"; for lang in $$LINGUAS; do printf "$$lang.po "; done) 33 | 34 | DISTFILES = Makefile POTFILES.in $(POFILES) 35 | EXTRA_DISTFILES = LINGUAS 36 | 37 | CATALOGS=$(shell LINGUAS="$(USE_LINGUAS)"; for lang in $$LINGUAS; do printf "$$lang.gmo "; done) 38 | 39 | .SUFFIXES: 40 | .SUFFIXES: .po .gmo 41 | 42 | .po.gmo: 43 | file=`echo $* | sed 's,.*/,,'`.gmo \ 44 | && rm -f $$file && $(GMSGFMT) -o $$file $< 45 | 46 | all: all-yes 47 | 48 | all-yes: $(CATALOGS) 49 | all-no: 50 | 51 | install: install-data 52 | install-data: install-data-yes 53 | install-data-no: all 54 | install-data-yes: all 55 | $(mkdir_p) $(DESTDIR)$(itlocaledir) 56 | linguas="$(USE_LINGUAS)"; \ 57 | for lang in $$linguas; do \ 58 | dir=$(DESTDIR)$(itlocaledir)/$$lang/LC_MESSAGES; \ 59 | $(mkdir_p) $$dir; \ 60 | if test -r $$lang.gmo; then \ 61 | $(INSTALL_DATA) $$lang.gmo $$dir/$(GETTEXT_PACKAGE).mo; \ 62 | echo "installing $$lang.gmo as $$dir/$(GETTEXT_PACKAGE).mo"; \ 63 | else \ 64 | $(INSTALL_DATA) $(srcdir)/$$lang.gmo $$dir/$(GETTEXT_PACKAGE).mo; \ 65 | echo "installing $(srcdir)/$$lang.gmo as" \ 66 | "$$dir/$(GETTEXT_PACKAGE).mo"; \ 67 | fi; \ 68 | done 69 | 70 | uninstall: 71 | linguas="$(USE_LINGUAS)"; \ 72 | for lang in $$linguas; do \ 73 | rm -f $(DESTDIR)$(itlocaledir)/$$lang/LC_MESSAGES/$(GETTEXT_PACKAGE).mo; \ 74 | done 75 | 76 | clean: 77 | rm -f *.gmo 78 | rm -f .intltool-merge-cache 79 | 80 | distdir = ../$(PACKAGE)-$(VERSION)/$(subdir) 81 | dist distdir: $(DISTFILES) 82 | dists="$(DISTFILES)"; \ 83 | extra_dists="$(EXTRA_DISTFILES)"; \ 84 | for file in $$extra_dists; do \ 85 | test -f $(srcdir)/$$file && dists="$$dists $(srcdir)/$$file"; \ 86 | done; \ 87 | for file in $$dists; do \ 88 | test -f $$file || file="$(srcdir)/$$file"; \ 89 | ln $$file $(distdir) 2> /dev/null \ 90 | || cp -p $$file $(distdir); \ 91 | done 92 | 93 | .NOEXPORT: 94 | -------------------------------------------------------------------------------- /po/POTFILES.in: -------------------------------------------------------------------------------- 1 | # List of source files containing translatable strings. 2 | # Please keep this file sorted alphabetically. 3 | data/time-slider.desktop.in 4 | usr/share/time-slider/glade/time-slider-delete.glade 5 | usr/share/time-slider/glade/time-slider-setup.glade 6 | usr/share/time-slider/glade/time-slider-snapshot.glade 7 | usr/share/time-slider/glade/time-slider-version.glade 8 | usr/share/time-slider/lib/time_slider/applet.py 9 | usr/share/time-slider/lib/time_slider/deletegui.py 10 | usr/share/time-slider/lib/time_slider/fileversion.py 11 | usr/share/time-slider/lib/time_slider/setupgui.py 12 | usr/share/time-slider/lib/time_slider/snapnowui.py 13 | -------------------------------------------------------------------------------- /po/ar.po: -------------------------------------------------------------------------------- 1 | # Copyright (C) 2009 Sun Microsystems, Inc 2 | # This file is distributed under the same license as the time-slider package. 3 | # 4 | # Desktop Discuss 5 | # 6 | msgid "" 7 | msgstr "" 8 | "Project-Id-Version: time-slider HEAD\n" 9 | "Report-Msgid-Bugs-To: \n" 10 | "POT-Creation-Date: 2009-12-07 22:37+0900\n" 11 | "PO-Revision-Date: 2010-01-20 20:31+0100\n" 12 | "Last-Translator: Hosam Al Ali\n" 13 | "Language-Team: Arabic language team \n" 14 | "MIME-Version: 1.0\n" 15 | "Content-Type: text/plain; charset=UTF-8\n" 16 | "Content-Transfer-Encoding: 8bit\n" 17 | 18 | #: ../data/time-slider.desktop.in.h:1 19 | msgid "Configure the system to take automatic snapshots of your data" 20 | msgstr "تاكيد النظام على أخذ صورة عن النظام عن البيانات " 21 | 22 | #: ../data/time-slider.desktop.in.h:2 23 | #: ../usr/share/time-slider/lib/time_slider/applet.py:185 24 | msgid "Time Slider" 25 | msgstr "شريط تمرير الوقت " 26 | 27 | #: ../usr/share/time-slider/glade/time-slider-delete.glade.h:2 28 | #: ../usr/share/time-slider/lib/time_slider/deletegui.py:242 29 | #: ../usr/share/time-slider/lib/time_slider/deletegui.py:342 30 | #, no-c-format, python-format 31 | msgid "%d snapshots will be deleted." 32 | msgstr "%d الصورة سوف تمحى " 33 | 34 | #: ../usr/share/time-slider/glade/time-slider-delete.glade.h:3 35 | msgid "Summary" 36 | msgstr " ملخص " 37 | 38 | #: ../usr/share/time-slider/glade/time-slider-delete.glade.h:4 39 | msgid "D_eselect All" 40 | msgstr "D_eselect All" 41 | 42 | #: ../usr/share/time-slider/glade/time-slider-delete.glade.h:5 43 | msgid "Delete Snapshots" 44 | msgstr "حذف صور النظام " 45 | 46 | #: ../usr/share/time-slider/glade/time-slider-delete.glade.h:6 47 | msgid "Deleting Snapshots" 48 | msgstr "حذف صور النظام " 49 | 50 | #: ../usr/share/time-slider/glade/time-slider-delete.glade.h:7 51 | msgid "Deleting snapshots..." 52 | msgstr "حذف صور النظام..." 53 | 54 | #: ../usr/share/time-slider/glade/time-slider-delete.glade.h:8 55 | msgid "Exit when done. " 56 | msgstr "الخروج عن الإنتهاء " 57 | 58 | #: ../usr/share/time-slider/glade/time-slider-delete.glade.h:9 59 | msgid "File System:" 60 | msgstr "نظام الملفات : " 61 | 62 | #: ../usr/share/time-slider/glade/time-slider-delete.glade.h:10 63 | msgid "Name:" 64 | msgstr "إسم : " 65 | 66 | #: ../usr/share/time-slider/glade/time-slider-delete.glade.h:11 67 | msgid "Press Delete to continue." 68 | msgstr "إضغط على حذف للإستمرار " 69 | 70 | #: ../usr/share/time-slider/glade/time-slider-delete.glade.h:12 71 | msgid "Scanning snapshots..." 72 | msgstr "فحص صور النظام " 73 | 74 | #: ../usr/share/time-slider/glade/time-slider-delete.glade.h:13 75 | msgid "Select ZFS snapshots to delete from the list below." 76 | msgstr "إختر ZFS صور النظام لحذف من اسفل القائمة " 77 | 78 | #: ../usr/share/time-slider/glade/time-slider-setup.glade.h:1 79 | msgid "Enable Time Slider" 80 | msgstr "تفعيل شريط تمرير الوقت" 81 | 82 | #: ../usr/share/time-slider/glade/time-slider-setup.glade.h:2 83 | msgid "File Systems To Back Up" 84 | msgstr " ملف النظام للنسخة الإحتياطية " 85 | 86 | #: ../usr/share/time-slider/glade/time-slider-setup.glade.h:3 87 | msgid "Advanced Options" 88 | msgstr "خيارات إضافية " 89 | 90 | #: ../usr/share/time-slider/glade/time-slider-setup.glade.h:4 91 | msgid "C_ustom:" 92 | msgstr "C_ustom:" 93 | 94 | #: ../usr/share/time-slider/glade/time-slider-setup.glade.h:5 95 | msgid "Configuring Time Slider..." 96 | msgstr "إعدادات شريط التوقيت..." 97 | 98 | #: ../usr/share/time-slider/glade/time-slider-setup.glade.h:6 99 | msgid "For advanced users" 100 | msgstr "من أجل المستخدمين المتقدمين " 101 | 102 | #: ../usr/share/time-slider/glade/time-slider-setup.glade.h:7 103 | msgid "Recommended for most users" 104 | msgstr "المطلوب من كل المستخدمين " 105 | 106 | #: ../usr/share/time-slider/glade/time-slider-setup.glade.h:8 107 | msgid "Reduce backups when storage space usage exceeds:" 108 | msgstr "تخفيض النسخة الإحتياطية عند تجاوز المساحة النخزينية " 109 | 110 | #: ../usr/share/time-slider/glade/time-slider-setup.glade.h:9 111 | msgid "Time Slider Manager" 112 | msgstr "إدارة شريط تمرير الوقت " 113 | 114 | #: ../usr/share/time-slider/glade/time-slider-setup.glade.h:10 115 | msgid "Time Slider backs up data regularly by taking timed ZFS Snapshots" 116 | msgstr "شريط تمرير الوقت يقوم بنسخ إحتياطي للبيانات بشكل منتظم " 117 | "بأخذ صورة للنظام " 118 | 119 | #: ../usr/share/time-slider/glade/time-slider-setup.glade.h:11 120 | msgid "_All" 121 | msgstr "_الكل " 122 | 123 | #: ../usr/share/time-slider/glade/time-slider-setup.glade.h:12 124 | msgid "_Delete Snapshots..." 125 | msgstr "_حذف صور النظام ...." 126 | 127 | #: ../usr/share/time-slider/glade/time-slider-setup.glade.h:13 128 | msgid "of file system capacity" 129 | msgstr "قدرة نظام ملفات النظام " 130 | 131 | #: ../usr/share/time-slider/glade/time-slider-snapshot.glade.h:1 132 | msgid "Take a snapshot now" 133 | msgstr "خذ نسخة من صورة النظام الأن " 134 | 135 | #: ../usr/share/time-slider/glade/time-slider-snapshot.glade.h:2 136 | msgid "Take a snapshot of " 137 | msgstr "خذ نسخة من صورة النظام " 138 | 139 | #: ../usr/share/time-slider/glade/time-slider-snapshot.glade.h:3 140 | msgid "with the name :" 141 | msgstr "بإسم :" 142 | 143 | #: ../usr/share/time-slider/glade/time-slider-version.glade.h:1 144 | msgid "10 Mo" 145 | msgstr "10 Mo" 146 | 147 | #: ../usr/share/time-slider/glade/time-slider-version.glade.h:2 148 | msgid "19/01/09" 149 | msgstr "19/01/09" 150 | 151 | #: ../usr/share/time-slider/glade/time-slider-version.glade.h:3 152 | msgid "Current Version" 153 | msgstr "الإصدار الحالي" 154 | 155 | #: ../usr/share/time-slider/glade/time-slider-version.glade.h:4 156 | msgid "Older Versions" 157 | msgstr "الإصدارات السابقة" 158 | 159 | #: ../usr/share/time-slider/glade/time-slider-version.glade.h:5 160 | msgid "Double click to open the file" 161 | msgstr "إضغط مرتين لفتح الملف " 162 | 163 | #: ../usr/share/time-slider/glade/time-slider-version.glade.h:6 164 | msgid "Open the current version of the file" 165 | msgstr "إفتح نفس إصدار الملف " 166 | 167 | #: ../usr/share/time-slider/glade/time-slider-version.glade.h:7 168 | msgid "Scanning for older versions" 169 | msgstr "الفحص للإصدارات الأقدم " 170 | 171 | #: ../usr/share/time-slider/glade/time-slider-version.glade.h:8 172 | msgid "Time Slider File Version Explorer" 173 | msgstr "متصفح ملف إصدار شريط تمرير الوقت " 174 | 175 | #: ../usr/share/time-slider/glade/time-slider-version.glade.h:9 176 | msgid "compare" 177 | msgstr "مقارنة " 178 | 179 | #: ../usr/share/time-slider/glade/time-slider-version.glade.h:10 180 | msgid "filename" 181 | msgstr "إسم الملف " 182 | 183 | #: ../usr/share/time-slider/glade/time-slider-version.glade.h:11 184 | msgid "gtk-close" 185 | msgstr "gtk-إغلاق " 186 | 187 | #: ../usr/share/time-slider/glade/time-slider-version.glade.h:12 188 | msgid "last modified : " 189 | msgstr "أخر تعديل : " 190 | 191 | #: ../usr/share/time-slider/glade/time-slider-version.glade.h:13 192 | msgid "name :" 193 | msgstr "إسم : " 194 | 195 | #: ../usr/share/time-slider/glade/time-slider-version.glade.h:14 196 | msgid "size : " 197 | msgstr "الحجم : " 198 | 199 | #: ../usr/share/time-slider/lib/time_slider/applet.py:57 200 | msgid "Time Slider: Low Space Warning" 201 | msgstr "شريط التوقيت : تحذير بإنخفاض المساحة" 202 | 203 | #: ../usr/share/time-slider/lib/time_slider/applet.py:75 204 | #, python-format 205 | msgid "Emergency: '%s' is full!" 206 | msgstr "الخطر : '%s' ممتلئ!" 207 | 208 | #: ../usr/share/time-slider/lib/time_slider/applet.py:76 209 | #, python-format 210 | msgid "The file system: '%s', is over %s%% full." 211 | msgstr "نظام الملفات: '%s', كامل %s%% ممتلئ." 212 | 213 | #: ../usr/share/time-slider/lib/time_slider/applet.py:78 214 | #, python-format 215 | msgid "" 216 | "The file system: '%s', is over %s%% full.\n" 217 | "As an emergency measure, Time Slider has destroyed all of its backups.\n" 218 | "To fix this problem, delete any unnecessary files on '%s', or add disk space " 219 | "(see ZFS documentation)." 220 | msgstr "" 221 | "نظام الملفات : `%s` وكامل %s%%. ممتلى \n" 222 | "والتدبير الإحترازي, شريط تمرير الوقت سوف يحذف كامل النسخ الإحتياطي . \n" 223 | "ولإصلاح هذه المشكلة , إحذف أي ملفات غير ضرورية '%s', أو أضف قرص أخر " 224 | 225 | #: ../usr/share/time-slider/lib/time_slider/applet.py:87 226 | #, python-format 227 | msgid "Emergency: '%s' is almost full!" 228 | msgstr "الخظر : '%s' على الأغلب كامل " 229 | 230 | #: ../usr/share/time-slider/lib/time_slider/applet.py:88 231 | #: ../usr/share/time-slider/lib/time_slider/applet.py:103 232 | #: ../usr/share/time-slider/lib/time_slider/applet.py:118 233 | #, python-format 234 | msgid "The file system: '%s', exceeded %s%% of its total capacity" 235 | msgstr "نظام الملفات: '%s', تجاوز %s%% فوق كل السعة الكلية " 236 | 237 | #: ../usr/share/time-slider/lib/time_slider/applet.py:91 238 | #, python-format 239 | msgid "" 240 | "The file system: '%s', exceeded %s%% of its total capacity. As an emerency " 241 | "measure, Time Slider has has destroyed most or all of its backups to prevent " 242 | "the disk becoming full. To prevent this from happening again, delete any " 243 | "unnecessary files on '%s', or add disk space (see ZFS documentation)." 244 | msgstr "" 245 | "نظام الملفات: '%s', تجاوز %s%% من اجمالي قدرتها. باعتبارها emerency" 246 | "قياس ، شريط الوقت سوف يلغي معظم ا كل ملفات النسخة الإحتياطي لمنع ملئ القرص . لمنع حدوث ذلك مرة أخرى, يجب عليك , delete any, حذف كل الملفات الغير مفيدة ضمنه '%s', أو أضف قرص جديد(إنظر ZFS دليل)." 247 | 248 | #: ../usr/share/time-slider/lib/time_slider/applet.py:102 249 | #, python-format 250 | msgid "Urgent: '%s' is almost full!" 251 | msgstr "عاجل : '%s' على الإغلب ممتلئ " 252 | 253 | #: ../usr/share/time-slider/lib/time_slider/applet.py:106 254 | #, python-format 255 | msgid "" 256 | "The file system: '%s', exceeded %s%% of its total capacity. As a remedial " 257 | "measure, Time Slider has destroyed some backups, and will destroy more, " 258 | "eventually all, as capacity continues to diminish.\n" 259 | "To prevent this from happening again, delete any unnecessary files on '%s', " 260 | "or add disk space (see ZFS documentation)." 261 | msgstr "" 262 | "The file system: '%s', exceeded %s%% of its total capacity. As a remedial " 263 | "measure, Time Slider has destroyed some backups, and will destroy more, " 264 | "eventually all, as capacity continues to diminish.\n" 265 | "To prevent this from happening again, delete any unnecessary files on '%s', " 266 | "or add disk space (see ZFS documentation)." 267 | 268 | #: ../usr/share/time-slider/lib/time_slider/applet.py:117 269 | #, python-format 270 | msgid "Warning: '%s' is getting full" 271 | msgstr "تحذير : '%s' الحصول على الكامل " 272 | 273 | #: ../usr/share/time-slider/lib/time_slider/applet.py:121 274 | #, python-format 275 | msgid "" 276 | "'%s' exceeded %s%% of its total capacity. To fix this, Time Slider has " 277 | "destroyed some recent backups, and will destroy more as capacity continues " 278 | "to diminish.\n" 279 | "To prevent this from happening again, delete any unnecessary files on '%s', " 280 | "or add disk space (see ZFS documentation).\n" 281 | msgstr "" 282 | "'%s' تجاوزت %s%% كامل المساحة المتاحة. لحل المشكلة, شريط الوقت يحذف كل ويزيل أخر ملفات النسخ الإحتياطي, وسوف يستمر بعملية الحذف الإزالة من أجل التقليص .\n" 283 | "لمنع حدوث ذلك مرة أخرى , يجب عليك حذف الملفات الغير مفيدة ضمنه'%s', " 284 | "أو اضف قرص جديد (إنظر ZFS دليل).\n" 285 | 286 | #: ../usr/share/time-slider/lib/time_slider/applet.py:137 287 | msgid "Details..." 288 | msgstr "تفاصيل..." 289 | 290 | #: ../usr/share/time-slider/lib/time_slider/deletegui.py:116 291 | #: ../usr/share/time-slider/lib/time_slider/deletegui.py:306 292 | #: ../usr/share/time-slider/lib/time_slider/setupgui.py:106 293 | msgid "Legacy" 294 | msgstr "إرث " 295 | 296 | #: ../usr/share/time-slider/lib/time_slider/deletegui.py:147 297 | #: ../usr/share/time-slider/lib/time_slider/setupgui.py:131 298 | msgid "Mount Point" 299 | msgstr "نقطة التحميل " 300 | 301 | #: ../usr/share/time-slider/lib/time_slider/deletegui.py:155 302 | #: ../usr/share/time-slider/lib/time_slider/setupgui.py:134 303 | msgid "File System Name" 304 | msgstr "إسم ملف النظام " 305 | 306 | #: ../usr/share/time-slider/lib/time_slider/deletegui.py:163 307 | msgid "Snapshot Name" 308 | msgstr "إسم صورة النظام " 309 | 310 | #: ../usr/share/time-slider/lib/time_slider/deletegui.py:171 311 | msgid "Creation Time" 312 | msgstr "إنشاء الوقت " 313 | 314 | #. Note to developers. 315 | #. The second element is for internal matching and should not 316 | #. be i18ned under any circumstances. 317 | #: ../usr/share/time-slider/lib/time_slider/deletegui.py:185 318 | #: ../usr/share/time-slider/lib/time_slider/deletegui.py:198 319 | msgid "All" 320 | msgstr "الكل " 321 | 322 | #: ../usr/share/time-slider/lib/time_slider/deletegui.py:199 323 | msgid "Monthly" 324 | msgstr "شهريا " 325 | 326 | #: ../usr/share/time-slider/lib/time_slider/deletegui.py:200 327 | msgid "Weekly" 328 | msgstr "إسبوعيا " 329 | 330 | #: ../usr/share/time-slider/lib/time_slider/deletegui.py:201 331 | msgid "Daily" 332 | msgstr "يومي " 333 | 334 | #: ../usr/share/time-slider/lib/time_slider/deletegui.py:202 335 | msgid "Hourly" 336 | msgstr "ساعي " 337 | 338 | #: ../usr/share/time-slider/lib/time_slider/deletegui.py:203 339 | msgid "1/4 Hourly" 340 | msgstr "1/4 ساعي " 341 | 342 | #: ../usr/share/time-slider/lib/time_slider/deletegui.py:223 343 | msgid "Snapshot can not be deleted" 344 | msgstr "صور النظام لا يمكن حذفها " 345 | 346 | #: ../usr/share/time-slider/lib/time_slider/deletegui.py:224 347 | #, python-format 348 | msgid "" 349 | "%s has one or more dependent clones and will not be deleted. To delete this " 350 | "snapshot, first delete all datasets and snapshots cloned from this snapshot." 351 | msgstr "" 352 | "%s هناك وأحد او أكثر من نسخ معتمدة على بعضها لا يمكن خذفها . " 353 | "لحذف صورة النظام , أولا أحذف كل datasetes ونسخ صور النظام من صور النظام " 354 | 355 | #: ../usr/share/time-slider/lib/time_slider/deletegui.py:240 356 | #: ../usr/share/time-slider/lib/time_slider/deletegui.py:340 357 | msgid "1 snapshot will be deleted." 358 | msgstr "1 صورة نظام سوف تحذف " 359 | 360 | #: ../usr/share/time-slider/lib/time_slider/deletegui.py:381 361 | msgid "Some snapshots could not be read" 362 | msgstr "بعض صور النظام لايمكن أن تقرأ " 363 | 364 | #: ../usr/share/time-slider/lib/time_slider/deletegui.py:405 365 | msgid "Some snapshots could not be deleted" 366 | msgstr "بعض صور النظام لايمكن أن تحذف " 367 | 368 | #: ../usr/share/time-slider/lib/time_slider/deletegui.py:539 369 | #: ../usr/share/time-slider/lib/time_slider/setupgui.py:422 370 | #: ../usr/share/time-slider/lib/time_slider/snapnowui.py:215 371 | msgid "Insufficient Priviliges" 372 | msgstr "الإمتيازات غير كافية " 373 | 374 | #: ../usr/share/time-slider/lib/time_slider/deletegui.py:540 375 | msgid "" 376 | "Snapshot deletion requires administrative privileges to run. You have not " 377 | "been assigned the necessaryadministrative priviliges.\n" 378 | "\n" 379 | "Consult your system administrator " 380 | msgstr "" 381 | "حذف صورة النظام تطلب إمتيازات المدراء لتتم ." 382 | "أنت لم تنقل أو تعرف على إمتيازات المدراء الضرورية . \n \n " 383 | 384 | #: ../usr/share/time-slider/lib/time_slider/fileversion.py:246 385 | #: ../usr/share/time-slider/lib/time_slider/fileversion.py:247 386 | msgid "Hint" 387 | msgstr "ملحوظة " 388 | 389 | #: ../usr/share/time-slider/lib/time_slider/fileversion.py:248 390 | msgid "" 391 | "Installing the optional meld package will enhance the file comparison " 392 | "visualization" 393 | msgstr "" 394 | "تنصيب إضافات meld الحزم سوف تحسن من عملية المقارنة المرئية " 395 | 396 | #: ../usr/share/time-slider/lib/time_slider/fileversion.py:316 397 | #: ../usr/share/time-slider/lib/time_slider/snapnowui.py:179 398 | msgid "Invalid arguments count." 399 | msgstr "فشل بعد الحجج " 400 | 401 | #: ../usr/share/time-slider/lib/time_slider/fileversion.py:318 402 | msgid "" 403 | "Version explorer requires 2 arguments :\n" 404 | "- The path of the root snapshot directory.\n" 405 | "- The filename to explore." 406 | msgstr "" 407 | "إصدار المستكشف يتطلب طلبين :\n" 408 | "- مسار مجلد صورة نظام حساب root" 409 | "- إسم الملف للإستعراض " 410 | 411 | #: ../usr/share/time-slider/lib/time_slider/setupgui.py:128 412 | msgid "Select" 413 | msgstr "إختر " 414 | 415 | #: ../usr/share/time-slider/lib/time_slider/setupgui.py:154 416 | msgid "Snapshot manager service dependency error" 417 | msgstr "خدمة إدارة صورة النظام تعتمد خطأ " 418 | 419 | #: ../usr/share/time-slider/lib/time_slider/setupgui.py:155 420 | #, python-format 421 | msgid "" 422 | "The snapshot manager service has been placed offline due to a dependency " 423 | "problem. The following dependency problems were found:\n" 424 | "\n" 425 | "%s\n" 426 | "\n" 427 | "Run \"svcs -xv\" from a command prompt for more information about these " 428 | "dependency problems." 429 | msgstr "" 430 | "خدمة إدارة صورة النظام تكون بحالة غير فعالة ويستحق الإعتمادية. اخطاء الإرتباطات التالية ممكن إيجادها:\n" 431 | "\n" 432 | "%s\n" 433 | "\n" 434 | "شغل الأمر التالي \"svcs -xv\" من خلال موجه الأوامر لمزيد من المعلومات حول أخطاء الإعتمادية." 435 | 436 | #: ../usr/share/time-slider/lib/time_slider/setupgui.py:169 437 | #: ../usr/share/time-slider/lib/time_slider/setupgui.py:186 438 | msgid "Snapshot manager service error" 439 | msgstr "خطأ خدمة إدارة صورة النظام " 440 | 441 | #: ../usr/share/time-slider/lib/time_slider/setupgui.py:170 442 | msgid "" 443 | "The snapshot manager service has encountered a problem and has been disabled " 444 | "until the problem is fixed.\n" 445 | "\n" 446 | "See the svcs(1) man page for more information." 447 | msgstr "" 448 | "خدمة إدارة صورة النظام تواجه المشاكل ولذلك هي غير فعالة حتى تحل المشكلة .\n " 449 | "\n " 450 | 451 | #: ../usr/share/time-slider/lib/time_slider/setupgui.py:187 452 | msgid "" 453 | "The snapshot manager service does not appear to be installed on this " 454 | "system.\n" 455 | "\n" 456 | "See the svcs(1) man page for more information." 457 | msgstr "" 458 | "خدمة إدارة صورة التظام لا تظهر على أنها منصبة ضمن النظام . \n " 459 | "إنظر الى svcs(1) man page for more information." 460 | 461 | #: ../usr/share/time-slider/lib/time_slider/setupgui.py:423 462 | msgid "" 463 | "The snapshot manager service requires administrative privileges to run. You " 464 | "have not been assigned the necessaryadministrative priviliges.\n" 465 | "\n" 466 | "Consult your system administrator " 467 | msgstr "" 468 | "خدمة إدارة صورة النظام تحتاج الى صلاحيات المدير لكي تعمل , وأنت لم تعرف على أساس الإمتيازات الضرورية كمدير . \n " 469 | "\n " 470 | 471 | #: ../usr/share/time-slider/lib/time_slider/snapnowui.py:116 472 | msgid "Invalid characters in snapshot name" 473 | msgstr "خطأ برموز إسم صورة النظام " 474 | 475 | #: ../usr/share/time-slider/lib/time_slider/snapnowui.py:117 476 | #: ../usr/share/time-slider/lib/time_slider/snapnowui.py:147 477 | #: ../usr/share/time-slider/lib/time_slider/snapnowui.py:180 478 | #: ../usr/share/time-slider/lib/time_slider/snapnowui.py:216 479 | msgid "Error" 480 | msgstr "خطأ " 481 | 482 | #: ../usr/share/time-slider/lib/time_slider/snapnowui.py:118 483 | msgid "" 484 | "Allowed characters for snapshot names are :\n" 485 | "[a-z][A-Z][0-9][-_.:\n" 486 | "All invalid characters will be removed\n" 487 | msgstr "" 488 | "الرموز المتاحة بإسم صورة النظام :\n" 489 | "[a-z][A-Z][0-9][-_.:\n" 490 | "وكل الرموز الغير متاحة قد أزيلت \n" 491 | 492 | #: ../usr/share/time-slider/lib/time_slider/snapnowui.py:146 493 | msgid "Error occured while creating the snapshot" 494 | msgstr "الخطأ حدث عند إنشاء صورة النظام " 495 | 496 | #: ../usr/share/time-slider/lib/time_slider/snapnowui.py:155 497 | msgid "Snapshot created successfully" 498 | msgstr "صورة النظام قد أنشأت بنجاح " 499 | 500 | #: ../usr/share/time-slider/lib/time_slider/snapnowui.py:156 501 | msgid "Success" 502 | msgstr "نجاح " 503 | 504 | #: ../usr/share/time-slider/lib/time_slider/snapnowui.py:157 505 | #, python-format 506 | msgid "" 507 | "A snapshot of zfs filesystem %(zfs_fs)s\n" 508 | "named %(valid_name)s\n" 509 | "has been created.\n" 510 | msgstr "" 511 | "الصورة ضمن نظام ملفات ZFS %(zfs_fs)s\n" 512 | "مسماه %(valid_name)s\n" 513 | "قد أنشأ .\n" 514 | 515 | #: ../usr/share/time-slider/lib/time_slider/snapnowui.py:181 516 | msgid "" 517 | "Snapshot Now requires 2 arguments :\n" 518 | "- The path of the directory to be snapshotted.\n" 519 | "- The zfs filesystem corresponding to this directory." 520 | msgstr "" 521 | "صورة النظام تحتاج طلبين : \n" 522 | "- مسار المجلد الخاص بصور النظام .\n" 523 | "- نظام ملفات zfs متطابق مع مع المجلد " 524 | 525 | #: ../usr/share/time-slider/lib/time_slider/snapnowui.py:217 526 | msgid "" 527 | "Snapshot Now requires administrative privileges to run. You have not been " 528 | "assigned the necessaryadministrative priviliges.\n" 529 | "\n" 530 | "Consult your system administrator " 531 | msgstr "" 532 | "صورة النظام الأن تحتاج الى صلاحيات مدير للعمل , وأنت لم تعرف على أساس الإمتيازات الضرورية كمدير . \n \n" 533 | "راجع مدير النظام " 534 | -------------------------------------------------------------------------------- /po/ru.po: -------------------------------------------------------------------------------- 1 | # Copyright (C) 2008 Sun Microsystems, Inc 2 | # This file is distributed under the same license as the time-slider package. 3 | # 4 | # Desktop Discuss 5 | # 6 | msgid "" 7 | msgstr "" 8 | "Project-Id-Version: time-slider HEAD\n" 9 | "Report-Msgid-Bugs-To: \n" 10 | "POT-Creation-Date: 2009-12-07 22:37+0900\n" 11 | "PO-Revision-Date: 2008-10-01 21:35+0900\n" 12 | "Last-Translator: desktop-discuss@opensolaris.org\n" 13 | "Language-Team: desktop-discuss@opensolaris.org\n" 14 | "MIME-Version: 1.0\n" 15 | "Content-Type: text/plain; charset=UTF-8\n" 16 | "Content-Transfer-Encoding: 8bit\n" 17 | 18 | #: ../data/time-slider.desktop.in.h:1 19 | msgid "Configure the system to take automatic snapshots of your data" 20 | msgstr "" 21 | 22 | #: ../data/time-slider.desktop.in.h:2 23 | #: ../usr/share/time-slider/lib/time_slider/applet.py:185 24 | msgid "Time Slider" 25 | msgstr "" 26 | 27 | #: ../usr/share/time-slider/glade/time-slider-delete.glade.h:2 28 | #: ../usr/share/time-slider/lib/time_slider/deletegui.py:242 29 | #: ../usr/share/time-slider/lib/time_slider/deletegui.py:342 30 | #, no-c-format, python-format 31 | msgid "%d snapshots will be deleted." 32 | msgstr "" 33 | 34 | #: ../usr/share/time-slider/glade/time-slider-delete.glade.h:3 35 | msgid "Summary" 36 | msgstr "" 37 | 38 | #: ../usr/share/time-slider/glade/time-slider-delete.glade.h:4 39 | msgid "D_eselect All" 40 | msgstr "" 41 | 42 | #: ../usr/share/time-slider/glade/time-slider-delete.glade.h:5 43 | msgid "Delete Snapshots" 44 | msgstr "" 45 | 46 | #: ../usr/share/time-slider/glade/time-slider-delete.glade.h:6 47 | msgid "Deleting Snapshots" 48 | msgstr "" 49 | 50 | #: ../usr/share/time-slider/glade/time-slider-delete.glade.h:7 51 | msgid "Deleting snapshots..." 52 | msgstr "" 53 | 54 | #: ../usr/share/time-slider/glade/time-slider-delete.glade.h:8 55 | msgid "Exit when done. " 56 | msgstr "" 57 | 58 | #: ../usr/share/time-slider/glade/time-slider-delete.glade.h:9 59 | msgid "File System:" 60 | msgstr "" 61 | 62 | #: ../usr/share/time-slider/glade/time-slider-delete.glade.h:10 63 | msgid "Name:" 64 | msgstr "" 65 | 66 | #: ../usr/share/time-slider/glade/time-slider-delete.glade.h:11 67 | msgid "Press Delete to continue." 68 | msgstr "" 69 | 70 | #: ../usr/share/time-slider/glade/time-slider-delete.glade.h:12 71 | msgid "Scanning snapshots..." 72 | msgstr "" 73 | 74 | #: ../usr/share/time-slider/glade/time-slider-delete.glade.h:13 75 | msgid "Select ZFS snapshots to delete from the list below." 76 | msgstr "" 77 | 78 | #: ../usr/share/time-slider/glade/time-slider-setup.glade.h:1 79 | msgid "Enable Time Slider" 80 | msgstr "" 81 | 82 | #: ../usr/share/time-slider/glade/time-slider-setup.glade.h:2 83 | msgid "File Systems To Back Up" 84 | msgstr "" 85 | 86 | #: ../usr/share/time-slider/glade/time-slider-setup.glade.h:3 87 | msgid "Advanced Options" 88 | msgstr "" 89 | 90 | #: ../usr/share/time-slider/glade/time-slider-setup.glade.h:4 91 | msgid "C_ustom:" 92 | msgstr "" 93 | 94 | #: ../usr/share/time-slider/glade/time-slider-setup.glade.h:5 95 | msgid "Configuring Time Slider..." 96 | msgstr "" 97 | 98 | #: ../usr/share/time-slider/glade/time-slider-setup.glade.h:6 99 | msgid "For advanced users" 100 | msgstr "" 101 | 102 | #: ../usr/share/time-slider/glade/time-slider-setup.glade.h:7 103 | msgid "Recommended for most users" 104 | msgstr "" 105 | 106 | #: ../usr/share/time-slider/glade/time-slider-setup.glade.h:8 107 | msgid "Reduce backups when storage space usage exceeds:" 108 | msgstr "" 109 | 110 | #: ../usr/share/time-slider/glade/time-slider-setup.glade.h:9 111 | msgid "Time Slider Manager" 112 | msgstr "" 113 | 114 | #: ../usr/share/time-slider/glade/time-slider-setup.glade.h:10 115 | msgid "Time Slider backs up data regularly by taking timed ZFS Snapshots" 116 | msgstr "" 117 | 118 | #: ../usr/share/time-slider/glade/time-slider-setup.glade.h:11 119 | msgid "_All" 120 | msgstr "" 121 | 122 | #: ../usr/share/time-slider/glade/time-slider-setup.glade.h:12 123 | msgid "_Delete Snapshots..." 124 | msgstr "" 125 | 126 | #: ../usr/share/time-slider/glade/time-slider-setup.glade.h:13 127 | msgid "of file system capacity" 128 | msgstr "" 129 | 130 | #: ../usr/share/time-slider/glade/time-slider-snapshot.glade.h:1 131 | msgid "Take a snapshot now" 132 | msgstr "" 133 | 134 | #: ../usr/share/time-slider/glade/time-slider-snapshot.glade.h:2 135 | msgid "Take a snapshot of " 136 | msgstr "" 137 | 138 | #: ../usr/share/time-slider/glade/time-slider-snapshot.glade.h:3 139 | msgid "with the name :" 140 | msgstr "" 141 | 142 | #: ../usr/share/time-slider/glade/time-slider-version.glade.h:1 143 | msgid "10 Mo" 144 | msgstr "" 145 | 146 | #: ../usr/share/time-slider/glade/time-slider-version.glade.h:2 147 | msgid "19/01/09" 148 | msgstr "" 149 | 150 | #: ../usr/share/time-slider/glade/time-slider-version.glade.h:3 151 | msgid "Current Version" 152 | msgstr "" 153 | 154 | #: ../usr/share/time-slider/glade/time-slider-version.glade.h:4 155 | msgid "Older Versions" 156 | msgstr "" 157 | 158 | #: ../usr/share/time-slider/glade/time-slider-version.glade.h:5 159 | msgid "Double click to open the file" 160 | msgstr "" 161 | 162 | #: ../usr/share/time-slider/glade/time-slider-version.glade.h:6 163 | msgid "Open the current version of the file" 164 | msgstr "" 165 | 166 | #: ../usr/share/time-slider/glade/time-slider-version.glade.h:7 167 | msgid "Scanning for older versions" 168 | msgstr "" 169 | 170 | #: ../usr/share/time-slider/glade/time-slider-version.glade.h:8 171 | msgid "Time Slider File Version Explorer" 172 | msgstr "" 173 | 174 | #: ../usr/share/time-slider/glade/time-slider-version.glade.h:9 175 | msgid "compare" 176 | msgstr "" 177 | 178 | #: ../usr/share/time-slider/glade/time-slider-version.glade.h:10 179 | msgid "filename" 180 | msgstr "" 181 | 182 | #: ../usr/share/time-slider/glade/time-slider-version.glade.h:11 183 | msgid "gtk-close" 184 | msgstr "" 185 | 186 | #: ../usr/share/time-slider/glade/time-slider-version.glade.h:12 187 | msgid "last modified : " 188 | msgstr "" 189 | 190 | #: ../usr/share/time-slider/glade/time-slider-version.glade.h:13 191 | msgid "name :" 192 | msgstr "" 193 | 194 | #: ../usr/share/time-slider/glade/time-slider-version.glade.h:14 195 | msgid "size : " 196 | msgstr "" 197 | 198 | #: ../usr/share/time-slider/lib/time_slider/applet.py:57 199 | msgid "Time Slider: Low Space Warning" 200 | msgstr "" 201 | 202 | #: ../usr/share/time-slider/lib/time_slider/applet.py:75 203 | #, python-format 204 | msgid "Emergency: '%s' is full!" 205 | msgstr "" 206 | 207 | #: ../usr/share/time-slider/lib/time_slider/applet.py:76 208 | #, python-format 209 | msgid "The file system: '%s', is over %s%% full." 210 | msgstr "" 211 | 212 | #: ../usr/share/time-slider/lib/time_slider/applet.py:78 213 | #, python-format 214 | msgid "" 215 | "The file system: '%s', is over %s%% full.\n" 216 | "As an emergency measure, Time Slider has destroyed all of its backups.\n" 217 | "To fix this problem, delete any unnecessary files on '%s', or add disk space " 218 | "(see ZFS documentation)." 219 | msgstr "" 220 | 221 | #: ../usr/share/time-slider/lib/time_slider/applet.py:87 222 | #, python-format 223 | msgid "Emergency: '%s' is almost full!" 224 | msgstr "" 225 | 226 | #: ../usr/share/time-slider/lib/time_slider/applet.py:88 227 | #: ../usr/share/time-slider/lib/time_slider/applet.py:103 228 | #: ../usr/share/time-slider/lib/time_slider/applet.py:118 229 | #, python-format 230 | msgid "The file system: '%s', exceeded %s%% of its total capacity" 231 | msgstr "" 232 | 233 | #: ../usr/share/time-slider/lib/time_slider/applet.py:91 234 | #, python-format 235 | msgid "" 236 | "The file system: '%s', exceeded %s%% of its total capacity. As an emerency " 237 | "measure, Time Slider has has destroyed most or all of its backups to prevent " 238 | "the disk becoming full. To prevent this from happening again, delete any " 239 | "unnecessary files on '%s', or add disk space (see ZFS documentation)." 240 | msgstr "" 241 | 242 | #: ../usr/share/time-slider/lib/time_slider/applet.py:102 243 | #, python-format 244 | msgid "Urgent: '%s' is almost full!" 245 | msgstr "" 246 | 247 | #: ../usr/share/time-slider/lib/time_slider/applet.py:106 248 | #, python-format 249 | msgid "" 250 | "The file system: '%s', exceeded %s%% of its total capacity. As a remedial " 251 | "measure, Time Slider has destroyed some backups, and will destroy more, " 252 | "eventually all, as capacity continues to diminish.\n" 253 | "To prevent this from happening again, delete any unnecessary files on '%s', " 254 | "or add disk space (see ZFS documentation)." 255 | msgstr "" 256 | 257 | #: ../usr/share/time-slider/lib/time_slider/applet.py:117 258 | #, python-format 259 | msgid "Warning: '%s' is getting full" 260 | msgstr "" 261 | 262 | #: ../usr/share/time-slider/lib/time_slider/applet.py:121 263 | #, python-format 264 | msgid "" 265 | "'%s' exceeded %s%% of its total capacity. To fix this, Time Slider has " 266 | "destroyed some recent backups, and will destroy more as capacity continues " 267 | "to diminish.\n" 268 | "To prevent this from happening again, delete any unnecessary files on '%s', " 269 | "or add disk space (see ZFS documentation).\n" 270 | msgstr "" 271 | 272 | #: ../usr/share/time-slider/lib/time_slider/applet.py:137 273 | msgid "Details..." 274 | msgstr "" 275 | 276 | #: ../usr/share/time-slider/lib/time_slider/deletegui.py:116 277 | #: ../usr/share/time-slider/lib/time_slider/deletegui.py:306 278 | #: ../usr/share/time-slider/lib/time_slider/setupgui.py:106 279 | msgid "Legacy" 280 | msgstr "" 281 | 282 | #: ../usr/share/time-slider/lib/time_slider/deletegui.py:147 283 | #: ../usr/share/time-slider/lib/time_slider/setupgui.py:131 284 | msgid "Mount Point" 285 | msgstr "" 286 | 287 | #: ../usr/share/time-slider/lib/time_slider/deletegui.py:155 288 | #: ../usr/share/time-slider/lib/time_slider/setupgui.py:134 289 | msgid "File System Name" 290 | msgstr "" 291 | 292 | #: ../usr/share/time-slider/lib/time_slider/deletegui.py:163 293 | msgid "Snapshot Name" 294 | msgstr "" 295 | 296 | #: ../usr/share/time-slider/lib/time_slider/deletegui.py:171 297 | msgid "Creation Time" 298 | msgstr "" 299 | 300 | #. Note to developers. 301 | #. The second element is for internal matching and should not 302 | #. be i18ned under any circumstances. 303 | #: ../usr/share/time-slider/lib/time_slider/deletegui.py:185 304 | #: ../usr/share/time-slider/lib/time_slider/deletegui.py:198 305 | msgid "All" 306 | msgstr "" 307 | 308 | #: ../usr/share/time-slider/lib/time_slider/deletegui.py:199 309 | msgid "Monthly" 310 | msgstr "" 311 | 312 | #: ../usr/share/time-slider/lib/time_slider/deletegui.py:200 313 | msgid "Weekly" 314 | msgstr "" 315 | 316 | #: ../usr/share/time-slider/lib/time_slider/deletegui.py:201 317 | msgid "Daily" 318 | msgstr "" 319 | 320 | #: ../usr/share/time-slider/lib/time_slider/deletegui.py:202 321 | msgid "Hourly" 322 | msgstr "" 323 | 324 | #: ../usr/share/time-slider/lib/time_slider/deletegui.py:203 325 | msgid "1/4 Hourly" 326 | msgstr "" 327 | 328 | #: ../usr/share/time-slider/lib/time_slider/deletegui.py:223 329 | msgid "Snapshot can not be deleted" 330 | msgstr "" 331 | 332 | #: ../usr/share/time-slider/lib/time_slider/deletegui.py:224 333 | #, python-format 334 | msgid "" 335 | "%s has one or more dependent clones and will not be deleted. To delete this " 336 | "snapshot, first delete all datasets and snapshots cloned from this snapshot." 337 | msgstr "" 338 | 339 | #: ../usr/share/time-slider/lib/time_slider/deletegui.py:240 340 | #: ../usr/share/time-slider/lib/time_slider/deletegui.py:340 341 | msgid "1 snapshot will be deleted." 342 | msgstr "" 343 | 344 | #: ../usr/share/time-slider/lib/time_slider/deletegui.py:381 345 | msgid "Some snapshots could not be read" 346 | msgstr "" 347 | 348 | #: ../usr/share/time-slider/lib/time_slider/deletegui.py:405 349 | msgid "Some snapshots could not be deleted" 350 | msgstr "" 351 | 352 | #: ../usr/share/time-slider/lib/time_slider/deletegui.py:539 353 | #: ../usr/share/time-slider/lib/time_slider/setupgui.py:422 354 | #: ../usr/share/time-slider/lib/time_slider/snapnowui.py:215 355 | msgid "Insufficient Priviliges" 356 | msgstr "" 357 | 358 | #: ../usr/share/time-slider/lib/time_slider/deletegui.py:540 359 | msgid "" 360 | "Snapshot deletion requires administrative privileges to run. You have not " 361 | "been assigned the necessaryadministrative priviliges.\n" 362 | "\n" 363 | "Consult your system administrator " 364 | msgstr "" 365 | 366 | #: ../usr/share/time-slider/lib/time_slider/fileversion.py:246 367 | #: ../usr/share/time-slider/lib/time_slider/fileversion.py:247 368 | msgid "Hint" 369 | msgstr "" 370 | 371 | #: ../usr/share/time-slider/lib/time_slider/fileversion.py:248 372 | msgid "" 373 | "Installing the optional meld package will enhance the file comparison " 374 | "visualization" 375 | msgstr "" 376 | 377 | #: ../usr/share/time-slider/lib/time_slider/fileversion.py:316 378 | #: ../usr/share/time-slider/lib/time_slider/snapnowui.py:179 379 | msgid "Invalid arguments count." 380 | msgstr "" 381 | 382 | #: ../usr/share/time-slider/lib/time_slider/fileversion.py:318 383 | msgid "" 384 | "Version explorer requires 2 arguments :\n" 385 | "- The path of the root snapshot directory.\n" 386 | "- The filename to explore." 387 | msgstr "" 388 | 389 | #: ../usr/share/time-slider/lib/time_slider/setupgui.py:128 390 | msgid "Select" 391 | msgstr "" 392 | 393 | #: ../usr/share/time-slider/lib/time_slider/setupgui.py:154 394 | msgid "Snapshot manager service dependency error" 395 | msgstr "" 396 | 397 | #: ../usr/share/time-slider/lib/time_slider/setupgui.py:155 398 | #, python-format 399 | msgid "" 400 | "The snapshot manager service has been placed offline due to a dependency " 401 | "problem. The following dependency problems were found:\n" 402 | "\n" 403 | "%s\n" 404 | "\n" 405 | "Run \"svcs -xv\" from a command prompt for more information about these " 406 | "dependency problems." 407 | msgstr "" 408 | 409 | #: ../usr/share/time-slider/lib/time_slider/setupgui.py:169 410 | #: ../usr/share/time-slider/lib/time_slider/setupgui.py:186 411 | msgid "Snapshot manager service error" 412 | msgstr "" 413 | 414 | #: ../usr/share/time-slider/lib/time_slider/setupgui.py:170 415 | msgid "" 416 | "The snapshot manager service has encountered a problem and has been disabled " 417 | "until the problem is fixed.\n" 418 | "\n" 419 | "See the svcs(1) man page for more information." 420 | msgstr "" 421 | 422 | #: ../usr/share/time-slider/lib/time_slider/setupgui.py:187 423 | msgid "" 424 | "The snapshot manager service does not appear to be installed on this " 425 | "system.\n" 426 | "\n" 427 | "See the svcs(1) man page for more information." 428 | msgstr "" 429 | 430 | #: ../usr/share/time-slider/lib/time_slider/setupgui.py:423 431 | msgid "" 432 | "The snapshot manager service requires administrative privileges to run. You " 433 | "have not been assigned the necessaryadministrative priviliges.\n" 434 | "\n" 435 | "Consult your system administrator " 436 | msgstr "" 437 | 438 | #: ../usr/share/time-slider/lib/time_slider/snapnowui.py:116 439 | msgid "Invalid characters in snapshot name" 440 | msgstr "" 441 | 442 | #: ../usr/share/time-slider/lib/time_slider/snapnowui.py:117 443 | #: ../usr/share/time-slider/lib/time_slider/snapnowui.py:147 444 | #: ../usr/share/time-slider/lib/time_slider/snapnowui.py:180 445 | #: ../usr/share/time-slider/lib/time_slider/snapnowui.py:216 446 | msgid "Error" 447 | msgstr "" 448 | 449 | #: ../usr/share/time-slider/lib/time_slider/snapnowui.py:118 450 | msgid "" 451 | "Allowed characters for snapshot names are :\n" 452 | "[a-z][A-Z][0-9][-_.:\n" 453 | "All invalid characters will be removed\n" 454 | msgstr "" 455 | 456 | #: ../usr/share/time-slider/lib/time_slider/snapnowui.py:146 457 | msgid "Error occured while creating the snapshot" 458 | msgstr "" 459 | 460 | #: ../usr/share/time-slider/lib/time_slider/snapnowui.py:155 461 | msgid "Snapshot created successfully" 462 | msgstr "" 463 | 464 | #: ../usr/share/time-slider/lib/time_slider/snapnowui.py:156 465 | msgid "Success" 466 | msgstr "" 467 | 468 | #: ../usr/share/time-slider/lib/time_slider/snapnowui.py:157 469 | #, python-format 470 | msgid "" 471 | "A snapshot of zfs filesystem %(zfs_fs)s\n" 472 | "named %(valid_name)s\n" 473 | "has been created.\n" 474 | msgstr "" 475 | 476 | #: ../usr/share/time-slider/lib/time_slider/snapnowui.py:181 477 | msgid "" 478 | "Snapshot Now requires 2 arguments :\n" 479 | "- The path of the directory to be snapshotted.\n" 480 | "- The zfs filesystem corresponding to this directory." 481 | msgstr "" 482 | 483 | #: ../usr/share/time-slider/lib/time_slider/snapnowui.py:217 484 | msgid "" 485 | "Snapshot Now requires administrative privileges to run. You have not been " 486 | "assigned the necessaryadministrative priviliges.\n" 487 | "\n" 488 | "Consult your system administrator " 489 | msgstr "" 490 | -------------------------------------------------------------------------------- /po/sv.po: -------------------------------------------------------------------------------- 1 | # Copyright (C) 2008 Sun Microsystems, Inc 2 | # This file is distributed under the same license as the time-slider package. 3 | # 4 | # Desktop Discuss 5 | # 6 | msgid "" 7 | msgstr "" 8 | "Project-Id-Version: time-slider HEAD\n" 9 | "Report-Msgid-Bugs-To: \n" 10 | "POT-Creation-Date: 2009-12-07 22:37+0900\n" 11 | "PO-Revision-Date: 2008-10-01 21:35+0900\n" 12 | "Last-Translator: desktop-discuss@opensolaris.org\n" 13 | "Language-Team: desktop-discuss@opensolaris.org\n" 14 | "MIME-Version: 1.0\n" 15 | "Content-Type: text/plain; charset=UTF-8\n" 16 | "Content-Transfer-Encoding: 8bit\n" 17 | 18 | #: ../data/time-slider.desktop.in.h:1 19 | msgid "Configure the system to take automatic snapshots of your data" 20 | msgstr "" 21 | 22 | #: ../data/time-slider.desktop.in.h:2 23 | #: ../usr/share/time-slider/lib/time_slider/applet.py:185 24 | msgid "Time Slider" 25 | msgstr "" 26 | 27 | #: ../usr/share/time-slider/glade/time-slider-delete.glade.h:2 28 | #: ../usr/share/time-slider/lib/time_slider/deletegui.py:242 29 | #: ../usr/share/time-slider/lib/time_slider/deletegui.py:342 30 | #, no-c-format, python-format 31 | msgid "%d snapshots will be deleted." 32 | msgstr "" 33 | 34 | #: ../usr/share/time-slider/glade/time-slider-delete.glade.h:3 35 | msgid "Summary" 36 | msgstr "" 37 | 38 | #: ../usr/share/time-slider/glade/time-slider-delete.glade.h:4 39 | msgid "D_eselect All" 40 | msgstr "" 41 | 42 | #: ../usr/share/time-slider/glade/time-slider-delete.glade.h:5 43 | msgid "Delete Snapshots" 44 | msgstr "" 45 | 46 | #: ../usr/share/time-slider/glade/time-slider-delete.glade.h:6 47 | msgid "Deleting Snapshots" 48 | msgstr "" 49 | 50 | #: ../usr/share/time-slider/glade/time-slider-delete.glade.h:7 51 | msgid "Deleting snapshots..." 52 | msgstr "" 53 | 54 | #: ../usr/share/time-slider/glade/time-slider-delete.glade.h:8 55 | msgid "Exit when done. " 56 | msgstr "" 57 | 58 | #: ../usr/share/time-slider/glade/time-slider-delete.glade.h:9 59 | msgid "File System:" 60 | msgstr "" 61 | 62 | #: ../usr/share/time-slider/glade/time-slider-delete.glade.h:10 63 | msgid "Name:" 64 | msgstr "" 65 | 66 | #: ../usr/share/time-slider/glade/time-slider-delete.glade.h:11 67 | msgid "Press Delete to continue." 68 | msgstr "" 69 | 70 | #: ../usr/share/time-slider/glade/time-slider-delete.glade.h:12 71 | msgid "Scanning snapshots..." 72 | msgstr "" 73 | 74 | #: ../usr/share/time-slider/glade/time-slider-delete.glade.h:13 75 | msgid "Select ZFS snapshots to delete from the list below." 76 | msgstr "" 77 | 78 | #: ../usr/share/time-slider/glade/time-slider-setup.glade.h:1 79 | msgid "Enable Time Slider" 80 | msgstr "" 81 | 82 | #: ../usr/share/time-slider/glade/time-slider-setup.glade.h:2 83 | msgid "File Systems To Back Up" 84 | msgstr "" 85 | 86 | #: ../usr/share/time-slider/glade/time-slider-setup.glade.h:3 87 | msgid "Advanced Options" 88 | msgstr "" 89 | 90 | #: ../usr/share/time-slider/glade/time-slider-setup.glade.h:4 91 | msgid "C_ustom:" 92 | msgstr "" 93 | 94 | #: ../usr/share/time-slider/glade/time-slider-setup.glade.h:5 95 | msgid "Configuring Time Slider..." 96 | msgstr "" 97 | 98 | #: ../usr/share/time-slider/glade/time-slider-setup.glade.h:6 99 | msgid "For advanced users" 100 | msgstr "" 101 | 102 | #: ../usr/share/time-slider/glade/time-slider-setup.glade.h:7 103 | msgid "Recommended for most users" 104 | msgstr "" 105 | 106 | #: ../usr/share/time-slider/glade/time-slider-setup.glade.h:8 107 | msgid "Reduce backups when storage space usage exceeds:" 108 | msgstr "" 109 | 110 | #: ../usr/share/time-slider/glade/time-slider-setup.glade.h:9 111 | msgid "Time Slider Manager" 112 | msgstr "" 113 | 114 | #: ../usr/share/time-slider/glade/time-slider-setup.glade.h:10 115 | msgid "Time Slider backs up data regularly by taking timed ZFS Snapshots" 116 | msgstr "" 117 | 118 | #: ../usr/share/time-slider/glade/time-slider-setup.glade.h:11 119 | msgid "_All" 120 | msgstr "" 121 | 122 | #: ../usr/share/time-slider/glade/time-slider-setup.glade.h:12 123 | msgid "_Delete Snapshots..." 124 | msgstr "" 125 | 126 | #: ../usr/share/time-slider/glade/time-slider-setup.glade.h:13 127 | msgid "of file system capacity" 128 | msgstr "" 129 | 130 | #: ../usr/share/time-slider/glade/time-slider-snapshot.glade.h:1 131 | msgid "Take a snapshot now" 132 | msgstr "" 133 | 134 | #: ../usr/share/time-slider/glade/time-slider-snapshot.glade.h:2 135 | msgid "Take a snapshot of " 136 | msgstr "" 137 | 138 | #: ../usr/share/time-slider/glade/time-slider-snapshot.glade.h:3 139 | msgid "with the name :" 140 | msgstr "" 141 | 142 | #: ../usr/share/time-slider/glade/time-slider-version.glade.h:1 143 | msgid "10 Mo" 144 | msgstr "" 145 | 146 | #: ../usr/share/time-slider/glade/time-slider-version.glade.h:2 147 | msgid "19/01/09" 148 | msgstr "" 149 | 150 | #: ../usr/share/time-slider/glade/time-slider-version.glade.h:3 151 | msgid "Current Version" 152 | msgstr "" 153 | 154 | #: ../usr/share/time-slider/glade/time-slider-version.glade.h:4 155 | msgid "Older Versions" 156 | msgstr "" 157 | 158 | #: ../usr/share/time-slider/glade/time-slider-version.glade.h:5 159 | msgid "Double click to open the file" 160 | msgstr "" 161 | 162 | #: ../usr/share/time-slider/glade/time-slider-version.glade.h:6 163 | msgid "Open the current version of the file" 164 | msgstr "" 165 | 166 | #: ../usr/share/time-slider/glade/time-slider-version.glade.h:7 167 | msgid "Scanning for older versions" 168 | msgstr "" 169 | 170 | #: ../usr/share/time-slider/glade/time-slider-version.glade.h:8 171 | msgid "Time Slider File Version Explorer" 172 | msgstr "" 173 | 174 | #: ../usr/share/time-slider/glade/time-slider-version.glade.h:9 175 | msgid "compare" 176 | msgstr "" 177 | 178 | #: ../usr/share/time-slider/glade/time-slider-version.glade.h:10 179 | msgid "filename" 180 | msgstr "" 181 | 182 | #: ../usr/share/time-slider/glade/time-slider-version.glade.h:11 183 | msgid "gtk-close" 184 | msgstr "" 185 | 186 | #: ../usr/share/time-slider/glade/time-slider-version.glade.h:12 187 | msgid "last modified : " 188 | msgstr "" 189 | 190 | #: ../usr/share/time-slider/glade/time-slider-version.glade.h:13 191 | msgid "name :" 192 | msgstr "" 193 | 194 | #: ../usr/share/time-slider/glade/time-slider-version.glade.h:14 195 | msgid "size : " 196 | msgstr "" 197 | 198 | #: ../usr/share/time-slider/lib/time_slider/applet.py:57 199 | msgid "Time Slider: Low Space Warning" 200 | msgstr "" 201 | 202 | #: ../usr/share/time-slider/lib/time_slider/applet.py:75 203 | #, python-format 204 | msgid "Emergency: '%s' is full!" 205 | msgstr "" 206 | 207 | #: ../usr/share/time-slider/lib/time_slider/applet.py:76 208 | #, python-format 209 | msgid "The file system: '%s', is over %s%% full." 210 | msgstr "" 211 | 212 | #: ../usr/share/time-slider/lib/time_slider/applet.py:78 213 | #, python-format 214 | msgid "" 215 | "The file system: '%s', is over %s%% full.\n" 216 | "As an emergency measure, Time Slider has destroyed all of its backups.\n" 217 | "To fix this problem, delete any unnecessary files on '%s', or add disk space " 218 | "(see ZFS documentation)." 219 | msgstr "" 220 | 221 | #: ../usr/share/time-slider/lib/time_slider/applet.py:87 222 | #, python-format 223 | msgid "Emergency: '%s' is almost full!" 224 | msgstr "" 225 | 226 | #: ../usr/share/time-slider/lib/time_slider/applet.py:88 227 | #: ../usr/share/time-slider/lib/time_slider/applet.py:103 228 | #: ../usr/share/time-slider/lib/time_slider/applet.py:118 229 | #, python-format 230 | msgid "The file system: '%s', exceeded %s%% of its total capacity" 231 | msgstr "" 232 | 233 | #: ../usr/share/time-slider/lib/time_slider/applet.py:91 234 | #, python-format 235 | msgid "" 236 | "The file system: '%s', exceeded %s%% of its total capacity. As an emerency " 237 | "measure, Time Slider has has destroyed most or all of its backups to prevent " 238 | "the disk becoming full. To prevent this from happening again, delete any " 239 | "unnecessary files on '%s', or add disk space (see ZFS documentation)." 240 | msgstr "" 241 | 242 | #: ../usr/share/time-slider/lib/time_slider/applet.py:102 243 | #, python-format 244 | msgid "Urgent: '%s' is almost full!" 245 | msgstr "" 246 | 247 | #: ../usr/share/time-slider/lib/time_slider/applet.py:106 248 | #, python-format 249 | msgid "" 250 | "The file system: '%s', exceeded %s%% of its total capacity. As a remedial " 251 | "measure, Time Slider has destroyed some backups, and will destroy more, " 252 | "eventually all, as capacity continues to diminish.\n" 253 | "To prevent this from happening again, delete any unnecessary files on '%s', " 254 | "or add disk space (see ZFS documentation)." 255 | msgstr "" 256 | 257 | #: ../usr/share/time-slider/lib/time_slider/applet.py:117 258 | #, python-format 259 | msgid "Warning: '%s' is getting full" 260 | msgstr "" 261 | 262 | #: ../usr/share/time-slider/lib/time_slider/applet.py:121 263 | #, python-format 264 | msgid "" 265 | "'%s' exceeded %s%% of its total capacity. To fix this, Time Slider has " 266 | "destroyed some recent backups, and will destroy more as capacity continues " 267 | "to diminish.\n" 268 | "To prevent this from happening again, delete any unnecessary files on '%s', " 269 | "or add disk space (see ZFS documentation).\n" 270 | msgstr "" 271 | 272 | #: ../usr/share/time-slider/lib/time_slider/applet.py:137 273 | msgid "Details..." 274 | msgstr "" 275 | 276 | #: ../usr/share/time-slider/lib/time_slider/deletegui.py:116 277 | #: ../usr/share/time-slider/lib/time_slider/deletegui.py:306 278 | #: ../usr/share/time-slider/lib/time_slider/setupgui.py:106 279 | msgid "Legacy" 280 | msgstr "" 281 | 282 | #: ../usr/share/time-slider/lib/time_slider/deletegui.py:147 283 | #: ../usr/share/time-slider/lib/time_slider/setupgui.py:131 284 | msgid "Mount Point" 285 | msgstr "" 286 | 287 | #: ../usr/share/time-slider/lib/time_slider/deletegui.py:155 288 | #: ../usr/share/time-slider/lib/time_slider/setupgui.py:134 289 | msgid "File System Name" 290 | msgstr "" 291 | 292 | #: ../usr/share/time-slider/lib/time_slider/deletegui.py:163 293 | msgid "Snapshot Name" 294 | msgstr "" 295 | 296 | #: ../usr/share/time-slider/lib/time_slider/deletegui.py:171 297 | msgid "Creation Time" 298 | msgstr "" 299 | 300 | #. Note to developers. 301 | #. The second element is for internal matching and should not 302 | #. be i18ned under any circumstances. 303 | #: ../usr/share/time-slider/lib/time_slider/deletegui.py:185 304 | #: ../usr/share/time-slider/lib/time_slider/deletegui.py:198 305 | msgid "All" 306 | msgstr "" 307 | 308 | #: ../usr/share/time-slider/lib/time_slider/deletegui.py:199 309 | msgid "Monthly" 310 | msgstr "" 311 | 312 | #: ../usr/share/time-slider/lib/time_slider/deletegui.py:200 313 | msgid "Weekly" 314 | msgstr "" 315 | 316 | #: ../usr/share/time-slider/lib/time_slider/deletegui.py:201 317 | msgid "Daily" 318 | msgstr "" 319 | 320 | #: ../usr/share/time-slider/lib/time_slider/deletegui.py:202 321 | msgid "Hourly" 322 | msgstr "" 323 | 324 | #: ../usr/share/time-slider/lib/time_slider/deletegui.py:203 325 | msgid "1/4 Hourly" 326 | msgstr "" 327 | 328 | #: ../usr/share/time-slider/lib/time_slider/deletegui.py:223 329 | msgid "Snapshot can not be deleted" 330 | msgstr "" 331 | 332 | #: ../usr/share/time-slider/lib/time_slider/deletegui.py:224 333 | #, python-format 334 | msgid "" 335 | "%s has one or more dependent clones and will not be deleted. To delete this " 336 | "snapshot, first delete all datasets and snapshots cloned from this snapshot." 337 | msgstr "" 338 | 339 | #: ../usr/share/time-slider/lib/time_slider/deletegui.py:240 340 | #: ../usr/share/time-slider/lib/time_slider/deletegui.py:340 341 | msgid "1 snapshot will be deleted." 342 | msgstr "" 343 | 344 | #: ../usr/share/time-slider/lib/time_slider/deletegui.py:381 345 | msgid "Some snapshots could not be read" 346 | msgstr "" 347 | 348 | #: ../usr/share/time-slider/lib/time_slider/deletegui.py:405 349 | msgid "Some snapshots could not be deleted" 350 | msgstr "" 351 | 352 | #: ../usr/share/time-slider/lib/time_slider/deletegui.py:539 353 | #: ../usr/share/time-slider/lib/time_slider/setupgui.py:422 354 | #: ../usr/share/time-slider/lib/time_slider/snapnowui.py:215 355 | msgid "Insufficient Priviliges" 356 | msgstr "" 357 | 358 | #: ../usr/share/time-slider/lib/time_slider/deletegui.py:540 359 | msgid "" 360 | "Snapshot deletion requires administrative privileges to run. You have not " 361 | "been assigned the necessaryadministrative priviliges.\n" 362 | "\n" 363 | "Consult your system administrator " 364 | msgstr "" 365 | 366 | #: ../usr/share/time-slider/lib/time_slider/fileversion.py:246 367 | #: ../usr/share/time-slider/lib/time_slider/fileversion.py:247 368 | msgid "Hint" 369 | msgstr "" 370 | 371 | #: ../usr/share/time-slider/lib/time_slider/fileversion.py:248 372 | msgid "" 373 | "Installing the optional meld package will enhance the file comparison " 374 | "visualization" 375 | msgstr "" 376 | 377 | #: ../usr/share/time-slider/lib/time_slider/fileversion.py:316 378 | #: ../usr/share/time-slider/lib/time_slider/snapnowui.py:179 379 | msgid "Invalid arguments count." 380 | msgstr "" 381 | 382 | #: ../usr/share/time-slider/lib/time_slider/fileversion.py:318 383 | msgid "" 384 | "Version explorer requires 2 arguments :\n" 385 | "- The path of the root snapshot directory.\n" 386 | "- The filename to explore." 387 | msgstr "" 388 | 389 | #: ../usr/share/time-slider/lib/time_slider/setupgui.py:128 390 | msgid "Select" 391 | msgstr "" 392 | 393 | #: ../usr/share/time-slider/lib/time_slider/setupgui.py:154 394 | msgid "Snapshot manager service dependency error" 395 | msgstr "" 396 | 397 | #: ../usr/share/time-slider/lib/time_slider/setupgui.py:155 398 | #, python-format 399 | msgid "" 400 | "The snapshot manager service has been placed offline due to a dependency " 401 | "problem. The following dependency problems were found:\n" 402 | "\n" 403 | "%s\n" 404 | "\n" 405 | "Run \"svcs -xv\" from a command prompt for more information about these " 406 | "dependency problems." 407 | msgstr "" 408 | 409 | #: ../usr/share/time-slider/lib/time_slider/setupgui.py:169 410 | #: ../usr/share/time-slider/lib/time_slider/setupgui.py:186 411 | msgid "Snapshot manager service error" 412 | msgstr "" 413 | 414 | #: ../usr/share/time-slider/lib/time_slider/setupgui.py:170 415 | msgid "" 416 | "The snapshot manager service has encountered a problem and has been disabled " 417 | "until the problem is fixed.\n" 418 | "\n" 419 | "See the svcs(1) man page for more information." 420 | msgstr "" 421 | 422 | #: ../usr/share/time-slider/lib/time_slider/setupgui.py:187 423 | msgid "" 424 | "The snapshot manager service does not appear to be installed on this " 425 | "system.\n" 426 | "\n" 427 | "See the svcs(1) man page for more information." 428 | msgstr "" 429 | 430 | #: ../usr/share/time-slider/lib/time_slider/setupgui.py:423 431 | msgid "" 432 | "The snapshot manager service requires administrative privileges to run. You " 433 | "have not been assigned the necessaryadministrative priviliges.\n" 434 | "\n" 435 | "Consult your system administrator " 436 | msgstr "" 437 | 438 | #: ../usr/share/time-slider/lib/time_slider/snapnowui.py:116 439 | msgid "Invalid characters in snapshot name" 440 | msgstr "" 441 | 442 | #: ../usr/share/time-slider/lib/time_slider/snapnowui.py:117 443 | #: ../usr/share/time-slider/lib/time_slider/snapnowui.py:147 444 | #: ../usr/share/time-slider/lib/time_slider/snapnowui.py:180 445 | #: ../usr/share/time-slider/lib/time_slider/snapnowui.py:216 446 | msgid "Error" 447 | msgstr "" 448 | 449 | #: ../usr/share/time-slider/lib/time_slider/snapnowui.py:118 450 | msgid "" 451 | "Allowed characters for snapshot names are :\n" 452 | "[a-z][A-Z][0-9][-_.:\n" 453 | "All invalid characters will be removed\n" 454 | msgstr "" 455 | 456 | #: ../usr/share/time-slider/lib/time_slider/snapnowui.py:146 457 | msgid "Error occured while creating the snapshot" 458 | msgstr "" 459 | 460 | #: ../usr/share/time-slider/lib/time_slider/snapnowui.py:155 461 | msgid "Snapshot created successfully" 462 | msgstr "" 463 | 464 | #: ../usr/share/time-slider/lib/time_slider/snapnowui.py:156 465 | msgid "Success" 466 | msgstr "" 467 | 468 | #: ../usr/share/time-slider/lib/time_slider/snapnowui.py:157 469 | #, python-format 470 | msgid "" 471 | "A snapshot of zfs filesystem %(zfs_fs)s\n" 472 | "named %(valid_name)s\n" 473 | "has been created.\n" 474 | msgstr "" 475 | 476 | #: ../usr/share/time-slider/lib/time_slider/snapnowui.py:181 477 | msgid "" 478 | "Snapshot Now requires 2 arguments :\n" 479 | "- The path of the directory to be snapshotted.\n" 480 | "- The zfs filesystem corresponding to this directory." 481 | msgstr "" 482 | 483 | #: ../usr/share/time-slider/lib/time_slider/snapnowui.py:217 484 | msgid "" 485 | "Snapshot Now requires administrative privileges to run. You have not been " 486 | "assigned the necessaryadministrative priviliges.\n" 487 | "\n" 488 | "Consult your system administrator " 489 | msgstr "" 490 | -------------------------------------------------------------------------------- /py-compile.py: -------------------------------------------------------------------------------- 1 | import compileall 2 | import os 3 | 4 | destdir = os.getenv("DESTDIR", ".") 5 | dir = destdir + "/usr" 6 | compileall.compile_dir(destdir, force=1) 7 | 8 | -------------------------------------------------------------------------------- /usr/bin/time-slider-setup: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python3.9 2 | 3 | import sys 4 | from os.path import dirname, join, pardir, abspath 5 | sys.path.insert(0, join(dirname(__file__), pardir, 'share', 6 | 'time-slider', 'lib')) 7 | 8 | from time_slider.setupgui import main 9 | 10 | # Pass through the executable path in case it needs to 11 | # be invoked via gksu (user has insufficient priviliges) 12 | main(abspath(__file__)) 13 | 14 | -------------------------------------------------------------------------------- /usr/lib/time-slider-delete: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python3.9 2 | 3 | import sys 4 | from os.path import dirname, join, pardir, abspath 5 | sys.path.insert(0, join(dirname(__file__), pardir, 'share', 6 | 'time-slider', 'lib')) 7 | 8 | from time_slider.deletegui import main 9 | 10 | # Pass through the executable path in case it needs to 11 | # be invoked via gksu (user has insufficient priviliges) 12 | main(abspath(__file__)) 13 | 14 | -------------------------------------------------------------------------------- /usr/lib/time-slider-notify: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python3.9 2 | 3 | import sys 4 | from os.path import dirname, join, pardir, abspath 5 | sys.path.insert(0, join(dirname(__file__), pardir, 'share', 6 | 'time-slider', 'lib')) 7 | 8 | from time_slider.applet import main 9 | 10 | # Pass through the executable path in case it needs to 11 | # be invoked via gksu (user has insufficient priviliges) 12 | main(abspath(__file__)) 13 | -------------------------------------------------------------------------------- /usr/lib/time-slider-snapshot: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python3.9 2 | 3 | import sys 4 | from os.path import dirname, join, pardir, abspath 5 | sys.path.insert(0, join(dirname(__file__), pardir, 'share', 6 | 'time-slider', 'lib')) 7 | 8 | from time_slider.snapnowui import main 9 | 10 | # Pass through the executable path in case it needs to 11 | # be invoked via gksu (user has insufficient priviliges) 12 | main(abspath(__file__)) 13 | 14 | -------------------------------------------------------------------------------- /usr/lib/time-slider-version: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python3.9 2 | 3 | import sys 4 | from os.path import dirname, join, pardir, abspath 5 | sys.path.insert(0, join(dirname(__file__), pardir, 'share', 6 | 'time-slider', 'lib')) 7 | 8 | from time_slider.fileversion import main 9 | 10 | main(abspath(__file__)) 11 | 12 | -------------------------------------------------------------------------------- /usr/lib/time-slider/plugins/rsync/rsync-backup: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python3.9 2 | import sys 3 | from os.path import dirname, join, pardir, abspath 4 | sys.path.insert(0, join(dirname(__file__), pardir, pardir, pardir, pardir, 5 | 'share', 'time-slider', 'lib', 'plugin')) 6 | 7 | from rsync.backup import main 8 | # Pass through the executable path in case it needs to 9 | # be invoked via gksu (user has insufficient priviliges) 10 | main(abspath(__file__)) 11 | -------------------------------------------------------------------------------- /usr/lib/time-slider/plugins/rsync/rsync-trigger: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python3.9 2 | import sys 3 | from os.path import dirname, join, pardir, abspath 4 | sys.path.insert(0, join(dirname(__file__), pardir, pardir, pardir, pardir, 5 | 'share', 'time-slider', 'lib', 'plugin')) 6 | 7 | from rsync.trigger import main 8 | # Pass through the executable path in case it needs to 9 | # be invoked via gksu (user has insufficient priviliges) 10 | main(abspath(__file__)) 11 | -------------------------------------------------------------------------------- /usr/lib/time-slider/plugins/zfssend/zfssend: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python3.9 2 | import sys 3 | from os.path import dirname, join, pardir, abspath 4 | sys.path.insert(0, join(dirname(__file__), pardir, pardir, pardir, pardir, 5 | 'share', 'time-slider', 'lib', 'plugin')) 6 | 7 | from zfssend.zfssend import main 8 | # Pass through the executable path in case it needs to 9 | # be invoked via gksu (user has insufficient priviliges) 10 | main(abspath(__file__)) 11 | -------------------------------------------------------------------------------- /usr/lib/time-sliderd: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python3.9 2 | import sys 3 | from os.path import dirname, join, pardir, abspath 4 | sys.path.insert(0, join(dirname(__file__), pardir, 'share', 5 | 'time-slider', 'lib')) 6 | 7 | from time_slider.timesliderd import main 8 | # Pass through the executable path in case it needs to 9 | # be invoked via gksu (user has insufficient priviliges) 10 | main(abspath(__file__)) 11 | 12 | -------------------------------------------------------------------------------- /usr/share/applications/time-slider.desktop: -------------------------------------------------------------------------------- 1 | [Desktop Entry] 2 | Encoding=UTF-8 3 | Name=Time Slider 4 | Comment=Configure the system to take automatic snapshots of your data 5 | Terminal=false 6 | Type=Application 7 | StartupNotify=true 8 | Exec=time-slider-setup 9 | Icon=time-slider-setup 10 | Categories=Application;System;Settings 11 | -------------------------------------------------------------------------------- /usr/share/icons/hicolor/16x16/apps/time-slider-setup.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OpenIndiana/time-slider/4f7d198e64af4058f40bfb2190a5a28e07a7ba8a/usr/share/icons/hicolor/16x16/apps/time-slider-setup.png -------------------------------------------------------------------------------- /usr/share/icons/hicolor/24x24/apps/time-slider-setup.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OpenIndiana/time-slider/4f7d198e64af4058f40bfb2190a5a28e07a7ba8a/usr/share/icons/hicolor/24x24/apps/time-slider-setup.png -------------------------------------------------------------------------------- /usr/share/icons/hicolor/32x32/apps/time-slider-setup.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OpenIndiana/time-slider/4f7d198e64af4058f40bfb2190a5a28e07a7ba8a/usr/share/icons/hicolor/32x32/apps/time-slider-setup.png -------------------------------------------------------------------------------- /usr/share/icons/hicolor/36x36/apps/time-slider-setup.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OpenIndiana/time-slider/4f7d198e64af4058f40bfb2190a5a28e07a7ba8a/usr/share/icons/hicolor/36x36/apps/time-slider-setup.png -------------------------------------------------------------------------------- /usr/share/icons/hicolor/48x48/apps/time-slider-setup.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OpenIndiana/time-slider/4f7d198e64af4058f40bfb2190a5a28e07a7ba8a/usr/share/icons/hicolor/48x48/apps/time-slider-setup.png -------------------------------------------------------------------------------- /usr/share/icons/hicolor/72x72/apps/time-slider-setup.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OpenIndiana/time-slider/4f7d198e64af4058f40bfb2190a5a28e07a7ba8a/usr/share/icons/hicolor/72x72/apps/time-slider-setup.png -------------------------------------------------------------------------------- /usr/share/icons/hicolor/96x96/apps/time-slider-setup.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OpenIndiana/time-slider/4f7d198e64af4058f40bfb2190a5a28e07a7ba8a/usr/share/icons/hicolor/96x96/apps/time-slider-setup.png -------------------------------------------------------------------------------- /usr/share/time-slider/lib/plugin/__init__.py: -------------------------------------------------------------------------------- 1 | # 2 | # CDDL HEADER START 3 | # 4 | # The contents of this file are subject to the terms of the 5 | # Common Development and Distribution License (the "License"). 6 | # You may not use this file except in compliance with the License. 7 | # 8 | # You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 9 | # or http://www.opensolaris.org/os/licensing. 10 | # See the License for the specific language governing permissions 11 | # and limitations under the License. 12 | # 13 | # When distributing Covered Code, include this CDDL HEADER in each 14 | # file and include the License file at usr/src/OPENSOLARIS.LICENSE. 15 | # If applicable, add the following below this CDDL HEADER, with the 16 | # fields enclosed by brackets "[]" replaced with your own identifying 17 | # information: Portions Copyright [yyyy] [name of copyright owner] 18 | # 19 | # CDDL HEADER END 20 | # 21 | 22 | import sys 23 | from os.path import abspath, dirname, join, pardir 24 | sys.path.insert(0, join(dirname(__file__), pardir)) 25 | 26 | -------------------------------------------------------------------------------- /usr/share/time-slider/lib/plugin/plugin.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python3.5 2 | # 3 | # CDDL HEADER START 4 | # 5 | # The contents of this file are subject to the terms of the 6 | # Common Development and Distribution License (the "License"). 7 | # You may not use this file except in compliance with the License. 8 | # 9 | # You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 10 | # or http://www.opensolaris.org/os/licensing. 11 | # See the License for the specific language governing permissions 12 | # and limitations under the License. 13 | # 14 | # When distributing Covered Code, include this CDDL HEADER in each 15 | # file and include the License file at usr/src/OPENSOLARIS.LICENSE. 16 | # If applicable, add the following below this CDDL HEADER, with the 17 | # fields enclosed by brackets "[]" replaced with your own identifying 18 | # information: Portions Copyright [yyyy] [name of copyright owner] 19 | # 20 | # CDDL HEADER END 21 | # 22 | 23 | import os 24 | import sys 25 | import subprocess 26 | import pluginsmf 27 | 28 | from time_slider import smf, autosnapsmf, util 29 | 30 | PLUGINBASEFMRI = "svc:/application/time-slider/plugin" 31 | 32 | 33 | class Plugin(Exception): 34 | 35 | def __init__(self, instanceName, debug=False): 36 | self.verbose = debug 37 | util.debug("Instantiating plugin for:\t%s" % (instanceName), self.verbose) 38 | self.smfInst = pluginsmf.PluginSMF(instanceName) 39 | self._proc = None 40 | 41 | # Note that the associated plugin service's start method checks 42 | # that the command is defined and executable. But SMF doesn't 43 | # bother to do this for offline services until all dependencies 44 | # (ie. time-slider) are brought online. 45 | # So we also check the permissions here. 46 | command = self.smfInst.get_trigger_command() 47 | try: 48 | statinfo = os.stat(command) 49 | other_x = (statinfo.st_mode & 0o1) 50 | if other_x == 0: 51 | raise RuntimeError('Plugin: %s:\nConfigured trigger command is not ' \ 52 | 'executable:\n%s' \ 53 | % (self.smfInst.instanceName, command)) 54 | except OSError: 55 | raise RuntimeError('Plugin: %s:\nCan not access the configured ' \ 56 | 'plugin/trigger_command:\n%s' \ 57 | % (self.smfInst.instanceName, command)) 58 | 59 | 60 | def execute(self, schedule, label): 61 | 62 | triggers = self.smfInst.get_trigger_list() 63 | try: 64 | triggers.index("all") 65 | except ValueError: 66 | try: 67 | triggers.index(schedule) 68 | except ValueError: 69 | return 70 | 71 | # Skip if already running 72 | if self.is_running() == True: 73 | util.debug("Plugin: %s is already running. Skipping execution" \ 74 | % (self.smfInst.instanceName), \ 75 | self.verbose) 76 | return 77 | # Skip if plugin FMRI has been disabled or placed into maintenance 78 | cmd = [smf.SVCSCMD, "-H", "-o", "state", self.smfInst.instanceName] 79 | outdata,errdata = util.run_command(cmd) 80 | state = outdata.strip() 81 | if state == "disabled" or state == "maintenance": 82 | util.debug("Plugin: %s is in %s state. Skipping execution" \ 83 | % (self.smfInst.instanceName, state), \ 84 | self.verbose) 85 | return 86 | 87 | cmd = self.smfInst.get_trigger_command() 88 | util.debug("Executing plugin command: %s" % str(cmd), self.verbose) 89 | svcFmri = "%s:%s" % (autosnapsmf.BASESVC, schedule) 90 | 91 | os.putenv("AUTOSNAP_FMRI", svcFmri) 92 | os.putenv("AUTOSNAP_LABEL", label) 93 | try: 94 | os.putenv("PLUGIN_FMRI", self.smfInst.instanceName) 95 | self._proc = subprocess.Popen(cmd, 96 | stdout=subprocess.PIPE, 97 | stderr=subprocess.PIPE, 98 | close_fds=True, 99 | universal_newlines=True) 100 | except OSError as message: 101 | raise RuntimeError("%s subprocess error:\n %s" % \ 102 | (cmd, str(message))) 103 | self._proc = None 104 | 105 | def is_running(self): 106 | if self._proc == None: 107 | util.debug("Plugin child process is not started", self.verbose) 108 | return False 109 | else: 110 | self._proc.poll() 111 | if self._proc.returncode == None: 112 | util.debug("Plugin child process is still running", 113 | self.verbose) 114 | return True 115 | else: 116 | util.debug("Plugin child process has ended", self.verbose) 117 | return False 118 | 119 | 120 | class PluginManager(): 121 | 122 | def __init__(self, debug=False): 123 | self.plugins = [] 124 | self.verbose = debug 125 | 126 | def execute_plugins(self, schedule, label): 127 | util.debug("Executing plugins for \"%s\" with label: \"%s\"" \ 128 | % (schedule, label), \ 129 | self.verbose) 130 | for plugin in self.plugins: 131 | plugin.execute(schedule, label) 132 | 133 | 134 | def refresh(self): 135 | self.plugins = [] 136 | cmd = [smf.SVCSCMD, "-H", "-o", "state,FMRI", PLUGINBASEFMRI] 137 | 138 | p = subprocess.Popen(cmd, 139 | stdout=subprocess.PIPE, 140 | stderr=subprocess.PIPE, 141 | close_fds=True, 142 | universal_newlines=True) 143 | outdata,errdata = p.communicate() 144 | err = p.wait() 145 | if err != 0: 146 | self._refreshLock.release() 147 | raise RuntimeError('%s failed with exit code %d\n%s' % \ 148 | (str(cmd), err, errdata)) 149 | for line in outdata.rstrip().split('\n'): 150 | line = line.rstrip().split() 151 | state = line[0] 152 | fmri = line[1] 153 | 154 | # Note that the plugins, being dependent on the time-slider service 155 | # themselves will typically be in an offline state when enabled. They will 156 | # transition to an "online" state once time-slider itself comes 157 | # "online" to satisfy it's dependency 158 | if state == "online" or state == "offline" or state == "degraded": 159 | util.debug("Found enabled plugin:\t%s" % (fmri), self.verbose) 160 | try: 161 | plugin = Plugin(fmri, self.verbose) 162 | self.plugins.append(plugin) 163 | except RuntimeError as message: 164 | sys.stderr.write("Ignoring misconfigured plugin: %s\n" \ 165 | % (fmri)) 166 | sys.stderr.write("Reason:\n%s\n" % (message)) 167 | else: 168 | util.debug("Found disabled plugin:\t%s" % (fmri), self.verbose) 169 | 170 | -------------------------------------------------------------------------------- /usr/share/time-slider/lib/plugin/pluginsmf.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python3.5 2 | # 3 | # CDDL HEADER START 4 | # 5 | # The contents of this file are subject to the terms of the 6 | # Common Development and Distribution License (the "License"). 7 | # You may not use this file except in compliance with the License. 8 | # 9 | # You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 10 | # or http://www.opensolaris.org/os/licensing. 11 | # See the License for the specific language governing permissions 12 | # and limitations under the License. 13 | # 14 | # When distributing Covered Code, include this CDDL HEADER in each 15 | # file and include the License file at usr/src/OPENSOLARIS.LICENSE. 16 | # If applicable, add the following below this CDDL HEADER, with the 17 | # fields enclosed by brackets "[]" replaced with your own identifying 18 | # information: Portions Copyright [yyyy] [name of copyright owner] 19 | # 20 | # CDDL HEADER END 21 | # 22 | 23 | import os 24 | import sys 25 | import subprocess 26 | from os.path import abspath, dirname, join, pardir 27 | sys.path.insert(0, join(dirname(__file__), pardir)) 28 | from time_slider import smf, autosnapsmf, util 29 | 30 | 31 | PLUGINBASEFMRI = "svc:/application/time-slider/plugin" 32 | PLUGINPROPGROUP = "plugin" 33 | 34 | class PluginSMF(smf.SMFInstance): 35 | 36 | def __init__(self, instanceName): 37 | smf.SMFInstance.__init__(self, instanceName) 38 | self.triggerCommand = None 39 | self.triggers = None 40 | 41 | def get_trigger_command(self): 42 | # FIXME Use mutex locking for MT safety 43 | if self.triggerCommand == None: 44 | value = self.get_prop(PLUGINPROPGROUP, "trigger_command") 45 | self.triggerCommand = value.strip() 46 | return self.triggerCommand 47 | 48 | def get_trigger_list(self): 49 | #FIXME Use mutex locking to make MT-safe 50 | if self.triggers == None: 51 | self.triggers = [] 52 | value = self.get_prop(PLUGINPROPGROUP, "trigger_on") 53 | 54 | # Strip out '\' characters inserted by svcprop 55 | triggerList = value.strip().replace('\\', '').split(',') 56 | for trigger in triggerList: 57 | self.triggers.append(trigger.strip()) 58 | return self.triggers 59 | 60 | def get_verbose(self): 61 | value = self.get_prop(PLUGINPROPGROUP, "verbose") 62 | if value == "true": 63 | return True 64 | else: 65 | return False 66 | 67 | -------------------------------------------------------------------------------- /usr/share/time-slider/lib/plugin/rsync/__init__.py: -------------------------------------------------------------------------------- 1 | # 2 | # CDDL HEADER START 3 | # 4 | # The contents of this file are subject to the terms of the 5 | # Common Development and Distribution License (the "License"). 6 | # You may not use this file except in compliance with the License. 7 | # 8 | # You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 9 | # or http://www.opensolaris.org/os/licensing. 10 | # See the License for the specific language governing permissions 11 | # and limitations under the License. 12 | # 13 | # When distributing Covered Code, include this CDDL HEADER in each 14 | # file and include the License file at usr/src/OPENSOLARIS.LICENSE. 15 | # If applicable, add the following below this CDDL HEADER, with the 16 | # fields enclosed by brackets "[]" replaced with your own identifying 17 | # information: Portions Copyright [yyyy] [name of copyright owner] 18 | # 19 | # CDDL HEADER END 20 | # 21 | 22 | import sys 23 | from os.path import abspath, dirname, join, pardir 24 | sys.path.insert(0, join(dirname(__file__), pardir, pardir)) 25 | 26 | -------------------------------------------------------------------------------- /usr/share/time-slider/lib/plugin/rsync/rsyncsmf.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python3.5 2 | # 3 | # CDDL HEADER START 4 | # 5 | # The contents of this file are subject to the terms of the 6 | # Common Development and Distribution License (the "License"). 7 | # You may not use this file except in compliance with the License. 8 | # 9 | # You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 10 | # or http://www.opensolaris.org/os/licensing. 11 | # See the License for the specific language governing permissions 12 | # and limitations under the License. 13 | # 14 | # When distributing Covered Code, include this CDDL HEADER in each 15 | # file and include the License file at usr/src/OPENSOLARIS.LICENSE. 16 | # If applicable, add the following below this CDDL HEADER, with the 17 | # fields enclosed by brackets "[]" replaced with your own identifying 18 | # information: Portions Copyright [yyyy] [name of copyright owner] 19 | # 20 | # CDDL HEADER END 21 | # 22 | 23 | import subprocess 24 | import threading 25 | #from string import letters, digits 26 | from plugin import pluginsmf 27 | 28 | RSYNCPROPGROUP = "rsync" 29 | RSYNCDIRPREFIX = "TIMESLIDER" 30 | RSYNCDIRSUFFIX = ".time-slider/rsync" 31 | RSYNCPARTIALSUFFIX = ".time-slider/.rsync-partial" 32 | RSYNCTRASHSUFFIX = ".time-slider/.trash" 33 | RSYNCLOCKSUFFIX = ".time-slider/.rsync-lock" 34 | RSYNCLOGSUFFIX = ".time-slider/.rsync-log" 35 | RSYNCCONFIGFILE = ".rsync-config" 36 | RSYNCFSTAG = "org.opensolaris:time-slider-rsync" 37 | 38 | class RsyncSMF(pluginsmf.PluginSMF): 39 | 40 | def __init__(self, instanceName): 41 | pluginsmf.PluginSMF.__init__(self, instanceName) 42 | self._archivedSchedules = None 43 | 44 | def get_cleanup_threshold(self): 45 | result = self.get_prop(RSYNCPROPGROUP, "cleanup_threshold").strip() 46 | return int(result) 47 | 48 | def get_target_dir(self): 49 | result = self.get_prop(RSYNCPROPGROUP, "target_dir").strip() 50 | # Strip out '\' characters inserted by svcprop 51 | return result.strip().replace('\\', '') 52 | 53 | def get_target_key(self): 54 | return self.get_prop(RSYNCPROPGROUP, "target_key").strip() 55 | 56 | def set_target_dir(self, path): 57 | self.set_string_prop(RSYNCPROPGROUP, "target_dir", path) 58 | 59 | def set_target_key(self, key): 60 | self.set_string_prop(RSYNCPROPGROUP, "target_key", key) 61 | 62 | def get_archived_schedules(self): 63 | #FIXME Use mutex locking to make MT-safe 64 | if self._archivedSchedules == None: 65 | self._archivedSchedules = [] 66 | value = self.get_prop(RSYNCPROPGROUP, "archived_schedules") 67 | 68 | # Strip out '\' characters inserted by svcprop 69 | archiveList = value.strip().replace('\\', '').split(',') 70 | for schedule in archiveList: 71 | self._archivedSchedules.append(schedule.strip()) 72 | return self._archivedSchedules 73 | 74 | def get_rsync_verbose(self): 75 | value = self.get_prop(RSYNCPROPGROUP, "verbose") 76 | if value == "true": 77 | return True 78 | else: 79 | return False 80 | 81 | def __str__(self): 82 | ret = "SMF Instance:\n" +\ 83 | "\tName:\t\t\t%s\n" % (self.instance_name) +\ 84 | "\tState:\t\t\t%s\n" % (self.svcstate) + \ 85 | "\tTriggers:\t\t%s\n" % str(self.get_triggers()) + \ 86 | "\tTarget Dir:\t%s\n" % self.get_target_dir() + \ 87 | "\tVerbose:\t\t\'%s\'" % str((self.get_verbose())) 88 | return ret 89 | 90 | -------------------------------------------------------------------------------- /usr/share/time-slider/lib/plugin/rsync/trigger.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python3.5 2 | # 3 | # CDDL HEADER START 4 | # 5 | # The contents of this file are subject to the terms of the 6 | # Common Development and Distribution License (the "License"). 7 | # You may not use this file except in compliance with the License. 8 | # 9 | # You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 10 | # or http://www.opensolaris.org/os/licensing. 11 | # See the License for the specific language governing permissions 12 | # and limitations under the License. 13 | # 14 | # When distributing Covered Code, include this CDDL HEADER in each 15 | # file and include the License file at usr/src/OPENSOLARIS.LICENSE. 16 | # If applicable, add the following below this CDDL HEADER, with the 17 | # fields enclosed by brackets "[]" replaced with your own identifying 18 | # information: Portions Copyright [yyyy] [name of copyright owner] 19 | # 20 | # CDDL HEADER END 21 | # 22 | 23 | import os 24 | import sys 25 | import subprocess 26 | import syslog 27 | 28 | from rsync import rsyncsmf 29 | from time_slider import util, smf, zfs 30 | 31 | verboseprop = "plugin/verbose" 32 | propbasename = "org.opensolaris:time-slider-plugin" 33 | 34 | 35 | def main(argv): 36 | # Check that appropriate environment variables have been 37 | # provided by time-sliderd 38 | # 39 | # The label used for the snapshot set just taken, ie. the 40 | # component proceeding the "@" in the snapshot name 41 | snaplabel = os.getenv("AUTOSNAP_LABEL") 42 | # The SMF fmri of the auto-snapshot instance corresponding to 43 | # the snapshot set just taken. 44 | snapfmri = os.getenv("AUTOSNAP_FMRI") 45 | # The SMF fmri of the time-slider plugin instance associated with 46 | # this command. 47 | pluginfmri = os.getenv("PLUGIN_FMRI") 48 | 49 | if pluginfmri == None: 50 | sys.stderr.write("No time-slider plugin SMF instance FMRI defined. " \ 51 | "This plugin does not support command line " 52 | "execution. Exiting\n") 53 | sys.exit(-1) 54 | syslog.openlog(pluginfmri, 0, syslog.LOG_DAEMON) 55 | 56 | cmd = [smf.SVCPROPCMD, "-p", verboseprop, pluginfmri] 57 | outdata,errdata = util.run_command(cmd) 58 | if outdata.rstrip() == "true": 59 | verbose = True 60 | else: 61 | verbose = False 62 | 63 | if snaplabel == None: 64 | log_error(syslog.LOG_ERR, 65 | "No snapshot label provided. Exiting") 66 | sys.exit(-1) 67 | if snapfmri == None: 68 | log_error(syslog.LOG_ERR, 69 | "No auto-snapshot SMF instance FMRI provided. Exiting") 70 | sys.exit(-1) 71 | 72 | schedule = snapfmri.rsplit(':', 1)[1] 73 | plugininstance = pluginfmri.rsplit(':', 1)[1] 74 | 75 | # The user property/tag used when tagging and holding zfs datasets 76 | propname = "%s:%s" % (propbasename, plugininstance) 77 | 78 | # Identifying snapshots is a 3 stage process. 79 | # 80 | # First: identify all snapshots matching the AUTOSNAP_LABEL 81 | # value passed in by the time-slider daemon. 82 | # 83 | # Second: Filter out snapshots of volumes, since rsync can only 84 | # back up filesystems. 85 | # 86 | # Third: we need to filter the results and ensure that the 87 | # filesystem corresponding to each snapshot is actually 88 | # tagged with the property (com.sun:auto-snapshot<:schedule>) 89 | # 90 | # This is necessary to avoid confusion whereby a snapshot might 91 | # have been sent|received from one zpool to another on the same 92 | # system. The received snapshot will show up in the first pass 93 | # results but is not actually part of the auto-snapshot set 94 | # created by time-slider. It also avoids incorrectly placing 95 | # zfs holds on the imported snapshots. 96 | 97 | 98 | datasets = zfs.Datasets() 99 | candidates = datasets.list_snapshots(snaplabel) 100 | autosnapsets = datasets.list_auto_snapshot_sets(schedule) 101 | autosnapfs = [name for [name,mount] in datasets.list_filesystems() \ 102 | if name in autosnapsets] 103 | snappeddatasets = [] 104 | snapnames = [name for [name,ctime] in candidates \ 105 | if name.split('@',1)[0] in autosnapfs] 106 | 107 | # Mark the snapshots with a user property. Doing this instead of 108 | # placing a physical hold on the snapshot allows time-slider to 109 | # expire the snapshots naturally or destroy them if a zpool fills 110 | # up and triggers a remedial cleanup. 111 | # It also prevents the possiblity of leaving snapshots lying around 112 | # indefinitely on the system if the plugin SMF instance becomes 113 | # disabled or having to release a pile of held snapshots. 114 | # We set org.opensolaris:time-slider-plugin: to "pending", 115 | # indicate 116 | snapshots = [] 117 | for snap in snapnames: 118 | snapshot = zfs.Snapshot(snap) 119 | fs = zfs.Filesystem(snapshot.fsname) 120 | if fs.get_user_property(rsyncsmf.RSYNCFSTAG) == "true": 121 | if fs.is_mounted() == True: 122 | snapshot.set_user_property(propname, "pending") 123 | util.debug("Marking %s as pending rsync" % (snap), verbose) 124 | else: 125 | util.debug("Ignoring snapshot of unmounted fileystem: %s" \ 126 | % (snap), verbose) 127 | 128 | def maintenance(svcfmri): 129 | log_error(syslog.LOG_ERR, 130 | "Placing plugin into maintenance state") 131 | cmd = [smf.SVCADMCMD, "mark", "maintenance", svcfmri] 132 | subprocess.Popen(cmd, close_fds=True) 133 | 134 | def log_error(loglevel, message): 135 | syslog.syslog(loglevel, message + '\n') 136 | sys.stderr.write(message + '\n') 137 | 138 | -------------------------------------------------------------------------------- /usr/share/time-slider/lib/plugin/zfssend/__init__.py: -------------------------------------------------------------------------------- 1 | # 2 | # CDDL HEADER START 3 | # 4 | # The contents of this file are subject to the terms of the 5 | # Common Development and Distribution License (the "License"). 6 | # You may not use this file except in compliance with the License. 7 | # 8 | # You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 9 | # or http://www.opensolaris.org/os/licensing. 10 | # See the License for the specific language governing permissions 11 | # and limitations under the License. 12 | # 13 | # When distributing Covered Code, include this CDDL HEADER in each 14 | # file and include the License file at usr/src/OPENSOLARIS.LICENSE. 15 | # If applicable, add the following below this CDDL HEADER, with the 16 | # fields enclosed by brackets "[]" replaced with your own identifying 17 | # information: Portions Copyright [yyyy] [name of copyright owner] 18 | # 19 | # CDDL HEADER END 20 | # 21 | 22 | import sys 23 | from os.path import abspath, dirname, join, pardir 24 | sys.path.insert(0, join(dirname(__file__), pardir, pardir)) 25 | 26 | -------------------------------------------------------------------------------- /usr/share/time-slider/lib/plugin/zfssend/zfssend.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python3.5 2 | # 3 | # CDDL HEADER START 4 | # 5 | # The contents of this file are subject to the terms of the 6 | # Common Development and Distribution License (the "License"). 7 | # You may not use this file except in compliance with the License. 8 | # 9 | # You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 10 | # or http://www.opensolaris.org/os/licensing. 11 | # See the License for the specific language governing permissions 12 | # and limitations under the License. 13 | # 14 | # When distributing Covered Code, include this CDDL HEADER in each 15 | # file and include the License file at usr/src/OPENSOLARIS.LICENSE. 16 | # If applicable, add the following below this CDDL HEADER, with the 17 | # fields enclosed by brackets "[]" replaced with your own identifying 18 | # information: Portions Copyright [yyyy] [name of copyright owner] 19 | # 20 | # CDDL HEADER END 21 | # 22 | 23 | import os 24 | import sys 25 | import subprocess 26 | import syslog 27 | from bisect import insort 28 | 29 | from time_slider import util, smf, zfs 30 | 31 | verboseprop = "plugin/verbose" 32 | propbasename = "org.opensolaris:time-slider-plugin" 33 | 34 | def main(argv): 35 | 36 | # Check appropriate environment variables habe been supplied 37 | # by time-slider 38 | # 39 | # The label used for the snapshot set just taken, ie. the 40 | # component proceeding the "@" in the snapshot name 41 | snaplabel = os.getenv("AUTOSNAP_LABEL") 42 | # The SMF fmri of the auto-snapshot instance corresponding to 43 | # the snapshot set just taken. 44 | snapfmri = os.getenv("AUTOSNAP_FMRI") 45 | # The SMF fmri of the time-slider plugin instance associated with 46 | # this command. 47 | pluginfmri = os.getenv("PLUGIN_FMRI") 48 | 49 | if pluginfmri == None: 50 | sys.stderr.write("No time-slider plugin SMF instance FMRI defined. " \ 51 | "This plugin does not support command line " 52 | "execution. Exiting\n") 53 | sys.exit(-1) 54 | syslog.openlog(pluginfmri, 0, syslog.LOG_DAEMON) 55 | 56 | cmd = [smf.SVCPROPCMD, "-p", verboseprop, pluginfmri] 57 | outdata,errdata = util.run_command(cmd) 58 | if outdata.rstrip() == "true": 59 | verbose = True 60 | else: 61 | verbose = False 62 | 63 | if snaplabel == None: 64 | log_error(syslog.LOG_ERR, 65 | "No snapshot label defined. Exiting") 66 | sys.exit(-1) 67 | if snapfmri == None: 68 | log_error(syslog.LOG_ERR, 69 | "No auto-snapshot SMF instance FMRI defined. Exiting") 70 | sys.exit(-1) 71 | 72 | schedule = snapfmri.rsplit(':', 1)[1] 73 | plugininstance = pluginfmri.rsplit(':', 1)[1] 74 | 75 | # The user property/tag used when tagging and holding zfs datasets 76 | propname = "%s:%s" % (propbasename, plugininstance) 77 | 78 | # Identifying snapshots is a two stage process. 79 | # 80 | # First: identify all snapshots matching the AUTOSNAP_LABEL 81 | # value passed in by the time-slider daemon. 82 | # 83 | # Second: we need to filter the results and ensure that the 84 | # filesystem/voluem corresponding to each snapshot is actually 85 | # tagged with the property (com.sun:auto-snapshot<:schedule>) 86 | # 87 | # This is necessary to avoid confusion whereby a snapshot might 88 | # have been sent|received from one zpool to another on the same 89 | # system. The received snapshot will show up in the first pass 90 | # results but is not actually part of the auto-snapshot set 91 | # created by time-slider. It also avoids incorrectly placing 92 | # zfs holds on the imported snapshots. 93 | 94 | datasets = zfs.Datasets() 95 | candidates = datasets.list_snapshots(snaplabel) 96 | originsets = datasets.list_auto_snapshot_sets(schedule) 97 | snappeddatasets = [] 98 | snapnames = [name for [name,ctime] in candidates \ 99 | if name.split('@',1)[0] in originsets] 100 | 101 | 102 | # Place a hold on the the newly created snapshots so 103 | # they can be backed up without fear of being destroyed 104 | # before the backup gets a chance to complete. 105 | for snap in snapnames: 106 | snapshot = zfs.Snapshot(snap) 107 | holds = snapshot.holds() 108 | try: 109 | holds.index(propname) 110 | except ValueError: 111 | util.debug("Placing hold on %s" % (snap), verbose) 112 | snapshot.hold(propname) 113 | datasetname = snapshot.fsname 114 | # Insert datasetnames in alphabetically sorted order because 115 | # zfs receive falls over if it receives a child before the 116 | # parent if the "-F" option is not used. 117 | insort(snappeddatasets, datasetname) 118 | 119 | # Find out the receive command property value 120 | cmd = [smf.SVCPROPCMD, "-c", "-p", "receive/command", pluginfmri] 121 | outdata,errdata = util.run_command(cmd) 122 | # Strip out '\' characters inserted by svcprop 123 | recvcmd = outdata.strip().replace('\\', '').split() 124 | 125 | # Check to see if the receive command is accessible and executable 126 | try: 127 | statinfo = os.stat(recvcmd[0]) 128 | other_x = (statinfo.st_mode & 0o1) 129 | if other_x == 0: 130 | log_error(syslog.LOG_ERR, 131 | "Plugin: %s: Configured receive/command is not " \ 132 | "executable: %s" \ 133 | % (pluginfmri, outdata)) 134 | maintenance(pluginfmri) 135 | sys.exit(-1) 136 | except OSError: 137 | log_error(syslog.LOG_ERR, 138 | "Plugin: %s: Can not access the configured " \ 139 | "receive/command: %s" \ 140 | % (pluginfmri, outdata)) 141 | maintenance(pluginfmri) 142 | sys.exit(-1) 143 | 144 | for dataset in snappeddatasets: 145 | sendcmd = None 146 | prevsnapname = None 147 | ds = zfs.ReadableDataset(dataset) 148 | prevlabel = ds.get_user_property(propname, True) 149 | 150 | snapname = "%s@%s" % (ds.name, snaplabel) 151 | if (prevlabel == None or prevlabel == '-' or len(prevlabel) == 0): 152 | # No previous backup - send a full replication stream 153 | sendcmd = [zfs.ZFSCMD, "send", snapname] 154 | util.debug("No previous backup registered for %s" % ds.name, verbose) 155 | else: 156 | # A record of a previous backup exists. 157 | # Check that it exists to enable send of an incremental stream. 158 | prevsnapname = "%s@%s" % (ds.name, prevlabel) 159 | util.debug("Previously sent snapshot: %s" % prevsnapname, verbose) 160 | prevsnap = zfs.Snapshot(prevsnapname) 161 | if prevsnap.exists(): 162 | sendcmd = [zfs.ZFSCMD, "send", "-i", prevsnapname, snapname] 163 | else: 164 | # This should not happen under normal operation since we 165 | # place a hold on the snapshot until it gets sent. So 166 | # getting here suggests that something else released the 167 | # hold on the snapshot, allowing it to get destroyed 168 | # prematurely. 169 | log_error(syslog.LOG_ERR, 170 | "Previously sent snapshot no longer exists: %s" \ 171 | % prevsnapname) 172 | maintenance(pluginfmri) 173 | sys.exit(-1) 174 | 175 | 176 | # Invoke the send and receive commands via pfexec(1) since 177 | # we are not using the role's shell to take care of that 178 | # for us. 179 | sendcmd.insert(0, smf.PFCMD) 180 | recvcmd.insert(0, smf.PFCMD) 181 | 182 | try: 183 | sendP = subprocess.Popen(sendcmd, 184 | stdout=subprocess.PIPE, 185 | stderr=subprocess.PIPE, 186 | close_fds=True) 187 | recvP = subprocess.Popen(recvcmd, 188 | stdin=sendP.stdout, 189 | stderr=subprocess.PIPE, 190 | close_fds=True) 191 | 192 | recvout,recverr = recvP.communicate() 193 | recverrno = recvP.wait() 194 | sendout,senderr = sendP.communicate() 195 | senderrno = sendP.wait() 196 | 197 | if senderrno != 0: 198 | raise RuntimeError("Send command: %s failed with exit code" \ 199 | "%d. Error message: \n%s" \ 200 | % (str(sendcmd), senderrno, senderr)) 201 | if recverrno != 0: 202 | raise RuntimeError("Receive command %s failed with exit " \ 203 | "code %d. Error message: \n%s" \ 204 | % (str(recvcmd), recverrno, recverr)) 205 | 206 | if prevsnapname != None: 207 | util.debug("Releasing hold on %s" % (prevsnapname), verbose) 208 | snapshot = zfs.Snapshot(prevsnapname) 209 | util.debug("Releasing hold on previous snapshot: %s" \ 210 | % (prevsnapname), 211 | verbose) 212 | snapshot.release(propname) 213 | except Exception as message: 214 | log_error(syslog.LOG_ERR, 215 | "Error during snapshot send/receive operation: %s" \ 216 | % (message)) 217 | 218 | maintenance(pluginfmri) 219 | sys.exit(-1) 220 | 221 | # Finally, after success, make a record of the latest backup 222 | # and release the old snapshot. 223 | ds.set_user_property(propname, snaplabel) 224 | util.debug("Sending of \"%s\"snapshot streams completed" \ 225 | % (snaplabel), 226 | verbose) 227 | 228 | def maintenance(svcfmri): 229 | log_error(syslog.LOG_ERR, 230 | "Placing plugin into maintenance state") 231 | cmd = [smf.SVCADMCMD, "mark", "maintenance", svcfmri] 232 | subprocess.Popen(cmd, close_fds=True) 233 | 234 | def log_error(loglevel, message): 235 | syslog.syslog(loglevel, message + '\n') 236 | sys.stderr.write(message + '\n') 237 | 238 | -------------------------------------------------------------------------------- /usr/share/time-slider/lib/time_slider/__init__.py: -------------------------------------------------------------------------------- 1 | # 2 | # CDDL HEADER START 3 | # 4 | # The contents of this file are subject to the terms of the 5 | # Common Development and Distribution License (the "License"). 6 | # You may not use this file except in compliance with the License. 7 | # 8 | # You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 9 | # or http://www.opensolaris.org/os/licensing. 10 | # See the License for the specific language governing permissions 11 | # and limitations under the License. 12 | # 13 | # When distributing Covered Code, include this CDDL HEADER in each 14 | # file and include the License file at usr/src/OPENSOLARIS.LICENSE. 15 | # If applicable, add the following below this CDDL HEADER, with the 16 | # fields enclosed by brackets "[]" replaced with your own identifying 17 | # information: Portions Copyright [yyyy] [name of copyright owner] 18 | # 19 | # CDDL HEADER END 20 | # 21 | 22 | import sys 23 | from os.path import abspath, dirname, join, pardir 24 | sys.path.insert(0, join(dirname(__file__), pardir, "plugin")) 25 | 26 | # here we define the path constants so that other modules can use it. 27 | # this allows us to get access to the shared files without having to 28 | # know the actual location, we just use the location of the current 29 | # file and use paths relative to that. 30 | SHARED_FILES = abspath(join(dirname(__file__), pardir, pardir)) 31 | LOCALE_PATH = join('/usr', 'share', 'locale') 32 | RESOURCE_PATH = join(SHARED_FILES, 'res') 33 | 34 | # the name of the gettext domain. because we have our translation files 35 | # not in a global folder this doesn't really matter, setting it to the 36 | # application name is a good idea tough. 37 | GETTEXT_DOMAIN = 'time-slider' 38 | 39 | # set up the gettext system and locales 40 | import gettext 41 | import locale 42 | 43 | locale.setlocale(locale.LC_ALL, '') 44 | gettext.bindtextdomain(GETTEXT_DOMAIN, LOCALE_PATH) 45 | gettext.textdomain(GETTEXT_DOMAIN) 46 | 47 | # register the gettext function for the whole interpreter as "_" 48 | import builtins 49 | builtins._ = gettext.gettext 50 | 51 | 52 | 53 | -------------------------------------------------------------------------------- /usr/share/time-slider/lib/time_slider/autosnapsmf.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python3.5 2 | # 3 | # CDDL HEADER START 4 | # 5 | # The contents of this file are subject to the terms of the 6 | # Common Development and Distribution License (the "License"). 7 | # You may not use this file except in compliance with the License. 8 | # 9 | # You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 10 | # or http://www.opensolaris.org/os/licensing. 11 | # See the License for the specific language governing permissions 12 | # and limitations under the License. 13 | # 14 | # When distributing Covered Code, include this CDDL HEADER in each 15 | # file and include the License file at usr/src/OPENSOLARIS.LICENSE. 16 | # If applicable, add the following below this CDDL HEADER, with the 17 | # fields enclosed by brackets "[]" replaced with your own identifying 18 | # information: Portions Copyright [yyyy] [name of copyright owner] 19 | # 20 | # CDDL HEADER END 21 | # 22 | 23 | import threading 24 | from time_slider import smf 25 | from time_slider import util 26 | 27 | factoryDefaultSchedules = ("monthly", "weekly", "daily", "hourly", "frequent") 28 | 29 | BASESVC= "svc:/system/filesystem/zfs/auto-snapshot" 30 | SNAPLABELPREFIX = "zfs-auto-snap" 31 | ZFSPROPGROUP = "zfs" 32 | 33 | 34 | # Bombarding the class with schedule queries causes the occasional 35 | # OSError exception due to interrupted system calls. 36 | # Serialising them helps prevent this unlikely event from occuring. 37 | _scheddetaillock = threading.RLock() 38 | 39 | class AutoSnap(smf.SMFInstance): 40 | 41 | def __init__(self, schedule): 42 | smf.SMFInstance.__init__(self, "%s:%s" % (BASESVC, schedule)) 43 | self.schedule = schedule 44 | 45 | def get_schedule_details(self): 46 | svc= "%s:%s" % (BASESVC, self.schedule) 47 | _scheddetaillock.acquire() 48 | try: 49 | interval = self.get_prop(ZFSPROPGROUP, "interval") 50 | period = int(self.get_prop(ZFSPROPGROUP, "period")) 51 | keep = int(self.get_prop(ZFSPROPGROUP, "keep")) 52 | 53 | except OSError as message: 54 | raise RuntimeError("%s subprocess error:\n %s" % \ 55 | (cmd, str(message))) 56 | finally: 57 | _scheddetaillock.release() 58 | 59 | return [self.schedule, interval, period, keep] 60 | 61 | # FIXME - merge with enable_default_schedules() 62 | def disable_default_schedules(): 63 | """ 64 | Disables the default auto-snapshot SMF instances corresponding 65 | to: "frequent", "hourly", "daily", "weekly" and "monthly" 66 | schedules 67 | Raises RuntimeError exception if unsuccessful 68 | """ 69 | 70 | for s in factoryDefaultSchedules: 71 | # Acquire the scheddetail lock since their status will 72 | # likely be changed as a result of enabling the instances. 73 | _scheddetaillock.acquire() 74 | instanceName = "%s:%s" % (BASESVC,s) 75 | svc = smf.SMFInstance(instanceName) 76 | svc.disable_service() 77 | _scheddetaillock.release() 78 | 79 | def enable_default_schedules(): 80 | """ 81 | Enables the default auto-snapshot SMF instances corresponding 82 | to: "frequent", "hourly", "daily", "weekly" and "monthly" 83 | schedules 84 | Raises RuntimeError exception if unsuccessful 85 | """ 86 | for s in factoryDefaultSchedules: 87 | # Acquire the scheddetail lock since their status will 88 | # likely be changed as a result of enabling the instances. 89 | _scheddetaillock.acquire() 90 | instanceName = "%s:%s" % (BASESVC,s) 91 | svc = smf.SMFInstance(instanceName) 92 | svc.enable_service() 93 | _scheddetaillock.release() 94 | 95 | def get_default_schedules(): 96 | """ 97 | Finds the default schedules that are enabled (online, offline or degraded) 98 | """ 99 | #This is not the fastest method but it is the safest, we need 100 | #to ensure that default schedules are processed in the pre-defined 101 | #order to ensure that the overlap between them is adhered to 102 | #correctly. monthly->weekly->daily->hourly->frequent. They have 103 | #to be processed first and they HAVE to be in the correct order. 104 | _defaultSchedules = [] 105 | for s in factoryDefaultSchedules: 106 | instanceName = "%s:%s" % (BASESVC,s) 107 | cmd = [smf.SVCSCMD, "-H", "-o", "state", instanceName] 108 | _scheddetaillock.acquire() 109 | try: 110 | outdata,errdata = util.run_command(cmd) 111 | finally: 112 | _scheddetaillock.release() 113 | result = outdata.rstrip() 114 | # Note that the schedules, being dependent on the time-slider service 115 | # itself will typically be in an offline state when enabled. They will 116 | # transition to an "online" state once time-slider itself comes 117 | # "online" to satisfy it's dependency 118 | if result == "online" or result == "offline" or result == "degraded": 119 | instance = AutoSnap(s) 120 | try: 121 | _defaultSchedules.append(instance.get_schedule_details()) 122 | except RuntimeError as message: 123 | raise RuntimeError("Error getting schedule details for " + \ 124 | "default auto-snapshot SMF instance:" + \ 125 | "\n\t" + instanceName + "\nDetails:\n" + \ 126 | str(message)) 127 | return _defaultSchedules 128 | 129 | def get_custom_schedules(): 130 | """ 131 | Finds custom schedules ie. not the factory default 132 | 'monthly', 'weekly', 'hourly', 'daily' and 'frequent' schedules 133 | """ 134 | _customSchedules = [] 135 | cmd = [smf.SVCSCMD, "-H", "-o", "state,FMRI", BASESVC] 136 | _scheddetaillock.acquire() 137 | try: 138 | outdata,errdata = util.run_command(cmd) 139 | finally: 140 | _scheddetaillock.release() 141 | 142 | for line in outdata.rstrip().split('\n'): 143 | line = line.rstrip().split() 144 | state = line[0] 145 | fmri = line[1] 146 | fmri = fmri.rsplit(":", 1) 147 | label = fmri[1] 148 | if label not in factoryDefaultSchedules: 149 | # Note that the schedules, being dependent on the time-slider service 150 | # itself will typically be in an offline state when enabled. They will 151 | # transition to an "online" state once time-slider itself comes 152 | # "online" to satisfy it's dependency 153 | if state == "online" or state == "offline" or state == "degraded": 154 | instance = AutoSnap(label) 155 | try: 156 | _customSchedules.append(instance.get_schedule_details()) 157 | except RuntimeError as message: 158 | raise RuntimeError("Error getting schedule details " + \ 159 | "for custom auto-snapshot SMF " + \ 160 | "instance:\n\t" + label + "\n" + \ 161 | "Details:\n" + str(message)) 162 | return _customSchedules 163 | 164 | 165 | if __name__ == "__main__": 166 | defaults = get_default_schedules() 167 | for sched in defaults: 168 | S = AutoSnap(sched[0]) 169 | print(S.get_schedule_details()) 170 | 171 | -------------------------------------------------------------------------------- /usr/share/time-slider/lib/time_slider/dbussvc.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python3.5 2 | # 3 | # CDDL HEADER START 4 | # 5 | # The contents of this file are subject to the terms of the 6 | # Common Development and Distribution License (the "License"). 7 | # You may not use this file except in compliance with the License. 8 | # 9 | # You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 10 | # or http://www.opensolaris.org/os/licensing. 11 | # See the License for the specific language governing permissions 12 | # and limitations under the License. 13 | # 14 | # When distributing Covered Code, include this CDDL HEADER in each 15 | # file and include the License file at usr/src/OPENSOLARIS.LICENSE. 16 | # If applicable, add the following below this CDDL HEADER, with the 17 | # fields enclosed by brackets "[]" replaced with your own identifying 18 | # information: Portions Copyright [yyyy] [name of copyright owner] 19 | # 20 | # CDDL HEADER END 21 | # 22 | 23 | 24 | import dbus 25 | import dbus.service 26 | import dbus.mainloop 27 | import dbus.mainloop.glib 28 | 29 | 30 | class AutoSnap(dbus.service.Object): 31 | """ 32 | D-Bus object for Time Slider's auto snapshot features. 33 | """ 34 | def __init__(self, bus, path, snapshotmanager): 35 | self.snapshotmanager = snapshotmanager 36 | self._bus = bus 37 | dbus.service.Object.__init__(self, 38 | bus, 39 | path) 40 | 41 | # Remedial cleanup signal 42 | @dbus.service.signal(dbus_interface="org.opensolaris.TimeSlider.autosnap", 43 | signature='suu') 44 | def capacity_exceeded(self, pool, severity, threshhold): 45 | pass 46 | 47 | class RsyncBackup(dbus.service.Object): 48 | """ 49 | D-Bus object for Time Slider's rsync backup feature. 50 | """ 51 | def __init__(self, bus, path): 52 | self._bus = bus 53 | dbus.service.Object.__init__(self, 54 | bus, 55 | path) 56 | 57 | # Rsync operation rsync_started signal 58 | @dbus.service.signal(dbus_interface="org.opensolaris.TimeSlider.plugin.rsync", 59 | signature='s') 60 | def rsync_started(self, target): 61 | pass 62 | 63 | # Rsync operation rsync_current signal 64 | @dbus.service.signal(dbus_interface="org.opensolaris.TimeSlider.plugin.rsync", 65 | signature='su') 66 | def rsync_current(self, snapshot, remaining): 67 | pass 68 | 69 | # Rsync operation rsync_complete signal 70 | @dbus.service.signal(dbus_interface="org.opensolaris.TimeSlider.plugin.rsync", 71 | signature='s') 72 | def rsync_complete(self, target): 73 | pass 74 | 75 | # Rsync operation rsync_synced signal 76 | @dbus.service.signal(dbus_interface="org.opensolaris.TimeSlider.plugin.rsync", 77 | signature='') 78 | def rsync_synced(self): 79 | pass 80 | 81 | # Rsync operation rsync_unsynced signal 82 | @dbus.service.signal(dbus_interface="org.opensolaris.TimeSlider.plugin.rsync", 83 | signature='u') 84 | def rsync_unsynced(self, queueSize): 85 | pass 86 | 87 | 88 | class Config(dbus.service.Object): 89 | """ 90 | D-Bus object representing Time Slider service configuration changes. 91 | """ 92 | def __init__(self, bus, path): 93 | self._bus = bus 94 | dbus.service.Object.__init__(self, 95 | bus, 96 | path) 97 | # Service configuration change signal. Nothing fancy for now. 98 | # Listeners need to figure out what changed for themselves. 99 | @dbus.service.signal(dbus_interface="org.opensolaris.TimeSlider.config", 100 | signature='') 101 | def config_changed(self): 102 | pass 103 | 104 | -------------------------------------------------------------------------------- /usr/share/time-slider/lib/time_slider/fileversion.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python3.5 2 | # 3 | # CDDL HEADER START 4 | # 5 | # The contents of this file are subject to the terms of the 6 | # Common Development and Distribution License (the "License"). 7 | # You may not use this file except in compliance with the License. 8 | # 9 | # You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 10 | # or http://www.opensolaris.org/os/licensing. 11 | # See the License for the specific language governing permissions 12 | # and limitations under the License. 13 | # 14 | # When distributing Covered Code, include this CDDL HEADER in each 15 | # file and include the License file at usr/src/OPENSOLARIS.LICENSE. 16 | # If applicable, add the following below this CDDL HEADER, with the 17 | # fields enclosed by brackets "[]" replaced with your own identifying 18 | # information: Portions Copyright [yyyy] [name of copyright owner] 19 | # 20 | # CDDL HEADER END 21 | # 22 | 23 | import time 24 | import getopt 25 | import os 26 | import sys 27 | import threading 28 | import subprocess 29 | import string 30 | #import traceback 31 | 32 | try: 33 | import gi 34 | gi.require_version('Gtk', '3.0') 35 | from gi.repository import Gtk, GObject, Gio, GLib, GdkPixbuf 36 | except: 37 | sys.exit(1) 38 | 39 | # here we define the path constants so that other modules can use it. 40 | # this allows us to get access to the shared files without having to 41 | # know the actual location, we just use the location of the current 42 | # file and use paths relative to that. 43 | SHARED_FILES = os.path.abspath(os.path.join(os.path.dirname(__file__), 44 | os.path.pardir, 45 | os.path.pardir)) 46 | LOCALE_PATH = os.path.join('/usr', 'share', 'locale') 47 | RESOURCE_PATH = os.path.join(SHARED_FILES, 'res') 48 | 49 | # the name of the gettext domain. because we have our translation files 50 | # not in a global folder this doesn't really matter, setting it to the 51 | # application name is a good idea tough. 52 | GETTEXT_DOMAIN = 'time-slider' 53 | 54 | KILOBYTES = 1024.0 55 | MEGABYTES = KILOBYTES*1024 56 | GIGABYTES = MEGABYTES*1024 57 | TERABYTES = GIGABYTES*1024 58 | 59 | class File: 60 | displayTemplates = [ 61 | (TERABYTES, '%0.1f TB'), 62 | (GIGABYTES, '%0.1f GB'), 63 | (MEGABYTES, '%0.1f MB'), 64 | (KILOBYTES, '%0.1f KB'), 65 | (0, '%0.1f B'),] 66 | 67 | def __init__(self, path): 68 | self.path = path 69 | self.file = Gio.File.new_for_path(path) 70 | try: 71 | self.info = self.file.query_info ("*", Gio.FileQueryInfoFlags.NONE) 72 | self.exist = True 73 | except GLib.Error: 74 | self.exist = False 75 | 76 | def get_icon (self): 77 | return Gtk.IconTheme.get_default().choose_icon (self.info.get_icon().get_property ("names") + ["unknown"], 48, Gtk.IconLookupFlags.USE_BUILTIN).load_icon () 78 | 79 | def get_size (self): 80 | amount = self.info.get_size () 81 | for treshold, template in self.displayTemplates: 82 | if amount > treshold: 83 | if treshold: 84 | amount = amount /treshold 85 | return "%s (%d bytes)" % (template % amount, self.info.get_size ()) 86 | return "%d byte" % amount 87 | 88 | def add_if_unique (self, versions): 89 | found = False 90 | for file in versions: 91 | if file.info.get_modification_time ().tv_sec == self.info.get_modification_time ().tv_sec: 92 | found = True 93 | if not found: 94 | versions.append (self) 95 | return True 96 | return False 97 | 98 | def get_mime_type (self): 99 | return Gio.content_type_guess(self.path)[0] 100 | 101 | 102 | ( COLUMN_ICON, 103 | COLUMN_NAME, 104 | COLUMN_STRING_DATE, 105 | COLUMN_DATE, 106 | COLUMN_SIZE 107 | ) = range (5) 108 | 109 | 110 | 111 | class FileVersionWindow: 112 | meld_hint_displayed = False 113 | 114 | def __init__(self, snap_path, file): 115 | self.snap_path = snap_path 116 | self.filename = file 117 | self.builder = Gtk.Builder() 118 | self.builder.set_translation_domain(GETTEXT_DOMAIN) 119 | 120 | self.builder.add_from_file("%s/../../ui/time-slider-version.ui" \ 121 | % (os.path.dirname(__file__))) 122 | 123 | self.window = self.builder.get_object("toplevel") 124 | self.progress = self.builder.get_object("progress") 125 | self.version_label = self.builder.get_object("num_versions_label") 126 | # signal dictionary 127 | dic = {"on_toplevel_delete_event": self.exit3 , 128 | "on_close_clicked": self.exit , 129 | "on_compare_button_clicked": self.on_compare_button_clicked, 130 | "on_current_file_button_clicked": self.on_current_file_button_clicked, 131 | "on_treeview_row_activated": self.on_treeview_row_activated, 132 | "on_treeview_cursor_changed": self.on_treeview_cursor_changed} 133 | self.builder.connect_signals(dic) 134 | 135 | self.filename_label = self.builder.get_object("filename_label") 136 | self.size_label = self.builder.get_object("size_label") 137 | self.date_label = self.builder.get_object("date_label") 138 | self.older_versions_label = self.builder.get_object("older_versions_label") 139 | self.compare_button = self.builder.get_object("compare_button") 140 | self.button_init = False 141 | 142 | self.window.show () 143 | 144 | self.file = File (file) 145 | self.filename_label.set_text (self.file.info.get_name ()) 146 | self.size_label.set_text (self.file.get_size ()) 147 | self.date_label.set_text (time.strftime ("%d/%m/%y %Hh%Ms%S", time.localtime(self.file.info.get_modification_time ().tv_sec))) 148 | self.builder.get_object("icon_image").set_from_pixbuf (self.file.get_icon ()) 149 | 150 | self.treeview = self.builder.get_object("treeview") 151 | self.model = Gtk.ListStore(GdkPixbuf.Pixbuf, 152 | GObject.TYPE_STRING, 153 | GObject.TYPE_STRING, 154 | GObject.TYPE_STRING, 155 | GObject.TYPE_STRING) 156 | 157 | self.treeview.set_model (self.model) 158 | self.__add_columns (self.treeview) 159 | 160 | self.scanner = VersionScanner (self) 161 | self.scanner.start() 162 | 163 | def __add_columns(self, treeview): 164 | model = treeview.get_model() 165 | 166 | renderer = Gtk.CellRendererPixbuf() 167 | column = Gtk.TreeViewColumn('Icon', renderer, pixbuf=COLUMN_ICON) 168 | treeview.append_column(column) 169 | 170 | self.date_column = Gtk.TreeViewColumn('Last Modified Date', Gtk.CellRendererText(), 171 | text=COLUMN_STRING_DATE) 172 | self.date_column.set_sort_column_id(COLUMN_DATE) 173 | treeview.append_column(self.date_column) 174 | 175 | # column for description 176 | column = Gtk.TreeViewColumn('Size', Gtk.CellRendererText(), 177 | text=COLUMN_SIZE) 178 | column.set_sort_column_id(COLUMN_SIZE) 179 | treeview.append_column(column) 180 | 181 | def add_file (self, file): 182 | iter = self.model.append () 183 | self.model.set (iter, 184 | COLUMN_ICON, file.get_icon (), 185 | COLUMN_NAME, file.path, 186 | COLUMN_STRING_DATE, time.strftime ("%d/%m/%y %Hh%Ms%S", time.localtime(file.info.get_modification_time ().tv_sec)), 187 | COLUMN_DATE, str(file.info.get_modification_time ().tv_sec), 188 | COLUMN_SIZE, file.get_size ()) 189 | 190 | def exit3 (self, blah, blih): 191 | self.exit (self) 192 | 193 | def exit (self, blah): 194 | self.scanner.join () 195 | Gtk.main_quit () 196 | 197 | def on_current_file_button_clicked (self, widget): 198 | subprocess.Popen (["gio", "open", self.filename]) 199 | 200 | def on_treeview_row_activated (self, treeview, path, column): 201 | (model, iter) = treeview.get_selection ().get_selected () 202 | filename = model.get (iter, 1)[0] 203 | subprocess.Popen (["gio", "open", filename]) 204 | 205 | def on_treeview_cursor_changed (self, treeview): 206 | if not self.button_init: 207 | self.button_init = True 208 | if self.file.get_mime_type ().find ("text") != -1 : 209 | self.compare_button.set_sensitive (True) 210 | 211 | def on_compare_button_clicked (self, widget): 212 | (model, iter) = self.treeview.get_selection ().get_selected () 213 | filename = model.get (iter, 1)[0] 214 | if os.path.exists ("/usr/bin/meld"): 215 | subprocess.Popen (["/usr/bin/meld",self.filename, filename]) 216 | else: 217 | if not self.meld_hint_displayed: 218 | dialog = Gtk.MessageDialog(None, 0, Gtk.MessageType.INFO, Gtk.ButtonsType.CLOSE, _("Hint")) 219 | dialog.set_title (_("Hint")) 220 | dialog.format_secondary_text(_("Installing the optional meld package will enhance the file comparison visualization")) 221 | dialog.run () 222 | dialog.destroy () 223 | self.meld_hint_displayed = True 224 | p1 = subprocess.Popen(["/usr/bin/diff", "-u", self.filename, filename], stdout=subprocess.PIPE, universal_newlines=True) 225 | p2 = subprocess.Popen(str.split ("/usr/bin/zenity --text-info --editable"), stdin=p1.stdout, stdout=subprocess.PIPE, universal_newlines=True) 226 | 227 | 228 | class VersionScanner(threading.Thread): 229 | 230 | def __init__(self, window): 231 | self.w = window 232 | self._stopevent = threading.Event() 233 | threading.Thread.__init__(self) 234 | 235 | def run(self): 236 | l = self.w.snap_path.split (".zfs", maxsplit=1) 237 | path_before_snap = l[0] 238 | l = self.w.filename.split (path_before_snap, maxsplit=1) 239 | path_after_snap = l[1] 240 | snap_path = "%s.zfs/snapshot/" % path_before_snap; 241 | dirs = os.listdir(snap_path) 242 | 243 | num_dirs = len(dirs) 244 | current_dir = 1 245 | 246 | GObject.idle_add (self.w.progress.set_pulse_step, (1.0 / num_dirs)) 247 | GObject.idle_add (self.w.progress.set_text, ("Scanning for older versions (%d/%d)" % (current_dir, num_dirs))) 248 | 249 | versions = [File (self.w.filename)] 250 | 251 | for dir in dirs: 252 | if not self._stopevent.isSet (): 253 | file = File ("%s%s/%s" % (snap_path, dir, path_after_snap)) 254 | if file.exist : 255 | if file.add_if_unique(versions): 256 | GObject.idle_add (self.w.add_file, file) 257 | fraction = self.w.progress.get_fraction () 258 | fraction += self.w.progress.get_pulse_step () 259 | if fraction > 1: 260 | fraction = 1 261 | 262 | GObject.idle_add (self.w.progress.set_fraction, fraction) 263 | current_dir += 1 264 | GObject.idle_add (self.w.progress.set_text, "Scanning for older versions (%d/%d)" % (current_dir, num_dirs)) 265 | else: 266 | return None 267 | 268 | GObject.idle_add(self.w.progress.hide) 269 | GObject.idle_add(self.w.older_versions_label.set_markup , "Older Versions (%d) " % (len(versions) - 1)) 270 | # sort by date 271 | GObject.idle_add(self.w.date_column.emit, "clicked") 272 | GObject.idle_add(self.w.date_column.emit, "clicked") 273 | 274 | def join(self, timeout=None): 275 | self._stopevent.set () 276 | threading.Thread.join(self, timeout) 277 | 278 | def main(argv): 279 | try: 280 | opts, args = getopt.getopt(sys.argv[1:], "", []) 281 | except getopt.GetoptError: 282 | sys.exit(2) 283 | if len(args) != 2: 284 | dialog = Gtk.MessageDialog(None, 285 | 0, 286 | Gtk.MessageType.ERROR, 287 | Gtk.ButtonsType.CLOSE, 288 | _("Invalid arguments count.")) 289 | dialog.set_title ("Error") 290 | dialog.format_secondary_text(_("Version explorer requires" 291 | " 2 arguments :\n- The path of the " 292 | "root snapshot directory.\n" 293 | "- The filename to explore.")) 294 | dialog.run() 295 | sys.exit (2) 296 | 297 | window = FileVersionWindow(args[0], args[1]) 298 | Gtk.main() 299 | -------------------------------------------------------------------------------- /usr/share/time-slider/lib/time_slider/rbac.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python3.5 2 | # 3 | # CDDL HEADER START 4 | # 5 | # The contents of this file are subject to the terms of the 6 | # Common Development and Distribution License (the "License"). 7 | # You may not use this file except in compliance with the License. 8 | # 9 | # You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 10 | # or http://www.opensolaris.org/os/licensing. 11 | # See the License for the specific language governing permissions 12 | # and limitations under the License. 13 | # 14 | # When distributing Covered Code, include this CDDL HEADER in each 15 | # file and include the License file at usr/src/OPENSOLARIS.LICENSE. 16 | # If applicable, add the following below this CDDL HEADER, with the 17 | # fields enclosed by brackets "[]" replaced with your own identifying 18 | # information: Portions Copyright [yyyy] [name of copyright owner] 19 | # 20 | # CDDL HEADER END 21 | # 22 | 23 | import os 24 | import pwd 25 | 26 | from time_slider import util 27 | 28 | class RBACprofile: 29 | 30 | def __init__(self, name = None): 31 | # Filtering through the pwd module is beneficial because 32 | # it will raise a KeyError exception for an invalid 33 | # name argument 34 | if name == None: 35 | euid = os.geteuid() 36 | pwnam = pwd.getpwuid(euid) 37 | self.uid = euid 38 | self.name = pwnam[0] 39 | else: 40 | pwnam = pwd.getpwnam(name) 41 | self.name = pwnam[0] 42 | self.uid = pwnam[2] 43 | 44 | self.profiles = self.get_profiles() 45 | self.auths = self.get_auths() 46 | 47 | def get_profiles(self): 48 | cmd = ["/usr/bin/profiles", self.name] 49 | profiles = [] 50 | outdata,errdata = util.run_command(cmd) 51 | for line in outdata.split('\n'): 52 | if line.isspace(): 53 | continue 54 | else: 55 | try: 56 | line.index(self.name + " :") 57 | except ValueError: 58 | profiles.append(line.strip()) 59 | # Remove "All" because it's (seemingly) meaningless 60 | try: 61 | profiles.remove("All") 62 | except ValueError: 63 | return profiles 64 | return profiles 65 | 66 | def get_auths(self): 67 | cmd = ["/usr/bin/auths", self.name] 68 | auths = [] 69 | outdata,errdata = util.run_command(cmd) 70 | auths = outdata.rstrip().split(",") 71 | return auths 72 | 73 | def has_profile(self, profile): 74 | # root is all powerful 75 | if self.uid == 0: 76 | return True 77 | try: 78 | self.profiles.index(profile) 79 | except ValueError: 80 | return False 81 | return True 82 | 83 | def has_auth(self, auth): 84 | """ Checks the user's authorisations to see if "auth" is 85 | assigned to the user. Recursively searches higher up 86 | for glob matching eg. solaris.network.hosts.read -> 87 | solaris.network.hosts.* -> solaris.network.* -> 88 | solaris.*, until a valid authorisation is found. 89 | Returns True if user has the "auth" authorisation, 90 | False otherwise""" 91 | try: 92 | self.auths.index(auth) 93 | return True 94 | except ValueError: 95 | subpattern = auth.rsplit(".", 1) 96 | # If there are still more "."s in the string 97 | if subpattern[0] != auth: 98 | # Try using the glob pattern if auth is not 99 | # already a glob pattern eg. solaris.device.* 100 | if subpattern[1] != "*": 101 | try: 102 | self.auths.index("%s.*" % subpattern[0]) 103 | return True 104 | except ValueError: 105 | pass 106 | # Strip another "." off the auth and carry on searching 107 | subsearch = subpattern[0].rsplit(".", 1) 108 | if subsearch[0] != subpattern[0]: 109 | return self.has_auth("%s.*" % subsearch[0]) 110 | return False 111 | 112 | if __name__ == "__main__": 113 | rbac = RBACprofile() 114 | print(rbac.name) 115 | print(rbac.uid) 116 | print(rbac.profiles) 117 | print(rbac.auths) 118 | 119 | -------------------------------------------------------------------------------- /usr/share/time-slider/lib/time_slider/smf.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python3.5 2 | # 3 | # CDDL HEADER START 4 | # 5 | # The contents of this file are subject to the terms of the 6 | # Common Development and Distribution License (the "License"). 7 | # You may not use this file except in compliance with the License. 8 | # 9 | # You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 10 | # or http://www.opensolaris.org/os/licensing. 11 | # See the License for the specific language governing permissions 12 | # and limitations under the License. 13 | # 14 | # When distributing Covered Code, include this CDDL HEADER in each 15 | # file and include the License file at usr/src/OPENSOLARIS.LICENSE. 16 | # If applicable, add the following below this CDDL HEADER, with the 17 | # fields enclosed by brackets "[]" replaced with your own identifying 18 | # information: Portions Copyright [yyyy] [name of copyright owner] 19 | # 20 | # CDDL HEADER END 21 | # 22 | 23 | import subprocess 24 | import threading 25 | from time_slider import util 26 | 27 | #SMF EXIT CODES 28 | SMF_EXIT_OK = 0 29 | SMF_EXIT_ERR_FATAL = 95 30 | SMF_EXIT_ERR_CONFIG = 96 31 | SMF_EXIT_MON_DEGRADE = 97 32 | SMF_EXIT_MON_OFFLINE = 98 33 | SMF_EXIT_ERR_NOSMF = 99 34 | SMF_EXIT_ERR_PERM = 100 35 | #SMF_EXIT_ERR_OTHER = non-zero 36 | 37 | 38 | # Commonly used command paths 39 | PFCMD = "/usr/bin/pfexec" 40 | SVCSCMD = "/usr/bin/svcs" 41 | SVCADMCMD = "/usr/sbin/svcadm" 42 | SVCCFGCMD = "/usr/sbin/svccfg" 43 | SVCPROPCMD = "/usr/bin/svcprop" 44 | 45 | 46 | class SMFInstance(Exception): 47 | 48 | def __init__(self, instanceName): 49 | self.instanceName = instanceName 50 | self.svcstate = self.get_service_state() 51 | self.svcdeps = self.get_service_dependencies() 52 | 53 | 54 | def get_service_dependencies(self): 55 | cmd = [SVCSCMD, "-H", "-o", "fmri", "-d", self.instanceName] 56 | outdata,errdata = util.run_command(cmd) 57 | result = outdata.rstrip().split("\n") 58 | return result 59 | 60 | def get_verbose(self): 61 | cmd = [SVCPROPCMD, "-c", "-p", \ 62 | DAEMONPROPGROUP + '/' + "verbose", \ 63 | self.instanceName] 64 | outdata,errdata = util.run_command(cmd) 65 | result = outdata.rstrip() 66 | if result == "true": 67 | return True 68 | else: 69 | return False 70 | 71 | def find_dependency_errors(self): 72 | errors = [] 73 | #FIXME - do this in one pass. 74 | for dep in self.svcdeps: 75 | cmd = [SVCSCMD, "-H", "-o", "state", dep] 76 | outdata,errdata = util.run_command(cmd) 77 | result = outdata.rstrip() 78 | if result != "online": 79 | errors.append("%s\t%s" % (result, dep)) 80 | return errors 81 | 82 | def get_service_state(self): 83 | cmd = [SVCSCMD, "-H", "-o", "state", self.instanceName] 84 | outdata,errdata = util.run_command(cmd) 85 | result = outdata.rstrip() 86 | return result 87 | 88 | def get_prop(self, propgroup, propname): 89 | cmd = [SVCPROPCMD, "-c", "-p", \ 90 | propgroup + '/' + propname,\ 91 | self.instanceName] 92 | outdata,errdata = util.run_command(cmd) 93 | result = outdata.rstrip() 94 | 95 | return result 96 | 97 | def set_prop(self, propgroup, propname, proptype, value): 98 | cmd = [PFCMD, SVCCFGCMD, "-s", self.instanceName, "setprop", \ 99 | propgroup + '/' + propname, "=", proptype + ":", \ 100 | value] 101 | util.run_command(cmd) 102 | self.refresh_service() 103 | 104 | def set_string_prop(self, propgroup, propname, value): 105 | cmd = [PFCMD, SVCCFGCMD, "-s", self.instanceName, "setprop", \ 106 | propgroup + '/' + propname, "=", "astring:", 107 | "\"%s\"" % (value)] 108 | util.run_command(cmd) 109 | self.refresh_service() 110 | 111 | def set_boolean_prop(self, propgroup, propname, value): 112 | if value == True: 113 | strval = "true" 114 | else: 115 | strval = "false" 116 | self.set_prop(propgroup, propname, "boolean", strval) 117 | 118 | def set_integer_prop(self, propgroup, propname, value): 119 | self.set_prop(propgroup, propname, "integer", str(value)) 120 | 121 | def refresh_service(self): 122 | cmd = [PFCMD, SVCADMCMD, "refresh", self.instanceName] 123 | p = subprocess.Popen(cmd, close_fds=True) 124 | 125 | def disable_service (self): 126 | if self.svcstate == "disabled": 127 | return 128 | cmd = [PFCMD, SVCADMCMD, "disable", self.instanceName] 129 | p = subprocess.Popen(cmd, close_fds=True) 130 | self.svcstate = self.get_service_state() 131 | 132 | def enable_service (self): 133 | if (self.svcstate == "online" or self.svcstate == "degraded"): 134 | return 135 | cmd = [PFCMD, SVCADMCMD, "enable", self.instanceName] 136 | p = subprocess.Popen(cmd, close_fds=True) 137 | self.svcstate = self.get_service_state() 138 | 139 | def mark_maintenance (self): 140 | cmd = [SVCADMCMD, "mark", "maintenance", self.instanceName] 141 | subprocess.Popen(cmd, close_fds=True) 142 | 143 | def __str__(self): 144 | ret = "SMF Instance:\n" +\ 145 | "\tName:\t\t\t%s\n" % (self.instanceName) +\ 146 | "\tState:\t\t\t%s\n" % (self.svcstate) 147 | return ret 148 | 149 | 150 | if __name__ == "__main__": 151 | S = SMFInstance('svc:/application/time-slider') 152 | print(S) 153 | 154 | -------------------------------------------------------------------------------- /usr/share/time-slider/lib/time_slider/snapnowui.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python3.5 2 | # 3 | # CDDL HEADER START 4 | # 5 | # The contents of this file are subject to the terms of the 6 | # Common Development and Distribution License (the "License"). 7 | # You may not use this file except in compliance with the License. 8 | # 9 | # You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 10 | # or http://www.opensolaris.org/os/licensing. 11 | # See the License for the specific language governing permissions 12 | # and limitations under the License. 13 | # 14 | # When distributing Covered Code, include this CDDL HEADER in each 15 | # file and include the License file at usr/src/OPENSOLARIS.LICENSE. 16 | # If applicable, add the following below this CDDL HEADER, with the 17 | # fields enclosed by brackets "[]" replaced with your own identifying 18 | # information: Portions Copyright [yyyy] [name of copyright owner] 19 | # 20 | # CDDL HEADER END 21 | # 22 | 23 | import sys 24 | import os 25 | import datetime 26 | import getopt 27 | import string 28 | import subprocess 29 | 30 | try: 31 | import gi 32 | gi.require_version('Gtk', '3.0') 33 | from gi.repository import Gtk 34 | except: 35 | sys.exit(1) 36 | 37 | # here we define the path constants so that other modules can use it. 38 | # this allows us to get access to the shared files without having to 39 | # know the actual location, we just use the location of the current 40 | # file and use paths relative to that. 41 | SHARED_FILES = os.path.abspath(os.path.join(os.path.dirname(__file__), 42 | os.path.pardir, 43 | os.path.pardir)) 44 | LOCALE_PATH = os.path.join('/usr', 'share', 'locale') 45 | RESOURCE_PATH = os.path.join(SHARED_FILES, 'res') 46 | 47 | # the name of the gettext domain. because we have our translation files 48 | # not in a global folder this doesn't really matter, setting it to the 49 | # application name is a good idea tough. 50 | GETTEXT_DOMAIN = 'time-slider' 51 | 52 | from time_slider import zfs 53 | from time_slider.rbac import RBACprofile 54 | 55 | class SnapshotNowDialog: 56 | 57 | def __init__(self, dir_path, zfs_fs): 58 | self.dir_path = dir_path 59 | self.zfs_fs = zfs_fs 60 | self.builder = Gtk.Builder() 61 | 62 | self.builder.set_translation_domain(GETTEXT_DOMAIN) 63 | 64 | self.builder.add_from_file("%s/../../ui/time-slider-snapshot.ui" \ 65 | % (os.path.dirname(__file__))) 66 | self.dialog = self.builder.get_object("dialog") 67 | self.dir_label = self.builder.get_object("dir_label") 68 | self.snap_name_entry = self.builder.get_object("snapshot_name_entry") 69 | # signal dictionary 70 | dic = {"on_closebutton_clicked" : Gtk.main_quit, 71 | "on_window_delete_event" : Gtk.main_quit, 72 | "on_cancel_clicked" : Gtk.main_quit, 73 | "on_ok_clicked" : self.__on_ok_clicked} 74 | self.builder.connect_signals(dic) 75 | 76 | 77 | self.snap_name_entry.connect("activate", self.__on_entry_activate, 0) 78 | 79 | self.dir_label.set_text(self.dir_path) 80 | self.snap_name_entry.set_text("my-snapshot-%s" % datetime.datetime.now().strftime("%Y-%m-%d_%Hh%M:%S")) 81 | 82 | self.dialog.show () 83 | 84 | def validate_name (self, name, showErrorDialog=False): 85 | #check name validity 86 | # from http://src.opensolaris.org/source/xref/onnv/onnv-gate/usr/src/common/zfs/zfs_namecheck.c#dataset_namecheck 87 | # http://src.opensolaris.org/source/xref/onnv/onnv-gate/usr/src/common/zfs/zfs_namecheck.c#valid_char 88 | 89 | invalid = False 90 | _validchars = string.ascii_letters + string.digits + \ 91 | "-_.:" 92 | 93 | valid_name = "" 94 | 95 | for c in name: 96 | if c in _validchars: 97 | valid_name = valid_name + c 98 | else: 99 | invalid = True 100 | 101 | if invalid and showErrorDialog: 102 | dialog = Gtk.MessageDialog(None, 103 | 0, 104 | Gtk.MessageType.ERROR, 105 | Gtk.ButtonsType.CLOSE, 106 | _("Invalid characters in snapshot name")) 107 | dialog.set_title (_("Error")) 108 | dialog.format_secondary_text(_("Allowed characters for snapshot names are :\n" 109 | "[a-z][A-Z][0-9][-_.:\n" 110 | "All invalid characters will be removed\n")) 111 | dialog.run () 112 | dialog.destroy () 113 | return valid_name 114 | 115 | 116 | def __on_entry_activate (self, widget, none): 117 | self.snap_name_entry.set_text (self.validate_name (self.snap_name_entry.get_text(), True)) 118 | return 119 | 120 | 121 | def __on_ok_clicked (self, widget): 122 | err = 0 123 | error = "" 124 | name = self.snap_name_entry.get_text() 125 | valid_name = self.validate_name (name, True) 126 | if name == valid_name: 127 | snap_name = "%s@%s" % (self.zfs_fs, self.validate_name (self.snap_name_entry.get_text())) 128 | cmd = [ "pfexec", "/usr/sbin/zfs", "snapshot", snap_name ] 129 | try: 130 | p = subprocess.Popen(cmd, 131 | stdout=subprocess.PIPE, 132 | stderr=subprocess.PIPE, 133 | close_fds=True, 134 | universal_newlines=True) 135 | outdata,errdata = p.communicate() 136 | err = p.wait() 137 | except OSError as message: 138 | error = str(message) 139 | 140 | if (err != 0): 141 | error = errdata 142 | 143 | if len(error) > 0: 144 | dialog = Gtk.MessageDialog(None, 145 | 0, 146 | Gtk.MessageType.ERROR, 147 | Gtk.ButtonsType.CLOSE, 148 | _("Error occured while creating the snapshot")) 149 | dialog.set_title (_("Error")) 150 | dialog.format_secondary_text(error) 151 | dialog.run () 152 | sys.exit(1) 153 | else: 154 | dialog = Gtk.MessageDialog(None, 155 | 0, 156 | Gtk.MessageType.INFO, 157 | Gtk.ButtonsType.CLOSE, 158 | _("Snapshot created successfully")) 159 | dialog.set_title (_("Success")) 160 | dialog.format_secondary_text(_("A snapshot of zfs filesystem %(zfs_fs)s\n" 161 | "named %(valid_name)s\n" 162 | "has been created.\n") % 163 | { "zfs_fs" : self.zfs_fs, "valid_name" : valid_name}) 164 | dialog.run () 165 | sys.exit(0) 166 | else: 167 | self.snap_name_entry.set_text (valid_name) 168 | 169 | def main(argv): 170 | try: 171 | opts,args = getopt.getopt(sys.argv[1:], "", []) 172 | except getopt.GetoptError: 173 | sys.exit(2) 174 | #FIXME 175 | #check for 2 args here we assume the arguments are correct 176 | if len(args) != 2: 177 | dialog = Gtk.MessageDialog(None, 178 | 0, 179 | Gtk.MessageType.ERROR, 180 | Gtk.ButtonsType.CLOSE, 181 | _("Invalid arguments count.")) 182 | dialog.set_title (_("Error")) 183 | dialog.format_secondary_text(_("Snapshot Now requires" 184 | " 2 arguments :\n- The path of the " 185 | "directory to be snapshotted.\n" 186 | "- The zfs filesystem corresponding " 187 | "to this directory.")) 188 | dialog.run() 189 | sys.exit (2) 190 | 191 | rbacp = RBACprofile() 192 | # The user security attributes checked are the following: 193 | # 1. The "Primary Administrator" role 194 | # 4. The "ZFS Files System Management" profile. 195 | # 196 | # Valid combinations of the above are: 197 | # - 1 or 4 198 | # Note that an effective UID=0 will match any profile search so 199 | # no need to check it explicitly. 200 | if rbacp.has_profile("ZFS File System Management"): 201 | manager = SnapshotNowDialog(args[0],args[1]) 202 | Gtk.main() 203 | elif os.path.exists(argv) and os.path.exists("/usr/bin/gksu"): 204 | # Run via gksu, which will prompt for the root password 205 | newargs = ["gksu", argv] 206 | for arg in args: 207 | newargs.append(arg) 208 | os.execv("/usr/bin/gksu", newargs) 209 | # Shouldn't reach this point 210 | sys.exit(1) 211 | else: 212 | dialog = Gtk.MessageDialog(None, 213 | 0, 214 | Gtk.MessageType.ERROR, 215 | Gtk.ButtonsType.CLOSE, 216 | _("Insufficient Priviliges")) 217 | dialog.set_title (_("Error")) 218 | dialog.format_secondary_text(_("Snapshot Now requires " 219 | "administrative privileges to run. " 220 | "You have not been assigned the necessary" 221 | "administrative priviliges." 222 | "\n\nConsult your system administrator ")) 223 | dialog.run() 224 | print(argv + " is not a valid executable path") 225 | sys.exit(1) 226 | 227 | if __name__ == "__main__": 228 | main(sys.argv[1:]) 229 | -------------------------------------------------------------------------------- /usr/share/time-slider/lib/time_slider/timeslidersmf.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python3.5 2 | # 3 | # CDDL HEADER START 4 | # 5 | # The contents of this file are subject to the terms of the 6 | # Common Development and Distribution License (the "License"). 7 | # You may not use this file except in compliance with the License. 8 | # 9 | # You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 10 | # or http://www.opensolaris.org/os/licensing. 11 | # See the License for the specific language governing permissions 12 | # and limitations under the License. 13 | # 14 | # When distributing Covered Code, include this CDDL HEADER in each 15 | # file and include the License file at usr/src/OPENSOLARIS.LICENSE. 16 | # If applicable, add the following below this CDDL HEADER, with the 17 | # fields enclosed by brackets "[]" replaced with your own identifying 18 | # information: Portions Copyright [yyyy] [name of copyright owner] 19 | # 20 | # CDDL HEADER END 21 | # 22 | 23 | import subprocess 24 | import threading 25 | from time_slider import smf 26 | from time_slider import util 27 | 28 | #SMF EXIT CODES 29 | SMF_EXIT_OK = 0 30 | SMF_EXIT_ERR_FATAL = 95 31 | SMF_EXIT_ERR_CONFIG = 96 32 | SMF_EXIT_MON_DEGRADE = 97 33 | SMF_EXIT_MON_OFFLINE = 98 34 | SMF_EXIT_ERR_NOSMF = 99 35 | SMF_EXIT_ERR_PERM = 100 36 | #SMF_EXIT_ERR_OTHER = non-zero 37 | 38 | cleanupTypes = ("warning", "critical", "emergency") 39 | 40 | SMFNAME = 'svc:/application/time-slider' 41 | ZFSPROPGROUP = "zfs" 42 | ZPOOLPROPGROUP = "zpool" 43 | DAEMONPROPGROUP = "daemon" 44 | 45 | # Commonly used command paths 46 | PFCMD = "/usr/bin/pfexec" 47 | SVCSCMD = "/usr/bin/svcs" 48 | SVCADMCMD = "/usr/sbin/svcadm" 49 | SVCCFGCMD = "/usr/sbin/svccfg" 50 | SVCPROPCMD = "/usr/bin/svcprop" 51 | 52 | 53 | class TimeSliderSMF(smf.SMFInstance): 54 | 55 | def __init__(self, instanceName = SMFNAME): 56 | smf.SMFInstance.__init__(self, instanceName) 57 | self._cleanupLevels = {} 58 | self._cleanupLevelsLock = threading.Lock() 59 | 60 | def get_keep_empties(self): 61 | if self.get_prop(ZFSPROPGROUP, "keep-empties") == "true": 62 | return True 63 | else: 64 | return False 65 | 66 | def is_custom_selection(self): 67 | value = self.get_prop(ZFSPROPGROUP, "custom-selection") 68 | if value == "true": 69 | return True 70 | else: 71 | return False 72 | 73 | def get_separator(self): 74 | result = self.get_prop(ZFSPROPGROUP, "sep") 75 | if len(result) != 1: 76 | raise ValueError("zfs/sep must be a single character length") 77 | return result 78 | 79 | def get_remedial_cleanup(self): 80 | value = self.get_prop(ZPOOLPROPGROUP, "remedial-cleanup") 81 | if value == "false": 82 | return False 83 | else: 84 | return True 85 | 86 | def get_cleanup_level(self, cleanupType): 87 | if cleanupType not in cleanupTypes: 88 | raise ValueError("\'%s\' is not a valid cleanup type" % \ 89 | (cleanupType)) 90 | self._cleanupLevelsLock.acquire() 91 | value = self.get_prop(ZPOOLPROPGROUP, "%s-level" % (cleanupType)) 92 | self._cleanupLevelsLock.release() 93 | return int(value) 94 | 95 | def set_cleanup_level(self, cleanupType, level): 96 | if cleanupType not in cleanupTypes: 97 | raise ValueError("\'%s\' is not a valid cleanup type" % \ 98 | (cleanupType)) 99 | if level < 0: 100 | raise ValueError("Cleanup level value can not not be negative") 101 | if cleanupType == "warning" and \ 102 | level > self.get_cleanup_level("critical"): 103 | raise ValueError("Warning cleanup level value can not exceed " + \ 104 | "critical cleanup level value") 105 | elif cleanupType == "critical" and \ 106 | level > self.get_cleanup_level("emergency"): 107 | raise ValueError("Critical cleanup level value can not " + \ 108 | "exceed emergency cleanup level value") 109 | elif level > 100: # Emergency type value 110 | raise ValueError("Cleanup level value can not exceed 100") 111 | 112 | self._cleanupLevelsLock.acquire() 113 | propname = "%s-level" % (cleanupType) 114 | self.set_integer_prop(ZPOOLPROPGROUP, propname, level) 115 | self._cleanupLevels[cleanupType] = level 116 | self._cleanupLevelsLock.release() 117 | self.refresh_service() 118 | 119 | def set_custom_selection(self, value): 120 | self.set_boolean_prop(ZFSPROPGROUP, "custom-selection", value) 121 | self.refresh_service() 122 | 123 | def get_verbose(self): 124 | value = self.get_prop(DAEMONPROPGROUP, "verbose") 125 | if value == "true": 126 | return True 127 | else: 128 | return False 129 | 130 | def __eq__(self, other): 131 | if self.fs_name == other.fs_name and \ 132 | self.interval == other.interval and \ 133 | self.period == other.period: 134 | return True 135 | return False 136 | 137 | def __str__(self): 138 | ret = "SMF Instance:\n" +\ 139 | "\tName:\t\t\t%s\n" % (self.instanceName) +\ 140 | "\tState:\t\t\t%s\n" % (self.svcstate) + \ 141 | "\tVerbose:\t\t%s\n" % str(self.get_verbose()) + \ 142 | "\tCustom Selction:\t%s\n" % str(self.is_custom_selection()) +\ 143 | "\tKeep Empties:\t\t%s\n" % str(self.get_keep_empties()) +\ 144 | "\tWarning Level:\t\t%d\n" % (self.get_cleanup_level("warning")) + \ 145 | "\tCritical Level:\t\t%d\n" % (self.get_cleanup_level("critical")) + \ 146 | "\tEmergency Level:\t%d\n" % (self.get_cleanup_level("emergency")) + \ 147 | "\tSeparator Char:\t\t\'%s\'" % (self.get_separator()) 148 | return ret 149 | 150 | 151 | if __name__ == "__main__": 152 | S = TimeSliderSMF('svc:/application/time-slider') 153 | print(S) 154 | 155 | -------------------------------------------------------------------------------- /usr/share/time-slider/lib/time_slider/util.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python3.5 2 | # CDDL HEADER START 3 | # 4 | # The contents of this file are subject to the terms of the 5 | # Common Development and Distribution License (the "License"). 6 | # You may not use this file except in compliance with the License. 7 | # 8 | # You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 9 | # or http://www.opensolaris.org/os/licensing. 10 | # See the License for the specific language governing permissions 11 | # and limitations under the License. 12 | # 13 | # When distributing Covered Code, include this CDDL HEADER in each 14 | # file and include the License file at usr/src/OPENSOLARIS.LICENSE. 15 | # If applicable, add the following below this CDDL HEADER, with the 16 | # fields enclosed by brackets "[]" replaced with your own identifying 17 | # information: Portions Copyright [yyyy] [name of copyright owner] 18 | # 19 | # CDDL HEADER END 20 | # 21 | 22 | import os 23 | import subprocess 24 | import sys 25 | import syslog 26 | import math 27 | from gi.repository import Gio, GLib 28 | 29 | def run_command(command, raise_on_try=True): 30 | """ 31 | Wrapper function around subprocess.Popen 32 | Returns a tuple of standard out and stander error. 33 | Throws a RunTimeError if the command failed to execute or 34 | if the command returns a non-zero exit status. 35 | """ 36 | try: 37 | p = subprocess.Popen(command, 38 | stdout=subprocess.PIPE, 39 | stderr=subprocess.PIPE, 40 | close_fds=True, 41 | universal_newlines=True) 42 | outdata,errdata = p.communicate() 43 | err = p.wait() 44 | except OSError as message: 45 | raise RuntimeError("%s subprocess error:\n %s" % \ 46 | (command, str(message))) 47 | if err != 0 and raise_on_try: 48 | raise RuntimeError('%s failed with exit code %d\n%s' % \ 49 | (str(command), err, errdata)) 50 | return outdata,errdata 51 | 52 | def debug(message, verbose): 53 | """ 54 | Prints message out to standard error and syslog if 55 | verbose = True. 56 | Note that the caller needs to first establish a syslog 57 | context using syslog.openlog() 58 | """ 59 | if verbose: 60 | syslog.syslog(syslog.LOG_NOTICE, message + '\n') 61 | sys.stderr.write(message + '\n') 62 | 63 | def log_error(loglevel, message): 64 | """ 65 | Trivial syslog wrapper that also outputs to stderr 66 | Requires caller to have first opened a syslog session 67 | using syslog.openlog() 68 | """ 69 | syslog.syslog(loglevel, message + '\n') 70 | sys.stderr.write(message + '\n') 71 | 72 | def get_filesystem_capacity(path): 73 | """Returns filesystem space usage of path as an integer percentage of 74 | the entire capacity of path. 75 | """ 76 | if not os.path.exists(path): 77 | raise ValueError("%s is a non-existent path" % path) 78 | f = os.statvfs(path) 79 | 80 | unavailBlocks = f.f_blocks - f.f_bavail 81 | capacity = int(math.ceil(100 * (unavailBlocks / float(f.f_blocks)))) 82 | 83 | return capacity 84 | 85 | def get_available_size(path): 86 | """Returns the available space in bytes under path""" 87 | if not os.path.exists(path): 88 | raise ValueError("%s is a non-existent path" % path) 89 | f = os.statvfs(path) 90 | free = int(f.f_bavail * f.f_frsize) 91 | 92 | return free 93 | 94 | def get_used_size(path): 95 | """Returns the used space in bytes of fileystem associated 96 | with path""" 97 | if not os.path.exists(path): 98 | raise ValueError("%s is a non-existent path" % path) 99 | f = os.statvfs(path) 100 | 101 | unavailBlocks = f.f_blocks - f.f_bavail 102 | used = int(unavailBlocks * f.f_frsize) 103 | 104 | return used 105 | 106 | def get_total_size(path): 107 | """Returns the total storage space in bytes of fileystem 108 | associated with path""" 109 | if not os.path.exists(path): 110 | raise ValueError("%s is a non-existent path" % path) 111 | f = os.statvfs(path) 112 | total = int(f.f_blocks * f.f_frsize) 113 | 114 | return total 115 | 116 | def path_to_volume(path): 117 | """ 118 | Tries to map a given path name to a gio Volume and 119 | returns the gio.Volume object the enclosing 120 | volume. 121 | If it fails to find an enclosing volume it returns 122 | None 123 | """ 124 | gFile = Gio.File.new_for_path(path) 125 | try: 126 | mount = gFile.find_enclosing_mount() 127 | except GLib.Error: 128 | return None 129 | else: 130 | if mount != None: 131 | volume = mount.get_volume() 132 | return volume 133 | return None 134 | -------------------------------------------------------------------------------- /usr/share/time-slider/ui/time-slider-snapshot.ui: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 400 6 | 5 7 | Take a snapshot now 8 | True 9 | GTK_WIN_POS_CENTER_ON_PARENT 10 | True 11 | time-slider-setup 12 | GDK_WINDOW_TYPE_HINT_DIALOG 13 | 14 | 15 | 16 | True 17 | 2 18 | 19 | 20 | True 21 | 2 22 | 23 | 24 | True 25 | gtk-dialog-question 26 | 6 27 | 28 | 29 | 30 | 31 | True 32 | 3 33 | 2 34 | 35 | 36 | 37 | 38 | 39 | True 40 | True 41 | 42 | 43 | 2 44 | 2 45 | 3 46 | 47 | 48 | 49 | 50 | True 51 | 0 52 | 53 | 54 | 1 55 | 2 56 | 57 | 58 | 59 | 60 | True 61 | 0 62 | Take a snapshot of 63 | 64 | 65 | GTK_FILL 66 | GTK_FILL 67 | 68 | 69 | 70 | 71 | True 72 | 0 73 | with the name : 74 | 75 | 76 | 1 77 | 2 78 | GTK_FILL 79 | GTK_FILL 80 | 81 | 82 | 83 | 84 | 1 85 | 86 | 87 | 88 | 89 | 3 90 | 1 91 | 92 | 93 | 94 | 95 | True 96 | GTK_BUTTONBOX_END 97 | 98 | 99 | True 100 | True 101 | True 102 | gtk-cancel 103 | True 104 | 105 | 106 | 107 | 108 | 109 | True 110 | True 111 | True 112 | gtk-ok 113 | True 114 | 115 | 116 | 117 | 1 118 | 119 | 120 | 121 | 122 | False 123 | GTK_PACK_END 124 | 125 | 126 | 127 | 128 | 129 | confirm_cancel 130 | confirm_snapshot 131 | 132 | 133 | 134 | -------------------------------------------------------------------------------- /usr/share/time-slider/ui/time-slider-version.ui: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 500 6 | 400 7 | True 8 | 5 9 | Time Slider File Version Explorer 10 | GTK_WIN_POS_CENTER_ON_PARENT 11 | time-slider-setup 12 | 13 | 14 | 15 | True 16 | 12 17 | 18 | 19 | True 20 | 21 | 22 | True 23 | 0 24 | 25 | 26 | True 27 | 5 28 | 5 29 | 5 30 | 31 | 32 | True 33 | 34 | 35 | True 36 | True 37 | True 38 | Open the current version of the file 39 | GTK_RELIEF_NONE 40 | 41 | 42 | 43 | True 44 | 24 45 | gtk-file 46 | 6 47 | 48 | 49 | 50 | 51 | False 52 | False 53 | 5 54 | 55 | 56 | 57 | 58 | True 59 | 3 60 | 2 61 | 62 | 63 | True 64 | 0 65 | last modified : 66 | 67 | 68 | 2 69 | 3 70 | 71 | 72 | 73 | 74 | True 75 | 0 76 | 19/01/09 77 | 78 | 79 | 1 80 | 2 81 | 2 82 | 3 83 | 84 | 85 | 86 | 87 | True 88 | 0 89 | size : 90 | 91 | 92 | 1 93 | 2 94 | 95 | 96 | 97 | 98 | True 99 | 0 100 | 10 Mo 101 | 102 | 103 | 1 104 | 2 105 | 1 106 | 2 107 | 108 | 109 | 110 | 111 | True 112 | 0 113 | filename 114 | 115 | 116 | 1 117 | 2 118 | 119 | 120 | 121 | 122 | True 123 | 0 124 | name : 125 | 126 | 127 | 128 | 129 | False 130 | False 131 | 1 132 | 133 | 134 | 135 | 136 | 137 | 138 | 139 | 140 | True 141 | <b>Current Version</b> 142 | True 143 | 144 | 145 | 146 | 147 | False 148 | False 149 | 4 150 | 151 | 152 | 153 | 154 | True 155 | 0 156 | 157 | 158 | True 159 | 5 160 | 5 161 | 5 162 | 5 163 | 164 | 165 | True 166 | 167 | 168 | True 169 | True 170 | Scanning for older versions 171 | 172 | 173 | False 174 | 175 | 176 | 177 | 178 | True 179 | True 180 | GTK_POLICY_AUTOMATIC 181 | GTK_POLICY_AUTOMATIC 182 | GTK_SHADOW_ETCHED_IN 183 | 184 | 185 | True 186 | True 187 | Double click to open the file 188 | True 189 | True 190 | 0 191 | True 192 | 193 | 194 | 195 | 196 | 197 | 198 | 1 199 | 200 | 201 | 202 | 203 | 204 | 205 | 206 | 207 | True 208 | <b>Older Versions</b> 209 | True 210 | 211 | 212 | 213 | 214 | 1 215 | 216 | 217 | 218 | 219 | 220 | 221 | True 222 | 6 223 | 18 224 | 225 | 226 | True 227 | False 228 | True 229 | True 230 | True 231 | Compare the current version and the selected older version 232 | 233 | 234 | 235 | True 236 | 237 | 238 | True 239 | gtk-print-preview 240 | 241 | 242 | 243 | 244 | True 245 | compare 246 | 247 | 248 | 1 249 | 250 | 251 | 252 | 253 | 254 | 255 | False 256 | False 257 | 258 | 259 | 260 | 261 | True 262 | 6 263 | GTK_BUTTONBOX_END 264 | 265 | 266 | 267 | 268 | 269 | True 270 | True 271 | True 272 | gtk-close 273 | True 274 | 275 | 276 | 277 | 1 278 | 279 | 280 | 281 | 282 | 1 283 | 284 | 285 | 286 | 287 | False 288 | False 289 | 1 290 | 291 | 292 | 293 | 294 | 295 | 296 | --------------------------------------------------------------------------------