Compare commits

...

No commits in common. "master" and "SUNW" have entirely different histories.
master ... SUNW

25 changed files with 2531 additions and 1143 deletions

1
.hgignore Normal file
View File

@ -0,0 +1 @@
proto

View File

@ -1,9 +0,0 @@
[ 1.2.5 (Unreleased) ]
* Start a changelog
* Accept PR#94 from aimileus/macos
- Replace --utc longform option with -u for macos compatibility
* Accept PR#107 from ArakniD/master
- Add optional label for snap removals
-- Jonathan Carter 2019-09-25, 15:24 SAST

339
COPYING
View File

@ -1,339 +0,0 @@
GNU GENERAL PUBLIC LICENSE
Version 2, June 1991
Copyright (C) 1989, 1991 Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
Everyone is permitted to copy and distribute verbatim copies
of this license document, but changing it is not allowed.
Preamble
The licenses for most software are designed to take away your
freedom to share and change it. By contrast, the GNU General Public
License is intended to guarantee your freedom to share and change free
software--to make sure the software is free for all its users. This
General Public License applies to most of the Free Software
Foundation's software and to any other program whose authors commit to
using it. (Some other Free Software Foundation software is covered by
the GNU Lesser General Public License instead.) You can apply it to
your programs, too.
When we speak of free software, we are referring to freedom, not
price. Our General Public Licenses are designed to make sure that you
have the freedom to distribute copies of free software (and charge for
this service if you wish), that you receive source code or can get it
if you want it, that you can change the software or use pieces of it
in new free programs; and that you know you can do these things.
To protect your rights, we need to make restrictions that forbid
anyone to deny you these rights or to ask you to surrender the rights.
These restrictions translate to certain responsibilities for you if you
distribute copies of the software, or if you modify it.
For example, if you distribute copies of such a program, whether
gratis or for a fee, you must give the recipients all the rights that
you have. You must make sure that they, too, receive or can get the
source code. And you must show them these terms so they know their
rights.
We protect your rights with two steps: (1) copyright the software, and
(2) offer you this license which gives you legal permission to copy,
distribute and/or modify the software.
Also, for each author's protection and ours, we want to make certain
that everyone understands that there is no warranty for this free
software. If the software is modified by someone else and passed on, we
want its recipients to know that what they have is not the original, so
that any problems introduced by others will not reflect on the original
authors' reputations.
Finally, any free program is threatened constantly by software
patents. We wish to avoid the danger that redistributors of a free
program will individually obtain patent licenses, in effect making the
program proprietary. To prevent this, we have made it clear that any
patent must be licensed for everyone's free use or not licensed at all.
The precise terms and conditions for copying, distribution and
modification follow.
GNU GENERAL PUBLIC LICENSE
TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
0. This License applies to any program or other work which contains
a notice placed by the copyright holder saying it may be distributed
under the terms of this General Public License. The "Program", below,
refers to any such program or work, and a "work based on the Program"
means either the Program or any derivative work under copyright law:
that is to say, a work containing the Program or a portion of it,
either verbatim or with modifications and/or translated into another
language. (Hereinafter, translation is included without limitation in
the term "modification".) Each licensee is addressed as "you".
Activities other than copying, distribution and modification are not
covered by this License; they are outside its scope. The act of
running the Program is not restricted, and the output from the Program
is covered only if its contents constitute a work based on the
Program (independent of having been made by running the Program).
Whether that is true depends on what the Program does.
1. You may copy and distribute verbatim copies of the Program's
source code as you receive it, in any medium, provided that you
conspicuously and appropriately publish on each copy an appropriate
copyright notice and disclaimer of warranty; keep intact all the
notices that refer to this License and to the absence of any warranty;
and give any other recipients of the Program a copy of this License
along with the Program.
You may charge a fee for the physical act of transferring a copy, and
you may at your option offer warranty protection in exchange for a fee.
2. You may modify your copy or copies of the Program or any portion
of it, thus forming a work based on the Program, and copy and
distribute such modifications or work under the terms of Section 1
above, provided that you also meet all of these conditions:
a) You must cause the modified files to carry prominent notices
stating that you changed the files and the date of any change.
b) You must cause any work that you distribute or publish, that in
whole or in part contains or is derived from the Program or any
part thereof, to be licensed as a whole at no charge to all third
parties under the terms of this License.
c) If the modified program normally reads commands interactively
when run, you must cause it, when started running for such
interactive use in the most ordinary way, to print or display an
announcement including an appropriate copyright notice and a
notice that there is no warranty (or else, saying that you provide
a warranty) and that users may redistribute the program under
these conditions, and telling the user how to view a copy of this
License. (Exception: if the Program itself is interactive but
does not normally print such an announcement, your work based on
the Program is not required to print an announcement.)
These requirements apply to the modified work as a whole. If
identifiable sections of that work are not derived from the Program,
and can be reasonably considered independent and separate works in
themselves, then this License, and its terms, do not apply to those
sections when you distribute them as separate works. But when you
distribute the same sections as part of a whole which is a work based
on the Program, the distribution of the whole must be on the terms of
this License, whose permissions for other licensees extend to the
entire whole, and thus to each and every part regardless of who wrote it.
Thus, it is not the intent of this section to claim rights or contest
your rights to work written entirely by you; rather, the intent is to
exercise the right to control the distribution of derivative or
collective works based on the Program.
In addition, mere aggregation of another work not based on the Program
with the Program (or with a work based on the Program) on a volume of
a storage or distribution medium does not bring the other work under
the scope of this License.
3. You may copy and distribute the Program (or a work based on it,
under Section 2) in object code or executable form under the terms of
Sections 1 and 2 above provided that you also do one of the following:
a) Accompany it with the complete corresponding machine-readable
source code, which must be distributed under the terms of Sections
1 and 2 above on a medium customarily used for software interchange; or,
b) Accompany it with a written offer, valid for at least three
years, to give any third party, for a charge no more than your
cost of physically performing source distribution, a complete
machine-readable copy of the corresponding source code, to be
distributed under the terms of Sections 1 and 2 above on a medium
customarily used for software interchange; or,
c) Accompany it with the information you received as to the offer
to distribute corresponding source code. (This alternative is
allowed only for noncommercial distribution and only if you
received the program in object code or executable form with such
an offer, in accord with Subsection b above.)
The source code for a work means the preferred form of the work for
making modifications to it. For an executable work, complete source
code means all the source code for all modules it contains, plus any
associated interface definition files, plus the scripts used to
control compilation and installation of the executable. However, as a
special exception, the source code distributed need not include
anything that is normally distributed (in either source or binary
form) with the major components (compiler, kernel, and so on) of the
operating system on which the executable runs, unless that component
itself accompanies the executable.
If distribution of executable or object code is made by offering
access to copy from a designated place, then offering equivalent
access to copy the source code from the same place counts as
distribution of the source code, even though third parties are not
compelled to copy the source along with the object code.
4. You may not copy, modify, sublicense, or distribute the Program
except as expressly provided under this License. Any attempt
otherwise to copy, modify, sublicense or distribute the Program is
void, and will automatically terminate your rights under this License.
However, parties who have received copies, or rights, from you under
this License will not have their licenses terminated so long as such
parties remain in full compliance.
5. You are not required to accept this License, since you have not
signed it. However, nothing else grants you permission to modify or
distribute the Program or its derivative works. These actions are
prohibited by law if you do not accept this License. Therefore, by
modifying or distributing the Program (or any work based on the
Program), you indicate your acceptance of this License to do so, and
all its terms and conditions for copying, distributing or modifying
the Program or works based on it.
6. Each time you redistribute the Program (or any work based on the
Program), the recipient automatically receives a license from the
original licensor to copy, distribute or modify the Program subject to
these terms and conditions. You may not impose any further
restrictions on the recipients' exercise of the rights granted herein.
You are not responsible for enforcing compliance by third parties to
this License.
7. If, as a consequence of a court judgment or allegation of patent
infringement or for any other reason (not limited to patent issues),
conditions are imposed on you (whether by court order, agreement or
otherwise) that contradict the conditions of this License, they do not
excuse you from the conditions of this License. If you cannot
distribute so as to satisfy simultaneously your obligations under this
License and any other pertinent obligations, then as a consequence you
may not distribute the Program at all. For example, if a patent
license would not permit royalty-free redistribution of the Program by
all those who receive copies directly or indirectly through you, then
the only way you could satisfy both it and this License would be to
refrain entirely from distribution of the Program.
If any portion of this section is held invalid or unenforceable under
any particular circumstance, the balance of the section is intended to
apply and the section as a whole is intended to apply in other
circumstances.
It is not the purpose of this section to induce you to infringe any
patents or other property right claims or to contest validity of any
such claims; this section has the sole purpose of protecting the
integrity of the free software distribution system, which is
implemented by public license practices. Many people have made
generous contributions to the wide range of software distributed
through that system in reliance on consistent application of that
system; it is up to the author/donor to decide if he or she is willing
to distribute software through any other system and a licensee cannot
impose that choice.
This section is intended to make thoroughly clear what is believed to
be a consequence of the rest of this License.
8. If the distribution and/or use of the Program is restricted in
certain countries either by patents or by copyrighted interfaces, the
original copyright holder who places the Program under this License
may add an explicit geographical distribution limitation excluding
those countries, so that distribution is permitted only in or among
countries not thus excluded. In such case, this License incorporates
the limitation as if written in the body of this License.
9. The Free Software Foundation may publish revised and/or new versions
of the General Public License from time to time. Such new versions will
be similar in spirit to the present version, but may differ in detail to
address new problems or concerns.
Each version is given a distinguishing version number. If the Program
specifies a version number of this License which applies to it and "any
later version", you have the option of following the terms and conditions
either of that version or of any later version published by the Free
Software Foundation. If the Program does not specify a version number of
this License, you may choose any version ever published by the Free Software
Foundation.
10. If you wish to incorporate parts of the Program into other free
programs whose distribution conditions are different, write to the author
to ask for permission. For software which is copyrighted by the Free
Software Foundation, write to the Free Software Foundation; we sometimes
make exceptions for this. Our decision will be guided by the two goals
of preserving the free status of all derivatives of our free software and
of promoting the sharing and reuse of software generally.
NO WARRANTY
11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS
TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE
PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
REPAIR OR CORRECTION.
12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
POSSIBILITY OF SUCH DAMAGES.
END OF TERMS AND CONDITIONS
How to Apply These Terms to Your New Programs
If you develop a new program, and you want it to be of the greatest
possible use to the public, the best way to achieve this is to make it
free software which everyone can redistribute and change under these terms.
To do so, attach the following notices to the program. It is safest
to attach them to the start of each source file to most effectively
convey the exclusion of warranty; and each file should have at least
the "copyright" line and a pointer to where the full notice is found.
<one line to give the program's name and a brief idea of what it does.>
Copyright (C) <year> <name of author>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License along
with this program; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
Also add information on how to contact you by electronic and paper mail.
If the program is interactive, make it output a short notice like this
when it starts in an interactive mode:
Gnomovision version 69, Copyright (C) year name of author
Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
This is free software, and you are welcome to redistribute it
under certain conditions; type `show c' for details.
The hypothetical commands `show w' and `show c' should show the appropriate
parts of the General Public License. Of course, the commands you use may
be called something other than `show w' and `show c'; they could even be
mouse-clicks or menu items--whatever suits your program.
You should also get your employer (if you work as a programmer) or your
school, if any, to sign a "copyright disclaimer" for the program, if
necessary. Here is a sample; alter the names:
Yoyodyne, Inc., hereby disclaims all copyright interest in the program
`Gnomovision' (which makes passes at compilers) written by James Hacker.
<signature of Ty Coon>, 1 April 1989
Ty Coon, President of Vice
This General Public License does not permit incorporating your program into
proprietary programs. If your program is a subroutine library, you may
consider it more useful to permit linking proprietary applications with the
library. If this is what you want to do, use the GNU Lesser General
Public License instead of this License.

95
Changelog Normal file
View File

@ -0,0 +1,95 @@
0.12
* Add event-based snapshots
* Add support to change the separator character in snapshot names
- set the default value of "zfs/sep" to "_"
- useful for CIFs clients that previously choked on colons in snapshot names
* Improved shutdown speed via http://blogs.sun.com/dp/entry/speeding_to_a_halt
* Add support to allow the user disable auto-snapshots of new pools
* Bugfix to allow snapshots of datasets with spaces in their names
* Bugfix to properly deal with namespace clashes in dataset names
* Exported $LAST_SNAP and $PREV_SNAP variables when performing backups
- useful to things like the "zfs-backup-to-s3" project on kenai.com
0.11
* Add RBAC support
- the service now runs under a zfssnap role
- service start/stop logs stay under /var/svc/log
- other log messages sent to syslog
* Add a 'zfs/interval' property value 'none' which doesn't use cron
* Add a cache of svcprops to the method script
* Add a com.sun:auto-snapshot user property used by all instances,
com.sun:auto-snapshot:$LABEL takes precedence
* Remove the seconds field of the snapshot name - it's not needed
* Changed the way // works with recursive snapshots - ignore
snapshot-children, and instead automatically determine when we can take
recursive snapshots based on which datasets have the zfs user properties
* Set avoidscrub to false by default (bug was fixed in in nv_94)
* Bugfix from Dan - Volumes are datasets too
* Automatically snapshot everything by setting com.sun:auto-snapshot=true
on startup. (this gets done on all top level datasets - an existing
property set to false on the top level dataset overrides this)
* Check for missed snapshots on startup
* Clean up shell style
* Clean up preremove script
* Remove the bundled GUI applications
* Tag the method script with the current changeset during the build
* Write this Changelog
0.10
* Bugfix from Reid Spencer - Tim can't spell "RECURSIVE" (hah, I can now!:-)
* Bugfix from Breandan Dezendorf - Labelled snapshots not being destroyed correctly
* Ability to avoid taking snapshots when pool scrubs are in progress
0.9
* SVR4 packaging (with ugly postinstall/preremove scripts)
* Add some canned instances for frequent,daily,hourly,weekly,monthly snapshots
* Add 'zfs/fs-name' '//' property value
* Change the timeout for the start/stop methods to infinity
* Allow power users to avoid having ':' in snapshot names via $SEP
* Add GUI utilities and some basic GNOME integration
0.8
* Bugfix from Dick Davies - fencepost error in crontab entries
* Add locking around crontab updates
* Add zfs/verbose feature for more logging if you really need it
0.7
* Bugfix from Bill Sommerfeld - make less logging noise
* Add logging to syslog
0.6
* Option to take backups using zfs send after snapshots are taken
* Allow us to have multiple schedules per filesystem (add labels)
* Better fault handling - drop SMF instance to 'maintenance' if stuff goes wrong
0.5
* Bugfix from Joe Little - recursive snapshots weren't respecting retention limits
0.4
* Make zenity gui work on s10u2
* Clean up the logic that the gui uses to name different instances
0.3
* Add snapshot retention limits
0.2
* Fix the gui to show a different string when changing the interval
* Fix a bug where seperate instances were removing each other's cron jobs
0.1
* Take periodic snapshots using cron and SMF
* Snapshot children
* Add zenity gui to create SMF manifests

View File

@ -1,28 +1,33 @@
PREFIX := /usr/local
DIR = \
`basename ${PWD}`
ZFS_AUTO_SNAPSHOT_CHANGESET = \
`hg identify`
all:
pkg: clean
mkdir -p proto
find src | cpio -pvdum proto
# we tag the method script during the build, which will
# only happen if we're building from the original hg source,
# not from the dist tarball - see the dist: target.
cat src/lib/svc/method/zfs-auto-snapshot | sed -e "s/~ZFS_AUTO_SNAPSHOT_CHANGESET~/${ZFS_AUTO_SNAPSHOT_CHANGESET}/g" > proto/src/lib/svc/method/zfs-auto-snapshot
pkgmk -f proto/src/prototype -p `uname -n``date +%Y%m%d%H%M%S` -d proto -r proto/src
install:
install -d $(DESTDIR)/etc/cron.d
install -d $(DESTDIR)/etc/cron.daily
install -d $(DESTDIR)/etc/cron.hourly
install -d $(DESTDIR)/etc/cron.weekly
install -d $(DESTDIR)/etc/cron.monthly
install -m 0644 etc/zfs-auto-snapshot.cron.frequent $(DESTDIR)/etc/cron.d/zfs-auto-snapshot
install etc/zfs-auto-snapshot.cron.hourly $(DESTDIR)/etc/cron.hourly/zfs-auto-snapshot
install etc/zfs-auto-snapshot.cron.daily $(DESTDIR)/etc/cron.daily/zfs-auto-snapshot
install etc/zfs-auto-snapshot.cron.weekly $(DESTDIR)/etc/cron.weekly/zfs-auto-snapshot
install etc/zfs-auto-snapshot.cron.monthly $(DESTDIR)/etc/cron.monthly/zfs-auto-snapshot
install -d $(DESTDIR)$(PREFIX)/share/man/man8
install -m 0644 src/zfs-auto-snapshot.8 $(DESTDIR)$(PREFIX)/share/man/man8/zfs-auto-snapshot.8
install -d $(DESTDIR)$(PREFIX)/sbin
install src/zfs-auto-snapshot.sh $(DESTDIR)$(PREFIX)/sbin/zfs-auto-snapshot
clean:
rm -rf proto/*
if [ -d proto ] ; then \
rmdir proto ; \
fi
uninstall:
rm $(DESTDIR)/etc/cron.d/zfs-auto-snapshot
rm $(DESTDIR)/etc/cron.hourly/zfs-auto-snapshot
rm $(DESTDIR)/etc/cron.daily/zfs-auto-snapshot
rm $(DESTDIR)/etc/cron.weekly/zfs-auto-snapshot
rm $(DESTDIR)/etc/cron.monthly/zfs-auto-snapshot
rm $(DESTDIR)$(PREFIX)/share/man/man8/zfs-auto-snapshot.8
rm $(DESTDIR)$(PREFIX)/sbin/zfs-auto-snapshot
dist: clean
# save off a copy of the method script before tagging it
cp src/lib/svc/method/zfs-auto-snapshot zfs-auto-snapshot.src
cat src/lib/svc/method/zfs-auto-snapshot | sed -e "s/~ZFS_AUTO_SNAPSHOT_CHANGESET~/${ZFS_AUTO_SNAPSHOT_CHANGESET}/g" > src/lib/svc/method/tagged-method-script
mv src/lib/svc/method/tagged-method-script src/lib/svc/method/zfs-auto-snapshot
grep "zfs-auto-snapshot changeset" src/lib/svc/method/zfs-auto-snapshot
tar cf ${DIR}.tar -C .. ${DIR}/Changelog -C .. ${DIR}/Makefile \
-C .. ${DIR}/README.zfs-auto-snapshot.txt -C .. ${DIR}/src
gzip ${DIR}.tar
# drop our saved method script back where we left it
cp zfs-auto-snapshot.src src/lib/svc/method/zfs-auto-snapshot

21
README
View File

@ -1,21 +0,0 @@
zfs-auto-snapshot:
An alternative implementation of the zfs-auto-snapshot service for Linux
that is compatible with zfs-linux and zfs-fuse.
Automatically create, rotate, and destroy periodic ZFS snapshots. This is
the utility that creates the @zfs-auto-snap_frequent, @zfs-auto-snap_hourly,
@zfs-auto-snap_daily, @zfs-auto-snap_weekly, and @zfs-auto-snap_monthly
snapshots if it is installed.
This program is a posixly correct bourne shell script. It depends only on
the zfs utilities and cron, and can run in the dash shell.
Installation:
-------------
wget https://github.com/zfsonlinux/zfs-auto-snapshot/archive/upstream/1.2.4.tar.gz
tar -xzf 1.2.4.tar.gz
cd zfs-auto-snapshot-upstream-1.2.4
make install

View File

@ -0,0 +1,187 @@
NAME
ZFS Automatic Snapshot SMF Service, version 0.12
DESCRIPTION
This is a simple SMF service which can will take automatic,
scheduled snapshots of given ZFS filesystems and can perform simple
incremental or full backups of those filesystems.
Documentation for the service is contained in the manifest file,
zfs-auto-snapshot.xml.
Version 0.11 removes the simple GUI applications that were used to
create manifests, or select which filesystems should be included
in the canned instances. These are superceded by the time-slider-setup
application.
INSTALLATION
To install, as root, pkgadd SUNWzfs-auto-snapshot. This package now contains
several canned SMF instances. These are:
online 1:17:43 svc:/system/filesystem/zfs/auto-snapshot:hourly
online 1:17:46 svc:/system/filesystem/zfs/auto-snapshot:monthly
online 1:17:46 svc:/system/filesystem/zfs/auto-snapshot:daily
online 1:17:48 svc:/system/filesystem/zfs/auto-snapshot:frequent
online 1:17:49 svc:/system/filesystem/zfs/auto-snapshot:weekly
online 1:17:49 svc:/system/filesystem/zfs/auto-snapshot:event
These instances use the special "//" fs-name to determine which filesystems
should be included in each snapshot schedule. See the description for "fs-name"
below.
The included instances have the following properties:
frequent snapshots every 15 mins, keeping 4 snapshots
hourly snapshots every hour, keeping 24 snapshots
daily snapshots every day, keeping 31 snapshots
weekly snapshots every week, keeping 7 snapshots
monthly snapshots every month, keeping 12 snapshots
event no automatic snapshots taken, keeps all snapshots
The :default service instance does not need to be enabled.
Additional instances of the service can also be created, for example to group
related sets of filesystems under a single service instance.
The properties each instance needs are:
zfs/fs-name The name of the filesystem. If the special filesystem
name "//" is used, then the system snapshots only
filesystems with the zfs user property
"com.sun:auto-snapshot:<label>" set to true, so to take
frequent snapshots of tank/timf, run the following zfs
command:
# zfs set com.sun:auto-snapshot:frequent=true tank/timf
The "snap-children" property is ignored when using this
fs-name value. Instead, the system automatically determines
when it's able to take recursive, vs. non-recursive snapshots
of the system, based on the values of the ZFS user properties.
zfs/interval [ hours | days | months | none]
When set to none, we don't take automatic snapshots, but
leave an SMF instance available for users to manually
fire the method script whenever they want - useful for
snapshotting on system events. This is used by the
svc:/system/filesystem/zfs/auto-snapshot:event instance.
zfs/keep How many snapshots to retain - eg. setting this to "4"
would keep only the four most recent snapshots. When each
new snapshot is taken, the oldest is destroyed. If a snapshot
has been cloned, the service will drop to maintenance mode
when attempting to destroy that snapshot. Setting to "all"
keeps all snapshots.
zfs/period How often you want to take snapshots, in intervals
set according to "zfs/interval"
(eg. every 10 days)
zfs/offset The time from the start of the current interval at which
snapshots should be taken, expressed in seconds. For example
to take snapshots hourly at 15 minutes into the hour,
zfs/offset = 900 (60 sec. * 15) To take daily snapshots at
15:43, zfs/offset = 56580.
zfs/snapshot-children "true" if you would like to recursively take snapshots
of all child filesystems of the specified fs-name.
This value is ignored when setting zfs/fs-name='//'
zfs/backup [ full | incremental | none ]
zfs/backup-save-cmd The command string used to save the backup stream.
zfs/backup-lock You shouldn't need to change this - but it should be
set to "unlocked" by default. We use it to indicate when
a backup is running.
zfs/label A label that can be used to differentiate this set of
snapshots from others, not required. If multiple
schedules are running on the same machine, using distinct
labels for each schedule is needed - otherwise one
schedule could remove snapshots taken by another schedule
according to it's snapshot-retention policy.
(see "zfs/keep")
zfs/verbose Set to false by default, setting to true makes the
service produce more output about what it's doing.
zfs/avoidscrub Set to false by default, this determines whether
we should avoid taking snapshots on any pools that have
a scrub or resilver in progress.
More info in the bugid:
6343667 need itinerary so interrupted scrub/resilver
doesn't have to start over
zfs/sep A character used to separate components of the snapshot
name generated by the service. Set to '_' by default.
(the original character ':' caused problems for CIFS
clients)
zfs/auto-include Set to true by default, this determines whether we should
set a property on all new pools (pools where com.sun:auto-snapshot
isn't already set) telling the system to automatically include
all datasets in that pool in the default schedules. Setting to
false will prevent newly imported pools from being snapshotted.
An example instance manifest is included in this archive.
ZFS PROPERTIES
See the description of 'zfs/fs-name' above for details on the com.sun:auto-snapshot
property, used to select datasets for inclusion into each snapshot schedule.
The 'com.sun:auto-snapshot-desc' property is set on every snapshot taken
by the service. Values for this property are not defined and are left to the individual
user. The service sets a value of 'Missed snapshot' in this property when snapshots
are taken on service start (due to a previous scheduled snapshot being missed)
Similarly, users can invoke the method script with an optional string which is then set
as a value to this property.
eg.
# /lib/svc/method/zfs-auto-snapshot svc:/system/filesystem/zfs/auto-snapshot:event "Samba connect"
# zfs get com.sun:auto-snapshot-desc rpool/timf@zfs-auto-snap_event-2009-06-22-1240
NAME PROPERTY VALUE SOURCE
rpool/timf@zfs-auto-snap_event-2009-06-22-1240 com.sun:auto-snapshot-desc Samba connect local
SECURITY
The service is run by a restricted role "zfssnap", which is created when installing
the service if it doesn't already exist. It has the "ZFS File System Administration"
RBAC Profile, as well as the solaris.smf.manage.zfs-auto-snapshot Authorization.
In order to see what the service is doing, you can view the SMF log files in
/var/svc/log for each service instance and syslog, with more detailed logging output
being sent to syslog when the "zfs/verbose" option is enabled.
SEE ALSO
More background about this service, along with implementation comments can be
found in web log posts at:
http://blogs.sun.com/timf/entry/zfs_automatic_snapshots_prototype_1
http://blogs.sun.com/timf/entry/zfs_automatic_snapshots_smf_service
http://blogs.sun.com/timf/entry/and_also_for_s10u2_zfs
http://blogs.sun.com/timf/entry/smf_philosophy_more_on_zfs
http://blogs.sun.com/timf/entry/zfs_automatic_snapshots_now_with
http://blogs.sun.com/timf/entry/zfs_automatic_snapshot_service_logging
http://blogs.sun.com/timf/entry/zfs_automatic_snapshots_0_8
http://blogs.sun.com/timf/entry/zfs_automatic_for_the_people
http://blogs.sun.com/timf/entry/zfs_automatic_snapshots_0_10
http://blogs.sun.com/timf/entry/zfs_automatic_snapshots_0_11
http://blogs.sun.com/timf/entry/zfs_automatic_snapshots_0_12
The ZFS Automatic Snapshot SMF Service is released under the terms of the CDDL.

View File

@ -1,6 +0,0 @@
#!/bin/sh
# Only call zfs-auto-snapshot if it's available
which zfs-auto-snapshot > /dev/null || exit 0
exec zfs-auto-snapshot --quiet --syslog --label=daily --keep=31 //

View File

@ -1,3 +0,0 @@
PATH="/usr/bin:/bin:/usr/local/sbin:/usr/sbin:/sbin"
*/15 * * * * root which zfs-auto-snapshot > /dev/null || exit 0 ; zfs-auto-snapshot --quiet --syslog --label=frequent --keep=4 //

View File

@ -1,6 +0,0 @@
#!/bin/sh
# Only call zfs-auto-snapshot if it's available
which zfs-auto-snapshot > /dev/null || exit 0
exec zfs-auto-snapshot --quiet --syslog --label=hourly --keep=24 //

View File

@ -1,6 +0,0 @@
#!/bin/sh
# Only call zfs-auto-snapshot if it's available
which zfs-auto-snapshot > /dev/null || exit 0
exec zfs-auto-snapshot --quiet --syslog --label=monthly --keep=12 //

View File

@ -1,6 +0,0 @@
#!/bin/sh
# Only call zfs-auto-snapshot if it's available
which zfs-auto-snapshot > /dev/null || exit 0
exec zfs-auto-snapshot --quiet --syslog --label=weekly --keep=8 //

View File

@ -0,0 +1,62 @@
<?xml version="1.0"?>
<!DOCTYPE service_bundle SYSTEM "/usr/share/lib/xml/dtd/service_bundle.dtd.1">
<service_bundle type='manifest' name='tank-timf-torrent,foo'>
<service
name='system/filesystem/zfs/auto-snapshot'
type='service'
version='0.11'>
<create_default_instance enabled='false' />
<instance name='tank-timf-torrent,foo' enabled='false' >
<exec_method
type='method'
name='start'
timeout_seconds='10' />
<exec_method
type='method'
name='stop'
exec='/lib/svc/method/zfs-auto-snapshot stop'
timeout_seconds='10' />
<property_group name='startd' type='framework'>
<propval name='duration' type='astring' value='transient' />
</property_group>
<!-- properties for zfs automatic snapshots -->
<property_group name="zfs" type="application">
<propval name="fs-name" type="astring" value="tank/timf/torrent"
override="true"/>
<propval name="interval" type="astring" value="hours"
override="true"/>
<propval name="period" type="astring" value="14"
override="true"/>
<propval name="offset" type="astring" value="0"
override="true"/>
<propval name="keep" type="astring" value="16"
override="true"/>
<propval name="snapshot-children" type="boolean" value="false"
override="true"/>
<propval name="backup" type="astring" value="incremental"
override="true"/>
<propval name="backup-save-cmd" type="astring" value="ssh timf@hostname /usr/bin/pfexec /usr/sbin/zfs receive tank/backup"
override="true"/>
<propval name="backup-lock" type="astring" value="unlocked"
override="true"/>
<propval name="label" type="astring" value="foo"
override="true"/>
<propval name="verbose" type="boolean" value="false"
override="true"/>
</property_group>
</instance>
<stability value='Unstable' />
</service>
</service_bundle>

2
src/copyright Normal file
View File

@ -0,0 +1,2 @@
Copyright 2009 Sun Microsystems, Inc. All rights reserved.
Use is subject to license terms.

116
src/i.manifest Executable file
View File

@ -0,0 +1,116 @@
#!/bin/sh
#
# CDDL HEADER START
#
# The contents of this file are subject to the terms of the
# Common Development and Distribution License (the "License").
# You may not use this file except in compliance with the License.
#
# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
# or http://www.opensolaris.org/os/licensing.
# See the License for the specific language governing permissions
# and limitations under the License.
#
# When distributing Covered Code, include this CDDL HEADER in each
# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
# If applicable, add the following below this CDDL HEADER, with the
# fields enclosed by brackets "[]" replaced with your own identifying
# information: Portions Copyright [yyyy] [name of copyright owner]
#
# CDDL HEADER END
#
#
# Copyright 2008 Sun Microsystems, Inc. All rights reserved.
# Use is subject to license terms.
#
# i.manifest - smf(5) service manifest install class action script
#
repfile=$PKG_INSTALL_ROOT/etc/svc/repository.db
export repfile
SVCCFG=/usr/sbin/svccfg
AWK=/usr/bin/awk
RM=/usr/bin/rm
CP=/usr/bin/cp
MV=/usr/bin/mv
CHMOD=/usr/bin/chmod
CHOWN=/usr/bin/chown
#
# Helper function. Handle services deathrow file.
# Arguments: $1:manifest file.
#
svc_deathrow()
{
TEMP=/tmp/svc_deathrow.$$
DEATHROW_FILE=${PKG_INSTALL_ROOT}/etc/svc/deathrow
#
# Services deathrow file handling, file format:
# <fmri>< ><manifest file>< ><package name>
# (field separator is a space character)
#
if [ -s ${DEATHROW_FILE} ]; then
#
# Manifest file could be from another Solaris version, bypass
# the service bundle and validation (we only need the fmris
# list). Calling svccfg inventory with SVCCFG_NOVALIDATE=1 is
# safe because there is no access to the alternate repository.
#
ENTITIES=`SVCCFG_NOVALIDATE=1 $SVCCFG inventory $1`
for fmri in $ENTITIES; do
#
# If fmri matches one in deathrow file, remove the
# line from the file.
#
>${TEMP}
$AWK "(\$1==\"$fmri\") \
{next}; {print}" ${DEATHROW_FILE} >>${TEMP} && \
$MV ${TEMP} ${DEATHROW_FILE}
$RM -f ${TEMP}
done
fi
}
#
# If the repository does not yet exist, create it from the appropriate seed. If
# for some reason the seeds do not exist, svccfg(1M) will create the repository
# automatically.
#
if [ ! -f $repfile ]; then
if [ -n "$SUNW_PKG_INSTALL_ZONENAME" -a \
"$SUNW_PKG_INSTALL_ZONENAME" != "global" ]; then
[ -f $PKG_INSTALL_ROOT/lib/svc/seed/nonglobal.db ] && \
$CP $PKG_INSTALL_ROOT/lib/svc/seed/nonglobal.db $repfile
else
[ -f $PKG_INSTALL_ROOT/lib/svc/seed/global.db ] && \
$CP $PKG_INSTALL_ROOT/lib/svc/seed/global.db $repfile
fi
$CHMOD 0600 $repfile
$CHOWN root:sys $repfile
fi
if [ ! -r $PKG_INSTALL_ROOT/etc/svc/volatile/repository_door ]; then
#
# smf(5) is not presently running for the destination environment.
# Since we presently cannot refresh without a running svc.startd(1M), we
# cannot consistently handle dependent placement. Defer to next boot.
#
while read src dst; do
$CP -p $src $dst
# deathrow handling
svc_deathrow $dst
done
else
#
# Local package install.
#
while read src dst; do
$CP -p $src $dst
[ "$PKG_INSTALL_ROOT" = "" -o "$PKG_INSTALL_ROOT" = "/" ] && \
SVCCFG_CHECKHASH=1 $SVCCFG import $dst
done
fi
exit 0

File diff suppressed because it is too large Load Diff

24
src/pkginfo Normal file
View File

@ -0,0 +1,24 @@
LC_MESSAGES=C
LANG=C
TZ=Eire
PATH=/sbin:/usr/sbin:/usr/bin:/usr/sadm/install/bin
OAMBASE=/usr/sadm/sysadm
SITENAME=Sun Microsystems, Inc.
PKG=SUNWzfs-auto-snapshot
NAME=ZFS Automatic Snapshot Service
ARCH=all
BASEDIR=/
VERSION=0.12
MAXINST=1
CATEGORY=application
DESC=Takes automatic snapshots of ZFS filesystems on a periodic basis.
PSTAMP=~PSTAMP~
VENDOR=Sun Microsystems, Inc.
HOTLINE=Please contact your local service provider
CLASSES=none manifest
EMAIL=zfs-auto-snapshot@opensolaris.org
SUNW_PKGVERS=1.0
SUNW_PKG_ALLZONES=false
SUNW_PKG_HOLLOW=false
PKG_NONABI_SYMLINKS=true
PKGINST=SUNWzfs-auto-snapshot

74
src/postinstall Executable file
View File

@ -0,0 +1,74 @@
#!/bin/sh
#
# CDDL HEADER START
#
# The contents of this file are subject to the terms of the
# Common Development and Distribution License, Version 1.0 only
# (the "License"). You may not use this file except in compliance
# with the License.
#
# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
# or http://www.opensolaris.org/os/licensing.
# See the License for the specific language governing permissions
# and limitations under the License.
#
# When distributing Covered Code, include this CDDL HEADER in each
# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
# If applicable, add the following below this CDDL HEADER, with the
# fields enclosed by brackets "[]" replaced with your own identifying
# information: Portions Copyright [yyyy] [name of copyright owner]
#
# CDDL HEADER END
#
# Copyright 2008 Sun Microsystems, Inc. All rights reserved.
# Use is subject to license terms.
#
# a postinstall script - adds zfssnap role.
# Return 0 if a user exists, 1 otherwise.
#
# Check if the first argument is 0, settting our return
# variable to an error otherwise
#
check_error() {
RETURN_CODE=$1
ERROR="$2"
if [ "$RETURN_CODE" -ne 0 ] ; then
echo "ERROR: $ERROR"
fi
}
user_exists() {
USER=$1
/usr/bin/grep "^$USER:" $BASEDIR/etc/passwd > /dev/null
return $?
}
auth_exists() {
AUTH=$1
/usr/bin/grep "^$AUTH:" $BASEDIR/etc/security/auth_attr > /dev/null
return $?
}
# add our authorization
auth_exists solaris.smf.manage.zfs-auto-snapshot
if [ $? -ne 0 ] ; then
echo "solaris.smf.manage.zfs-auto-snapshot:::Manage the ZFS Automatic Snapshot Service::" \
>> /etc/security/auth_attr
fi
# add the zfssnap role - probably only works on a local system :-(
user_exists zfssnap
if [ $? -ne 0 ] ; then
/usr/sbin/roleadd -d / -u 51 -c "ZFS Automatic Snapshots role" \
-P "ZFS File System Management" \
-A solaris.smf.manage.zfs-auto-snapshot -m zfssnap
check_error $? "Unable to create zfssnap role!"
/usr/bin/passwd -r files -N zfssnap
check_error $? "Unable to make zfssnap a no-password account"
else
echo "zfssnap role already exists."
fi

15
src/prototype Normal file
View File

@ -0,0 +1,15 @@
i pkginfo
i copyright
i postinstall
i i.manifest
i r.manifest
d none lib 0755 root bin
d none lib/svc 0755 root bin
d none lib/svc/method 0755 root bin
f none lib/svc/method/zfs-auto-snapshot 0755 root bin
d none var 0755 root sys
d none var/svc 0755 root sys
d none var/svc/manifest 0755 root sys
d none var/svc/manifest/system 0755 root sys
d none var/svc/manifest/system/filesystem 0755 root sys
f manifest var/svc/manifest/system/filesystem/auto-snapshot.xml 0644 root sys

186
src/r.manifest Executable file
View File

@ -0,0 +1,186 @@
#!/bin/sh
#
# CDDL HEADER START
#
# The contents of this file are subject to the terms of the
# Common Development and Distribution License (the "License").
# You may not use this file except in compliance with the License.
#
# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
# or http://www.opensolaris.org/os/licensing.
# See the License for the specific language governing permissions
# and limitations under the License.
#
# When distributing Covered Code, include this CDDL HEADER in each
# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
# If applicable, add the following below this CDDL HEADER, with the
# fields enclosed by brackets "[]" replaced with your own identifying
# information: Portions Copyright [yyyy] [name of copyright owner]
#
# CDDL HEADER END
#
#
# Copyright 2008 Sun Microsystems, Inc. All rights reserved.
# Use is subject to license terms.
#
#
# r.manifest - smf(5) manifest remove class action script
#
MFSTSCAN=/lib/svc/bin/mfstscan
SVCCFG=/usr/sbin/svccfg
SVCPROP=/usr/bin/svcprop
SVCADM=/usr/sbin/svcadm
AWK=/usr/bin/awk
CP=/usr/bin/cp
RM=/usr/bin/rm
# number of seconds to wait before killing processes
STOP_DELAY=60
#
# Helper function. Delete the manifest hash value.
# Arguments: $1: manifest file.
#
svc_delhash()
{
$SVCCFG delhash $1 >/dev/null 2>&1
if [ "$?" != "0" ];then
# this Solaris release doesn't have delhash command
pg_name=`$MFSTSCAN -t $1`
if $SVCPROP -q -p $pg_name smf/manifest; then
$SVCCFG -s smf/manifest delpg $pg_name
fi
fi
}
#
# Helper function. Handle services deathrow file.
# Arguments: $1:manifest file, $2:package name.
#
svc_deathrow()
{
DEATHROW_FILE=${PKG_INSTALL_ROOT}/etc/svc/deathrow
# remove alternate root from manifest path
manifest=`echo "${PKG_INSTALL_ROOT} $1" | $AWK \
'{ print substr($2, length($1)+1); }'`
#
# Services deathrow file handling, file format:
# <fmri>< ><manifest file>< ><package name>
# (field separator is a space character)
#
# Manifest file could be from another Solaris version, bypass the
# the service bundle and validation (we only need the fmris list).
# Calling svccfg inventory with SVCCFG_NOVALIDATE=1 is safe because
# there is no access to the alternate repository.
#
ENTITIES=`SVCCFG_NOVALIDATE=1 $SVCCFG inventory $1`
for fmri in $ENTITIES; do
# add to services deathrow file
echo ${fmri} ${manifest} $2 >> ${DEATHROW_FILE}
done
}
wait_disable() {
svcinst=$1
wait_time=$2
while [ ${nsec:=0} -lt $wait_time ]; do
state=`$SVCPROP -p restarter/state $svcinst`
if [ "$state" = "disabled" -o "$state" = "maintenance" ]; then
nstate=`$SVCPROP -p restarter/next_state $svcinst`
if [ "$nstate" = "none" ]; then
return 0
fi
fi
/usr/bin/sleep 1
nsec=`expr ${nsec} + 1`
done
return 1
}
if [ "$PKG_INSTALL_ROOT" != "" -a "$PKG_INSTALL_ROOT" != "/" ]; then
#
# We can't safely disable the service in this case.
#
smf_alive=no
else
#
# We can verify if the service is disabled prior to
# removal.
#
if [ -r /etc/svc/volatile/repository_door ]; then
smf_alive=yes
fi
fi
while read mfst; do
if [ "$smf_alive" = "yes" ]; then
ENTITIES=`$SVCCFG inventory $mfst`
for fmri in $ENTITIES; do
# Determine whether fmri refers to an instance or a service.
$SVCPROP -p restarter/state $fmri >/dev/null 2>&1
if [ $? -eq 1 ]; then
# this is a service fmri, all instances have been deleted
$SVCCFG delete $fmri 2>/dev/null
# process next instance
continue
fi
#
# Try to disable the instance within a reasonable amount of time
# (eg. 60 secs). If it fails, forcibly delete the instance.
#
echo "Waiting up to $STOP_DELAY seconds for $fmri to stop..."
$SVCADM disable $fmri 2>/dev/null
if [ $? -eq 0 ]; then
wait_disable $fmri $STOP_DELAY
if [ $? -eq 0 ]; then
# the instance is disabled and can be safely deleted
$SVCCFG delete $fmri 2>/dev/null
# process next instance
continue
fi
echo "Failed to disable $fmri after $STOP_DELAY seconds"
else
echo "Failed to disable $fmri"
fi
echo "Force deleting $fmri"
ctid=`$SVCPROP -p restarter/contract $fmri 2>/dev/null`
tctid=`$SVCPROP -p restarter/transient_contract $fmri 2>/dev/null`
$SVCCFG delete -f $fmri
#
# Kill any remaining processes.
# pkill must occur after the delete to prevent startd
# from retrying the STOP method.
#
if [ -n "${tctid}" -a "${tctid}" -gt 1 ]; then
# kill the STOP method processes
/usr/bin/pkill -9 -c $tctid
fi
if [ -n "${ctid}" -a "${ctid}" -gt 1 ]; then
# kill any remaining running processes for the instance
/usr/bin/pkill -9 -c $ctid
fi
done
#
# Delete the manifest hash value.
#
svc_delhash $mfst
else
# deathrow handling
svc_deathrow $mfst $PKGINST
fi
$RM -f $mfst
done
exit 0

View File

@ -0,0 +1,59 @@
<?xml version="1.0"?>
<!DOCTYPE service_bundle SYSTEM "/usr/share/lib/xml/dtd/service_bundle.dtd.1">
<service_bundle type='manifest' name='space-archive'>
<!-- a simple instance that snapshots the space/archive filesystem every 6 hours -->
<service
name='system/filesystem/zfs/auto-snapshot'
type='service'
version='0.12'>
<create_default_instance enabled='false' />
<instance name='space-archive' enabled='false' >
<property_group name='startd' type='framework'>
<propval name='duration' type='astring' value='transient' />
</property_group>
<!-- properties for zfs automatic snapshots -->
<property_group name="zfs" type="application">
<propval name="fs-name" type="astring" value="space/archive"
override="true"/>
<propval name="interval" type="astring" value="hours"
override="true"/>
<propval name="period" type="astring" value="6"
override="true"/>
<propval name="offset" type="astring" value="0"
override="true"/>
<propval name="keep" type="astring" value="2"
override="true"/>
<propval name="snapshot-children" type="boolean" value="false"
override="true"/>
<propval name="backup" type="astring" value="none"
override="true"/>
<propval name="backup-save-cmd" type="astring" value=""
override="true"/>
<propval name="backup-lock" type="astring" value="unlocked"
override="true"/>
<propval name="label" type="astring" value=""
override="true"/>
<propval name="verbose" type="boolean" value="false"
override="true"/>
<propval name="avoidscrub" type="boolean" value="true"
override="true"/>
<propval name="sep" type="astring" value="_"
override="true"/>
<propval name="auto-include" type="boolean" value="true"
override="true"/>
</property_group>
</instance>
<stability value='Unstable' />
</service>
</service_bundle>

View File

@ -0,0 +1,62 @@
<?xml version="1.0"?>
<!DOCTYPE service_bundle SYSTEM "/usr/share/lib/xml/dtd/service_bundle.dtd.1">
<service_bundle type='manifest' name='space-timf,backup'>
<!-- A service instance that backs up space/timf every month, keeping
122 months of snapshots. It saves each snapshot stream into the /extra
filesystem, naming the files according to the snapshot name -->
<service
name='system/filesystem/zfs/auto-snapshot'
type='service'
version='0.12'>
<create_default_instance enabled='false' />
<instance name='space-timf,backup' enabled='false' >
<property_group name='startd' type='framework'>
<propval name='duration' type='astring' value='transient' />
</property_group>
<!-- properties for zfs automatic snapshots -->
<property_group name="zfs" type="application">
<propval name="fs-name" type="astring" value="space/timf"
override="true"/>
<propval name="interval" type="astring" value="months"
override="true"/>
<propval name="period" type="astring" value="1"
override="true"/>
<propval name="offset" type="astring" value="0"
override="true"/>
<propval name="keep" type="astring" value="122"
override="true"/>
<propval name="snapshot-children" type="boolean" value="true"
override="true"/>
<propval name="backup" type="astring" value="full"
override="true"/>
<propval name="backup-save-cmd" type="astring" value="eval cat > /extra/`echo $LAST_SNAP | sed -e 's#/#-#g'`"
override="true"/>
<propval name="backup-lock" type="astring" value="unlocked"
override="true"/>
<propval name="label" type="astring" value="backup"
override="true"/>
<propval name="verbose" type="boolean" value="false"
override="true"/>
<propval name="avoidscrub" type="boolean" value="true"
override="true"/>
<propval name="sep" type="astring" value="_"
override="true"/>
<propval name="auto-include" type="boolean" value="true"
override="true"/>
</property_group>
</instance>
<stability value='Unstable' />
</service>
</service_bundle>

View File

@ -0,0 +1,551 @@
<?xml version="1.0"?>
<!DOCTYPE service_bundle SYSTEM "/usr/share/lib/xml/dtd/service_bundle.dtd.1">
<!--
CDDL HEADER START
The contents of this file are subject to the terms of the
Common Development and Distribution License, Version 1.0 only
(the "License"). You may not use this file except in compliance
with the License.
You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
or http://www.opensolaris.org/os/licensing.
See the License for the specific language governing permissions
and limitations under the License.
When distributing Covered Code, include this CDDL HEADER in each
file and include the License file at usr/src/OPENSOLARIS.LICENSE.
If applicable, add the following below this CDDL HEADER, with the
fields enclosed by brackets "[]" replaced with your own identifying
information: Portions Copyright [yyyy] [name of copyright owner]
CDDL HEADER END
Copyright 2009 Sun Microsystems, Inc. All rights reserved.
Use is subject to license terms.
-->
<service_bundle type='manifest' name='SUNWzfs-auto-snapshot:filesystem-auto-snapshot'>
<service
name='system/filesystem/zfs/auto-snapshot'
type='service'
version='0.12'>
<!-- we need to be multi-user -->
<dependency
name='multi-user'
grouping='require_all'
restart_on='none'
type='service'>
<service_fmri value='svc:/milestone/multi-user' />
</dependency>
<!-- we also need cron -->
<dependency
name="cron"
grouping="require_all"
restart_on="none"
type="service">
<service_fmri value="svc:/system/cron" />
</dependency>
<exec_method
type='method'
name='start'
exec='/lib/svc/method/zfs-auto-snapshot start'
timeout_seconds='0'>
<method_context>
<method_credential user='zfssnap' group='daemon' />
</method_context>
</exec_method>
<exec_method
type='method'
name='stop'
exec='/lib/svc/method/zfs-auto-snapshot stop'
timeout_seconds='0' >
<method_context>
<method_credential user='zfssnap' group='daemon' />
</method_context>
</exec_method>
<property_group name='startd' type='framework'>
<propval name='duration' type='astring' value='transient' />
</property_group>
<property_group name='general' type='framework'>
<propval name='action_authorization' type='astring'
value='solaris.smf.manage.zfs-auto-snapshot' />
<propval name='value_authorization' type='astring'
value='solaris.smf.manage.zfs-auto-snapshot' />
</property_group>
<!-- The properties we expect that any instance will define:
fs-name : The name of the filesystem we want to snapshot.
The special filesystem name "//" indicates we should
look at the com.sun:auto-snapshot ZFS user
properties on datasets, set to "true" if the dataset
should have snapshots taken by this instance.
If unset, snapshots will not be taken by this instance.
The snapshot-children property is ignored when using
this setting, instead the system will automatically
determine how to take snapshots, based on which datasets
have true, false or unset property values.
Setting com.sun:auto-snapshot:<label> will override
the general setting for com.sun:auto-snapshot.
interval : minutes | hours | days | months | none
For the interval "none" a cron job is not created for that
instance - instead the user can manually fire the method
script to take snapshots defined by the rest of the properties
in the instance. The period and offset values are ignored in
this case.
period : How many (m,h,d,m) do we wait between snapshots
offset : The offset into the time period we want
keep : How many snapshots we should keep, otherwise, we
delete the oldest when we hit this threshold
snapshot-children : Whether we should recursively snapshot
all filesystems contained within. Ignored when
using the "//" fs-name value.
backup : If we want to perform a "zfs send" for our backup
we set this - either to "full" or "incremental".
If set to "none", we don't perform backups.
backup-save-cmd : A command string to save the backup - if unset,
we return an error and move the service to
maintenance.
backup-lock : A string we set when a backup operation is in
progress, to prevent two backups from the same
service instance running into each other. Not
completely flawless, but okay. Due to 6338294,
we use the keyword "unlocked" to indicate that
the lock is not held.
label : A string that allows us to differentiate this set
of snapshot schedules from others configured for the
same filesystem. This is not usually needed and can
be left unset, but it can be useful in some
situations (particularly for backups).
verbose : Set to false by default, setting to true results
in the service printing more detail in the log
about what it's doing.
avoidscrub : Set to false by default, this determines whether
we should avoid taking snapshots on any pools that have
a scrub or resilver in progress.
More info in the bugid:
6343667 need itinerary so interrupted scrub/resilver
doesn't have to start over
sep: Set to '_' by default, this is the character used to
separate datestamps used in snapshot names.
auto-include: Set to 'true' by default, this determines whether
on startup, we should set a property on all new pools
seen by the service telling the service to take snapshots
on that pool.
-->
<property_group name="zfs" type="application">
<propval name="fs-name" type="astring" value="Not set" override="true"/>
<propval name="interval" type="astring" value="Not set" override="true"/>
<propval name="offset" type="astring" value="Not set" override="true"/>
<propval name="snapshot-children" type="boolean" value="false"
override="true"/>
<propval name="keep" type="astring" value="all" override="true"/>
<propval name="backup" type="astring" value="none" override="true"/>
<propval name="backup-save-cmd" type="astring" value="" override="true"/>
<propval name="backup-lock" type="astring" value="unlocked"
override="true"/>
<propval name="label" type="astring" value="" override="true"/>
<propval name="verbose" type="boolean" value="false" override="true"/>
<propval name="avoidscrub" type="boolean" value="false" override="true"/>
<propval name="sep" type="astring" value="_" override="true"/>
<propval name="auto-include" type="boolean" value="true" override="true"/>
</property_group>
<!-- We now define a set of default instances to take frequent,
hourly, daily, weekly and monthly snapshots -->
<!-- This instance recursively snapshots all
filesystems marked with the ZFS User Property
com.sun:auto-snapshot:frequent=true every
15 minutes, and keeps 4 of these snapshots into the past.
-->
<instance name='frequent' enabled='false' >
<property_group name='general' type='framework'>
<propval name='action_authorization' type='astring'
value='solaris.smf.manage.zfs-auto-snapshot' />
<propval name='value_authorization' type='astring'
value='solaris.smf.manage.zfs-auto-snapshot' />
</property_group>
<property_group name="zfs" type="application">
<propval name="fs-name" type="astring" value="//"
override="true"/>
<propval name="interval" type="astring" value="minutes"
override="true"/>
<propval name="period" type="astring" value="15"
override="true"/>
<propval name="offset" type="astring" value="0"
override="true"/>
<propval name="keep" type="astring" value="4"
override="true"/>
<propval name="snapshot-children" type="boolean" value="true"
override="true"/>
<propval name="backup" type="astring" value="none"
override="true"/>
<propval name="backup-save-cmd" type="astring" value="not set"
override="true"/>
<propval name="backup-lock" type="astring" value="unlocked"
override="true"/>
<propval name="label" type="astring" value="frequent"
override="true"/>
<propval name="verbose" type="boolean" value="false"
override="true"/>
<propval name="avoidscrub" type="boolean" value="false"
override="false"/>
<propval name="sep" type="astring" value="_"
override="true"/>
<propval name="auto-include" type="boolean" value="true" override="true"/>
</property_group>
</instance>
<!-- This instance recursively snapshots all
filesystems marked with the ZFS User Property
com.sun:auto-snapshot:hourly=true every
hour, and keeps 24 of these snapshots into the past.
-->
<instance name='hourly' enabled='false' >
<property_group name='general' type='framework'>
<propval name='action_authorization' type='astring'
value='solaris.smf.manage.zfs-auto-snapshot' />
<propval name='value_authorization' type='astring'
value='solaris.smf.manage.zfs-auto-snapshot' />
</property_group>
<property_group name="zfs" type="application">
<propval name="fs-name" type="astring" value="//"
override="true"/>
<propval name="interval" type="astring" value="hours"
override="true"/>
<propval name="period" type="astring" value="1"
override="true"/>
<propval name="offset" type="astring" value="0"
override="true"/>
<propval name="keep" type="astring" value="24"
override="true"/>
<propval name="snapshot-children" type="boolean" value="true"
override="true"/>
<propval name="backup" type="astring" value="none"
override="true"/>
<propval name="backup-save-cmd" type="astring" value="not set"
override="true"/>
<propval name="backup-lock" type="astring" value="unlocked"
override="true"/>
<propval name="label" type="astring" value="hourly"
override="true"/>
<propval name="verbose" type="boolean" value="false"
override="true"/>
<propval name="avoidscrub" type="boolean" value="false"
override="false"/>
<propval name="sep" type="astring" value="_"
override="true"/>
<propval name="auto-include" type="boolean" value="true" override="true"/>
</property_group>
</instance>
<!-- This instance recursively snapshots all
filesystems marked with the ZFS User Property
com.sun:auto-snapshot:daily=true every
day, and keeps 31 of these snapshots into the past.
-->
<instance name='daily' enabled='false' >
<property_group name='general' type='framework'>
<propval name='action_authorization' type='astring'
value='solaris.smf.manage.zfs-auto-snapshot' />
<propval name='value_authorization' type='astring'
value='solaris.smf.manage.zfs-auto-snapshot' />
</property_group>
<property_group name="zfs" type="application">
<propval name="fs-name" type="astring" value="//"
override="true"/>
<propval name="interval" type="astring" value="days"
override="true"/>
<propval name="period" type="astring" value="1"
override="true"/>
<propval name="offset" type="astring" value="0"
override="true"/>
<propval name="keep" type="astring" value="31"
override="true"/>
<propval name="snapshot-children" type="boolean" value="true"
override="true"/>
<propval name="backup" type="astring" value="none"
override="true"/>
<propval name="backup-save-cmd" type="astring" value="not set"
override="true"/>
<propval name="backup-lock" type="astring" value="unlocked"
override="true"/>
<propval name="label" type="astring" value="daily"
override="true"/>
<propval name="verbose" type="boolean" value="false"
override="true"/>
<propval name="avoidscrub" type="boolean" value="false"
override="false"/>
<propval name="sep" type="astring" value="_"
override="true"/>
<propval name="auto-include" type="boolean" value="true" override="true"/>
</property_group>
</instance>
<!-- This instance recursively snapshots all
filesystems marked with the ZFS User Property
com.sun:auto-snapshot:weekly=true every
7 days, and keeps 4 of these snapshots into the past.
-->
<instance name='weekly' enabled='false' >
<property_group name='general' type='framework'>
<propval name='action_authorization' type='astring'
value='solaris.smf.manage.zfs-auto-snapshot' />
<propval name='value_authorization' type='astring'
value='solaris.smf.manage.zfs-auto-snapshot' />
</property_group>
<property_group name="zfs" type="application">
<propval name="fs-name" type="astring" value="//"
override="true"/>
<propval name="interval" type="astring" value="days"
override="true"/>
<propval name="period" type="astring" value="7"
override="true"/>
<propval name="offset" type="astring" value="0"
override="true"/>
<propval name="keep" type="astring" value="4"
override="true"/>
<propval name="snapshot-children" type="boolean" value="true"
override="true"/>
<propval name="backup" type="astring" value="none"
override="true"/>
<propval name="backup-save-cmd" type="astring" value="not set"
override="true"/>
<propval name="backup-lock" type="astring" value="unlocked"
override="true"/>
<propval name="label" type="astring" value="weekly"
override="true"/>
<propval name="verbose" type="boolean" value="false"
override="true"/>
<propval name="avoidscrub" type="boolean" value="false"
override="false"/>
<propval name="sep" type="astring" value="_"
override="true"/>
<propval name="auto-include" type="boolean" value="true" override="true"/>
</property_group>
</instance>
<!-- This instance recursively snapshots all
filesystems marked with the ZFS User Property
com.sun:auto-snapshot:monthly=true every
month, and keeps 12 of these snapshots into the past.
-->
<instance name='monthly' enabled='false' >
<property_group name='general' type='framework'>
<propval name='action_authorization' type='astring'
value='solaris.smf.manage.zfs-auto-snapshot' />
<propval name='value_authorization' type='astring'
value='solaris.smf.manage.zfs-auto-snapshot' />
</property_group>
<property_group name="zfs" type="application">
<propval name="fs-name" type="astring" value="//"
override="true"/>
<propval name="interval" type="astring" value="months"
override="true"/>
<propval name="period" type="astring" value="1"
override="true"/>
<propval name="offset" type="astring" value="0"
override="true"/>
<propval name="keep" type="astring" value="12"
override="true"/>
<propval name="snapshot-children" type="boolean" value="true"
override="true"/>
<propval name="backup" type="astring" value="none"
override="true"/>
<propval name="backup-save-cmd" type="astring" value="not set"
override="true"/>
<propval name="backup-lock" type="astring" value="unlocked"
override="true"/>
<propval name="label" type="astring" value="monthly"
override="true"/>
<propval name="verbose" type="boolean" value="false"
override="true"/>
<propval name="avoidscrub" type="boolean" value="true"
override="false"/>
<propval name="sep" type="astring" value="_"
override="true"/>
<propval name="auto-include" type="boolean" value="true" override="true"/>
</property_group>
</instance>
<!-- This instance takes snapshots on events, rather than
being called from cron. Events are tagged by a string
argument to the method script. Filesystems marked with
the ZFS User Property com.sun:auto-snapshot:event=true
are included in this schedule. We do not destroy event
driven snapshots, however that can be overridden in the
SMF instance properties.
-->
<instance name='event' enabled='true' >
<property_group name='general' type='framework'>
<propval name='action_authorization' type='astring'
value='solaris.smf.manage.zfs-auto-snapshot' />
<propval name='value_authorization' type='astring'
value='solaris.smf.manage.zfs-auto-snapshot' />
</property_group>
<property_group name="zfs" type="application">
<propval name="fs-name" type="astring" value="//"
override="true"/>
<propval name="interval" type="astring" value="none"
override="true"/>
<propval name="period" type="astring" value="0"
override="true"/>
<propval name="offset" type="astring" value="0"
override="true"/>
<propval name="keep" type="astring" value="all"
override="true"/>
<propval name="snapshot-children" type="boolean" value="true"
override="true"/>
<propval name="backup" type="astring" value="none"
override="true"/>
<propval name="backup-save-cmd" type="astring" value="not set"
override="true"/>
<propval name="backup-lock" type="astring" value="unlocked"
override="true"/>
<propval name="label" type="astring" value="event"
override="true"/>
<propval name="verbose" type="boolean" value="false"
override="true"/>
<propval name="avoidscrub" type="boolean" value="true"
override="false"/>
<propval name="sep" type="astring" value="_"
override="true"/>
<propval name="auto-include" type="boolean" value="true" override="true"/>
</property_group>
</instance>
<stability value='Unstable' />
<template>
<common_name>
<loctext xml:lang='C'>ZFS automatic snapshots</loctext>
</common_name>
<description>
<loctext xml:lang='C'>
This service provides system support for taking automatic snapshots of ZFS
filesystems.
In order to use this service, you must create a service instance per set of automatic snapshots you want to take.
The on starting a service instance, a cron job corresponding to the properties set in the instance is created on the host. This cron job will regularly take snapshots of the specified ZFS filesystem.
On stopping the service, that cron job is removed.
We also have the ability to perform backups, done using the "zfs send" command. A property set in the service called "backup-save-cmd" can be configured as the command used to save the backup stream. See the zfs(1M) man page for an example. The backups can be either "full" backups, or "incremental" backups - for each incremental backup, a full backup must be configured first. If for some reason an incremental backup fails, a full backup is performed instead.
By default, snapshots will be taken of any datasets resident on pools that are currently being scrubbed or resilvered. This can behaviour can be changed using the zfs/avoid scrub service property.
Care should be taken when configuring backups to ensure that the time granularity of the cron job is sufficient to allow the backup to complete between invocations of each backup. We perform locking to ensure that two backups of the same filesystem cannot run simultaneously, but will move the service into "maintenance" state should this occur.
</loctext>
</description>
</template>
</service>
</service_bundle>

View File

@ -1,91 +0,0 @@
.TH ZFS-AUTO-SNAPSHOT "8" "June 16, 2013" "zfs-auto-snapshot.sh" "System Administration Commands"
.SH NAME
zfs-auto-snapshot \- take regular ZFS snapshots
.SH SYNOPSIS
.B zfs-auto-snapshot
[\fIoptions\fR] [\fI-l label\fR] \fI<'//' | name \fR[\fIname\fR...]\fI>\fR
.SH DESCRIPTION
.B zfs-auto-snapshot
automatically creates, rotates, and destroys snapshots for all your
ZFS datasets, and is compatible with both zfsonlinux and zfs-fuse.
.SH OPTIONS
.TP
\fB\-\-default\-exclude\fR
By default \fBzfs-auto-snapshot\fR will snapshot all datasets except
for those in which the user-property \fBcom.sun:auto-snapshot\fR is
set to \fBfalse\fR. This option reverses the behavior and requires
\fBcom.sun:auto-snapshot\fR to be set to \fBtrue\fR.
.TP
\fB\-d\fR, \fB\-\-debug\fR
Print debugging messages.
.TP
\fB\-e\fR, \fB\-\-event\fR=\fIEVENT\fR
Set the com.sun:auto\-snapshot\-desc property to EVENT.
.TP
\fB\-\-fast\fR
Use a faster zfs list invocation.
.TP
\fB\-n\fR, \fB\-\-dry\-run\fR
Print actions without actually doing anything.
.TP
\fB\-s\fR, \fB\-\-skip\-scrub\fR
Do not snapshot filesystems in scrubbing pools.
.TP
\fB\-h\fR, \fB\-\-help\fR
Print the usage message.
.TP
\fB\-k\fR, \fB\-\-keep\fR=\fINUM\fR
Keep NUM recent snapshots and destroy older snapshots.
.TP
\fB\-l\fR, \fB\-\-label\fR=\fILAB\fR
LAB is usually 'hourly', 'daily', or 'monthly'.
.TP
\fB\-p\fR, \fB\-\-prefix\fR=\fIPRE\fR
PRE is 'zfs\-auto\-snap' by default.
.TP
\fB\-q\fR, \fB\-\-quiet\fR
Suppress warnings and notices at the console.
.TP
\fB\-\-send\-full\fR=\fIF\fR
Send zfs full backup. Unimplemented.
.TP
\fB\-\-send\-incr\fR=\fIF\fR
Send zfs incremental backup. Unimplemented.
.TP
\fB\-\-sep\fR=\fICHAR\fR
Use CHAR to separate date stamps in snapshot names.
.TP
\fB\-g\fR, \fB\-\-syslog\fR
Write messages into the system log.
.TP
\fB\-r\fR, \fB\-\-recursive\fR
Snapshot named filesystem and all descendants.
.TP
\fB\-v\fR, \fB\-\-verbose\fR
Print info messages.
.TP
\fB\-\-pre-snapshot\fR=\fICOMMAND\fR
Command to run before each dataset is snapshotted.
It is passed the dataset and snapshot name. If it
returns non-zero, snapshotting this dataset is
aborted.
.TP
\fB\-\-post-snapshot\fR=\fICOMMAND\fR
Command to run after each dataset is snapshotted.
It is passed the dataset and snapshot name.
.TP
\fB\-\-destroy-only\fR
Do not create new snapshots, but do destroy older
snapshots. Has no effect unless used with \fB\-k\fR.
.IP
A non-obvious use may be construction of cron jobs or
scripts that run pre-snapshot command(s), then run
zfs-auto-snapshot (without \fB\-k\fR) to quickly
snapshot all datasets, then run post-snapshot
command(s) and clean up with zfs-auto-snapshot
\fB\-\-destroy-only\fR.
.TP
name
Filesystem and volume names, or '//' for all ZFS datasets.
.SH SEE ALSO
.BR zfs (8)

View File

@ -1,631 +0,0 @@
#!/bin/sh
# zfs-auto-snapshot for Linux
# Automatically create, rotate, and destroy periodic ZFS snapshots.
# Copyright 2011 Darik Horn <dajhorn@vanadac.com>
#
# This program is free software; you can redistribute it and/or modify it under
# the terms of the GNU General Public License as published by the Free Software
# Foundation; either version 2 of the License, or (at your option) any later
# version.
#
# This program is distributed in the hope that it will be useful, but WITHOUT
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
# FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
# details.
#
# You should have received a copy of the GNU General Public License along with
# this program; if not, write to the Free Software Foundation, Inc., 59 Temple
# Place, Suite 330, Boston, MA 02111-1307 USA
#
# Set the field separator to a literal tab and newline.
IFS="
"
# Set default program options.
opt_backup_full=''
opt_backup_incremental=''
opt_default_exclude=''
opt_dry_run=''
opt_event='-'
opt_fast_zfs_list=''
opt_keep=''
opt_label=''
opt_prefix='zfs-auto-snap'
opt_recursive=''
opt_sep='_'
opt_setauto=''
opt_syslog=''
opt_skip_scrub=''
opt_verbose=''
opt_pre_snapshot=''
opt_post_snapshot=''
opt_do_snapshots=1
opt_min_size=0
# Global summary statistics.
DESTRUCTION_COUNT='0'
SNAPSHOT_COUNT='0'
WARNING_COUNT='0'
# Other global variables.
SNAPSHOTS_OLD=''
print_usage ()
{
echo "Usage: $0 [options] [-l label] <'//' | name [name...]>
--default-exclude Exclude datasets if com.sun:auto-snapshot is unset.
-d, --debug Print debugging messages.
-e, --event=EVENT Set the com.sun:auto-snapshot-desc property to EVENT.
--fast Use a faster zfs list invocation.
-n, --dry-run Print actions without actually doing anything.
-s, --skip-scrub Do not snapshot filesystems in scrubbing pools.
-h, --help Print this usage message.
-k, --keep=NUM Keep NUM recent snapshots and destroy older snapshots.
-l, --label=LAB LAB is usually 'hourly', 'daily', or 'monthly'.
-p, --prefix=PRE PRE is 'zfs-auto-snap' by default.
-q, --quiet Suppress warnings and notices at the console.
--send-full=F Send zfs full backup. Unimplemented.
--send-incr=F Send zfs incremental backup. Unimplemented.
--sep=CHAR Use CHAR to separate date stamps in snapshot names.
-g, --syslog Write messages into the system log.
-r, --recursive Snapshot named filesystem and all descendants.
-v, --verbose Print info messages.
--destroy-only Only destroy older snapshots, do not create new ones.
name Filesystem and volume names, or '//' for all ZFS datasets.
"
}
print_log () # level, message, ...
{
LEVEL=$1
shift 1
case $LEVEL in
(eme*)
test -n "$opt_syslog" && logger -t "$opt_prefix" -p daemon.emerge $*
echo Emergency: $* 1>&2
;;
(ale*)
test -n "$opt_syslog" && logger -t "$opt_prefix" -p daemon.alert $*
echo Alert: $* 1>&2
;;
(cri*)
test -n "$opt_syslog" && logger -t "$opt_prefix" -p daemon.crit $*
echo Critical: $* 1>&2
;;
(err*)
test -n "$opt_syslog" && logger -t "$opt_prefix" -p daemon.err $*
echo Error: $* 1>&2
;;
(war*)
test -n "$opt_syslog" && logger -t "$opt_prefix" -p daemon.warning $*
test -z "$opt_quiet" && echo Warning: $* 1>&2
;;
(not*)
test -n "$opt_syslog" && logger -t "$opt_prefix" -p daemon.notice $*
test -z "$opt_quiet" && echo $*
;;
(inf*)
# test -n "$opt_syslog" && logger -t "$opt_prefix" -p daemon.info $*
test -z "$opt_quiet" && test -n "$opt_verbose" && echo $*
;;
(deb*)
# test -n "$opt_syslog" && logger -t "$opt_prefix" -p daemon.debug $*
test -n "$opt_debug" && echo Debug: $*
;;
(*)
test -n "$opt_syslog" && logger -t "$opt_prefix" $*
echo $* 1>&2
;;
esac
}
do_run () # [argv]
{
if [ -n "$opt_dry_run" ]
then
echo $*
RC="$?"
else
eval $*
RC="$?"
if [ "$RC" -eq '0' ]
then
print_log debug "$*"
else
print_log warning "$* returned $RC"
fi
fi
return "$RC"
}
do_snapshots () # properties, flags, snapname, oldglob, [targets...]
{
local PROPS="$1"
local FLAGS="$2"
local NAME="$3"
local GLOB="$4"
local TARGETS="$5"
local KEEP=''
local RUNSNAP=1
# global DESTRUCTION_COUNT
# global SNAPSHOT_COUNT
# global WARNING_COUNT
# global SNAPSHOTS_OLD
for ii in $TARGETS
do
# Check if size check is > 0
size_check_skip=0
if [ "$opt_min_size" -gt 0 ]
then
bytes_written=`zfs get -Hp -o value written $ii`
kb_written=$(( $bytes_written / 1024 ))
if [ "$kb_written" -lt "$opt_min_size" ]
then
size_check_skip=1
if [ $opt_verbose -gt 0 ]
then
echo "Skipping target $ii, only $kb_written kB written since last snap. opt_min_size is $opt_min_size"
fi
fi
fi
if [ -n "$opt_do_snapshots" -a "$size_check_skip" -eq 0 ]
then
if [ "$opt_pre_snapshot" != "" ]
then
do_run "$opt_pre_snapshot $ii $NAME" || RUNSNAP=0
fi
if [ $RUNSNAP -eq 1 ] && do_run "zfs snapshot $PROPS $FLAGS '$ii@$NAME'"
then
[ "$opt_post_snapshot" != "" ] && do_run "$opt_post_snapshot $ii $NAME"
SNAPSHOT_COUNT=$(( $SNAPSHOT_COUNT + 1 ))
else
WARNING_COUNT=$(( $WARNING_COUNT + 1 ))
continue
fi
fi
# Retain at most $opt_keep number of old snapshots of this filesystem,
# including the one that was just recently created.
test -z "$opt_keep" && continue
KEEP="$opt_keep"
# ASSERT: The old snapshot list is sorted by increasing age.
for jj in $SNAPSHOTS_OLD
do
# Check whether this is an old snapshot of the filesystem.
if [ -z "${jj#$ii@$GLOB}" ]
then
KEEP=$(( $KEEP - 1 ))
if [ "$KEEP" -le '0' ]
then
if do_run "zfs destroy -d $FLAGS '$jj'"
then
DESTRUCTION_COUNT=$(( $DESTRUCTION_COUNT + 1 ))
else
WARNING_COUNT=$(( $WARNING_COUNT + 1 ))
fi
fi
fi
done
done
}
# main ()
# {
if [ "$(uname)" = "Darwin" ]; then
GETOPT_BIN="$(brew --prefix gnu-getopt 2> /dev/null || echo /usr/local)/bin/getopt"
else
GETOPT_BIN="getopt"
fi
GETOPT=$($GETOPT_BIN \
--longoptions=default-exclude,dry-run,fast,skip-scrub,recursive \
--longoptions=event:,keep:,label:,prefix:,sep: \
--longoptions=debug,help,quiet,syslog,verbose \
--longoptions=pre-snapshot:,post-snapshot:,destroy-only \
--longoptions=min-size: \
--options=dnshe:l:k:p:rs:qgvm: \
-- "$@" ) \
|| exit 128
eval set -- "$GETOPT"
while [ "$#" -gt '0' ]
do
case "$1" in
(-d|--debug)
opt_debug='1'
opt_quiet=''
opt_verbose='1'
shift 1
;;
(--default-exclude)
opt_default_exclude='1'
shift 1
;;
(-e|--event)
if [ "${#2}" -gt '1024' ]
then
print_log error "The $1 parameter must be less than 1025 characters."
exit 139
elif [ "${#2}" -gt '0' ]
then
opt_event="$2"
fi
shift 2
;;
(--fast)
opt_fast_zfs_list='1'
shift 1
;;
(-n|--dry-run)
opt_dry_run='1'
shift 1
;;
(-s|--skip-scrub)
opt_skip_scrub='1'
shift 1
;;
(-h|--help)
print_usage
exit 0
;;
(-k|--keep)
if ! test "$2" -gt '0' 2>/dev/null
then
print_log error "The $1 parameter must be a positive integer."
exit 129
fi
opt_keep="$2"
shift 2
;;
(-l|--label)
opt_label="$2"
shift 2
;;
(-m|--min-size)
opt_min_size="$2"
shift 2
;;
(-p|--prefix)
opt_prefix="$2"
while test "${#opt_prefix}" -gt '0'
do
case $opt_prefix in
([![:alnum:]_.:\ -]*)
print_log error "The $1 parameter must be alphanumeric."
exit 130
;;
esac
opt_prefix="${opt_prefix#?}"
done
opt_prefix="$2"
shift 2
;;
(-q|--quiet)
opt_debug=''
opt_quiet='1'
opt_verbose=''
shift 1
;;
(-r|--recursive)
opt_recursive='1'
shift 1
;;
(--sep)
case "$2" in
([[:alnum:]_.:\ -])
:
;;
('')
print_log error "The $1 parameter must be non-empty."
exit 131
;;
(*)
print_log error "The $1 parameter must be one alphanumeric character."
exit 132
;;
esac
opt_sep="$2"
shift 2
;;
(-g|--syslog)
opt_syslog='1'
shift 1
;;
(-v|--verbose)
opt_quiet=''
opt_verbose='1'
shift 1
;;
(--pre-snapshot)
opt_pre_snapshot="$2"
shift 2
;;
(--post-snapshot)
opt_post_snapshot="$2"
shift 2
;;
(--destroy-only)
opt_do_snapshots=''
shift 1
;;
(--)
shift 1
break
;;
esac
done
if [ "$#" -eq '0' ]
then
print_log error "The filesystem argument list is empty."
exit 133
fi
# Count the number of times '//' appears on the command line.
SLASHIES='0'
for ii in "$@"
do
test "$ii" = '//' && SLASHIES=$(( $SLASHIES + 1 ))
done
if [ "$#" -gt '1' -a "$SLASHIES" -gt '0' ]
then
print_log error "The // must be the only argument if it is given."
exit 134
fi
# These are the only times that `zpool status` or `zfs list` are invoked, so
# this program for Linux has a much better runtime complexity than the similar
# Solaris implementation.
ZPOOL_STATUS=$(env LC_ALL=C zpool status 2>&1 ) \
|| { print_log error "zpool status $?: $ZPOOL_STATUS"; exit 135; }
ZFS_LIST=$(env LC_ALL=C zfs list -H -t filesystem,volume -s name \
-o name,com.sun:auto-snapshot,com.sun:auto-snapshot:"$opt_label") \
|| { print_log error "zfs list $?: $ZFS_LIST"; exit 136; }
if [ -n "$opt_fast_zfs_list" ]
then
# Check if a snapshot label is being used, in which case restrict the old
# snapshot removal to only snapshots with the same label format
if [ -n "$opt_label" ]
then
SNAPSHOTS_OLD=$(env LC_ALL=C zfs list -H -t snapshot -o name -s name | \
grep "$opt_prefix"_"$opt_label" | \
awk '{ print substr( $0, length($0) - 14, length($0) ) " " $0}' | \
sort -r -k1,1 -k2,2 | \
awk '{ print substr( $0, 17, length($0) )}') \
|| { print_log error "zfs list $?: $SNAPSHOTS_OLD"; exit 137; }
else
SNAPSHOTS_OLD=$(env LC_ALL=C zfs list -H -t snapshot -o name -s name | \
grep $opt_prefix | \
awk '{ print substr( $0, length($0) - 14, length($0) ) " " $0}' | \
sort -r -k1,1 -k2,2 | \
awk '{ print substr( $0, 17, length($0) )}') \
|| { print_log error "zfs list $?: $SNAPSHOTS_OLD"; exit 137; }
fi
else
SNAPSHOTS_OLD=$(env LC_ALL=C zfs list -H -t snapshot -S creation -o name) \
|| { print_log error "zfs list $?: $SNAPSHOTS_OLD"; exit 137; }
fi
# Verify that each argument is a filesystem or volume.
for ii in "$@"
do
test "$ii" = '//' && continue 1
while read NAME PROPERTIES
do
test "$ii" = "$NAME" && continue 2
done <<-HERE
$ZFS_LIST
HERE
print_log error "$ii is not a ZFS filesystem or volume."
exit 138
done
# Get a list of pools that are being scrubbed.
ZPOOLS_SCRUBBING=$(echo "$ZPOOL_STATUS" | awk -F ': ' \
'$1 ~ /^ *pool$/ { pool = $2 } ; \
$1 ~ /^ *scan$/ && $2 ~ /scrub in progress/ { print pool }' \
| sort )
# Get a list of pools that cannot do a snapshot.
ZPOOLS_NOTREADY=$(echo "$ZPOOL_STATUS" | awk -F ': ' \
'$1 ~ /^ *pool$/ { pool = $2 } ; \
$1 ~ /^ *state$/ && $2 !~ /ONLINE|DEGRADED/ { print pool } ' \
| sort)
# Get a list of datasets for which snapshots are explicitly disabled.
NOAUTO=$(echo "$ZFS_LIST" | awk -F '\t' \
'tolower($2) ~ /false/ || tolower($3) ~ /false/ {print $1}')
# If the --default-exclude flag is set, then exclude all datasets that lack
# an explicit com.sun:auto-snapshot* property. Otherwise, include them.
if [ -n "$opt_default_exclude" ]
then
# Get a list of datasets for which snapshots are explicitly enabled.
CANDIDATES=$(echo "$ZFS_LIST" | awk -F '\t' \
'tolower($2) ~ /true/ || tolower($3) ~ /true/ {print $1}')
else
# Invert the NOAUTO list.
CANDIDATES=$(echo "$ZFS_LIST" | awk -F '\t' \
'tolower($2) !~ /false/ && tolower($3) !~ /false/ {print $1}')
fi
# Initialize the list of datasets that will get a recursive snapshot.
TARGETS_RECURSIVE=''
# Initialize the list of datasets that will get a non-recursive snapshot.
TARGETS_REGULAR=''
for ii in $CANDIDATES
do
# Qualify dataset names so variable globbing works properly.
# Suppose ii=tanker/foo and jj=tank sometime during the loop.
# Just testing "$ii" != ${ii#$jj} would incorrectly match.
iii="$ii/"
# Exclude datasets
# * that are not named on the command line or
# * those whose prefix is not on the command line (if --recursive flag is set)
IN_ARGS='0'
for jj in "$@"
do
# Ibid regarding iii.
jjj="$jj/"
if [ "$jj" = '//' -o "$jj" = "$ii" ]
then
IN_ARGS=$(( $IN_ARGS + 1 ))
elif [ -n "$opt_recursive" -a "$iii" != "${iii#$jjj}" ]
then
IN_ARGS=$(( $IN_ARGS + 1 ))
fi
done
if [ "$IN_ARGS" -eq '0' ]
then
continue
fi
# Exclude datasets in pools that cannot do a snapshot.
for jj in $ZPOOLS_NOTREADY
do
# Ibid regarding iii.
jjj="$jj/"
# Check whether the pool name is a prefix of the dataset name.
if [ "$iii" != "${iii#$jjj}" ]
then
print_log info "Excluding $ii because pool $jj is not ready."
continue 2
fi
done
# Exclude datasets in scrubbing pools if the --skip-scrub flag is set.
test -n "$opt_skip_scrub" && for jj in $ZPOOLS_SCRUBBING
do
# Ibid regarding iii.
jjj="$jj/"
# Check whether the pool name is a prefix of the dataset name.
if [ "$iii" != "${iii#$jjj}" ]
then
print_log info "Excluding $ii because pool $jj is scrubbing."
continue 2
fi
done
for jj in $NOAUTO
do
# Ibid regarding iii.
jjj="$jj/"
# The --recursive switch only matters for non-wild arguments.
if [ -z "$opt_recursive" -a "$1" != '//' ]
then
# Snapshot this dataset non-recursively.
print_log debug "Including $ii for regular snapshot."
TARGETS_REGULAR="${TARGETS_REGULAR:+$TARGETS_REGULAR }$ii" # nb: \t
continue 2
# Check whether the candidate name is a prefix of any excluded dataset name.
elif [ "$jjj" != "${jjj#$iii}" ]
then
# Snapshot this dataset non-recursively.
print_log debug "Including $ii for regular snapshot."
TARGETS_REGULAR="${TARGETS_REGULAR:+$TARGETS_REGULAR }$ii" # nb: \t
continue 2
fi
done
for jj in $TARGETS_RECURSIVE
do
# Ibid regarding iii.
jjj="$jj/"
# Check whether any included dataset is a prefix of the candidate name.
if [ "$iii" != "${iii#$jjj}" ]
then
print_log debug "Excluding $ii because $jj includes it recursively."
continue 2
fi
done
# Append this candidate to the recursive snapshot list because it:
#
# * Does not have an exclusionary property.
# * Is in a pool that can currently do snapshots.
# * Does not have an excluded descendent filesystem.
# * Is not the descendant of an already included filesystem.
#
print_log debug "Including $ii for recursive snapshot."
TARGETS_RECURSIVE="${TARGETS_RECURSIVE:+$TARGETS_RECURSIVE }$ii" # nb: \t
done
# Linux lacks SMF and the notion of an FMRI event, but always set this property
# because the SUNW program does. The dash character is the default.
SNAPPROP="-o com.sun:auto-snapshot-desc='$opt_event'"
# ISO style date; fifteen characters: YYYY-MM-DD-HHMM
# On Solaris %H%M expands to 12h34.
# We use the shortfirm -u here because --utc is not supported on macos.
DATE=$(date -u +%F-%H%M)
# The snapshot name after the @ symbol.
SNAPNAME="${opt_prefix:+$opt_prefix$opt_sep}${opt_label:+$opt_label}-$DATE"
# The expression for matching old snapshots. -YYYY-MM-DD-HHMM
SNAPGLOB="${opt_prefix:+$opt_prefix$opt_sep}${opt_label:+$opt_label}-???????????????"
if [ -n "$opt_do_snapshots" ]
then
test -n "$TARGETS_REGULAR" \
&& print_log info "Doing regular snapshots of $TARGETS_REGULAR"
test -n "$TARGETS_RECURSIVE" \
&& print_log info "Doing recursive snapshots of $TARGETS_RECURSIVE"
if test -n "$opt_keep" && [ "$opt_keep" -ge "1" ]
then
print_log info "Destroying all but the newest $opt_keep snapshots of each dataset."
fi
elif test -n "$opt_keep" && [ "$opt_keep" -ge "1" ]
then
test -n "$TARGETS_REGULAR" \
&& print_log info "Destroying all but the newest $opt_keep snapshots of $TARGETS_REGULAR"
test -n "$TARGETS_RECURSIVE" \
&& print_log info "Recursively destroying all but the newest $opt_keep snapshots of $TARGETS_RECURSIVE"
else
print_log notice "Only destroying snapshots, but count of snapshots to preserve not given. Nothing to do."
fi
test -n "$opt_dry_run" \
&& print_log info "Doing a dry run. Not running these commands..."
do_snapshots "$SNAPPROP" "" "$SNAPNAME" "$SNAPGLOB" "$TARGETS_REGULAR"
do_snapshots "$SNAPPROP" "-r" "$SNAPNAME" "$SNAPGLOB" "$TARGETS_RECURSIVE"
print_log notice "@$SNAPNAME," \
"$SNAPSHOT_COUNT created," \
"$DESTRUCTION_COUNT destroyed," \
"$WARNING_COUNT warnings."
exit 0
# }