mirror of
https://github.com/wanderleihuttel/vchanger.git
synced 2025-10-11 03:46:17 +00:00
Import project from sourceforge.net
This commit is contained in:
14
src/Makefile.am
Normal file
14
src/Makefile.am
Normal 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
722
src/Makefile.in
Normal 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
176
src/bconsole.cpp
Normal 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
29
src/bconsole.h
Normal 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
851
src/changerstate.cpp
Normal 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
137
src/changerstate.h
Normal 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
81
src/compat/getline.c
Normal 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
38
src/compat/getline.h
Normal 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
118
src/compat/gettimeofday.c
Normal 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
62
src/compat/gettimeofday.h
Normal 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
46
src/compat/getuid.c
Normal 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
45
src/compat/getuid.h
Normal 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
57
src/compat/localtime_r.c
Normal 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
39
src/compat/localtime_r.h
Normal 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
126
src/compat/readlink.c
Normal 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
42
src/compat/readlink.h
Normal 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
39
src/compat/sleep.c
Normal 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
42
src/compat/sleep.h
Normal 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
87
src/compat/symlink.c
Normal 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
42
src/compat/symlink.h
Normal 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
57
src/compat/syslog.c
Normal 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
80
src/compat/syslog.h
Normal 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
37
src/compat_defs.h
Normal 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
993
src/diskchanger.cpp
Normal 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
80
src/diskchanger.h
Normal 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
73
src/errhandler.cpp
Normal 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
43
src/errhandler.h
Normal 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
1185
src/inifile.cpp
Normal file
File diff suppressed because it is too large
Load Diff
190
src/inifile.h
Normal file
190
src/inifile.h
Normal 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 §, 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 §, 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 §, 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 §) { return FindSection(sect.c_str()) != kwmap.end(); }
|
||||
bool GetKey(const char *key, tString &base, size_t &ndx, tString §, tString &kw);
|
||||
bool GetKey(const tString &key, tString &base, size_t &ndx, tString §, 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 §, tString &kw);
|
||||
inline bool ParseKey(const tString &key, tString &base, size_t &ndx, tString §, 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 §) { 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
206
src/loghandler.cpp
Normal 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
70
src/loghandler.h
Normal 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
726
src/mypopen.cpp
Normal 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
37
src/mypopen.h
Normal 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
24
src/targetver.h
Normal 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
476
src/tstring.cpp
Normal 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
80
src/tstring.h
Normal 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
192
src/util.cpp
Normal 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
33
src/util.h
Normal 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
364
src/uuidlookup.c
Normal 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
37
src/uuidlookup.h
Normal 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
661
src/vchanger.cpp
Normal 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
309
src/vconf.cpp
Normal 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
70
src/vconf.h
Normal 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
259
src/win32_util.c
Normal 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
50
src/win32_util.h
Normal 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 */
|
Reference in New Issue
Block a user