Add vchanger 1.0.3 code

This commit is contained in:
Wanderlei Hüttel 2020-05-27 10:23:25 -03:00
parent 7490e52578
commit d7a0e39e91
44 changed files with 3448 additions and 2309 deletions

View File

@ -7,3 +7,6 @@ Principle Authors:
Josh Fisher
Contributors:
Bill Arlofski
Wanderlei Hüttel
Steven A. Falco

View File

@ -1,5 +1,5 @@
COPYRIGHTS:
Vchanger is Copyright (C) 2008-2015 Josh Fisher.
Vchanger is Copyright (C) 2008-2020 Josh Fisher.
LICENSE:
Vchanger is licensed under the GNU GPL version 2, the full text

View File

@ -1,5 +1,26 @@
vchanger ChangeLog
1.0.3 (2020-05-06)
- Redesign locking mechanism for multiple instances using POSIX
semaphores.
- Use at, rather than nohup, in scripts invoked by udev rules to fix
nohup not working as expected on some platforms.
(Patch from Steven A. Falco)
- Add spec file for Fedora 29 (Provided by Steven A. Falco)
- Correct number of slots reported by SIZE command (Fixes bug 17)
- Rename logging variables that conflict with cmath's log function (Fixes bug 18)
1.0.2 (2018-06-14)
- Use named mutex to prevent instances of vchanger invoked by Bacula during
a bconsole call from initiating further bconsole calls. Prevents a race
condition caused by the need for Bacula to invoke additional instances of
vchanger when vchanger invokes bconsole to issue 'update slots' and
'label barcodes' commands. (Fixes bug 15)
- Prevent load command from loading the same virtual slot into more than
one virtual drive. (Fixes bug 13)
- Fix bconsole update slots command needs drive to be specified. [Patch
from Bill Arlofski] (Fixes bug 14)
- Improve generated volume label format. [Patch from Wanderlei Huttel]
- Additional logging to help debug udev/UUID assignment problems
1.0.1 (2015-06-09)
- When looking up the mountpoint of a magazine by UUID with libudev,
also look for mountpoint of device alias names in DEVLINKS in addition

View File

@ -1,4 +1,34 @@
Release Notes for vchanger 1.0.1 2015-06-09
Release Notes for vchanger 1.0.3 2020-05-07
Release 1.0.3
This is mostly a bug fix release, correcting the number of slots
reported by SIZE/LIST commands, a compilation error on FreeBSD,
and failure of the launch scripts invoked by udev on some platforms.
The locking mechanism to allow multiple instances and automatically
issuing 'update slots' and other commands to bconsole has been
redesigned to use POSIX semaphores.
Bugs Fixed:
17 SIZE/LIST commands return wrong number of slots
18 Compilation fails on FreeBSD 13 (head)
==================================================================
Release 1.0.2
This is a bug fix release, fixing three issues found in version 1.0.1
and improving volume label formatting for CREATEVOLS command and logging
of udev/UUID mountpoint discovery.
Bugs Fixed:
13 LOAD command allows loading slot into multiple drives
14 Hang when bconsole called to update slots
15 Race condition in bconsole call may hang vchanger
==================================================================
Release 1.0.1

View File

@ -15,6 +15,9 @@
/* Define to 1 if you have the <ctype.h> header file. */
#undef HAVE_CTYPE_H
/* have header direct.h */
#undef HAVE_DIRECT_H
/* Define to 1 if you have the <dirent.h> header file. */
#undef HAVE_DIRENT_H
@ -69,7 +72,7 @@
/* Define to 1 if you have the <locale.h> header file. */
#undef HAVE_LOCALE_H
/* Define to 1 if you have the `localtime_r' function. */
/* have function localtime_r */
#undef HAVE_LOCALTIME_R
/* Define to 1 if you have the <memory.h> header file. */
@ -96,6 +99,9 @@
/* Define to 1 if you have the `readlink' function. */
#undef HAVE_READLINK
/* Define to 1 if you have the <semaphore.h> header file. */
#undef HAVE_SEMAPHORE_H
/* Define to 1 if you have the `setlocale' function. */
#undef HAVE_SETLOCALE
@ -148,6 +154,9 @@
*/
#undef HAVE_SYS_DIR_H
/* Define to 1 if you have the <sys/mman.h> header file. */
#undef HAVE_SYS_MMAN_H
/* Define to 1 if you have the <sys/mount.h> header file. */
#undef HAVE_SYS_MOUNT_H

316
config.h.in~ Normal file
View File

@ -0,0 +1,316 @@
/* config.h.in. Generated from configure.ac by autoheader. */
/* Copyright notice */
#undef COPYRIGHT_NOTICE
/* Define to 1 if you have the <alloca.h> header file. */
#undef HAVE_ALLOCA_H
/* Have blkid_evaluate_tag function */
#undef HAVE_BLKID_EVALUATE_TAG
/* Have blkid_get_devname function */
#undef HAVE_BLKID_GET_DEVNAME
/* Define to 1 if you have the <ctype.h> header file. */
#undef HAVE_CTYPE_H
/* have header direct.h */
#undef HAVE_DIRECT_H
/* Define to 1 if you have the <dirent.h> header file. */
#undef HAVE_DIRENT_H
/* Define to 1 if you don't have `vprintf' but do have `_doprnt.' */
#undef HAVE_DOPRNT
/* Define to 1 if you have the <errno.h> header file. */
#undef HAVE_ERRNO_H
/* Define to 1 if you have the <fcntl.h> header file. */
#undef HAVE_FCNTL_H
/* Define to 1 if you have the `getfsstat' function. */
#undef HAVE_GETFSSTAT
/* Define to 1 if you have the `getline' function. */
#undef HAVE_GETLINE
/* Define to 1 if you have the `getmntent' function. */
#undef HAVE_GETMNTENT
/* Define to 1 if you have the `getmntent_r' function. */
#undef HAVE_GETMNTENT_R
/* Define to 1 if you have the <getopt.h> header file. */
#undef HAVE_GETOPT_H
/* Define to 1 if you have the `gettimeofday' function. */
#undef HAVE_GETTIMEOFDAY
/* Define to 1 if you have the `getuid' function. */
#undef HAVE_GETUID
/* Define to 1 if you have the <grp.h> header file. */
#undef HAVE_GRP_H
/* Define to 1 if you have the <inttypes.h> header file. */
#undef HAVE_INTTYPES_H
/* Define to 1 if you have the <io.h> header file. */
#undef HAVE_IO_H
/* Define to 1 if you have the <libgen.h> header file. */
#undef HAVE_LIBGEN_H
/* Define to 1 if you have the <libudev.h> header file. */
#undef HAVE_LIBUDEV_H
/* Define to 1 if you have the <limits.h> header file. */
#undef HAVE_LIMITS_H
/* Define to 1 if you have the <locale.h> header file. */
#undef HAVE_LOCALE_H
/* have function localtime_r */
#undef HAVE_LOCALTIME_R
/* Define to 1 if you have the <memory.h> header file. */
#undef HAVE_MEMORY_H
/* Define to 1 if you have the <mntent.h> header file. */
#undef HAVE_MNTENT_H
/* Define to 1 if you have the <ndir.h> header file, and it defines `DIR'. */
#undef HAVE_NDIR_H
/* Define to 1 if you have the <optarg.h> header file. */
#undef HAVE_OPTARG_H
/* Define to 1 if you have the `pipe' function. */
#undef HAVE_PIPE
/* Define to 1 if you have the <pthread.h> header file. */
#undef HAVE_PTHREAD_H
/* Define to 1 if you have the <pwd.h> header file. */
#undef HAVE_PWD_H
/* Define to 1 if you have the `readlink' function. */
#undef HAVE_READLINK
/* Define to 1 if you have the `setlocale' function. */
#undef HAVE_SETLOCALE
/* have header shlobj.h */
#undef HAVE_SHLOBJ_H
/* Define to 1 if you have the <signal.h> header file. */
#undef HAVE_SIGNAL_H
/* Define to 1 if you have the `sleep' function. */
#undef HAVE_SLEEP
/* Define to 1 if you have the <stdarg.h> header file. */
#undef HAVE_STDARG_H
/* Define to 1 if stdbool.h conforms to C99. */
#undef HAVE_STDBOOL_H
/* Define to 1 if you have the <stddef.h> header file. */
#undef HAVE_STDDEF_H
/* Define to 1 if you have the <stdint.h> header file. */
#undef HAVE_STDINT_H
/* Define to 1 if you have the <stdio.h> header file. */
#undef HAVE_STDIO_H
/* Define to 1 if you have the <stdlib.h> header file. */
#undef HAVE_STDLIB_H
/* Define to 1 if you have the <strings.h> header file. */
#undef HAVE_STRINGS_H
/* Define to 1 if you have the <string.h> header file. */
#undef HAVE_STRING_H
/* Define to 1 if you have the `symlink' function. */
#undef HAVE_SYMLINK
/* Define to 1 if you have the `syslog' function. */
#undef HAVE_SYSLOG
/* Define to 1 if you have the <syslog.h> header file. */
#undef HAVE_SYSLOG_H
/* Define to 1 if you have the <sys/bitypes.h> header file. */
#undef HAVE_SYS_BITYPES_H
/* Define to 1 if you have the <sys/dir.h> header file, and it defines `DIR'.
*/
#undef HAVE_SYS_DIR_H
/* Define to 1 if you have the <sys/mman.h> header file. */
#undef HAVE_SYS_MMAN_H
/* Define to 1 if you have the <sys/mount.h> header file. */
#undef HAVE_SYS_MOUNT_H
/* Define to 1 if you have the <sys/ndir.h> header file, and it defines `DIR'.
*/
#undef HAVE_SYS_NDIR_H
/* Define to 1 if you have the <sys/param.h> header file. */
#undef HAVE_SYS_PARAM_H
/* Define to 1 if you have the <sys/select.h> header file. */
#undef HAVE_SYS_SELECT_H
/* Define to 1 if you have the <sys/stat.h> header file. */
#undef HAVE_SYS_STAT_H
/* Define to 1 if you have the <sys/timespec.h> header file. */
#undef HAVE_SYS_TIMESPEC_H
/* Define to 1 if you have the <sys/time.h> header file. */
#undef HAVE_SYS_TIME_H
/* Define to 1 if you have the <sys/types.h> header file. */
#undef HAVE_SYS_TYPES_H
/* Define to 1 if you have the <sys/ucred.h> header file. */
#undef HAVE_SYS_UCRED_H
/* Define to 1 if you have the <sys/wait.h> header file. */
#undef HAVE_SYS_WAIT_H
/* Define to 1 if you have the <time.h> header file. */
#undef HAVE_TIME_H
/* Define to 1 if typeof works with your compiler. */
#undef HAVE_TYPEOF
/* Define to 1 if you have the <unistd.h> header file. */
#undef HAVE_UNISTD_H
/* Define to 1 if you have the <utime.h> header file. */
#undef HAVE_UTIME_H
/* Define to 1 if you have the <varargs.h> header file. */
#undef HAVE_VARARGS_H
/* Define to 1 if you have the `vprintf' function. */
#undef HAVE_VPRINTF
/* have header windows.h */
#undef HAVE_WINDOWS_H
/* have header winioctl.h */
#undef HAVE_WINIOCTL_H
/* have header winreg.h */
#undef HAVE_WINREG_H
/* have header winsock.h */
#undef HAVE_WINSOCK_H
/* Define to 1 if the system has the type `_Bool'. */
#undef HAVE__BOOL
/* Name of package */
#undef PACKAGE
/* Define to the address where bug reports for this package should be sent. */
#undef PACKAGE_BUGREPORT
/* Define to the full name of this package. */
#undef PACKAGE_NAME
/* Define to the full name and version of this package. */
#undef PACKAGE_STRING
/* Define to the one symbol short name of this package. */
#undef PACKAGE_TARNAME
/* Define to the home page for this package. */
#undef PACKAGE_URL
/* Define to the version of this package. */
#undef PACKAGE_VERSION
/* Define to 1 if you have the ANSI C header files. */
#undef STDC_HEADERS
/* Version number of package */
#undef VERSION
/* Enable large inode numbers on Mac OS X 10.5. */
#ifndef _DARWIN_USE_64_BIT_INODE
# define _DARWIN_USE_64_BIT_INODE 1
#endif
/* Number of bits in a file offset, on hosts where this is settable. */
#undef _FILE_OFFSET_BITS
/* Define for large files, on AIX-style hosts. */
#undef _LARGE_FILES
/* Define for Solaris 2.5.1 so the uint32_t typedef from <sys/synch.h>,
<pthread.h>, or <semaphore.h> is not used. If the typedef were allowed, the
#define below would cause a syntax error. */
#undef _UINT32_T
/* Define for Solaris 2.5.1 so the uint64_t typedef from <sys/synch.h>,
<pthread.h>, or <semaphore.h> is not used. If the typedef were allowed, the
#define below would cause a syntax error. */
#undef _UINT64_T
/* Define to empty if `const' does not conform to ANSI C. */
#undef const
/* Define to `int' if <sys/types.h> doesn't define. */
#undef gid_t
/* Define to `__inline__' or `__inline' if that's what the C compiler
calls it, or to nothing if 'inline' is not supported under any name. */
#ifndef __cplusplus
#undef inline
#endif
/* Define to the type of a signed integer type of width exactly 32 bits if
such a type exists and the standard includes do not define it. */
#undef int32_t
/* Define to the type of a signed integer type of width exactly 64 bits if
such a type exists and the standard includes do not define it. */
#undef int64_t
/* Define to `int' if <sys/types.h> does not define. */
#undef mode_t
/* Define to `long int' if <sys/types.h> does not define. */
#undef off_t
/* Define to `int' if <sys/types.h> does not define. */
#undef pid_t
/* Define to `unsigned int' if <sys/types.h> does not define. */
#undef size_t
/* Define to `int' if <sys/types.h> does not define. */
#undef ssize_t
/* Define to __typeof__ if your compiler spells it that way. */
#undef typeof
/* Define to `int' if <sys/types.h> doesn't define. */
#undef uid_t
/* Define to the type of an unsigned integer type of width exactly 32 bits if
such a type exists and the standard includes do not define it. */
#undef uint32_t
/* Define to the type of an unsigned integer type of width exactly 64 bits if
such a type exists and the standard includes do not define it. */
#undef uint64_t

216
configure vendored
View File

@ -1,6 +1,6 @@
#! /bin/sh
# Guess values for system-dependent variables and create Makefiles.
# Generated by GNU Autoconf 2.69 for vchanger 1.0.1.
# Generated by GNU Autoconf 2.69 for vchanger 1.0.3.
#
# Report bugs to <jfisher@jaybus.com>.
#
@ -580,8 +580,8 @@ MAKEFLAGS=
# Identity of this package.
PACKAGE_NAME='vchanger'
PACKAGE_TARNAME='vchanger'
PACKAGE_VERSION='1.0.1'
PACKAGE_STRING='vchanger 1.0.1'
PACKAGE_VERSION='1.0.3'
PACKAGE_STRING='vchanger 1.0.3'
PACKAGE_BUGREPORT='jfisher@jaybus.com'
PACKAGE_URL=''
@ -1279,7 +1279,7 @@ if test "$ac_init_help" = "long"; then
# Omit some internal or obsolete options to make the list less imposing.
# This message is too long to be a string in the A/UX 3.1 sh.
cat <<_ACEOF
\`configure' configures vchanger 1.0.1 to adapt to many kinds of systems.
\`configure' configures vchanger 1.0.3 to adapt to many kinds of systems.
Usage: $0 [OPTION]... [VAR=VALUE]...
@ -1345,7 +1345,7 @@ fi
if test -n "$ac_init_help"; then
case $ac_init_help in
short | recursive ) echo "Configuration of vchanger 1.0.1:";;
short | recursive ) echo "Configuration of vchanger 1.0.3:";;
esac
cat <<\_ACEOF
@ -1439,7 +1439,7 @@ fi
test -n "$ac_init_help" && exit $ac_status
if $ac_init_version; then
cat <<\_ACEOF
vchanger configure 1.0.1
vchanger configure 1.0.3
generated by GNU Autoconf 2.69
Copyright (C) 2012 Free Software Foundation, Inc.
@ -2026,11 +2026,57 @@ $as_echo "$ac_res" >&6; }
eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno
} # ac_fn_c_check_func
# ac_fn_c_check_decl LINENO SYMBOL VAR INCLUDES
# ---------------------------------------------
# Tests whether SYMBOL is declared in INCLUDES, setting cache variable VAR
# accordingly.
ac_fn_c_check_decl ()
{
as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack
as_decl_name=`echo $2|sed 's/ *(.*//'`
as_decl_use=`echo $2|sed -e 's/(/((/' -e 's/)/) 0&/' -e 's/,/) 0& (/g'`
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether $as_decl_name is declared" >&5
$as_echo_n "checking whether $as_decl_name is declared... " >&6; }
if eval \${$3+:} false; then :
$as_echo_n "(cached) " >&6
else
cat confdefs.h - <<_ACEOF >conftest.$ac_ext
/* end confdefs.h. */
$4
int
main ()
{
#ifndef $as_decl_name
#ifdef __cplusplus
(void) $as_decl_use;
#else
(void) $as_decl_name;
#endif
#endif
;
return 0;
}
_ACEOF
if ac_fn_c_try_compile "$LINENO"; then :
eval "$3=yes"
else
eval "$3=no"
fi
rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
fi
eval ac_res=\$$3
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5
$as_echo "$ac_res" >&6; }
eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno
} # ac_fn_c_check_decl
cat >config.log <<_ACEOF
This file contains any messages produced by compilers while
running configure, to aid debugging if configure makes a mistake.
It was created by vchanger $as_me 1.0.1, which was
It was created by vchanger $as_me 1.0.3, which was
generated by GNU Autoconf 2.69. Invocation command line was
$ $0 $@
@ -2342,12 +2388,14 @@ as_fn_append ac_header_list " grp.h"
as_fn_append ac_header_list " pwd.h"
as_fn_append ac_header_list " dirent.h"
as_fn_append ac_header_list " fcntl.h"
as_fn_append ac_header_list " sys/mman.h"
as_fn_append ac_header_list " sys/select.h"
as_fn_append ac_header_list " optarg.h"
as_fn_append ac_header_list " pthread.h"
as_fn_append ac_header_list " libgen.h"
as_fn_append ac_header_list " io.h"
as_fn_append ac_header_list " signal.h"
as_fn_append ac_header_list " semaphore.h"
# Check that the precious variables saved in the cache have kept the same
# value.
ac_cache_corrupted=false
@ -2417,7 +2465,7 @@ ac_compiler_gnu=$ac_cv_c_compiler_gnu
$as_echo "#define COPYRIGHT_NOTICE \"vchanger Copyright (c) 2006-2015 Josh Fisher\"" >>confdefs.h
$as_echo "#define COPYRIGHT_NOTICE \"vchanger Copyright (c) 2006-2020 Josh Fisher\"" >>confdefs.h
@ -2938,7 +2986,7 @@ fi
# Define the identity of the package.
PACKAGE='vchanger'
VERSION='1.0.1'
VERSION='1.0.3'
cat >>confdefs.h <<_ACEOF
@ -5514,6 +5562,70 @@ else
as_fn_error $? "\"could not find libpthread\"" "$LINENO" 5
fi
ac_fn_c_check_header_mongrel "$LINENO" "sys/mman.h" "ac_cv_header_sys_mman_h" "$ac_includes_default"
if test "x$ac_cv_header_sys_mman_h" = xyes; then :
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for library containing shm_open" >&5
$as_echo_n "checking for library containing shm_open... " >&6; }
if ${ac_cv_search_shm_open+:} false; then :
$as_echo_n "(cached) " >&6
else
ac_func_search_save_LIBS=$LIBS
cat confdefs.h - <<_ACEOF >conftest.$ac_ext
/* end confdefs.h. */
/* Override any GCC internal prototype to avoid an error.
Use char because int might match the return type of a GCC
builtin and then its argument prototype would still apply. */
#ifdef __cplusplus
extern "C"
#endif
char shm_open ();
int
main ()
{
return shm_open ();
;
return 0;
}
_ACEOF
for ac_lib in '' rt; do
if test -z "$ac_lib"; then
ac_res="none required"
else
ac_res=-l$ac_lib
LIBS="-l$ac_lib $ac_func_search_save_LIBS"
fi
if ac_fn_c_try_link "$LINENO"; then :
ac_cv_search_shm_open=$ac_res
fi
rm -f core conftest.err conftest.$ac_objext \
conftest$ac_exeext
if ${ac_cv_search_shm_open+:} false; then :
break
fi
done
if ${ac_cv_search_shm_open+:} false; then :
else
ac_cv_search_shm_open=no
fi
rm conftest.$ac_ext
LIBS=$ac_func_search_save_LIBS
fi
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_search_shm_open" >&5
$as_echo "$ac_cv_search_shm_open" >&6; }
ac_res=$ac_cv_search_shm_open
if test "$ac_res" != no; then :
test "$ac_res" = "none required" || LIBS="$ac_res $LIBS"
else
as_fn_error $? "shm_open() not found" "$LINENO" 5
fi
fi
# Support using libudev or libblkid for UUID lookup
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for library containing udev_new" >&5
$as_echo_n "checking for library containing udev_new... " >&6; }
@ -5960,6 +6072,10 @@ done
@ -6011,6 +6127,15 @@ $as_echo "#define HAVE_SHLOBJ_H /**/" >>confdefs.h
fi
ac_fn_c_check_header_compile "$LINENO" "direct.h" "ac_cv_header_direct_h" "#include <windows.h>
"
if test "x$ac_cv_header_direct_h" = xyes; then :
$as_echo "#define HAVE_DIRECT_H /**/" >>confdefs.h
fi
# Checks for functions.
for ac_func in vprintf
do :
@ -6031,32 +6156,18 @@ fi
done
for ac_func in setlocale getmntent getmntent_r getfsstat
do :
as_ac_var=`$as_echo "ac_cv_func_$ac_func" | $as_tr_sh`
ac_fn_c_check_func "$LINENO" "$ac_func" "$as_ac_var"
if eval test \"x\$"$as_ac_var"\" = x"yes"; then :
cat >>confdefs.h <<_ACEOF
#define `$as_echo "HAVE_$ac_func" | $as_tr_cpp` 1
_ACEOF
ac_fn_c_check_decl "$LINENO" "localtime_r" "ac_cv_have_decl_localtime_r" "$ac_includes_default
#include <pthread.h>
#include <time.h>
"
if test "x$ac_cv_have_decl_localtime_r" = xyes; then :
$as_echo "#define HAVE_LOCALTIME_R /**/" >>confdefs.h
fi
done
ac_fn_c_check_func "$LINENO" "getline" "ac_cv_func_getline"
if test "x$ac_cv_func_getline" = xyes; then :
$as_echo "#define HAVE_GETLINE 1" >>confdefs.h
else
case " $LIBOBJS " in
*" getline.$ac_objext "* ) ;;
*) LIBOBJS="$LIBOBJS getline.$ac_objext"
;;
esac
fi
ac_fn_c_check_func "$LINENO" "gettimeofday" "ac_cv_func_gettimeofday"
if test "x$ac_cv_func_gettimeofday" = xyes; then :
$as_echo "#define HAVE_GETTIMEOFDAY 1" >>confdefs.h
@ -6070,6 +6181,19 @@ esac
fi
ac_fn_c_check_func "$LINENO" "getline" "ac_cv_func_getline"
if test "x$ac_cv_func_getline" = xyes; then :
$as_echo "#define HAVE_GETLINE 1" >>confdefs.h
else
case " $LIBOBJS " in
*" getline.$ac_objext "* ) ;;
*) LIBOBJS="$LIBOBJS getline.$ac_objext"
;;
esac
fi
ac_fn_c_check_func "$LINENO" "getuid" "ac_cv_func_getuid"
if test "x$ac_cv_func_getuid" = xyes; then :
$as_echo "#define HAVE_GETUID 1" >>confdefs.h
@ -6083,19 +6207,6 @@ esac
fi
ac_fn_c_check_func "$LINENO" "localtime_r" "ac_cv_func_localtime_r"
if test "x$ac_cv_func_localtime_r" = xyes; then :
$as_echo "#define HAVE_LOCALTIME_R 1" >>confdefs.h
else
case " $LIBOBJS " in
*" localtime_r.$ac_objext "* ) ;;
*) LIBOBJS="$LIBOBJS localtime_r.$ac_objext"
;;
esac
fi
ac_fn_c_check_func "$LINENO" "pipe" "ac_cv_func_pipe"
if test "x$ac_cv_func_pipe" = xyes; then :
$as_echo "#define HAVE_PIPE 1" >>confdefs.h
@ -6163,6 +6274,19 @@ fi
for ac_func in setlocale getmntent getmntent_r getfsstat
do :
as_ac_var=`$as_echo "ac_cv_func_$ac_func" | $as_tr_sh`
ac_fn_c_check_func "$LINENO" "$ac_func" "$as_ac_var"
if eval test \"x\$"$as_ac_var"\" = x"yes"; then :
cat >>confdefs.h <<_ACEOF
#define `$as_echo "HAVE_$ac_func" | $as_tr_cpp` 1
_ACEOF
fi
done
ac_config_files="$ac_config_files Makefile src/Makefile doc/Makefile scripts/Makefile"
@ -6700,7 +6824,7 @@ cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1
# report actual input values of CONFIG_FILES etc. instead of their
# values after options handling.
ac_log="
This file was extended by vchanger $as_me 1.0.1, which was
This file was extended by vchanger $as_me 1.0.3, which was
generated by GNU Autoconf 2.69. Invocation command line was
CONFIG_FILES = $CONFIG_FILES
@ -6766,7 +6890,7 @@ _ACEOF
cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1
ac_cs_config="`$as_echo "$ac_configure_args" | sed 's/^ //; s/[\\""\`\$]/\\\\&/g'`"
ac_cs_version="\\
vchanger config.status 1.0.1
vchanger config.status 1.0.3
configured by $0, generated by GNU Autoconf 2.69,
with options \\"\$ac_cs_config\\"

View File

@ -1,9 +1,9 @@
# -*- Autoconf -*-
# Process this file with autoconf to produce a configure script.
AC_PREREQ([2.63])
AC_INIT([vchanger], [1.0.1], [jfisher@jaybus.com])
AC_DEFINE([COPYRIGHT_NOTICE],["AC_PACKAGE_NAME Copyright (c) 2006-2015 Josh Fisher"],[Copyright notice])
AC_PREREQ([2.69])
AC_INIT([vchanger], [1.0.3], [jfisher@jaybus.com])
AC_DEFINE([COPYRIGHT_NOTICE],["AC_PACKAGE_NAME Copyright (c) 2006-2020 Josh Fisher"],[Copyright notice])
AC_CONFIG_SRCDIR([src])
AC_CONFIG_LIBOBJ_DIR([src/compat])
AC_CONFIG_HEADERS([config.h])
@ -34,6 +34,9 @@ AC_SYS_LARGEFILE
# Use multithreading
AC_SEARCH_LIBS([pthread_create], [pthread], [], [AC_MSG_ERROR(["could not find libpthread"])])
AC_CHECK_HEADER([sys/mman.h],
[ AC_SEARCH_LIBS([shm_open],[rt],[],[AC_MSG_ERROR([shm_open() not found])])
])
# Support using libudev or libblkid for UUID lookup
AC_SEARCH_LIBS([udev_new],[udev],[AC_CHECK_HEADERS([libudev.h])])
AC_CHECK_HEADER([blkid/blkid.h],
@ -50,21 +53,27 @@ AC_CHECK_HEADERS_ONCE([stdio.h stdlib.h string.h stdarg.h stddef.h])
AC_CHECK_HEADERS_ONCE([time.h sys/time.h sys/timespec.h sys/wait.h limits.h locale.h syslog.h])
AC_CHECK_HEADERS_ONCE([sys/types.h strings.h alloca.h sys/bitypes.h getopt.h utime.h sys/stat.h])
AC_CHECK_HEADERS_ONCE([inttypes.h ctype.h errno.h unistd.h varargs.h mntent.h])
AC_CHECK_HEADERS_ONCE([sys/param.h sys/mount.h sys/ucred.h grp.h pwd.h dirent.h fcntl.h])
AC_CHECK_HEADERS_ONCE([sys/select.h optarg.h pthread.h libgen.h io.h signal.h])
AC_CHECK_HEADER([windows.h],
[AC_DEFINE([HAVE_WINDOWS_H],,[have header windows.h])
AC_CHECK_HEADERS_ONCE([sys/param.h sys/mount.h sys/ucred.h grp.h pwd.h dirent.h fcntl.h sys/mman.h])
AC_CHECK_HEADERS_ONCE([sys/select.h optarg.h pthread.h libgen.h io.h signal.h semaphore.h])
AC_CHECK_HEADER([windows.h], [AC_DEFINE([HAVE_WINDOWS_H],,[have header windows.h])
WINLDADD=-static])
AC_SUBST(WINLDADD)
AC_CHECK_HEADER([winioctl.h], [AC_DEFINE([HAVE_WINIOCTL_H],,[have header winioctl.h])], [], [#include <windows.h>])
AC_CHECK_HEADER([winsock.h], [AC_DEFINE([HAVE_WINSOCK_H],,[have header winsock.h])], [], [#include <windows.h>])
AC_CHECK_HEADER([winreg.h], [AC_DEFINE([HAVE_WINREG_H],,[have header winreg.h])], [], [#include <windows.h>])
AC_CHECK_HEADER([shlobj.h], [AC_DEFINE([HAVE_SHLOBJ_H],,[have header shlobj.h])], [], [#include <windows.h>])
AC_CHECK_HEADER([direct.h], [AC_DEFINE([HAVE_DIRECT_H],,[have header direct.h])], [], [#include <windows.h>])
# Checks for functions.
AC_FUNC_VPRINTF
AC_CHECK_FUNCS([setlocale getmntent getmntent_r getfsstat])
AC_REPLACE_FUNCS([getline gettimeofday getuid localtime_r pipe readlink sleep symlink syslog])
AC_CHECK_DECL([localtime_r], [AC_DEFINE([HAVE_LOCALTIME_R],,[have function localtime_r])], [],
[AC_INCLUDES_DEFAULT
#include <pthread.h>
#include <time.h>])
AC_REPLACE_FUNCS([gettimeofday getline getuid pipe readlink sleep symlink syslog])
AC_CHECK_FUNCS([setlocale getmntent getmntent_r getfsstat])
AC_CONFIG_FILES([Makefile src/Makefile doc/Makefile scripts/Makefile])

View File

@ -0,0 +1,5 @@
/var/log/vchanger/*.log {
missingok
notifempty
sharedscripts
}

View File

@ -1,4 +1,5 @@
# This file contains udev rules for automounting drives assigned to vchanger
# This file contains example udev rules for automounting filesystems assigned to vchanger
# See vchangerHowto.html for details on generating udev rules for vchanger magazines.
#
# Rules for magazine 0 (uuid:7b4526c4-d8e9-48ba-b227-f67f855a0dc7)
ACTION=="add",SUBSYSTEM=="block", ENV{ID_FS_UUID}=="7b4526c4-d8e9-48ba-b227-f67f855a0dc7", RUN+="/usr/libexec/vchanger/vchanger-launch-mount.sh 7b4526c4-d8e9-48ba-b227-f67f855a0dc7"

View File

@ -1,4 +1,4 @@
# Example vchanger 1.0.0 config file
# Example vchanger 1.0.2 config file
#
# Storage Resource Name of the Storage resource defined in bacula-dir.conf
@ -10,27 +10,27 @@
# User User to run as when invoked by root. This must be the
# owner of the volume files controlled by this changer, and
# so will need to be the same user that bacula-sd runs as.
# [Default: none ]
User = bacula
# [Default: bacula ]
#User = bacula
#
# Group Group to run as when invoked by root. This should be the
# owner group of the volume files controlled by this changer,
# and should also be the default group pf the user that
# and should also be the default group of the user that
# bacula-sd runs as.
# [Default: none ]
group = tape
# [Default: tape ]
#group = tape
#
# Work Dir Directory where virtual drive and magazine state information
# and symlinks for this changer are stored.
# [Default: /var/spool/vchanger/[StorageResource] ]
# [Default: /var/spool/vchanger/{StorageResource} ]
#work dir = "/var/spool/vchanger/vcahnger"
#
# Logfile Path to log file for this changer.
# [Default: /var/log/vchanger/[StorageResource].log ]
#logfile = "/var/log/vchanger/vchanger.log"
# [Default: {WorkDir}/{StorageResource}.log ]
#logfile = "/var/spool/vchanger/vchanger.log"
#
# Log Level Sets the level of detail being logged. An integer value from
@ -50,7 +50,8 @@ group = tape
#
# bconsole config Path to the config file bconsole will use when vchanger
# invokes bconsole. By default, bconsole will be invoked
# without the -c flag.
# without the -c flag. The file must be readable by the user
# vchanger is run as.
# [Default: none ]
#bconsole config = /etc/bacula/bconsole.conf
@ -69,7 +70,9 @@ group = tape
# by prefixing the string "UUID:" to the filesystem's UUID.
# For magazines specified by UUID, the mountpoint of the
# filesystem will be queried from the system. Note that vchanger
# does not attempt to mount the filesystem.
# does not attempt to mount the magazines that are specified
# by filesystem UUID. Vchanger utilizes all magazines that are
# already mounted at the time it is invoked.
# [Default: none ]
#magazine = "uuid:4fcb1422-f15c-4d7a-8a32-a4dcc0af5e00"
#Magazine = "/mnt/backup2"

View File

@ -2,12 +2,12 @@
.\" Title: vchanger
.\" Author: Josh Fisher <jfisher@jaybus.com>
.\" Generator: DocBook XSL Stylesheets v1.78.1 <http://docbook.sf.net/>
.\" Date: 06/03/2015
.\" Date: 05/11/2020
.\" Manual: vchanger Manual
.\" Source: vchanger 1.0.1
.\" Source: vchanger 1.0.3
.\" Language: English
.\"
.TH "VCHANGER" "8" "06/03/2015" "vchanger 1\&.0\&.1" "vchanger Manual"
.TH "VCHANGER" "8" "05/11/2020" "vchanger 1\&.0\&.3" "vchanger Manual"
.\" -----------------------------------------------------------------
.\" * Define some portability stuff
.\" -----------------------------------------------------------------
@ -40,7 +40,7 @@ vchanger \- Virtual disk\-based autochanger for Bacula network backup system
\fBvchanger\fR [\fIOptions\fR] config REFRESH
.SH "DESCRIPTION"
.sp
The \fBvchanger(8)\fR utility is used to emulate and control a virtual autochanger within the Bacula network backup system environment\&. Backup volumes stored on multiple disk filesystems are mapped to a single set of virtual slots, allowing an unlimited number of virtual drives for concurrent backup jobs and easy, unlimited scaling to any size by simply adding additional disks/filesystems,
The \fBvchanger(8)\fR utility is used to emulate and control a virtual autochanger within the Bacula network backup system environment\&. Backup volumes stored on multiple disk filesystems are mapped to a single set of virtual slots\&. This allows an unlimited number of virtual drives and an unlimited number of virtual slots spread across an unlimited number of physical disk drives to be assigned to a single autochanger\&. This allows unlimited scaling of the cirtual autochanger simply by adding additional disk drives\&.
.sp
Vchanger is primarily deigned for use with removable disk drives\&. Its ability to interact with Bacula and determine removable drive mount points through udev allow for plug\-n\-play operation when attaching and detaching removable disk drives\&.
.sp
@ -142,9 +142,9 @@ command to Bacula if required\&.
.sp
\fBBacula Interaction\fR
.sp
By default, vcahgner will invoke bconsole and issue commands to Bacula when certain operator actions are needed\&. When anything happens that changes the current set of volume files being used, vchanger will invoke bconsole and issue an \fIupdate slots\fR command\&. For example, when the operator attaches a removable drive defined as one of the changer\(cqs magazines, the volume files on the removable drive must be mapped to virtual slots\&. Since the volume\-to\-slot mapping will have changed, Bacula will need to be informed of the change via the \fIupdate slots\fR command\&. The \fBREFRESH\fR command can be invoked to force vchanger to update state info and trigger \fIupdate slots\fR if needed\&.
By default, vcahgner will invoke bconsole and issue commands to Bacula when certain operator actions are needed\&. When anything happens that changes the current set of volume files being used, (the virtual slot to volume file mapping), vchanger will invoke bconsole and issue an \fIupdate slots\fR command\&. For example, when the operator attaches a removable drive defined as one of the changer\(cqs magazines, the volume files on the removable drive must be mapped to virtual slots\&. Since the slot\-to\-volume mapping will have changed, Bacula will need to be informed of the change via the \fIupdate slots\fR command\&. The \fBREFRESH\fR command can be invoked to force vchanger to update state info and trigger \fIupdate slots\fR if needed\&.
.sp
Additionally, when new volumes are created with the \fBCREATEVOLS\fR command, vchanger will invoke bconsole and issue a \fIlabel barcodes\fR command to write volume labels on the newly created volume files\&.
Additionally, when new volumes are created with the \fBCREATEVOLS\fR command, vchanger will invoke bconsole and issue a \fIlabel barcodes\fR command to allow Bacula to write volume labels on the newly created volume files\&.
.SH "COMMAND LINE OPTIONS"
.PP
\fB\-u, \-\-user\fR=\fIuid\fR
@ -195,7 +195,7 @@ See the vchangerHowto\&.html file included in the doc directory of the source di
\fBvchanger\&.conf(5)\fR
.SH "COPYRIGHT"
.sp
Copyright 2006\-2015 Josh Fisher
Copyright 2006\-2020 Josh Fisher
.sp
This is free software; See the source for copying conditions\&. There is NO warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE\&.
.SH "AUTHOR"

View File

@ -3,7 +3,7 @@ VCHANGER(8)
Josh Fisher <jfisher@jaybus.com>
:doctype: manpage
:man source: vchanger
:man version: 1.0.1
:man version: 1.0.3
:man manual: vchanger Manual
NAME
@ -27,9 +27,11 @@ DESCRIPTION
The *vchanger(8)* utility is used to emulate and control a virtual
autochanger within the Bacula network backup system environment.
Backup volumes stored on multiple disk filesystems are mapped to a
single set of virtual slots, allowing an unlimited number of virtual
drives for concurrent backup jobs and easy, unlimited scaling to any
size by simply adding additional disks/filesystems,
single set of virtual slots. This allows an unlimited number of
virtual drives and an unlimited number of virtual slots spread
across an unlimited number of physical disk drives to be assigned
to a single autochanger. This allows unlimited scaling of the cirtual
autochanger simply by adding additional disk drives.
Vchanger is primarily deigned for use with removable disk drives. Its
ability to interact with Bacula and determine removable drive mount
@ -115,18 +117,19 @@ Additionally, the following extended commands are supported.
By default, vcahgner will invoke bconsole and issue commands to Bacula
when certain operator actions are needed. When anything happens that
changes the current set of volume files being used, vchanger will
invoke bconsole and issue an 'update slots' command. For example,
when the operator attaches a removable drive defined as one of the
changer's magazines, the volume files on the removable drive must be
mapped to virtual slots. Since the volume-to-slot mapping will have
changed, Bacula will need to be informed of the change via the
'update slots' command. The *REFRESH* command can be invoked to force
vchanger to update state info and trigger 'update slots' if needed.
changes the current set of volume files being used, (the virtual slot
to volume file mapping), vchanger will invoke bconsole and issue an
'update slots' command. For example, when the operator attaches a
removable drive defined as one of the changer's magazines, the volume
files on the removable drive must be mapped to virtual slots. Since
the slot-to-volume mapping will have changed, Bacula will need to be
informed of the change via the 'update slots' command. The *REFRESH*
command can be invoked to force vchanger to update state info and
trigger 'update slots' if needed.
Additionally, when new volumes are created with the *CREATEVOLS* command,
vchanger will invoke bconsole and issue a 'label barcodes' command to
write volume labels on the newly created volume files.
allow Bacula to write volume labels on the newly created volume files.
COMMAND LINE OPTIONS
--------------------
@ -172,7 +175,7 @@ SEE ALSO
COPYRIGHT
---------
Copyright 2006-2015 Josh Fisher
Copyright 2006-2020 Josh Fisher
This is free software;
See the source for copying conditions.

View File

@ -2,12 +2,12 @@
.\" Title: vchanger.conf
.\" Author: Josh Fisher <jfisher@jaybus.com>
.\" Generator: DocBook XSL Stylesheets v1.78.1 <http://docbook.sf.net/>
.\" Date: 06/03/2015
.\" Date: 05/11/2020
.\" Manual: vchanger Manual
.\" Source: vchanger.conf 1.0.1
.\" Source: vchanger.conf 1.0.3
.\" Language: English
.\"
.TH "VCHANGER\&.CONF" "5" "06/03/2015" "vchanger\&.conf 1\&.0\&.1" "vchanger Manual"
.TH "VCHANGER\&.CONF" "5" "05/11/2020" "vchanger\&.conf 1\&.0\&.3" "vchanger Manual"
.\" -----------------------------------------------------------------
.\" * Define some portability stuff
.\" -----------------------------------------------------------------
@ -134,7 +134,7 @@ See the vchangerHowto\&.html file included in the doc directory of the source di
\fBvchanger(8)\fR
.SH "COPYRIGHT"
.sp
Copyright 2006\-2015 Josh Fisher
Copyright 2006\-2020 Josh Fisher
.sp
This is free software; See the source for copying conditions\&. There is NO warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE\&.
.SH "AUTHOR"

View File

@ -3,7 +3,7 @@ VCHANGER.CONF(5)
Josh Fisher <jfisher@jaybus.com>
:doctype: manpage
:man source: vchanger.conf
:man version: 1.0.1
:man version: 1.0.3
:man manual: vchanger Manual
NAME
@ -112,7 +112,7 @@ SEE ALSO
COPYRIGHT
---------
Copyright 2006-2015 Josh Fisher
Copyright 2006-2020 Josh Fisher
This is free software;
See the source for copying conditions.

View File

@ -1,7 +1,7 @@
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
<html>
<head>
<meta content="text/html; charset=windows-1252" http-equiv="content-type">
<meta http-equiv="content-type" content="text/html; charset=windows-1252">
<title></title>
<meta name="CREATED" content="20061109;11301500">
<meta name="CHANGEDBY" content="Josh Fisher">
@ -28,6 +28,18 @@ UL.no_image { list-style:none; }
<p style="font-weight:bold; margin-bottom:0;">Josh Fisher</p>
<p>&lt;jfisher at jaybus dot com&gt;</p>
<p style="font-weight:bold;">Revision History</p>
<p class="p_zero_bottom">Revision 1.0.3 2020-05-11</p>
<p class="p_indent">Documented changes related to version 1.0.3 of the
software. This release fixes a few bugs affecting compilation on some
platforms and correcting the number of slots reported by the SIZE command.
Additionally, the IPC locking mechanism used is changed to use POSIX
semaphores</p>
<p class="p_zero_bottom">Revision 1.0.2 2018-06-14</p>
<p class="p_indent">Documented changes related to version 1.0.2 of the
software. This release fixes a few bugs affecting interaction with
bconsole, changes the default formatting of the barcode labels for new
volumes, and adds additional logging of udev/UUID mountpoint discovery
steps.</p>
<p class="p_zero_bottom">Revision 1.0.1 2015-06-09</p>
<p class="p_indent">Documented changes related to version 1.0.1 of the
software. This is a minor bug fix release.</p>
@ -97,6 +109,8 @@ UL.no_image { list-style:none; }
<ul class="no_image">
<li>5.3.1. <a href="#win32_build">Installing the Windows Version
from Source</a></li>
<li>5.3.2. <a href="#win32_installer_build">Building the Windows
installer</a></li>
</ul>
</li>
</ul>
@ -161,7 +175,7 @@ UL.no_image { list-style:none; }
an open source network backup solution. Bacula can use many types of
backup devices, one of them being robotic tape libraries, also known as
autochangers. Bacula utilizes these devices through an interface known as
the <a href="http://www.bacula.org/7.0.x-manuals/en/main/index.html">Bacula
the <a href="http://www.bacula.org/9.6.x-manuals/en/main/Autochanger_Resource.html#SECTION0032130000000000000000">Bacula
Autochanger
Interface</a>. Because autochangers are controlled in various
proprietary ways, the Bacula Autochanger Interface provides a generalized
@ -172,7 +186,7 @@ Autochanger
Interface to act as a virtual autochanger, using files on disk storage in
place of tapes.</p>
<h2><a name="copyright"></a>1.1 Copyright And License</h2>
<p>This document, Vchanger<em>HOWTO</em>, is Copyright (c) 2008-2015 by <em><span
<p>This document, Vchanger<em>HOWTO</em>, is Copyright (c) 2008-2020 by <em><span
style="font-style: normal">Josh Fisher</span></em>. Permission is
granted to copy, distribute and/or modify this document under the terms of
the GNU Free Documentation License, Version 1.1 or any later version
@ -197,9 +211,9 @@ Users
<p>Thanks, also, to all those who frequent the <a href="https://lists.sourceforge.net/lists/listinfo/bacula-users">Bacula
User's e-mail list</a>, and of course to Kern Sibbald and the other <a
href="http://www.bacula.org/">Bacula</a> developers.</p>
<p>Bacula<sub>®</sub> is a registered trademark of Kern Sibbald.</p>
<p>Windows<sub>®</sub> is a registered trademark of Microsoft Corporation in
the United States and other countries.</p>
<p>Bacula<sub>&reg;</sub> is a registered trademark of Kern Sibbald.</p>
<p>Windows<sub>&reg;</sub> is a registered trademark of Microsoft
Corporation in the United States and other countries.</p>
<h2><a name="feedback"></a>1.4 Feedback</h2>
<p><a href="https://lists.sourceforge.net/lists/listinfo/vchanger-users">Vchanger
Users
@ -211,17 +225,18 @@ Users
information about Bacula is available at the Bacula website <a href="http://www.bacula.org/">http://www.bacula.org</a>.</p>
<p>An <i>autochanger</i> is a backup storage device consisting of one or
more tape drives, a mechanical tape library where a number of tape
cartridges are stored, and an individually addressable robotic device
capable of physically moving tape cartridges between the tape library and
the tape drives. The tape library contains mechanical slots, each of which
can hold one tape cartridge. In most cases, the slots are located in one
or more mechanical magazines, (tape caddies), that can be inserted into
and removed from one or more or the tape library's magazine bays.</p>
<p>A <i>virtual autochanger</i> emulates a tape autochanger using disk
storage, rather than tapes. Note that in the strictest sense, vchanger is
not a tape library emulator, in that it does not implement a device driver
with SCSI command interface. Rather it utilizes an API implemented by
Bacula to manipulate autochangers.</p>
cartridges are stored in slots, and an individually addressable robotic
device capable of physically moving tape cartridges between the tape
library slots and the tape drives. The tape library contains mechanical
slots, each of which can hold one tape cartridge. In most cases, the slots
are located in one or more mechanical magazines, (tape caddies), that can
be inserted into and removed from one or more or the tape library's
magazine bays.</p>
<p>A vchanger <i>autochanger</i> emulates a tape autochanger using files on
disk storage in place of tapes. Note that in the strictest sense, vchanger
is not a tape library emulator, in that it does not implement a device
driver with SCSI command interface. Rather it utilizes an API implemented
by Bacula to manipulate autochangers.</p>
<p>A <em>slot</em> is a physical location in a tape library that may
contain one volume (ie. tape cartridge).</p>
<p>A <i>magazine</i> is a mechanical tape storage device having a number of
@ -241,11 +256,11 @@ Users
stream.</p>
<p>The term drive, unless otherwise stated, refers to one of an
autochanger's drives, a device capable of reading and writing data from/to
a single tape or volume file.</p>
a single volume at a time.</p>
<h1><a name="overview"></a>3. Overview</h1>
<p>The Bacula Storage Daemon controls an autochanger by invoking a script or
program, passing it command line arguments defined by the <a target="_blank"
href="http://www.bacula.org/7.0.x-manuals/en/main/Autochanger_Resource.html#SECTION001830000000000000000">Bacula
href="http://www.bacula.org/9.6.x-manuals/en/main/Autochanger_Resource.html#SECTION001830000000000000000">Bacula
Autochanger
Interface</a>. The interface defines a set of commands and associated
arguments to load and unload the autochanger's drives, determine which
@ -261,8 +276,8 @@ Autochanger
into tape drives, as the mtx-changer script does for tape autochanger
hardware, vchanger "loads" disk file volumes from virtual slots into
virtual drives by creating symlinks pointing to volume files located on
one or more filesystems or directories assigned to the virtual
autochanger.</p>
one or more virtual magazines, where a virtual magazine is a disk file
system or a directory.</p>
<p>Using autochanger interface commands, Bacula interacts with a vchanger
autochanger in exactly the same way as it interacts with a tape
autochanger. Therefore to understand how Bacula interacts with a vchanger
@ -308,15 +323,16 @@ Autochanger
configuration for a vchanger autochanger looks very much the same,</p>
<pre style="margin-left: 2.5em;"># /etc/bacula/bacula-sd.conf<br>Autochanger {<br> Name = vchgr<br> Changer Device = /etc/vchanger/vchgr.conf<br> Changer Command = "/usr/bin/vchanger %c %o %S %a %d"<br> Device = vchgr-0, vchgr-1<br>}<br>Device {<br> Name = vchgr-0<br> Drive Index = 0<br> Autochanger = yes<br> Archive Device = /var/spool/vchanger/vchgr/0<br>}<br>Device {<br> Name = vchgr-1<br> Drive Index = 1<br> Autochanger = yes<br> Archive Device = /var/spool/vchanger/vchgr/1<br>}
</pre>
<p>One obvious difference is that vchanger is invoked as the <span style="font-style: italic; font-weight: bold;">Changer
Command</span> in place of the mtx-changer script, and with the <span style="font-style: italic; font-weight: bold;">Changer
Device</span> passed as parameter 1 being the path to a vchanger
configuration file, rather than the device node of a tape library robot.
The only other difference is the <span style="font-style: italic; font-weight: bold;">Archive
Device</span> setting in each of the <span style="font-style: italic; font-weight: bold;">Device
</span>resources. Rather than the device node of a physical tape drive,
the <span style="font-style: italic; font-weight: bold;">Archive Device</span>
of a vchanger autochanger's <span style="font-style: italic; font-weight: bold;">Device
<p>One obvious difference is that <span style="font-style: italic; font-weight: bold;">Changer
Command</span> defines the path to the vchanger binary, rather than the
mtx-changer script, with<span style="font-style: italic; font-weight: bold;"></span>
parameter 1 being the path to a vchanger configuration file, rather than
the device node of a tape library robot. The only other difference is the
<span style="font-style: italic; font-weight: bold;">Archive Device</span>
setting in each of the <span style="font-style: italic; font-weight: bold;">Device
</span>resources associated with the <i><b>Autochanger </b></i>resource.
Rather than the device node of a physical tape drive, the <span style="font-style: italic; font-weight: bold;">Archive
Device</span> of a vchanger autochanger's <span style="font-style: italic; font-weight: bold;">Device
</span>resource specifies the path to a symlink that will act as a
virtual drive. When vchanger is invoked with the LOAD command, it creates
this symlink pointing to the volume file associated with the requested
@ -392,7 +408,7 @@ Autochanger
resource to be used with multiple disk storage <span style="font-style: italic; font-weight: bold;">Device
</span>resources as a disk-based virtual autochanger.</p>
<p>For example, the following placed in the Bacula Storage Daemon
configuration defines a "native" disk virtual autochanger named FileChgr1
configuration defines a native disk virtual autochanger named FileChgr1
with two virtual drives. The two <span style="font-style: italic; font-weight: bold;">Device
</span>resources (virtual drives) allow two volume files to be open at
the same time, and of course more than two <span style="font-style: italic; font-weight: bold;">Device
@ -431,10 +447,10 @@ Device {
<p>Notice that both <span style="font-style: italic; font-weight: bold;">Device
</span>resources specify the same <span style="font-style: italic; font-weight: bold;">Archive
Device</span> and <span style="font-style: italic; font-weight: bold;">Media
Type</span>. This is because the <span style="font-style: italic; font-weight: bold;">Storage
</span>resource in the Director configuration may only specify a single
<span style="font-style: italic; font-weight: bold;">Media Type</span>. A
volume is given the <span style="font-style: italic; font-weight: bold;">Media
Type</span>. This is because the corresponding <span style="font-style: italic; font-weight: bold;">Autochanger
</span>resource in the Director's bacula-dir.conf configuration may only
specify a single <span style="font-style: italic; font-weight: bold;">Media
Type</span>. A volume is given the <span style="font-style: italic; font-weight: bold;">Media
Type</span> of the <span style="font-style: italic; font-weight: bold;">Device
</span>it is written with. Since a location may have several tape drives
of the same type, Bacula uses the <span style="font-style: italic; font-weight: bold;">Media
@ -451,34 +467,36 @@ Device {
Types</span> to prevent Bacula from attempting to load a <span style="font-weight: bold; font-style: italic;">Device</span>
with a volume file that does not exist on its <span style="font-weight: bold; font-style: italic;">Archive
Device</span>. That in turn means that all of the autochanger's <span style="font-style: italic; font-weight: bold;">Devices</span>
could not be associated to a single <span style="font-style: italic; font-weight: bold;">Storage
could not be associated to a single <span style="font-style: italic; font-weight: bold;">Autochanger
</span>resource<span style="font-style: italic;"> </span>in the
Director configuration, and so defeats the purpose. </p>
Director configuration. </p>
<h2><a name="native_autochanger"></a>3.2. Why vchanger?</h2>
<p>Because Bacula currently requires a <span style="font-style: italic; font-weight: bold;">Storage
</span>resource to specify a single <span style="font-style: italic; font-weight: bold;">Media
</span>and <b>Autochanger</b> resources to specify a single <span style="font-style: italic; font-weight: bold;">Media
Type</span>, the native virtual autochanger is, for practical purposes,
limited to utilizing a single directory as volume file storage. If volume
files are contained in multiple filesystems, such as removable drives,
then those filesystems can only be mounted at that directory one at a
time. With vchanger, an unlimited number of filesystems may be
simultaneously mounted at different mount point directories with all
volume files on all filesystems having the same <span style="font-style: italic; font-weight: bold;">Media
Type</span>. This allows an unlimited number of simultaneously available
volume files and an unlimited number of <span style="font-style: italic; font-weight: bold;">Device
limited to utilizing a single directory as volume file storage for a
particular <i><b>Media Type</b></i>. If volume files are contained in
multiple filesystems, such as removable drives, then those filesystems can
only be mounted at that directory one at a time. With vchanger, an
unlimited number of filesystems may be simultaneously mounted at different
mount point directories with all volume files on all filesystems having
the same <span style="font-style: italic; font-weight: bold;">Media Type</span>.
This allows an unlimited number of simultaneously available volume files
and an unlimited number of <span style="font-style: italic; font-weight: bold;">Device
</span>resources (virtual drives), where any volume file on any
filesystem can be "loaded" into any of the autochanger's <span style="font-style: italic; font-weight: bold;">Device
</span>resources using only a single <span style="font-style: italic; font-weight: bold;">Storage
</span>resource in the Bacula Director. This gives vchanger the
following advantages:</p>
</span>resources using only a single <span style="font-style: italic; font-weight: bold;">Autochanger
</span>resource in the Director's bacula-dir.conf file. This gives
vchanger the following advantages:</p>
<ul>
<li>Available volume storage space can be easily scaled by adding
additional filesystems or directories</li>
<li>Scaling available volume storage space requires no change to the
Bacula configuration and no restart or reload of any Bacula daemons</li>
<li>All volumes have the same Media Type and so can be moved between
filesystems without any need to update volume information in the Bacula
catalog.</li>
<li>All volumes have the same <i><b>Media Type</b></i> and so can be
moved between filesystems without any need to update volume information
in the Bacula catalog (other than an <i>update slots</i> command in
bconsole to update the volume's slot number).</li>
<li>Filesystems can be specified by UUID and vchanger will query the OS
for the mount point when needed. This allows any type of automounting to
be used and the filesystems to be mounted at arbitrary paths.</li>
@ -489,27 +507,27 @@ Device {
interventions to swap removable drives. </li>
</ul>
<p>By default, vchanger supports integration with Bacula through issuing
commands via bconsole. Whenever a change is detected in the volume file to
virtual slot mapping, for example when a removable drive is attached or
detached, vchanger will issue an <span style="font-style: italic;">update
commands via bconsole. Whenever a change is detected in the
volume-file-to-virtual-slot mapping, for example when a removable drive is
attached or detached, vchanger will issue an <span style="font-style: italic;">update
slots</span> command to synchronize Bacula's view of the autochan<span style="font-style: italic;"></span>ger's
available volumes. Also, when vchanger is used to add volume files to an
autochanger, a <span style="font-style: italic;">label barcodes</span>
command is automatically issued to label the new volumes.</p>
<p>Vchanger also automates the generation of udev rules and scripts that may
be used to automatically mount and unmount the filesystems defined in
autochanger configuration files. When a removable drive is attached or
detached, a script may be launched by udev to mount or unmount the drive's
partition and issue an <span style="font-style: italic;">update slots</span>
command via bconsole. These udev rules and scripts allow the attaching and
detaching of&nbsp; removable drives used by vchanger to be a true
plug-n-play operation.<br>
command is automatically issued to label the newly created volumes.</p>
<p>Vchanger also provides scripts that may be used to automate the
generation of udev rules and automatically mount and unmount the
filesystems defined in autochanger configuration files. When a removable
drive is attached or detached, a script may be launched by udev to mount
or unmount the drive's partition and issue an <span style="font-style: italic;">update
slots</span> command via bconsole. These udev rules and scripts allow
the attaching and detaching of removable drives used by vchanger to be a
true plug-n-play operation.<br>
</p>
<h1><a name="implementation"></a>4. Virtual Autochanger Implementation</h1>
<p>Vchanger implements the following <a target="_blank" href="http://www.bacula.org/7.0.x-manuals/en/main/Autochanger_Resource.html#SECTION001830000000000000000">Bacula
<p>Vchanger implements the following <a target="_blank" href="http://www.bacula.org/9.6.x-manuals/en/main/Autochanger_Resource.html#SECTION001830000000000000000">Bacula
Autochanger Interface</a> commands:</p>
<table style="margin-left: 2rem;" border="0" cellpadding="0" cellspacing="0"
width="664">
<table style="margin-left: 2rem;" width="664" cellspacing="0" cellpadding="0"
border="0">
<colgroup><col width="132"> <col width="532"> </colgroup>
<tbody>
<tr valign="top">
@ -557,8 +575,8 @@ Device {
</table>
<p style="margin-top: 3ex;">Vchanger also implements the following
undocumented command defined in mtx-changer since Bacula version 5.1.0:</p>
<table style="margin-left: 2rem;" border="0" cellpadding="0" cellspacing="0"
width="664">
<table style="margin-left: 2rem;" width="664" cellspacing="0" cellpadding="0"
border="0">
<colgroup><col width="132"> <col width="532"> </colgroup>
<tbody>
<tr valign="top">
@ -575,7 +593,7 @@ Device {
extended commands that are not part of the documented Bacula Autochanger
Interface:</p>
<table style="page-break-before: auto; page-break-after: auto; margin-left: 2rem;"
border="0" cellpadding="0" cellspacing="0" width="664">
width="664" cellspacing="0" cellpadding="0" border="0">
<colgroup><col width="132"> <col width="532"> </colgroup>
<tbody>
<tr valign="top">
@ -607,8 +625,8 @@ Device {
</table>
<p style="margin-top: 3ex;"><span style="font-style: normal">The Bacula
Storage Daemon calls vchanger with 5 positional parameters defined as:</span></p>
<table style="margin-left: 2rem;" border="0" cellpadding="0" cellspacing="0"
width="664">
<table style="margin-left: 2rem;" width="664" cellspacing="0" cellpadding="0"
border="0">
<colgroup><col width="132"> <col width="532"> </colgroup>
<tbody>
<tr valign="top">
@ -675,16 +693,15 @@ Device {
drive and magazine state information and where the symlinks to volume
files will be created for loaded virtual drives.</p>
<h2><a name="virtual_magazines"></a>4.1. Virtual Magazines</h2>
<p>Virtual autochangers using vchanger treat the directories and filesystems
assigned to them as "virtual magazines", each able to contain a variable
number of volume files. This is analogous to the mechanical tape
magazines, (tape caddies), that many tape autochangers use. These tape
autochangers have one or more bays into which removable tape magazines can
be inserted. Each tape magazine contains a fixed number of slots into
which individual tape cartridges may be inserted. The principle difference
is that tape magazines have a fixed number of physical slots, whereas
vchanger virtual magazines have a variable and unlimited number of virtual
slots.</p>
<p>Vchanger autochangers treat the directories and filesystems assigned to
them as "virtual magazines", each able to contain a variable number of
volume files. This is analogous to the mechanical tape magazines, (tape
caddies), that many tape autochangers use. These tape autochangers have
one or more bays into which removable tape magazines can be inserted. Each
tape magazine contains a fixed number of slots into which individual tape
cartridges may be inserted. The principle difference is that tape
magazines have a fixed number of physical slots, whereas vchanger virtual
magazines have a variable and unlimited number of virtual slots.</p>
<p>Virtual magazines are filesystems or directories that are assigned to an
autochanger by including <em style="font-weight: bold;">Magazine</em><span
style="font-weight: bold;"> </span>directive lines in the vchanger
@ -702,25 +719,34 @@ Device {
determined solely by the order in which the <span style="font-style: italic; font-weight: bold;">Magazine
</span>definitions appear in the vchanger configuration file. This
allows assigning integer numbers to the magazines to ease tracking of
multiple magazines. For example, consider an autochanger associated with a
<span style="font-style: italic; font-weight: bold;">Storage </span>resource
multiple magazines. For example, consider an autochanger associated with
an <span style="font-style: italic; font-weight: bold;">Autochanger </span>resource
in bacula-dir.conf named "changer1" for which we are using 10 RDX
cartridges as its virtual magazines. The default volume naming scheme used
by the CREATEVOLS command will name the first volume file created on the
first <span style="font-style: italic; font-weight: bold;">Magazine </span>as
"changer1_0_0", the 23rd volume created on the second <span style="font-style: italic; font-weight: bold;">Magazine
</span>as "changer1_1_22", and so on. When Bacula later requests a
"changer1_0000_0000", the 23rd volume created on the second <span style="font-style: italic; font-weight: bold;">Magazine
</span>as "changer1_0001_0022", and so on. When Bacula later requests a
volume that is on a RDX cartridge that is not currently mounted, this
volume file naming scheme will make it very easy to determine which RDX
cartridge needs to be attached in order to make the requested volume
available.</p>
<p>The default volume naming scheme used by the CREATEVOLS command is not
required. The CREATEVOLS command accepts a pattern string that may be used
to name the volume files it creates. In fact, the CREATEVOLS command is
simply a convenience. A volume file may be created by creating an empty
file on the magazine using touch, etc., and then issuing a <b><i>label
barcodes</i></b> command in bconsole. Volume files may have any
arbitrary name. Existing volume files may be moved from one magazine to
another, requiring only an <b><i>update slots</i></b> command in bconsole
to inform Bacula of the change in slot assignments.</p>
<p>On most systems, filesystems to be used as magazines may be specified by
filesystem UUID, rather than mountpoint directory. This is useful on
systems where udev, Gnome Nautilus, other dbus-enabled automounters, and
etc. are used to automatically mount removable drives whenever they are
attached. Since vchanger is not normally running as root, it makes no
attempt to mount those filesystems. It only queries the system to
determine the current mount point of an already mounted filesystem.</p>
determine the current mount points of its assigned magazine filesystems.</p>
<p>Note that there is nothing special about the directories and filesystems
being used as magazines. A directory containing volume files created by
Bacula's native disk storage handling can be used as a vchanger magazine.
@ -729,17 +755,17 @@ Device {
individual volume files can be moved between the two. The only stipulation
is that the volume must have the same <span style="font-style: italic; font-weight: bold;">Media
Type</span> as the bacula-dir.conf <span style="font-style: italic; font-weight: bold;">Storage</span>
resource that it will be used with. If existing volumes are being moved to
a <span style="font-style: italic; font-weight: bold;">Storage</span>
resource that<span style="font-style: italic; font-weight: bold;"> </span>has
a different <span style="font-style: italic; font-weight: bold;">Media
or <b>Autochanger</b> resource that it will be used with. If existing
volumes are being moved to a <span style="font-style: italic; font-weight: bold;">Storage</span>
or <b>Autochanger </b>resource that<span style="font-style: italic; font-weight: bold;">
</span>has a different <span style="font-style: italic; font-weight: bold;">Media
Type</span>, then it will be necessary to manually update the <span style="font-style: italic; font-weight: bold;">Media
Type</span> for those volumes.</p>
<h2><a name="volume_files"></a>4.2. Volume Files</h2>
<p>A volume file is simply a regular file on one of the defined magazine
directories or file systems. A file cannot be used by Bacula until a
Bacula volume label has been written to it. Bacula supports bulk labeling
of volumes using barcodes. Many tape autochangers have barcode reader
directories or filesystems. A file cannot be used by Bacula until a Bacula
volume label has been written to it. Bacula supports bulk labeling of
volumes using barcodes. Many tape autochangers have barcode reader
hardware that can read the name of a tape cartridge from a barcode printed
on an adhesive paper label attached to the tape cartridge. The tape
autochanger can be commanded to read the barcodes and list the names of
@ -747,25 +773,26 @@ Device {
barcodes</span> command acquires this information from the tape
autochanger and writes a Bacula volume label to each unlabeled tape,
naming it according to the tape's barcode. Vchanger emulates a barcode
reader by using the volume file's filename as the barcode for that volume.</p>
reader by using the volume file's filename as the barcode for that volume.
</p>
<p>While it is recommended to use the default volume naming scheme
implemented by the CREATEVOLS command, it is not required and volume files
can have any filename. Just keep in mind that vchanger uses the volume
file's filename as its barcode label. This means that the filename must
match the volume label that was written to the file when the volume was
labeled. Volumes cannot be renamed by simply renaming the file to a
different filename. They would also have to have a new Bacula volume label
written to them.</p>
different filename. if renamed, they should also have a new Bacula volume
label written to them.</p>
<h2><a name="virtual_slots"></a>4.3. Virtual Slots</h2>
<p>Each time vchanger is invoked it maps the volume files on all attached
magazines to a virtual slot number. State information kept in the
<p>Each time vchanger is invoked it maps the volume files on all currently
mounted magazines to a virtual slot number. State information kept in the
autochanger's work directory is used to keep volume-to-slot mapping as
consistent as possible when removable disk drives are attached and
detached from the system. Drive state files contain information about the
volume last loaded into a virtual drive and are named 'drive_state-N',
where N is the drive number. Magazine state files contain information
about the magazines that were attached when vchanger was last invoked and
are named 'bay_state-N', where N is the magazine index.</p>
consistent as possible as removable disk drives are attached and detached
from the system. Drive state files contain information about the volume
last loaded into a virtual drive and are named 'drive_state-N', where N is
the drive number. Magazine state files contain information about the
magazines that were attached when vchanger was last invoked and are named
'bay_state-N', where N is the magazine index.</p>
<p>Whenever anything happens to change the volume-to-slot mapping, Bacula
must be informed of the change. This is because Bacula tracks the contents
of autochanger slots in its catalog, as it must know which volumes are
@ -848,8 +875,8 @@ Device {
number of the range of virtual slots mapped to the magazine's volume
files, and <span style="font-style: italic;">mountpoint </span>is the
magazine's directory or current filesystem mount point.</p>
<p>The CREATEVOLS command is used to add new volume files to a magazine and
cause Bacula to write volume labels to them.</p>
<p>The CREATEVOLS command is used to add new volume files to a magazine and,
optionally, cause Bacula to write volume labels to them.</p>
<p>The REFRESH command causes vchanger to refresh the current state of
magazines and virtual drives and issue a <span style="font-weight: bold; font-style: italic;">update
slots</span> command via bconsole if needed. This command is intended to
@ -894,8 +921,8 @@ Device {
<h2><a name="win32_build"></a>5.3.1. Installing the Windows Version from
Source</h2>
<p>The Windows version was developed under <a href="http://www.centos.org/">Centos</a>
7.0 using the <a href="http://mingw-w64.sourceforge.net/">MinGW-w64</a>
(gcc-4.9.1) cross-compiler system. On a Linux system with MinGW-w64
7.8 using the <a href="http://mingw-w64.sourceforge.net/">MinGW-w64</a>
(gcc-4.9.3) cross-compiler system. On a Linux system with MinGW-w64
development tools installed, the Windows version can be built as follows:</p>
<p>Unpack the gzip compressed tar archive in your home directory.</p>
<pre>[]$ cd ~
@ -904,11 +931,12 @@ Device {
vchanger</p>
<pre>[]$ cd vchanger
[]$ ./configure --host=x86_64-w64-mingw32 --build=`./config.guess` --prefix=./win32
[]$ make
[]$ make install-strip</pre>
[]$ make</pre>
<p style="margin-top: 0.08in">The win32 executable, vchanger.exe, will be in
~/vchanger/win32/bin and the Windows version documentation in
~/vchanger/win32/share/doc/vchanger.</p>
<h2><a name="win32_installer_build"></a>5.3.2. Building the Windows
installer</h2>
<p>Building the Windows installer for vchanger requires the makensis
ustility from the NSIS project in addition to MinGW-w64. A shell script
named win32build in the source distribution will configure and build both
@ -929,11 +957,12 @@ Device {
<p>The Bacula Storage Daemon does not usually run as root. Since vchanger
will be invoked by the Storage Daemon, and so will run as the same user
that it does, permissions for magazine filesystems must be set to allow
read/write access to that user. This can be done by mounting the new
partition somewhere, then setting the appropriate permissions to give
read/write access to the user that the Storage Daemon runs as. It may also
be necessary to configure SELinux or AppArmour to allow the user that the
Storage Daemon runs as to read/write the magazine filesystems.</p>
read/write access to the user:group that the bacula-sd Storage Daemon runs
as. This can be done by mounting the new partition somewhere, then setting
the appropriate permissions to give read/write access to the user that the
Storage Daemon runs as. It may also be necessary to configure SELinux or
AppArmour to allow the user that the Storage Daemon runs as to have
read/write the magazine filesystems.</p>
<h2><a name="determining_uuid"></a>6.2. Determining the Magazine's
Filesystem UUID</h2>
<p>On most POSIX systems, the blkid utility can be used to view the UUID of
@ -956,9 +985,9 @@ Device {
but this may not suffice for use with daemons, such as the Bacula Storage
Daemon that will normally invoke vchanger. The block storage device
containing the magazine's filesystem partition must, by some means, be
mounted before it can be used by vchanger. Since vchanger will be invoked
by the Bacula Storage Daemon, and will not ordinarily run with superuser
privileges, it does not itself attempt to mount filesystems. </p>
mounted before it can be used by vchanger or Bacula. Since vchanger will
be invoked by the Bacula Storage Daemon, and will not ordinarily run with
superuser privileges, it does not itself attempt to mount filesystems. </p>
<h2><a name="udev"></a>7.1. udev and Hot-swappable Drives</h2>
<p><a href="https://www.kernel.org/pub/linux/utils/kernel/hotplug/udev/udev.html">Udev</a>
is the subsystem that dynamically assigns device nodes to hot swappable
@ -1002,16 +1031,27 @@ UUID=a4902c05-e47d-40e0-9db7-b3d03d08c266 /mnt/vchanger/mag1 ext4 defaults,noaut
<pre style="margin-left: 2.5em;">[]# mount /mnt/vchanger/mag0<br>[]# vchanger /etc/vchanger/vchanger.conf refresh</pre>
<p>After use, the removable drive can then be removed using:</p>
<pre style="margin-left: 2.5em;">[]# umount /mnt/vchanger/mag0<br>[]# vchanger /etc/vchanger/vchanger.conf refresh</pre>
<p>It is also possible, when manually mounting magazine partitions, to use
the mountpoint path in the <span style="font-style: italic; font-weight: bold;">Magazine
</span>directive, rather than the UUID. When a magazine is specified by
<p>It is also possible, when manually mounting magazine partitions, to use a
mountpoint path in the <span style="font-style: italic; font-weight: bold;">Magazine
</span>directive, rather than a UUID. When a magazine is specified by
path (rather than by UUID), vchanger assumes the magazine is mounted if
that path exists. However, if that directory path exists while the
magazine's partition is not mounted, then that directory will be part of
the underlying filesystem rather than the unmounted magazine partition's
filesystem. For this reason, specifying directory paths in Magazine
directives is only recommended for permanently mounted filesystems.
Filesystems that are not permanently mounted should be specified by UUID.
directives is only recommended for permanently mounted filesystems. For
filesystems that are not permanently mounted, specifying magazines by UUID
is more flexible, allowing for multiple simultaneously mounted disks and
udev mount/unmount scripts that automate the bconsole calls needed to keep
Bacula in sync. </p>
<p>When specifying a magazine by directory path, rather than filesystem
UUID, vchanger will use any files in that directory as volume files. For
example, one could specify a single magazine= line in the vchanger
configuration file, then mount removable disks there one at a time. At
each invocation, vchanger will assign whatever files exist in that
directory to its virtual slots. However, this would in turn require
manually running 'update slots' in bconsole and would prevent mounting
more than one disk at a time, so will require more operator intervention.
</p>
<h2><a name="udev_mounting"></a>7.3. Using udev To Mount Removable Drive
Partitions</h2>
@ -1053,6 +1093,10 @@ UUID=a4902c05-e47d-40e0-9db7-b3d03d08c266 /mnt/vchanger/mag1 ext4 defaults,noaut
<p>After adding or changing Magazine directives in a vchanger configuration
file, configure automounting through udev by:</p>
<pre>[]# vchanger-genudevrules &gt;/etc/udev/rules.d/96-vchanger.rules<br>[]# udevadm control --reload-rules</pre>
<p>Note that it may be necessary to detach and then reattach currently
attached magazine drives following a change in udev rules in order to
ensure that the new rules are applied. </p>
<p></p>
<h2><a name="autofs"></a>7.4. Using autofs To Mount Removable Drive
Partitions</h2>
<p>The <a href="http://www.autofs.org/">autofs</a> daemon provides a way to
@ -1138,21 +1182,23 @@ style="font-style: normal">9667f83c-6150-44c7-b0d4-57564f174b35</span></pre>
at a directory in an NTFS directory tree. It should be noted that only the
mount point directory must be on an NTFS volume. The partition being
mounted may have a FAT32 file system or any other file system for which
there is a file system driver installed. See “<a href="http://technet.microsoft.com/en-us/library/cc753321.aspx">Assign
a mount point folder path to a drive</a>” for instructions on assigning
mountpoints for removable drive partitions. Like with drive letters, it is
not possible to assign the same mountpoint to more than one drive.</p>
there is a file system driver installed. See &ldquo;<a href="http://technet.microsoft.com/en-us/library/cc753321.aspx">Assign
a mount point folder path to a drive</a>&rdquo; for instructions on
assigning mountpoints for removable drive partitions. Like with drive
letters, it is not possible to assign the same mountpoint to more than one
drive.</p>
<p>On Windows, magazine partitions should always be specified by UUID
(Volume Serial Number). This ensures that vchanger will be able to find
the partition's mountpoint without prior knowledge of which drive letter
or mountpoint Windows has currently assigned to the drive.</p>
<h1><a name="vchanger_config"></a>8. Configuring vchanger</h1>
<p>Each vchanger autochanger is defined by a configuration file. Multiple
autochangers may be defined as long as each is given a unique name, its
own unique work directory, and its own unique configuration file. By
default, vchanger configuration files are placed in the /etc/vchanger
directory. The configuration files <b>must be given permissions</b> that
allow the user that the Bacula Storage Daemon runs as to have read access.</p>
autochangers may be defined as long as each is given a unique <i><b>Storage
Resource</b></i> name, its own unique work directory, and its own
unique configuration file. By default, vchanger configuration files are
placed in the /etc/vchanger directory. The configuration files <b>must be
given permissions</b> that allow the user that the Bacula Storage Daemon
runs as to have read access.</p>
<p style="font-style: normal">A vchanger configuration file consists of
keyword = value pairs. Comments are defined by a '#' character, and cause
text from the '#' until the next newline character to be ignored. Values
@ -1161,7 +1207,8 @@ style="font-style: normal">9667f83c-6150-44c7-b0d4-57564f174b35</span></pre>
appearing inside of the quoted value must be escaped by prepending with a
'\' character. The following keywords are defined for an autochanger
configuration file:</p>
<table style="margin-left: 2rem; width: 50em;" border="0" cellpadding="0" cellspacing="0">
<table style="margin-left: 2rem; width: 50em;" cellspacing="0" cellpadding="0"
border="0">
<colgroup><col width="172"> <col width="492"> </colgroup>
<tbody>
<tr valign="top">
@ -1171,7 +1218,9 @@ style="font-style: normal">9667f83c-6150-44c7-b0d4-57564f174b35</span></pre>
<td width="492">
<p>[Required] The name of the Storage resource, defined in the
Bacula Director configuration file, that is associated with this
autochanger. <br>
autochanger. This also names the vchanger autochanger and is used
in constructing the default volume naming prefix used by the
CREATEVOLS command.<br>
Default: none</p>
</td>
</tr>
@ -1238,7 +1287,9 @@ style="font-style: normal">9667f83c-6150-44c7-b0d4-57564f174b35</span></pre>
<td width="492">
<p>The path to the bconsole binary that vchanger will invoke to
issue 'update slots' and 'label barcodes' commands to Bacula. To
disable interaction with Bacula, set to the empty string.<br>
disable interaction with Bacula via bconsole, set to the empty
string. When disabled, 'update slots' and 'label barcodes'
commands will need to be issued manually from within bconsole.<br>
Default: /usr/sbin/bconsole</p>
</td>
</tr>
@ -1248,7 +1299,9 @@ style="font-style: normal">9667f83c-6150-44c7-b0d4-57564f174b35</span></pre>
</td>
<td width="492">
<p>The path to the configuration file to use when invoking bconsole<span
style="font-style: normal"></span>.<br>
style="font-style: normal"></span>. The user that vchanger is
invoked by, usually the user that the Bacula Storage Daemon runs
as, must have permissions to read the bconsole.conf file.<br>
Default: /etc/bacula/bconsole.conf</p>
</td>
</tr>
@ -1268,11 +1321,11 @@ style="font-style: normal">9667f83c-6150-44c7-b0d4-57564f174b35</span></pre>
</td>
<td width="492">
<p>[Required] Defines either the path to a directory or the UUID of
a filesystem partition (prepended by the string “UUID:”) that is
to be used as a magazine containing volume files . The magazine
directive assigns a directory or partition to this autochanger.
This directive may appear multiple times to assign multiple
magazines.<br>
a filesystem partition (prepended by the string
&ldquo;UUID:&rdquo;) that is to be used as a magazine containing
volume files. The Magazine directive assigns a directory or
partition to this autochanger. This directive may appear multiple
times to assign multiple magazines.<br>
Default: none</p>
</td>
</tr>
@ -1286,8 +1339,8 @@ style="font-style: normal">9667f83c-6150-44c7-b0d4-57564f174b35</span></pre>
directive to the vchanger configuration file. For removable storage, the <span
style="font-weight: bold; font-style: italic;">Magazine </span>directive
should&nbsp;specify the filesystem UUID (or the mountpoint path if using
autofs). If a directory on permanently mounted fixed-disk storage is being
used as a magazine, then the <span style="font-weight: bold; font-style: italic;">Magazine
autofs or manual mounting). If a directory on permanently mounted
fixed-disk storage is being used as a magazine, then the <span style="font-weight: bold; font-style: italic;">Magazine
</span>directive should specify the directory path. In either case, the
filesystem or directory must by writable by the user that Bacula Storage
Daemon runs as. The LISTMAGS vchanger command can be used to determine
@ -1351,17 +1404,16 @@ Magazine = UUID:5039284a<span style="font-style: normal">-4312-57d1-92c4-3547100
][# chown bacula:tape /mnt/vchanger/5039284a<span style="font-style: normal">-4312-57d1-92c4-354710032c79</span>
][# chmod 750 /mnt/vchanger/5039284a<span style="font-style: normal">-4312-57d1-92c4-354710032c79</span>
</pre>
<p>The Storage resource that this autochanger is associated with is defined
in bacula-dir.conf as:</p>
<p>The Autochanger resource that this autochanger is associated with is
defined in bacula-dir.conf as:</p>
<pre># /etc/bacula/bacula-dir.conf<br>...<br># vchanger autochanger vchanger-1
Storage {
Autochanger {
Name = vchanger-1
Address = 192.168.1.9
SDPort = 9103
Password = "password"
Device = usb-vchanger-1
Media Type = File
Autochanger = yes;
Maximum Concurrent Jobs = 20
}
<span style="font-style: normal">...</span>
@ -1419,8 +1471,8 @@ Device {
volumes<span style="font-style: italic;"></span>. We can now add some
volumes with:</p>
<pre>[]# vchanger /etc/vchanger/vchanger-1.conf createvols 0 2
creating label 'vchanger-1_0_0'
creating label 'vchanger-1_0_1'
creating label 'vchanger-1_0000_0000'
creating label 'vchanger-1_0000_0001'
Created 2 volume files on magazine 0
</pre>
<p>Now the output from a LISTMAGS<span style="font-weight: bold; font-style: italic;">
@ -1432,7 +1484,7 @@ Created 2 volume files on magazine 0
mapped to virtual slots 1-2. We can issue the LIST command to see what
volumes the vchanger-1 autochanger has in its virtual slots.</p>
<pre>[]# vchanger /etc/vchanger/vchanger-1.conf list
1:vchanger-1_0_0<br>2:vchanger-1_0_1</pre>
1:vchanger-1_0000_0000<br>2:vchanger-1_0000_0001</pre>
<p>This shows that two volume files are in slots 1 and 2. We can use
bconsole to see what Bacula thinks the autochanger status is.</p>
<pre>[]# echo "status slots storage=vchanger-1 drive=0" | bconsole
@ -1448,9 +1500,9 @@ Device "vchanger-1" has 2 slots.
Connecting to Storage daemon vchanger-1 at 192.168.1.9:9103 ...
3306 Issuing autochanger "list" command.
Slot | Volume Name | Status | Media Type | Pool |
------+------------------+-----------+----------------------+--------------------|
1 | vchanger-1_0_0 | Append | File | Scratch |
2 | vchanger-1_0_1 | Append | File | Scratch | </pre>
------+------------------------+-----------+----------------------+--------------------|
1 | vchanger-1_0000_0000 | Append | File | Scratch |
2 | vchanger-1_0000_0001 | Append | File | Scratch | </pre>
<p>This shows that the CREATEVOLS<span style="font-style: italic; font-weight: bold;">
</span>command has created two volume files on the first magazine and has
successfully labeled them using the <span style="font-style: italic;">l<span
@ -1458,13 +1510,13 @@ Connecting to Storage daemon vchanger-1 at 192.168.1.9:9103 ...
bconsole.</p>
<p>Now attach the second USB drive and add some volumes to it using:</p>
<pre>[]# vchanger /etc/vchanger/vchanger-1.conf createvols 1 3
creating label 'vchanger-1_1_0'
creating label 'vchanger-1_1_1'<br>creating label 'vchanger-1_1_2'<br>Created 3 volume files on magazine 1
creating label 'vchanger-1_0001_0000'
creating label 'vchanger-1_0001_0001'<br>creating label 'vchanger-1_0001_0002'<br>Created 3 volume files on magazine 1
</pre>
<p>and confirm that the volumes were added as expected with:</p>
<pre>[]# vchanger /etc/vchanger/vchanger-1.conf listmags<br>0:2:1:/mnt/vchanger/9667f83c-6150-44c7-b0d4-57564f174b35
1:3:3:/mnt/vchanger/5039284a<span style="font-style: normal">-4312-57d1-92c4-354710032c79</span><br><br>[]# vchanger /etc/vchanger/vchanger-1.conf list
1:vchanger-1_0_0<br>2:vchanger-1_0_1<br>3:vchanger-1_1_0<br>4:vchanger-1_1_1<br>5:vchanger-1_1_2<br><br>[]# echo "status slots storage=vchanger-1 drive=0" | bconsole
1:vchanger-1_0000_0000<br>2:vchanger-1_0000_0001<br>3:vchanger-1_0001_0000<br>4:vchanger-1_0001_0001<br>5:vchanger-1_0001_0002<br><br>[]# echo "status slots storage=vchanger-1 drive=0" | bconsole
Connecting to Director 127.0.0.1:9101
1000 OK: bacula-dir Version: 5.2.13 (19 February 2013)
Enter a period to cancel a command.
@ -1477,11 +1529,11 @@ Device "vchanger-1" has 2 slots.
Connecting to Storage daemon vchanger-1 at 192.168.1.9:9103 ...
3306 Issuing autochanger "list" command.
Slot | Volume Name | Status | Media Type | Pool |
------+------------------+-----------+----------------------+--------------------|
1 | vchanger-1_0_0 | Append | File | Scratch |<br> 2 | vchanger-1_0_1 | Append | File | Scratch |
3 | vchanger-1_1_0 | Append | File | Scratch |
4 | vchanger-1_1_1 | Append | File | Scratch |
5 | vchanger-1_1_2 | Append | File | Scratch |
------+------------------------+-----------+----------------------+--------------------|
1 | vchanger-1_0000_0000 | Append | File | Scratch |<br> 2 | vchanger-1_0000_0001 | Append | File | Scratch |
3 | vchanger-1_0001_0000 | Append | File | Scratch |
4 | vchanger-1_0001_0001 | Append | File | Scratch |
5 | vchanger-1_0001_0002 | Append | File | Scratch |
</pre>
<p>If the first USB drive is now unplugged, the script invoked by udev
should unmount the first magazine partition and cause an <span style="font-style: italic; font-weight: bold;">update
@ -1493,9 +1545,9 @@ Connecting to Storage daemon vchanger-1 at 192.168.1.9:9103 ...
[]# vchanger /etc/vchanger/vchanger-1.conf list
1:
2:
3:vchanger-1_1_0
4:vchanger-1_1_1
5:vchanger-1_1_2
3:vchanger-1_0001_0000
4:vchanger-1_0001_0001
5:vchanger-1_0001_0002
[]# echo "status slots storage=vchanger-1 drive=0" | bconsole
Connecting to Director 127.0.0.1:9101
@ -1510,25 +1562,25 @@ Device "vchanger-1" has 2 slots.
Connecting to Storage daemon vchanger-1 at 192.168.1.9:9103 ...
3306 Issuing autochanger "list" command.
Slot | Volume Name | Status | Media Type | Pool |
------+------------------+-----------+----------------------+--------------------|<br> 1*| | ? | ? | ? |<br> 2*| | ? | ? | ? |<br>&nbsp; 3 | vchanger-1_1_0 | Append | File | Scratch |
4 | vchanger-1_1_1 | Append | File | Scratch |
5 | vchanger-1_1_2 | Append | File | Scratch |
------+------------------------+-----------+----------------------+--------------------|<br> 1*| | ? | ? | ? |<br> 2*| | ? | ? | ? |<br>&nbsp; 3 | vchanger-1_0001_0000 | Append | File | Scratch |
4 | vchanger-1_0001_0001 | Append | File | Scratch |
5 | vchanger-1_0001_0002 | Append | File | Scratch |
</pre>
<p>Let's now examine what happens when a volume file is "loaded" into a
virtual drive.</p>
<pre>[]# vchanger /etc/vchanger/vchanger-1.conf load 3 /dev/null 0
[]# ls -l /var/spool/vchanger/vchanger-1
lrwxrwxrwx 1 bacula tape 29 Mar 1 16:46 0 -&gt; /mnt/vchanger/5039284a<span style="font-style: normal">-4312-57d1-92c4-354710032c79</span>/vchanger-1_1_0
lrwxrwxrwx 1 bacula tape 29 Mar 1 16:46 0 -&gt; /mnt/vchanger/5039284a<span style="font-style: normal">-4312-57d1-92c4-354710032c79</span>/vchanger-1_0001_0000
-rw-r----- 1 bacula tape 21 Mar 1 16:46 bay_state-1
-rw-r----- 1 bacula tape 30 Mar 1 16:46 drive_state-0
</pre>
<p>The LOAD command created a symlink named '0' in the autochanger's work
directory that points to the file 'vchanger-1_1_0' in the filesystem
directory that points to the file 'vchanger-1_0001_0000' in the filesystem
mounted at /mnt/vchanger/5039284a-4312-57d1-92c4-354710032c79, the volume
file that is mapped to virtual slot 3. The symlink named '0' is this
autochanger's drive 0. Notice that the path to this symlink was defined in
bacula-sd.conf as the <span style="font-weight: bold; font-style: italic;">Archive
Device</span> for this autochanger's drive 0. When Bacula opens th the
Device</span> for this autochanger's drive 0. When Bacula opens the
symlink for reading or writing, it will actually be opening the volume
file that the symlink points to.&nbsp;</p>
<p>The LOADED command can be used to check the status of a drive.</p>
@ -1616,20 +1668,19 @@ Device {
<pre># /etc/bacula/bacula-dir.conf
# ...
# Local USB drive virtual autochanger
Storage {
Autochanger {
Name = vchanger-1 # same as 'Storage Resource' in the vchanger config file
Address = sd-server-address
Password = "secret_password"
Device = usb-changer # name of the Autochanger resource in bacula-sd.conf
Media Type = File
Autochanger = yes;
}
# eof</pre>
<h1><a name="appendixa"></a>Appendix A. vchanger Commands</h1>
<h2><a name="command_list"></a>A.1. LIST Command</h2>
<p style="margin-top: 0in; margin-bottom: 0in; font-style: normal">Bacula
issues this command to an autochanger to list to stdout the barcode
labels of volumes in the autochanger's slots. Many tape autochanger
issues this command to an autochanger to list to stdout the &ldquo;barcode
labels&rdquo; of volumes in the autochanger's slots. Many tape autochanger
robots have barcode readers such that tapes can be affixed with an
adhesive barcode label that identifies the tape. This allows Bacula to
automate the process of creating volume labels by utilizing the
@ -1640,19 +1691,19 @@ Storage {
volume file).</p>
<h2><a name="command_listall"></a>A.2. LISTALL Command</h2>
<p>It is not clear that Bacula currently uses this command internally, and
it is not specified in the <a href="http://www.bacula.org/3.0.x-manuals/en/concepts/concepts/Autochanger_Resource.html#SECTION0018130000000000000000">Bacula
it is not specified in the <a href="http://www.bacula.org/9.6.x-manuals/en/concepts/concepts/Autochanger_Resource.html#SECTION0018130000000000000000">Bacula
Autochanger Interface</a> documentation. Nevertheless, it is implemented
in Bacula's mtx-changer script since version 5.1.0 and is used by some
web-based admin utilities, so has been implemented in vchanger.&nbsp;This
command is similar to the LIST command except that it also lists current
drive status in addition to slot status.</p>
<h2><a name="command_load"></a>A.3. LOAD Command</h2>
<p style="font-style: normal">The load command is used to “load” a volume
file from a virtual slot into a virtual drive. A tape autochanger does
this by physically moving the tape located in the requested library slot
into a tape drive. Bacula reads and writes volume data from/to the tape
drive's device file. With vchanger, a filesystem symlink at a known path
is used as a virtual drive in place of a tape drive's device file. A
<p style="font-style: normal">The load command is used to &ldquo;load&rdquo;
a volume file from a virtual slot into a virtual drive. A tape autochanger
does this by physically moving the tape located in the requested library
slot into a tape drive. Bacula reads and writes volume data from/to the
tape drive's device file. With vchanger, a filesystem symlink at a known
path is used as a virtual drive in place of a tape drive's device file. A
volume file is then loaded by making the target of that symlink point to
the volume file mapped to the requested slot. </p>
<p style="font-style: normal">Parameter 3 gives the autochanger slot number
@ -1663,8 +1714,8 @@ Storage {
<p style="font-weight: normal">This command is issued to determine which
slot, if any, is loaded into a drive. If a drive is loaded, then the
virtual slot number corresponding to the loaded volume file is written to
stdout. If the drive is not loaded, the string “0” is written to stdout to
inform Bacula that the drive is not loaded.</p>
stdout. If the drive is not loaded, the string &ldquo;0&rdquo; is written
to stdout to inform Bacula that the drive is not loaded.</p>
<h2><a name="command_slots"></a>A.5. SLOTS Command</h2>
<p style="margin-top: 0in; margin-bottom: 0in; font-style: normal">This
command simply prints to stdout the maximum number of volume files that
@ -1681,21 +1732,21 @@ Storage {
this by deleting the virtual drive's symlink.</p>
<h2><a name="command_createvols"></a>A.7. CREATEVOLS Command</h2>
<p><span style="font-weight: normal">This is an extended command that is not
part of the <a href="http://www.bacula.org/3.0.x-manuals/en/concepts/concepts/Autochanger_Resource.html#SECTION0018130000000000000000">Bacula
part of the <a href="http://www.bacula.org/9.6.x-manuals/en/concepts/concepts/Autochanger_Resource.html#SECTION0018130000000000000000">Bacula
Autochanger
Interface</a></span> <span style="font-weight: normal">API, and is
used to add volume files to a magazine filesystem and cause them to be
labeled with a Bacula volume label. The format of this command is</span></p>
<pre style="margin-top: 0.01in; margin-bottom: 0.2in">vchanger config_file createcols mag_ndx count [start_num] [--label=prefix] [--pool=pool_name]</pre>
<p style="margin-top: 0.08in">where:</p>
<table style="margin-left: 3rem;" border="0" cellpadding="0" cellspacing="0"
width="664">
<table style="margin-left: 3rem;" width="664" cellspacing="0" cellpadding="0"
border="0">
<colgroup><col width="131"> <col width="533"> </colgroup>
<tbody>
</tbody>
</table>
<table style="margin-left: 1rem; width: 50em; margin-bottom: 1rem;" border="0"
cellpadding="0" cellspacing="0">
<table style="margin-left: 1rem; width: 50em; margin-bottom: 1rem;" cellspacing="0"
cellpadding="0" border="0">
<tbody>
<tr>
<td width="9em">
@ -1746,8 +1797,10 @@ Autochanger
</td>
<td>
<p>An optional prefix string used in naming the created volume
files. The default prefix string is the autochanger's name
concatenated with '_' plus the magazine index number.</p>
files.&nbsp; The filenames created will be the prefix string
concatenated with the integer uniqueness number. The default
prefix string is the autochanger's name concatenated with '_',
followed by the magazine index number, followed by another '_'.</p>
</td>
</tr>
<tr>
@ -1769,14 +1822,14 @@ Autochanger
this reason, vchanger will also issue the <span style="font-weight: bold; font-style: italic;">update
slots</span> command to Bacula whenever new volume files are added.</p>
<h2><a name="command_listmags"></a>A.8. LISTMAGS Command</h2>
<p>This is an extended command that is not part of the <a href="http://www.bacula.org/3.0.x-manuals/en/concepts/concepts/Autochanger_Resource.html#SECTION0018130000000000000000">Bacula
<p>This is an extended command that is not part of the <a href="http://www.bacula.org/9.6.x-manuals/en/concepts/concepts/Autochanger_Resource.html#SECTION0018130000000000000000">Bacula
Autochanger
Interface</a> API, and is used to list status information about the
autochanger's assigned magazines. The format of this command is</p>
<pre style="margin-top: 0.01in; margin-bottom: 0.2in">vchanger config_file listmags</pre>
<p style="margin-top: 0.08in">where:</p>
<table style="margin-left: 1em; margin-bottom: 1em;" border="0" cellpadding="0"
cellspacing="0" width="664">
<table style="margin-left: 1em; margin-bottom: 1em;" width="664" cellspacing="0"
cellpadding="0" border="0">
<colgroup><col width="131"> <col width="533"> </colgroup>
<tbody>
<tr valign="top">
@ -1793,8 +1846,8 @@ Autochanger
to stdout, one line per bay. The format of an output line is:</p>
<pre style="margin-top: 0.01in; margin-bottom: 0.2in">mag_ndx:count:start_slot:mountpoint</pre>
<p style="margin-top: 0.08in">where:</p>
<table style="margin-left: 1em; margin-bottom: 1em;" border="0" cellpadding="0"
cellspacing="0" width="664">
<table style="margin-left: 1em; margin-bottom: 1em;" width="664" cellspacing="0"
cellpadding="0" border="0">
<colgroup><col width="131"> <col width="533"> </colgroup>
<tbody>
<tr valign="top">

View File

@ -3,8 +3,8 @@
#
Summary: A virtual autochanger for Bacula
Name: vchanger
Version: 1.0.1
Release: 1.el6
Version: 1.0.3
Release: 1%{dist}
License: GPLv2
Group: System Environment/Daemons
Source: http://sourceforge.net/projects/vchanger/files/vchanger/%{version}/vchanger-%{version}.tar.gz
@ -35,6 +35,7 @@ make
rm -rf $RPM_BUILD_ROOT
mkdir -p ${RPM_BUILD_ROOT}%{_bindir}
mkdir -p ${RPM_BUILD_ROOT}%{_sysconfdir}/sysconfig
mkdir -p ${RPM_BUILD_ROOT}%{_sysconfdir}/logrotate.d
mkdir -p ${RPM_BUILD_ROOT}%{_sysconfdir}/%{name}
mkdir -p ${RPM_BUILD_ROOT}%{_libexecdir}/%{name}
mkdir -p ${RPM_BUILD_ROOT}%{_mandir}/man5
@ -44,6 +45,7 @@ mkdir -m 0770 -p ${RPM_BUILD_ROOT}%{_localstatedir}/spool/%{name}
mkdir -m 0755 -p ${RPM_BUILD_ROOT}%{_localstatedir}/log/%{name}
make DESTDIR=${RPM_BUILD_ROOT} install-strip
install -m 0644 scripts/vchanger.default ${RPM_BUILD_ROOT}%{_sysconfdir}/sysconfig/vchanger
install -m 0644 contrib/vchanger.logrotate ${RPM_BUILD_ROOT}%{_sysconfdir}/logrotate.d/vchanger
%clean
rm -rf $RPM_BUILD_ROOT
@ -52,6 +54,7 @@ rm -rf $RPM_BUILD_ROOT
%defattr(-,root,root,-)
%{_bindir}/*
%{_libexecdir}/%{name}/*
%{_sysconfdir}/logrotate.d/*
%doc %{_docdir}/%{name}-%{version}/AUTHORS
%doc %{_docdir}/%{name}-%{version}/ChangeLog
%doc %{_docdir}/%{name}-%{version}/COPYING
@ -85,6 +88,10 @@ if [ $1 -eq 1 ] ; then
fi
%changelog
* Thu May 6 2020 Josh Fisher <jfisher@jaybus.com>
- Updated to release 1.0.2
* Thu Jun 14 2018 Josh Fisher <jfisher@jaybus.com>
- Updated to release 1.0.2
* Wed Jun 3 2015 Josh Fisher <jfisher@jaybus.com>
- Updated to release 1.0.1
* Fri Apr 3 2015 Josh Fisher <jfisher@jaybus.com>

View File

@ -3,8 +3,8 @@
#
Summary: A virtual autochanger for Bacula
Name: vchanger
Version: 1.0.1
Release: 1.el7
Version: 1.0.3
Release: 1%{dist}
License: GPLv2
Group: System Environment/Daemons
Source: http://sourceforge.net/projects/vchanger/files/vchanger/%{version}/vchanger-%{version}.tar.gz
@ -14,6 +14,9 @@ Packager: Josh Fisher <jfisher@jaybus.com>
BuildRoot: %{_tmppath}/%{name}-%{version}-%{release}-root-%(%{__id_u} -n)
Requires(post): /bin/mkdir, /bin/chown, /usr/bin/getent, /usr/sbin/useradd, /usr/sbin/groupadd
# For libudev support
BuildRequires: systemd-devel
%description
Vchanger implements a virtual autochanger for use with the Bacula open source
network backup system. Vchanger emulates a magazine-based tape autoloader
@ -35,6 +38,7 @@ make
rm -rf $RPM_BUILD_ROOT
mkdir -p ${RPM_BUILD_ROOT}%{_bindir}
mkdir -p ${RPM_BUILD_ROOT}%{_sysconfdir}/sysconfig
mkdir -p ${RPM_BUILD_ROOT}%{_sysconfdir}/logrotate.d
mkdir -p ${RPM_BUILD_ROOT}%{_sysconfdir}/%{name}
mkdir -p ${RPM_BUILD_ROOT}%{_libexecdir}/%{name}
mkdir -p ${RPM_BUILD_ROOT}%{_mandir}/man5
@ -44,6 +48,7 @@ mkdir -m 0770 -p ${RPM_BUILD_ROOT}%{_localstatedir}/spool/%{name}
mkdir -m 0755 -p ${RPM_BUILD_ROOT}%{_localstatedir}/log/%{name}
make DESTDIR=${RPM_BUILD_ROOT} install-strip
install -m 0644 scripts/vchanger.default ${RPM_BUILD_ROOT}%{_sysconfdir}/sysconfig/vchanger
install -m 0644 contrib/vchanger.logrotate ${RPM_BUILD_ROOT}%{_sysconfdir}/logrotate.d/vchanger
%clean
rm -rf $RPM_BUILD_ROOT
@ -52,6 +57,7 @@ rm -rf $RPM_BUILD_ROOT
%defattr(-,root,root,-)
%{_bindir}/*
%{_libexecdir}/%{name}/*
%{_sysconfdir}/logrotate.d/*
%doc %{_docdir}/%{name}-%{version}/AUTHORS
%doc %{_docdir}/%{name}-%{version}/ChangeLog
%doc %{_docdir}/%{name}-%{version}/COPYING
@ -85,6 +91,10 @@ if [ $1 -eq 1 ] ; then
fi
%changelog
* Thu May 6 2020 Josh Fisher <jfisher@jaybus.com>
- Updated to release 1.0.2
* Thu Jun 14 2018 Josh Fisher <jfisher@jaybus.com>
- Updated to release 1.0.2
* Wed Jun 3 2015 Josh Fisher <jfisher@jaybus.com>
- Updated to release 1.0.1
* Fri Apr 3 2015 Josh Fisher <jfisher@jaybus.com>

106
rpm/vchanger.f29.spec Normal file
View File

@ -0,0 +1,106 @@
#
# Spec file for vchanger - Fedora 29
#
%global debug_package %{nil}
Summary: A virtual autochanger for Bacula
Name: vchanger
Version: 1.0.3
Release: 1%{dist}
License: GPLv2
Group: System Environment/Daemons
Source: http://sourceforge.net/projects/vchanger/files/vchanger/%{version}/vchanger-%{version}.tar.gz
URL: http://vchanger.sourceforge.net
Vendor: Josh Fisher.
Packager: Josh Fisher <jfisher@jaybus.com>
BuildRoot: %{_tmppath}/%{name}-%{version}-%{release}-root-%(%{__id_u} -n)
Requires(post): /usr/bin/mkdir, /usr/bin/chown, /usr/bin/getent, /usr/sbin/useradd, /usr/sbin/groupadd
# For libudev support
BuildRequires: systemd-devel
%description
Vchanger implements a virtual autochanger for use with the Bacula open source
network backup system. Vchanger emulates a magazine-based tape autoloader
using disk file system mountpoints as virtual magazines and the files in
each virtual magazine as virtual tape volumes. Vchanger is primarily
designed to use an unlimited number of removable disk drives as an easily
scalable virtual autochanger and allow seamless removal of backups for
offsite storage.
%prep
%setup -q -n %{name}
%build
CFLAGS="%{optflags} -D_GNU_SOURCE -D_FILE_OFFSET_BITS=64 -D_LARGEFILE_SOURCE" \
%configure
make
%install
rm -rf $RPM_BUILD_ROOT
mkdir -p ${RPM_BUILD_ROOT}%{_bindir}
mkdir -p ${RPM_BUILD_ROOT}%{_sysconfdir}/sysconfig
mkdir -p ${RPM_BUILD_ROOT}%{_sysconfdir}/logrotate.d
mkdir -p ${RPM_BUILD_ROOT}%{_sysconfdir}/%{name}
mkdir -p ${RPM_BUILD_ROOT}%{_libexecdir}/%{name}
mkdir -p ${RPM_BUILD_ROOT}%{_mandir}/man5
mkdir -p ${RPM_BUILD_ROOT}%{_mandir}/man8
mkdir -p ${RPM_BUILD_ROOT}%{_docdir}/%{name}-%{version}
mkdir -m 0770 -p ${RPM_BUILD_ROOT}%{_localstatedir}/spool/%{name}
mkdir -m 0755 -p ${RPM_BUILD_ROOT}%{_localstatedir}/log/%{name}
make DESTDIR=${RPM_BUILD_ROOT} install-strip
install -m 0644 scripts/vchanger.default ${RPM_BUILD_ROOT}%{_sysconfdir}/sysconfig/vchanger
install -m 0644 contrib/vchanger.logrotate ${RPM_BUILD_ROOT}%{_sysconfdir}/logrotate.d/vchanger
%clean
rm -rf $RPM_BUILD_ROOT
%files
%defattr(-,root,root,-)
%{_bindir}/*
%{_libexecdir}/%{name}/*
%{_sysconfdir}/logrotate.d/*
%doc %{_docdir}/%{name}-%{version}/AUTHORS
%doc %{_docdir}/%{name}-%{version}/ChangeLog
%doc %{_docdir}/%{name}-%{version}/COPYING
%doc %{_docdir}/%{name}-%{version}/INSTALL
%doc %{_docdir}/%{name}-%{version}/NEWS
%doc %{_docdir}/%{name}-%{version}/README
%doc %{_docdir}/%{name}-%{version}/ReleaseNotes
%doc %{_docdir}/%{name}-%{version}/vchanger-example.conf
%doc %{_docdir}/%{name}-%{version}/example-vchanger-udev.rules
%doc %{_docdir}/%{name}-%{version}/vchangerHowto.html
%{_mandir}/man5/*
%{_mandir}/man8/*
%config(noreplace) %{_sysconfdir}/sysconfig/vchanger
%post
if [ $1 -eq 1 ] ; then
/usr/bin/getent group tape &>/dev/null || /usr/sbin/groupadd -r tape
/usr/bin/getent group bacula &>/dev/null || /usr/sbin/groupadd -r bacula
/usr/bin/getent passwd bacula &>/dev/null || /usr/sbin/useradd -r -g bacula -d %{_localstatedir}/spool/bacula -s /bin/bash bacula
if [ ! -d %{_localstatedir}/spool/vchanger ] ; then
/bin/mkdir -p -m 0770 %{_localstatedir}/spool/vchanger
/bin/chown bacula:tape %{_localstatedir}/spool/vchanger
fi
if [ ! -d %{_localstatedir}/log/vchanger ] ; then
/bin/mkdir -p -m 0755 %{_localstatedir}/log/vchanger
/bin/chown bacula:tape %{_localstatedir}/log/vchanger
fi
if [ ! -d %{_sysconfdir}/vchanger ] ; then
/bin/mkdir -p -m 0755 %{_sysconfdir}/vchanger
fi
fi
%changelog
* Thu May 6 2020 Josh Fisher <jfisher@jaybus.com>
- Updated to release 1.0.3
* Mon Dec 24 2018 Steven A. Falco <stevenfalco@gmail.com>
- Various changes for Fedora 29
* Thu Jun 14 2018 Josh Fisher <jfisher@jaybus.com>
- Updated to release 1.0.2
* Wed Jun 3 2015 Josh Fisher <jfisher@jaybus.com>
- Updated to release 1.0.1
* Fri Apr 3 2015 Josh Fisher <jfisher@jaybus.com>
- Initial spec file

View File

@ -1,6 +1,6 @@
#!/bin/bash
#
# vchanger-genudevrules ( vchanger v.1.0.1 ) 2015-06-03
# vchanger-genudevrules ( vchanger v.1.0.3 ) 2020-05-06
#
# Search all autochanger configuration files for magazines being defined
# by filesystem UUID and print to stdout the udev rules for launching

View File

@ -1,12 +1,14 @@
#!/bin/sh
#
# vchanger-launch-mount.sh ( vchanger v.1.0.1 ) 2015-06-03
# vchanger-launch-mount.sh ( vchanger v.1.0.3 ) 2020-05-06
#
# This script is used to run the vchanger-mount-uuid.sh script in
# another [background] process launched by the at command in order
# to prevent delays when invoked by a udev rule.
# This script is used to run the vchanger-mount-uuid.sh script as
# a detached process and immediately exit. This is to prevent delays
# when invoked by a udev rule.
#
VCHANGER_MOUNT=/usr/libexec/vchanger/vchanger-mount-uuid.sh
{
$VCHANGER_MOUNT $1
} &
# For some reason, nohup doesn't work, but "at now" does. This may have to
# do with cgroups.
#nohup $VCHANGER_MOUNT $1 </dev/null >/dev/null 2>&1 &
echo "$VCHANGER_MOUNT $1 </dev/null >/dev/null 2>&1" | at now

View File

@ -1,12 +1,14 @@
#!/bin/sh
#
# vchanger-launch-umount.sh ( vchanger v.1.0.1 ) 2015-06-03
# vchanger-launch-umount.sh ( vchanger v.1.0.3 ) 2020-05-06
#
# This script is used to run the vchanger-umount-uuid.sh script in
# another [background] process launched by the at command in order
# to prevent delays when invoked by a udev rule.
#
VCHANGER_UMOUNT=/usr/libexec/vchanger/vchanger-umount-uuid.sh
{
$VCHANGER_UMOUNT $1
} &
# For some reason, nohup doesn't work, but "at now" does. This may have to
# do with cgroups.
#nohup $VCHANGER_UMOUNT $1 </dev/null >/dev/null 2>&1 &
echo "$VCHANGER_UMOUNT $1 </dev/null >/dev/null 2>&1" | at now

View File

@ -1,6 +1,6 @@
#!/bin/bash
#
# vchanger-mount-uuid.sh ( vchanger v.1.0.1 ) 2015-06-03
# vchanger-mount-uuid.sh ( vchanger v.1.0.3 ) 2020-05-06
#
# This script is used to mount the filesystem having the
# UUID specified in parameter 1 at a fixed path. The path

View File

@ -1,6 +1,6 @@
#!/bin/bash
#
# vchanger-umount-uuid.sh ( vchanger v.1.0.1 ) 2015-06-03
# vchanger-umount-uuid.sh ( vchanger v.1.0.3 ) 2020-05-06
#
# This script is used to unmount the filesystem having the
# UUID specified in parameter 1. The mountpoint path

View File

@ -4,11 +4,10 @@ AM_CXXFLAGS = -DLOCALSTATEDIR='"${localstatedir}"'
AM_LDFLAGS = @WINLDADD@
bin_PROGRAMS = vchanger
vchanger_SOURCES = compat/getline.c compat/gettimeofday.c \
compat/localtime_r.c \
compat/readlink.c \
compat/readlink.c compat/semaphore.c \
compat/symlink.c compat/sleep.c compat/syslog.c \
win32_util.c uuidlookup.c bconsole.cpp \
tstring.cpp inifile.cpp mypopen.cpp \
tstring.cpp inifile.cpp mymutex.cpp mypopen.cpp \
vconf.cpp loghandler.cpp errhandler.cpp \
util.cpp changerstate.cpp diskchanger.cpp \
vchanger.cpp

View File

@ -91,12 +91,12 @@ CONFIG_CLEAN_VPATH_FILES =
am__installdirs = "$(DESTDIR)$(bindir)"
PROGRAMS = $(bin_PROGRAMS)
am_vchanger_OBJECTS = getline.$(OBJEXT) gettimeofday.$(OBJEXT) \
localtime_r.$(OBJEXT) readlink.$(OBJEXT) symlink.$(OBJEXT) \
readlink.$(OBJEXT) semaphore.$(OBJEXT) symlink.$(OBJEXT) \
sleep.$(OBJEXT) syslog.$(OBJEXT) win32_util.$(OBJEXT) \
uuidlookup.$(OBJEXT) bconsole.$(OBJEXT) tstring.$(OBJEXT) \
inifile.$(OBJEXT) mypopen.$(OBJEXT) vconf.$(OBJEXT) \
loghandler.$(OBJEXT) errhandler.$(OBJEXT) util.$(OBJEXT) \
changerstate.$(OBJEXT) diskchanger.$(OBJEXT) \
inifile.$(OBJEXT) mymutex.$(OBJEXT) mypopen.$(OBJEXT) \
vconf.$(OBJEXT) loghandler.$(OBJEXT) errhandler.$(OBJEXT) \
util.$(OBJEXT) changerstate.$(OBJEXT) diskchanger.$(OBJEXT) \
vchanger.$(OBJEXT)
vchanger_OBJECTS = $(am_vchanger_OBJECTS)
vchanger_LDADD = $(LDADD)
@ -269,11 +269,10 @@ AM_CFLAGS = -DLOCALSTATEDIR='"${localstatedir}"'
AM_CXXFLAGS = -DLOCALSTATEDIR='"${localstatedir}"'
AM_LDFLAGS = @WINLDADD@
vchanger_SOURCES = compat/getline.c compat/gettimeofday.c \
compat/localtime_r.c \
compat/readlink.c \
compat/readlink.c compat/semaphore.c \
compat/symlink.c compat/sleep.c compat/syslog.c \
win32_util.c uuidlookup.c bconsole.cpp \
tstring.cpp inifile.cpp mypopen.cpp \
tstring.cpp inifile.cpp mymutex.cpp mypopen.cpp \
vconf.cpp loghandler.cpp errhandler.cpp \
util.cpp changerstate.cpp diskchanger.cpp \
vchanger.cpp
@ -372,10 +371,11 @@ distclean-compile:
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/getline.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gettimeofday.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/inifile.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/localtime_r.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/loghandler.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/mymutex.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/mypopen.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/readlink.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/semaphore.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sleep.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/symlink.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/syslog.Po@am__quote@
@ -428,20 +428,6 @@ gettimeofday.obj: compat/gettimeofday.c
@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o gettimeofday.obj `if test -f 'compat/gettimeofday.c'; then $(CYGPATH_W) 'compat/gettimeofday.c'; else $(CYGPATH_W) '$(srcdir)/compat/gettimeofday.c'; fi`
localtime_r.o: compat/localtime_r.c
@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT localtime_r.o -MD -MP -MF $(DEPDIR)/localtime_r.Tpo -c -o localtime_r.o `test -f 'compat/localtime_r.c' || echo '$(srcdir)/'`compat/localtime_r.c
@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/localtime_r.Tpo $(DEPDIR)/localtime_r.Po
@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='compat/localtime_r.c' object='localtime_r.o' libtool=no @AMDEPBACKSLASH@
@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o localtime_r.o `test -f 'compat/localtime_r.c' || echo '$(srcdir)/'`compat/localtime_r.c
localtime_r.obj: compat/localtime_r.c
@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT localtime_r.obj -MD -MP -MF $(DEPDIR)/localtime_r.Tpo -c -o localtime_r.obj `if test -f 'compat/localtime_r.c'; then $(CYGPATH_W) 'compat/localtime_r.c'; else $(CYGPATH_W) '$(srcdir)/compat/localtime_r.c'; fi`
@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/localtime_r.Tpo $(DEPDIR)/localtime_r.Po
@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='compat/localtime_r.c' object='localtime_r.obj' libtool=no @AMDEPBACKSLASH@
@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o localtime_r.obj `if test -f 'compat/localtime_r.c'; then $(CYGPATH_W) 'compat/localtime_r.c'; else $(CYGPATH_W) '$(srcdir)/compat/localtime_r.c'; fi`
readlink.o: compat/readlink.c
@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT readlink.o -MD -MP -MF $(DEPDIR)/readlink.Tpo -c -o readlink.o `test -f 'compat/readlink.c' || echo '$(srcdir)/'`compat/readlink.c
@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/readlink.Tpo $(DEPDIR)/readlink.Po
@ -456,6 +442,20 @@ readlink.obj: compat/readlink.c
@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o readlink.obj `if test -f 'compat/readlink.c'; then $(CYGPATH_W) 'compat/readlink.c'; else $(CYGPATH_W) '$(srcdir)/compat/readlink.c'; fi`
semaphore.o: compat/semaphore.c
@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT semaphore.o -MD -MP -MF $(DEPDIR)/semaphore.Tpo -c -o semaphore.o `test -f 'compat/semaphore.c' || echo '$(srcdir)/'`compat/semaphore.c
@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/semaphore.Tpo $(DEPDIR)/semaphore.Po
@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='compat/semaphore.c' object='semaphore.o' libtool=no @AMDEPBACKSLASH@
@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o semaphore.o `test -f 'compat/semaphore.c' || echo '$(srcdir)/'`compat/semaphore.c
semaphore.obj: compat/semaphore.c
@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT semaphore.obj -MD -MP -MF $(DEPDIR)/semaphore.Tpo -c -o semaphore.obj `if test -f 'compat/semaphore.c'; then $(CYGPATH_W) 'compat/semaphore.c'; else $(CYGPATH_W) '$(srcdir)/compat/semaphore.c'; fi`
@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/semaphore.Tpo $(DEPDIR)/semaphore.Po
@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='compat/semaphore.c' object='semaphore.obj' libtool=no @AMDEPBACKSLASH@
@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o semaphore.obj `if test -f 'compat/semaphore.c'; then $(CYGPATH_W) 'compat/semaphore.c'; else $(CYGPATH_W) '$(srcdir)/compat/semaphore.c'; fi`
symlink.o: compat/symlink.c
@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT symlink.o -MD -MP -MF $(DEPDIR)/symlink.Tpo -c -o symlink.o `test -f 'compat/symlink.c' || echo '$(srcdir)/'`compat/symlink.c
@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/symlink.Tpo $(DEPDIR)/symlink.Po

View File

@ -1,6 +1,6 @@
/* bconsole.cpp
*
* Copyright (C) 2008-2014 Josh Fisher
* Copyright (C) 2008-2018 Josh Fisher
*
* This program is free software. You may redistribute it and/or modify
* it under the terms of the GNU General Public License, as published by
@ -47,18 +47,22 @@
#include <ctype.h>
#endif
#include "compat_defs.h"
#include "util.h"
#include "loghandler.h"
#include "mymutex.h"
#include "mypopen.h"
#include "vconf.h"
#include "bconsole.h"
#ifndef HAVE_WINDOWS_H
/*
* Function to issue command in Bacula console.
* Returns zero on success, or errno if there was an error running the command
* or a timeout occurred.
*/
int issue_bconsole_command(const char *bcmd)
static int issue_bconsole_command(const char *bcmd)
{
int pid, rc, n, len, fno_in = -1, fno_out = -1;
struct timeval tv;
@ -66,7 +70,6 @@ int issue_bconsole_command(const char *bcmd)
tString cmd, tmp;
char buf[4096];
#ifndef HAVE_WINDOWS_H
/* Build command line */
cmd = conf.bconsole;
if (cmd.empty()) return 0;
@ -76,11 +79,11 @@ int issue_bconsole_command(const char *bcmd)
}
cmd += " -n -u 30";
/* Start bconsole process */
log.Debug("bconsole: running '%s'", bcmd);
vlog.Debug("running '%s'", cmd.c_str());
pid = mypopen_raw(cmd.c_str(), &fno_in, &fno_out, NULL);
if (pid < 0) {
rc = errno;
log.Error("bconsole: run failed errno=%d", rc);
vlog.Error("bconsole run failed errno=%d", rc);
errno = rc;
return rc;
}
@ -91,7 +94,7 @@ int issue_bconsole_command(const char *bcmd)
FD_SET(fno_in, &rfd);
rc = select(fno_in + 1, NULL, &rfd, NULL, &tv);
if (rc == 0) {
log.Error("bconsole: timed out waiting to send command");
vlog.Error("timeout waiting to send command to bconsole");
close(fno_in);
close(fno_out);
errno = ETIMEDOUT;
@ -99,20 +102,21 @@ int issue_bconsole_command(const char *bcmd)
}
if (rc < 0) {
rc = errno;
log.Error("bconsole: errno=%d waiting to send command", rc);
vlog.Error("errno=%d waiting to send command to bconsole", rc);
close(fno_in);
close(fno_out);
errno = rc;
return rc;
}
/* Send command to bconsole's stdin */
vlog.Debug("sending bconsole command '%s'", bcmd);
len = strlen(bcmd);
n = 0;
while (n < len) {
rc = write(fno_in, bcmd + n, len - n);
if (rc < 0) {
rc = errno;
log.Error("bconsole: send to bconsole's stdin failed errno=%d", rc);
vlog.Error("send to bconsole's stdin failed errno=%d", rc);
close(fno_in);
close(fno_out);
errno = rc;
@ -120,57 +124,90 @@ int issue_bconsole_command(const char *bcmd)
}
n += rc;
}
if (write(fno_in, "\n", 1) != 1) {
rc = errno;
vlog.Error("send to bconsole's stdin failed errno=%d", rc);
close(fno_in);
/* Read stdout from bconsole */
memset(buf, 0, sizeof(buf));
tmp.clear();
while (true) {
tv.tv_sec = 30;
tv.tv_usec = 0;
FD_ZERO(&rfd);
FD_SET(fno_out, &rfd);
rc = select(fno_out + 1, &rfd, NULL, NULL, &tv);
if (rc == 0) {
log.Error("bconsole: timed out waiting for bconsole output");
close(fno_out);
errno = ETIMEDOUT;
return ETIMEDOUT;
}
if (rc < 0) {
rc = errno;
log.Error("bconsole: errno=%d waiting for bconsole output", rc);
close(fno_out);
errno = rc;
return rc;
}
rc = read(fno_out, buf, 4095);
if (rc < 0) {
rc = errno;
log.Error("bconsole: errno=%d reading bconsole stdout", rc);
close(fno_out);
errno = rc;
return rc;
} else if (rc > 0) {
buf[rc] = 0;
tmp += buf;
} else break;
}
close(fno_out);
log.Debug("bconsole: output:\n%s", tmp.c_str());
/* Wait for bconsole process to finish */
close(fno_in);
pid = waitpid(pid, &rc, 0);
if (!WIFEXITED(rc)) {
log.Error("bconsole: abnormal exit of bconsole process");
vlog.Error("abnormal exit of bconsole process");
close(fno_out);
return EPIPE;
}
if (WEXITSTATUS(rc)) {
log.Error("bconsole: exited with rc=%d", WEXITSTATUS(rc));
vlog.Error("bconsole: exited with rc=%d", WEXITSTATUS(rc));
close(fno_out);
return WEXITSTATUS(rc);
}
return 0;
#else
return EINVAL;
#endif
/* Read stdout from bconsole */
vlog.Debug("bconsole: bconsole terminated normally");
memset(buf, 0, sizeof(buf));
tmp.clear();
rc = read(fno_out, buf, 4095);
while (rc > 0) {
buf[rc] = 0;
tmp += buf;
rc = read(fno_out, buf, 4095);
}
if (rc < 0) {
rc = errno;
vlog.Error("errno=%d reading bconsole stdout", rc);
close(fno_out);
errno = rc;
return rc;
}
close(fno_out);
vlog.Debug("bconsole output:\n%s", tmp.c_str());
return 0;
}
/*
* Function to fork a new process and issue commands in Bacula console to
* perform update slots and/or label new volumes using barcodes.
*/
void IssueBconsoleCommands(bool update_slots, bool label_barcodes)
{
tString cmd;
/* Check if update needed */
if (!update_slots && !label_barcodes) return; /* Nothing to do */
/* Perform update slots command in bconsole */
if (update_slots) {
tFormat(cmd, "update slots storage=\"%s\" drive=\"0\"", conf.storage_name.c_str());
if(issue_bconsole_command(cmd.c_str())) {
vlog.Error("WARNING! 'update slots' needed in bconsole");
}
vlog.Info("bconsole update slots command success");
}
/* Perform label barcodes command in bconsole */
if (label_barcodes) {
tFormat(cmd, "label storage=\"%s\" pool=\"%s\" barcodes\nyes\nyes\n", conf.storage_name.c_str(),
conf.def_pool.c_str());
if (issue_bconsole_command(cmd.c_str())) {
vlog.Error("WARNING! 'label barcodes' needed in bconsole");
}
vlog.Info("bconsole label barcodes command success");
}
}
#else
/*
* Bconsole interaction is not currently supported on Windows
*/
void IssueBconsoleCommands(bool update_slots, bool label_barcodes)
{
return;
}
#endif

View File

@ -2,7 +2,7 @@
*
* This file is part of vchanger by Josh Fisher.
*
* vchanger copyright (C) 2008-2014 Josh Fisher
* vchanger copyright (C) 2008-2017 Josh Fisher
*
* vchanger is free software.
* You may redistribute it and/or modify it under the terms of the
@ -24,6 +24,6 @@
#ifndef BCONSOLE_H_
#define BCONSOLE_H_
int issue_bconsole_command(const char *bcmd);
void IssueBconsoleCommands(bool update_slots, bool label_barcodes);
#endif /* BCONSOLE_H_ */

View File

@ -2,7 +2,7 @@
*
* This file is part of vchanger by Josh Fisher.
*
* vchanger copyright (C) 2008-2015 Josh Fisher
* vchanger copyright (C) 2008-2018 Josh Fisher
*
* vchanger is free software.
* You may redistribute it and/or modify it under the terms of the
@ -173,7 +173,7 @@ int MagazineState::save()
if (mag_bay < 0) {
verr.SetErrorWithErrno(EINVAL, "cannot save state of invalid magazine %d", mag_bay);
log.Error("ERROR! %s", verr.GetErrorMsg());
vlog.Error("ERROR! %s", verr.GetErrorMsg());
return EINVAL;
}
/* Build path to state file */
@ -191,7 +191,7 @@ int MagazineState::save()
rc = errno;
umask(old_mask);
verr.SetErrorWithErrno(rc, "cannot open magazine %d state file for writing", mag_bay);
log.Error("ERROR! %s", verr.GetErrorMsg());
vlog.Error("ERROR! %s", verr.GetErrorMsg());
return rc;
}
/* Save magazine device (directory or UUID), number of volumes, and start of
@ -203,12 +203,12 @@ int MagazineState::save()
unlink(sname);
umask(old_mask);
verr.SetErrorWithErrno(rc, "cannot write to magazine %d state file", mag_bay);
log.Error("ERROR! %s", verr.GetErrorMsg());
vlog.Error("ERROR! %s", verr.GetErrorMsg());
return rc;
}
fclose(FS);
umask(old_mask);
log.Notice("saved state of magazine %d", mag_bay);
vlog.Notice("saved state of magazine %d", mag_bay);
return 0;
}
@ -230,7 +230,7 @@ int MagazineState::restore()
if (mag_bay < 0) {
verr.SetErrorWithErrno(EINVAL, "cannot restore state of invalid magazine %d", mag_bay);
log.Error("ERROR! %s", verr.GetErrorMsg());
vlog.Error("ERROR! %s", verr.GetErrorMsg());
return EINVAL;
}
clear();
@ -250,7 +250,7 @@ int MagazineState::restore()
/* No read permission? */
rc = errno;
verr.SetErrorWithErrno(rc, "cannot open magazine %d state file for reading", mag_bay);
log.Error("ERROR! %s", verr.GetErrorMsg());
vlog.Error("ERROR! %s", verr.GetErrorMsg());
return rc;
}
if (tGetLine(line, FS) == NULL) {
@ -259,7 +259,7 @@ int MagazineState::restore()
/* error reading bay state file */
fclose(FS);
verr.SetErrorWithErrno(rc, "error reading magazine %d state file", mag_bay);
log.Error("ERROR! %s", verr.GetErrorMsg());
vlog.Error("ERROR! %s", verr.GetErrorMsg());
return rc;
}
}
@ -270,7 +270,7 @@ int MagazineState::restore()
p = 0;
if (tParseCSV(word, line, p) <= 0) {
/* bay state file should not be empty, assume it didn't exist */
log.Warning("WARNING! magazine %d state file was empty, deleting it", mag_bay);
vlog.Warning("WARNING! magazine %d state file was empty, deleting it", mag_bay);
unlink(sname);
return 0;
}
@ -285,7 +285,7 @@ int MagazineState::restore()
/* Bay state file is corrupt.
* Treat as if it was not mounted at last invocation */
clear();
log.Warning("WARNING! magazine %d state file corrupt, deleting it", mag_bay);
vlog.Warning("WARNING! magazine %d state file corrupt, deleting it", mag_bay);
unlink(sname);
return 0;
}
@ -293,7 +293,7 @@ int MagazineState::restore()
/* Corrupt bay state file, assume it doesn't exist */
clear();
unlink(sname);
log.Warning("WARNING! magazine %d state file has invalid number of slots field, deleting it", mag_bay);
vlog.Warning("WARNING! magazine %d state file has invalid number of slots field, deleting it", mag_bay);
return 0;
}
prev_num_slots = (int)strtol(word.c_str(), NULL, 10);
@ -302,7 +302,7 @@ int MagazineState::restore()
clear();
prev_num_slots = 0;
unlink(sname);
log.Warning("WARNING! magazine %d state file has invalid number of slots field, deleting it", mag_bay);
vlog.Warning("WARNING! magazine %d state file has invalid number of slots field, deleting it", mag_bay);
return 0;
}
@ -312,7 +312,7 @@ int MagazineState::restore()
* Treat as if it was not mounted at last invocation */
clear();
prev_num_slots = 0;
log.Warning("WARNING! magazine %d state file corrupt, deleting it", mag_bay);
vlog.Warning("WARNING! magazine %d state file corrupt, deleting it", mag_bay);
unlink(sname);
return 0;
}
@ -321,7 +321,7 @@ int MagazineState::restore()
clear();
prev_num_slots = 0;
unlink(sname);
log.Warning("WARNING! magazine %d state file has invalid virtual slot assignment field, deleting it",
vlog.Warning("WARNING! magazine %d state file has invalid virtual slot assignment field, deleting it",
mag_bay);
return 0;
}
@ -332,11 +332,11 @@ int MagazineState::restore()
prev_num_slots = 0;
prev_start_slot = 0;
unlink(sname);
log.Warning("WARNING! magazine %d state file has invalid virtual slot assignment field, deleting it",
vlog.Warning("WARNING! magazine %d state file has invalid virtual slot assignment field, deleting it",
mag_bay);
return 0;
}
log.Notice("restored state of magazine %d", mag_bay);
vlog.Notice("restored state of magazine %d", mag_bay);
return 0;
}
@ -389,7 +389,7 @@ int MagazineState::UpdateMagazineFormat()
fs = fopen(lname.c_str(), "r");
if (fs == NULL) {
verr.SetErrorWithErrno(errno, "failed to find loaded%d file when updating magazine %d", drv, mag_bay);
log.Error("ERROR! %s", verr.GetErrorMsg());
vlog.Error("ERROR! %s", verr.GetErrorMsg());
de = readdir(dir);
continue;
}
@ -397,7 +397,7 @@ int MagazineState::UpdateMagazineFormat()
fclose(fs);
if (str.empty()) {
verr.SetError(-1, "loaded%d file empty when updating magazine %d", drv, mag_bay);
log.Error("ERROR! %s", verr.GetErrorMsg());
vlog.Error("ERROR! %s", verr.GetErrorMsg());
de = readdir(dir);
continue;
}
@ -406,7 +406,7 @@ int MagazineState::UpdateMagazineFormat()
if (rename(fname.c_str(), vname.c_str())) {
verr.SetError(EINVAL, "unable to rename 'drive%d' on magazine %d",
drv, mag_bay);
log.Error("ERROR! %s", verr.GetErrorMsg());
vlog.Error("ERROR! %s", verr.GetErrorMsg());
}
}
de = readdir(dir);
@ -445,7 +445,7 @@ int MagazineState::UpdateMagazineFormat()
tFormat(fname, "%s%sindex", mountpoint.c_str(), DIR_DELIM);
unlink(fname.c_str());
log.Warning("magaine %d updated from old format", mag_bay);
vlog.Warning("magaine %d updated from old format", mag_bay);
return 0;
}
@ -494,7 +494,7 @@ int MagazineState::Mount()
}
if (rc) {
verr.SetError(rc, "system error determining mountpoint from UUID");
log.Error("ERROR! %s", verr.GetErrorMsg());
vlog.Error("ERROR! %s", verr.GetErrorMsg());
mountpoint.clear();
return -1;
}
@ -510,7 +510,7 @@ int MagazineState::Mount()
/* Ensure access to magazine mountpoint */
if (access(mountpoint.c_str(), W_OK) != 0) {
verr.SetError(rc, "no write access to directory %s", mountpoint.c_str());
log.Error("%s", verr.GetErrorMsg());
vlog.Error("%s", verr.GetErrorMsg());
mountpoint.clear();
return -5;
}
@ -529,7 +529,7 @@ int MagazineState::Mount()
/* could not open mountpoint dir */
rc = errno;
verr.SetErrorWithErrno(rc, "cannot open directory '%s'", mountpoint.c_str());
log.Error("ERROR! %s", verr.GetErrorMsg());
vlog.Error("ERROR! %s", verr.GetErrorMsg());
mountpoint.clear();
if (rc == ENOTDIR || rc == ENOENT) return -3;
if (rc == EACCES) return -5;
@ -659,7 +659,7 @@ int MagazineState::CreateVolume(const char *vol_label_in)
}
if (rc != ENOENT) {
verr.SetErrorWithErrno(rc, "error %d accessing volumes on magazine %d", rc, mag_bay);
log.Error("MagazineState::CreateVolume: %s", verr.GetErrorMsg());
vlog.Error("MagazineState::CreateVolume: %s", verr.GetErrorMsg());
return -1;
}
/* Create new volume file on magazine */
@ -667,7 +667,7 @@ int MagazineState::CreateVolume(const char *vol_label_in)
if (!fs) {
rc = errno;
verr.SetErrorWithErrno(rc, "error %d creating volume on magazine %d", rc, mag_bay);
log.Error("MagazineState::CreateVolume: %s", verr.GetErrorMsg());
vlog.Error("MagazineState::CreateVolume: %s", verr.GetErrorMsg());
return -1;
}
fclose(fs);
@ -676,7 +676,7 @@ int MagazineState::CreateVolume(const char *vol_label_in)
new_mslot.label = label;
mslot.push_back(new_mslot);
++num_slots;
log.Notice("created volume '%s' on magazine %d (%s)", label.c_str(), mag_bay, mag_dev.c_str());
vlog.Notice("created volume '%s' on magazine %d (%s)", label.c_str(), mag_bay, mag_dev.c_str());
return 0;
}
@ -784,7 +784,7 @@ void DynamicConfig::save()
/* Unable to open dynamic.conf file for writing */
rc = errno;
umask(old_mask);
log.Error("ERROR! cannot open dynamic.conf file for writing (errno=%d)", rc);
vlog.Error("ERROR! cannot open dynamic.conf file for writing (errno=%d)", rc);
return;
}
/* Save max slot number in use to dynamic configuration */
@ -794,12 +794,12 @@ void DynamicConfig::save()
fclose(FS);
unlink(sname);
umask(old_mask);
log.Error("ERROR! i/o error writing dynamic.conf file (errno=%d)", rc);
vlog.Error("ERROR! i/o error writing dynamic.conf file (errno=%d)", rc);
return;
}
fclose(FS);
umask(old_mask);
log.Notice("saved dynamic configuration (max used slot: %d)", max_slot);
vlog.Notice("saved dynamic configuration (max used slot: %d)", max_slot);
}
@ -827,7 +827,7 @@ void DynamicConfig::restore()
if (!FS) {
/* No read permission? */
rc = errno;
log.Error("ERROR! cannot open dynamic.conf file for restore (errno=%d)", rc);
vlog.Error("ERROR! cannot open dynamic.conf file for restore (errno=%d)", rc);
return;
}
if (tGetLine(line, FS) == NULL) {
@ -835,7 +835,7 @@ void DynamicConfig::restore()
if (!feof(FS)) {
/* error reading bay state file */
fclose(FS);
log.Error("ERROR! i/o error reading dynamic.conf file (errno=%d)", rc);
vlog.Error("ERROR! i/o error reading dynamic.conf file (errno=%d)", rc);
return;
}
}

197
src/compat/semaphore.c Normal file
View File

@ -0,0 +1,197 @@
/* semaphore.c
*
* This file is part of vchanger by Josh Fisher.
*
* vchanger copyright (C) 2020 Josh Fisher
*
* vchanger is free software.
* You may redistribute it and/or modify it under the terms of the
* GNU General Public License version 2, as published by the Free
* Software Foundation.
*
* vchanger is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
* See the GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with vchanger. See the file "COPYING". If not,
* write to: The Free Software Foundation, Inc.,
* 59 Temple Place - Suite 330,
* Boston, MA 02111-1307, USA.
*/
#include "config.h"
#ifndef HAVE_SEMAPHORE_H
#ifdef HAVE_WINDOWS_H
#include "targetver.h"
#include <windows.h>
#include <limits.h>
#include "win32_util.h"
#endif
/*
#ifdef HAVE_ERRNO_H
#include <errno.h>
#endif
*/
#include "compat/semaphore.h"
#ifdef HAVE_WINDOWS_H
/*-------------------------------------------------
* Emulate POSIX.1-2001 sem_open() function using win32/win64 CreateSemaphoreW() function.
* On success address of semaphore, else NULL on error.
*-------------------------------------------------*/
sem_t* sem_open(const char *name, int oflag, ...)
{
DWORD rc;
long mode, value;
wchar_t *wname = NULL;
size_t wname_sz = 0;
sem_t *fd;
va_list vl;
va_start(vl, oflag);
mode = (long)va_arg(vl, unsigned int);
value = (long)va_arg(vl, unsigned int);
va_end(vl);
/* Convert path strings to UTF16 encoding */
if (!AnsiToUTF16(name, &wname, &wname_sz)) {
rc = ERROR_BAD_PATHNAME;
errno = w32errno(rc);
return NULL;
}
fd = (sem_t*)CreateSemaphoreW(NULL, value, LONG_MAX, wname);
free(wname);
if (fd == NULL) {
errno = w32errno(GetLastError());
}
return fd;
}
/*-------------------------------------------------
* Emulate POSIX.1-2001 sem_close() function using win32/win64 CloseHandle() function.
* On success returns zero, else negative on error.
*-------------------------------------------------*/
int sem_close(sem_t *sem)
{
if (sem == NULL) {
errno = EINVAL;
return -1;
}
CloseHandle((HANDLE)sem);
return 0;
}
/*-------------------------------------------------
* Emulate POSIX.1-2001 sem_unnlink() function by doing nothing. Windows will
* automatically unlink the object when the last open handle is closed.
* Returns zero.
*-------------------------------------------------*/
int sem_unlink(const char *name)
{
return 0;
}
/*-------------------------------------------------
* Emulate POSIX.1-2001 sem_post() function using win32/win64 ReleaseSemaphore() function.
* On success returns zero, else negative on error.
*-------------------------------------------------*/
int sem_post(sem_t *sem)
{
if (sem == NULL) {
errno = EINVAL;
return -1;
}
ReleaseSemaphore((HANDLE)sem, 1, NULL);
return 0;
}
/*-------------------------------------------------
* Emulate POSIX.1-2001 sem_wait() function using win32/win64 WaitForSingleObjectEx() function.
* On success returns zero, else negative on error.
*-------------------------------------------------*/
int sem_wait(sem_t *sem)
{
DWORD reason;
if (sem == NULL) {
errno = EINVAL;
return -1;
}
reason = WaitForSingleObjectEx((HANDLE)sem, INFINITE, FALSE);
if (reason == 0) return 0;
switch (reason) {
case WAIT_IO_COMPLETION:
errno = EINTR;
break;
case WAIT_TIMEOUT:
errno = ETIMEDOUT;
break;
default:
errno = EINVAL;
break;
}
return -1;
}
/*-------------------------------------------------
* Emulate POSIX.1-2001 sem_timedwait() function using win32/win64 WaitForSingleObjectEx()
* function.
* On success returns zero, else negative on error.
*-------------------------------------------------*/
int sem_timedwait(sem_t *sem, const struct timespec *timeout)
{
DWORD reason, mt;
if (sem == NULL) {
errno = EINVAL;
return -1;
}
/* semaphore.h functions use absolute time and Windows needs a time interval */
timeout->tv_sec -= time(NULL);
mt = timeout->tv_sec * 1000 + (timeout->tv_nsec / 1000000);
reason = WaitForSingleObjectEx((HANDLE)sem, mt, FALSE);
if (reason == WAIT_OBJECT_0) return 0;
switch (reason) {
case WAIT_IO_COMPLETION:
errno = EINTR;
break;
case WAIT_TIMEOUT:
errno = ETIMEDOUT;
break;
default:
errno = EINVAL;
break;
}
return -1;
}
/*-------------------------------------------------
* Emulate POSIX.1-2001 sem_trywait() function using win32/win64 WaitForSingleObjectEx()
* function.
* On success returns zero, else negative on error.
*-------------------------------------------------*/
int sem_trywait(sem_t *sem)
{
DWORD reason;
if (sem == NULL) {
errno = EINVAL;
return -1;
}
reason = WaitForSingleObjectEx((HANDLE)sem, 0, FALSE);
if (reason == 0) return 0;
errno = EAGAIN;
return -1;
}
#endif
#endif

54
src/compat/semaphore.h Normal file
View File

@ -0,0 +1,54 @@
/* semaphore.h
*
* This file is part of vchanger by Josh Fisher.
*
* vchanger copyright (C) 2020 Josh Fisher
*
* vchanger is free software.
* You may redistribute it and/or modify it under the terms of the
* GNU General Public License version 2, as published by the Free
* Software Foundation.
*
* vchanger is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
* See the GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with vchanger. See the file "COPYING". If not,
* write to: The Free Software Foundation, Inc.,
* 59 Temple Place - Suite 330,
* Boston, MA 02111-1307, USA.
*/
#ifndef _SEMAPHORE_H
#define _SEMAPHORE_H
#ifndef HAVE_SEMAPHORE_H
/* For systems without symlink() function, use internal version */
typedef union
{
char __size[32];
long int __align;
} sem_t;
#ifdef __cplusplus
extern "C" {
#endif
sem_t* sem_open(const char *name, int oflag, ...);
int sem_post(sem_t *sem);
int sem_wait(sem_t *sem);
int sem_trywait(sem_t *sem);
int sem_timedwait(sem_t *sem, const struct timespec *abs_timeout);
int sem_close(sem_t *sem);
int sem_unlink(const char *name);
#ifdef __cplusplus
}
#endif
#endif
#endif /* _SEMAPHORE_H */

View File

@ -2,7 +2,7 @@
*
* This file is part of vchanger by Josh Fisher.
*
* vchanger copyright (C) 2008-2015 Josh Fisher
* vchanger copyright (C) 2008-2020 Josh Fisher
*
* vchanger is free software.
* You may redistribute it and/or modify it under the terms of the
@ -65,20 +65,13 @@
#include "loghandler.h"
#include "bconsole.h"
#include "diskchanger.h"
#include "vconf.h"
/*=================================================
* Class DiskChanger
*=================================================*/
/*-------------------------------------------------
* destructor
*-------------------------------------------------*/
DiskChanger::~DiskChanger()
{
Unlock();
}
/*-------------------------------------------------
* Protected method to read previous state of magazine bays.
@ -148,6 +141,7 @@ void DiskChanger::InitializeVirtSlots()
/* Create all known slots as initially empty */
vslot.clear();
vs.clear();
for (s = 0; s <= dconf.max_slot; s++) {
vs.vs = s;
vslot.push_back(vs);
@ -155,7 +149,7 @@ void DiskChanger::InitializeVirtSlots()
/* Re-create virtual slots that existed previously if possible */
for (m = 0; m < (int)magazine.size(); m++) {
/* Create slots if needed to match max slot used by previous magazines */
last = magazine[m].prev_start_slot + magazine[m].prev_num_slots;
last = magazine[m].prev_start_slot + magazine[m].prev_num_slots - 1;
if (last >= (int)vslot.size()) {
vs.clear();
while ((int)vslot.size() <= last) {
@ -165,11 +159,11 @@ void DiskChanger::InitializeVirtSlots()
}
/* Check this magazine's slots */
if (magazine[m].empty()) {
log.Info("magazine %d is not mounted", m);
vlog.Info("magazine %d is not mounted", m);
/* magazine is not currently mounted, so will have no slots assigned */
if (magazine[m].prev_start_slot) {
/* Since it was previously mounted, an 'update slots' is needed */
log.Warning("update slots needed. magazine %d no longer mounted; previous: %d volumes in slots %d-%d", m,
vlog.Warning("update slots needed. magazine %d no longer mounted; previous: %d volumes in slots %d-%d", m,
magazine[m].prev_num_slots, magazine[m].prev_start_slot,
magazine[m].prev_start_slot + magazine[m].prev_num_slots - 1);
needs_update = true;
@ -177,12 +171,12 @@ void DiskChanger::InitializeVirtSlots()
continue;
}
/* Magazine is currently mounted, so check for change in slot assignment */
log.Info("magazine %d has %d volumes on %s", m, magazine[m].num_slots,
vlog.Info("magazine %d has %d volumes on %s", m, magazine[m].num_slots,
magazine[m].mountpoint.c_str());
if (magazine[m].num_slots != magazine[m].prev_num_slots) {
/* Number of volumes has changed or magazine was not previously mounted, so
* needs new slot assignment and also 'update slots' will be needed */
log.Warning("update slots needed. magazine %d has %d volumes, previously had %d", m,
vlog.Warning("update slots needed. magazine %d has %d volumes, previously had %d", m,
magazine[m].num_slots, magazine[m].prev_num_slots);
needs_update = true;
continue;
@ -194,7 +188,7 @@ void DiskChanger::InitializeVirtSlots()
/* Magazine is mounted, was previously mounted, and has the same volume count,
* so attempt to assign to the same slots previously assigned */
found = false;
for (v = magazine[m].prev_start_slot; v < (int)vslot.size(); v++) {
for (v = magazine[m].prev_start_slot; v < magazine[m].prev_start_slot + magazine[m].prev_num_slots; v++) {
if (!vslot[v].empty()) {
found = true;
break;
@ -204,7 +198,7 @@ void DiskChanger::InitializeVirtSlots()
/* Slot used previously has already been assigned to another magazine.
* Magazine will need to be assigned a new slot range, so an
* 'update slots' will also be needed. */
log.Warning("update slots needed. magazine %d previous slots %d-%d are not available", m,
vlog.Warning("update slots needed. magazine %d previous slots %d-%d are not available", m,
magazine[m].prev_start_slot, magazine[m].prev_start_slot + magazine[m].prev_num_slots - 1);
needs_update = true;
continue;
@ -216,7 +210,7 @@ void DiskChanger::InitializeVirtSlots()
vslot[v].mag_bay = m;
vslot[v].mag_slot = s;
}
log.Notice("%d volumes on magazine %d assigned slots %d-%d", magazine[m].num_slots, m,
vlog.Notice("%d volumes on magazine %d assigned slots %d-%d", magazine[m].num_slots, m,
magazine[m].start_slot, magazine[m].start_slot + magazine[m].num_slots - 1);
}
@ -230,7 +224,7 @@ void DiskChanger::InitializeVirtSlots()
vslot[v].mag_bay = m;
vslot[v].mag_slot = s;
}
log.Notice("%d volumes on magazine %d assigned slots %d-%d", magazine[m].num_slots, m,
vlog.Notice("%d volumes on magazine %d assigned slots %d-%d", magazine[m].num_slots, m,
magazine[m].start_slot, magazine[m].start_slot + magazine[m].num_slots - 1);
}
@ -263,7 +257,7 @@ int DiskChanger::InitializeDrives()
if (!d) {
rc = errno;
verr.SetErrorWithErrno(rc, "error %d accessing work directory", rc);
log.Error("ERROR! %s", verr.GetErrorMsg());
vlog.Error("ERROR! %s", verr.GetErrorMsg());
return rc;
}
de = readdir(d);
@ -297,7 +291,7 @@ int DiskChanger::InitializeDrives()
drive.push_back(ds);
/* Attempt to restore drive's last state */
if (RestoreDriveState(n)) {
log.Error("ERROR! %s", verr.GetErrorMsg());
vlog.Error("ERROR! %s", verr.GetErrorMsg());
}
}
return 0;
@ -351,7 +345,7 @@ int DiskChanger::CreateDriveSymlink(int drv)
lname[rc] = 0;
if (fname == lname) {
/* symlink already exists */
log.Info("found symlink for drive %d -> %s", drv, fname.c_str());
vlog.Info("found symlink for drive %d -> %s", drv, fname.c_str());
return 0;
}
/* Symlink points to wrong mountpoint, so delete and re-create */
@ -362,7 +356,7 @@ int DiskChanger::CreateDriveSymlink(int drv)
verr.SetErrorWithErrno(rc, "error %d creating symlink for drive %d", rc, drv);
return rc;
}
log.Notice("created symlink for drive %d -> %s", drv, fname.c_str());
vlog.Notice("created symlink for drive %d -> %s", drv, fname.c_str());
return 0;
}
@ -389,7 +383,7 @@ int DiskChanger::RemoveDriveSymlink(int drv)
verr.SetErrorWithErrno(errno, "error %d deleting symlink for drive %d: ", rc, drv);
return rc;
}
log.Notice("deleted symlink for drive %d", drv);
vlog.Notice("deleted symlink for drive %d", drv);
return 0;
}
@ -416,7 +410,7 @@ int DiskChanger::SaveDriveState(int drv)
tFormat(sname, "%s%sdrive_state-%d", conf.work_dir.c_str(), DIR_DELIM, drv);
if (drive[drv].empty()) {
if (access(sname.c_str(), F_OK) == 0) {
log.Notice("deleted state file for drive %d", drv);
vlog.Notice("deleted state file for drive %d", drv);
}
unlink(sname.c_str());
return 0;
@ -443,7 +437,7 @@ int DiskChanger::SaveDriveState(int drv)
}
fclose(FS);
umask(old_mask);
log.Notice("wrote state file for drive %d", drv);
vlog.Notice("wrote state file for drive %d", drv);
return 0;
}
@ -477,7 +471,7 @@ int DiskChanger::RestoreDriveState(int drv)
if (stat(sname.c_str(), &st)) {
/* drive state file not found, so drive is not loaded */
RemoveDriveSymlink(drv);
log.Info("drive %d previously unloaded", drv);
vlog.Info("drive %d previously unloaded", drv);
return 0;
}
/* Read loaded volume info from state file */
@ -526,7 +520,7 @@ int DiskChanger::RestoreDriveState(int drv)
}
if (v >= (int)vslot.size()) {
/* Volume last loaded is no longer available. Change state to unloaded. */
log.Notice("volume %s no longer available, unloading drive %d",
vlog.Notice("volume %s no longer available, unloading drive %d",
labl.c_str(), drv);
unlink(sname.c_str());
RemoveDriveSymlink(drv);
@ -538,7 +532,7 @@ int DiskChanger::RestoreDriveState(int drv)
if ((rc = CreateDriveSymlink(drv)) != 0) {
/* Unable to create symlink */
drive[drv].vs = -1;
log.Error("ERROR! %s", verr.GetErrorMsg());
vlog.Error("ERROR! %s", verr.GetErrorMsg());
return rc;
}
@ -546,7 +540,7 @@ int DiskChanger::RestoreDriveState(int drv)
vslot[v].drv = drv;
m = vslot[v].mag_bay;
ms = vslot[v].mag_slot;
log.Notice("drive %d previously loaded from slot %d (%s)", drv, v, magazine[m].GetVolumeLabel(ms));
vlog.Notice("drive %d previously loaded from slot %d (%s)", drv, v, magazine[m].GetVolumeLabel(ms));
return 0;
}
@ -562,7 +556,6 @@ int DiskChanger::RestoreDriveState(int drv)
int DiskChanger::Initialize()
{
/* Make sure we have a lock on this changer */
if (Lock()) return verr.GetError();
magazine.clear();
vslot.clear();
drive.clear();
@ -591,38 +584,38 @@ int DiskChanger::LoadDrive(int drv, int slot)
{
int rc, m, ms;
if (!changer_lock) {
verr.SetError(EINVAL, "changer not initialized");
log.Error("ERROR! %s", verr.GetErrorMsg());
return EINVAL;
}
if (drv < 0) {
verr.SetError(EINVAL, "invalid drive number %d", drv);
log.Error("ERROR! %s", verr.GetErrorMsg());
vlog.Error("ERROR! %s", verr.GetErrorMsg());
return EINVAL;
}
SetMaxDrive(drv);
if (slot < 1 || slot >= (int)vslot.size()) {
verr.SetError(EINVAL, "cannot load drive %d from invalid slot %d", drv, slot);
log.Error("ERROR! %s", verr.GetErrorMsg());
vlog.Error("ERROR! %s", verr.GetErrorMsg());
return EINVAL;
}
if (!drive[drv].empty()) {
if (drive[drv].vs == slot) return 0; /* already loaded from this slot */
verr.SetError(EBUSY, "drive %d already loaded from slot %d", drv, slot);
log.Error("ERROR! %s", verr.GetErrorMsg());
vlog.Error("ERROR! %s", verr.GetErrorMsg());
return EBUSY;
}
if (vslot[slot].drv >= 0) {
verr.SetError(EINVAL, "requested slot %d already loaded in drive %d", slot, drv);
vlog.Error("ERROR! %s", verr.GetErrorMsg());
return ENOENT;
}
if (vslot[slot].empty()) {
verr.SetError(EINVAL, "cannot load drive %d from empty slot %d", drv, slot);
log.Error("ERROR! %s", verr.GetErrorMsg());
vlog.Error("ERROR! %s", verr.GetErrorMsg());
return ENOENT;
}
/* Create symlink for drive pointing to volume file */
drive[drv].vs = slot;
if ((rc = CreateDriveSymlink(drv))) {
drive[drv].vs = -1;
log.Error("ERROR! %s", verr.GetErrorMsg());
vlog.Error("ERROR! %s", verr.GetErrorMsg());
return rc;
}
/* Save state of newly loaded drive */
@ -630,14 +623,14 @@ int DiskChanger::LoadDrive(int drv, int slot)
/* Error writing drive state file */
RemoveDriveSymlink(drv);
drive[drv].vs = -1;
log.Error("ERROR! %s", verr.GetErrorMsg());
vlog.Error("ERROR! %s", verr.GetErrorMsg());
return rc;
}
/* Assign virtual slot to drive */
vslot[slot].drv = drv;
m = vslot[slot].mag_bay;
ms = vslot[slot].mag_slot;
log.Notice("loaded drive %d from slot %d (%s)", drv, slot, magazine[m].GetVolumeLabel(ms));
vlog.Notice("loaded drive %d from slot %d (%s)", drv, slot, magazine[m].GetVolumeLabel(ms));
return 0;
}
@ -652,14 +645,9 @@ int DiskChanger::UnloadDrive(int drv)
{
int rc;
if (!changer_lock) {
verr.SetError(EINVAL, "changer not initialized");
log.Error("ERROR! %s", verr.GetErrorMsg());
return EINVAL;
}
if (drv < 0) {
verr.SetError(EINVAL, "invalid drive number %d", drv);
log.Error("ERROR! %s", verr.GetErrorMsg());
vlog.Error("ERROR! %s", verr.GetErrorMsg());
return EINVAL;
}
SetMaxDrive(drv);
@ -669,7 +657,7 @@ int DiskChanger::UnloadDrive(int drv)
}
/* Remove drive's symlink */
if ((rc = RemoveDriveSymlink(drv)) != 0) {
log.Error("ERROR! %s", verr.GetErrorMsg());
vlog.Error("ERROR! %s", verr.GetErrorMsg());
return rc;
}
/* Remove virtual slot assignment */
@ -677,10 +665,10 @@ int DiskChanger::UnloadDrive(int drv)
drive[drv].vs = -1;
/* Update drive state file (will delete state file due to negative slot number) */
if ((rc = SaveDriveState(drv)) != 0) {
log.Error("ERROR! %s", verr.GetErrorMsg());
vlog.Error("ERROR! %s", verr.GetErrorMsg());
return rc;
}
log.Notice("unloaded drive %d", drv);
vlog.Notice("unloaded drive %d", drv);
return 0;
}
@ -699,26 +687,21 @@ int DiskChanger::CreateVolumes(int bay, int count, int start, const char *label_
tString label, label_prefix(label_prefix_in);
int i;
if (!changer_lock) {
verr.SetError(EINVAL, "changer not initialized");
log.Error("ERROR! %s", verr.GetErrorMsg());
return -1;
}
if (bay < 0 || bay >= (int)magazine.size()) {
verr.SetError(EINVAL, "invalid magazine");
log.Error("ERROR! %s", verr.GetErrorMsg());
vlog.Error("ERROR! %s", verr.GetErrorMsg());
return -1;
}
if (count < 1) count = 1;
tStrip(tRemoveEOL(label_prefix));
if (label_prefix.empty()) {
/* Default prefix is storage-name_magazine-number */
tFormat(label_prefix, "%s_%d_", conf.storage_name.c_str(), bay);
tFormat(label_prefix, "%s_%04d_", conf.storage_name.c_str(), bay);
}
if (start < 0) {
/* Find highest uniqueness number for this filename prefix */
for (i = magazine[bay].num_slots * 5; i > 0; i--) {
tFormat(label, "%s_%d", label_prefix.c_str(), i);
tFormat(label, "%s%04d", label_prefix.c_str(), i);
if (magazine[bay].GetVolumeSlot(label) >= 0) break;
}
start = i;
@ -733,6 +716,7 @@ int DiskChanger::CreateVolumes(int bay, int count, int start, const char *label_
}
fprintf(stdout, "creating label '%s'\n", label.c_str());
if (magazine[bay].CreateVolume(label)) {
/* On failure, update magazine state if any were created */
if (i) magazine[bay].save();
return -1;
}
@ -743,61 +727,7 @@ int DiskChanger::CreateVolumes(int bay, int count, int start, const char *label_
/* New mag state will require 'update slots' and 'label barcodes' in Bacula */
needs_update = true;
needs_label = true;
log.Notice("update slots needed. %d volumes added to magazine %d",count , bay);
return 0;
}
/*-------------------------------------------------
* Method to cause Bacula to update its catalog to reflect
* changes in the available volumes
*-------------------------------------------------*/
int DiskChanger::UpdateBacula()
{
int rc;
FILE *update_lock;
tString cmd;
char lockfile[4096];
/* Check if update needed */
if (!needs_update && !needs_label) return 0; /* Nothing to do */
/* Create update lock lockfile */
snprintf(lockfile, sizeof(lockfile), "%s%s%s.updatelock", conf.work_dir.c_str(), DIR_DELIM,
conf.storage_name.c_str());
rc = exclusive_fopen(lockfile, &update_lock);
if (rc == EEXIST) {
/* Update already in progress in another process, so skip */
return 0;
}
if (rc) {
/* error creating lockfile, so skip */
log.Error("bconsole: errno=%d creating update lockfile", rc);
if (needs_update)
log.Error("WARNING! 'update slots' needed in bconsole");
if (needs_label)
log.Error("WARNING! 'label barcodes' needed in bconsole");
return 0;
}
log.Debug("created update lockfile for pid %d", getpid());
/* Perform update slots command in bconsole */
if (needs_update) {
/* Issue update slots command in bconsole */
tFormat(cmd, "update slots storage=\"%s\"", conf.storage_name.c_str());
if(issue_bconsole_command(cmd.c_str())) {
log.Error("WARNING! 'update slots' needed in bconsole");
}
}
/* Perform label barcodes command in bconsole */
if (needs_label) {
tFormat(cmd, "label storage=\"%s\" pool=\"%s\" barcodes\nyes\nyes\n", conf.storage_name.c_str(),
conf.def_pool.c_str());
if (issue_bconsole_command(cmd.c_str())) {
log.Error("WARNING! 'label barcodes' needed in bconsole");
}
}
/* Obtain changer lock before removing update lock */
fclose(update_lock);
unlink(lockfile);
log.Debug("removed update lockfile for pid %d", getpid());
vlog.Notice("%d volumes added to magazine %d",count , bay);
return 0;
}
@ -809,7 +739,7 @@ const char* DiskChanger::GetVolumeLabel(int slot)
{
if (slot <= 0 || slot >= (int)vslot.size()) {
verr.SetError(-1, "volume label request from invalid slot %d", slot);
log.Error("ERROR! %s", verr.GetErrorMsg());
vlog.Error("ERROR! %s", verr.GetErrorMsg());
return NULL;
}
if (vslot[slot].empty()) return "";
@ -825,7 +755,7 @@ const char* DiskChanger::GetVolumePath(tString &path, int slot)
path.clear();
if (slot <= 0 || slot >= (int)vslot.size()) {
verr.SetError(-1, "volume path request from invalid slot %d", slot);
log.Error("ERROR! %s", verr.GetErrorMsg());
vlog.Error("ERROR! %s", verr.GetErrorMsg());
return NULL;
}
if (vslot[slot].empty()) return path.c_str();
@ -833,78 +763,6 @@ const char* DiskChanger::GetVolumePath(tString &path, int slot)
}
/*-------------------------------------------------
* Protected method to lock changer device using a lock file such that
* only one process at a time may execute changer commands on the
* same autochanger. If another process has the lock, then this process
* will sleep 1 second before trying again. This try/wait loop will continue
* until the lock is obtained or 'timeout' seconds have expired. If
* timeout = 0 then only tries to obtain lock once. If timeout < 0
* then doesn't return until the lock is obtained.
* On success, returns true. Otherwise on error or timeout, sets
* lasterr negative and returns false.
*------------------------------------------------*/
int DiskChanger::Lock(long timeout_seconds)
{
int rc;
time_t timeout = 0;
char lockfile[4096];
if (changer_lock) return 0;
if (timeout_seconds < 0) {
timeout_seconds = 3600 * 24 * 365;
}
if (timeout_seconds > 0) {
timeout = time(NULL) + timeout_seconds;
}
snprintf(lockfile, sizeof(lockfile), "%s%s%s.lock", conf.work_dir.c_str(), DIR_DELIM,
conf.storage_name.c_str());
rc = exclusive_fopen(lockfile, &changer_lock);
if (rc == EEXIST && timeout == 0) {
/* timeout=0 means do not wait */
verr.SetErrorWithErrno(rc, "cannot open lockfile");
log.Error("ERROR! %s", verr.GetErrorMsg());
return -1;
}
while (rc == EEXIST) {
/* sleep before trying again */
sleep(1);
if (time(NULL) > timeout) {
verr.SetError(EBUSY, "timeout waiting for lockfile");
log.Error("ERROR! %s", verr.GetErrorMsg());
return EACCES;
}
rc = exclusive_fopen(lockfile, &changer_lock);
}
if (rc) {
verr.SetErrorWithErrno(rc, "cannot open lockfile");
log.Error("ERROR! %s", verr.GetErrorMsg());
return -1;
}
/* Write PID to lockfile and leave open for exclusive R/W */
fprintf(changer_lock, "%d", getpid());
fflush(changer_lock);
log.Debug("created lockfile for pid %d", getpid());
return 0;
}
/*-------------------------------------------------
* Protected method to unlock changer device
*------------------------------------------------*/
void DiskChanger::Unlock()
{
char lockfile[4096];
if (!changer_lock) return;
fclose(changer_lock);
changer_lock = NULL;
snprintf(lockfile, sizeof(lockfile), "%s%s%s.lock", conf.work_dir.c_str(), DIR_DELIM,
conf.storage_name.c_str());
log.Debug("removing lockfile for pid %d", getpid());
unlink(lockfile);
}
/*-------------------------------------------------
* Method returns true if magazine is not mounted,
* else returns false.

View File

@ -2,7 +2,7 @@
*
* This file is part of vchanger by Josh Fisher.
*
* vchanger copyright (C) 2008-2015 Josh Fisher
* vchanger copyright (C) 2008-2020 Josh Fisher
*
* vchanger is free software.
* You may redistribute it and/or modify it under the terms of the
@ -30,13 +30,12 @@
class DiskChanger
{
public:
DiskChanger() : changer_lock(NULL), needs_update(false), needs_label(false) {}
virtual ~DiskChanger();
DiskChanger() : needs_update(false), needs_label(false) {}
virtual ~DiskChanger() {};
int Initialize();
int LoadDrive(int drv, int slot);
int UnloadDrive(int drv);
int CreateVolumes(int bay, int count, int start = -1, const char *label_prefix = "");
int UpdateBacula();
const char* GetVolumeLabel(int slot);
const char* GetVolumePath(tString &fname, int slot);
bool MagazineEmpty(int bay) const;
@ -54,8 +53,6 @@ public:
inline const char* GetErrorMsg() const { return verr.GetErrorMsg(); }
inline bool NeedsUpdate() const { return needs_update; }
inline bool NeedsLabel() const { return needs_label; }
int Lock(long timeout = 30);
void Unlock();
protected:
void InitializeMagazines();
int FindEmptySlotRange(int count);
@ -67,7 +64,6 @@ protected:
int SaveDriveState(int drv);
int RestoreDriveState(int drv);
protected:
FILE *changer_lock;
bool needs_update;
bool needs_label;
ErrorHandler verr;

View File

@ -1,6 +1,6 @@
/* loghandler.cpp
*
* Copyright (C) 2013-2014 Josh Fisher
* Copyright (C) 2013-2018 Josh Fisher
*
* This program is free software. You may redistribute it and/or modify
* it under the terms of the GNU General Public License, as published by
@ -22,6 +22,9 @@
#ifdef HAVE_STDIO_H
#include <stdio.h>
#endif
#ifdef HAVE_UNISTD_H
#include <unistd.h>
#endif
#ifdef HAVE_TIME_H
#include <time.h>
#endif
@ -34,13 +37,11 @@
#ifdef HAVE_STDARG_H
#include <stdarg.h>
#endif
#ifndef HAVE_LOCALTIME_R
#include "compat/localtime_r.h"
#endif
#define LOGHANDLER_SOURCE 1
#include "loghandler.h"
LogHandler log;
LogHandler vlog;
LogHandler::LogHandler() : use_syslog(false), max_debug_level(LOG_WARNING), errfs(stderr)
{
@ -179,7 +180,7 @@ void LogHandler::WriteLog(int priority, const char *fmt, va_list vl)
size_t n;
struct tm bt;
time_t t;
char buf[1024];
char ftim[128], buf[4096];
Lock();
if (priority > max_debug_level || priority < LOG_EMERG || !fmt) {
Unlock();
@ -187,8 +188,8 @@ void LogHandler::WriteLog(int priority, const char *fmt, va_list vl)
}
t = time(NULL);
localtime_r(&t, &bt);
strftime(buf, 100, "%b %d %T: ", &bt);
strncpy(buf + strlen(buf), fmt, sizeof(buf) - strlen(buf));
strftime(ftim, 100, "%b %d %T: ", &bt);
snprintf(buf, sizeof(buf), "%s [%d]: %s", ftim, getpid(), fmt);
if (use_syslog) vsyslog(priority, buf, vl);
else {
n = strlen(buf);
@ -204,3 +205,17 @@ void LogHandler::WriteLog(int priority, const char *fmt, va_list vl)
}
Unlock();
}
/***************************************************************************************
* C wrapper to write to LogHandler object
***************************************************************************************/
extern "C" void LogHandler_write(int level, const char *fmt, ...)
{
va_list vl;
va_start(vl, fmt);
vlog.WriteLog(level, fmt, vl);
va_end(vl);
}

View File

@ -1,6 +1,6 @@
/* loghandler.h
*
* Copyright (C) 2013-2014 Josh Fisher
* Copyright (C) 2013-2018 Josh Fisher
*
* This program is free software. You may redistribute it and/or modify
* it under the terms of the GNU General Public License, as published by
@ -32,6 +32,15 @@
#include <pthread.h>
#endif
#ifdef __cplusplus
extern "C" {
#endif
void LogHandler_write(int level, const char *format, ...);
#ifdef __cplusplus
}
#endif
#ifdef __cplusplus
class LogHandler
{
public:
@ -50,6 +59,7 @@ public:
void Debug(const char *fmt, ... );
void MajorDebug(const char *fmt, ... );
inline bool UsingSyslog() { return use_syslog; }
friend void LogHandler_write(int level, const char *format, ...);
protected:
void Lock();
void Unlock();
@ -64,7 +74,8 @@ protected:
};
#ifndef LOGHANDLER_SOURCE
extern LogHandler log;
extern LogHandler vlog;
#endif
#endif
#endif /* _LOGHANDLER_H_ */

131
src/mymutex.cpp Normal file
View File

@ -0,0 +1,131 @@
/* mymutex.cpp
*
* Copyright (C) 2017-2020 Josh Fisher
*
* This program is free software. You may redistribute it and/or modify
* it under the terms of the GNU General Public License, as published by
* the Free Software Foundation; either version 2, or (at your option)
* any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. See the file "COPYING". If not,
* see <http://www.gnu.org/licenses/>.
*/
#include "config.h"
#ifdef HAVE_WINDOWS_H
#include "targetver.h"
#include <windows.h>
#include "win32_util.h"
#endif
#include <stdio.h>
#include <stdlib.h>
#ifdef HAVE_STRING_H
#include <string.h>
#endif
#ifdef HAVE_STDINT_H
#include <stdint.h>
#endif
#ifdef HAVE_LIMITS_H
#include <limits.h>
#endif
#ifdef HAVE_UNISTD_H
#include <unistd.h>
#endif
#ifdef HAVE_ERRNO_H
#include <errno.h>
#endif
#ifdef HAVE_TIME_H
#include <time.h>
#endif
#ifdef HAVE_SYS_TIME_H
#include <sys/time.h>
#endif
#ifdef HAVE_SYS_STAT_H
#include <sys/stat.h>
#endif
#ifdef HAVE_SYS_WAIT_H
#include <sys/wait.h>
#endif
#ifdef HAVE_FCNTL_H
#include <fcntl.h>
#endif
#ifdef HAVE_SEMAPHORE_H
#include <semaphore.h>
#endif
#include "compat/semaphore.h"
#include "loghandler.h"
#include "mypopen.h"
/*
* Function to create a mutex owned by the caller. Waits up to max_wait seconds
* for the mutex to be created. If max_wait is negative, waits indefinitely. If
* max_wait is zero, tries once to create mutex and does not block.
* On success, returns the handle of a named mutex. On error, returns zero and
* sets errno appropriately.
*/
void* mymutex_create(const char *storage_name)
{
char lockname[4096];
if (!storage_name || !storage_name[0]) {
/* Only create named mutex */
errno = EINVAL;
return 0;
}
#ifdef HAVE_WINDOWS_H
snprintf(lockname, sizeof(lockname), "vchanger-%s", storage_name);
#else
snprintf(lockname, sizeof(lockname), "/vchanger-%s", storage_name);
#endif
return (void*)sem_open(lockname, O_CREAT, 0770, 1);
}
/*
* Function to lock an opened mutex given by fd.
* On success, returns zero. On error, returns -1 and
* sets errno appropriately.
*/
int mymutex_lock(void *fd, time_t wait_sec)
{
struct timespec ts;
if (wait_sec == 0) return sem_trywait((sem_t*)fd);
ts.tv_sec = time(NULL) + wait_sec; /* semaphore.h functions use absolute time */
ts.tv_nsec = 0;
return sem_timedwait((sem_t*)fd, &ts);
}
/*
* Function to unlock an opened mutex given by fd.
* On success, returns zero. On error, returns -1 and
* sets errno appropriately.
*/
int mymutex_unlock(void *fd)
{
return sem_post((sem_t*)fd);
}
/*
* Function to destroy a mutex owned by the caller.
* On success, returns zero. On error, returns -1 and
* sets errno appropriately.
*/
void mymutex_destroy(const char *name, void *fd)
{
if (fd) {
sem_post((sem_t*)fd);
sem_close((sem_t*)fd);
}
if (name && name[0]) sem_unlink(name);
}

32
src/mymutex.h Normal file
View File

@ -0,0 +1,32 @@
/* mymutex.h
*
* Copyright (C) 2017-2020 Josh Fisher
*
* This program is free software. You may redistribute it and/or modify
* it under the terms of the GNU General Public License, as published by
* the Free Software Foundation; either version 2, or (at your option)
* any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. See the file "COPYING". If not,
* see <http://www.gnu.org/licenses/>.
*/
#ifndef _MYMUTEX_H_
#define _MYMUTEX_H_ 1
#ifndef HAVE_TIME_H
#include <time.h>
#endif
void* mymutex_create(const char *storage_name);
int mymutex_lock(void* fd, time_t wait_sec);
int mymutex_unlock(void* fd);
int mymutex_destroy(const char *storage_name, void* fd);
#endif /* _MYPOPEN_H_ */

View File

@ -179,7 +179,7 @@ static int do_mypopen_raw(const char *cline, int *fno_stdin, int *fno_stdout, in
/* Build argv array from command line string */
if (mypopen_args(cline, argv, argc, argbuf, sizeof(argbuf))) {
/* Invalid args, so terminate child */
log.Debug("popen: invalid cmdline args for child");
vlog.Debug("popen: invalid cmdline args for child");
errno = EINVAL;
return -1;
}
@ -193,7 +193,7 @@ static int do_mypopen_raw(const char *cline, int *fno_stdin, int *fno_stdout, in
/* error creating pipe */
return -1;
}
log.Debug("popen: child stdin uses pipe (%d -> %d)", pipe_in[0], pipe_in[1]);
vlog.Debug("popen: child stdin uses pipe (%d -> %d)", pipe_in[0], pipe_in[1]);
} else if (*fno_stdin == STDIN_FILENO) {
/* Caller specified stdin so just let child inherit it */
fno_stdin = NULL;
@ -212,7 +212,7 @@ static int do_mypopen_raw(const char *cline, int *fno_stdin, int *fno_stdout, in
errno = rc;
return -1;
}
log.Debug("popen: child stdout uses pipe (%d -> %d)", pipe_out[0], pipe_out[1]);
vlog.Debug("popen: child stdout uses pipe (%d -> %d)", pipe_out[0], pipe_out[1]);
} else {
if (*fno_stdout == STDOUT_FILENO) fno_stdout = NULL;
else fsync(*fno_stdout);
@ -231,7 +231,7 @@ static int do_mypopen_raw(const char *cline, int *fno_stdin, int *fno_stdout, in
errno = rc;
return -1;
}
log.Debug("popen: child stderr uses pipe (%d -> %d)", pipe_err[0], pipe_err[1]);
vlog.Debug("popen: child stderr uses pipe (%d -> %d)", pipe_err[0], pipe_err[1]);
} else {
if (*fno_stderr == STDERR_FILENO) fno_stderr = NULL;
else fsync(*fno_stderr);
@ -239,7 +239,7 @@ static int do_mypopen_raw(const char *cline, int *fno_stdin, int *fno_stdout, in
}
/* fork a child process to run the command in */
log.Debug("popen: forking now");
vlog.Debug("popen: forking now");
pid = fork();
switch (pid)
{
@ -251,7 +251,7 @@ static int do_mypopen_raw(const char *cline, int *fno_stdin, int *fno_stdout, in
case 0: /* child is running */
/* close pipe ends always used by parent */
log.Debug("popen: child closing pipe ends %d,%d,%d used by parent", pipe_in[1], pipe_out[0], pipe_err[0]);
vlog.Debug("popen: child closing pipe ends %d,%d,%d used by parent", pipe_in[1], pipe_out[0], pipe_err[0]);
if (pipe_in[1] >= 0) close(pipe_in[1]);
if (pipe_out[0] >= 0) close(pipe_out[0]);
if (pipe_err[0] >= 0) close(pipe_err[0]);
@ -259,7 +259,7 @@ static int do_mypopen_raw(const char *cline, int *fno_stdin, int *fno_stdout, in
if (fno_stdin) {
if (*fno_stdin < 0) {
/* Read end of pipe will be child's stdin */
log.Debug("popen: child will read stdin from %d", pipe_in[0]);
vlog.Debug("popen: child will read stdin from %d", pipe_in[0]);
dup2(pipe_in[0], STDIN_FILENO);
close(pipe_in[0]);
} else {
@ -272,7 +272,7 @@ static int do_mypopen_raw(const char *cline, int *fno_stdin, int *fno_stdout, in
if (fno_stdout) {
if (*fno_stdout < 0) {
/* Write end of pipe will be child's stdout */
log.Debug("popen: child will write stdout to %d", pipe_out[1]);
vlog.Debug("popen: child will write stdout to %d", pipe_out[1]);
dup2(pipe_out[1], STDOUT_FILENO);
close(pipe_out[1]);
} else {
@ -285,7 +285,7 @@ static int do_mypopen_raw(const char *cline, int *fno_stdin, int *fno_stdout, in
if (fno_stderr) {
if (*fno_stderr < 0) {
/* Write end of pipe will be child's stderr */
log.Debug("popen: child will write stderr to %d", pipe_err[1]);
vlog.Debug("popen: child will write stderr to %d", pipe_err[1]);
dup2(pipe_err[1], STDERR_FILENO);
close(pipe_err[1]);
} else {
@ -295,7 +295,7 @@ static int do_mypopen_raw(const char *cline, int *fno_stdin, int *fno_stdout, in
}
}
/* now run the command */
log.Debug("popen: child executing '%s'", argv[0]);
vlog.Debug("popen: child executing '%s'", argv[0]);
execvp(argv[0], argv);
/* only gets here if execvp fails */
return -1;
@ -304,7 +304,7 @@ static int do_mypopen_raw(const char *cline, int *fno_stdin, int *fno_stdout, in
/* parent is running this */
/* close pipe ends always used by child */
log.Debug("popen: parent closing pipe ends %d,%d,%d used by child", pipe_in[0], pipe_out[1], pipe_err[1]);
vlog.Debug("popen: parent closing pipe ends %d,%d,%d used by child", pipe_in[0], pipe_out[1], pipe_err[1]);
if (pipe_in[0] >= 0) close(pipe_in[0]);
if (pipe_out[1] >= 0) close(pipe_out[1]);
if (pipe_err[1] >= 0) close(pipe_err[1]);
@ -314,25 +314,25 @@ static int do_mypopen_raw(const char *cline, int *fno_stdin, int *fno_stdout, in
if (*fno_stdin < 0) {
/* Caller will be writing to child's stdin through pipe */
*fno_stdin = pipe_in[1];
log.Debug("popen: parent writes child's stdin to %d", pipe_in[1]);
vlog.Debug("popen: parent writes child's stdin to %d", pipe_in[1]);
}
}
if (fno_stdout) {
if (*fno_stdout < 0) {
/* Caller will be reading from child's stdout through pipe */
*fno_stdout = pipe_out[0];
log.Debug("popen: parent reads child's stdout from %d", pipe_out[0]);
vlog.Debug("popen: parent reads child's stdout from %d", pipe_out[0]);
}
}
if (fno_stderr) {
if (*fno_stderr < 0) {
/* Caller will be reading from child's stderr through pipe */
*fno_stderr = pipe_err[0];
log.Debug("popen: parent reads child's stderr from %d", pipe_err[0]);
vlog.Debug("popen: parent reads child's stderr from %d", pipe_err[0]);
}
}
//sleep(2);
log.Debug("popen: parent returning pid=%d of child", pid);
vlog.Debug("popen: parent returning pid=%d of child", pid);
return pid;
}
@ -360,7 +360,7 @@ static int do_mypopen_raw(const char *cline, int *fno_stdin, int *fno_stdout, in
/* Build argv array from command line string */
if (mypopen_args(cline, argv, argc, argbuf, sizeof(argbuf))) {
/* Invalid args, so terminate child */
log.Debug("popen: invalid cmdline args for child");
vlog.Debug("popen: invalid cmdline args for child");
errno = EINVAL;
return -1;
}
@ -375,7 +375,7 @@ static int do_mypopen_raw(const char *cline, int *fno_stdin, int *fno_stdout, in
return -1;
}
child_in = pipe_in[0];
log.Debug("popen: child stdin uses pipe (%d -> %d)", pipe_in[0], pipe_in[1]);
vlog.Debug("popen: child stdin uses pipe (%d -> %d)", pipe_in[0], pipe_in[1]);
} else {
if (*fno_stdin != STDIN_FILENO) {
/* Caller supplied an open file to use as child's stdin */
@ -397,7 +397,7 @@ static int do_mypopen_raw(const char *cline, int *fno_stdin, int *fno_stdout, in
return -1;
}
child_out = pipe_out[1];
log.Debug("popen: child stdout uses pipe (%d -> %d)", pipe_out[0], pipe_out[1]);
vlog.Debug("popen: child stdout uses pipe (%d -> %d)", pipe_out[0], pipe_out[1]);
} else {
if (*fno_stdout != STDOUT_FILENO) {
/* Caller supplied open file to use as child's stdout */
@ -419,7 +419,7 @@ static int do_mypopen_raw(const char *cline, int *fno_stdin, int *fno_stdout, in
return -1;
}
child_err = pipe_err[1];
log.Debug("popen: child stderr uses pipe (%d -> %d)", pipe_err[0], pipe_err[1]);
vlog.Debug("popen: child stderr uses pipe (%d -> %d)", pipe_err[0], pipe_err[1]);
} else {
if (*fno_stderr != STDERR_FILENO) {
/* Caller supplied an open file to use as child's stderr */
@ -547,7 +547,7 @@ static int do_mypopen_raw(const char *cline, int *fno_stdin, int *fno_stdout, in
if (pipe_out[0] >= 0) *fno_stdout = pipe_out[0];
if (pipe_err[0] >= 0) *fno_stderr = pipe_err[0];
log.Debug("popen: parent returning pid=%d of child", pid);
vlog.Debug("popen: parent returning pid=%d of child", pid);
return pid;
}

View File

@ -2,7 +2,7 @@
*
* This file is part of vchanger by Josh Fisher.
*
* vchanger copyright (C) 2008-2013 Josh Fisher
* vchanger copyright (C) 2008-2018 Josh Fisher
*
* vchanger is free software.
* You may redistribute it and/or modify it under the terms of the
@ -41,6 +41,7 @@
#endif
#include "uuidlookup.h"
#include "loghandler.h"
#ifdef HAVE_WINDOWS_H
@ -230,6 +231,7 @@ static int GetDevMountpoint(char *mountp, size_t mountp_sz, const char *devname)
*/
static int GetDevMountpoint(char *mountp, size_t mountp_sz, const char *devname)
{
LogHandler_write(LOG_ERROR, "build does not support getmntent() or getfsstat() calls");
return -1;
}
@ -256,8 +258,8 @@ int GetMountpointFromUUID(char *mountp, size_t mountp_sz, const char *uuid_str)
struct udev_list_entry *devices, *dev_list_entry;
struct udev_device *dev;
int rc = -3;
const char *dev_name, *path, *uuid;
size_t n, pos, dev_name_len;
const char *dev_name, *dev_links, *path, *uuid;
size_t n, pos, dev_links_len;
char devlink[4096];
if (!mountp || !mountp_sz) return -2;
@ -280,29 +282,50 @@ int GetMountpointFromUUID(char *mountp, size_t mountp_sz, const char *uuid_str)
dev_name = udev_device_get_property_value(dev, "DEVNAME");
if (dev_name == NULL) {
/* Failed to get kernel device node */
LogHandler_write(LOG_DEBUG, "filesystem %s has no udev assigned device node",
uuid_str);
break;
}
LogHandler_write(LOG_DEBUG, "filesystem %s has udev assigned device %s",
uuid_str, dev_name);
/* Lookup mountpoint of the kernel device node */
rc = GetDevMountpoint(mountp, mountp_sz, dev_name);
if (rc == 0) break;
/* If not mounted as the DEVNAME, also check if mounted as
* a device alias name from DEVLINKS */
dev_name = udev_device_get_property_value(dev, "DEVLINKS");
if (dev_name == NULL) {
/* Failed to get device alias links */
if (rc == 0) {
/* Found mountpoint */
LogHandler_write(LOG_DEBUG, "filesystem %s (device %s) mounted at %s", uuid_str, dev_name, mountp);
break;
}
dev_name_len = strlen(dev_name);
if (rc == -4) {
/* If not mounted as the DEVNAME, also check if mounted as
* a device alias name from DEVLINKS */
dev_links = udev_device_get_property_value(dev, "DEVLINKS");
if (dev_links == NULL) {
/* No device alias links found */
break;
}
LogHandler_write(LOG_DEBUG, "device %s not found in system mounts, searching all udev device aliases",
dev_name);
/* For each device alias, look for a mountpoint */
dev_links_len = strlen(dev_links);
pos = 0;
while (rc == -4 && pos < dev_name_len) {
for (n = pos; n < dev_name_len && !isblank(dev_name[n]); n++) ;
while (rc == -4 && pos < dev_links_len) {
for (n = pos; n < dev_links_len && !isblank(dev_links[n]); n++) ;
n -= pos;
memmove(devlink, dev_name + pos, n);
memmove(devlink, dev_links + pos, n);
devlink[n] = 0;
rc = GetDevMountpoint(mountp, mountp_sz, devlink);
pos += n;
while (pos < dev_name_len && isblank(dev_name[pos])) ++pos;
if (rc == 0) {
/* Device alias is mounted */
LogHandler_write(LOG_DEBUG, "filesystem %s (device %s) mounted at %s", uuid_str, devlink,
mountp);
break;
}
rc = -4; /* Ignore other errors from attempt to get alias's mountpoint */
pos += n;
while (pos < dev_links_len && isblank(dev_links[pos])) ++pos;
}
}
if (rc == -4) LogHandler_write(LOG_DEBUG, "filesystem %s (device %s) not mounted", uuid_str, dev_name);
break;
}
}
@ -340,7 +363,11 @@ int GetMountpointFromUUID(char *mountp, size_t mountp_sz, const char *uuid_str)
#else
dev_name = blkid_get_devname(NULL, "UUID", uuid_str);
#endif
if (!dev_name) return -3; /* no device with UUID found */
if (!dev_name) {
LogHandler_write(LOG_DEBUG, "filesystem %s not found", uuid_str);
return -3;
}
LogHandler_write(LOG_DEBUG, "libblkid found filesystem %s at device %s", uuid_str, dev_name);
/* find mount point for device */
rc = GetDevMountpoint(mountp, mountp_sz, dev_name);
@ -355,6 +382,7 @@ int GetMountpointFromUUID(char *mountp, size_t mountp_sz, const char *uuid_str)
*/
int GetMountpointFromUUID(char *mountp, size_t mountp_sz, const char *uuid_str)
{
LogHandler_write(LOG_DEBUG, "GetMountpointFromUUID: UUID lookups not supported by this build");
return -1;
}

View File

@ -2,7 +2,7 @@
*
* This file is part of the vchanger package
*
* vchanger copyright (C) 2008-2015 Josh Fisher
* vchanger copyright (C) 2008-2020 Josh Fisher
*
* vchanger is free software.
* You may redistribute it and/or modify it under the terms of the
@ -31,6 +31,9 @@
#ifdef HAVE_STDINT_H
#include <stdint.h>
#endif
#ifdef HAVE_ERRNO_H
#include <errno.h>
#endif
#ifdef HAVE_GETOPT_H
#include <getopt.h>
#endif
@ -53,7 +56,10 @@
#include "util.h"
#include "compat_defs.h"
#include "loghandler.h"
#include "errhandler.h"
#include "diskchanger.h"
#include "mymutex.h"
#include "bconsole.h"
DiskChanger changer;
@ -80,6 +86,7 @@ typedef struct _cmdparams_s
{
bool print_version;
bool print_help;
bool force;
int command;
int slot;
int drive;
@ -115,12 +122,15 @@ static void print_help(void)
" changer defined by vchanger configuration file\n"
" 'config_file' using 'slot', 'device', and 'drive'\n"
" vchanger [options] config_file LISTMAGS\n"
" vchanger extension to list info on all defined magazines.\n"
" API extension to list info on all defined magazines.\n"
" vchanger [options] config_file CREATEVOLS mag_ndx count [start] [CREATEVOLS options]\n"
" vchanger extension to create 'count' empty volume files on the magazine at\n"
" index 'mag_ndx'. If specified, 'start' is the lowest integer to use when\n"
" API extension to create 'count' empty volume files on the magazine at\n"
" index 'mag_ndx'. If specified, 'start' is the lowest integer to use in\n"
" appending integers to the label prefix when generating volume names.\n"
" vchanger [options] config_file REFRESH\n"
" API extension to issue an Update Slots command in bconsole if a change\n"
" in the virtual slot to volume file mapping is detected. The --force flag\n"
" forces the bconsole call regardless detected changes.\n"
" vchanger --version\n"
" print version info\n"
" vchanger --help\n"
@ -131,13 +141,13 @@ static void print_help(void)
"\nCREATEVOLS command options:\n"
" -l, --label=string string to use as a prefix for determining the\n"
" barcode label of the volume files created. Labels\n"
" will be of the form 'string'_N, where N is an\n"
" integer. By default the prefix will be generated\n"
" using the changer name and the position of the\n"
" magazine's declaration in the configuration file.\n"
" --pool=string Overrides the default pool, defined in the vchanger\n"
" config file, that new volumes should be placed into\n"
" when labeling newly created volumes.\n"
" will be of the form 'string'N, where N is a\n"
" 4 digit integer with leading zeros. The magazine\n"
" name is used as the prefix string by default.\n"
" --pool=string Overrides the default pool that new volumes should\n"
" be placed into when labeling newly created volumes.\n"
"\nREFRESH command options:\n"
" --force Force a bconsole update slots command to be invoked\n"
"\nReport bugs to %s.\n", PACKAGE_BUGREPORT);
}
@ -147,6 +157,7 @@ static void print_help(void)
#define LONGONLYOPT_VERSION 0
#define LONGONLYOPT_HELP 1
#define LONGONLYOPT_POOL 2
#define LONGONLYOPT_FORCE 3
static int parse_cmdline(int argc, char *argv[])
{
@ -158,10 +169,12 @@ static int parse_cmdline(int argc, char *argv[])
{ "group", 1, 0, 'g' },
{ "label", 1, 0, 'l' },
{ "pool", 1, 0, LONGONLYOPT_POOL },
{ "force", 0, 0, LONGONLYOPT_FORCE },
{ 0, 0, 0, 0 } };
cmdl.print_version = false;
cmdl.print_help = false;
cmdl.force = false;
cmdl.command = 0;
cmdl.slot = 0;
cmdl.drive = 0;
@ -198,6 +211,9 @@ static int parse_cmdline(int argc, char *argv[])
case LONGONLYOPT_POOL:
cmdl.pool = optarg;
break;
case LONGONLYOPT_FORCE:
cmdl.force = true;
break;
default:
fprintf(stderr, "unknown option %s\n", optarg);
return -1;
@ -237,6 +253,11 @@ static int parse_cmdline(int argc, char *argv[])
fprintf(stderr, "flag --pool not valid for this command\n");
return -1;
}
/* Make sure only REFRESH command has --force flag */
if (cmdl.force && cmdl.command != CMD_REFRESH) {
fprintf(stderr, "flag --force not valid for this command\n");
return -1;
}
/* Check param 3 exists */
++ndx;
if (ndx >= argc) {
@ -378,7 +399,7 @@ static int do_list_cmd()
fprintf(stdout, "%d:%s\n", slot, changer.GetVolumeLabel(slot));
}
}
log.Info(" SUCCESS sent list to stdout");
vlog.Info(" SUCCESS sent list to stdout");
return 0;
}
@ -390,7 +411,7 @@ static int do_list_cmd()
static int do_slots_cmd()
{
fprintf(stdout, "%d\n", changer.NumSlots());
log.Info(" SUCCESS reporting %d slots", changer.NumSlots());
vlog.Info(" SUCCESS reporting %d slots", changer.NumSlots());
return 0;
}
@ -403,10 +424,10 @@ static int do_load_cmd()
{
if (changer.LoadDrive(cmdl.drive, cmdl.slot)) {
fprintf(stderr, "%s\n", changer.GetErrorMsg());
log.Error(" ERROR loading slot %d into drive %d", cmdl.slot, cmdl.drive);
vlog.Error(" ERROR loading slot %d into drive %d", cmdl.slot, cmdl.drive);
return 1;
}
log.Info(" SUCCESS loading slot %d into drive %d", cmdl.slot, cmdl.drive);
vlog.Info(" SUCCESS loading slot %d into drive %d", cmdl.slot, cmdl.drive);
return 0;
}
@ -419,10 +440,10 @@ static int do_unload_cmd()
{
if (changer.UnloadDrive(cmdl.drive)) {
fprintf(stderr, "%s\n", changer.GetErrorMsg());
log.Error(" ERROR unloading slot %d from drive %d", cmdl.slot, cmdl.drive);
vlog.Error(" ERROR unloading slot %d from drive %d", cmdl.slot, cmdl.drive);
return 1;
}
log.Info(" SUCCESS unloading slot %d from drive %d", cmdl.slot, cmdl.drive);
vlog.Info(" SUCCESS unloading slot %d from drive %d", cmdl.slot, cmdl.drive);
return 0;
}
@ -437,10 +458,11 @@ static int do_loaded_cmd()
int slot = changer.GetDriveSlot(cmdl.drive);
if (slot < 0) slot = 0;
fprintf(stdout, "%d\n", slot);
log.Info(" SUCCESS reporting drive %d loaded from slot %d", cmdl.drive, slot);
vlog.Info(" SUCCESS reporting drive %d loaded from slot %d", cmdl.drive, slot);
return 0;
}
/*-------------------------------------------------
* LISTALL Command
* Prints state of drives (loaded or empty), followed by state
@ -471,7 +493,7 @@ static int do_list_all()
fprintf(stdout, "S:%d:E\n", n);
}
}
log.Info(" SUCCESS sent listall to stdout");
vlog.Info(" SUCCESS sent listall to stdout");
return 0;
}
@ -487,7 +509,7 @@ static int do_list_magazines()
if (changer.NumMagazines() == 0) {
fprintf(stdout, "No magazines are defined\n");
log.Info(" SUCCESS no magazines are defined");
vlog.Info(" SUCCESS no magazines are defined");
return 0;
}
for (n = 0; n < changer.NumMagazines(); n++) {
@ -498,10 +520,11 @@ static int do_list_magazines()
changer.GetMagazineStartSlot(n), changer.GetMagazineMountpoint(n));
}
}
log.Info(" SUCCESS listing magazine info to stdout");
vlog.Info(" SUCCESS listing magazine info to stdout");
return 0;
}
/*-------------------------------------------------
* CREATEVOLS (Create Volumes) Command
* Creates volume files on the specified magazine
@ -511,12 +534,12 @@ static int do_create_vols()
/* Create new volume files on magazine */
if (changer.CreateVolumes(cmdl.mag_bay, cmdl.count, cmdl.slot, cmdl.label_prefix.c_str())) {
fprintf(stderr, "%s\n", changer.GetErrorMsg());
log.Error(" ERROR");
vlog.Error(" ERROR: %s", changer.GetErrorMsg());
return -1;
}
fprintf(stdout, "Created %d volume files on magazine %d\n",
cmdl.count, cmdl.mag_bay);
log.Info(" SUCCESS");
vlog.Info(" SUCCESS");
return 0;
}
@ -529,13 +552,14 @@ int main(int argc, char *argv[])
int rc;
FILE *fs = NULL;
int32_t error_code;
void *command_mux = NULL, *bconsole_mux = NULL;
#ifdef HAVE_LOCALE_H
setlocale(LC_ALL, "");
#endif
/* Log initially to stderr */
log.OpenLog(stderr, LOG_ERR);
vlog.OpenLog(stderr, LOG_ERR);
/* parse the command line */
if ((error_code = parse_cmdline(argc, argv)) != 0) {
print_help();
@ -551,6 +575,7 @@ int main(int argc, char *argv[])
print_help();
return 0;
}
/* Read vchanger config file */
if (!conf.Read(cmdl.config_file)) {
return 1;
@ -573,89 +598,137 @@ int main(int argc, char *argv[])
fprintf(stderr, "Error opening opening log file\n");
return 1;
}
log.OpenLog(fs, conf.log_level);
vlog.OpenLog(fs, conf.log_level);
}
/* Validate and commit configuration parameters */
if (!conf.Validate()) {
fprintf(stderr, "ERROR! configuration file error\n");
return 1;
}
#ifndef HAVE_WINDOWS_H
/* Ignore SIGPIPE signals */
signal(SIGPIPE, SIG_IGN);
#endif
/* Initialize changer. A lock file is created to serialize access
/* Open/create named mutex */
command_mux = mymutex_create("vchanger-command");
if (command_mux == 0) {
vlog.Error("ERROR! failed to create named mutex errno=%d", errno);
fprintf(stderr, "ERROR! failed to create named mutex errno=%d\n", errno);
return 1;
}
/* Lock mutex to perform command */
if (mymutex_lock(command_mux, 300)) {
vlog.Error("ERROR! failed to lock named mutex errno=%d", errno);
fprintf(stderr, "ERROR! failed to lock named mutex errno=%d\n", errno);
mymutex_destroy("vchanger-command", command_mux);
return 1;
}
/* Initialize changer. A named mutex is created to serialize access
* to the changer. As a result, changer initialization may block
* for up to 30 seconds, and may fail if a timeout is reached */
if (changer.Initialize()) {
vlog.Error("%s", changer.GetErrorMsg());
fprintf(stderr, "%s\n", changer.GetErrorMsg());
mymutex_destroy("vchanger-command", command_mux);
return 1;
}
/* Perform command */
switch (cmdl.command) {
case CMD_LIST:
log.Debug("==== preforming LIST command pid=%d", getpid());
vlog.Debug("==== preforming LIST command");
error_code = do_list_cmd();
break;
case CMD_SLOTS:
log.Debug("==== preforming SLOTS command pid=%d", getpid());
vlog.Debug("==== preforming SLOTS command");
error_code = do_slots_cmd();
break;
case CMD_LOAD:
log.Debug("==== preforming LOAD command pid=%d", getpid());
vlog.Debug("==== preforming LOAD command");
error_code = do_load_cmd();
break;
case CMD_UNLOAD:
log.Debug("==== preforming UNLOAD command pid=%d", getpid());
vlog.Debug("==== preforming UNLOAD command");
error_code = do_unload_cmd();
break;
case CMD_LOADED:
log.Debug("==== preforming LOADED command pid=%d", getpid());
vlog.Debug("==== preforming LOADED command");
error_code = do_loaded_cmd();
break;
case CMD_LISTALL:
log.Debug("==== preforming LISTALL command pid=%d", getpid());
vlog.Debug("==== preforming LISTALL command");
error_code = do_list_all();
break;
case CMD_LISTMAGS:
log.Debug("==== preforming LISTMAGS command pid=%d", getpid());
vlog.Debug("==== preforming LISTMAGS command");
error_code = do_list_magazines();
break;
case CMD_CREATEVOLS:
log.Debug("==== preforming CREATEVOLS command pid=%d", getpid());
vlog.Debug("==== preforming CREATEVOLS command");
error_code = do_create_vols();
break;
case CMD_REFRESH:
log.Debug("==== preforming REFRESH command pid=%d", getpid());
vlog.Debug("==== preforming REFRESH command");
error_code = 0;
log.Info(" SUCCESS pid=%d", getpid());
break;
}
changer.Unlock();
/* If there was an error, then exit */
if (error_code) return error_code;
if (error_code) {
mymutex_destroy("vchanger-command", command_mux);
return error_code;
}
/* If not updating Bacula, then exit */
#ifdef HAVE_WINDOWS_H
conf.bconsole = ""; /* Issuing bconsole commands not implemented on Windows */
#endif
if (conf.bconsole.empty()) {
/* Bacula interaction via bconsole is disabled, so log warnings */
if (changer.NeedsUpdate())
log.Error("WARNING! 'update slots' needed in bconsole pid=%d", getpid());
vlog.Error("WARNING! 'update slots' needed in bconsole pid=%d", getpid());
if (changer.NeedsLabel())
log.Error("WARNING! 'label barcodes' needed in bconsole pid=%d", getpid());
vlog.Error("WARNING! 'label barcodes' needed in bconsole pid=%d", getpid());
mymutex_destroy("vchanger-command", command_mux);
return 0;
}
/* Update Bacula via bconsole */
#ifndef HAVE_WINDOWS_H
changer.UpdateBacula();
#else
/* Auto-update of bacula not working for Windows */
if (changer.NeedsUpdate())
log.Error("WARNING! 'update slots' needed in bconsole");
if (changer.NeedsLabel())
log.Error("WARNING! 'label barcodes' needed in bconsole");
#endif
/* Create named mutex to prevent further bconsole commands when bconsole
* commands have already been initiated */
bconsole_mux = mymutex_create("vchanger-bconsole");
if (bconsole_mux == 0) {
vlog.Error("ERROR! failed to create named mutex errno=%d", errno);
fprintf(stderr, "ERROR! failed to create named mutex errno=%d\n", errno);
mymutex_destroy("vchanger-command", command_mux);
return 1;
}
/* Lock mutex to perform command */
if (mymutex_lock(bconsole_mux, 0)) {
/* If bconsole mutex is locked because another instance has previously invoked
* bconsole, then this instance is the result of bconsole itself invoking
* additional vchanger processes to handle the previous instance's bconsole
* command. So tto prevent a race condition, this instance must not invoke
* further bconsole processes. */
vlog.Info("invoked from bconsole - skipping further bconsole commands", errno);
mymutex_destroy("vchanger-bconsole", bconsole_mux);
mymutex_destroy("vchanger-command", command_mux);
return 0;
}
/* Unlock the command mutex long enough to issue bconsole commands.
* Note that the bconsole mutex is left locked to prevent a race condition
* should the invoked bconsole process need to invoke additional
* instances of vchanger. */
mymutex_unlock(command_mux);
IssueBconsoleCommands(changer.NeedsUpdate() | cmdl.force, changer.NeedsLabel());
mymutex_lock(command_mux, 300);
/* Cleanup */
mymutex_destroy("vchanger-bconsole", command_mux);
mymutex_destroy("vchanger-command", bconsole_mux);
return 0;
}

View File

@ -2,7 +2,7 @@
*
* This file is part of vchanger by Josh Fisher.
*
* vchanger copyright (C) 2008-2014 Josh Fisher
* vchanger copyright (C) 2008-2018 Josh Fisher
*
* vchanger is free software.
* You may redistribute it and/or modify it under the terms of the
@ -45,26 +45,21 @@
#include <sys/stat.h>
#endif
#ifdef HAVE_WINDOWS_H
#include "targetver.h"
#include <windows.h>
#endif
#ifdef HAVE_SHLOBJ_H
#include <shlobj.h>
#endif
#ifdef HAVE_DIRECT_H
#include <direct.h>
#define DIR_DELIM "\\"
#define DIR_DELIM_C '\\'
#define MAG_VOLUME_MASK 0
#else
#define DIR_DELIM "/"
#define DIR_DELIM_C '/'
#define MAG_VOLUME_MASK S_IWGRP|S_IRWXO
#endif
#include "compat_defs.h"
#include "loghandler.h"
#include "util.h"
#define __VCONF_SOURCE 1
#include "vconf.h"
/* Global configuration object */
VchangerConfig conf;
/*-------------------------------------------
* Config file keywords and defaults
@ -145,19 +140,19 @@ bool VchangerConfig::Read(const char *cfile)
tmp_ini.ClearKeywordValues();
if (!cfile || !cfile[0]) {
log.Error("config file not specified");
vlog.Error("config file not specified");
return false;
}
/* Does config file exist */
if (access(cfile, R_OK)) {
log.Error("could not access config file %s", cfile);
vlog.Error("could not access config file %s", cfile);
return false;
}
/* Read config file values */
rc = tmp_ini.Read(cfile);
if (rc) {
if (rc > 0) log.Error("Parse error in %s at line %d", cfile, rc);
else log.Error("could not open config file %s", cfile);
if (rc > 0) vlog.Error("Parse error in %s at line %d", cfile, rc);
else vlog.Error("could not open config file %s", cfile);
return false;
}
/* Update keyword values */
@ -168,7 +163,7 @@ bool VchangerConfig::Read(const char *cfile)
storage_name = (const char*)keyword[VK_STORAGE_NAME];
tStrip(storage_name);
if (storage_name.empty()) {
log.Error("config file keyword '%s' must specify a non-empty string", VK_STORAGE_NAME);
vlog.Error("config file keyword '%s' must specify a non-empty string", VK_STORAGE_NAME);
return false;
}
/* Update defaults for this changer name */
@ -181,7 +176,7 @@ bool VchangerConfig::Read(const char *cfile)
work_dir = (const char*)keyword[VK_WORK_DIR];
tStrip(work_dir);
if (work_dir.empty()) {
log.Error("config file keyword '%s' must specify a non-empty string", VK_WORK_DIR);
vlog.Error("config file keyword '%s' must specify a non-empty string", VK_WORK_DIR);
return false;
}
}
@ -191,7 +186,7 @@ bool VchangerConfig::Read(const char *cfile)
logfile = (const char*)keyword[VK_LOGFILE];
tStrip(logfile);
if (logfile.empty()) {
log.Error("config file keyword '%s' must specify a non-empty string", VK_LOGFILE);
vlog.Error("config file keyword '%s' must specify a non-empty string", VK_LOGFILE);
return false;
}
}
@ -200,7 +195,7 @@ bool VchangerConfig::Read(const char *cfile)
if (keyword[VK_LOG_LEVEL].IsSet()) {
log_level = (int)keyword[VK_LOG_LEVEL];
if (log_level < 0 || log_level > 7) {
log.Error("config file keyword '%s' must specify a value between 0 and 7 inclusive", VK_LOG_LEVEL);
vlog.Error("config file keyword '%s' must specify a value between 0 and 7 inclusive", VK_LOG_LEVEL);
return false;
}
}
@ -210,7 +205,7 @@ bool VchangerConfig::Read(const char *cfile)
user = (const char*)keyword[VK_USER];
tStrip(user);
if (user.empty()) {
log.Error("keyword '%s' value cannot be empty", VK_USER);
vlog.Error("keyword '%s' value cannot be empty", VK_USER);
return false;
}
}
@ -220,7 +215,7 @@ bool VchangerConfig::Read(const char *cfile)
group = (const char*)keyword[VK_GROUP];
tStrip(group);
if (group.empty()) {
log.Error("keyword '%s' value cannot be empty", VK_GROUP);
vlog.Error("keyword '%s' value cannot be empty", VK_GROUP);
return false;
}
}
@ -242,7 +237,7 @@ bool VchangerConfig::Read(const char *cfile)
def_pool = (const char*)keyword[VK_DEF_POOL];
tStrip(def_pool);
if (def_pool.empty()) {
log.Error("keyword '%s' value cannot be empty", VK_DEF_POOL);
vlog.Error("keyword '%s' value cannot be empty", VK_DEF_POOL);
return false;
}
}
@ -252,13 +247,13 @@ bool VchangerConfig::Read(const char *cfile)
magazine = keyword[VK_MAGAZINE];
}
if (magazine.empty()) {
log.Error("config file keyword '%s' must appear at least once", VK_MAGAZINE);
vlog.Error("config file keyword '%s' must appear at least once", VK_MAGAZINE);
return false;
}
for (n = 0; n < (int)magazine.size(); n++) {
tStrip(magazine[n]);
if (magazine[n].empty()) {
log.Error("config file keyword '%s' cannot be set to the empty string", VK_MAGAZINE);
vlog.Error("config file keyword '%s' cannot be set to the empty string", VK_MAGAZINE);
return false;
}
}
@ -283,13 +278,13 @@ bool VchangerConfig::Validate()
#else
if (_mkdir(work_dir.c_str())) {
#endif
log.Error("could not create work directory '%s'", work_dir.c_str());
vlog.Error("could not create work directory '%s'", work_dir.c_str());
umask(old_mask);
return false;
}
umask(old_mask);
} else {
log.Error("could not access work directory '%s'", work_dir.c_str());
vlog.Error("could not access work directory '%s'", work_dir.c_str());
return false;
}
}
@ -299,7 +294,7 @@ bool VchangerConfig::Validate()
if (!bconsole_config.empty()) {
if (access(bconsole_config.c_str(), R_OK)) {
/* If bconsole config doesn't exist or is not readable, disable use of bconsole */
log.Warning("cannot read bconsole config file. Disabling Bacula interaction.");
vlog.Warning("cannot read bconsole config file. Disabling Bacula interaction.");
bconsole.clear();
bconsole_config.clear();
}

View File

@ -2,7 +2,7 @@
*
* This file is part of vchanger by Josh Fisher.
*
* vchanger copyright (C) 2008-2014 Josh Fisher
* vchanger copyright (C) 2008-2018 Josh Fisher
*
* vchanger is free software.
* You may redistribute it and/or modify it under the terms of the
@ -65,6 +65,7 @@ extern char DEFAULT_STATEDIR[4096];
#else
char DEFAULT_LOGDIR[4096];
char DEFAULT_STATEDIR[4096];
VchangerConfig conf;
#endif
#endif /* _VCONF_H_ */

View File

@ -1,5 +1,5 @@
#................-
#. Date of creation: 2015-06-01
#. Date of creation: 2020-05-06
#. Name: installer.nsi
#................-
#..- Package parameters ...
@ -9,7 +9,15 @@ SetCompressor lzma
!include MUI2.nsh
!include WinMessages.nsh
!include x64.nsh
!insertmacro MUI_LANGUAGE English
!insertmacro MUI_PAGE_WELCOME
!insertmacro MUI_PAGE_LICENSE "license.txt"
!insertmacro MUI_PAGE_DIRECTORY
!insertmacro MUI_PAGE_INSTFILES
!insertmacro MUI_PAGE_FINISH
!insertmacro MUI_UNPAGE_CONFIRM
!insertmacro MUI_UNPAGE_INSTFILES
!insertmacro MUI_LANGUAGE "English"
#... Installation Windows Parameters ..
!define APPNAME "vchanger"
!define COMPANYNAME "Josh Fisher"
@ -17,20 +25,20 @@ SetCompressor lzma
# These three must be integers
!define VERSIONMAJOR 1
!define VERSIONMINOR 0
!define VERSIONBUILD 1
!define VERSIONBUILD 3
# These will be displayed by the "Click here for support information" link in "Add/Remove Programs"
!define HELPURL "http://sourceforge.net/projects/vchanger/" # "Support Information" link
Name "vchanger 1.0.1"
VIProductVersion "1.0.1.0"
Name "vchanger 1.0.3"
VIProductVersion "1.0.3.0"
VIAddVersionKey /LANG=${LANG_ENGLISH} "ProductName" "vchanger Installer"
VIAddVersionKey /LANG=${LANG_ENGLISH} "LegalCopyright" "Copyright (c) Josh Fisher 2008-2015"
VIAddVersionKey /LANG=${LANG_ENGLISH} "LegalCopyright" "Copyright (c) Josh Fisher 2008-2020"
VIAddVersionKey /LANG=${LANG_ENGLISH} "FileDescription" "vchanger Windows Installer"
VIAddVersionKey /LANG=${LANG_ENGLISH} "FileVersion" "1.0.1"
VIAddVersionKey /LANG=${LANG_ENGLISH} "FileVersion" "1.0.3"
ShowInstDetails nevershow
SilentInstall normal
RequestExecutionLevel admin
AutoCloseWindow True
OutFile "vchanger-1.0.1.exe"
OutFile "vchanger-1.0.3.exe"
PageEx license
LicenseData "license.txt"
@ -121,13 +129,13 @@ Function .onInit
SetShellVarContext all
#Determine the bitness of the OS and enable the correct section
${If} ${RunningX64}
SectionSetFlags SEC0001 ${SECTION_OFF}
SectionSetFlags SEC0002 ${SF_SELECTED}
SectionSetFlags ${SEC0001} ${SECTION_OFF}
SectionSetFlags ${SEC0002} ${SF_SELECTED}
StrCpy $INSTDIR "$PROGRAMFILES64\vchanger"
SetRegView 64
${Else}
SectionSetFlags SEC0002 ${SECTION_OFF}
SectionSetFlags SEC0001 ${SF_SELECTED}
SectionSetFlags ${SEC0002} ${SECTION_OFF}
SectionSetFlags ${SEC0001} ${SF_SELECTED}
StrCpy $INSTDIR "$PROGRAMFILES32\vchanger"
SetRegView 32
${EndIf}

View File

@ -2,7 +2,7 @@
#
# Build for 32-bit and 64-bit Windows binary and create NSIS installer
#
VERS=1.0.1
VERS=1.0.3
rm -f ./win32/vchanger-$VERS.exe
rm -f ./win32/vchanger.exe
rm -f ./win32/vchanger64.exe