├── .dockerignore ├── .editorconfig ├── .gitattributes ├── .github ├── stale.yml └── workflows │ ├── build.yml │ ├── build_deb_packages.yaml │ ├── release.yml │ ├── spelling.yml │ ├── spelling │ ├── excludes.txt │ ├── expect.txt │ └── patterns.txt │ └── test.yml ├── .gitignore ├── AUTHORS ├── CHANGES ├── CHANGES.old ├── COPYRIGHT ├── Dockerfile ├── LICENSE ├── Makefile.am ├── NEWS.md ├── README.md ├── VERSION ├── aclocal.m4 ├── bash_completion.d └── znapzendzetup ├── bin ├── znapzend ├── znapzendzetup └── znapzendztatz ├── bootstrap.sh ├── build_deb.sh ├── configure.ac ├── conftools ├── install-sh └── missing ├── contrib ├── autocreate-test.sh ├── synczbe └── test-splitHostDataSet.sh ├── cpanfile.common ├── cpanfile.test ├── debian ├── .gitignore ├── Makefile.am ├── changelog ├── compat ├── control ├── rules └── znapzend.links.in ├── doc ├── znapzend.pod ├── znapzendzetup.pod └── znapzendztatz.pod ├── init ├── README.md ├── org.znapzend.plist.in ├── znapzend.default ├── znapzend.freebsd.in ├── znapzend.service.in ├── znapzend.sysv.in ├── znapzend.upstart.in └── znapzend.xml.in ├── lib ├── Makefile.am ├── ZnapZend.pm └── ZnapZend │ ├── Config.pm │ ├── InheritLevels.pm │ ├── Time.pm │ └── ZFS.pm ├── man ├── znapzend.1 ├── znapzendzetup.1 └── znapzendztatz.1 ├── packaging └── checkinstall │ ├── README.md │ ├── checkinstall.sh │ ├── description-pak │ ├── postinstall-pak │ └── preremove-pak ├── release.sh ├── t ├── autoscrub.t ├── mbuffer ├── pfexec ├── ssh ├── sudo ├── zfs ├── znapzend-daemonize.t ├── znapzend-lib-splitter.t ├── znapzend.t ├── znapzendzetup.t ├── znapzendztatz.t └── zpool ├── test.sh └── thirdparty ├── Makefile.am ├── cpanfile-5.22.4.snapshot ├── cpanfile-5.30.snapshot ├── cpanfile-5.32.snapshot ├── cpanfile-5.34.0.snapshot ├── cpanfile-5.34.snapshot ├── cpanfile-5.36.snapshot └── cpanfile-5.38.snapshot /.dockerignore: -------------------------------------------------------------------------------- 1 | /Dockerfile 2 | /*.md 3 | /.gitignore 4 | /AUTHORS 5 | /CHANGES 6 | /COPYRIGHT 7 | /LICENSE 8 | /.git 9 | /.github 10 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | # EditorConfig is awesome: http://EditorConfig.org 2 | 3 | # top-most EditorConfig file 4 | root = true 5 | 6 | # Unix-style newlines with a newline ending every file 7 | [*] 8 | end_of_line = lf 9 | insert_final_newline = true 10 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | # Windows script files should be CR+LF always: 2 | *.bat text eol=crlf 3 | 4 | # Unix/Linux script files should be LF always: 5 | *.sh text eol=lf 6 | *.m4 text eol=lf 7 | *.ac text eol=lf 8 | *.am text eol=lf 9 | /conftools/* text eol=lf 10 | 11 | # Buildable sources (mostly for docker on windows builds to pass): 12 | *.in text eol=lf 13 | *.pm text eol=lf 14 | /bin/* text eol=lf 15 | 16 | # Some files are binary always: 17 | *.png bin 18 | *.ico bin 19 | 20 | # The rest are assumed text sources with platform-dependent EOL being okay, 21 | # or we let Git guess otherwise: 22 | #* text=auto 23 | * text=auto eol=lf 24 | -------------------------------------------------------------------------------- /.github/stale.yml: -------------------------------------------------------------------------------- 1 | # Number of days of inactivity before an issue becomes stale 2 | daysUntilStale: 60 3 | # Number of days of inactivity before a stale issue is closed 4 | daysUntilClose: 7 5 | # Issues with these labels will never be considered stale 6 | exemptLabels: 7 | - bug 8 | - security 9 | # Label to use when marking an issue as stale 10 | staleLabel: stale 11 | # Comment to post when marking an issue as stale. Set to `false` to disable 12 | markComment: > 13 | This issue has been automatically marked as stale because it has not had 14 | recent activity. It will be closed if no further activity occurs. Thank you 15 | for your contributions. 16 | # Comment to post when closing a stale issue. Set to `false` to disable 17 | closeComment: false -------------------------------------------------------------------------------- /.github/workflows/build.yml: -------------------------------------------------------------------------------- 1 | name: Build Docker 2 | 3 | on: 4 | push: 5 | branches-ignore: 6 | - master 7 | tags-ignore: 8 | - "*" 9 | pull_request: 10 | types: [opened, reopened] 11 | 12 | jobs: 13 | docker: 14 | runs-on: ubuntu-latest 15 | steps: 16 | - uses: actions/checkout@v2 17 | - name: Run test within image 18 | run: docker build --target test . 19 | -------------------------------------------------------------------------------- /.github/workflows/build_deb_packages.yaml: -------------------------------------------------------------------------------- 1 | name: Build .deb packages 2 | 3 | on: 4 | push: 5 | tags: 6 | - "v*" 7 | 8 | jobs: 9 | build_deb_packages: 10 | strategy: 11 | fail-fast: false 12 | matrix: 13 | include: 14 | - distribution: debian 15 | version: 10 16 | - distribution: debian 17 | version: 11 18 | - distribution: debian 19 | version: 12 20 | - distribution: ubuntu 21 | version: 18.04 22 | - distribution: ubuntu 23 | version: 20.04 24 | - distribution: ubuntu 25 | version: 22.04 26 | - distribution: ubuntu 27 | version: 24.04 28 | 29 | runs-on: ubuntu-latest 30 | name: Build package for ${{ matrix.distribution }} ${{ matrix.version }} 31 | container: 32 | image: ${{ matrix.distribution }}:${{ matrix.version }} 33 | env: 34 | DEBIAN_FRONTEND: noninteractive 35 | # small hack to make caches work because the cache action lives outside the container 36 | options: --mount type=volume,dst=/__w/znapzend/znapzend/,volume-driver=local,volume-opt=type=none,volume-opt=o=bind,volume-opt=device=${{ github.workspace }} 37 | defaults: 38 | run: 39 | shell: bash 40 | steps: 41 | - uses: actions/checkout@v3 42 | - name: Install OS requirements 43 | run: apt-get update && apt-get -yq install perl unzip autoconf carton debhelper pkg-config 44 | - name: CPAN cache 45 | id: cpan_cache 46 | uses: actions/cache@v3 47 | with: 48 | path: thirdparty 49 | key: ${{ matrix.distribution }}-cpan-${{ matrix.version }}-${{ hashFiles('cpanfile.common', '*/cpanfile', 'Makefile.am', '*/Makefile.am') }} 50 | - name: Build package 51 | id: build_package 52 | run: bash build_deb.sh ${{ matrix.distribution }} ${{ matrix.version }} 53 | - name: Release deb files 54 | uses: softprops/action-gh-release@v1 55 | if: startsWith(github.ref, 'refs/tags/') 56 | with: 57 | files: ${{ github.workspace }}/${{ steps.build_package.outputs.package_name }} 58 | env: 59 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 60 | -------------------------------------------------------------------------------- /.github/workflows/release.yml: -------------------------------------------------------------------------------- 1 | name: Release 2 | 3 | on: 4 | push: 5 | branches: 6 | - master 7 | tags: 8 | - "*" 9 | 10 | jobs: 11 | docker: 12 | runs-on: ubuntu-latest 13 | steps: 14 | - uses: actions/checkout@v2 15 | - name: Login to image registry 16 | run: docker login -u ${{ secrets.DOCKER_HUB_USERNAME }} -p ${{ secrets.DOCKER_HUB_PASSWORD }} 17 | - name: Build runtime image 18 | run: docker build --target runtime -t ${{ github.repository }}:${GITHUB_REF##*/} . 19 | - name: Run test within image 20 | run: docker build --target test . 21 | - name: Push runtime image 22 | run: docker push ${{ github.repository }}:${GITHUB_REF##*/} 23 | -------------------------------------------------------------------------------- /.github/workflows/spelling.yml: -------------------------------------------------------------------------------- 1 | name: Spell checking 2 | on: 3 | push: 4 | schedule: 5 | # * is a special character in YAML so you have to quote this string 6 | - cron: '15 * * * *' 7 | 8 | jobs: 9 | build: 10 | name: Spell checking 11 | runs-on: ubuntu-latest 12 | steps: 13 | - uses: actions/checkout@v2 14 | with: 15 | fetch-depth: 0 16 | extra_dictionaries: 17 | cspell:software-terms/dict/softwareTerms.txt 18 | cspell:filetypes/filetypes.txt 19 | # Languages below are not used in this project, but common terms are 20 | cspell:php/dict/php.txt 21 | cspell:node/dict/node.txt 22 | cspell:python/src/python/python-lib.txt 23 | - uses: check-spelling/check-spelling@v0.0.22 24 | env: 25 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 26 | bucket: .github/workflows/ 27 | project: spelling 28 | -------------------------------------------------------------------------------- /.github/workflows/spelling/excludes.txt: -------------------------------------------------------------------------------- 1 | ^debian/znapzend\.links\.in 2 | ^init/org\.znapzend\.plist\.in$ 3 | (?:^|/)go\.mod$ 4 | (?:^|/)go\.sum$ 5 | (?:^|/)package-lock\.json$ 6 | SUMS$ 7 | \.ai$ 8 | \.bmp$ 9 | \.cer$ 10 | \.class$ 11 | \.crl$ 12 | \.crt$ 13 | \.csr$ 14 | \.dll$ 15 | \.DS_Store$ 16 | \.eot$ 17 | \.eps$ 18 | \.exe$ 19 | \.gif$ 20 | \.graffle$ 21 | \.gz$ 22 | \.icns$ 23 | \.ico$ 24 | \.jar$ 25 | \.jpeg$ 26 | \.jpg$ 27 | \.key$ 28 | \.lib$ 29 | \.lock$ 30 | \.map$ 31 | \.min\.. 32 | \.mp3$ 33 | \.mp4$ 34 | \.otf$ 35 | \.pdf$ 36 | \.pem$ 37 | \.png$ 38 | \.psd$ 39 | \.sig$ 40 | \.so$ 41 | \.svg$ 42 | \.svgz$ 43 | \.tar$ 44 | \.tgz$ 45 | \.ttf$ 46 | \.woff 47 | \.xcf$ 48 | \.xls 49 | \.xpm$ 50 | \.yml$ 51 | \.zip$ 52 | \.1$ 53 | Makefile\.am$ 54 | ^AUTHORS$ 55 | ^CHANGES$ 56 | ^CHANGES\.old$ 57 | ^excludes\.txt$ 58 | ^whitelist\.txt$ 59 | ^expect\.txt$ 60 | ^\.github/workflows/spelling/ 61 | -------------------------------------------------------------------------------- /.github/workflows/spelling/expect.txt: -------------------------------------------------------------------------------- 1 | ABuild 2 | acconfig 3 | acinclude 4 | aclocal 5 | Aclosed 6 | adata 7 | admins 8 | Affero 9 | aix 10 | alpinelinux 11 | amd 12 | AMTAR 13 | ANDK 14 | anewconfig 15 | anotherchild 16 | anothersource 17 | api 18 | apk 19 | args 20 | ARGSTR 21 | ARGV 22 | asciiphil 23 | asdf 24 | astring 25 | atime 26 | ATOOMIC 27 | attr 28 | austingroupbugs 29 | autocommit 30 | autoconf 31 | autocreation 32 | autom 33 | automake 34 | automounting 35 | autoreconf 36 | autoscrub 37 | autotarget 38 | Autotools 39 | Backend 40 | backticks 41 | backupplan 42 | backupset 43 | badkey 44 | bak 45 | Balert 46 | bashisms 47 | bashrc 48 | Bcreate 49 | Bdebug 50 | Bdelete 51 | beadm 52 | Bedit 53 | bergulian 54 | Berr 55 | Bexport 56 | Bimport 57 | bindir 58 | Binfo 59 | blib 60 | Bnoaction 61 | Bnot 62 | bossert 63 | bpatsubst 64 | Bpidfile 65 | Bpost 66 | Bpre 67 | bserv 68 | Bsyslog 69 | buildpackage 70 | Bwarning 71 | Bzfs 72 | Bzip 73 | Bznapzend 74 | Bznapzendzetup 75 | Bznapzendztatz 76 | canmount 77 | CBuilder 78 | cdn 79 | cfg 80 | cgi 81 | cgit 82 | checkinstall 83 | chf 84 | chgrp 85 | chgrpcmd 86 | chgrpprog 87 | childds 88 | chkconfig 89 | chmod 90 | chmodcmd 91 | chmodprog 92 | chof 93 | chown 94 | chowncmd 95 | chownprog 96 | CHRISN 97 | chroot 98 | CJM 99 | cmds 100 | cmp 101 | cmpprog 102 | cnf 103 | codepath 104 | cois 105 | commandline 106 | compat 107 | compgen 108 | COMPREPLY 109 | condrestart 110 | config 111 | conftemp 112 | conftest 113 | conftools 114 | CONNEC 115 | Consor 116 | coprs 117 | copyleft 118 | coreutils 119 | COUNTFAIL 120 | COUNTOK 121 | cpan 122 | cpanfile 123 | cpanify 124 | cpanm 125 | cpanmin 126 | CPANSNAPV 127 | cpio 128 | cpprog 129 | crlf 130 | crt 131 | CVS 132 | Cwd 133 | CWORD 134 | CXX 135 | cygpath 136 | cygwin 137 | daemonization 138 | daemonize 139 | daemonized 140 | DAGOLDEN 141 | datadir 142 | DBD 143 | DBOOK 144 | de'u 145 | debhelper 146 | debian 147 | dedup 148 | defn 149 | defun 150 | deps 151 | dest 152 | DESTDIR 153 | destfail 154 | desttemp 155 | dirname 156 | dirs 157 | distclean 158 | distdir 159 | DISTRIBUTIONNAME 160 | distro 161 | dmp 162 | dnf 163 | dnl 164 | DNO 165 | dockerfile 166 | DOCTYPE 167 | DOITPROG 168 | donotask 169 | dpkg 170 | DRBASE 171 | dsa 172 | dsq 173 | dst 174 | dstbase 175 | dstdir 176 | dstdirslash 177 | dstds 178 | dstdsname 179 | DSTFILE 180 | dstname 181 | dsttmp 182 | DTDs 183 | DTIPS 184 | DTRT 185 | dummydataset 186 | ebuild 187 | ecdsa 188 | eckels 189 | egrep 190 | elif 191 | elsif 192 | enableval 193 | endif 194 | ENTRYPOINT 195 | envval 196 | envvar 197 | eol 198 | errline 199 | errmsg 200 | esac 201 | esyscmd 202 | ETag 203 | ev 204 | eventloop 205 | executables 206 | EXEEXT 207 | exitstatus 208 | exitval 209 | expr 210 | Extor 211 | EXTRADIST 212 | failsafes 213 | Fcntl 214 | fedorainfracloud 215 | fh 216 | fhp 217 | filehandle 218 | filepath 219 | fileset 220 | filesystem 221 | firstsnap 222 | FIXME 223 | fmri 224 | fnord 225 | foced 226 | foreach 227 | forkcall 228 | FQSN 229 | freebsd 230 | FRONTEND 231 | fsf 232 | ftp 233 | GARU 234 | Gedeo 235 | gerczei 236 | Getopt 237 | gh 238 | github 239 | gitignore 240 | gitlab 241 | gmail 242 | gmake 243 | gmtime 244 | gmx 245 | gnumake 246 | gnutar 247 | Gregy 248 | grep 249 | gtar 250 | gz 251 | Gzip 252 | HAARG 253 | hadfl 254 | hashmaps 255 | Hassler 256 | Heitm 257 | heitmueller 258 | HMSz 259 | homedir 260 | Honame 261 | hostname 262 | howtogeek 263 | html 264 | http 265 | Hyperlink 266 | Hypnotoad 267 | icap 268 | ico 269 | Icommand 270 | Icommon 271 | Icreate 272 | Idataset 273 | Idestroy 274 | Idocuments 275 | idx 276 | Iexport 277 | Ifacility 278 | ifdef 279 | Ifeature 280 | IFELSE 281 | Ifilepath 282 | ifndef 283 | ifset 284 | ifval 285 | Ihome 286 | Ilimited 287 | illumos 288 | imandir 289 | img 290 | incrementals 291 | ings 292 | INGY 293 | inh 294 | inheritedattr 295 | initscripts 296 | installinfo 297 | Inumber 298 | Inztructionz 299 | ioloop 300 | Ioptions 301 | Ip 302 | Ipath 303 | Ipictures 304 | Irecursive 305 | IRI 306 | IRIX 307 | iro 308 | isa 309 | isbn 310 | Isend 311 | Iskip 312 | Isnapshots 313 | Isnapsuffix 314 | Isources 315 | isrc 316 | Itank 317 | Ithirdparty 318 | ithreads 319 | Itimeout 320 | Iusbbackup 321 | Iuser 322 | Ivalue 323 | Iznapzendzetup 324 | JB 325 | JBERGER 326 | jenkins 327 | jimklimov 328 | json 329 | keygen 330 | killproc 331 | Klimov 332 | LASTCOMMON 333 | launchctl 334 | launchd 335 | Lce 336 | ldap 337 | ldapi 338 | Leay 339 | LEONT 340 | lgpl 341 | libdir 342 | LINENO 343 | linux 344 | listds 345 | listsnaps 346 | listsnapshots 347 | localattr 348 | localhost 349 | localtime 350 | loctext 351 | logbias 352 | logfile 353 | loglevel 354 | logto 355 | lowmem 356 | lpr 357 | LRds 358 | lsb 359 | ltrim 360 | mailprog 361 | mailto 362 | MAINPID 363 | Makefiles 364 | makeinfo 365 | manpage 366 | manpath 367 | manualsnap 368 | MAREKR 369 | mariadb 370 | mariadblock 371 | Mbuf 372 | mbuffer 373 | mbuffersize 374 | MConfig 375 | MDevel 376 | metadata 377 | MExt 378 | Meyering 379 | MIKIHOSHI 380 | MIO 381 | missingpool 382 | Mkbootstrap 383 | mkdir 384 | mkdirprog 385 | Mksymlists 386 | mms 387 | modled 388 | mojolicious 389 | Morbo 390 | mountpoint 391 | Mozilla 392 | MPTZONEROOTS 393 | mvcmd 394 | mvprog 395 | MYMETA 396 | myprint 397 | mysql 398 | NAL 399 | namespace 400 | namespaced 401 | nano 402 | NDTIPS 403 | newcfgdata 404 | newestbe 405 | nf 406 | nh 407 | nntp 408 | noaction 409 | noauto 410 | nobase 411 | nodelay 412 | nodestroy 413 | nodoc 414 | NOEXIT 415 | NONINFRINGEMENT 416 | nosets 417 | nostdinc 418 | notest 419 | NOTMAKE 420 | nroff 421 | NSTIPS 422 | nsub 423 | nvpool 424 | nytprof 425 | OALDERS 426 | OBJC 427 | OBJCXX 428 | Occitan 429 | OCSP 430 | ODBC 431 | oep 432 | oetiker 433 | oi 434 | oid 435 | ojo 436 | opencsw 437 | openindiana 438 | openlog 439 | openssh 440 | Overloader 441 | padlen 442 | pak 443 | passphrase 444 | passwordless 445 | perl 446 | perldoc 447 | PETDANCE 448 | PEVANS 449 | pfexec 450 | php 451 | pid 452 | pidfile 453 | pipefail 454 | Pipely 455 | PJCJ 456 | pkglicense 457 | pkgname 458 | pkgonly 459 | pkgrelease 460 | pkgsource 461 | pkgversion 462 | pkill 463 | plaintar 464 | plist 465 | plugin 466 | png 467 | poolrootfs 468 | populat 469 | posix 470 | prebuilt 471 | precmd 472 | prefork 473 | prereq 474 | primarycache 475 | printf 476 | printsrc 477 | propname 478 | propstring 479 | propval 480 | Proxmox 481 | psgi 482 | pstcmd 483 | pwd 484 | QNX 485 | qprefix 486 | qq 487 | qw 488 | qwe 489 | rbash 490 | RCAPUTO 491 | RCLAMP 492 | rcvar 493 | README 494 | recurseparent 495 | recv 496 | recvu 497 | redhat 498 | refquota 499 | refreservation 500 | regex 501 | regexing 502 | regexp 503 | remotehost 504 | RESFINAL 505 | respawn 506 | resync 507 | RETVAL 508 | RHEL 509 | rindex 510 | RJBS 511 | rlogin 512 | rmcmd 513 | rmdir 514 | rmprog 515 | rmtmp 516 | ron 517 | rootds 518 | rootfs 519 | rootfs'es 520 | rpool 521 | rr 522 | rsa 523 | rsync 524 | rtsp 525 | rtspu 526 | runlevel 527 | runonce 528 | rw 529 | sbin 530 | sbindir 531 | screation 532 | SCRIPTFILE 533 | scriptversion 534 | secondarycache 535 | SEENDST 536 | SEENSRC 537 | selftest 538 | sendmail 539 | Sereal 540 | setsid 541 | sftp 542 | shellwords 543 | shlibs 544 | shtml 545 | Sidama 546 | SIGHUP 547 | SIGINT 548 | SIGKILL 549 | SIGTERM 550 | smartmatch 551 | smf 552 | snapname 553 | snapsuffix 554 | snaptime 555 | snews 556 | SNO 557 | softprops 558 | solaris 559 | sourced 560 | sourcemissing 561 | sourcetype 562 | sourcing 563 | SPAMALOT 564 | spamming 565 | src 566 | srcame 567 | srcdir 568 | srcds 569 | srcdsname 570 | srcenabled 571 | srcenabledsuffix 572 | srcfile 573 | srcname 574 | srcsuffix 575 | ssh 576 | SSL 577 | standalone 578 | startd 579 | startstop 580 | stderr 581 | stdin 582 | stdout 583 | stefan 584 | stip 585 | strftime 586 | stringify 587 | stripcmd 588 | stripprog 589 | strptime 590 | subdataset 591 | SUBDIRS 592 | subr 593 | substr 594 | substvars 595 | subsys 596 | sudo 597 | sudoers 598 | SULLR 599 | svcadm 600 | svccfg 601 | svcdir 602 | svcinstall 603 | SVCINSTALLDIR 604 | svcname 605 | svg 606 | Svk 607 | svn 608 | Symdump 609 | symlink 610 | synczbe 611 | sys 612 | sysadmin 613 | sysconfdir 614 | syslog 615 | systemctl 616 | systemd 617 | sysutils 618 | sysv 619 | SZ 620 | TABCHAR 621 | tabledata 622 | Tagset 623 | tarball 624 | tardir 625 | TARNAME 626 | tattr 627 | telnet 628 | tempfile 629 | templated 630 | testsuite 631 | texi 632 | texinfo 633 | tfilesystem 634 | tgerczei 635 | thirdparty 636 | timeslot 637 | tinherit 638 | TION 639 | tium 640 | tlowmem 641 | TLS 642 | tmp 643 | tmpdir 644 | tobi 645 | todo 646 | TOTHINK 647 | tpropnames 648 | trecurse 649 | troff 650 | tsformat 651 | tsnapshot 652 | tzoffset 653 | ubuntu 654 | uid 655 | umask 656 | umontreal 657 | uncomment 658 | Uncompress 659 | unconfigured 660 | UNCONSIDER 661 | undef 662 | undef'ed 663 | unicode 664 | uniq 665 | unsubscribe 666 | Uplevel 667 | URI 668 | url 669 | usbbackup 670 | usedbysnapshot 671 | useradd 672 | userland 673 | usermod 674 | username 675 | userprop 676 | usr 677 | ustar 678 | utf 679 | Utils 680 | Validator 681 | varname 682 | ve 683 | versi 684 | versio 685 | VOS 686 | VPATH 687 | vroff 688 | vue 689 | waitpid 690 | westes 691 | wget 692 | whitespaces 693 | windoz 694 | withval 695 | WNOHANG 696 | WORKDIR 697 | workflow 698 | Wperl 699 | WSCHAR 700 | wu 701 | www 702 | wx 703 | xargs 704 | xcode 705 | xf 706 | xml 707 | xno 708 | xor 709 | Xproperties 710 | xset 711 | xtrue 712 | yacc 713 | yaml 714 | Ymd 715 | yml 716 | yy 717 | zbe 718 | ZConfig 719 | zetup 720 | zfs 721 | zfsinherit 722 | zfslist 723 | zfsutils 724 | zfsutilsdep 725 | ZFSW 726 | ZFSZONEROOTS 727 | ZL 728 | Zlib 729 | znap 730 | znapdest 731 | ZNAPVER 732 | znapzend 733 | ZNAPZENDOPTIONS 734 | znapzendsetup 735 | ZNAPZENDTEST 736 | znapzendzetup 737 | znapzendztatz 738 | zoneadm 739 | zoneroots 740 | zpool 741 | zxvf 742 | -------------------------------------------------------------------------------- /.github/workflows/spelling/patterns.txt: -------------------------------------------------------------------------------- 1 | cpanfile.+ 2 | -------------------------------------------------------------------------------- /.github/workflows/test.yml: -------------------------------------------------------------------------------- 1 | name: Run Tests 2 | 3 | on: 4 | push: 5 | branches: 6 | - master 7 | tags-ignore: 8 | - "*" 9 | pull_request: 10 | 11 | jobs: 12 | unit: 13 | runs-on: ubuntu-latest 14 | strategy: 15 | matrix: 16 | perl: 17 | - "5.32" 18 | - "5.30" 19 | - "5.26" 20 | - "5.36" 21 | # - "5.22" 22 | # - "5.18" 23 | # - "5.10" 24 | name: Perl ${{ matrix.perl }} 25 | steps: 26 | - uses: actions/checkout@v3 27 | - name: Setup perl 28 | uses: shogo82148/actions-setup-perl@v1 29 | with: 30 | perl-version: ${{ matrix.perl }} 31 | - run: ./bootstrap.sh 32 | - run: ./configure --prefix=$HOME/znapzend 33 | - run: make test-deps 34 | - run: ./test.sh 35 | - run: make install 36 | - run: "echo \"repo_token: ${{ secrets.GITHUB_TOKEN }}\" > .coveralls.yml" 37 | - run: perl -I./thirdparty/lib/perl5 thirdparty/bin/cover -report coveralls 38 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | blib/ 2 | .build/ 3 | _build/ 4 | cover_db/ 5 | inc/ 6 | autom4te.cache/ 7 | Build 8 | !Build/ 9 | Build.bat 10 | .last_cover_stats 11 | Makefile 12 | Makefile.old 13 | Makefile.in 14 | configure 15 | MANIFEST.bak 16 | META.yml 17 | MYMETA.yml 18 | nytprof.out 19 | pm_to_blib 20 | .#* 21 | autom4te.cache/ 22 | config.log 23 | config.status 24 | /cpanfile 25 | /cpanfile.snapshot 26 | thirdparty/* 27 | !thirdparty/Makefile.am 28 | !thirdparty/cpanfile*snapshot 29 | *.tar.gz 30 | init/znapzend.freebsd 31 | init/znapzend.service 32 | init/znapzend.sysv 33 | init/znapzend.upstart 34 | init/znapzend.xml 35 | init/org.znapzend.plist 36 | .DS_Store 37 | znapzend*.pid 38 | dump.dmp 39 | *.selftest-rewritten 40 | *~ 41 | conftools 42 | -------------------------------------------------------------------------------- /AUTHORS: -------------------------------------------------------------------------------- 1 | Dominik Hassler 2 | Tobias Oetiker 3 | -------------------------------------------------------------------------------- /CHANGES: -------------------------------------------------------------------------------- 1 | znapzend (0.23.2) UNRELEASED; urgency=medium 2 | 3 | * added missing cpanfile.test #662 4 | * build deb for ubuntu 24.4 5 | 6 | -- Tobi Oetiker Thu, 27 Jun 2024 12:10:28 +0200 7 | 8 | znapzend (0.23.0) unstable; urgency=medium 9 | 10 | * Fixed `autoCreation` behavior broken in 0.22.0 release -- @jimklimov 11 | * Updated recipes for `make check` to install Perl modules it needs 12 | (previously was only done as part of GitHub checks); renamed the 13 | `cpanfile` to `cpanfile.common` to avoid changing the Git-tracked 14 | resource file during build -- @jimklimov 15 | * Fix `make install` of Perl modules with a custom `DESTDIR` and/or when 16 | applying `configure --libdir=...` requirements -- @jimklimov 17 | * Renamed `lib/inheritLevels.pm` to `lib/ZnapZend/InheritLevels.pm`, in 18 | order to minimize confusion (especially when targeting OS-wide Perl 19 | module installations) -- @jimklimov 20 | * Applied same markup style to older CHANGES logged entries -- @jimklimov 21 | 22 | -- Tobi Oetiker Fri, 14 Jun 2024 14:42:00 +0200 23 | 24 | znapzend (0.22.0) unstable; urgency=medium 25 | 26 | * Add debian 12 package -- @moetiker 27 | * Maintenance release: refine splitting of 28 | `[[user@]host:]dataset[:with-colons][@snap[:with-colons]]` strings 29 | to work for the realistic majority of use-cases; fix back support 30 | of pool root dataset in such spec -- @jimklimov 31 | * Update self-tests with verification that 32 | `[[user@]host:]dataset[:with-colons][@snap[:with-colons]]` string 33 | decoding yields expected results -- @jimklimov 34 | * Extended handling of `org.znapzend:enabled=off` setting for sub-trees: 35 | now if the same intermediate dataset declares `org.znapzend:recursive=on`, 36 | the disablement of znapzend handling takes place for descendants as well 37 | (previously it had effect only exactly for datasets that had set 38 | `org.znapzend:enabled=off` as a local ZFS property) -- @jimklimov 39 | * Fixed CI recipes and contents for spell-checker -- @jimklimov 40 | * Added rc-script and integration documentation for FreeBSD and similar 41 | platforms -- @jimklimov 42 | * Converted `configure.ac` and numerous `Makefile.am` to avoid GNU Make 43 | syntax in favor of portability: tested with Solaris/illumos Sun make 44 | and with FreeBSD make -- @jimklimov 45 | * Extended `--autoCreation` effect (or lack thereof) to newly appearing 46 | sub-datasets; added a `--noautoCreation` option to help override 47 | configuration file settings (where used) -- @jimklimov 48 | * Introduced `dst_N_autocreation` setting via ZFS properties 49 | (per-destination, inheritable) -- @jimklimov 50 | 51 | -- Jim Klimov Tue, 12 Mar 2024 13:42:28 +0100 52 | 53 | znapzend (0.21.2) unstable; urgency=medium 54 | 55 | * Maintenance release: Automate `.deb` package builds 56 | 57 | -- Tobias Bossert Tue, 12 Apr 2023 10:12:54 +0100 58 | 59 | znapzend (0.21.1) unstable; urgency=medium 60 | 61 | * Clear log handle on receiving a `USR1` signal -- @oetiker 62 | * Removed dependency on `ForkCall` -- @fdd-rev 63 | * Fix regex in `splitHostDataSet` to understand dataset names with 64 | colons -- @jimklimov 65 | * Fix deletion of many snaps for many datasets, and handle several 66 | not-"enabled" sub-trees under one schedule -- @jimklimov 67 | * Try harder when faced with a restricted shell at the remote 68 | end -- @jimklimov 69 | 70 | -- Tobias Oetiker Tue, 20 Jan 2022 16:13:24 +0100 71 | 72 | znapzend (0.21.0) unstable; urgency=medium 73 | 74 | * Fixed delay redefined warning 75 | * Check if retention plans are sensible (error out on retention shorter than 76 | interval in retention=>interval expressions) 77 | * Fix mail program call sequence #540 -- @oetiker, @gchmurka123 78 | * Make aborted recv resumable using the `resume` feature -- @aarononeal 79 | 80 | -- Tobias Oetiker Mon, 28 Jun 202119:25:46 +0200 81 | 82 | znapzend (0.0.0) unstable; urgency=medium 83 | 84 | * Older changes available in CHANGES.old 85 | 86 | -- Tobias Oetiker Mon, 22 Feb 2021 08:38:28 +0100 87 | -------------------------------------------------------------------------------- /COPYRIGHT: -------------------------------------------------------------------------------- 1 | znapzend - A ZFS backup system 2 | 3 | Copyright by Dominik Hassler, Tobias Oetiker and the other people listed 4 | in the AUTHORS file. 5 | 6 | 2024-06-27 7 | 8 | All rights reserved. 9 | 10 | GNU GPL License 11 | =============== 12 | 13 | This program is free software; you can redistribute it and/or modify it 14 | under the terms of the GNU General Public License as published by the Free 15 | Software Foundation; either version 3 of the License, or (at your option) 16 | any later version. 17 | 18 | This program is distributed in the hope that it will be useful, but WITHOUT 19 | ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 20 | FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for 21 | more details. 22 | 23 | You should have received a copy of the GNU General Public License along 24 | with this program; if not, write to the Free Software Foundation, Inc., 25 | 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA 26 | -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | 2 | ##### Builder image 3 | ARG PERL_BUILD_VERSION=5.38-buster 4 | ARG ALPINE_VERSION=3.19 5 | FROM docker.io/library/perl:${PERL_BUILD_VERSION} as builder 6 | 7 | WORKDIR /usr/local/src 8 | 9 | COPY . /usr/local/src 10 | 11 | RUN \ 12 | ./bootstrap.sh && \ 13 | ./configure --prefix=/opt/znapzend && \ 14 | make && \ 15 | make install 16 | 17 | ##### Runtime image 18 | FROM docker.io/library/alpine:${ALPINE_VERSION} as runtime 19 | 20 | ARG PERL_VERSION=5.38.2-r0 21 | 22 | RUN \ 23 | # mbuffer is not in main currently, and community keys expire over time, 24 | # so gotta bump ALPINE_VERSION above regularly (and likely PERL_VERSION 25 | # as dictated by the newer OS release). Request its package first to fail 26 | # fast in such case. Note that while "--allow-untrusted" might be an option, 27 | # shared libraries referenced by the binary make an older distro outdated. 28 | apk add --no-cache --repository http://dl-cdn.alpinelinux.org/alpine/edge/community/ mbuffer && \ 29 | # nano is for the interactive "edit" command in znapzendzetup if preferred over vi 30 | apk add --no-cache zfs curl bash autoconf automake nano perl=${PERL_VERSION} openssh && \ 31 | ln -s /dev/stdout /var/log/syslog && \ 32 | ln -s /usr/bin/perl /usr/local/bin/perl 33 | 34 | COPY --from=builder /opt/znapzend/ /opt/znapzend 35 | 36 | RUN \ 37 | ln -s /opt/znapzend/bin/znapzend /usr/bin/znapzend && \ 38 | ln -s /opt/znapzend/bin/znapzendzetup /usr/bin/znapzendzetup && \ 39 | ln -s /opt/znapzend/bin/znapzendztatz /usr/bin/znapzendztatz 40 | 41 | ENTRYPOINT [ "/bin/bash", "-c" ] 42 | CMD [ "znapzend --logto=/dev/stdout" ] 43 | 44 | ##### Tests 45 | FROM builder as test 46 | 47 | RUN \ 48 | cpan Devel::Cover && \ 49 | cpan Test::SharedFork 50 | 51 | RUN \ 52 | ./test.sh 53 | -------------------------------------------------------------------------------- /Makefile.am: -------------------------------------------------------------------------------- 1 | # Author: Tobi Oetiker 2 | # License: Public Domain 3 | 4 | AUTOMAKE_OPTIONS = foreign 5 | 6 | SUFFIXES = .1 .man .pm 7 | 8 | SUBDIRS = lib thirdparty debian 9 | 10 | BIN = bin/@PACKAGE@ bin/znapzendzetup bin/znapzendztatz 11 | PM = @PERL_MODULES_EXTRA_DIST@ 12 | MAN = man/znapzend.1 man/znapzendzetup.1 man/znapzendztatz.1 13 | POD = doc/znapzend.pod doc/znapzendzetup.pod doc/znapzendztatz.pod 14 | 15 | RM_F = $(RM) -f 16 | 17 | GENERATED_EXTRADIST = $(MAN) 18 | EXTRA_DIST = VERSION COPYRIGHT README.md LICENSE CHANGES AUTHORS cpanfile.common cpanfile.test $(BIN) $(PM) \ 19 | $(GENERATED_EXTRADIST) init/README.md init/org.znapzend.plist.in init/znapzend.default \ 20 | init/znapzend.service.in init/znapzend.sysv.in init/znapzend.upstart.in init/znapzend.xml.in \ 21 | t/autoscrub.t t/ssh t/znapzend.t t/znapzendztatz.t t/mbuffer \ 22 | t/zfs t/znapzendzetup.t t/zpool \ 23 | debian/znapzend.links.in 24 | 25 | YEAR = @CONFIG_YEAR@ 26 | DATE = @CONFIG_DATE@ 27 | 28 | if ENABLE_SVCINSTALL 29 | 30 | svcdir = @SVCINSTALLDIR@ 31 | svc_DATA = init/znapzend.xml 32 | 33 | endif 34 | 35 | datadir = $(prefix) 36 | 37 | README.md COPYRIGHT: VERSION 38 | $(AM_V_GEN)$(PERL) -i -p -e 's/\d{4}-\d{2}-\d{2}/$(DATE)/g;s/[0-4]\.\d+\.\d+/$(PACKAGE_VERSION)/g' $@ 39 | 40 | README: README.md 41 | cp README.md README 42 | 43 | imandir = $(mandir)/man1 44 | iman_DATA = $(MAN) 45 | 46 | doc/%.pod: bin/% VERSION 47 | $(AM_V_GEN)mkdir -p doc;grep -A100000 '=head1 NAME' $< > $@ 48 | 49 | man/%.1: bin/% VERSION 50 | $(AM_V_GEN)mkdir -p man; test $(POD2MAN) = "no" || $(POD2MAN) --release=$(VERSION) --center=$(PACKAGE_NAME) $< > $@ 51 | 52 | dist_bin_SCRIPTS = $(BIN) 53 | 54 | # Regenerate MAN pages and perl POD files from original script sources 55 | docs: $(POD) $(MAN) 56 | 57 | dist-hook: docs 58 | $(PERL) -i -p -e 's/^my .*#\s*VERSION/my \$$VERSION = q{$(PACKAGE_VERSION)}; # VERSION/' $(distdir)/$(BIN) 59 | 60 | install-exec-hook: 61 | [ "$(PERL5LIB)" = "" ] || cd "$(DESTDIR)$(exec_prefix)" && $(PERL) -i -p -e 's{.*# PERL5LIB}{use lib qw($(PERL5LIB)); # PERL5LIB}' $(BIN) || true 62 | cd "$(DESTDIR)$(exec_prefix)" && $(PERL) -i -p -e 's{^use .*# LIBDIR}{use lib qw($(libdir)); # LIBDIR}' $(BIN) 63 | cd "$(DESTDIR)$(exec_prefix)" && $(PERL) -i -p -e 's{^#!.*perl.*}{#!$(PERL)};' $(BIN) 64 | 65 | cpanfile: cpanfile.common 66 | cp "$?" "$@" 67 | 68 | test-deps: 69 | +cd thirdparty && $(MAKE) $(AM_MAKEFLAGS) test-deps 70 | 71 | if !DEB_BUILD 72 | check: test-deps 73 | @echo "NOTE: You may want to also/instead run test.sh" >&2 74 | $(PERL) -Ithirdparty/lib/perl5 "-MExtUtils::Command::MM" "-e" "test_harness(1, 'lib','thirdparty/lib/perl5')" $(srcdir)/t/*.t 75 | endif 76 | 77 | # Clean up files that may be generated by the likes of 78 | # `DEBUG_ZNAPZEND_SELFTEST_REWRITE=yes ./t/znapzend.t` 79 | clean-selftest-rewritten: 80 | $(RM_F) */*.selftest-rewritten */*/*.selftest-rewritten 81 | 82 | clean-pidfiles: 83 | if test -n "`ls znapzend*.pid 2>/dev/null || true`" ; then \ 84 | kill -15 `cat znapzend*.pid` || true ; \ 85 | $(RM) znapzend*.pid ; \ 86 | fi 87 | 88 | clean-local: clean-selftest-rewritten clean-pidfiles 89 | -------------------------------------------------------------------------------- /NEWS.md: -------------------------------------------------------------------------------- 1 | # Release Notes 2 | 3 | ## Version 0.20.0 / 2020-03-23 4 | 5 | ### New Features 6 | 7 | * build system switched to carton for better dependency tracking 8 | 9 | * docker version available in `oetiker/znapzend:master` see README.md for details 10 | 11 | * `--recursive` option for run-once datasets. 12 | 13 | * `--inherited` allow run-once on datasets with only an inherited plan. 14 | 15 | * `--focedSnapshotSuffix=x` for non generated snapshot suffix in run-once. 16 | 17 | * `--nodelay` temporarily ignore delays in backup plans for speedy debugging. 18 | 19 | * new `--features` 20 | 21 | * `sendRaw` to NOT decrypt datasets prior to sending 22 | * `skipIntermediates` do not send intermediate snapshots 23 | * `lowmemRecurse` trade memory for speed when running large recursive jobs 24 | * `zfsGetType` speed up recursive dataset handling ... see znapzend manual page for details. 25 | 26 | * znapzendzetup supports `--features` too 27 | 28 | * new options for znapzendztatz: `--inherited` and `--setup` 29 | 30 | 31 | -------------------------------------------------------------------------------- /VERSION: -------------------------------------------------------------------------------- 1 | 0.23.2 2 | -------------------------------------------------------------------------------- /bash_completion.d/znapzendzetup: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # Copyright (c) 2016, Stefan Heitmüller 4 | 5 | # Permission is hereby granted, free of charge, to any person 6 | # obtaining a copy of this software and associated documentation 7 | # files (the "Software"), to deal in the Software without 8 | # restriction, including without limitation the rights to use, 9 | # copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | # copies of the Software, and to permit persons to whom the 11 | # Software is furnished to do so, subject to the following 12 | # conditions: 13 | 14 | # The above copyright notice and this permission notice shall be 15 | # included in all copies or substantial portions of the Software. 16 | 17 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 18 | # EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES 19 | # OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 20 | # NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT 21 | # HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, 22 | # WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 23 | # FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 24 | # OTHER DEALINGS IN THE SOFTWARE. 25 | 26 | if [[ -w /dev/zfs ]]; then 27 | __ZFS_CMD="zfs" 28 | __ZNAPZENDZETUP_CMD="znapzendzetup" 29 | else 30 | __ZFS_CMD="sudo zfs" 31 | __ZNAPZENDZETUP_CMD="sudo znapzendzetup" 32 | fi 33 | 34 | __znapzendzetup_get_commands() 35 | { 36 | $__ZNAPZENDZETUP_CMD | awk '/^\s{8}[a-z]/ {print $1}' | cut -f1 -d '|' | uniq | sort 37 | } 38 | 39 | __znapzendzetup_list_datasets() 40 | { 41 | $__ZNAPZENDZETUP_CMD list 2>&1 | grep -E '^*** backup plan: .* ***$' | awk '{print $(NF-1)}' | uniq | sort 42 | } 43 | 44 | __zfs_list_datasets() 45 | { 46 | $__ZFS_CMD list -H -o name -t filesystem,volume 47 | } 48 | 49 | __znapzendzetup_complete() 50 | { 51 | local cur prev cmd cmds 52 | COMPREPLY=() 53 | # Don't split on colon 54 | _get_comp_words_by_ref -n : -c cur -p prev -w COMP_WORDS -i COMP_CWORD 55 | cmd="${COMP_WORDS[1]}" 56 | 57 | if [[ ${prev##*/} == znapzendzetup ]] 58 | then 59 | cmds=$(__znapzendzetup_get_commands) 60 | COMPREPLY=($(compgen -W "$cmds -?" -- "$cur")) 61 | return 0 62 | fi 63 | 64 | case "${cmd}" in 65 | create|delete|edit|enable|disable|export|list) 66 | COMPREPLY=($(compgen -W "$(__znapzendzetup_list_datasets)" -- "$cur")) 67 | ;; 68 | import) 69 | COMPREPLY=($(compgen -W "$(__zfs_list_datasets)" -- "$cur")) 70 | ;; 71 | esac 72 | 73 | __ltrim_colon_completions "$cur" 74 | return 0 75 | } 76 | 77 | complete -F __znapzendzetup_complete znapzendzetup 78 | -------------------------------------------------------------------------------- /bin/znapzendztatz: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env perl 2 | 3 | use lib qw(); # PERL5LIB 4 | use FindBin; use lib "$FindBin::RealBin/../lib", "$FindBin::RealBin/../thirdparty/lib/perl5"; # LIBDIR 5 | 6 | use Getopt::Long qw(:config posix_default no_ignore_case); 7 | use Pod::Usage; 8 | 9 | use Mojo::Base -strict; 10 | use Mojo::Log; 11 | use ZnapZend::Config; 12 | use ZnapZend::ZFS; 13 | my $VERSION = q{0.23.2}; # VERSION 14 | my $zConfig; 15 | my $zZfs; 16 | my $zTime = ZnapZend::Time->new(); 17 | 18 | sub dumpStats { 19 | my $stats = shift; 20 | my $tabs = shift; 21 | my $setup = shift; 22 | 23 | if (! $tabs) { 24 | print ' ' x (5 - length($stats->{usage})); 25 | } 26 | print $stats->{usage}; 27 | print ($tabs ? "\t" : " "); 28 | print "$stats->{last_snap}"; 29 | print ($tabs ? "\t" : " "); 30 | print "$stats->{dataset}"; 31 | if ($setup) { 32 | print ($tabs ? "\t" : " "); 33 | print "$stats->{definitionDataset}"; 34 | print ($tabs ? "\t" : " "); 35 | print "$stats->{key}"; 36 | } 37 | print "\n"; 38 | } 39 | 40 | sub collectData { 41 | my $definitionDataset = shift; 42 | my $key = shift; 43 | my $dataset = shift; 44 | my $snapFilter = shift; 45 | my %data; 46 | my $snapshots = $zZfs->listSnapshots($dataset, $snapFilter); 47 | 48 | $data{usage} = $zZfs->usedBySnapshots($dataset); 49 | my $lastSnap = $snapshots->[-1] // ' @No Snapshots Yet '; 50 | ($data{last_snap}) = $lastSnap =~ /^.+\@([^\@]+)$/; 51 | $data{dataset} = $dataset; 52 | $data{definitionDataset} = $definitionDataset; 53 | $data{key} = $key; 54 | 55 | return \%data; 56 | } 57 | 58 | sub main { 59 | my $opts = {}; 60 | 61 | GetOptions($opts, qw(H help|h recursive|r only-enabled inherited pfexec debug sudo rootExec=s man timeWarp=i setup)) or exit 1; 62 | 63 | if ($opts->{pfexec}) { 64 | warn "--pfexec is deprecated. Use --rootExec=pfexec instead\n"; 65 | $opts->{rootExec} = 'pfexec'; 66 | } elsif ($opts->{sudo}) { 67 | warn "--sudo is deprecated. Use --rootExec=sudo instead\n"; 68 | $opts->{rootExec} = 'sudo'; 69 | } 70 | 71 | if (defined($opts->{debug})) { 72 | $opts->{debug} = ( $opts->{debug} eq 'off' ? 0 : 1 ); 73 | } else { 74 | $opts->{debug} = 0; 75 | } 76 | 77 | my $zLog = Mojo::Log->new(); 78 | $zZfs = ZnapZend::ZFS->new(zLog => $zLog, 79 | rootExec => $opts->{rootExec}, 80 | debug => $opts->{debug}); 81 | $zConfig = ZnapZend::Config->new(zLog => $zLog, 82 | rootExec => $opts->{rootExec}, 83 | timeWarp => $opts->{timeWarp}, 84 | debug => $opts->{debug}); 85 | 86 | $opts->{help} && do { 87 | pod2usage(-exitval => 'NOEXIT'); 88 | 89 | ### RM_COMM_4_TEST ### # remove ### RM_COMM_4_TEST ### comments for testing purpose. 90 | ### RM_COMM_4_TEST ### $opts = {}; 91 | 92 | return 1; 93 | }; 94 | $opts->{man} && pod2usage(-exitstatus => 0, -verbose => 2); 95 | 96 | # MAYBE make $enabledOnly parameter public in Config.pm 97 | my $recurse = (defined($opts->{"recursive"})); 98 | my $inherit = (defined($opts->{"inherited"})); 99 | my $backupSets = ($opts->{"only-enabled"} 100 | ? $zConfig->getBackupSetEnabled($recurse, $inherit, pop @ARGV) 101 | : $zConfig->getBackupSet($recurse, $inherit, pop @ARGV)) 102 | or die "ERROR: cannot list backup config\n"; 103 | 104 | #check if there is at least one valid backup set 105 | @$backupSets or die "ERROR: no valid znapzend setup found on source\n"; 106 | 107 | my $dummyTime = $zTime->createSnapshotTime(time + ($opts->{timeWarp} // 0), $backupSets->[0]->{tsformat}); 108 | 109 | #print header 110 | if (!$opts->{H}) { 111 | my $padlen = (length($dummyTime) - length('LAST SNAPSHOT'));; 112 | $padlen = 0 if ($padlen < 0); 113 | print 'USED LAST SNAPSHOT' . ' ' x $padlen . ($opts->{H} ? "\t" : " ") . "DATASET"; 114 | if ($opts->{setup}) { 115 | print ' SETUP FS SETUP KEY'; 116 | } 117 | print "\n"; 118 | } 119 | 120 | for my $backupSet (@$backupSets){ 121 | my $datasets = $backupSet->{recursive} eq 'on' && $opts->{recursive} 122 | ? $zZfs->listSubDataSets($backupSet->{src}) : [$backupSet->{src}]; 123 | 124 | # TOTHINK: Consider forcedSnapshotSuffix here? Not really known outside of bin/znapzend anyway... 125 | my $snapFilter = $zTime->getSnapshotFilter($backupSet->{tsformat}); 126 | 127 | #source dataset 128 | for my $dataset (@$datasets){ 129 | dumpStats(collectData($backupSet->{src}, "src", $dataset, $snapFilter), $opts->{H}, $opts->{setup}); 130 | 131 | #destination datasets 132 | for (keys %$backupSet){ 133 | my ($key) = /^(dst_[^_]+)$/ or next; 134 | 135 | #skipping destination if it does not exists 136 | if (!$backupSet->{$key . '_valid'}){ 137 | print "*** WARNING: destination '$backupSet->{$key}'" 138 | . " does not exist! ***\n\n"; 139 | } 140 | else{ 141 | my $dstDataSet = $dataset; 142 | $dstDataSet =~ s/^$backupSet->{src}/$backupSet->{$key}/; 143 | dumpStats(collectData($backupSet->{src}, $key, $dstDataSet, $snapFilter), $opts->{H}, $opts->{setup}); 144 | } 145 | } 146 | 147 | } 148 | } 149 | return 1; 150 | } 151 | 152 | main(); 153 | 154 | 1; 155 | 156 | __END__ 157 | 158 | =head1 NAME 159 | 160 | znapzendztatz - znapzend statistics utility 161 | 162 | =head1 SYNOPSIS 163 | 164 | B [I...] [src_dataset] 165 | 166 | --debug print debugging details from some library routines 167 | -H do not print headers and separate fields by a single tab 168 | instead of arbitrary white space 169 | -r,--recursive show statistics for dataset and sub datasets 170 | --inherited allow to specify a dataset which just inherits a backup plan 171 | --only-enabled only show statistics for enabled datasets 172 | --setup show the configuration key and the filesystem that defined the configuration 173 | --rootExec=x exec zfs with this command to obtain root privileges (sudo or pfexec) 174 | --timeWarp=x act as if you were shifted by x seconds into the future 175 | --man show man-page and exit 176 | -h,--help display this help and exit 177 | 178 | =head1 DESCRIPTION 179 | 180 | znapzendztatz shows statistics of snapshots created and storage space usage 181 | 182 | =head1 COPYRIGHT 183 | 184 | Copyright (c) 2014 by OETIKER+PARTNER AG. All rights reserved. 185 | 186 | =head1 LICENSE 187 | 188 | This program is free software: you can redistribute it and/or modify it 189 | under the terms of the GNU General Public License as published by the Free 190 | Software Foundation, either version 3 of the License, or (at your option) 191 | any later version. 192 | 193 | This program is distributed in the hope that it will be useful, but WITHOUT 194 | ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 195 | FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for 196 | more details. 197 | 198 | You should have received a copy of the GNU General Public License along with 199 | this program. If not, see L. 200 | 201 | =head1 AUTHOR 202 | 203 | Stobi@oetiker.chE> 204 | Shadfl@cpan.orgE> 205 | 206 | =head1 HISTORY 207 | 208 | 2014-06-29 had Flexible snapshot time format 209 | 2014-06-05 had Initial Version 210 | 211 | =cut 212 | -------------------------------------------------------------------------------- /bootstrap.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | autoreconf --force --install --verbose --make || exit 3 | #if (which git 2>/dev/null >/dev/null) && test -d .git; then 4 | # git log --full-history --simplify-merges --dense --no-merges > CHANGES 5 | #fi 6 | # EOF 7 | -------------------------------------------------------------------------------- /build_deb.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | set -ex 3 | 4 | DISTRIBUTION_NAME=$1 5 | DISTRIBUTION_VERSION=$2 6 | 7 | #Overriding $HOME to prevent permissions issues when running on github actions 8 | mkdir -p /tmp/home 9 | chmod 0777 /tmp/home 10 | export HOME=/tmp/home 11 | 12 | dh_clean 13 | dpkg-buildpackage -us -uc -nc 14 | 15 | # set filename 16 | # znapzend_$VERSION~$DISTRIBUTIONNAME$DISTRIBUTION_VERSION_amd64.deb 17 | release_number=${DISTRIBUTION_VERSION:0:2} 18 | package_name=$(basename ../znapzend_*.deb | sed 's/_amd64.deb$//')~${DISTRIBUTION_NAME}${release_number}_amd64.deb 19 | mv ../znapzend_*.deb "$package_name" 20 | 21 | # set action output 22 | echo "package_name=$package_name" >> $GITHUB_OUTPUT 23 | -------------------------------------------------------------------------------- /configure.ac: -------------------------------------------------------------------------------- 1 | # Author: Tobi Oetiker 2 | # License: Public Domain 3 | 4 | AC_INIT([znapzend],[m4_esyscmd(tr -d '\n' < VERSION)],[tobi@oetiker.ch]) 5 | AC_PREREQ([2.69]) 6 | AC_CONFIG_AUX_DIR(conftools) 7 | 8 | AC_MSG_CHECKING(in to see how you are doing) 9 | AC_MSG_RESULT(keep fighting man!) 10 | 11 | # need this to allow long path names 12 | AM_INIT_AUTOMAKE([1.9 tar-ustar foreign no-dependencies no-installinfo no-texinfo.tex nostdinc]) 13 | AM_MAINTAINER_MODE 14 | 15 | m4_ifdef([AM_SILENT_RULES], [AM_SILENT_RULES([yes])]) 16 | 17 | AC_PREFIX_DEFAULT(/opt/$PACKAGE_NAME-$PACKAGE_VERSION) 18 | dnl Fix this early so we can expand with eval later; note literal single quotes in exec_prefix 19 | AS_IF([test x"${prefix}" = x"NONE"], [prefix="${ac_default_prefix}"]) 20 | AS_IF([test x"${exec_prefix}" = x"NONE"], [exec_prefix='${prefix}']) 21 | 22 | AC_ARG_VAR(PERL, [Path to local perl binary]) 23 | AC_PATH_PROG(PERL, perl, no) 24 | AC_PATH_PROG(CURL, curl, no) 25 | AC_PATH_PROG(WGET, wget, no) 26 | AC_PATH_PROG(POD2MAN, pod2man, no) 27 | AC_PATH_PROG(RM, rm, no) 28 | 29 | AC_MSG_CHECKING([for CONFIG_YEAR and CONFIG_DATE]) 30 | CONFIG_YEAR="`date +%Y`" 31 | CONFIG_DATE="`date +%Y-%m-%d`" 32 | AC_MSG_RESULT([got ${CONFIG_YEAR} and ${CONFIG_DATE} respectively]) 33 | AC_SUBST(CONFIG_YEAR) 34 | AC_SUBST(CONFIG_DATE) 35 | 36 | AC_ARG_ENABLE([deb_build], 37 | AS_HELP_STRING([--enable-deb_build Enable debian package build specifics]), 38 | [deb_build=true], 39 | [deb_build=false]) 40 | 41 | AM_CONDITIONAL([DEB_BUILD], [test x$deb_build = xtrue]) 42 | 43 | # TODO: with some respect to legacy, split such settings to be named 44 | # after respective service management framework; code supports several 45 | AC_ARG_ENABLE(svcinstall, 46 | AS_HELP_STRING([--enable-svcinstall=DIR],[install svc manifest into given directory 47 | ]), 48 | [SVCINSTALLDIR=$enableval], 49 | [SVCINSTALLDIR=no] 50 | ) 51 | AC_SUBST(SVCINSTALLDIR) 52 | AM_CONDITIONAL(ENABLE_SVCINSTALL,[test $SVCINSTALLDIR != no]) 53 | 54 | SVCNAME_SMF="oep/znapzend" 55 | AC_ARG_WITH(svcname-smf, 56 | AS_HELP_STRING([--with-svcname-smf=SMF_FMRI],[use specified SMF FMRI in manifest (defaults to oep/znapzend) 57 | ]), 58 | [AC_MSG_CHECKING([user-provided SMF service name of '$withval']) 59 | SVCNAME_SMF="`echo "$withval" | sed -e 's,^svc:,,' -e 's,^/*,,' -e 's,:.*$,,'`" 60 | ],[AC_MSG_CHECKING([default SMF service name])] 61 | ) 62 | AC_MSG_RESULT([$SVCNAME_SMF]) 63 | AC_SUBST(SVCNAME_SMF) 64 | 65 | AC_MSG_CHECKING(for way to cat URLs) 66 | URL_CAT="" 67 | DEFAULT_URL_CAT_CURL="$CURL --location --insecure" 68 | DEFAULT_URL_CAT_WGET="$WGET -O -" 69 | 70 | AC_ARG_WITH(url-cat, 71 | AS_HELP_STRING([--with-url-cat=(curl|wget)],[use specified program (with further predefined arguments added as needed) to cat URLs 72 | ]), 73 | [AS_CASE("$withval", 74 | [curl],[AS_IF([test -n "$CURL" && test -x "$CURL"], 75 | [URL_CAT="$DEFAULT_URL_CAT_CURL"], 76 | [AC_MSG_ERROR([Asked to preconfigure use of $withval but one is not in PATH])])], 77 | [wget],[AS_IF([test -n "$WGET" && test -x "$WGET"], 78 | [URL_CAT="$DEFAULT_URL_CAT_WGET"], 79 | [AC_MSG_ERROR([Asked to preconfigure use of $withval but one is not in PATH])])], 80 | [*],[AC_MSG_ERROR([Asked to preconfigure use of $withval which is not an URL cat-er we support])])], 81 | [AC_ARG_WITH(url-cat-program, 82 | AS_HELP_STRING([--with-url-cat-program=(/path/name --args)],[use specified custom program with arguments to cat URLs 83 | ]), 84 | [URL_CAT="$withval"], 85 | [ 86 | AS_IF([test -x "$WGET"],[URL_CAT="$DEFAULT_URL_CAT_WGET"], 87 | [AS_IF([test -x "$CURL"],[URL_CAT="$DEFAULT_URL_CAT_CURL"])] 88 | )]) 89 | ]) 90 | 91 | AS_IF([test -z "$URL_CAT"], 92 | [URL_CAT="false : neither curl nor wget found, nor --with-url-cat-program=... specified"]) 93 | 94 | AC_MSG_RESULT([$URL_CAT]) 95 | AC_SUBST(URL_CAT) 96 | 97 | 98 | # Note: current Mojolicious as of mid-2020 requires even Perl 5.16+ 99 | # Older versions needed 5.10.1+ 100 | ac_perl_version="5.16.0" 101 | 102 | if test "x$PERL" != "x"; then 103 | AC_MSG_CHECKING(for perl version greater than or equal to $ac_perl_version) 104 | $PERL -e "use $ac_perl_version;" >/dev/null 2>&1 105 | if test $? -ne 0; then 106 | AC_MSG_RESULT(no); 107 | AC_MSG_WARN(at least version $ac_perl_version is required to run modern mojolicious) 108 | fi 109 | else 110 | AC_MSG_ERROR(could not find perl) 111 | fi 112 | AC_SUBST(MOJOLICIOUS_VERSION_CONSTRAINT) 113 | # if test -n "$MOJOLICIOUS_VERSION_CONSTRAINT" ; then 114 | # AC_MSG_WARN(The available Perl version limits mojolicious version$MOJOLICIOUS_VERSION_CONSTRAINT) 115 | # fi 116 | 117 | AC_MSG_CHECKING(is perl reasonably complete) 118 | if $PERL -MExtUtils::MakeMaker -e '' 2>/dev/null; then 119 | AC_MSG_RESULT(yes. ExtUtils::MakeMaker is available); 120 | else 121 | AC_MSG_RESULT(no) 122 | AC_MSG_ERROR([a complete perl ('perl-core' in the redhat world) installation is required]) 123 | fi 124 | 125 | AC_MSG_CHECKING([if require a c compiler to get perl modules compiled]) 126 | if $PERL -MIO::Socket::IP -e 'exit($IO::Socket::IP::VERSION < 0.37)' 2>/dev/null; then 127 | AC_MSG_RESULT(no) 128 | else 129 | AC_MSG_RESULT(yes) 130 | perl_cc=`$PERL -MConfig -e 'print $Config{cc}'` 131 | AC_PATH_PROG(PERL_CC_PATH, $perl_cc, no) 132 | AC_MSG_CHECKING(is perls favorite c compiler ($perl_cc) available) 133 | if test x$PERL_CC_PATH = xno; then 134 | AC_MSG_RESULT(no) 135 | AC_MSG_ERROR([perl needs the '$perl_cc' compiler package to build dependencies]) 136 | else 137 | AC_MSG_RESULT(yes) 138 | fi 139 | fi 140 | 141 | AC_PROG_GREP 142 | 143 | dnl We tried to make te Makefile.am syntax portable, but hiccups are possible 144 | AC_ARG_VAR(GMAKE, [Path to local GNU Make binary]) 145 | AC_PATH_PROGS(GMAKE, [gnumake gmake make]) 146 | 147 | AC_MSG_CHECKING([for gnu make availability]) 148 | if ( $GMAKE --version 2> /dev/null | $GREP GNU > /dev/null 2>&1 ); then 149 | AC_MSG_RESULT([$GMAKE is GNU make]) 150 | else 151 | AC_MSG_WARN([GNU make not found, build recipes might fail. Try setting the GMAKE environment variable.]) 152 | fi 153 | 154 | AC_ARG_ENABLE(pkgonly, 155 | AS_HELP_STRING([--enable-pkgonly],[Skip all checking])) 156 | AC_SUBST(enable_pkgonly) 157 | 158 | # TODO: Is this block needed? No code seems to refer this varname... 159 | actual_prefix=$prefix 160 | if test x$actual_prefix = xNONE; then 161 | actual_prefix=$ac_default_prefix 162 | fi 163 | 164 | AC_MSG_CHECKING(the price for bergulian eckels) 165 | AC_MSG_RESULT(way to expensive!) 166 | 167 | AC_ARG_VAR(PERL5LIB, [Colon separated list of perl library directories]) 168 | AC_SUBST(PERL5LIB) 169 | 170 | AC_MSG_CHECKING([for PERL_MODULES_EXTRA_DIST]) 171 | PERL_MODULES_EXTRA_DIST="`find "${srcdir}/lib/" -name "*.pm" | sed "s,^${srcdir}/,"'$(top_srcdir)/', | tr '\n' ' '`" 172 | AC_MSG_RESULT([${PERL_MODULES_EXTRA_DIST}]) 173 | AC_SUBST(PERL_MODULES_EXTRA_DIST) 174 | 175 | AC_MSG_CHECKING([for PERL_MODULES]) 176 | PERL_MODULES="`cd "${srcdir}/lib/" && ( find . -name "*.pm" | sed 's,^\./,,' | tr '\n' ' ' )`" 177 | AC_MSG_RESULT([${PERL_MODULES}]) 178 | AC_SUBST(PERL_MODULES) 179 | 180 | AC_MSG_CHECKING([for PERL_CONFIG_VERSION]) 181 | PERL_CONFIG_VERSION="`${PERL} -MConfig -e 'my $v = $Config{version}; $v =~ s/\.\d+$//; print $v;'`" 182 | AC_MSG_RESULT([${PERL_CONFIG_VERSION}]) 183 | AC_SUBST(PERL_CONFIG_VERSION) 184 | 185 | AC_MSG_CHECKING([for PERL_THIRDPARTY_DIST cache]) 186 | PERL_THIRDPARTY_DIST="`cd thirdparty && test -d cache && find cache -type f | tr '\n' ' '`" 187 | AS_IF([test x"${PERL_THIRDPARTY_DIST}" = x], [AC_MSG_RESULT([empty])], [AC_MSG_RESULT([populated])]) 188 | AC_SUBST(PERL_THIRDPARTY_DIST) 189 | 190 | dnl Autotools default expansion of path settings results in further $variables 191 | dnl so for practical use in static configs this yarn has to be fully unrolled: 192 | dnl expand ${prefix} and write it out 193 | conftemp="${prefix}" 194 | eval conftemp=\"${conftemp}\" 195 | eval conftemp=\"${conftemp}\" 196 | PREFIX=${conftemp} 197 | AC_DEFINE_UNQUOTED(PREFIX, "${conftemp}", [Default base path for architecture-independent product files]) 198 | AC_SUBST(PREFIX) 199 | 200 | dnl same for exec_prefix 201 | conftemp="${exec_prefix}" 202 | eval conftemp=\"${conftemp}\" 203 | eval conftemp=\"${conftemp}\" 204 | EXEC_PREFIX=${conftemp} 205 | AC_DEFINE_UNQUOTED(EXEC_PREFIX, "${conftemp}", [Default base path for architecture-dependent product files]) 206 | AC_SUBST(EXEC_PREFIX) 207 | 208 | dnl same for datadir 209 | conftemp="${datadir}" 210 | eval conftemp=\"${conftemp}\" 211 | eval conftemp=\"${conftemp}\" 212 | DATADIR=${conftemp} 213 | AC_DEFINE_UNQUOTED(DATADIR, "${conftemp}", [Default path for data files]) 214 | AC_SUBST(DATADIR) 215 | 216 | dnl same for sysconfdir 217 | conftemp="${sysconfdir}" 218 | eval conftemp=\"${conftemp}\" 219 | eval conftemp=\"${conftemp}\" 220 | SYSCONFDIR=${conftemp} 221 | AC_DEFINE_UNQUOTED(SYSCONFDIR, "${conftemp}", [Default path for configuration files]) 222 | AC_SUBST(SYSCONFDIR) 223 | 224 | dnl same for bindir 225 | conftemp="${bindir}" 226 | eval conftemp=\"${conftemp}\" 227 | eval conftemp=\"${conftemp}\" 228 | BINDIR=${conftemp} 229 | AC_DEFINE_UNQUOTED(BINDIR, "${conftemp}", [Default path for user executables]) 230 | AC_SUBST(BINDIR) 231 | 232 | dnl same for sbindir 233 | conftemp="${sbindir}" 234 | eval conftemp=\"${conftemp}\" 235 | eval conftemp=\"${conftemp}\" 236 | SBINDIR=${conftemp} 237 | AC_DEFINE_UNQUOTED(SBINDIR, "${conftemp}", [Default path for system executables]) 238 | AC_SUBST(SBINDIR) 239 | 240 | dnl same for libdir 241 | conftemp="${libdir}" 242 | eval conftemp=\"${conftemp}\" 243 | eval conftemp=\"${conftemp}\" 244 | LIBDIR=${conftemp} 245 | AC_DEFINE_UNQUOTED(LIBDIR, "${conftemp}", [Default path for system libraries]) 246 | AC_SUBST(LIBDIR) 247 | 248 | AC_MSG_NOTICE([Generating "data" files from templates, see below for executable scripts]) 249 | AC_CONFIG_FILES([ 250 | Makefile 251 | thirdparty/Makefile 252 | lib/Makefile 253 | debian/Makefile 254 | debian/znapzend.links 255 | init/znapzend.service 256 | init/znapzend.xml 257 | init/org.znapzend.plist 258 | ]) 259 | 260 | AC_MSG_NOTICE([Generating templated script files that should be marked executable]) 261 | m4_foreach_w([SCRIPTFILE], [ 262 | init/znapzend.freebsd 263 | init/znapzend.sysv 264 | init/znapzend.upstart 265 | ], [ 266 | dnl Autoconf substitutes the token above specified in plain text, 267 | dnl e.g. the brace below is empty and bracket gives verbatim varname 268 | dnl AC_MSG_NOTICE([Script: SCRIPTFILE brace:(${SCRIPTFILE}) bracket:([SCRIPTFILE])]) 269 | AC_CONFIG_FILES(SCRIPTFILE, chmod +x "SCRIPTFILE") 270 | ]) 271 | 272 | AC_SUBST(VERSION) 273 | 274 | AC_OUTPUT 275 | 276 | if test x$mod_ok = x0; then 277 | cat <, 1996. 8 | 9 | # This program is free software; you can redistribute it and/or modify 10 | # it under the terms of the GNU General Public License as published by 11 | # the Free Software Foundation; either version 2, or (at your option) 12 | # any later version. 13 | 14 | # This program is distributed in the hope that it will be useful, 15 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 16 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 17 | # GNU General Public License for more details. 18 | 19 | # You should have received a copy of the GNU General Public License 20 | # along with this program. If not, see . 21 | 22 | # As a special exception to the GNU General Public License, if you 23 | # distribute this file as part of a program that contains a 24 | # configuration script generated by Autoconf, you may include it under 25 | # the same distribution terms that you use for the rest of that program. 26 | 27 | if test $# -eq 0; then 28 | echo 1>&2 "Try '$0 --help' for more information" 29 | exit 1 30 | fi 31 | 32 | case $1 in 33 | 34 | --is-lightweight) 35 | # Used by our autoconf macros to check whether the available missing 36 | # script is modern enough. 37 | exit 0 38 | ;; 39 | 40 | --run) 41 | # Back-compat with the calling convention used by older automake. 42 | shift 43 | ;; 44 | 45 | -h|--h|--he|--hel|--help) 46 | echo "\ 47 | $0 [OPTION]... PROGRAM [ARGUMENT]... 48 | 49 | Run 'PROGRAM [ARGUMENT]...', returning a proper advice when this fails due 50 | to PROGRAM being missing or too old. 51 | 52 | Options: 53 | -h, --help display this help and exit 54 | -v, --version output version information and exit 55 | 56 | Supported PROGRAM values: 57 | aclocal autoconf autoheader autom4te automake makeinfo 58 | bison yacc flex lex help2man 59 | 60 | Version suffixes to PROGRAM as well as the prefixes 'gnu-', 'gnu', and 61 | 'g' are ignored when checking the name. 62 | 63 | Send bug reports to ." 64 | exit $? 65 | ;; 66 | 67 | -v|--v|--ve|--ver|--vers|--versi|--versio|--version) 68 | echo "missing $scriptversion (GNU Automake)" 69 | exit $? 70 | ;; 71 | 72 | -*) 73 | echo 1>&2 "$0: unknown '$1' option" 74 | echo 1>&2 "Try '$0 --help' for more information" 75 | exit 1 76 | ;; 77 | 78 | esac 79 | 80 | # Run the given program, remember its exit status. 81 | "$@"; st=$? 82 | 83 | # If it succeeded, we are done. 84 | test $st -eq 0 && exit 0 85 | 86 | # Also exit now if we it failed (or wasn't found), and '--version' was 87 | # passed; such an option is passed most likely to detect whether the 88 | # program is present and works. 89 | case $2 in --version|--help) exit $st;; esac 90 | 91 | # Exit code 63 means version mismatch. This often happens when the user 92 | # tries to use an ancient version of a tool on a file that requires a 93 | # minimum version. 94 | if test $st -eq 63; then 95 | msg="probably too old" 96 | elif test $st -eq 127; then 97 | # Program was missing. 98 | msg="missing on your system" 99 | else 100 | # Program was found and executed, but failed. Give up. 101 | exit $st 102 | fi 103 | 104 | perl_URL=https://www.perl.org/ 105 | flex_URL=https://github.com/westes/flex 106 | gnu_software_URL=https://www.gnu.org/software 107 | 108 | program_details () 109 | { 110 | case $1 in 111 | aclocal|automake) 112 | echo "The '$1' program is part of the GNU Automake package:" 113 | echo "<$gnu_software_URL/automake>" 114 | echo "It also requires GNU Autoconf, GNU m4 and Perl in order to run:" 115 | echo "<$gnu_software_URL/autoconf>" 116 | echo "<$gnu_software_URL/m4/>" 117 | echo "<$perl_URL>" 118 | ;; 119 | autoconf|autom4te|autoheader) 120 | echo "The '$1' program is part of the GNU Autoconf package:" 121 | echo "<$gnu_software_URL/autoconf/>" 122 | echo "It also requires GNU m4 and Perl in order to run:" 123 | echo "<$gnu_software_URL/m4/>" 124 | echo "<$perl_URL>" 125 | ;; 126 | esac 127 | } 128 | 129 | give_advice () 130 | { 131 | # Normalize program name to check for. 132 | normalized_program=`echo "$1" | sed ' 133 | s/^gnu-//; t 134 | s/^gnu//; t 135 | s/^g//; t'` 136 | 137 | printf '%s\n' "'$1' is $msg." 138 | 139 | configure_deps="'configure.ac' or m4 files included by 'configure.ac'" 140 | case $normalized_program in 141 | autoconf*) 142 | echo "You should only need it if you modified 'configure.ac'," 143 | echo "or m4 files included by it." 144 | program_details 'autoconf' 145 | ;; 146 | autoheader*) 147 | echo "You should only need it if you modified 'acconfig.h' or" 148 | echo "$configure_deps." 149 | program_details 'autoheader' 150 | ;; 151 | automake*) 152 | echo "You should only need it if you modified 'Makefile.am' or" 153 | echo "$configure_deps." 154 | program_details 'automake' 155 | ;; 156 | aclocal*) 157 | echo "You should only need it if you modified 'acinclude.m4' or" 158 | echo "$configure_deps." 159 | program_details 'aclocal' 160 | ;; 161 | autom4te*) 162 | echo "You might have modified some maintainer files that require" 163 | echo "the 'autom4te' program to be rebuilt." 164 | program_details 'autom4te' 165 | ;; 166 | bison*|yacc*) 167 | echo "You should only need it if you modified a '.y' file." 168 | echo "You may want to install the GNU Bison package:" 169 | echo "<$gnu_software_URL/bison/>" 170 | ;; 171 | lex*|flex*) 172 | echo "You should only need it if you modified a '.l' file." 173 | echo "You may want to install the Fast Lexical Analyzer package:" 174 | echo "<$flex_URL>" 175 | ;; 176 | help2man*) 177 | echo "You should only need it if you modified a dependency" \ 178 | "of a man page." 179 | echo "You may want to install the GNU Help2man package:" 180 | echo "<$gnu_software_URL/help2man/>" 181 | ;; 182 | makeinfo*) 183 | echo "You should only need it if you modified a '.texi' file, or" 184 | echo "any other file indirectly affecting the aspect of the manual." 185 | echo "You might want to install the Texinfo package:" 186 | echo "<$gnu_software_URL/texinfo/>" 187 | echo "The spurious makeinfo call might also be the consequence of" 188 | echo "using a buggy 'make' (AIX, DU, IRIX), in which case you might" 189 | echo "want to install GNU make:" 190 | echo "<$gnu_software_URL/make/>" 191 | ;; 192 | *) 193 | echo "You might have modified some files without having the proper" 194 | echo "tools for further handling them. Check the 'README' file, it" 195 | echo "often tells you about the needed prerequisites for installing" 196 | echo "this package. You may also peek at any GNU archive site, in" 197 | echo "case some other package contains this missing '$1' program." 198 | ;; 199 | esac 200 | } 201 | 202 | give_advice "$1" | sed -e '1s/^/WARNING: /' \ 203 | -e '2,$s/^/ /' >&2 204 | 205 | # Propagate the correct exit status (expected to be 127 for a program 206 | # not found, 63 for a program that failed due to version mismatch). 207 | exit $st 208 | 209 | # Local variables: 210 | # eval: (add-hook 'before-save-hook 'time-stamp) 211 | # time-stamp-start: "scriptversion=" 212 | # time-stamp-format: "%:y-%02m-%02d.%02H" 213 | # time-stamp-time-zone: "UTC0" 214 | # time-stamp-end: "; # UTC" 215 | # End: 216 | -------------------------------------------------------------------------------- /contrib/autocreate-test.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | TEST_FILE1=./test1.img 4 | TEST_FILE1_FULL=$(realpath -s "${TEST_FILE1}") 5 | TEST_FILE2=./test2.img 6 | TEST_FILE2_FULL=$(realpath -s "${TEST_FILE2}") 7 | TEST_SIZE=300M 8 | 9 | POOL1=test1 10 | SUB1=${POOL1}/sub1 11 | SUB1_2=${SUB1}/sub2 12 | SUB2=${POOL1}/sub2 13 | POOL2=test2 14 | 15 | set -x 16 | 17 | # Pool creation... 18 | zpool destroy ${POOL1} 19 | rm -f "${TEST_FILE1_FULL}" 20 | truncate -s ${TEST_SIZE} "${TEST_FILE1_FULL}" 21 | zpool create ${POOL1} "${TEST_FILE1_FULL}" 22 | zfs create ${SUB1} 23 | zfs create ${SUB1_2} 24 | zfs create ${SUB2} 25 | 26 | zpool destroy ${POOL2} 27 | rm -f "${TEST_FILE2_FULL}" 28 | truncate -s ${TEST_SIZE} "${TEST_FILE2_FULL}" 29 | zpool create ${POOL2} "${TEST_FILE2_FULL}" 30 | 31 | # Znapzend setup... 32 | znapzendzetup create --donotask --tsformat='%Y%m%dT%H%M%S' --recursive SRC '1h=>15min,2d=>1h,14d=>1d,3m=>1w' test1 DST:nas '1h=>15min,2d=>1h,14d=>1d,3m=>1w,1y=>1m' test2 33 | 34 | # Try the new autoCreation property... 35 | znapzendzetup enable-dst-autocreation ${POOL1} nas 36 | 37 | # Output zfs properties... 38 | zfs get all ${POOL1} | grep org.znapzend 39 | 40 | # Manually set the property since it doesn't work from znapzendzetup. 41 | # TODO: Remove this after verifying https://github.com/oetiker/znapzend/pull/657 fixes this... 42 | zfs set org.znapzend:dst_nas_autocreation=on ${POOL1} 43 | 44 | # Output zfs properties... 45 | zfs get all ${POOL1} | grep org.znapzend 46 | 47 | # Znapzend test... 48 | znapzend --debug --logto=/dev/stdout --runonce=${POOL1} --autoCreation 49 | 50 | # Output datasets... 51 | zfs list -r ${POOL1} ${POOL2} 52 | -------------------------------------------------------------------------------- /contrib/test-splitHostDataSet.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | # Help test chosen regex for splitHostDataSet() 4 | # (C) 2022 by Jim Klimov 5 | 6 | # https://github.com/oetiker/znapzend/issues/585 7 | #RE='/^(?:(.+)\s)?([^\s]+)$/' 8 | #RE='/^(?:([^:\/]+):)?([^:]+|[^:@]+\@.+)$/' 9 | RE='/^(?:([^:\/]+):)?([^@\s]+|[^@\s]+\@[^@\s]+)$/' 10 | 11 | echo 'NOTE: For each probe this should return an array with two strings:' 12 | echo ' empty "", "remotehost" or "user@remotehost" as applicable,' 13 | echo ' and the dataset(@snapshot) name' 14 | # "root@host -p 22 -id /tmp/key rpool@snaptime-12:34:56" \ 15 | for S in \ 16 | "rpool/zones/nut/jenkins-worker/ROOT/openindiana-20220315-backup-1" \ 17 | "rpool/zones/nut/jenkins-worker/ROOT/openindiana-20220315-backup-1@snap" \ 18 | "rpool/zones/nut/jenkins-worker/ROOT/openindiana-20220315-backup-1@snaptime-12:34:56" \ 19 | "rpool/zones/nut/jenkins-worker/ROOT/openindiana-2022:03:15-backup-1" \ 20 | "rpool/zones/nut/jenkins-worker/ROOT/openindiana-2022:03:15-backup-1@snap" \ 21 | "rpool/zones/nut/jenkins-worker/ROOT/openindiana-2022:03:15-backup-1@snaptime-12:34:56" \ 22 | "remotehost:pond/data/set" \ 23 | "remotehost:pond/data/set@snap" \ 24 | "remotehost:pond/data/set@snaptime-12:34:56" \ 25 | "remotehost:pond/data/set/openindiana-2022:03:15-backup-1" \ 26 | "remotehost:pond/data/set/openindiana-2022:03:15-backup-1@snap" \ 27 | "remotehost:pond/data/set/openindiana-2022:03:15-backup-1@snaptime-12:34:56" \ 28 | "user@remotehost:pond/data/set" \ 29 | "user@remotehost:pond/data/set@snap" \ 30 | "user@remotehost:pond/data/set@snaptime-12:34:56" \ 31 | "user@remotehost:pond/data/set/openindiana-2022:03:15-backup-1" \ 32 | "user@remotehost:pond/data/set/openindiana-2022:03:15-backup-1@snap" \ 33 | "user@remotehost:pond/data/set/openindiana-2022:03:15-backup-1@snaptime-12:34:56" \ 34 | "rpool" \ 35 | "rpool@snap" \ 36 | "rpool@snaptime-12:34:56" \ 37 | "remotehost:rpool" \ 38 | "remotehost:rpool@snap" \ 39 | "remotehost:rpool@snaptime-12:34:56" \ 40 | "user@remotehost:rpool" \ 41 | "user@remotehost:rpool@snap" \ 42 | "user@remotehost:rpool@snaptime-12:34:56" \ 43 | ; do 44 | perl -e 'print STDERR "[D] Split \"" . $ARGV[0] . "\" into:\n\t[\"" . join("\", \"", ($ARGV[0] =~ '"$RE"')) . "\"]\n";' "$S" 45 | done 46 | 47 | -------------------------------------------------------------------------------- /cpanfile.common: -------------------------------------------------------------------------------- 1 | requires 'Mojolicious'; 2 | requires 'Scalar::Util', '>= 1.45'; 3 | requires 'Mojo::Log::Role::Clearable'; 4 | -------------------------------------------------------------------------------- /cpanfile.test: -------------------------------------------------------------------------------- 1 | requires 'Test::SharedFork'; 2 | requires 'Test::Exception'; 3 | requires 'Test::More'; 4 | requires 'Devel::Cover'; 5 | requires "Devel::Cover::Report::Coveralls"; 6 | requires "Pod::Coverage"; 7 | -------------------------------------------------------------------------------- /debian/.gitignore: -------------------------------------------------------------------------------- 1 | znapzend.links 2 | znapzend.service 3 | znapzend.default 4 | znapzend.bash-completion 5 | znapzend.substvars 6 | znapzend 7 | .debhelper 8 | znapzend.debhelper.log 9 | files 10 | -------------------------------------------------------------------------------- /debian/Makefile.am: -------------------------------------------------------------------------------- 1 | if DEB_BUILD 2 | 3 | bash_completion: 4 | cp ../bash_completion.d/znapzendzetup znapzend.bash-completion 5 | 6 | service_files: 7 | cp ../init/znapzend.service znapzend.service 8 | 9 | defaults: 10 | cp ../init/znapzend.default znapzend.default 11 | 12 | all: defaults service_files bash_completion 13 | 14 | endif 15 | -------------------------------------------------------------------------------- /debian/changelog: -------------------------------------------------------------------------------- 1 | ../CHANGES -------------------------------------------------------------------------------- /debian/compat: -------------------------------------------------------------------------------- 1 | 9 2 | -------------------------------------------------------------------------------- /debian/control: -------------------------------------------------------------------------------- 1 | Source: znapzend 2 | Section: system 3 | Priority: optional 4 | Maintainer: Tobias Bossert 5 | Build-Depends: build-essential, debhelper (>= 9), perl 6 | 7 | Package: znapzend 8 | Architecture: any 9 | Depends: ${shlibs:Depends}, perl, zfsutils-linux 10 | Description: ZnapZend is a ZFS centric backup tool to create snapshots and send them to backup locations 11 | -------------------------------------------------------------------------------- /debian/rules: -------------------------------------------------------------------------------- 1 | #!/usr/bin/make -f 2 | # -*- makefile -*- 3 | 4 | # Uncomment this to turn on verbose mode. 5 | export DH_VERBOSE=1 6 | 7 | # This has to be exported to make some magic below work. 8 | export DH_OPTIONS 9 | 10 | export DH_ALWAYS_EXCLUDE=CVS:.svn:.git: 11 | 12 | override_dh_auto_configure: 13 | ./bootstrap.sh 14 | ./configure --enable-deb_build 15 | 16 | %: 17 | dh $@ 18 | -------------------------------------------------------------------------------- /debian/znapzend.links.in: -------------------------------------------------------------------------------- 1 | @BINDIR@/znapzend /usr/local/bin/znapzend 2 | @BINDIR@/znapzendzetup /usr/local/bin/znapzendzetup 3 | @BINDIR@/znapzendztatz /usr/local/bin/znapzendztatz 4 | 5 | @DATADIR@/man/man1/znapzend.1 /usr/share/man/man1/znapzend.1 6 | @DATADIR@/man/man1/znapzendzetup.1 /usr/share/man/man1/znapzendzetup.1 7 | @DATADIR@/man/man1/znapzendztatz.1 /usr/share/man/man1/znapzendztatz.1 8 | 9 | -------------------------------------------------------------------------------- /doc/znapzendztatz.pod: -------------------------------------------------------------------------------- 1 | =head1 NAME 2 | 3 | znapzendztatz - znapzend statistics utility 4 | 5 | =head1 SYNOPSIS 6 | 7 | B [I...] [src_dataset] 8 | 9 | --debug print debugging details from some library routines 10 | -H do not print headers and separate fields by a single tab 11 | instead of arbitrary white space 12 | -r,--recursive show statistics for dataset and sub datasets 13 | --inherited allow to specify a dataset which just inherits a backup plan 14 | --only-enabled only show statistics for enabled datasets 15 | --setup show the configuration key and the filesystem that defined the configuration 16 | --rootExec=x exec zfs with this command to obtain root privileges (sudo or pfexec) 17 | --timeWarp=x act as if you were shifted by x seconds into the future 18 | --man show man-page and exit 19 | -h,--help display this help and exit 20 | 21 | =head1 DESCRIPTION 22 | 23 | znapzendztatz shows statistics of snapshots created and storage space usage 24 | 25 | =head1 COPYRIGHT 26 | 27 | Copyright (c) 2014 by OETIKER+PARTNER AG. All rights reserved. 28 | 29 | =head1 LICENSE 30 | 31 | This program is free software: you can redistribute it and/or modify it 32 | under the terms of the GNU General Public License as published by the Free 33 | Software Foundation, either version 3 of the License, or (at your option) 34 | any later version. 35 | 36 | This program is distributed in the hope that it will be useful, but WITHOUT 37 | ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 38 | FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for 39 | more details. 40 | 41 | You should have received a copy of the GNU General Public License along with 42 | this program. If not, see L. 43 | 44 | =head1 AUTHOR 45 | 46 | Stobi@oetiker.chE> 47 | Shadfl@cpan.orgE> 48 | 49 | =head1 HISTORY 50 | 51 | 2014-06-29 had Flexible snapshot time format 52 | 2014-06-05 had Initial Version 53 | 54 | =cut 55 | -------------------------------------------------------------------------------- /init/README.md: -------------------------------------------------------------------------------- 1 | # Init scripts 2 | 3 | This directory includes integration for different service management 4 | frameworks, so that ```znapzend``` works for you automatically (as 5 | long as you have set up the dataset retention policies with the 6 | ```znapzendzetup``` utility). 7 | 8 | Since the service manifests contain the absolute path to the 9 | ```znapzend``` install directory, they are not contained in the 10 | prebuilt version. But you can get a copy from github and roll 11 | your own by building the project properly or by just manually 12 | replacing the tags in the corresponding ```.in``` template file 13 | to match your existing system layout (replace ```@BINDIR@``` usually 14 | with ```/usr/local/bin``` to match other setup documentation). 15 | 16 | ## macOS/launchd 17 | 18 | For macOS launchd, you can copy the generated ```org.znapzend.plist``` 19 | file to ```/Library/LaunchDaemons``` and then start the daemon with: 20 | 21 | ```sh 22 | launchctl load /Library/LaunchDaemons/org.znapzend.plist 23 | ``` 24 | 25 | ```Note:``` It is recommended to ```not``` set the ```--daemonize``` flag of ```znapzend``` 26 | as launchd will lose control of the process. Check out ```init/org.znapzend.plist.in``` 27 | for an example plist. 28 | 29 | To reload the daemon by sending a HUP signal, use this command: 30 | 31 | ```sh 32 | sudo launchctl kill -HUP system/org.znapzend 33 | ``` 34 | 35 | ## Solaris/Illumos 36 | 37 | For solaris/illumos OSes you can tell configure to install a znapzend 38 | service manifest into a particular location (no default imposed) by 39 | calling configure with the option like 40 | ```--enable-svcinstall=/var/svc/manifest/site```. 41 | You can also define the service name (defaults to ```oep/znapzend```) 42 | by calling configure with the option like 43 | ```--with-svcname-smf=system/filesystem/zfs/znapzend```. 44 | 45 | After you have installed the manifest file, verify and import it: 46 | 47 | ```sh 48 | svccfg validate /var/svc/manifest/site/znapzend.xml 49 | svccfg import /var/svc/manifest/site/znapzend.xml 50 | ``` 51 | 52 | and then enable the service 53 | 54 | ```sh 55 | svcadm enable oep/znapzend 56 | ``` 57 | 58 | ## Systemd 59 | 60 | For systemd based systems, you can copy the generated ```znapzend.service``` 61 | file to ```/etc/systemd/system/``` and then enable and start the daemon. 62 | 63 | ```sh 64 | systemctl enable znapzend.service 65 | systemctl start znapzend.service 66 | ``` 67 | 68 | If you want to set parameters for the znapzend daemon separately from the 69 | unit file, copy ```znapzend.default``` to ```/etc/default/znapzend``` and 70 | edit it. 71 | 72 | ## Upstart 73 | 74 | For upstart based systems, you can copy the generated ```znapzend.upstart``` 75 | file to ```/etc/init/znapzend.conf``` and start the daemon. 76 | 77 | ```sh 78 | service znapzend start 79 | ``` 80 | 81 | If you want to set parameters for the znapzend daemon separately from the 82 | upstart file, copy ```znapzend.default``` to ```/etc/default/znapzend``` 83 | and edit it. 84 | 85 | ## System V 86 | 87 | For systems with SysV-based initscripts, you can copy the generated 88 | ```znapzend.sysv``` file to ```/etc/init.d/znapzend``` and then enable and 89 | start the daemon. 90 | 91 | For Red Hat systems (RHEL, RHEL derivatives, and Fedora): 92 | 93 | ```sh 94 | chkconfig znapzend on 95 | service znapzend start 96 | ``` 97 | 98 | For Debian systems: 99 | 100 | ```sh 101 | update-rc.d znapzend defaults 102 | service znapzend start 103 | ``` 104 | 105 | If you want to set parameters for the znapzend daemon separately from the 106 | init script, copy ```znapzend.default``` to ```/etc/default/znapzend``` 107 | and edit it. 108 | 109 | ## FreeBSD 110 | 111 | For systems based on FreeBSD, you can copy the generated ```znapzend.freebsd``` 112 | file to ```/etc/rc.d/znapzend``` and make sure it is executable, and then add 113 | the following line(s) to your ```/etc/rc.conf``` file to enable or disable 114 | znapzend as a service: 115 | 116 | ```sh 117 | # znapzend_enable (bool): Set to "NO" by default. 118 | # Set it to "YES" to enable znapzend. 119 | znapzend_enable="YES" 120 | ``` 121 | 122 | If you want to set parameters for the znapzend daemon separately from the 123 | rc-script, copy ```znapzend.default``` to ```/etc/defaults/znapzend``` 124 | and edit it. Note that the rc-script in all cases provides a set of its 125 | own ```command_args``` to manage daemonization, logging and PID file use. 126 | -------------------------------------------------------------------------------- /init/org.znapzend.plist.in: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | EnvironmentVariables 6 | 7 | PATH 8 | /usr/local/zfs/bin:/usr/local/bin:/usr/bin:/bin:/usr/sbin:/sbin 9 | 10 | KeepAlive 11 | 12 | Crashed 13 | 14 | 15 | Label 16 | org.znapzend 17 | ProgramArguments 18 | 19 | @BINDIR@/znapzend 20 | 21 | RunAtLoad 22 | 23 | StandardErrorPath 24 | /var/log/org.znapzend.stderr 25 | StandardOutPath 26 | /var/log/org.znapzend.stdout 27 | ThrottleInterval 28 | 30 29 | 30 | 31 | -------------------------------------------------------------------------------- /init/znapzend.default: -------------------------------------------------------------------------------- 1 | # Command line options for znapzend 2 | ZNAPZENDOPTIONS="--autoCreation" 3 | -------------------------------------------------------------------------------- /init/znapzend.freebsd.in: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | # PROVIDE: znapzend 4 | # REQUIRE: LOGIN 5 | # KEYWORD: shutdown 6 | 7 | # 8 | # Add the following line to /etc/rc.conf to enable znapzend: 9 | # znapzend_enable (bool): Set to "NO" by default. 10 | # Set it to "YES" to enable znapzend. 11 | # 12 | 13 | # This file in znapzend project originated in FreeBSD ports integration at: 14 | # https://cgit.freebsd.org/ports/blame/sysutils/znapzend/files/znapzend.in 15 | 16 | . /etc/rc.subr 17 | 18 | name=znapzend 19 | desc="Znapzend backup daemon" 20 | rcvar=znapzend_enable 21 | 22 | load_rc_config $name 23 | 24 | : ${znapzend_enable:=NO} 25 | 26 | extra_commands=reload 27 | command_interpreter=/usr/local/bin/perl 28 | sig_reload=HUP 29 | pidfile=/var/run/${name}.pid 30 | command=@PREFIX@/bin/${name} 31 | command_args="--daemonize --pidfile=${pidfile} --logto=/var/log/znapzend.log" 32 | 33 | if grep ZNAPZENDOPTIONS= /etc/defaults/znapzend > /dev/null ; then 34 | . /etc/defaults/znapzend && command_args="$command_args $ZNAPZENDOPTIONS" 35 | fi 36 | 37 | run_rc_command "$1" 38 | -------------------------------------------------------------------------------- /init/znapzend.service.in: -------------------------------------------------------------------------------- 1 | [Unit] 2 | Description=ZnapZend - ZFS Backup System 3 | Documentation=man:znapzend 4 | After=zfs-import-cache.service 5 | After=zfs-import-scan.service 6 | 7 | [Service] 8 | EnvironmentFile=-/etc/default/znapzend 9 | ExecStart=@BINDIR@/znapzend $ZNAPZENDOPTIONS 10 | ExecReload=/bin/kill -HUP $MAINPID 11 | Restart=on-failure 12 | # might be necessary on low power systems 13 | # Nice=19 14 | # IOSchedulingClass=2 15 | # IOSchedulingPriority=7 16 | 17 | [Install] 18 | WantedBy=multi-user.target 19 | -------------------------------------------------------------------------------- /init/znapzend.sysv.in: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # 3 | # ZnapZend - ZFS Backup System 4 | # 5 | # chkconfig: 2345 20 80 6 | # config: /etc/default/znapzend 7 | # probe: true 8 | 9 | # Source function library. 10 | . /etc/init.d/functions 11 | 12 | if [ -e /etc/default/znapzend ]; then 13 | . /etc/default/znapzend 14 | fi 15 | 16 | start() { 17 | echo -n "Starting znapzend: " 18 | daemon --check znapzend @BINDIR@/znapzend --daemonize $ZNAPZENDOPTIONS 19 | RETVAL=$? 20 | if [ $RETVAL -ne 0 ]; then 21 | return $RETVAL 22 | fi 23 | touch /var/lock/subsys/znapzend 24 | return 0 25 | } 26 | 27 | stop() { 28 | echo -n "Shutting down znapzend: " 29 | killproc znapzend 30 | RETVAL=$? 31 | rm -f /var/lock/subsys/znapzend 32 | return $RETVAL 33 | } 34 | 35 | case "$1" in 36 | start) 37 | start 38 | ;; 39 | stop) 40 | stop 41 | ;; 42 | status) 43 | status znapzend 44 | ;; 45 | restart) 46 | stop 47 | start 48 | ;; 49 | reload) 50 | killproc znapzend -HUP 51 | ;; 52 | condrestart) 53 | [ -f /var/lock/subsys/znapzend ] && restart || : 54 | ;; 55 | *) 56 | echo "Usage: znapzend {start|stop|status|reload|restart}" 57 | exit 1 58 | ;; 59 | esac 60 | exit $? 61 | -------------------------------------------------------------------------------- /init/znapzend.upstart.in: -------------------------------------------------------------------------------- 1 | # znapzend: upstart file at /etc/init/znapzend.conf 2 | 3 | description "znapzend" 4 | 5 | respawn 6 | 7 | start on [2345] 8 | stop on runlevel [06] 9 | 10 | expect fork 11 | reload signal HUP 12 | 13 | script 14 | set -a 15 | if [ -e /etc/default/znapzend ]; then 16 | . /etc/default/znapzend 17 | fi 18 | @BINDIR@/znapzend --daemonize $ZNAPZENDOPTIONS 19 | end script 20 | -------------------------------------------------------------------------------- /init/znapzend.xml.in: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 9 | 10 | 12 | 13 | 14 | 15 | 20 | 21 | 22 | 23 | 24 | 26 | 27 | 28 | 33 | 34 | 39 | 40 | 45 | 46 | 47 | 48 | 49 | 50 | 52 | 53 | 65 | 66 | 67 | -------------------------------------------------------------------------------- /lib/Makefile.am: -------------------------------------------------------------------------------- 1 | # Author: Tobi Oetiker 2 | # License: Public Domain 3 | 4 | PM = @PERL_MODULES@ 5 | 6 | datadir = $(libdir) 7 | nobase_data_DATA = $(PM) 8 | 9 | #END 10 | -------------------------------------------------------------------------------- /lib/ZnapZend/InheritLevels.pm: -------------------------------------------------------------------------------- 1 | package ZnapZend::InheritLevels; 2 | # Note: classes made by Class::Struct may not be sub-namespaced 3 | # (UPDATE: comment might be valid at least in older Perl versions?) 4 | 5 | use Class::Struct; 6 | 7 | ### Property inheritance levels - how deep we go in routines 8 | ### that (need to) care about these nuances beyond a boolean. 9 | ### Primarily intended for getSnapshotProperties() to get props 10 | ### defined by snapshots of parent datasets with same snapnames. 11 | ### For "usual" datasets (filesystem,volume) `zfs` returns the 12 | ### properties inherited from higher level datasets; but for 13 | ### snapshots it only returns the same - not from higher snaps. 14 | struct ('ZnapZend::InheritLevels' => { 15 | zfs_local => '$', # set to ask for properties defined locally in dataset 16 | zfs_inherit => '$', # set to ask for properties inherited from higher datasets 17 | zfs_received => '$', # set to ask for properties received during "zfs send|recv" (no-op for now, mentioned for completeness) 18 | snapshot_recurse_parent => '$' # "manually" (not via zfs tools) look in same-named snapshots of parent datasets 19 | } ) ; 20 | 21 | # NOTE that some versions of zfs do not inherit values from same-named 22 | # snapshot of a parent dataset, but only from a "real" dataset higher 23 | # in the data hierarchy, so the "-s inherit" argument may be effectively 24 | # ignored by OS for the purposes of *such* inheritance. Reasonable modes 25 | # of sourcing properties include: 26 | # 0 = only local 27 | # 1 = local + inherit as defined by zfs 28 | # 2 = local + recurse into parent that has same snapname 29 | # 3 = local + inherit as defined by zfs + recurse into parent 30 | # In older code other name-code mappings included: 31 | # local_only => 0, 32 | # local_zfsinherit => 1, 33 | # local_recurseparent => 2, 34 | # local_recurseparent_zfsinherit => 3, 35 | 36 | sub getInhMode { 37 | # Method to return a string for "zfs get -s ..." based on the flags in class instance 38 | my $self = shift; 39 | my $inhMode = ''; 40 | if ($self->zfs_local) { 41 | $inhMode .= 'local'; 42 | } 43 | if ($self->zfs_inherit) { 44 | if ($inhMode eq '') { 45 | $inhMode = 'inherited'; 46 | } else { 47 | $inhMode .= ',inherited'; 48 | } 49 | } 50 | if ($self->zfs_received) { 51 | if ($inhMode eq '') { 52 | $inhMode = 'received'; 53 | } else { 54 | $inhMode .= ',received'; 55 | } 56 | } 57 | return $inhMode; 58 | } 59 | 60 | sub reset { 61 | # Without args, just resets all fields back to undef so they can be 62 | # re-assigned later. With an arg tries to import a legacy setting by 63 | # number or string, or copy from another instance of InheritLevels. 64 | my $self = shift; 65 | 66 | $self->zfs_local(undef); 67 | $self->zfs_inherit(undef); 68 | $self->zfs_received(undef); 69 | $self->snapshot_recurse_parent(undef); 70 | if (@_) { 71 | my $arg = shift; 72 | # Assign from legacy values 73 | if ($arg->isa('ZnapZend::InheritLevels')) { 74 | $self->zfs_local($arg->zfs_local); 75 | $self->zfs_inherit($arg->zfs_inherit); 76 | $self->zfs_received($arg->zfs_received); 77 | $self->snapshot_recurse_parent($arg->snapshot_recurse_parent); 78 | } elsif ($arg == 0 or $arg eq 'local_only' or $arg eq 'zfs_local') { 79 | $self->zfs_local(1); 80 | } elsif ($arg == 1 or $arg eq 'local_zfsinherit') { 81 | $self->zfs_local(1); 82 | $self->zfs_inherit(1); 83 | } elsif ($arg == 2 or $arg eq 'local_recurseparent') { 84 | $self->zfs_local(1); 85 | $self->snapshot_recurse_parent(1); 86 | } elsif ($arg == 3 or $arg eq 'local_recurseparent_zfsinherit') { 87 | $self->zfs_local(1); 88 | $self->snapshot_recurse_parent(1); 89 | $self->zfs_inherit(1); 90 | } elsif (!defined($arg) or $arg == undef) { 91 | 1; # No-op, keep fields undef 92 | } else { 93 | warn "ZnapZend::InheritLevels::reset() got unsupported argument '$arg'\n"; 94 | return 0; 95 | } 96 | } 97 | return 1; 98 | } 99 | 100 | 1; 101 | 102 | __END__ 103 | 104 | =head1 NAME 105 | 106 | ZnapZend::InheritLevels - helper struct for various options of ZFS property inheritance 107 | 108 | =head1 SYNOPSIS 109 | 110 | use ZnapZend::InheritLevels; 111 | use ZnapZend::ZFS; 112 | ... 113 | my $inherit = ZnapZend::InheritLevels->new; 114 | $inherit->zfs_local(1); 115 | $inherit->zfs_inherit(1); 116 | my $properties = $self->getSnapshotProperties($snapshot, $recurse, $inherit, @dstSyncedProps); 117 | ... 118 | 119 | =head1 DESCRIPTION 120 | 121 | this object makes zfs property request inheritance settings easier to use 122 | 123 | currently used by ZnapZend::ZFS routines getSnapshotProperties and 124 | mostRecentCommonSnapshot but not really useful for getDataSetProperties 125 | so not yet utilized there 126 | 127 | =head1 ATTRIBUTES 128 | 129 | =head2 zfs_local 130 | 131 | ask for values defined locally in a dataset or snapshot itself 132 | 133 | =head2 zfs_inherit 134 | 135 | ask for values defined in parents of dataset or snapshot itself 136 | as reported by "zfs get" command on the current system; this may 137 | not include values defined in snapshots of a parent dataset's that 138 | are named same as the snapshot you are requesting properties of 139 | 140 | =head2 zfs_received 141 | 142 | ask for values defined by receiving a dataset or snapshot 143 | by "zfs send|zfs recv" 144 | 145 | (zfs_received is not currently used by znapzend but may have 146 | some meaning in the future) 147 | 148 | =head2 snapshot_recurse_parent 149 | 150 | meaningful only for requests of properties of snapshots: recursively 151 | ask for values defined locally in a same-named snapshot of a parent 152 | or a further ancestor dataset 153 | 154 | =head1 COPYRIGHT 155 | 156 | Copyright (c) 2020 by OETIKER+PARTNER AG. All rights reserved. 157 | 158 | =head1 LICENSE 159 | 160 | This program is free software: you can redistribute it and/or modify it 161 | under the terms of the GNU General Public License as published by the Free 162 | Software Foundation, either version 3 of the License, or (at your option) 163 | any later version. 164 | 165 | This program is distributed in the hope that it will be useful, but WITHOUT 166 | ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 167 | FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for 168 | more details. 169 | 170 | You should have received a copy of the GNU General Public License along with 171 | this program. If not, see L. 172 | 173 | =head1 AUTHOR 174 | 175 | Sjimklimov@gmail.comE>, 176 | Stobi@oetiker.chE> 177 | -------------------------------------------------------------------------------- /lib/ZnapZend/Time.pm: -------------------------------------------------------------------------------- 1 | package ZnapZend::Time; 2 | 3 | use Mojo::Base -base; 4 | use Time::Piece; 5 | use Time::Seconds; 6 | 7 | ### attributes ### 8 | has configUnits => sub { 9 | { 10 | s => 'seconds', 11 | sec => 'seconds', 12 | second => 'seconds', 13 | min => 'minutes', 14 | mins => 'minutes', 15 | minute => 'minutes', 16 | h => 'hours', 17 | hour => 'hours', 18 | d => 'days', 19 | day => 'days', 20 | w => 'weeks', 21 | week => 'weeks', 22 | m => 'months', 23 | mon => 'months', 24 | mons => 'months', 25 | month => 'months', 26 | y => 'years', 27 | year => 'years', 28 | } 29 | }; 30 | 31 | has unitFactors => sub { 32 | { 33 | years => 3600 * 24 * 365.25, 34 | months => 3600 * 24 * 30, 35 | weeks => 3600 * 24 * 7, 36 | days => 3600 * 24, 37 | hours => 3600, 38 | minutes => 60, 39 | seconds => 1, 40 | } 41 | }; 42 | 43 | has scrubFilter => sub { qr/scrub repaired/ }; 44 | has scrubTimeFilter => sub { qr/[A-Z][a-z]{2}\s+\d{1,2}\s+\d{2}:\d{2}:\d{2}\s+\d{4}/ }; 45 | has scrubTimeFormat => sub { q{%b %d %H:%M:%S %Y} }; 46 | has timeWarp => sub { undef }; 47 | 48 | ### private methods ### 49 | my $intervalToTimestamp = sub { 50 | my $time = shift; 51 | my $interval = shift; 52 | 53 | return $interval * (int($time / $interval) + 1); 54 | }; 55 | 56 | my $timeToTimestamp = sub { 57 | my $self = shift; 58 | my ($time, $unit) = @_; 59 | 60 | return $time * $self->unitFactors->{$unit}; 61 | }; 62 | 63 | my $getSnapshotTimestamp = sub { 64 | my $self = shift; 65 | my $snapshot = shift; 66 | my $timeFormat = shift; 67 | 68 | # TOTHINK: Consider forcedSnapshotSuffix here? We do not clean these anyway... 69 | my $snapFilter = $self->getSnapshotFilter($timeFormat); 70 | 71 | if (my ($snapshotTimestamp) = $snapshot =~ /^.+\@($snapFilter)$/){ 72 | my $snapshotTime = Time::Piece->strptime($snapshotTimestamp, $timeFormat) 73 | or die "ERROR: cannot extract time of '$snapshot'\n"; 74 | 75 | return $snapshotTime->epoch; 76 | } 77 | 78 | return 0; 79 | }; 80 | 81 | ### public methods ### 82 | sub checkTimeUnit { 83 | my $self = shift; 84 | my $arg = shift; 85 | my ($time, $unit) = $arg =~ /^(\d+)\s*([a-z]+)$/; 86 | 87 | if ($time && $unit){ 88 | return $time . $self->configUnits()->{$unit} if (exists $self->configUnits->{$unit}); 89 | return "$time$unit" if grep { $_ eq $unit } values %{$self->configUnits}; 90 | } 91 | return undef; 92 | }; 93 | 94 | my $parseDuration = sub { 95 | my $self = shift; 96 | my $duration = shift; 97 | 98 | my ($value, $unit) = $duration =~ /^(\d+)([a-z]+)$/; 99 | die "ERROR: cannot parse expression '$duration'\n" 100 | unless ($value && $unit); 101 | die "ERROR: unknown unit '$unit'\n" 102 | unless exists $self->unitFactors->{$unit}; 103 | return $self->$timeToTimestamp($value, $unit); 104 | }; 105 | 106 | sub backupPlanToHash { 107 | my $self = shift; 108 | my $backupPlan = shift; 109 | my %backupPlan; 110 | 111 | my @planItems = split /,/, $backupPlan; 112 | 113 | for my $planItem (@planItems){ 114 | my ($retentionExp,$intervalExp) = split '=>', $planItem, 2; 115 | my $retention = $self->$parseDuration($retentionExp); 116 | my $interval = $self->$parseDuration($intervalExp); 117 | die "ERROR: the retention $retentionExp is shorter than interval $interval Exp in backup plan $backupPlan\n" 118 | if $retention < $interval; 119 | 120 | die "ERROR: retention time '$retentionExp' already specified\n" 121 | if exists $backupPlan{$retention}; 122 | $backupPlan{$retention} = $interval; 123 | } 124 | 125 | return \%backupPlan; 126 | } 127 | 128 | sub useUTC { 129 | my $self = shift; 130 | my $timeFormat = shift; 131 | 132 | return $timeFormat =~ /Z$/; 133 | } 134 | 135 | sub getInterval { 136 | my $self = shift; 137 | my $backupHash = shift; 138 | 139 | return (sort { $a<=>$b } (values %$backupHash))[0]; 140 | } 141 | 142 | sub createSnapshotTime { 143 | my $self = shift; 144 | my $timeStamp = shift; 145 | my $timeFormat = shift; 146 | 147 | my $time = gmtime($timeStamp); 148 | return $time->strftime($timeFormat); 149 | } 150 | 151 | sub getNextSnapshotTimestamp { 152 | my $self = shift; 153 | my $interval = shift; 154 | #useUTC is second argument 155 | my $time = $self->getTimestamp(shift); 156 | 157 | return $intervalToTimestamp->($time, $interval); 158 | } 159 | 160 | sub getSnapshotsToDestroy { 161 | my $self = shift; 162 | my $snapshots = shift; 163 | my $timePlan = shift; 164 | my $timeFormat = shift; 165 | my $time = $_[0] || $self->getTimestamp($self->useUTC($timeFormat)); 166 | my $keepPattern = $_[1]; 167 | if ( !defined($keepPattern) || ! $keepPattern ) { $keepPattern = "" ; } 168 | my %timeslots; 169 | my @toDestroy; 170 | 171 | #initialize with maximum time to keep backups since we run from old to new backups 172 | my $maxAge = (sort { $a<=>$b } keys %$timePlan)[-1]; 173 | 174 | for my $snapshot (@$snapshots){ 175 | if ($keepPattern ne "") { 176 | if ($snapshot =~ $keepPattern) { 177 | next; 178 | } 179 | } 180 | 181 | #get snapshot age 182 | my $snapshotTimestamp = $self->$getSnapshotTimestamp($snapshot, $timeFormat); 183 | my $snapshotAge = $time - $snapshotTimestamp; 184 | #get valid snapshot schedule for this dataset 185 | for my $key (sort { $a<=>$b } keys %$timePlan){ 186 | if ($key >= $snapshotAge){ 187 | $maxAge = $key; 188 | last; 189 | } 190 | } 191 | #maxAge should never be 0 or less, still do a check for safety 192 | $maxAge > 0 or die "ERROR: snapshot maximum age is 0! this would delete all your snapshots.\n"; 193 | #check if snapshot is older than the maximum age; removes all snapshots that are older than the maximum time to keep 194 | if ($snapshotAge > $maxAge){ 195 | push @toDestroy, $snapshot if $snapshotTimestamp != $time; #make sure, latest snapshot won't be deleted 196 | next; 197 | } 198 | #calculate timeslot 199 | my $timeslot = int($snapshotTimestamp / $timePlan->{$maxAge}); 200 | #check if timeslot is already occupied, if so, push this snapshot to the destroy list 201 | if (exists $timeslots{$maxAge} && exists $timeslots{$maxAge}->{$timeslot}){ 202 | push @toDestroy, $snapshot if $snapshotTimestamp != $time; #make sure, latest snapshot won't be deleted 203 | } 204 | else{ 205 | #define timeslot 206 | $timeslots{$maxAge}->{$timeslot} = 1; 207 | } 208 | } 209 | return \@toDestroy; 210 | } 211 | 212 | sub getLastScrubTimestamp { 213 | my $self = shift; 214 | my $zpoolStatus = shift; 215 | my $scrubFilter = $self->scrubFilter; 216 | my $scrubTimeFilter = $self->scrubTimeFilter; 217 | my $scrubTimeFormat = $self->scrubTimeFormat; 218 | 219 | for (@$zpoolStatus){ 220 | next if !/$scrubFilter/; 221 | 222 | /($scrubTimeFilter)$/ or die "ERROR: cannot parse last scrub time\n"; 223 | my $scrubTime = Time::Piece->strptime($1, $scrubTimeFormat) or die "ERROR: cannot parse last scrub time\n"; 224 | 225 | return $scrubTime->epoch; 226 | } 227 | 228 | return 0; 229 | } 230 | 231 | sub getTimestamp { 232 | my $self = shift; 233 | #useUTC flag set? 234 | my $time = $_[0] ? gmtime : localtime; 235 | if ($self->timeWarp){ 236 | $time = $time + Time::Seconds->new($self->timeWarp); 237 | } 238 | #need to call method seconds as addition will return a Time::Seconds object 239 | return ($time->epoch + $time->tzoffset)->seconds; 240 | } 241 | 242 | sub checkTimeFormat { 243 | my $self = shift; 244 | my $timeFormat = shift; 245 | 246 | $timeFormat =~ /^(?:%[YmdHMSz]|[\w\-.:])+$/ or die "ERROR: timestamp format not valid. check your syntax\n"; 247 | 248 | #just a made-up timestamp to check if strftime and strptime work 249 | my $timeToCheck = 1014416542; 250 | 251 | my $formattedTime = $self->createSnapshotTime($timeToCheck, $timeFormat) 252 | or die "ERROR: timestamp format not valid. check your syntax\n"; 253 | 254 | my $resultingTime = $self->$getSnapshotTimestamp("dummydataset\@$formattedTime", $timeFormat) 255 | or die "ERROR: timestamp format not valid. check your syntax\n"; 256 | 257 | return $timeToCheck == $resultingTime; #times should be equal 258 | } 259 | 260 | sub getSnapshotFilter { 261 | my $self = shift; 262 | my $timeFormat = shift; 263 | 264 | $timeFormat =~ s/%[mdHMS]/\\d{2}/g; 265 | $timeFormat =~ s/%Y/\\d{4}/g; 266 | $timeFormat =~ s/%z/[-+]\\d{4}/g; 267 | 268 | # escape dot ('.') character 269 | $timeFormat =~ s/\./\\./g; 270 | 271 | return $timeFormat; 272 | } 273 | 274 | 1; 275 | 276 | __END__ 277 | 278 | =head1 NAME 279 | 280 | ZnapZend::Time - znapzend time class 281 | 282 | =head1 SYNOPSIS 283 | 284 | use ZnapZend::Time; 285 | ... 286 | my $zTime = ZnapZend::Time->new(); 287 | ... 288 | 289 | =head1 DESCRIPTION 290 | 291 | znapzend time management class 292 | 293 | =head1 ATTRIBUTES 294 | 295 | =head2 debug 296 | 297 | print debug information to STDERR 298 | 299 | =head2 noaction 300 | 301 | do a dry run. no changes to the filesystem will be performed 302 | 303 | =head1 METHODS 304 | 305 | =head2 checkTimeUnit 306 | 307 | checks if time and time unit are valid 308 | 309 | =head2 backupPlanToHash 310 | 311 | converts a backup plan to a timestamp hash 312 | 313 | =head2 useUTC 314 | 315 | returns whether UTC or localtime will be used for the given time format 316 | 317 | =head2 getInterval 318 | 319 | returns the smallest time interval within a backup plan -> this will be the snapshot creation interval 320 | 321 | =head2 createSnapshotTime 322 | 323 | returns a formatted string from a timestamp and a timestamp format 324 | 325 | =head2 getNextSnapshotTimestamp 326 | 327 | returns a timestamp when the next action (i.e. snapshot creation) has to be done for a specific backup set 328 | 329 | =head2 snapshotsToDestroy 330 | 331 | returns a list of snapshots which have to be destroyed according to the backup plan 332 | 333 | =head2 getLastScrubTimestamp 334 | 335 | extracts the time scrub ran (and finished) last on a pool 336 | 337 | =head2 getTimestamp 338 | 339 | returns the current timestamp 340 | 341 | =head2 checkTimeFormat 342 | 343 | checks if a given timestamp format is valid 344 | 345 | =head2 getSnapshotFilter 346 | 347 | returns a regex pattern to match the snapshot time format 348 | 349 | =head1 COPYRIGHT 350 | 351 | Copyright (c) 2014 by OETIKER+PARTNER AG. All rights reserved. 352 | 353 | =head1 LICENSE 354 | 355 | This program is free software: you can redistribute it and/or modify it 356 | under the terms of the GNU General Public License as published by the Free 357 | Software Foundation, either version 3 of the License, or (at your option) 358 | any later version. 359 | 360 | This program is distributed in the hope that it will be useful, but WITHOUT 361 | ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 362 | FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for 363 | more details. 364 | 365 | You should have received a copy of the GNU General Public License along with 366 | this program. If not, see L. 367 | 368 | =head1 AUTHOR 369 | 370 | Stobi@oetiker.chE> 371 | Shadfl@cpan.orgE> 372 | 373 | =head1 HISTORY 374 | 375 | 2014-07-22 had Pre and post snapshot commands 376 | 2014-06-29 had Flexible snapshot time format 377 | 2014-06-10 had localtime implementation 378 | 2014-06-01 had Multi destination backup 379 | 2014-05-30 had Initial Version 380 | 381 | =cut 382 | 383 | -------------------------------------------------------------------------------- /man/znapzendztatz.1: -------------------------------------------------------------------------------- 1 | .\" Automatically generated by Pod::Man 4.11 (Pod::Simple 3.35) 2 | .\" 3 | .\" Standard preamble: 4 | .\" ======================================================================== 5 | .de Sp \" Vertical space (when we can't use .PP) 6 | .if t .sp .5v 7 | .if n .sp 8 | .. 9 | .de Vb \" Begin verbatim text 10 | .ft CW 11 | .nf 12 | .ne \\$1 13 | .. 14 | .de Ve \" End verbatim text 15 | .ft R 16 | .fi 17 | .. 18 | .\" Set up some character translations and predefined strings. \*(-- will 19 | .\" give an unbreakable dash, \*(PI will give pi, \*(L" will give a left 20 | .\" double quote, and \*(R" will give a right double quote. \*(C+ will 21 | .\" give a nicer C++. Capital omega is used to do unbreakable dashes and 22 | .\" therefore won't be available. \*(C` and \*(C' expand to `' in nroff, 23 | .\" nothing in troff, for use with C<>. 24 | .tr \(*W- 25 | .ds C+ C\v'-.1v'\h'-1p'\s-2+\h'-1p'+\s0\v'.1v'\h'-1p' 26 | .ie n \{\ 27 | . ds -- \(*W- 28 | . ds PI pi 29 | . if (\n(.H=4u)&(1m=24u) .ds -- \(*W\h'-12u'\(*W\h'-12u'-\" diablo 10 pitch 30 | . if (\n(.H=4u)&(1m=20u) .ds -- \(*W\h'-12u'\(*W\h'-8u'-\" diablo 12 pitch 31 | . ds L" "" 32 | . ds R" "" 33 | . ds C` "" 34 | . ds C' "" 35 | 'br\} 36 | .el\{\ 37 | . ds -- \|\(em\| 38 | . ds PI \(*p 39 | . ds L" `` 40 | . ds R" '' 41 | . ds C` 42 | . ds C' 43 | 'br\} 44 | .\" 45 | .\" Escape single quotes in literal strings from groff's Unicode transform. 46 | .ie \n(.g .ds Aq \(aq 47 | .el .ds Aq ' 48 | .\" 49 | .\" If the F register is >0, we'll generate index entries on stderr for 50 | .\" titles (.TH), headers (.SH), subsections (.SS), items (.Ip), and index 51 | .\" entries marked with X<> in POD. Of course, you'll have to process the 52 | .\" output yourself in some meaningful fashion. 53 | .\" 54 | .\" Avoid warning from groff about undefined register 'F'. 55 | .de IX 56 | .. 57 | .nr rF 0 58 | .if \n(.g .if rF .nr rF 1 59 | .if (\n(rF:(\n(.g==0)) \{\ 60 | . if \nF \{\ 61 | . de IX 62 | . tm Index:\\$1\t\\n%\t"\\$2" 63 | .. 64 | . if !\nF==2 \{\ 65 | . nr % 0 66 | . nr F 2 67 | . \} 68 | . \} 69 | .\} 70 | .rr rF 71 | .\" 72 | .\" Accent mark definitions (@(#)ms.acc 1.5 88/02/08 SMI; from UCB 4.2). 73 | .\" Fear. Run. Save yourself. No user-serviceable parts. 74 | . \" fudge factors for nroff and troff 75 | .if n \{\ 76 | . ds #H 0 77 | . ds #V .8m 78 | . ds #F .3m 79 | . ds #[ \f1 80 | . ds #] \fP 81 | .\} 82 | .if t \{\ 83 | . ds #H ((1u-(\\\\n(.fu%2u))*.13m) 84 | . ds #V .6m 85 | . ds #F 0 86 | . ds #[ \& 87 | . ds #] \& 88 | .\} 89 | . \" simple accents for nroff and troff 90 | .if n \{\ 91 | . ds ' \& 92 | . ds ` \& 93 | . ds ^ \& 94 | . ds , \& 95 | . ds ~ ~ 96 | . ds / 97 | .\} 98 | .if t \{\ 99 | . ds ' \\k:\h'-(\\n(.wu*8/10-\*(#H)'\'\h"|\\n:u" 100 | . ds ` \\k:\h'-(\\n(.wu*8/10-\*(#H)'\`\h'|\\n:u' 101 | . ds ^ \\k:\h'-(\\n(.wu*10/11-\*(#H)'^\h'|\\n:u' 102 | . ds , \\k:\h'-(\\n(.wu*8/10)',\h'|\\n:u' 103 | . ds ~ \\k:\h'-(\\n(.wu-\*(#H-.1m)'~\h'|\\n:u' 104 | . ds / \\k:\h'-(\\n(.wu*8/10-\*(#H)'\z\(sl\h'|\\n:u' 105 | .\} 106 | . \" troff and (daisy-wheel) nroff accents 107 | .ds : \\k:\h'-(\\n(.wu*8/10-\*(#H+.1m+\*(#F)'\v'-\*(#V'\z.\h'.2m+\*(#F'.\h'|\\n:u'\v'\*(#V' 108 | .ds 8 \h'\*(#H'\(*b\h'-\*(#H' 109 | .ds o \\k:\h'-(\\n(.wu+\w'\(de'u-\*(#H)/2u'\v'-.3n'\*(#[\z\(de\v'.3n'\h'|\\n:u'\*(#] 110 | .ds d- \h'\*(#H'\(pd\h'-\w'~'u'\v'-.25m'\f2\(hy\fP\v'.25m'\h'-\*(#H' 111 | .ds D- D\\k:\h'-\w'D'u'\v'-.11m'\z\(hy\v'.11m'\h'|\\n:u' 112 | .ds th \*(#[\v'.3m'\s+1I\s-1\v'-.3m'\h'-(\w'I'u*2/3)'\s-1o\s+1\*(#] 113 | .ds Th \*(#[\s+2I\s-2\h'-\w'I'u*3/5'\v'-.3m'o\v'.3m'\*(#] 114 | .ds ae a\h'-(\w'a'u*4/10)'e 115 | .ds Ae A\h'-(\w'A'u*4/10)'E 116 | . \" corrections for vroff 117 | .if v .ds ~ \\k:\h'-(\\n(.wu*9/10-\*(#H)'\s-2\u~\d\s+2\h'|\\n:u' 118 | .if v .ds ^ \\k:\h'-(\\n(.wu*10/11-\*(#H)'\v'-.4m'^\v'.4m'\h'|\\n:u' 119 | . \" for low resolution devices (crt and lpr) 120 | .if \n(.H>23 .if \n(.V>19 \ 121 | \{\ 122 | . ds : e 123 | . ds 8 ss 124 | . ds o a 125 | . ds d- d\h'-1'\(ga 126 | . ds D- D\h'-1'\(hy 127 | . ds th \o'bp' 128 | . ds Th \o'LP' 129 | . ds ae ae 130 | . ds Ae AE 131 | .\} 132 | .rm #[ #] #H #V #F C 133 | .\" ======================================================================== 134 | .\" 135 | .IX Title "ZNAPZENDZTATZ 1" 136 | .TH ZNAPZENDZTATZ 1 "2024-06-27" "0.23.2" "znapzend" 137 | .\" For nroff, turn off justification. Always turn off hyphenation; it makes 138 | .\" way too many mistakes in technical documents. 139 | .if n .ad l 140 | .nh 141 | .SH "NAME" 142 | znapzendztatz \- znapzend statistics utility 143 | .SH "SYNOPSIS" 144 | .IX Header "SYNOPSIS" 145 | \&\fBznapzendztatz\fR [\fIoptions\fR...] [src_dataset] 146 | .PP 147 | .Vb 11 148 | \& \-\-debug print debugging details from some library routines 149 | \& \-H do not print headers and separate fields by a single tab 150 | \& instead of arbitrary white space 151 | \& \-r,\-\-recursive show statistics for dataset and sub datasets 152 | \& \-\-inherited allow to specify a dataset which just inherits a backup plan 153 | \& \-\-only\-enabled only show statistics for enabled datasets 154 | \& \-\-setup show the configuration key and the filesystem that defined the configuration 155 | \& \-\-rootExec=x exec zfs with this command to obtain root privileges (sudo or pfexec) 156 | \& \-\-timeWarp=x act as if you were shifted by x seconds into the future 157 | \& \-\-man show man\-page and exit 158 | \& \-h,\-\-help display this help and exit 159 | .Ve 160 | .SH "DESCRIPTION" 161 | .IX Header "DESCRIPTION" 162 | znapzendztatz shows statistics of snapshots created and storage space usage 163 | .SH "COPYRIGHT" 164 | .IX Header "COPYRIGHT" 165 | Copyright (c) 2014 by \s-1OETIKER+PARTNER AG.\s0 All rights reserved. 166 | .SH "LICENSE" 167 | .IX Header "LICENSE" 168 | This program is free software: you can redistribute it and/or modify it 169 | under the terms of the \s-1GNU\s0 General Public License as published by the Free 170 | Software Foundation, either version 3 of the License, or (at your option) 171 | any later version. 172 | .PP 173 | This program is distributed in the hope that it will be useful, but \s-1WITHOUT 174 | ANY WARRANTY\s0; without even the implied warranty of \s-1MERCHANTABILITY\s0 or 175 | \&\s-1FITNESS FOR A PARTICULAR PURPOSE.\s0 See the \s-1GNU\s0 General Public License for 176 | more details. 177 | .PP 178 | You should have received a copy of the \s-1GNU\s0 General Public License along with 179 | this program. If not, see . 180 | .SH "AUTHOR" 181 | .IX Header "AUTHOR" 182 | Tobias\ Oetiker\ 183 | Dominik\ Hassler\ 184 | .SH "HISTORY" 185 | .IX Header "HISTORY" 186 | 2014\-06\-29 had Flexible snapshot time format 187 | 2014\-06\-05 had Initial Version 188 | -------------------------------------------------------------------------------- /packaging/checkinstall/README.md: -------------------------------------------------------------------------------- 1 | Create Package 2 | -------------- 3 | 4 | You can create a package to install (useful for multiple hosts) with _checkinstall_. Best practice is to to this somewhere outside _/home_ (e.g. _/tmp_ ) or you'll get prompted if you'd like to include/exclude files. 5 | 6 | ```sh 7 | cd /tmp/ 8 | git clone https://github.com/oetiker/znapzend 9 | # git checkout 0.xx.yy 10 | cd znapzend 11 | packaging/checkinstall/checkinstall.sh 12 | ``` 13 | -------------------------------------------------------------------------------- /packaging/checkinstall/checkinstall.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # Exit when encountering an undefined variable 4 | set -u 5 | 6 | # Determine the Ubuntu version we are running on. Versions before 7 | # 16.04 had a package called zfsutils, from 16.04 onwards the package 8 | # is called zfsutils-linux 9 | DistroMajorVersion=$(lsb_release -r |cut -f2 | cut -d. -f1) 10 | DistroMinorVersion=$(lsb_release -r |cut -f2 | cut -d. -f2) 11 | if [ ${DistroMajorVersion} -ge 16 ] && [ ${DistroMinorVersion} -ge 4 ]; then 12 | zfsutilsdep="zfsutils-linux" 13 | else 14 | zfsutilsdep="zfsutils" 15 | fi 16 | 17 | # Compile znapzend 18 | make clean 19 | ./configure --prefix=/usr 20 | make 21 | 22 | # Create the package 23 | ln -s packaging/checkinstall/*-pak . 24 | sudo checkinstall --default \ 25 | --install=no \ 26 | --pkgname=znapzend \ 27 | --pkgversion=$(git describe --abbrev=0 --tags | sed 's,^v,,') \ 28 | --pkglicense=GPL \ 29 | --pkgrelease=1 \ 30 | --pkgsource="https://github.com/oetiker/znapzend" \ 31 | --requires="${zfsutilsdep}\|zfs,mbuffer" \ 32 | --provides=znapzend \ 33 | --nodoc \ 34 | --backup=no 35 | rm -f *-pak 36 | -------------------------------------------------------------------------------- /packaging/checkinstall/description-pak: -------------------------------------------------------------------------------- 1 | ZnapZend is a ZFS centric backup tool. It relies on snapshot, send and receive todo its work. It has the built-in ability to to manage both local snapshots as well as remote copies by thinning them out as time progresses. 2 | 3 | The ZnapZend configuration is stored as properties in the ZFS filesystem itself. 4 | 5 | More info: https://github.com/oetiker/znapzend 6 | -------------------------------------------------------------------------------- /packaging/checkinstall/postinstall-pak: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | service zfs-znapzend status >/dev/null 4 | 5 | if [ $? -eq 0 ]; then 6 | service zfs-znapzend restart 7 | fi 8 | -------------------------------------------------------------------------------- /packaging/checkinstall/preremove-pak: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | service zfs-znapzend status >/dev/null 4 | 5 | if [ $? -eq 0 ]; then 6 | service zfs-znapzend stop 7 | fi 8 | -------------------------------------------------------------------------------- /release.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | set -e 3 | V=$(cat VERSION) 4 | P=znapzend 5 | rm -f config.status 6 | ./bootstrap.sh 7 | ./configure 8 | VERSION=$(cat VERSION) 9 | debchange -m -v $V 10 | make 11 | make dist 12 | git checkout -b v${V} || true 13 | git commit -a 14 | git push --set-upstream origin v${V} 15 | #gh release create v${V} --title "ZnapZend $V" --notes-file release-notes-$V.md ${P}-${V}.tar.gz'#Source Archive' 16 | gh release create v${V} --title "ZnapZend $V" ${P}-${V}.tar.gz 17 | -------------------------------------------------------------------------------- /t/autoscrub.t: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env perl 2 | 3 | use strict; 4 | use warnings; 5 | 6 | use FindBin; 7 | $ENV{PATH} = "$FindBin::Bin:$ENV{PATH}"; 8 | my $buildDir; 9 | 10 | BEGIN { 11 | $buildDir = shift @ARGV // "$FindBin::Bin/../"; 12 | } 13 | 14 | # PERL5LIB 15 | use lib "$FindBin::Bin/../lib"; 16 | use lib "$buildDir/thirdparty/lib/perl5"; 17 | #place bin path to lib so it is stored in @INC 18 | use lib "$FindBin::Bin/../bin"; 19 | 20 | unshift @INC, sub { 21 | my (undef, $filename) = @_; 22 | return () if $filename !~ /ZnapZend/; 23 | if (my $found = (grep { -e $_ } map { "$_/$filename" } grep { !ref } @INC)[0] ) { 24 | local $/ = undef; 25 | open my $fh, '<', $found or die("Can't read module file $found\n"); 26 | my $module_text = <$fh>; 27 | close $fh; 28 | 29 | # define everything in a sub, so Devel::Cover will DTRT 30 | # NB this introduces no extra linefeeds so D::C's line numbers 31 | # in reports match the file on disk 32 | $module_text =~ s/(.*?package\s+\S+)(.*)__END__/$1sub classWrapper {$2} classWrapper();/s; 33 | 34 | # unhide private methods to avoid "Variable will not stay shared" 35 | # warnings that appear due to change of applicable scoping rules 36 | # Note: not '\s*' in the start of string, to avoid matching and 37 | # removing blank lines before the private sub definitions. 38 | $module_text =~ s/^[ \t]*my\s+(\S+\s*=\s*sub.*)$/our $1/gm; 39 | 40 | if(defined($ENV{DEBUG_ZNAPZEND_SELFTEST_REWRITE})) { 41 | open(my $fhp, '>', $found . '.selftest-rewritten') or warn "Could not open " . $found . '.selftest-rewritten'; 42 | if ($fhp) { print $fhp $module_text ; close $fhp; } 43 | } 44 | 45 | # filehandle on the scalar 46 | open $fh, '<', \$module_text; 47 | 48 | # and put it into %INC too so that it looks like we loaded the code 49 | # from the file directly 50 | $INC{$filename} = $found; 51 | return $fh; 52 | } 53 | else { 54 | return (); 55 | } 56 | }; 57 | 58 | use Test::More tests => 10; 59 | 60 | use_ok 'ZnapZend::ZFS'; 61 | use_ok 'ZnapZend::Time'; 62 | 63 | my $zZFS = ZnapZend::ZFS->new(); 64 | my $zTime = ZnapZend::Time->new(); 65 | 66 | is (ref $zZFS,'ZnapZend::ZFS', 'instantiation of ZFS'); 67 | 68 | is (ref $zTime, 'ZnapZend::Time', 'instantiation of Time'); 69 | 70 | isnt ($zZFS->listPools(), '', 'list pools'); 71 | 72 | my $zpoolStatus = $zZFS->zpoolStatus('tank'); 73 | isnt ($zpoolStatus, '', 'zpool status'); 74 | 75 | is ($zZFS->startScrub('tank'), 1, 'start scrub'); 76 | 77 | is ($zZFS->stopScrub('tank'), 1, 'stop scrub'); 78 | 79 | is ($zZFS->scrubActive('tank'), 0, 'scrub active'); 80 | 81 | isnt ($zTime->getLastScrubTimestamp($zpoolStatus), 0, 'last scrub time'); 82 | 83 | done_testing; 84 | 85 | 1; 86 | -------------------------------------------------------------------------------- /t/mbuffer: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/oetiker/znapzend/0d3931631652301a7465978b7424f9bb3fc7a5bc/t/mbuffer -------------------------------------------------------------------------------- /t/pfexec: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | echo "# $*" >&2 4 | exec "$@" 5 | -------------------------------------------------------------------------------- /t/ssh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env perl 2 | 3 | use strict; 4 | use warnings; 5 | 6 | #print ssh command 7 | print STDERR '# ssh ' . join(' ', @ARGV) . "\n"; 8 | 9 | while (@ARGV){ 10 | shift @ARGV && next if $ARGV[0] =~ /^-|=/; 11 | 12 | shift @ARGV; 13 | last; 14 | } 15 | 16 | system @ARGV; 17 | 18 | 1; 19 | 20 | -------------------------------------------------------------------------------- /t/sudo: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | echo "# $*" >&2 4 | exec "$@" 5 | -------------------------------------------------------------------------------- /t/znapzend-daemonize.t: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env perl 2 | 3 | use strict; 4 | use warnings; 5 | 6 | use FindBin; 7 | $ENV{PATH} = $FindBin::RealBin.':'.$ENV{PATH}; 8 | my $buildDir; 9 | 10 | BEGIN { 11 | $buildDir = shift @ARGV // $FindBin::Bin."/../"; 12 | } 13 | 14 | # Track child PIDs spawned by test 15 | our @test_arr_children = (); 16 | sub test_arr_children { \@test_arr_children }; 17 | 18 | # PERL5LIB 19 | use lib "$FindBin::RealBin/../lib"; 20 | use lib "$buildDir/thirdparty/lib/perl5"; 21 | #place bin path to lib so it is stored in @INC 22 | use lib "$FindBin::RealBin/../bin"; 23 | 24 | unshift @INC, sub { 25 | my (undef, $filename) = @_; 26 | return () if $filename !~ /ZnapZend|znapzend/; 27 | if (my $found = (grep { -e $_ } map { "$_/$filename" } grep { !ref } @INC)[0] ) { 28 | local $/ = undef; 29 | open my $fh, '<', $found or die("Can't read module file $found\n"); 30 | my $module_text = <$fh>; 31 | close $fh; 32 | 33 | # define everything in a sub, so Devel::Cover will DTRT 34 | # NB this introduces no extra linefeeds so D::C's line numbers 35 | # in reports match the file on disk 36 | $module_text =~ s/(.*?package\s+\S+)(.*)__END__/$1sub classWrapper {$2} classWrapper();/s; 37 | 38 | # uncomment testing code 39 | $module_text =~ s/### RM_COMM_4_TEST(|_main) ###//sg; 40 | 41 | # unhide private methods to avoid "Variable will not stay shared" 42 | # warnings that appear due to change of applicable scoping rules 43 | # Note: not '\s*' in the start of string, to avoid matching and 44 | # removing blank lines before the private sub definitions. 45 | $module_text =~ s/^[ \t]*my\s+(\S+\s*=\s*sub.*)$/our $1/gm; 46 | 47 | if(defined($ENV{DEBUG_ZNAPZEND_SELFTEST_REWRITE})) { 48 | open(my $fhp, '>', $found . '.selftest-rewritten') or warn "Could not open " . $found . '.selftest-rewritten'; 49 | if ($fhp) { print $fhp $module_text ; close $fhp; } 50 | } 51 | 52 | # filehandle on the scalar 53 | open $fh, '<', \$module_text; 54 | 55 | # and put it into %INC too so that it looks like we loaded the code 56 | # from the file directly 57 | $INC{$filename} = $found; 58 | return $fh; 59 | } 60 | else { 61 | return (); 62 | } 63 | }; 64 | 65 | sub runCommand { 66 | @ARGV = @_; 67 | 68 | eval { main(); }; 69 | 70 | if ($@) { 71 | # Presumably a die() handler caught something 72 | print STDERR "EXCEPTION: " . $@ . "\n"; 73 | return 0; 74 | }; 75 | 76 | # Return "true" if not failed :) 77 | 1; 78 | } 79 | 80 | sub runCommand_canThrow { 81 | @ARGV = @_; 82 | 83 | return main(); 84 | } 85 | 86 | ### use threads; 87 | use Test::More; 88 | use Test::Exception; 89 | use Test::SharedFork; ### NOTE: Conflicts with ithreads 90 | 91 | use POSIX ":sys_wait_h"; 92 | 93 | # Succeed the Test::More part... 94 | use_ok 'ZnapZend'; 95 | 96 | #load program 97 | @ARGV = qw(--help); 98 | do 'znapzend' or die "ERROR: loading program znapzend $@\n"; 99 | 100 | # For tests below we run the real forking and daemonization and test how it 101 | # behaves. Thanks to Test::SharedFork above these are counted correctly - 102 | # mind that after forking inside znapzend code, we have two outcomes in log. 103 | # Note that for child processes the test harness intercepts and hides their 104 | # STDERR and STDOUT streams, and the parent process with many tests runs 105 | # quickly. The tests for pidfile conflict detection (#1b and #3) rely on 106 | # this so are a bit prone to race condition like cases. 107 | # As for exit codes, we expect "1" for normal completion of a daemon (which 108 | # runs one loop for tests), or "254" for parent process "fake-exit()", or 109 | # "255" for pre-forking "fake-die()" due to pidfile conflict. Due to this 110 | # tests below expect "(1 or 254)" for normal daemonization or "(1 or 255)" 111 | # where we accept that pidfile conflict might not happen in fact. 112 | 113 | # Allow testing forkedTest() itself :) 114 | sub myprint { 115 | print STDERR "=====> '" . join(',', @_) . "'\n"; 116 | return 1; 117 | } 118 | 119 | sub forkedTest { 120 | # Args are similar to those passed into Test->is_num(): 121 | # $0 = expected code for child (primary worker) 122 | # $1 = expected code for parent (dies just after fork, or in sanity checks before) 123 | # $2 = comment to print about the test 124 | # $3 = routine to use for testing 125 | # @4 = args to that call (array) 126 | my ($expResChild) = $_[0]; # can be undef, number or array ref 127 | my ($expResParent) = $_[1]; 128 | my ($expTxt) = $_[2]; 129 | my ($expCall) = $_[3]; 130 | my (@expCallArgs) = @{$_[4]}; 131 | 132 | # Usually forked/daemonized subprocess can return "ok"/"not ok" twice, 133 | # for parent and child, so we use a trick to run the tested routine 134 | # first, and then report on how that went in child and parent processes 135 | # (assuming the child does fork). 136 | my $testPID = $$; 137 | 138 | # Small debug of test routine 139 | print STDERR "=== BEGIN test in parent $$ before forking: '$expTxt'\n"; 140 | myprint(@expCallArgs); 141 | 142 | # This can fork 143 | my $res = &{$expCall}(@expCallArgs); 144 | 145 | if ( $$ != $testPID ) { 146 | # Make a test verdict for one leg of the forked tests, the daemon 147 | if (ref($expResChild) eq 'ARRAY') { 148 | print STDERR "=== INTERPRET test in child $$ (res=$res, exp in '" . join(" or ", @$expResChild) . "'): '$expTxt'\n"; 149 | ok( scalar(grep{$res == $_} @$expResChild) == 1, $expTxt . " (child)" ); 150 | } else { 151 | print STDERR "=== INTERPRET test in child $$ (res=$res, exp=$expResChild): '$expTxt'\n"; 152 | is($res, $expResChild, $expTxt . " (child)" ); 153 | } 154 | print STDERR "=== ENDED test in child with $res: '$expTxt'\n"; 155 | exit(); 156 | } else { 157 | # Make a test verdict for parent which is usually nearly no-op 158 | if (ref($expResParent) eq 'ARRAY') { 159 | print STDERR "=== INTERPRET test in parent $$ (res=$res, exp in '" . join(" or ", @$expResParent) . "'): '$expTxt'\n"; 160 | ok( scalar(grep{$res == $_} @$expResParent) == 1, $expTxt . " (parent)" ); 161 | } else { 162 | print STDERR "=== INTERPRET test in parent $$ (res=$res, exp=$expResParent): '$expTxt'\n"; 163 | is($res, $expResParent, $expTxt . " (parent)" ); 164 | } 165 | } 166 | } 167 | 168 | 169 | # forkedTest (undef, 1, 'Testing test framework', 170 | # \&myprint, [ '--daemonize', '--debug', '--features=oracleMode,recvu', 171 | # '--pidfile=znapzend.pid' ] ); 172 | 173 | # Check that pidfile conflict detection works 174 | forkedTest (1, 254, 'Daemons be here: znapzend --daemonize #1a', 175 | \&runCommand_canThrow, [ '--daemonize', '--debug', '--features=oracleMode,recvu', 176 | '--pidfile=znapzend.pid' ] ); 177 | 178 | # IF daemon #1 is still alive, we can check if we conflict in pidfile 179 | # (255 before forking) or work normally (1 in child, 254 in parent): 180 | my @expResConflict = (255, 254); 181 | forkedTest (1, \@expResConflict, 'Daemons be here: znapzend --daemonize #1b', 182 | \&runCommand_canThrow, [ '--daemonize', '--debug', '-n', '--pidfile=znapzend.pid' ] ); 183 | 184 | # There should be no conflict for different PID file though 185 | # (Note in real life two znapzends not given paths to work on 186 | # would discover same datasets and policies and conflict while 187 | # sending/receiving stuff) 188 | forkedTest (1, 254, 'Daemons be here: znapzend --daemonize #2', 189 | \&runCommand_canThrow, [ '--daemonize', '--debug', '--features=compressed', '--pidfile=znapzend2.pid' ] ); 190 | 191 | # ASSUMPTION: Eval and time should cover up the users for that pidfile 192 | # For coverage, test also the daemon mode doing nothing R/W wise 193 | forkedTest (1, \@expResConflict, 'Daemons be here: znapzend --daemonize #3', 194 | \&runCommand_canThrow, [ '--daemonize', '-n', '--pidfile=znapzend.pid' ] ); 195 | 196 | print STDERR "=== Parent test launcher is done, waiting for child daemons...\n"; 197 | 198 | # From https://perldoc.perl.org/functions/waitpid.html suggestions: 199 | #my $kid; 200 | #do { 201 | # $kid = waitpid(-1, WNOHANG); 202 | #} while $kid > 0; 203 | 204 | while (scalar(@test_arr_children)) { 205 | ####SPAMALOT### print STDERR "=== REMAINS : " . @test_arr_children . " : " . join (', ', @test_arr_children) . "\n"; 206 | for my $kid (@test_arr_children) { 207 | if ( $kid == waitpid($kid, WNOHANG) ) { 208 | print STDERR "=== Parent reaped child daemon PID=$kid\n"; 209 | @test_arr_children = grep { $kid != $_ } @test_arr_children; 210 | } 211 | } 212 | } 213 | 214 | done_testing(); 215 | 216 | 1; 217 | -------------------------------------------------------------------------------- /t/znapzend-lib-splitter.t: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env perl 2 | 3 | # Test library methods around [[user@]host:]dataset[@snap] splitting 4 | # since there are many use-cases and combinations to take care of. 5 | # We do so below by constructing "task" strings from components we 6 | # know to be a dataset name and some defined (or not) remote spec 7 | # and/or snapshot name, and de-constructing it back with the class 8 | # method. 9 | # 10 | # Copyright (C) 2024 by Jim Klimov 11 | 12 | use strict; 13 | use warnings; 14 | 15 | # Avoid issues if we monkey-patch included sources in a wrong way 16 | use warnings FATAL => 'recursion'; 17 | 18 | use FindBin; 19 | $ENV{PATH} = $FindBin::RealBin.':'.$ENV{PATH}; 20 | my $buildDir; 21 | 22 | BEGIN { 23 | $buildDir = shift @ARGV // $FindBin::RealBin."/../"; 24 | } 25 | 26 | # PERL5LIB 27 | use lib "$FindBin::RealBin/../lib"; 28 | use lib "$buildDir/thirdparty/lib/perl5"; 29 | #place bin path to lib so it is stored in @INC 30 | use lib "$FindBin::RealBin/../bin"; 31 | 32 | unshift @INC, sub { 33 | my (undef, $filename) = @_; 34 | return () if $filename !~ /ZnapZend|ZnapZend.Config|ZFS|znapzend/; 35 | if (my $found = (grep { -e $_ } map { "$_/$filename" } grep { !ref } @INC)[0] ) { 36 | local $/ = undef; 37 | open my $fh, '<', $found or die("Can't read module file $found\n"); 38 | my $module_text = <$fh>; 39 | close $fh; 40 | 41 | # define everything in a sub, so Devel::Cover will DTRT 42 | # NB this introduces no extra linefeeds so D::C's line numbers 43 | # in reports match the file on disk 44 | $module_text =~ s/(.*?package\s+\S+)(.*)__END__/$1sub classWrapper {$2} classWrapper();/s; 45 | 46 | # unhide private methods to avoid "Variable will not stay shared" 47 | # warnings that appear due to change of applicable scoping rules 48 | # Note: not '\s*' in the start of string, to avoid matching and 49 | # removing blank lines before the private sub definitions. 50 | $module_text =~ s/^[ \t]*my\s+(\S+\s*=\s*sub.*)$/our $1/gm; 51 | 52 | # For this test, also strip dollars from tested private method 53 | # names so we can actually call them from the test context. 54 | if($filename =~ /ZFS/) { 55 | $module_text =~ s/^1;$/### Quick drop-in\nsub splitDataSetSnapshot {return \$splitDataSetSnapshot->(\$_[1]);}\nsub splitHostDataSet {return \$splitHostDataSet->(\$_[1]);}\n\n1;\n/gm; 56 | } elsif($filename =~ /Config/) { 57 | $module_text =~ s/^1;$/### Quick drop-in\nsub splitHostDataSet {return \$splitHostDataSet->(\$_[1]);}\n\n1;\n/gm; 58 | } 59 | 60 | if(defined($ENV{DEBUG_ZNAPZEND_SELFTEST_REWRITE})) { 61 | open(my $fhp, '>', $found . '.selftest-rewritten') or warn "Could not open " . $found . '.selftest-rewritten'; 62 | if ($fhp) { print $fhp $module_text ; close $fhp; } 63 | } 64 | 65 | # filehandle on the scalar 66 | open $fh, '<', \$module_text; 67 | 68 | # and put it into %INC too so that it looks like we loaded the code 69 | # from the file directly 70 | $INC{$filename} = $found; 71 | 72 | warn ("Imported '$found'"); 73 | return $fh; 74 | } 75 | else { 76 | return (); 77 | } 78 | }; 79 | 80 | sub stringify { 81 | my $s = shift; 82 | return $s if defined($s); 83 | return ""; 84 | } 85 | 86 | sub printTaskReportCFG { 87 | print STDERR "[D:zCFG] task='" . stringify($_[0]) . 88 | "' => remote='" . stringify($_[1]) . 89 | "' dataSet='" . stringify($_[2]) . "'\n"; 90 | } 91 | 92 | sub printTaskReportZFS { 93 | print STDERR "[D:zZFS] task='" . stringify($_[0]) . 94 | "' => remote='" . stringify($_[1]) . 95 | "' dataSetPathAndSnap='" . stringify($_[2]) . 96 | "' => dataSet='" . stringify($_[3]) . 97 | "' snapshot='" . stringify($_[4]) . "'\n"; 98 | } 99 | 100 | use Test::More; 101 | 102 | use_ok 'ZnapZend::ZFS'; 103 | 104 | my $zZFS = ZnapZend::ZFS->new(); 105 | 106 | is (ref $zZFS,'ZnapZend::ZFS', 'instantiation of ZFS'); 107 | 108 | # NOTE: In absence of any hints we can not reliably discern below 109 | # a task='poolrootfs@snap-2:3' 110 | # vs. task='username@hostname:poolrootfs' 111 | # which one is a local pool's root dataset with a funny but legal 112 | # snapshot name, and which one is a remote user@host spec with a 113 | # remote pool's root dataset. For practical purposes, we proclaim 114 | # preference for the former: we are more likely to look at funny 115 | # local snapshot names, than to back up to (or otherwise care about) 116 | # remote pools' ROOT datasets. 117 | 118 | for my $r (qw(undef hostname username@hostname)) { 119 | for my $d (qw(poolrootfs rpool/dataset rpool/dataset:with-colon)) { 120 | for my $s (qw(undef snapname snap-1 snap-2:3 snap-12:35:00)) { 121 | #EXAMPLE# my $task = 'user@host:dataset@snapname'; 122 | 123 | my $task = ''; 124 | if ($r ne "undef") { $task .= $r . ':'; } 125 | $task .= $d; 126 | if ($s ne "undef") { $task .= '@' . $s; } 127 | 128 | # Decode it back, see if we can 129 | # Note the methods are externalized from the module for the test by patcher above 130 | my ($remote, $dataSetPathAndSnap) = $zZFS->splitHostDataSet($task); 131 | my ($dataSet, $snapshot) = $zZFS->splitDataSetSnapshot($dataSetPathAndSnap); 132 | printTaskReportZFS($task, $remote, $dataSetPathAndSnap, $dataSet, $snapshot); 133 | 134 | is (defined ($dataSet), 1, "dataSet should always be defined after parsing"); 135 | 136 | # See big comment above: 137 | if ($task eq 'username@hostname:poolrootfs') { 138 | isnt (defined ($remote), 1, "BOGUS exceptional test case: remote should be not defined after parsing for this exceptional test case"); 139 | is (($dataSet eq "username"), 1, "BOGUS exceptional test case: dataSet has expected BOGUS value after parsing for this exceptional test case"); 140 | is (($snapshot eq "hostname:poolrootfs"), 1, "BOGUS exceptional test case: snapshot has expected BOGUS value after parsing for this exceptional test case"); 141 | } else { 142 | is (($dataSet eq $d), 1, "dataSet has expected value after parsing"); 143 | 144 | if ($r ne "undef") { 145 | is (defined ($remote), 1, "remote should be defined after parsing this test case"); 146 | is (($remote eq $r), 1, "remote has expected value after parsing"); 147 | } else { 148 | isnt (defined ($remote), 1, "remote should not be defined after parsing this test case"); 149 | } 150 | 151 | if ($s ne "undef") { 152 | is (defined ($snapshot), 1, "snapshot should be defined after parsing this test case"); 153 | is (($snapshot eq $s), 1, "snapshot has expected value after parsing"); 154 | } else { 155 | isnt (defined ($snapshot), 1, "snapshot should not be defined after parsing this test case"); 156 | } 157 | } 158 | } 159 | } 160 | } 161 | 162 | # This module has its own definition of splitHostDataSet for 163 | # znapzendzetup property parsing - without snapshot parts 164 | use_ok 'ZnapZend::Config'; 165 | 166 | my $zCFG = ZnapZend::Config->new(); 167 | 168 | is (ref $zCFG,'ZnapZend::Config', 'instantiation of Config'); 169 | 170 | for my $r (qw(undef hostname username@hostname)) { 171 | for my $d (qw(poolrootfs rpool/dataset rpool/dataset:with-colon)) { 172 | #EXAMPLE# my $task = 'user@host:dataset'; 173 | 174 | my $task = ''; 175 | if ($r ne "undef") { $task .= $r . ':'; } 176 | $task .= $d; 177 | 178 | # Decode it back, see if we can 179 | # Note the methods are externalized from the module for the test by patcher above 180 | my ($remote, $dataSet) = $zCFG->splitHostDataSet($task); 181 | printTaskReportCFG($task, $remote, $dataSet); 182 | 183 | is (defined ($dataSet), 1, "dataSet should always be defined after parsing"); 184 | is (($dataSet eq $d), 1, "dataSet has expected value after parsing"); 185 | 186 | if ($r ne "undef") { 187 | is (defined ($remote), 1, "remote should be defined after parsing this test case"); 188 | is (($remote eq $r), 1, "remote has expected value after parsing"); 189 | } else { 190 | isnt (defined ($remote), 1, "remote should not be defined after parsing this test case"); 191 | } 192 | } 193 | } 194 | 195 | done_testing; 196 | 197 | 1; 198 | -------------------------------------------------------------------------------- /t/znapzend.t: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env perl 2 | 3 | use strict; 4 | use warnings; 5 | 6 | use FindBin; 7 | $ENV{PATH} = $FindBin::RealBin . ':' . $ENV{PATH}; 8 | my $buildDir; 9 | 10 | BEGIN { 11 | $buildDir = shift @ARGV // $FindBin::RealBin.'/../'; 12 | } 13 | 14 | # PERL5LIB 15 | use lib "$FindBin::Bin/../lib"; 16 | use lib "$buildDir/thirdparty/lib/perl5"; 17 | #place bin path to lib so it is stored in @INC 18 | use lib "$FindBin::Bin/../bin"; 19 | 20 | unshift @INC, sub { 21 | my (undef, $filename) = @_; 22 | return () if $filename !~ /ZnapZend|znapzend/; 23 | if (my $found = (grep { -e $_ } map { "$_/$filename" } grep { !ref } @INC)[0] ) { 24 | local $/ = undef; 25 | open my $fh, '<', $found or die("Can't read module file $found\n"); 26 | my $module_text = <$fh>; 27 | close $fh; 28 | 29 | # define everything in a sub, so Devel::Cover will DTRT 30 | # NB this introduces no extra linefeeds so D::C's line numbers 31 | # in reports match the file on disk 32 | $module_text =~ s/(.*?package\s+\S+)(.*)__END__/$1sub classWrapper {$2} classWrapper();/s; 33 | 34 | # uncomment testing code 35 | $module_text =~ s/### RM_COMM_4_TEST ###//sg; 36 | 37 | # unhide private methods to avoid "Variable will not stay shared" 38 | # warnings that appear due to change of applicable scoping rules 39 | # Note: not '\s*' in the start of string, to avoid matching and 40 | # removing blank lines before the private sub definitions. 41 | $module_text =~ s/^[ \t]*my\s+(\S+\s*=\s*sub.*)$/our $1/gm; 42 | 43 | if(defined($ENV{DEBUG_ZNAPZEND_SELFTEST_REWRITE})) { 44 | open(my $fhp, '>', $found . '.selftest-rewritten') or warn "Could not open " . $found . '.selftest-rewritten'; 45 | if ($fhp) { print $fhp $module_text ; close $fhp; } 46 | } 47 | 48 | # filehandle on the scalar 49 | open $fh, '<', \$module_text; 50 | 51 | # and put it into %INC too so that it looks like we loaded the code 52 | # from the file directly 53 | $INC{$filename} = $found; 54 | return $fh; 55 | } 56 | else { 57 | return (); 58 | } 59 | }; 60 | 61 | sub runCommand { 62 | @ARGV = @_; 63 | 64 | eval { main(); }; 65 | 66 | if ($@) { 67 | # Presumably a die() handler caught something 68 | print STDERR "EXCEPTION: " . $@ . "\n"; 69 | return 0; 70 | }; 71 | 72 | # Return "true" if not failed :) 73 | 1; 74 | } 75 | 76 | sub runCommand_canThrow { 77 | @ARGV = @_; 78 | 79 | main(); 80 | } 81 | 82 | use Test::More; 83 | use Test::Exception; 84 | 85 | use_ok 'ZnapZend'; 86 | 87 | #load program 88 | @ARGV = qw(--help); 89 | do 'znapzend' or die "ERROR: loading program znapzend $@\n"; 90 | 91 | # seems to allow tests to continue so why not? 92 | is (runCommand('--help'), 1, 'znapzend help'); 93 | 94 | is (runCommand(), 1, 'znapzend'); 95 | 96 | throws_ok { runCommand_canThrow(qw(--runonce=nosets) ) } qr/No backup set defined or enabled/, 97 | 'znapzend dies with no backup sets defined or enabled at startup'; 98 | 99 | $ENV{'ZNAPZENDTEST_ZFS_GET_ZEND_DELAY'} = '1'; 100 | is (runCommand(qw(--runonce=tank/source)), 1, 'znapzend --runonce=tank/source with zend-delay==1'); 101 | is (runCommand(qw(--nodelay --runonce=tank/source)), 1, 'znapzend --runonce=tank/source with zend-delay==1 and --nodelay (should ignore the plan setting)'); 102 | $ENV{'ZNAPZENDTEST_ZFS_GET_ZEND_DELAY'} = undef; 103 | 104 | # Try an invalid string, should ignore and proceed without a delay 105 | $ENV{'ZNAPZENDTEST_ZFS_GET_ZEND_DELAY'} = ' qwe '; 106 | # TODO : Find a way to check stderr for qr/Option 'zend-delay' has an invalid value/ 107 | is (runCommand(qw(--runonce=tank/source)), 108 | 1, 'znapzend --runonce=tank/source with zend-delay==" qwe " complains but survives'); 109 | $ENV{'ZNAPZENDTEST_ZFS_GET_ZEND_DELAY'} = undef; 110 | 111 | is (runCommand(qw(--runonce)), 1, 'znapzend runonce without dataset specified succeeds (should find all backup plans)'); 112 | is (runCommand(qw(--runonce=)), 1, 'znapzend runonce with empty dataset == without dataset specified succeeds (should find all backup plans)'); 113 | is (runCommand(qw(--runonce=tank -r)), 1, 'znapzend runonce recursing from a dataset without plan (pool root) succeeds'); 114 | 115 | is (runCommand(qw(--inherited --runonce=tank/source/child)), 1, 'znapzend runonce of a dataset with only an inherited plan succeeds with --inherited flag'); 116 | is (runCommand(qw(--inherited --recursive --runonce=tank/source/child)), 1, 'znapzend runonce of a dataset with only an inherited plan succeeds with --inherited --recursive flag'); 117 | is (runCommand(qw(--inherited --recursive --runonce=tank)), 1, 'znapzend runonce of a dataset only whose descendants have a plan succeeds with --inherited --recursive flag'); 118 | 119 | # Coverage for various failure codepaths of inherited +/- recursive modes 120 | is (runCommand(qw(--inherited --runonce=tank)), 0, 'znapzend runonce of a dataset without a plan fails also with --inherited flag'); 121 | is (runCommand(qw(--recursive --runonce=tank/source/child)), 0, 'znapzend runonce of a dataset with only an inherited plan fails with only --recursive flag and without --inherited'); 122 | is (runCommand(qw(--runonce=tank/source/child)), 0, 'znapzend runonce of a dataset with only an inherited plan fails without --inherit flag'); 123 | is (runCommand(qw(--runonce=tank/dest-disabled)), 1, 'cover znapzend runonce of a dataset with original backup plan and a disabled destination - does not fail'); 124 | 125 | # TODO: Add handling and testing for inherited-config datasets with a locally defined bits of the backup plan, e.g. disabled destinations? 126 | #is (runCommand(qw(--inherited --runonce=tank/source/dest-disabled)), 0, 'cover znapzend runonce of a dataset with inherited backup plan and a disabled destination - such mixing is not supported at the moment'); 127 | 128 | # Valid and invalid variants of forced snapshot name 129 | is (runCommand(qw(--runonce=tank/source --forcedSnapshotSuffix=manualsnap)), 1, 'znapzend --runonce=tank/source --forcedSnapshotSuffix=manualsnap succeeds'); 130 | #is (runCommand(qw(--runonce --forcedSnapshotSuffix=@manualsnap)), 1, 'znapzend --runonce --forcedSnapshotSuffix=@manualsnap succeeds (removes leading @)'); 131 | is (runCommand(qw(--runonce=tank/source --forcedSnapshotSuffix=@manualsnap)), 1, 'znapzend --runonce=tank/source --forcedSnapshotSuffix=@manualsnap succeeds (removes leading @)'); 132 | 133 | is (runCommand(qw(--runonce=tank/source --forcedSnapshotSuffix=manual@snap)), 0, 'znapzend --runonce=tank/source --forcedSnapshotSuffix=manual@snap fails (invalid chars in string)'); 134 | is (runCommand(qw(--runonce=tank/source), '--forcedSnapshotSuffix=manual snap'), 0, 'znapzend --runonce=tank/source --forcedSnapshotSuffix=manual" "snap fails (invalid chars in string)'); 135 | ### SKIPPED : This one gets failed by arg processing routines in a way that test program is killed 136 | #is (runCommand(qw(--runonce=tank/source --forcedSnapshotSuffix=)), 0, 'znapzend --runonce=tank/source --forcedSnapshotSuffix= fails (empty snapname)'); 137 | #throws_ok { runCommand_canThrow(qw(--runonce=tank/source --forcedSnapshotSuffix=) ) } qr/Option forcedSnapshotSuffix requires an argument/, 138 | # 'znapzend --runonce=tank/source --forcedSnapshotSuffix= fails (empty snapname)'; 139 | is (runCommand(qw(--runonce=tank/source), '--forcedSnapshotSuffix= '), 0, 'znapzend --runonce=tank/source --forcedSnapshotSuffix=" " fails (snapname becomes empty after chomp)'); 140 | is (runCommand(qw(--forcedSnapshotSuffix=manualsnap)), 0, 'znapzend --forcedSnapshotSuffix=manualsnap fails (not in runonce mode)'); 141 | 142 | # The --since=x argument currently does not really have failure modes 143 | # (a name might not be in the source dataset snapshot history, for whatever 144 | # reason), so we just need to test its codepath for coverage mostly 145 | is (runCommand(qw(--runonce=tank/source), '--since=20200101-stableSystem'), 1, 146 | 'znapzend --runonce=tank/source --since=20200101-stableSystem succeeds'); 147 | 148 | # Series of tests over usual tank/source with different options 149 | is (runCommand(qw(--runonce=tank/source), '--features=oracleMode,recvu,compressed'), 150 | 1, 'znapzend --features=oracleMode,recvu,compressed --runonce=tank/source succeeds'); 151 | is (runCommand(qw(--runonce=tank/source), '--features=skipIntermediates'), 152 | 1, 'znapzend --features=skipIntermediates --runonce=tank/source succeeds'); 153 | is (runCommand(qw(--runonce=tank/source -i -I), '--features=skipIntermediates'), 154 | 1, 'znapzend --features=skipIntermediates --runonce=tank/source -i -I succeeds (should complain about both opposing flags used)'); 155 | 156 | # Coverage for various failure codepaths 157 | $ENV{'ZNAPZENDTEST_ZFS_GET_DST0PRECMD_FAIL'} = '1'; 158 | is (runCommand(qw(--runonce=tank/source)), 1, 'znapzend sendRecvCleanup with a failed DST PRE command'); 159 | is (runCommand(qw(--runonce=tank/source --skipOnPreSnapCmdFail)), 1, 'znapzend sendRecvCleanup with a failed DST PRE command and --skipOnPreSnapCmdFail'); 160 | is (runCommand(qw(--runonce=tank/source --skipOnPreSendCmdFail)), 1, 'znapzend sendRecvCleanup with a failed DST PRE command and --skipOnPreSendCmdFail'); 161 | $ENV{'ZNAPZENDTEST_ZFS_GET_DST0PRECMD_FAIL'} = undef; 162 | 163 | $ENV{'ZNAPZENDTEST_ZFS_GET_DST0PSTCMD_FAIL'} = '1'; 164 | is (runCommand(qw(--runonce=tank/source)), 1, 'znapzend sendRecvCleanup with a failed DST POST command'); 165 | is (runCommand(qw(--runonce=tank/source --skipOnPreSnapCmdFail)), 1, 'znapzend sendRecvCleanup with a failed DST POST command and --skipOnPreSnapCmdFail'); 166 | is (runCommand(qw(--runonce=tank/source --skipOnPreSendCmdFail)), 1, 'znapzend sendRecvCleanup with a failed DST POST command and --skipOnPreSendCmdFail'); 167 | $ENV{'ZNAPZENDTEST_ZFS_GET_DST0PSTCMD_FAIL'} = undef; 168 | 169 | $ENV{'ZNAPZENDTEST_ZFS_FAIL_send'} = '1'; 170 | is (runCommand(qw(--runonce=tank/source)), 1, 'znapzend sendRecvCleanup with a failed ZFS SEND command'); 171 | $ENV{'ZNAPZENDTEST_ZFS_FAIL_send'} = undef; 172 | 173 | $ENV{'ZNAPZENDTEST_ZFS_FAIL_recv'} = '1'; 174 | is (runCommand(qw(--runonce=tank/source)), 1, 'znapzend sendRecvCleanup with a failed ZFS RECV command'); 175 | $ENV{'ZNAPZENDTEST_ZFS_FAIL_recv'} = undef; 176 | 177 | $ENV{'ZNAPZENDTEST_ZFS_FAIL_destroy'} = '1'; 178 | is (runCommand(qw(--runonce=tank/source)), 1, 'znapzend sendRecvCleanup with a failed ZFS DESTROY command'); 179 | $ENV{'ZNAPZENDTEST_ZFS_FAIL_destroy'} = undef; 180 | 181 | $ENV{'ZNAPZENDTEST_ZFS_FAIL_snapshot'} = '1'; 182 | is (runCommand(qw(--runonce=tank/source)), 1, 'znapzend sendRecvCleanup with a failed ZFS snapshot command'); 183 | $ENV{'ZNAPZENDTEST_ZFS_FAIL_snapshot'} = undef; 184 | 185 | $ENV{'ZNAPZENDTEST_ZFS_SUCCEED_snapshot'} = '1'; 186 | is (runCommand(qw(--runonce=tank/source)), 1, 'znapzend sendRecvCleanup with a successful ZFS snapshot command'); 187 | $ENV{'ZNAPZENDTEST_ZFS_SUCCEED_snapshot'} = undef; 188 | 189 | is (runCommand(qw(--runonce=tank/source --autoCreation)), 1, 'znapzend --autoCreation --runonce=tank/source'); 190 | $ENV{'ZNAPZENDTEST_ZFS_FAIL_create'} = '1'; 191 | is (runCommand(qw(--runonce=tank/source --autoCreation)), 0, 'znapzend --autoCreation --runonce=tank/source with a failed ZFS create command - fails'); 192 | $ENV{'ZNAPZENDTEST_ZFS_FAIL_create'} = undef; 193 | 194 | is (runCommand(qw(--runonce=tank/source), '--features=zfsGetType'), 195 | 1, 'znapzend --features=zfsGetType --runonce=tank/source succeeds with new ZFS'); 196 | is (runCommand(qw(--inherited --runonce=tank/source/child --features=zfsGetType)), 197 | 1, 'znapzend --inherited --features=zfsGetType --runonce=tank/source/child succeeds with new ZFS'); 198 | $ENV{'ZNAPZENDTEST_ZFS_GET_TYPE_UNHANDLED'} = '1'; 199 | is (runCommand(qw(--runonce=tank/source), '--features=zfsGetType'), 200 | 0, 'znapzend --features=zfsGetType --runonce=tank/source fails with old ZFS'); 201 | $ENV{'ZNAPZENDTEST_ZFS_GET_TYPE_UNHANDLED'} = undef; 202 | undef $ENV{'ZNAPZENDTEST_ZFS_GET_TYPE_UNHANDLED'}; 203 | 204 | # Cover another codepath for ZFS that lists snapshots by default 205 | $ENV{'ZNAPZENDTEST_ZPOOL_DEFAULT_listsnapshots'} = 'on'; 206 | is (runCommand(qw(--runonce=tank/source), '--features=zfsGetType'), 207 | 1, 'znapzend --features=zfsGetType --runonce=tank/source succeeds with new ZFS when it lists snapshots'); 208 | is (runCommand(qw(--runonce=tank/source)), 209 | 1, 'znapzend --runonce=tank/source succeeds with new ZFS when it lists snapshots'); 210 | is (runCommand(qw(--inherited --recursive --runonce=tank/source/child --features=zfsGetType)), 211 | 1, 'znapzend --inherited --recursive --features=zfsGetType --runonce=tank/source/child succeeds with new ZFS when it lists snapshots'); 212 | $ENV{'ZNAPZENDTEST_ZFS_GET_TYPE_UNHANDLED'} = '1'; 213 | is (runCommand(qw(--runonce=tank/source), '--features=zfsGetType'), 214 | 0, 'znapzend --features=zfsGetType --runonce=tank/source fails with old ZFS when it lists snapshots'); 215 | $ENV{'ZNAPZENDTEST_ZFS_GET_TYPE_UNHANDLED'} = undef; 216 | $ENV{'ZNAPZENDTEST_ZPOOL_DEFAULT_listsnapshots'} = undef; 217 | 218 | # Test codepath for cleaning of snapshots even if destination is offline 219 | is (runCommand(qw(--cleanOffline --debug)), 220 | 1, 'znapzend --cleanOffline succeeds'); 221 | 222 | # Note: test for daemonized mode are offloaded to another file 223 | 224 | done_testing; 225 | 226 | 1; 227 | -------------------------------------------------------------------------------- /t/znapzendzetup.t: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env perl 2 | 3 | use strict; 4 | use warnings; 5 | 6 | use FindBin; 7 | $ENV{PATH} = $FindBin::RealBin.':'.$ENV{PATH}; 8 | my $buildDir; 9 | 10 | BEGIN { 11 | $buildDir = shift @ARGV // $FindBin::RealBin."/../"; 12 | } 13 | 14 | # PERL5LIB 15 | use lib "$FindBin::Bin/../lib"; 16 | use lib "$buildDir/thirdparty/lib/perl5"; 17 | #place bin path to lib so it is stored in @INC 18 | use lib "$FindBin::Bin/../bin"; 19 | 20 | unshift @INC, sub { 21 | my (undef, $filename) = @_; 22 | return () if $filename !~ /ZnapZend|znapzendzetup/; 23 | if (my $found = (grep { -e $_ } map { "$_/$filename" } grep { !ref } @INC)[0] ) { 24 | local $/ = undef; 25 | open my $fh, '<', $found or die("Can't read module file $found\n"); 26 | my $module_text = <$fh>; 27 | close $fh; 28 | 29 | # define everything in a sub, so Devel::Cover will DTRT 30 | # NB this introduces no extra linefeeds so D::C's line numbers 31 | # in reports match the file on disk 32 | $module_text =~ s/(.*?package\s+\S+)(.*)__END__/$1sub classWrapper {$2} classWrapper();/s; 33 | 34 | # uncomment testing code 35 | $module_text =~ s/### RM_COMM_4_TEST ###//sg; 36 | 37 | # unhide private methods to avoid "Variable will not stay shared" 38 | # warnings that appear due to change of applicable scoping rules 39 | # Note: not '\s*' in the start of string, to avoid matching and 40 | # removing blank lines before the private sub definitions. 41 | $module_text =~ s/^[ \t]*my\s+(\S+\s*=\s*sub.*)$/our $1/gm; 42 | 43 | if(defined($ENV{DEBUG_ZNAPZEND_SELFTEST_REWRITE})) { 44 | open(my $fhp, '>', $found . '.selftest-rewritten') or warn "Could not open " . $found . '.selftest-rewritten'; 45 | if ($fhp) { print $fhp $module_text ; close $fhp; } 46 | } 47 | 48 | # filehandle on the scalar 49 | open $fh, '<', \$module_text; 50 | 51 | # and put it into %INC too so that it looks like we loaded the code 52 | # from the file directly 53 | $INC{$filename} = $found; 54 | return $fh; 55 | } 56 | else { 57 | return (); 58 | } 59 | }; 60 | 61 | sub runCommand { 62 | my $mainOpt = shift; 63 | @ARGV = @_; 64 | 65 | eval { main($mainOpt); }; 66 | 67 | if ($@) { 68 | # Presumably a die() handler caught something 69 | print STDERR "EXCEPTION: " . $@ . "\n"; 70 | return 0; 71 | }; 72 | 73 | # Return "true" if not failed :) 74 | 1; 75 | } 76 | 77 | use Test::More; 78 | 79 | use_ok 'ZnapZend'; 80 | 81 | #load program 82 | @ARGV = qw(help); 83 | do 'znapzendzetup' or die "ERROR: loading program znapzendzetup $@\n"; 84 | 85 | is (runCommand('help'), 1, 'znapzendzetup help'); 86 | 87 | is (runCommand('list'), 1, 'znapzendzetup list'); 88 | 89 | is (runCommand(qw(create SRC 1h=>10min tank/source), 90 | qw(DST 1h=>10min backup/destination)), 1, 'znapzendzetup create'); 91 | 92 | is (runCommand(qw(edit SRC 1h=>10min tank/source), 93 | qw(DST:0 1h=>10min backup/destination)), 1, 'znapzendzetup edit'); 94 | 95 | is (runCommand(qw(edit SRC 33asdf=>10min tank/source), 96 | qw(DST:0 1h=>10min backup/destination)), 0, 'znapzendzetup edit'); 97 | 98 | is (runCommand(qw(edit SRC 33sec=>10min tank/source), 99 | qw(DST:0 1h=>10min backup/destination)), 0, 'znapzendzetup edit'); 100 | 101 | is (runCommand(qw(edit tank/source)), 1, 'znapzendzetup edit src_dataset'); 102 | 103 | is (runCommand(qw(create --donotask --tsformat=%Y%m%d-%H%M%S SRC 1h=>10min tank/source), 104 | qw(DST 1h=>10min backup/destination)), 1, 'znapzendzetup create --donotask'); 105 | 106 | is (runCommand(qw(edit --donotask --tsformat=%Y%m%d-%H%M%S SRC 1h=>10min tank/source), 107 | qw(DST:0 1h=>10min backup/destination)), 1, 'znapzendzetup edit --donotask'); 108 | 109 | is (runCommand(qw(enable tank/source)), 1, 'znapzendzetup enable'); 110 | 111 | is (runCommand(qw(disable tank/source)), 1, 'znapzendzetup disable'); 112 | 113 | is (runCommand(qw(delete tank/source)), 1, 'znapzendzetup delete'); 114 | 115 | is (runCommand(qw(delete --dst='0' tank/source)), 1, 'znapzendzetup delete destination'); 116 | 117 | { 118 | local *STDOUT; 119 | open STDOUT, ">./dump.dmp"; 120 | is (runCommand(qw(export tank/source)), 1, 'znapzendzetup export'); 121 | } 122 | 123 | is (runCommand(qw(import tank/source ./dump.dmp)), 1, 'znapzendzetup import'); 124 | is (runCommand(qw(import --write tank/source ./dump.dmp)), 1, 'znapzendzetup import --write'); 125 | 126 | # Cover various codepaths for successes and failures... 127 | # Destination can be passed by number (N) or zfs attr name (dst_N) 128 | # TODO? Add by target dataset value as the more user-meaningful variant? 129 | is (runCommand(qw(enable-dst tank/source dst_0)), 1, 'znapzendzetup enable-dst tank/source dst_0 - succeeds'); 130 | is (runCommand(qw(disable-dst tank/source dst_0)), 1, 'znapzendzetup disable-dst tank/source dst_0 - succeeds'); 131 | is (runCommand(qw(enable-dst tank/source DST:0)), 1, 'znapzendzetup enable-dst tank/source DST:0 - succeeds'); 132 | is (runCommand(qw(disable-dst tank/source DST:0)), 1, 'znapzendzetup disable-dst tank/source DST:0 - succeeds'); 133 | is (runCommand(qw(enable-dst tank/source 0)), 1, 'znapzendzetup enable-dst tank/source 0 - succeeds (0=>dst_0)'); 134 | is (runCommand(qw(disable-dst tank/source 0)), 1, 'znapzendzetup disable-dst tank/source 0 - succeeds (0=>dst_0)'); 135 | is (runCommand(qw(enable-dst tank/dest-disabled dst_0)), 1, 'znapzendzetup enable-dst tank/dest-disabled dst_0 - succeeds (processing codepath with dst_0_enabled present in zfs args)'); 136 | is (runCommand(qw(disable-dst tank/dest-disabled dst_0)), 1, 'znapzendzetup disable-dst tank/dest-disabled dst_0 - succeeds (processing codepath with dst_0_enabled present in zfs args)'); 137 | 138 | # Destination-management fails for a number of expected reasons 139 | is (runCommand(qw(enable-dst tank/source dst_1_badkey)), 0, 'znapzendzetup enable-dst tank/source dst_1_badkey - fails (arg is not a dst ID pattern)'); 140 | is (runCommand(qw(disable-dst tank/source dst_1_badkey)), 0, 'znapzendzetup disable-dst tank/source dst_1_badkey - fails (arg is not a dst ID pattern)'); 141 | is (runCommand(qw(enable-dst tank/source 1)), 0, 'znapzendzetup enable-dst tank/source 1 - fails (no 1=>dst_1 there)'); 142 | is (runCommand(qw(disable-dst tank/source 1)), 0, 'znapzendzetup disable-dst tank/source 1 - fails (no 1=>dst_1 there)'); 143 | is (runCommand(qw(enable-dst tank/sourcemissing dst_whatever)), 0, 'znapzendzetup enable-dst tank/sourcemissing dst_whatever - fails (no such dataset)'); 144 | is (runCommand(qw(disable-dst tank/sourcemissing dst_whatever)), 0, 'znapzendzetup disable-dst tank/sourcemissing dst_whatever - fails (no such dataset)'); 145 | is (runCommand(qw(enable-dst tank/sourcemissing)), 0, 'znapzendzetup enable-dst tank/sourcemissing - fails (bad arg list)'); 146 | is (runCommand(qw(disable-dst tank/sourcemissing)), 0, 'znapzendzetup disable-dst tank/sourcemissing - fails (bad arg list)'); 147 | 148 | # This one calls "zfs list -r" and then many times "zfs get" 149 | is (runCommand(qw(list), '--features=lowmemRecurse,sudo', qw(--debug -r tank/source)), 1, 'znapzendzetup list --features=lowmemRecurse,sudo --debug -r tank/source'); 150 | # This one calls "zfs get -r" 151 | is (runCommand(qw(list --debug --recursive tank/source)), 1, 'znapzendzetup list --debug --recursive tank/source'); 152 | # This one should follow a codepath of undefined "dataSet" to show and so all datasets known to zfs (mock) 153 | is (runCommand(qw(list)), 1, 'znapzendzetup list'); 154 | # This one should follow a codepath of a dataset array with several entries 155 | is (runCommand(qw(list tank/source tank/anothersource)), 1, 'znapzendzetup list two trees'); 156 | is (runCommand(qw(list --features=lowmemRecurse -r tank/source tank/anothersource)), 1, 'znapzendzetup list lowmem two trees'); 157 | 158 | is (runCommand(qw(list --features=zfsGetType -r tank/source)), 1, 'znapzendzetup list with zfsGetType feature and new zfs - succeeds'); 159 | is (runCommand(qw(list --features=zfsGetType --inherited -r tank/source)), 1, 'znapzendzetup list with zfsGetType feature and --inherited and new zfs - succeeds'); 160 | $ENV{'ZNAPZENDTEST_ZFS_GET_TYPE_UNHANDLED'} = '1'; 161 | is (runCommand(qw(list --features=zfsGetType -r tank/source)), 0, 'znapzendzetup list with zfsGetType feature and old zfs - fails'); 162 | is (runCommand(qw(list --features=zfsGetType --inherited -r tank/source)), 0, 'znapzendzetup list with zfsGetType feature and --inherited and old zfs - fails'); 163 | $ENV{'ZNAPZENDTEST_ZFS_GET_TYPE_UNHANDLED'} = undef; 164 | 165 | # Enabled dataset with an inherited plan: 166 | is (runCommand(qw(list --debug --inherited tank/source/child)), 1, 'znapzendzetup list --inherited tank/source/child succeeds'); 167 | is (runCommand(qw(list --debug --recursive --inherited tank/source/child)), 1, 'znapzendzetup list --inherited -r tank/source/child succeeds (finds only it, not the grandchild)'); 168 | is (runCommand(qw(list --debug --recursive --inherited tank)), 1, 'znapzendzetup list --inherited -r tank succeeds (finds only source and anothersource, not the descendants)'); 169 | is (runCommand(qw(list --debug --recursive --inherited tank tank/source/child)), 1, 'znapzendzetup list --inherited -r tank tank/source/child succeeds (finds source and anothersource via recursion, and the explicit tank/source/child, but not other descendants)'); 170 | 171 | # These should fail 172 | is (runCommand(qw(list --debug --inherited tank)), 0, 'znapzendzetup list --inherited tank (non-recursive) fails'); 173 | is (runCommand(qw(list tank/source/child)), 0, 'znapzendzetup list tank/source/child (no inheritance) fails'); 174 | is (runCommand(qw(list --recursive tank/source/child)), 0, 'znapzendzetup list -r tank/source/child (no inheritance, no descendants with a local config) fails'); 175 | 176 | is (runCommand(qw(list --debug --inherited backup)), 0, 'znapzendzetup list --inherited backup (non-recursive) fails'); 177 | is (runCommand(qw(list --debug --recursive backup)), 0, 'znapzendzetup list --recursive backup fails'); 178 | is (runCommand(qw(list --debug --inherited --recursive backup)), 0, 'znapzendzetup list --inherited --recursive backup fails'); 179 | is (runCommand(qw(list --debug backup)), 0, 'znapzendzetup list backup (non-recursive) fails'); 180 | 181 | is (runCommand(qw(list missingpool)), 0, 'znapzendzetup list missingpool'); 182 | is (runCommand(qw(list -r missingpool)), 0, 'znapzendzetup list -r missingpool'); 183 | is (runCommand(qw(list --features=lowmemRecurse missingpool)), 0, 'znapzendzetup list --features=lowmemRecurse missingpool'); 184 | is (runCommand(qw(list --features=lowmemRecurse -r missingpool)), 0, 'znapzendzetup list --features=lowmemRecurse -r missingpool'); 185 | is (runCommand(qw(export missingpool)), 0, 'znapzendzetup export missingpool'); 186 | 187 | $ENV{'ZNAPZENDTEST_ZFS_FAIL_list'} = '1'; 188 | is (runCommand(qw(list)), 0, 'znapzendzetup list'); 189 | $ENV{'ZNAPZENDTEST_ZFS_FAIL_list'} = undef; 190 | 191 | # Code-cover parsing of bad argument lists 192 | is (runCommand(), 0, 'znapzendzetup - fails'); 193 | is (runCommand(qw(bogusMainOpt)), 0, 'znapzendzetup - fails'); 194 | 195 | is (runCommand(qw(delete)), 0, 'znapzendzetup delete - fails'); 196 | is (runCommand(qw(enable)), 0, 'znapzendzetup enable - fails'); 197 | is (runCommand(qw(disable)), 0, 'znapzendzetup disable - fails'); 198 | is (runCommand(qw(enable-dst)), 0, 'znapzendzetup enable-dst - fails'); 199 | is (runCommand(qw(disable-dst)), 0, 'znapzendzetup disable-dst - fails'); 200 | is (runCommand(qw(export)), 0, 'znapzendzetup export - fails'); 201 | 202 | done_testing; 203 | 204 | 1; 205 | -------------------------------------------------------------------------------- /t/znapzendztatz.t: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env perl 2 | 3 | use strict; 4 | use warnings; 5 | 6 | use FindBin; 7 | $ENV{PATH} = $FindBin::RealBin.':'.$ENV{PATH}; 8 | my $buildDir; 9 | 10 | BEGIN { 11 | $buildDir = shift @ARGV // $FindBin::RealBin."/../"; 12 | } 13 | 14 | # PERL5LIB 15 | use lib "$FindBin::RealBin/../lib"; 16 | use lib "$buildDir/thirdparty/lib/perl5"; 17 | #place bin path to lib so it is stored in @INC 18 | use lib "$FindBin::RealBin/../bin"; 19 | 20 | unshift @INC, sub { 21 | my (undef, $filename) = @_; 22 | return () if $filename !~ /ZnapZend|znapzendztatz/; 23 | if (my $found = (grep { -e $_ } map { "$_/$filename" } grep { !ref } @INC)[0] ) { 24 | local $/ = undef; 25 | open my $fh, '<', $found or die("Can't read module file $found\n"); 26 | my $module_text = <$fh>; 27 | close $fh; 28 | 29 | # define everything in a sub, so Devel::Cover will DTRT 30 | # NB this introduces no extra linefeeds so D::C's line numbers 31 | # in reports match the file on disk 32 | $module_text =~ s/(.*?package\s+\S+)(.*)__END__/$1sub classWrapper {$2} classWrapper();/s; 33 | 34 | # unhide private methods to avoid "Variable will not stay shared" 35 | # warnings that appear due to change of applicable scoping rules 36 | # Note: not '\s*' in the start of string, to avoid matching and 37 | # removing blank lines before the private sub definitions. 38 | $module_text =~ s/^[ \t]*my\s+(\S+\s*=\s*sub.*)$/our $1/gm; 39 | 40 | if(defined($ENV{DEBUG_ZNAPZEND_SELFTEST_REWRITE})) { 41 | open(my $fhp, '>', $found . '.selftest-rewritten') or warn "Could not open " . $found . '.selftest-rewritten'; 42 | if ($fhp) { print $fhp $module_text ; close $fhp; } 43 | } 44 | 45 | # filehandle on the scalar 46 | open $fh, '<', \$module_text; 47 | 48 | # and put it into %INC too so that it looks like we loaded the code 49 | # from the file directly 50 | $INC{$filename} = $found; 51 | return $fh; 52 | } 53 | else { 54 | return (); 55 | } 56 | }; 57 | 58 | sub runCommand { 59 | @ARGV = @_; 60 | 61 | eval { main(); }; 62 | 63 | if ($@) { 64 | # Presumably a die() handler caught something 65 | print STDERR "EXCEPTION: " . $@ . "\n"; 66 | return 0; 67 | }; 68 | 69 | # Return "true" if not failed :) 70 | 1; 71 | } 72 | 73 | use Test::More; 74 | 75 | use_ok 'ZnapZend'; 76 | 77 | #load program 78 | @ARGV = qw(--help); 79 | do 'znapzendztatz' or die "ERROR: loading program znapzendztatz $@\n"; 80 | 81 | is (runCommand('--help'), 1, 'znapzendztatz help'); 82 | 83 | is (runCommand(), 1, 'znapzendztatz'); 84 | 85 | done_testing; 86 | 87 | 1; 88 | -------------------------------------------------------------------------------- /t/zpool: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env perl 2 | 3 | use strict; 4 | use warnings; 5 | 6 | use FindBin; 7 | use Time::Piece; 8 | 9 | my %dataSets = ( 10 | 'tank' => '', 11 | 'backup' => '', 12 | 'remote' => '', 13 | ); 14 | 15 | #print zfs command 16 | print STDERR '# zpool ' . join(' ', @ARGV) . "\n"; 17 | 18 | my $command = shift @ARGV or exit 1; 19 | 20 | for ($command){ 21 | /^(?:scrub)$/ && exit; 22 | 23 | /^list$/ && do { 24 | for (sort keys %dataSets){ 25 | print "$_\n"; 26 | } 27 | exit; 28 | }; 29 | 30 | /^status$/ && do { 31 | exit if !exists $dataSets{$ARGV[-1]}; 32 | my $time = localtime(int(time / 60) * 60 - 3600); 33 | print " scan: scrub repaired 0 in 22h22m with 0 errors on $time\n"; 34 | 35 | exit; 36 | }; 37 | 38 | exit 1; 39 | } 40 | 41 | 1; 42 | 43 | -------------------------------------------------------------------------------- /test.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | # Fail on a test line that dies so we can notice it 4 | set -e 5 | 6 | perl -I./thirdparty/lib/perl5 \ 7 | -MDevel::Cover=+ignore,thirdparty ./t/znapzend.t 8 | perl -I./thirdparty/lib/perl5 \ 9 | -MDevel::Cover=+ignore,thirdparty ./t/znapzend-daemonize.t 10 | perl -I./thirdparty/lib/perl5 \ 11 | -MDevel::Cover=+ignore,thirdparty ./t/znapzendzetup.t 12 | perl -I./thirdparty/lib/perl5 \ 13 | -MDevel::Cover=+ignore,thirdparty ./t/znapzendztatz.t 14 | perl -I./thirdparty/lib/perl5 \ 15 | -MDevel::Cover=+ignore,thirdparty ./t/autoscrub.t 16 | perl -I./thirdparty/lib/perl5 \ 17 | -MDevel::Cover=+ignore,thirdparty ./t/znapzend-lib-splitter.t 18 | -------------------------------------------------------------------------------- /thirdparty/Makefile.am: -------------------------------------------------------------------------------- 1 | # Copyright (C) 2019 Tobias Oetiker 2 | 3 | AUTOMAKE_OPTIONS = foreign 4 | CLEANFILES = 5 | 6 | THIRDPARTY_DIR = $(abs_builddir) 7 | 8 | #GNU# THIRDPARTY_DIST :+ $(shell test -d cache && find cache -type f ) 9 | # THIRDPARTY_DIST = @PERL_THIRDPARTY_DIST@ 10 | CPANSNAPV = cpanfile-@PERL_CONFIG_VERSION@.snapshot 11 | 12 | 13 | #EXTRA_DIST = $(THIRDPARTY_DIST) $(wildcard bin/cpanm) 14 | #EXTRA_DIST = bin/cpanm $(wildcard cpanfile*snapshot) 15 | EXTRA_DIST = bin/cpanm cpanfile*snapshot 16 | 17 | all-local: touch 18 | 19 | ../cpanfile: 20 | +cd .. && $(MAKE) $(AM_MAKEFLAGS) cpanfile 21 | 22 | # NOTE: CPANM pulls a number of dependencies which seem to get installed in 23 | # parallel - in a random chaotic order (pun intended), so a few iterations 24 | # may be required - up to the amount of dependencies in the worst case. 25 | # It seems that neutering inheritance of parallel/nested "make" also helps; 26 | # alternately properly annotating such inheritance (`+` starting the line) 27 | # might also help. Trying both belts and suspenders here. 28 | # To add injury to insult, CPANM also uses `make` and is impacted by e.g. 29 | # the DESTDIR setting (if caller of znapzend `make` passes one) which may 30 | # also be made known via MAKEFLAGS (as of GNU Make). 31 | touch: bin/cpanm carton/bin/carton $(CPANSNAPV) 32 | $(AM_V_at)echo "** Installing Dependencies using $(CPANSNAPV)" 33 | cp $(CPANSNAPV) ../cpanfile.snapshot 34 | # if ever DBD::ODBC is compiled, make sure we get the utf8 version 35 | +$(AM_V_at)TRIES=5 ; unset DESTDIR || true ; unset MAKEFLAGS || true ; \ 36 | while test "$${TRIES}" -gt 0 ; do \ 37 | PERL_CPANM_OPT= PERL_CPANM_HOME=$(THIRDPARTY_DIR) DBD_ODBC_UNICODE=1 PERL5LIB=$(THIRDPARTY_DIR)/carton/lib/perl5 PERL_CARTON_PATH=$(THIRDPARTY_DIR) $(PERL) $(THIRDPARTY_DIR)/carton/bin/carton install \ 38 | && exit ; \ 39 | TRIES="`expr $${TRIES} - 1`" ; \ 40 | echo "** RETRY Installing Dependencies using $(CPANSNAPV) (attempts left: $${TRIES})" ; \ 41 | MAKEFLAGS="" ; export MAKEFLAGS ; \ 42 | done 43 | $(AM_V_at)rm -f ../cpanfile.snapshot 44 | $(AM_V_at)touch touch 45 | 46 | bin/cpanm: 47 | $(AM_V_at)echo "** Installing CPANM tool" 48 | $(AM_V_at)mkdir -p bin 49 | $(URL_CAT) https://cpanmin.us > bin/cpanm 50 | $(AM_V_at)chmod 755 bin/cpanm 51 | 52 | carton/bin/carton: bin/cpanm 53 | $(AM_V_at)echo "** Installing/Checking Carton tool" 54 | +$(AM_V_at)TRIES=5 ; unset DESTDIR || true ; unset MAKEFLAGS || true ; \ 55 | while ! test -x carton/bin/carton ; do \ 56 | PERL_CPANM_OPT= PERL_CPANM_HOME=$(THIRDPARTY_DIR) $(PERL) bin/cpanm -q --notest --local-lib-contained $(THIRDPARTY_DIR)/carton Carton Date::Parse \ 57 | && exit ; \ 58 | TRIES="`expr $${TRIES} - 1`" ; \ 59 | if test "$${TRIES}" -lt 1 ; then exit 1 ; fi ; \ 60 | echo "** RETRY Installing/Checking Carton tool (attempts left: $${TRIES})" ; \ 61 | MAKEFLAGS="" ; export MAKEFLAGS ; \ 62 | done 63 | test -x carton/bin/carton 64 | 65 | test-deps: ../cpanfile.common ../cpanfile.test carton/bin/carton 66 | $(AM_V_at)echo "** Installing Test Dependencies using Carton install" 67 | $(AM_V_at)cat ../cpanfile.common ../cpanfile.test > ../cpanfile 68 | $(AM_V_at)rm -f ../cpanfile.snapshot 69 | +$(AM_V_at)TRIES=5 ; unset DESTDIR || true ; unset MAKEFLAGS || true ; \ 70 | while test "$${TRIES}" -gt 0 ; do \ 71 | PERL_CPANM_OPT= PERL_CPANM_HOME=$(THIRDPARTY_DIR) PERL5LIB=$(THIRDPARTY_DIR)/carton/lib/perl5 PERL_CARTON_PATH=$(THIRDPARTY_DIR) $(PERL) $(THIRDPARTY_DIR)/carton/bin/carton install \ 72 | && exit ; \ 73 | TRIES="`expr $${TRIES} - 1`" ; \ 74 | echo "** RETRY Installing Test Dependencies using Carton install (attempts left: $${TRIES})" ; \ 75 | MAKEFLAGS="" ; export MAKEFLAGS ; \ 76 | done 77 | $(AM_V_at)rm -f ../cpanfile ../cpanfile.snapshot 78 | $(AM_V_at)touch "$@" 79 | 80 | CLEANFILES += test-deps 81 | 82 | $(CPANSNAPV): ../cpanfile carton/bin/carton 83 | $(AM_V_at)echo "** Installing Dependencies using Carton install" 84 | test -f $(CPANSNAPV) && cp $(CPANSNAPV) ../cpanfile.snapshot || true 85 | # if ever DBD::ODBC is compiled, make sure we get the utf8 version 86 | +$(AM_V_at)TRIES=5 ; unset DESTDIR || true ; unset MAKEFLAGS || true ; \ 87 | while test "$${TRIES}" -gt 0 ; do \ 88 | PERL_CPANM_OPT= PERL_CPANM_HOME=$(THIRDPARTY_DIR) DBD_ODBC_UNICODE=1 PERL5LIB=$(THIRDPARTY_DIR)/carton/lib/perl5 PERL_CARTON_PATH=$(THIRDPARTY_DIR) $(PERL) $(THIRDPARTY_DIR)/carton/bin/carton install \ 89 | && exit ; \ 90 | TRIES="`expr $${TRIES} - 1`" ; \ 91 | echo "** RETRY Installing Dependencies using $(CPANSNAPV) (attempts left: $${TRIES})" ; \ 92 | MAKEFLAGS="" ; export MAKEFLAGS ; \ 93 | done 94 | mv ../cpanfile.snapshot $(CPANSNAPV) 95 | $(AM_V_at)touch touch 96 | 97 | update: $(CPANSNAPV) 98 | $(AM_V_at)echo "** Updating Dependencies using Carton update" 99 | $(AM_V_at)cp $(CPANSNAPV) ../cpanfile.snapshot 100 | +$(AM_V_at)TRIES=5 ; unset DESTDIR || true ; unset MAKEFLAGS || true ; \ 101 | while test "$${TRIES}" -gt 0 ; do \ 102 | $(AM_V_at)PERL_CPANM_OPT= PERL_CPANM_HOME=$(THIRDPARTY_DIR) PERL5LIB=$(THIRDPARTY_DIR)/carton/lib/perl5 PERL_CARTON_PATH=$(THIRDPARTY_DIR) $(PERL) $(THIRDPARTY_DIR)/carton/bin/carton update \ 103 | && exit ; \ 104 | TRIES="`expr $${TRIES} - 1`" ; \ 105 | echo "** RETRY Installing Dependencies using $(CPANSNAPV) (attempts left: $${TRIES})" ; \ 106 | MAKEFLAGS="" ; export MAKEFLAGS ; \ 107 | done 108 | $(AM_V_at)mv ../cpanfile.snapshot $(CPANSNAPV) 109 | 110 | clean-local: 111 | ls -1 | grep -v Makefile | grep -v cpanfile | grep -v bin | xargs rm -rf 112 | 113 | distclean-local: 114 | ls -1 | grep -v Makefile | grep -v cpanfile | xargs rm -rf 115 | 116 | install-exec-hook: 117 | cp -fr lib/perl5/* $(DESTDIR)$(libdir) 118 | -------------------------------------------------------------------------------- /thirdparty/cpanfile-5.22.4.snapshot: -------------------------------------------------------------------------------- 1 | # carton snapshot format: version 1.0 2 | DISTRIBUTIONS 3 | ExtUtils-Config-0.008 4 | pathname: L/LE/LEONT/ExtUtils-Config-0.008.tar.gz 5 | provides: 6 | ExtUtils::Config 0.008 7 | requirements: 8 | Data::Dumper 0 9 | ExtUtils::MakeMaker 6.30 10 | strict 0 11 | warnings 0 12 | ExtUtils-Helpers-0.026 13 | pathname: L/LE/LEONT/ExtUtils-Helpers-0.026.tar.gz 14 | provides: 15 | ExtUtils::Helpers 0.026 16 | ExtUtils::Helpers::Unix 0.026 17 | ExtUtils::Helpers::VMS 0.026 18 | ExtUtils::Helpers::Windows 0.026 19 | requirements: 20 | Carp 0 21 | Exporter 5.57 22 | ExtUtils::MakeMaker 0 23 | File::Basename 0 24 | File::Copy 0 25 | File::Spec::Functions 0 26 | Text::ParseWords 3.24 27 | perl 5.006 28 | strict 0 29 | warnings 0 30 | ExtUtils-InstallPaths-0.012 31 | pathname: L/LE/LEONT/ExtUtils-InstallPaths-0.012.tar.gz 32 | provides: 33 | ExtUtils::InstallPaths 0.012 34 | requirements: 35 | Carp 0 36 | ExtUtils::Config 0.002 37 | ExtUtils::MakeMaker 0 38 | File::Spec 0 39 | perl 5.006 40 | strict 0 41 | warnings 0 42 | IO-Pipely-0.005 43 | pathname: R/RC/RCAPUTO/IO-Pipely-0.005.tar.gz 44 | provides: 45 | IO::Pipely 0.005 46 | requirements: 47 | Exporter 5.68 48 | ExtUtils::MakeMaker 6.30 49 | Fcntl 1.06 50 | IO::Socket 1.31 51 | Symbol 1.06 52 | base 2.18 53 | strict 0 54 | warnings 0 55 | Module-Build-0.4231 56 | pathname: L/LE/LEONT/Module-Build-0.4231.tar.gz 57 | provides: 58 | Module::Build 0.4231 59 | Module::Build::Base 0.4231 60 | Module::Build::Compat 0.4231 61 | Module::Build::Config 0.4231 62 | Module::Build::Cookbook 0.4231 63 | Module::Build::Dumper 0.4231 64 | Module::Build::Notes 0.4231 65 | Module::Build::PPMMaker 0.4231 66 | Module::Build::Platform::Default 0.4231 67 | Module::Build::Platform::MacOS 0.4231 68 | Module::Build::Platform::Unix 0.4231 69 | Module::Build::Platform::VMS 0.4231 70 | Module::Build::Platform::VOS 0.4231 71 | Module::Build::Platform::Windows 0.4231 72 | Module::Build::Platform::aix 0.4231 73 | Module::Build::Platform::cygwin 0.4231 74 | Module::Build::Platform::darwin 0.4231 75 | Module::Build::Platform::os2 0.4231 76 | Module::Build::PodParser 0.4231 77 | requirements: 78 | CPAN::Meta 2.142060 79 | Cwd 0 80 | Data::Dumper 0 81 | ExtUtils::CBuilder 0.27 82 | ExtUtils::Install 0 83 | ExtUtils::Manifest 0 84 | ExtUtils::Mkbootstrap 0 85 | ExtUtils::ParseXS 2.21 86 | File::Basename 0 87 | File::Compare 0 88 | File::Copy 0 89 | File::Find 0 90 | File::Path 0 91 | File::Spec 0.82 92 | Getopt::Long 0 93 | Module::Metadata 1.000002 94 | Perl::OSType 1 95 | Pod::Man 2.17 96 | TAP::Harness 3.29 97 | Text::Abbrev 0 98 | Text::ParseWords 0 99 | perl 5.006001 100 | version 0.87 101 | Module-Build-Tiny-0.039 102 | pathname: L/LE/LEONT/Module-Build-Tiny-0.039.tar.gz 103 | provides: 104 | Module::Build::Tiny 0.039 105 | requirements: 106 | CPAN::Meta 0 107 | DynaLoader 0 108 | Exporter 5.57 109 | ExtUtils::CBuilder 0 110 | ExtUtils::Config 0.003 111 | ExtUtils::Helpers 0.020 112 | ExtUtils::Install 0 113 | ExtUtils::InstallPaths 0.002 114 | ExtUtils::ParseXS 0 115 | File::Basename 0 116 | File::Find 0 117 | File::Path 0 118 | File::Spec::Functions 0 119 | Getopt::Long 2.36 120 | JSON::PP 2 121 | Pod::Man 0 122 | TAP::Harness::Env 0 123 | perl 5.006 124 | strict 0 125 | warnings 0 126 | Mojo-IOLoop-Delay-8.76 127 | pathname: J/JB/JBERGER/Mojo-IOLoop-Delay-8.76.tar.gz 128 | provides: 129 | Mojo::IOLoop::Delay 8.76 130 | requirements: 131 | Module::Build::Tiny 0.034 132 | Mojolicious 0 133 | Mojo-IOLoop-ForkCall-0.21 134 | pathname: J/JB/JBERGER/Mojo-IOLoop-ForkCall-0.21.tar.gz 135 | provides: 136 | Mojo::IOLoop::ForkCall 0.21 137 | Mojolicious::Plugin::ForkCall undef 138 | requirements: 139 | IO::Pipely 0 140 | Module::Build 0.38 141 | Mojo::IOLoop::Delay 0 142 | Mojolicious 5.08 143 | Perl::OSType 0 144 | Mojolicious-9.19 145 | pathname: S/SR/SRI/Mojolicious-9.19.tar.gz 146 | provides: 147 | Mojo undef 148 | Mojo::Asset undef 149 | Mojo::Asset::File undef 150 | Mojo::Asset::Memory undef 151 | Mojo::Base undef 152 | Mojo::ByteStream undef 153 | Mojo::Cache undef 154 | Mojo::Collection undef 155 | Mojo::Content undef 156 | Mojo::Content::MultiPart undef 157 | Mojo::Content::Single undef 158 | Mojo::Cookie undef 159 | Mojo::Cookie::Request undef 160 | Mojo::Cookie::Response undef 161 | Mojo::DOM undef 162 | Mojo::DOM::CSS undef 163 | Mojo::DOM::HTML undef 164 | Mojo::Date undef 165 | Mojo::DynamicMethods undef 166 | Mojo::EventEmitter undef 167 | Mojo::Exception undef 168 | Mojo::File undef 169 | Mojo::Headers undef 170 | Mojo::HelloWorld undef 171 | Mojo::Home undef 172 | Mojo::IOLoop undef 173 | Mojo::IOLoop::Client undef 174 | Mojo::IOLoop::Server undef 175 | Mojo::IOLoop::Stream undef 176 | Mojo::IOLoop::Subprocess undef 177 | Mojo::IOLoop::TLS undef 178 | Mojo::JSON undef 179 | Mojo::JSON::Pointer undef 180 | Mojo::Loader undef 181 | Mojo::Log undef 182 | Mojo::Message undef 183 | Mojo::Message::Request undef 184 | Mojo::Message::Response undef 185 | Mojo::Parameters undef 186 | Mojo::Path undef 187 | Mojo::Promise undef 188 | Mojo::Reactor undef 189 | Mojo::Reactor::EV undef 190 | Mojo::Reactor::Poll undef 191 | Mojo::Server undef 192 | Mojo::Server::CGI undef 193 | Mojo::Server::Daemon undef 194 | Mojo::Server::Hypnotoad undef 195 | Mojo::Server::Morbo undef 196 | Mojo::Server::Morbo::Backend undef 197 | Mojo::Server::Morbo::Backend::Poll undef 198 | Mojo::Server::PSGI undef 199 | Mojo::Server::Prefork undef 200 | Mojo::Template undef 201 | Mojo::Transaction undef 202 | Mojo::Transaction::HTTP undef 203 | Mojo::Transaction::WebSocket undef 204 | Mojo::URL undef 205 | Mojo::Upload undef 206 | Mojo::UserAgent undef 207 | Mojo::UserAgent::CookieJar undef 208 | Mojo::UserAgent::Proxy undef 209 | Mojo::UserAgent::Server undef 210 | Mojo::UserAgent::Transactor undef 211 | Mojo::Util undef 212 | Mojo::WebSocket undef 213 | Mojolicious 9.19 214 | Mojolicious::Command undef 215 | Mojolicious::Command::Author::cpanify undef 216 | Mojolicious::Command::Author::generate undef 217 | Mojolicious::Command::Author::generate::app undef 218 | Mojolicious::Command::Author::generate::dockerfile undef 219 | Mojolicious::Command::Author::generate::lite_app undef 220 | Mojolicious::Command::Author::generate::makefile undef 221 | Mojolicious::Command::Author::generate::plugin undef 222 | Mojolicious::Command::Author::inflate undef 223 | Mojolicious::Command::cgi undef 224 | Mojolicious::Command::daemon undef 225 | Mojolicious::Command::eval undef 226 | Mojolicious::Command::get undef 227 | Mojolicious::Command::prefork undef 228 | Mojolicious::Command::psgi undef 229 | Mojolicious::Command::routes undef 230 | Mojolicious::Command::version undef 231 | Mojolicious::Commands undef 232 | Mojolicious::Controller undef 233 | Mojolicious::Lite undef 234 | Mojolicious::Plugin undef 235 | Mojolicious::Plugin::Config undef 236 | Mojolicious::Plugin::DefaultHelpers undef 237 | Mojolicious::Plugin::EPLRenderer undef 238 | Mojolicious::Plugin::EPRenderer undef 239 | Mojolicious::Plugin::HeaderCondition undef 240 | Mojolicious::Plugin::JSONConfig undef 241 | Mojolicious::Plugin::Mount undef 242 | Mojolicious::Plugin::NotYAMLConfig undef 243 | Mojolicious::Plugin::TagHelpers undef 244 | Mojolicious::Plugins undef 245 | Mojolicious::Renderer undef 246 | Mojolicious::Routes undef 247 | Mojolicious::Routes::Match undef 248 | Mojolicious::Routes::Pattern undef 249 | Mojolicious::Routes::Route undef 250 | Mojolicious::Sessions undef 251 | Mojolicious::Static undef 252 | Mojolicious::Types undef 253 | Mojolicious::Validator undef 254 | Mojolicious::Validator::Validation undef 255 | Test::Mojo undef 256 | ojo undef 257 | requirements: 258 | ExtUtils::MakeMaker 0 259 | IO::Socket::IP 0.37 260 | Sub::Util 1.41 261 | perl 5.016 262 | Scalar-List-Utils-1.56 263 | pathname: P/PE/PEVANS/Scalar-List-Utils-1.56.tar.gz 264 | provides: 265 | List::Util 1.56 266 | List::Util::XS 1.56 267 | Scalar::Util 1.56 268 | Sub::Util 1.56 269 | requirements: 270 | ExtUtils::MakeMaker 0 271 | perl 5.006 272 | Sub-Uplevel-0.2800 273 | pathname: D/DA/DAGOLDEN/Sub-Uplevel-0.2800.tar.gz 274 | provides: 275 | Sub::Uplevel 0.2800 276 | requirements: 277 | Carp 0 278 | ExtUtils::MakeMaker 6.17 279 | constant 0 280 | perl 5.006 281 | strict 0 282 | warnings 0 283 | Test-Exception-0.43 284 | pathname: E/EX/EXODIST/Test-Exception-0.43.tar.gz 285 | provides: 286 | Test::Exception 0.43 287 | requirements: 288 | Carp 0 289 | Exporter 0 290 | ExtUtils::MakeMaker 0 291 | Sub::Uplevel 0.18 292 | Test::Builder 0.7 293 | Test::Builder::Tester 1.07 294 | Test::Harness 2.03 295 | base 0 296 | perl 5.006001 297 | strict 0 298 | warnings 0 299 | Test-SharedFork-0.35 300 | pathname: E/EX/EXODIST/Test-SharedFork-0.35.tar.gz 301 | provides: 302 | Test::SharedFork 0.35 303 | Test::SharedFork::Array undef 304 | Test::SharedFork::Scalar undef 305 | Test::SharedFork::Store undef 306 | requirements: 307 | ExtUtils::MakeMaker 6.64 308 | File::Temp 0 309 | Test::Builder 0.32 310 | Test::Builder::Module 0 311 | Test::More 0.88 312 | perl 5.008_001 313 | -------------------------------------------------------------------------------- /thirdparty/cpanfile-5.30.snapshot: -------------------------------------------------------------------------------- 1 | # carton snapshot format: version 1.0 2 | DISTRIBUTIONS 3 | Class-Method-Modifiers-2.15 4 | pathname: E/ET/ETHER/Class-Method-Modifiers-2.15.tar.gz 5 | provides: 6 | Class::Method::Modifiers 2.15 7 | requirements: 8 | B 0 9 | Carp 0 10 | Exporter 0 11 | ExtUtils::MakeMaker 0 12 | base 0 13 | perl 5.006 14 | strict 0 15 | warnings 0 16 | ExtUtils-Config-0.008 17 | pathname: L/LE/LEONT/ExtUtils-Config-0.008.tar.gz 18 | provides: 19 | ExtUtils::Config 0.008 20 | requirements: 21 | Data::Dumper 0 22 | ExtUtils::MakeMaker 6.30 23 | strict 0 24 | warnings 0 25 | ExtUtils-Helpers-0.026 26 | pathname: L/LE/LEONT/ExtUtils-Helpers-0.026.tar.gz 27 | provides: 28 | ExtUtils::Helpers 0.026 29 | ExtUtils::Helpers::Unix 0.026 30 | ExtUtils::Helpers::VMS 0.026 31 | ExtUtils::Helpers::Windows 0.026 32 | requirements: 33 | Carp 0 34 | Exporter 5.57 35 | ExtUtils::MakeMaker 0 36 | File::Basename 0 37 | File::Copy 0 38 | File::Spec::Functions 0 39 | Text::ParseWords 3.24 40 | perl 5.006 41 | strict 0 42 | warnings 0 43 | ExtUtils-InstallPaths-0.012 44 | pathname: L/LE/LEONT/ExtUtils-InstallPaths-0.012.tar.gz 45 | provides: 46 | ExtUtils::InstallPaths 0.012 47 | requirements: 48 | Carp 0 49 | ExtUtils::Config 0.002 50 | ExtUtils::MakeMaker 0 51 | File::Spec 0 52 | perl 5.006 53 | strict 0 54 | warnings 0 55 | Module-Build-Tiny-0.039 56 | pathname: L/LE/LEONT/Module-Build-Tiny-0.039.tar.gz 57 | provides: 58 | Module::Build::Tiny 0.039 59 | requirements: 60 | CPAN::Meta 0 61 | DynaLoader 0 62 | Exporter 5.57 63 | ExtUtils::CBuilder 0 64 | ExtUtils::Config 0.003 65 | ExtUtils::Helpers 0.020 66 | ExtUtils::Install 0 67 | ExtUtils::InstallPaths 0.002 68 | ExtUtils::ParseXS 0 69 | File::Basename 0 70 | File::Find 0 71 | File::Path 0 72 | File::Spec::Functions 0 73 | Getopt::Long 2.36 74 | JSON::PP 2 75 | Pod::Man 0 76 | TAP::Harness::Env 0 77 | perl 5.006 78 | strict 0 79 | warnings 0 80 | Mojo-Log-Clearable-1.001 81 | pathname: D/DB/DBOOK/Mojo-Log-Clearable-1.001.tar.gz 82 | provides: 83 | Mojo::Log::Clearable 1.001 84 | Mojo::Log::Role::Clearable 1.001 85 | requirements: 86 | Class::Method::Modifiers 2.11 87 | Module::Build::Tiny 0.034 88 | Mojolicious 7.42 89 | Role::Tiny 2.000002 90 | perl 5.010001 91 | Mojolicious-9.30 92 | pathname: S/SR/SRI/Mojolicious-9.30.tar.gz 93 | provides: 94 | Mojo undef 95 | Mojo::Asset undef 96 | Mojo::Asset::File undef 97 | Mojo::Asset::Memory undef 98 | Mojo::Base undef 99 | Mojo::ByteStream undef 100 | Mojo::Cache undef 101 | Mojo::Collection undef 102 | Mojo::Content undef 103 | Mojo::Content::MultiPart undef 104 | Mojo::Content::Single undef 105 | Mojo::Cookie undef 106 | Mojo::Cookie::Request undef 107 | Mojo::Cookie::Response undef 108 | Mojo::DOM undef 109 | Mojo::DOM::CSS undef 110 | Mojo::DOM::HTML undef 111 | Mojo::Date undef 112 | Mojo::DynamicMethods undef 113 | Mojo::EventEmitter undef 114 | Mojo::Exception undef 115 | Mojo::File undef 116 | Mojo::Headers undef 117 | Mojo::HelloWorld undef 118 | Mojo::Home undef 119 | Mojo::IOLoop undef 120 | Mojo::IOLoop::Client undef 121 | Mojo::IOLoop::Server undef 122 | Mojo::IOLoop::Stream undef 123 | Mojo::IOLoop::Subprocess undef 124 | Mojo::IOLoop::TLS undef 125 | Mojo::JSON undef 126 | Mojo::JSON::Pointer undef 127 | Mojo::Loader undef 128 | Mojo::Log undef 129 | Mojo::Message undef 130 | Mojo::Message::Request undef 131 | Mojo::Message::Response undef 132 | Mojo::Parameters undef 133 | Mojo::Path undef 134 | Mojo::Promise undef 135 | Mojo::Reactor undef 136 | Mojo::Reactor::EV undef 137 | Mojo::Reactor::Poll undef 138 | Mojo::Server undef 139 | Mojo::Server::CGI undef 140 | Mojo::Server::Daemon undef 141 | Mojo::Server::Hypnotoad undef 142 | Mojo::Server::Morbo undef 143 | Mojo::Server::Morbo::Backend undef 144 | Mojo::Server::Morbo::Backend::Poll undef 145 | Mojo::Server::PSGI undef 146 | Mojo::Server::Prefork undef 147 | Mojo::Template undef 148 | Mojo::Transaction undef 149 | Mojo::Transaction::HTTP undef 150 | Mojo::Transaction::WebSocket undef 151 | Mojo::URL undef 152 | Mojo::Upload undef 153 | Mojo::UserAgent undef 154 | Mojo::UserAgent::CookieJar undef 155 | Mojo::UserAgent::Proxy undef 156 | Mojo::UserAgent::Server undef 157 | Mojo::UserAgent::Transactor undef 158 | Mojo::Util undef 159 | Mojo::WebSocket undef 160 | Mojolicious 9.30 161 | Mojolicious::Command undef 162 | Mojolicious::Command::Author::cpanify undef 163 | Mojolicious::Command::Author::generate undef 164 | Mojolicious::Command::Author::generate::app undef 165 | Mojolicious::Command::Author::generate::dockerfile undef 166 | Mojolicious::Command::Author::generate::lite_app undef 167 | Mojolicious::Command::Author::generate::makefile undef 168 | Mojolicious::Command::Author::generate::plugin undef 169 | Mojolicious::Command::Author::inflate undef 170 | Mojolicious::Command::cgi undef 171 | Mojolicious::Command::daemon undef 172 | Mojolicious::Command::eval undef 173 | Mojolicious::Command::get undef 174 | Mojolicious::Command::prefork undef 175 | Mojolicious::Command::psgi undef 176 | Mojolicious::Command::routes undef 177 | Mojolicious::Command::version undef 178 | Mojolicious::Commands undef 179 | Mojolicious::Controller undef 180 | Mojolicious::Lite undef 181 | Mojolicious::Plugin undef 182 | Mojolicious::Plugin::Config undef 183 | Mojolicious::Plugin::DefaultHelpers undef 184 | Mojolicious::Plugin::EPLRenderer undef 185 | Mojolicious::Plugin::EPRenderer undef 186 | Mojolicious::Plugin::HeaderCondition undef 187 | Mojolicious::Plugin::JSONConfig undef 188 | Mojolicious::Plugin::Mount undef 189 | Mojolicious::Plugin::NotYAMLConfig undef 190 | Mojolicious::Plugin::TagHelpers undef 191 | Mojolicious::Plugins undef 192 | Mojolicious::Renderer undef 193 | Mojolicious::Routes undef 194 | Mojolicious::Routes::Match undef 195 | Mojolicious::Routes::Pattern undef 196 | Mojolicious::Routes::Route undef 197 | Mojolicious::Sessions undef 198 | Mojolicious::Static undef 199 | Mojolicious::Types undef 200 | Mojolicious::Validator undef 201 | Mojolicious::Validator::Validation undef 202 | Test::Mojo undef 203 | ojo undef 204 | requirements: 205 | ExtUtils::MakeMaker 0 206 | IO::Socket::IP 0.37 207 | Sub::Util 1.41 208 | perl 5.016 209 | Role-Tiny-2.002004 210 | pathname: H/HA/HAARG/Role-Tiny-2.002004.tar.gz 211 | provides: 212 | Role::Tiny 2.002004 213 | Role::Tiny::With 2.002004 214 | requirements: 215 | Exporter 5.57 216 | perl 5.006 217 | -------------------------------------------------------------------------------- /thirdparty/cpanfile-5.32.snapshot: -------------------------------------------------------------------------------- 1 | # carton snapshot format: version 1.0 2 | DISTRIBUTIONS 3 | Class-Method-Modifiers-2.15 4 | pathname: E/ET/ETHER/Class-Method-Modifiers-2.15.tar.gz 5 | provides: 6 | Class::Method::Modifiers 2.15 7 | requirements: 8 | B 0 9 | Carp 0 10 | Exporter 0 11 | ExtUtils::MakeMaker 0 12 | base 0 13 | perl 5.006 14 | strict 0 15 | warnings 0 16 | ExtUtils-Config-0.008 17 | pathname: L/LE/LEONT/ExtUtils-Config-0.008.tar.gz 18 | provides: 19 | ExtUtils::Config 0.008 20 | requirements: 21 | Data::Dumper 0 22 | ExtUtils::MakeMaker 6.30 23 | strict 0 24 | warnings 0 25 | ExtUtils-Helpers-0.026 26 | pathname: L/LE/LEONT/ExtUtils-Helpers-0.026.tar.gz 27 | provides: 28 | ExtUtils::Helpers 0.026 29 | ExtUtils::Helpers::Unix 0.026 30 | ExtUtils::Helpers::VMS 0.026 31 | ExtUtils::Helpers::Windows 0.026 32 | requirements: 33 | Carp 0 34 | Exporter 5.57 35 | ExtUtils::MakeMaker 0 36 | File::Basename 0 37 | File::Copy 0 38 | File::Spec::Functions 0 39 | Text::ParseWords 3.24 40 | perl 5.006 41 | strict 0 42 | warnings 0 43 | ExtUtils-InstallPaths-0.012 44 | pathname: L/LE/LEONT/ExtUtils-InstallPaths-0.012.tar.gz 45 | provides: 46 | ExtUtils::InstallPaths 0.012 47 | requirements: 48 | Carp 0 49 | ExtUtils::Config 0.002 50 | ExtUtils::MakeMaker 0 51 | File::Spec 0 52 | perl 5.006 53 | strict 0 54 | warnings 0 55 | Module-Build-Tiny-0.047 56 | pathname: L/LE/LEONT/Module-Build-Tiny-0.047.tar.gz 57 | provides: 58 | Module::Build::Tiny 0.047 59 | requirements: 60 | CPAN::Meta 0 61 | DynaLoader 0 62 | Exporter 5.57 63 | ExtUtils::CBuilder 0 64 | ExtUtils::Config 0.003 65 | ExtUtils::Helpers 0.020 66 | ExtUtils::Install 0 67 | ExtUtils::InstallPaths 0.002 68 | ExtUtils::ParseXS 0 69 | File::Basename 0 70 | File::Find 0 71 | File::Path 0 72 | File::Spec::Functions 0 73 | Getopt::Long 2.36 74 | JSON::PP 2 75 | Pod::Man 0 76 | TAP::Harness::Env 0 77 | perl 5.006 78 | strict 0 79 | warnings 0 80 | Mojo-Log-Clearable-1.001 81 | pathname: D/DB/DBOOK/Mojo-Log-Clearable-1.001.tar.gz 82 | provides: 83 | Mojo::Log::Clearable 1.001 84 | Mojo::Log::Role::Clearable 1.001 85 | requirements: 86 | Class::Method::Modifiers 2.11 87 | Module::Build::Tiny 0.034 88 | Mojolicious 7.42 89 | Role::Tiny 2.000002 90 | perl 5.010001 91 | Mojolicious-9.36 92 | pathname: S/SR/SRI/Mojolicious-9.36.tar.gz 93 | provides: 94 | Mojo undef 95 | Mojo::Asset undef 96 | Mojo::Asset::File undef 97 | Mojo::Asset::Memory undef 98 | Mojo::Base undef 99 | Mojo::ByteStream undef 100 | Mojo::Cache undef 101 | Mojo::Collection undef 102 | Mojo::Content undef 103 | Mojo::Content::MultiPart undef 104 | Mojo::Content::Single undef 105 | Mojo::Cookie undef 106 | Mojo::Cookie::Request undef 107 | Mojo::Cookie::Response undef 108 | Mojo::DOM undef 109 | Mojo::DOM::CSS undef 110 | Mojo::DOM::HTML undef 111 | Mojo::Date undef 112 | Mojo::DynamicMethods undef 113 | Mojo::EventEmitter undef 114 | Mojo::Exception undef 115 | Mojo::File undef 116 | Mojo::Headers undef 117 | Mojo::HelloWorld undef 118 | Mojo::Home undef 119 | Mojo::IOLoop undef 120 | Mojo::IOLoop::Client undef 121 | Mojo::IOLoop::Server undef 122 | Mojo::IOLoop::Stream undef 123 | Mojo::IOLoop::Subprocess undef 124 | Mojo::IOLoop::TLS undef 125 | Mojo::JSON undef 126 | Mojo::JSON::Pointer undef 127 | Mojo::Loader undef 128 | Mojo::Log undef 129 | Mojo::Message undef 130 | Mojo::Message::Request undef 131 | Mojo::Message::Response undef 132 | Mojo::Parameters undef 133 | Mojo::Path undef 134 | Mojo::Promise undef 135 | Mojo::Reactor undef 136 | Mojo::Reactor::EV undef 137 | Mojo::Reactor::Poll undef 138 | Mojo::Server undef 139 | Mojo::Server::CGI undef 140 | Mojo::Server::Daemon undef 141 | Mojo::Server::Hypnotoad undef 142 | Mojo::Server::Morbo undef 143 | Mojo::Server::Morbo::Backend undef 144 | Mojo::Server::Morbo::Backend::Poll undef 145 | Mojo::Server::PSGI undef 146 | Mojo::Server::Prefork undef 147 | Mojo::Template undef 148 | Mojo::Transaction undef 149 | Mojo::Transaction::HTTP undef 150 | Mojo::Transaction::WebSocket undef 151 | Mojo::URL undef 152 | Mojo::Upload undef 153 | Mojo::UserAgent undef 154 | Mojo::UserAgent::CookieJar undef 155 | Mojo::UserAgent::Proxy undef 156 | Mojo::UserAgent::Server undef 157 | Mojo::UserAgent::Transactor undef 158 | Mojo::Util undef 159 | Mojo::WebSocket undef 160 | Mojolicious 9.36 161 | Mojolicious::Command undef 162 | Mojolicious::Command::Author::cpanify undef 163 | Mojolicious::Command::Author::generate undef 164 | Mojolicious::Command::Author::generate::app undef 165 | Mojolicious::Command::Author::generate::dockerfile undef 166 | Mojolicious::Command::Author::generate::lite_app undef 167 | Mojolicious::Command::Author::generate::makefile undef 168 | Mojolicious::Command::Author::generate::plugin undef 169 | Mojolicious::Command::Author::inflate undef 170 | Mojolicious::Command::cgi undef 171 | Mojolicious::Command::daemon undef 172 | Mojolicious::Command::eval undef 173 | Mojolicious::Command::get undef 174 | Mojolicious::Command::prefork undef 175 | Mojolicious::Command::psgi undef 176 | Mojolicious::Command::routes undef 177 | Mojolicious::Command::version undef 178 | Mojolicious::Commands undef 179 | Mojolicious::Controller undef 180 | Mojolicious::Lite undef 181 | Mojolicious::Plugin undef 182 | Mojolicious::Plugin::Config undef 183 | Mojolicious::Plugin::DefaultHelpers undef 184 | Mojolicious::Plugin::EPLRenderer undef 185 | Mojolicious::Plugin::EPRenderer undef 186 | Mojolicious::Plugin::HeaderCondition undef 187 | Mojolicious::Plugin::JSONConfig undef 188 | Mojolicious::Plugin::Mount undef 189 | Mojolicious::Plugin::NotYAMLConfig undef 190 | Mojolicious::Plugin::TagHelpers undef 191 | Mojolicious::Plugins undef 192 | Mojolicious::Renderer undef 193 | Mojolicious::Routes undef 194 | Mojolicious::Routes::Match undef 195 | Mojolicious::Routes::Pattern undef 196 | Mojolicious::Routes::Route undef 197 | Mojolicious::Sessions undef 198 | Mojolicious::Static undef 199 | Mojolicious::Types undef 200 | Mojolicious::Validator undef 201 | Mojolicious::Validator::Validation undef 202 | Test::Mojo undef 203 | ojo undef 204 | requirements: 205 | ExtUtils::MakeMaker 0 206 | IO::Socket::IP 0.37 207 | Sub::Util 1.41 208 | perl 5.016 209 | Role-Tiny-2.002004 210 | pathname: H/HA/HAARG/Role-Tiny-2.002004.tar.gz 211 | provides: 212 | Role::Tiny 2.002004 213 | Role::Tiny::With 2.002004 214 | requirements: 215 | Exporter 5.57 216 | perl 5.006 217 | -------------------------------------------------------------------------------- /thirdparty/cpanfile-5.34.0.snapshot: -------------------------------------------------------------------------------- 1 | # carton snapshot format: version 1.0 2 | DISTRIBUTIONS 3 | CPAN-Meta-Requirements-2.143 4 | pathname: R/RJ/RJBS/CPAN-Meta-Requirements-2.143.tar.gz 5 | provides: 6 | CPAN::Meta::Requirements 2.143 7 | CPAN::Meta::Requirements::Range 2.143 8 | requirements: 9 | B 0 10 | Carp 0 11 | ExtUtils::MakeMaker 6.17 12 | perl 5.010000 13 | strict 0 14 | version 0.88 15 | warnings 0 16 | CPAN-Requirements-Dynamic-0.001 17 | pathname: L/LE/LEONT/CPAN-Requirements-Dynamic-0.001.tar.gz 18 | provides: 19 | CPAN::Requirements::Dynamic 0.001 20 | requirements: 21 | CPAN::Meta::Prereqs 0 22 | CPAN::Meta::Requirements::Range 0 23 | Carp 0 24 | ExtUtils::Config 0 25 | ExtUtils::HasCompiler 0 26 | ExtUtils::MakeMaker 0 27 | IPC::Cmd 0 28 | Module::Metadata 0 29 | Parse::CPAN::Meta 0 30 | Perl::OSType 0 31 | perl 5.006 32 | strict 0 33 | warnings 0 34 | ExtUtils-Config-0.009 35 | pathname: L/LE/LEONT/ExtUtils-Config-0.009.tar.gz 36 | provides: 37 | ExtUtils::Config 0.009 38 | ExtUtils::Config::MakeMaker 0.009 39 | requirements: 40 | Data::Dumper 0 41 | ExtUtils::MakeMaker 0 42 | ExtUtils::MakeMaker::Config 0 43 | perl 5.006 44 | strict 0 45 | warnings 0 46 | ExtUtils-HasCompiler-0.025 47 | pathname: L/LE/LEONT/ExtUtils-HasCompiler-0.025.tar.gz 48 | provides: 49 | ExtUtils::HasCompiler 0.025 50 | requirements: 51 | Carp 0 52 | DynaLoader 0 53 | Exporter 0 54 | ExtUtils::MakeMaker 0 55 | ExtUtils::Mksymlists 0 56 | File::Basename 0 57 | File::Spec::Functions 0 58 | File::Temp 0 59 | base 0 60 | perl 5.006 61 | strict 0 62 | warnings 0 63 | ExtUtils-Helpers-0.026 64 | pathname: L/LE/LEONT/ExtUtils-Helpers-0.026.tar.gz 65 | provides: 66 | ExtUtils::Helpers 0.026 67 | ExtUtils::Helpers::Unix 0.026 68 | ExtUtils::Helpers::VMS 0.026 69 | ExtUtils::Helpers::Windows 0.026 70 | requirements: 71 | Carp 0 72 | Exporter 5.57 73 | ExtUtils::MakeMaker 0 74 | File::Basename 0 75 | File::Copy 0 76 | File::Spec::Functions 0 77 | Text::ParseWords 3.24 78 | perl 5.006 79 | strict 0 80 | warnings 0 81 | ExtUtils-InstallPaths-0.013 82 | pathname: L/LE/LEONT/ExtUtils-InstallPaths-0.013.tar.gz 83 | provides: 84 | ExtUtils::InstallPaths 0.013 85 | requirements: 86 | Carp 0 87 | ExtUtils::Config 0.002 88 | ExtUtils::MakeMaker 0 89 | File::Spec 0 90 | perl 5.006 91 | strict 0 92 | warnings 0 93 | IO-Pipely-0.006 94 | pathname: R/RC/RCAPUTO/IO-Pipely-0.006.tar.gz 95 | provides: 96 | IO::Pipely 0.006 97 | requirements: 98 | Exporter 5.72 99 | ExtUtils::MakeMaker 0 100 | Fcntl 1.13 101 | IO::Socket 1.38 102 | Symbol 1.08 103 | base 0 104 | perl 5.004 105 | strict 0 106 | warnings 0 107 | Module-Build-0.4234 108 | pathname: L/LE/LEONT/Module-Build-0.4234.tar.gz 109 | provides: 110 | Module::Build 0.4234 111 | Module::Build::Base 0.4234 112 | Module::Build::Compat 0.4234 113 | Module::Build::Config 0.4234 114 | Module::Build::Cookbook 0.4234 115 | Module::Build::Dumper 0.4234 116 | Module::Build::Notes 0.4234 117 | Module::Build::PPMMaker 0.4234 118 | Module::Build::Platform::Default 0.4234 119 | Module::Build::Platform::MacOS 0.4234 120 | Module::Build::Platform::Unix 0.4234 121 | Module::Build::Platform::VMS 0.4234 122 | Module::Build::Platform::VOS 0.4234 123 | Module::Build::Platform::Windows 0.4234 124 | Module::Build::Platform::aix 0.4234 125 | Module::Build::Platform::cygwin 0.4234 126 | Module::Build::Platform::darwin 0.4234 127 | Module::Build::Platform::os2 0.4234 128 | Module::Build::PodParser 0.4234 129 | requirements: 130 | CPAN::Meta 2.142060 131 | Cwd 0 132 | Data::Dumper 0 133 | ExtUtils::CBuilder 0.27 134 | ExtUtils::Install 0 135 | ExtUtils::Manifest 0 136 | ExtUtils::Mkbootstrap 0 137 | ExtUtils::ParseXS 2.21 138 | File::Basename 0 139 | File::Compare 0 140 | File::Copy 0 141 | File::Find 0 142 | File::Path 0 143 | File::Spec 0.82 144 | Getopt::Long 0 145 | Module::Metadata 1.000002 146 | Perl::OSType 1 147 | TAP::Harness 3.29 148 | Text::Abbrev 0 149 | Text::ParseWords 0 150 | perl 5.006001 151 | version 0.87 152 | Module-Build-Tiny-0.048 153 | pathname: L/LE/LEONT/Module-Build-Tiny-0.048.tar.gz 154 | provides: 155 | Module::Build::Tiny 0.048 156 | requirements: 157 | CPAN::Meta 0 158 | CPAN::Requirements::Dynamic 0 159 | DynaLoader 0 160 | Exporter 5.57 161 | ExtUtils::CBuilder 0 162 | ExtUtils::Config 0.003 163 | ExtUtils::Helpers 0.020 164 | ExtUtils::Install 0 165 | ExtUtils::InstallPaths 0.002 166 | ExtUtils::ParseXS 0 167 | File::Basename 0 168 | File::Find 0 169 | File::Path 0 170 | File::Spec::Functions 0 171 | Getopt::Long 2.36 172 | JSON::PP 2 173 | Pod::Man 0 174 | TAP::Harness::Env 0 175 | perl 5.006 176 | strict 0 177 | warnings 0 178 | Mojo-IOLoop-Delay-8.76 179 | pathname: J/JB/JBERGER/Mojo-IOLoop-Delay-8.76.tar.gz 180 | provides: 181 | Mojo::IOLoop::Delay 8.76 182 | requirements: 183 | Module::Build::Tiny 0.034 184 | Mojolicious 0 185 | Mojo-IOLoop-ForkCall-0.21 186 | pathname: J/JB/JBERGER/Mojo-IOLoop-ForkCall-0.21.tar.gz 187 | provides: 188 | Mojo::IOLoop::ForkCall 0.21 189 | Mojolicious::Plugin::ForkCall undef 190 | requirements: 191 | IO::Pipely 0 192 | Module::Build 0.38 193 | Mojo::IOLoop::Delay 0 194 | Mojolicious 5.08 195 | Perl::OSType 0 196 | Mojolicious-9.37 197 | pathname: S/SR/SRI/Mojolicious-9.37.tar.gz 198 | provides: 199 | Mojo undef 200 | Mojo::Asset undef 201 | Mojo::Asset::File undef 202 | Mojo::Asset::Memory undef 203 | Mojo::Base undef 204 | Mojo::BaseUtil undef 205 | Mojo::ByteStream undef 206 | Mojo::Cache undef 207 | Mojo::Collection undef 208 | Mojo::Content undef 209 | Mojo::Content::MultiPart undef 210 | Mojo::Content::Single undef 211 | Mojo::Cookie undef 212 | Mojo::Cookie::Request undef 213 | Mojo::Cookie::Response undef 214 | Mojo::DOM undef 215 | Mojo::DOM::CSS undef 216 | Mojo::DOM::HTML undef 217 | Mojo::Date undef 218 | Mojo::DynamicMethods undef 219 | Mojo::EventEmitter undef 220 | Mojo::Exception undef 221 | Mojo::File undef 222 | Mojo::Headers undef 223 | Mojo::HelloWorld undef 224 | Mojo::Home undef 225 | Mojo::IOLoop undef 226 | Mojo::IOLoop::Client undef 227 | Mojo::IOLoop::Server undef 228 | Mojo::IOLoop::Stream undef 229 | Mojo::IOLoop::Subprocess undef 230 | Mojo::IOLoop::TLS undef 231 | Mojo::JSON undef 232 | Mojo::JSON::Pointer undef 233 | Mojo::Loader undef 234 | Mojo::Log undef 235 | Mojo::Message undef 236 | Mojo::Message::Request undef 237 | Mojo::Message::Response undef 238 | Mojo::Parameters undef 239 | Mojo::Path undef 240 | Mojo::Promise undef 241 | Mojo::Reactor undef 242 | Mojo::Reactor::EV undef 243 | Mojo::Reactor::Poll undef 244 | Mojo::Server undef 245 | Mojo::Server::CGI undef 246 | Mojo::Server::Daemon undef 247 | Mojo::Server::Hypnotoad undef 248 | Mojo::Server::Morbo undef 249 | Mojo::Server::Morbo::Backend undef 250 | Mojo::Server::Morbo::Backend::Poll undef 251 | Mojo::Server::PSGI undef 252 | Mojo::Server::Prefork undef 253 | Mojo::Template undef 254 | Mojo::Transaction undef 255 | Mojo::Transaction::HTTP undef 256 | Mojo::Transaction::WebSocket undef 257 | Mojo::URL undef 258 | Mojo::Upload undef 259 | Mojo::UserAgent undef 260 | Mojo::UserAgent::CookieJar undef 261 | Mojo::UserAgent::Proxy undef 262 | Mojo::UserAgent::Server undef 263 | Mojo::UserAgent::Transactor undef 264 | Mojo::Util undef 265 | Mojo::WebSocket undef 266 | Mojolicious 9.37 267 | Mojolicious::Command undef 268 | Mojolicious::Command::Author::cpanify undef 269 | Mojolicious::Command::Author::generate undef 270 | Mojolicious::Command::Author::generate::app undef 271 | Mojolicious::Command::Author::generate::dockerfile undef 272 | Mojolicious::Command::Author::generate::lite_app undef 273 | Mojolicious::Command::Author::generate::makefile undef 274 | Mojolicious::Command::Author::generate::plugin undef 275 | Mojolicious::Command::Author::inflate undef 276 | Mojolicious::Command::cgi undef 277 | Mojolicious::Command::daemon undef 278 | Mojolicious::Command::eval undef 279 | Mojolicious::Command::get undef 280 | Mojolicious::Command::prefork undef 281 | Mojolicious::Command::psgi undef 282 | Mojolicious::Command::routes undef 283 | Mojolicious::Command::version undef 284 | Mojolicious::Commands undef 285 | Mojolicious::Controller undef 286 | Mojolicious::Lite undef 287 | Mojolicious::Plugin undef 288 | Mojolicious::Plugin::Config undef 289 | Mojolicious::Plugin::DefaultHelpers undef 290 | Mojolicious::Plugin::EPLRenderer undef 291 | Mojolicious::Plugin::EPRenderer undef 292 | Mojolicious::Plugin::HeaderCondition undef 293 | Mojolicious::Plugin::JSONConfig undef 294 | Mojolicious::Plugin::Mount undef 295 | Mojolicious::Plugin::NotYAMLConfig undef 296 | Mojolicious::Plugin::TagHelpers undef 297 | Mojolicious::Plugins undef 298 | Mojolicious::Renderer undef 299 | Mojolicious::Routes undef 300 | Mojolicious::Routes::Match undef 301 | Mojolicious::Routes::Pattern undef 302 | Mojolicious::Routes::Route undef 303 | Mojolicious::Sessions undef 304 | Mojolicious::Static undef 305 | Mojolicious::Types undef 306 | Mojolicious::Validator undef 307 | Mojolicious::Validator::Validation undef 308 | Test::Mojo undef 309 | ojo undef 310 | requirements: 311 | ExtUtils::MakeMaker 0 312 | IO::Socket::IP 0.37 313 | Sub::Util 1.41 314 | perl 5.016 315 | Sub-Uplevel-0.2800 316 | pathname: D/DA/DAGOLDEN/Sub-Uplevel-0.2800.tar.gz 317 | provides: 318 | Sub::Uplevel 0.2800 319 | requirements: 320 | Carp 0 321 | ExtUtils::MakeMaker 6.17 322 | constant 0 323 | perl 5.006 324 | strict 0 325 | warnings 0 326 | Test-Exception-0.43 327 | pathname: E/EX/EXODIST/Test-Exception-0.43.tar.gz 328 | provides: 329 | Test::Exception 0.43 330 | requirements: 331 | Carp 0 332 | Exporter 0 333 | ExtUtils::MakeMaker 0 334 | Sub::Uplevel 0.18 335 | Test::Builder 0.7 336 | Test::Builder::Tester 1.07 337 | Test::Harness 2.03 338 | base 0 339 | perl 5.006001 340 | strict 0 341 | warnings 0 342 | -------------------------------------------------------------------------------- /thirdparty/cpanfile-5.36.snapshot: -------------------------------------------------------------------------------- 1 | # carton snapshot format: version 1.0 2 | DISTRIBUTIONS 3 | Class-Method-Modifiers-2.15 4 | pathname: E/ET/ETHER/Class-Method-Modifiers-2.15.tar.gz 5 | provides: 6 | Class::Method::Modifiers 2.15 7 | requirements: 8 | B 0 9 | Carp 0 10 | Exporter 0 11 | ExtUtils::MakeMaker 0 12 | base 0 13 | perl 5.006 14 | strict 0 15 | warnings 0 16 | ExtUtils-Config-0.008 17 | pathname: L/LE/LEONT/ExtUtils-Config-0.008.tar.gz 18 | provides: 19 | ExtUtils::Config 0.008 20 | requirements: 21 | Data::Dumper 0 22 | ExtUtils::MakeMaker 6.30 23 | strict 0 24 | warnings 0 25 | ExtUtils-Helpers-0.026 26 | pathname: L/LE/LEONT/ExtUtils-Helpers-0.026.tar.gz 27 | provides: 28 | ExtUtils::Helpers 0.026 29 | ExtUtils::Helpers::Unix 0.026 30 | ExtUtils::Helpers::VMS 0.026 31 | ExtUtils::Helpers::Windows 0.026 32 | requirements: 33 | Carp 0 34 | Exporter 5.57 35 | ExtUtils::MakeMaker 0 36 | File::Basename 0 37 | File::Copy 0 38 | File::Spec::Functions 0 39 | Text::ParseWords 3.24 40 | perl 5.006 41 | strict 0 42 | warnings 0 43 | ExtUtils-InstallPaths-0.012 44 | pathname: L/LE/LEONT/ExtUtils-InstallPaths-0.012.tar.gz 45 | provides: 46 | ExtUtils::InstallPaths 0.012 47 | requirements: 48 | Carp 0 49 | ExtUtils::Config 0.002 50 | ExtUtils::MakeMaker 0 51 | File::Spec 0 52 | perl 5.006 53 | strict 0 54 | warnings 0 55 | IO-Pipely-0.006 56 | pathname: R/RC/RCAPUTO/IO-Pipely-0.006.tar.gz 57 | provides: 58 | IO::Pipely 0.006 59 | requirements: 60 | Exporter 5.72 61 | ExtUtils::MakeMaker 0 62 | Fcntl 1.13 63 | IO::Socket 1.38 64 | Symbol 1.08 65 | base 0 66 | perl 5.004 67 | strict 0 68 | warnings 0 69 | Module-Build-0.4234 70 | pathname: L/LE/LEONT/Module-Build-0.4234.tar.gz 71 | provides: 72 | Module::Build 0.4234 73 | Module::Build::Base 0.4234 74 | Module::Build::Compat 0.4234 75 | Module::Build::Config 0.4234 76 | Module::Build::Cookbook 0.4234 77 | Module::Build::Dumper 0.4234 78 | Module::Build::Notes 0.4234 79 | Module::Build::PPMMaker 0.4234 80 | Module::Build::Platform::Default 0.4234 81 | Module::Build::Platform::MacOS 0.4234 82 | Module::Build::Platform::Unix 0.4234 83 | Module::Build::Platform::VMS 0.4234 84 | Module::Build::Platform::VOS 0.4234 85 | Module::Build::Platform::Windows 0.4234 86 | Module::Build::Platform::aix 0.4234 87 | Module::Build::Platform::cygwin 0.4234 88 | Module::Build::Platform::darwin 0.4234 89 | Module::Build::Platform::os2 0.4234 90 | Module::Build::PodParser 0.4234 91 | requirements: 92 | CPAN::Meta 2.142060 93 | Cwd 0 94 | Data::Dumper 0 95 | ExtUtils::CBuilder 0.27 96 | ExtUtils::Install 0 97 | ExtUtils::Manifest 0 98 | ExtUtils::Mkbootstrap 0 99 | ExtUtils::ParseXS 2.21 100 | File::Basename 0 101 | File::Compare 0 102 | File::Copy 0 103 | File::Find 0 104 | File::Path 0 105 | File::Spec 0.82 106 | Getopt::Long 0 107 | Module::Metadata 1.000002 108 | Perl::OSType 1 109 | TAP::Harness 3.29 110 | Text::Abbrev 0 111 | Text::ParseWords 0 112 | perl 5.006001 113 | version 0.87 114 | Module-Build-Tiny-0.047 115 | pathname: L/LE/LEONT/Module-Build-Tiny-0.047.tar.gz 116 | provides: 117 | Module::Build::Tiny 0.047 118 | requirements: 119 | CPAN::Meta 0 120 | DynaLoader 0 121 | Exporter 5.57 122 | ExtUtils::CBuilder 0 123 | ExtUtils::Config 0.003 124 | ExtUtils::Helpers 0.020 125 | ExtUtils::Install 0 126 | ExtUtils::InstallPaths 0.002 127 | ExtUtils::ParseXS 0 128 | File::Basename 0 129 | File::Find 0 130 | File::Path 0 131 | File::Spec::Functions 0 132 | Getopt::Long 2.36 133 | JSON::PP 2 134 | Pod::Man 0 135 | TAP::Harness::Env 0 136 | perl 5.006 137 | strict 0 138 | warnings 0 139 | Mojo-IOLoop-Delay-8.76 140 | pathname: J/JB/JBERGER/Mojo-IOLoop-Delay-8.76.tar.gz 141 | provides: 142 | Mojo::IOLoop::Delay 8.76 143 | requirements: 144 | Module::Build::Tiny 0.034 145 | Mojolicious 0 146 | Mojo-IOLoop-ForkCall-0.21 147 | pathname: J/JB/JBERGER/Mojo-IOLoop-ForkCall-0.21.tar.gz 148 | provides: 149 | Mojo::IOLoop::ForkCall 0.21 150 | Mojolicious::Plugin::ForkCall undef 151 | requirements: 152 | IO::Pipely 0 153 | Module::Build 0.38 154 | Mojo::IOLoop::Delay 0 155 | Mojolicious 5.08 156 | Perl::OSType 0 157 | Mojo-Log-Clearable-1.001 158 | pathname: D/DB/DBOOK/Mojo-Log-Clearable-1.001.tar.gz 159 | provides: 160 | Mojo::Log::Clearable 1.001 161 | Mojo::Log::Role::Clearable 1.001 162 | requirements: 163 | Class::Method::Modifiers 2.11 164 | Module::Build::Tiny 0.034 165 | Mojolicious 7.42 166 | Role::Tiny 2.000002 167 | perl 5.010001 168 | Mojolicious-9.35 169 | pathname: S/SR/SRI/Mojolicious-9.35.tar.gz 170 | provides: 171 | Mojo undef 172 | Mojo::Asset undef 173 | Mojo::Asset::File undef 174 | Mojo::Asset::Memory undef 175 | Mojo::Base undef 176 | Mojo::ByteStream undef 177 | Mojo::Cache undef 178 | Mojo::Collection undef 179 | Mojo::Content undef 180 | Mojo::Content::MultiPart undef 181 | Mojo::Content::Single undef 182 | Mojo::Cookie undef 183 | Mojo::Cookie::Request undef 184 | Mojo::Cookie::Response undef 185 | Mojo::DOM undef 186 | Mojo::DOM::CSS undef 187 | Mojo::DOM::HTML undef 188 | Mojo::Date undef 189 | Mojo::DynamicMethods undef 190 | Mojo::EventEmitter undef 191 | Mojo::Exception undef 192 | Mojo::File undef 193 | Mojo::Headers undef 194 | Mojo::HelloWorld undef 195 | Mojo::Home undef 196 | Mojo::IOLoop undef 197 | Mojo::IOLoop::Client undef 198 | Mojo::IOLoop::Server undef 199 | Mojo::IOLoop::Stream undef 200 | Mojo::IOLoop::Subprocess undef 201 | Mojo::IOLoop::TLS undef 202 | Mojo::JSON undef 203 | Mojo::JSON::Pointer undef 204 | Mojo::Loader undef 205 | Mojo::Log undef 206 | Mojo::Message undef 207 | Mojo::Message::Request undef 208 | Mojo::Message::Response undef 209 | Mojo::Parameters undef 210 | Mojo::Path undef 211 | Mojo::Promise undef 212 | Mojo::Reactor undef 213 | Mojo::Reactor::EV undef 214 | Mojo::Reactor::Poll undef 215 | Mojo::Server undef 216 | Mojo::Server::CGI undef 217 | Mojo::Server::Daemon undef 218 | Mojo::Server::Hypnotoad undef 219 | Mojo::Server::Morbo undef 220 | Mojo::Server::Morbo::Backend undef 221 | Mojo::Server::Morbo::Backend::Poll undef 222 | Mojo::Server::PSGI undef 223 | Mojo::Server::Prefork undef 224 | Mojo::Template undef 225 | Mojo::Transaction undef 226 | Mojo::Transaction::HTTP undef 227 | Mojo::Transaction::WebSocket undef 228 | Mojo::URL undef 229 | Mojo::Upload undef 230 | Mojo::UserAgent undef 231 | Mojo::UserAgent::CookieJar undef 232 | Mojo::UserAgent::Proxy undef 233 | Mojo::UserAgent::Server undef 234 | Mojo::UserAgent::Transactor undef 235 | Mojo::Util undef 236 | Mojo::WebSocket undef 237 | Mojolicious 9.35 238 | Mojolicious::Command undef 239 | Mojolicious::Command::Author::cpanify undef 240 | Mojolicious::Command::Author::generate undef 241 | Mojolicious::Command::Author::generate::app undef 242 | Mojolicious::Command::Author::generate::dockerfile undef 243 | Mojolicious::Command::Author::generate::lite_app undef 244 | Mojolicious::Command::Author::generate::makefile undef 245 | Mojolicious::Command::Author::generate::plugin undef 246 | Mojolicious::Command::Author::inflate undef 247 | Mojolicious::Command::cgi undef 248 | Mojolicious::Command::daemon undef 249 | Mojolicious::Command::eval undef 250 | Mojolicious::Command::get undef 251 | Mojolicious::Command::prefork undef 252 | Mojolicious::Command::psgi undef 253 | Mojolicious::Command::routes undef 254 | Mojolicious::Command::version undef 255 | Mojolicious::Commands undef 256 | Mojolicious::Controller undef 257 | Mojolicious::Lite undef 258 | Mojolicious::Plugin undef 259 | Mojolicious::Plugin::Config undef 260 | Mojolicious::Plugin::DefaultHelpers undef 261 | Mojolicious::Plugin::EPLRenderer undef 262 | Mojolicious::Plugin::EPRenderer undef 263 | Mojolicious::Plugin::HeaderCondition undef 264 | Mojolicious::Plugin::JSONConfig undef 265 | Mojolicious::Plugin::Mount undef 266 | Mojolicious::Plugin::NotYAMLConfig undef 267 | Mojolicious::Plugin::TagHelpers undef 268 | Mojolicious::Plugins undef 269 | Mojolicious::Renderer undef 270 | Mojolicious::Routes undef 271 | Mojolicious::Routes::Match undef 272 | Mojolicious::Routes::Pattern undef 273 | Mojolicious::Routes::Route undef 274 | Mojolicious::Sessions undef 275 | Mojolicious::Static undef 276 | Mojolicious::Types undef 277 | Mojolicious::Validator undef 278 | Mojolicious::Validator::Validation undef 279 | Test::Mojo undef 280 | ojo undef 281 | requirements: 282 | ExtUtils::MakeMaker 0 283 | IO::Socket::IP 0.37 284 | Sub::Util 1.41 285 | perl 5.016 286 | Role-Tiny-2.002004 287 | pathname: H/HA/HAARG/Role-Tiny-2.002004.tar.gz 288 | provides: 289 | Role::Tiny 2.002004 290 | Role::Tiny::With 2.002004 291 | requirements: 292 | Exporter 5.57 293 | perl 5.006 294 | Sub-Uplevel-0.2800 295 | pathname: D/DA/DAGOLDEN/Sub-Uplevel-0.2800.tar.gz 296 | provides: 297 | Sub::Uplevel 0.2800 298 | requirements: 299 | Carp 0 300 | ExtUtils::MakeMaker 6.17 301 | constant 0 302 | perl 5.006 303 | strict 0 304 | warnings 0 305 | Test-Exception-0.43 306 | pathname: E/EX/EXODIST/Test-Exception-0.43.tar.gz 307 | provides: 308 | Test::Exception 0.43 309 | requirements: 310 | Carp 0 311 | Exporter 0 312 | ExtUtils::MakeMaker 0 313 | Sub::Uplevel 0.18 314 | Test::Builder 0.7 315 | Test::Builder::Tester 1.07 316 | Test::Harness 2.03 317 | base 0 318 | perl 5.006001 319 | strict 0 320 | warnings 0 321 | Test-SharedFork-0.35 322 | pathname: E/EX/EXODIST/Test-SharedFork-0.35.tar.gz 323 | provides: 324 | Test::SharedFork 0.35 325 | Test::SharedFork::Array undef 326 | Test::SharedFork::Scalar undef 327 | Test::SharedFork::Store undef 328 | requirements: 329 | ExtUtils::MakeMaker 6.64 330 | File::Temp 0 331 | Test::Builder 0.32 332 | Test::Builder::Module 0 333 | Test::More 0.88 334 | perl 5.008_001 335 | -------------------------------------------------------------------------------- /thirdparty/cpanfile-5.38.snapshot: -------------------------------------------------------------------------------- 1 | # carton snapshot format: version 1.0 2 | DISTRIBUTIONS 3 | Class-Method-Modifiers-2.15 4 | pathname: E/ET/ETHER/Class-Method-Modifiers-2.15.tar.gz 5 | provides: 6 | Class::Method::Modifiers 2.15 7 | requirements: 8 | B 0 9 | Carp 0 10 | Exporter 0 11 | ExtUtils::MakeMaker 0 12 | base 0 13 | perl 5.006 14 | strict 0 15 | warnings 0 16 | ExtUtils-Config-0.008 17 | pathname: L/LE/LEONT/ExtUtils-Config-0.008.tar.gz 18 | provides: 19 | ExtUtils::Config 0.008 20 | requirements: 21 | Data::Dumper 0 22 | ExtUtils::MakeMaker 6.30 23 | strict 0 24 | warnings 0 25 | ExtUtils-Helpers-0.026 26 | pathname: L/LE/LEONT/ExtUtils-Helpers-0.026.tar.gz 27 | provides: 28 | ExtUtils::Helpers 0.026 29 | ExtUtils::Helpers::Unix 0.026 30 | ExtUtils::Helpers::VMS 0.026 31 | ExtUtils::Helpers::Windows 0.026 32 | requirements: 33 | Carp 0 34 | Exporter 5.57 35 | ExtUtils::MakeMaker 0 36 | File::Basename 0 37 | File::Copy 0 38 | File::Spec::Functions 0 39 | Text::ParseWords 3.24 40 | perl 5.006 41 | strict 0 42 | warnings 0 43 | ExtUtils-InstallPaths-0.012 44 | pathname: L/LE/LEONT/ExtUtils-InstallPaths-0.012.tar.gz 45 | provides: 46 | ExtUtils::InstallPaths 0.012 47 | requirements: 48 | Carp 0 49 | ExtUtils::Config 0.002 50 | ExtUtils::MakeMaker 0 51 | File::Spec 0 52 | perl 5.006 53 | strict 0 54 | warnings 0 55 | Module-Build-Tiny-0.047 56 | pathname: L/LE/LEONT/Module-Build-Tiny-0.047.tar.gz 57 | provides: 58 | Module::Build::Tiny 0.047 59 | requirements: 60 | CPAN::Meta 0 61 | DynaLoader 0 62 | Exporter 5.57 63 | ExtUtils::CBuilder 0 64 | ExtUtils::Config 0.003 65 | ExtUtils::Helpers 0.020 66 | ExtUtils::Install 0 67 | ExtUtils::InstallPaths 0.002 68 | ExtUtils::ParseXS 0 69 | File::Basename 0 70 | File::Find 0 71 | File::Path 0 72 | File::Spec::Functions 0 73 | Getopt::Long 2.36 74 | JSON::PP 2 75 | Pod::Man 0 76 | TAP::Harness::Env 0 77 | perl 5.006 78 | strict 0 79 | warnings 0 80 | Mojo-Log-Clearable-1.001 81 | pathname: D/DB/DBOOK/Mojo-Log-Clearable-1.001.tar.gz 82 | provides: 83 | Mojo::Log::Clearable 1.001 84 | Mojo::Log::Role::Clearable 1.001 85 | requirements: 86 | Class::Method::Modifiers 2.11 87 | Module::Build::Tiny 0.034 88 | Mojolicious 7.42 89 | Role::Tiny 2.000002 90 | perl 5.010001 91 | Mojolicious-9.36 92 | pathname: S/SR/SRI/Mojolicious-9.36.tar.gz 93 | provides: 94 | Mojo undef 95 | Mojo::Asset undef 96 | Mojo::Asset::File undef 97 | Mojo::Asset::Memory undef 98 | Mojo::Base undef 99 | Mojo::ByteStream undef 100 | Mojo::Cache undef 101 | Mojo::Collection undef 102 | Mojo::Content undef 103 | Mojo::Content::MultiPart undef 104 | Mojo::Content::Single undef 105 | Mojo::Cookie undef 106 | Mojo::Cookie::Request undef 107 | Mojo::Cookie::Response undef 108 | Mojo::DOM undef 109 | Mojo::DOM::CSS undef 110 | Mojo::DOM::HTML undef 111 | Mojo::Date undef 112 | Mojo::DynamicMethods undef 113 | Mojo::EventEmitter undef 114 | Mojo::Exception undef 115 | Mojo::File undef 116 | Mojo::Headers undef 117 | Mojo::HelloWorld undef 118 | Mojo::Home undef 119 | Mojo::IOLoop undef 120 | Mojo::IOLoop::Client undef 121 | Mojo::IOLoop::Server undef 122 | Mojo::IOLoop::Stream undef 123 | Mojo::IOLoop::Subprocess undef 124 | Mojo::IOLoop::TLS undef 125 | Mojo::JSON undef 126 | Mojo::JSON::Pointer undef 127 | Mojo::Loader undef 128 | Mojo::Log undef 129 | Mojo::Message undef 130 | Mojo::Message::Request undef 131 | Mojo::Message::Response undef 132 | Mojo::Parameters undef 133 | Mojo::Path undef 134 | Mojo::Promise undef 135 | Mojo::Reactor undef 136 | Mojo::Reactor::EV undef 137 | Mojo::Reactor::Poll undef 138 | Mojo::Server undef 139 | Mojo::Server::CGI undef 140 | Mojo::Server::Daemon undef 141 | Mojo::Server::Hypnotoad undef 142 | Mojo::Server::Morbo undef 143 | Mojo::Server::Morbo::Backend undef 144 | Mojo::Server::Morbo::Backend::Poll undef 145 | Mojo::Server::PSGI undef 146 | Mojo::Server::Prefork undef 147 | Mojo::Template undef 148 | Mojo::Transaction undef 149 | Mojo::Transaction::HTTP undef 150 | Mojo::Transaction::WebSocket undef 151 | Mojo::URL undef 152 | Mojo::Upload undef 153 | Mojo::UserAgent undef 154 | Mojo::UserAgent::CookieJar undef 155 | Mojo::UserAgent::Proxy undef 156 | Mojo::UserAgent::Server undef 157 | Mojo::UserAgent::Transactor undef 158 | Mojo::Util undef 159 | Mojo::WebSocket undef 160 | Mojolicious 9.36 161 | Mojolicious::Command undef 162 | Mojolicious::Command::Author::cpanify undef 163 | Mojolicious::Command::Author::generate undef 164 | Mojolicious::Command::Author::generate::app undef 165 | Mojolicious::Command::Author::generate::dockerfile undef 166 | Mojolicious::Command::Author::generate::lite_app undef 167 | Mojolicious::Command::Author::generate::makefile undef 168 | Mojolicious::Command::Author::generate::plugin undef 169 | Mojolicious::Command::Author::inflate undef 170 | Mojolicious::Command::cgi undef 171 | Mojolicious::Command::daemon undef 172 | Mojolicious::Command::eval undef 173 | Mojolicious::Command::get undef 174 | Mojolicious::Command::prefork undef 175 | Mojolicious::Command::psgi undef 176 | Mojolicious::Command::routes undef 177 | Mojolicious::Command::version undef 178 | Mojolicious::Commands undef 179 | Mojolicious::Controller undef 180 | Mojolicious::Lite undef 181 | Mojolicious::Plugin undef 182 | Mojolicious::Plugin::Config undef 183 | Mojolicious::Plugin::DefaultHelpers undef 184 | Mojolicious::Plugin::EPLRenderer undef 185 | Mojolicious::Plugin::EPRenderer undef 186 | Mojolicious::Plugin::HeaderCondition undef 187 | Mojolicious::Plugin::JSONConfig undef 188 | Mojolicious::Plugin::Mount undef 189 | Mojolicious::Plugin::NotYAMLConfig undef 190 | Mojolicious::Plugin::TagHelpers undef 191 | Mojolicious::Plugins undef 192 | Mojolicious::Renderer undef 193 | Mojolicious::Routes undef 194 | Mojolicious::Routes::Match undef 195 | Mojolicious::Routes::Pattern undef 196 | Mojolicious::Routes::Route undef 197 | Mojolicious::Sessions undef 198 | Mojolicious::Static undef 199 | Mojolicious::Types undef 200 | Mojolicious::Validator undef 201 | Mojolicious::Validator::Validation undef 202 | Test::Mojo undef 203 | ojo undef 204 | requirements: 205 | ExtUtils::MakeMaker 0 206 | IO::Socket::IP 0.37 207 | Sub::Util 1.41 208 | perl 5.016 209 | Role-Tiny-2.002004 210 | pathname: H/HA/HAARG/Role-Tiny-2.002004.tar.gz 211 | provides: 212 | Role::Tiny 2.002004 213 | Role::Tiny::With 2.002004 214 | requirements: 215 | Exporter 5.57 216 | perl 5.006 217 | --------------------------------------------------------------------------------