/****************************************************************************
**
** Copyright (C) 2003-2006 Frank Hemer <frank@hemer.org>,
** Tilo Riemer <riemer@crossvc.com>
**
**
**----------------------------------------------------------------------------
**
**----------------------------------------------------------------------------
**
** CrossVC is available under two different licenses:
**
** If CrossVC is linked against the GPLed version of Qt
** CrossVC is released under the terms of GPL also.
**
** If CrossVC is linked against a nonGPLed version of Qt
** CrossVC is released under the terms of the
** CrossVC License for non-Unix platforms (CLNU)
**
**
** CrossVC License for non-Unix platforms (CLNU):
**
** Redistribution and use in binary form, without modification,
** are permitted provided that the following conditions are met:
**
** 1. Redistributions in binary form must reproduce the above copyright
** notice, this list of conditions and the following disclaimer in the
** documentation and/or other materials provided with the distribution.
** 2. It is not permitted to distribute the binary package under a name
** different than CrossVC.
** 3. The name of the authors may not be used to endorse or promote
** products derived from this software without specific prior written
** permission.
** 4. The source code is the creative property of the authors.
** Extensions and development under the terms of the Gnu Public License
** are limited to the Unix platform. Any distribution or compilation of
** the source code against libraries licensed other than gpl requires
** the written permission of the authors.
**
**
** THIS SOFTWARE IS PROVIDED BY THE AUTHOR "AS IS" AND ANY EXPRESS OR
** IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
** WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
** ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
** DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
** DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
** GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
** INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
** WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
** NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
** SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
**
**
**
** CrossVC License for Unix platforms:
**
** This program is free software; you can redistribute it and/or modify
** it under the terms of the GNU General Public License as published by
** the Free Software Foundation, version 2 of the License.
** 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 version 2 for more details.
**
** You should have received a copy of the GNU General Public License
** along with this program; if not, write to the Free Software Foundation,
** Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
**
*****************************************************************************/
#include "config.h"
#include "globals.h"
#include <qapplication.h>
#include <qdir.h>
#include <qprocess.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/un.h>
#include <sys/stat.h>
#include <errno.h>
#include <fcntl.h>
#include <signal.h>
#include <unistd.h>
#include "DNotifyConnector.h"
#define errstr strerror(errno)
static int wfd = -1;
static void ** map = NULL;
static unsigned int max_fd = 0;
// static int snd = 0;
// static int rcv = 0;
static void
sigrt_handler(int, siginfo_t *si, void *)
{
static time_t lmt = 0;
static time_t lct = 0;
static int lfd = -1;
// qDebug("signal received");
int fd = si->si_fd;
// qDebug("fd: "+QString::number(fd));
// qDebug("fd: "+QString::number(fd)+", sending: "+QString::number(snd++)+", errno: "+QString::number(si->si_errno)+", code: "+QString::number(si->si_code));
if (wfd > 0) {
struct stat buf;
fstat(fd,&buf);
if (fd == lfd) {
if ( (buf.st_mtime == lmt) && (buf.st_ctime == lct) ) {
return;
}
else {
lmt = buf.st_mtime;
lct = buf.st_ctime;
}
} else {
lfd = fd;
lmt = buf.st_mtime;
lct = buf.st_ctime;
}
if ( write( wfd, &map[fd], sizeof(void*)) ) {
} else {
qDebug("signal handler: write failed: "+QString(errstr)+", err: "+QString::number(errno));
}
} else qDebug("signal handler: no socket fd for writing");
// qDebug("signal handler done\n");
}
DNotifyConnector::DNotifyConnector() : DirConnector(), m_child(-1), sockfd(-1), socketNotifier(0) {
int maxfd = 1024;
QProcess proc;
proc.addArgument("sh");
proc.addArgument("-c");
proc.addArgument("ulimit -n");
if (proc.start()) {
while (proc.isRunning()) {
qApp->processEvents();
}
if ( (proc.normalExit()) ) {
if ( (proc.exitStatus() == 0) && proc.canReadLineStdout() ) {
bool ok = FALSE;
int tmp = proc.readLineStdout().toInt(&ok);
if (ok) {
maxfd = tmp;
}
}
}
}// else qDebug("couldn't start proc");
qDebug("DNotify: maxfd set to: "+QString::number(maxfd));
int sv[2];
socketpair(AF_UNIX, SOCK_STREAM, 0, sv);
m_child = fork();
if (m_child < 0) {
qDebug("cannot fork child: "+QString(errstr));
return;
}
if (m_child == 0) { /* child */
// qDebug("fork ok");
wfd = sv[1];
map = new (void*)[maxfd];
for (int i = 0; i < maxfd; ++i) {
map[i] = NULL;
}
struct sigaction act;
sigset_t signalset;
sigemptyset(&signalset);
sigaddset(&signalset, SIGRTMAX);
/* Work around lpthread bug (libc/4927). */
if (sigprocmask(SIG_UNBLOCK, &signalset, NULL) < 0) {
qDebug("sigprocmask failed: "+QString(errstr)+", err: "+QString::number(errno));
return;
}
if (sigpending(&signalset) < 0) {
qDebug("sigpending failed: "+QString(errstr)+", err: "+QString::number(errno));
return;
}
/* Register SIGRTMIN signal handlers. */
act.sa_sigaction = sigrt_handler;
sigemptyset(&act.sa_mask);
act.sa_flags = SA_SIGINFO | SA_RESTART;
if (sigaction(SIGRTMAX, &act, NULL) < 0) {
qDebug("sigaction failed: "+QString(errstr)+", err: "+QString::number(errno));
return;
}
do {
char action;
if ( read( sv[1], &action, 1) == 1) {
void * dir;
if (action == 'a') {
// qDebug("received action: add");
int len;
if ( read( sv[1], &len, sizeof(int)) == sizeof(int)) {
char * path;
path = new char[len+1];
if ( read( sv[1], path, len) == len) {
path[len] = '\0';
if ( read( sv[1], &dir, sizeof(void*)) == sizeof(void*)) {
// qDebug("path is: "+QString(path));
int fd = open(path, O_RDONLY);
if (fd <= 0) {
qDebug("open returned faulty fd: "+QString::number(fd));
qDebug("current opened max fd: "+QString::number(max_fd));
qDebug("probably filedescriptors are exhausted, check with: 'ulimit -n'");
qDebug("DNotify exiting");
kill(getpid(),SIGKILL);
} else if (fd >= maxfd) {
qDebug("filedescriptor id exceeds size: "+QString::number(fd));
qDebug("DNotify exiting");
kill(getpid(),SIGKILL);
} else {
// qDebug("fd is: "+QString::number(fd));
bool failed = FALSE;
if (fcntl(fd, F_SETSIG, SIGRTMAX) < 0) {
qDebug("child: fcntl F_SETSIG on `"+QString(path)+"' failed: "+QString(errstr)+", err: "+QString::number(errno));
failed = TRUE;
}
if (fcntl(fd, F_NOTIFY, /*DN_ACCESS|*/DN_MODIFY|DN_CREATE|DN_DELETE|DN_RENAME|DN_ATTRIB|DN_MULTISHOT) < 0) {
qDebug("child: fcntl F_NOTIFY on `"+QString(path)+"' failed: "+QString(errstr)+", err: "+QString::number(errno));
failed = TRUE;
}
if (!failed) {
if (sigprocmask(SIG_BLOCK, &signalset, NULL) < 0) {
qDebug("sigprocmask failed: "+QString(errstr)+", err: "+QString::number(errno));
return;
}
map[fd] = dir;//qDebug("added");
if (sigprocmask(SIG_UNBLOCK, &signalset, NULL) < 0) {
qDebug("sigprocmask failed: "+QString(errstr)+", err: "+QString::number(errno));
return;
}
if (fd > (int)max_fd) max_fd = fd;
}
}
}
}
delete path;
}
} else if (action == 'r') {
// qDebug("received action: remove");
if ( read( sv[1], &dir, sizeof(void*)) == sizeof(void*)) {
if (sigprocmask(SIG_BLOCK, &signalset, NULL) < 0) {
qDebug("sigprocmask failed: "+QString(errstr)+", err: "+QString::number(errno));
return;
}
for (unsigned int i = 0; i <= max_fd; ++i) {
if (map[i] == dir) {
// qDebug("found fd: "+QString::number(i));
if ( close(i) < 0) {
qDebug("child close("+QString::number(i)+") failed: "+QString(errstr)+", err: "+QString::number(errno));
} else {
map[i] = NULL;//qDebug("removing");
}
break;
}
}
if (sigprocmask(SIG_UNBLOCK, &signalset, NULL) < 0) {
qDebug("sigprocmask failed: "+QString(errstr)+", err: "+QString::number(errno));
return;
}
}
} else {
qDebug("received unknown action");
}
} else {
qDebug("child read failed: "+QString(errstr)+", err: "+QString::number(errno));
}
} while(getppid() != 1);
delete [] map;
qDebug("child exiting");
exit(-1);
} else { /* parent */
sockfd = sv[0];
int flags = fcntl(sockfd, F_GETFL);
fcntl(sockfd, F_SETFL, flags | O_NONBLOCK);
socketNotifier = new QSocketNotifier(sockfd, QSocketNotifier::Read, 0);
connect(socketNotifier, SIGNAL(activated(int)), SLOT(readNotify()));
FD_ZERO(&m_wfds);
FD_SET(sockfd, &m_wfds);
}
m_initiated = true;
setRunning(true);
// qDebug("dnotify done");
}
DNotifyConnector::~DNotifyConnector() {
if (m_child > -1) {
kill(m_child,SIGKILL);
// qDebug("killed child");
}
setRunning(false);
}
void DNotifyConnector::readNotify() {
// qDebug("received notification");
// qDebug("receiving: "+QString::number(rcv++));
DirBase * dir;
if ( read( sockfd, &dir, sizeof(DirBase*)) == sizeof(DirBase*)) {
// qDebug("received notification of dirName: "+dir->fullName());
QString fileName = QString::null;
bool hlp = FALSE;
addToDirQueue(dir, fileName, hlp);
}
// qDebug("notification done\n");
}
bool DNotifyConnector::addMonitoredDir(DirBase * dir) {
if (!m_initiated) {
qDebug("trying to add: "+dir->fullName()+" but dnotify not initiated");
return FALSE;
}
const char * path = dir->fullName().latin1();
int len = strlen(path);
char action = 'a';
if (sockfd > 0) {
int n = -1;
struct timeval tv;
tv.tv_sec = 5;
tv.tv_usec = 0;
if ( (n = select(sockfd+1,NULL,&m_wfds,NULL,&tv)) > 0) {
if ( ( write( sockfd, &action, 1) == 1 ) &&
( write( sockfd, &len, sizeof(int)) == sizeof(int) ) &&
( write( sockfd, path, len) == len ) &&
( write( sockfd, &dir, sizeof(DirBase*)) == sizeof(DirBase *)) ) {
return TRUE;
} else {
qDebug("addMonitoredDir: write failed: "+QString(errstr)+", err: "+QString::number(errno));
}
} else if ( n == 0 ) {//overload
qDebug("addMonitoredDir: timeout on write --- disabling dirwatch, fallback to status checking (poll-mode)");
} else qDebug("addMonitoredDir: select: "+QString(errstr)+", err: "+QString::number(errno));
} else qDebug("addMonitoredDir: no socket fd for writing");
return FALSE;
}
void DNotifyConnector::releaseMonitoredDir(DirBase * dir) {
if (!m_initiated) {
qDebug("trying to release: "+dir->fullName()+", but dnotify not initiated");
return;
}
char action = 'r';
int n = 0;
if (sockfd > 0) {
if ( select(sockfd+1,NULL,&m_wfds,NULL,NULL) > 0) {
if ( ( (n = write( sockfd, &action, 1)) == 1 ) &&
( (n = write( sockfd, &dir, sizeof(DirBase*))) == sizeof(DirBase *)) ) {
} else {
qDebug("releaseMonitoredDir: write failed: "+QString(errstr)+", err: "+QString::number(errno));
}
} else qDebug("releaseMonitoredDir: select: "+QString(errstr)+", err: "+QString::number(errno));
} else qDebug("releaseMonitoredDir: no socket fd for writing");
return;
}
syntax highlighted by Code2HTML, v. 0.9.1