Import project from sourceforge.net

This commit is contained in:
Wanderlei Hüttel
2016-08-11 08:12:38 -03:00
commit 5a08594ac8
83 changed files with 27069 additions and 0 deletions

14
src/Makefile.am Normal file
View File

@@ -0,0 +1,14 @@
AUTOMAKE_OPTIONS = foreign
AM_CFLAGS = -DLOCALSTATEDIR='"${localstatedir}"'
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/symlink.c compat/sleep.c compat/syslog.c \
win32_util.c uuidlookup.c bconsole.cpp \
tstring.cpp inifile.cpp mypopen.cpp \
vconf.cpp loghandler.cpp errhandler.cpp \
util.cpp changerstate.cpp diskchanger.cpp \
vchanger.cpp

722
src/Makefile.in Normal file
View File

@@ -0,0 +1,722 @@
# Makefile.in generated by automake 1.13.4 from Makefile.am.
# @configure_input@
# Copyright (C) 1994-2013 Free Software Foundation, Inc.
# This Makefile.in is free software; the Free Software Foundation
# gives unlimited permission to copy and/or distribute it,
# with or without modifications, as long as this notice is preserved.
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY, to the extent permitted by law; without
# even the implied warranty of MERCHANTABILITY or FITNESS FOR A
# PARTICULAR PURPOSE.
@SET_MAKE@
VPATH = @srcdir@
am__is_gnu_make = test -n '$(MAKEFILE_LIST)' && test -n '$(MAKELEVEL)'
am__make_running_with_option = \
case $${target_option-} in \
?) ;; \
*) echo "am__make_running_with_option: internal error: invalid" \
"target option '$${target_option-}' specified" >&2; \
exit 1;; \
esac; \
has_opt=no; \
sane_makeflags=$$MAKEFLAGS; \
if $(am__is_gnu_make); then \
sane_makeflags=$$MFLAGS; \
else \
case $$MAKEFLAGS in \
*\\[\ \ ]*) \
bs=\\; \
sane_makeflags=`printf '%s\n' "$$MAKEFLAGS" \
| sed "s/$$bs$$bs[$$bs $$bs ]*//g"`;; \
esac; \
fi; \
skip_next=no; \
strip_trailopt () \
{ \
flg=`printf '%s\n' "$$flg" | sed "s/$$1.*$$//"`; \
}; \
for flg in $$sane_makeflags; do \
test $$skip_next = yes && { skip_next=no; continue; }; \
case $$flg in \
*=*|--*) continue;; \
-*I) strip_trailopt 'I'; skip_next=yes;; \
-*I?*) strip_trailopt 'I';; \
-*O) strip_trailopt 'O'; skip_next=yes;; \
-*O?*) strip_trailopt 'O';; \
-*l) strip_trailopt 'l'; skip_next=yes;; \
-*l?*) strip_trailopt 'l';; \
-[dEDm]) skip_next=yes;; \
-[JT]) skip_next=yes;; \
esac; \
case $$flg in \
*$$target_option*) has_opt=yes; break;; \
esac; \
done; \
test $$has_opt = yes
am__make_dryrun = (target_option=n; $(am__make_running_with_option))
am__make_keepgoing = (target_option=k; $(am__make_running_with_option))
pkgdatadir = $(datadir)/@PACKAGE@
pkgincludedir = $(includedir)/@PACKAGE@
pkglibdir = $(libdir)/@PACKAGE@
pkglibexecdir = $(libexecdir)/@PACKAGE@
am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd
install_sh_DATA = $(install_sh) -c -m 644
install_sh_PROGRAM = $(install_sh) -c
install_sh_SCRIPT = $(install_sh) -c
INSTALL_HEADER = $(INSTALL_DATA)
transform = $(program_transform_name)
NORMAL_INSTALL = :
PRE_INSTALL = :
POST_INSTALL = :
NORMAL_UNINSTALL = :
PRE_UNINSTALL = :
POST_UNINSTALL = :
bin_PROGRAMS = vchanger$(EXEEXT)
subdir = src
DIST_COMMON = $(srcdir)/Makefile.in $(srcdir)/Makefile.am \
$(top_srcdir)/depcomp
ACLOCAL_M4 = $(top_srcdir)/aclocal.m4
am__aclocal_m4_deps = $(top_srcdir)/configure.ac
am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \
$(ACLOCAL_M4)
mkinstalldirs = $(install_sh) -d
CONFIG_HEADER = $(top_builddir)/config.h
CONFIG_CLEAN_FILES =
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) \
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) \
vchanger.$(OBJEXT)
vchanger_OBJECTS = $(am_vchanger_OBJECTS)
vchanger_LDADD = $(LDADD)
AM_V_P = $(am__v_P_@AM_V@)
am__v_P_ = $(am__v_P_@AM_DEFAULT_V@)
am__v_P_0 = false
am__v_P_1 = :
AM_V_GEN = $(am__v_GEN_@AM_V@)
am__v_GEN_ = $(am__v_GEN_@AM_DEFAULT_V@)
am__v_GEN_0 = @echo " GEN " $@;
am__v_GEN_1 =
AM_V_at = $(am__v_at_@AM_V@)
am__v_at_ = $(am__v_at_@AM_DEFAULT_V@)
am__v_at_0 = @
am__v_at_1 =
DEFAULT_INCLUDES = -I.@am__isrc@ -I$(top_builddir)
depcomp = $(SHELL) $(top_srcdir)/depcomp
am__depfiles_maybe = depfiles
am__mv = mv -f
AM_V_lt = $(am__v_lt_@AM_V@)
am__v_lt_ = $(am__v_lt_@AM_DEFAULT_V@)
am__v_lt_0 = --silent
am__v_lt_1 =
COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \
$(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS)
AM_V_CC = $(am__v_CC_@AM_V@)
am__v_CC_ = $(am__v_CC_@AM_DEFAULT_V@)
am__v_CC_0 = @echo " CC " $@;
am__v_CC_1 =
CCLD = $(CC)
LINK = $(CCLD) $(AM_CFLAGS) $(CFLAGS) $(AM_LDFLAGS) $(LDFLAGS) -o $@
AM_V_CCLD = $(am__v_CCLD_@AM_V@)
am__v_CCLD_ = $(am__v_CCLD_@AM_DEFAULT_V@)
am__v_CCLD_0 = @echo " CCLD " $@;
am__v_CCLD_1 =
CXXCOMPILE = $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) \
$(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS)
AM_V_CXX = $(am__v_CXX_@AM_V@)
am__v_CXX_ = $(am__v_CXX_@AM_DEFAULT_V@)
am__v_CXX_0 = @echo " CXX " $@;
am__v_CXX_1 =
CXXLD = $(CXX)
CXXLINK = $(CXXLD) $(AM_CXXFLAGS) $(CXXFLAGS) $(AM_LDFLAGS) $(LDFLAGS) \
-o $@
AM_V_CXXLD = $(am__v_CXXLD_@AM_V@)
am__v_CXXLD_ = $(am__v_CXXLD_@AM_DEFAULT_V@)
am__v_CXXLD_0 = @echo " CXXLD " $@;
am__v_CXXLD_1 =
SOURCES = $(vchanger_SOURCES)
DIST_SOURCES = $(vchanger_SOURCES)
am__can_run_installinfo = \
case $$AM_UPDATE_INFO_DIR in \
n|no|NO) false;; \
*) (install-info --version) >/dev/null 2>&1;; \
esac
am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) $(LISP)
# Read a list of newline-separated strings from the standard input,
# and print each of them once, without duplicates. Input order is
# *not* preserved.
am__uniquify_input = $(AWK) '\
BEGIN { nonempty = 0; } \
{ items[$$0] = 1; nonempty = 1; } \
END { if (nonempty) { for (i in items) print i; }; } \
'
# Make sure the list of sources is unique. This is necessary because,
# e.g., the same source file might be shared among _SOURCES variables
# for different programs/libraries.
am__define_uniq_tagged_files = \
list='$(am__tagged_files)'; \
unique=`for i in $$list; do \
if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \
done | $(am__uniquify_input)`
ETAGS = etags
CTAGS = ctags
DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST)
ACLOCAL = @ACLOCAL@
AMTAR = @AMTAR@
AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@
AUTOCONF = @AUTOCONF@
AUTOHEADER = @AUTOHEADER@
AUTOMAKE = @AUTOMAKE@
AWK = @AWK@
CC = @CC@
CCDEPMODE = @CCDEPMODE@
CFLAGS = @CFLAGS@
CPP = @CPP@
CPPFLAGS = @CPPFLAGS@
CXX = @CXX@
CXXDEPMODE = @CXXDEPMODE@
CXXFLAGS = @CXXFLAGS@
CYGPATH_W = @CYGPATH_W@
DEFS = @DEFS@
DEPDIR = @DEPDIR@
ECHO_C = @ECHO_C@
ECHO_N = @ECHO_N@
ECHO_T = @ECHO_T@
EGREP = @EGREP@
EXEEXT = @EXEEXT@
GREP = @GREP@
INSTALL = @INSTALL@
INSTALL_DATA = @INSTALL_DATA@
INSTALL_PROGRAM = @INSTALL_PROGRAM@
INSTALL_SCRIPT = @INSTALL_SCRIPT@
INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@
LDFLAGS = @LDFLAGS@
LIBOBJS = @LIBOBJS@
LIBS = @LIBS@
LTLIBOBJS = @LTLIBOBJS@
MAKEINFO = @MAKEINFO@
MKDIR_P = @MKDIR_P@
OBJEXT = @OBJEXT@
PACKAGE = @PACKAGE@
PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@
PACKAGE_NAME = @PACKAGE_NAME@
PACKAGE_STRING = @PACKAGE_STRING@
PACKAGE_TARNAME = @PACKAGE_TARNAME@
PACKAGE_URL = @PACKAGE_URL@
PACKAGE_VERSION = @PACKAGE_VERSION@
PATH_SEPARATOR = @PATH_SEPARATOR@
SET_MAKE = @SET_MAKE@
SHELL = @SHELL@
STRIP = @STRIP@
VERSION = @VERSION@
WINLDADD = @WINLDADD@
abs_builddir = @abs_builddir@
abs_srcdir = @abs_srcdir@
abs_top_builddir = @abs_top_builddir@
abs_top_srcdir = @abs_top_srcdir@
ac_ct_CC = @ac_ct_CC@
ac_ct_CXX = @ac_ct_CXX@
am__include = @am__include@
am__leading_dot = @am__leading_dot@
am__quote = @am__quote@
am__tar = @am__tar@
am__untar = @am__untar@
bindir = @bindir@
build_alias = @build_alias@
builddir = @builddir@
datadir = @datadir@
datarootdir = @datarootdir@
docdir = @docdir@
dvidir = @dvidir@
exec_prefix = @exec_prefix@
host_alias = @host_alias@
htmldir = @htmldir@
includedir = @includedir@
infodir = @infodir@
install_sh = @install_sh@
libdir = @libdir@
libexecdir = @libexecdir@
localedir = @localedir@
localstatedir = @localstatedir@
mandir = @mandir@
mkdir_p = @mkdir_p@
oldincludedir = @oldincludedir@
pdfdir = @pdfdir@
prefix = @prefix@
program_transform_name = @program_transform_name@
psdir = @psdir@
sbindir = @sbindir@
sharedstatedir = @sharedstatedir@
srcdir = @srcdir@
sysconfdir = @sysconfdir@
target_alias = @target_alias@
top_build_prefix = @top_build_prefix@
top_builddir = @top_builddir@
top_srcdir = @top_srcdir@
AUTOMAKE_OPTIONS = foreign
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/symlink.c compat/sleep.c compat/syslog.c \
win32_util.c uuidlookup.c bconsole.cpp \
tstring.cpp inifile.cpp mypopen.cpp \
vconf.cpp loghandler.cpp errhandler.cpp \
util.cpp changerstate.cpp diskchanger.cpp \
vchanger.cpp
all: all-am
.SUFFIXES:
.SUFFIXES: .c .cpp .o .obj
$(srcdir)/Makefile.in: $(srcdir)/Makefile.am $(am__configure_deps)
@for dep in $?; do \
case '$(am__configure_deps)' in \
*$$dep*) \
( cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ) \
&& { if test -f $@; then exit 0; else break; fi; }; \
exit 1;; \
esac; \
done; \
echo ' cd $(top_srcdir) && $(AUTOMAKE) --foreign src/Makefile'; \
$(am__cd) $(top_srcdir) && \
$(AUTOMAKE) --foreign src/Makefile
.PRECIOUS: Makefile
Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status
@case '$?' in \
*config.status*) \
cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \
*) \
echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe)'; \
cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe);; \
esac;
$(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES)
cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
$(top_srcdir)/configure: $(am__configure_deps)
cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
$(ACLOCAL_M4): $(am__aclocal_m4_deps)
cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
$(am__aclocal_m4_deps):
install-binPROGRAMS: $(bin_PROGRAMS)
@$(NORMAL_INSTALL)
@list='$(bin_PROGRAMS)'; test -n "$(bindir)" || list=; \
if test -n "$$list"; then \
echo " $(MKDIR_P) '$(DESTDIR)$(bindir)'"; \
$(MKDIR_P) "$(DESTDIR)$(bindir)" || exit 1; \
fi; \
for p in $$list; do echo "$$p $$p"; done | \
sed 's/$(EXEEXT)$$//' | \
while read p p1; do if test -f $$p \
; then echo "$$p"; echo "$$p"; else :; fi; \
done | \
sed -e 'p;s,.*/,,;n;h' \
-e 's|.*|.|' \
-e 'p;x;s,.*/,,;s/$(EXEEXT)$$//;$(transform);s/$$/$(EXEEXT)/' | \
sed 'N;N;N;s,\n, ,g' | \
$(AWK) 'BEGIN { files["."] = ""; dirs["."] = 1 } \
{ d=$$3; if (dirs[d] != 1) { print "d", d; dirs[d] = 1 } \
if ($$2 == $$4) files[d] = files[d] " " $$1; \
else { print "f", $$3 "/" $$4, $$1; } } \
END { for (d in files) print "f", d, files[d] }' | \
while read type dir files; do \
if test "$$dir" = .; then dir=; else dir=/$$dir; fi; \
test -z "$$files" || { \
echo " $(INSTALL_PROGRAM_ENV) $(INSTALL_PROGRAM) $$files '$(DESTDIR)$(bindir)$$dir'"; \
$(INSTALL_PROGRAM_ENV) $(INSTALL_PROGRAM) $$files "$(DESTDIR)$(bindir)$$dir" || exit $$?; \
} \
; done
uninstall-binPROGRAMS:
@$(NORMAL_UNINSTALL)
@list='$(bin_PROGRAMS)'; test -n "$(bindir)" || list=; \
files=`for p in $$list; do echo "$$p"; done | \
sed -e 'h;s,^.*/,,;s/$(EXEEXT)$$//;$(transform)' \
-e 's/$$/$(EXEEXT)/' \
`; \
test -n "$$list" || exit 0; \
echo " ( cd '$(DESTDIR)$(bindir)' && rm -f" $$files ")"; \
cd "$(DESTDIR)$(bindir)" && rm -f $$files
clean-binPROGRAMS:
-test -z "$(bin_PROGRAMS)" || rm -f $(bin_PROGRAMS)
vchanger$(EXEEXT): $(vchanger_OBJECTS) $(vchanger_DEPENDENCIES) $(EXTRA_vchanger_DEPENDENCIES)
@rm -f vchanger$(EXEEXT)
$(AM_V_CXXLD)$(CXXLINK) $(vchanger_OBJECTS) $(vchanger_LDADD) $(LIBS)
mostlyclean-compile:
-rm -f *.$(OBJEXT)
distclean-compile:
-rm -f *.tab.c
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/bconsole.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/changerstate.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/diskchanger.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/errhandler.Po@am__quote@
@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)/mypopen.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/readlink.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@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/tstring.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/util.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/uuidlookup.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/vchanger.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/vconf.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/win32_util.Po@am__quote@
.c.o:
@am__fastdepCC_TRUE@ $(AM_V_CC)$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $<
@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po
@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c $<
.c.obj:
@am__fastdepCC_TRUE@ $(AM_V_CC)$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ `$(CYGPATH_W) '$<'`
@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po
@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c `$(CYGPATH_W) '$<'`
getline.o: compat/getline.c
@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT getline.o -MD -MP -MF $(DEPDIR)/getline.Tpo -c -o getline.o `test -f 'compat/getline.c' || echo '$(srcdir)/'`compat/getline.c
@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/getline.Tpo $(DEPDIR)/getline.Po
@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='compat/getline.c' object='getline.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 getline.o `test -f 'compat/getline.c' || echo '$(srcdir)/'`compat/getline.c
getline.obj: compat/getline.c
@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT getline.obj -MD -MP -MF $(DEPDIR)/getline.Tpo -c -o getline.obj `if test -f 'compat/getline.c'; then $(CYGPATH_W) 'compat/getline.c'; else $(CYGPATH_W) '$(srcdir)/compat/getline.c'; fi`
@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/getline.Tpo $(DEPDIR)/getline.Po
@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='compat/getline.c' object='getline.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 getline.obj `if test -f 'compat/getline.c'; then $(CYGPATH_W) 'compat/getline.c'; else $(CYGPATH_W) '$(srcdir)/compat/getline.c'; fi`
gettimeofday.o: compat/gettimeofday.c
@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT gettimeofday.o -MD -MP -MF $(DEPDIR)/gettimeofday.Tpo -c -o gettimeofday.o `test -f 'compat/gettimeofday.c' || echo '$(srcdir)/'`compat/gettimeofday.c
@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/gettimeofday.Tpo $(DEPDIR)/gettimeofday.Po
@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='compat/gettimeofday.c' object='gettimeofday.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 gettimeofday.o `test -f 'compat/gettimeofday.c' || echo '$(srcdir)/'`compat/gettimeofday.c
gettimeofday.obj: compat/gettimeofday.c
@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT gettimeofday.obj -MD -MP -MF $(DEPDIR)/gettimeofday.Tpo -c -o gettimeofday.obj `if test -f 'compat/gettimeofday.c'; then $(CYGPATH_W) 'compat/gettimeofday.c'; else $(CYGPATH_W) '$(srcdir)/compat/gettimeofday.c'; fi`
@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/gettimeofday.Tpo $(DEPDIR)/gettimeofday.Po
@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='compat/gettimeofday.c' object='gettimeofday.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 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
@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='compat/readlink.c' object='readlink.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 readlink.o `test -f 'compat/readlink.c' || echo '$(srcdir)/'`compat/readlink.c
readlink.obj: compat/readlink.c
@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT readlink.obj -MD -MP -MF $(DEPDIR)/readlink.Tpo -c -o readlink.obj `if test -f 'compat/readlink.c'; then $(CYGPATH_W) 'compat/readlink.c'; else $(CYGPATH_W) '$(srcdir)/compat/readlink.c'; fi`
@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/readlink.Tpo $(DEPDIR)/readlink.Po
@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='compat/readlink.c' object='readlink.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 readlink.obj `if test -f 'compat/readlink.c'; then $(CYGPATH_W) 'compat/readlink.c'; else $(CYGPATH_W) '$(srcdir)/compat/readlink.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
@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='compat/symlink.c' object='symlink.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 symlink.o `test -f 'compat/symlink.c' || echo '$(srcdir)/'`compat/symlink.c
symlink.obj: compat/symlink.c
@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT symlink.obj -MD -MP -MF $(DEPDIR)/symlink.Tpo -c -o symlink.obj `if test -f 'compat/symlink.c'; then $(CYGPATH_W) 'compat/symlink.c'; else $(CYGPATH_W) '$(srcdir)/compat/symlink.c'; fi`
@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/symlink.Tpo $(DEPDIR)/symlink.Po
@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='compat/symlink.c' object='symlink.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 symlink.obj `if test -f 'compat/symlink.c'; then $(CYGPATH_W) 'compat/symlink.c'; else $(CYGPATH_W) '$(srcdir)/compat/symlink.c'; fi`
sleep.o: compat/sleep.c
@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT sleep.o -MD -MP -MF $(DEPDIR)/sleep.Tpo -c -o sleep.o `test -f 'compat/sleep.c' || echo '$(srcdir)/'`compat/sleep.c
@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/sleep.Tpo $(DEPDIR)/sleep.Po
@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='compat/sleep.c' object='sleep.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 sleep.o `test -f 'compat/sleep.c' || echo '$(srcdir)/'`compat/sleep.c
sleep.obj: compat/sleep.c
@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT sleep.obj -MD -MP -MF $(DEPDIR)/sleep.Tpo -c -o sleep.obj `if test -f 'compat/sleep.c'; then $(CYGPATH_W) 'compat/sleep.c'; else $(CYGPATH_W) '$(srcdir)/compat/sleep.c'; fi`
@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/sleep.Tpo $(DEPDIR)/sleep.Po
@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='compat/sleep.c' object='sleep.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 sleep.obj `if test -f 'compat/sleep.c'; then $(CYGPATH_W) 'compat/sleep.c'; else $(CYGPATH_W) '$(srcdir)/compat/sleep.c'; fi`
syslog.o: compat/syslog.c
@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT syslog.o -MD -MP -MF $(DEPDIR)/syslog.Tpo -c -o syslog.o `test -f 'compat/syslog.c' || echo '$(srcdir)/'`compat/syslog.c
@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/syslog.Tpo $(DEPDIR)/syslog.Po
@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='compat/syslog.c' object='syslog.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 syslog.o `test -f 'compat/syslog.c' || echo '$(srcdir)/'`compat/syslog.c
syslog.obj: compat/syslog.c
@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT syslog.obj -MD -MP -MF $(DEPDIR)/syslog.Tpo -c -o syslog.obj `if test -f 'compat/syslog.c'; then $(CYGPATH_W) 'compat/syslog.c'; else $(CYGPATH_W) '$(srcdir)/compat/syslog.c'; fi`
@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/syslog.Tpo $(DEPDIR)/syslog.Po
@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='compat/syslog.c' object='syslog.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 syslog.obj `if test -f 'compat/syslog.c'; then $(CYGPATH_W) 'compat/syslog.c'; else $(CYGPATH_W) '$(srcdir)/compat/syslog.c'; fi`
.cpp.o:
@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXXCOMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $<
@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po
@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@
@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXXCOMPILE) -c -o $@ $<
.cpp.obj:
@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXXCOMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ `$(CYGPATH_W) '$<'`
@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po
@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@
@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXXCOMPILE) -c -o $@ `$(CYGPATH_W) '$<'`
ID: $(am__tagged_files)
$(am__define_uniq_tagged_files); mkid -fID $$unique
tags: tags-am
TAGS: tags
tags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files)
set x; \
here=`pwd`; \
$(am__define_uniq_tagged_files); \
shift; \
if test -z "$(ETAGS_ARGS)$$*$$unique"; then :; else \
test -n "$$unique" || unique=$$empty_fix; \
if test $$# -gt 0; then \
$(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \
"$$@" $$unique; \
else \
$(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \
$$unique; \
fi; \
fi
ctags: ctags-am
CTAGS: ctags
ctags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files)
$(am__define_uniq_tagged_files); \
test -z "$(CTAGS_ARGS)$$unique" \
|| $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \
$$unique
GTAGS:
here=`$(am__cd) $(top_builddir) && pwd` \
&& $(am__cd) $(top_srcdir) \
&& gtags -i $(GTAGS_ARGS) "$$here"
cscopelist: cscopelist-am
cscopelist-am: $(am__tagged_files)
list='$(am__tagged_files)'; \
case "$(srcdir)" in \
[\\/]* | ?:[\\/]*) sdir="$(srcdir)" ;; \
*) sdir=$(subdir)/$(srcdir) ;; \
esac; \
for i in $$list; do \
if test -f "$$i"; then \
echo "$(subdir)/$$i"; \
else \
echo "$$sdir/$$i"; \
fi; \
done >> $(top_builddir)/cscope.files
distclean-tags:
-rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags
distdir: $(DISTFILES)
@srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \
topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \
list='$(DISTFILES)'; \
dist_files=`for file in $$list; do echo $$file; done | \
sed -e "s|^$$srcdirstrip/||;t" \
-e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \
case $$dist_files in \
*/*) $(MKDIR_P) `echo "$$dist_files" | \
sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \
sort -u` ;; \
esac; \
for file in $$dist_files; do \
if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \
if test -d $$d/$$file; then \
dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \
if test -d "$(distdir)/$$file"; then \
find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \
fi; \
if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \
cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \
find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \
fi; \
cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \
else \
test -f "$(distdir)/$$file" \
|| cp -p $$d/$$file "$(distdir)/$$file" \
|| exit 1; \
fi; \
done
check-am: all-am
check: check-am
all-am: Makefile $(PROGRAMS)
installdirs:
for dir in "$(DESTDIR)$(bindir)"; do \
test -z "$$dir" || $(MKDIR_P) "$$dir"; \
done
install: install-am
install-exec: install-exec-am
install-data: install-data-am
uninstall: uninstall-am
install-am: all-am
@$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am
installcheck: installcheck-am
install-strip:
if test -z '$(STRIP)'; then \
$(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \
install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \
install; \
else \
$(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \
install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \
"INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'" install; \
fi
mostlyclean-generic:
clean-generic:
distclean-generic:
-test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES)
-test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES)
maintainer-clean-generic:
@echo "This command is intended for maintainers to use"
@echo "it deletes files that may require special tools to rebuild."
clean: clean-am
clean-am: clean-binPROGRAMS clean-generic mostlyclean-am
distclean: distclean-am
-rm -rf ./$(DEPDIR)
-rm -f Makefile
distclean-am: clean-am distclean-compile distclean-generic \
distclean-tags
dvi: dvi-am
dvi-am:
html: html-am
html-am:
info: info-am
info-am:
install-data-am:
install-dvi: install-dvi-am
install-dvi-am:
install-exec-am: install-binPROGRAMS
install-html: install-html-am
install-html-am:
install-info: install-info-am
install-info-am:
install-man:
install-pdf: install-pdf-am
install-pdf-am:
install-ps: install-ps-am
install-ps-am:
installcheck-am:
maintainer-clean: maintainer-clean-am
-rm -rf ./$(DEPDIR)
-rm -f Makefile
maintainer-clean-am: distclean-am maintainer-clean-generic
mostlyclean: mostlyclean-am
mostlyclean-am: mostlyclean-compile mostlyclean-generic
pdf: pdf-am
pdf-am:
ps: ps-am
ps-am:
uninstall-am: uninstall-binPROGRAMS
.MAKE: install-am install-strip
.PHONY: CTAGS GTAGS TAGS all all-am check check-am clean \
clean-binPROGRAMS clean-generic cscopelist-am ctags ctags-am \
distclean distclean-compile distclean-generic distclean-tags \
distdir dvi dvi-am html html-am info info-am install \
install-am install-binPROGRAMS install-data install-data-am \
install-dvi install-dvi-am install-exec install-exec-am \
install-html install-html-am install-info install-info-am \
install-man install-pdf install-pdf-am install-ps \
install-ps-am install-strip installcheck installcheck-am \
installdirs maintainer-clean maintainer-clean-generic \
mostlyclean mostlyclean-compile mostlyclean-generic pdf pdf-am \
ps ps-am tags tags-am uninstall uninstall-am \
uninstall-binPROGRAMS
# Tell versions [3.59,3.63) of GNU make to not export all variables.
# Otherwise a system limit (for SysV at least) may be exceeded.
.NOEXPORT:

176
src/bconsole.cpp Normal file
View File

@@ -0,0 +1,176 @@
/* bconsole.cpp
*
* Copyright (C) 2008-2014 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"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#ifdef HAVE_UNISTD_H
#include <unistd.h>
#endif
#ifdef HAVE_SYS_TYPES_H
#include <sys/types.h>
#endif
#ifdef HAVE_SYS_TIME_H
#include <sys/time.h>
#endif
#ifdef HAVE_SYS_WAIT_H
#include <sys/wait.h>
#endif
#ifdef HAVE_SYS_SELECT_H
#include <sys/select.h>
#else
#ifdef HAVE_WINSOCK_H
#include <winsock.h>
#endif
#endif
#ifdef HAVE_ERRNO_H
#include <errno.h>
#endif
#ifdef HAVE_CTYPE_H
#include <ctype.h>
#endif
#include "loghandler.h"
#include "mypopen.h"
#include "vconf.h"
#include "bconsole.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)
{
int pid, rc, n, len, fno_in = -1, fno_out = -1;
struct timeval tv;
fd_set rfd;
tString cmd, tmp;
char buf[4096];
#ifndef HAVE_WINDOWS_H
/* Build command line */
cmd = conf.bconsole;
if (cmd.empty()) return 0;
if (!conf.bconsole_config.empty()) {
cmd += " -c ";
cmd += conf.bconsole_config;
}
cmd += " -n -u 30";
/* Start bconsole process */
log.Debug("bconsole: running '%s'", bcmd);
pid = mypopen_raw(cmd.c_str(), &fno_in, &fno_out, NULL);
if (pid < 0) {
rc = errno;
log.Error("bconsole: run failed errno=%d", rc);
errno = rc;
return rc;
}
/* Wait for bconsole to accept input */
tv.tv_sec = 30;
tv.tv_usec = 0;
FD_ZERO(&rfd);
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");
close(fno_in);
close(fno_out);
errno = ETIMEDOUT;
return ETIMEDOUT;
}
if (rc < 0) {
rc = errno;
log.Error("bconsole: errno=%d waiting to send command", rc);
close(fno_in);
close(fno_out);
errno = rc;
return rc;
}
/* Send command to bconsole's stdin */
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);
close(fno_in);
close(fno_out);
errno = rc;
return rc;
}
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;
}
close(fno_out);
log.Debug("bconsole: output:\n%s", tmp.c_str());
/* Wait for bconsole process to finish */
pid = waitpid(pid, &rc, 0);
if (!WIFEXITED(rc)) {
log.Error("bconsole: abnormal exit of bconsole process");
return EPIPE;
}
if (WEXITSTATUS(rc)) {
log.Error("bconsole: exited with rc=%d", WEXITSTATUS(rc));
return WEXITSTATUS(rc);
}
return 0;
#else
return EINVAL;
#endif
}

29
src/bconsole.h Normal file
View File

@@ -0,0 +1,29 @@
/* bconsole.h
*
* This file is part of vchanger by Josh Fisher.
*
* vchanger copyright (C) 2008-2014 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 BCONSOLE_H_
#define BCONSOLE_H_
int issue_bconsole_command(const char *bcmd);
#endif /* BCONSOLE_H_ */

851
src/changerstate.cpp Normal file
View File

@@ -0,0 +1,851 @@
/* changerstate.cpp
*
* This file is part of vchanger by Josh Fisher.
*
* vchanger copyright (C) 2008-2015 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.
*
* Provides a class to track state of virtual drives using files in
* the vchanger state directory.
*/
#include "config.h"
#include "compat_defs.h"
#ifdef HAVE_STDIO_H
#include <stdio.h>
#endif
#ifdef HAVE_STDLIB_H
#include <stdlib.h>
#endif
#ifdef HAVE_STRING_H
#include <string.h>
#endif
#ifdef HAVE_UNISTD_H
#include <unistd.h>
#endif
#ifdef HAVE_STDDEF_H
#include <stddef.h>
#endif
#ifdef HAVE_SYS_TYPES_H
#include <sys/types.h>
#endif
#ifdef HAVE_SYS_STAT_H
#include <sys/stat.h>
#endif
#ifdef HAVE_DIRENT_H
#include <dirent.h>
#endif
#ifdef HAVE_CTYPE_H
#include <ctype.h>
#endif
#ifdef HAVE_ERRNO_H
#include <errno.h>
#endif
#include "compat/getline.h"
#include "compat/readlink.h"
#include "compat/symlink.h"
#include "vconf.h"
#include "loghandler.h"
#include "errhandler.h"
#include "util.h"
#define __CHANGERSTATE_SOURCE 1
#include "changerstate.h"
#include "uuidlookup.h"
///////////////////////////////////////////////////
// Class MagazineSlot
///////////////////////////////////////////////////
MagazineSlot::MagazineSlot(const MagazineSlot &b)
{
mag_bay = b.mag_bay;
mag_slot = b.mag_slot;
label = b.label;
}
MagazineSlot& MagazineSlot::operator=(const MagazineSlot &b)
{
if (&b != this) {
mag_bay = b.mag_bay;
mag_slot = b.mag_slot;
label = b.label;
}
return *this;
}
bool MagazineSlot::operator==(const MagazineSlot &b)
{
if (&b == this) return true;
if (label == b.label) return true;
return false;
}
bool MagazineSlot::operator!=(const MagazineSlot &b)
{
if (&b == this) return false;
if (label != b.label) return true;
return false;
}
/*-------------------------------------------------
* Method to clear object values
*-------------------------------------------------*/
void MagazineSlot::clear()
{
mag_bay = -1;
mag_slot = -1;
label.clear();
}
///////////////////////////////////////////////////
// Class MagazineState
///////////////////////////////////////////////////
MagazineState::MagazineState(const MagazineState &b)
{
mag_bay = b.mag_bay;
num_slots = b.num_slots;
start_slot = b.start_slot;
prev_num_slots = b.prev_num_slots;
prev_start_slot = b.prev_start_slot;
mag_dev = b.mag_dev;
mountpoint = b.mountpoint;
mslot = b.mslot;
verr = b.verr;
}
MagazineState& MagazineState::operator=(const MagazineState &b)
{
if (&b != this) {
mag_bay = b.mag_bay;
num_slots = b.num_slots;
start_slot = b.start_slot;
prev_num_slots = b.prev_num_slots;
prev_start_slot = b.prev_start_slot;
mag_dev = b.mag_dev;
mountpoint = b.mountpoint;
mslot = b.mslot;
verr = b.verr;
}
return *this;
}
void MagazineState::clear()
{
/* Notice that device and bay number are not cleared */
num_slots = 0;
start_slot = 0;
mountpoint.clear();
mslot.clear();
verr.clear();
}
/*-------------------------------------------------
* Method to save current state of magazine bay to a file in
* the work directory named "bay" + bay_number.
* On success returns zero, otherwise sets lasterr and
* returns errno.
*-------------------------------------------------*/
int MagazineState::save()
{
mode_t old_mask;
int rc;
FILE *FS;
char sname[4096];
if (mag_bay < 0) {
verr.SetErrorWithErrno(EINVAL, "cannot save state of invalid magazine %d", mag_bay);
log.Error("ERROR! %s", verr.GetErrorMsg());
return EINVAL;
}
/* Build path to state file */
snprintf(sname, sizeof(sname), "%s%sbay_state-%d", conf.work_dir.c_str(), DIR_DELIM, mag_bay);
/* Remove magazine state files for unmounted magazines */
if (mountpoint.empty() || mslot.empty()) {
unlink(sname);
return 0;
}
/* Write state file for mounted magazine */
old_mask = umask(027);
FS = fopen(sname, "w");
if (!FS) {
/* Unable to open state file for writing */
rc = errno;
umask(old_mask);
verr.SetErrorWithErrno(rc, "cannot open magazine %d state file for writing", mag_bay);
log.Error("ERROR! %s", verr.GetErrorMsg());
return rc;
}
/* Save magazine device (directory or UUID), number of volumes, and start of
* virtual slot range it is assigned */
if (fprintf(FS, "%s,%d,%d\n", mag_dev.c_str(), num_slots, start_slot) < 0) {
/* I/O error writing state file */
rc = errno;
fclose(FS);
unlink(sname);
umask(old_mask);
verr.SetErrorWithErrno(rc, "cannot write to magazine %d state file", mag_bay);
log.Error("ERROR! %s", verr.GetErrorMsg());
return rc;
}
fclose(FS);
umask(old_mask);
log.Notice("saved state of magazine %d", mag_bay);
return 0;
}
/*-------------------------------------------------
* Method to restore state of magazine from a file in the work
* directory named "bay_state-N", where N is the bay number.
* On success returns zero, otherwise sets lasterr and
* returns errno.
*-------------------------------------------------*/
int MagazineState::restore()
{
int rc;
tString line, word;
struct stat st;
FILE *FS;
size_t p;
char sname[4096];
if (mag_bay < 0) {
verr.SetErrorWithErrno(EINVAL, "cannot restore state of invalid magazine %d", mag_bay);
log.Error("ERROR! %s", verr.GetErrorMsg());
return EINVAL;
}
clear();
prev_num_slots = 0;
prev_start_slot = 0;
snprintf(sname, sizeof(sname), "%s%sbay_state-%d", conf.work_dir.c_str(), DIR_DELIM, mag_bay);
/* Check for existing state file */
if (stat(sname, &st)) {
/* magazine bay state file not found, so bay did not previously
* contain a magazine */
return 0;
}
/* Read bay state file */
FS = fopen(sname, "r");
if (!FS) {
/* No read permission? */
rc = errno;
verr.SetErrorWithErrno(rc, "cannot open magazine %d state file for reading", mag_bay);
log.Error("ERROR! %s", verr.GetErrorMsg());
return rc;
}
if (tGetLine(line, FS) == NULL) {
rc = errno;
if (!feof(FS)) {
/* error reading bay state file */
fclose(FS);
verr.SetErrorWithErrno(rc, "error reading magazine %d state file", mag_bay);
log.Error("ERROR! %s", verr.GetErrorMsg());
return rc;
}
}
fclose(FS);
/* Get magazine device (UUID or path specified in config) */
tStrip(tRemoveEOL(line));
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);
unlink(sname);
return 0;
}
if (mag_dev != word) {
/* Order of mag bays has changed in config file so ignore old state */
unlink(sname);
return 0;
}
/* Get number of slots */
if (tParseCSV(word, line, p) <= 0) {
/* 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);
unlink(sname);
return 0;
}
if (!isdigit(word[0])) {
/* 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);
return 0;
}
prev_num_slots = (int)strtol(word.c_str(), NULL, 10);
if (prev_num_slots < 0) {
/* Corrupt bay state file, assume it doesn't exist */
clear();
prev_num_slots = 0;
unlink(sname);
log.Warning("WARNING! magazine %d state file has invalid number of slots field, deleting it", mag_bay);
return 0;
}
/* Get virtual slot number offset */
if (tParseCSV(word, line, p) <= 0) {
/* Bay state file is corrupt.
* 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);
unlink(sname);
return 0;
}
if (!isdigit(word[0])) {
/* Corrupt bay state file, assume it doesn't exist */
clear();
prev_num_slots = 0;
unlink(sname);
log.Warning("WARNING! magazine %d state file has invalid virtual slot assignment field, deleting it",
mag_bay);
return 0;
}
prev_start_slot = (int)strtol(word.c_str(), NULL, 10);
if (prev_start_slot <= 0) {
/* Corrupt bay state file, assume it doesn't exist */
clear();
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",
mag_bay);
return 0;
}
log.Notice("restored state of magazine %d", mag_bay);
return 0;
}
/*-------------------------------------------------
* Method to update a magazine from vchanger version 0.x format
* to the format used with version 1.0.0 or higher.
* Return values are:
* 0 Success
*-------------------------------------------------*/
int MagazineState::UpdateMagazineFormat()
{
FILE *fs;
DIR *dir;
struct dirent *de;
struct stat st;
tString str, fname, lname, vname;
int drv;
/* Rename driveN files to their volume file name */
dir = opendir(mountpoint.c_str());
if (!dir) return -1;
de = readdir(dir);
while (de) {
/* Skip if not regular file */
tFormat(fname, "%s%s%s", mountpoint.c_str(), DIR_DELIM, de->d_name);
stat(fname.c_str(), &st);
if (!S_ISREG(st.st_mode)) {
de = readdir(dir);
continue;
}
/* Skip index file */
if (tCaseCmp(de->d_name, "index") == 0) {
de = readdir(dir);
continue;
}
str = de->d_name;
if (str.find("drive") == 0) {
str.erase(0, 5);
if (str.find_first_of("0123456789") == tString::npos) {
de = readdir(dir);
continue;
}
if (str.find_first_not_of("0123456789") != tString::npos) {
de = readdir(dir);
continue;
}
drv = (int)strtol(str.c_str(), NULL, 10);
tFormat(lname, "%s%sloaded%d", mountpoint.c_str(), DIR_DELIM, drv);
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());
de = readdir(dir);
continue;
}
tGetLine(str, fs);
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());
de = readdir(dir);
continue;
}
tStrip(tRemoveEOL(str));
tFormat(vname, "%s%s%s", mountpoint.c_str(), DIR_DELIM, str.c_str());
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());
}
}
de = readdir(dir);
}
closedir(dir);
/* Delete loadedN files */
dir = opendir(mountpoint.c_str());
de = readdir(dir);
while (de) {
str = de->d_name;
/* Skip if not regular file */
tFormat(fname, "%s%s%s", mountpoint.c_str(), DIR_DELIM, de->d_name);
stat(fname.c_str(), &st);
if (!S_ISREG(st.st_mode)) {
de = readdir(dir);
continue;
}
if (str.find("loaded") == 0) {
str.erase(0, 6);
if (str.find_first_of("0123456789") == tString::npos) {
de = readdir(dir);
continue;
}
if (str.find_first_not_of("0123456789") != tString::npos) {
de = readdir(dir);
continue;
}
unlink(fname.c_str());
}
de = readdir(dir);
}
closedir(dir);
/* Delete index file */
tFormat(fname, "%s%sindex", mountpoint.c_str(), DIR_DELIM);
unlink(fname.c_str());
log.Warning("magaine %d updated from old format", mag_bay);
return 0;
}
/*-------------------------------------------------
* Method to determine mountpoint of magazine and assign its volume files
* to magazine slots. Regular files on the magazine are assigned slots in
* ascending alphanumeric order by filename beginning with magazine slot zero.
* If the magazine's device string begins with "UUID:" (case insensitive),
* then it specifies the UUID of a file system on a disk partition to be used
* as the virtual magazine. Otherwise, it specifies a directory to be used as
* the virtual magazine. If a UUID is given, then the system is queried to
* determine the mountpoint of the filesystem with the given UUID. The magazine
* device must already be mounted or configured to be auto-mounted.
* Return values are:
* 0 Magazine assigned successfully
* -1 system error
* -2 parameter error
* -3 magname not found or not mounted
* -5 permission denied
*-------------------------------------------------*/
int MagazineState::Mount()
{
int rc, s;
DIR *dir;
struct dirent *de;
struct stat st;
tString fname, line, path;
MagazineSlot v;
std::list<tString> vname;
std::list<tString>::iterator p;
char buf[4096];
clear();
if (tCaseFind(mag_dev, "uuid:") != 0) {
/* magazine specified as filesystem path */
mountpoint = mag_dev;
} else {
/* magazine specified as UUID, so query OS for mountpoint */
rc = GetMountpointFromUUID(buf, sizeof(buf), mag_dev.substr(5).c_str());
mountpoint = buf;
if (rc == -3 || rc == -4) {
/* magazine device not found or not mounted */
mountpoint.clear();
return -3;
}
if (rc) {
verr.SetError(rc, "system error determining mountpoint from UUID");
log.Error("ERROR! %s", verr.GetErrorMsg());
mountpoint.clear();
return -1;
}
}
/* Check mountpoint exists */
if (access(mountpoint.c_str(), F_OK) != 0) {
/* Mountpoint not found */
mountpoint.clear();
return -3;
}
/* 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());
mountpoint.clear();
return -5;
}
/* If this magazine contains a file named index then assume it was
* created by an old version of vchanger and prepare it for use
* by removing meta-information files. */
tFormat(fname, "%s%sindex", mountpoint.c_str(), DIR_DELIM);
if (access(fname.c_str(), F_OK) == 0) {
UpdateMagazineFormat();
}
/* Build list of this magazine's volume files */
dir = opendir(mountpoint.c_str());
if (!dir) {
/* could not open mountpoint dir */
rc = errno;
verr.SetErrorWithErrno(rc, "cannot open directory '%s'", mountpoint.c_str());
log.Error("ERROR! %s", verr.GetErrorMsg());
mountpoint.clear();
if (rc == ENOTDIR || rc == ENOENT) return -3;
if (rc == EACCES) return -5;
return -1;
}
de = readdir(dir);
while (de) {
/* Skip if not regular file */
tFormat(path, "%s%s%s", mountpoint.c_str(), DIR_DELIM, de->d_name);
stat(path.c_str(), &st);
if (!S_ISREG(st.st_mode)) {
de = readdir(dir);
continue;
}
/* Writable regular files on magazine are considered volume files */
if (access(path.c_str(), W_OK) == 0) {
vname.push_back(de->d_name);
}
de = readdir(dir);
}
closedir(dir);
if (vname.empty()) {
/* Magazine is ready for use but has no volumes */
start_slot = 0;
num_slots = 0;
return 0;
}
/* Assign volume files to slots in alphanumeric order */
vname.sort();
s = 0;
for (p = vname.begin(); p != vname.end(); p++) {
v.mag_bay = mag_bay;
v.label = *p;
v.mag_slot = s++;
mslot.push_back(v);
}
num_slots = (int)mslot.size();
return 0;
}
/*-------------------------------------------------
* Method to get path to volume file in a magazine slot
* On success returns path, else returns empty string
*-------------------------------------------------*/
const char* MagazineState::GetVolumeLabel(int ms) const
{
if (ms >= 0 && ms < (int)mslot.size() && !mslot[ms].empty()) {
return mslot[ms].label.c_str();
}
return "";
}
/*-------------------------------------------------
* Method to get path to volume file in a magazine slot
* On success returns path, else returns empty string
*-------------------------------------------------*/
tString MagazineState::GetVolumePath(int ms)
{
tString result;
if (ms >= 0 && ms < (int)mslot.size()) {
tFormat(result, "%s%s%s", mountpoint.c_str(), DIR_DELIM, GetVolumeLabel(ms));
}
return result;
}
/*-------------------------------------------------
* Method to get path to volume file in a magazine slot
* On success returns path, else returns empty string
*-------------------------------------------------*/
const char* MagazineState::GetVolumePath(tString &path, int ms)
{
path = GetVolumePath(ms);
return path.c_str();
}
/*-------------------------------------------------
* Method to get magazine slot containing a label.
* On success returns magazine slot number, else negative.
*-------------------------------------------------*/
int MagazineState::GetVolumeSlot(const char *label)
{
int n;
for (n = 0; n < num_slots; n++) {
if (mslot[n].label == label) return n;
}
return -1;
}
/*-------------------------------------------------
* Method to create a new volume file. 'vol_label_in' gives the
* name of the new volume file to create on the magazine. If empty,
* then a volume file name is generated based on the magazine's name.
* A new magazine slot is appended to hold the new volume and a new
* virtual slot is appended that maps to the new magazine slot.
* On success returns zero, else sets lasterr and returns negative
*-------------------------------------------------*/
int MagazineState::CreateVolume(const char *vol_label_in)
{
int rc = 0, slot;
FILE *fs;
tString fname, label(vol_label_in);
MagazineSlot new_mslot;
if (label.empty()) {
slot = (int)mslot.size();
--slot;
while(rc == 0) {
++slot;
tFormat(label, "%s_%d_%d", conf.storage_name.c_str(), mag_bay, slot);
tFormat(fname, "%s%s%s", mountpoint.c_str(), DIR_DELIM, label.c_str());
if (access(fname.c_str(), F_OK)) rc = errno;
else rc = 0;
}
} else {
tFormat(fname, "%s%s%s", mountpoint.c_str(), DIR_DELIM, label.c_str());
if (access(fname.c_str(), F_OK)) rc = errno;
else rc = 0;
if (rc == 0) {
verr.SetErrorWithErrno(rc, "volume %s already exists on magazine %d", label.c_str(), mag_bay);
return EEXIST;
}
}
if (rc != ENOENT) {
verr.SetErrorWithErrno(rc, "error %d accessing volumes on magazine %d", rc, mag_bay);
log.Error("MagazineState::CreateVolume: %s", verr.GetErrorMsg());
return -1;
}
/* Create new volume file on magazine */
fs = fopen(fname.c_str(), "w");
if (!fs) {
rc = errno;
verr.SetErrorWithErrno(rc, "error %d creating volume on magazine %d", rc, mag_bay);
log.Error("MagazineState::CreateVolume: %s", verr.GetErrorMsg());
return -1;
}
fclose(fs);
new_mslot.mag_bay = mag_bay;
new_mslot.mag_slot = mslot.size();
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());
return 0;
}
/*-------------------------------------------------
* Method to assign bay number and device for this magazine
*-------------------------------------------------*/
void MagazineState::SetBay(int bay, const char *dev)
{
mag_bay = bay;
mag_dev = dev;
clear();
}
///////////////////////////////////////////////////
// Class VirtualSlot
///////////////////////////////////////////////////
VirtualSlot::VirtualSlot(const VirtualSlot &b)
{
vs = b.vs;
drv = b.drv;
mag_bay = b.mag_bay;
mag_slot = b.mag_slot;
}
VirtualSlot& VirtualSlot::operator=(const VirtualSlot &b)
{
if (this != &b) {
vs = b.vs;
drv = b.drv;
mag_bay = b.mag_bay;
mag_slot = b.mag_slot;
}
return *this;
}
/*-------------------------------------------------
* Method to clear an virtual slot's values
*-------------------------------------------------*/
void VirtualSlot::clear()
{
drv = -1;
mag_bay = -1;
mag_slot = -1;
}
///////////////////////////////////////////////////
// Class DriveState
///////////////////////////////////////////////////
DriveState::DriveState(const DriveState &b)
{
drv = b.drv;
vs = b.vs;
}
DriveState& DriveState::operator=(const DriveState &b)
{
if (&b != this) {
drv = b.drv;
vs = b.vs;
}
return *this;
}
/*
* Method to clear a drive's values
*/
void DriveState::clear()
{
/* do not clear drive number */
vs = -1;
}
///////////////////////////////////////////////////
// Class DynamicConfig
///////////////////////////////////////////////////
/*-------------------------------------------------
* Method to save dynamic configuration info to a file in
* the work directory named dynamic.conf.
*-------------------------------------------------*/
void DynamicConfig::save()
{
mode_t old_mask;
int rc;
FILE *FS;
char sname[4096];
if (max_slot < 10) max_slot = 10;
/* Build path to dynamic.conf file */
snprintf(sname, sizeof(sname), "%s%sdynamic.conf", conf.work_dir.c_str(), DIR_DELIM);
/* Write dynamic config info */
old_mask = umask(027);
FS = fopen(sname, "w");
if (!FS) {
/* 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);
return;
}
/* Save max slot number in use to dynamic configuration */
if (fprintf(FS, "max_used_slot=%d\n", max_slot) < 0) {
/* I/O error writing dynamic.conf file */
rc = errno;
fclose(FS);
unlink(sname);
umask(old_mask);
log.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);
}
/*-------------------------------------------------
* Method to restore dynamic configuration info.
*-------------------------------------------------*/
void DynamicConfig::restore()
{
int rc;
tString line;
struct stat st;
FILE *FS;
char sname[4096];
if (max_slot < 10) max_slot = 10;
/* Build path to dynamic.conf file */
snprintf(sname, sizeof(sname), "%s%sdynamic.conf", conf.work_dir.c_str(), DIR_DELIM);
/* Check for existing file */
if (stat(sname, &st)) {
/* dynamic configuration file not found */
return;
}
/* Read dynamic.conf file */
FS = fopen(sname, "r");
if (!FS) {
/* No read permission? */
rc = errno;
log.Error("ERROR! cannot open dynamic.conf file for restore (errno=%d)", rc);
return;
}
if (tGetLine(line, FS) == NULL) {
rc = errno;
if (!feof(FS)) {
/* error reading bay state file */
fclose(FS);
log.Error("ERROR! i/o error reading dynamic.conf file (errno=%d)", rc);
return;
}
}
fclose(FS);
/* Get magazine device (UUID or path specified in config) */
tStrip(tRemoveEOL(line));
if (tCaseFind(line, "max_used_slot") == 0) {
max_slot = (int)strtol(line.substr(14).c_str(), NULL, 10);
if (max_slot < 10) max_slot = 10;
}
}

137
src/changerstate.h Normal file
View File

@@ -0,0 +1,137 @@
/* changerstate.h
*
* This file is part of vchanger by Josh Fisher.
*
* vchanger copyright (C) 2008-2015 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 CHANGERSTATE_H_
#define CHANGERSTATE_H_
#include <vector>
#include "tstring.h"
#include "errhandler.h"
class MagazineSlot
{
public:
MagazineSlot() : mag_bay(-1), mag_slot(-1) {}
MagazineSlot(const MagazineSlot &b);
virtual ~MagazineSlot() {}
MagazineSlot& operator=(const MagazineSlot &b);
bool operator==(const MagazineSlot &b);
bool operator!=(const MagazineSlot &b);
void clear();
inline bool empty() { return label.empty(); }
inline bool empty() const { return label.empty(); }
inline bool operator==(const char *lab) { return (label == lab); }
inline bool operator!=(const char *lab) { return (label != lab); }
public:
int mag_bay;
int mag_slot;
tString label;
};
typedef std::vector<MagazineSlot> MagazineSlotArray;
class MagazineState
{
public:
MagazineState() : mag_bay(-1), num_slots(0), start_slot(0), prev_num_slots(0), prev_start_slot(0) {}
MagazineState(const MagazineState &b);
virtual ~MagazineState() {}
MagazineState& operator=(const MagazineState &b);
void clear();
int save();
int restore();
int Mount();
void SetBay(int bay, const char *dev);
inline void SetBay(int bay, const tString &dev) { SetBay(bay, dev.c_str()); }
tString GetVolumePath(int mag_slot);
const char* GetVolumePath(tString &path, int slot);
const char* GetVolumeLabel(int mag_slot) const;
int GetVolumeSlot(const char *fname);
inline int GetVolumeSlot(const tString &fname) { return GetVolumeSlot(fname.c_str()); }
int CreateVolume(const char *vol_label = "");
inline int CreateVolume(const tString &labl) { return CreateVolume(labl.c_str()); }
inline bool empty() { return mountpoint.empty(); }
inline bool empty() const { return mountpoint.empty(); }
protected:
int ReadMagazineIndex();
int UpdateMagazineFormat();
public:
int mag_bay;
int num_slots;
int start_slot;
int prev_num_slots;
int prev_start_slot;
tString mag_dev;
tString mountpoint;
MagazineSlotArray mslot;
ErrorHandler verr;
};
typedef std::vector<MagazineState> MagazineStateArray;
class VirtualSlot
{
public:
VirtualSlot() : vs(0), drv(-1), mag_bay(-1), mag_slot(-1) {}
VirtualSlot(const VirtualSlot &b);
virtual ~VirtualSlot() {}
VirtualSlot& operator=(const VirtualSlot &b);
void clear();
bool empty() { return mag_bay < 0; }
bool empty() const { return mag_bay < 0; }
public:
int vs;
int drv;
int mag_bay;
int mag_slot;
};
typedef std::vector<VirtualSlot> VirtualSlotArray;
class DynamicConfig
{
public:
DynamicConfig() : max_slot(0) {}
void save();
void restore();
public:
int max_slot;
};
class DriveState
{
public:
DriveState() : drv(-1), vs(-1) {}
DriveState(const DriveState &b);
virtual ~DriveState() {}
DriveState& operator=(const DriveState &b);
void clear();
inline bool empty() { return vs < 0; }
inline bool empty() const { return vs < 0; }
public:
int drv;
int vs;
};
typedef std::vector<DriveState> DriveStateArray;
#endif /*CHANGERSTATE_H_*/

81
src/compat/getline.c Normal file
View File

@@ -0,0 +1,81 @@
/* getline.c
*
* This file is part of vchanger by Josh Fisher.
*
* vchanger copyright (C) 2008-2013 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_GETLINE
#ifdef HAVE_WINDOWS_H
#include "targetver.h"
#endif
#include <stdio.h>
#ifdef HAVE_STDLIB_H
#include <stdlib.h>
#endif
#ifdef HAVE_SYS_TYPES_H
#include <sys/types.h>
#endif
#include "compat/getline.h"
/*
* Emulate GNU extension function getline().
*/
ssize_t getline(char **lineptr, size_t *lineptr_sz, FILE *stream)
{
int c;
size_t n = 0;
c = fgetc(stream);
if (c == EOF) {
return -1;
}
if (*lineptr == NULL) {
*lineptr_sz = 4096;
*lineptr = (char*)malloc(*lineptr_sz);
if (*lineptr == NULL) {
return -1;
}
}
while (c != EOF && c != '\n')
{
if (n + 1 >= *lineptr_sz) {
*lineptr_sz += 4096;
*lineptr = (char*)realloc(*lineptr, *lineptr_sz);
if (*lineptr == NULL) {
return -1;
}
}
(*lineptr)[n++] = (char)c;
c = fgetc(stream);
}
if (c == '\n') {
if (n && (*lineptr)[n-1] == '\r') {
(*lineptr)[n-1] = '\n';
} else {
(*lineptr)[n++] = '\n';
}
}
(*lineptr)[n] = 0;
return (ssize_t)n;
}
#endif

38
src/compat/getline.h Normal file
View File

@@ -0,0 +1,38 @@
/* getline.h
*
* This file is part of vchanger by Josh Fisher.
*
* vchanger copyright (C) 2008-2013 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 _GETLINE_H_
#define _GETLINE_H_
#ifndef HAVE_GETLINE
/* For systems without getline function, use internal version */
#ifdef __cplusplus
extern "C" {
#endif
ssize_t getline(char **lineptr, size_t *n, FILE *stream);
#ifdef __cplusplus
}
#endif
#endif
#endif /* _GETLINE_H */

118
src/compat/gettimeofday.c Normal file
View File

@@ -0,0 +1,118 @@
/* gettimeofday.c
*
* This file is part of vchanger by Josh Fisher.
*
* vchanger copyright (C) 2008-2013 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_GETTIMEOFDAY
#ifdef HAVE_WINDOWS_H
#include "targetver.h"
#include <windows.h>
#endif
#ifdef HAV_SYS_TYPES_H
#include <sys/types.h>
#endif
#ifdef HAVE_TIME_H
#include <time.h>
#endif
#ifdef HAVE_SYS_TIME_H
#include <sys/time.h>
#endif
#include "compat/gettimeofday.h"
#ifndef HAVE_WINDOWS_H
/*
* Emulate POSIX.1-2001 gettimeofday() function using the time() function.
*/
int gettimeofday(struct timeval *tv, struct timezone* tz)
{
if (tv != NULL) {
tv->tv_sec = time(NULL);
tv->tv_usec = 0;
}
if (tz != NULL) {
if (!tzflag) {
tzset();
tzflag++;
}
// Adjust for the timezone west of Greenwich
tz->tz_minuteswest = _timezone / 60;
tz->tz_dsttime = _daylight;
}
return 0;
}
#else
/*
* Emulate POSIX.1-2001 gettimeofday() function using the win32
* GetSystemTimeAsFIleTime() system function.
*/
int gettimeofday(struct timeval *tv, struct timezone* tz)
{
FILETIME ft;
int64_t tmpres = 0;
static int tzflag = 0;
if (tv != NULL) {
GetSystemTimeAsFileTime(&ft);
/* The GetSystemTimeAsFileTime returns the number of 100 nanosecond
* intervals since Jan 1, 1601 in a structure. Copy the high bits to
* the 64 bit tmpres, shift it left by 32 then or in the low 32 bits.
*/
tmpres |= ft.dwHighDateTime;
tmpres <<= 32;
tmpres |= ft.dwLowDateTime;
/* Convert to microseconds by dividing by 10 */
tmpres /= 10;
/* The Unix epoch starts on Jan 1 1970. Need to subtract the difference
* in seconds from Jan 1 1601.
*/
tmpres -= DELTA_EPOCH_IN_MICROSECS;
/* Finally change microseconds to seconds and place in the seconds value.
* The modulus picks up the microseconds.
*/
tv->tv_sec = tmpres / 1000000L;
tv->tv_usec = (tmpres % 1000000L);
}
if (NULL != tz) {
if (!tzflag) {
_tzset();
tzflag++;
}
// Adjust for the timezone west of Greenwich
tz->tz_minuteswest = _timezone / 60;
tz->tz_dsttime = _daylight;
}
return 0;
}
#endif
#endif

62
src/compat/gettimeofday.h Normal file
View File

@@ -0,0 +1,62 @@
/* gettimeofday.h
*
* This file is part of vchanger by Josh Fisher.
*
* vchanger copyright (C) 2008-2013 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 _GETTIMEOFDAY_H
#define _GETTIMEOFDAY_H
#ifndef HAVE_GETTIMEOFDAY
/* For systems without gettimeofday function, use internal version */
#ifdef HAVE_WINDOWS_H
#define EPOCH_FILETIME (116444736000000000LL) // 100 ns intervals between FILETIME epoch and Unix epoch
#if defined(_MSC_VER) || defined(_MSC_EXTENSIONS)
#define DELTA_EPOCH_IN_MICROSECS 11644473600000000Ui64
#else
#define DELTA_EPOCH_IN_MICROSECS 11644473600000000ULL
#endif
#define suseconds_t long
struct timeval
{
time_t tv_sec;
suseconds_t tv_usec;
};
struct timezone
{
int tz_minuteswest;
int tz_dsttime;
};
#endif
#ifdef __cplusplus
extern "C" {
#endif
int gettimeofday(struct timeval *tv, struct timezone *tz);
#ifdef __cplusplus
}
#endif
#endif
#endif /* _GETTIMEOFDAY_H */

46
src/compat/getuid.c Normal file
View File

@@ -0,0 +1,46 @@
/* getuid.c
*
* This file is part of vchanger by Josh Fisher.
*
* vchanger copyright (C) 2008-2014 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_GETUID
#ifdef HAVE_SYS_TYPES_H
#include <sys/types.h>
#endif
#include "compat/getuid.h"
/*
* Emulate GNU extension function getline().
*/
uid_t getuid()
{
return 999;
}
gid_t getgid()
{
return 999;
}
#endif

45
src/compat/getuid.h Normal file
View File

@@ -0,0 +1,45 @@
/* getuid.h
*
* This file is part of vchanger by Josh Fisher.
*
* vchanger copyright (C) 2008-2014 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 _GETUID_H_
#define _GETUID_H_
#ifndef HAVE_GETUID
/* For systems without getuid function, use internal version */
#ifdef HAVE_WINDOWS_H
typedef unsigned int uid_t;
typedef unsigned int gid_t;
#endif
#ifdef __cplusplus
extern "C" {
#endif
uid_t getuid();
gid_t getgid();
#ifdef __cplusplus
}
#endif
#endif
#endif /* _GETUID_H */

57
src/compat/localtime_r.c Normal file
View File

@@ -0,0 +1,57 @@
/*
*
* This file is part of vchanger by Josh Fisher.
*
* vchanger copyright (C) 2008-2013 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_LOCALTIME_R
#ifdef HAVE_WINDOWS_H
#include "targetver.h"
#endif
#ifdef HAVE_TIME_H
#include <time.h>
#endif
#ifdef HAVE_SYS_TIME_H
#include <sys/time.h>
#endif
#ifdef HAVE_STRING_H
#include <string.h>
#endif
#include "compat/localtime_r.h"
/*
* Emulate SUSv2 localtime_r() function using C89 localtime(). On Win32
* localtime() is thread safe. For all others, note that this keeps the
* caller from using the unsafe buffer over any extended period,
* but it is still not thread safe and will fail if two threads call this
* function nearly simultaneously.
*/
struct tm* localtime_r(const time_t *timep, struct tm *buf)
{
struct tm *tm1 = localtime(timep);
if (!tm1) return NULL;
memcpy(buf, tm1, sizeof(struct tm));
return buf;
}
#endif

39
src/compat/localtime_r.h Normal file
View File

@@ -0,0 +1,39 @@
/*
*
* This file is part of vchanger by Josh Fisher.
*
* vchanger copyright (C) 2008-2013 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 _LOCALTIME_R_H_
#define _LOCALTIME_R_H_
#ifndef HAVE_LOCALTIME_R
/* For systems without localtime_r function, use internal version */
#ifdef __cplusplus
extern "C" {
#endif
struct tm* localtime_r(const time_t *timep, struct tm *buf);
#ifdef __cplusplus
}
#endif
#endif
#endif /* _LOCALTIME_R_H_ */

126
src/compat/readlink.c Normal file
View File

@@ -0,0 +1,126 @@
/* readlink.c
*
* This file is part of vchanger by Josh Fisher.
*
* vchanger copyright (C) 2013 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_READLINK
#ifdef HAVE_WINDOWS_H
#include "targetver.h"
#include <windows.h>
#endif
#ifdef HAVE_SYS_TYPES_H
#include <sys/types.h>
#endif
#ifdef HAVE_STDDEF_H
#include <stddef.h>
#endif
#ifdef HAVE_ERRNO_H
#include <errno.h>
#endif
#ifdef HAVE_TIME_H
#include <time.h>
#endif
#include "win32_util.h"
#include "compat/readlink.h"
#ifdef HAVE_WINDOWS_H
/*-------------------------------------------------
* Emulate POSIX.1-2001 readlink() function using win32/win64 CreateFileW()
* and GetFinalPathNameByHandleW() functions.
* On success returns the length of the target path string. On
* error returns -1 and sets errno.
*-------------------------------------------------*/
ssize_t readlink(const char *path_in, char *buf, size_t bufsiz)
{
HANDLE hFS;
DWORD dw;
wchar_t *p16, *path = NULL;
size_t path_sz = 0;
/* Convert path string to UTF16 encoding */
if (!AnsiToUTF16(path_in, &path, &path_sz)) {
errno = w32errno(ERROR_BAD_PATHNAME);
return -1;
}
/* Check attributes of path to see if it is a symlink */
dw = GetFileAttributesW(path);
if (dw == INVALID_FILE_ATTRIBUTES) {
/* Error getting attributes */
errno = w32errno(GetLastError());
free(path);
return -1;
}
if ((dw & FILE_ATTRIBUTE_REPARSE_POINT) == 0) {
/* path does not point to a symlink */
errno = EINVAL;
return -1;
}
/* Get an open handle to the symlink's target file */
hFS = CreateFileW(path, FILE_READ_ATTRIBUTES, FILE_SHARE_READ|FILE_SHARE_WRITE|FILE_SHARE_DELETE,
NULL, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, NULL);
if (hFS == INVALID_HANDLE_VALUE) {
/* Failed to open file */
errno = w32errno(GetLastError());
free(path);
return -1;
}
/* Get the full final UTF-16 path name of the opened file */
free(path);
path = (wchar_t*)malloc(65536);
dw = GetFinalPathNameByHandleW(hFS, path, 32768, FILE_NAME_OPENED);
if (dw == 0) {
/* Failed to get final path */
errno = w32errno(GetLastError());
CloseHandle(hFS);
free(path);
return -1;
}
/* Close file handle and get pointer to UTF-16 target path */
CloseHandle(hFS);
p16 = path;
if (path[0] == L'\\' && path[2] == L'?' && path[3] == L'\\') {
p16 = path + 4;
dw -= 4;
}
if (dw > bufsiz) {
/* Caller's buffer too small */
errno = EINVAL;
free(path);
return -1;
}
/* Convert target path to ANSI in caller's buffer */
if (!UTF16ToAnsi(p16, &buf, &bufsiz)) {
/* Failed to convert to ANSI */
errno = EINVAL;
free(path);
return -1;
}
free(path);
return strlen(buf);
}
#endif
#endif

42
src/compat/readlink.h Normal file
View File

@@ -0,0 +1,42 @@
/* readlink.h
*
* This file is part of vchanger by Josh Fisher.
*
* vchanger copyright (C) 2008-2013 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 _READLINK_H
#define _READLINK_H
#ifndef HAVE_READLINK
/* For systems without readlink function, use internal version */
#ifdef __cplusplus
extern "C" {
#endif
ssize_t readlink(const char *path, char *buf, size_t bufsiz);
#ifdef __cplusplus
}
#endif
#endif
#endif /* _READLINK_H */

39
src/compat/sleep.c Normal file
View File

@@ -0,0 +1,39 @@
/* sleep.c
*
* This file is part of vchanger by Josh Fisher.
*
* vchanger copyright (C) 2008-2014 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"
#ifdef HAVE_WINDOWS_H
#include "targetver.h"
#include "windows.h"
/*
* Emulate GNU extension function getline().
*/
unsigned int sleep(unsigned int seconds)
{
Sleep(seconds * 1000);
return 0;
}
#endif

42
src/compat/sleep.h Normal file
View File

@@ -0,0 +1,42 @@
/* sleep.h
*
* This file is part of vchanger by Josh Fisher.
*
* vchanger copyright (C) 2008-2014 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 _SLEEP_H
#define _SLEEP_H
#ifdef HAVE_WINDOWS_H
/* For Windows provide sleep() function using Win32 API Sleep() */
#ifdef __cplusplus
extern "C" {
#endif
unsigned int sleep(unsigned int seconds);
#ifdef __cplusplus
}
#endif
#endif
#endif /* _SLEEP_H */

87
src/compat/symlink.c Normal file
View File

@@ -0,0 +1,87 @@
/* symlink.c
*
* This file is part of vchanger by Josh Fisher.
*
* vchanger copyright (C) 2013 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_SYMLINK
#ifdef HAVE_WINDOWS_H
#include "targetver.h"
#include <windows.h>
#include "win32_util.h"
#endif
/*
#ifdef HAVE_ERRNO_H
#include <errno.h>
#endif
*/
#include "compat/symlink.h"
#ifdef HAVE_WINDOWS_H
/*-------------------------------------------------
* Emulate POSIX.1-2001 symlink() function using win32/win64 CreateSymbolicLinkW() function.
* On success returns zero, else negative on error.
*-------------------------------------------------*/
int symlink(const char *oldpath, const char *newpath)
{
DWORD rc, attr, dwFlags;
wchar_t *woldpath = NULL, *wnewpath = NULL;
size_t woldpath_sz = 0, wnewpath_sz = 0;
/* Convert path strings to UTF16 encoding */
if (!AnsiToUTF16(oldpath, &woldpath, &woldpath_sz)) {
rc = ERROR_BAD_PATHNAME;
errno = w32errno(rc);
return -1;
}
if (!AnsiToUTF16(newpath, &wnewpath, &wnewpath_sz)) {
rc = ERROR_BAD_PATHNAME;
errno = w32errno(rc);
free(woldpath);
return -1;
}
/* Determine if oldpath is a file or directory */
attr = GetFileAttributesW(woldpath);
if (attr == INVALID_FILE_ATTRIBUTES) {
free(woldpath);
free(wnewpath);
errno = w32errno(GetLastError());
return -1;
}
dwFlags = (attr & FILE_ATTRIBUTE_DIRECTORY) == 0 ? 0 : 1;
/* Create the symlink */
if (CreateSymbolicLinkW(wnewpath, woldpath, dwFlags)) {
free(woldpath);
free(wnewpath);
return 0;
}
free(woldpath);
free(wnewpath);
errno = w32errno(GetLastError());
return -1;
}
#endif
#endif

42
src/compat/symlink.h Normal file
View File

@@ -0,0 +1,42 @@
/* symlink.h
*
* This file is part of vchanger by Josh Fisher.
*
* vchanger copyright (C) 2013 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 _SYMLINK_H
#define _SYMLINK_H
#ifndef HAVE_SYMLINK
/* For systems without symlink() function, use internal version */
#ifdef __cplusplus
extern "C" {
#endif
int symlink(const char *oldpath, const char *newpath);
#ifdef __cplusplus
}
#endif
#endif
#endif /* _SYMLINK_H */

57
src/compat/syslog.c Normal file
View File

@@ -0,0 +1,57 @@
/* compat_syslog.c
*
* This file is part of vchanger by Josh Fisher.
*
* Provide syslog() replacement function for non-POSIX systems.
*
* vchanger copyright (C) 2008-2013 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_SYSLOG
#ifdef HAVE_STDARG_H
#include <stdarg.h>
#endif
#include "compat/syslog.h"
#ifdef HAVE_WINDOWS_H
/* Windows doesn't do syslog() ... ignore for now. Perhaps win32 event logging
* could be used to emulate syslog in future.
*/
void openlog(const char *app, int option, int facility)
{
}
void closelog(void)
{
}
void syslog(int type, const char *fmt, ...)
{
}
void vsyslog(int type, const char *fmt, va_list ap)
{
}
#endif
#endif

80
src/compat/syslog.h Normal file
View File

@@ -0,0 +1,80 @@
/* compat_syslog.h
*
* This file is part of vchanger by Josh Fisher.
*
* vchanger copyright (C) 2008-2013 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 _COMPAT_SYSLOG_H_
#define _COMPAT_SYSLOG_H_
#ifndef HAVE_SYSLOG
/* For systems without syslog(), provide fake one */
#define LOG_EMERG 0
#define LOG_ALERT 1
#define LOG_CRIT 2
#define LOG_ERR 3
#define LOG_WARNING 4
#define LOG_NOTICE 5
#define LOG_INFO 6
#define LOG_DEBUG 7
#define LOG_KERN 0
#define LOG_USER 8
#define LOG_MAIL 16
#define LOG_DAEMON 24
#define LOG_AUTH 32
#define LOG_SYSLOG 40
#define LOG_LPR 48
#define LOG_NEWS 56
#define LOG_UUCP 64
#define LOG_CRON 72
#define LOG_AUTHPRIV 80
#define LOG_FTP 88
#define LOG_LOCAL0 128
#define LOG_LOCAL1 136
#define LOG_LOCAL2 144
#define LOG_LOCAL3 152
#define LOG_LOCAL4 160
#define LOG_LOCAL5 168
#define LOG_LOCAL6 176
#define LOG_LOCAL7 184
#define LOG_PID 1
#define LOG_CONS 2
#define LOG_ODELAY 4
#define LOG_NDELAY 8
#define LOG_NOWAIT 16
#define LOG_PERROR 32
#ifdef __cplusplus
extern "C" {
#endif
void syslog(int type, const char *fmt, ...);
void vsyslog(int type, const char *fmt, va_list ap);
void openlog(const char *app, int option, int facility);
void closelog(void);
#ifdef __cplusplus
}
#endif
#endif
#endif /* __SYSLOG_H */

37
src/compat_defs.h Normal file
View File

@@ -0,0 +1,37 @@
/* vchanger_common.h
*
* This file is part of vchanger by Josh Fisher.
*
* vchanger copyright (C) 2008-2015 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 _COMPAT_DEFS_H_
#define _COMPAT_DEFS_H_ 1
#ifdef HAVE_WINDOWS_H
#include "targetver.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
#endif /* _VCHANGER_COMMON_H_ */

993
src/diskchanger.cpp Normal file
View File

@@ -0,0 +1,993 @@
/* diskchanger.cpp
*
* This file is part of vchanger by Josh Fisher.
*
* vchanger copyright (C) 2008-2015 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.
*
* The bulk of the work gets done by the DiskChanger class, which does
* the actual "loading" and "unloading" of volumes.
*/
#include "config.h"
#include "compat_defs.h"
#ifdef HAVE_STDIO_H
#include <stdio.h>
#endif
#ifdef HAVE_STDLIB_H
#include <stdlib.h>
#endif
#ifdef HAVE_STRING_H
#include <string.h>
#endif
#ifdef HAVE_SYS_TYPES_H
#include <sys/types.h>
#endif
#ifdef HAVE_DIRENT_H
#include <dirent.h>
#endif
#ifdef HAVE_SYS_STAT_H
#include <sys/stat.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_UNISTD_H
#include <unistd.h>
#endif
#include "compat/gettimeofday.h"
#include "compat/readlink.h"
#include "compat/sleep.h"
#include "compat/symlink.h"
#include "util.h"
#include "loghandler.h"
#include "bconsole.h"
#include "diskchanger.h"
/*=================================================
* Class DiskChanger
*=================================================*/
/*-------------------------------------------------
* destructor
*-------------------------------------------------*/
DiskChanger::~DiskChanger()
{
Unlock();
}
/*-------------------------------------------------
* Protected method to read previous state of magazine bays.
* Returns zero on success, else negative and sets lasterr
*-------------------------------------------------*/
void DiskChanger::InitializeMagazines()
{
int n;
MagazineState m;
magazine.clear();
for (n = 0; (size_t)n < conf.magazine.size(); n++) {
m.SetBay(n, conf.magazine[n].c_str());
m.prev_num_slots = 0;
m.prev_start_slot = 0;
magazine.push_back(m);
/* Restore previous slot count and starting virtual slot */
magazine[n].restore();
/* Get mountpoint and build magazine slot array */
magazine[n].Mount();
}
}
/*-------------------------------------------------
* Protected method to find the start of an empty range of
* 'count' virtual slots, adding slots if needed.
* Returns the starting slot number of the range found.
*------------------------------------------------*/
int DiskChanger::FindEmptySlotRange(int count)
{
VirtualSlot vs;
int start = 0, n = 1, found = 0;
/* Find next empty slot */
while (n < (int)vslot.size() && vslot[n].mag_bay >= 0) n++;
start = n;
while (n < (int)vslot.size() && found < count) {
if (vslot[n].mag_bay < 0) {
++found;
++n;
} else {
found = 0;
while (n < (int)vslot.size() && vslot[n].mag_bay >= 0) n++;
start = n;
}
}
if (found >= count) return start;
while (found < count) {
vs.vs = n++;
vslot.push_back(vs);
++found;
}
return start;
}
/*-------------------------------------------------
* Protected method to initialize array of virtual slot and
* assign magazine volumes to virtual slots. When possible,
* volumes are assigned to the same slot they were in
* previously.
*------------------------------------------------*/
void DiskChanger::InitializeVirtSlots()
{
int s, m, v, last;
VirtualSlot vs;
bool found;
/* Create all known slots as initially empty */
vslot.clear();
for (s = 0; s <= dconf.max_slot; s++) {
vs.vs = s;
vslot.push_back(vs);
}
/* 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;
if (last >= (int)vslot.size()) {
vs.clear();
while ((int)vslot.size() <= last) {
vs.vs = (int)vslot.size();
vslot.push_back(vs);
}
}
/* Check this magazine's slots */
if (magazine[m].empty()) {
log.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,
magazine[m].prev_num_slots, magazine[m].prev_start_slot,
magazine[m].prev_start_slot + magazine[m].prev_num_slots - 1);
needs_update = true;
}
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,
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,
magazine[m].num_slots, magazine[m].prev_num_slots);
needs_update = true;
continue;
}
if (magazine[m].num_slots == 0) {
/* Magazine has no volumes so needs no slot assignment */
continue;
}
/* 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++) {
if (!vslot[v].empty()) {
found = true;
break;
}
}
if (found) {
/* 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,
magazine[m].prev_start_slot, magazine[m].prev_start_slot + magazine[m].prev_num_slots - 1);
needs_update = true;
continue;
}
/* Assign this magazine's volumes to the same slots as previously assigned */
magazine[m].start_slot = magazine[m].prev_start_slot;
for (s = 0; s < magazine[m].num_slots; s++) {
v = magazine[m].start_slot + s;
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,
magazine[m].start_slot, magazine[m].start_slot + magazine[m].num_slots - 1);
}
/* Assign slots to mounted magazines that have not already been assigned. */
for (m = 0; m < (int)magazine.size(); m++) {
if (magazine[m].empty() || magazine[m].start_slot > 0) continue;
if (magazine[m].num_slots == 0) continue;
magazine[m].start_slot = FindEmptySlotRange(magazine[m].num_slots);
for (s = 0; s < magazine[m].num_slots; s++) {
v = magazine[m].start_slot + s;
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,
magazine[m].start_slot, magazine[m].start_slot + magazine[m].num_slots - 1);
}
/* Save updated state of magazines */
for (m = 0; m < (int)magazine.size(); m++) {
magazine[m].save();
}
/* Update dynamic configuration info */
if ((int)vslot.size() >= dconf.max_slot) {
dconf.max_slot = (int)vslot.size() - 1;
dconf.save();
}
}
/*-------------------------------------------------
* Protected method to initialize state of virtual drives.
* On success, returns zero. On error, returns non-zero.
*------------------------------------------------*/
int DiskChanger::InitializeDrives()
{
int n, rc, max_drive = -1;
DIR *d;
struct dirent *de;
DriveState ds;
tString tmp;
/* For each drive for which a state file exists. try to restore its state */
d = opendir(conf.work_dir.c_str());
if (!d) {
rc = errno;
verr.SetErrorWithErrno(rc, "error %d accessing work directory", rc);
log.Error("ERROR! %s", verr.GetErrorMsg());
return rc;
}
de = readdir(d);
while (de) {
/* Find next drive state file */
tmp = de->d_name;
if (tmp.find("drive_state-") == 0) {
tmp.erase(0, 12);
if (tmp.find_first_of("0123456789") == tString::npos) {
de = readdir(d);
continue;
}
if (tmp.find_first_not_of("0123456789") != tString::npos) {
de = readdir(d);
continue;
}
n = (int)strtol(tmp.c_str(), NULL, 10);
if (n > max_drive) max_drive = n;
}
de = readdir(d);
}
closedir(d);
if (max_drive < 0) {
/* No drive state files exist, so create at least one drive */
max_drive = 0;
}
/* Restore last known state of virtual drives where possible. */
for (n = 0; n <= max_drive; n++) {
ds.drv = n;
drive.push_back(ds);
/* Attempt to restore drive's last state */
if (RestoreDriveState(n)) {
log.Error("ERROR! %s", verr.GetErrorMsg());
}
}
return 0;
}
/*-------------------------------------------------
* Protected method to ensure that there are at least n drives
*------------------------------------------------*/
void DiskChanger::SetMaxDrive(int need)
{
int n = (int)drive.size();
DriveState ds;
while (n <= need) {
ds.drv = n++;
drive.push_back(ds);
}
}
/*
* Method to create symlink for drive pointing to currently loaded volume file
*/
int DiskChanger::CreateDriveSymlink(int drv)
{
int mag, mslot, rc;
tString sname, fname;
char lname[4096];
if (drv < 0 || drv >= (int)drive.size()) {
verr.SetError(EINVAL, "cannot create symlink for invalid drive %d", drv);
return EINVAL;
}
if (drive[drv].vs <= 0) {
verr.SetError(ENOENT, "cannot create symlink for unloaded drive %d", drv);
return ENOENT;
}
mag = vslot[drive[drv].vs].mag_bay;
mslot = vslot[drive[drv].vs].mag_slot;
fname = magazine[mag].GetVolumePath(mslot);
if (fname.empty()) {
verr.SetError(ENOENT, "cannot create symlink for unloaded drive %d", drv);
return ENOENT;
}
tFormat(sname, "%s%s%d", conf.work_dir.c_str(), DIR_DELIM, drv);
rc = readlink(sname.c_str(), lname, sizeof(lname));
if (rc > 0) {
if (rc >= (int)sizeof(lname)) {
verr.SetError(ENAMETOOLONG, "symlink target too long on readlink for drive %d", drv);
return ENAMETOOLONG;
}
lname[rc] = 0;
if (fname == lname) {
/* symlink already exists */
log.Info("found symlink for drive %d -> %s", drv, fname.c_str());
return 0;
}
/* Symlink points to wrong mountpoint, so delete and re-create */
if (RemoveDriveSymlink(drv)) return EEXIST;
}
if (symlink(fname.c_str(), sname.c_str())) {
rc = errno;
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());
return 0;
}
/*-------------------------------------------------
* Method to delete this drive's symlink.
* On success returns zero, else on error sets lasterr and
* returns errno.
*-------------------------------------------------*/
int DiskChanger::RemoveDriveSymlink(int drv)
{
int rc;
tString sname;
if (drv < 0 || drv >= (int)drive.size()) {
verr.SetError(EINVAL, "cannot delete symlink for invalid drive %d", drv);
return EINVAL;
}
/* Remove symlink pointing to loaded volume file */
tFormat(sname, "%s%s%d", conf.work_dir.c_str(), DIR_DELIM, drv);
if (unlink(sname.c_str())) {
if (errno == ENOENT) return 0; /* Ignore if not found */
/* System error preventing deletion of symlink */
rc = errno;
verr.SetErrorWithErrno(errno, "error %d deleting symlink for drive %d: ", rc, drv);
return rc;
}
log.Notice("deleted symlink for drive %d", drv);
return 0;
}
/*-------------------------------------------------
* Method to save current drive state, device string and
* volume label (filename), to a file in the work
* directory named "drive_state-N", where N is the drive number.
* On success returns zero, else on error sets lasterr and
* returns errno.
*-------------------------------------------------*/
int DiskChanger::SaveDriveState(int drv)
{
mode_t old_mask;
FILE *FS;
int rc, mag, mslot;
tString sname;
if (drv < 0 || drv >= (int)drive.size()) {
verr.SetError(EINVAL, "cannot save state of invalid drive %d", drv);
return EINVAL;
}
/* Delete old state file */
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);
}
unlink(sname.c_str());
return 0;
}
old_mask = umask(027);
FS = fopen(sname.c_str(), "w");
if (!FS) {
/* Unable to open state file */
rc = errno;
umask(old_mask);
verr.SetErrorWithErrno(rc, "failed opening state file for drive %d", drv);
return rc;
}
mag = vslot[drive[drv].vs].mag_bay;
mslot = vslot[drive[drv].vs].mag_slot;
if (fprintf(FS, "%s,%s\n", magazine[mag].mag_dev.c_str(),
magazine[mag].GetVolumeLabel(mslot)) < 0) {
/* I/O error writing state file */
rc = errno;
fclose(FS);
umask(old_mask);
verr.SetErrorWithErrno(rc, "error %d writing state file for drive %d", rc, drv);
return rc;
}
fclose(FS);
umask(old_mask);
log.Notice("wrote state file for drive %d", drv);
return 0;
}
/*-------------------------------------------------
* Method to restore drive state from a file in the work directory
* named "drive_state-N", where N is drive_number. If the volume
* previously loaded is available, then restore drive to the loaded
* state, otherwise set drive unloaded and remove the symlink and
* state file for this drive.
* On success returns zero, else on error sets lasterr and
* returns errno.
*-------------------------------------------------*/
int DiskChanger::RestoreDriveState(int drv)
{
int rc, v, m, ms;
tString line, dev, labl;
size_t p;
struct stat st;
FILE *FS;
tString sname;
if (drv < 0 || drv >= (int)drive.size()) {
verr.SetError(EINVAL, "cannot restore state of invalid drive %d", drv);
return EINVAL;
}
drive[drv].clear();
/* Check for existing state file */
tFormat(sname, "%s%sdrive_state-%d", conf.work_dir.c_str(), DIR_DELIM, 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);
return 0;
}
/* Read loaded volume info from state file */
FS = fopen(sname.c_str(), "r");
if (!FS) {
/* Error opening state file, so leave drive unloaded */
verr.SetError(EACCES, "drive %d state file is not readable", drv);
return EACCES;
}
if (tGetLine(line, FS) == NULL) {
if (!feof(FS)) {
/* i/o error reading line from state file. Change state to unloaded */
rc = ferror(FS);
fclose(FS);
unlink(sname.c_str());
RemoveDriveSymlink(drv);
verr.SetErrorWithErrno(rc, "error %d reading state file for drive %d", rc, drv);
return rc;
}
}
fclose(FS);
tStrip(tRemoveEOL(line));
/* Extract the device drive was last loaded from */
p = 0;
rc = tParseCSV(dev, line, p);
if (rc != 1 || dev.empty()) {
/* Device string not found. Change state to unloaded. */
verr.SetError(EINVAL, "deleting corrupt state file for drive %d", drv);
unlink(sname.c_str());
RemoveDriveSymlink(drv);
return EINVAL;
}
/* Extract label of volume drive was last loaded from */
rc = tParseCSV(labl, line, p);
if (rc != 1 || labl.empty()) {
/* Label string not found. Change state to unloaded. */
verr.SetError(EINVAL, "deleting corrupt state file for drive %d", drv);
unlink(sname.c_str());
RemoveDriveSymlink(drv);
return EINVAL;
}
/* Find virtual slot assigned the volume file last loaded in drive */
for (v = 1; v < (int)vslot.size(); v++) {
if (labl == GetVolumeLabel(v)) break;
}
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",
labl.c_str(), drv);
unlink(sname.c_str());
RemoveDriveSymlink(drv);
return 0;
}
drive[drv].vs = v;
/* Ensure symlink exists or create it */
if ((rc = CreateDriveSymlink(drv)) != 0) {
/* Unable to create symlink */
drive[drv].vs = -1;
log.Error("ERROR! %s", verr.GetErrorMsg());
return rc;
}
/* Assign drive to virtual slot */
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));
return 0;
}
/*-------------------------------------------------
* Method to initialize changer parameters and state of magazines,
* virtual slots, and virtual drives.
* On success, returns zero. On error, returns negative.
* In either case, obtains a lock on the changer unless the lock operation
* itself fails. The lock will be released when the DiskChanger object
* is destroyed.
*------------------------------------------------*/
int DiskChanger::Initialize()
{
/* Make sure we have a lock on this changer */
if (Lock()) return verr.GetError();
magazine.clear();
vslot.clear();
drive.clear();
dconf.restore();
needs_update = false;
/* Initialize array of mounted magazines */
InitializeMagazines();
/* Initialize array of virtual slots */
InitializeVirtSlots();
/* Initialize array of virtual drives */
if (InitializeDrives()) return verr.GetError();
return 0;
}
/*-------------------------------------------------
* Method to load virtual drive 'drv' from virtual slot 'slot'.
* Returns zero on success, else sets lasterr and
* returns negative.
*------------------------------------------------*/
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());
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());
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());
return EBUSY;
}
if (vslot[slot].empty()) {
verr.SetError(EINVAL, "cannot load drive %d from empty slot %d", drv, slot);
log.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());
return rc;
}
/* Save state of newly loaded drive */
if ((rc = SaveDriveState(drv)) != 0) {
/* Error writing drive state file */
RemoveDriveSymlink(drv);
drive[drv].vs = -1;
log.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));
return 0;
}
/*-------------------------------------------------
* Method to unload volume in virtual drive 'drv'. Deletes symlink
* and state file for the drive.
* On success, returns zero. Otherwise sets lasterr and returns
* errno.
*------------------------------------------------*/
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());
return EINVAL;
}
SetMaxDrive(drv);
if (drive[drv].empty()) {
/* Drive is already empty so assume successful */
return 0;
}
/* Remove drive's symlink */
if ((rc = RemoveDriveSymlink(drv)) != 0) {
log.Error("ERROR! %s", verr.GetErrorMsg());
return rc;
}
/* Remove virtual slot assignment */
vslot[drive[drv].vs].drv = -1;
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());
return rc;
}
log.Notice("unloaded drive %d", drv);
return 0;
}
/*-------------------------------------------------
* Method to create new volume files in virtual slots 'slot1' through 'slot2'.
* Use volume labels (barcodes) of the form prefix + '_' + mag_slot_number, where
* mag_slot_number is the magazine relative slot number of the magazine slot that
* the virtual slot maps to. If 'label_prefix' is blank, then use the magazine name
* of the magazine the virtual slot is mapped onto as the prefix.
* Returns zero on success, else returns negative and sets lasterr.
*------------------------------------------------*/
int DiskChanger::CreateVolumes(int bay, int count, int start, const char *label_prefix_in)
{
MagazineSlot vol;
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());
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);
}
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);
if (magazine[bay].GetVolumeSlot(label) >= 0) break;
}
start = i;
}
for (i = 0; i < count; i++) {
tFormat(label, "%s_%d", label_prefix.c_str(), start);
if (!magazine[bay].empty()) {
while (magazine[bay].GetVolumeSlot(label) >= 0) {
++start;
tFormat(label, "%s_%d", label_prefix.c_str(), start);
}
}
fprintf(stdout, "creating label '%s'\n", label.c_str());
if (magazine[bay].CreateVolume(label)) {
if (i) magazine[bay].save();
return -1;
}
++start;
}
/* Update magazine state */
magazine[bay].save();
/* 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());
return 0;
}
/*-------------------------------------------------
* Method to get label of volume in this slot
*-------------------------------------------------*/
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());
return NULL;
}
if (vslot[slot].empty()) return "";
return magazine[vslot[slot].mag_bay].GetVolumeLabel(vslot[slot].mag_slot);
}
/*-------------------------------------------------
* Method to get filename path of volume in this slot
*-------------------------------------------------*/
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());
return NULL;
}
if (vslot[slot].empty()) return path.c_str();
return magazine[vslot[slot].mag_bay].GetVolumePath(path, vslot[slot].mag_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.
*------------------------------------------------*/
bool DiskChanger::MagazineEmpty(int mag) const
{
if (mag < 0 || mag >= (int)magazine.size()) return true;
return magazine[mag].empty();
}
/*-------------------------------------------------
* Method returns true if no magazine volume is assigned
* to virtual slot, else returns false.
*------------------------------------------------*/
bool DiskChanger::SlotEmpty(int slot) const
{
if (slot <= 0 || slot >= (int)vslot.size()) return true;
return vslot[slot].empty();
}
/*-------------------------------------------------
* Method returns true if no magazine volume is loaded
* into drive drv, else returns false.
*------------------------------------------------*/
bool DiskChanger::DriveEmpty(int drv) const
{
if (drv < 0 || drv >= (int)drive.size()) return true;
return drive[drv].empty();
}
/*-------------------------------------------------
* Method to get virtual slot currently loaded in virtual drive 'drv'.
* If drive is loaded then returns the virtual slot number of the
* loaded volume. If unloaded, returns zero.
*------------------------------------------------*/
int DiskChanger::GetDriveSlot(int drv) const
{
if (drv < 0 || drv >= (int)drive.size()) return 0;
return drive[drv].vs;
}
/*-------------------------------------------------
* Method to get drive a virtual slot is currently loaded in.
* Returns the drive number a slot's volume is loaded in.
* If unloaded, returns negative.
*------------------------------------------------*/
int DiskChanger::GetSlotDrive(int slot) const
{
if (slot <= 0 || slot >= (int)vslot.size()) return -1;
return vslot[slot].drv;
}
/*-------------------------------------------------
* Method to return number of volumes on magazine 'mag'.
*------------------------------------------------*/
int DiskChanger::GetMagazineSlots(int mag) const
{
if (mag < 0 || mag >= (int)magazine.size()) return 0;
return magazine[mag].num_slots;
}
/*-------------------------------------------------
* Method to return the start of the virtual slot range
* that is assigned to magazine 'mag' volumes.
*------------------------------------------------*/
int DiskChanger::GetMagazineStartSlot(int mag) const
{
if (mag < 0 || mag >= (int)magazine.size()) return 0;
return magazine[mag].start_slot;
}
/*-------------------------------------------------
* Method to return the mountpoint of magazine 'mag'.
*------------------------------------------------*/
const char* DiskChanger::GetMagazineMountpoint(int mag) const
{
if (mag < 0 || mag >= (int)magazine.size()) return "";
return magazine[mag].mountpoint.c_str();
}

80
src/diskchanger.h Normal file
View File

@@ -0,0 +1,80 @@
/* diskchanger.h
*
* This file is part of vchanger by Josh Fisher.
*
* vchanger copyright (C) 2008-2015 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 DISKCHANGER_H_
#define DISKCHANGER_H_
#include "vconf.h"
#include "errhandler.h"
#include "changerstate.h"
class DiskChanger
{
public:
DiskChanger() : changer_lock(NULL), 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;
bool SlotEmpty(int slot) const;
bool DriveEmpty(int drv) const;
int GetDriveSlot(int drv) const;
int GetSlotDrive(int slot) const;
int GetMagazineSlots(int mag) const;
int GetMagazineStartSlot(int mag) const;
const char* GetMagazineMountpoint(int mag) const;
inline int NumDrives() { return (int)drive.size(); }
inline int NumMagazines() { return (int)magazine.size(); }
inline int NumSlots() { return (int)vslot.size() - 1; }
inline int GetError() { return verr.GetError(); }
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);
int InitializeDrives();
void InitializeVirtSlots();
void SetMaxDrive(int n);
int CreateDriveSymlink(int drv);
int RemoveDriveSymlink(int drv);
int SaveDriveState(int drv);
int RestoreDriveState(int drv);
protected:
FILE *changer_lock;
bool needs_update;
bool needs_label;
ErrorHandler verr;
DynamicConfig dconf;
MagazineStateArray magazine;
DriveStateArray drive;
VirtualSlotArray vslot;
};
#endif /*DISKCHANGER_H_*/

73
src/errhandler.cpp Normal file
View File

@@ -0,0 +1,73 @@
/* errhandler.cpp
*
* This file is part of vchanger by Josh Fisher.
*
* vchanger copyright (C) 2008-2014 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.
*
* Provides a class for logging messages to a file
*/
#include "config.h"
#include <stdio.h>
#ifdef HAVE_STRING_H
#include <string.h>
#endif
#ifdef HAVE_STDARG_H
#include <stdarg.h>
#endif
#ifdef HAVE_VARARGS_H
#include <varargs.h>
#endif
#include "errhandler.h"
///////////////////////////////////////////////////
// Class ErrorHandler
///////////////////////////////////////////////////
/*-------------------------------------------------
* Method to set error and error message
*------------------------------------------------*/
void ErrorHandler::SetError(int errnum, const char *fmt, ...)
{
va_list vl;
char buf[4096];
err = errnum;
va_start(vl, fmt);
vsnprintf(buf, sizeof(buf), fmt, vl);
va_end(vl);
msg = buf;
}
/*-------------------------------------------------
* Method to set error and error message and append the text
* returned from strerror(errnum).
*------------------------------------------------*/
void ErrorHandler::SetErrorWithErrno(int errnum, const char *fmt, ...)
{
va_list vl;
char buf[4096];
err = errnum;
va_start(vl, fmt);
vsnprintf(buf, sizeof(buf), fmt, vl);
va_end(vl);
msg = buf;
msg += ": ";
msg += strerror(err);
}

43
src/errhandler.h Normal file
View File

@@ -0,0 +1,43 @@
/* errhandler.h
*
* This file is part of vchanger by Josh Fisher.
*
* vchanger copyright (C) 2008-2014 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 _ERRHANDLER_H_
#define _ERRHANDLER_H_ 1
#include "tstring.h"
class ErrorHandler
{
public:
ErrorHandler() : err(0) {}
~ErrorHandler() {}
inline void clear() { err = 0; msg.clear(); }
void SetError(int errnum, const char *fmt, ...);
void SetErrorWithErrno(int errnum, const char *fmt, ...);
inline const char* GetErrorMsg() const { return msg.c_str(); }
inline int GetError() { return err; }
protected:
int err;
tString msg;
};
#endif /* _ERRHANDLER_H_ */

1185
src/inifile.cpp Normal file

File diff suppressed because it is too large Load Diff

190
src/inifile.h Normal file
View File

@@ -0,0 +1,190 @@
/* inifile.h
*
* Copyright (C) 2013-2014 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 _INIPARSE_H_
#define _INIPARSE_H_ 1
#include <list>
#include "tstring.h"
#define INIKEYWORDTYPE_SECTION 0
#define INIKEYWORDTYPE_ORDERED_SECTION 1
#define INIKEYWORDTYPE_SZ 2
#define INIKEYWORDTYPE_MULTISZ 3
#define INIKEYWORDTYPE_LONGLONG 4
#define INIKEYWORDTYPE_ULONGLONG 5
#define INIKEYWORDTYPE_LONG 6
#define INIKEYWORDTYPE_ULONG 7
#define INIKEYWORDTYPE_INT 8
#define INIKEYWORDTYPE_UINT 9
#define INIKEYWORDTYPE_SHORT 10
#define INIKEYWORDTYPE_USHORT 11
#define INIKEYWORDTYPE_CHAR 12
#define INIKEYWORDTYPE_UCHAR 13
#define INIKEYWORDTYPE_LONGDOUBLE 14
#define INIKEYWORDTYPE_DOUBLE 15
#define INIKEYWORDTYPE_FLOAT 16
#define INIKEYWORDTYPE_BOOL 17
class IniValue
{
public:
IniValue() : type(INIKEYWORDTYPE_SZ) {}
IniValue(const IniValue &b) : type(b.type), value(b.value) {}
virtual ~IniValue() {}
IniValue& operator=(const IniValue &b);
IniValue& operator=(const tStringArray &b);
IniValue& operator=(const tString &b);
IniValue& operator=(const char *b);
IniValue& operator=(long long b);
IniValue& operator=(unsigned long long b);
IniValue& operator=(long b);
IniValue& operator=(unsigned long b);
IniValue& operator=(int b);
IniValue& operator=(unsigned int b);
IniValue& operator=(short b);
IniValue& operator=(unsigned short b);
IniValue& operator=(char b);
IniValue& operator=(unsigned char b);
IniValue& operator=(long double b);
IniValue& operator=(double b);
IniValue& operator=(float b);
IniValue& operator=(bool b);
IniValue& operator+=(const IniValue &b);
IniValue& operator+=(const tStringArray &b);
IniValue& operator+=(const tString &b);
IniValue& operator+=(const char *b);
IniValue& operator+=(const char c);
long long GetLONG() const;
unsigned long long GetULONG() const;
long double GetDOUBLE() const;
bool GetBOOL() const;
const char* GetSZ() const;
const tString& GetString() const;
size_t size() const;
inline void SetType(int newtype) { type = newtype; }
inline void clear() { value.clear(); }
inline int GetType() const { return type; }
inline bool empty() const { return size() == 0; }
inline bool IsSet() const { return value.size() != 0; }
inline operator long long() { return GetLONG(); }
inline operator unsigned long long() { return GetULONG(); }
inline operator long() { return (long)GetLONG(); }
inline operator unsigned long() { return (unsigned long)GetULONG(); }
inline operator int() { return (int)GetULONG(); }
inline operator unsigned int() { return (unsigned int)GetULONG(); }
inline operator short() { return (short)GetULONG(); }
inline operator unsigned short() { return (unsigned short)GetULONG(); }
inline operator char() { return (char)GetULONG(); }
inline operator unsigned char() { return (unsigned char)GetULONG(); }
inline operator long double() { return GetDOUBLE(); }
inline operator double() { return (double)GetDOUBLE(); }
inline operator float() { return (float)GetDOUBLE(); }
inline operator bool() { return GetBOOL(); }
inline operator const char*() const { return GetSZ(); }
inline operator const tString&() const { return GetString(); }
inline operator const tStringArray&() const { return value; }
friend class IniFile;
protected:
int type;
tStringArray value;
};
class IniValuePair
{
public:
IniValuePair() {}
virtual ~IniValuePair() {}
inline IniValuePair& operator=(const IniValuePair &b)
{
if (&b != this) {
key = b.key;
value = b.value;
}
return *this;
}
public:
tString key;
IniValue value;
};
typedef std::list<IniValuePair> IniValuePairList;
class IniFile
{
public:
IniFile() {}
IniFile(const IniFile &b);
virtual ~IniFile() {}
IniFile& operator=(const IniFile &b);
IniValue& operator[](const char *key);
inline IniValue& operator[](const tString &key) { return operator[](key.c_str()); }
inline const IniValue& operator[](const char *key) const
{ return (const IniValue&)((*this)[key]); }
inline const IniValue& operator[](const tString &key) const
{ return (const IniValue&)((*this)[key.c_str()]); }
bool AddKeyword(const char *kw, int kwtype = INIKEYWORDTYPE_SZ);
inline bool AddKeyword(const tString &kw, int kwtype = INIKEYWORDTYPE_SZ)
{ return AddKeyword(kw.c_str(), kwtype); }
bool AddSection(const char *sect, bool is_ordered = false);
inline bool AddSection(const tString &sect, bool is_ordered = false)
{ return AddSection(sect.c_str(), is_ordered); }
bool AddSectionKeyword(const char *sect, const char *kw, int kwtype = INIKEYWORDTYPE_SZ);
inline bool AddSectionKeyword(const tString &sect, const char *kw, int kwtype = INIKEYWORDTYPE_SZ)
{ return AddSectionKeyword(sect.c_str(), kw, kwtype); }
inline bool AddSectionKeyword(const char *sect, const tString &kw, int kwtype = INIKEYWORDTYPE_SZ)
{ return AddSectionKeyword(sect, kw.c_str(), kwtype); }
inline bool AddSectionKeyword(const tString &sect, const tString &kw, int kwtype = INIKEYWORDTYPE_SZ)
{ return AddSectionKeyword(sect.c_str(), kw.c_str(), kwtype); }
inline bool KeywordExists(const char *kw) { return FindValue(kw) != kwmap.end(); }
inline bool KeywordExists(const tString &kw) { return FindValue(kw.c_str()) != kwmap.end(); }
inline bool SectionExists(const char *sect) { return FindSection(sect) != kwmap.end(); }
inline bool SectionExists(const tString &sect) { return FindSection(sect.c_str()) != kwmap.end(); }
bool GetKey(const char *key, tString &base, size_t &ndx, tString &sect, tString &kw);
bool GetKey(const tString &key, tString &base, size_t &ndx, tString &sect, tString &kw)
{ return GetKey(key.c_str(), base, ndx, sect, kw); }
void ClearKeywordValues();
bool Update(const IniFile &b);
int Read(FILE* stream_in);
int Read(const char *fname);
inline int Read(const tString &fname) { return Read(fname.c_str()); }
size_t GetSetKeywords(tStringArray &kw);
size_t GetKeywords(tStringArray &kw);
inline void clear() { kwmap.clear(); }
inline bool empty() { return kwmap.empty(); }
inline tString GetErrorMessage() { return err_msg; }
protected:
bool ParseKey(const char *key, tString &base, size_t &ndx, tString &sect, tString &kw);
inline bool ParseKey(const tString &key, tString &base, size_t &ndx, tString &sect, tString &kw)
{ return ParseKey(key.c_str(), base, ndx, sect, kw); }
IniValuePairList::iterator FindKey(const char *key);
inline IniValuePairList::iterator FindKey(const tString &key) { return FindKey(key.c_str()); }
IniValuePairList::iterator FindValue(const char *key);
inline IniValuePairList::iterator FindValue(const tString &key) { return FindValue(key.c_str()); }
IniValuePairList::iterator FindSection(const char *sect);
inline IniValuePairList::iterator FindSection(const tString &sect) { return FindSection(sect.c_str()); }
int ReadToken(FILE* fs, tString &str);
protected:
IniValuePairList kwmap;
IniValue bogus;
tString err_msg;
};
#endif /* _INIPARSE_H_ */

206
src/loghandler.cpp Normal file
View File

@@ -0,0 +1,206 @@
/* loghandler.cpp
*
* Copyright (C) 2013-2014 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"
#include "compat_defs.h"
#ifdef HAVE_STDIO_H
#include <stdio.h>
#endif
#ifdef HAVE_TIME_H
#include <time.h>
#endif
#ifdef HAVE_STDLIB_H
#include <stdlib.h>
#endif
#ifdef HAVE_STRING_H
#include <string.h>
#endif
#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::LogHandler() : use_syslog(false), max_debug_level(LOG_WARNING), errfs(stderr)
{
#ifdef HAVE_PTHREAD_H
pthread_mutex_init(&mut, NULL);
#endif
}
LogHandler::~LogHandler()
{
if (use_syslog) closelog();
#ifdef HAVE_PTHREAD_H
pthread_mutex_destroy(&mut);
#endif
}
void LogHandler::OpenLog(FILE *fs, int max_level)
{
Lock();
if (use_syslog) closelog();
if (max_level < LOG_EMERG || max_level > LOG_DEBUG) max_level = LOG_DEBUG;
errfs = fs;
use_syslog = false;
max_debug_level = max_level;
Unlock();
}
void LogHandler::OpenLog(const char *ident, int facility, int max_level,
int syslog_options)
{
Lock();
if (use_syslog) closelog();
if (facility < LOG_KERN || facility > LOG_LOCAL7) facility = LOG_DAEMON;
if (max_level < LOG_EMERG || max_level > LOG_DEBUG) max_level = LOG_DEBUG;
openlog(ident, syslog_options, facility);
use_syslog = true;
errfs = stderr;
if (errfs == NULL) errfs = stderr;
max_debug_level = max_level;
Unlock();
}
void LogHandler::Emergency(const char *fmt, ...)
{
va_list vl;
va_start(vl, fmt);
WriteLog(LOG_EMERG, fmt, vl);
va_end(vl);
}
void LogHandler::Alert(const char *fmt, ...)
{
va_list vl;
va_start(vl, fmt);
WriteLog(LOG_ALERT, fmt, vl);
va_end(vl);
}
void LogHandler::Critical(const char *fmt, ...)
{
va_list vl;
va_start(vl, fmt);
WriteLog(LOG_CRIT, fmt, vl);
va_end(vl);
}
void LogHandler::Error(const char *fmt, ...)
{
va_list vl;
va_start(vl, fmt);
WriteLog(LOG_ERR, fmt, vl);
va_end(vl);
}
void LogHandler::Warning(const char *fmt, ...)
{
va_list vl;
va_start(vl, fmt);
WriteLog(LOG_WARNING, fmt, vl);
va_end(vl);
}
void LogHandler::Notice(const char *fmt, ...)
{
va_list vl;
va_start(vl, fmt);
WriteLog(LOG_NOTICE, fmt, vl);
va_end(vl);
}
void LogHandler::Info(const char *fmt, ...)
{
va_list vl;
va_start(vl, fmt);
WriteLog(LOG_INFO, fmt, vl);
va_end(vl);
}
void LogHandler::Debug(const char *fmt, ...)
{
va_list vl;
va_start(vl, fmt);
WriteLog(LOG_DEBUG, fmt, vl);
va_end(vl);
}
void LogHandler::MajorDebug(const char *fmt, ...)
{
#ifdef MAJOR_DEBUG
va_list vl;
va_start(vl, fmt);
WriteLog(LOG_DEBUG, fmt, vl);
va_end(vl);
#endif
}
// Method to acquire mutex lock
void LogHandler::Lock()
{
#ifdef HAVE_PTHREAD_H
pthread_mutex_lock(&mut);
#endif
}
// Method to release mutex lock
void LogHandler::Unlock()
{
#ifdef HAVE_PTHREAD_H
pthread_mutex_unlock(&mut);
#endif
}
// Method to write to log
void LogHandler::WriteLog(int priority, const char *fmt, va_list vl)
{
size_t n;
struct tm bt;
time_t t;
char buf[1024];
Lock();
if (priority > max_debug_level || priority < LOG_EMERG || !fmt) {
Unlock();
return;
}
t = time(NULL);
localtime_r(&t, &bt);
strftime(buf, 100, "%b %d %T: ", &bt);
strncpy(buf + strlen(buf), fmt, sizeof(buf) - strlen(buf));
if (use_syslog) vsyslog(priority, buf, vl);
else {
n = strlen(buf);
if (!n) {
fprintf(errfs, "\n");
} else {
vfprintf(errfs, buf, vl);
if (buf[n - 1] != '\n') {
fprintf(errfs, "\n");
}
fflush(errfs);
}
}
Unlock();
}

70
src/loghandler.h Normal file
View File

@@ -0,0 +1,70 @@
/* loghandler.h
*
* Copyright (C) 2013-2014 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 _LOGHANDLER_H_
#define _LOGHANDLER_H_ 1
#ifdef HAVE_SYSLOG_H
#include <syslog.h>
#else
#include "compat/syslog.h"
#endif
#ifdef HAVE_STDARG_H
#include <stdarg.h>
#endif
#ifdef HAVE_PTHREAD_H
#include <pthread.h>
#endif
class LogHandler
{
public:
LogHandler();
virtual ~LogHandler();
void OpenLog(FILE *fs, int max_level = LOG_WARNING);
void OpenLog(const char *ident = NULL, int facility = LOG_DAEMON,
int max_level = LOG_WARNING, int syslog_options = 0);
void Emergency(const char *fmt, ... );
void Alert(const char *fmt, ... );
void Critical(const char *fmt, ... );
void Error(const char *fmt, ... );
void Warning(const char *fmt, ... );
void Notice(const char *fmt, ... );
void Info(const char *fmt, ... );
void Debug(const char *fmt, ... );
void MajorDebug(const char *fmt, ... );
inline bool UsingSyslog() { return use_syslog; }
protected:
void Lock();
void Unlock();
void WriteLog(int priority, const char *fmt, va_list vl);
protected:
bool use_syslog;
int max_debug_level;
FILE *errfs;
#ifdef HAVE_PTHREAD_H
pthread_mutex_t mut;
#endif
};
#ifndef LOGHANDLER_SOURCE
extern LogHandler log;
#endif
#endif /* _LOGHANDLER_H_ */

726
src/mypopen.cpp Normal file
View File

@@ -0,0 +1,726 @@
/* mypopen.cpp
*
* Copyright (C) 2013-2014 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"
#include <stdio.h>
#include <stdlib.h>
#ifdef HAVE_STRING_H
#include <string.h>
#endif
#ifdef HAVE_CTYPE_H
#include <ctype.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_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
#include "loghandler.h"
#include "mypopen.h"
/*
* Function to parse command line into argv and argc.
* cmd is command line to be executed. Arguments in cmd will be
* parsed and copied to buffer. On output, each element of argv[]
* will point to a NULL-terminated string located in buffer.
* argv[0] will point to the command and argc will be updated to
* indicate the number of arguments found (including the command
* in argv[0]). On input, if *argv_in is NULL, then memory for
* the argv[] array will be allocated and the caller will be
* responsible for freeing it. If *argv_in is non-null, then
* *argc must be set to the max number of elements that *argv_in
* can hold, including the terminating NULL pointer.
*/
static int mypopen_args(const char *cmd, char* (&argv)[50], int &argc, char *buffer, size_t buffer_sz)
{
char quote, *b, *b1;
const char *p;
int maxargs = 50;
/* Initialize arg array */
if (argc < 2) return -1;
maxargs = argc;
argc = 0;
memset(argv, 0, maxargs * sizeof(char*));
/* Initialize arg buffer */
if (strlen(cmd) + 1 >= buffer_sz) return -1;
*buffer = '\0';
/* Parse args */
b = buffer;
p = cmd;
b1 = b;
--maxargs; /* Ensure room for trailing NULL arg ptr */
while (*p) {
/* skip whitespace between args */
if (isspace(*p)) {
++p;
continue;
}
/* Copy next arg to buffer */
if (*p == '"' || *p == '\'') {
/* Quoted string is treated as one arg */
quote = *p;
++p;
while (*p && *p != quote) {
if (*p == '\\') {
++p;
if (*p == 0) break;
}
*b = *p;
++b;
++p;
}
} else {
/* Arg is space-delimited */
while (*p && !isspace(*p)) {
*b = *p;
++b;
++p;
}
}
*b = '\0';
++b;
/* Set argument array pointer */
argv[argc] = b1;
/* Position to next arg */
b1 = b;
argc += 1;
if (argc >= maxargs) {
/* Max args exceeded */
argc = 0;
return -1;
}
}
/* Check for no args at all */
if (argc == 0) {
return -1;
}
return 0;
}
/*
* Function to close all pipes that may be open
*/
static void CloseAllPipes(int *pipe_in, int *pipe_out, int *pipe_err)
{
if (pipe_in[0] >= 0) close(pipe_in[0]);
if (pipe_in[1] >= 0) close(pipe_in[1]);
if (pipe_out[0] >= 0) close(pipe_out[0]);
if (pipe_out[1] >= 0) close(pipe_out[1]);
if (pipe_err[0] >= 0) close(pipe_err[0]);
if (pipe_err[1] >= 0) close(pipe_err[1]);
}
/*
* Function to fork a child process, specifying the command and arguments to be
* run in the child and the child's standard i/o files. If fno_stdXXX is NULL,
* then the child will inherit the parent's stdXXX. If *fno_stdXXX is -1, then
* a pipe will be created with the child's stdXXX being one end of the pipe and
* the other end of the pipe being passed back to the caller in *fno_stdXXX. If
* *fno_stdXXX is zero or greater, then it specifies a file descriptor that will be
* used as the corresponding stdXXX in the child.
* On success, returns the pid of the child. On error, returns -1 and sets errno.
*/
#ifndef HAVE_WINDOWS_H
static int do_mypopen_raw(const char *cline, int *fno_stdin, int *fno_stdout, int *fno_stderr)
{
int rc, pipe_in[2], pipe_out[2], pipe_err[2];
int n, pid = -1, argc = 50;
char* argv[50];
char argbuf[4096];
/* Sanity check parameters */
if (!cline || !cline[0]) {
errno = EINVAL;
return -1;
}
for (n = 0; n < 2; n++)
{
pipe_in[n] = -1;
pipe_out[n] = -1;
pipe_err[n] = -1;
}
/* 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");
errno = EINVAL;
return -1;
}
/* Setup child's stdin */
if (fno_stdin) {
if (*fno_stdin < 0) {
/* Caller requests a pipe by specifying descriptor -1 */
rc = pipe(pipe_in);
if (rc) {
/* error creating pipe */
return -1;
}
log.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;
}
}
/* Setup child's stdout */
if (fno_stdout) {
if (*fno_stdout < 0) {
/* Caller requests a pipe by specifying descriptor -1 */
rc = pipe(pipe_out);
if (rc) {
/* error creating pipe */
rc = errno;
CloseAllPipes(pipe_in, pipe_out, pipe_err);
errno = rc;
return -1;
}
log.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);
}
}
/* Setup child's stderr */
if (fno_stderr) {
if (*fno_stderr < 0) {
/* Caller requests a pipe by specifying descriptor -1 */
rc = pipe(pipe_err);
if (rc) {
/* error creating pipe */
rc = errno;
CloseAllPipes(pipe_in, pipe_out, pipe_err);
errno = rc;
return -1;
}
log.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);
}
}
/* fork a child process to run the command in */
log.Debug("popen: forking now");
pid = fork();
switch (pid)
{
case -1: /* error creating process */
rc = errno;
CloseAllPipes(pipe_in, pipe_out, pipe_err);
errno = rc;
return -1;
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]);
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]);
/* assign child's stdin */
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]);
dup2(pipe_in[0], STDIN_FILENO);
close(pipe_in[0]);
} else {
/* Child's stdin will read from caller's input file */
dup2(*fno_stdin, STDIN_FILENO);
close(*fno_stdin);
}
}
/* assign child's stdout */
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]);
dup2(pipe_out[1], STDOUT_FILENO);
close(pipe_out[1]);
} else {
/* Child's stdout will write to caller's output file */
dup2(*fno_stdout, STDOUT_FILENO);
close(*fno_stdout);
}
}
/* assign child's stderr */
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]);
dup2(pipe_err[1], STDERR_FILENO);
close(pipe_err[1]);
} else {
/* Child's stderr will write to caller's error file */
dup2(*fno_stderr, STDERR_FILENO);
close(*fno_stderr);
}
}
/* now run the command */
log.Debug("popen: child executing '%s'", argv[0]);
execvp(argv[0], argv);
/* only gets here if execvp fails */
return -1;
}
/* 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]);
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]);
/* Pass pipe ends being used back to caller */
if (fno_stdin) {
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]);
}
}
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]);
}
}
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]);
}
}
//sleep(2);
log.Debug("popen: parent returning pid=%d of child", pid);
return pid;
}
#else
static int do_mypopen_raw(const char *cline, int *fno_stdin, int *fno_stdout, int *fno_stderr)
{
int rc, pipe_in[2], pipe_out[2], pipe_err[2];
int save_in = -1, save_out = -1, save_err = -1;
int child_in = -1, child_out = -1, child_err = -1;
int n, pid = -1, argc = 50;
char *argv[50], argbuf[4096];
/* Sanity check parameters */
if (!cline || !cline[0]) {
errno = EINVAL;
return -1;
}
for (n = 0; n < 2; n++)
{
pipe_in[n] = -1;
pipe_out[n] = -1;
pipe_err[n] = -1;
}
/* 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");
errno = EINVAL;
return -1;
}
/* Determine child's stdin */
if (fno_stdin) {
if (*fno_stdin < 0) {
/* Caller requests a pipe for writing to child's stdin */
rc = _pipe(pipe_in, 4096, O_NOINHERIT);
if (rc) {
/* error creating pipe */
return -1;
}
child_in = pipe_in[0];
log.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 */
child_in = *fno_stdin;
}
}
}
/* Determine child's stdout */
if (fno_stdout) {
if (*fno_stdout < 0) {
/* Caller requests a pipe for reading from child's stdout */
rc = _pipe(pipe_out, 4096, O_NOINHERIT);
if (rc) {
/* error creating pipe */
rc = errno;
CloseAllPipes(pipe_in, pipe_out, pipe_err);
errno = rc;
return -1;
}
child_out = pipe_out[1];
log.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 */
child_out = *fno_stdout;
}
}
}
/* Setup child's stderr */
if (fno_stderr) {
if (*fno_stderr < 0) {
/* Caller requests a pipe for reading from child's stderr */
rc = _pipe(pipe_err, 5096, O_NOINHERIT);
if (rc) {
/* error creating pipe */
rc = errno;
CloseAllPipes(pipe_in, pipe_out, pipe_err);
errno = rc;
return -1;
}
child_err = pipe_err[1];
log.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 */
child_err = *fno_stderr;
}
}
}
/* Save original stdin and make stdin a duplicate of file child is to use */
if (child_in >= 0) {
save_in = _dup(STDIN_FILENO);
if (save_in < 0) {
rc = errno;
CloseAllPipes(pipe_in, pipe_out, pipe_err);
errno = rc;
return -1;
}
if(_dup2(child_in, STDIN_FILENO) != 0) {
rc = errno;
close(save_in);
CloseAllPipes(pipe_in, pipe_out, pipe_err);
errno = rc;
return -1;
}
}
/* Save original stdout and make stdout a duplicate of file child is to use */
if (child_out >= 0) {
save_out = _dup(STDOUT_FILENO);
if (save_out < 0) {
rc = errno;
if (save_in >= 0) {
_dup2(save_in, STDIN_FILENO);
close(save_in);
pipe_in[0] = -1;
}
CloseAllPipes(pipe_in, pipe_out, pipe_err);
errno = rc;
return -1;
}
if(_dup2(child_out, STDOUT_FILENO) != 0) {
rc = errno;
close(save_out);
if (save_in >= 0) {
_dup2(save_in, STDIN_FILENO);
close(save_in);
pipe_in[0] = -1;
}
CloseAllPipes(pipe_in, pipe_out, pipe_err);
errno = rc;
return -1;
}
}
/* Save original stderr and make stderr a duplicate of file child is to use */
if (child_err >= 0) {
save_err = _dup(STDERR_FILENO);
if (save_err < 0) {
rc = errno;
if (save_in >= 0) {
_dup2(save_in, STDIN_FILENO);
close(save_in);
pipe_in[0] = -1;
}
if (save_out >= 0) {
_dup2(save_out, STDOUT_FILENO);
close(save_out);
pipe_out[1] = -1;
}
CloseAllPipes(pipe_in, pipe_out, pipe_err);
errno = rc;
return -1;
}
if(_dup2(child_err, STDERR_FILENO) != 0) {
rc = errno;
close(save_out);
if (save_in >= 0) {
_dup2(save_in, STDIN_FILENO);
close(save_in);
pipe_in[0] = -1;
}
if (save_out >= 0) {
_dup2(save_out, STDOUT_FILENO);
close(save_out);
pipe_out[1] = -1;
}
CloseAllPipes(pipe_in, pipe_out, pipe_err);
errno = rc;
return -1;
}
}
/* Spawn the child process */
rc = 0;
pid = _spawnvp(_P_NOWAIT, argv[0], argv);
if (pid < 0) {
/* spawn failed */
rc = errno;
}
/* Restore parent's stdin, stdout, and stderr */
if (save_in >= 0) {
_dup2(save_in, STDIN_FILENO);
close(save_in);
pipe_in[0] = -1;
}
if (save_out >= 0) {
_dup2(save_out, STDOUT_FILENO);
close(save_out);
pipe_out[1] = -1;
}
if (save_err >= 0) {
_dup2(save_err, STDERR_FILENO);
close(save_err);
pipe_err[1] = -1;
}
if (rc) {
CloseAllPipes(pipe_in, pipe_out, pipe_err);
return -1;
}
/* Pass requested pipe ends back to caller */
if (pipe_in[1] >= 0) *fno_stdin = pipe_in[1];
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);
return pid;
}
#endif
/*
* Function to fork a child process, specifying the command and arguments to be
* run in the child and the child's standard i/o files. If fno_stdXXX is NULL,
* then the child will inherit the parent's stdXXX. If *fno_stdXXX is -1, then
* a pipe will be created with the child's stdXXX being one end of the pipe and
* the other end of the pipe being passed back to the caller in *fno_stdXXX. If
* *fno_stdXXX zero or greater, then it specifies a file descriptor that will be
* used as stdXXX in the child.
* On success, returns the pid of the child. On error, returns -1 and sets errno.
*/
int mypopen_raw(const char *command, int *fno_stdin, int *fno_stdout, int *fno_stderr)
{
return do_mypopen_raw(command, fno_stdin, fno_stdout, fno_stderr);
}
/*
* Function to fork a child process, specifying the command to be run in the child
* and acquiring open file streams to the child's stdXXX files. If any of fs_XXX are
* NULL, then the child will inherit the parent's corresponding stdXXX. If fs_XXX points
* to an open file stream, then the child will use that file as its stdXXX. If fs_XXX
* points to NULL, then on return *fs_XXX will be an open file connected by a pipe to
* the child's corresponding stdXXX.
* On success, returns the pid of the child process. On error, returns -1 and
* sets errno appropriately.
*/
int mypopen(const char *cmd, FILE **fs_in, FILE **fs_out, FILE **fs_err)
{
int pid, fno_in = -1, fno_out = -1, fno_err = -1;
int *in_p = NULL, *out_p = NULL, *err_p = NULL;
if (fs_in) {
in_p = &fno_in;
if (*fs_in) {
fno_in = fileno(*fs_in);
if (fno_in == STDIN_FILENO) {
in_p = NULL;
fs_in = NULL;
}
}
}
if (fs_out) {
out_p = &fno_out;
if (*fs_out) {
fno_out = fileno(*fs_out);
if (fno_out == STDOUT_FILENO) {
out_p = NULL;
fs_out = NULL;
}
}
}
if (fs_err) {
err_p = &fno_err;
if (*fs_err) {
fno_err = fileno(*fs_err);
if (fno_err == STDERR_FILENO) {
err_p = NULL;
fs_err = NULL;
}
}
}
pid = do_mypopen_raw(cmd, in_p, out_p, err_p);
if (pid < 0) return -1;
if (fs_in && *fs_in == NULL) *fs_in = fdopen(fno_in, "w");
if (fs_out && *fs_out == NULL) *fs_out = fdopen(fno_out, "r");
if (fs_err && *fs_err == NULL) *fs_err = fdopen(fno_err, "r");
return pid;
}
/*
* Function to fork a child process, specifying the command to be run in the child
* and files the child will use for stdXXX. If cmd_XXX is non-null, then it specifies
* the name of a file that the child is to use as its stdXXX. If the filename is empty
* then "/dev/null" is used. If cmd_XXX is NULL, then the child will use the caller's stdXXX.
* On success, returns the result code from the command. On error, returns -1 and
* sets errno appropriately.
*/
int mypopenrw(const char *command, const char *cmd_in, const char *cmd_out, const char *cmd_err)
{
int pid, rc, st;
int fno_in = -1, fno_out = -1, fno_err = -1;
int *fno_in_p = NULL, *fno_out_p = NULL, *fno_err_p = NULL;
/* Execute command in a child process, setting child's stdin, stdout, and stderr */
if (cmd_in) {
if (cmd_in[0]) fno_in = open(cmd_in, O_RDONLY);
else fno_in = open("/dev/null", O_RDONLY);
if (fno_in < 0) return -1;
fno_in_p = &fno_in;
}
if (cmd_out) {
if (cmd_out[0]) {
#ifndef HAVE_WINDOWS_H
fno_out = open(cmd_out, O_WRONLY|O_CREAT, S_IRUSR|S_IWUSR|S_IRGRP);
#else
fno_out = _open(cmd_out, O_WRONLY|O_CREAT, _S_IWRITE);
#endif
} else fno_out = open("/dev/null", O_WRONLY);
if (fno_out < 0) {
rc = errno;
if (fno_in >= 0) close(fno_in);
errno = rc;
return -1;
}
fno_out_p = &fno_out;
}
if (cmd_err) {
if (cmd_err[0]) {
#ifndef HAVE_WINDOWS_H
fno_err = open(cmd_err, O_WRONLY|O_CREAT, S_IRUSR|S_IWUSR|S_IRGRP);
#else
fno_err = _open(cmd_err, O_WRONLY|O_CREAT, _S_IWRITE);
#endif
} else fno_err = open("/dev/null", O_WRONLY);
if (fno_err < 0) {
rc = errno;
if (fno_in >= 0) close(fno_in);
if (fno_out >= 0) close(fno_out);
errno = rc;
return -1;
}
fno_err_p = &fno_err;
}
pid = do_mypopen_raw(command, fno_in_p, fno_out_p, fno_err_p);
if (pid < 0) {
/* Command execution failed */
if (fno_in >= 0) close(fno_in);
if (fno_out >= 0) close(fno_out);
if (fno_err >= 0) close(fno_err);
return -1;
}
/* Wait for child process to exit */
#ifndef HAVE_WINDOWS_H
rc = waitpid(pid, &st, 0);
while (rc < 0) {
if (errno != EINTR) {
if (fno_in >= 0) close(fno_in);
if (fno_out >= 0) close(fno_out);
if (fno_err >= 0) close(fno_err);
return -1;
}
rc = waitpid(pid, &st, 0);
}
/* get child's result code */
if (!WIFEXITED(st)) {
/* Command was killed for some reason */
return -1;
}
rc = WEXITSTATUS(st);
#else
if (_cwait(&rc, pid, WAIT_CHILD) != pid) {
if (fno_in >= 0) close(fno_in);
if (fno_out >= 0) close(fno_out);
if (fno_err >= 0) close(fno_err);
return -1;
}
#endif
if (fno_in >= 0) {
close(fno_in);
}
if (fno_out >= 0) {
close(fno_out);
}
if (fno_err >= 0) {
close(fno_err);
}
return rc;
}

37
src/mypopen.h Normal file
View File

@@ -0,0 +1,37 @@
/* mypopen.h
*
* Copyright (C) 2013-2014 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 _MYPOPEN_H_
#define _MYPOPEN_H_ 1
#include "tstring.h"
int mypopen_raw(const char *command, int *fno_stdin, int *fno_stdout, int *fno_stderr);
inline int mypopen_raw(const tString &command, int *fno_stdin, int *fno_stdout, int *fno_stderr)
{ return mypopen_raw(command.c_str(), fno_stdin, fno_stdout, fno_stderr); }
int mypopen(const char *command, FILE **cmd_stdin = NULL, FILE **cmd_stdout = NULL,
FILE **cmd_stderr = NULL);
inline int mypopen(const tString &command, FILE **cmd_stdin = NULL, FILE **cmd_stdout = NULL,
FILE **cmd_stderr = NULL)
{ return mypopen(command.c_str(), cmd_stdin, cmd_stdout, cmd_stderr); }
int mypopenrw(const char *command, const char *cmd_in = NULL, const char *cmd_out = NULL, const char *cmd_err = NULL);
inline int mypopenrw(const tString &command, const char *cmd_in = NULL, const char *cmd_out = NULL, const char *cmd_err = NULL)
{ return mypopenrw(command.c_str(), cmd_in, cmd_out, cmd_err); }
#endif /* _MYPOPEN_H_ */

24
src/targetver.h Normal file
View File

@@ -0,0 +1,24 @@
/* The following macros define the minimum required platform. The minimum required platform
* is the earliest version of Windows, Internet Explorer etc. that has the necessary features to run
* your application. The macros work by enabling all features available on platform versions up to and
* including the version specified.
*
* Modify the following defines if you have to target a platform prior to the ones specified below.
* Refer to MSDN for the latest info on corresponding values for different platforms.
*/
#ifndef _TARGETVER_H_
#define _TARGETVER_H_ 1
#ifdef HAVE_WINDOWS_H
#ifndef WINVER
#define WINVER 0x0600
#endif
#ifndef _WIN32_WINNT
#define _WIN32_WINNT 0x0600
#endif
#ifndef _WIN32_IE
#define _WIN32_IE 0x0700
#endif
#define _POSIX 1
#endif
#endif

476
src/tstring.cpp Normal file
View File

@@ -0,0 +1,476 @@
/* tstring.cpp
*
* This file is part of the msafe package by Josh Fisher.
*
* vchanger copyright (C) 2006-2014 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 msafe. 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"
#include <stdio.h>
#ifdef HAVE_STDLIB_H
#include <stdlib.h>
#endif
#ifdef HAVE_STDARG_H
#include <stdarg.h>
#endif
#ifdef HAVE_CTYPE_H
#include <ctype.h>
#endif
#ifdef HAVE_STRING_H
#include <string.h>
#endif
#ifdef HAVE_STRINGS_H
#include <strings.h>
#endif
#include "tstring.h"
/*
* Function to make string lower case
*/
tString& tToLower(tString &b)
{
tStringIterator p;
for (p = b.begin(); p != b.end(); p++) {
*p = tolower(*p);
}
return b;
}
tString tToLower(const char *sin)
{
tString b(sin);
tToLower(b);
return b;
}
/*
* Function to make string upper case
*/
tString& tToUpper(tString &b)
{
tStringIterator p;
for (p = b.begin(); p != b.end(); p++) {
*p = toupper(*p);
}
return b;
}
tString tToUpper(const char *sin)
{
tString b(sin);
tToUpper(b);
return b;
}
/*
* Function to strip leading whitespace
*/
tString& tStripLeft(tString &b)
{
tStringIterator p;
for (p = b.begin(); p != b.end() && isblank(*p); p++) ;
if (p != b.begin()) {
b.erase(b.begin(), p);
}
return b;
}
tString tStripLeft(const char *sin)
{
tString b(sin);
tStripLeft(b);
return b;
}
/*
* Function to strip trailing blanks
*/
tString& tStripRight(tString &b)
{
tStringIterator p = b.begin();
size_t n = b.find_last_not_of(" \t");
if (n != tString::npos) p += (n + 1);
b.erase(p, b.end());
return b;
}
tString tStripRight(const char *sin)
{
tString b(sin);
tStripRight(b);
return b;
}
/*
* Function to strip leading and trailing whitespace
*/
tString& tStrip(tString &b)
{
return tStripLeft(tStripRight(b));
}
tString tStrip(const char *sin)
{
tString b(sin);
tStrip(b);
return b;
}
/*
* Function to remove all SP and HTAB chars from b
*/
tString& tRemoveWS(tString &b)
{
size_t n = b.find_first_of(" \t");
while (n != tString::npos) {
b.erase(n, 1);
n = b.find_first_of(" \t", n);
}
return b;
}
tString tRemoveWS(const char *sin)
{
tString b(sin);
tRemoveWS(b);
return b;
}
/*
* Function to remove trailing line ending
*/
tString& tRemoveEOL(tString &b)
{
if (b.empty() || b[b.size() - 1] != '\n') return b;
b.erase(b.end() - 1);
if (!b.empty() && b[b.size() - 1] == '\r') b.erase(b.end() - 1);
return b;
}
tString tRemoveEOL(const char *sin)
{
tString b(sin);
tRemoveEOL(b);
return b;
}
/*
* Function to change LF endings to CRLF
*/
tString& tLF2CRLF(tString &b)
{
size_t n = b.find_first_of('\n');
while (n != tString::npos) {
if (n && b[n-1] != '\r') {
b.insert(n, 1, '\r');
n += 2;
} else ++n;
n = b.find_first_of('\n', n);
}
return b;
}
tString tLF2CRLF(const char *sin)
{
tString b(sin);
tLF2CRLF(b);
return b;
}
/*
* Function to change CRLF endings to LF
*/
tString& tCRLF2LF(tString &b)
{
size_t n = b.find("\r\n");
while (n != tString::npos) {
b.erase(n, 1);
n = b.find("\r\n", n);
}
return b;
}
tString tCRLF2LF(const char *sin)
{
tString b(sin);
tCRLF2LF(b);
return b;
}
/*
* Function to extract the next line of text from sin
* Returns line extracted.
*/
tString tExtractLine(const tString &b, size_t &pos)
{
size_t p1 = pos;
pos = b.find('\n', pos);
if (pos == tString::npos) pos = b.size();
else ++pos;
return b.substr(p1, pos - p1);
}
tString tExtractLine(const char *sin, size_t &pos)
{
tString b(sin);
return tExtractLine(b, pos);
}
/*
* Function to compare two case-insensitive strings as in C function strcasecmp()
*/
int tCaseCmp(const char *a, const char *b)
{
if (!a || !a[0]) return b && b[0] ? -1 : 0;
if (!b || !b[0]) return 1;
return strcasecmp(a, b);
}
/*
* Function to do case-insensitive search for character b from position pos
* in string a
*/
size_t tCaseFind(const tString &a, char b, size_t pos)
{
b = tolower(b);
tString str = a;
tToLower(str);
return str.find(b, pos);
}
/*
* Function to do case-insensitive search for C string b from position pos
* in string a
*/
size_t tCaseFind(const tString &a, const char *b_in, size_t pos)
{
tString str(a), b(b_in);
tToLower(str);
tToLower(b);
return str.find(b, pos);
}
/*
* Function to get line of text from a file stream
*/
const char* tGetLine(tString &str, FILE *FS)
{
char buf[4096];
if (fgets(buf, sizeof(buf), FS) == NULL) {
str.clear();
return NULL;
}
str = buf;
return str.c_str();
}
/*
* Function to format a string using sprintf
*/
const char* tFormat(tString &str, const char *fmt, ...)
{
char buf[8192];
va_list vl;
va_start(vl, fmt);
vsnprintf(buf, sizeof(buf), fmt, vl);
va_end(vl);
buf[8191] = 0;
str = buf;
return str.c_str();
}
/*
* Function to parse next token from position 'pos' in string 'str'.
* The text determining the token is placed in 'word'. 'pos' is
* updated to reference the character following the token text, or
* end of string if no characters follow the token text.
* Returns:
* 0 Past end of string
* 'A' Atom was found
* ' ' Whitespace was found
* '\n' EOL char sequence was found
* <other> Special char was found
*/
char tParseStandard(tString &word, const char *str, size_t &pos, tString special, tString ws)
{
size_t n, slen;
if (ws.empty()) ws = " \t";
word.clear();
if (!str || !str[0]) return 0;
slen = strlen(str);
if (pos >= slen) {
pos = slen;
return 0;
}
if (ws.find(str[pos]) != tString::npos) {
/* Extract whitespace sequence */
for (n = pos; n < slen && ws.find(str[n]) != tString::npos; n++) ;
word.insert(0, str + pos, n - pos);
pos = n;
return ' ';
}
if (special.find(str[pos]) != tString::npos) {
/* Extract special character */
word = str[pos++];
return word[0];
}
if (str[pos] == '\r') {
/* Extract CR or CRLF line ending */
word = '\r';
++pos;
if (str[pos] == '\n') {
word += '\n';
++pos;
}
return '\n';
}
if (str[pos] == '\n') {
/* Extract LF line ending */
word = '\n';
++pos;
return '\n';
}
/* Extract atom */
for (n = pos; n < slen && str[n] != '\r' && str[n] != '\n' && ws.find(str[n]) == tString::npos
&& special.find(str[n]) == tString::npos; n++) ;
word.insert(0, str + pos, n - pos);
pos = n;
return 'A';
}
/*
* Function to parse next value in comma-separated-value string 'str'
* on or after position 'pos' and store the value in 'word'. The value
* returned in 'word' will have quoting and escape sequences decoded.
* 'pos' is updated and set to the position in 'str' of the character
* following the next delimiter (',') or end of line/string if there are
* no more delimiters.
* Returns:
* -1 Missing closing quote
* 0 Past end of string
* 1 Found column field
* 2 Found line ending
*/
int tParseCSV(tString &word, const char *str, size_t &pos)
{
size_t p, slen = strlen(str);
char c;
tString w;
word.clear();
c = tParseStandard(w, str, pos, "\",");
if (!c) return 0; /* At end of string */
if (c == '\n') {
/* EOL found */
word = w;
return 2;
}
/* Skip leading whitespace */
while (c == ' ') c = tParseStandard(w, str, pos, "\",");
if (c == 0) {
/* Found empty field terminated by end of string */
return 0;
}
if (c == '\n') {
/* Found empty field terminated by EOL */
word = w;
return 2;
}
/* extract field */
while (c && c != '\n' && c != ',') {
if (c == '"') {
/* Extracting quoted string */
c = tParseStandard(w, str, pos, "\"\\");
while (c && c != '"') {
if (c == '\\') {
switch (str[pos]) {
case 0:
return -1;
case 'r':
word += '\r';
break;
case 'n':
word += '\n';
break;
case 't':
word += '\t';
break;
case 'v':
word += '\v';
break;
case 'b':
word += '\b';
break;
case 'a':
word += '\a';
break;
case 'x':
/* Process hex escape sequence */
if (isxdigit(str[pos + 1])) {
++pos;
w.clear();
for (p = 0; pos < slen && p < 2 && isxdigit(str[pos]); p++) {
w += str[pos++];
}
word += (char)strtoul(w.c_str(), NULL, 16);
--pos;
} else word += 'x';
break;
default:
if (str[pos] >= '0' && str[pos] <= '7') {
/* Process octal escape sequence */
w.clear();
for (p = 0; pos < slen && p < 3 && str[pos] >= '0' && str[pos] <= '7'; p++) {
w += str[pos++];
}
word += (char)strtoul(w.c_str(), NULL, 8);
--pos;
} else {
/* anything else following an escape is a literal */
word += str[pos];
}
}
++pos;
} else {
word += w;
}
}
if (c != '"') {
/* Closing '"' not found */
return -1;
}
} else if (c == ' ' || c == '\n') {
/* Extracting unquoted whitespace. Replace with single SP */
if (c == ' ') word += ' ';
}
else {
/* Extracting atom or special char */
word += w;
}
c = tParseStandard(w, str, pos, "\",");
}
/* Found a field */
if (c == '\n') {
/* If line ending terminated field, put it back */
pos -= w.size();
}
return 1;
}

80
src/tstring.h Normal file
View File

@@ -0,0 +1,80 @@
/* tstring.h
*
* This file is part of msafe by Josh Fisher.
*
* vchanger copyright (C) 2006-2014 Josh Fisher
*
* vchanger 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.
*
* 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 msafe. See the file "COPYING". If not,
* write to: The Free Software Foundation, Inc.,
* 59 Temple Place - Suite 330,
* Boston, MA 02111-1307, USA.
*/
#ifndef _TSTRING_H_
#define _TSTRING_H_
#include <string>
#include <list>
#include <vector>
#include <map>
typedef std::basic_string<char> tString;
typedef tString::iterator tStringIterator;
typedef tString::const_iterator tStringConstIterator;
typedef tString::reverse_iterator tStringReverseIterator;
typedef tString::const_reverse_iterator tStringConstReverseIterator;
typedef std::list<tString> tStringList;
typedef tStringList::iterator tStringListIterator;
typedef tStringList::const_iterator tStringListConstIterator;
typedef tStringList::reverse_iterator tStringListReverseIterator;
typedef tStringList::const_reverse_iterator tStringListConstReverseIterator;
typedef std::vector<tString> tStringArray;
typedef std::map<tString, tString> tStringMap;
typedef std::map<tString, tStringList> tStringListMap;
tString& tToLower(tString &b);
tString tToLower(const char *sin);
tString& tToUpper(tString &b);
tString tToUpper(const char *sin);
tString& tStripLeft(tString &b);
tString tStripLeft(const char *sin);
tString& tStripRight(tString &b);
tString tStripRight(const char *sin);
tString& tStrip(tString &b);
tString tStrip(const char *sin);
tString& tRemoveWS(tString &b);
tString tRemoveWS(const char *b);
tString& tRemoveEOL(tString &b);
tString tRemoveEOL(const char *sin);
tString& tLF2CRLF(tString &b);
tString tLF2CRLF(const char *sin);
tString& tCRLF2LF(tString &b);
tString tCRLF2LF(const char *sin);
tString tExtractLine(const tString &b, size_t &pos);
tString tExtractLine(const char *sin, size_t &pos);
size_t tCaseFind(const tString &str, char c, size_t pos = 0);
size_t tCaseFind(const tString &str, const char *pat, size_t pos = 0);
inline size_t tCaseFind(const tString &str, const tString &pat, size_t pos = 0)
{ return tCaseFind(str, pat.c_str(), pos); }
int tCaseCmp(const char *a, const char *b);
inline int tCaseCmp(const tString &a, const char *b) { return tCaseCmp(a.c_str(), b); }
inline int tCaseCmp(const char *a, const tString &b) { return tCaseCmp(a, b.c_str()); }
inline int tCaseCmp(const tString &a, const tString &b) { return tCaseCmp(a.c_str(), b.c_str()); }
const char* tGetLine(tString &str, FILE *FS);
const char* tFormat(tString &str, const char *fmt, ...);
char tParseStandard(tString &word, const char *str, size_t &pos, tString special = "", tString ws = "");
int tParseCSV(tString &word, const char *str, size_t &pos);
inline int tParseCSV(tString &word, const tString &str, size_t &pos)
{ return tParseCSV(word, str.c_str(), pos); }
#endif

192
src/util.cpp Normal file
View File

@@ -0,0 +1,192 @@
/* util.cpp
*
* This file is part of vchanger by Josh Fisher.
*
* vchanger copyright (C) 2008-2014 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.
*
* This file simply provides some utility functions
*/
#include "config.h"
#include <stdio.h>
#ifdef HAVE_STDLIB_H
#include <stdlib.h>
#endif
#ifdef HAVE_STRING_H
#include <string.h>
#endif
#ifdef HAVE_CTYPE_H
#include <ctype.h>
#endif
#ifdef HAVE_ERRNO_H
#include <errno.h>
#endif
#ifdef HAVE_UNISTD_H
#include <unistd.h>
#endif
#ifdef HAVE_STDARG_H
#include <stdarg.h>
#endif
#ifdef HAVE_SYS_TYPES_H
#include <sys/types.h>
#endif
#ifdef HAVE_SYS_STAT_H
#include <sys/stat.h>
#endif
#ifdef HAVE_FCNTL_H
#include <fcntl.h>
#endif
#ifdef HAVE_GRP_H
#include <grp.h>
#endif
#ifdef HAVE_PWD_H
#include <pwd.h>
#endif
#ifdef HAVE_TIME_H
#include <time.h>
#endif
#ifdef HAVE_SYS_TIME_H
#include <sys/time.h>
#endif
#include "util.h"
/*-------------------------------------------------
* Function to return elapsed time between two struct timeval values
* in microseconds.
*------------------------------------------------*/
long timeval_et(struct timeval *tv1, struct timeval *tv2)
{
if (!tv1 || !tv2) return 0;
return ((tv2->tv_sec - tv1->tv_sec) * 1000000) + tv2->tv_usec - tv1->tv_usec;
}
/*-------------------------------------------------
* Function to open file 'fname' for write in exclusive access mode.
* On success, returns the opened stream. Otherwise, on error returns
* NULL.
*------------------------------------------------*/
int exclusive_fopen(const char *fname, FILE **fs)
{
int fd, result = 0;
*fs = NULL;
fd = open(fname, O_CREAT | O_EXCL | O_RDWR, S_IRUSR | S_IWUSR);
if (fd > 0) {
*fs = fdopen(fd, "w");
if (*fs == NULL) {
result = errno;
close(fd);
unlink(fname);
}
} else {
result = errno;
}
return result;
}
/*-------------------------------------------------
* Function to copy file 'from_path' to new file 'to_path'.
* On success returns zero, else returns errno
*------------------------------------------------*/
int file_copy(const char *to_path, const char *from_path)
{
int rc;
size_t n;
FILE *to, *from;
char *buf;
size_t buf_sz = 1024 * 1024;
if (access(to_path, F_OK) == 0) return EEXIST;
from = fopen(from_path, "r");
if (!from) return errno;
to = fopen(to_path, "w");
if (!to) {
rc = errno;
fclose(from);
return rc;
}
buf = (char*)malloc(buf_sz);
while ((n = fread(buf, 1, buf_sz, from)) > 0) {
if (fwrite(buf, 1 , n, to) != n) {
rc = ferror(to);
fclose(to);
unlink(to_path);
fclose(from);
free(buf);
return rc;
}
}
free(buf);
fclose(to);
if (!feof(from)) {
rc = ferror(from);
unlink(to_path);
fclose(from);
return rc;
}
fclose(from);
return 0;
}
/*-------------------------------------------------
* Function to drop root privileges and change persona to uid:gid
* of the given user name and group name.
* On success returns zero, else on error returns errno.
*------------------------------------------------*/
int drop_privs(const char *uname, const char *gname)
{
#ifdef HAVE_WINDOWS_H
return 0; /* For windows ignore user switching */
#else
gid_t new_gid;
struct passwd *pw;
struct group *gr;
if (!uname || !uname[0] || getuid()) return 0; /* Nothing to do */
if ((pw = getpwnam(uname)) == NULL) return errno;
if (pw->pw_uid == 0) return 0; /* already running as root */
new_gid = pw->pw_gid;
if (gname && gname[0]) {
/* find given group */
if ((gr = getgrnam(gname)) == NULL) return errno; /* no such group */
new_gid = gr->gr_gid;
}
/* Set supplemental groups */
if (initgroups(uname, new_gid)) return errno;
/* Start running as given group */
if (setgid(new_gid)) return errno;
/* Drop root and run as given user */
if (setuid(pw->pw_uid)) return errno;
return 0;
#endif
}
/*-------------------------------------------------
* Function returns zero if current user not superuser
* else non-zero.
*------------------------------------------------*/
int is_root_user()
{
#ifndef HAVE_WINDOWS_H
return getuid() == 0 ? 1 : 0;
#endif
return 0;
}

33
src/util.h Normal file
View File

@@ -0,0 +1,33 @@
/* util.h
*
* This file is part of vchanger by Josh Fisher.
*
* vchanger copyright (C) 2008-2014 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 _UTIL_H_
#define _UTIL_H_ 1
/* Utility Functions */
long timeval_et(struct timeval *tv1, struct timeval *tv2);
int exclusive_fopen(const char *fname, FILE **fs);
int file_copy(const char *to, const char *from);
int drop_privs(const char *uname, const char *gname);
int is_root_user();
#endif /* _UTIL_H_ */

364
src/uuidlookup.c Normal file
View File

@@ -0,0 +1,364 @@
/* uuidlookup.c
*
* This file is part of vchanger by Josh Fisher.
*
* vchanger copyright (C) 2008-2013 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"
#include <stdio.h>
#ifdef HAVE_STDLIB_H
#include <stdlib.h>
#endif
#ifdef HAVE_SYS_TYPES_H
#include <sys/types.h>
#endif
#ifdef HAVE_STRING_H
#include <string.h>
#endif
#ifdef HAVE_STRINGS_H
#include <strings.h>
#endif
#ifdef HAVE_CTYPE_H
#include <ctype.h>
#endif
#include "uuidlookup.h"
#ifdef HAVE_WINDOWS_H
#include "targetver.h"
#include <windows.h>
#include <winioctl.h>
#include "win32_util.h"
/*
* Locates disk partition containing file system with volume serial number
* given by 'uuid_str'. If found, and the filesystem is mounted, returns the
* first mountpoint found in 'mountp'. On success, returns zero. On error,
* returns negative value :
* -1 system error
* -2 parameter error
* -3 volume with given uuid not found
* -4 'mountp' buffer too small
*/
int GetMountpointFromUUID(char *mountp, size_t mountp_sz, const char *uuid_str)
{
HANDLE hnd, hFile;
wchar_t volname[2048];
wchar_t pname[2048];
char tmp[128], *utf8name = NULL;
BOOL rc;
DWORD volser, count, eov, uuid;
size_t n;
/* Convert uuid_str to binary. Note that win32 volume serial number is not
* in standard UUID format, but is the form xxxx-xxxx, where x is a hex digit.
* Win32 returns volume serial number as 32-bit unsigned binary.
*/
mountp[0] = '\0';
strncpy(tmp, uuid_str, sizeof(tmp));
if (strlen(uuid_str) != 9 || tmp[4] != '-') {
/* invalid volume serial number */
return -2;
}
for (n = 4; n < 9; n++) {
tmp[n] = tmp[n+1];
}
uuid = strtoul(tmp, NULL, 16);
/* Enumerate volumes on this system looking for matching volume serial number.
* Skip unmounted volumes.
*/
hnd = FindFirstVolumeW(volname, 2048);
if (hnd == INVALID_HANDLE_VALUE) {
/* out of memory?? permissions problem?? */
return -1;
}
rc = TRUE;
while (rc) {
/* make sure it's a proper volume name */
if (volname[0] == L'\\' && volname[1] == L'\\' && volname[2] == L'?'
&& volname[3] == L'\\' && volname[eov] == L'\\') {
/* Get volume's first mountpoint */
if (GetVolumePathNamesForVolumeNameW(volname, pname, 2048, &count)) {
/* retrieve serial from volume information */
if (GetVolumeInformationW(pname, NULL, 0, &volser, NULL, NULL, NULL, 0)) {
/* see if this volume matches requested UUID */
if (volser == uuid) {
/* found matching volume. UTF16 mountpoint path is in pname. */
break;
}
}
}
}
/* get next volume name */
rc = FindNextVolumeW(hnd, volname, 2048);
}
FindVolumeClose(hnd);
if (!rc) {
return -3; /* volume not found */
}
/* Convert UTF16 mountpoint path to UTF8 */
UTF16ToAnsi(pname, &utf8name, &n);
if (n > mountp_sz) {
free(utf8name);
return -4; /* mountp buffer too small */
}
strncpy(mountp, utf8name, mountp_sz);
free(utf8name);
return 0;
}
#else
#ifdef HAVE_MNTENT_H
#include <mntent.h>
/*
* Lookup mount point for device 'devname' and place in 'mountp'
* using SunOS 4.1.3+ function getmntent().
* On success, returns zero. On error, returns negative value :
* -1 system error
* -2 parameter error
* -4 devname not mounted
* -5 mountp buffer too small
*/
static int GetDevMountpoint(char *mountp, size_t mountp_sz, const char *devname)
{
size_t n;
FILE *fs;
struct mntent *ent;
int rc;
mountp[0] = '\0';
if (!mountp_sz || !devname || !strlen(devname)) return -2;
/* Read either /proc/mounts or /etc/mtab depending on build system's glibc */
fs = setmntent(_PATH_MOUNTED, "r");
if (fs == NULL) return -1; /* unknown non-POSIX system?? */
rc = -4;
ent = getmntent(fs);
while (ent)
{
if (strcasecmp(devname, ent->mnt_fsname) == 0) {
n = strlen(ent->mnt_dir);
if (n >= mountp_sz) {
rc = -5;
break;
}
memmove(mountp, ent->mnt_dir, n);
mountp[n] = 0;
rc = 0;
break;
}
ent = getmntent(fs);
}
endmntent(fs);
return rc;
}
#else
#ifdef HAVE_GETFSSTAT
#ifdef HAVE_SYS_PARAM_H
#include <sys/param.h>
#endif
#ifdef HAVE_SYS_MOUNT_H
#include <sys/mount.h>
#endif
#ifdef HAVE_SYS_UCRED_H
#include <sys/ucred.h>
#endif
/*
* Lookup mount point for device 'devname' and place in 'mountp'
* using BSD4.4+ function getfsstat().
* On success, returns zero. On error, returns negative value :
* -1 system error
* -2 parameter error
* -4 devname not mounted
*/
static int GetDevMountpoint(char *mountp, size_t mountp_sz, const char *devname)
{
struct statfs *fs;
int mcount, fs_size, n, rc = -4;
mountp[0] = '\0';
if (!devname || !strlen(devname)) return -2;
mcount = getfsstat(NULL, 0, MNT_WAIT);
if (mcount < 1) return -1;
fs_size = (mcount + 1) * sizeof(struct statfs);
fs = (struct statfs*)malloc(fs_size);
if (!fs) return -1;
mcount = getfsstat(fs, fs_size, MNT_NOWAIT);
for (n = 0; n < mcount; n++)
{
if (strcasecmp(devname, fs[n].f_mntfromname) == 0) {
strncpy(mountp, fs[n].f_mntonname, mountp_sz);
rc = 0;
}
}
free(fs);
return rc;
}
#else
/*
* System has neither getmntent() nor getfsstat(). Return system error.
*/
static int GetDevMountpoint(char *mountp, size_t mountp_sz, const char *devname)
{
return -1;
}
#endif /* HAVE_GETFSSTAT */
#endif /* HAVE_MNTENT_H */
#ifdef HAVE_LIBUDEV_H
#include <libudev.h>
/*
* Locates disk partition containing file system with UUID given by 'uuid_str'.
* If found, and the filesystem is mounted, returns the first mountpoint found in
* string 'mountp'. On success, returns zero. On error, returns negative value :
* -1 system error
* -2 parameter error
* -3 filesystem with given uuid not found
* -4 filesystem not mounted
* -5 mountp buffer too small
*/
int GetMountpointFromUUID(char *mountp, size_t mountp_sz, const char *uuid_str)
{
struct udev *udev;
struct udev_enumerate *enumerate;
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;
char devlink[4096];
if (!mountp || !mountp_sz) return -2;
if (!uuid_str || !strlen(uuid_str)) return -2;
/* Enumerate devices with a filesystem UUID property */
udev = udev_new();
if (!udev) return -1;
enumerate = udev_enumerate_new(udev);
udev_enumerate_add_match_property(enumerate, "ID_FS_UUID", uuid_str);
udev_enumerate_scan_devices(enumerate);
devices = udev_enumerate_get_list_entry(enumerate);
/* Find device with specified UUID */
udev_list_entry_foreach(dev_list_entry, devices) {
path = udev_list_entry_get_name(dev_list_entry);
dev = udev_device_new_from_syspath(udev, path);
uuid = udev_device_get_property_value(dev, "ID_FS_UUID");
if (uuid && strcasecmp(uuid, uuid_str) == 0) {
/* Found device with this UUID, so get its kernel device node */
dev_name = udev_device_get_property_value(dev, "DEVNAME");
if (dev_name == NULL) {
/* Failed to get kernel device node */
break;
}
/* 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 */
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;
}
break;
}
}
udev_enumerate_unref(enumerate);
udev_unref(udev);
return rc;
}
#else
#ifdef HAVE_BLKID_BLKID_H
#include <blkid/blkid.h>
/*
* Locates disk partition containing file system with UUID given by 'uuid_str'.
* If found, and the filesystem is mounted, returns the first mountpoint found in
* string 'mountp'. On success, returns zero. On error, returns negative value :
* -1 system error
* -2 parameter error
* -3 volume with given uuid not found
* -4 volume not mounted
* -5 mountp buffer too small
*/
int GetMountpointFromUUID(char *mountp, size_t mountp_sz, const char *uuid_str)
{
int rc;
char *dev_name;
if (!mountp || !mountp_sz) return -2;
if (!uuid_str || !strlen(uuid_str)) return -2;
/* Get device with requested UUID from libblkid */
#ifdef HAVE_BLKID_EVALUATE_TAG
dev_name = blkid_evaluate_tag("UUID", uuid_str, NULL);
#else
dev_name = blkid_get_devname(NULL, "UUID", uuid_str);
#endif
if (!dev_name) return -3; /* no device with UUID found */
/* find mount point for device */
rc = GetDevMountpoint(mountp, mountp_sz, dev_name);
free(dev_name);
return rc;
}
#else
/*
* If built without libudev or libblkid support, then UUID lookup is a system error
*/
int GetMountpointFromUUID(char *mountp, size_t mountp_sz, const char *uuid_str)
{
return -1;
}
#endif /* HAVE_BLKID_BLKID_H */
#endif /* HAVE_LIBUDEV_H */
#endif /* HAVE_WINDOWS_H */

37
src/uuidlookup.h Normal file
View File

@@ -0,0 +1,37 @@
/* uuidlookup.h
*
* This file is part of vchanger by Josh Fisher.
*
* vchanger copyright (C) 2008-2013 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 __UUIDLOOKUP_H_
#define __UUIDLOOKUP_H_
#ifdef __cplusplus
extern "C" {
#endif
int GetMountpointFromUUID(char *mountp, size_t mountp_sz, const char *uuid_str);
#ifdef __cplusplus
}
#endif
#endif /* __UUIDLOOKUP_H_ */

661
src/vchanger.cpp Normal file
View File

@@ -0,0 +1,661 @@
/* vchanger.cpp
*
* This file is part of the vchanger package
*
* vchanger copyright (C) 2008-2015 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"
#ifdef HAVE_STDIO_H
#include <stdio.h>
#endif
#ifdef HAVE_STDLIB_H
#include <stdlib.h>
#endif
#ifdef HAVE_STDINT_H
#include <stdint.h>
#endif
#ifdef HAVE_GETOPT_H
#include <getopt.h>
#endif
#ifdef HAVE_UNISTD_H
#include <unistd.h>
#endif
#ifdef HAVE_CTYPE_H
#include <ctype.h>
#endif
#ifdef HAVE_LOCALE_H
#include <locale.h>
#endif
#ifdef HAVE_STRING_H
#include <string.h>
#endif
#ifdef HAVE_SIGNAL_H
#include <signal.h>
#endif
#include "util.h"
#include "compat_defs.h"
#include "loghandler.h"
#include "diskchanger.h"
DiskChanger changer;
/*-------------------------------------------------
* Commands
* ------------------------------------------------*/
#define NUM_AUTOCHANGER_COMMANDS 9
static char autochanger_command[NUM_AUTOCHANGER_COMMANDS][32] = { "list", "slots", "load",
"unload", "loaded", "listall", "listmags", "createvols", "refresh" };
#define CMD_LIST 0
#define CMD_SLOTS 1
#define CMD_LOAD 2
#define CMD_UNLOAD 3
#define CMD_LOADED 4
#define CMD_LISTALL 5
#define CMD_LISTMAGS 6
#define CMD_CREATEVOLS 7
#define CMD_REFRESH 8
/*-------------------------------------------------
* Command line parameters
* ------------------------------------------------*/
typedef struct _cmdparams_s
{
bool print_version;
bool print_help;
int command;
int slot;
int drive;
int mag_bay;
int count;
tString label_prefix;
tString pool;
tString runas_user;
tString runas_group;
tString config_file;
tString archive_device;
} CMDPARAMS;
CMDPARAMS cmdl;
/*-------------------------------------------------
* Function to print version info to stdout
*------------------------------------------------*/
static void print_version(void)
{
fprintf(stdout, "%s version %s\n", PACKAGE_NAME, PACKAGE_VERSION);
fprintf(stdout, "\n%s.\n", COPYRIGHT_NOTICE);
}
/*-------------------------------------------------
* Function to print command help to stdout
*------------------------------------------------*/
static void print_help(void)
{
fprintf(stdout, "vchanger version %s\n\n", PACKAGE_VERSION);
fprintf(stdout, "USAGE:\n\n"
" vchanger [options] config_file command slot device drive\n"
" Perform Bacula Autochanger API command for virtual\n"
" 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"
" 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"
" appending integers to the label prefix when generating volume names.\n"
" vchanger [options] config_file REFRESH\n"
" vchanger --version\n"
" print version info\n"
" vchanger --help\n"
" print help\n"
"\nGeneral options:\n"
" -u, --user=uid user to run as (when invoked by root)\n"
" -g, --group=gid group to run as (when invoked by root)\n"
"\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"
"\nReport bugs to %s.\n", PACKAGE_BUGREPORT);
}
/*-------------------------------------------------
* Function to parse command line parameters
*------------------------------------------------*/
#define LONGONLYOPT_VERSION 0
#define LONGONLYOPT_HELP 1
#define LONGONLYOPT_POOL 2
static int parse_cmdline(int argc, char *argv[])
{
int c, ndx = 0;
tString tmp;
struct option options[] = { { "version", 0, 0, LONGONLYOPT_VERSION },
{ "help", 0, 0, LONGONLYOPT_HELP },
{ "user", 1, 0, 'u' },
{ "group", 1, 0, 'g' },
{ "label", 1, 0, 'l' },
{ "pool", 1, 0, LONGONLYOPT_POOL },
{ 0, 0, 0, 0 } };
cmdl.print_version = false;
cmdl.print_help = false;
cmdl.command = 0;
cmdl.slot = 0;
cmdl.drive = 0;
cmdl.mag_bay = 0;
cmdl.count = 0;
cmdl.label_prefix.clear();
cmdl.pool.clear();
cmdl.runas_user.clear();
cmdl.runas_group.clear();
cmdl.config_file.clear();
cmdl.archive_device.clear();
/* process the command line */
for (;;) {
c = getopt_long(argc ,argv, "u:g:l:", options, NULL);
if (c == -1) break;
switch (c) {
case LONGONLYOPT_VERSION:
cmdl.print_version = true;
cmdl.print_help = false;
return 0;
case LONGONLYOPT_HELP:
cmdl.print_version = false;
cmdl.print_help = true;
return 0;
case 'u':
cmdl.runas_user = optarg;
break;
case 'g':
cmdl.runas_group = optarg;
break;
case 'l':
cmdl.label_prefix = optarg;
break;
case LONGONLYOPT_POOL:
cmdl.pool = optarg;
break;
default:
fprintf(stderr, "unknown option %s\n", optarg);
return -1;
}
}
/* process positional params */
ndx = optind;
/* First parameter is the vchanger config file path */
if (ndx >= argc) {
fprintf(stderr, "missing parameter 1 (config_file)\n");
return -1;
}
cmdl.config_file = argv[ndx];
/* Second parameter is the command */
++ndx;
if (ndx >= argc) {
fprintf(stderr, "missing parameter 2 (command)\n");
return -1;
}
tmp = argv[ndx];
tToLower(tStrip(tmp));
for (cmdl.command = 0; cmdl.command < NUM_AUTOCHANGER_COMMANDS; cmdl.command++) {
if (tmp == autochanger_command[cmdl.command]) break;
}
if (cmdl.command >= NUM_AUTOCHANGER_COMMANDS) {
fprintf(stderr, "'%s' is not a recognized command", argv[ndx]);
return -1;
}
/* Make sure only CREATEVOLS command has -l flag */
if (!cmdl.label_prefix.empty() && cmdl.command != CMD_CREATEVOLS) {
fprintf(stderr, "flag -l not valid for this command\n");
return -1;
}
/* Make sure only CREATEVOLS command has --pool flag */
if (!cmdl.pool.empty() && cmdl.command != CMD_CREATEVOLS) {
fprintf(stderr, "flag --pool not valid for this command\n");
return -1;
}
/* Check param 3 exists */
++ndx;
if (ndx >= argc) {
/* Only 2 parameters given */
switch (cmdl.command) {
case CMD_LIST:
case CMD_LISTALL:
case CMD_SLOTS:
case CMD_LISTMAGS:
case CMD_REFRESH:
return 0; /* OK, because these commands only need 2 parameters */
case CMD_CREATEVOLS:
fprintf(stderr, "missing parameter 3 (magazine index)\n");
break;
default:
fprintf(stderr, "missing parameter 3 (slot number)\n");
break;
}
return -1;
}
/* Process parameter 3 */
switch (cmdl.command) {
case CMD_LIST:
case CMD_LISTALL:
case CMD_SLOTS:
case CMD_LISTMAGS:
case CMD_REFRESH:
return 0; /* These commands only need 2 params, so ignore extraneous */
case CMD_CREATEVOLS:
/* Param 3 for CREATEVOLS command is magazine index */
cmdl.mag_bay = (int)strtol(argv[ndx], NULL, 10);
if (cmdl.mag_bay < 0) {
fprintf(stderr, "invalid magazine index in parameter 3\n");
return -1;
}
break;
case CMD_LOADED:
/* slot is ignored for LOADED command, so just set to 1 */
cmdl.slot = 1;
break;
default:
/* Param 3 for all other commands is the slot number */
cmdl.slot = (int)strtol(argv[ndx], NULL, 10);
if (cmdl.slot < 1) {
fprintf(stderr, "invalid slot number in parameter 3\n");
return -1;
}
break;
}
/* Check param 4 exists */
++ndx;
if (ndx >= argc) {
/* Only 3 parameters given */
switch (cmdl.command) {
case CMD_CREATEVOLS:
fprintf(stderr, "missing parameter 4 (count)\n");
break;
default:
fprintf(stderr, "missing parameter 4 (archive device)\n");
break;
}
return -1;
}
/* Process param 4 */
switch (cmdl.command) {
case CMD_CREATEVOLS:
/* Param 4 for CREATEVOLS command is volume count */
cmdl.count = (int)strtol(argv[ndx], NULL, 10);
if (cmdl.count <= 0 ) {
fprintf(stderr, "invalid count in parameter 4\n");
return -1;
}
break;
default:
/* Param 4 for all other commands is the archive device path */
cmdl.archive_device = argv[ndx];
break;
}
/* Check param 5 exists */
++ndx;
if (ndx >= argc) {
/* Only 4 parameters given */
switch (cmdl.command) {
case CMD_CREATEVOLS:
cmdl.slot = -1;
return 0; /* OK, because parameter 5 optional */
default:
fprintf(stderr, "missing parameter 5 (drive index)\n");
break;
}
return -1;
}
switch (cmdl.command) {
case CMD_CREATEVOLS:
cmdl.slot = (int)strtol(argv[ndx], NULL, 10);
if (cmdl.slot < 0) cmdl.slot = -1;
break;
default:
/* Param 5 for all other commands is drive index number */
if (!isdigit(argv[ndx][0])) {
fprintf(stderr, "invalid drive index in parameter 5\n");
return -1;
}
cmdl.drive = (int)strtol(argv[ndx], NULL, 10);
if (cmdl.drive < 0) {
fprintf(stderr, "invalid drive index in parameter 5\n");
return -1;
}
break;
}
/* note that any extraneous parameters are simply ignored */
return 0;
}
/*-------------------------------------------------
* LIST Command
* Prints a line on stdout for each autochanger slot that contains a
* volume file, even if that volume is currently loaded in a drive.
* Output is of the form:
* s:barcode
* where 's' is the one-based virtual slot number and 'barcode' is the barcode
* label of the volume in the slot. The volume in the slot is a file on one
* of the changer's magazines. A magazine is a directory, which is usually the
* mountpoint of a filesystem partition. The changer has one or more
* magazines, each of which may or may not be attached. Each volume file on
* each magazine is mapped to a virtual slot. The barcode is the volume filename.
*------------------------------------------------*/
static int do_list_cmd()
{
int slot, num_slots = changer.NumSlots();
/* Print all slot numbers, adding volume labels for non-empty slots */
for (slot = 1; slot <= num_slots; slot++) {
if (changer.SlotEmpty(slot)) {
fprintf(stdout, "%d:\n", slot);
} else {
fprintf(stdout, "%d:%s\n", slot, changer.GetVolumeLabel(slot));
}
}
log.Info(" SUCCESS sent list to stdout");
return 0;
}
/*-------------------------------------------------
* SLOTS Command
* Prints the number of virtual slots the changer has
*------------------------------------------------*/
static int do_slots_cmd()
{
fprintf(stdout, "%d\n", changer.NumSlots());
log.Info(" SUCCESS reporting %d slots", changer.NumSlots());
return 0;
}
/*-------------------------------------------------
* LOAD Command
* Loads the volume file mapped to a virtual slot into a virtual drive
*------------------------------------------------*/
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);
return 1;
}
log.Info(" SUCCESS loading slot %d into drive %d", cmdl.slot, cmdl.drive);
return 0;
}
/*-------------------------------------------------
* UNLOAD Command
* Unloads the volume in a virtual drive
*------------------------------------------------*/
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);
return 1;
}
log.Info(" SUCCESS unloading slot %d from drive %d", cmdl.slot, cmdl.drive);
return 0;
}
/*-------------------------------------------------
* LOADED Command
* Prints the virtual slot number of the volume file currently loaded
* into a virtual drive, or zero if the drive is unloaded.
*------------------------------------------------*/
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);
return 0;
}
/*-------------------------------------------------
* LISTALL Command
* Prints state of drives (loaded or empty), followed by state
* of virtual slots (full or empty).
*------------------------------------------------*/
static int do_list_all()
{
int n, s, num_slots = changer.NumSlots();
/* Print drive state info */
for (n = 0; n < changer.NumDrives(); n++) {
if (changer.DriveEmpty(n)) {
fprintf(stdout, "D:%d:E\n", n);
} else {
s = changer.GetDriveSlot(n);
fprintf(stdout, "D:%d:F:%d:%s\n", n, s,
changer.GetVolumeLabel(s));
}
}
/* Print slot state info */
for (n = 1; n <= num_slots; n++) {
if (changer.SlotEmpty(n)) {
fprintf(stdout, "S:%d:E\n", n);
} else {
if (changer.GetSlotDrive(n) < 0)
fprintf(stdout, "S:%d:F:%s\n", n, changer.GetVolumeLabel(n));
else
fprintf(stdout, "S:%d:E\n", n);
}
}
log.Info(" SUCCESS sent listall to stdout");
return 0;
}
/*-------------------------------------------------
* LISTMAGS (List Magazines) Command
* Prints a listing of all magazine bays and info on the magazine
* (if any) each bay contains.
*------------------------------------------------*/
static int do_list_magazines()
{
int n;
if (changer.NumMagazines() == 0) {
fprintf(stdout, "No magazines are defined\n");
log.Info(" SUCCESS no magazines are defined");
return 0;
}
for (n = 0; n < changer.NumMagazines(); n++) {
if (changer.MagazineEmpty(n)) {
fprintf(stdout, "%d:::\n", n);
} else {
fprintf(stdout, "%d:%d:%d:%s\n", n, changer.GetMagazineSlots(n),
changer.GetMagazineStartSlot(n), changer.GetMagazineMountpoint(n));
}
}
log.Info(" SUCCESS listing magazine info to stdout");
return 0;
}
/*-------------------------------------------------
* CREATEVOLS (Create Volumes) Command
* Creates volume files on the specified magazine
*------------------------------------------------*/
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");
return -1;
}
fprintf(stdout, "Created %d volume files on magazine %d\n",
cmdl.count, cmdl.mag_bay);
log.Info(" SUCCESS");
return 0;
}
/* ------------- Main -------------------------*/
int main(int argc, char *argv[])
{
int rc;
FILE *fs = NULL;
int32_t error_code;
#ifdef HAVE_LOCALE_H
setlocale(LC_ALL, "");
#endif
/* Log initially to stderr */
log.OpenLog(stderr, LOG_ERR);
/* parse the command line */
if ((error_code = parse_cmdline(argc, argv)) != 0) {
print_help();
return 1;
}
/* Check for --version flag */
if (cmdl.print_version) {
print_version();
return 0;
}
/* Check for --help flag */
if (cmdl.print_help) {
print_help();
return 0;
}
/* Read vchanger config file */
if (!conf.Read(cmdl.config_file)) {
return 1;
}
/* User:group from cmdline overrides config file values */
if (cmdl.runas_user.size()) conf.user = cmdl.runas_user;
if (cmdl.runas_group.size()) conf.group = cmdl.runas_group;
/* Pool from cmdline overrides config file */
if (!cmdl.pool.empty()) conf.def_pool = cmdl.pool;
/* If root, try to run as configured user:group */
rc = drop_privs(conf.user.c_str(), conf.group.c_str());
if (rc) {
fprintf(stderr, "Error %d attempting to run as user '%s'", rc, conf.user.c_str());
return 1;
}
/* Start logging to log file specified in configuration file */
if (!conf.logfile.empty()) {
fs = fopen(conf.logfile.c_str(), "a");
if (fs == NULL) {
fprintf(stderr, "Error opening opening log file\n");
return 1;
}
log.OpenLog(fs, conf.log_level);
}
/* Validate and commit configuration parameters */
if (!conf.Validate()) {
return 1;
}
#ifndef HAVE_WINDOWS_H
/* Ignore SIGPIPE signals */
signal(SIGPIPE, SIG_IGN);
#endif
/* Initialize changer. A lock file 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()) {
fprintf(stderr, "%s\n", changer.GetErrorMsg());
return 1;
}
/* Perform command */
switch (cmdl.command) {
case CMD_LIST:
log.Debug("==== preforming LIST command pid=%d", getpid());
error_code = do_list_cmd();
break;
case CMD_SLOTS:
log.Debug("==== preforming SLOTS command pid=%d", getpid());
error_code = do_slots_cmd();
break;
case CMD_LOAD:
log.Debug("==== preforming LOAD command pid=%d", getpid());
error_code = do_load_cmd();
break;
case CMD_UNLOAD:
log.Debug("==== preforming UNLOAD command pid=%d", getpid());
error_code = do_unload_cmd();
break;
case CMD_LOADED:
log.Debug("==== preforming LOADED command pid=%d", getpid());
error_code = do_loaded_cmd();
break;
case CMD_LISTALL:
log.Debug("==== preforming LISTALL command pid=%d", getpid());
error_code = do_list_all();
break;
case CMD_LISTMAGS:
log.Debug("==== preforming LISTMAGS command pid=%d", getpid());
error_code = do_list_magazines();
break;
case CMD_CREATEVOLS:
log.Debug("==== preforming CREATEVOLS command pid=%d", getpid());
error_code = do_create_vols();
break;
case CMD_REFRESH:
log.Debug("==== preforming REFRESH command pid=%d", getpid());
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 not updating Bacula, then exit */
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());
if (changer.NeedsLabel())
log.Error("WARNING! 'label barcodes' needed in bconsole pid=%d", getpid());
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
return 0;
}

309
src/vconf.cpp Normal file
View File

@@ -0,0 +1,309 @@
/* vconf.cpp
*
* This file is part of vchanger by Josh Fisher.
*
* vchanger copyright (C) 2008-2014 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.
*
* Provides class for reading configuration file and providing access
* to config variables.
*/
#include "config.h"
#include <stdio.h>
#ifdef HAVE_STDLIB_H
#include <stdlib.h>
#endif
#ifdef HAVE_UNISTD_H
#include <unistd.h>
#endif
#ifdef HAVE_STRING_H
#include <string.h>
#endif
#ifdef HAVE_ERRNO_H
#include <errno.h>
#endif
#ifdef HAVE_SYSLOG_H
#include <syslog.h>
#endif
#ifdef HAVE_SYS_STAT_H
#include <sys/stat.h>
#endif
#ifdef HAVE_WINDOWS_H
#include "targetver.h"
#include <windows.h>
#include <shlobj.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 "loghandler.h"
#include "util.h"
#define __VCONF_SOURCE 1
#include "vconf.h"
/* Global configuration object */
VchangerConfig conf;
/*-------------------------------------------
* Config file keywords and defaults
*-------------------------------------------*/
#define VK_STORAGE_NAME "storage resource"
#define VK_MAGAZINE "magazine"
#define VK_WORK_DIR "work dir"
#define VK_LOGFILE "logfile"
#define VK_LOG_LEVEL "log level"
#define VK_USER "user"
#define VK_GROUP "group"
#define VK_BCONSOLE "bconsole"
#define VK_BCONSOLE_CONFIG "bconsole config"
#define VK_DEF_POOL "default pool"
/*================================================
* Class VchangerConfig
*================================================*/
/*--------------------------------------------------
* Default constructor
*------------------------------------------------*/
VchangerConfig::VchangerConfig() : log_level(DEFAULT_LOG_LEVEL)
{
#ifdef HAVE_WINDOWS_H
char tmp[4096];
wchar_t wtmp[2048];
#endif
storage_name = DEFAULT_STORAGE_NAME;
/* Set default Bacula work directory and vchanger config file path */
#ifdef HAVE_WINDOWS_H
/* Bacula installs its work directory in the FOLDERID_ProgramData folder, which
* is in a different location depending on Windows version. */
wtmp[0] = 0;
memset(tmp, 0, sizeof(tmp));
if (SHGetFolderPathW(NULL, CSIDL_COMMON_APPDATA, NULL, SHGFP_TYPE_CURRENT, wtmp) == S_OK) {
wcstombs(tmp, wtmp, sizeof(tmp) - 1);
snprintf(DEFAULT_STATEDIR, sizeof(DEFAULT_STATEDIR), "%s%svchanger", tmp, DIR_DELIM);
snprintf(DEFAULT_LOGDIR, sizeof(DEFAULT_LOGDIR), "%s%svchanger", tmp, DIR_DELIM);
}
#else
snprintf(DEFAULT_STATEDIR, sizeof(DEFAULT_STATEDIR), "%s/spool/vchanger", LOCALSTATEDIR);
snprintf(DEFAULT_LOGDIR, sizeof(DEFAULT_LOGDIR), "%s/log/vchanger", LOCALSTATEDIR);
#endif
tFormat(work_dir, "%s%s%s", DEFAULT_STATEDIR, DIR_DELIM, storage_name.c_str());
/* Set default logfile path and log level */
tFormat(logfile, "%s%s%s.log", DEFAULT_LOGDIR, DIR_DELIM, storage_name.c_str());
/* Set default runas user and group */
user = DEFAULT_USER;
group = DEFAULT_GROUP;
/* Set default bconsole binary path */
bconsole = DEFAULT_BCONSOLE;
/* Set default pool for created volumes */
def_pool = DEFAULT_POOL;
/* Define config file keywords */
keyword.AddKeyword(VK_MAGAZINE, INIKEYWORDTYPE_MULTISZ);
keyword.AddKeyword(VK_WORK_DIR, INIKEYWORDTYPE_SZ);
keyword.AddKeyword(VK_LOGFILE, INIKEYWORDTYPE_SZ);
keyword.AddKeyword(VK_LOG_LEVEL, INIKEYWORDTYPE_LONG);
keyword.AddKeyword(VK_USER, INIKEYWORDTYPE_SZ);
keyword.AddKeyword(VK_GROUP, INIKEYWORDTYPE_SZ);
keyword.AddKeyword(VK_BCONSOLE, INIKEYWORDTYPE_SZ);
keyword.AddKeyword(VK_BCONSOLE_CONFIG, INIKEYWORDTYPE_SZ);
keyword.AddKeyword(VK_STORAGE_NAME, INIKEYWORDTYPE_SZ);
keyword.AddKeyword(VK_DEF_POOL, INIKEYWORDTYPE_SZ);
}
/*-------------------------------------------------
* Method to read config file and set config values from keyword
* value pairs. On success, returns true. Otherwise returns false.
*------------------------------------------------*/
bool VchangerConfig::Read(const char *cfile)
{
int rc, n;
IniFile tmp_ini = keyword;
tmp_ini.ClearKeywordValues();
if (!cfile || !cfile[0]) {
log.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);
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);
return false;
}
/* Update keyword values */
keyword.Update(tmp_ini);
/* Get bacula-dir.conf storage resource name associated with this changer name */
if (keyword[VK_STORAGE_NAME].IsSet()) {
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);
return false;
}
/* Update defaults for this changer name */
tFormat(work_dir, "%s%s%s", DEFAULT_STATEDIR, DIR_DELIM, storage_name.c_str());
tFormat(logfile, "%s%s%s.log", DEFAULT_LOGDIR, DIR_DELIM, storage_name.c_str());
}
/* Get work directory for this changer */
if (keyword[VK_WORK_DIR].IsSet()) {
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);
return false;
}
}
/* Get logfile path */
if (keyword[VK_LOGFILE].IsSet()) {
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);
return false;
}
}
/* Get log level */
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);
return false;
}
}
/* Get user to run as */
if (keyword[VK_USER].IsSet()) {
user = (const char*)keyword[VK_USER];
tStrip(user);
if (user.empty()) {
log.Error("keyword '%s' value cannot be empty", VK_USER);
return false;
}
}
/* Get group to run as */
if (keyword[VK_GROUP].IsSet()) {
group = (const char*)keyword[VK_GROUP];
tStrip(group);
if (group.empty()) {
log.Error("keyword '%s' value cannot be empty", VK_GROUP);
return false;
}
}
/* Get path of bconsole binary */
if (keyword[VK_BCONSOLE].IsSet()) {
bconsole = (const char*)keyword[VK_BCONSOLE];
tStrip(bconsole);
}
/* Get path of bconsole config file */
if (keyword[VK_BCONSOLE_CONFIG].IsSet()) {
bconsole_config = (const char*)keyword[VK_BCONSOLE_CONFIG];
tStrip(bconsole_config);
}
/* Get default pool */
if (keyword[VK_DEF_POOL].IsSet()) {
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);
return false;
}
}
/* Get list of assigned magazines */
if (keyword[VK_MAGAZINE].IsSet()) {
magazine = keyword[VK_MAGAZINE];
}
if (magazine.empty()) {
log.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);
return false;
}
}
return true;
}
/*-------------------------------------------------
* Method to validate config file values and commit default
* values for any keywords not specified
*------------------------------------------------*/
bool VchangerConfig::Validate()
{
mode_t old_mask;
/* Validate work directory is usable */
if (access(work_dir.c_str(), W_OK)) {
if (errno == ENOENT) {
/* If not found then try to create work dir */
old_mask = umask(027);
#ifndef HAVE_WINDOWS_H
if (mkdir(work_dir.c_str(), 0770)) {
#else
if (_mkdir(work_dir.c_str())) {
#endif
log.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());
return false;
}
}
/* Validate bconsole config file is readable */
if (bconsole.empty()) bconsole_config.clear();
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.");
bconsole.clear();
bconsole_config.clear();
}
}
return true;
}

70
src/vconf.h Normal file
View File

@@ -0,0 +1,70 @@
/* vconf.h
*
* This file is part of vchanger by Josh Fisher.
*
* vchanger copyright (C) 2008-2014 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 _VCONF_H_
#define _VCONF_H_ 1
#include "inifile.h"
#define DEFAULT_LOG_LEVEL 3
#define DEFAULT_USER "bacula"
#define DEFAULT_GROUP "tape"
#define DEFAULT_BCONSOLE "/usr/sbin/bconsole"
#define DEFAULT_STORAGE_NAME "vchanger"
#define DEFAULT_POOL "Scratch"
/* Configuration values */
class VchangerConfig
{
public:
IniFile keyword;
tString work_dir;
tString config_file;
tString logfile;
int log_level;
tString user;
tString group;
tString bconsole;
tString bconsole_config;
tString storage_name;
tString def_pool;
tStringArray magazine;
public:
VchangerConfig();
virtual ~VchangerConfig() {}
bool Read(const char *cfile);
inline bool Read(const tString &cfile) { return Read(cfile.c_str()); }
bool Validate();
};
#ifndef __VCONF_SOURCE
extern VchangerConfig conf;
extern char DEFAULT_LOGDIR[4096];
extern char DEFAULT_STATEDIR[4096];
#else
char DEFAULT_LOGDIR[4096];
char DEFAULT_STATEDIR[4096];
#endif
#endif /* _VCONF_H_ */

259
src/win32_util.c Normal file
View File

@@ -0,0 +1,259 @@
/* win32_util.c
*
* This file is part of vchanger by Josh Fisher.
*
* vchanger copyright (C) 2008-2012 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"
#ifdef HAVE_WINDOWS_H
#include "targetver.h"
#include <windows.h>
#include <winerror.h>
#ifdef HAVE_STDLIB_H
#include <stdlib.h>
#endif
#ifdef HAVE_STDINT_H
#include <stdint.h>
#endif
#ifdef HAVE_ERRNO_H
#include <errno.h>
#endif
#include "win32_util.h"
#ifndef ENODATA
#define ENODATA EIO
#endif
#ifndef ENOMEDIUM
#define ENOMEDIUM ENOSPC
#endif
/*-------------------------------------------------
* Convert 100 ns intervals since 1601-01-01 00:00:00 UTC in 'ftime' to
* seconds since 1970-01-01 00:00:00 UTC and return as type time_t.
*-------------------------------------------------*/
time_t ftime2utime(const FILETIME *ftime)
{
uint64_t ut = ftime->dwHighDateTime;
ut <<= 32;
ut |= ftime->dwLowDateTime; /* 100 ns intervals since FILETIME epoch */
ut -= EPOCH_FILETIME; /* convert to 100 ns intervals since Unix epoch */
ut /= 10000000; /* convert to seconds since Unix epoch */
return (time_t)ut;
}
/*-------------------------------------------------
* Utility function to convert an ANSI string 'str' to a UTF-16 string.
* 'u16str' is pointer to a pointer to a caller supplied buffer where the
* UTF-16 string will be created. 'u16size' is a pointer to a size_t variable
* that contains the size of the supplied buffer. On entry, if the pointer that
* 'u16str' points to is NULL, then a buffer for the result UTF-16 string will
* be allocated using malloc. A pointer to the allocated buffer will be
* returned in *u16str and the size of the allocated buffer (in bytes) will be
* returned in *u16size. It is the caller's responsibility to free the
* allocated buffer. If the caller supplies a buffer then no memory will be
* allocated, and if the buffer is too small then NULL will be returned.
* On success, a pointer to the UTF-16 string is returned.
* On error, NULL is returned.
*-------------------------------------------------*/
wchar_t* AnsiToUTF16(const char *str, wchar_t **u16str, size_t *u16size)
{
size_t needed;
UINT acp = GetACP(); /* Use current active code page for conversion */
if (!str) {
return NULL; /* NULL input string is an error */
}
/* Determine size of buffer needed for the UTF-16 string */
needed = MultiByteToWideChar(acp, 0, str, -1, NULL, 0);
if (*u16str) {
/* If caller supplied a buffer, check that it's large enough */
if (*u16size < needed) {
return NULL; /* caller's buffer is too small */
}
} else {
/* If caller did not supply buffer, then allocate one */
*u16size = needed * sizeof(wchar_t);
*u16str = (wchar_t*)malloc(*u16size);
}
/* Do the ANSI to UTF-16 conversion */
MultiByteToWideChar(acp, 0, str, -1, *u16str, *u16size);
return *u16str;
}
/*-------------------------------------------------
* Utility function to convert UTF-16 string 'u16str' to an ANSI string.
* 'str' is a pointer to a pointer to a caller supplied buffer where the UTF-8
* string will be created. 'str_size' is a pointer to a size_t variable that
* contains the size of the supplied buffer. On entry, if the pointer that
* 'str' points to is NULL, then a buffer for the result ANSI string will
* be allocated using malloc. A pointer to the allocated buffer will be
* returned in *str and the size of the allocated buffer (in bytes) will
* be returned in *str_size. It is the caller's responsibility to free the
* allocated buffer.If the caller supplies a buffer then no memory will be
* allocated, and if the buffer is too small then NULL will be returned.
* On success, a pointer to the ANSI string is returned.
* On error, NULL is returned.
*-------------------------------------------------*/
char *UTF16ToAnsi(const wchar_t *u16str, char **str, size_t *str_size)
{
size_t needed;
UINT acp = GetACP(); /* Use current code page for conversion */
if (!u16str) {
return NULL; /* NULL input string is an error */
}
/* Determine size of buffer needed for UTF-8 string */
needed = WideCharToMultiByte(acp, 0, u16str, -1, NULL, 0, NULL, NULL);
if (*str) {
/* If caller supplied a buffer, check that it's large enough */
if (*str_size < needed) {
return NULL; // caller's buffer is too small */
}
} else {
/* If caller did not supply buffer, then allocate one */
*str_size = needed;
*str = (char*)malloc(*str_size);
}
/* Do the UTF-16 to ANSI conversion */
WideCharToMultiByte(acp, 0, u16str, -1, *str, *str_size, NULL, NULL);
return *str;
}
/*-------------------------------------------------
* Translate win32 error codes to errno
*-------------------------------------------------*/
int w32errno(DWORD werr)
{
switch (werr) {
case ERROR_SUCCESS:
return 0;
case ERROR_ACCESS_DENIED:
case ERROR_SHARING_VIOLATION:
case ERROR_LOCK_VIOLATION:
return EACCES;
case ERROR_NO_PROC_SLOTS:
case ERROR_MORE_DATA:
case ERROR_MAX_THRDS_REACHED:
case ERROR_ACTIVE_CONNECTIONS:
case ERROR_DEVICE_IN_USE:
return EAGAIN;
case ERROR_INVALID_HANDLE:
return EBADF;
case ERROR_BUSY:
case ERROR_PIPE_BUSY:
case ERROR_PIPE_CONNECTED:
case ERROR_SIGNAL_PENDING:
case ERROR_CHILD_NOT_COMPLETE:
return EBUSY;
case ERROR_WAIT_NO_CHILDREN:
return ECHILD;
case ERROR_POSSIBLE_DEADLOCK:
return EDEADLOCK;
case ERROR_FILE_EXISTS:
case ERROR_ALREADY_EXISTS:
return EEXIST;
case ERROR_PROCESS_ABORTED:
case ERROR_NOACCESS:
return EFAULT;
case ERROR_INVALID_AT_INTERRUPT_TIME:
return EINTR;
case ERROR_INVALID_DATA:
case ERROR_INVALID_PARAMETER:
case ERROR_FILENAME_EXCED_RANGE:
case ERROR_META_EXPANSION_TOO_LONG:
case ERROR_INVALID_SIGNAL_NUMBER:
case ERROR_THREAD_1_INACTIVE:
case ERROR_BAD_PIPE:
case ERROR_NO_TOKEN:
case ERROR_NEGATIVE_SEEK:
case ERROR_BAD_USERNAME:
return EINVAL;
case ERROR_OPEN_FAILED:
case ERROR_SIGNAL_REFUSED:
case ERROR_NO_SIGNAL_SENT:
case ERROR_IO_DEVICE:
case ERROR_CRC:
return EIO;
case ERROR_TOO_MANY_OPEN_FILES:
return EMFILE;
case ERROR_NO_MORE_SEARCH_HANDLES:
case ERROR_NO_MORE_FILES:
return ENFILE;
case ERROR_HANDLE_EOF:
return ENODATA;
case ERROR_BAD_DEVICE:
case ERROR_BAD_UNIT:
case ERROR_INVALID_DRIVE:
return ENODEV;
case ERROR_FILE_NOT_FOUND:
case ERROR_PATH_NOT_FOUND:
case ERROR_INVALID_NAME:
case ERROR_BAD_PATHNAME:
case ERROR_BAD_NETPATH:
case ERROR_BAD_NET_NAME:
return ENOENT;
case ERROR_SHARING_BUFFER_EXCEEDED:
return ENOLCK;
case ERROR_NOT_CONNECTED:
case ERROR_NOT_READY:
return ENOMEDIUM;
case ERROR_NOT_ENOUGH_MEMORY:
case ERROR_OUTOFMEMORY:
return ENOMEM;
case ERROR_DISK_FULL:
case ERROR_END_OF_MEDIA:
case ERROR_EOM_OVERFLOW:
case ERROR_NO_DATA_DETECTED:
case ERROR_HANDLE_DISK_FULL:
return ENOSPC;
case ERROR_CALL_NOT_IMPLEMENTED:
case ERROR_NOT_SUPPORTED:
return ENOSYS;
case ERROR_DIRECTORY:
return ENOTDIR;
case ERROR_DIR_NOT_EMPTY:
return ENOTEMPTY;
case ERROR_FILE_INVALID:
return ENXIO;
case ERROR_NOT_OWNER:
case ERROR_CANNOT_MAKE:
return EPERM;
case ERROR_BROKEN_PIPE:
case ERROR_NO_DATA:
return EPIPE;
case ERROR_WRITE_PROTECT:
return EROFS;
case ERROR_SETMARK_DETECTED:
case ERROR_BEGINNING_OF_MEDIA:
return ESPIPE;
case ERROR_NOT_SAME_DEVICE:
return EXDEV;
}
return ENOSYS;
}
#endif

50
src/win32_util.h Normal file
View File

@@ -0,0 +1,50 @@
/* win32_util.h
*
* This file is part of vchanger by Josh Fisher.
*
* vchanger copyright (C) 2008-2013 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 COMPAT_UTIL_H_
#define COMPAT_UTIL_H_
#ifdef HAVE_WINDOWS_H
/* Some utility functions needed for Windows version */
/* number of 100 ns intervals between FILETIME epoch and Unix epoch */
#define EPOCH_FILETIME (116444736000000000LL)
#ifdef __cplusplus
extern "C" {
#endif
time_t ftime2utime(const FILETIME *time);
wchar_t* AnsiToUTF16(const char *str, wchar_t **u16str, size_t *u16size);
char *UTF16ToAnsi(const wchar_t *u16str, char **str, size_t *str_size);
int w32errno(DWORD werr);
#ifdef __cplusplus
}
#endif
#endif
#endif /* _COMPAT_UTIL_H */