Add vchanger 1.0.3 code

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

View File

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

View File

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

View File

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

View File

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

View File

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

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

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

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

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

View File

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

View File

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

View File

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

View File

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

131
src/mymutex.cpp Normal file
View File

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

32
src/mymutex.h Normal file
View File

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

View File

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

View File

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

View File

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

View File

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

View File

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