Windows: monitor, reexec, pinging process. Not done yet.

This commit is contained in:
Jean-Francois Dockes 2022-01-18 20:27:44 +00:00
parent 266967bea7
commit 497b61e017
8 changed files with 204 additions and 57 deletions

View File

@ -567,7 +567,6 @@ bool startMonitor(RclConfig *conf, int opts)
}
}
#ifndef _WIN32
// Check for a config change
if (!(opts & RCLMON_NOCONFCHECK) && o_reexec && conf->sourceChanged()) {
LOGDEB("Rclmonprc: config changed, reexecuting myself\n");
@ -577,7 +576,6 @@ bool startMonitor(RclConfig *conf, int opts)
o_reexec->removeArg("-n");
o_reexec->reexec();
}
#endif // ! _WIN32
}
LOGDEB("Rclmonprc: calling queue setTerminate\n");
rclEQ.setTerminate();

View File

@ -103,9 +103,7 @@ static struct option long_options[] = {
{0, 0, 0, 0}
};
#ifndef _WIN32
ReExec *o_reexec;
#endif
// Globals for atexit cleanup
static ConfIndexer *confindexer;
@ -458,25 +456,19 @@ static void lockorexit(Pidfile *pidfile, RclConfig *config)
if (pid > 0) {
cerr << "Can't become exclusive indexer: " << pidfile->getreason()
<< ". Return (other pid?): " << pid << endl;
#ifndef _WIN32
// Have a look at the status file. If the other process is
// a monitor we can tell it to start an incremental pass
// by touching the configuration file
DbIxStatus status;
readIdxStatus(config, status);
if (status.hasmonitor) {
string cmd("touch ");
string path = path_cat(config->getConfDir(), "recoll.conf");
cmd += path;
int status;
if ((status = system(cmd.c_str()))) {
cerr << cmd << " failed with status " << status << endl;
if (!path_utimes(path, nullptr)) {
cerr << "Could not notify indexer" << endl;
} else {
cerr << "Monitoring indexer process was notified of "
"indexing request\n";
cerr << "Monitoring indexer process was notified of indexing request" << endl;
}
}
#endif
} else {
cerr << "Can't become exclusive indexer: " << pidfile->getreason()
<< endl;
@ -525,16 +517,17 @@ static void flushIdxReasons()
static vector<const char*> argstovector(int argc, wchar_t **argv, vector<string>& storage)
#else
#define WARGTOSTRING(w) (w)
static vector<const char*> argstovector(int argc, char **argv, vector<string>& storage)
static vector<const char*> argstovector(int argc, char **argv, vector<string>& storage)
#endif
{
vector<const char *> args(argc+1);
storage.resize(argc+1);
storage.resize(argc);
thisprog = path_absolute(WARGTOSTRING(argv[0]));
for (int i = 0; i < argc; i++) {
storage[i] = WARGTOSTRING(argv[i]);
args[i] = storage[i].c_str();
}
args[argc] = 0;
return args;
}
@ -556,17 +549,13 @@ int wmain(int argc, wchar_t *argv[])
int main(int argc, char *argv[])
#endif
{
#ifndef _WIN32
// The reexec struct is used by the daemon to shed memory after
// the initial indexing pass and to restart when the configuration
// changes
o_reexec = new ReExec;
o_reexec->init(argc, argv);
#endif
// Only actually useful on Windows: convert wargs to utf-8 chars
vector<string> astore;
vector<const char*> args = argstovector(argc, argv, astore);
// The reexec struct is used by the daemon to shed memory after
// the initial indexing pass and to restart when the configuration
// changes
o_reexec = new ReExec(astore);
vector<string> selpatterns;
int sleepsecs{60};
@ -683,10 +672,7 @@ int main(int argc, char *argv[])
exit(0);
}
#ifndef _WIN32
o_reexec->atexit(cleanup);
#endif
vector<string> nonexist;
if (!checktopdirs(config, nonexist)) {
std::cerr << "topdirs not set or only contains invalid paths.\n";
@ -848,8 +834,8 @@ int main(int argc, char *argv[])
Usage();
statusUpdater()->setMonitor(true);
if (!(op_flags&OPT_D)) {
LOGDEB("recollindex: daemonizing\n");
#ifndef _WIN32
LOGDEB("recollindex: daemonizing\n");
if (daemon(0,0) != 0) {
addIdxReason("monitor", "daemon() failed");
cerr << "daemon() failed, errno " << errno << endl;
@ -895,16 +881,13 @@ int main(int argc, char *argv[])
}
}
deleteZ(confindexer);
#ifndef _WIN32
o_reexec->insertArgs(vector<string>(1, "-n"));
LOGINFO("recollindex: reexecuting with -n after initial full "
"pass\n");
LOGINFO("recollindex: reexecuting with -n after initial full pass\n");
// Note that -n will be inside the reexec when we come
// back, but the monitor will explicitly strip it before
// starting a config change exec to ensure that we do a
// purging pass in this latter case (full restart).
o_reexec->reexec();
#endif
}
statusUpdater()->update(DbIxStatus::DBIXS_MONITOR, "");

View File

@ -176,13 +176,15 @@ bool renameormove(const char *src, const char *dst, string &reason)
reason += string("Chown ") + dst + "Error : " + strerror(errno);
}
}
struct timeval times[2];
#endif
struct path_timeval times[2];
times[0].tv_sec = st.st_atime;
times[0].tv_usec = 0;
times[1].tv_sec = st.st_mtime;
times[1].tv_usec = 0;
utimes(dst, times);
#endif
path_utimes(dst, times);
// All ok, get rid of origin
if (!path_unlink(src)) {
reason += string("Can't unlink ") + src + "Error : " + strerror(errno);

View File

@ -1064,11 +1064,6 @@ std::string ExecCmd::waitStatusAsString(int wstatus)
/// ReExec class methods ///////////////////////////////////////////////////
ReExec::ReExec(int argc, char *args[])
{
init(argc, args);
}
void ReExec::init(int argc, char *args[])
{
for (int i = 0; i < argc; i++) {
m_argv.push_back(args[i]);
@ -1081,6 +1076,17 @@ void ReExec::init(int argc, char *args[])
free(cd);
}
ReExec::ReExec(const std::vector<std::string>& args)
: m_argv(args)
{
m_cfd = open(".", 0);
char *cd = getcwd(0, 0);
if (cd) {
m_curdir = cd;
}
free(cd);
}
void ReExec::insertArgs(const vector<string>& args, int idx)
{
vector<string>::iterator it;

View File

@ -284,7 +284,8 @@ class ReExec {
public:
ReExec() {}
ReExec(int argc, char *argv[]);
void init(int argc, char *argv[]);
// Mostly useful with Windows and wmain: args must be utf-8
ReExec(const std::vector<std::string>& args);
int atexit(void (*function)(void)) {
m_atexitfuncs.push(function);
return 0;
@ -302,7 +303,7 @@ public:
private:
std::vector<std::string> m_argv;
std::string m_curdir;
int m_cfd;
int m_cfd{-1};
std::string m_reason;
std::stack<void (*)(void)> m_atexitfuncs;
};

View File

@ -100,6 +100,7 @@
#include <direct.h>
#include <Shlobj.h>
#include <Stringapiset.h>
#include <sys/utime.h>
#if !defined(S_IFLNK)
#define S_IFLNK 0
@ -152,6 +153,7 @@
#else /* !_WIN32 -> */
#include <unistd.h>
#include <sys/time.h>
#include <sys/param.h>
#include <pwd.h>
#include <sys/file.h>
@ -333,8 +335,6 @@ static bool path_isdriveabs(const string& s)
/* Can be OR'd in to one of the above. */
# define LOCK_NB 4 /* Don't block when locking. */
#include <io.h>
/* Determine the current size of a file. Because the other braindead
* APIs we'll call need lower/upper 32 bit pairs, keep the file size
* like that too.
@ -953,6 +953,34 @@ bool path_rmdir(const std::string& path)
return RMDIR(syspath) == 0;
}
bool path_utimes(const std::string& path, struct path_timeval _tv[2])
{
#ifdef _WIN32
struct _utimbuf times;
if (nullptr == _tv) {
times.actime = times.modtime = time(0L);
} else {
times.actime = _tv[0].tv_sec;
times.modtime = _tv[1].tv_sec;
}
SYSPATH(path, syspath);
return _wutime(syspath, &times) != -1;
#else
struct timeval tvb[2];
if (nullptr == _tv) {
gettimeofday(tvb, nullptr);
tvb[1].tv_sec = tvb[0].tv_sec;
tvb[1].tv_usec = tvb[0].tv_usec;
} else {
tvb[0].tv_sec = _tv[0].tv_sec;
tvb[0].tv_usec = _tv[0].tv_usec;
tvb[1].tv_sec = _tv[1].tv_sec;
tvb[1].tv_usec = _tv[1].tv_usec;
}
return utimes(path.c_str(), tvb) == 0;
#endif
}
bool path_streamopen(const std::string& path, int mode, std::fstream& outstream)
{
#if defined(_WIN32) && defined (_MSC_VER)
@ -1462,11 +1490,35 @@ Pidfile::~Pidfile()
this->close();
}
#ifdef _WIN32
// It appears that we can't read the locked file on Windows, so we use
// separate files for locking and holding the data.
static std::string pid_data_path(const std::string& path)
{
// Remove extension. append -data to name, add back extension.
auto ext = path_suffix(path);
auto spath = path_cat(path_getfather(path), path_basename(path, ext));
if (spath.back() == '.')
spath.pop_back();
if (!ext.empty())
spath += std::string("-data") + "." + ext;
return spath;
}
#endif // _WIN32
int Pidfile::read_pid()
{
#ifdef _WIN32
// It appears that we can't read the locked file on Windows, so use an aux file
auto path = pid_data_path(m_path);
SYSPATH(path, syspath);
#else
SYSPATH(m_path, syspath);
#endif
int fd = OPEN(syspath, O_RDONLY);
if (fd == -1) {
if (errno != ENOENT)
m_reason = "Open RDONLY failed: [" + m_path + "]: " + strerror(errno);
return -1;
}
@ -1474,12 +1526,14 @@ int Pidfile::read_pid()
int i = read(fd, buf, sizeof(buf) - 1);
::close(fd);
if (i <= 0) {
m_reason = "Read failed: [" + m_path + "]: " + strerror(errno);
return -1;
}
buf[i] = '\0';
char *endptr;
int pid = strtol(buf, &endptr, 10);
if (endptr != &buf[i]) {
m_reason = "Bad pid contents: [" + m_path + "]: " + strerror(errno);
return - 1;
}
return pid;
@ -1501,8 +1555,8 @@ int Pidfile::flopen()
lockdata.l_whence = SEEK_SET;
if (fcntl(m_fd, F_SETLK, &lockdata) != 0) {
int serrno = errno;
this->close()
errno = serrno;
this->close();
errno = serrno;
m_reason = "fcntl lock failed";
return -1;
}
@ -1538,18 +1592,33 @@ int Pidfile::open()
int Pidfile::write_pid()
{
#ifdef _WIN32
// It appears that we can't read the locked file on Windows, so use an aux file
auto path = pid_data_path(m_path);
SYSPATH(path, syspath);
int fd;
if ((fd = OPEN(syspath, O_RDWR | O_CREAT, 0644)) == -1) {
m_reason = "Open failed: [" + path + "]: " + strerror(errno);
return -1;
}
#else
int fd = m_fd;
#endif
/* truncate to allow multiple calls */
if (ftruncate(m_fd, 0) == -1) {
if (ftruncate(fd, 0) == -1) {
m_reason = "ftruncate failed";
return -1;
}
char pidstr[20];
sprintf(pidstr, "%u", int(getpid()));
lseek(m_fd, 0, 0);
if (::write(m_fd, pidstr, strlen(pidstr)) != (PATHUT_SSIZE_T)strlen(pidstr)) {
lseek(fd, 0, 0);
if (::write(fd, pidstr, strlen(pidstr)) != (PATHUT_SSIZE_T)strlen(pidstr)) {
m_reason = "write failed";
return -1;
}
#ifdef _WIN32
close(fd);
#endif
return 0;
}

View File

@ -143,8 +143,7 @@ struct PathStat {
uint64_t pst_blocks;
uint64_t pst_blksize;
};
extern int path_fileprops(const std::string path, struct PathStat *stp,
bool follow = true);
extern int path_fileprops(const std::string path, struct PathStat *stp, bool follow = true);
/// Return separator for PATH environment variable
@ -171,8 +170,7 @@ private:
};
/// Dump directory
extern bool listdir(const std::string& dir, std::string& reason,
std::set<std::string>& entries);
extern bool listdir(const std::string& dir, std::string& reason, std::set<std::string>& entries);
/** A small wrapper around statfs et al, to return percentage of disk
occupation
@ -189,7 +187,15 @@ bool path_chdir(const std::string& path);
std::string path_cwd();
bool path_unlink(const std::string& path);
bool path_rmdir(const std::string& path);
// Setting file times. Windows defines timeval in winsock2.h but it seems safer to use local def
// Also on Windows, we use _wutime and ignore the tv_usec part.
typedef struct path_timeval {
long tv_sec;
long tv_usec;
} path_timeval;
bool path_utimes(const std::string& path, struct path_timeval times[2]);
/* Open file, trying to do the right thing with non-ASCII paths on
* Windows, where it only works with MSVC at the moment if the path is
* not ASCII, because it uses fstream(wchar_t*), which is an MSVC
@ -200,12 +206,10 @@ bool path_rmdir(const std::string& path);
*
* @param path an utf-8 file path.
* @param mode is an std::fstream mode (ios::in etc.) */
extern bool path_streamopen(
const std::string& path, int mode, std::fstream& outstream);
extern bool path_streamopen(const std::string& path, int mode, std::fstream& outstream);
/// Encode according to rfc 1738
extern std::string url_encode(const std::string& url,
std::string::size_type offs = 0);
extern std::string url_encode(const std::string& url, std::string::size_type offs = 0);
extern std::string url_decode(const std::string& encoded);
//// Convert to file path if url is like file://. This modifies the
//// input (and returns a copy for convenience)

View File

@ -1168,3 +1168,87 @@ std::string ExecCmd::waitStatusAsString(int wstatus)
return oss.str();
}
///////////// ReExec class methods.
ReExec::ReExec(int argc, char *args[])
{
for (int i = 0; i < argc; i++) {
m_argv.push_back(args[i]);
}
}
ReExec::ReExec(const std::vector<std::string>& args)
: m_argv(args)
{
}
void ReExec::insertArgs(const vector<string>& args, int idx)
{
vector<string>::iterator it;
unsigned int cmpoffset = (unsigned int) - 1;
if (idx == -1 || string::size_type(idx) >= m_argv.size()) {
it = m_argv.end();
if (m_argv.size() >= args.size()) {
cmpoffset = m_argv.size() - args.size();
}
} else {
it = m_argv.begin() + idx;
if (idx + args.size() <= m_argv.size()) {
cmpoffset = idx;
}
}
// Check that the option is not already there
if (cmpoffset != (unsigned int) - 1) {
bool allsame = true;
for (unsigned int i = 0; i < args.size(); i++) {
if (m_argv[cmpoffset + i] != args[i]) {
allsame = false;
break;
}
}
if (allsame) {
return;
}
}
m_argv.insert(it, args.begin(), args.end());
}
void ReExec::removeArg(const string& arg)
{
for (vector<string>::iterator it = m_argv.begin(); it != m_argv.end(); it++) {
if (*it == arg) {
it = m_argv.erase(it);
}
}
}
void ReExec::reexec()
{
// Execute the atexit funcs
while (!m_atexitfuncs.empty()) {
(m_atexitfuncs.top())();
m_atexitfuncs.pop();
}
// Allocate arg vector (1 more for final 0)
typedef const wchar_t *Ccharp;
Ccharp *argv;
argv = (Ccharp *)malloc((m_argv.size() + 1) * sizeof(wchar_t *));
if (argv == 0) {
LOGERR("ExecCmd::doexec: malloc() failed. errno " << errno << "\n");
return;
}
// Fill up argv
int i = 0;
std::vector<std::unique_ptr<wchar_t[]>> ptrs;
for (const auto& arg: m_argv) {
ptrs.push_back(utf8towchar(arg));
argv[i++] = ptrs.back().get();
}
argv[i] = nullptr;
_wexecvp(argv[0], argv);
}