From b1ee6a0c8dc117f22197979081a90116c76bbf6b Mon Sep 17 00:00:00 2001 From: "(no author)" <(no author)@91ce42f0-d328-0410-95d8-f526ca767f89> Date: Mon, 12 Jan 2004 18:13:31 +0000 Subject: [PATCH] This commit was manufactured by cvs2svn to create tag 'Release-1.32f-3'. git-svn-id: https://bacula.svn.sourceforge.net/svnroot/bacula/tags/Release-1.32f-3@1007 91ce42f0-d328-0410-95d8-f526ca767f89 --- bacula/.cvsignore | 4 + bacula/ChangeLog | 294 ++ bacula/CheckList | 2 +- bacula/Makefile.in | 1 + bacula/ReleaseNotes | 147 +- bacula/autoconf/acconfig.h | 4 +- bacula/autoconf/aclocal.m4 | 5 + bacula/autoconf/config.h.in | 4 +- bacula/autoconf/configure.in | 140 +- bacula/configure | 213 +- bacula/kernstodo | 679 ++-- bacula/platforms/debian/bacula-director | 59 + bacula/platforms/freebsd/.cvsignore | 4 + bacula/platforms/freebsd/pthreads-fix.txt | 193 ++ bacula/platforms/redhat/bacula-dir.in | 2 +- bacula/platforms/redhat/bacula-fd.in | 2 +- bacula/platforms/redhat/bacula-sd.in | 2 +- bacula/platforms/redhat/bacula.spec.in | 258 +- bacula/platforms/solaris/Makefile.in | 13 +- bacula/platforms/suse/Makefile.in | 81 + bacula/platforms/suse/bacula-dir.in | 44 + bacula/platforms/suse/bacula-fd.in | 43 + bacula/platforms/suse/bacula-sd.in | 43 + bacula/scripts/bacula.in | 108 +- bacula/scripts/set-gnome1.4 | 18 + bacula/scripts/set-gnome2 | 16 + bacula/src/.cvsignore | 1 + bacula/src/Makefile.in | 2 + bacula/src/baconfig.h | 2 +- bacula/src/bc_types.h | 2 +- bacula/src/cats/Makefile.in | 2 + bacula/src/cats/bdb_find.c | 11 +- bacula/src/cats/bdb_get.c | 2 +- bacula/src/cats/cats.h | 331 +- bacula/src/cats/make_catalog_backup.in | 4 +- bacula/src/cats/make_catalog_backup.in.patch | 16 + bacula/src/cats/make_mysql_tables.in | 2 +- bacula/src/cats/mysql.c | 4 + bacula/src/cats/protos.h | 4 +- bacula/src/cats/sql_delete.c | 8 +- bacula/src/cats/sql_find.c | 92 +- bacula/src/cats/sql_get.c | 49 +- bacula/src/cats/sql_list.c | 10 +- bacula/src/cats/sql_update.c | 5 +- bacula/src/cats/update_bacula_tables.in | 13 + bacula/src/cats/update_mysql_tables.in | 61 + bacula/src/cats/update_sqlite_tables.in | 207 ++ bacula/src/console/Makefile.in | 2 +- bacula/src/console/console.c | 19 +- bacula/src/console2.glade | 3080 +++++++++++++++++ bacula/src/console2.gladep | 8 + bacula/src/dird/.cvsignore | 1 + bacula/src/dird/Makefile.in | 4 +- bacula/src/dird/authenticate.c | 4 +- bacula/src/dird/backup.c | 19 +- bacula/src/dird/bacula-dir.conf.in | 2 +- bacula/src/dird/bsr.c | 55 +- bacula/src/dird/catreq.c | 16 +- bacula/src/dird/dird.c | 3 +- bacula/src/dird/dird_conf.c | 79 +- bacula/src/dird/dird_conf.h | 6 +- bacula/src/dird/fd_cmds.c | 9 +- bacula/src/dird/getmsg.c | 7 +- bacula/src/dird/inc_conf.c | 23 +- bacula/src/dird/job.c | 31 +- bacula/src/dird/msgchan.c | 24 +- bacula/src/dird/next_vol.c | 47 +- bacula/src/dird/protos.h | 3 +- bacula/src/dird/restore.c | 13 +- bacula/src/dird/run_conf.c | 528 +-- bacula/src/dird/scheduler.c | 11 +- bacula/src/dird/sql_cmds.c | 71 +- bacula/src/dird/ua_cmds.c | 93 +- bacula/src/dird/ua_input.c | 3 +- bacula/src/dird/ua_label.c | 11 +- bacula/src/dird/ua_output.c | 151 +- bacula/src/dird/ua_prune.c | 6 +- bacula/src/dird/ua_restore.c | 241 +- bacula/src/dird/ua_run.c | 110 +- bacula/src/dird/ua_select.c | 26 +- bacula/src/dird/ua_status.c | 489 +-- bacula/src/dird/ua_tree.c | 150 +- bacula/src/dird/verify.c | 107 +- bacula/src/filed/.cvsignore | 1 + bacula/src/filed/filed.c | 90 +- bacula/src/filed/job.c | 103 +- bacula/src/filed/restore.c | 63 +- bacula/src/filed/status.c | 222 +- bacula/src/filed/verify.c | 11 +- bacula/src/filed/win32/bin/README.txt | 5 +- bacula/src/filed/win32/bin/chown.exe | Bin 32256 -> 31744 bytes bacula/src/filed/win32/bin/cygwin1.dll | Bin 910152 -> 971618 bytes bacula/src/filed/win32/bin/cygz.dll | Bin 50688 -> 61440 bytes bacula/src/filed/win32/bin/mount.exe | Bin 19456 -> 11776 bytes bacula/src/filed/win32/bin/sh.exe | Bin 69632 -> 76800 bytes bacula/src/filed/win32/bin/umount.exe | Bin 15872 -> 7168 bytes bacula/src/findlib/attribs.c | 3 + bacula/src/findlib/bfile.c | 9 +- bacula/src/findlib/create_file.c | 9 + bacula/src/findlib/makepath.c | 18 +- bacula/src/gnome2-console/.cvsignore | 1 + bacula/src/gnome2-console/Makefile.in | 125 + bacula/src/gnome2-console/authenticate.c | 82 + bacula/src/gnome2-console/callbacks.c | 504 +++ bacula/src/gnome2-console/callbacks.h | 240 ++ bacula/src/gnome2-console/console.c | 612 ++++ bacula/src/gnome2-console/console.h | 61 + bacula/src/gnome2-console/console_conf.c | 268 ++ bacula/src/gnome2-console/console_conf.h | 60 + .../src/gnome2-console/gnome-console.conf.in | 15 + bacula/src/gnome2-console/interface.c | 1464 ++++++++ bacula/src/gnome2-console/interface.h | 11 + bacula/src/gnome2-console/support.c | 115 + bacula/src/gnome2-console/support.h | 37 + .../gnome2-console/test-gnome-console.conf | 10 + bacula/src/jcr.h | 10 +- bacula/src/lib/alist.h | 4 +- bacula/src/lib/bits.h | 2 +- bacula/src/lib/bnet.c | 57 +- bacula/src/lib/bpipe.c | 3 + bacula/src/lib/bsock.h | 1 + bacula/src/lib/bsys.c | 4 +- bacula/src/lib/btime.c | 110 +- bacula/src/lib/btime.h | 24 +- bacula/src/lib/cram-md5.c | 1 + bacula/src/lib/daemon.c | 13 +- bacula/src/lib/dlist.c | 8 +- bacula/src/lib/dlist.h | 9 + bacula/src/lib/jcr.c | 95 +- bacula/src/lib/message.c | 37 +- bacula/src/lib/parse_conf.c | 5 +- bacula/src/lib/protos.h | 15 +- bacula/src/lib/scan.c | 16 +- bacula/src/lib/serial.c | 2 +- bacula/src/lib/signal.c | 5 +- bacula/src/lib/tree.c | 74 +- bacula/src/lib/tree.h | 26 +- bacula/src/lib/util.c | 116 +- bacula/src/lib/var.c | 10 +- bacula/src/lib/watchdog.c | 3 + bacula/src/stored/.cvsignore | 1 + bacula/src/stored/acquire.c | 105 +- bacula/src/stored/append.c | 30 +- bacula/src/stored/askdir.c | 17 +- bacula/src/stored/autochanger.c | 13 +- bacula/src/stored/bacula-sd.conf.in | 2 + bacula/src/stored/bcopy.c | 6 +- bacula/src/stored/bextract.c | 8 +- bacula/src/stored/block.c | 94 +- bacula/src/stored/block.h | 2 +- bacula/src/stored/bls.c | 2 +- bacula/src/stored/bscan.c | 21 +- bacula/src/stored/btape.c | 853 ++++- bacula/src/stored/butil.c | 14 +- bacula/src/stored/dev.c | 72 +- bacula/src/stored/dev.h | 4 + bacula/src/stored/device.c | 13 +- bacula/src/stored/dircmd.c | 8 +- bacula/src/stored/fd_cmds.c | 8 +- bacula/src/stored/job.c | 10 +- bacula/src/stored/label.c | 23 +- bacula/src/stored/match_bsr.c | 87 +- bacula/src/stored/mount.c | 321 +- bacula/src/stored/parse_bsr.c | 3 + bacula/src/stored/protos.h | 191 +- bacula/src/stored/read_record.c | 166 +- bacula/src/stored/record.h | 2 + bacula/src/stored/status.c | 197 +- bacula/src/stored/stored.c | 102 +- bacula/src/stored/stored_conf.c | 146 +- bacula/src/stored/stored_conf.h | 2 +- bacula/src/tools/testfind.c | 8 +- bacula/src/tools/testls.c | 48 +- bacula/src/version.h | 6 +- regress/.cvsignore | 7 - regress/README | 85 - regress/all-non-root-tape-tests | 13 - regress/all-non-root-tests | 28 - regress/all-root-tests | 9 - regress/all-tape-and-file-tests | 30 - regress/all-tests | 8 - regress/scripts/.cvsignore | 12 - regress/scripts/bacula-dir-tape.conf.in | 132 - regress/scripts/bacula-sd-tape.conf.in | 54 - regress/scripts/bacula-sd.conf.in | 74 - regress/scripts/cleanup | 9 - regress/scripts/cleanup-tape.in | 21 - regress/scripts/copy-confs | 5 - regress/scripts/copy-tape-confs | 5 - regress/scripts/copy-test-confs | 5 - regress/scripts/copy-testa-confs | 5 - regress/scripts/do_sed | 41 - regress/scripts/exclude-dev-test | 5 - regress/scripts/exclude-etc-test | 1 - regress/scripts/exclude-lib-test | 5 - regress/scripts/regress-config | 20 - regress/scripts/setup | 47 - regress/scripts/test-bacula-dir.conf.in | 298 -- regress/scripts/test-bacula-fd.conf.in | 33 - regress/scripts/test-bacula-sd.conf.in | 106 - regress/scripts/test-console.conf.in | 10 - regress/scripts/testa-bacula-dir.conf.in | 149 - regress/tests/backup-bacula-tape | 56 - regress/tests/backup-bacula-test | 55 - regress/tests/bextract-test | 59 - regress/tests/bscan-test | 83 - regress/tests/bsr-opt-test | 68 - regress/tests/compressed-test | 60 - regress/tests/concurrent-jobs-test | 77 - regress/tests/dev-test-root | 67 - regress/tests/etc-test-root | 67 - regress/tests/four-concurrent-jobs-test | 80 - regress/tests/lib-test-root | 67 - regress/tests/recycle-test | 89 - regress/tests/restore-by-file-test | 67 - regress/tests/six-vol-test | 66 - regress/tests/small-file-size-tape | 60 - regress/tests/span-vol-test | 65 - regress/tests/sparse-compressed-test | 56 - regress/tests/sparse-test | 56 - regress/tests/test0 | 3 - regress/tests/two-jobs-test | 81 - regress/tests/two-vol-test | 61 - regress/tests/two-volume-tape | 70 - regress/tests/verify-vol-test | 55 - regress/tests/weird-files-test | 68 - regress/tests/weird-files2-test | 84 - regress/weird-files.tar.gz | Bin 1510 -> 0 bytes 228 files changed, 13423 insertions(+), 5447 deletions(-) create mode 100644 bacula/platforms/debian/bacula-director create mode 100644 bacula/platforms/freebsd/.cvsignore create mode 100644 bacula/platforms/freebsd/pthreads-fix.txt create mode 100644 bacula/platforms/suse/Makefile.in create mode 100755 bacula/platforms/suse/bacula-dir.in create mode 100755 bacula/platforms/suse/bacula-fd.in create mode 100755 bacula/platforms/suse/bacula-sd.in create mode 100755 bacula/scripts/set-gnome1.4 create mode 100755 bacula/scripts/set-gnome2 create mode 100644 bacula/src/cats/make_catalog_backup.in.patch create mode 100755 bacula/src/cats/update_bacula_tables.in create mode 100755 bacula/src/cats/update_mysql_tables.in create mode 100755 bacula/src/cats/update_sqlite_tables.in create mode 100644 bacula/src/console2.glade create mode 100644 bacula/src/console2.gladep create mode 100644 bacula/src/gnome2-console/Makefile.in create mode 100644 bacula/src/gnome2-console/authenticate.c create mode 100644 bacula/src/gnome2-console/callbacks.c create mode 100644 bacula/src/gnome2-console/callbacks.h create mode 100644 bacula/src/gnome2-console/console.c create mode 100644 bacula/src/gnome2-console/console.h create mode 100644 bacula/src/gnome2-console/console_conf.c create mode 100644 bacula/src/gnome2-console/console_conf.h create mode 100644 bacula/src/gnome2-console/gnome-console.conf.in create mode 100644 bacula/src/gnome2-console/interface.c create mode 100644 bacula/src/gnome2-console/interface.h create mode 100644 bacula/src/gnome2-console/support.c create mode 100644 bacula/src/gnome2-console/support.h create mode 100644 bacula/src/gnome2-console/test-gnome-console.conf delete mode 100644 regress/.cvsignore delete mode 100644 regress/README delete mode 100755 regress/all-non-root-tape-tests delete mode 100755 regress/all-non-root-tests delete mode 100755 regress/all-root-tests delete mode 100755 regress/all-tape-and-file-tests delete mode 100755 regress/all-tests delete mode 100644 regress/scripts/.cvsignore delete mode 100644 regress/scripts/bacula-dir-tape.conf.in delete mode 100644 regress/scripts/bacula-sd-tape.conf.in delete mode 100644 regress/scripts/bacula-sd.conf.in delete mode 100755 regress/scripts/cleanup delete mode 100755 regress/scripts/cleanup-tape.in delete mode 100755 regress/scripts/copy-confs delete mode 100755 regress/scripts/copy-tape-confs delete mode 100755 regress/scripts/copy-test-confs delete mode 100755 regress/scripts/copy-testa-confs delete mode 100755 regress/scripts/do_sed delete mode 100644 regress/scripts/exclude-dev-test delete mode 100644 regress/scripts/exclude-etc-test delete mode 100644 regress/scripts/exclude-lib-test delete mode 100755 regress/scripts/regress-config delete mode 100755 regress/scripts/setup delete mode 100644 regress/scripts/test-bacula-dir.conf.in delete mode 100644 regress/scripts/test-bacula-fd.conf.in delete mode 100644 regress/scripts/test-bacula-sd.conf.in delete mode 100644 regress/scripts/test-console.conf.in delete mode 100644 regress/scripts/testa-bacula-dir.conf.in delete mode 100755 regress/tests/backup-bacula-tape delete mode 100755 regress/tests/backup-bacula-test delete mode 100755 regress/tests/bextract-test delete mode 100755 regress/tests/bscan-test delete mode 100755 regress/tests/bsr-opt-test delete mode 100755 regress/tests/compressed-test delete mode 100755 regress/tests/concurrent-jobs-test delete mode 100755 regress/tests/dev-test-root delete mode 100755 regress/tests/etc-test-root delete mode 100755 regress/tests/four-concurrent-jobs-test delete mode 100755 regress/tests/lib-test-root delete mode 100755 regress/tests/recycle-test delete mode 100755 regress/tests/restore-by-file-test delete mode 100755 regress/tests/six-vol-test delete mode 100755 regress/tests/small-file-size-tape delete mode 100755 regress/tests/span-vol-test delete mode 100755 regress/tests/sparse-compressed-test delete mode 100755 regress/tests/sparse-test delete mode 100755 regress/tests/test0 delete mode 100755 regress/tests/two-jobs-test delete mode 100755 regress/tests/two-vol-test delete mode 100755 regress/tests/two-volume-tape delete mode 100755 regress/tests/verify-vol-test delete mode 100755 regress/tests/weird-files-test delete mode 100755 regress/tests/weird-files2-test delete mode 100644 regress/weird-files.tar.gz diff --git a/bacula/.cvsignore b/bacula/.cvsignore index e87bcd24e3..62866bd26b 100644 --- a/bacula/.cvsignore +++ b/bacula/.cvsignore @@ -1,5 +1,9 @@ 1 2 +set-gnome2 +set-gnome1.4 +kerns-gprof-config +autom4te.cache Makefile bacula btraceback diff --git a/bacula/ChangeLog b/bacula/ChangeLog index bf5d6580de..47c922e917 100644 --- a/bacula/ChangeLog +++ b/bacula/ChangeLog @@ -1,4 +1,298 @@ +Patched 1.32f-3 + +2004-01-12 Version 1.32f-3 12Jan04 Release +- Modify findlib/makepath.c to create all parent directories with full + permissions. This should solve the access problems on restoring files + on Win32 systems. +- Modify restore to report **** Restore Error **** if any error + are found. I.e. a file could not be created. +- Change a few errors into warnings -- e.g. if permissions could + not be set, but the file is actually restored. + +2004-01-12 Version 1.32f-2 06Jan04 Release +- This release has two patches applied: + 1.32f-1-weekofmonth.patch + 1.32f-2-eom-nextvol.patch +- Fix an incorrect calcualtion of the week of the month reported + by Volker Sauer -- patch 1. +- Fix bug reported by Phil (could not duplicate here) where at the end + of a Volume, Bacula wanted a new Volume and got into a loop requesting + it, then gave up -- Patch 2. +- Modify selection of next Volume to select most currently appended Volume, + or if none oldest recycled Volume (problem reported by Lars) -- Patch 2. +- Added new spec file from Scott. + +2003-12-31 Version 1.32f 31Dec03 Release +- Note, this change affects only the Win32 FD. +- Fixed Win32 FD crash due to missing argument in status command. It + always crashed if there was a job that had previously run. Thanks to + Christopher Hull for finding and diagnosing this problem. + +2003-12-24 Version 1.32e 26Dec03 Release +26Dec03 +- Fixed static configuration of gnome console reported by Alan Brown. +24Dec03 +- Made restore use the base FileSet name instead of modifications of the FileSet. +- Fix "restore" to always look at storage keyword and to use get_storage_resource() +- Fix seg fault in restore if no client found/specified. +- Made JobBytes print in 14 columns instead of 12 in status reports. +- Install static-gnome-console if built. +- Set max changer wait from 2 mins to 5 to avoid timout while tapes load. +23Dec03 +- Added additional error messages to smtp. +21Dec03 +- Back ported the following items from 1.33: +- Added suse platform directory and configure code. Must be tweaked for + SuSe. +- Added debian platform directory. +- Fixed fd.in (thanks Dan) to configure subsystem directory. +- Back ported fix to Week of Month from 1.33 +- Back ported new Week of Year code from 1.33 +- Add JobId to Running jobs and Terminated Jobs status list. +- Fixed "Phil's" bug where after doing a restore from a tape, if the next + operation was an append to the same tape, the number of files on the + tape and in the catalog got out of sync. +- Fixed bug in Terminated Jobs status list that repeated the same job. + +2003-12-09 Version 1.32e 08Dec03 Release +24Nov03 +- Sort FileSet selection list by CreateTime. +- Add "lsmark", and "estimate" to tree routines. +- Doing a mark or unmark now prints how many entries were changed. +- Add command argument parsing to btape.c +- Enhance EOT to print file:block on message. +- Add repeat counts on btape bsf, fsf, bsr, fsr, and weof commands. +- Enhance btape's fill command to be much clearer and more reliable. +- Add state file to btape so that unfill command can be done any time + after a fill command. +- Use reposition_dev() to position for read back of last block. +22Nov03 +- Cleaned up the btape "fill" command to compare the last block written + and read rather than just printing them. +21Nov03 +- Implement btape test for autochanger. +- Implement btape test for Fast Forward Space File. +- Moved up to cygwin 1.5.5-1 +20Nov03 +- Ensure that Volumes are selected from oldest LastWritten data/time. +- A couple of bug fixes ensuring the proper ordering of volumes. +19Nov03 +- Return oldest LastWritten for find_next_volume. +- Enhance SD status if debug_level > 1 to show details of dev status. +18Nov03 +- Create update_bacula_tables, ... scripts and modify configure and Makefiles + Not used in 1.32e +- Eliminate is_num() and use is_an_integer(). +- Start daemons at level 90 rather than 20 so that MySQL will already + be started. +15Nov03 +- In the bacula start/stop script, ordered the stop: FD SD Dir to + give the SD the best chances of updating the catalog before dying. +- Fixed the signal handler to pass the signal to the exit_handler() + previously it passed 1. +- Modified SD so that on normal shutdown, it walks through all jcrs and + cancels them so that the Volume status will be updated in the catalog. +- Found and fixed a bug where ST_LABEL was not set in append mode + (when a different tape was accepted other than the original one + proposed by the DIR. +12Nov03 +- Change getdomainname() prototype for Darwin. +- Add gethost_strerror() to create correct error message for + gethostbyname(). +- After doing a kill() of a stalled connection in watchdog, turn off + the timer to prevent an infinite loop. +- Allow Bacula to rewrite the label on a disk volume. +11Nov03 +- Add L_NONE for Admin and Restore jobs and update level_to_str() +- Fix segfault from double free of RestoreBootstrap in job.c +10Nov03 +- Check if volume has expired when doing an update media for the SD +07Nov03 +- Fix bug reported by Lars where an incorrect Volume name was printed + by the "status dir" command. +06Nov03 +- Pretty up a few error messages printed by smtp. +- Make btime_t int64_t so that one can do arithmetic. +- Implement since as utime (64 bit UTC). +- Compute clock diff between Dir and FD, and adjust since time. +- Apply SQL fix from Nic Bellamy (thanks). +- Apply John's zlib #ifdefing fix. +05Nov03 +- Add Dan's with-sd-user, ... to configure.in. +- Add Dan's userid and group modifications to bacula.in +- Lots of documentation updates. +- Make console print "Enter a period to cancel a command" when starting. +- Fix the "list nextvol" command so that it doesn't try to close the + database twice, giving a segfault. +- Make dir_ask_sysop_to_mount_next_volume() return immediately if a slot is + specified. +- Correct some of the messages in testfind.c (pointed out by Dan -- thanks). +- Alias fd to client, sd to storage. +- Changed order of Console commands so that short commands such as q (quit) + are more logical. + +2003-11-03 Version 1.32d 02Nov03 Release +02Nov03 +- Mainly a bug fix release. +- Do a clean of both Gnome directories. +- Require that FileSet id match when finding an Incremental + previous job. This was already the case for a Full. +- Print message if no status returned from FD. +- Correct "Do not forget to mount the drive" message. Test was + backward. +- "status dir" stopped scanning the run records on the first + one that matched giving an incomplete listing. +- Edit commas in Bytes on "estimate" command output. + +2003-10-30 Version 1.32c 30Oct03 Release +29Oct03 +- Add %v to job edit codes. It edits in the VolumeName(s). +- Add code to ensure that fds 0,1, and 2 are defined by dup'ing them + to /dev/null if necessary. Mostly for Windows that does not have them. +- Error check dir_create_jobmedia_record() 2 places in acquire.c +26Oct03 +- More doc fixes. +- Make message buffer longer for a status message that was + truncated. +- Put termination on varargs calls in gnome2-console. +- Scroll only if text sent and not for status update. +- Add all possible Status codes the jobstatus_to_ascii() +25Oct03 +- Add new spec file and cats patch from Scott. +- Optimize tree.c a bit -- turn off debug code, keep node fname + length for fast rejection, add some statistics, allocate + in 100K and 1Meg chunks, use bool, uint16_t and uint8_t to + reduce node packet size. +24Oct03 +- Eliminate ua_retention.c that was not used. +- Improve error message when closing brace missing in conf file. +- More doc updates. +- Eliminate Don't forget to mount if it is a disk file. +- Fix Gnome2 scrolling and blank screen problems. +- Eliminate multiple JobIds in restore selection list. +- Fix non-portable varargs code in var.c +- Make doc fixes/changes suggested by Dan Languille +23Oct03 +- Document new features. +- Implement mod of Verify Job at the run prompt. +- Correct SQL table definitions so that MySQL and SQLite + have all the values in the same position. +- Correct a typo in configure.in when configuring GNOME. +- In doing a "status dir" make it loop over all the run + commands in the Schedule rather than doing on the first one. +- Close all unused file descriptors in bpipe.c otherwise if + a daemon is started, it will keep our TCP/IP port open. +22Oct03 +- Print block read error (checksum, I/O, BB01, ...) once then + the number found at the end of the reading. +- Implement RunAfterFailedJob +- Change db_find_job_start_time() to require a Full save before + running an Incremental or Differential job. +- Remove has_volume_expired from code that updates vol info +21Oct03 +- Implement "delete job" +20Oct03 +- More documentation, add Marc Brueckner's tips to manual. +- Tweak gnome2-console scroll window. +- Turn off some debug info. +18Oct03 +- Modify Verify to accept VerifyJob = xx, where the last backup job + of job xxx will be verified. +- Add changing the Pool name for a Volume to "update volume" +- Write most of the code for Verify Disk to Catalog. +- Recreate the src/gnome2-console directory. +- Change all the text handling code to the Gnome 2.0 way. +- Correct the way verify filenames are returned to the Director so + that directories are in canonical form (i.e. trailing /). +- Handle casting bug in glade-2 by sed'ing support.c in gnome2-console. + +2003-10-15 Version 1.32b 14Oct03 Release +14Oct03 +- Modify configure so that if threaded MySQL client library + is not present, Bacula will link with the non-threaded + version. +- Updates to the Web pages and to the manual. +- Remove trademark symbol from title. Phil pointed out that it + does not display correctly in a title. +11Oct03 +- Implement restore by file before date. +- Change restore arguments a bit so that you can feed it + multiple jobid= specifications or multiple file= specifications. +- Pass restore with run option on to run_cmd. +- Make run-cmd not prompt if it has a "run" on the command line. +10Oct03 +- When pruning, select only old orphanned jobs to delete so that + the current job is not pruned too. +09Oct03 +- Corrected return status for bsf_dev and bsr_dev in block.c and btape.c +- dev.c used incorrect ruturn status for bsf_dev in the BSF at EOM + code. This caused all appends on FreeBSD to fail. +- Turn on fast block rejection code. +08Oct03 +- Optimize file index searches by adding a count to the bootstrap. +- Write single files/blocks to bootstrap without the second part. +- Add current Volume status to the cannot use this Volume message. +- Zero the rx->bsr in ua_restore when freeing so it doesn't get + freed twice. +- Lots of testing on the restore +- I noticed that SD and FD bootstrap files were not always + deleted, so delete them as soon as possible. +- Restore by file (or by selecting files) created some + horrible looking bsr files that defeated the forward + spacing code, so fix write_findex to work right. +- Add zlib_strerror() routine in filed/restore in case + of zlib errors. +- In filed/restore.c make sure all error returns cleanup + and close the open file descriptor. +- Make sure to set *non* over filename in attr packet + after file is found, so error messages that print the + filename don't print an old, incorrect name. +- Allow bclose() to be called after closing the file. +- Fix a number of unclear help messages, ... reported by + Phil in btape. +- Retweak stored/read_record.c so that it does forward + spacing at the beginning of every tape, not just the + first one. +- Print repositioning message if verbose is set. + +2003-10-01 Version 1.32a 03Oct03 Release +04Oct03 +- Combine the code in ua_output and ua_status that searches + the run records. +03Oct03 +- Fixed "list nextvol" to search for the correct pool in the + Schedule Run records. +- Correct an error in is_block_zero. It found a false + match if the first 1016 bytes of a 32K buffer and + the last 248 bytes are zero. Broke the sparse option. +01Oct03 +- More documentation. +- Test if multiple mail addresses works. Yes. +- Add debug Jmsg() to trigger if the file I/O packet is + not closed in the FD, i.e file descriptor leaks. +- When error occurs reading label in mount.c, start from + the very top so that the retry count is in effect. +- Zap the mode in soft links in testls.c for regression + testing. + +2003-10-01 Version 1.32 30Sep03 Release +28Sep03 +- Enhance manual faq, regression ... +- Make FreeBSD read sizes always be a multiple of 512. Needed + to read raw disks. +- Make FreeBSD accept block AND character devices for raw + disk reads. On FreeBSD there are no block devices. +27Sep03 +- Fix printing of EndTime on job report after rescheduling. +- Fix jobq.c error returns to clean up before returning. +- Make Cleaning tape aware of "unlabeled" volumes. +- If a job is rescheduled, ensure that old job is + removed from SD's jcr queue -- i.e. it is still waiting + for FD connection. +- Test rescheduling code. +- Change ./configure to detect Cygwin enviornments. + 2003-09-26 Version 1.32 26Sep03 Beta - Add regression and GUI-interface chapters to the manual. - Fix "label" of a volume that is already in the catalog, diff --git a/bacula/CheckList b/bacula/CheckList index ff26ed374b..ccc71d7e79 100644 --- a/bacula/CheckList +++ b/bacula/CheckList @@ -8,7 +8,7 @@ - Update ChangeLog (add release date) - Do a cvs commit - Do a cvs -q export -D now -d bacula-1.nn bacula -- Build new bacula-1.nn to ensure everything is committed +- Run the regression tests on the new bacula - Write ReleaseNotes - If everything is good - cd bacula/k diff --git a/bacula/Makefile.in b/bacula/Makefile.in index 87c3a4da94..65b008f80c 100755 --- a/bacula/Makefile.in +++ b/bacula/Makefile.in @@ -149,6 +149,7 @@ distclean: @$(RMF) -r doc/techlogs/2002/CVS doc/techlogs/2001/CVS doc/techlogs/1.27/CVS @$(RMF) -r examples/CVS intl/CVS scripts/CVS @$(RMF) -r po/CVS src/immortal/CVS rescue/freebsd/CVS rescue/solaris/CVS + @$(RMF) -r CVS src/gnome-console/CVS src/tconsole/CVS distdirs: mkdir ../$(VERNAME); diff --git a/bacula/ReleaseNotes b/bacula/ReleaseNotes index 668426ebe3..8d4d212948 100644 --- a/bacula/ReleaseNotes +++ b/bacula/ReleaseNotes @@ -1,9 +1,143 @@ - Release Notes for Bacula 1.32 + Release Notes for Bacula 1.32f-3 - Bacula code: Total files = 259 Total lines = 77,702 (*.h *.c *.in) + Bacula code: Total files = 262 Total lines = 80,352 (*.h *.c *.in) -Major Changes this Release: +2004-01-12 Version 1.32f-3 12Jan04 Release +- Modify findlib/makepath.c to create all parent directories with full + permissions. This should solve the access problems on restoring files + on Win32 systems. +- Modify restore to report **** Restore Error **** if any error + are found. I.e. a file could not be created. +- Change a few errors into warnings -- e.g. if permissions could + not be set, but the file is actually restored. + +Release 1.32f-2: +- This release has two patches applied: + 1.32f-1-weekofmonth.patch + 1.32f-2-eom-nextvol.patch +- Fix an incorrect calcualtion of the week of the month reported + by Volker Sauer -- patch 1. +- Fix bug reported by Phil (could not duplicate here) where at the end + of a Volume, Bacula wanted a new Volume and got into a loop requesting + it, then gave up -- Patch 2. +- Modify selection of next Volume to select most currently appended Volume, + or if none oldest recycled Volume (problem reported by Lars) -- Patch 2. +- Added new spec file from Scott. + +Changes since 1.32e: +- Note, this change affects only the Win32 FD. +- Fixed Win32 FD crash due to missing argument in status command. It + always crashed if there was a job that had previously run. Thanks to + Christopher Hull for finding and diagnosing this problem. + +Most Significant Changes since 1.32e-08Dec03 +- Added additional error messages to smtp. +- Added suse platform directory and configure code. Must be tweaked for + SuSe. +- Added debian platform directory. +- Fixed fd.in (thanks Dan) to configure subsystem directory. +- Back ported fix to Week of Month from 1.33 +- Add JobId to Running jobs and Terminated Jobs status list. +- Fixed "Phil's" bug where after doing a restore from a tape, if the next + operation was an append to the same tape, the number of files on the + tape and in the catalog got out of sync. +- Fixed bug in Terminated Jobs status list that repeated the same job. + +Most Significant Changes since 1.32d +- All 1.32d patches applied. +- Fixed seg fault in restore of multiple simultaneous jobs to + a single Volume. +- Fixed thread race problem in multiple simultaneous jobs to + a single Volume where the volume lable is not in the first + tape block, so the tape is not recognized. +- Enhance "fill" command of btape -- simpler output. Use -v to + cause last block to be dumped after write and after re-read. +- Added an autochanger test to the btape "test" command. It is + automatically invoked if the autochanger is properly configured. It + is still a bit primitive but covers the essential of testing + the mtx-changer script with your autochanger. +- Added forward space file test to btape "test" command. +- New version 1.5.5-1 Cygwin. +- Select the oldest LastWritten volume during recycling. +- Modify SD to update the catalog database when it is shutdown, + even if the job is canceled. +- The console program will run all commands it finds in ~/.bconsolerc + at startup. +- Add Dan Langille's changes to the bacula start/stop script that + permit dropping root permissions just after startup. +- The "mark" command in the restore command now provides feedback + on how many files were marked. + +Other Changes since 1.32d: +- Fixed getdomainname() prototype on Darwin. +- Fixed an SQL typo (missing space) in "list volumes jobid=xxx" +- Fixed crash with "list job" +- Fixed crash with ' entered as a filename to be restored (any + name causing an SQL error would cause the crash). +- Fixed | syntax in FileSet Include. +- Corrected crash in restore in some weird cases where the bootstrap + file defined in the resource. +- Fixed a crash when storage daemon did not accept unknown Device. +- Fixed a rare crash in list nextvol +- Fixed a compile error if zlib does not exist. +- Finally got the Gnome console running correctly under Gnome 2.x +- Print human readable message on error by gethostbyname(). +- Fixed a time conversion problem in tape label. +- A number of enhancements to btape (more work to be done). +- Updates to Solaris system installation files. +- New bacula.spec.in from Scott Barninger -- including a rescue rpm. +- Slightly different listing and list sorting, more important fields + are first. + + +Items to note: !!!!! +- All daemons should be compatible with all other 1.32x versions, + you can mix and match (hopefully). + + +Change in previous releases: +Most Significant Changes since 1.32c +- Mainly a bug fix release. +- Do a clean of both Gnome directories. +- Require that FileSet id match when finding an Incremental + previous job. This was already the case for a Full. +- Print message if no status returned from FD. +- Correct "Do not forget to mount the drive" message. Test was + backward. +- "status dir" stopped scanning the run records on the first + one that matched giving an incomplete listing. +- Edit commas in Bytes on "estimate" command output. + +Most Significant Changes since 1.32b +- Implemented a RunAfterFailedJob record in the Job resource. +- Implemented "delete job" command in the Console. +- Gnome 2.0 console compiles and works. +- Implemented VerifyJob record in the Job resource + that tells Verify which job to verify (JobId not required) +- First cut Verify Disk to Catalog +- Fix "status dir" to examine all run commands in Schedule. +- Close unused file descriptors in bpipe.c +- There is now a patch for FreeBSD 4.8 pthreads that + fixes the problems of data loss at the end of a tape. + Please see: + /platforms/freebsd/pthreads-fix.txt +- Fixed (I think) the elusive Windows "packet too big" bug. +- Added %v to RunBefore/After editing codes. It edits in + a list of Volumes used for the job (not tested). + +Most Significant Changes since 1.32a: +- Improve forward space file/block during restore, many + optimizations. +- Fix a Bacula bug that did not allow appending to a tape + on FreeBSD systems. +- Fix pruning so that it will not prune the current job. +- Modify configure to use non-threaded MySQL client lib if + the threaded version is not present. +- Implement restore by file before date. +- When pruning don't prune the current job. + +Major Changes 1.32a Release: - Implemented forward space file/block whenever possible during restore. Restoring a small number of files is now much faster. @@ -22,9 +156,14 @@ Major Changes this Release: be used is also added to the "status dir" output. - Lots of fixes with variable expansion and counter variables - Implemented a new Include/Exclude syntax. +- While writing a tape, an end of file mark will be written + every 1Gb. This makes restores faster. If you want to + change this use "Maximum File Size" in the SD Device + resource. -Other Changes this Release: +Other Changes 1.32a Release: +- Fixed sparse file bug. - A warning message is sent when a job starts that will be blocked because the user did an "unmount". - Block checksum errors if any are printed in the job report. diff --git a/bacula/autoconf/acconfig.h b/bacula/autoconf/acconfig.h index a6fe066c74..e9cef42055 100644 --- a/bacula/autoconf/acconfig.h +++ b/bacula/autoconf/acconfig.h @@ -23,6 +23,9 @@ /* Define if you want to use MySQL */ #undef HAVE_MYSQL +/* Defined if MySQL thread safe library is present */ +#undef HAVE_THREAD_SAFE_MYSQL + /* Define if you want to use embedded MySQL */ #undef HAVE_EMBEDDED_MYSQL @@ -204,4 +207,3 @@ #undef HAVE_OLD_SOCKOPT #undef HAVE_BIGENDIAN - diff --git a/bacula/autoconf/aclocal.m4 b/bacula/autoconf/aclocal.m4 index 8f0aab87b2..1294fb1381 100644 --- a/bacula/autoconf/aclocal.m4 +++ b/bacula/autoconf/aclocal.m4 @@ -390,7 +390,12 @@ Which DBMS do you want to use (please select only one): fi fi SQL_INCLUDE=-I$MYSQL_INCDIR + if test -f $MYSQL_LIBDIR/libmysqlclient_r.a; then SQL_LFLAGS="-L$MYSQL_LIBDIR -lmysqlclient_r -lz" + AC_DEFINE(HAVE_THREAD_SAFE_MYSQL) + else + SQL_LFLAGS="-L$MYSQL_LIBDIR -lmysqlclient -lz" + fi SQL_BINDIR=$MYSQL_BINDIR AC_DEFINE(HAVE_MYSQL) diff --git a/bacula/autoconf/config.h.in b/bacula/autoconf/config.h.in index 13c47a8636..db3f80dc3c 100644 --- a/bacula/autoconf/config.h.in +++ b/bacula/autoconf/config.h.in @@ -24,6 +24,9 @@ /* Define if you want to use MySQL */ #undef HAVE_MYSQL +/* Defined if MySQL thread safe library is present */ +#undef HAVE_THREAD_SAFE_MYSQL + /* Define if you want to use embedded MySQL */ #undef HAVE_EMBEDDED_MYSQL @@ -205,7 +208,6 @@ #undef HAVE_BIGENDIAN - /* Define to 1 if the `closedir' function returns void instead of `int'. */ #undef CLOSEDIR_VOID diff --git a/bacula/autoconf/configure.in b/bacula/autoconf/configure.in index f00fbe38a9..d47e0d82d4 100644 --- a/bacula/autoconf/configure.in +++ b/bacula/autoconf/configure.in @@ -172,18 +172,20 @@ if test x$support_gnome = xyes; then pkg=$? if test $pkg = 0; then GNOME_INCLUDEDIR=`pkg-config --cflags-only-I libgnomeui-2.0` - GNOMEUI_LIBS=`pkg-config --libs-only-other libgnomeui-2.0` + GNOMEUI_LIBS=`pkg-config --libs-only-l libgnomeui-2.0` GNOME_LIBDIR=`pkg-config --libs libgnomeui-2.0` GNOME_LIBS=`pkg-config --libs-only-l libgnomeui-2.0` AC_SUBST(GNOME_INCLUDEDIR) AC_SUBST(GNOMEUI_LIBS) AC_SUBST(GNOME_LIBDIR) AC_SUBST(GNOME_LIBS) - GNOME_DIR=src/gnome-console + GNOME_DIR=src/gnome2-console + gnome_version="version 2.x" else -dnl 1.4 stuff +dnl do 1.4 stuff GNOME_INIT GNOME_DIR=src/gnome-console + gnome_version="version 1.4" fi fi AC_SUBST(GNOME_DIR) @@ -272,10 +274,13 @@ AC_ARG_ENABLE(static-cons, fi]) STATIC_CONS= +STATIC_GNOME_CONS= if test x$support_static_cons = xyes; then STATIC_CONS="static-console" + STATIC_GNOME_CONS="static-gnome-console" fi AC_SUBST(STATIC_CONS) +AC_SUBST(STATIC_GNOME_CONS) # ------------------------------------------- # client_only (default off) @@ -505,7 +510,7 @@ AC_ARG_WITH(tcp-wrappers, [ if test "x$withval" != "xno" ; then saved_LIBS="$LIBS" - LIBS="$LIBS -lwrap -lnsl" + LIBS="$saved_LIBS -lwrap" AC_MSG_CHECKING(for libwrap) AC_TRY_LINK( [ #include @@ -517,8 +522,21 @@ AC_ARG_WITH(tcp-wrappers, AC_MSG_RESULT(yes) AC_DEFINE(HAVE_LIBWRAP) TCPW_MSG="yes" - ], - [AC_MSG_ERROR([*** libwrap missing]) ] + ], [ + LIBS="$saved_LIBS -lwrap -lnsl" + AC_TRY_LINK( + [ #include + int deny_severity = 0; + int allow_severity = 0; + struct request_info *req; ], + [ hosts_access(req); ], + [ + AC_MSG_RESULT(yes) + AC_DEFINE(HAVE_LIBWRAP) + TCPW_MSG="yes" + ], + [AC_MSG_ERROR([*** libwrap missing]) ] ] + ) ) fi ] @@ -728,7 +746,78 @@ AC_SUBST(fd_password) AC_SUBST(sd_password) +# +# Handle users and groups for each daemon +# +dir_user= +AC_ARG_WITH(dir_user, + [ --with-dir-user=USER specify user for Director daemon], + [ + if test "x$withval" != "x" ; then + dir_user=$withval + fi + ] +) +dir_group= +AC_ARG_WITH(dir_group, + [ --with-dir-group=GROUP specify group for Director daemon], + [ + if test "x$withval" != "x" ; then + dir_group=$withval + fi + ] +) + +sd_user= +AC_ARG_WITH(sd_user, + [ --with-sd-user=USER specify user for Storage daemon], + [ + if test "x$withval" != "x" ; then + sd_user=$withval + fi + ] +) + +sd_group= +AC_ARG_WITH(sd_group, + [ --with-sd-group=GROUP specify group for Storage daemon], + [ + if test "x$withval" != "x" ; then + sd_group=$withval + fi + ] +) + +fd_user= +AC_ARG_WITH(fd_user, + [ --with-fd-user=USER specify user for File daemon], + [ + if test "x$withval" != "x" ; then + fd_user=$withval + fi + ] +) + +fd_group= +AC_ARG_WITH(fd_group, + [ --with-fd-group=GROUP specify group for File daemon], + [ + if test "x$withval" != "x" ; then + fd_group=$withval + fi + ] +) + + + + +AC_SUBST(dir_user) +AC_SUBST(dir_group) +AC_SUBST(sd_user) +AC_SUBST(sd_group) +AC_SUBST(fd_user) +AC_SUBST(fd_group) # ------------------------------------------------ # Bacula check for various SQL database engines @@ -1346,6 +1435,11 @@ suse) TAPEDRIVE="/dev/nst0" PSCMD="ps -e -o pid,command" hostname=`hostname -s` + PFILES="${PFILES} \ + platforms/suse/Makefile \ + platforms/suse/bacula-fd \ + platforms/suse/bacula-sd \ + platforms/suse/bacula-dir" ;; suse5) DISTNAME=suse @@ -1353,6 +1447,11 @@ suse5) TAPEDRIVE="/dev/nst0" PSCMD="ps -e -o pid,command" hostname=`hostname -s` + PFILES="${PFILES} \ + platforms/suse/Makefile \ + platforms/suse/bacula-fd \ + platforms/suse/bacula-sd \ + platforms/suse/bacula-dir" ;; unknown) DISTVER=unknown @@ -1420,6 +1519,8 @@ AC_OUTPUT([autoconf/Make.common \ src/console/console.conf \ src/gnome-console/Makefile \ src/gnome-console/gnome-console.conf \ + src/gnome2-console/Makefile \ + src/gnome2-console/gnome-console.conf \ src/tconsole/Makefile \ src/dird/Makefile \ src/dird/bacula-dir.conf \ @@ -1432,14 +1533,14 @@ AC_OUTPUT([autoconf/Make.common \ src/cats/Makefile \ src/cats/make_catalog_backup \ src/cats/delete_catalog_backup \ - src/cats/alter_mysql_tables \ src/cats/make_mysql_tables \ src/cats/drop_mysql_tables \ + src/cats/update_mysql_tables \ src/cats/create_mysql_database \ src/cats/grant_mysql_privileges \ - src/cats/alter_sqlite_tables \ src/cats/make_sqlite_tables \ src/cats/drop_sqlite_tables \ + src/cats/update_sqlite_tables \ src/cats/create_sqlite_database \ src/cats/sqlite \ src/cats/mysql \ @@ -1448,6 +1549,7 @@ AC_OUTPUT([autoconf/Make.common \ src/cats/drop_bdb_tables \ src/cats/make_bacula_tables \ src/cats/drop_bacula_tables \ + src/cats/update_bacula_tables \ src/findlib/Makefile \ src/tools/Makefile \ $PFILES ], @@ -1462,12 +1564,13 @@ chmod 755 src/cats/make_mysql_tables src/cats/drop_mysql_tables chmod 755 src/cats/make_test_tables src/cats/drop_test_tables chmod 755 src/cats/create_mysql_database chmod 755 src/cats/make_catalog_backup src/cats/delete_catalog_backup -chmod 755 src/cats/alter_mysql_tables chmod 755 src/cats/grant_mysql_privileges chmod 755 src/cats/make_sqlite_tables src/cats/drop_sqlite_tables +chmod 755 src/cats/update_sqlite_tables chmod 755 src/cats/make_bacula_tables src/cats/drop_bacula_tables +chmod 755 src/cats/update_mysql_tables +chmod 755 src/cats/update_bacula_tables src/cats/update_mysql_tables chmod 755 src/cats/create_sqlite_database -chmod 755 src/cats/alter_sqlite_tables chmod 755 src/cats/sqlite chmod 755 src/cats/make_bdb_tables src/cats/drop_bdb_tables chmod 755 src/cats/create_bdb_database @@ -1529,9 +1632,18 @@ Configuration on `date`: Job Output Email: ${job_email} Traceback Email: ${dump_email} SMTP Host Address: ${smtp_host} - Director Port ${dir_port} - File daemon Port ${fd_port} - Storage daemon Port ${sd_port} + + Director Port: ${dir_port} + File daemon Port: ${fd_port} + Storage daemon Port: ${sd_port} + + Director User: ${dir_user} + Director Group: ${dir_group} + Storage Daemon User: ${dir_user} + Storage DaemonGroup: ${dir_group} + File Daemon User: ${dir_user} + File Daemon Group: ${dir_group} + SQL binaries Directory ${SQL_BINDIR} Large file support: $largefile_support @@ -1539,7 +1651,7 @@ Configuration on `date`: TCP Wrappers support: ${TCPW_MSG} ZLIB support: ${have_zlib} enable-smartalloc: ${support_smartalloc} - enable-gnome: ${support_gnome} + enable-gnome: ${support_gnome} ${gnome_version} client-only: ${build_client_only} " > config.out diff --git a/bacula/configure b/bacula/configure index 89b636774c..4d6eb606dd 100755 --- a/bacula/configure +++ b/bacula/configure @@ -308,7 +308,7 @@ ac_includes_default="\ # include #endif" -ac_subst_vars='SHELL PATH_SEPARATOR PACKAGE_NAME PACKAGE_TARNAME PACKAGE_VERSION PACKAGE_STRING PACKAGE_BUGREPORT exec_prefix prefix program_transform_name bindir sbindir libexecdir datadir sysconfdir sharedstatedir localstatedir libdir includedir oldincludedir infodir mandir build_alias host_alias target_alias DEFS ECHO_C ECHO_N ECHO_T LIBS BUILD_DIR TRUEPRG FALSEPRG build build_cpu build_vendor build_os host host_cpu host_vendor host_os VERSION DATE LSMDATE CC CFLAGS LDFLAGS CPPFLAGS ac_ct_CC EXEEXT OBJEXT CXX CXXFLAGS ac_ct_CXX CPP EGREP INSTALL_PROGRAM INSTALL_SCRIPT INSTALL_DATA RANLIB ac_ct_RANLIB MV RM CP SED AWK ECHO CMP TBL AR OPENSSL MTX PKGCONFIG ARFLAGS MAKE_SHELL LOCAL_LIBS LOCAL_CFLAGS LOCAL_LDFLAGS LOCAL_DEFS HAVE_SUN_OS_TRUE HAVE_SUN_OS_FALSE HAVE_OSF1_OS_TRUE HAVE_OSF1_OS_FALSE HAVE_AIX_OS_TRUE HAVE_AIX_OS_FALSE HAVE_HPUX_OS_TRUE HAVE_HPUX_OS_FALSE HAVE_LINUX_OS_TRUE HAVE_LINUX_OS_FALSE HAVE_FREEBSD_OS_TRUE HAVE_FREEBSD_OS_FALSE HAVE_NETBSD_OS_TRUE HAVE_NETBSD_OS_FALSE HAVE_OPENBSD_OS_TRUE HAVE_OPENBSD_OS_FALSE HAVE_BSDI_OS_TRUE HAVE_BSDI_OS_FALSE HAVE_SGI_OS_TRUE HAVE_SGI_OS_FALSE HAVE_IRIX_OS_TRUE HAVE_IRIX_OS_FALSE HAVE_DARWIN_OS_TRUE HAVE_DARWIN_OS_FALSE INSIDE_GNOME_COMMON_TRUE INSIDE_GNOME_COMMON_FALSE MSGFMT GNOME_INCLUDEDIR GNOMEUI_LIBS GNOME_LIBDIR GNOME_LIBS GNOMEGNORBA_LIBS GTKXMHTML_LIBS ZVT_LIBS GNOME_CONFIG ORBIT_CONFIG ORBIT_IDL HAVE_ORBIT_TRUE HAVE_ORBIT_FALSE ORBIT_CFLAGS ORBIT_LIBS HAVE_GNORBA_TRUE HAVE_GNORBA_FALSE GNORBA_CFLAGS GNORBA_LIBS GNOME_APPLETS_LIBS GNOME_DOCKLETS_LIBS GNOME_CAPPLET_LIBS GNOME_DIR TTOOL_LDFLAGS STATIC_FD STATIC_SD STATIC_DIR STATIC_CONS ALL_DIRS CONS_INC CONS_LIBS CONS_LDFLAGS READLINE_SRC working_dir scriptdir dump_email job_email smtp_host piddir subsysdir baseport dir_port fd_port sd_port dir_password fd_password sd_password SQL_LFLAGS SQL_INCLUDE SQL_BINDIR cats DB_NAME GETCONF ac_ct_GETCONF X_CFLAGS X_PRE_LIBS X_LIBS X_EXTRA_LIBS LIBOBJS ALLOCA FDLIBS DEBUG DINCLUDE DLIB DB_LIBS WCFLAGS WLDFLAGS OBJLIST hostname TAPEDRIVE PSCMD WIN32 DISTNAME DISTVER LTLIBOBJS' +ac_subst_vars='SHELL PATH_SEPARATOR PACKAGE_NAME PACKAGE_TARNAME PACKAGE_VERSION PACKAGE_STRING PACKAGE_BUGREPORT exec_prefix prefix program_transform_name bindir sbindir libexecdir datadir sysconfdir sharedstatedir localstatedir libdir includedir oldincludedir infodir mandir build_alias host_alias target_alias DEFS ECHO_C ECHO_N ECHO_T LIBS BUILD_DIR TRUEPRG FALSEPRG build build_cpu build_vendor build_os host host_cpu host_vendor host_os VERSION DATE LSMDATE CC CFLAGS LDFLAGS CPPFLAGS ac_ct_CC EXEEXT OBJEXT CXX CXXFLAGS ac_ct_CXX CPP EGREP INSTALL_PROGRAM INSTALL_SCRIPT INSTALL_DATA RANLIB ac_ct_RANLIB MV RM CP SED AWK ECHO CMP TBL AR OPENSSL MTX PKGCONFIG ARFLAGS MAKE_SHELL LOCAL_LIBS LOCAL_CFLAGS LOCAL_LDFLAGS LOCAL_DEFS HAVE_SUN_OS_TRUE HAVE_SUN_OS_FALSE HAVE_OSF1_OS_TRUE HAVE_OSF1_OS_FALSE HAVE_AIX_OS_TRUE HAVE_AIX_OS_FALSE HAVE_HPUX_OS_TRUE HAVE_HPUX_OS_FALSE HAVE_LINUX_OS_TRUE HAVE_LINUX_OS_FALSE HAVE_FREEBSD_OS_TRUE HAVE_FREEBSD_OS_FALSE HAVE_NETBSD_OS_TRUE HAVE_NETBSD_OS_FALSE HAVE_OPENBSD_OS_TRUE HAVE_OPENBSD_OS_FALSE HAVE_BSDI_OS_TRUE HAVE_BSDI_OS_FALSE HAVE_SGI_OS_TRUE HAVE_SGI_OS_FALSE HAVE_IRIX_OS_TRUE HAVE_IRIX_OS_FALSE HAVE_DARWIN_OS_TRUE HAVE_DARWIN_OS_FALSE INSIDE_GNOME_COMMON_TRUE INSIDE_GNOME_COMMON_FALSE MSGFMT GNOME_INCLUDEDIR GNOMEUI_LIBS GNOME_LIBDIR GNOME_LIBS GNOMEGNORBA_LIBS GTKXMHTML_LIBS ZVT_LIBS GNOME_CONFIG ORBIT_CONFIG ORBIT_IDL HAVE_ORBIT_TRUE HAVE_ORBIT_FALSE ORBIT_CFLAGS ORBIT_LIBS HAVE_GNORBA_TRUE HAVE_GNORBA_FALSE GNORBA_CFLAGS GNORBA_LIBS GNOME_APPLETS_LIBS GNOME_DOCKLETS_LIBS GNOME_CAPPLET_LIBS GNOME_DIR TTOOL_LDFLAGS STATIC_FD STATIC_SD STATIC_DIR STATIC_CONS STATIC_GNOME_CONS ALL_DIRS CONS_INC CONS_LIBS CONS_LDFLAGS READLINE_SRC working_dir scriptdir dump_email job_email smtp_host piddir subsysdir baseport dir_port fd_port sd_port dir_password fd_password sd_password dir_user dir_group sd_user sd_group fd_user fd_group SQL_LFLAGS SQL_INCLUDE SQL_BINDIR cats DB_NAME GETCONF ac_ct_GETCONF X_CFLAGS X_PRE_LIBS X_LIBS X_EXTRA_LIBS LIBOBJS ALLOCA FDLIBS DEBUG DINCLUDE DLIB DB_LIBS WCFLAGS WLDFLAGS OBJLIST hostname TAPEDRIVE PSCMD WIN32 DISTNAME DISTVER LTLIBOBJS' ac_subst_files='MCOMMON' # Initialize some variables set by options. @@ -886,6 +886,12 @@ Optional Packages: --with-dir-password=PASSWORD specify Director's password --with-fd-password=PASSWORD specify Client's password --with-sd-password=PASSWORD specify Storage daemon's password + --with-dir-user=USER specify user for Director daemon + --with-dir-group=GROUP specify group for Director daemon + --with-sd-user=USER specify user for Storage daemon + --with-sd-group=GROUP specify group for Storage daemon + --with-fd-user=USER specify user for File daemon + --with-fd-group=GROUP specify group for File daemon Which DBMS do you want to use (please select only one): --with-mysql=DIR Include MySQL support. DIR is the MySQL base @@ -4341,14 +4347,15 @@ if test x$support_gnome = xyes; then pkg=$? if test $pkg = 0; then GNOME_INCLUDEDIR=`pkg-config --cflags-only-I libgnomeui-2.0` - GNOMEUI_LIBS=`pkg-config --libs-only-other libgnomeui-2.0` + GNOMEUI_LIBS=`pkg-config --libs-only-l libgnomeui-2.0` GNOME_LIBDIR=`pkg-config --libs libgnomeui-2.0` GNOME_LIBS=`pkg-config --libs-only-l libgnomeui-2.0` - GNOME_DIR=src/gnome-console + GNOME_DIR=src/gnome2-console + gnome_version="version 2.x" else @@ -4690,6 +4697,7 @@ echo "${ECHO_T}unknown library" >&6 GNOME_DIR=src/gnome-console + gnome_version="version 1.4" fi fi @@ -4793,11 +4801,14 @@ if test "${enable_static_cons+set}" = set; then fi; STATIC_CONS= +STATIC_GNOME_CONS= if test x$support_static_cons = xyes; then STATIC_CONS="static-console" + STATIC_GNOME_CONS="static-gnome-console" fi + # ------------------------------------------- # client_only (default off) # ------------------------------------------- @@ -6326,7 +6337,7 @@ if test "${with_tcp_wrappers+set}" = set; then if test "x$withval" != "xno" ; then saved_LIBS="$LIBS" - LIBS="$LIBS -lwrap -lnsl" + LIBS="$saved_LIBS -lwrap" echo "$as_me:$LINENO: checking for libwrap" >&5 echo $ECHO_N "checking for libwrap... $ECHO_C" >&6 cat >conftest.$ac_ext <<_ACEOF @@ -6369,6 +6380,52 @@ _ACEOF TCPW_MSG="yes" +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + + LIBS="$saved_LIBS -lwrap -lnsl" + cat >conftest.$ac_ext <<_ACEOF +#line $LINENO "configure" +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ + #include + int deny_severity = 0; + int allow_severity = 0; + struct request_info *req; +int +main () +{ + hosts_access(req); + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext conftest$ac_exeext +if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5 + (eval $ac_link) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -s conftest$ac_exeext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + + echo "$as_me:$LINENO: result: yes" >&5 +echo "${ECHO_T}yes" >&6 + cat >>confdefs.h <<\_ACEOF +#define HAVE_LIBWRAP 1 +_ACEOF + + TCPW_MSG="yes" + else echo "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 @@ -6377,6 +6434,9 @@ sed 's/^/| /' conftest.$ac_ext >&5 echo "$as_me: error: *** libwrap missing" >&2;} { (exit 1); exit 1; }; } +fi +rm -f conftest.$ac_objext conftest$ac_exeext conftest.$ac_ext + fi rm -f conftest.$ac_objext conftest$ac_exeext conftest.$ac_ext fi @@ -6624,6 +6684,95 @@ fi +# +# Handle users and groups for each daemon +# +dir_user= + +# Check whether --with-dir_user or --without-dir_user was given. +if test "${with_dir_user+set}" = set; then + withval="$with_dir_user" + + if test "x$withval" != "x" ; then + dir_user=$withval + fi + + +fi; + +dir_group= + +# Check whether --with-dir_group or --without-dir_group was given. +if test "${with_dir_group+set}" = set; then + withval="$with_dir_group" + + if test "x$withval" != "x" ; then + dir_group=$withval + fi + + +fi; + +sd_user= + +# Check whether --with-sd_user or --without-sd_user was given. +if test "${with_sd_user+set}" = set; then + withval="$with_sd_user" + + if test "x$withval" != "x" ; then + sd_user=$withval + fi + + +fi; + +sd_group= + +# Check whether --with-sd_group or --without-sd_group was given. +if test "${with_sd_group+set}" = set; then + withval="$with_sd_group" + + if test "x$withval" != "x" ; then + sd_group=$withval + fi + + +fi; + +fd_user= + +# Check whether --with-fd_user or --without-fd_user was given. +if test "${with_fd_user+set}" = set; then + withval="$with_fd_user" + + if test "x$withval" != "x" ; then + fd_user=$withval + fi + + +fi; + +fd_group= + +# Check whether --with-fd_group or --without-fd_group was given. +if test "${with_fd_group+set}" = set; then + withval="$with_fd_group" + + if test "x$withval" != "x" ; then + fd_group=$withval + fi + + +fi; + + + + + + + + + # ------------------------------------------------ @@ -6685,7 +6834,15 @@ echo "$as_me: error: Invalid MySQL directory $withval - unable to find mysql.h u fi fi SQL_INCLUDE=-I$MYSQL_INCDIR + if test -f $MYSQL_LIBDIR/libmysqlclient_r.a; then SQL_LFLAGS="-L$MYSQL_LIBDIR -lmysqlclient_r -lz" + cat >>confdefs.h <<\_ACEOF +#define HAVE_THREAD_SAFE_MYSQL 1 +_ACEOF + + else + SQL_LFLAGS="-L$MYSQL_LIBDIR -lmysqlclient -lz" + fi SQL_BINDIR=$MYSQL_BINDIR cat >>confdefs.h <<\_ACEOF @@ -17373,6 +17530,11 @@ suse) TAPEDRIVE="/dev/nst0" PSCMD="ps -e -o pid,command" hostname=`hostname -s` + PFILES="${PFILES} \ + platforms/suse/Makefile \ + platforms/suse/bacula-fd \ + platforms/suse/bacula-sd \ + platforms/suse/bacula-dir" ;; suse5) DISTNAME=suse @@ -17380,6 +17542,11 @@ suse5) TAPEDRIVE="/dev/nst0" PSCMD="ps -e -o pid,command" hostname=`hostname -s` + PFILES="${PFILES} \ + platforms/suse/Makefile \ + platforms/suse/bacula-fd \ + platforms/suse/bacula-sd \ + platforms/suse/bacula-dir" ;; unknown) DISTVER=unknown @@ -17424,7 +17591,7 @@ if test "x${subsysdir}" = "x${sbindir}" ; then exit 1 fi - ac_config_files="$ac_config_files autoconf/Make.common Makefile rescue/Makefile rescue/linux/Makefile rescue/freebsd/Makefile rescue/solaris/Makefile scripts/startmysql scripts/stopmysql scripts/btraceback scripts/startit scripts/stopit scripts/console scripts/gconsole scripts/bacula scripts/fd scripts/Makefile scripts/logrotate scripts/bacula.desktop.gnome1 scripts/bacula.desktop.gnome2 scripts/mtx-changer doc/Makefile src/Makefile src/host.h src/console/Makefile src/console/console.conf src/gnome-console/Makefile src/gnome-console/gnome-console.conf src/tconsole/Makefile src/dird/Makefile src/dird/bacula-dir.conf src/lib/Makefile src/stored/Makefile src/stored/bacula-sd.conf src/filed/Makefile src/filed/bacula-fd.conf src/filed/win32/Makefile src/cats/Makefile src/cats/make_catalog_backup src/cats/delete_catalog_backup src/cats/alter_mysql_tables src/cats/make_mysql_tables src/cats/drop_mysql_tables src/cats/create_mysql_database src/cats/grant_mysql_privileges src/cats/alter_sqlite_tables src/cats/make_sqlite_tables src/cats/drop_sqlite_tables src/cats/create_sqlite_database src/cats/sqlite src/cats/mysql src/cats/create_bdb_database src/cats/make_bdb_tables src/cats/drop_bdb_tables src/cats/make_bacula_tables src/cats/drop_bacula_tables src/findlib/Makefile src/tools/Makefile $PFILES" + ac_config_files="$ac_config_files autoconf/Make.common Makefile rescue/Makefile rescue/linux/Makefile rescue/freebsd/Makefile rescue/solaris/Makefile scripts/startmysql scripts/stopmysql scripts/btraceback scripts/startit scripts/stopit scripts/console scripts/gconsole scripts/bacula scripts/fd scripts/Makefile scripts/logrotate scripts/bacula.desktop.gnome1 scripts/bacula.desktop.gnome2 scripts/mtx-changer doc/Makefile src/Makefile src/host.h src/console/Makefile src/console/console.conf src/gnome-console/Makefile src/gnome-console/gnome-console.conf src/gnome2-console/Makefile src/gnome2-console/gnome-console.conf src/tconsole/Makefile src/dird/Makefile src/dird/bacula-dir.conf src/lib/Makefile src/stored/Makefile src/stored/bacula-sd.conf src/filed/Makefile src/filed/bacula-fd.conf src/filed/win32/Makefile src/cats/Makefile src/cats/make_catalog_backup src/cats/delete_catalog_backup src/cats/make_mysql_tables src/cats/drop_mysql_tables src/cats/update_mysql_tables src/cats/create_mysql_database src/cats/grant_mysql_privileges src/cats/make_sqlite_tables src/cats/drop_sqlite_tables src/cats/update_sqlite_tables src/cats/create_sqlite_database src/cats/sqlite src/cats/mysql src/cats/create_bdb_database src/cats/make_bdb_tables src/cats/drop_bdb_tables src/cats/make_bacula_tables src/cats/drop_bacula_tables src/cats/update_bacula_tables src/findlib/Makefile src/tools/Makefile $PFILES" ac_config_commands="$ac_config_commands default" cat >confcache <<\_ACEOF # This file is a shell script that caches the results of configure @@ -17980,6 +18147,8 @@ do "src/console/console.conf" ) CONFIG_FILES="$CONFIG_FILES src/console/console.conf" ;; "src/gnome-console/Makefile" ) CONFIG_FILES="$CONFIG_FILES src/gnome-console/Makefile" ;; "src/gnome-console/gnome-console.conf" ) CONFIG_FILES="$CONFIG_FILES src/gnome-console/gnome-console.conf" ;; + "src/gnome2-console/Makefile" ) CONFIG_FILES="$CONFIG_FILES src/gnome2-console/Makefile" ;; + "src/gnome2-console/gnome-console.conf" ) CONFIG_FILES="$CONFIG_FILES src/gnome2-console/gnome-console.conf" ;; "src/tconsole/Makefile" ) CONFIG_FILES="$CONFIG_FILES src/tconsole/Makefile" ;; "src/dird/Makefile" ) CONFIG_FILES="$CONFIG_FILES src/dird/Makefile" ;; "src/dird/bacula-dir.conf" ) CONFIG_FILES="$CONFIG_FILES src/dird/bacula-dir.conf" ;; @@ -17992,14 +18161,14 @@ do "src/cats/Makefile" ) CONFIG_FILES="$CONFIG_FILES src/cats/Makefile" ;; "src/cats/make_catalog_backup" ) CONFIG_FILES="$CONFIG_FILES src/cats/make_catalog_backup" ;; "src/cats/delete_catalog_backup" ) CONFIG_FILES="$CONFIG_FILES src/cats/delete_catalog_backup" ;; - "src/cats/alter_mysql_tables" ) CONFIG_FILES="$CONFIG_FILES src/cats/alter_mysql_tables" ;; "src/cats/make_mysql_tables" ) CONFIG_FILES="$CONFIG_FILES src/cats/make_mysql_tables" ;; "src/cats/drop_mysql_tables" ) CONFIG_FILES="$CONFIG_FILES src/cats/drop_mysql_tables" ;; + "src/cats/update_mysql_tables" ) CONFIG_FILES="$CONFIG_FILES src/cats/update_mysql_tables" ;; "src/cats/create_mysql_database" ) CONFIG_FILES="$CONFIG_FILES src/cats/create_mysql_database" ;; "src/cats/grant_mysql_privileges" ) CONFIG_FILES="$CONFIG_FILES src/cats/grant_mysql_privileges" ;; - "src/cats/alter_sqlite_tables" ) CONFIG_FILES="$CONFIG_FILES src/cats/alter_sqlite_tables" ;; "src/cats/make_sqlite_tables" ) CONFIG_FILES="$CONFIG_FILES src/cats/make_sqlite_tables" ;; "src/cats/drop_sqlite_tables" ) CONFIG_FILES="$CONFIG_FILES src/cats/drop_sqlite_tables" ;; + "src/cats/update_sqlite_tables" ) CONFIG_FILES="$CONFIG_FILES src/cats/update_sqlite_tables" ;; "src/cats/create_sqlite_database" ) CONFIG_FILES="$CONFIG_FILES src/cats/create_sqlite_database" ;; "src/cats/sqlite" ) CONFIG_FILES="$CONFIG_FILES src/cats/sqlite" ;; "src/cats/mysql" ) CONFIG_FILES="$CONFIG_FILES src/cats/mysql" ;; @@ -18008,6 +18177,7 @@ do "src/cats/drop_bdb_tables" ) CONFIG_FILES="$CONFIG_FILES src/cats/drop_bdb_tables" ;; "src/cats/make_bacula_tables" ) CONFIG_FILES="$CONFIG_FILES src/cats/make_bacula_tables" ;; "src/cats/drop_bacula_tables" ) CONFIG_FILES="$CONFIG_FILES src/cats/drop_bacula_tables" ;; + "src/cats/update_bacula_tables" ) CONFIG_FILES="$CONFIG_FILES src/cats/update_bacula_tables" ;; "src/findlib/Makefile" ) CONFIG_FILES="$CONFIG_FILES src/findlib/Makefile" ;; "src/tools/Makefile" ) CONFIG_FILES="$CONFIG_FILES src/tools/Makefile" ;; "$PFILES" ) CONFIG_FILES="$CONFIG_FILES $PFILES" ;; @@ -18201,6 +18371,7 @@ s,@STATIC_FD@,$STATIC_FD,;t t s,@STATIC_SD@,$STATIC_SD,;t t s,@STATIC_DIR@,$STATIC_DIR,;t t s,@STATIC_CONS@,$STATIC_CONS,;t t +s,@STATIC_GNOME_CONS@,$STATIC_GNOME_CONS,;t t s,@ALL_DIRS@,$ALL_DIRS,;t t s,@CONS_INC@,$CONS_INC,;t t s,@CONS_LIBS@,$CONS_LIBS,;t t @@ -18220,6 +18391,12 @@ s,@sd_port@,$sd_port,;t t s,@dir_password@,$dir_password,;t t s,@fd_password@,$fd_password,;t t s,@sd_password@,$sd_password,;t t +s,@dir_user@,$dir_user,;t t +s,@dir_group@,$dir_group,;t t +s,@sd_user@,$sd_user,;t t +s,@sd_group@,$sd_group,;t t +s,@fd_user@,$fd_user,;t t +s,@fd_group@,$fd_group,;t t s,@SQL_LFLAGS@,$SQL_LFLAGS,;t t s,@SQL_INCLUDE@,$SQL_INCLUDE,;t t s,@SQL_BINDIR@,$SQL_BINDIR,;t t @@ -18777,12 +18954,13 @@ chmod 755 src/cats/make_mysql_tables src/cats/drop_mysql_tables chmod 755 src/cats/make_test_tables src/cats/drop_test_tables chmod 755 src/cats/create_mysql_database chmod 755 src/cats/make_catalog_backup src/cats/delete_catalog_backup -chmod 755 src/cats/alter_mysql_tables chmod 755 src/cats/grant_mysql_privileges chmod 755 src/cats/make_sqlite_tables src/cats/drop_sqlite_tables +chmod 755 src/cats/update_sqlite_tables chmod 755 src/cats/make_bacula_tables src/cats/drop_bacula_tables +chmod 755 src/cats/update_mysql_tables +chmod 755 src/cats/update_bacula_tables src/cats/update_mysql_tables chmod 755 src/cats/create_sqlite_database -chmod 755 src/cats/alter_sqlite_tables chmod 755 src/cats/sqlite chmod 755 src/cats/make_bdb_tables src/cats/drop_bdb_tables chmod 755 src/cats/create_bdb_database @@ -18844,9 +19022,18 @@ Configuration on `date`: Job Output Email: ${job_email} Traceback Email: ${dump_email} SMTP Host Address: ${smtp_host} - Director Port ${dir_port} - File daemon Port ${fd_port} - Storage daemon Port ${sd_port} + + Director Port: ${dir_port} + File daemon Port: ${fd_port} + Storage daemon Port: ${sd_port} + + Director User: ${dir_user} + Director Group: ${dir_group} + Storage Daemon User: ${dir_user} + Storage DaemonGroup: ${dir_group} + File Daemon User: ${dir_user} + File Daemon Group: ${dir_group} + SQL binaries Directory ${SQL_BINDIR} Large file support: $largefile_support @@ -18854,7 +19041,7 @@ Configuration on `date`: TCP Wrappers support: ${TCPW_MSG} ZLIB support: ${have_zlib} enable-smartalloc: ${support_smartalloc} - enable-gnome: ${support_gnome} + enable-gnome: ${support_gnome} ${gnome_version} client-only: ${build_client_only} " > config.out diff --git a/bacula/kernstodo b/bacula/kernstodo index 3edeac324b..61badcf765 100644 --- a/bacula/kernstodo +++ b/bacula/kernstodo @@ -1,5 +1,5 @@ Kern's ToDo List - 26 September 2003 + 07 December 2003 Documentation to do: (any release a little bit at a time) - Document running a test version. @@ -11,55 +11,287 @@ Documentation to do: (any release a little bit at a time) - VXA drives have a "cleaning required" indicator, but Exabyte recommends preventive cleaning after every 75 hours of operation. + From Phil: + In this context, it should be noted that Exabyte has a command-line + vxatool utility available for free download.  (The current version is + vxatool-3.72.) It can get diagnostic info, read, write and erase tapes, + test the drive, unload tapes, change drive settings, flash new firmware, + etc. + Of particular interest in this context is that vxatool -i will + report, among other details, the time since last cleaning in tape motion + minutes.  This information can be retrieved (and settings changed, for + that matter) through the generic-SCSI device even when Bacula has the + regular tape device locked.  (Needless to say, I don't recommend + changing tape settings while a job is running.) - Lookup HP cleaning recommendations. - Lookup HP tape replacement recommendations (see trouble shooting autochanger) +- Create a man page for each binary (Debian package requirement). Testing to do: (painful) - that ALL console command line options work and are always implemented - blocksize recognition code. - Test if rewind at end of tape waits for tape to rewind. - Test cancel at EOM. -- Test not zeroing Autochanger slot when it is wrong. -- Figure out how to use ssh or stunnel to protect Bacula communications. -- Test connect timeouts. -For 1.32 Testing/Documentation: -- Document new records in Director. SDAddress SDDeviceName, SDPassword. +For 1.33 Testing/Documentation: +- Document new alias records in Director. SDAddress SDDeviceName, SDPassword. FDPassword, FDAddress, DBAddress, DBPort, DBPassword. -- Document that it is safe to use the drive when the lights stop flashing. - Document new Include/Exclude ... -- Document all the status codes JobLevel, JobType, JobStatus. - Add test of exclusion, test multiple Include {} statements. - Add counter variable test. - -For 1.32: -- Add GUI interface to manual -- Document that Volume pruning can delete last Full backup and - hence you will not have a valid backup. -- Clarify the fact that having the Bacula cygwin1.dll loaded - is not the same as having cygwin installed. -- Fix sparse file handeling so that it always reads a multiple - of 512. Currently, it subtracts 8 bytes (for faddr). -- Separate Dir heartbeat in FD from the SD heartbeat. +- Document ln -sf /usr/lib/libncurses.so /usr/lib/libtermcap.so + and install the esound-dev  package for compiling Console on SuSE. +- Add an example of using a FIFO in dirdconf.wml +- Add an item to the FAQ about running jobs in different timezones. +- Add some examples of job editing codes. +- Document Dan's new --with-dir-user, ... options. +- Figure out how to use ssh or stunnel to protect Bacula communications. + Add Dan's work to manual + For 1.33 +- Add a .list all files in the restore tree (probably also a list all files) + Do both a long and short form. +- Add a Media record flag that indicates that the Volume does disk + addressing. +- Implement VolAddr, which is used when Volume is addressed like a disk, + and form it from VolFile and VolBlock. +- Make multiple restore jobs for multiple media types specifying + the proper storage type. +- Implement MediaType keyword in bsr? +- Fix fast block rejection (stored/read_record.c:118). It passes a null + pointer (rec) to try_repositioning(). +- See if a restore job can add a file to the tape (prohibit this). +- Look at extracting Win data from BackupRead. +- Having dashes in filenames apparently creates problems for restore + by filename. +- Add data compare on write/read in btape "test". +- Implement RestoreJobRetention? Maybe better "JobRetention" in a Job, + which would take precidence over the Catalog "JobRetention". +- Implement Label Format in Add and Label console commands. +- Possibly up network buffers to 65K. Put on variable. +- Put email tape request delays on one or more variables. User wants + to cancel the job after a certain time interval. Maximum Mount Wait? + Job, Client, Device, Pool, or Volume? + Is it possible to make this a directive which is *optional* in multiple + resources, like Level? If so, I think I'd make it an optional directive + in Job, Client, and Pool, with precedence such that Job overrides Client + which in turn overrides Pool. +- Fix Ctl-C crashing the Console (readline?). +- Finish work on conio.c + + +After 1.33: +- Look at adding SQL server and Exchange support for Windows. +- Restore: Enter Filename: 'C:/Documents and Settings/Comercial/My + Documents/MOP/formulário de registro BELAS ARTES.doc' causes Bacula to + crash. +- Each DVD-RAM disk would be a volume, just like each tape is + a volume. It's a 4.7GB media with random access, but there's nothing about + it that I can see that makes it so different than a tape from  bacula's + perspective. Why couldn't I back up to a bare floppy as a volume (ignoring + the media capacity?) +- Make dev->file and dev->block_num signed integers so that -1 can + be an invalid value which happens with BSR. +- Create VolAddr for disk files in place of VolFile and VolBlock. This + is needed to properly specify ranges. +- Print bsmtp output to job report so that problems will be seen. +- Pass the number of files to be restored to the FD for reporting +- Add progress of files/bytes to SD and FD. +- Don't continue Restore if no files selected. +- Print warning message if FileId > 4 billion +- do a "messages" before the first prompt in Console +- Add a date and time stamp at the beginning of every line in the + Job report (Volker Sauer). +- Client does not show busy during Estimate command. +- Implement Console mtx commands. +- Look at 2Gb limit for SQLite. +- Implement 3 Pools for a Job: + Job { +   Name = ... +   Full Backup Pool = xxx +   Incremental Backup Pool = yyy +   Differential Backup Pool = zzz + } +- Add a default DB password to MySQL. + GRANT all privileges ON bacula.* TO bacula@localhost IDENTIFIED BY + 'bacula_password'; + FLUSH PRIVILEGES; +- Define week of year for scheduler. W01, W02, ... + Week 01 of a year is per definition the first week that has the + Thursday in this year, which is equivalent to the week that contains the + fourth day of January. In other words, the first week of a new year is + the week that has the majority of its days in the new year. Week 01 + might also contain days from the previous year and the week before week + 01 of a year is the last week (52 or 53) of the previous year even if it + contains days from the new year. A week starts with Monday (day 1) and + ends with Sunday (day 7). For example, the first week of the year 1997 + lasts from 1996-12-30 to 1997-01-05 and can be written in standard + notation as + + 1997-W01 or 1997W01 + + The week notation can also be extended by a number indicating the day + of the week. For example, the day 1996-12-31, which is the Tuesday (day + 2) of the first week of 1997, can also be written as + + 1997-W01-2 or 1997W012 + +- Implement a Mount Command and an Unmount Command where + the users could specify a system command to be performed + to do the mount, after which Bacula could attempt to + read the device. This is for Removeable media such as a CDROM. + - Most likely, this mount command would be invoked explicitly + by the user using the current Console "mount" and "unmount" + commands -- the Storage Daemon would do the right thing + depending on the exact nature of the device. + - As with tape drives, when Bacula wanted a new removable + disk mounted, it would unmount the old one, and send a message + to the user, who would then use "mount" as described above + once he had actually inserted the disk. +- Implement dump/print label to UA +- Implement disk spooling. Two parts: 1. Spool to disk then + immediately to tape to speed up tape operations. 2. Spool to + disk only when the tape is full, then when a tape is hung move + it to tape. +- Scratch Pool where the volumes can be re-assigned to any Pool. +- bextract is sending everything to the log file ****FIXME**** +- Add Progress command that periodically reports the progress of + a job or all jobs. +- Restrict characters permitted in a Resource name, and don't permit + duplicate names. +- Allow multiple Storage specifications (or multiple names on + a single Storage specification) in the Job record. Thus a job + can be backed up to a number of storage devices. +- Implement some way for the File daemon to contact the Director + to start a job or pass its DHCP obtained IP number. +- Implement multiple Consoles. +- Implement a query tape prompt/replace feature for a console +- From Johan? + Two jobs ready to go, first one blocked waiting for media + Cancel 2nd job ("waiting execution" one) + Cancel blocked job + boom - segfault* +- Copy console @ code to gnome2-console +- Make AES the only encryption algorithm see + http://csrc.nist.gov/CryptoToolkit/aes/). It's + an officially adopted standard, has survived peer + review, and provides keys up to 256 bits. +- Add ctl-c to console to stop current command and discard buffered + output. +- Estimate to Tibs never returns. +- Think about how space could be freed up on a tape -- perhaps this + is a Merge or Compact feature that is needed. +- Modify FileSet, did not upgrade the current Increment job, but + waited for the next job to be upgraded. +- Take a careful look at SetACL http://setacl.sourceforge.net +- Implement a where command for the tree telling where a file + is located. +- Take a careful look at Level for the estimate command, maybe make + it a command line option. +- Add Volume name to "I cannot write on this volume because" +- Make restore job check if all the files are actually restored. +- Make tree walk routines like cd, ls, ... more user friendly + by handling spaces better. +- Write your PID file and chown root:wheel before drop. +- Make sure there is no symlink in a file before creating a + file (attack). +- Look at mktemp or mkstemp(3). + mktemp and mkstemp create files with predictable names too. That's + not the vulnerability. The vulnerability is in creating files without + using the O_EXCL flag, which means "only create this file if it doesn't + exist, including if the file is a dangling symlink." + + It is *NOT* enough to do the equivalent of + + if doesn't exist $filename + then create $filename + + because between the test and the create another process could have + gotten the CPU and created the file. You must use atomic functions + (those that don't get interrupted by other processes) and O_EXCL is + the only way for this particular example. +- Automatically create pools, but instead of looking for what + in in Job records, walk through the pool resources. +- Check and double check tree code, why does it take so long? +- Add device name to "Current Volume not acceptable because ..." +- Make sure that Bacula rechecks the tape after the 20 min wait. +- Set IO_NOWAIT on Bacula TCP/IP packets. +- Try doing a raw partition backup and restore by mounting a + Windows partition. +- Report CVS problems to SourceForge. +- Implement .consolerc for Console +- From Lars Köllers: + Yes, it would allow to highly automatic the request for new tapes. If a + tape is empty, bacula reads the barcodes (native or simulated), and if + an unused tape is found, it runs the label command with all the + necessary parameters. + + By the way can bacula automatically "move" an empty/purged volume say + in the "short" pool to the "long" pool if this pool runs out of volume + space? +- Either restrict the characters in a name, or fix the problem + emailing with names containing / (smtp command line breaks). +- Eliminate orphaned jobs: dbcheck, normal pruning, delete job command. + Hm.  Well, there are the remaining orphaned job records: + + |   105 | Llioness Save  | 0000-00-00 00:00:00 | B    | D     |        0 |             0 | f         | + |   110 | Llioness Save  | 0000-00-00 00:00:00 | B    | I     |        0 |             0 | f         | + |   115 | Llioness Save  | 2003-09-10 02:22:03 | B    | I     |        0 |             0 | A         | + |   128 | Catalog Save   | 2003-09-11 03:53:32 | B    | I     |        0 |             0 | C         | + |   131 | Catalog Save   | 0000-00-00 00:00:00 | B    | I     |        0 |             0 | f         | + + As you can see, three of the five are failures.  I already deleted the + one restore and one other failure using the by-client option.  Deciding + what is an orphaned job is a tricky problem though, I agree.  All these + records have or had 0 files/ 0 bytes, except for the restore.  With no + files, of course, I don't know of the job ever actually becomes + associated with a Volume. + + (I'm not sure if this is documented anywhere -- what are the meanings of + all the possible JobStatus codes?) + + Looking at my database, it appears to me as though all the "orphaned" + jobs fit into one of two categories: + + 1)  The Job record has a StartTime but no EndTime, and the job is not +     currently running; + or + 2)  The Job record has an EndTime, indicating that it completed, but +     it has no associated JobMedia record. + + + This does suggest an approach.  If failed jobs (or jobs that, for some + other reason, write no files) are associated with a volume via a + JobMedia record, then they should be purged when the associated volume + is purged.  I see two ways to handle jobs that are NOT associated with a + specific volume: + + 1)  purge them automatically whenever any volume is manually purged; + or + 2)  add an option to the purge command to manually purge all jobs with +     no associated volume. + + I think Restore jobs also fall into category 2 above .... so one might + want to make that "The Job record has an EndTime,, but no associated + JobMedia record, and is not a Restore job." +- make "btape /tmp" work. +- Make sure a rescheduled job is properly reported by status. +- Walk through the Pool records rather than the Job records + in dird.c to create/update pools. +- What to do about "list files job=xxx". +- Implement scan: for every slot it finds, zero the slot of + Volume other volume having that slot. - When job rescheduled, status gives is waiting for Client Rufus to connect to Storage File. Dir needs to inform SD that job is rescheduled. - Fix get_storage_from_media_type (ua_restore) to use command line storage= -- Enhance "update slots" to include a "scan" feature - scan 1; scan 1-5; scan 1,2,4 ... to update the catalog -- Allow a slot or range of slots on the label barcodes command. - Don't print "Warning: Wrong Volume mounted ..." if mounting second volume. - Make Dmsg look at global before calling subroutine. - Enable trace output at runtime for Win32 -- Make sure that Volumes are recycled based on "Least recently used" - rather than lowest MediaId. - Available volumes for autochangers (see patrick@baanboard.com 3 Sep 03 and 4 Sep) scan slots. -- Upgrade to cygwin 1.5 -- Get MySQL 3.23.58 - Get and test MySQL 4.0 - Do a complete audit of all pthreads_mutex, cond, ... to ensure that any that are dynamically initialized are destroyed when no longer used. @@ -71,7 +303,7 @@ For 1.33 - Use system dependent calls to get more precise info on tape errors. - Add heartbeat from FD to SD if hb interval expires. - Suppress read error on blank tape when doing a label. -- Can we dynamically change FileSets. +- Can we dynamically change FileSets? - If pool specified to label command and Label Format is specified, automatically generate the Volume name. - Take a careful look a the Basic recycling algorithm. When Bacula @@ -101,9 +333,6 @@ For 1.33 time as the user walks through the tree). - Possibly use the hash code if the user selects all for a restore command. - Orphaned Dir buffer at parse_conf.c:373 => store_dir -- Implement some way for the File daemon to contact the Director - to start a job or pass its DHCP obtained IP number. -- Implement multiple Consoles. - Add Console usr permissions. - Fix "restore all" to bypass building the tree. - Fix restore to list errors if Invalid block found, and if # files @@ -114,12 +343,9 @@ For 1.33 be sure this works with admin jobs so that the user can get prompted to insert the correct tape. Possibly some way to say to run the job but don't save the files. -- Add JobName= to VerifyToCatalog so that all verifies can be done at the end. - Implement FileOptions (see end of this document) - Make things like list where a file is saved case independent for Windows. -- Edit the Client/Storage name into authentication failure messages. -- Implement job in VerifyToCatalog - Implement migrate - Implement a PostgreSQL driver. - Bacula needs to propagate SD errors. @@ -131,7 +357,6 @@ For 1.33 > > prod4-sd: End of medium on Volume "REU007" Bytes=16,303,521,933 - Use autochanger to handle multiple devices. -- Fix packet too big problem. - Add SuSE install doc to list. - Check and rechedk "Invalid block number" - Make bextract release the drive properly between tapes @@ -146,16 +371,10 @@ For 1.33 - Something is not right in last block of fill command. - Implement a Recycle command - Add FileSet to command line arguments for restore. -- Allow multiple Storage specifications (or multiple names on - a single Storage specification) in the Job record. Thus a job - can be backed up to a number of storage devices. - Add client name to cram-md5 challenge so Director can immediately verify if it is the correct client. -- Implement ClientRunBeforeJob and ClientRunAfterJob. - Add JobLevel in FD status (but make sure it is defined). - Audit all UA commands to ensure that we always prompt where possible. -- Restrict characters permitted in a Resource name, and don't permit - duplicate names. - Check Jmsg in bnet, may not work, must dup bsock. - Suppress Job Name in Jmsg for console - Create Pools that are referenced in a Run statement at startup if possible. @@ -181,12 +400,7 @@ For 1.33 all Differential and Incremental jobs obsoleted by that Full backup. This would let people minimize the number of tapes they're keeping on hand without having to master the art of retention times. -- Implement new serialize subroutines - send(socket, "string", &Vol, "uint32", &i, NULL) -- Scratch Pool where the volumes can be re-assigned to any Pool. - Implement a M_SECURITY message class. -- Implement forward spacing block/file: position_device(bsr) -- - just before read_block_from_device(); - When doing a Backup send all attributes back to the Director, who would then figure out what files have been deleted. - Currently in mount.c:236 the SD simply creates a Volume. It should have @@ -201,11 +415,6 @@ For 1.33 low level routines by accessing it and using Jmsg(). - Cancel waiting for Client connect in SD if FD goes away. -- Add Progress command that periodically reports the progress of - a job or all jobs. -- One block was orphaned in the SD probably after cancel. - - - Examine Bare Metal restore problem (a FD crash exists somewhere ...). - Implement timeout in response() when it should come quickly. - Implement console @echo command. @@ -220,30 +429,12 @@ For 1.33 - Implement restore "current system", but take all files without doing selection tree -- so that jobs without File records can be restored. -- Implement disk spooling. Two parts: 1. Spool to disk then - immediately to tape to speed up tape operations. 2. Spool to - disk only when the tape is full, then when a tape is hung move - it to tape. - Implement a relocatable bacula.spec -- Implement dump/print label to UA - Add prefixlinks to where or not where absolute links to FD. - Issue message to mount a new tape before the rewind. - Simplified client job initiation for portables. - If SD cannot open a drive, make it periodically retry. -- Implement LabelTemplate (at least first cut). - Add more of the config info to the tape label. -- Implement a Mount Command and an Unmount Command where - the users could specify a system command to be performed - to do the mount, after which Bacula could attempt to - read the device. This is for Removeable media such as a CDROM. - - Most likely, this mount command would be invoked explicitly - by the user using the current Console "mount" and "unmount" - commands -- the Storage Daemon would do the right thing - depending on the exact nature of the device. - - As with tape drives, when Bacula wanted a new removable - disk mounted, it would unmount the old one, and send a message - to the user, who would then use "mount" as described above - once he had actually inserted the disk. - If tape is marked read-only, then try opening it read-only rather than failing, and remember that it cannot be written. @@ -295,7 +486,6 @@ For 1.33 - Compare tape to Client files (attributes, or attributes and data) - Make all database Ids 64 bit. - Write an applet for Linux. -- Implement new inter-daemon communications protocol. - Allow console commands to detach or run in background. - Fix status delay on storage daemon during rewind. - Add SD message variables to control operator wait time @@ -468,193 +658,6 @@ Migration triggered by: Highwater mark (keep total size) Lowwater mark -Projects: - Bacula Projects Roadmap - 17 August 2002 - last update 8 May 2003 - -Item 1: Multiple simultaneous Jobs. (done) -Done -- Restore part needs better implementation to work correctly - Also, it needs considerable testing - - What: Permit multiple simultaneous jobs in Bacula. - - Why: An enterprise level solution needs to go fast without the - need for the system administrator to carefully tweak - timing. Based on the benchmarks, during a full - backup, NetWorker typically hit 10 times the bandwidth to - the tape compared to Bacula--largely. This is probably due to - running parallel jobs and multi-threaded filling of buffers - and writing them to tape. This should also make things work - better when you have a mix of fast and slow machines backing - up at the same time. - - Notes: Bacula was designed to run multiple simultaneous jobs. Thus - implementing this is a matter of some small cleanups and - careful testing. - - -Item 2: Make the Storage daemon use intermediate file storage to buffer data. -Deferred -- not necessary yet -- possibly implement with Migration. - - What: If data is coming into the SD too fast, buffer it to - disk if the user has configured this option. - - Why: This would be nice, especially if it more or less falls out - when implementing (1) above. If not, it probably should not - be given a high priority because fundamentally the backup time - is limited by the tape bandwidth. Even though you may finish a - client job quicker by spilling to disk, you still have to - eventually get it onto tape. If intermediate disk buffering - allows us to improve write bandwidth to tape, it may make - sense. - - Notes: Whether or not this is implemented will depend upon performance - testing after item 1 is implemented. - - -Item 3: Write the bscan program -- also write a bcopy program. -Done - - What: Write a program that reads a Bacula tape and puts all the - appropriate data into the catalog. This allows recovery - from a tape that is no longer in the database, or it allows - re-creation of a database if lost. - - Why: This is a fundamental robustness and disaster recovery tool - which will increase the comfort level of a sysadmin - considering adopting Bacula. - - Notes: A skeleton of this program already exists, but much work - needs to be done. Implementing this will also make apparent - any deficiencies in the current Bacula tape format. - - -Item 4: Implement Base jobs. - - What: A base job is sort of like a Full save except that you - will want the FileSet to contain only files that are unlikely - to change in the future (i.e. a snapshot of most of your - system after installing it). After the base job has been run, - when you are doing a Full save, you can specify to exclude - all files saved by the base job that have not been modified. - - Why: This is something none of the competition does, as far as we know - (except BackupPC, which is a Perl program that saves to disk - only). It is big win for the user, it makes Bacula stand out - as offering a unique optimization that immediately saves time - and money. - - Notes: Big savings in tape usage. Will require more resources because - the e. DIR must send FD a list of files/attribs, and the FD must - search the list and compare it for each file to be saved. - - -Item 5: Implement Label templates -Done - - What: This is a mechanism whereby Bacula can automatically create - a tape label for new tapes according to a detailed specification - provided by the user. - - Why: It is a major convenience item for folks who use automated label - creation. - - Notes: Bacula already has a working form of automatic tape label - creation, but it is very crude. The design for the complete - tape labeling project is already documented in the manual. - - -Item 6: Write a regression script. -Done -- Continue to expand its testing. - - What: This is an automatic script that runs and tests as many features - of Bacula as possible. The output is compared to previous - versions of Bacula and any differences are reported. - - Why: This is an enormous help in preventing introduction of new - errors in parts of the program that already work correctly. - - Notes: This probably should be ranked higher, it's something the typical - user doesn't see. Depending on how it's implemented, it may - make sense to defer it until the archival tape format and - user interface mature. - - -Item 7: GUI for interactive restore -Item 8: GUI for interactive backup - - What: The current interactive restore is implemented with a tty - interface. It would be much nicer to be able to "see" the - list of files backed up in typical GUI tree format. - The same mechanism could also be used for creating - ad-hoc backup FileSets (item 8). - - Why: Ease of use -- especially for the end user. - - Notes: Rather than implementing in Gtk, we probably should go directly - for a Browser implementation, even if doing so meant the - capability wouldn't be available until much later. Not only - is there the question of Windows sites, most - Solaris/HP/IRIX, etc, shops can't currently run Gtk programs - without installing lots of stuff admins are very wary about. - Real sysadmins will always use the command line anyway, and - the user who's doing an interactive restore or backup of his - own files will in most cases be on a Windows machine running - Exploder. - - -Item 9: Add SSL to daemon communications. - - What: This provides for secure communications between the daemons. - - Why: This would allow doing backup across the Internet without - privacy concerns (or with much less concern). - - Notes: The vast majority of near term potential users will be backing up - a single site over a LAN and, correctly or not, they probably - won't be concerned with security, at least not enough to go to - the trouble to set up keys, etc. to screw things down. We suspect - that many users genuinely interested in multi-site backup - already run some form of VPN software in their internetwork - connections, and are willing to delegate security to that layer. - - -Item 10: Define definitive tape format. -Done (version 1.27) - - What: Define that definitive tape format that will not change - for the next millennium. - - Why: Stability, security. - - Notes: See notes for item 11 below. - - -Item 11: New daemon communication protocol. - - What: The current daemon to daemon protocol is basically an ASCII - printf() and sending the buffer. On the receiving end, the - buffer is sscanf()ed to unpack it. The new scheme would - be a binary format that allows quick packing and unpacking - of any data type with named fields. - - Why: Using binary packing would be faster. Named fields will permit - error checking to ensure that what is sent is what the - receiver really wants. - - Notes: These are internal improvements in the interest of the - long-term stability and evolution of the program. On the one - hand, the sooner they're done, the less code we have to rip - up when the time comes to install them. On the other hand, they - don't bring an immediately perceptible benefit to potential - users. Item 10 and possibly item 11 should be deferred until Bacula - is well established with a growing user community more or - less happy with the feature set. At that time, it will make a - good "next generation" upgrade in the interest of data - immortality. - - ====================================================== @@ -662,11 +665,16 @@ Item 11: New daemon communication protocol. It is somewhat like a Full save becomes an incremental since the Base job (or jobs) plus other non-base files. Need: -- New BaseFile table that contains: - JobId, BaseJobId, FileId (from Base). +- A Base backup is same as Full backup, just different type. +- New BaseFiles table that contains: + BaseId - index + BaseJobId - Base JobId referenced for this FileId (needed ???) + JobId - JobId currently running + FileId - File not backed up, exists in Base Job + FileIndex - FileIndex from Base Job. i.e. for each base file that exists but is not saved because it has not changed, the File daemon sends the JobId, BaseId, - and FileId back to the Director who creates the DB entry. + FileId, FileIndex back to the Director who creates the DB entry. - To initiate a Base save, the Director sends the FD the FileId, and full filename for each file in the Base. - When the FD finds a Base file, he requests the Director to @@ -760,6 +768,9 @@ Options records: asterisk preceding the name indicates a feature not currently implemented. + - Regexp "xxx" - Match regular expression + - Wild "xxx" - Do a wild card match + For Backup Jobs: - Compression= (GZIP, ...) - Signature= (MD5, SHA1, ...) @@ -771,6 +782,9 @@ Options records: - *Reader= (filename) - external read (backup) program - *Plugin= (filename) - read/write plugin module + - Include= (yes/no) - Include the file matched no additional + patterns are applied. + For Verify Jobs: - verify= (ipnougsamc5) - verify options @@ -808,7 +822,7 @@ Example: Include { Compression = GZIP; Signature = MD5 - Match = /*.?*/ # matches all files. + Wild = /*.?*/ # matches all files. File = / } } @@ -826,13 +840,13 @@ Example: Options { Signature = MD5 # Note multiple Matches are ORed - Match = /*.gz/ # matches .gz files */ - Match = /*.Z/ # matches .Z files */ + Wild = "*.gz" # matches .gz files + Wild = "*.Z" # matches .Z files } Options { Compression = GZIP Signature = MD5 - Match = /*.?*/ # matches all files + Wild = "*.?*" # matches all files } File = / } @@ -848,7 +862,7 @@ Questions: - Is it necessary to provide some means of ANDing regular expressions and negation? (not currently planned) - e.g. Match = /*.gz/ && !/big.gz/ + e.g. Wild = /*.gz/ && !/big.gz/ - I see that Networker has a "null" module which, if specified, does not backup the file, but does make an record of the file in the catalog @@ -884,76 +898,71 @@ Need: Done: (see kernsdone for more) -- Implement new alist in FileSet scanning. -- bls should continue reading even if it finds Win32 data on the tape. - The error should be Warning rather the Error. -- Add user configurable timeout for connecting to SD. -- Unsaved Flag in Job record (use JobMissingFiles). -- Base Flag in Job record. -- Configure mtx-changer to have correct path to mtx. -- Add all command line arguments to "update", e.g. slot=nn volStatus=append, ... -- Make some way so that if a machine is skipped because it is not up - that Bacula will continue retrying for a specified period of time -- - periodically. -- Implement all command line args on run. -- Implement command line "restore" args. -- Implement "restore current select=no" -- Restore file modified before date -- Restore -- do nothing but show what would happen -- Add estimate to Console commands -- Use read_record.c in SD code. -- Fix read_record to handle multiple sessions. -- Tip from Steve Allam - mt -f /dev/nst0 defblksize 0 -- Document "status" in the console. -- Document driving console from shell script. -- Write JobMedia records with max file size is reached on tape. -- Handle the case of multiple JobMedia records pending (i.e. the - thread is slow and multiple situations requiring a JobMedia - record occur). -- Do performance analysis on the restore tree routines. -- Fix maximum file size (block.c) to generate JobMedia records. -- Make the default file size 1GB on the tape. -- Implement forward spacing between files. -- Add Machine type (Linux/Windows) to Status report for daemons. - Look at src/host.h -- Use repositioning at the beginning of the tape. -- Do full check the command line args in update (e.g. VolStatus ...). -- Specify list of files to restore + +=== after 1.32c +- John's Full save failed with 1.32c FD and 1.31 Dir no FD status, + and no error message. +- Add fd and st as Console keywords. +- Recycling volume with a Slot requires an operator intervention: + rufus-dir: Start Backup JobId 18, Job=kernsave.2003-11-01_21.23.52 + rufus-dir: Pruned 1 Job on Volume Vol01 from catalog. + rufus-dir: There are no Jobs associated with Volume Vol01. Marking it purged. + rufus-dir: Recycled volume "Vol01" + rufus-sd: Please mount Volume "Vol01" on Storage Device "DDS-4" for Job kernsave.2003-11-01_21.23.52 + Use "mount" command to release Job. +- Implement Dan's bacula script (email of 26 Oct). +- Add JobName= to VerifyToCatalog so that all verifies can be done at the end. +- Edit the Client/Storage name into authentication failure messages. +- Fix packet too big problem. This is most likely a Windows TCP stack + problem. - Implement ClientRunBeforeJob and ClientRunAfterJob. -- Make | and < work on FD side. -- Check to see if "blocked" is set during restore. -- Figure out what is interrupting sql command in console. -- Make new job print warning User Unmounted Tape. -- Test recycling and purging (code changed in db_find_next_volume and - in recycle.c). -- Document SDConnectTimeout (in FD). -- Add restore by filename test. -- Document restore by files. -- Make variable expansion work correctly. -- Implement List Volume Job=xxx or List scheduled volumes or Status Director -- Copy static programs into install directory. -- Think about changing Storage resource Device record to be - SDDeviceName. -- Add RunBeforeJob and RunAfterJob to the Client program. -- Need return status on read_cb() from read_records(). Need multiple - records -- one per Job, maybe a JCR or some other structure with - a block and a record. -- LabelFormat on tape volume apparently creates the db record but - never actually labels the volume. -- Recycling a volume when two jobs are using it is going to break. Fixed. -- Document list nextvol and new format status dir. -- Client files in Win32 with Unix eol conventions doesn't work. -- Either fix or document that fill command in btape can be - compressed enormously by the hardware - a 36GB tape wrote 750GB! -- Add multiple character duration qualifiers. -- Require some modifer. -- Restrict characters permitted in a Resource name, and don't permit - duplicate names. -- Figure out some way to ignore or get past checksum errors in - reading. -- The SD spooling file gets created even if it is not used. -- Look at Cleaning tape in ua_label.c for media create/update -- Add regression testing to the manual -- End time: in job output of rescheduled job is time of first run. -- Document list nextvol and status output. +- Implement forward spacing block/file: position_device(bsr) -- + just before read_block_from_device(); +=== for 1.33 +- Change console to bconsole. +- Change smtp to bsmtp. +- Fix time difference problem between Bacula and Client + so that everything is in GMT. +- Fix TimeZone problem! +- Mount a tape that is not right for the job (wrong # files on tape) + Bacula asks for another tape, fix problems with first tape and + say "mount". All works OK, but status shows: + Device /dev/nst0 open but no Bacula volume is mounted. + Total Bytes=1,153,820,213 Blocks=17,888 Bytes/block=64,502 + Positioned at File=9 Block=3,951 + Full Backup job Rufus.2003-10-26_16.45.31 using Volume "DLT-24Oct03" on device /dev/nst0 + Files=21,003 Bytes=253,954,408 Bytes/sec=2,919,016 + FDReadSeqNo=192,134 in_msg=129830 out_msg=5 fd=7 +- Upgrade to cygwin 1.5 +- Optimize fsf not to read. +- Use ioctl() fsf if it exists. Figure out where we are from + the mt_status command. Use slow fsf only if other does not work. +- Enhance "update slots" to include a "scan" feature + scan 1; scan 1-5; scan 1,2,4 ... to update the catalog +- Allow a slot or range of slots on the label barcodes command. +- Finish implementation of Verify=DiskToCatalog +- Make sure that Volumes are recycled based on "Least recently used" + rather than lowest MediaId. +- Add flag to write only one EOF mark on the tape. +- Implement autochanger testing in btape "test" command. +- Implement lmark to list everyfile marked. +- Make mark/unmark report how many files marked/unmarked. +- Keep last 5 or 10 completed jobs and show them in a similar list. +- Make a Running Jobs: output similar to current Scheduled Jobs: +- Change "create_media_record in bscan to use Archive instead of Full. +- Have some way to estimate the restore size or have it printed. +- Volume problems occurs if you have valid volume, written, then it is + truncated. You get 12-Nov-2003 11:48 rufus-sd: kernsave.2003-11-12_11.48.09 Warning: mount.c:228 Volume on /tmp is not a Bacula labeled Volume, because: + block.c:640 Read zero bytes on device /tmp. +- Make sure that 64 bit I/O packets are used on Cygwin. +- Add to supported autochangers + OS             : FreeBSD-4.9 + Auto-Changer    : QUALSTAR TLS-4210 +   Manufufactur  : Qualstar +   Tapes         : 12 (AIT1: 36GB, AIT2: 50GB all uncompressed) +   Drives        : 2xAIT2 (installed in the Qualstar: SONY SDX-500C AIT2) +- Document estimate command in tree. +- Document lsmark command in tree. +- Setup a standard job that builds a bootstrap file and saves + it with the catalog database. + diff --git a/bacula/platforms/debian/bacula-director b/bacula/platforms/debian/bacula-director new file mode 100644 index 0000000000..4d40a11154 --- /dev/null +++ b/bacula/platforms/debian/bacula-director @@ -0,0 +1,59 @@ +#! /bin/sh +# bacula-director SysV init script for Bacula-FD. +# +# Written by Miquel van Smoorenburg . +# Modified for Debian GNU/Linux by Ian Murdock . +# Customized for Bacula by Jose Luis Tallon +# Modified RJM 2-12-03 to fix errors with pidfile name +# +PATH=/sbin:/bin:/usr/sbin:/usr/bin +DAEMON=/usr/sbin/bacula-dir +NAME="bacula-dir" +PORT=9101 +DESC="Bacula Director" +ARGS="-c /etc/bacula/bacula-dir.conf -u bacula -g bacula" + +test -f $DAEMON || exit 0 + +set -e + +if [ -n "`getent services bacula-dir`" ]; then + PORT=`getent services bacula-dir | awk '{ gsub("/tcp","",$2); print $2; }'` +fi + +PIDFILE=/var/run/bacula/$NAME.$PORT.pid + +case "$1" in + start) + if [ -f /etc/bacula/do_not_run ]; then + echo "Not starting $DESC: disabled via /etc/bacula/do_not_run" + exit 0 + fi + + echo -n "Starting $DESC: " + start-stop-daemon --start --quiet --pidfile $PIDFILE --exec $DAEMON -- $ARGS + echo "$NAME." + ;; + + stop) + echo -n "Stopping $DESC: " + start-stop-daemon --oknodo --stop --quiet --pidfile $PIDFILE --exec $DAEMON + echo "$NAME." + ;; + + restart|force-reload) + echo -n "Restarting $DESC: " + start-stop-daemon --stop --quiet --pidfile $PIDFILE --exec $DAEMON + sleep 1 + start-stop-daemon --start --quiet --pidfile $PIDFILE --exec $DAEMON -- $ARGS + echo "$NAME." + ;; + *) + N=/etc/init.d/$NAME + # echo "Usage: $N {start|stop|restart|reload|force-reload}" >&2 + echo "Usage: $N {start|stop|restart|force-reload}" >&2 + exit 1 + ;; +esac + +exit 0 diff --git a/bacula/platforms/freebsd/.cvsignore b/bacula/platforms/freebsd/.cvsignore new file mode 100644 index 0000000000..babb54ed4f --- /dev/null +++ b/bacula/platforms/freebsd/.cvsignore @@ -0,0 +1,4 @@ +Makefile +bacula-dir +bacula-fd +bacula-sd diff --git a/bacula/platforms/freebsd/pthreads-fix.txt b/bacula/platforms/freebsd/pthreads-fix.txt new file mode 100644 index 0000000000..d4fa099c38 --- /dev/null +++ b/bacula/platforms/freebsd/pthreads-fix.txt @@ -0,0 +1,193 @@ +From bacula-users-admin@lists.sourceforge.net Mon Oct 20 23:44:13 2003 +Return-Path: +Received: from sc8-sf-list2.sourceforge.net (lists.sourceforge.net + [66.35.250.206]) by matou.sibbald.com (8.11.6/8.11.6) with ESMTP id + h9KLiDY13657 for ; Mon, 20 Oct 2003 23:44:13 +0200 +Received: from sc8-sf-list1-b.sourceforge.net ([10.3.1.13] + helo=sc8-sf-list1.sourceforge.net) by sc8-sf-list2.sourceforge.net with + esmtp (Exim 3.31-VA-mm2 #1 (Debian)) id 1ABhpS-00013k-00; Mon, 20 Oct 2003 + 14:44:42 -0700 +Received: from sc8-sf-mx1-b.sourceforge.net ([10.3.1.11] + helo=sc8-sf-mx1.sourceforge.net) by sc8-sf-list1.sourceforge.net with esmtp + (Cipher TLSv1:DES-CBC3-SHA:168) (Exim 3.31-VA-mm2 #1 (Debian)) id + 1ABhnp-0007qu-00 for ; Mon, 20 Oct 2003 + 14:43:01 -0700 +Received: from bast.unixathome.org ([66.11.174.150] ident=postfix) by + sc8-sf-mx1.sourceforge.net with esmtp (Exim 4.22) id 1ABfiR-0002Iv-Q8 for + bacula-users@lists.sourceforge.net; Mon, 20 Oct 2003 12:29:19 -0700 +Received: from wocker (wocker.unixathome.org [192.168.0.99]) by + bast.unixathome.org (Postfix) with ESMTP id 258913F53 for + ; Mon, 20 Oct 2003 15:25:33 -0400 (EDT) +From: "Dan Langille" +To: bacula-users@lists.sourceforge.net +MIME-Version: 1.0 +Message-ID: <3F93FF4E.14552.13ACB682@localhost> +Priority: normal +X-mailer: Pegasus Mail for Windows (v4.02a) +Content-type: text/plain; charset=US-ASCII +Content-description: Mail message body +X-Spam-Score: 0.0 (/) +X-Spam-Report: 0.0/5.0 Spam Filtering performed by sourceforge.net. See + http://spamassassin.org/tag/ for more details. Report problems to + https://sf.net/tracker/?func=add&group_id=1&atid=200001 +Subject: [Bacula-users] FreeBSD - large backups to tape +Sender: bacula-users-admin@lists.sourceforge.net +Errors-To: bacula-users-admin@lists.sourceforge.net +X-BeenThere: bacula-users@lists.sourceforge.net +X-Mailman-Version: 2.0.9-sf.net +Precedence: bulk +List-Help: +List-Post: +List-Subscribe: + , + +List-Id: Bacula user's email list for support and discussions + +List-Unsubscribe: + , + +List-Archive: + +Date: Mon, 20 Oct 2003 15:29:18 -0400 +Content-Transfer-Encoding: 8bit + +Kern and I have been working on a FreeBSD/Bacula problem. +He's asked me to post this to the list. The problem was within the +FreeBSD pthreads library. A solution has been found. + +PROBLEM DESCRIPTION: + +The FreeBSD pthreads library does not properly handle End Of Tape. +This problem will be fixed in FreeBSD 4.9. The bug results in more +data being written to the tape than could be read. Any backup which +involved more than one tape would be incomplete. + +DEMONSTRATION: + +To demonstrate the problem, tapetest.c can be obtained from +http://www.freebsd.org/cgi/query-pr.cgi?pr=56274 + +This tests without pthreads: + + * If you build this program with: + * + * c++ -g -O2 -Wall -c tapetest.c + * c++ -g -O2 -Wall tapetest.o -o tapetest + * + * Procedure for testing tape + * ./tapetest /dev/your-tape-device + * rewind + * rawfill + * rewind + * scan + * + * The output will be something like: + * + * ======== + * Rewound /dev/nsa0 + * *Begin writing blocks of 64512 bytes. + * ++++++++++++++++++++ ... + * Write failed. Last block written=17294. stat=0 ERR=Unknown +error: 0 + * weof_dev + * Wrote EOF to /dev/nsa0 + * *Rewound /dev/nsa0 + * *Starting scan at file 0 + * 17294 blocks of 64512 bytes in file 0 + * End of File mark. + * End of File mark. + * End of tape + * Total files=1, blocks=17294, bytes = 1115670528 + * ======== + * + * which is correct. Notice that the return status is + * 0, while in the example below, which fails, the return + * status is -1. + +This tests with pthreads: + + * If you build this program with: + * + * c++ -g -O2 -Wall -pthread -c tapetest.c + * c++ -g -O2 -Wall -pthread tapetest.o -o tapetest + * Note, we simply added -pthread compared to the + * previous example. + * + * Procedure for testing tape + * ./tapetest /dev/your-tape-device + * rewind + * rawfill + * rewind + * scan + * + * The output will be something like: + * + * ======== + * Rewound /dev/nsa0 + * *Begin writing blocks of 64512 bytes. + * +++++++++++++++++++++++++++++ ... + * Write failed. Last block written=17926. stat=-1 ERR=No space left on device + * weof_dev + * Wrote EOF to /dev/nsa0 + * *Rewound /dev/nsa0 + * *Starting scan at file 0 + * 17913 blocks of 64512 bytes in file 0 + * End of File mark. + * End of File mark. + * End of tape + * Total files=1, blocks=17913, bytes = 1155603456 + * ======== + * + * which is incroorect because it wrote 17,926 blocks but read + * back only 17,913 blocks + +If you get the same number of blocks written and read WHEN using +pthreads, then you've been correctly patched. + +SOLUTION: + +For FreeBSD prior to 4.9, you have two choices to ensure proper +backups. These instructions assume you are familiar with patching +FreeBSD and already have the FreeBSD source code installed on your +machine. + +1 - cvsup and build your system to FreeBSD 4.8-STABLE + +2 - Apply this patch. + +http://www.freebsd.org/cgi/cvsweb.cgi/src/lib/libc_r/uthread/uthread_write.c.diff?r1=1.16.2.6&r2=1.16.2.7 + +To apply the patch, follow these instructions as root. + +fetch -o pthread.diff http://www.freebsd.org/cgi/cvsweb.cgi/src/lib/libc_r/uthread/uthread_write.c.diff?r1=1.16.2.6\&r2=1.16.2.7 + +cd /usr/src/lib/libc_r/uthread/ +patch < /path/to/pthread.diff +cd .. +make all install + +I recommend restarting Bacula. + + +TESTING: + +I suggest running tapetest on your patched system and then +conducting a backup which spans two tapes. Restore the data +and compare to the original. If not identical, please let us know. + +Thanks + +-- +Dan Langille : http://www.langille.org/ + + + +------------------------------------------------------- +This SF.net email is sponsored by OSDN developer relations +Here's your chance to show off your extensive product knowledge +We want to know what you know. Tell us and you have a chance to win $100 +http://www.zoomerang.com/survey.zgi?HRPT1X3RYQNC5V4MLNSV3E54 +_______________________________________________ +Bacula-users mailing list +Bacula-users@lists.sourceforge.net +https://lists.sourceforge.net/lists/listinfo/bacula-users diff --git a/bacula/platforms/redhat/bacula-dir.in b/bacula/platforms/redhat/bacula-dir.in index d568cdb999..05fec1b712 100755 --- a/bacula/platforms/redhat/bacula-dir.in +++ b/bacula/platforms/redhat/bacula-dir.in @@ -3,7 +3,7 @@ # bacula This shell script takes care of starting and stopping # the bacula Director daemon # -# chkconfig: 2345 20 99 +# chkconfig: 2345 90 99 # description: It comes by night and sucks the vital essence from your computers. # # For Bacula release @VERSION@ (@DATE@) -- @DISTNAME@ diff --git a/bacula/platforms/redhat/bacula-fd.in b/bacula/platforms/redhat/bacula-fd.in index 19f04f028b..8b725bc722 100755 --- a/bacula/platforms/redhat/bacula-fd.in +++ b/bacula/platforms/redhat/bacula-fd.in @@ -3,7 +3,7 @@ # bacula This shell script takes care of starting and stopping # the bacula File daemon. # -# chkconfig: 2345 20 99 +# chkconfig: 2345 90 99 # description: It comes by night and sucks the vital essence from your computers. # # For Bacula release @VERSION@ (@DATE@) -- @DISTNAME@ diff --git a/bacula/platforms/redhat/bacula-sd.in b/bacula/platforms/redhat/bacula-sd.in index a4bfdd4f5a..e58287625d 100755 --- a/bacula/platforms/redhat/bacula-sd.in +++ b/bacula/platforms/redhat/bacula-sd.in @@ -3,7 +3,7 @@ # bacula This shell script takes care of starting and stopping # the bacula Storage daemon. # -# chkconfig: 2345 20 99 +# chkconfig: 2345 90 99 # description: It comes by night and sucks the vital essence from your computers. # # For Bacula release @VERSION@ (@DATE@) -- @DISTNAME@ diff --git a/bacula/platforms/redhat/bacula.spec.in b/bacula/platforms/redhat/bacula.spec.in index f69b37527f..75c47ba49b 100644 --- a/bacula/platforms/redhat/bacula.spec.in +++ b/bacula/platforms/redhat/bacula.spec.in @@ -1,60 +1,70 @@ +# Platform Build Configuration +# basic defines for every build %define depkgs ../depkgs +%define depkgs_version 24Jul03 +%define tomsrtbt tomsrtbt-2.0.103 -# -# You must build the package with at least one define -# e.g. rpmbuild -ba --define "build_rh7 1" bacula.spec -# -# If you want the MySQL version, use: -# rpmbuild -ba --define "build_mysql 1" --define "build_rh7 1" bacula.spec -# - +# platform defines - set one below or define the build_xxx on the command line +# RedHat builds %define rh7 0 %{?build_rh7:%define rh7 1} - %define rh8 0 %{?build_rh8:%define rh8 1} - %define rh9 0 %{?build_rh9:%define rh9 1} - +# Fedora Core 1 build +%define fc1 0 +%{?build_fc1:%define fc1 1} +# Whitebox Enterprise build +%define wb3 0 +%{?build_wb3:%define wb3 1} + +# database defines +# set mysql for MySQL support, leave unset for sqlite support %define mysql 0 %{?build_mysql:%define mysql 1} - -%if %{rh7} -%define rh_version rh7 -%endif -%if %{rh8} -%define rh_version rh8 -%endif -%if %{rh9} -%define rh_version rh9 -%endif - Summary: Bacula - The Network Backup Solution Name: bacula Version: @VERSION@ Release: 1 Group: System Environment/Daemons Copyright: GPL v2 -Source: http://www.prdownloads.sourceforge.net/bacula/%{name}-%{version}.tar.gz +Source0:http://www.prdownloads.sourceforge.net/bacula/%{name}-%{version}.tar.gz +Source1:http://www.prdownloads.sourceforge.net/bacula/depkgs-%{depkgs_version}.tar.gz +Source2:http://www.tux.org/pub/distributions/tinylinux/tomsrtbt/%{tomsrtbt}.tar.gz BuildRoot: %{_tmppath}/%{name}-root URL: http://www.bacula.org/ Vendor: The Bacula Team Distribution: The Bacula Team Packager: D. Scott Barninger -Requires: gnome-libs >= 1.4 -Requires: readline +BuildRequires: readline-devel, atk-devel, ncurses-devel, pango-devel +BuildRequires: libstdc++-devel, libtermcap-devel, libxml2-devel, zlib-devel +%if %{rh7} +BuildRequires: gtk+-devel >= 1.2 BuildRequires: gnome-libs-devel >= 1.4 -BuildRequires: readline-devel +BuildRequires: glibc-devel >= 2.2 +BuildRequires: ORBit-devel +BuildRequires: bonobo-devel +BuildRequires: GConf-devel +%else +BuildRequires: gtk2-devel >= 2.0 +BuildRequires: libgnomeui-devel >= 2.0 +BuildRequires: glibc-devel >= 2.3 +BuildRequires: ORBit2-devel +BuildRequires: libart_lgpl-devel >= 2.0 +BuildRequires: libbonobo-devel >= 2.0 +BuildRequires: libbonoboui-devel >= 2.0 +BuildRequires: bonobo-activation-devel >= 2.0 +BuildRequires: GConf2-devel +BuildRequires: linc-devel +%endif + %if %{mysql} -Requires: mysql >= 3.23 -Requires: mysql-server >= 3.23 BuildRequires: mysql-devel >= 3.23 %endif - %description Bacula - It comes by night and sucks the vital essence from your computers. @@ -67,18 +77,44 @@ features that make it easy to find and recover lost or damaged files. Bacula source code has been released under the GPL version 2 license. %if %{mysql} -%package mysql-%{rh_version} +%package mysql %else -%package sqlite-%{rh_version} +%package sqlite %endif Summary: Bacula - The Network Backup Solution Group: System Environment/Daemons +Provides: bacula-dir, bacula-sd, bacula-fd +Requires: readline, perl, atk, ncurses, pango, libstdc++ +Requires: libtermcap, libxml2, zlib +%if %{rh7} +Requires: gtk+ >= 1.2 +Requires: gnome-libs >= 1.4 +Requires: glibc >= 2.2 +Requires: ORBit +Requires: bonobo +Requires: GConf +%else +Requires: gtk2 >= 2.0 +Requires: libgnomeui >= 2.0 +Requires: glibc >= 2.3 +Requires: ORBit2 +Requires: libart_lgpl >= 2.0 +Requires: libbonobo >= 2.0 +Requires: libbonoboui >= 2.0 +Requires: bonobo-activation >= 2.0 +Requires: GConf2 +Requires: linc +%endif +%if %{mysql} +Requires: mysql >= 3.23 +Requires: mysql-server >= 3.23 +%endif %if %{mysql} -%description mysql-%{rh_version} +%description mysql %else -%description sqlite-%{rh_version} +%description sqlite %endif Bacula - It comes by night and sucks the vital essence from your computers. @@ -97,10 +133,22 @@ This build requires MySQL to be installed separately as the catalog database. This build incorporates sqlite as the catalog database, statically compiled. %endif -%package client-%{rh_version} +%package client Summary: Bacula - The Network Backup Solution Group: System Environment/Daemons -%description client-%{rh_version} +Provides: bacula-fd +Requires: readline, perl, libstdc++, zlib +%if %{rh7} +Requires: gtk+ >= 1.2 +Requires: gnome-libs >= 1.4 +Requires: glibc >= 2.2 +%else +Requires: gtk2 >= 2.0 +Requires: libgnomeui >= 2.0 +Requires: glibc >= 2.3 +%endif + +%description client Bacula - It comes by night and sucks the vital essence from your computers. Bacula is a set of computer programs that permit you (or the system @@ -113,10 +161,35 @@ Bacula source code has been released under the GPL version 2 license. This is the File daemon (Client) only package. +%package rescue + +Summary: Bacula - The Network Backup Solution +Group: System Environment/Daemons +Requires: coreutils, util-linux, libc5, bacula-fd + +%description rescue +Bacula - It comes by night and sucks the vital essence from your computers. + +Bacula is a set of computer programs that permit you (or the system +administrator) to manage backup, recovery, and verification of computer +data across a network of computers of different kinds. In technical terms, +it is a network client/server based backup program. Bacula is relatively +easy to use and efficient, while offering many advanced storage management +features that make it easy to find and recover lost or damaged files. +Bacula source code has been released under the GPL version 2 license. + +This package installs scripts for disaster recovery and builds rescue +floppy disks for bare metal recovery. This package includes tomsrtbt +(http://www.toms.net/rb/, by Tom Oehser, Tom@Toms.NET) to provide a tool +to build a boot floppy disk. + +You need to have the bacula-sqlite, bacula-mysql or bacula-client package for +your platform installed and configured before installing this package. %prep -%setup +%setup -b 1 +%setup -b 2 %build @@ -131,6 +204,9 @@ cd ${cwd} # patch the make_sqlite_tables script for installation bindir patch src/cats/make_sqlite_tables.in src/cats/make_sqlite_tables.in.patch +# patch the make_catalog_backup script for installation bindir +patch src/cats/make_catalog_backup.in src/cats/make_catalog_backup.in.patch + %configure \ --prefix=/usr \ --sbindir=/usr/sbin \ @@ -138,6 +214,7 @@ patch src/cats/make_sqlite_tables.in src/cats/make_sqlite_tables.in.patch --with-scriptdir=/etc/bacula \ --enable-smartalloc \ --enable-gnome \ + --enable-static-fd \ %if %{mysql} --with-mysql \ %else @@ -148,6 +225,10 @@ patch src/cats/make_sqlite_tables.in src/cats/make_sqlite_tables.in.patch --with-subsys-dir=/var/lock/subsys make +cd src/filed +strip static-bacula-fd +cd ../../ + %install cwd=${PWD} @@ -156,6 +237,8 @@ mkdir -p $RPM_BUILD_ROOT/etc/logrotate.d mkdir -p $RPM_BUILD_ROOT/usr/share/pixmaps mkdir -p $RPM_BUILD_ROOT/usr/share/gnome/apps/System mkdir -p $RPM_BUILD_ROOT/usr/share/applications +mkdir -p $RPM_BUILD_ROOT/etc/bacula/rescue +mkdir -p $RPM_BUILD_ROOT/etc/bacula/rescue/tomsrtbt %if ! %{mysql} mkdir -p $RPM_BUILD_ROOT/usr/lib/sqlite @@ -206,12 +289,30 @@ cp ../depkgs/sqlite/libsqlite.a $RPM_BUILD_ROOT/usr/lib/sqlite/libsqlite.a # install the logrotate file cp scripts/logrotate $RPM_BUILD_ROOT/etc/logrotate.d/bacula +# install the rescue stuff +# these are the rescue scripts +cp rescue/linux/backup.etc.list $RPM_BUILD_ROOT/etc/bacula/rescue/ +cp rescue/linux/format_floppy $RPM_BUILD_ROOT/etc/bacula/rescue/ +cp rescue/linux/getdiskinfo $RPM_BUILD_ROOT/etc/bacula/rescue/ +cp rescue/linux/make_rescue_disk $RPM_BUILD_ROOT/etc/bacula/rescue/ +cp rescue/linux/restore_bacula $RPM_BUILD_ROOT/etc/bacula/rescue/ +cp rescue/linux/restore_etc $RPM_BUILD_ROOT/etc/bacula/rescue/ +cp rescue/linux/run_grub $RPM_BUILD_ROOT/etc/bacula/rescue/ +cp rescue/linux/run_lilo $RPM_BUILD_ROOT/etc/bacula/rescue/ +cp rescue/linux/sfdisk.bz2 $RPM_BUILD_ROOT/etc/bacula/rescue/ + +# this is the static file daemon +cp src/filed/static-bacula-fd $RPM_BUILD_ROOT/etc/bacula/rescue/bacula-fd + +# this is the tom's root boot disk +cp ../%{tomsrtbt}/* $RPM_BUILD_ROOT/etc/bacula/rescue/tomsrtbt/ + %clean [ "$RPM_BUILD_ROOT" != "/" ] && rm -rf "$RPM_BUILD_ROOT" %if %{mysql} -%files mysql-%{rh_version} +%files mysql %defattr(-,root,root) %attr(0754,root,root) /etc/bacula/bacula @@ -250,7 +351,7 @@ cp scripts/logrotate $RPM_BUILD_ROOT/etc/logrotate.d/bacula %attr(0750,root,root) /usr/sbin/* -%post mysql-%{rh_version} +%post mysql # delete then add our links /sbin/chkconfig --del bacula-dir /sbin/chkconfig --del bacula-fd @@ -271,7 +372,7 @@ echo "Creating MySQL bacula database..." echo "Creating bacula tables..." /etc/bacula/make_mysql_tables -%preun mysql-%{rh_version} +%preun mysql # delete our links /sbin/chkconfig --del bacula-dir /sbin/chkconfig --del bacula-fd @@ -279,7 +380,7 @@ echo "Creating bacula tables..." %else -%files sqlite-%{rh_version} +%files sqlite %defattr(-,root,root) %attr(0754,root,root) /etc/bacula/bacula @@ -318,7 +419,7 @@ echo "Creating bacula tables..." %attr(0750,root,root) /usr/sbin/* %attr(0750,root,root) /usr/lib/sqlite/sqlite -%post sqlite-%{rh_version} +%post sqlite # delete then add our links /sbin/chkconfig --del bacula-dir /sbin/chkconfig --del bacula-fd @@ -333,7 +434,7 @@ chmod 0755 /usr/sbin/gnome-console # create the tables /etc/bacula/make_sqlite_tables -%preun sqlite-%{rh_version} +%preun sqlite # delete our links /sbin/chkconfig --del bacula-dir /sbin/chkconfig --del bacula-fd @@ -341,7 +442,7 @@ chmod 0755 /usr/sbin/gnome-console %endif -%files client-%{rh_version} +%files client %defattr(-,root,root) %attr(0754,root,root) /etc/bacula/fd @@ -359,16 +460,83 @@ chmod 0755 /usr/sbin/gnome-console %attr(0750,root,root) /usr/sbin/smtp -%post client-%{rh_version} +%post client # delete then add our links /sbin/chkconfig --del bacula-fd /sbin/chkconfig --add bacula-fd -%preun client-%{rh_version} +%preun client # delete our links /sbin/chkconfig --del bacula-fd +%files rescue +%defattr(-,root,root) +%attr(0644,root,root) /etc/bacula/rescue/backup.etc.list +%attr(0754,root,root) /etc/bacula/rescue/format_floppy +%attr(0754,root,root) /etc/bacula/rescue/getdiskinfo +%attr(0754,root,root) /etc/bacula/rescue/make_rescue_disk +%attr(0754,root,root) /etc/bacula/rescue/restore_bacula +%attr(0754,root,root) /etc/bacula/rescue/restore_etc +%attr(0754,root,root) /etc/bacula/rescue/run_grub +%attr(0754,root,root) /etc/bacula/rescue/run_lilo +%attr(0644,root,root) /etc/bacula/rescue/sfdisk.bz2 +%attr(0754,root,root) /etc/bacula/rescue/bacula-fd +/etc/bacula/rescue/tomsrtbt/* + +%post rescue +# link our current installed conf file to the rescue directory +ln -s /etc/bacula-fd.conf /etc/bacula/rescue/bacula-fd.conf + +echo +echo "Ready to create the rescue files for this system." +echo "Press to continue..." +read A +echo + +# run getdiskinfo +echo "Running getdiskinfo..." +cd /etc/bacula/rescue +./getdiskinfo + +echo +echo "Finished." +echo "To create a boot disk run \"./install.s\" from the /etc/bacula/rescue/tomsrtbt/" +echo "directory. To make the bacula rescue disk run" +echo "\"./make_rescue_disk --copy-static-bacula --copy-etc-files\" " +echo "from the /etc/bacula/rescue directory. To recreate the rescue" +echo "information for this system run ./getdiskinfo again." +echo + +%preun rescue +# remove the files created after the initial rpm installation +rm -f /etc/bacula/rescue/bacula-fd.conf +rm -f /etc/bacula/rescue/partition.* +rm -f /etc/bacula/rescue/format.* +rm -f /etc/bacula/rescue/mount_drives +rm -f /etc/bacula/rescue/start_network +rm -f /etc/bacula/rescue/sfdisk +rm -rf /etc/bacula/rescue/diskinfo/* + %changelog +* Sat Jan 10 2004 D. Scott Barninger +- added virtual package Provides bacula-dir, bacula-sd, bacula-fd +- added bacula-fd as Requires for rescue package +- added build tag for Fedora Core 1 +- cleaned up dependancies for all builds +* Thu Jan 1 2004 D. Scott Barninger +- removed rh_version from package names +- added platform build configuration section to beginning of file +* Tue Nov 25 2003 D. Scott Barninger +- removed make_static_bacula script from rescue package install +* Sun Nov 23 2003 D. Scott Barninger +- Added define at top of file for depkgs version +- Added rescue sub-package +- Moved requires statements into proper sub-package locations +* Mon Oct 27 2003 D. Scott Barninger +- Corrected Requires for Gnome 1.4/2.0 builds +* Fri Oct 24 2003 D. Scott Barninger +- Added separate Source declaration for depkgs +- added patch for make_catalog_backup script * Mon May 11 2003 D. Scott Barninger - Misc changes to mysql/sqlite build and rh7/8 menu differences - Added rh_version to sub-package names diff --git a/bacula/platforms/solaris/Makefile.in b/bacula/platforms/solaris/Makefile.in index 7cf9123a32..8d39622226 100644 --- a/bacula/platforms/solaris/Makefile.in +++ b/bacula/platforms/solaris/Makefile.in @@ -4,6 +4,9 @@ # # 15 November 2001 -- Kern Sibbald # +# 03 November 2003 corrections to the paths made by +# Kenneth ragnor at virtualsd dot net +# # for Bacula release @VERSION@ (@DATE@) -- @DISTNAME@ # @@ -32,7 +35,7 @@ install-autostart-sd: @rm -f /etc/rc0.d/K20bacula-sd @rm -f /etc/rc1.d/S99bacula-sd @rm -f /etc/rc2.d/S99bacula-sd - @$(INSTALL_PROGRAM) -m 744 bacula-sd /etc/rc.d/init.d/bacula-sd + @$(INSTALL_PROGRAM) -m 744 bacula-sd /etc/init.d/bacula-sd # set symlinks for script at startup and shutdown @ln -f -s /etc/init.d/bacula-sd /etc/rc0.d/K20bacula-sd @ln -f -s /etc/init.d/bacula-sd /etc/rc1.d/S99bacula-sd @@ -43,7 +46,7 @@ install-autostart-dir: @rm -f /etc/rc0.d/K20bacula-dir @rm -f /etc/rc1.d/S99bacula-dir @rm -f /etc/rc2.d/S99bacula-dir - @$(INSTALL_PROGRAM) -m 744 bacula-dir /etc/rc.d/init.d/bacula-dir + @$(INSTALL_PROGRAM) -m 744 bacula-dir /etc/init.d/bacula-dir # set symlinks for script at startup and shutdown @ln -f -s /etc/init.d/bacula-dir /etc/rc0.d/K20bacula-dir @ln -f -s /etc/init.d/bacula-dir /etc/rc1.d/S99bacula-dir @@ -58,20 +61,20 @@ uninstall-autostart-fd: @rm -f /etc/rc0.d/K20bacula-fd @rm -f /etc/rc1.d/S99bacula-fd @rm -f /etc/rc2.d/S99bacula-fd - @rm -f /etc/rc.d/init.d/bacula-fd + @rm -f /etc/init.d/bacula-fd uninstall-autostart-sd: @rm -f /etc/rc0.d/K20bacula-sd @rm -f /etc/rc1.d/S99bacula-sd @rm -f /etc/rc2.d/S99bacula-sd - @rm -f /etc/rc.d/init.d/bacula-sd + @rm -f /etc/init.d/bacula-sd uninstall-autostart-dir: @rm -f /etc/rc0.d/K20bacula-dir @rm -f /etc/rc1.d/S99bacula-dir @rm -f /etc/rc2.d/S99bacula-dir - @rm -f /etc/rc.d/init.d/bacula-dir + @rm -f /etc/init.d/bacula-dir clean: @rm -f bacula-sd bacula-fd bacula-dir diff --git a/bacula/platforms/suse/Makefile.in b/bacula/platforms/suse/Makefile.in new file mode 100644 index 0000000000..a2dcad99d1 --- /dev/null +++ b/bacula/platforms/suse/Makefile.in @@ -0,0 +1,81 @@ +# +# This file is used as the template to create the +# Makefile for the SuSe specific installation. +# +# +# for Bacula release @VERSION@ (@DATE@) -- @DISTNAME@ +# + +INSTALL = @INSTALL@ +INSTALL_PROGRAM = @INSTALL_PROGRAM@ + +nothing: + +install: install-autostart + +install-autostart: install-autostart-fd install-autostart-sd install-autostart-dir + + +install-autostart-fd: + @if test x$(DESTDIR) = x -a -f /etc/rc.d/init.d/bacula-fd; then \ + /sbin/chkconfig --del bacula-fd; \ + fi + @$(INSTALL_PROGRAM) -m 744 bacula-fd $(DESTDIR)/etc/rc.d/init.d/bacula-fd + # set symlinks for script at startup and shutdown + @if test x$(DESTDIR) = x ; then \ + /sbin/chkconfig --add bacula-fd; \ + fi + + +install-autostart-sd: + @if test x$(DESTDIR) = x -a -f /etc/rc.d/init.d/bacula-sd; then \ + /sbin/chkconfig --del bacula-sd; \ + fi + @$(INSTALL_PROGRAM) -m 744 bacula-sd $(DESTDIR)/etc/rc.d/init.d/bacula-sd + # set symlinks for script at startup and shutdown + @if test x$(DESTDIR) = x ; then \ + /sbin/chkconfig --add bacula-sd; \ + fi + + +install-autostart-dir: + @if test x$(DESTDIR) = x -a -f /etc/rc.d/init.d/bacula-dir; then \ + /sbin/chkconfig --del bacula-dir; \ + fi + @$(INSTALL_PROGRAM) -m 744 bacula-dir $(DESTDIR)/etc/rc.d/init.d/bacula-dir + # set symlinks for script at startup and shutdown + @if test x$(DESTDIR) = x ; then \ + /sbin/chkconfig --add bacula-dir; \ + fi + + +uninstall: uninstall-autostart + +uninstall-autostart: uninstall-autostart-fd uninstall-autostart-sd uninstall-autostart-dir + +uninstall-autostart-fd: + @if test x$(DESTDIR) = x -a -f /etc/rc.d/init.d/bacula-fd; then \ + /sbin/chkconfig --del bacula-fd; \ + fi + @rm -f $(DESTDIR)/etc/rc.d/init.d/bacula-fd + + +uninstall-autostart-sd: + @if test x$(DESTDIR) = x -a -f /etc/rc.d/init.d/bacula-sd; then \ + /sbin/chkconfig --del bacula-sd; \ + fi + @rm -f $(DESTDIR)/etc/rc.d/init.d/bacula-sd + +uninstall-autostart-dir: + @if test x$(DESTDIR) = x -a -f /etc/rc.d/init.d/bacula-dir; then \ + /sbin/chkconfig --del bacula-dir; \ + fi + @rm -f $(DESTDIR)/etc/rc.d/init.d/bacula-dir + +clean: + @rm -f 1 2 3 + +distclean: clean + @rm -f Makefile bacula-*.spec bacula.*.spec bacula.spec + @rm -f bacula-sd bacula-fd bacula-dir + @rm -rf CVS diff --git a/bacula/platforms/suse/bacula-dir.in b/bacula/platforms/suse/bacula-dir.in new file mode 100755 index 0000000000..05fec1b712 --- /dev/null +++ b/bacula/platforms/suse/bacula-dir.in @@ -0,0 +1,44 @@ +#! /bin/sh +# +# bacula This shell script takes care of starting and stopping +# the bacula Director daemon +# +# chkconfig: 2345 90 99 +# description: It comes by night and sucks the vital essence from your computers. +# +# For Bacula release @VERSION@ (@DATE@) -- @DISTNAME@ +# + +# Source function library +. /etc/rc.d/init.d/functions + +RETVAL=0 +case "$1" in + start) + echo -n "Starting the Bacula Director: " + daemon @sbindir@/bacula-dir $2 -c @sysconfdir@/bacula-dir.conf + RETVAL=$? + echo + [ $RETVAL -eq 0 ] && touch @subsysdir@/bacula-dir + ;; + stop) + echo -n "Stopping the Director daemon: " + killproc @sbindir@/bacula-dir + RETVAL=$? + echo + [ $RETVAL -eq 0 ] && rm -f @subsysdir@/bacula-dir + ;; + restart) + $0 stop + sleep 5 + $0 start + ;; + status) + status @sbindir@/bacula-dir + ;; + *) + echo "Usage: $0 {start|stop|restart|status}" + exit 1 + ;; +esac +exit 0 diff --git a/bacula/platforms/suse/bacula-fd.in b/bacula/platforms/suse/bacula-fd.in new file mode 100755 index 0000000000..8b725bc722 --- /dev/null +++ b/bacula/platforms/suse/bacula-fd.in @@ -0,0 +1,43 @@ +#! /bin/sh +# +# bacula This shell script takes care of starting and stopping +# the bacula File daemon. +# +# chkconfig: 2345 90 99 +# description: It comes by night and sucks the vital essence from your computers. +# +# For Bacula release @VERSION@ (@DATE@) -- @DISTNAME@ +# + +# Source function library +. /etc/rc.d/init.d/functions + +case "$1" in + start) + echo -n "Starting the Bacula File daemon: " + daemon @sbindir@/bacula-fd $2 -c @sysconfdir@/bacula-fd.conf + RETVAL=$? + echo + [ $RETVAL -eq 0 ] && touch @subsysdir@/bacula-fd + ;; + stop) + echo -n "Stopping the Bacula File daemon: " + killproc @sbindir@/bacula-fd + RETVAL=$? + echo + [ $RETVAL -eq 0 ] && rm -f @subsysdir@/bacula-fd + ;; + restart) + $0 stop + sleep 5 + $0 start + ;; + status) + status @sbindir@/bacula-fd + ;; + *) + echo "Usage: $0 {start|stop|restart|status}" + exit 1 + ;; +esac +exit 0 diff --git a/bacula/platforms/suse/bacula-sd.in b/bacula/platforms/suse/bacula-sd.in new file mode 100755 index 0000000000..e58287625d --- /dev/null +++ b/bacula/platforms/suse/bacula-sd.in @@ -0,0 +1,43 @@ +#! /bin/sh +# +# bacula This shell script takes care of starting and stopping +# the bacula Storage daemon. +# +# chkconfig: 2345 90 99 +# description: It comes by night and sucks the vital essence from your computers. +# +# For Bacula release @VERSION@ (@DATE@) -- @DISTNAME@ +# + +# Source function library +. /etc/rc.d/init.d/functions + +case "$1" in + start) + echo -n "Starting the Bacula Storage daemon: " + daemon @sbindir@/bacula-sd $2 -c @sysconfdir@/bacula-sd.conf + RETVAL=$? + echo + [ $RETVAL -eq 0 ] && touch @subsysdir@/bacula-sd + ;; + stop) + echo -n "Stopping the Bacula Storage daemon: " + killproc @sbindir@/bacula-sd + RETVAL=$? + echo + [ $RETVAL -eq 0 ] && rm -f @subsysdir@/bacula-sd + ;; + restart) + $0 stop + sleep 5 + $0 start + ;; + status) + status @sbindir@/bacula-sd + ;; + *) + echo "Usage: $0 {start|stop|restart|status}" + exit 1 + ;; +esac +exit 0 diff --git a/bacula/scripts/bacula.in b/bacula/scripts/bacula.in index f530ba66a3..1fbd1a98ee 100755 --- a/bacula/scripts/bacula.in +++ b/bacula/scripts/bacula.in @@ -11,6 +11,22 @@ PSCMD="@PSCMD@" +BACBIN=@sbindir@ +BACCFG=@sysconfdir@ +PIDDIR=@piddir@ +SUBSYSDIR=@subsysdir@ + +DIR_PORT=9101 +FD_PORT=9102 +SD_PORT=9103 + +DIR_USER=@dir_user@ +DIR_GROUP=@dir_group@ +FD_USER=@fd_user@ +FD_GROUP=@fd_group@ +SD_USER=@sd_user@ +SD_GROUP=@sd_group@ + # A function to stop a program. killproc() { RC=0 @@ -69,7 +85,7 @@ killproc() { fi # Remove pid file if any. if [ "$notset" = "1" ]; then - rm -f @piddir@/$base.$2.pid + rm -f ${PIDDIR}/$base.$2.pid fi return $RC } @@ -87,8 +103,8 @@ pidofproc() { base=`basename $1` # First try PID file - if [ -f @piddir@/$base.$2.pid ] ; then - pid=`head -1 @piddir@/$base.$2.pid` + if [ -f ${PIDDIR}/$base.$2.pid ] ; then + pid=`head -1 ${PIDDIR}/$base.$2.pid` if [ "$pid" != "" ] ; then echo $pid return 0 @@ -138,15 +154,15 @@ status() { fi # Next try the PID files - if [ -f @piddir@/$base.$2.pid ] ; then - pid=`head -1 @piddir@/$base.$2.pid` + if [ -f ${PIDDIR}/$base.$2.pid ] ; then + pid=`head -1 ${PIDDIR}/$base.$2.pid` if [ "$pid" != "" ] ; then echo "$base dead but pid file exists" return 1 fi fi # See if the subsys lock exists - if [ -f @subsysdir@/$base ] ; then + if [ -f ${SUBSYSDIR}/$base ] ; then echo "$base dead but subsys locked" return 2 fi @@ -165,33 +181,81 @@ failure() { case "$1" in start) - echo "Starting the Storage daemon" - @sbindir@/bacula-sd $2 -v -c @sysconfdir@/bacula-sd.conf - echo "Starting the File daemon" - @sbindir@/bacula-fd $2 -v -c @sysconfdir@/bacula-fd.conf - sleep 2 - echo "Starting the Director daemon" - @sbindir@/bacula-dir $2 -v -c @sysconfdir@/bacula-dir.conf + [ -x ${BACBIN}/bacula-sd ] && { + echo "Starting the Storage daemon" + OPTIONS='' + if [ "${SD_USER}" != '' ]; then + OPTIONS="${OPTIONS} -u ${SD_USER}" + fi + + if [ "${SD_GROUP}" != '' ]; then + OPTIONS="${OPTIONS} -g ${SD_GROUP}" + fi + + ${BACBIN}/bacula-sd $2 ${OPTIONS} -v -c ${BACCFG}/bacula-sd.conf + } + + [ -x ${BACBIN}/bacula-fd ] && { + echo "Starting the File daemon" + OPTIONS='' + if [ "${FD_USER}" != '' ]; then + OPTIONS="${OPTIONS} -u ${FD_USER}" + fi + + if [ "${FD_GROUP}" != '' ]; then + OPTIONS="${OPTIONS} -g ${FD_GROUP}" + fi + + ${BACBIN}/bacula-fd $2 ${OPTIONS} -v -c ${BACCFG}/bacula-fd.conf + } + + [ -x ${BACBIN}/bacula-dir ] && { + sleep 2 + echo "Starting the Director daemon" + OPTIONS='' + if [ "${DIR_USER}" != '' ]; then + OPTIONS="${OPTIONS} -u ${DIR_USER}" + fi + + if [ "${DIR_GROUP}" != '' ]; then + OPTIONS="${OPTIONS} -g ${DIR_GROUP}" + fi + + ${BACBIN}/bacula-dir $2 ${OPTIONS} -v -c ${BACCFG}/bacula-dir.conf + } ;; + stop) - echo "Stopping the File daemon" - killproc @sbindir@/bacula-fd @fd_port@ - echo "Stopping the Storage daemon" - killproc @sbindir@/bacula-sd @sd_port@ - echo "Stopping the Director daemon" - killproc @sbindir@/bacula-dir @dir_port@ + # Stop the FD first so that SD will fail jobs and update catalog + [ -x ${BACBIN}/bacula-fd ] && { + echo "Stopping the File daemon" + killproc ${BACBIN}/bacula-fd ${FD_PORT} + } + + [ -x ${BACBIN}/bacula-sd ] && { + echo "Stopping the Storage daemon" + killproc ${BACBIN}/bacula-sd ${SD_PORT} + } + + [ -x ${BACBIN}/bacula-dir ] && { + echo "Stopping the Director daemon" + killproc ${BACBIN}/bacula-dir ${DIR_PORT} + } echo ;; + restart) $0 stop sleep 5 $0 start ;; + status) - status @sbindir@/bacula-sd @sd_port@ - status @sbindir@/bacula-fd @fd_port@ - status @sbindir@/bacula-dir @dir_port@ + [ -x ${BACBIN}/bacula-sd ] && status ${BACBIN}/bacula-sd ${SD_PORT} + [ -x ${BACBIN}/bacula-fd ] && status ${BACBIN}/bacula-fd ${FD_PORT} + [ -x ${BACBIN}/bacula-dir ] && status ${BACBIN}/bacula-dir ${DIR_PORT} ;; + *) echo "Usage: $0 {start|stop|restart|status}" exit 1 diff --git a/bacula/scripts/set-gnome1.4 b/bacula/scripts/set-gnome1.4 new file mode 100755 index 0000000000..d76e5a2a76 --- /dev/null +++ b/bacula/scripts/set-gnome1.4 @@ -0,0 +1,18 @@ +#!/bin/sh +# +# Simple script to change the package configuration to +# Gnome 1.4 level. +# +MUID=`/usr/bin/id -u` +if [ $MUID != 0 ] ; then + echo " " + echo "You must be root to run this script." + echo " " + exit 1 +fi +cd /usr/lib/pkgconfig +if test -f libgnomeui-2.0.pc.orig ; then + rm -f libgnomeui-2.0.pc +else + mv libgnomeui-2.0.pc libgnomeui-2.0.pc.orig +fi diff --git a/bacula/scripts/set-gnome2 b/bacula/scripts/set-gnome2 new file mode 100755 index 0000000000..414499dd7f --- /dev/null +++ b/bacula/scripts/set-gnome2 @@ -0,0 +1,16 @@ +#!/bin/sh +# +# Simple script to set the Gnome package level +# to Gnome 2.0 +# +MUID=`/usr/bin/id -u` +if [ $MUID != 0 ] ; then + echo " " + echo "You must be root to run this script." + echo " " + exit 1 +fi +cd /usr/lib/pkgconfig +if test -f libgnomeui-2.0.pc.orig ; then + cp -fp libgnomeui-2.0.pc.orig libgnomeui-2.0.pc +fi diff --git a/bacula/src/.cvsignore b/bacula/src/.cvsignore index 018076a9be..a9c61f762d 100644 --- a/bacula/src/.cvsignore +++ b/bacula/src/.cvsignore @@ -5,3 +5,4 @@ Makefile config.h testprogs host.h +perlgui diff --git a/bacula/src/Makefile.in b/bacula/src/Makefile.in index 1f32ee5812..8b67e61486 100644 --- a/bacula/src/Makefile.in +++ b/bacula/src/Makefile.in @@ -40,6 +40,8 @@ Makefile: $(srcdir)/Makefile.in $(topdir)/config.status clean: @$(RMF) core core.* a.out *.o *.bak *~ *.intpro *.extpro 1 2 3 + (cd gnome-console; $(MAKE) clean) + (cd gnome2-console; $(MAKE) clean) realclean: clean @$(RMF) tags diff --git a/bacula/src/baconfig.h b/bacula/src/baconfig.h index 123f0128ef..b9c6cd9719 100644 --- a/bacula/src/baconfig.h +++ b/bacula/src/baconfig.h @@ -434,7 +434,7 @@ extern int thr_setconcurrency(int); #ifdef HAVE_DARWIN_OS /* Apparently someone forgot to wrap getdomainname as a C function */ -extern "C" int getdomainname(char *name, size_t len); +extern "C" int getdomainname(char *name, int len); /* Darwin lib fnmatch() doesn't work, so use our own */ #undef HAVE_FNMATCH diff --git a/bacula/src/bc_types.h b/bacula/src/bc_types.h index a8a4a4bed0..a27c3270df 100644 --- a/bacula/src/bc_types.h +++ b/bacula/src/bc_types.h @@ -181,7 +181,7 @@ typedef float float32_t; #define uintmax_t u_intmax_t /* Bacula time -- Unix time with microseconds */ -#define btime_t uint64_t +#define btime_t int64_t /* Unix time (time_t) widened to 64 bits */ #define utime_t int64_t diff --git a/bacula/src/cats/Makefile.in b/bacula/src/cats/Makefile.in index e4ee4d48af..a50533f635 100644 --- a/bacula/src/cats/Makefile.in +++ b/bacula/src/cats/Makefile.in @@ -65,7 +65,9 @@ realclean: clean $(RMF) create_bdb_database drop_bdb_tables make_dbd_tables $(RMF) make_catalog_backup delete_catalog_backup $(RMF) alter_mysql_tables alter_sqlite_tables create_sqlite_database + $(RMF) update_mysql_tables update_sqlite_tables $(RMF) drop_bacula_tables drop_sqlite_tables make_bacula_tables + $(RMF) update_bacula_tables $(RMF) drop_bdb_tables make_bdb_tables mysql distclean: realclean diff --git a/bacula/src/cats/bdb_find.c b/bacula/src/cats/bdb_find.c index 8526946474..c184ec45c3 100644 --- a/bacula/src/cats/bdb_find.c +++ b/bacula/src/cats/bdb_find.c @@ -141,10 +141,13 @@ StartTime=%100s", &JobId, Name, cType, cLevel, StartTime) == 5) { * * Find a Volume for a given PoolId, MediaType, and VolStatus * + * Note! this does not correctly implement InChanger. + * * Returns: 0 on failure * numrows on success - */ -int db_find_next_volume(JCR *jcr, B_DB *mdb, int item, MEDIA_DBR *mr) + */ +int +db_find_next_volume(JCR *jcr, B_DB *mdb, int item, MEDIA_DBR *mr) { MEDIA_DBR omr; int stat = 0; @@ -176,7 +179,9 @@ int db_find_next_volume(JCR *jcr, B_DB *mdb, int item, MEDIA_DBR *mr) return stat; } -int db_find_last_jobid(JCR *jcr, B_DB *mdb, JOB_DBR *jr) { return 0; } +int +db_find_last_jobid(JCR *jcr, B_DB *mdb, char *Name, JOB_DBR *jr) +{ return 0; } #endif /* HAVE_BACULA_DB */ diff --git a/bacula/src/cats/bdb_get.c b/bacula/src/cats/bdb_get.c index e152c227ba..cbabc7a3aa 100644 --- a/bacula/src/cats/bdb_get.c +++ b/bacula/src/cats/bdb_get.c @@ -492,7 +492,7 @@ int db_get_fileset_record(JCR *jcr, B_DB *mdb, FILESET_DBR *fsr) -int db_get_file_attributes_record(JCR *jcr, B_DB *mdb, char *fname, FILE_DBR *fdbr) +int db_get_file_attributes_record(JCR *jcr, B_DB *mdb, char *fname, JOB_DBR *jr, FILE_DBR *fdbr) { return 0; } int db_get_job_volume_parameters(JCR *jcr, B_DB *mdb, uint32_t JobId, VOL_PARAMS **VolParams) diff --git a/bacula/src/cats/cats.h b/bacula/src/cats/cats.h index f4746db3c8..2cacbcdaa4 100644 --- a/bacula/src/cats/cats.h +++ b/bacula/src/cats/cats.h @@ -57,70 +57,70 @@ struct sqlite { char dummy; }; -#define IS_NUM(x) ((x) == 1) -#define IS_NOT_NULL(x) ((x) == 1) +#define IS_NUM(x) ((x) == 1) +#define IS_NOT_NULL(x) ((x) == 1) typedef struct s_sql_field { - char *name; /* name of column */ - uint32_t length; /* length */ - uint32_t max_length; /* max length */ - uint32_t type; /* type */ - uint32_t flags; /* flags */ + char *name; /* name of column */ + uint32_t length; /* length */ + uint32_t max_length; /* max length */ + uint32_t type; /* type */ + uint32_t flags; /* flags */ } SQL_FIELD; /* * This is the "real" definition that should only be * used inside sql.c and associated database interface * subroutines. - * S Q L I T E + * S Q L I T E */ typedef struct s_db { - BQUEUE bq; /* queue control */ - brwlock_t lock; /* transaction lock */ + BQUEUE bq; /* queue control */ + brwlock_t lock; /* transaction lock */ struct sqlite *db; char **result; - int nrow; /* nrow returned from sqlite */ - int ncolumn; /* ncolum returned from sqlite */ - int num_rows; /* used by code */ - int row; /* seek row */ - int have_insert_id; /* do not have insert id */ - int fields_defined; /* set when fields defined */ - int field; /* seek field */ - SQL_FIELD **fields; /* defined fields */ + int nrow; /* nrow returned from sqlite */ + int ncolumn; /* ncolum returned from sqlite */ + int num_rows; /* used by code */ + int row; /* seek row */ + int have_insert_id; /* do not have insert id */ + int fields_defined; /* set when fields defined */ + int field; /* seek field */ + SQL_FIELD **fields; /* defined fields */ int ref_count; char *db_name; char *db_user; - char *db_address; /* host name address */ - char *db_socket; /* socket for local access */ + char *db_address; /* host name address */ + char *db_socket; /* socket for local access */ char *db_password; - int db_port; /* port for host name address */ + int db_port; /* port for host name address */ int connected; - char *sqlite_errmsg; /* error message returned by sqlite */ - POOLMEM *errmsg; /* nicely edited error message */ - POOLMEM *cmd; /* SQL command string */ - POOLMEM *cached_path; /* cached path name */ - int cached_path_len; /* length of cached path */ - uint32_t cached_path_id; /* cached path id */ - int transaction; /* transaction started */ - int changes; /* changes during transaction */ - POOLMEM *fname; /* Filename only */ - POOLMEM *path; /* Path only */ - POOLMEM *esc_name; /* Escaped file/path name */ - int fnl; /* file name length */ - int pnl; /* path name length */ + char *sqlite_errmsg; /* error message returned by sqlite */ + POOLMEM *errmsg; /* nicely edited error message */ + POOLMEM *cmd; /* SQL command string */ + POOLMEM *cached_path; /* cached path name */ + int cached_path_len; /* length of cached path */ + uint32_t cached_path_id; /* cached path id */ + int transaction; /* transaction started */ + int changes; /* changes during transaction */ + POOLMEM *fname; /* Filename only */ + POOLMEM *path; /* Path only */ + POOLMEM *esc_name; /* Escaped file/path name */ + int fnl; /* file name length */ + int pnl; /* path name length */ } B_DB; /* * "Generic" names for easier conversion * - * S Q L I T E + * S Q L I T E */ #define sql_store_result(x) x->result #define sql_free_result(x) my_sqlite_free_table(x) #define sql_fetch_row(x) my_sqlite_fetch_row(x) #define sql_query(x, y) my_sqlite_query(x, y) -#define sql_close(x) sqlite_close((x)->db) +#define sql_close(x) sqlite_close((x)->db) #define sql_strerror(x) (x)->sqlite_errmsg?(x)->sqlite_errmsg:"unknown" #define sql_num_rows(x) (x)->nrow #define sql_data_seek(x, i) (x)->row = i @@ -129,7 +129,7 @@ typedef struct s_db { #define sql_field_seek(x, y) my_sqlite_field_seek(x, y) #define sql_fetch_field(x) my_sqlite_fetch_field(x) #define sql_num_fields(x) (unsigned)((x)->ncolumn) -#define SQL_ROW char** +#define SQL_ROW char** @@ -151,11 +151,11 @@ extern void my_sqlite_free_table(B_DB *mdb); * used inside sql.c and associated database interface * subroutines. * - * M Y S Q L + * M Y S Q L */ typedef struct s_db { - BQUEUE bq; /* queue control */ - brwlock_t lock; /* transaction lock */ + BQUEUE bq; /* queue control */ + brwlock_t lock; /* transaction lock */ MYSQL mysql; MYSQL *db; MYSQL_RES *result; @@ -164,22 +164,22 @@ typedef struct s_db { char *db_name; char *db_user; char *db_password; - char *db_address; /* host address */ - char *db_socket; /* socket for local access */ - int db_port; /* port of host address */ - int have_insert_id; /* do have insert_id() */ + char *db_address; /* host address */ + char *db_socket; /* socket for local access */ + int db_port; /* port of host address */ + int have_insert_id; /* do have insert_id() */ int connected; - POOLMEM *errmsg; /* nicely edited error message */ - POOLMEM *cmd; /* SQL command string */ + POOLMEM *errmsg; /* nicely edited error message */ + POOLMEM *cmd; /* SQL command string */ POOLMEM *cached_path; - int cached_path_len; /* length of cached path */ + int cached_path_len; /* length of cached path */ uint32_t cached_path_id; - int changes; /* changes made to db */ - POOLMEM *fname; /* Filename only */ - POOLMEM *path; /* Path only */ - POOLMEM *esc_name; /* Escaped file/path name */ - int fnl; /* file name length */ - int pnl; /* path name length */ + int changes; /* changes made to db */ + POOLMEM *fname; /* Filename only */ + POOLMEM *path; /* Path only */ + POOLMEM *esc_name; /* Escaped file/path name */ + int fnl; /* file name length */ + int pnl; /* path name length */ } B_DB; @@ -188,7 +188,7 @@ typedef struct s_db { #define sql_free_result(x) mysql_free_result((x)->result) #define sql_fetch_row(x) mysql_fetch_row((x)->result) #define sql_query(x, y) mysql_query((x)->db, y) -#define sql_close(x) mysql_close((x)->db) +#define sql_close(x) mysql_close((x)->db) #define sql_strerror(x) mysql_error((x)->db) #define sql_num_rows(x) mysql_num_rows((x)->result) #define sql_data_seek(x, i) mysql_data_seek((x)->result, i) @@ -197,8 +197,8 @@ typedef struct s_db { #define sql_field_seek(x, y) mysql_field_seek((x)->result, y) #define sql_fetch_field(x) mysql_fetch_field((x)->result) #define sql_num_fields(x) mysql_num_fields((x)->result) -#define SQL_ROW MYSQL_ROW -#define SQL_FIELD MYSQL_FIELD +#define SQL_ROW MYSQL_ROW +#define SQL_FIELD MYSQL_FIELD #else /* USE BACULA DB routines */ @@ -207,17 +207,17 @@ typedef struct s_db { /* Change this each time there is some incompatible * file format change!!!! */ -#define BDB_VERSION 12 /* file version number */ +#define BDB_VERSION 12 /* file version number */ struct s_control { - int bdb_version; /* Version number */ - uint32_t JobId; /* next Job Id */ - uint32_t PoolId; /* next Pool Id */ - uint32_t MediaId; /* next Media Id */ - uint32_t JobMediaId; /* next JobMedia Id */ - uint32_t ClientId; /* next Client Id */ - uint32_t FileSetId; /* nest FileSet Id */ - time_t time; /* time file written */ + int bdb_version; /* Version number */ + uint32_t JobId; /* next Job Id */ + uint32_t PoolId; /* next Pool Id */ + uint32_t MediaId; /* next Media Id */ + uint32_t JobMediaId; /* next JobMedia Id */ + uint32_t ClientId; /* next Client Id */ + uint32_t FileSetId; /* nest FileSet Id */ + time_t time; /* time file written */ }; @@ -225,23 +225,23 @@ struct s_control { * Bacula internal DB */ typedef struct s_db { - BQUEUE bq; /* queue control */ -/* pthread_mutex_t mutex; */ /* single thread lock */ - brwlock_t lock; /* transaction lock */ - int ref_count; /* number of times opened */ - struct s_control control; /* control file structure */ - int cfd; /* control file device */ - FILE *jobfd; /* Jobs records file descriptor */ - FILE *poolfd; /* Pool records fd */ - FILE *mediafd; /* Media records fd */ - FILE *jobmediafd; /* JobMedia records fd */ - FILE *clientfd; /* Client records fd */ - FILE *filesetfd; /* FileSet records fd */ - char *db_name; /* name of database */ - POOLMEM *errmsg; /* nicely edited error message */ - POOLMEM *cmd; /* Command string */ + BQUEUE bq; /* queue control */ +/* pthread_mutex_t mutex; */ /* single thread lock */ + brwlock_t lock; /* transaction lock */ + int ref_count; /* number of times opened */ + struct s_control control; /* control file structure */ + int cfd; /* control file device */ + FILE *jobfd; /* Jobs records file descriptor */ + FILE *poolfd; /* Pool records fd */ + FILE *mediafd; /* Media records fd */ + FILE *jobmediafd; /* JobMedia records fd */ + FILE *clientfd; /* Client records fd */ + FILE *filesetfd; /* FileSet records fd */ + char *db_name; /* name of database */ + POOLMEM *errmsg; /* nicely edited error message */ + POOLMEM *cmd; /* Command string */ POOLMEM *cached_path; - int cached_path_len; /* length of cached path */ + int cached_path_len; /* length of cached path */ uint32_t cached_path_id; } B_DB; @@ -255,12 +255,12 @@ typedef struct s_db { #define DELETE_DB(jcr, db, cmd) DeleteDB(__FILE__, __LINE__, jcr, db, cmd) -#else /* not __SQL_C */ +#else /* not __SQL_C */ /* This is a "dummy" definition for use outside of sql.c */ -typedef struct s_db { - int dummy; /* for SunOS compiler */ +typedef struct s_db { + int dummy; /* for SunOS compiler */ } B_DB; #endif /* __SQL_C */ @@ -269,7 +269,7 @@ extern uint32_t bacula_db_version; /* ***FIXME*** FileId_t should be uint64_t */ typedef uint32_t FileId_t; -typedef uint32_t DBId_t; /* general DB id type */ +typedef uint32_t DBId_t; /* general DB id type */ typedef uint32_t JobId_t; #define faddr_t long @@ -283,18 +283,18 @@ typedef uint32_t JobId_t; /* Job record */ struct JOB_DBR { JobId_t JobId; - char Job[MAX_NAME_LENGTH]; /* Job unique name */ - char Name[MAX_NAME_LENGTH]; /* Job base name */ - int Type; /* actually char(1) */ - int Level; /* actually char(1) */ - int JobStatus; /* actually char(1) */ - uint32_t ClientId; /* Id of client */ - uint32_t PoolId; /* Id of pool */ - uint32_t FileSetId; /* Id of FileSet */ - time_t SchedTime; /* Time job scheduled */ - time_t StartTime; /* Job start time */ - time_t EndTime; /* Job termination time */ - utime_t JobTDate; /* Backup time/date in seconds */ + char Job[MAX_NAME_LENGTH]; /* Job unique name */ + char Name[MAX_NAME_LENGTH]; /* Job base name */ + int Type; /* actually char(1) */ + int Level; /* actually char(1) */ + int JobStatus; /* actually char(1) */ + uint32_t ClientId; /* Id of client */ + uint32_t PoolId; /* Id of pool */ + uint32_t FileSetId; /* Id of FileSet */ + time_t SchedTime; /* Time job scheduled */ + time_t StartTime; /* Job start time */ + time_t EndTime; /* Job termination time */ + utime_t JobTDate; /* Backup time/date in seconds */ uint32_t VolSessionId; uint32_t VolSessionTime; uint32_t JobFiles; @@ -305,8 +305,8 @@ struct JOB_DBR { /* Note, FirstIndex, LastIndex, Start/End File and Block * are only used in the JobMedia record. */ - uint32_t FirstIndex; /* First index this Volume */ - uint32_t LastIndex; /* Last index this Volume */ + uint32_t FirstIndex; /* First index this Volume */ + uint32_t LastIndex; /* Last index this Volume */ uint32_t StartFile; uint32_t EndFile; uint32_t StartBlock; @@ -324,28 +324,28 @@ struct JOB_DBR { */ /* JobMedia record */ struct JOBMEDIA_DBR { - uint32_t JobMediaId; /* record id */ - JobId_t JobId; /* JobId */ - uint32_t MediaId; /* MediaId */ - uint32_t FirstIndex; /* First index this Volume */ - uint32_t LastIndex; /* Last index this Volume */ - uint32_t StartFile; /* File for start of data */ - uint32_t EndFile; /* End file on Volume */ - uint32_t StartBlock; /* start block on tape */ - uint32_t EndBlock; /* last block */ + uint32_t JobMediaId; /* record id */ + JobId_t JobId; /* JobId */ + uint32_t MediaId; /* MediaId */ + uint32_t FirstIndex; /* First index this Volume */ + uint32_t LastIndex; /* Last index this Volume */ + uint32_t StartFile; /* File for start of data */ + uint32_t EndFile; /* End file on Volume */ + uint32_t StartBlock; /* start block on tape */ + uint32_t EndBlock; /* last block */ }; /* Volume Parameter structure */ struct VOL_PARAMS { char VolumeName[MAX_NAME_LENGTH]; /* Volume name */ - uint32_t VolIndex; /* Volume seqence no. */ - uint32_t FirstIndex; /* First index this Volume */ - uint32_t LastIndex; /* Last index this Volume */ - uint32_t StartFile; /* File for start of data */ - uint32_t EndFile; /* End file on Volume */ - uint32_t StartBlock; /* start block on tape */ - uint32_t EndBlock; /* last block */ + uint32_t VolIndex; /* Volume seqence no. */ + uint32_t FirstIndex; /* First index this Volume */ + uint32_t LastIndex; /* Last index this Volume */ + uint32_t StartFile; /* File for start of data */ + uint32_t EndFile; /* End file on Volume */ + uint32_t StartBlock; /* start block on tape */ + uint32_t EndBlock; /* last block */ }; @@ -354,9 +354,9 @@ struct VOL_PARAMS { * records (e.g. pathname, filename, fileattributes). */ struct ATTR_DBR { - char *fname; /* full path & filename */ - char *link; /* link if any */ - char *attr; /* attributes statp */ + char *fname; /* full path & filename */ + char *link; /* link if any */ + char *attr; /* attributes statp */ uint32_t FileIndex; uint32_t Stream; JobId_t JobId; @@ -378,26 +378,26 @@ struct FILE_DBR { char LStat[256]; /* int Status; */ char SIG[50]; - int SigType; /* NO_SIG/MD5_SIG/SHA1_SIG */ + int SigType; /* NO_SIG/MD5_SIG/SHA1_SIG */ }; /* Pool record -- same format as database */ struct POOL_DBR { uint32_t PoolId; - char Name[MAX_NAME_LENGTH]; /* Pool name */ - uint32_t NumVols; /* total number of volumes */ - uint32_t MaxVols; /* max allowed volumes */ - int UseOnce; /* set to use once only */ - int UseCatalog; /* set to use catalog */ - int AcceptAnyVolume; /* set to accept any volume sequence */ - int AutoPrune; /* set to prune automatically */ - int Recycle; /* default Vol recycle flag */ - utime_t VolRetention; /* retention period in seconds */ - utime_t VolUseDuration; /* time in secs volume can be used */ - uint32_t MaxVolJobs; /* Max Jobs on Volume */ - uint32_t MaxVolFiles; /* Max files on Volume */ - uint64_t MaxVolBytes; /* Max bytes on Volume */ - char PoolType[MAX_NAME_LENGTH]; + char Name[MAX_NAME_LENGTH]; /* Pool name */ + uint32_t NumVols; /* total number of volumes */ + uint32_t MaxVols; /* max allowed volumes */ + int32_t UseOnce; /* set to use once only */ + int32_t UseCatalog; /* set to use catalog */ + int32_t AcceptAnyVolume; /* set to accept any volume sequence */ + int32_t AutoPrune; /* set to prune automatically */ + int32_t Recycle; /* default Vol recycle flag */ + utime_t VolRetention; /* retention period in seconds */ + utime_t VolUseDuration; /* time in secs volume can be used */ + uint32_t MaxVolJobs; /* Max Jobs on Volume */ + uint32_t MaxVolFiles; /* Max files on Volume */ + uint64_t MaxVolBytes; /* Max bytes on Volume */ + char PoolType[MAX_NAME_LENGTH]; char LabelFormat[MAX_NAME_LENGTH]; /* Extra stuff not in DB */ faddr_t rec_addr; @@ -405,34 +405,37 @@ struct POOL_DBR { /* Media record -- same as the database */ struct MEDIA_DBR { - uint32_t MediaId; /* Unique volume id */ + uint32_t MediaId; /* Unique volume id */ char VolumeName[MAX_NAME_LENGTH]; /* Volume name */ char MediaType[MAX_NAME_LENGTH]; /* Media type */ - uint32_t PoolId; /* Pool id */ - time_t FirstWritten; /* Time Volume first written */ - time_t LastWritten; /* Time Volume last written */ - time_t LabelDate; /* Date/Time Volume labeled */ - uint32_t VolJobs; /* number of jobs on this medium */ - uint32_t VolFiles; /* Number of files */ - uint32_t VolBlocks; /* Number of blocks */ - uint32_t VolMounts; /* Number of times mounted */ - uint32_t VolErrors; /* Number of read/write errors */ - uint32_t VolWrites; /* Number of writes */ - uint32_t VolReads; /* Number of reads */ - uint64_t VolBytes; /* Number of bytes written */ - uint64_t MaxVolBytes; /* Max bytes to write to Volume */ - uint64_t VolCapacityBytes; /* capacity estimate */ - utime_t VolRetention; /* Volume retention in seconds */ - utime_t VolUseDuration; /* time in secs volume can be used */ - uint32_t MaxVolJobs; /* Max Jobs on Volume */ - uint32_t MaxVolFiles; /* Max files on Volume */ - int Recycle; /* recycle yes/no */ - int32_t Slot; /* slot in changer */ - char VolStatus[20]; /* Volume status */ + uint32_t PoolId; /* Pool id */ + time_t FirstWritten; /* Time Volume first written */ + time_t LastWritten; /* Time Volume last written */ + time_t LabelDate; /* Date/Time Volume labeled */ + uint32_t VolJobs; /* number of jobs on this medium */ + uint32_t VolFiles; /* Number of files */ + uint32_t VolBlocks; /* Number of blocks */ + uint32_t VolMounts; /* Number of times mounted */ + uint32_t VolErrors; /* Number of read/write errors */ + uint32_t VolWrites; /* Number of writes */ + uint32_t VolReads; /* Number of reads */ + uint64_t VolBytes; /* Number of bytes written */ + uint64_t MaxVolBytes; /* Max bytes to write to Volume */ + uint64_t VolCapacityBytes; /* capacity estimate */ + utime_t VolRetention; /* Volume retention in seconds */ + utime_t VolUseDuration; /* time in secs volume can be used */ + uint32_t MaxVolJobs; /* Max Jobs on Volume */ + uint32_t MaxVolFiles; /* Max files on Volume */ + int32_t Recycle; /* recycle yes/no */ + int32_t Slot; /* slot in changer */ + int32_t Drive; /* drive in changer */ + int32_t InChanger; /* Volume currently in changer */ + int32_t MediaAddressing; /* Type of media addressing file/tape */ + char VolStatus[20]; /* Volume status */ /* Extra stuff not in DB */ - faddr_t rec_addr; /* found record address */ + faddr_t rec_addr; /* found record address */ /* Since the database returns times as strings, this is how we pass - * them back. + * them back. */ char cFirstWritten[MAX_TIME_LENGTH]; /* FirstWritten returned from DB */ char cLastWritten[MAX_TIME_LENGTH]; /* LastWritten returned from DB */ @@ -441,12 +444,12 @@ struct MEDIA_DBR { /* Client record -- same as the database */ struct CLIENT_DBR { - uint32_t ClientId; /* Unique Client id */ + uint32_t ClientId; /* Unique Client id */ int AutoPrune; utime_t FileRetention; utime_t JobRetention; - char Name[MAX_NAME_LENGTH]; /* Client name */ - char Uname[256]; /* Uname for client */ + char Name[MAX_NAME_LENGTH]; /* Client name */ + char Uname[256]; /* Uname for client */ }; /* Counter record as in database */ @@ -461,16 +464,16 @@ struct COUNTER_DBR { /* FileSet record -- same as the database */ struct FILESET_DBR { - uint32_t FileSetId; /* Unique FileSet id */ + uint32_t FileSetId; /* Unique FileSet id */ char FileSet[MAX_NAME_LENGTH]; /* FileSet name */ - char MD5[50]; /* MD5 signature of include/exclude */ - time_t CreateTime; /* date created */ + char MD5[50]; /* MD5 signature of include/exclude */ + time_t CreateTime; /* date created */ /* * This is where we return CreateTime */ char cCreateTime[MAX_TIME_LENGTH]; /* CreateTime as returned from DB */ /* Not in DB but returned by db_create_fileset() */ - bool created; /* set when record newly created */ + bool created; /* set when record newly created */ }; diff --git a/bacula/src/cats/make_catalog_backup.in b/bacula/src/cats/make_catalog_backup.in index 94115576b2..fe9c774fc3 100755 --- a/bacula/src/cats/make_catalog_backup.in +++ b/bacula/src/cats/make_catalog_backup.in @@ -6,9 +6,9 @@ cd @working_dir@ rm -f bacula.sql if test xsqlite = x@DB_NAME@ ; then -echo ".dump" | @SQL_BINDIR@/sqlite bacula.db >bacula.sql + echo ".dump" | @SQL_BINDIR@/sqlite bacula.db >bacula.sql else -@SQL_BINDIR@/mysqldump $* -f --opt bacula >bacula.sql + @SQL_BINDIR@/mysqldump $* -f --opt bacula >bacula.sql fi # # To read back a MySQL database use: diff --git a/bacula/src/cats/make_catalog_backup.in.patch b/bacula/src/cats/make_catalog_backup.in.patch new file mode 100644 index 0000000000..14d3adc16f --- /dev/null +++ b/bacula/src/cats/make_catalog_backup.in.patch @@ -0,0 +1,16 @@ +9c9 +< echo ".dump" | @SQL_BINDIR@/sqlite bacula.db >bacula.sql +--- +> echo ".dump" | /usr/lib/sqlite/sqlite bacula.db >bacula.sql +11c11 +< @SQL_BINDIR@/mysqldump $* -f --opt bacula >bacula.sql +--- +> /usr/bin/mysqldump $* -f --opt bacula >bacula.sql +16c16 +< # rm -f @SQL_BINDIR@/../var/bacula/* +--- +> # rm -f /var/lib/mysql/bacula/* +22c22 +< # sqlite bacula.db # /usr/lib/sqlite/sqlite bacula.db connected = TRUE; V(mutex); @@ -188,7 +190,9 @@ db_close_database(JCR *jcr, B_DB *mdb) { P(mutex); mdb->ref_count--; +#ifdef HAVE_TREAD_SAFE_MYSQL my_thread_end(); +#endif if (mdb->ref_count == 0) { qdchain(&mdb->bq); if (mdb->connected && mdb->db) { diff --git a/bacula/src/cats/protos.h b/bacula/src/cats/protos.h index 6ce3efee0c..ab233e474a 100644 --- a/bacula/src/cats/protos.h +++ b/bacula/src/cats/protos.h @@ -61,7 +61,7 @@ int db_delete_media_record(JCR *jcr, B_DB *mdb, MEDIA_DBR *mr); /* find.c */ int db_find_job_start_time(JCR *jcr, B_DB *mdb, JOB_DBR *jr, POOLMEM **stime); -int db_find_last_jobid(JCR *jcr, B_DB *mdb, JOB_DBR *jr); +int db_find_last_jobid(JCR *jcr, B_DB *mdb, char *Name, JOB_DBR *jr); int db_find_next_volume(JCR *jcr, B_DB *mdb, int index, MEDIA_DBR *mr); /* get.c */ @@ -69,7 +69,7 @@ int db_get_pool_record(JCR *jcr, B_DB *db, POOL_DBR *pdbr); int db_get_client_record(JCR *jcr, B_DB *mdb, CLIENT_DBR *cr); int db_get_job_record(JCR *jcr, B_DB *mdb, JOB_DBR *jr); int db_get_job_volume_names(JCR *jcr, B_DB *mdb, uint32_t JobId, POOLMEM **VolumeNames); -int db_get_file_attributes_record(JCR *jcr, B_DB *mdb, char *fname, FILE_DBR *fdbr); +int db_get_file_attributes_record(JCR *jcr, B_DB *mdb, char *fname, JOB_DBR *jr, FILE_DBR *fdbr); int db_get_fileset_record(JCR *jcr, B_DB *mdb, FILESET_DBR *fsr); int db_get_media_record(JCR *jcr, B_DB *mdb, MEDIA_DBR *mr); int db_get_num_media_records(JCR *jcr, B_DB *mdb); diff --git a/bacula/src/cats/sql_delete.c b/bacula/src/cats/sql_delete.c index 45de5dc65c..5a0790804e 100644 --- a/bacula/src/cats/sql_delete.c +++ b/bacula/src/cats/sql_delete.c @@ -153,7 +153,7 @@ static int delete_handler(void *ctx, int num_fields, char **row) */ static int do_media_purge(B_DB *mdb, MEDIA_DBR *mr) { - char *query = (char *)get_pool_memory(PM_MESSAGE); + POOLMEM *query = get_pool_memory(PM_MESSAGE); struct s_del_ctx del; int i; @@ -173,11 +173,11 @@ static int do_media_purge(B_DB *mdb, MEDIA_DBR *mr) for (i=0; i < del.num_ids; i++) { Dmsg1(400, "Delete JobId=%d\n", del.JobId[i]); - Mmsg(&query, "DELETE FROM Job WHERE JobId=%d", del.JobId[i]); + Mmsg(&query, "DELETE FROM Job WHERE JobId=%u", del.JobId[i]); db_sql_query(mdb, query, NULL, (void *)NULL); - Mmsg(&query, "DELETE FROM File WHERE JobId=%d", del.JobId[i]); + Mmsg(&query, "DELETE FROM File WHERE JobId=%u", del.JobId[i]); db_sql_query(mdb, query, NULL, (void *)NULL); - Mmsg(&query, "DELETE FROM JobMedia WHERE JobId=%d", del.JobId[i]); + Mmsg(&query, "DELETE FROM JobMedia WHERE JobId=%u", del.JobId[i]); db_sql_query(mdb, query, NULL, (void *)NULL); } free(del.JobId); diff --git a/bacula/src/cats/sql_find.c b/bacula/src/cats/sql_find.c index c0379235f8..815136e76e 100644 --- a/bacula/src/cats/sql_find.c +++ b/bacula/src/cats/sql_find.c @@ -53,8 +53,10 @@ extern void print_result(B_DB *mdb); extern int QueryDB(char *file, int line, JCR *jcr, B_DB *db, char *select_cmd); /* - * Find job start time. Used to find last full save - * for Incremental and Differential saves. + * Find job start time if JobId specified, otherwise + * find last full save for Incremental and Differential saves. + * + * StartTime is returned in stime * * Returns: 0 on failure * 1 on success, jr is unchanged, but stime is set @@ -63,7 +65,6 @@ int db_find_job_start_time(JCR *jcr, B_DB *mdb, JOB_DBR *jr, POOLMEM **stime) { SQL_ROW row; - uint32_t JobId; db_lock(mdb); @@ -71,26 +72,23 @@ db_find_job_start_time(JCR *jcr, B_DB *mdb, JOB_DBR *jr, POOLMEM **stime) /* If no Id given, we must find corresponding job */ if (jr->JobId == 0) { /* Differential is since last Full backup */ - if (jr->Level == L_DIFFERENTIAL) { Mmsg(&mdb->cmd, -"SELECT JobId FROM Job WHERE JobStatus='T' AND Type='%c' AND " +"SELECT StartTime FROM Job WHERE JobStatus='T' AND Type='%c' AND " "Level='%c' AND Name='%s' AND ClientId=%u AND FileSetId=%u " "ORDER BY StartTime DESC LIMIT 1", jr->Type, L_FULL, jr->Name, jr->ClientId, jr->FileSetId); + + if (jr->Level == L_DIFFERENTIAL) { + /* SQL cmd for Differential backup already edited above */ + /* Incremental is since last Full, Incremental, or Differential */ } else if (jr->Level == L_INCREMENTAL) { - Mmsg(&mdb->cmd, -"SELECT JobId FROM Job WHERE JobStatus='T' AND Type='%c' AND " -"Level IN ('%c','%c','%c') AND Name='%s' AND ClientId=%u " -"ORDER BY StartTime DESC LIMIT 1", - jr->Type, L_INCREMENTAL, L_DIFFERENTIAL, L_FULL, jr->Name, - jr->ClientId); - } else { - Mmsg1(&mdb->errmsg, _("Unknown level=%d\n"), jr->Level); - db_unlock(mdb); - return 0; - } - Dmsg1(100, "Submitting: %s\n", mdb->cmd); + /* + * For an Incremental job, we must first ensure + * that a Full backup was done (cmd edited above) + * then we do a second look to find the most recent + * backup + */ if (!QUERY_DB(jcr, mdb, mdb->cmd)) { Mmsg2(&mdb->errmsg, _("Query error for start time request: ERR=%s\nCMD=%s\n"), sql_strerror(mdb), mdb->cmd); @@ -99,18 +97,27 @@ db_find_job_start_time(JCR *jcr, B_DB *mdb, JOB_DBR *jr, POOLMEM **stime) } if ((row = sql_fetch_row(mdb)) == NULL) { sql_free_result(mdb); - Mmsg(&mdb->errmsg, _("No prior Job record found.\n")); + Mmsg(&mdb->errmsg, _("No prior Full backup Job record found.\n")); db_unlock(mdb); return 0; } - JobId = atoi(row[0]); sql_free_result(mdb); + /* Now edit SQL command for Incremental Job */ + Mmsg(&mdb->cmd, +"SELECT StartTime FROM Job WHERE JobStatus='T' AND Type='%c' AND " +"Level IN ('%c','%c','%c') AND Name='%s' AND ClientId=%u " +"AND FileSetId=%u ORDER BY StartTime DESC LIMIT 1", + jr->Type, L_INCREMENTAL, L_DIFFERENTIAL, L_FULL, jr->Name, + jr->ClientId, jr->FileSetId); } else { - JobId = jr->JobId; /* search for particular id */ + Mmsg1(&mdb->errmsg, _("Unknown level=%d\n"), jr->Level); + db_unlock(mdb); + return 0; } - + } else { Dmsg1(100, "Submitting: %s\n", mdb->cmd); - Mmsg(&mdb->cmd, "SELECT StartTime FROM Job WHERE Job.JobId=%u", JobId); + Mmsg(&mdb->cmd, "SELECT StartTime FROM Job WHERE Job.JobId=%u", jr->JobId); + } if (!QUERY_DB(jcr, mdb, mdb->cmd)) { pm_strcpy(stime, ""); /* set EOS */ @@ -121,7 +128,7 @@ db_find_job_start_time(JCR *jcr, B_DB *mdb, JOB_DBR *jr, POOLMEM **stime) } if ((row = sql_fetch_row(mdb)) == NULL) { - Mmsg2(&mdb->errmsg, _("No Job found for JobId=%u: ERR=%s\n"), JobId, sql_strerror(mdb)); + Mmsg1(&mdb->errmsg, _("No Job record found: ERR=%s\n"), sql_strerror(mdb)); sql_free_result(mdb); db_unlock(mdb); return 0; @@ -144,7 +151,7 @@ db_find_job_start_time(JCR *jcr, B_DB *mdb, JOB_DBR *jr, POOLMEM **stime) * 0 on failure */ int -db_find_last_jobid(JCR *jcr, B_DB *mdb, JOB_DBR *jr) +db_find_last_jobid(JCR *jcr, B_DB *mdb, char *Name, JOB_DBR *jr) { SQL_ROW row; @@ -152,20 +159,27 @@ db_find_last_jobid(JCR *jcr, B_DB *mdb, JOB_DBR *jr) db_lock(mdb); if (jr->Level == L_VERIFY_CATALOG) { Mmsg(&mdb->cmd, -"SELECT JobId FROM Job WHERE Type='V' AND Level='%c' AND Name='%s' AND " +"SELECT JobId FROM Job WHERE Type='V' AND Level='%c' AND " +" JobStatus='T' AND Name='%s' AND " "ClientId=%u ORDER BY StartTime DESC LIMIT 1", L_VERIFY_INIT, jr->Name, jr->ClientId); - } else if (jr->Level == L_VERIFY_VOLUME_TO_CATALOG) { + } else if (jr->Level == L_VERIFY_VOLUME_TO_CATALOG || + jr->Level == L_VERIFY_DISK_TO_CATALOG) { + if (Name) { + Mmsg(&mdb->cmd, +"SELECT JobId FROM Job WHERE Type='B' AND JobStatus='T' AND " +"Name='%s' ORDER BY StartTime DESC LIMIT 1", Name); + } else { Mmsg(&mdb->cmd, -"SELECT JobId FROM Job WHERE Type='B' AND " -"ClientId=%u ORDER BY StartTime DESC LIMIT 1", - jr->ClientId); +"SELECT JobId FROM Job WHERE Type='B' AND JobStatus='T' AND " +"ClientId=%u ORDER BY StartTime DESC LIMIT 1", jr->ClientId); + } } else { Mmsg1(&mdb->errmsg, _("Unknown Job level=%c\n"), jr->Level); db_unlock(mdb); return 0; } - + Dmsg1(100, "Query: %s\n", mdb->cmd); if (!QUERY_DB(jcr, mdb, mdb->cmd)) { db_unlock(mdb); return 0; @@ -204,6 +218,7 @@ db_find_next_volume(JCR *jcr, B_DB *mdb, int item, MEDIA_DBR *mr) { SQL_ROW row; int numrows; + char *order; db_lock(mdb); if (item == -1) { /* find oldest volume */ @@ -218,12 +233,19 @@ db_find_next_volume(JCR *jcr, B_DB *mdb, int item, MEDIA_DBR *mr) item = 1; } else { /* Find next available volume */ + if (strcmp(mr->VolStatus, "Recycled") == 0 || + strcmp(mr->VolStatus, "Purged") == 0) { + order = "ORDER BY LastWritten ASC,MediaId"; /* take oldest */ + } else { + order = "ORDER BY LastWritten DESC,MediaId"; /* take most recently written */ + } Mmsg(&mdb->cmd, "SELECT MediaId,VolumeName,VolJobs,VolFiles,VolBlocks," -"VolBytes,VolMounts,VolErrors,VolWrites,MaxVolBytes,VolCapacityBytes," -"VolRetention,VolUseDuration,MaxVolJobs,MaxVolFiles,Recycle,Slot," -"FirstWritten,LastWritten,VolStatus " -"FROM Media WHERE PoolId=%u AND MediaType='%s' AND VolStatus='%s' " -"ORDER BY MediaId", mr->PoolId, mr->MediaType, mr->VolStatus); + "VolBytes,VolMounts,VolErrors,VolWrites,MaxVolBytes,VolCapacityBytes," + "VolRetention,VolUseDuration,MaxVolJobs,MaxVolFiles,Recycle,Slot," + "FirstWritten,LastWritten,VolStatus " + "FROM Media WHERE PoolId=%u AND MediaType='%s' AND VolStatus='%s' " + "%s LIMIT 1", + mr->PoolId, mr->MediaType, mr->VolStatus, order); } if (!QUERY_DB(jcr, mdb, mdb->cmd)) { db_unlock(mdb); diff --git a/bacula/src/cats/sql_get.c b/bacula/src/cats/sql_get.c index 7ff3a44fe6..29d28c8cac 100644 --- a/bacula/src/cats/sql_get.c +++ b/bacula/src/cats/sql_get.c @@ -48,7 +48,7 @@ */ /* Forward referenced functions */ -static int db_get_file_record(JCR *jcr, B_DB *mdb, FILE_DBR *fdbr); +static int db_get_file_record(JCR *jcr, B_DB *mdb, JOB_DBR *jr, FILE_DBR *fdbr); static int db_get_filename_record(JCR *jcr, B_DB *mdb); static int db_get_path_record(JCR *jcr, B_DB *mdb); @@ -67,10 +67,10 @@ extern void split_path_and_filename(JCR *jcr, B_DB *mdb, char *fname); * Returns: 0 on failure * 1 on success with the File record in FILE_DBR */ -int db_get_file_attributes_record(JCR *jcr, B_DB *mdb, char *fname, FILE_DBR *fdbr) +int db_get_file_attributes_record(JCR *jcr, B_DB *mdb, char *fname, JOB_DBR *jr, FILE_DBR *fdbr) { int stat; - Dmsg1(20, "Enter get_file_from_catalog fname=%s \n", fname); + Dmsg1(100, "db_get_file_att_record fname=%s \n", fname); db_lock(mdb); split_path_and_filename(jcr, mdb, fname); @@ -79,7 +79,7 @@ int db_get_file_attributes_record(JCR *jcr, B_DB *mdb, char *fname, FILE_DBR *fd fdbr->PathId = db_get_path_record(jcr, mdb); - stat = db_get_file_record(jcr, mdb, fdbr); + stat = db_get_file_record(jcr, mdb, jr, fdbr); db_unlock(mdb); @@ -99,15 +99,23 @@ int db_get_file_attributes_record(JCR *jcr, B_DB *mdb, char *fname, FILE_DBR *fd * "normal" if a new file is found during Verify. */ static -int db_get_file_record(JCR *jcr, B_DB *mdb, FILE_DBR *fdbr) +int db_get_file_record(JCR *jcr, B_DB *mdb, JOB_DBR *jr, FILE_DBR *fdbr) { SQL_ROW row; int stat = 0; + if (jcr->JobLevel == L_VERIFY_DISK_TO_CATALOG) { Mmsg(&mdb->cmd, -"SELECT FileId, LStat, MD5 from File where File.JobId=%u and File.PathId=%u and \ -File.FilenameId=%u", fdbr->JobId, fdbr->PathId, fdbr->FilenameId); - +"SELECT FileId, LStat, MD5 FROM File,Job WHERE " +"File.JobId=Job.JobId AND File.PathId=%u AND " +"File.FilenameId=%u AND Job.Type='B' AND Job.JobSTATUS='T' AND " +"ClientId=%u ORDER BY StartTime DESC LIMIT 1", + fdbr->PathId, fdbr->FilenameId, jr->ClientId); + } else { + Mmsg(&mdb->cmd, +"SELECT FileId, LStat, MD5 FROM File WHERE File.JobId=%u AND File.PathId=%u AND " +"File.FilenameId=%u", fdbr->JobId, fdbr->PathId, fdbr->FilenameId); + } Dmsg3(050, "Get_file_record JobId=%u FilenameId=%u PathId=%u\n", fdbr->JobId, fdbr->FilenameId, fdbr->PathId); @@ -254,15 +262,15 @@ int db_get_job_record(JCR *jcr, B_DB *mdb, JOB_DBR *jr) db_lock(mdb); if (jr->JobId == 0) { - Mmsg(&mdb->cmd, "SELECT VolSessionId,VolSessionTime,\ -PoolId,StartTime,EndTime,JobFiles,JobBytes,JobTDate,Job,JobStatus,\ -Type,Level \ -FROM Job WHERE Job='%s'", jr->Job); + Mmsg(&mdb->cmd, "SELECT VolSessionId,VolSessionTime," +"PoolId,StartTime,EndTime,JobFiles,JobBytes,JobTDate,Job,JobStatus," +"Type,Level,ClientId " +"FROM Job WHERE Job='%s'", jr->Job); } else { - Mmsg(&mdb->cmd, "SELECT VolSessionId,VolSessionTime,\ -PoolId,StartTime,EndTime,JobFiles,JobBytes,JobTDate,Job,JobStatus,\ -Type,Level \ -FROM Job WHERE JobId=%u", jr->JobId); + Mmsg(&mdb->cmd, "SELECT VolSessionId,VolSessionTime," +"PoolId,StartTime,EndTime,JobFiles,JobBytes,JobTDate,Job,JobStatus," +"Type,Level,ClientId " +"FROM Job WHERE JobId=%u", jr->JobId); } if (!QUERY_DB(jcr, mdb, mdb->cmd)) { @@ -288,6 +296,7 @@ FROM Job WHERE JobId=%u", jr->JobId); jr->JobStatus = (int)*row[9]; jr->Type = (int)*row[10]; jr->Level = (int)*row[11]; + jr->ClientId = str_to_uint64(row[12]); sql_free_result(mdb); db_unlock(mdb); @@ -299,7 +308,8 @@ FROM Job WHERE JobId=%u", jr->JobId); * Returns: 0 on error or no Volumes found * number of volumes on success * Volumes are concatenated in VolumeNames - * separated by a vertical bar (|). + * separated by a vertical bar (|) in the order + * that they were written. * * Returns: number of volumes on success */ @@ -311,8 +321,9 @@ int db_get_job_volume_names(JCR *jcr, B_DB *mdb, uint32_t JobId, POOLMEM **Volum db_lock(mdb); Mmsg(&mdb->cmd, -"SELECT VolumeName FROM JobMedia,Media WHERE JobMedia.JobId=%u " -"AND JobMedia.MediaId=Media.MediaId GROUP BY VolumeName", JobId); + "SELECT VolumeName,JobMedia.VolIndex FROM JobMedia,Media WHERE " + "JobMedia.JobId=%u AND JobMedia.MediaId=Media.MediaId " + "GROUP BY VolumeName ORDER BY JobMedia.VolIndex", JobId); Dmsg1(130, "VolNam=%s\n", mdb->cmd); *VolumeNames[0] = 0; diff --git a/bacula/src/cats/sql_list.c b/bacula/src/cats/sql_list.c index 9ebd839f80..8637c5c7b4 100644 --- a/bacula/src/cats/sql_list.c +++ b/bacula/src/cats/sql_list.c @@ -151,12 +151,12 @@ db_list_media_records(JCR *jcr, B_DB *mdb, MEDIA_DBR *mdbr, } } else { if (mdbr->VolumeName[0] != 0) { - Mmsg(&mdb->cmd, "SELECT MediaId,VolumeName,MediaType,VolStatus," - "VolBytes,LastWritten,VolRetention,Recycle,Slot " + Mmsg(&mdb->cmd, "SELECT MediaId,VolumeName,VolStatus," + "VolBytes,VolFiles,VolRetention,Recycle,Slot,MediaType,LastWritten " "FROM Media WHERE Media.VolumeName='%s'", mdbr->VolumeName); } else { - Mmsg(&mdb->cmd, "SELECT MediaId,VolumeName,MediaType,VolStatus," - "VolBytes,LastWritten,VolRetention,Recycle,Slot " + Mmsg(&mdb->cmd, "SELECT MediaId,VolumeName,VolStatus," + "VolBytes,VolFiles,VolRetention,Recycle,Slot,MediaType,LastWritten " "FROM Media WHERE Media.PoolId=%u ORDER BY MediaId", mdbr->PoolId); } } @@ -190,7 +190,7 @@ void db_list_jobmedia_records(JCR *jcr, B_DB *mdb, uint32_t JobId, } else { if (JobId > 0) { /* do by JobId */ - Mmsg(&mdb->cmd, "SELECT JobId,Media.VolumeName,FirstIndex,LastIndex" + Mmsg(&mdb->cmd, "SELECT JobId,Media.VolumeName,FirstIndex,LastIndex " "FROM JobMedia,Media WHERE Media.MediaId=JobMedia.MediaId " "AND JobMedia.JobId=%u", JobId); } else { diff --git a/bacula/src/cats/sql_update.c b/bacula/src/cats/sql_update.c index 510c21bab2..4acb2b8f59 100644 --- a/bacula/src/cats/sql_update.c +++ b/bacula/src/cats/sql_update.c @@ -103,9 +103,10 @@ db_update_job_start_record(JCR *jcr, B_DB *mdb, JOB_DBR *jr) JobTDate = (btime_t)stime; db_lock(mdb); - Mmsg(&mdb->cmd, "UPDATE Job SET Level='%c', StartTime='%s', \ -ClientId=%u, JobTDate=%s WHERE JobId=%u", + Mmsg(&mdb->cmd, "UPDATE Job SET Level='%c', StartTime='%s'," +"ClientId=%u, JobTDate=%s WHERE JobId=%u", (char)(jr->Level), dt, jr->ClientId, edit_uint64(JobTDate, ed1), jr->JobId); + stat = UPDATE_DB(jcr, mdb, mdb->cmd); db_unlock(mdb); mdb->changes = 0; diff --git a/bacula/src/cats/update_bacula_tables.in b/bacula/src/cats/update_bacula_tables.in new file mode 100755 index 0000000000..a1b1984cac --- /dev/null +++ b/bacula/src/cats/update_bacula_tables.in @@ -0,0 +1,13 @@ +#!/bin/sh +# +# This routine alters the appropriately configured +# Bacula tables for either MySQL or SQLite +# +if test xsqlite = x@DB_NAME@ ; then + echo "Altering SQLite tables" + . ./update_sqlite_tables +fi +if test xmysql = x@DB_NAME@ ; then + echo "Altering MySQL tables" + . ./update_mysql_tables +fi diff --git a/bacula/src/cats/update_mysql_tables.in b/bacula/src/cats/update_mysql_tables.in new file mode 100755 index 0000000000..9da912da98 --- /dev/null +++ b/bacula/src/cats/update_mysql_tables.in @@ -0,0 +1,61 @@ +#!/bin/sh +# +# Shell script to update MySQL tables from version 1.32 to 1.33 +# +echo " " +echo "Depending on the size of your database," +echo "this script may take several minutes to run." +echo " " +bindir=@SQL_BINDIR@ + +if $bindir/mysql $* -f <msg, env); + pm_strcat(&UA_sock->msg, "/.bconsolerc"); + fd = fopen(UA_sock->msg, "r"); + if (fd) { + read_and_process_input(fd, UA_sock); + fclose(fd); + } + } + read_and_process_input(stdin, UA_sock); if (UA_sock) { @@ -423,9 +437,8 @@ get_cmd(FILE *input, char *prompt, BSOCK *sock, int sec) if (!line) { exit(1); } - strcpy(sock->msg, line); - strip_trailing_junk(sock->msg); - sock->msglen = strlen(sock->msg); + strip_trailing_junk(line); + sock->msglen = pm_strcpy(&sock->msg, line); if (sock->msglen) { add_history(sock->msg); } diff --git a/bacula/src/console2.glade b/bacula/src/console2.glade new file mode 100644 index 0000000000..2c2619445c --- /dev/null +++ b/bacula/src/console2.glade @@ -0,0 +1,3080 @@ + + + + + + + True + Bacula Console + GTK_WINDOW_TOPLEVEL + GTK_WIN_POS_CENTER + False + True + False + + + + + True + False + 0 + + + + 1 + True + GTK_SHADOW_OUT + GTK_POS_LEFT + GTK_POS_TOP + + + + True + + + + True + _File + True + + + + True + + + + True + Connect to Director + _Connect + True + + + + + + + True + Disconnect from Director + _Disconnect + True + + + + + + + True + + + + + + True + gtk-quit + True + + Exit + + + + + + + + + + + + True + _Edit + True + + + + True + + + + True + gtk-cut + True + + Cut + + + + + + + + True + gtk-copy + True + + Copy + + + + + + + + True + gtk-paste + True + + Paste + + + + + + + + True + gtk-clear + True + + Clear + + + + + + + + True + + + + + + + + + + True + Display Messages + _View + True + + + + True + + + + True + _Display Messages + True + + + + + + + + + + + True + _Settings + True + + + + True + + + + True + gtk-preferences + True + + Preferences + + + + + + + + + + + + True + _Help + True + + + + True + + + + True + gnome-stock-about + True + + About + + + + + + + + + + + + + 0 + False + True + + + + + + 1 + True + GTK_SHADOW_OUT + GTK_POS_LEFT + GTK_POS_TOP + + + + 1 + True + GTK_ORIENTATION_HORIZONTAL + GTK_TOOLBAR_BOTH + True + + + + True + Connect to Director + Connect + True + gtk-new + + + + + + + True + Run a Job + Run + True + gtk-execute + + + + + + + True + Display Messages + Msgs + True + gtk-find + + + + + + + True + Restore + True + gtk-revert-to-saved + + + + + + + True + Label + True + gtk-save-as + + + + + + + + 0 + False + True + + + + + + True + False + 0 + + + + True + GTK_POLICY_AUTOMATIC + GTK_POLICY_AUTOMATIC + GTK_SHADOW_IN + GTK_CORNER_TOP_LEFT + + + + True + False + GTK_JUSTIFY_LEFT + GTK_WRAP_WORD + True + 0 + 0 + 0 + 0 + 0 + 0 + + + + + + 0 + True + True + + + + + + True + False + 0 + + + + True + Command: + False + False + GTK_JUSTIFY_CENTER + False + False + 0.5 + 0.5 + 0 + 0 + + + 0 + False + True + + + + + + True + Enter Commands Here + True + True + True + True + True + True + 150 + + True + * + False + + + + + 0 + True + True + + + + + 0 + False + True + + + + + + True + False + 0 + + + + True + 0 + 0.5 + GTK_SHADOW_ETCHED_IN + + + + True + Status: + False + False + GTK_JUSTIFY_CENTER + False + False + 0.5 + 0.5 + 0 + 0 + + + + + 0 + False + True + + + + + + True + 0 + 0.5 + GTK_SHADOW_ETCHED_IN + + + + True + + False + False + GTK_JUSTIFY_LEFT + False + False + 0 + 0.5 + 0 + 0 + + + + + 0 + True + True + + + + + 0 + False + False + + + + + 0 + True + True + + + + + + + + True + About Bacula Console + GTK_WINDOW_TOPLEVEL + GTK_WIN_POS_NONE + True + True + False + True + + + + True + False + 0 + + + + True + GTK_BUTTONBOX_END + + + + True + False + 0 + + + + True + + False + False + GTK_JUSTIFY_CENTER + False + False + 0.5 + 0.5 + 0 + 0 + + + 0 + True + False + + + + + + 1 + True + True + True + gtk-ok + True + GTK_RELIEF_NORMAL + + + + 0 + False + False + + + + + + + 0 + False + True + GTK_PACK_END + + + + + + True + False + 0 + + + + True + False + 0 + + + + True + Bacula Console 1.32c (24 Oct 03) + + False + False + GTK_JUSTIFY_CENTER + False + False + 0.5 + 0.5 + 0 + 0 + + + 0 + False + False + + + + + + True + + + 0 + False + False + + + + + + True + Copyright (c) 1999 - 2002, Kern Sibbald and John Walker + False + False + GTK_JUSTIFY_LEFT + False + False + 0.1 + 0.5 + 0 + 0 + + + 0 + True + False + + + + + + True + Authors: Kern Sibbald and John Walker + False + False + GTK_JUSTIFY_LEFT + False + False + 0.0400001 + 0.5 + 0 + 0 + + + 0 + True + False + + + + + + True + It comes by night and sucks the essence from your computers + False + False + GTK_JUSTIFY_LEFT + False + False + 0.15 + 0.5 + 0 + 0 + + + 0 + True + False + + + + + 0 + True + True + + + + + 0 + True + True + + + + + + + + True + Select Director + GTK_WINDOW_TOPLEVEL + GTK_WIN_POS_NONE + True + True + False + True + + + + True + False + 0 + + + + True + GTK_BUTTONBOX_END + + + + True + False + 0 + + + + True + True + gtk-ok + True + GTK_RELIEF_NORMAL + + + + 0 + False + False + + + + + + True + + False + False + GTK_JUSTIFY_CENTER + False + False + 0.5 + 0.5 + 2 + 0 + + + 0 + False + False + + + + + + True + True + gtk-cancel + True + GTK_RELIEF_NORMAL + + + + 0 + False + False + + + + + + + 0 + False + True + GTK_PACK_END + + + + + + True + False + 0 + + + + True + + False + False + GTK_JUSTIFY_CENTER + False + False + 0.48 + 0.46 + 0 + 0 + + + 0 + False + False + + + + + + True + Select Director + False + False + GTK_JUSTIFY_CENTER + False + False + 0.5 + 0.5 + 0 + 0 + + + 0 + False + False + + + + + + 6 + True + False + True + False + True + False + + + + True + True + True + True + 0 + + True + * + False + + + + + + True + GTK_SELECTION_BROWSE + + + + True + + + + True + + False + False + GTK_JUSTIFY_LEFT + False + False + 0 + 0.5 + 0 + 0 + + + + + + + + + 0 + False + False + + + + + 0 + True + True + + + + + + + + True + Run a Job + GTK_WINDOW_TOPLEVEL + GTK_WIN_POS_NONE + False + True + False + True + + + + True + False + 0 + + + + True + GTK_BUTTONBOX_END + + + + True + GTK_BUTTONBOX_END + 8 + + + + True + True + True + gtk-ok + True + GTK_RELIEF_NORMAL + + + + + + + True + True + True + gtk-cancel + True + GTK_RELIEF_NORMAL + + + + + + + + 0 + False + True + GTK_PACK_END + + + + + + True + False + 0 + + + + True + False + 0 + + + + True + Run a Job + False + False + GTK_JUSTIFY_CENTER + False + False + 0.5 + 0.5 + 0 + 9 + + + 2 + False + False + + + + + + True + False + 0 + + + + True + False + 0 + + + + True + Job: + False + False + GTK_JUSTIFY_RIGHT + False + False + 0.5 + 0.5 + 0 + 0 + + + 0 + False + False + + + + + + True + True + False + False + True + False + + + + True + True + False + True + 0 + + True + * + False + + + + + + + True + GTK_SELECTION_BROWSE + + + + True + + + + True + + False + False + GTK_JUSTIFY_LEFT + False + False + 0 + 0.5 + 0 + 0 + + + + + + + + + 1 + True + True + + + + + + True + Type: + False + False + GTK_JUSTIFY_CENTER + False + False + 0.5 + 0.5 + 0 + 0 + + + 0 + False + False + + + + + + True + True + False + False + True + False + + + + True + True + False + True + 0 + + True + * + False + + + + + + True + GTK_SELECTION_BROWSE + + + + True + + + + True + + False + False + GTK_JUSTIFY_LEFT + False + False + 0 + 0.5 + 0 + 0 + + + + + + + + + 6 + False + False + + + + + 0 + True + True + + + + + + True + + False + False + GTK_JUSTIFY_CENTER + False + False + 0.5 + 0.5 + 0 + 0 + + + 30 + False + False + + + + + 2 + False + False + + + + + + True + False + 0 + + + + True + Client: + False + False + GTK_JUSTIFY_RIGHT + False + False + 0.5 + 0.5 + 0 + 0 + + + 0 + False + False + + + + + + True + True + False + False + True + False + + + + True + True + False + True + 0 + + True + * + False + + + + + + True + GTK_SELECTION_BROWSE + + + + True + + + + True + + False + False + GTK_JUSTIFY_LEFT + False + False + 0 + 0.5 + 0 + 0 + + + + + + + + + 1 + True + True + + + + + + True + + False + False + GTK_JUSTIFY_CENTER + False + False + 0.5 + 0.5 + 0 + 0 + + + 123 + False + False + + + + + 0 + True + True + + + + + + True + False + 0 + + + + True + FileSet: + False + False + GTK_JUSTIFY_RIGHT + False + False + 0.5 + 0.5 + 0 + 0 + + + 0 + False + False + + + + + + True + True + False + False + True + False + + + + True + True + False + True + 0 + + True + * + False + + + + + + True + GTK_SELECTION_BROWSE + + + + True + + + + True + + False + False + GTK_JUSTIFY_LEFT + False + False + 0 + 0.5 + 0 + 0 + + + + + + + + + 0 + True + True + + + + + + True + True + View FileSet + True + GTK_RELIEF_NORMAL + + + + 10 + False + True + + + + + + True + + False + False + GTK_JUSTIFY_CENTER + False + False + 0.5 + 0.5 + 0 + 0 + + + 65 + False + False + + + + + 2 + True + True + + + + + + True + False + 0 + + + + True + Level: + False + False + GTK_JUSTIFY_RIGHT + False + False + 0.5 + 0.5 + 0 + 0 + + + 0 + False + False + + + + + + True + True + False + False + True + False + + + + True + True + False + True + 0 + + True + * + False + + + + + + True + GTK_SELECTION_BROWSE + + + + True + + + + True + + False + False + GTK_JUSTIFY_LEFT + False + False + 0 + 0.5 + 0 + 0 + + + + + + + + True + + + + True + + False + False + GTK_JUSTIFY_LEFT + False + False + 0 + 0.5 + 0 + 0 + + + + + + + + + 0 + True + True + + + + + + True + + False + False + GTK_JUSTIFY_CENTER + False + False + 0.5 + 0.5 + 0 + 0 + + + 100 + False + False + + + + + 2 + True + True + + + + + + True + False + 0 + + + + True + Pool: + False + False + GTK_JUSTIFY_CENTER + False + False + 0.5 + 0.5 + 0 + 0 + + + 0 + False + False + + + + + + True + True + False + False + True + False + + + + True + True + False + True + 0 + + True + * + False + + + + + + True + GTK_SELECTION_BROWSE + + + + True + + + + True + + False + False + GTK_JUSTIFY_LEFT + False + False + 0 + 0.5 + 0 + 0 + + + + + + + + True + + + + True + + False + False + GTK_JUSTIFY_LEFT + False + False + 0 + 0.5 + 0 + 0 + + + + + + + + + 0 + True + True + + + + + + True + + False + False + GTK_JUSTIFY_CENTER + False + False + 0.5 + 0.5 + 0 + 0 + + + 120 + False + False + + + + + 2 + True + True + + + + + + True + False + 0 + + + + True + Storage: + False + False + GTK_JUSTIFY_CENTER + False + False + 0.5 + 0.5 + 0 + 0 + + + 0 + False + False + + + + + + True + True + False + False + True + False + + + + True + True + False + True + 0 + + True + * + False + + + + + + True + GTK_SELECTION_BROWSE + + + + True + + + + True + + False + False + GTK_JUSTIFY_LEFT + False + False + 0 + 0.5 + 0 + 0 + + + + + + + + True + + + + True + + False + False + GTK_JUSTIFY_LEFT + False + False + 0 + 0.5 + 0 + 0 + + + + + + + + + 0 + True + True + + + + + + True + + False + False + GTK_JUSTIFY_CENTER + False + False + 0.5 + 0.5 + 0 + 0 + + + 120 + False + False + + + + + 0 + True + True + + + + + + True + False + 0 + + + + True + Messages: + False + False + GTK_JUSTIFY_CENTER + False + False + 0.5 + 0.5 + 0 + 0 + + + 0 + False + False + + + + + + True + True + False + False + True + False + + + + True + True + False + True + 0 + + True + * + False + + + + + + True + GTK_SELECTION_BROWSE + + + + True + + + + True + + False + False + GTK_JUSTIFY_LEFT + False + False + 0 + 0.5 + 0 + 0 + + + + + + + + + 0 + True + True + + + + + + True + + False + False + GTK_JUSTIFY_CENTER + False + False + 0.5 + 0.5 + 0 + 0 + + + 120 + False + False + + + + + 0 + True + True + + + + + + True + False + 0 + + + + True + Where: + False + False + GTK_JUSTIFY_CENTER + False + False + 0.5 + 0.5 + 0 + 0 + + + 0 + False + False + + + + + + True + True + True + True + 0 + + True + * + False + + + 0 + True + True + + + + + + True + + False + False + GTK_JUSTIFY_CENTER + False + False + 0.5 + 0.5 + 0 + 0 + + + 120 + False + False + + + + + 2 + True + True + + + + + 0 + True + True + + + + + + True + False + 0 + + + + True + When: + False + False + GTK_JUSTIFY_CENTER + False + False + 0.5 + 0.5 + 0 + 0 + + + 0 + False + False + + + + + + True + True + True + True + 0 + + True + * + False + + + 0 + True + True + + + + + + True + + False + False + GTK_JUSTIFY_CENTER + False + False + 0.5 + 0.5 + 0 + 0 + + + 120 + False + False + + + + + 0 + True + True + + + + + + True + + False + False + GTK_JUSTIFY_CENTER + False + False + 0.5 + 0.5 + 0 + 0 + + + 0 + False + False + + + + + 0 + True + True + + + + + + + + True + Restore File Selection + GTK_WINDOW_TOPLEVEL + GTK_WIN_POS_NONE + True + True + False + + + + True + False + 0 + + + + True + GTK_POLICY_ALWAYS + GTK_POLICY_ALWAYS + GTK_SHADOW_IN + GTK_CORNER_TOP_LEFT + + + + True + True + True + False + False + True + + + + + 0 + True + True + + + + + + True + False + 0 + + + + True + cwd: + False + False + GTK_JUSTIFY_CENTER + False + False + 0.5 + 0.5 + 0 + 0 + + + 0 + False + True + + + + + + True + Enter Commands Here + True + True + True + True + True + True + 0 + + True + * + False + + + + + 0 + True + True + + + + + 0 + False + True + + + + + + True + False + 0 + + + + True + + False + False + GTK_JUSTIFY_FILL + False + False + 0.5 + 0.5 + 59 + 0 + + + 0 + True + False + + + + + + 9 + True + True + gtk-ok + True + GTK_RELIEF_NORMAL + + + + 0 + True + False + + + + + 0 + False + True + + + + + + + + True + Restore Files + GTK_WINDOW_TOPLEVEL + GTK_WIN_POS_NONE + True + True + False + True + + + + True + False + 0 + + + + True + GTK_BUTTONBOX_END + + + + True + GTK_BUTTONBOX_END + 8 + + + + True + True + True + gtk-ok + True + GTK_RELIEF_NORMAL + + + + + + + True + True + True + gtk-cancel + True + GTK_RELIEF_NORMAL + + + + + + + + 0 + False + True + GTK_PACK_END + + + + + + True + False + 0 + + + + 8 + True + 0 + 0.5 + GTK_SHADOW_ETCHED_IN + + + + True + False + 0 + + + + True + + False + False + GTK_JUSTIFY_CENTER + False + False + 0.5 + 0.5 + 0 + 0 + + + 0 + False + False + GTK_PACK_END + + + + + + True + False + 0 + + + + True + + False + False + GTK_JUSTIFY_CENTER + False + False + 0.5 + 0.5 + 0 + 2 + + + 0 + False + False + + + + + + 3 + True + True + Select most recent backup + True + GTK_RELIEF_NORMAL + True + False + True + + + 0 + False + False + + + + + + 5 + True + True + Select list of Jobs + True + GTK_RELIEF_NORMAL + False + False + True + + + 0 + False + False + + + + + + 4 + True + True + Find a specific file + True + GTK_RELIEF_NORMAL + False + False + True + + + 0 + False + False + + + + + + True + + False + False + GTK_JUSTIFY_CENTER + False + False + 0.5 + 0.5 + 0 + 0 + + + 0 + False + False + + + + + 0 + True + True + + + + + + + + True + Select by: + False + False + GTK_JUSTIFY_LEFT + False + False + 0.5 + 0.5 + 0 + 0 + + + label_item + + + + + 0 + True + True + + + + + + True + False + 0 + + + + + + + + 50 + True + True + gtk-apply + True + GTK_RELIEF_NORMAL + + + + 0 + False + False + + + + + + + + + 0 + False + False + + + + + 0 + True + True + + + + + + + + True + Label a Volume + GTK_WINDOW_TOPLEVEL + GTK_WIN_POS_NONE + True + True + False + True + + + + True + False + 0 + + + + True + GTK_BUTTONBOX_END + + + + True + GTK_BUTTONBOX_END + 8 + + + + True + True + True + gtk-ok + True + GTK_RELIEF_NORMAL + + + + + + + True + True + True + gtk-cancel + True + GTK_RELIEF_NORMAL + + + + + + + + 0 + False + True + GTK_PACK_END + + + + + + True + False + 0 + + + + True + Label a Volume + False + False + GTK_JUSTIFY_CENTER + False + False + 0.5 + 0.5 + 0 + 9 + + + 0 + False + False + + + + + + 5 + True + False + 0 + + + + True + Storage: + False + False + GTK_JUSTIFY_CENTER + False + False + 0.15 + 0.5 + 0 + 0 + + + 0 + True + True + + + + + + True + True + False + False + True + False + + + + True + True + False + True + 0 + + True + * + False + + + + + + True + GTK_SELECTION_BROWSE + + + + True + + + + True + + False + False + GTK_JUSTIFY_LEFT + False + False + 0 + 0.5 + 0 + 0 + + + + + + + + True + + + + True + + False + False + GTK_JUSTIFY_LEFT + False + False + 0 + 0.5 + 0 + 0 + + + + + + + + + 15 + True + True + + + + + + + + + 0 + False + True + + + + + + 5 + True + False + 0 + + + + True + Pool: + False + False + GTK_JUSTIFY_LEFT + False + False + 0.12 + 0.5 + 0 + 0 + + + 0 + True + True + + + + + + True + True + False + False + True + False + + + + True + True + False + True + 0 + + True + * + False + + + + + + True + GTK_SELECTION_BROWSE + + + + True + + + + True + + False + False + GTK_JUSTIFY_LEFT + False + False + 0 + 0.5 + 0 + 0 + + + + + + + + True + + + + True + + False + False + GTK_JUSTIFY_LEFT + False + False + 0 + 0.5 + 0 + 0 + + + + + + + + + 14 + True + True + + + + + + + + + 0 + False + True + + + + + + 5 + True + False + 0 + + + + True + Volume Name: + False + False + GTK_JUSTIFY_LEFT + False + False + 0.17 + 0.5 + 0 + 0 + + + 0 + True + True + + + + + + True + True + True + True + 0 + + True + * + False + + + 0 + True + True + + + + + + + + + 0 + False + True + + + + + + 5 + True + False + 0 + + + + True + Slot: + False + False + GTK_JUSTIFY_LEFT + False + False + 0.09 + 0.5 + 0 + 0 + + + 0 + False + True + + + + + + True + True + 1 + 0 + True + GTK_UPDATE_ALWAYS + False + False + 0 0 10000 1 10 10 + + + 0 + True + True + + + + + + + + + 0 + True + True + + + + + + True + + False + False + GTK_JUSTIFY_CENTER + False + False + 0.5 + 0.5 + 0 + 0 + + + 0 + False + False + + + + + 0 + False + True + + + + + + + diff --git a/bacula/src/console2.gladep b/bacula/src/console2.gladep new file mode 100644 index 0000000000..8e701cb6f4 --- /dev/null +++ b/bacula/src/console2.gladep @@ -0,0 +1,8 @@ + + + + + console + gnome-console + gnome2-console + diff --git a/bacula/src/dird/.cvsignore b/bacula/src/dird/.cvsignore index e5ca4c1c7b..fb6ff2128c 100644 --- a/bacula/src/dird/.cvsignore +++ b/bacula/src/dird/.cvsignore @@ -8,3 +8,4 @@ btraceback btraceback.gdb startit stopit +dird.conf diff --git a/bacula/src/dird/Makefile.in b/bacula/src/dird/Makefile.in index 299cf2c663..851d93d336 100644 --- a/bacula/src/dird/Makefile.in +++ b/bacula/src/dird/Makefile.in @@ -30,7 +30,7 @@ SVRSRCS = dird.c admin.c authenticate.c \ recycle.c restore.c run_conf.c \ scheduler.c sql_cmds.c \ ua_cmds.c ua_dotcmds.c \ - ua_query.c ua_retention.c \ + ua_query.c \ ua_input.c ua_label.c ua_output.c ua_prune.c \ ua_purge.c ua_restore.c ua_run.c \ ua_select.c ua_server.c \ @@ -43,7 +43,7 @@ SVROBJS = dird.o admin.o authenticate.o \ recycle.o restore.o run_conf.o \ scheduler.o sql_cmds.o \ ua_cmds.o ua_dotcmds.o \ - ua_query.o ua_retention.o \ + ua_query.o \ ua_input.o ua_label.o ua_output.o ua_prune.o \ ua_purge.o ua_restore.o ua_run.o \ ua_select.o ua_server.o \ diff --git a/bacula/src/dird/authenticate.c b/bacula/src/dird/authenticate.c index b41ca667c6..9ec6d81bf1 100644 --- a/bacula/src/dird/authenticate.c +++ b/bacula/src/dird/authenticate.c @@ -69,7 +69,7 @@ int authenticate_storage_daemon(JCR *jcr) } if (!cram_md5_get_auth(sd, jcr->store->password, ssl_need) || !cram_md5_auth(sd, jcr->store->password, ssl_need)) { - Jmsg0(jcr, M_FATAL, 0, _("Director and Storage daemon passwords not the same.\n")); + Jmsg0(jcr, M_FATAL, 0, _("Director and Storage daemon passwords or names not the same.\n")); return 0; } Dmsg1(116, ">stored: %s", sd->msg); @@ -106,7 +106,7 @@ int authenticate_file_daemon(JCR *jcr) } if (!cram_md5_get_auth(fd, jcr->client->password, ssl_need) || !cram_md5_auth(fd, jcr->client->password, ssl_need)) { - Jmsg(jcr, M_FATAL, 0, _("Director and File daemon passwords not the same.\n")); + Jmsg(jcr, M_FATAL, 0, _("Director and File daemon passwords or names not the same.\n")); return 0; } Dmsg1(116, ">filed: %s", fd->msg); diff --git a/bacula/src/dird/backup.c b/bacula/src/dird/backup.c index 8d547b4065..2480afbcee 100644 --- a/bacula/src/dird/backup.c +++ b/bacula/src/dird/backup.c @@ -228,7 +228,7 @@ int wait_for_job_termination(JCR *jcr) { int32_t n = 0; BSOCK *fd = jcr->file_bsock; - int fd_ok = FALSE; + bool fd_ok = false; uint32_t JobFiles, Errors; uint64_t ReadBytes, JobBytes; @@ -237,7 +237,7 @@ int wait_for_job_termination(JCR *jcr) while ((n = bget_dirmsg(fd)) >= 0) { if (!fd_ok && sscanf(fd->msg, EndJob, &jcr->FDJobStatus, &JobFiles, &ReadBytes, &JobBytes, &Errors) == 5) { - fd_ok = TRUE; + fd_ok = true; set_jcr_job_status(jcr, jcr->FDJobStatus); Dmsg1(100, "FDStatus=%c\n", (char)jcr->JobStatus); } else { @@ -257,15 +257,18 @@ int wait_for_job_termination(JCR *jcr) /* Note, the SD stores in jcr->JobFiles/ReadBytes/JobBytes/Errors */ wait_for_storage_daemon_termination(jcr); + /* Return values from FD */ if (fd_ok) { jcr->JobFiles = JobFiles; jcr->Errors = Errors; jcr->ReadBytes = ReadBytes; jcr->JobBytes = JobBytes; + } else { + Jmsg(jcr, M_FATAL, 0, _("No Job status returned from FD.\n")); } -// Dmsg4(000, "fd_ok=%d FDJS=%d JS=%d SDJS=%d\n", fd_ok, jcr->FDJobStatus, +// Dmsg4(100, "fd_ok=%d FDJS=%d JS=%d SDJS=%d\n", fd_ok, jcr->FDJobStatus, // jcr->JobStatus, jcr->SDJobStatus); /* Return the first error status we find Dir, FD, or SD */ @@ -295,7 +298,7 @@ static void backup_cleanup(JCR *jcr, int TermCode, char *since, FILESET_DBR *fsr double kbps, compression; utime_t RunTime; - Dmsg0(100, "Enter backup_cleanup()\n"); + Dmsg2(100, "Enter backup_cleanup %d %c\n", TermCode, TermCode); memset(&mr, 0, sizeof(mr)); set_jcr_job_status(jcr, TermCode); @@ -337,8 +340,12 @@ static void backup_cleanup(JCR *jcr, int TermCode, char *since, FILESET_DBR *fsr VolCount = db_get_job_volume_parameters(jcr, jcr->db, jcr->JobId, &VolParams); if (VolCount == 0) { - Jmsg(jcr, M_ERROR, 0, _("Could not get Job Volume Parameters. ERR=%s\n"), - db_strerror(jcr->db)); + Jmsg(jcr, M_ERROR, 0, _("Could not get Job Volume Parameters to " + "update Bootstrap file. ERR=%s\n"), db_strerror(jcr->db)); + if (jcr->SDJobFiles != 0) { + set_jcr_job_status(jcr, JS_ErrorTerminated); + } + } for (int i=0; i < VolCount; i++) { /* Write the record */ diff --git a/bacula/src/dird/bacula-dir.conf.in b/bacula/src/dird/bacula-dir.conf.in index be3f7e3626..9c00dfbdb4 100644 --- a/bacula/src/dird/bacula-dir.conf.in +++ b/bacula/src/dird/bacula-dir.conf.in @@ -185,7 +185,7 @@ Messages { # # WARNING! the following will create a file that you must cycle from # time to time as it will grow indefinitely. However, it will -# also keep all your messages if the scroll off the console. +# also keep all your messages if they scroll off the console. # append = "@working_dir@/log" = all, !skipped } diff --git a/bacula/src/dird/bsr.c b/bacula/src/dird/bsr.c index ae8ddf2a7e..9457c12bac 100644 --- a/bacula/src/dird/bsr.c +++ b/bacula/src/dird/bsr.c @@ -35,6 +35,7 @@ /* Forward referenced functions */ static void write_bsr(UAContext *ua, RBSR *bsr, FILE *fd); +void print_bsr(UAContext *ua, RBSR *bsr); /* @@ -62,21 +63,31 @@ static void free_findex(RBSR_FINDEX *fi) * range regardless of volume. The FirstIndex and LastIndex * passed in here are for the current volume, so when * writing out the fi, constrain them to those values. + * + * We are called here once for each JobMedia record + * for each Volume. */ -static void write_findex(UAContext *ua, RBSR_FINDEX *fi, +static uint32_t write_findex(UAContext *ua, RBSR_FINDEX *fi, int32_t FirstIndex, int32_t LastIndex, FILE *fd) { - if (fi) { + uint32_t count = 0; + for ( ; fi; fi=fi->next) { int32_t findex, findex2; + if ((fi->findex >= FirstIndex && fi->findex <= LastIndex) || + (fi->findex2 >= FirstIndex && fi->findex2 <= LastIndex) || + (fi->findex < FirstIndex && fi->findex2 > LastIndex)) { findex = fi->findex < FirstIndex ? FirstIndex : fi->findex; findex2 = fi->findex2 > LastIndex ? LastIndex : fi->findex2; if (findex == findex2) { fprintf(fd, "FileIndex=%d\n", findex); + count++; } else { fprintf(fd, "FileIndex=%d-%d\n", findex, findex2); + count += findex2 - findex + 1; + } } - write_findex(ua, fi->next, FirstIndex, LastIndex, fd); } + return count; } /* @@ -101,13 +112,15 @@ static bool is_volume_selected(RBSR_FINDEX *fi, static void print_findex(UAContext *ua, RBSR_FINDEX *fi) { - if (fi) { + bsendmsg(ua, "fi=0x%x\n", (unsigned)fi); + for ( ; fi; fi=fi->next) { if (fi->findex == fi->findex2) { bsendmsg(ua, "FileIndex=%d\n", fi->findex); +// Dmsg1(100, "FileIndex=%d\n", fi->findex); } else { bsendmsg(ua, "FileIndex=%d-%d\n", fi->findex, fi->findex2); +// Dmsg2(100, "FileIndex=%d-%d\n", fi->findex, fi->findex2); } - print_findex(ua, fi->next); } } @@ -124,10 +137,10 @@ void free_bsr(RBSR *bsr) { if (bsr) { free_findex(bsr->fi); - free_bsr(bsr->next); if (bsr->VolParams) { free(bsr->VolParams); } + free_bsr(bsr->next); free(bsr); } } @@ -210,6 +223,11 @@ int write_bsr_file(UAContext *ua, RBSR *bsr) static void write_bsr(UAContext *ua, RBSR *bsr, FILE *fd) { if (bsr) { + uint32_t count; + /* + * For a given volume, loop over all the JobMedia records. + * VolCount is the number of JobMedia records. + */ for (int i=0; i < bsr->VolCount; i++) { if (!is_volume_selected(bsr->fi, bsr->VolParams[i].FirstIndex, bsr->VolParams[i].LastIndex)) { @@ -219,21 +237,32 @@ static void write_bsr(UAContext *ua, RBSR *bsr, FILE *fd) fprintf(fd, "Volume=\"%s\"\n", bsr->VolParams[i].VolumeName); fprintf(fd, "VolSessionId=%u\n", bsr->VolSessionId); fprintf(fd, "VolSessionTime=%u\n", bsr->VolSessionTime); + if (bsr->VolParams[i].StartFile == bsr->VolParams[i].EndFile) { + fprintf(fd, "VolFile=%u\n", bsr->VolParams[i].StartFile); + } else { fprintf(fd, "VolFile=%u-%u\n", bsr->VolParams[i].StartFile, bsr->VolParams[i].EndFile); + } + if (bsr->VolParams[i].StartBlock == bsr->VolParams[i].EndBlock) { + fprintf(fd, "VolFile=%u\n", bsr->VolParams[i].StartBlock); + } else { fprintf(fd, "VolBlock=%u-%u\n", bsr->VolParams[i].StartBlock, bsr->VolParams[i].EndBlock); - -// Dmsg2(000, "bsr VolParam FI=%u LI=%u\n", + } +// Dmsg2(100, "bsr VolParam FI=%u LI=%u\n", // bsr->VolParams[i].FirstIndex, bsr->VolParams[i].LastIndex); - write_findex(ua, bsr->fi, bsr->VolParams[i].FirstIndex, + + count = write_findex(ua, bsr->fi, bsr->VolParams[i].FirstIndex, bsr->VolParams[i].LastIndex, fd); + if (count) { + fprintf(fd, "Count=%u\n", count); + } } write_bsr(ua, bsr->next, fd); } } -static void print_bsr(UAContext *ua, RBSR *bsr) +void print_bsr(UAContext *ua, RBSR *bsr) { if (bsr) { for (int i=0; i < bsr->VolCount; i++) { @@ -296,7 +325,7 @@ void add_findex(RBSR *bsr, uint32_t JobId, int32_t findex) } /* - * At this point, bsr points to bsr containing JobId, + * At this point, bsr points to bsr containing this JobId, * and we are sure that there is at least one fi record. */ lfi = fi = bsr->fi; @@ -318,6 +347,10 @@ void add_findex(RBSR *bsr, uint32_t JobId, int32_t findex) if (findex == (fi->findex2 + 1)) { /* extend up */ RBSR_FINDEX *nfi; fi->findex2 = findex; + /* + * If the following record contains one higher, merge its + * file index by extending it up. + */ if (fi->next && ((findex+1) == fi->next->findex)) { nfi = fi->next; fi->findex2 = nfi->findex2; diff --git a/bacula/src/dird/catreq.c b/bacula/src/dird/catreq.c index 05f7f1e884..91feaff4bb 100644 --- a/bacula/src/dird/catreq.c +++ b/bacula/src/dird/catreq.c @@ -145,8 +145,10 @@ void catalog_request(JCR *jcr, BSOCK *bs, char *msg) /* * Now try recycling if necessary + * reason set non-NULL if we cannot use it + * We also check for Volume being expired. */ - is_volume_valid_or_recyclable(jcr, &mr, &reason); + check_if_volume_valid_or_recyclable(jcr, &mr, &reason); } } if (reason == NULL) { @@ -164,7 +166,8 @@ void catalog_request(JCR *jcr, BSOCK *bs, char *msg) Dmsg2(100, "Vol Info for %s: %s", jcr->Job, bs->msg); } else { /* Not suitable volume */ - bnet_fsend(bs, "1998 Volume \"%s\" %s.\n", mr.VolumeName, reason); + bnet_fsend(bs, "1998 Volume \"%s\" status is %s, %s.\n", mr.VolumeName, + mr.VolStatus, reason); } } else { @@ -216,14 +219,6 @@ void catalog_request(JCR *jcr, BSOCK *bs, char *msg) bstrncpy(mr.VolStatus, sdmr.VolStatus, sizeof(mr.VolStatus)); mr.Slot = sdmr.Slot; - /* - * Apply expiration periods and limits, if not a label request, - * and ignore status because if !label we won't use it. - */ - if (!label) { - has_volume_expired(jcr, &mr); - } - Dmsg2(200, "db_update_media_record. Stat=%s Vol=%s\n", mr.VolStatus, mr.VolumeName); /* * Write the modified record to the DB @@ -237,6 +232,7 @@ void catalog_request(JCR *jcr, BSOCK *bs, char *msg) bnet_fsend(bs, "1992 Update Media error\n"); Dmsg0(190, "send error\n"); } + has_volume_expired(jcr, &mr); /* if expired, change Media record */ db_unlock(jcr->db); /* diff --git a/bacula/src/dird/dird.c b/bacula/src/dird/dird.c index eb0a4758ec..b413042122 100644 --- a/bacula/src/dird/dird.c +++ b/bacula/src/dird/dird.c @@ -97,7 +97,6 @@ int main (int argc, char *argv[]) textdomain("bacula-dir"); init_msg(NULL, NULL); /* initialize message handler */ daemon_start_time = time(NULL); - memset(&last_job, 0, sizeof(last_job)); while ((ch = getopt(argc, argv, "c:d:fg:r:stu:v?")) != -1) { switch (ch) { @@ -241,7 +240,7 @@ static void terminate_dird(int sig) delete_pid_file(director->pid_directory, "bacula-dir", director->DIRport); stop_watchdog(); - signal(SIGCHLD, SIG_IGN); /* don't worry about children now */ +// signal(SIGCHLD, SIG_IGN); /* don't worry about children now */ term_scheduler(); if (runjob) { free(runjob); diff --git a/bacula/src/dird/dird_conf.c b/bacula/src/dird/dird_conf.c index 47c9f98581..687ab5523c 100644 --- a/bacula/src/dird/dird_conf.c +++ b/bacula/src/dird/dird_conf.c @@ -203,6 +203,7 @@ static struct res_items job_items[] = { {"pool", store_res, ITEM(res_job.pool), R_POOL, 0, 0}, {"client", store_res, ITEM(res_job.client), R_CLIENT, 0, 0}, {"fileset", store_res, ITEM(res_job.fileset), R_FILESET, 0, 0}, + {"verifyjob", store_res, ITEM(res_job.verify_job), R_JOB, 0, 0}, {"where", store_dir, ITEM(res_job.RestoreWhere), 0, 0, 0}, {"replace", store_replace, ITEM(res_job.replace), 0, ITEM_DEFAULT, REPLACE_ALWAYS}, {"bootstrap",store_dir, ITEM(res_job.RestoreBootstrap), 0, 0, 0}, @@ -214,6 +215,7 @@ static struct res_items job_items[] = { {"prunevolumes", store_yesno, ITEM(res_job.PruneVolumes), 1, ITEM_DEFAULT, 0}, {"runbeforejob", store_str, ITEM(res_job.RunBeforeJob), 0, 0, 0}, {"runafterjob", store_str, ITEM(res_job.RunAfterJob), 0, 0, 0}, + {"runafterfailedjob", store_str, ITEM(res_job.RunAfterFailedJob), 0, 0, 0}, {"clientrunbeforejob", store_str, ITEM(res_job.ClientRunBeforeJob), 0, 0, 0}, {"clientrunafterjob", store_str, ITEM(res_job.ClientRunAfterJob), 0, 0, 0}, {"spoolattributes", store_yesno, ITEM(res_job.SpoolAttributes), 1, ITEM_DEFAULT, 0}, @@ -223,6 +225,7 @@ static struct res_items job_items[] = { {"rescheduleinterval", store_time, ITEM(res_job.RescheduleInterval), 0, ITEM_DEFAULT, 60 * 30}, {"rescheduletimes", store_pint, ITEM(res_job.RescheduleTimes), 0, 0, 0}, {"priority", store_pint, ITEM(res_job.Priority), 0, ITEM_DEFAULT, 10}, + {"jobretention", store_time, ITEM(res_job.JobRetention), 0, 0, 0}, {NULL, NULL, NULL, 0, 0, 0} }; @@ -345,7 +348,10 @@ struct s_jl joblevels[] = { {"Catalog", L_VERIFY_CATALOG, JT_VERIFY}, {"InitCatalog", L_VERIFY_INIT, JT_VERIFY}, {"VolumeToCatalog", L_VERIFY_VOLUME_TO_CATALOG, JT_VERIFY}, + {"DiskToCatalog", L_VERIFY_DISK_TO_CATALOG, JT_VERIFY}, {"Data", L_VERIFY_DATA, JT_VERIFY}, + {" ", L_NONE, JT_ADMIN}, + {" ", L_NONE, JT_RESTORE}, {NULL, 0} }; @@ -410,7 +416,7 @@ char *level_to_str(int level) void dump_resource(int type, RES *reshdr, void sendit(void *sock, char *fmt, ...), void *sock) { URES *res = (URES *)reshdr; - int recurse = 1; + bool recurse = true; char ed1[100], ed2[100]; if (res == NULL) { @@ -419,7 +425,7 @@ void dump_resource(int type, RES *reshdr, void sendit(void *sock, char *fmt, ... } if (type < 0) { /* no recursion */ type = - type; - recurse = 0; + recurse = false; } switch (type) { case R_DIRECTOR: @@ -483,7 +489,8 @@ void dump_resource(int type, RES *reshdr, void sendit(void *sock, char *fmt, ... res->res_cat.db_port, res->res_cat.db_name, NPRT(res->res_cat.db_user)); break; case R_JOB: - sendit(sock, "Job: name=%s JobType=%d level=%s Priority=%d MaxJobs=%u\n", + sendit(sock, "%s: name=%s JobType=%d level=%s Priority=%d MaxJobs=%u\n", + type == R_JOB ? "Job" : "JobDefs", res->res_job.hdr.name, res->res_job.JobType, level_to_str(res->res_job.level), res->res_job.Priority, res->res_job.MaxConcurrentJobs); @@ -514,6 +521,9 @@ void dump_resource(int type, RES *reshdr, void sendit(void *sock, char *fmt, ... if (res->res_job.RunAfterJob) { sendit(sock, " --> RunAfter=%s\n", NPRT(res->res_job.RunAfterJob)); } + if (res->res_job.RunAfterFailedJob) { + sendit(sock, " --> RunAfterFailed=%s\n", NPRT(res->res_job.RunAfterFailedJob)); + } if (res->res_job.WriteBootstrap) { sendit(sock, " --> WriteBootstrap=%s\n", NPRT(res->res_job.WriteBootstrap)); } @@ -527,6 +537,11 @@ void dump_resource(int type, RES *reshdr, void sendit(void *sock, char *fmt, ... } else { sendit(sock, "!!! No Pool resource\n"); } + if (res->res_job.verify_job) { + sendit(sock, " --> "); + dump_resource(-type, (RES *)res->res_job.verify_job, sendit, sock); + } + break; if (res->res_job.messages) { sendit(sock, " --> "); dump_resource(-R_MSGS, (RES *)res->res_job.messages, sendit, sock); @@ -565,43 +580,52 @@ next_run: bstrncat(buf, num, sizeof(buf)); } } - strcat(buf, "\n"); + bstrncat(buf, "\n", sizeof(buf)); sendit(sock, buf); - strcpy(buf, " mday="); + bstrncpy(buf, " mday=", sizeof(buf)); for (i=0; i<31; i++) { if (bit_is_set(i, run->mday)) { sprintf(num, "%d ", i+1); - strcat(buf, num); + bstrncat(buf, num, sizeof(buf)); } } - strcat(buf, "\n"); + bstrncat(buf, "\n", sizeof(buf)); sendit(sock, buf); - strcpy(buf, " month="); + bstrncpy(buf, " month=", sizeof(buf)); for (i=0; i<12; i++) { if (bit_is_set(i, run->month)) { sprintf(num, "%d ", i+1); - strcat(buf, num); + bstrncat(buf, num, sizeof(buf)); } } - strcat(buf, "\n"); + bstrncat(buf, "\n", sizeof(buf)); sendit(sock, buf); - strcpy(buf, " wday="); + bstrncpy(buf, " wday=", sizeof(buf)); for (i=0; i<7; i++) { if (bit_is_set(i, run->wday)) { sprintf(num, "%d ", i+1); - strcat(buf, num); + bstrncat(buf, num, sizeof(buf)); } } - strcat(buf, "\n"); + bstrncat(buf, "\n", sizeof(buf)); sendit(sock, buf); - strcpy(buf, " wpos="); + bstrncpy(buf, " wom=", sizeof(buf)); for (i=0; i<5; i++) { - if (bit_is_set(i, run->wpos)) { + if (bit_is_set(i, run->wom)) { sprintf(num, "%d ", i+1); - strcat(buf, num); + bstrncat(buf, num, sizeof(buf)); } } - strcat(buf, "\n"); + bstrncat(buf, "\n", sizeof(buf)); + sendit(sock, buf); + bstrncpy(buf, " woy=", sizeof(buf)); + for (i=0; i<54; i++) { + if (bit_is_set(i, run->woy)) { + sprintf(num, "%d ", i); + bstrncat(buf, num, sizeof(buf)); + } + } + bstrncat(buf, "\n", sizeof(buf)); sendit(sock, buf); sendit(sock, " mins=%d\n", run->minute); if (run->pool) { @@ -830,6 +854,9 @@ void free_resource(int type) if (res->res_job.RunAfterJob) { free(res->res_job.RunAfterJob); } + if (res->res_job.RunAfterFailedJob) { + free(res->res_job.RunAfterFailedJob); + } if (res->res_job.ClientRunBeforeJob) { free(res->res_job.ClientRunBeforeJob); } @@ -916,15 +943,17 @@ void save_resource(int type, struct res_items *items, int pass) res->res_dir.messages = res_all.res_dir.messages; break; case R_JOB: - if ((res = (URES *)GetResWithName(R_JOB, res_all.res_dir.hdr.name)) == NULL) { - Emsg1(M_ERROR_TERM, 0, "Cannot find Job resource %s\n", res_all.res_dir.hdr.name); + if ((res = (URES *)GetResWithName(type, res_all.res_dir.hdr.name)) == NULL) { + Emsg1(M_ERROR_TERM, 0, "Cannot find Job resource %s\n", + res_all.res_dir.hdr.name); } - res->res_job.messages = res_all.res_job.messages; - res->res_job.schedule = res_all.res_job.schedule; - res->res_job.client = res_all.res_job.client; - res->res_job.fileset = res_all.res_job.fileset; - res->res_job.storage = res_all.res_job.storage; - res->res_job.pool = res_all.res_job.pool; + res->res_job.messages = res_all.res_job.messages; + res->res_job.schedule = res_all.res_job.schedule; + res->res_job.client = res_all.res_job.client; + res->res_job.fileset = res_all.res_job.fileset; + res->res_job.storage = res_all.res_job.storage; + res->res_job.pool = res_all.res_job.pool; + res->res_job.verify_job = res_all.res_job.verify_job; if (res->res_job.JobType == 0) { Emsg1(M_ERROR_TERM, 0, "Job Type not defined for Job resource %s\n", res_all.res_dir.hdr.name); } diff --git a/bacula/src/dird/dird_conf.h b/bacula/src/dird/dird_conf.h index 5c4aad6b66..5fe488885e 100644 --- a/bacula/src/dird/dird_conf.h +++ b/bacula/src/dird/dird_conf.h @@ -185,6 +185,7 @@ struct JOB { char *RestoreBootstrap; /* Bootstrap file */ char *RunBeforeJob; /* Run program before Job */ char *RunAfterJob; /* Run program after Job */ + char *RunAfterFailedJob; /* Run program after Job that errs */ char *ClientRunBeforeJob; /* Run client program before Job */ char *ClientRunAfterJob; /* Run client program after Job */ char *WriteBootstrap; /* Where to write bootstrap Job updates */ @@ -200,6 +201,7 @@ struct JOB { int RescheduleOnError; /* Set to reschedule on error */ int RescheduleTimes; /* Number of times to reschedule job */ utime_t RescheduleInterval; /* Reschedule interval */ + utime_t JobRetention; /* job retention period in seconds */ MSGS *messages; /* How and where to send messages */ SCHED *schedule; /* When -- Automatic schedule */ @@ -207,6 +209,7 @@ struct JOB { FILESET *fileset; /* What to backup -- Fileset */ STORE *storage; /* Where is device -- Storage daemon */ POOL *pool; /* Where is media -- Media Pool */ + JOB *verify_job; /* Job name to verify */ uint32_t NumConcurrentJobs; /* number of concurrent jobs running */ }; @@ -345,5 +348,6 @@ struct RUN { char mday[nbytes_for_bits(31)]; /* bit set for each day of month */ char month[nbytes_for_bits(12)]; /* bit set for each month */ char wday[nbytes_for_bits(7)]; /* bit set for each day of the week */ - char wpos[nbytes_for_bits(5)]; /* week position */ + char wom[nbytes_for_bits(5)]; /* week of month */ + char woy[nbytes_for_bits(54)]; /* week of year */ }; diff --git a/bacula/src/dird/fd_cmds.c b/bacula/src/dird/fd_cmds.c index 4d7f1e25b9..5fbfaf54ee 100644 --- a/bacula/src/dird/fd_cmds.c +++ b/bacula/src/dird/fd_cmds.c @@ -155,7 +155,7 @@ void get_level_since_time(JCR *jcr, char *since, int since_len) bstrncpy(since, ", since=", since_len); bstrncat(since, jcr->stime, since_len); } - Dmsg1(115, "Last start time = %s\n", jcr->stime); + Dmsg1(100, "Last start time = %s\n", jcr->stime); break; } } @@ -236,6 +236,7 @@ static int send_list(JCR *jcr, int list) p = (char *)ie->name_list.get(j); switch (*p) { case '|': + p++; /* skip over the | */ fd->msg = edit_job_codes(jcr, fd->msg, p, ""); bpipe = open_bpipe(fd->msg, 0, "r"); if (!bpipe) { @@ -250,6 +251,7 @@ static int send_list(JCR *jcr, int list) } else { bstrncpy(buf, "0 ", sizeof(buf)); } + Dmsg1(100, "Opts=%s\n", buf); optlen = strlen(buf); while (fgets(buf+optlen, sizeof(buf)-optlen, bpipe->rfd)) { fd->msglen = Mmsg(&fd->msg, "%s", buf); @@ -279,6 +281,7 @@ static int send_list(JCR *jcr, int list) } else { bstrncpy(buf, "0 ", sizeof(buf)); } + Dmsg1(100, "Opts=%s\n", buf); optlen = strlen(buf); while (fgets(buf+optlen, sizeof(buf)-optlen, ffd)) { fd->msglen = Mmsg(&fd->msg, "%s", buf); @@ -293,15 +296,15 @@ static int send_list(JCR *jcr, int list) p++; /* skip over \ */ /* Note, fall through wanted */ default: + Dmsg2(100, "numopts=%d opts=%s\n", ie->num_opts, NPRT(ie->opts_list[0]->opts)); if (ie->num_opts) { pm_strcpy(&fd->msg, ie->opts_list[0]->opts); pm_strcat(&fd->msg, " "); } else { pm_strcpy(&fd->msg, "0 "); } - pm_strcat(&fd->msg, p); + fd->msglen = pm_strcat(&fd->msg, p); Dmsg1(100, "Inc/Exc name=%s\n", fd->msg); - fd->msglen = strlen(fd->msg); if (!bnet_send(fd)) { Jmsg(jcr, M_FATAL, 0, _(">filed: write error on socket\n")); goto bail_out; diff --git a/bacula/src/dird/getmsg.c b/bacula/src/dird/getmsg.c index b2309f3709..a4d71bc4c7 100644 --- a/bacula/src/dird/getmsg.c +++ b/bacula/src/dird/getmsg.c @@ -95,7 +95,8 @@ int bget_dirmsg(BSOCK *bs) bnet_fsend(bs, OK_msg); /* send response */ break; case BNET_HEARTBEAT: - /* Dmsg0(000, "Got heartbeat.\n"); */ +// encode_time(time(NULL), Job); +// Dmsg1(100, "%s got heartbeat.\n", Job); break; case BNET_HB_RESPONSE: break; @@ -104,6 +105,10 @@ int bget_dirmsg(BSOCK *bs) bnet_fsend(bs, "Status OK\n"); bnet_sig(bs, BNET_EOD); break; + case BNET_BTIME: /* send Bacula time */ + char ed1[50]; + bnet_fsend(bs, "btime %s\n", edit_uint64(get_current_btime(),ed1)); + break; default: Emsg1(M_WARNING, 0, _("bget_dirmsg: unknown bnet signal %d\n"), bs->msglen); return n; diff --git a/bacula/src/dird/inc_conf.c b/bacula/src/dird/inc_conf.c index bacd5bbead..1e9c69c694 100644 --- a/bacula/src/dird/inc_conf.c +++ b/bacula/src/dird/inc_conf.c @@ -151,8 +151,9 @@ static struct s_fs_opt FS_options[] = { /* - * Scan for old Include options (keyword=option) is converted into one or - * two characters. Verifyopts=xxxx is Vxxxx: + * Scan for right hand side of Include options (keyword=option) is + * converted into one or two characters. Verifyopts=xxxx is Vxxxx: + * Whatever is found is concatenated to the opts string. */ static void scan_include_options(LEX *lc, int keyword, char *opts, int optlen) { @@ -167,6 +168,7 @@ static void scan_include_options(LEX *lc, int keyword, char *opts, int optlen) bstrncat(opts, "V", optlen); /* indicate Verify */ bstrncat(opts, lc->str, optlen); bstrncat(opts, ":", optlen); /* terminate it */ + Dmsg3(100, "Catopts=%s option=%s optlen=%d\n", opts, option,optlen); /* * Standard keyword options for Include/Exclude @@ -185,7 +187,7 @@ static void scan_include_options(LEX *lc, int keyword, char *opts, int optlen) scan_err1(lc, "Expected a FileSet option keyword, got:%s:", lc->str); } else { /* add option */ bstrncat(opts, option, optlen); - Dmsg3(200, "Catopts=%s option=%s optlen=%d\n", opts, option,optlen); + Dmsg3(100, "Catopts=%s option=%s optlen=%d\n", opts, option,optlen); } } @@ -238,6 +240,7 @@ void store_inc(LEX *lc, struct res_items *item, int index, int pass) if ((token=lex_get_token(lc, T_ALL)) != T_EQUALS) { scan_err1(lc, _("expected an = following keyword, got: %s"), lc->str); } else { + /* Scan right hand side of option */ scan_include_options(lc, keyword, inc_opts, sizeof(inc_opts)); } if (token == T_BOB) { @@ -258,7 +261,7 @@ void store_inc(LEX *lc, struct res_items *item, int index, int pass) } setup_current_opts(); bstrncpy(res_incexe.current_opts->opts, inc_opts, MAX_FOPTS); - Dmsg1(200, "incexe opts=%s\n", res_incexe.current_opts->opts); + Dmsg2(100, "old pass=%d incexe opts=%s\n", pass, res_incexe.current_opts->opts); /* Create incexe structure */ Dmsg0(200, "Create INCEXE structure\n"); @@ -472,7 +475,9 @@ static void store_fname(LEX *lc, struct res_items *item, int index, int pass) scan_to_eol(lc); } - +/* + * New style options come here + */ static void store_opts(LEX *lc, struct res_items *item, int index, int pass) { int i; @@ -481,6 +486,7 @@ static void store_opts(LEX *lc, struct res_items *item, int index, int pass) inc_opts[0] = 0; keyword = INC_KW_NONE; + /* Look up the keyword */ for (i=0; FS_option_kw[i].name; i++) { if (strcasecmp(item->name, FS_option_kw[i].name) == 0) { keyword = FS_option_kw[i].token; @@ -490,14 +496,13 @@ static void store_opts(LEX *lc, struct res_items *item, int index, int pass) if (keyword == INC_KW_NONE) { scan_err1(lc, "Expected a FileSet keyword, got: %s", lc->str); } - Dmsg2(200, "keyword=%d %s\n", keyword, FS_option_kw[keyword].name); + /* Now scan for the value */ scan_include_options(lc, keyword, inc_opts, sizeof(inc_opts)); - if (pass == 1) { setup_current_opts(); - bstrncat(res_incexe.current_opts->opts, inc_opts, MAX_FOPTS); + bstrncpy(res_incexe.current_opts->opts, inc_opts, MAX_FOPTS); + Dmsg2(100, "new pass=%d incexe opts=%s\n", pass, res_incexe.current_opts->opts); } - scan_to_eol(lc); } diff --git a/bacula/src/dird/job.c b/bacula/src/dird/job.c index 597edcfafb..e84a6422cc 100644 --- a/bacula/src/dird/job.c +++ b/bacula/src/dird/job.c @@ -206,13 +206,18 @@ static void *job_thread(void *arg) Pmsg1(0, "Unimplemented job type: %d\n", jcr->JobType); break; } - if (jcr->job->RunAfterJob) { + if ((jcr->job->RunAfterJob && jcr->JobStatus == JS_Terminated) || + (jcr->job->RunAfterFailedJob && jcr->JobStatus != JS_Terminated)) { POOLMEM *after = get_pool_memory(PM_FNAME); int status; BPIPE *bpipe; char line[MAXSTRING]; - after = edit_job_codes(jcr, after, jcr->job->RunAfterJob, ""); + if (jcr->JobStatus == JS_Terminated) { + after = edit_job_codes(jcr, after, jcr->job->RunAfterJob, ""); + } else { + after = edit_job_codes(jcr, after, jcr->job->RunAfterFailedJob, ""); + } bpipe = open_bpipe(after, 0, "r"); free_pool_memory(after); while (fgets(line, sizeof(line), bpipe->rfd)) { @@ -220,8 +225,13 @@ static void *job_thread(void *arg) } status = close_bpipe(bpipe); if (status != 0) { - Jmsg(jcr, M_FATAL, 0, _("RunAfterJob returned non-zero status=%d\n"), - status); + if (jcr->JobStatus == JS_Terminated) { + Jmsg(jcr, M_FATAL, 0, _("RunAfterJob returned non-zero status=%d\n"), + status); + } else { + Jmsg(jcr, M_FATAL, 0, _("RunAfterFailedJob returned non-zero status=%d\n"), + status); + } set_jcr_job_status(jcr, JS_FatalError); update_job_end_record(jcr); } @@ -397,7 +407,15 @@ void set_jcr_defaults(JCR *jcr, JOB *job) { jcr->job = job; jcr->JobType = job->JobType; - jcr->JobLevel = job->level; + switch (jcr->JobType) { + case JT_ADMIN: + case JT_RESTORE: + jcr->JobLevel = L_NONE; + break; + default: + jcr->JobLevel = job->level; + break; + } jcr->JobPriority = job->Priority; jcr->store = job->storage; jcr->client = job->client; @@ -411,6 +429,7 @@ void set_jcr_defaults(JCR *jcr, JOB *job) jcr->messages = job->messages; if (jcr->RestoreBootstrap) { free(jcr->RestoreBootstrap); + jcr->RestoreBootstrap = NULL; } /* This can be overridden by Console program */ if (job->RestoreBootstrap) { @@ -427,7 +446,7 @@ void set_jcr_defaults(JCR *jcr, JOB *job) break; case JT_RESTORE: case JT_ADMIN: - jcr->JobLevel = L_FULL; + jcr->JobLevel = L_NONE; break; default: break; diff --git a/bacula/src/dird/msgchan.c b/bacula/src/dird/msgchan.c index 304cfd5336..1f3d42bb9c 100644 --- a/bacula/src/dird/msgchan.c +++ b/bacula/src/dird/msgchan.c @@ -95,7 +95,6 @@ int start_storage_daemon_job(JCR *jcr) BSOCK *sd; char auth_key[100]; POOLMEM *device_name, *pool_name, *pool_type, *media_type; - int device_name_len, pool_name_len, pool_type_len, media_type_len; storage = jcr->store; sd = jcr->store_bsock; @@ -136,24 +135,18 @@ int start_storage_daemon_job(JCR *jcr) /* * Send use device = xxx media = yyy pool = zzz */ - device_name_len = strlen(storage->dev_name) + 1; - media_type_len = strlen(storage->media_type) + 1; - pool_type_len = strlen(jcr->pool->pool_type) + 1; - pool_name_len = strlen(jcr->pool->hdr.name) + 1; - device_name = get_memory(device_name_len); - pool_name = get_memory(pool_name_len); - pool_type = get_memory(pool_type_len); - media_type = get_memory(media_type_len); - memcpy(device_name, storage->dev_name, device_name_len); - memcpy(media_type, storage->media_type, media_type_len); - memcpy(pool_type, jcr->pool->pool_type, pool_type_len); - memcpy(pool_name, jcr->pool->hdr.name, pool_name_len); + device_name = get_pool_memory(PM_NAME); + pool_name = get_pool_memory(PM_NAME); + pool_type = get_pool_memory(PM_NAME); + media_type = get_pool_memory(PM_NAME); + pm_strcpy(&device_name, storage->dev_name); + pm_strcpy(&media_type, storage->media_type); + pm_strcpy(&pool_type, jcr->pool->pool_type); + pm_strcpy(&pool_name, jcr->pool->hdr.name); bash_spaces(device_name); bash_spaces(media_type); bash_spaces(pool_type); bash_spaces(pool_name); - sd->msg = check_pool_memory_size(sd->msg, sizeof(device_name) + - device_name_len + media_type_len + pool_type_len + pool_name_len); bnet_fsend(sd, use_device, device_name, media_type, pool_name, pool_type); Dmsg1(110, ">stored: %s", sd->msg); status = response(jcr, sd, OK_device, "Use Device", NO_DISPLAY); @@ -163,7 +156,6 @@ int start_storage_daemon_job(JCR *jcr) " Storage daemon didn't accept Device \"%s\" because:\n %s"), device_name, pool_type/* sd->msg */); } - free_memory(device_name); free_memory(media_type); free_memory(pool_name); diff --git a/bacula/src/dird/next_vol.c b/bacula/src/dird/next_vol.c index 043fd1ac93..a27a0ad865 100644 --- a/bacula/src/dird/next_vol.c +++ b/bacula/src/dird/next_vol.c @@ -69,6 +69,9 @@ int find_next_volume_for_append(JCR *jcr, MEDIA_DBR *mr, int create) } } + /* + * Don't do a purge if called from a command i.e. create == 0. + */ if (!ok && (jcr->pool->purge_oldest_volume || jcr->pool->recycle_oldest_volume)) { Dmsg2(200, "No next volume found. PurgeOldest=%d\n RecyleOldest=%d", @@ -81,10 +84,10 @@ int find_next_volume_for_append(JCR *jcr, MEDIA_DBR *mr, int create) Dmsg0(400, "Try purge.\n"); /* Try to purge oldest volume */ ua = new_ua_context(jcr); - if (jcr->pool->purge_oldest_volume) { + if (jcr->pool->purge_oldest_volume && create) { Jmsg(jcr, M_INFO, 0, _("Purging oldest volume \"%s\"\n"), mr->VolumeName); ok = purge_jobs_from_volume(ua, mr); - } else { + } else if (jcr->pool->recycle_oldest_volume) { Jmsg(jcr, M_INFO, 0, _("Pruning oldest volume \"%s\"\n"), mr->VolumeName); ok = prune_volume(ua, mr); } @@ -100,11 +103,6 @@ int find_next_volume_for_append(JCR *jcr, MEDIA_DBR *mr, int create) if (ok) { /* If we can use the volume, check if it is expired */ if (has_volume_expired(jcr, mr)) { - /* Need to update media */ - if (!db_update_media_record(jcr, jcr->db, mr)) { - Jmsg(jcr, M_ERROR, 0, _("Catalog error updating volume \"%s\". ERR=%s"), - mr->VolumeName, db_strerror(jcr->db)); - } if (retry++ < 200) { /* sanity check */ continue; /* try again from the top */ } else { @@ -172,6 +170,13 @@ bool has_volume_expired(JCR *jcr, MEDIA_DBR *mr) } } } + if (expired) { + /* Need to update media */ + if (!db_update_media_record(jcr, jcr->db, mr)) { + Jmsg(jcr, M_ERROR, 0, _("Catalog error updating volume \"%s\". ERR=%s"), + mr->VolumeName, db_strerror(jcr->db)); + } + } return expired; } @@ -181,39 +186,44 @@ bool has_volume_expired(JCR *jcr, MEDIA_DBR *mr) * Returns: on failure - reason = NULL * on success - reason - pointer to reason */ -bool is_volume_valid_or_recyclable(JCR *jcr, MEDIA_DBR *mr, char **reason) +void check_if_volume_valid_or_recyclable(JCR *jcr, MEDIA_DBR *mr, char **reason) { int ok; *reason = NULL; /* Check if a duration or limit has expired */ - has_volume_expired(jcr, mr); + if (has_volume_expired(jcr, mr)) { + *reason = "volume has expired"; + /* Keep going because we may be able to recycle volume */ + } /* * Now see if we can use the volume as is */ if (strcmp(mr->VolStatus, "Append") == 0 || strcmp(mr->VolStatus, "Recycle") == 0) { - return true; + *reason = NULL; + return; } /* - * Check if the Volume is alreay marked for recycling + * Check if the Volume is already marked for recycling */ if (strcmp(mr->VolStatus, "Purged") == 0) { if (recycle_volume(jcr, mr)) { Jmsg(jcr, M_INFO, 0, "Recycled current volume \"%s\"\n", mr->VolumeName); - return true; + *reason = NULL; + return; } else { /* In principle this shouldn't happen */ - *reason = "recycling of current volume failed"; - return false; + *reason = "and recycling of current volume failed"; + return; } } /* At this point, the volume is not valid for writing */ - *reason = "not Append, Purged or Recycle"; + *reason = "but should be Append, Purged or Recycle"; /* * What we're trying to do here is see if the current volume is @@ -239,15 +249,14 @@ bool is_volume_valid_or_recyclable(JCR *jcr, MEDIA_DBR *mr, char **reason) /* If fully purged, recycle current volume */ if (recycle_volume(jcr, mr)) { Jmsg(jcr, M_INFO, 0, "Recycled current volume \"%s\"\n", mr->VolumeName); - return true; /* Good volume */ + *reason = NULL; } else { - *reason = "not Append, Purged or Recycle (recycling of the " + *reason = "but should be Append, Purged or Recycle (recycling of the " "current volume failed)"; } } else { - *reason = "not Append, Purged or Recycle (cannot automatically " + *reason = "but should be Append, Purged or Recycle (cannot automatically " "recycle current volume, as it still contains unpruned data)"; } } - return *reason ? false : true; } diff --git a/bacula/src/dird/protos.h b/bacula/src/dird/protos.h index a4b48609de..38878c7f7b 100644 --- a/bacula/src/dird/protos.h +++ b/bacula/src/dird/protos.h @@ -102,7 +102,7 @@ extern void wait_for_storage_daemon_termination(JCR *jcr); /* next_vol.c */ int find_next_volume_for_append(JCR *jcr, MEDIA_DBR *mr, int create); bool has_volume_expired(JCR *jcr, MEDIA_DBR *mr); -bool is_volume_valid_or_recyclable(JCR *jcr, MEDIA_DBR *mr, char **reason); +void check_if_volume_valid_or_recyclable(JCR *jcr, MEDIA_DBR *mr, char **reason); /* newvol.c */ int newVolume(JCR *jcr, MEDIA_DBR *mr); @@ -132,6 +132,7 @@ int is_volume_name_legal(UAContext *ua, char *name); /* ua_output.c */ void prtit(void *ctx, char *msg); int complete_jcr_for_job(JCR *jcr, JOB *job, POOL *pool); +RUN *find_next_run(RUN *run, JOB *job, time_t &runtime); /* ua_server.c */ void bsendmsg(void *sock, char *fmt, ...); diff --git a/bacula/src/dird/restore.c b/bacula/src/dird/restore.c index b40a5dc09c..21ad8c0a2b 100644 --- a/bacula/src/dird/restore.c +++ b/bacula/src/dird/restore.c @@ -73,7 +73,7 @@ int do_restore(JCR *jcr) } memset(&rjr, 0, sizeof(rjr)); - jcr->jr.Level = 'F'; /* Full restore */ + jcr->jr.Level = L_FULL; /* Full restore */ jcr->jr.StartTime = jcr->start_time; if (!db_update_job_start_record(jcr, jcr->db, &jcr->jr)) { Jmsg(jcr, M_ERROR, 0, "%s", db_strerror(jcr->db)); @@ -88,6 +88,7 @@ int do_restore(JCR *jcr) /* * The following code is kept temporarily for compatibility. * It is the predecessor to the Bootstrap file. + * DEPRECATED */ if (!jcr->RestoreBootstrap) { /* @@ -286,14 +287,18 @@ static void restore_cleanup(JCR *jcr, int TermCode) msg_type = M_ERROR; /* Generate error message */ if (jcr->store_bsock) { bnet_sig(jcr->store_bsock, BNET_TERMINATE); - pthread_cancel(jcr->SD_msg_chan); + if (jcr->SD_msg_chan) { + pthread_cancel(jcr->SD_msg_chan); + } } break; case JS_Canceled: term_msg = _("Restore Canceled"); if (jcr->store_bsock) { bnet_sig(jcr->store_bsock, BNET_TERMINATE); - pthread_cancel(jcr->SD_msg_chan); + if (jcr->SD_msg_chan) { + pthread_cancel(jcr->SD_msg_chan); + } } break; default: @@ -324,7 +329,7 @@ End time: %s\n\ Files Restored: %s\n\ Bytes Restored: %s\n\ Rate: %.1f KB/s\n\ -Non-fatal FD Errors: %d\n\ +FD Errors: %d\n\ FD termination status: %s\n\ SD termination status: %s\n\ Termination: %s\n\n"), diff --git a/bacula/src/dird/run_conf.c b/bacula/src/dird/run_conf.c index d08d9e315b..66fafec301 100644 --- a/bacula/src/dird/run_conf.c +++ b/bacula/src/dird/run_conf.c @@ -47,7 +47,8 @@ enum e_state { s_weekly, s_monthly, s_hourly, - s_wpos, /* 1st, 2nd, ...*/ + s_wom, /* 1st, 2nd, ...*/ + s_woy, /* week of year w00 - w53 */ }; struct s_keyw { @@ -105,59 +106,48 @@ static struct s_keyw keyw[] = { {N_("monthly"), s_monthly, 0}, {N_("hourly"), s_hourly, 0}, - {N_("1st"), s_wpos, 0}, - {N_("2nd"), s_wpos, 1}, - {N_("3rd"), s_wpos, 2}, - {N_("4th"), s_wpos, 3}, - {N_("5th"), s_wpos, 4}, - - {N_("first"), s_wpos, 0}, - {N_("second"), s_wpos, 1}, - {N_("third"), s_wpos, 2}, - {N_("fourth"), s_wpos, 3}, - {N_("fifth"), s_wpos, 4}, + {N_("1st"), s_wom, 0}, + {N_("2nd"), s_wom, 1}, + {N_("3rd"), s_wom, 2}, + {N_("4th"), s_wom, 3}, + {N_("5th"), s_wom, 4}, + + {N_("first"), s_wom, 0}, + {N_("second"), s_wom, 1}, + {N_("third"), s_wom, 2}, + {N_("fourth"), s_wom, 3}, + {N_("fifth"), s_wom, 4}, {NULL, s_none, 0} }; -static int have_hour, have_mday, have_wday, have_month, have_wpos; -static int have_at; +static bool have_hour, have_mday, have_wday, have_month, have_wom; +static bool have_at, have_woy; static RUN lrun; static void clear_defaults() { - have_hour = have_mday = have_wday = have_month = have_wpos = TRUE; + have_hour = have_mday = have_wday = have_month = have_wom = have_woy = true; clear_bit(0,lrun.hour); clear_bits(0, 30, lrun.mday); clear_bits(0, 6, lrun.wday); clear_bits(0, 11, lrun.month); - clear_bits(0, 4, lrun.wpos); + clear_bits(0, 4, lrun.wom); + clear_bits(0, 53, lrun.woy); } static void set_defaults() { - have_hour = have_mday = have_wday = have_month = have_wpos = FALSE; - have_at = FALSE; + have_hour = have_mday = have_wday = have_month = have_wom = have_woy = false; + have_at = false; set_bit(0,lrun.hour); set_bits(0, 30, lrun.mday); set_bits(0, 6, lrun.wday); set_bits(0, 11, lrun.month); - set_bits(0, 4, lrun.wpos); + set_bits(0, 4, lrun.wom); + set_bits(0, 53, lrun.woy); } -/* Check if string is a number */ -static int is_num(char *num) -{ - char *p = num; - int ch; - while ((ch = *p++)) { - if (ch < '0' || ch > '9') { - return FALSE; - } - } - return TRUE; -} - /* Keywords (RHS) permitted in Run records */ static struct s_kw RunFields[] = { {"pool", 'P'}, @@ -183,7 +173,8 @@ static struct s_kw RunFields[] = { */ void store_run(LEX *lc, struct res_items *item, int index, int pass) { - int i, j, found; + int i, j; + bool found; int token, state, state2 = 0, code = 0, code2 = 0; int options = lc->options; RUN **run = (RUN **)(item->value); @@ -198,12 +189,12 @@ void store_run(LEX *lc, struct res_items *item, int index, int pass) memset(&lrun, 0, sizeof(RUN)); /* scan for Job level "full", "incremental", ... */ - for (found=TRUE; found; ) { - found = FALSE; + for (found=true; found; ) { + found = false; token = lex_get_token(lc, T_NAME); for (i=0; RunFields[i].name; i++) { if (strcasecmp(lc->str, RunFields[i].name) == 0) { - found = TRUE; + found = true; if (lex_get_token(lc, T_ALL) != T_EQUALS) { scan_err1(lc, "Expected an equals, got: %s", lc->str); /* NOT REACHED */ @@ -281,7 +272,7 @@ void store_run(LEX *lc, struct res_items *item, int index, int pass) if (strcasecmp(lc->str, joblevels[j].level_name) == 0) { lrun.level = joblevels[j].level; lrun.job_type = joblevels[j].job_type; - found = TRUE; + found = true; break; } } @@ -298,243 +289,284 @@ void store_run(LEX *lc, struct res_items *item, int index, int pass) for ( ; token != T_EOL; (token = lex_get_token(lc, T_ALL))) { int len, pm = 0; switch (token) { - case T_NUMBER: - state = s_mday; - code = atoi(lc->str) - 1; - if (code < 0 || code > 30) { - scan_err0(lc, _("Day number out of range (1-31)")); - } + case T_NUMBER: + state = s_mday; + code = atoi(lc->str) - 1; + if (code < 0 || code > 30) { + scan_err0(lc, _("Day number out of range (1-31)")); + } + break; + case T_NAME: /* this handles drop through from keyword */ + case T_UNQUOTED_STRING: + if (strchr(lc->str, (int)'-')) { + state = s_range; break; - case T_NAME: /* this handles drop through from keyword */ - case T_UNQUOTED_STRING: - if (strchr(lc->str, (int)'-')) { - state = s_range; - break; + } + if (strchr(lc->str, (int)':')) { + state = s_time; + break; + } + if (lc->str_len == 3 && (lc->str[0] == 'w' || lc->str[0] == 'W') && + is_an_integer(lc->str+1)) { + code = atoi(lc->str+1); + if (code < 0 || code > 53) { + scan_err0(lc, _("Week number out of range (0-53)")); } - if (strchr(lc->str, (int)':')) { - state = s_time; + state = s_woy; /* week of year */ + break; + } + /* everything else must be a keyword */ + for (i=0; keyw[i].name; i++) { + if (strcasecmp(lc->str, keyw[i].name) == 0) { + state = keyw[i].state; + code = keyw[i].code; + i = 0; break; } - /* everything else must be a keyword */ - for (i=0; keyw[i].name; i++) { - if (strcasecmp(lc->str, keyw[i].name) == 0) { - state = keyw[i].state; - code = keyw[i].code; - i = 0; - break; - } - } - if (i != 0) { - scan_err1(lc, _("Job type field: %s in run record not found"), lc->str); - /* NOT REACHED */ - } - break; - case T_COMMA: - continue; - default: - scan_err2(lc, _("Unexpected token: %d:%s"), token, lc->str); + } + if (i != 0) { + scan_err1(lc, _("Job type field: %s in run record not found"), lc->str); /* NOT REACHED */ - break; + } + break; + case T_COMMA: + continue; + default: + scan_err2(lc, _("Unexpected token: %d:%s"), token, lc->str); + /* NOT REACHED */ + break; } switch (state) { - case s_none: - continue; - case s_mday: /* day of month */ - if (!have_mday) { - clear_bits(0, 30, lrun.mday); - clear_bits(0, 6, lrun.wday); - have_mday = TRUE; + case s_none: + continue; + case s_mday: /* day of month */ + if (!have_mday) { + clear_bits(0, 30, lrun.mday); + clear_bits(0, 6, lrun.wday); + have_mday = true; + } + set_bit(code, lrun.mday); + break; + case s_month: /* month of year */ + if (!have_month) { + clear_bits(0, 11, lrun.month); + have_month = true; + } + set_bit(code, lrun.month); + break; + case s_wday: /* week day */ + if (!have_wday) { + clear_bits(0, 6, lrun.wday); + clear_bits(0, 30, lrun.mday); + have_wday = true; + } + set_bit(code, lrun.wday); + break; + case s_wom: /* Week of month 1st, ... */ + if (!have_wom) { + clear_bits(0, 4, lrun.wom); + have_wom = true; + } + set_bit(code, lrun.wom); + break; + case s_woy: + if (!have_woy) { + clear_bits(0, 53, lrun.woy); + have_woy = true; + } + set_bit(code, lrun.woy); + break; + case s_time: /* time */ + if (!have_at) { + scan_err0(lc, _("Time must be preceded by keyword AT.")); + /* NOT REACHED */ + } + if (!have_hour) { + clear_bit(0, lrun.hour); + } + p = strchr(lc->str, ':'); + if (!p) { + scan_err0(lc, _("Time logic error.\n")); + /* NOT REACHED */ + } + *p++ = 0; /* separate two halves */ + code = atoi(lc->str); + len = strlen(p); + if (len > 2 && p[len-1] == 'm') { + if (p[len-2] == 'a') { + pm = 0; + } else if (p[len-2] == 'p') { + pm = 1; + } else { + scan_err0(lc, _("Bad time specification.")); + /* NOT REACHED */ } - set_bit(code, lrun.mday); - break; - case s_month: /* month of year */ - if (!have_month) { - clear_bits(0, 11, lrun.month); - have_month = TRUE; + } else { + pm = 0; + } + code2 = atoi(p); + if (pm) { + code += 12; + } + if (code < 0 || code > 23 || code2 < 0 || code2 > 59) { + scan_err0(lc, _("Bad time specification.")); + /* NOT REACHED */ + } + set_bit(code, lrun.hour); + lrun.minute = code2; + have_hour = true; + break; + case s_at: + have_at = true; + break; + case s_range: + p = strchr(lc->str, '-'); + if (!p) { + scan_err0(lc, _("Range logic error.\n")); + } + *p++ = 0; /* separate two halves */ + + /* Check for day range */ + if (is_an_integer(lc->str) && is_an_integer(p)) { + code = atoi(lc->str) - 1; + code2 = atoi(p) - 1; + if (code < 0 || code > 30 || code2 < 0 || code2 > 30) { + scan_err0(lc, _("Bad day range specification.")); } - set_bit(code, lrun.month); - break; - case s_wday: /* week day */ - if (!have_wday) { - clear_bits(0, 6, lrun.wday); + if (!have_mday) { clear_bits(0, 30, lrun.mday); - have_wday = TRUE; + clear_bits(0, 6, lrun.wday); + have_mday = true; } - set_bit(code, lrun.wday); - break; - case s_wpos: /* Week position 1st, ... */ - if (!have_wpos) { - clear_bits(0, 4, lrun.wpos); - have_wpos = TRUE; + if (code < code2) { + set_bits(code, code2, lrun.mday); + } else { + set_bits(code, 30, lrun.mday); + set_bits(0, code2, lrun.mday); } - set_bit(code, lrun.wpos); break; - case s_time: /* time */ - if (!have_at) { - scan_err0(lc, _("Time must be preceded by keyword AT.")); - /* NOT REACHED */ - } - if (!have_hour) { - clear_bit(0, lrun.hour); + } + /* Check for week of year range */ + if (strlen(lc->str) == 3 && strlen(p) == 3 && + (lc->str[0] == 'w' || lc->str[0] == 'W') && + (p[0] == 'w' || p[0] == 'W') && + is_an_integer(lc->str+1) && is_an_integer(p+1)) { + code = atoi(lc->str+1); + code2 = atoi(p+1); + if (code < 0 || code > 53 || code2 < 0 || code2 > 53) { + scan_err0(lc, _("Week number out of range (0-53)")); } - p = strchr(lc->str, ':'); - if (!p) { - scan_err0(lc, _("Time logic error.\n")); - /* NOT REACHED */ + if (!have_woy) { + clear_bits(0, 53, lrun.woy); + have_woy = true; } - *p++ = 0; /* separate two halves */ - code = atoi(lc->str); - len = strlen(p); - if (len > 2 && p[len-1] == 'm') { - if (p[len-2] == 'a') { - pm = 0; - } else if (p[len-2] == 'p') { - pm = 1; - } else { - scan_err0(lc, _("Bad time specification.")); - /* NOT REACHED */ - } + if (code < code2) { + set_bits(code, code2, lrun.woy); } else { - pm = 0; + set_bits(code, 53, lrun.woy); + set_bits(0, code2, lrun.woy); } - code2 = atoi(p); - if (pm) { - code += 12; - } - if (code < 0 || code > 23 || code2 < 0 || code2 > 59) { - scan_err0(lc, _("Bad time specification.")); - /* NOT REACHED */ - } - set_bit(code, lrun.hour); - lrun.minute = code2; - have_hour = TRUE; - break; - case s_at: - have_at = TRUE; break; - case s_range: - p = strchr(lc->str, '-'); - if (!p) { - scan_err0(lc, _("Range logic error.\n")); - } - *p++ = 0; /* separate two halves */ - - /* Check for day range */ - if (is_num(lc->str) && is_num(p)) { - code = atoi(lc->str) - 1; - code2 = atoi(p) - 1; - if (code < 0 || code > 30 || code2 < 0 || code2 > 30) { - scan_err0(lc, _("Bad day range specification.")); - } - if (!have_mday) { - clear_bits(0, 30, lrun.mday); - clear_bits(0, 6, lrun.wday); - have_mday = TRUE; - } - if (code < code2) { - set_bits(code, code2, lrun.mday); - } else { - set_bits(code, 30, lrun.mday); - set_bits(0, code2, lrun.mday); - } + } + /* lookup first half of keyword range (week days or months) */ + lcase(lc->str); + for (i=0; keyw[i].name; i++) { + if (strcmp(lc->str, keyw[i].name) == 0) { + state = keyw[i].state; + code = keyw[i].code; + i = 0; break; } + } + if (i != 0 || (state != s_month && state != s_wday && state != s_wom)) { + scan_err0(lc, _("Invalid month, week or position day range")); + /* NOT REACHED */ + } - /* lookup first half of keyword range (week days or months) */ - lcase(lc->str); - for (i=0; keyw[i].name; i++) { - if (strcmp(lc->str, keyw[i].name) == 0) { - state = keyw[i].state; - code = keyw[i].code; - i = 0; - break; - } + /* Lookup end of range */ + lcase(p); + for (i=0; keyw[i].name; i++) { + if (strcmp(p, keyw[i].name) == 0) { + state2 = keyw[i].state; + code2 = keyw[i].code; + i = 0; + break; } - if (i != 0 || (state != s_month && state != s_wday && state != s_wpos)) { - scan_err0(lc, _("Invalid month, week or position day range")); - /* NOT REACHED */ + } + if (i != 0 || state != state2 || code == code2) { + scan_err0(lc, _("Invalid month, weekday or position range")); + /* NOT REACHED */ + } + if (state == s_wday) { + if (!have_wday) { + clear_bits(0, 6, lrun.wday); + clear_bits(0, 30, lrun.mday); + have_wday = true; } - - /* Lookup end of range */ - lcase(p); - for (i=0; keyw[i].name; i++) { - if (strcmp(p, keyw[i].name) == 0) { - state2 = keyw[i].state; - code2 = keyw[i].code; - i = 0; - break; - } + if (code < code2) { + set_bits(code, code2, lrun.wday); + } else { + set_bits(code, 6, lrun.wday); + set_bits(0, code2, lrun.wday); } - if (i != 0 || state != state2 || code == code2) { - scan_err0(lc, _("Invalid month, weekday or position range")); - /* NOT REACHED */ + } else if (state == s_month) { + if (!have_month) { + clear_bits(0, 30, lrun.month); + have_month = true; } - if (state == s_wday) { - if (!have_wday) { - clear_bits(0, 6, lrun.wday); - clear_bits(0, 30, lrun.mday); - have_wday = TRUE; - } - if (code < code2) { - set_bits(code, code2, lrun.wday); - } else { - set_bits(code, 6, lrun.wday); - set_bits(0, code2, lrun.wday); - } - } else if (state == s_month) { - if (!have_month) { - clear_bits(0, 30, lrun.month); - have_month = TRUE; - } - if (code < code2) { - set_bits(code, code2, lrun.month); - } else { - /* this is a bit odd, but we accept it anyway */ - set_bits(code, 30, lrun.month); - set_bits(0, code2, lrun.month); - } + if (code < code2) { + set_bits(code, code2, lrun.month); } else { - /* Must be position */ - if (!have_wpos) { - clear_bits(0, 4, lrun.wpos); - have_wpos = TRUE; - } - if (code < code2) { - set_bits(code, code2, lrun.wpos); - } else { - set_bits(code, 4, lrun.wpos); - set_bits(0, code2, lrun.wpos); - } - } - break; - case s_hourly: - clear_defaults(); - set_bits(0, 23, lrun.hour); - set_bits(0, 30, lrun.mday); - set_bits(0, 11, lrun.month); - set_bits(0, 4, lrun.wpos); - break; - case s_weekly: - clear_defaults(); - set_bit(0, lrun.wday); - set_bits(0, 11, lrun.month); - set_bits(0, 4, lrun.wpos); - break; - case s_daily: - clear_defaults(); - set_bits(0, 30, lrun.mday); - set_bits(0, 11, lrun.month); - set_bits(0, 4, lrun.wpos); - break; - case s_monthly: - clear_defaults(); - set_bits(0, 11, lrun.month); - set_bits(0, 4, lrun.wpos); - break; - default: - scan_err0(lc, _("Unexpected run state\n")); - /* NOT REACHED */ - break; + /* this is a bit odd, but we accept it anyway */ + set_bits(code, 30, lrun.month); + set_bits(0, code2, lrun.month); + } + } else { + /* Must be position */ + if (!have_wom) { + clear_bits(0, 4, lrun.wom); + have_wom = true; + } + if (code < code2) { + set_bits(code, code2, lrun.wom); + } else { + set_bits(code, 4, lrun.wom); + set_bits(0, code2, lrun.wom); + } + } + break; + case s_hourly: + clear_defaults(); + set_bits(0, 23, lrun.hour); + set_bits(0, 30, lrun.mday); + set_bits(0, 11, lrun.month); + set_bits(0, 4, lrun.wom); + set_bits(0, 53, lrun.woy); + break; + case s_weekly: + clear_defaults(); + set_bit(0, lrun.wday); + set_bits(0, 11, lrun.month); + set_bits(0, 4, lrun.wom); + set_bits(0, 53, lrun.woy); + break; + case s_daily: + clear_defaults(); + set_bits(0, 30, lrun.mday); + set_bits(0, 11, lrun.month); + set_bits(0, 4, lrun.wom); + set_bits(0, 53, lrun.woy); + break; + case s_monthly: + clear_defaults(); + set_bits(0, 11, lrun.month); + set_bits(0, 4, lrun.wom); + set_bits(0, 53, lrun.woy); + break; + default: + scan_err0(lc, _("Unexpected run state\n")); + /* NOT REACHED */ + break; } } diff --git a/bacula/src/dird/scheduler.c b/bacula/src/dird/scheduler.c index 5633fb3294..ebb63ddec3 100644 --- a/bacula/src/dird/scheduler.c +++ b/bacula/src/dird/scheduler.c @@ -189,7 +189,7 @@ static void find_runs() JOB *job; SCHED *sched; struct tm tm; - int hour, next_hour, minute, mday, wday, month, wpos; + int hour, next_hour, minute, mday, wday, month, wom, woy; Dmsg0(200, "enter find_runs()\n"); num_runjobs = 0; @@ -205,7 +205,8 @@ static void find_runs() mday = tm.tm_mday - 1; wday = tm.tm_wday; month = tm.tm_mon; - wpos = (tm.tm_mday - 1) / 7; + wom = mday / 7; + woy = tm_woy(now); /* get week of year */ /* Loop through all jobs */ LockRes(); @@ -221,7 +222,9 @@ static void find_runs() */ if ((bit_is_set(hour, run->hour) || bit_is_set(next_hour, run->hour)) && (bit_is_set(mday, run->mday) || bit_is_set(wday, run->wday)) && - bit_is_set(month, run->month) && bit_is_set(wpos, run->wpos)) { + bit_is_set(month, run->month) && + bit_is_set(wom, run->wom) && + bit_is_set(woy, run->woy)) { /* find time (time_t) job is to be run */ localtime_r(&now, &tm); @@ -234,7 +237,7 @@ static void find_runs() if (bit_is_set(next_hour, run->hour)) { tm.tm_hour++; if (tm.tm_hour > 23) { - tm.tm_hour = 0; + continue; /* next day */ } runtime = mktime(&tm); add_job(job, run, now, runtime); diff --git a/bacula/src/dird/sql_cmds.c b/bacula/src/dird/sql_cmds.c index ce8e774cc7..610aa8a9d1 100644 --- a/bacula/src/dird/sql_cmds.c +++ b/bacula/src/dird/sql_cmds.c @@ -57,17 +57,30 @@ char *drop_deltabs[] = { "DROP INDEX DelInx1", NULL}; + /* List of SQL commands to create temp table and indicies */ char *create_deltabs[] = { "CREATE TABLE DelCandidates (" +#ifdef HAVE_MYSQL "JobId INTEGER UNSIGNED NOT NULL, " "PurgedFiles TINYINT, " "FileSetId INTEGER UNSIGNED, " "JobFiles INTEGER UNSIGNED, " -#ifdef HAVE_MYSQL "JobStatus BINARY(1))", #else +#ifdef HAVE_POSTGRESQL + "JobId INTEGER NOT NULL, " + "PurgedFiles SMALLINT, " + "FileSetId INTEGER, " + "JobFiles INTEGER, " + "JobStatus char(1))", +#else + "JobId INTEGER UNSIGNED NOT NULL, " + "PurgedFiles TINYINT, " + "FileSetId INTEGER UNSIGNED, " + "JobFiles INTEGER UNSIGNED, " "JobStatus CHAR)", +#endif #endif "CREATE INDEX DelInx1 ON DelCandidates (JobId)", NULL}; @@ -85,12 +98,14 @@ char *insert_delcand = /* Select Jobs from the DelCandidates table that have a * more recent backup -- i.e. are not the only backup. * This is the list of Jobs to delete for a Backup Job. + * At the same time, we select "orphanned" jobs + * (i.e. no files, ...) for deletion. */ char *select_backup_del = "SELECT DelCandidates.JobId " "FROM Job,DelCandidates " - "WHERE (DelCandidates.JobFiles=0) OR " - "(DelCandidates.JobStatus!='T') OR " + "WHERE (JobTDate<%s AND ((DelCandidates.JobFiles=0) OR " + "(DelCandidates.JobStatus!='T'))) OR " "(Job.JobTDate>%s " "AND Job.ClientId=%u " "AND Job.Type='B' " @@ -171,7 +186,20 @@ char *uar_del_temp = "DROP TABLE temp"; char *uar_del_temp1 = "DROP TABLE temp1"; char *uar_create_temp = - "CREATE TABLE temp (JobId INTEGER UNSIGNED NOT NULL," + "CREATE TABLE temp (" +#ifdef HAVE_POSTGRESQL + "JobId INTEGER NOT NULL," + "JobTDate BIGINT," + "ClientId INTEGER," + "Level CHAR," + "JobFiles INTEGER," + "StartTime TEXT," + "VolumeName TEXT," + "StartFile INTEGER," + "VolSessionId INTEGER," + "VolSessionTime INTEGER)"; +#else + "JobId INTEGER UNSIGNED NOT NULL," "JobTDate BIGINT UNSIGNED," "ClientId INTEGER UNSIGNED," "Level CHAR," @@ -181,20 +209,28 @@ char *uar_create_temp = "StartFile INTEGER UNSIGNED," "VolSessionId INTEGER UNSIGNED," "VolSessionTime INTEGER UNSIGNED)"; +#endif char *uar_create_temp1 = - "CREATE TABLE temp1 (JobId INTEGER UNSIGNED NOT NULL," + "CREATE TABLE temp1 (" +#ifdef HAVE_POSTGRESQL + "JobId INTEGER NOT NULL," + "JobTDate BIGINT)"; +#else + "JobId INTEGER UNSIGNED NOT NULL," "JobTDate BIGINT UNSIGNED)"; +#endif char *uar_last_full = "INSERT INTO temp1 SELECT Job.JobId,JobTdate " - "FROM Client,Job,JobMedia,Media WHERE Client.ClientId=%u " + "FROM Client,Job,JobMedia,Media,FileSet WHERE Client.ClientId=%u " "AND Job.ClientId=%u " "AND Job.StartTime<'%s' " "AND Level='F' AND JobStatus='T' " "AND JobMedia.JobId=Job.JobId " "AND JobMedia.MediaId=Media.MediaId " - "AND Job.FileSetId=%u " + "AND Job.FileSetId=FileSet.FileSetId " + "AND FileSet.FileSet='%s' " "ORDER BY Job.JobTDate DESC LIMIT 1"; char *uar_full = @@ -210,39 +246,42 @@ char *uar_inc_dec = "INSERT INTO temp SELECT Job.JobId,Job.JobTDate,Job.ClientId," "Job.Level,Job.JobFiles,Job.StartTime,Media.VolumeName,JobMedia.StartFile," "Job.VolSessionId,Job.VolSessionTime " - "FROM Job,JobMedia,Media " - "WHERE Job.JobTDate>%s AND Job.StartTime < '%s' " + "FROM Job,JobMedia,Media,FileSet " + "WHERE Job.JobTDate>%s AND Job.StartTime<'%s' " "AND Job.ClientId=%u " "AND JobMedia.JobId=Job.JobId " "AND JobMedia.MediaId=Media.MediaId " "AND Job.Level IN ('I', 'D') AND JobStatus='T' " - "AND Job.FileSetId=%u " - "GROUP BY Job.JobId"; + "AND Job.FileSetId=FileSet.FileSetId " + "AND FileSet.FileSet='%s' "; char *uar_list_temp = "SELECT JobId,Level,JobFiles,StartTime,VolumeName,StartFile," - "VolSessionId,VolSessionTime FROM temp"; + "VolSessionId,VolSessionTime FROM temp " + "ORDER BY StartTime ASC"; + char *uar_sel_jobid_temp = "SELECT JobId FROM temp"; char *uar_sel_all_temp1 = "SELECT * FROM temp1"; -/* Select filesets for this Client */ +/* Select FileSet names for this Client */ char *uar_sel_fileset = - "SELECT FileSet.FileSetId,FileSet.FileSet,FileSet.CreateTime FROM Job," + "SELECT DISTINCT FileSet.FileSet FROM Job," "Client,FileSet WHERE Job.FileSetId=FileSet.FileSetId " "AND Job.ClientId=%u AND Client.ClientId=%u " - "GROUP BY FileSet.FileSetId ORDER BY FileSet.FileSetId"; + "ORDER BY FileSet.FileSet"; /* Find MediaType used by this Job */ char *uar_mediatype = "SELECT MediaType FROM JobMedia,Media WHERE JobMedia.JobId=%u " "AND JobMedia.MediaId=Media.MediaId"; -/* Find JobId, FileIndex for a given path/file */ +/* Find JobId, FileIndex for a given path/file and date */ char *uar_jobid_fileindex = "SELECT Job.JobId, File.FileIndex FROM Job,File,Path,Filename,Client " "WHERE Job.JobId=File.JobId " + "AND Job.StartTime<'%s' " "AND Path.Path='%s' " "AND Filename.Name='%s' " "AND Client.Name='%s' " diff --git a/bacula/src/dird/ua_cmds.c b/bacula/src/dird/ua_cmds.c index 7acb0d1216..bc3c3b47b7 100644 --- a/bacula/src/dird/ua_cmds.c +++ b/bacula/src/dird/ua_cmds.c @@ -50,11 +50,11 @@ extern int messagescmd(UAContext *ua, char *cmd); extern int autodisplaycmd(UAContext *ua, char *cmd); extern int sqlquerycmd(UAContext *ua, char *cmd); extern int querycmd(UAContext *ua, char *cmd); -extern int runcmd(UAContext *ua, char *cmd); +extern int run_cmd(UAContext *ua, char *cmd); extern int retentioncmd(UAContext *ua, char *cmd); extern int prunecmd(UAContext *ua, char *cmd); extern int purgecmd(UAContext *ua, char *cmd); -extern int restorecmd(UAContext *ua, char *cmd); +extern int restore_cmd(UAContext *ua, char *cmd); extern int label_cmd(UAContext *ua, char *cmd); extern int relabel_cmd(UAContext *ua, char *cmd); extern int update_slots(UAContext *ua); /* ua_label.c */ @@ -75,6 +75,7 @@ static int update_volume(UAContext *ua); static int update_pool(UAContext *ua); static int delete_volume(UAContext *ua); static int delete_pool(UAContext *ua); +static int delete_job(UAContext *ua); static int mount_cmd(UAContext *ua, char *cmd); static int release_cmd(UAContext *ua, char *cmd); static int update_cmd(UAContext *ua, char *cmd); @@ -94,23 +95,23 @@ static struct cmdstruct commands[] = { { N_("estimate"), estimate_cmd, _("performs FileSet estimate, listing gives full listing")}, { N_("exit"), quit_cmd, _("exit = quit")}, { N_("help"), help_cmd, _("print this command")}, + { N_("list"), list_cmd, _("list [pools | jobs | jobtotals | media | files jobid=]; from catalog")}, { N_("label"), label_cmd, _("label a tape")}, - { N_("list"), list_cmd, _("list [pools | jobs | jobtotals | media | files job=]; from catalog")}, { N_("llist"), llist_cmd, _("full or long list like list command")}, { N_("messages"), messagescmd, _("messages")}, { N_("mount"), mount_cmd, _("mount ")}, { N_("prune"), prunecmd, _("prune expired records from catalog")}, { N_("purge"), purgecmd, _("purge records from catalog")}, - { N_("query"), querycmd, _("query catalog")}, { N_("quit"), quit_cmd, _("quit")}, + { N_("query"), querycmd, _("query catalog")}, + { N_("restore"), restore_cmd, _("restore files")}, { N_("relabel"), relabel_cmd, _("relabel a tape")}, { N_("release"), release_cmd, _("release ")}, - { N_("restore"), restorecmd, _("restore files")}, - { N_("run"), runcmd, _("run ")}, + { N_("run"), run_cmd, _("run ")}, + { N_("status"), status_cmd, _("status [storage | client]=")}, { N_("setdebug"), setdebug_cmd, _("sets debug level")}, { N_("show"), show_cmd, _("show (resource records) [jobs | pools | ... | all]")}, { N_("sqlquery"), sqlquerycmd, _("use SQL to query catalog")}, - { N_("status"), status_cmd, _("status [storage | client]=")}, { N_("time"), time_cmd, _("print current time")}, { N_("unmount"), unmount_cmd, _("unmount ")}, { N_("update"), update_cmd, _("update Volume or Pool")}, @@ -799,6 +800,26 @@ static void update_volrecycle(UAContext *ua, char *val, MEDIA_DBR *mr) free_pool_memory(query); } +/* Modify the Pool in which this Volume is located */ +static void update_volpool(UAContext *ua, char *val, MEDIA_DBR *mr) +{ + POOL_DBR pr; + POOLMEM *query; + memset(&pr, 0, sizeof(pr)); + bstrncpy(pr.Name, val, sizeof(pr.Name)); + if (!get_pool_dbr(ua, &pr)) { + return; + } + query = get_pool_memory(PM_MESSAGE); + Mmsg(&query, "UPDATE Media SET PoolId=%u WHERE MediaId=%u", pr.PoolId, mr->MediaId); + if (!db_sql_query(ua->db, query, NULL, NULL)) { + bsendmsg(ua, "%s", db_strerror(ua->db)); + } else { + bsendmsg(ua, _("New Pool is: %s\n"), pr.Name); + } + free_pool_memory(query); +} + /* * Update a media record -- allows you to change the * Volume status. E.g. if you want Bacula to stop @@ -808,6 +829,7 @@ static void update_volrecycle(UAContext *ua, char *val, MEDIA_DBR *mr) static int update_volume(UAContext *ua) { MEDIA_DBR mr; + POOL_DBR pr; POOLMEM *query; char ed1[30]; bool done = false; @@ -819,6 +841,7 @@ static int update_volume(UAContext *ua) N_("MaxVolFiles"), /* 4 */ N_("MaxVolBytes"), /* 5 */ N_("Recycle"), /* 6 */ + N_("Pool"), /* 7 */ NULL }; for (int i=0; kw[i]; i++) { @@ -849,6 +872,8 @@ static int update_volume(UAContext *ua) case 6: update_volrecycle(ua, ua->argv[j], &mr); break; + case 7: + update_volpool(ua, ua->argv[j], &mr); } done = true; } @@ -869,6 +894,7 @@ static int update_volume(UAContext *ua) add_prompt(ua, _("Recycle Flag")); add_prompt(ua, _("Slot")); add_prompt(ua, _("Volume Files")); + add_prompt(ua, _("Pool")); add_prompt(ua, _("Done")); switch (do_prompt(ua, "", _("Select parameter to modify"), NULL, 0)) { case 0: /* Volume Status */ @@ -944,7 +970,6 @@ static int update_volume(UAContext *ua) case 7: /* Slot */ int slot; - POOL_DBR pr; memset(&pr, 0, sizeof(POOL_DBR)); pr.PoolId = mr.PoolId; @@ -999,6 +1024,19 @@ static int update_volume(UAContext *ua) free_pool_memory(query); break; + case 9: /* Volume's Pool */ + memset(&pr, 0, sizeof(POOL_DBR)); + pr.PoolId = mr.PoolId; + if (!db_get_pool_record(ua->jcr, ua->db, &pr)) { + bsendmsg(ua, "%s", db_strerror(ua->db)); + return 0; + } + bsendmsg(ua, _("Current Pool is: %s\n"), pr.Name); + if (!get_cmd(ua, _("Enter new Pool name: "))) { + return 0; + } + update_volpool(ua, ua->cmd, &mr); + return 1; default: /* Done or error */ bsendmsg(ua, "Selection done.\n"); return 1; @@ -1414,15 +1452,13 @@ static int delete_cmd(UAContext *ua, char *cmd) static char *keywords[] = { N_("volume"), N_("pool"), + N_("job"), NULL}; if (!open_db(ua)) { return 1; } - bsendmsg(ua, _( -"In general it is not a good idea to delete either a\n" -"Pool or a Volume since they may contain data.\n\n")); switch (find_arg_keyword(ua, keywords)) { case 0: @@ -1431,9 +1467,17 @@ static int delete_cmd(UAContext *ua, char *cmd) case 1: delete_pool(ua); return 1; + case 2: + delete_job(ua); + return 1; default: break; } + + bsendmsg(ua, _( +"In general it is not a good idea to delete either a\n" +"Pool or a Volume since they may contain data.\n\n")); + switch (do_keyword_prompt(ua, _("Choose catalog item to delete"), keywords)) { case 0: delete_volume(ua); @@ -1441,6 +1485,9 @@ static int delete_cmd(UAContext *ua, char *cmd) case 1: delete_pool(ua); break; + case 2: + delete_job(ua); + return 1; default: bsendmsg(ua, _("Nothing done.\n")); break; @@ -1448,6 +1495,30 @@ static int delete_cmd(UAContext *ua, char *cmd) return 1; } +static int delete_job(UAContext *ua) +{ + POOLMEM *query = get_pool_memory(PM_MESSAGE); + JobId_t JobId; + + int i = find_arg_with_value(ua, "jobid"); + if (i >= 0) { + JobId = str_to_int64(ua->argv[i]); + } else if (!get_pint(ua, _("Enter JobId to delete: "))) { + return 0; + } else { + JobId = ua->pint32_val; + } + Mmsg(&query, "DELETE FROM Job WHERE JobId=%u", JobId); + db_sql_query(ua->db, query, NULL, (void *)NULL); + Mmsg(&query, "DELETE FROM File WHERE JobId=%u", JobId); + db_sql_query(ua->db, query, NULL, (void *)NULL); + Mmsg(&query, "DELETE FROM JobMedia WHERE JobId=%u", JobId); + db_sql_query(ua->db, query, NULL, (void *)NULL); + free_pool_memory(query); + bsendmsg(ua, _("Job %u and associated records deleted from the catalog.\n"), JobId); + return 1; +} + /* * Delete media records from database -- dangerous */ diff --git a/bacula/src/dird/ua_input.c b/bacula/src/dird/ua_input.c index c0f3ea74ed..39c4e33ebf 100644 --- a/bacula/src/dird/ua_input.c +++ b/bacula/src/dird/ua_input.c @@ -55,8 +55,7 @@ int get_cmd(UAContext *ua, char *prompt) if (is_bnet_stop(sock)) { return 0; /* error or terminate */ } - ua->cmd = check_pool_memory_size(ua->cmd, sock->msglen+1); - bstrncpy(ua->cmd, sock->msg, sock->msglen+1); + pm_strcpy(&ua->cmd, sock->msg); strip_trailing_junk(ua->cmd); if (strcmp(ua->cmd, ".messages") == 0) { qmessagescmd(ua, ua->cmd); diff --git a/bacula/src/dird/ua_label.c b/bacula/src/dird/ua_label.c index 028c5e74b7..ecbf8aa38f 100644 --- a/bacula/src/dird/ua_label.c +++ b/bacula/src/dird/ua_label.c @@ -148,8 +148,8 @@ static int do_label(UAContext *ua, char *cmd, int relabel) char dev_name[MAX_NAME_LENGTH]; MEDIA_DBR mr, omr; POOL_DBR pr; + bool print_reminder = true; int ok = FALSE; - int mounted = FALSE; int i; bool media_record_exists = false; static char *barcode_keyword[] = { @@ -284,12 +284,17 @@ checkName: /* Here we can get * 3001 OK mount. Device=xxx or * 3001 Mounted Volume vvvv + * 3906 is cannot mount non-tape + * So for those, no need to print a reminder */ - mounted = strncmp(sd->msg, "3001 ", 5) == 0; + if (strncmp(sd->msg, "3001 ", 5) == 0 || + strncmp(sd->msg, "3906 ", 5) == 0) { + print_reminder = false; } } } - if (!mounted) { + } + if (print_reminder) { bsendmsg(ua, _("Do not forget to mount the drive!!!\n")); } bnet_sig(sd, BNET_TERMINATE); diff --git a/bacula/src/dird/ua_output.c b/bacula/src/dird/ua_output.c index b79001982e..52cd33cd87 100644 --- a/bacula/src/dird/ua_output.c +++ b/bacula/src/dird/ua_output.c @@ -304,7 +304,7 @@ static int do_list_cmd(UAContext *ua, char *cmd, e_list_type llist) /* List MEDIA or VOLUMES */ } else if (strcasecmp(ua->argk[i], _("media")) == 0 || strcasecmp(ua->argk[i], _("volumes")) == 0) { - int done = FALSE; + bool done = false; for (j=i+1; jargc; j++) { if (strcasecmp(ua->argk[j], _("job")) == 0 && ua->argv[j]) { bstrncpy(jr.Job, ua->argv[j], MAX_NAME_LENGTH); @@ -320,7 +320,7 @@ static int do_list_cmd(UAContext *ua, char *cmd, e_list_type llist) n = db_get_job_volume_names(ua->jcr, ua->db, jobid, &VolumeName); bsendmsg(ua, _("Jobid %d used %d Volume(s): %s\n"), jobid, n, VolumeName); free_pool_memory(VolumeName); - done = TRUE; + done = true; } /* if no job or jobid keyword found, then we list all media */ if (!done) { @@ -372,6 +372,11 @@ static int do_list_cmd(UAContext *ua, char *cmd, e_list_type llist) strcasecmp(ua->argk[i], _("nextvolume")) == 0) { JOB *job; JCR *jcr = ua->jcr; + POOL *pool; + RUN *run; + time_t runtime; + bool found = false; + i = find_arg_with_value(ua, "job"); if (i <= 0) { if ((job = select_job_resource(ua)) == NULL) { @@ -386,21 +391,37 @@ static int do_list_cmd(UAContext *ua, char *cmd, e_list_type llist) } } } - if (!complete_jcr_for_job(jcr, job, NULL)) { - return 1; - } + for (run=NULL; (run = find_next_run(run, job, runtime)); ) { + pool = run ? run->pool : NULL; + if (!complete_jcr_for_job(jcr, job, pool)) { + return 1; + } - if (!find_next_volume_for_append(jcr, &mr, 0)) { - bsendmsg(ua, "Could not find next Volume\n"); + if (!find_next_volume_for_append(jcr, &mr, 0)) { + bsendmsg(ua, _("Could not find next Volume.\n")); + if (jcr->db) { + db_close_database(jcr, jcr->db); + jcr->db = NULL; + } + return 1; + } else { + bsendmsg(ua, _("The next Volume to be used by Job \"%s\" will be %s\n"), + job->hdr.name, mr.VolumeName); + found = true; + } + if (jcr->db) { + db_close_database(jcr, jcr->db); + jcr->db = NULL; + } + } + if (jcr->db) { db_close_database(jcr, jcr->db); jcr->db = NULL; - return 1; - } else { - bsendmsg(ua, "The next Volume to be used by Job \"%s\" will be %s\n", - job->hdr.name, mr.VolumeName); } - db_close_database(jcr, jcr->db); - jcr->db = NULL; + if (!found) { + bsendmsg(ua, _("Could not find next Volume.\n")); + } + return 1; } else { bsendmsg(ua, _("Unknown list keyword: %s\n"), NPRT(ua->argk[i])); } @@ -408,6 +429,101 @@ static int do_list_cmd(UAContext *ua, char *cmd, e_list_type llist) return 1; } +/* + * For a given job, we examine all his run records + * to see if it is scheduled today or tomorrow. + */ +RUN *find_next_run(RUN *run, JOB *job, time_t &runtime) +{ + time_t now, tomorrow; + SCHED *sched; + struct tm tm; + int mday, wday, month, wom, tmday, twday, tmonth, twom, i, hour; + int woy, twoy; + int tod, tom; + + Dmsg0(200, "enter find_runs()\n"); + + sched = job->schedule; + if (sched == NULL) { /* scheduled? */ + return NULL; /* no nothing to report */ + } + /* Break down current time into components */ + now = time(NULL); + localtime_r(&now, &tm); + mday = tm.tm_mday - 1; + wday = tm.tm_wday; + month = tm.tm_mon; + wom = mday / 7; + woy = tm_woy(now); + + /* Break down tomorrow into components */ + tomorrow = now + 60 * 60 * 24; + localtime_r(&tomorrow, &tm); + tmday = tm.tm_mday - 1; + twday = tm.tm_wday; + tmonth = tm.tm_mon; + twom = tmday / 7; + twoy = tm_woy(tomorrow); + + if (run == NULL) { + run = sched->run; + } else { + run = run->next; + } + for ( ; run; run=run->next) { + /* + * Find runs in next 24 hours + */ + tod = (bit_is_set(mday, run->mday) || bit_is_set(wday, run->wday)) && + bit_is_set(month, run->month) && bit_is_set(wom, run->wom) && + bit_is_set(woy, run->woy); + + tom = (bit_is_set(tmday, run->mday) || bit_is_set(twday, run->wday)) && + bit_is_set(tmonth, run->month) && bit_is_set(twom, run->wom) && + bit_is_set(twoy, run->woy); + + Dmsg2(200, "tod=%d tom=%d\n", tod, tom); + if (tod) { /* Jobs scheduled today (next 24 hours) */ + /* find time (time_t) job is to be run */ + localtime_r(&now, &tm); + hour = 0; + for (i=tm.tm_hour; i < 24; i++) { + if (bit_is_set(i, run->hour)) { + tm.tm_hour = i; + tm.tm_min = run->minute; + tm.tm_sec = 0; + runtime = mktime(&tm); + if (runtime > now) { + return run; /* found it, return run resource */ + } + } + } + } + +// Dmsg2(200, "runtime=%d now=%d\n", runtime, now); + if (tom) { /* look at jobs scheduled tomorrow */ + localtime_r(&tomorrow, &tm); + hour = 0; + for (i=0; i < 24; i++) { + if (bit_is_set(i, run->hour)) { + hour = i; + break; + } + } + tm.tm_hour = hour; + tm.tm_min = run->minute; + tm.tm_sec = 0; + runtime = mktime(&tm); + Dmsg2(200, "truntime=%d now=%d\n", runtime, now); + if (runtime < tomorrow) { + return run; /* found it, return run resource */ + } + } + } /* end for loop over runs */ + /* Nothing found */ + return NULL; +} /* * Fill in the remaining fields of the jcr as if it * is going to run the job. @@ -438,8 +554,10 @@ int complete_jcr_for_job(JCR *jcr, JOB *job, POOL *pool) if (create_pool(jcr, jcr->db, jcr->pool, POOL_OP_CREATE) < 0) { Jmsg(jcr, M_FATAL, 0, _("Pool %s not in database. %s"), pr.Name, db_strerror(jcr->db)); - db_close_database(jcr, jcr->db); - jcr->db = NULL; + if (jcr->db) { + db_close_database(jcr, jcr->db); + jcr->db = NULL; + } return 0; } else { Jmsg(jcr, M_INFO, 0, _("Pool %s created in database.\n"), pr.Name); @@ -538,11 +656,12 @@ again: len = bvsnprintf(msg, maxlen, fmt, arg_ptr); va_end(arg_ptr); if (len < 0 || len >= maxlen) { - msg = realloc_pool_memory(msg, maxlen + 200); + msg = realloc_pool_memory(msg, maxlen + maxlen/2); goto again; } if (bs) { + bs->msg = msg; bs->msglen = len; bnet_send(bs); } else { /* No UA, send to Job */ diff --git a/bacula/src/dird/ua_prune.c b/bacula/src/dird/ua_prune.c index cd6684a3f1..2278405d80 100644 --- a/bacula/src/dird/ua_prune.c +++ b/bacula/src/dird/ua_prune.c @@ -270,7 +270,7 @@ int prune_files(UAContext *ua, CLIENT *client) del.JobId = (JobId_t *)malloc(sizeof(JobId_t) * del.max_ids); - /* Now process same set but making delete list */ + /* Now process same set but making a delete list */ db_sql_query(ua->db, query, file_delete_handler, (void *)&del); for (i=0; i < del.num_ids; i++) { @@ -286,7 +286,7 @@ int prune_files(UAContext *ua, CLIENT *client) * Now mark Job as having files purged. This is necessary to * avoid having too many Jobs to process in future prunings. If * we don't do this, the number of JobId's in our in memory list - * will grow very large. + * could grow very large. */ Mmsg(&query, upd_Purged, del.JobId[i]); db_sql_query(ua->db, query, NULL, (void *)NULL); @@ -415,7 +415,7 @@ int prune_jobs(UAContext *ua, CLIENT *client, int JobType) switch (JobType) { case JT_ADMIN: case JT_BACKUP: - Mmsg(&query, select_backup_del, ed1, cr.ClientId); + Mmsg(&query, select_backup_del, ed1, ed1, cr.ClientId); break; case JT_RESTORE: Mmsg(&query, select_restore_del, ed1, cr.ClientId); diff --git a/bacula/src/dird/ua_restore.c b/bacula/src/dird/ua_restore.c index c195a50ef4..36733d0ab2 100644 --- a/bacula/src/dird/ua_restore.c +++ b/bacula/src/dird/ua_restore.c @@ -38,7 +38,8 @@ /* Imported functions */ -extern int runcmd(UAContext *ua, char *cmd); +extern int run_cmd(UAContext *ua, char *cmd); +extern void print_bsr(UAContext *ua, RBSR *bsr); /* Imported variables */ extern char *uar_list_jobs, *uar_file, *uar_sel_files; @@ -100,15 +101,17 @@ static void build_directory_tree(UAContext *ua, RESTORE_CTX *rx); static void free_rx(RESTORE_CTX *rx); static void split_path_and_filename(RESTORE_CTX *rx, char *fname); static int jobid_fileindex_handler(void *ctx, int num_fields, char **row); -static int insert_file_into_findex_list(UAContext *ua, RESTORE_CTX *rx, char *file); -static void insert_one_file(UAContext *ua, RESTORE_CTX *rx); +static int insert_file_into_findex_list(UAContext *ua, RESTORE_CTX *rx, char *file, + char *date); +static void insert_one_file(UAContext *ua, RESTORE_CTX *rx, char *date); static int get_client_name(UAContext *ua, RESTORE_CTX *rx); +static int get_date(UAContext *ua, char *date, int date_len); /* * Restore files * */ -int restorecmd(UAContext *ua, char *cmd) +int restore_cmd(UAContext *ua, char *cmd) { RESTORE_CTX rx; /* restore context */ JOB *job = NULL; @@ -128,8 +131,7 @@ int restorecmd(UAContext *ua, char *cmd) } if (!open_db(ua)) { - free_rx(&rx); - return 0; + goto bail_out; } /* Ensure there is at least one Restore Job */ @@ -147,8 +149,7 @@ int restorecmd(UAContext *ua, char *cmd) bsendmsg(ua, _( "No Restore Job Resource found. You must create at least\n" "one before running this command.\n")); - free_rx(&rx); - return 0; + goto bail_out; } /* @@ -159,29 +160,25 @@ int restorecmd(UAContext *ua, char *cmd) */ switch (user_select_jobids_or_files(ua, &rx)) { case 0: - free_rx(&rx); - return 0; /* error */ + goto bail_out; case 1: /* select by jobid */ build_directory_tree(ua, &rx); break; - case 2: + case 2: /* select by filename, no tree needed */ break; } if (rx.bsr->JobId) { if (!complete_bsr(ua, rx.bsr)) { /* find Vol, SessId, SessTime from JobIds */ bsendmsg(ua, _("Unable to construct a valid BSR. Cannot continue.\n")); - free_rx(&rx); - return 0; + goto bail_out; } -// print_bsr(ua, rx.bsr); write_bsr_file(ua, rx.bsr); - bsendmsg(ua, _("\n%u file%s selected to restore.\n\n"), rx.selected_files, + bsendmsg(ua, _("\n%u file%s selected to be restored.\n\n"), rx.selected_files, rx.selected_files==1?"":"s"); } else { - bsendmsg(ua, _("No files selected to restore.\n")); - free_rx(&rx); - return 0; + bsendmsg(ua, _("No files selected to be restored.\n")); + goto bail_out; } if (rx.restore_jobs == 1) { @@ -190,12 +187,14 @@ int restorecmd(UAContext *ua, char *cmd) job = select_restore_job_resource(ua); } if (!job) { - bsendmsg(ua, _("No Restore Job resource found!\n")); - free_rx(&rx); - return 0; + goto bail_out; } get_client_name(ua, &rx); + if (!rx.ClientName) { + bsendmsg(ua, _("No Restore Job resource found!\n")); + goto bail_out; + } /* Build run command */ if (rx.where) { @@ -210,19 +209,27 @@ int restorecmd(UAContext *ua, char *cmd) job->hdr.name, rx.ClientName, rx.store?rx.store->hdr.name:"", working_directory); } - + if (find_arg(ua, _("run")) >= 0) { + pm_strcat(&ua->cmd, " run"); /* pass it on to the run command */ + } Dmsg1(400, "Submitting: %s\n", ua->cmd); parse_ua_args(ua); - runcmd(ua, ua->cmd); + run_cmd(ua, ua->cmd); bsendmsg(ua, _("Restore command done.\n")); free_rx(&rx); return 1; + +bail_out: + free_rx(&rx); + return 0; + } static void free_rx(RESTORE_CTX *rx) { free_bsr(rx->bsr); + rx->bsr = NULL; if (rx->JobIds) { free_pool_memory(rx->JobIds); rx->JobIds = NULL; @@ -255,7 +262,6 @@ static int get_client_name(UAContext *ua, RESTORE_CTX *rx) } memset(&cr, 0, sizeof(cr)); if (!get_client_dbr(ua, &cr)) { - free_rx(rx); return 0; } bstrncpy(rx->ClientName, cr.Name, sizeof(rx->ClientName)); @@ -272,6 +278,7 @@ static int user_select_jobids_or_files(UAContext *ua, RESTORE_CTX *rx) { char *p; char date[MAX_TIME_LENGTH]; + bool have_date = false; JobId_t JobId; JOB_DBR jr; bool done = false; @@ -284,6 +291,7 @@ static int user_select_jobids_or_files(UAContext *ua, RESTORE_CTX *rx) "Select the most recent backup for a client", "Select backup for a client before a specified time", "Enter a list of files to restore", + "Enter a list of files to restore before a specified time", "Cancel", NULL }; @@ -292,24 +300,26 @@ static int user_select_jobids_or_files(UAContext *ua, RESTORE_CTX *rx) "current", /* 1 */ "before", /* 2 */ "file", /* 3 */ + "select", /* 4 */ NULL }; + *rx->JobIds = 0; switch (find_arg_keyword(ua, kw)) { case 0: /* jobid */ - i = find_arg_with_value(ua, _("jobid")); - if (i < 0) { - return 0; + for ( ;; ) { + i = find_arg_with_value(ua, _("jobid")); + if (i < 0) { + break; + } + pm_strcpy(&rx->JobIds, ua->argv[i]); + ua->argk[i][0] = 0; /* "consume" jobid= */ } - pm_strcpy(&rx->JobIds, ua->argv[i]); done = true; break; case 1: /* current */ bstrutime(date, sizeof(date), time(NULL)); - if (!select_backups_before_date(ua, rx, date)) { - return 0; - } - done = true; + have_date = true; break; case 2: /* before */ i = find_arg_with_value(ua, _("before")); @@ -321,12 +331,12 @@ static int user_select_jobids_or_files(UAContext *ua, RESTORE_CTX *rx) return 0; } bstrncpy(date, ua->argv[i], sizeof(date)); - if (!select_backups_before_date(ua, rx, date)) { - return 0; - } - done = true; + have_date = true; break; case 3: /* file */ + if (!have_date) { + bstrutime(date, sizeof(date), time(NULL)); + } if (!get_client_name(ua, rx)) { return 0; } @@ -336,12 +346,21 @@ static int user_select_jobids_or_files(UAContext *ua, RESTORE_CTX *rx) break; } pm_strcpy(&ua->cmd, ua->argv[i]); - insert_one_file(ua, rx); - ua->argk[i][0] = 0; + insert_one_file(ua, rx, date); + ua->argk[i][0] = 0; /* "consume" the file= */ } /* Check MediaType and select storage that corresponds */ get_storage_from_mediatype(ua, &rx->name_list, rx); return 2; + case 4: /* select */ + if (!have_date) { + bstrutime(date, sizeof(date), time(NULL)); + } + if (!select_backups_before_date(ua, rx, date)) { + return 0; + } + done = true; + break; default: break; } @@ -402,26 +421,44 @@ static int user_select_jobids_or_files(UAContext *ua, RESTORE_CTX *rx) } break; case 5: /* select backup at specified time */ - bsendmsg(ua, _("The restored files will the most current backup\n" - "BEFORE the date you specify below.\n\n")); + if (!get_date(ua, date, sizeof(date))) { + return 0; + } + if (!select_backups_before_date(ua, rx, date)) { + return 0; + } + break; + case 6: /* Enter files */ + bstrutime(date, sizeof(date), time(NULL)); + if (!get_client_name(ua, rx)) { + return 0; + } + bsendmsg(ua, _("Enter file names, or < to enter a filename\n" + "containg a list of file names, and terminate\n" + "them with a blank line.\n")); for ( ;; ) { - if (!get_cmd(ua, _("Enter date as YYYY-MM-DD HH:MM:SS :"))) { + if (!get_cmd(ua, _("Enter filename: "))) { return 0; } - if (str_to_utime(ua->cmd) != 0) { + len = strlen(ua->cmd); + if (len == 0) { break; } - bsendmsg(ua, _("Improper date format.\n")); - } - bstrncpy(date, ua->cmd, sizeof(date)); - if (!select_backups_before_date(ua, rx, date)) { + insert_one_file(ua, rx, date); + } + /* Check MediaType and select storage that corresponds */ + get_storage_from_mediatype(ua, &rx->name_list, rx); + return 2; + case 7: /* enter files backed up before specified time */ + if (!get_date(ua, date, sizeof(date))) { return 0; } - break; - case 6: /* Enter files */ if (!get_client_name(ua, rx)) { return 0; } + bsendmsg(ua, _("Enter file names, or < to enter a filename\n" + "containg a list of file names, and terminate\n" + "them with a blank line.\n")); for ( ;; ) { if (!get_cmd(ua, _("Enter filename: "))) { return 0; @@ -430,13 +467,14 @@ static int user_select_jobids_or_files(UAContext *ua, RESTORE_CTX *rx) if (len == 0) { break; } - insert_one_file(ua, rx); + insert_one_file(ua, rx, date); } /* Check MediaType and select storage that corresponds */ get_storage_from_mediatype(ua, &rx->name_list, rx); return 2; + - case 7: /* Cancel or quit */ + case 8: /* Cancel or quit */ return 0; } } @@ -473,7 +511,24 @@ static int user_select_jobids_or_files(UAContext *ua, RESTORE_CTX *rx) return 1; } -static void insert_one_file(UAContext *ua, RESTORE_CTX *rx) +static int get_date(UAContext *ua, char *date, int date_len) +{ + bsendmsg(ua, _("The restored files will the most current backup\n" + "BEFORE the date you specify below.\n\n")); + for ( ;; ) { + if (!get_cmd(ua, _("Enter date as YYYY-MM-DD HH:MM:SS :"))) { + return 0; + } + if (str_to_utime(ua->cmd) != 0) { + break; + } + bsendmsg(ua, _("Improper date format.\n")); + } + bstrncpy(date, ua->cmd, date_len); + return 1; +} + +static void insert_one_file(UAContext *ua, RESTORE_CTX *rx, char *date) { FILE *ffd; char file[5000]; @@ -490,14 +545,14 @@ static void insert_one_file(UAContext *ua, RESTORE_CTX *rx) } while (fgets(file, sizeof(file), ffd)) { line++; - if (!insert_file_into_findex_list(ua, rx, file)) { + if (!insert_file_into_findex_list(ua, rx, file, date)) { bsendmsg(ua, _("Error occurred on line %d of %s\n"), line, p); } } fclose(ffd); break; default: - insert_file_into_findex_list(ua, rx, ua->cmd); + insert_file_into_findex_list(ua, rx, ua->cmd, date); break; } } @@ -507,12 +562,14 @@ static void insert_one_file(UAContext *ua, RESTORE_CTX *rx) * lookup the most recent backup in the catalog to get the JobId * and FileIndex, then insert them into the findex list. */ -static int insert_file_into_findex_list(UAContext *ua, RESTORE_CTX *rx, char *file) +static int insert_file_into_findex_list(UAContext *ua, RESTORE_CTX *rx, char *file, + char *date) { strip_trailing_junk(file); split_path_and_filename(rx, file); - Mmsg(&rx->query, uar_jobid_fileindex, rx->path, rx->fname, rx->ClientName); + Mmsg(&rx->query, uar_jobid_fileindex, date, rx->path, rx->fname, rx->ClientName); rx->found = false; + /* Find and insert jobid and File Index */ if (!db_sql_query(ua->db, rx->query, jobid_fileindex_handler, (void *)rx)) { bsendmsg(ua, _("Query failed: %s. ERR=%s\n"), rx->query, db_strerror(ua->db)); @@ -523,7 +580,7 @@ static int insert_file_into_findex_list(UAContext *ua, RESTORE_CTX *rx, char *fi } rx->selected_files++; /* - * Find the FileSets for this JobId and add to the name_list + * Find the MediaTypes for this JobId and add to the name_list */ Mmsg(&rx->query, uar_mediatype, rx->JobId); if (!db_sql_query(ua->db, rx->query, unique_name_list_handler, (void *)&rx->name_list)) { @@ -620,7 +677,7 @@ static void build_directory_tree(UAContext *ua, RESTORE_CTX *rx) bsendmsg(ua, "%s", db_strerror(ua->db)); } /* - * Find the FileSets for this JobId and add to the name_list + * Find the MediaTypes for this JobId and add to the name_list */ Mmsg(&rx->query, uar_mediatype, JobId); if (!db_sql_query(ua->db, rx->query, unique_name_list_handler, (void *)&rx->name_list)) { @@ -666,6 +723,7 @@ static int select_backups_before_date(UAContext *ua, RESTORE_CTX *rx, char *date CLIENT_DBR cr; char fileset_name[MAX_NAME_LENGTH]; char ed1[50]; + int i; /* Create temp tables */ @@ -687,27 +745,40 @@ static int select_backups_before_date(UAContext *ua, RESTORE_CTX *rx, char *date bstrncpy(rx->ClientName, cr.Name, sizeof(rx->ClientName)); /* - * Select FileSet + * Get FileSet */ - Mmsg(&rx->query, uar_sel_fileset, cr.ClientId, cr.ClientId); - start_prompt(ua, _("The defined FileSet resources are:\n")); - if (!db_sql_query(ua->db, rx->query, fileset_handler, (void *)ua)) { - bsendmsg(ua, "%s\n", db_strerror(ua->db)); + memset(&fsr, 0, sizeof(fsr)); + i = find_arg_with_value(ua, "FileSet"); + if (i >= 0) { + bstrncpy(fsr.FileSet, ua->argv[i], sizeof(fsr.FileSet)); + if (!db_get_fileset_record(ua->jcr, ua->db, &fsr)) { + bsendmsg(ua, _("Error getting FileSet \"%s\": ERR=%s\n"), fsr.FileSet, + db_strerror(ua->db)); + i = -1; + } } - if (do_prompt(ua, _("FileSet"), _("Select FileSet resource"), + if (i < 0) { /* fileset not found */ + Mmsg(&rx->query, uar_sel_fileset, cr.ClientId, cr.ClientId); + start_prompt(ua, _("The defined FileSet resources are:\n")); + if (!db_sql_query(ua->db, rx->query, fileset_handler, (void *)ua)) { + bsendmsg(ua, "%s\n", db_strerror(ua->db)); + } + if (do_prompt(ua, _("FileSet"), _("Select FileSet resource"), fileset_name, sizeof(fileset_name)) < 0) { - goto bail_out; - } - fsr.FileSetId = atoi(fileset_name); /* Id is first part of name */ - if (!db_get_fileset_record(ua->jcr, ua->db, &fsr)) { - bsendmsg(ua, _("Error getting FileSet record: %s\n"), db_strerror(ua->db)); - bsendmsg(ua, _("This probably means you modified the FileSet.\n" + goto bail_out; + } + + bstrncpy(fsr.FileSet, fileset_name, sizeof(fsr.FileSet)); + if (!db_get_fileset_record(ua->jcr, ua->db, &fsr)) { + bsendmsg(ua, _("Error getting FileSet record: %s\n"), db_strerror(ua->db)); + bsendmsg(ua, _("This probably means you modified the FileSet.\n" "Continuing anyway.\n")); + } } /* Find JobId of last Full backup for this client, fileset */ - Mmsg(&rx->query, uar_last_full, cr.ClientId, cr.ClientId, date, fsr.FileSetId); + Mmsg(&rx->query, uar_last_full, cr.ClientId, cr.ClientId, date, fsr.FileSet); if (!db_sql_query(ua->db, rx->query, NULL, NULL)) { bsendmsg(ua, "%s\n", db_strerror(ua->db)); goto bail_out; @@ -718,7 +789,7 @@ static int select_backups_before_date(UAContext *ua, RESTORE_CTX *rx, char *date bsendmsg(ua, "%s\n", db_strerror(ua->db)); goto bail_out; } - /* Note, this is needed as I don't seem to get the callback + /* Note, this is needed because I don't seem to get the callback * from the call just above. */ rx->JobTDate = 0; @@ -732,7 +803,7 @@ static int select_backups_before_date(UAContext *ua, RESTORE_CTX *rx, char *date /* Now find all Incremental/Decremental Jobs after Full save */ Mmsg(&rx->query, uar_inc_dec, edit_uint64(rx->JobTDate, ed1), date, - cr.ClientId, fsr.FileSetId); + cr.ClientId, fsr.FileSet); if (!db_sql_query(ua->db, rx->query, NULL, NULL)) { bsendmsg(ua, "%s\n", db_strerror(ua->db)); } @@ -759,6 +830,7 @@ bail_out: return stat; } + /* Return next JobId from comma separated list */ static int next_jobid_from_list(char **p, uint32_t *JobId) { @@ -826,14 +898,15 @@ static int last_full_handler(void *ctx, int num_fields, char **row) } /* - * Callback handler build fileset prompt list + * Callback handler build FileSet name prompt list */ static int fileset_handler(void *ctx, int num_fields, char **row) { - char prompt[MAX_NAME_LENGTH+200]; - snprintf(prompt, sizeof(prompt), "%s %s %s", row[0], row[1], row[2]); - add_prompt((UAContext *)ctx, prompt); + /* row[0] = FileSet (name) */ + if (row[0]) { + add_prompt((UAContext *)ctx, row[0]); + } return 0; } @@ -891,6 +964,7 @@ static void free_name_list(NAME_LIST *name_list) } if (name_list->name) { free(name_list->name); + name_list->name = NULL; } name_list->max_ids = 0; name_list->num_ids = 0; @@ -898,9 +972,6 @@ static void free_name_list(NAME_LIST *name_list) static void get_storage_from_mediatype(UAContext *ua, NAME_LIST *name_list, RESTORE_CTX *rx) { - char name[MAX_NAME_LENGTH]; - STORE *store = NULL; - if (name_list->num_ids > 1) { bsendmsg(ua, _("Warning, the JobIds that you selected refer to more than one MediaType.\n" "Restore is not possible. The MediaTypes used are:\n")); @@ -915,16 +986,8 @@ static void get_storage_from_mediatype(UAContext *ua, NAME_LIST *name_list, REST return; } - start_prompt(ua, _("The defined Storage resources are:\n")); - LockRes(); - while ((store = (STORE *)GetNextRes(R_STORAGE, (RES *)store))) { - if (strcmp(store->media_type, name_list->name[0]) == 0) { - add_prompt(ua, store->hdr.name); - } - } - UnlockRes(); - do_prompt(ua, _("Storage"), _("Select Storage resource"), name, sizeof(name)); - rx->store = (STORE *)GetResWithName(R_STORAGE, name); + rx->store = get_storage_resource(ua, false /* don't use default */); + if (!rx->store) { bsendmsg(ua, _("\nWarning. Unable to find Storage resource for\n" "MediaType %s, needed by the Jobs you selected.\n" diff --git a/bacula/src/dird/ua_run.c b/bacula/src/dird/ua_run.c index 9a4a559dce..eaef046732 100644 --- a/bacula/src/dird/ua_run.c +++ b/bacula/src/dird/ua_run.c @@ -44,15 +44,17 @@ extern struct s_kw ReplaceOptions[]; * run jobid=nn * */ -int runcmd(UAContext *ua, char *cmd) +int run_cmd(UAContext *ua, char *cmd) { JCR *jcr; char *job_name, *level_name, *jid, *store_name, *pool_name; char *where, *fileset_name, *client_name, *bootstrap, *replace; - char *when; + char *when, *verify_job_name; int Priority = 0; - int i, j, found, opt; + int i, j, opt; + bool found; JOB *job = NULL; + JOB *verify_job = NULL; STORE *store = NULL; CLIENT *client = NULL; FILESET *fileset = NULL; @@ -71,9 +73,12 @@ int runcmd(UAContext *ua, char *cmd) N_("when"), N_("priority"), N_("yes"), /* 12 -- if you change this change YES_POS too */ + N_("run"), /* 13 -- if you change this change RUN_POS too */ + N_("verifyjob"), NULL}; #define YES_POS 12 +#define RUN_POS 13 if (!open_db(ua)) { return 1; @@ -90,14 +95,15 @@ int runcmd(UAContext *ua, char *cmd) fileset_name = NULL; bootstrap = NULL; replace = NULL; + verify_job_name = NULL; for (i=1; iargc; i++) { - found = False; + found = false; Dmsg2(200, "Doing arg %d = %s\n", i, ua->argk[i]); for (j=0; !found && kw[j]; j++) { if (strcasecmp(ua->argk[i], _(kw[j])) == 0) { - /* Note, yes has no value, so do not err */ - if (!ua->argv[i] && j != YES_POS /*yes*/) { + /* Note, yes and run have no value, so do not err */ + if (!ua->argv[i] && (j != YES_POS /*yes*/ && j != RUN_POS)) { bsendmsg(ua, _("Value missing for keyword %s\n"), ua->argk[i]); return 1; } @@ -109,7 +115,7 @@ int runcmd(UAContext *ua, char *cmd) return 1; } job_name = ua->argv[i]; - found = True; + found = true; break; case 1: /* JobId */ if (jid) { @@ -117,7 +123,7 @@ int runcmd(UAContext *ua, char *cmd) return 1; } jid = ua->argv[i]; - found = True; + found = true; break; case 2: /* client */ if (client_name) { @@ -125,7 +131,7 @@ int runcmd(UAContext *ua, char *cmd) return 1; } client_name = ua->argv[i]; - found = True; + found = true; break; case 3: /* fileset */ if (fileset_name) { @@ -133,7 +139,7 @@ int runcmd(UAContext *ua, char *cmd) return 1; } fileset_name = ua->argv[i]; - found = True; + found = true; break; case 4: /* level */ if (level_name) { @@ -141,7 +147,7 @@ int runcmd(UAContext *ua, char *cmd) return 1; } level_name = ua->argv[i]; - found = True; + found = true; break; case 5: /* storage */ if (store_name) { @@ -149,7 +155,7 @@ int runcmd(UAContext *ua, char *cmd) return 1; } store_name = ua->argv[i]; - found = True; + found = true; break; case 6: /* pool */ if (pool_name) { @@ -157,7 +163,7 @@ int runcmd(UAContext *ua, char *cmd) return 1; } pool_name = ua->argv[i]; - found = True; + found = true; break; case 7: /* where */ if (where) { @@ -165,7 +171,7 @@ int runcmd(UAContext *ua, char *cmd) return 1; } where = ua->argv[i]; - found = True; + found = true; break; case 8: /* bootstrap */ if (bootstrap) { @@ -173,7 +179,7 @@ int runcmd(UAContext *ua, char *cmd) return 1; } bootstrap = ua->argv[i]; - found = True; + found = true; break; case 9: /* replace */ if (replace) { @@ -181,7 +187,7 @@ int runcmd(UAContext *ua, char *cmd) return 1; } replace = ua->argv[i]; - found = True; + found = true; break; case 10: /* When */ if (when) { @@ -189,7 +195,7 @@ int runcmd(UAContext *ua, char *cmd) return 1; } when = ua->argv[i]; - found = True; + found = true; break; case 11: /* Priority */ if (Priority) { @@ -203,8 +209,18 @@ int runcmd(UAContext *ua, char *cmd) } break; case 12: /* yes */ - found = True; + case 13: /* run */ + found = true; break; + case 14: /* Verify Job */ + if (verify_job_name) { + bsendmsg(ua, _("Verify Job specified twice.\n")); + return 1; + } + verify_job_name = ua->argv[i]; + found = true; + break; + default: break; } @@ -297,6 +313,15 @@ int runcmd(UAContext *ua, char *cmd) return 1; } + if (verify_job_name) { + verify_job = (JOB *)GetResWithName(R_JOB, verify_job_name); + if (!verify_job) { + bsendmsg(ua, _("Verify Job \"%s\" not found.\n"), verify_job_name); + verify_job = select_job_resource(ua); + } + } else { + verify_job = job->verify_job; + } /* Create JCR to run job */ jcr = new_jcr(sizeof(JCR), dird_free_jcr); @@ -396,6 +421,32 @@ Priority: %d\n"), } } level_name = NULL; + if (jcr->JobType == JT_BACKUP) { + bsendmsg(ua, _("Run %s job\n\ +JobName: %s\n\ +FileSet: %s\n\ +Level: %s\n\ +Client: %s\n\ +Storage: %s\n\ +Pool: %s\n\ +When: %s\n\ +Priority: %d\n"), + _("Backup"), + job->hdr.name, + jcr->fileset->hdr.name, + level_to_str(jcr->JobLevel), + jcr->client->hdr.name, + jcr->store->hdr.name, + NPRT(jcr->pool->hdr.name), + bstrutime(dt, sizeof(dt), jcr->sched_time), + jcr->JobPriority); + } else { /* JT_VERIFY */ + char *Name; + if (jcr->job->verify_job) { + Name = jcr->job->verify_job->hdr.name; + } else { + Name = ""; + } bsendmsg(ua, _("Run %s job\n\ JobName: %s\n\ FileSet: %s\n\ @@ -403,17 +454,20 @@ Level: %s\n\ Client: %s\n\ Storage: %s\n\ Pool: %s\n\ +Verify Job: %s\n\ When: %s\n\ Priority: %d\n"), - jcr->JobType==JT_BACKUP?_("Backup"):_("Verify"), + _("Verify"), job->hdr.name, jcr->fileset->hdr.name, level_to_str(jcr->JobLevel), jcr->client->hdr.name, jcr->store->hdr.name, NPRT(jcr->pool->hdr.name), + Name, bstrutime(dt, sizeof(dt), jcr->sched_time), jcr->JobPriority); + } break; case JT_RESTORE: if (jcr->RestoreJobId == 0 && !jcr->RestoreBootstrap) { @@ -513,6 +567,9 @@ Priority: %d\n"), if (jcr->JobType == JT_BACKUP || jcr->JobType == JT_VERIFY) { add_prompt(ua, _("Pool")); /* 7 */ + if (jcr->JobType == JT_VERIFY) { + add_prompt(ua, _("Verify Job")); /* 8 */ + } } else if (jcr->JobType == JT_RESTORE) { add_prompt(ua, _("Bootstrap")); /* 7 */ add_prompt(ua, _("Where")); /* 8 */ @@ -554,6 +611,7 @@ Priority: %d\n"), add_prompt(ua, _("Initialize Catalog")); add_prompt(ua, _("Verify Catalog")); add_prompt(ua, _("Verify Volume to Catalog")); + add_prompt(ua, _("Verify Disk to Catalog")); add_prompt(ua, _("Verify Volume Data (not yet implemented)")); switch (do_prompt(ua, "", _("Select level"), NULL, 0)) { case 0: @@ -566,6 +624,9 @@ Priority: %d\n"), jcr->JobLevel = L_VERIFY_VOLUME_TO_CATALOG; break; case 3: + jcr->JobLevel = L_VERIFY_DISK_TO_CATALOG; + break; + case 4: jcr->JobLevel = L_VERIFY_DATA; break; default: @@ -636,6 +697,7 @@ Priority: %d\n"), } goto try_again; case 7: + /* Pool or Bootstrap depending on JobType */ if (jcr->JobType == JT_BACKUP || jcr->JobType == JT_VERIFY) { /* Pool */ pool = select_pool_resource(ua); @@ -668,6 +730,16 @@ Priority: %d\n"), } goto try_again; case 8: + /* Verify Job */ + if (jcr->JobType == JT_VERIFY) { + JOB *job = select_job_resource(ua); + if (job) { + jcr->job->verify_job = job; + } else { + jcr->job->verify_job = NULL; + } + goto try_again; + } /* Where */ if (!get_cmd(ua, _("Please enter path prefix for restore (/ for none): "))) { break; diff --git a/bacula/src/dird/ua_select.c b/bacula/src/dird/ua_select.c index f3afec0d05..98d2528c4a 100644 --- a/bacula/src/dird/ua_select.c +++ b/bacula/src/dird/ua_select.c @@ -84,6 +84,12 @@ int find_arg_keyword(UAContext *ua, char **list) return -1; } +/* + * Given one keyword, find the first one that + * is in the argument list. + * Returns: argk index (always gt 0) + * -1 if not found + */ int find_arg(UAContext *ua, char *keyword) { for (int i=1; iargc; i++) { @@ -272,7 +278,8 @@ CLIENT *get_client_resource(UAContext *ua) int i; for (i=1; iargc; i++) { - if (strcasecmp(ua->argk[i], _("client")) == 0 && ua->argv[i]) { + if ((strcasecmp(ua->argk[i], _("client")) == 0 || + strcasecmp(ua->argk[i], _("fd")) == 0) && ua->argv[i]) { client = (CLIENT *)GetResWithName(R_CLIENT, ua->argv[i]); if (client) { return client; @@ -305,7 +312,8 @@ int get_client_dbr(UAContext *ua, CLIENT_DBR *cr) bsendmsg(ua, _("Could not find Client %s: ERR=%s"), cr->Name, db_strerror(ua->db)); } for (i=1; iargc; i++) { - if (strcasecmp(ua->argk[i], _("client")) == 0 && ua->argv[i]) { + if ((strcasecmp(ua->argk[i], _("client")) == 0 || + strcasecmp(ua->argk[i], _("fd")) == 0) && ua->argv[i]) { bstrncpy(cr->Name, ua->argv[i], sizeof(cr->Name)); if (!db_get_client_record(ua->jcr, ua->db, cr)) { bsendmsg(ua, _("Could not find Client %s: ERR=%s"), ua->argv[i], @@ -341,7 +349,7 @@ int select_client_dbr(UAContext *ua, CLIENT_DBR *cr) return 0; } if (num_clients <= 0) { - bsendmsg(ua, _("No clients defined. Run a job to create one.\n")); + bsendmsg(ua, _("No clients defined. You must run a job before using this command.\n")); return 0; } @@ -730,22 +738,24 @@ STORE *get_storage_resource(UAContext *ua, int use_default) for (i=1; iargc; i++) { if (use_default && !ua->argv[i]) { + /* Ignore scan and barcode(s) keywords */ + if (strncasecmp("scan", ua->argk[i], 4) == 0 || + strncasecmp("barcode", ua->argk[i], 7) == 0) { + continue; + } /* Default argument is storage */ if (store_name) { bsendmsg(ua, _("Storage name given twice.\n")); return NULL; } - /* Ignore barcode(s) keywords */ - if (strncasecmp("barcode", ua->argk[i], 7) == 0) { - continue; - } store_name = ua->argk[i]; if (*store_name == '?') { *store_name = 0; break; } } else { - if (strcasecmp(ua->argk[i], _("storage")) == 0) { + if (strcasecmp(ua->argk[i], _("storage")) == 0 || + strcasecmp(ua->argk[i], _("sd")) == 0) { store_name = ua->argv[i]; break; diff --git a/bacula/src/dird/ua_status.c b/bacula/src/dird/ua_status.c index 44ca5bac40..06a96bbbe2 100644 --- a/bacula/src/dird/ua_status.c +++ b/bacula/src/dird/ua_status.c @@ -34,7 +34,9 @@ extern char my_name[]; extern time_t daemon_start_time; extern struct s_last_job last_job; -static void print_jobs_scheduled(UAContext *ua); +static void list_scheduled_jobs(UAContext *ua); +static void list_running_jobs(UAContext *ua); +static void list_terminated_jobs(UAContext *ua); static void do_storage_status(UAContext *ua, STORE *store); static void do_client_status(UAContext *ua, CLIENT *client); static void do_director_status(UAContext *ua, char *cmd); @@ -190,133 +192,28 @@ static void do_all_status(UAContext *ua, char *cmd) static void do_director_status(UAContext *ua, char *cmd) { - JCR *jcr; - int njobs = 0; - char *msg; - char dt[MAX_TIME_LENGTH], b1[30], b2[30]; - int pool_mem = FALSE; + char dt[MAX_TIME_LENGTH]; bsendmsg(ua, "%s Version: " VERSION " (" BDATE ") %s %s %s\n", my_name, HOST_OS, DISTNAME, DISTVER); bstrftime(dt, sizeof(dt), daemon_start_time); + strcpy(dt+7, dt+9); /* cut century */ bsendmsg(ua, _("Daemon started %s, %d Job%s run.\n"), dt, last_job.NumJobs, last_job.NumJobs == 1 ? "" : "s"); - if (last_job.NumJobs > 0) { - char termstat[30]; - - bstrftime(dt, sizeof(dt), last_job.end_time); - bsendmsg(ua, _("Last Job %s finished at %s\n"), last_job.Job, dt); - jobstatus_to_ascii(last_job.JobStatus, termstat, sizeof(termstat)); - - bsendmsg(ua, _(" Files=%s Bytes=%s Termination Status=%s\n"), - edit_uint64_with_commas(last_job.JobFiles, b1), - edit_uint64_with_commas(last_job.JobBytes, b2), - termstat); - } - lock_jcr_chain(); - for (jcr=NULL; (jcr=get_next_jcr(jcr)); njobs++) { - if (jcr->JobId == 0) { /* this is us */ - bstrftime(dt, sizeof(dt), jcr->start_time); - bsendmsg(ua, _("Console connected at %s\n"), dt); - free_locked_jcr(jcr); - njobs--; - continue; - } - switch (jcr->JobStatus) { - case JS_Created: - msg = _("is waiting execution"); - break; - case JS_Running: - msg = _("is running"); - break; - case JS_Blocked: - msg = _("is blocked"); - break; - case JS_Terminated: - msg = _("has terminated"); - break; - case JS_ErrorTerminated: - msg = _("has erred"); - break; - case JS_Canceled: - msg = _("has been canceled"); - break; - case JS_WaitFD: - msg = (char *) get_pool_memory(PM_FNAME); - Mmsg(&msg, _("is waiting on Client %s"), jcr->client->hdr.name); - pool_mem = TRUE; - break; - case JS_WaitSD: - msg = (char *) get_pool_memory(PM_FNAME); - Mmsg(&msg, _("is waiting on Storage %s"), jcr->store->hdr.name); - pool_mem = TRUE; - break; - case JS_WaitStoreRes: - msg = _("is waiting on max Storage jobs"); - break; - case JS_WaitClientRes: - msg = _("is waiting on max Client jobs"); - break; - case JS_WaitJobRes: - msg = _("is waiting on max Job jobs"); - break; - case JS_WaitPriority: - msg = _("is waiting for higher priority jobs to finish"); - break; - case JS_WaitMaxJobs: - msg = _("is waiting on max total jobs"); - break; - case JS_WaitStartTime: - msg = _("is waiting for its start time"); - break; - - - default: - msg = (char *) get_pool_memory(PM_FNAME); - Mmsg(&msg, _("is in unknown state %c"), jcr->JobStatus); - pool_mem = TRUE; - break; - } - /* - * Now report Storage daemon status code - */ - switch (jcr->SDJobStatus) { - case JS_WaitMount: - if (pool_mem) { - free_pool_memory(msg); - pool_mem = FALSE; - } - msg = _("is waiting for a mount request"); - break; - case JS_WaitMedia: - if (pool_mem) { - free_pool_memory(msg); - pool_mem = FALSE; - } - msg = _("is waiting for an appendable Volume"); - break; - case JS_WaitFD: - if (!pool_mem) { - msg = (char *) get_pool_memory(PM_FNAME); - pool_mem = TRUE; - } - Mmsg(&msg, _("is waiting for Client %s to connect to Storage %s"), - jcr->client->hdr.name, jcr->store->hdr.name); - break; - } - bsendmsg(ua, _("JobId %d Job %s %s.\n"), jcr->JobId, jcr->Job, msg); - if (pool_mem) { - free_pool_memory(msg); - pool_mem = FALSE; - } - free_locked_jcr(jcr); - } - unlock_jcr_chain(); - - if (njobs == 0) { - bsendmsg(ua, _("No jobs are running.\n")); - } - print_jobs_scheduled(ua); + /* + * List scheduled Jobs + */ + list_scheduled_jobs(ua); + + /* + * List running jobs + */ + list_running_jobs(ua); + + /* + * List terminated jobs + */ + list_terminated_jobs(ua); bsendmsg(ua, "====\n"); } @@ -398,6 +295,7 @@ static void prt_runhdr(UAContext *ua) static void prt_runtime(UAContext *ua, JOB *job, int level, time_t runtime, POOL *pool) { char dt[MAX_TIME_LENGTH]; + char *level_ptr; bool ok = false; bool close_db = false; JCR *jcr = ua->jcr; @@ -417,9 +315,18 @@ static void prt_runtime(UAContext *ua, JOB *job, int level, time_t runtime, POOL } } bstrftime(dt, sizeof(dt), runtime); + strcpy(dt+7, dt+9); /* cut century */ + switch (job->JobType) { + case JT_ADMIN: + case JT_RESTORE: + level_ptr = " "; + break; + default: + level_ptr = level_to_str(level); + break; + } bsendmsg(ua, _("%-14s %-8s %-18s %-18s %s\n"), - level_to_str(level), job_type_to_str(job->JobType), dt, job->hdr.name, - mr.VolumeName); + level_ptr, job_type_to_str(job->JobType), dt, job->hdr.name, mr.VolumeName); if (close_db) { db_close_database(jcr, jcr->db); } @@ -428,109 +335,269 @@ static void prt_runtime(UAContext *ua, JOB *job, int level, time_t runtime, POOL } /* - * Find all jobs to be run this hour - * and the next hour. + * Find all jobs to be run in roughly the + * next 24 hours. */ -static void print_jobs_scheduled(UAContext *ua) +static void list_scheduled_jobs(UAContext *ua) { - time_t now, runtime, tomorrow; + time_t runtime; RUN *run; JOB *job; - SCHED *sched; - struct tm tm; - int mday, wday, month, wpos, tmday, twday, tmonth, twpos, i, hour; - int tod, tom; - int found; - int hdr_printed = FALSE; - int level; + int level, num_jobs = 0; + bool hdr_printed = false; Dmsg0(200, "enter find_runs()\n"); - now = time(NULL); - localtime_r(&now, &tm); - mday = tm.tm_mday - 1; - wday = tm.tm_wday; - month = tm.tm_mon; - wpos = (tm.tm_mday - 1) / 7; - - tomorrow = now + 60 * 60 * 24; - localtime_r(&tomorrow, &tm); - tmday = tm.tm_mday - 1; - twday = tm.tm_wday; - tmonth = tm.tm_mon; - twpos = (tm.tm_mday - 1) / 7; - /* Loop through all jobs */ LockRes(); for (job=NULL; (job=(JOB *)GetNextRes(R_JOB, (RES *)job)); ) { - level = job->level; - sched = job->schedule; - if (sched == NULL) { /* scheduled? */ - continue; /* no, skip this job */ - } - for (run=sched->run; run; run=run->next) { + for (run=NULL; (run = find_next_run(run, job, runtime)); ) { + level = job->level; if (run->level) { level = run->level; } - /* - * Find runs in next 24 hours - */ - tod = (bit_is_set(mday, run->mday) || bit_is_set(wday, run->wday)) && - bit_is_set(month, run->month) && bit_is_set(wpos, run->wpos); - - tom = (bit_is_set(tmday, run->mday) || bit_is_set(twday, run->wday)) && - bit_is_set(tmonth, run->month) && bit_is_set(wpos, run->wpos); - - Dmsg2(200, "tod=%d tom=%d\n", tod, tom); - found = FALSE; - if (tod) { /* Jobs scheduled today (next 24 hours) */ - /* find time (time_t) job is to be run */ - localtime_r(&now, &tm); - hour = 0; - for (i=tm.tm_hour; i < 24; i++) { - if (bit_is_set(i, run->hour)) { - tm.tm_hour = i; - tm.tm_min = run->minute; - tm.tm_sec = 0; - runtime = mktime(&tm); - if (runtime > now) { - if (!hdr_printed) { - hdr_printed = TRUE; - prt_runhdr(ua); - } - prt_runtime(ua, job, level, runtime, run->pool); - found = TRUE; - break; - } - } - } + if (!hdr_printed) { + prt_runhdr(ua); + hdr_printed = true; } + prt_runtime(ua, job, level, runtime, run->pool); + num_jobs++; + } -// Dmsg2(200, "runtime=%d now=%d\n", runtime, now); - if (!found && tom) { /* look at jobs scheduled tomorrow */ - localtime_r(&tomorrow, &tm); - hour = 0; - for (i=0; i < 24; i++) { - if (bit_is_set(i, run->hour)) { - hour = i; - break; - } - } - tm.tm_hour = hour; - tm.tm_min = run->minute; - tm.tm_sec = 0; - runtime = mktime(&tm); - Dmsg2(200, "truntime=%d now=%d\n", runtime, now); - if (runtime < tomorrow) { - if (!hdr_printed) { - hdr_printed = TRUE; - prt_runhdr(ua); - } - prt_runtime(ua, job, level, runtime, run->pool); - } - } - } /* end for loop over runs */ } /* end for loop over resources */ UnlockRes(); + if (num_jobs == 0) { + bsendmsg(ua, _("No Scheduled Jobs.\n")); + } else { + bsendmsg(ua, "\n"); + } Dmsg0(200, "Leave find_runs()\n"); } + +static void list_running_jobs(UAContext *ua) +{ + JCR *jcr; + int njobs = 0; + char *msg; + char dt[MAX_TIME_LENGTH]; + char level[10]; + bool pool_mem = false; + + lock_jcr_chain(); + for (jcr=NULL; (jcr=get_next_jcr(jcr)); njobs++) { + if (jcr->JobId == 0) { /* this is us */ + /* this is a console or other control job. We only show console + * jobs in the status output. + */ + if (jcr->JobType == JT_CONSOLE) { + bstrftime(dt, sizeof(dt), jcr->start_time); + strcpy(dt+7, dt+9); /* cut century */ + bsendmsg(ua, _("Console connected at %s\n"), dt); + } + njobs--; + } + free_locked_jcr(jcr); + } + if (njobs == 0) { + unlock_jcr_chain(); + bsendmsg(ua, _("No Running Jobs.\n")); + return; + } + njobs = 0; + bsendmsg(ua, _("\nRunning Jobs:\n")); + bsendmsg(ua, _("Level JobId Job Status\n")); + bsendmsg(ua, _("====================================================================\n")); + for (jcr=NULL; (jcr=get_next_jcr(jcr)); njobs++) { + if (jcr->JobId == 0) { /* this is us */ + njobs--; + free_locked_jcr(jcr); + continue; + } + switch (jcr->JobStatus) { + case JS_Created: + msg = _("is waiting execution"); + break; + case JS_Running: + msg = _("is running"); + break; + case JS_Blocked: + msg = _("is blocked"); + break; + case JS_Terminated: + msg = _("has terminated"); + break; + case JS_ErrorTerminated: + msg = _("has erred"); + break; + case JS_Error: + msg = _("has errors"); + break; + case JS_FatalError: + msg = _("has a fatal error"); + break; + case JS_Differences: + msg = _("has verify differences"); + break; + case JS_Canceled: + msg = _("has been canceled"); + break; + case JS_WaitFD: + msg = (char *) get_pool_memory(PM_FNAME); + Mmsg(&msg, _("is waiting on Client %s"), jcr->client->hdr.name); + pool_mem = true; + break; + case JS_WaitSD: + msg = (char *) get_pool_memory(PM_FNAME); + Mmsg(&msg, _("is waiting on Storage %s"), jcr->store->hdr.name); + pool_mem = true; + break; + case JS_WaitStoreRes: + msg = _("is waiting on max Storage jobs"); + break; + case JS_WaitClientRes: + msg = _("is waiting on max Client jobs"); + break; + case JS_WaitJobRes: + msg = _("is waiting on max Job jobs"); + break; + case JS_WaitMaxJobs: + msg = _("is waiting on max total jobs"); + break; + case JS_WaitStartTime: + msg = _("is waiting for its start time"); + break; + case JS_WaitPriority: + msg = _("is waiting for higher priority jobs to finish"); + break; + + default: + msg = (char *) get_pool_memory(PM_FNAME); + Mmsg(&msg, _("is in unknown state %c"), jcr->JobStatus); + pool_mem = true; + break; + } + /* + * Now report Storage daemon status code + */ + switch (jcr->SDJobStatus) { + case JS_WaitMount: + if (pool_mem) { + free_pool_memory(msg); + pool_mem = false; + } + msg = _("is waiting for a mount request"); + break; + case JS_WaitMedia: + if (pool_mem) { + free_pool_memory(msg); + pool_mem = false; + } + msg = _("is waiting for an appendable Volume"); + break; + case JS_WaitFD: + if (!pool_mem) { + msg = (char *) get_pool_memory(PM_FNAME); + pool_mem = true; + } + Mmsg(&msg, _("is waiting for Client %s to connect to Storage %s"), + jcr->client->hdr.name, jcr->store->hdr.name); + break; + } + switch (jcr->JobType) { + case JT_ADMIN: + case JT_RESTORE: + bstrncpy(level, " ", sizeof(level)); + break; + default: + bstrncpy(level, level_to_str(jcr->JobLevel), sizeof(level)); + level[4] = 0; + break; + } + + bsendmsg(ua, _("%-4s %6d %-20s %s\n"), + level, + jcr->JobId, + jcr->Job, + msg); + + if (pool_mem) { + free_pool_memory(msg); + pool_mem = false; + } + free_locked_jcr(jcr); + } + unlock_jcr_chain(); + + bsendmsg(ua, "\n"); +} + +static void list_terminated_jobs(UAContext *ua) +{ + char dt[MAX_TIME_LENGTH], b1[30], b2[30]; + char level[10]; + + if (last_jobs->empty()) { + bsendmsg(ua, _("No Terminated Jobs.\n")); + return; + } + lock_last_jobs_list(); + struct s_last_job *je; + bsendmsg(ua, _("\nTerminated Jobs:\n")); + bsendmsg(ua, _(" JobId Level Files Bytes Status Finished Name \n")); + bsendmsg(ua, _("======================================================================\n")); + for (je=NULL; (je=(s_last_job *)last_jobs->next(je)); ) { + char JobName[MAX_NAME_LENGTH]; + char *termstat; + + bstrftime(dt, sizeof(dt), je->end_time); + strcpy(dt+7, dt+9); /* cut century */ + switch (je->JobType) { + case JT_ADMIN: + case JT_RESTORE: + bstrncpy(level, " ", sizeof(level)); + break; + default: + bstrncpy(level, level_to_str(je->JobLevel), sizeof(level)); + level[4] = 0; + break; + } + switch (je->JobStatus) { + case JS_Created: + termstat = "Created"; + break; + case JS_FatalError: + case JS_ErrorTerminated: + termstat = "Error"; + break; + case JS_Differences: + termstat = "Diffs"; + break; + case JS_Canceled: + termstat = "Cancel"; + break; + case JS_Terminated: + termstat = "OK"; + break; + default: + termstat = "Other"; + break; + } + bstrncpy(JobName, je->Job, sizeof(JobName)); + /* There are three periods after the Job name */ + char *p; + for (int i=0; i<3; i++) { + if ((p=strrchr(JobName, '.')) != NULL) { + *p = 0; + } + } + bsendmsg(ua, _("%6d %-4s %8s %14s %-7s %-8s %s\n"), + je->JobId, + level, + edit_uint64_with_commas(je->JobFiles, b1), + edit_uint64_with_commas(je->JobBytes, b2), + termstat, + dt, JobName); + } + bsendmsg(ua, "\n"); + unlock_last_jobs_list(); +} diff --git a/bacula/src/dird/ua_tree.c b/bacula/src/dird/ua_tree.c index 29db4a832d..0a87e265ec 100644 --- a/bacula/src/dird/ua_tree.c +++ b/bacula/src/dird/ua_tree.c @@ -1,7 +1,8 @@ /* * * Bacula Director -- User Agent Database File tree for Restore - * command. + * command. This file interacts with the user implementing the + * UA tree commands. * * Kern Sibbald, July MMII * @@ -40,7 +41,9 @@ static int markcmd(UAContext *ua, TREE_CTX *tree); static int countcmd(UAContext *ua, TREE_CTX *tree); static int findcmd(UAContext *ua, TREE_CTX *tree); static int lscmd(UAContext *ua, TREE_CTX *tree); +static int lsmark(UAContext *ua, TREE_CTX *tree); static int dircmd(UAContext *ua, TREE_CTX *tree); +static int estimatecmd(UAContext *ua, TREE_CTX *tree); static int helpcmd(UAContext *ua, TREE_CTX *tree); static int cdcmd(UAContext *ua, TREE_CTX *tree); static int pwdcmd(UAContext *ua, TREE_CTX *tree); @@ -50,17 +53,19 @@ static int quitcmd(UAContext *ua, TREE_CTX *tree); struct cmdstruct { char *key; int (*func)(UAContext *ua, TREE_CTX *tree); char *help; }; static struct cmdstruct commands[] = { - { N_("mark"), markcmd, _("mark file for restoration")}, - { N_("unmark"), unmarkcmd, _("unmark file for restoration")}, { N_("cd"), cdcmd, _("change current directory")}, - { N_("pwd"), pwdcmd, _("print current working directory")}, - { N_("ls"), lscmd, _("list current directory")}, - { N_("dir"), dircmd, _("list current directory")}, { N_("count"), countcmd, _("count marked files")}, - { N_("find"), findcmd, _("find files")}, + { N_("dir"), dircmd, _("list current directory")}, { N_("done"), quitcmd, _("leave file selection mode")}, + { N_("estimate"), estimatecmd, _("estimate restore size")}, { N_("exit"), quitcmd, _("exit = done")}, + { N_("find"), findcmd, _("find files")}, { N_("help"), helpcmd, _("print help")}, + { N_("lsmark"), lsmark, _("list the marked files")}, + { N_("ls"), lscmd, _("list current directory")}, + { N_("mark"), markcmd, _("mark file to be restored")}, + { N_("pwd"), pwdcmd, _("print current working directory")}, + { N_("unmark"), unmarkcmd, _("unmark file to be restored")}, { N_("?"), helpcmd, _("print help")}, }; #define comsize (sizeof(commands)/sizeof(struct cmdstruct)) @@ -163,7 +168,7 @@ int insert_tree_handler(void *ctx, int num_fields, char **row) new_node->FileIndex = atoi(row[2]); new_node->JobId = atoi(row[3]); new_node->type = type; - new_node->extract = 1; /* extract all by default */ + new_node->extract = true; /* extract all by default */ tree->cnt++; return 0; } @@ -174,19 +179,23 @@ int insert_tree_handler(void *ctx, int num_fields, char **row) * down the tree setting all children if the * node is a directory. */ -static void set_extract(UAContext *ua, TREE_NODE *node, TREE_CTX *tree, int value) +static int set_extract(UAContext *ua, TREE_NODE *node, TREE_CTX *tree, bool extract) { TREE_NODE *n; FILE_DBR fdbr; struct stat statp; + int count = 0; - node->extract = value; + node->extract = extract; + if (node->type != TN_NEWDIR) { + count++; + } /* For a non-file (i.e. directory), we see all the children */ if (node->type != TN_FILE) { for (n=node->child; n; n=n->sibling) { - set_extract(ua, n, tree, value); + count += set_extract(ua, n, tree, extract); } - } else if (value) { + } else if (extract) { char cwd[2000]; /* Ordinary file, we get the full path, look up the * attributes, decode them, and if we are hard linked to @@ -195,57 +204,65 @@ static void set_extract(UAContext *ua, TREE_NODE *node, TREE_CTX *tree, int valu tree_getpath(node, cwd, sizeof(cwd)); fdbr.FileId = 0; fdbr.JobId = node->JobId; - if (db_get_file_attributes_record(ua->jcr, ua->db, cwd, &fdbr)) { + if (db_get_file_attributes_record(ua->jcr, ua->db, cwd, NULL, &fdbr)) { int32_t LinkFI; decode_stat(fdbr.LStat, &statp, &LinkFI); /* decode stat pkt */ /* * If we point to a hard linked file, traverse the tree to - * find that file, and mark it for restoration as well. It + * find that file, and mark it to be restored as well. It * must have the Link we just obtained and the same JobId. */ if (LinkFI) { for (n=first_tree_node(tree->root); n; n=next_tree_node(n)) { if (n->FileIndex == LinkFI && n->JobId == node->JobId) { - n->extract = 1; + n->extract = true; break; } } } } } + return count; } static int markcmd(UAContext *ua, TREE_CTX *tree) { TREE_NODE *node; + int count = 0; - if (ua->argc < 2) - return 1; - if (!tree->node->child) { + if (ua->argc < 2 || !tree->node->child) { + bsendmsg(ua, _("No files marked.\n")); return 1; } - for (node = tree->node->child; node; node=node->sibling) { - if (fnmatch(ua->argk[1], node->fname, 0) == 0) { - set_extract(ua, node, tree, 1); + for (int i=1; i < ua->argc; i++) { + for (node = tree->node->child; node; node=node->sibling) { + if (fnmatch(ua->argk[i], node->fname, 0) == 0) { + count += set_extract(ua, node, tree, true); + } } } + if (count == 0) { + bsendmsg(ua, _("No files marked.\n")); + } else { + bsendmsg(ua, _("%d file%s marked.\n"), count, count==0?"":"s"); + } return 1; } static int countcmd(UAContext *ua, TREE_CTX *tree) { - int total, extract; + int total, num_extract; - total = extract = 0; + total = num_extract = 0; for (TREE_NODE *node=first_tree_node(tree->root); node; node=next_tree_node(node)) { if (node->type != TN_NEWDIR) { total++; if (node->extract) { - extract++; + num_extract++; } } } - bsendmsg(ua, "%d total files. %d marked for restoration.\n", total, extract); + bsendmsg(ua, "%d total files. %d marked to be restored.\n", total, num_extract); return 1; } @@ -287,13 +304,34 @@ static int lscmd(UAContext *ua, TREE_CTX *tree) return 1; } +/* + * Ls command that lists only the marked files + */ +static int lsmark(UAContext *ua, TREE_CTX *tree) +{ + TREE_NODE *node; + + if (!tree->node->child) { + return 1; + } + for (node = tree->node->child; node; node=node->sibling) { + if (ua->argc == 1 || fnmatch(ua->argk[1], node->fname, 0) == 0 && + node->extract) { + bsendmsg(ua, "%s%s%s\n", node->extract?"*":"", node->fname, + (node->type==TN_DIR||node->type==TN_NEWDIR)?"/":""); + } + } + return 1; +} + + extern char *getuser(uid_t uid); extern char *getgroup(gid_t gid); /* * This is actually the long form used for "dir" */ -static void ls_output(char *buf, char *fname, int extract, struct stat *statp) +static void ls_output(char *buf, char *fname, bool extract, struct stat *statp) { char *p, *f; char ec1[30]; @@ -338,7 +376,7 @@ static int dircmd(UAContext *ua, TREE_CTX *tree) tree_getpath(node, cwd, sizeof(cwd)); fdbr.FileId = 0; fdbr.JobId = node->JobId; - if (db_get_file_attributes_record(ua->jcr, ua->db, cwd, &fdbr)) { + if (db_get_file_attributes_record(ua->jcr, ua->db, cwd, NULL, &fdbr)) { int32_t LinkFI; decode_stat(fdbr.LStat, &statp, &LinkFI); /* decode stat pkt */ ls_output(buf, cwd, node->extract, &statp); @@ -354,6 +392,45 @@ static int dircmd(UAContext *ua, TREE_CTX *tree) } +static int estimatecmd(UAContext *ua, TREE_CTX *tree) +{ + int total, num_extract; + uint64_t total_bytes = 0; + FILE_DBR fdbr; + struct stat statp; + char cwd[1100]; + char ec1[50]; + + total = num_extract = 0; + for (TREE_NODE *node=first_tree_node(tree->root); node; node=next_tree_node(node)) { + if (node->type != TN_NEWDIR) { + total++; + /* If regular file, get size */ + if (node->extract && node->type == TN_FILE) { + num_extract++; + tree_getpath(node, cwd, sizeof(cwd)); + fdbr.FileId = 0; + fdbr.JobId = node->JobId; + if (db_get_file_attributes_record(ua->jcr, ua->db, cwd, NULL, &fdbr)) { + int32_t LinkFI; + decode_stat(fdbr.LStat, &statp, &LinkFI); /* decode stat pkt */ + if (S_ISREG(statp.st_mode) && statp.st_size > 0) { + total_bytes += statp.st_size; + } + } + /* Directory, count only */ + } else if (node->extract) { + num_extract++; + } + } + } + bsendmsg(ua, "%d total files; %d marked to be restored; %s bytes.\n", + total, num_extract, edit_uint64_with_commas(total_bytes, ec1)); + return 1; +} + + + static int helpcmd(UAContext *ua, TREE_CTX *tree) { unsigned int i; @@ -413,17 +490,24 @@ static int pwdcmd(UAContext *ua, TREE_CTX *tree) static int unmarkcmd(UAContext *ua, TREE_CTX *tree) { TREE_NODE *node; + int count = 0; - if (ua->argc < 2) - return 1; - if (!tree->node->child) { + if (ua->argc < 2 || !tree->node->child) { + bsendmsg(ua, _("No files unmarked.\n")); return 1; } - for (node = tree->node->child; node; node=node->sibling) { - if (fnmatch(ua->argk[1], node->fname, 0) == 0) { - set_extract(ua, node, tree, 0); + for (int i=1; i < ua->argc; i++) { + for (node = tree->node->child; node; node=node->sibling) { + if (fnmatch(ua->argk[i], node->fname, 0) == 0) { + count += set_extract(ua, node, tree, false); + } } } + if (count == 0) { + bsendmsg(ua, _("No files unmarked.\n")); + } else { + bsendmsg(ua, _("%d file%s unmarked.\n"), count, count==0?"":"s"); + } return 1; } diff --git a/bacula/src/dird/verify.c b/bacula/src/dird/verify.c index 40faac1e41..5fc210e2b0 100644 --- a/bacula/src/dird/verify.c +++ b/bacula/src/dird/verify.c @@ -64,26 +64,41 @@ static int missing_handler(void *ctx, int num_fields, char **row); */ int do_verify(JCR *jcr) { - char *level; + char *level, *Name; BSOCK *fd; - JOB_DBR jr; - JobId_t JobId = 0; + JOB_DBR jr, verify_jr; + JobId_t verify_jobid = 0; int stat; + memset(&verify_jr, 0, sizeof(verify_jr)); + if (!jcr->verify_jr) { + jcr->verify_jr = &verify_jr; + } if (!get_or_create_client_record(jcr)) { goto bail_out; } Dmsg1(9, "bdird: created client %s record\n", jcr->client->hdr.name); - /* If we are doing a verify from the catalog, - * we must look up the time and date of the - * last full verify. + /* + * Find JobId of last job that ran. E.g. + * for VERIFY_CATALOG we want the JobId of the last INIT. + * for VERIFY_VOLUME_TO_CATALOG, we want the JobId of the + * last backup Job. */ if (jcr->JobLevel == L_VERIFY_CATALOG || - jcr->JobLevel == L_VERIFY_VOLUME_TO_CATALOG) { + jcr->JobLevel == L_VERIFY_VOLUME_TO_CATALOG || + jcr->JobLevel == L_VERIFY_DISK_TO_CATALOG) { memcpy(&jr, &jcr->jr, sizeof(jr)); - if (!db_find_last_jobid(jcr, jcr->db, &jr)) { + if (jcr->job->verify_job && + (jcr->JobLevel == L_VERIFY_VOLUME_TO_CATALOG || + jcr->JobLevel == L_VERIFY_DISK_TO_CATALOG)) { + Name = jcr->job->verify_job->hdr.name; + } else { + Name = NULL; + } + Dmsg1(100, "find last jobid for: %s\n", NPRT(Name)); + if (!db_find_last_jobid(jcr, jcr->db, Name, &jr)) { if (jcr->JobLevel == L_VERIFY_CATALOG) { Jmsg(jcr, M_FATAL, 0, _( "Unable to find JobId of previous InitCatalog Job.\n" @@ -95,8 +110,8 @@ int do_verify(JCR *jcr) } goto bail_out; } - JobId = jr.JobId; - Dmsg1(20, "Last full id=%d\n", JobId); + verify_jobid = jr.JobId; + Dmsg1(100, "Last full jobid=%d\n", verify_jobid); } jcr->jr.JobId = jcr->JobId; @@ -111,28 +126,30 @@ int do_verify(JCR *jcr) jcr->fname = get_pool_memory(PM_FNAME); } - jcr->jr.JobId = JobId; /* save target JobId */ - /* Print Job Start message */ Jmsg(jcr, M_INFO, 0, _("Start Verify JobId %d Job=%s\n"), jcr->JobId, jcr->Job); + /* + * Now get the job record for the previous backup that interests + * us. We use the verify_jobid that we found above. + */ if (jcr->JobLevel == L_VERIFY_CATALOG || - jcr->JobLevel == L_VERIFY_VOLUME_TO_CATALOG) { - memset(&jr, 0, sizeof(jr)); - jr.JobId = JobId; - if (!db_get_job_record(jcr, jcr->db, &jr)) { + jcr->JobLevel == L_VERIFY_VOLUME_TO_CATALOG || + jcr->JobLevel == L_VERIFY_DISK_TO_CATALOG) { + verify_jr.JobId = verify_jobid; + if (!db_get_job_record(jcr, jcr->db, &verify_jr)) { Jmsg(jcr, M_FATAL, 0, _("Could not get job record for previous Job. ERR=%s"), db_strerror(jcr->db)); goto bail_out; } - if (jr.JobStatus != 'T') { + if (verify_jr.JobStatus != 'T') { Jmsg(jcr, M_FATAL, 0, _("Last Job %d did not terminate normally. JobStatus=%c\n"), - JobId, jr.JobStatus); + verify_jobid, verify_jr.JobStatus); goto bail_out; } Jmsg(jcr, M_INFO, 0, _("Verifying against JobId=%d Job=%s\n"), - JobId, jr.Job); + verify_jr.JobId, verify_jr.Job); } /* @@ -144,12 +161,12 @@ int do_verify(JCR *jcr) if (jcr->JobLevel == L_VERIFY_VOLUME_TO_CATALOG) { RBSR *bsr = new_bsr(); UAContext *ua; - bsr->JobId = jr.JobId; + bsr->JobId = verify_jr.JobId; ua = new_ua_context(jcr); complete_bsr(ua, bsr); bsr->fi = new_findex(); bsr->fi->findex = 1; - bsr->fi->findex2 = jr.JobFiles; + bsr->fi->findex2 = verify_jr.JobFiles; if (!write_bsr_file(ua, bsr)) { free_ua_context(ua); free_bsr(bsr); @@ -188,6 +205,12 @@ int do_verify(JCR *jcr) } else { jcr->sd_auth_key = bstrdup("dummy"); /* dummy Storage daemon key */ } + + if (jcr->JobLevel == L_VERIFY_DISK_TO_CATALOG && jcr->job->verify_job) { + jcr->fileset = jcr->job->verify_job->fileset; + } + Dmsg2(100, "ClientId=%u JobLevel=%c\n", verify_jr.ClientId, jcr->JobLevel); + /* * OK, now connect to the File daemon * and ask him for the files. @@ -200,6 +223,7 @@ int do_verify(JCR *jcr) set_jcr_job_status(jcr, JS_Running); fd = jcr->file_bsock; + Dmsg0(30, ">filed: Send include list\n"); if (!send_include_list(jcr)) { goto bail_out; @@ -262,6 +286,9 @@ int do_verify(JCR *jcr) case L_VERIFY_DATA: level = "data"; break; + case L_VERIFY_DISK_TO_CATALOG: + level="disk_to_catalog"; + break; default: Jmsg1(jcr, M_FATAL, 0, _("Unimplemented save level %d\n"), jcr->JobLevel); goto bail_out; @@ -290,12 +317,19 @@ int do_verify(JCR *jcr) Dmsg0(10, "Verify level=catalog\n"); jcr->sd_msg_thread_done = true; /* no SD msg thread, so it is done */ jcr->SDJobStatus = JS_Terminated; - get_attributes_and_compare_to_catalog(jcr, JobId); + get_attributes_and_compare_to_catalog(jcr, verify_jobid); break; case L_VERIFY_VOLUME_TO_CATALOG: Dmsg0(10, "Verify level=volume\n"); - get_attributes_and_compare_to_catalog(jcr, JobId); + get_attributes_and_compare_to_catalog(jcr, verify_jobid); + break; + + case L_VERIFY_DISK_TO_CATALOG: + Dmsg0(10, "Verify level=disk_to_catalog\n"); + jcr->sd_msg_thread_done = true; /* no SD msg thread, so it is done */ + jcr->SDJobStatus = JS_Terminated; + get_attributes_and_compare_to_catalog(jcr, verify_jobid); break; case L_VERIFY_INIT: @@ -312,7 +346,6 @@ int do_verify(JCR *jcr) } stat = wait_for_job_termination(jcr); - verify_cleanup(jcr, stat); return 1; @@ -333,8 +366,9 @@ static void verify_cleanup(JCR *jcr, int TermCode) char *term_msg; int msg_type; JobId_t JobId; + char *Name; -// Dmsg1(000, "Enter verify_cleanup() TermCod=%d\n", TermCode); +// Dmsg1(100, "Enter verify_cleanup() TermCod=%d\n", TermCode); JobId = jcr->jr.JobId; set_jcr_job_status(jcr, TermCode); @@ -358,21 +392,29 @@ static void verify_cleanup(JCR *jcr, int TermCode) break; default: term_msg = term_code; - sprintf(term_code, _("Inappropriate term code: %c\n"), TermCode); + bsnprintf(term_code, sizeof(term_code), + _("Inappropriate term code: %d %c\n"), TermCode, TermCode); break; } bstrftime(sdt, sizeof(sdt), jcr->jr.StartTime); bstrftime(edt, sizeof(edt), jcr->jr.EndTime); + if (jcr->job->verify_job) { + Name = jcr->job->verify_job->hdr.name; + } else { + Name = ""; + } jobstatus_to_ascii(jcr->FDJobStatus, fd_term_msg, sizeof(fd_term_msg)); if (jcr->JobLevel == L_VERIFY_VOLUME_TO_CATALOG) { - jobstatus_to_ascii(jcr->SDJobStatus, sd_term_msg, sizeof(sd_term_msg)); + jobstatus_to_ascii(jcr->SDJobStatus, sd_term_msg, sizeof(sd_term_msg)); Jmsg(jcr, msg_type, 0, _("Bacula " VERSION " (" LSMDATE "): %s\n\ JobId: %d\n\ Job: %s\n\ FileSet: %s\n\ Verify Level: %s\n\ Client: %s\n\ +Verify JobId: %d\n\ +Verify Job: %s\n\ Start time: %s\n\ End time: %s\n\ Files Examined: %s\n\ @@ -386,6 +428,8 @@ Termination: %s\n\n"), jcr->fileset->hdr.name, level_to_str(jcr->JobLevel), jcr->client->hdr.name, + jcr->verify_jr->JobId, + Name, sdt, edt, edit_uint64_with_commas(jcr->JobFiles, ec1), @@ -400,6 +444,8 @@ Job: %s\n\ FileSet: %s\n\ Verify Level: %s\n\ Client: %s\n\ +Verify JobId: %d\n\ +Verify Job: %s\n\ Start time: %s\n\ End time: %s\n\ Files Examined: %s\n\ @@ -412,6 +458,8 @@ Termination: %s\n\n"), jcr->fileset->hdr.name, level_to_str(jcr->JobLevel), jcr->client->hdr.name, + jcr->verify_jr->JobId, + Name, sdt, edt, edit_uint64_with_commas(jcr->JobFiles, ec1), @@ -465,7 +513,7 @@ int get_attributes_and_compare_to_catalog(JCR *jcr, JobId_t JobId) fname = check_pool_memory_size(fname, fd->msglen); jcr->fname = check_pool_memory_size(jcr->fname, fd->msglen); - Dmsg1(400, "Atts+SIG=%s\n", fd->msg); + Dmsg1(200, "Atts+SIG=%s\n", fd->msg); if ((len = sscanf(fd->msg, "%ld %d %100s", &file_index, &stream, fname)) != 3) { Jmsg3(jcr, M_FATAL, 0, _("birddb, jcr->fname, &fdbr)) { + if (!db_get_file_attributes_record(jcr, jcr->db, jcr->fname, + jcr->verify_jr, &fdbr)) { Jmsg(jcr, M_INFO, 0, _("New file: %s\n"), jcr->fname); Dmsg1(020, _("File not in catalog: %s\n"), jcr->fname); stat = JS_Differences; diff --git a/bacula/src/filed/.cvsignore b/bacula/src/filed/.cvsignore index f97f16974f..9edec26956 100644 --- a/bacula/src/filed/.cvsignore +++ b/bacula/src/filed/.cvsignore @@ -7,3 +7,4 @@ host.h startit static-bacula-fd stopit +filed.conf diff --git a/bacula/src/filed/filed.c b/bacula/src/filed/filed.c index 4ad2c704d2..aa8dbe07be 100644 --- a/bacula/src/filed/filed.c +++ b/bacula/src/filed/filed.c @@ -99,54 +99,52 @@ int main (int argc, char *argv[]) init_msg(NULL, NULL); daemon_start_time = time(NULL); - memset(&last_job, 0, sizeof(last_job)); - while ((ch = getopt(argc, argv, "c:d:fg:istu:v?")) != -1) { switch (ch) { - case 'c': /* configuration file */ - if (configfile != NULL) { - free(configfile); - } - configfile = bstrdup(optarg); - break; - - case 'd': /* debug level */ - debug_level = atoi(optarg); - if (debug_level <= 0) { - debug_level = 1; - } - break; - - case 'f': /* run in foreground */ - foreground = TRUE; - break; - - case 'g': /* set group */ - gid = optarg; - break; - - case 'i': - inetd_request = TRUE; - break; - case 's': - no_signals = TRUE; - break; - - case 't': - test_config = TRUE; - break; - - case 'u': /* set userid */ - uid = optarg; - break; - - case 'v': /* verbose */ - verbose++; - break; - - case '?': - default: - usage(); + case 'c': /* configuration file */ + if (configfile != NULL) { + free(configfile); + } + configfile = bstrdup(optarg); + break; + + case 'd': /* debug level */ + debug_level = atoi(optarg); + if (debug_level <= 0) { + debug_level = 1; + } + break; + + case 'f': /* run in foreground */ + foreground = TRUE; + break; + + case 'g': /* set group */ + gid = optarg; + break; + + case 'i': + inetd_request = TRUE; + break; + case 's': + no_signals = TRUE; + break; + + case 't': + test_config = TRUE; + break; + + case 'u': /* set userid */ + uid = optarg; + break; + + case 'v': /* verbose */ + verbose++; + break; + + case '?': + default: + usage(); } } diff --git a/bacula/src/filed/job.c b/bacula/src/filed/job.c index e79f6142bd..23796405bd 100644 --- a/bacula/src/filed/job.c +++ b/bacula/src/filed/job.c @@ -307,7 +307,7 @@ static int estimate_cmd(JCR *jcr) } make_estimate(jcr); bnet_fsend(dir, OKest, jcr->num_files_examined, - edit_uint64(jcr->JobBytes, ed2)); + edit_uint64_with_commas(jcr->JobBytes, ed2)); bnet_sig(dir, BNET_EOD); return 1; } @@ -436,6 +436,7 @@ static void add_fname_to_list(JCR *jcr, char *fname, int list) switch (*p) { case '|': + p++; /* skip over | */ fn = get_pool_memory(PM_FNAME); fn = edit_job_codes(jcr, fn, p, ""); bpipe = open_bpipe(fn, 0, "r"); @@ -590,7 +591,7 @@ static int bootstrap_cmd(JCR *jcr) static int level_cmd(JCR *jcr) { BSOCK *dir = jcr->dir_bsock; - POOLMEM *level; + POOLMEM *level, *buf = NULL; struct tm tm; time_t mtime; int mtime_only; @@ -598,10 +599,7 @@ static int level_cmd(JCR *jcr) level = get_memory(dir->msglen+1); Dmsg1(110, "level_cmd: %s", dir->msg); if (sscanf(dir->msg, "level = %s ", level) != 1) { - pm_strcpy(&jcr->errmsg, dir->msg); - Jmsg1(jcr, M_FATAL, 0, _("Bad level command: %s\n"), jcr->errmsg); - free_memory(level); - return 0; + goto bail_out; } /* Base backup requested? */ if (strcmp(level, "base") == 0) { @@ -612,16 +610,14 @@ static int level_cmd(JCR *jcr) /* * Backup requested since