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

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

@ -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

@ -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

@ -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
@ -6,7 +36,7 @@
Additionally, the Windows installer was fixed to correctly install on
64-bit Windows and create Start Menu items.
Bugs Fixed:
Bugs Fixed:
9 Vchanger may hang when issuing commands to Bacula
10 uuidlookup.c compilation failure on FreeBSD 9.3

@ -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

@ -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

@ -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\\"

@ -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])

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

@ -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"

@ -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"

@ -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"

@ -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.

@ -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"

@ -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.

File diff suppressed because it is too large Load Diff

@ -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>

@ -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

@ -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

@ -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

@ -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

@ -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

@ -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

@ -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

@ -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

@ -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

@ -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;
}
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;
if (write(fno_in, "\n", 1) != 1) {
rc = errno;
vlog.Error("send to bconsole's stdin failed errno=%d", rc);
close(fno_in);
close(fno_out);
errno = rc;
return rc;
}
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);
}
/* 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;
#else
return EINVAL;
#endif
}
/*
* 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

@ -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_ */

@ -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

@ -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

@ -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 */

@ -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.

@ -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;

@ -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);
}

@ -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

@ -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

@ -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_ */

@ -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;
}

@ -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);
pos = 0;
while (rc == -4 && pos < dev_name_len) {
for (n = pos; n < dev_name_len && !isblank(dev_name[n]); n++) ;
n -= pos;
memmove(devlink, dev_name + 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 == -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_links_len) {
for (n = pos; n < dev_links_len && !isblank(dev_links[n]); n++) ;
n -= pos;
memmove(devlink, dev_links + pos, n);
devlink[n] = 0;
rc = GetDevMountpoint(mountp, mountp_sz, devlink);
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;
}

@ -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;
}

@ -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();
}

@ -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_ */

@ -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}

@ -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