cleanup of filedescriptors, added dryrun option

This commit is contained in:
Axel Kittenberger 2010-11-25 22:10:24 +00:00
parent 134f570ac4
commit 5519e2be8e
5 changed files with 210 additions and 140 deletions

View File

@ -1,7 +1,7 @@
AUTOMAKE_OPTIONS = foreign
CFLAGS = -Wall $(LIBLUA_CFLAGS)
bin_PROGRAMS = lsyncd
lsyncd_SOURCES = lsyncd.c lsyncd.lua inotify.c
lsyncd_SOURCES = lsyncd.h lsyncd.c lsyncd.lua inotify.c
lsyncd_LDADD = $(LIBLUA_LIBS)
exampledir = $(datarootdir)/doc/@PACKAGE@
dist_example_DATA = \

View File

@ -252,8 +252,13 @@ static char * readbuf = NULL;
* to the runner.
*/
static void
inotify_ready(lua_State *L, int fd, void *extra)
inotify_ready(lua_State *L, struct observance *observance)
{
if (observance->fd != inotify_fd) {
logstring("Error",
"internal fail, inotify_ready on non-inotify file descriptor.");
exit(-1); // ERRNO
}
while(true) {
ptrdiff_t len;
int err;
@ -307,6 +312,18 @@ inotify_ready(lua_State *L, int fd, void *extra)
}
}
/**
* Called by function pointer when the core doesnt want the
* inotify fd anymore (term, hup or overflow)
*/
static void
inotify_tidy(struct observance *observance)
{
close(observance->fd);
free(readbuf);
readbuf = NULL;
}
/**
* registers inotify functions.
*/
@ -338,16 +355,6 @@ open_inotify(lua_State *L) {
close_exec_fd(inotify_fd);
non_block_fd(inotify_fd);
observe_fd(inotify_fd, inotify_ready, NULL, NULL);
}
/**
* closes inotify
*/
extern void
close_inotify() {
close(inotify_fd);
free(readbuf);
readbuf = NULL;
observe_fd(inotify_fd, inotify_ready, NULL, inotify_tidy, NULL);
}

209
lsyncd.c
View File

@ -158,7 +158,7 @@ add_logcat(const char *name, int priority)
return true;
}
if (!strcmp("scarce", name)) {
settings.log_level = LOG_ERR;
settings.log_level = LOG_WARNING;
return true;
}
@ -364,26 +364,31 @@ struct pipemsg {
* writeable again
*/
static void
pipe_writey(lua_State *L, int fd, void *extra) {
struct pipemsg *pm = (struct pipemsg *) extra;
int len = write(fd, pm->text + pm->pos, pm->tlen - pm->pos);
bool do_close = false;
pipe_writey(lua_State *L, struct observance *observance) {
struct pipemsg *pm = (struct pipemsg *) observance->extra;
int len = write(observance->fd, pm->text + pm->pos, pm->tlen - pm->pos);
pm->pos += len;
if (len < 0) {
logstring("Normal", "broken pipe.");
do_close = true;
nonobserve_fd(observance->fd);
} else if (pm->pos >= pm->tlen) {
logstring("Debug", "finished pipe.");
do_close = true;
}
if (do_close) {
close(fd);
free(pm->text);
free(extra);
unobserve_fd(fd);
nonobserve_fd(observance->fd);
}
}
/**
* TODO
*/
static void
pipe_tidy(struct observance *observance)
{
struct pipemsg *pm = (struct pipemsg *) observance->extra;
close(observance->fd);
free(pm->text);
free(pm);
}
/*****************************************************************************
* helper routines.
@ -691,7 +696,6 @@ l_exec(lua_State *L)
len = write(pipefd[1], pipe_text, tlen);
if (len < 0) {
logstring("Normal", "immediatly broken pipe.");
close(pipefd[0]);
}
if (len == tlen) {
/* usual and best case, the pipe accepted all input -> close */
@ -699,14 +703,13 @@ l_exec(lua_State *L)
logstring("Exec", "one-sweeped pipe");
} else {
struct pipemsg *pm;
logstring("Exec", "adding pipe observer");
logstring("Exec", "adding pipe observance");
pm = s_calloc(1, sizeof(struct pipemsg));
pm->text = s_strdup(pipe_text);
pm->tlen = tlen;
pm->pos = len;
observe_fd(pipefd[1], NULL, pipe_writey, pm);
observe_fd(pipefd[1], NULL, pipe_writey, pipe_tidy, pm);
}
close(pipefd[0]);
}
free(argv);
lua_pushnumber(L, pid);
@ -887,6 +890,7 @@ l_configure(lua_State *L)
if (!settings.log_file) {
settings.log_syslog = true;
}
// XXX
logstring("Debug", "daemonizing now.");
if (daemon(0, 0)) {
logstring("Error", "Failed to daemonize");
@ -973,38 +977,12 @@ load_runner_func(lua_State *L,
}
/**
* An observer to be called when a file descritor becomes
* read-ready or write-ready.
*/
struct observer {
/**
* The file descriptor to observe.
*/
int fd;
/**
* Function to call when read becomes ready.
*/
void (*ready)(lua_State *L, int fd, void *extra);
/**
* Function to call when write becomes ready.
*/
void (*writey)(lua_State *L, int fd, void *extra);
/**
* Extra tokens to pass to the functions-
*/
void *extra;
};
/**
* List of file descriptor watches.
*/
static struct observer * observers = NULL;
static int observers_len = 0;
static int observers_size = 0;
static struct observance * observances = NULL;
static int observances_len = 0;
static int observances_size = 0;
/**
* List of file descriptors to unobserve.
@ -1012,14 +990,14 @@ static int observers_size = 0;
* not be altered, thus unobserve stores here the
* actions that will be delayed.
*/
static int *unobservers = NULL;
static int unobservers_len = 0;
static int unobservers_size = 0;
static int *nonobservances = NULL;
static int nonobservances_len = 0;
static int nonobservances_size = 0;
/**
* true while the observers list is being handled.
* true while the observances list is being handled.
*/
static bool observer_action = false;
static bool observance_action = false;
/**
* Core watches a filedescriptor to become ready,
@ -1027,83 +1005,88 @@ static bool observer_action = false;
*/
extern void
observe_fd(int fd,
void (*ready)(lua_State *L, int fd, void *extra),
void (*writey)(lua_State *L, int fd, void *extra),
void *extra)
void (*ready) (lua_State *L, struct observance *observance),
void (*writey)(lua_State *L, struct observance *observance),
void (*tidy) (struct observance *observance),
void *extra)
{
int pos;
if (observer_action) {
if (observance_action) {
// TODO
logstring("Error",
"Adding observers in ready/writey handlers not yet supported");
"Adding observance in ready/writey handlers not yet supported");
exit(-1); // ERRNO
}
if (observers_len + 1 > observers_size) {
observers_size = observers_len + 1;
observers = s_realloc(observers,
observers_size * sizeof(struct observer));
if (observances_len + 1 > observances_size) {
observances_size = observances_len + 1;
observances = s_realloc(observances,
observances_size * sizeof(struct observance));
}
for(pos = 0; pos < observers_len; pos++) {
if (observers[pos].fd <= fd) {
for(pos = 0; pos < observances_len; pos++) {
if (observances[pos].fd <= fd) {
break;
}
}
if (observers[pos].fd == fd) {
if (observances[pos].fd == fd) {
logstring("Error",
"Observing already an observed file descriptor.");
exit(-1); // ERRNO
}
memmove(observers + pos + 1, observers + pos,
(observers_len - pos) * (sizeof(struct observer)));
memmove(observances + pos + 1, observances + pos,
(observances_len - pos) * (sizeof(struct observance)));
observers_len++;
observers[pos].fd = fd;
observers[pos].ready = ready;
observers[pos].writey = writey;
observers[pos].extra = extra;
observances_len++;
observances[pos].fd = fd;
observances[pos].ready = ready;
observances[pos].writey = writey;
observances[pos].tidy = tidy;
observances[pos].extra = extra;
}
/**
* Makes core no longer watch fd.
*/
extern void
unobserve_fd(int fd)
nonobserve_fd(int fd)
{
int pos;
if (observer_action) {
if (observance_action) {
/* this function is called through a ready/writey handler
* while the core works through the observer list, thus
* while the core works through the observances list, thus
* it does not alter the list, but stores this actions
* on a stack
*/
unobservers_len++;
if (unobservers_len > unobservers_size) {
unobservers_size = unobservers_len;
unobservers = s_realloc(unobservers,
unobservers_size * sizeof(int));
nonobservances_len++;
if (nonobservances_len > nonobservances_size) {
nonobservances_size = nonobservances_len;
nonobservances = s_realloc(nonobservances,
nonobservances_size * sizeof(int));
}
unobservers[unobservers_len - 1] = fd;
nonobservances[nonobservances_len - 1] = fd;
return;
}
/* looks for the fd */
for(pos = 0; pos < observers_len; pos++) {
if (observers[pos].fd == fd) {
for(pos = 0; pos < observances_len; pos++) {
if (observances[pos].fd == fd) {
break;
}
}
if (pos >= observers_len) {
if (pos >= observances_len) {
logstring("Error",
"internal fail, not observer file descriptor in unobserve");
"internal fail, not observed file descriptor in nonobserve_fd()");
exit(-1); //ERRNO
}
if (observances[pos].tidy) {
observances[pos].tidy(observances + pos);
}
/* and moves the list down */
memmove(observers + pos, observers + pos + 1,
(observers_len - pos) * (sizeof(struct observer)));
observers_len--;
memmove(observances + pos, observances + pos + 1,
(observances_len - pos) * (sizeof(struct observance)));
observances_len--;
}
/**
@ -1164,52 +1147,52 @@ masterloop(lua_State *L)
FD_ZERO(&rfds);
FD_ZERO(&wfds);
for(pi = 0; pi < observers_len; pi++) {
int fd = observers[pi].fd;
if (observers[pi].ready) {
for(pi = 0; pi < observances_len; pi++) {
int fd = observances[pi].fd;
if (observances[pi].ready) {
FD_SET(fd, &rfds);
}
if (observers[pi].writey) {
if (observances[pi].writey) {
FD_SET(fd, &wfds);
}
}
/* the great select */
pr = pselect(
observers[observers_len - 1].fd + 1,
observances[observances_len - 1].fd + 1,
&rfds, &wfds, NULL,
have_alarm ? &tv : NULL, &sigset);
if (pr >= 0) {
/* walks through the observers calling ready/writey fds */
observer_action = true;
for(pi = 0; pi < observers_len; pi++) {
int fd = observers[pi].fd;
void *extra = observers[pi].extra;
/* walks through the observances calling ready/writey fds */
observance_action = true;
for(pi = 0; pi < observances_len; pi++) {
struct observance *obs = observances + pi;
int fd = obs->fd;
if (hup || term) {
break;
}
if (observers[pi].ready && FD_ISSET(fd, &rfds)) {
observers[pi].ready(L, fd, extra);
if (obs->ready && FD_ISSET(fd, &rfds)) {
obs->ready(L, obs);
}
if (hup || term) {
break;
}
if (unobservers_len > 0 &&
unobservers[unobservers_len - 1] == fd) {
/* ready() unobserved itself */
if (nonobservances_len > 0 &&
nonobservances[nonobservances_len - 1] == fd) {
/* ready() nonobserved itself */
continue;
}
if (observers[pi].writey && FD_ISSET(fd, &wfds)) {
observers[pi].writey(L, fd, extra);
if (obs->writey && FD_ISSET(fd, &wfds)) {
obs->writey(L, obs);
}
}
observer_action = false;
observance_action = false;
/* work through delayed unobserve_fd() calls */
for (pi = 0; pi < unobservers_len; pi++) {
unobserve_fd(unobservers[pi]);
for (pi = 0; pi < nonobservances_len; pi++) {
nonobserve_fd(nonobservances[pi]);
}
unobservers_len = 0;
nonobservances_len = 0;
}
}
}
@ -1305,6 +1288,7 @@ main1(int argc, char *argv[])
/* prepares logging early */
int i = 1;
add_logcat("Normal", LOG_NOTICE);
add_logcat("Warn", LOG_WARNING);
add_logcat("Error", LOG_ERR);
while (i < argc) {
if (strcmp(argv[i], "-log") && strcmp(argv[i], "--log")) {
@ -1528,6 +1512,16 @@ main1(int argc, char *argv[])
masterloop(L);
/* cleanup */
{
/* close/frees observances */
int i;
for(i = 0; i < observances_len; i++) {
struct observance *obs = observances + i;
if (obs->tidy) {
obs->tidy(obs);
}
}
}
{
/* frees logging categories */
int ci;
@ -1556,7 +1550,6 @@ main1(int argc, char *argv[])
settings.log_level = 0,
settings.nodaemon = false,
close_inotify();
lua_close(L);
return 0;
}

View File

@ -103,23 +103,43 @@ extern void non_block_fd(int fd);
/* Sets the close-on-exit flag for a file descriptor. */
extern void close_exec_fd(int fd);
/**
* An observance calls functions when a file descritor becomes
* read-ready or write-ready.
*/
struct observance {
/* The file descriptor observed. */
int fd;
/* Function to call when read becomes ready. */
void (*ready)(lua_State *L, struct observance *observance);
/* Function to call when write becomes ready. */
void (*writey)(lua_State *L, struct observance *observance);
/* Function that tidies up, closes fd, frees extra data. etc. */
void (*tidy)(struct observance *observance);
/* Extra tokens to pass to the functions */
void *extra;
};
/* makes the core to observe a file descriptor */
extern void observe_fd(
int fd,
void (*ready)(lua_State *L, int fd, void *extra),
void (*writey)(lua_State *L, int fd, void *extra),
void *extra);
extern void
observe_fd(int fd,
void (*ready) (lua_State *L, struct observance *observance),
void (*writey)(lua_State *L, struct observance *observance),
void (*tidy) (struct observance *observance),
void *extra);
/* stops the core to observe a file descriptor */
extern void unobserve_fd(int fd);
extern void nonobserve_fd(int fd);
/*-----------------------------------------------------------------------------
* inotify
*/
extern void register_inotify(lua_State *L);
extern void open_inotify(lua_State *L);
extern void close_inotify();
#endif

View File

@ -1402,9 +1402,6 @@ local Syncs = (function()
end
-- loads a default value for an option if not existent
if not settings then
settings = {}
end
local defaultValues = {
'action',
'collapse',
@ -2075,6 +2072,7 @@ USAGE:
lsyncd [OPTIONS] -rsyncssh [SOURCE] [HOST] [TARGETDIR]
OPTIONS:
-dryrun Subprocesses are not actually invoked.
-help Shows this
-log all Logs everything (debug)
-log scarce Logs errors only
@ -2111,6 +2109,10 @@ function runner.configure(args)
-- a list of all valid --options
local options = {
-- log is handled by core already.
dryrun =
{0, function()
clSettings.dryrun=true
end},
log =
{1, nil},
logfile =
@ -2407,13 +2409,42 @@ function spawn(agent, binary, ...)
if lsyncdStatus == "fade" then
log("Normal", "ignored spawn processs since status fading")
end
local pid = lsyncd.exec(binary, ...)
if pid and pid > 0 then
if not settings.dryrun then
local pid = lsyncd.exec(binary, ...)
if pid and pid > 0 then
local sync = InletControl.getSync()
local delay = InletControl.getDelay(agent)
if delay then
delay.status = "active"
sync.processes[pid] = delay
else
local dlist = InletControl.getDelayList(agent)
if not dlist then
error("spawning with an unknown agent", 2)
end
for k, d in pairs(dlist) do
if type(k) == "number" then
d.status = "active"
end
end
sync.processes[pid] = dlist
end
end
else
local a1, a2 = ...
if a1 ~= "<" then
log("Normal", "would call ", binary, " ", table.concat({...}, " "))
else
local aa = {...}
table.remove(aa, 1)
table.remove(aa, 1)
log("Normal", "would call ", binary, " ", table.concat(aa, " "),
"\ninput pipe <\n", a2)
end
local sync = InletControl.getSync()
local delay = InletControl.getDelay(agent)
if delay then
delay.status = "active"
sync.processes[pid] = delay
sync:removeDelay(delay)
else
local dlist = InletControl.getDelayList(agent)
if not dlist then
@ -2421,10 +2452,9 @@ function spawn(agent, binary, ...)
end
for k, d in pairs(dlist) do
if type(k) == "number" then
d.status = "active"
sync:removeDelay(d)
end
end
sync.processes[pid] = dlist
end
end
end
@ -2436,7 +2466,6 @@ function spawnShell(agent, command, ...)
return spawn(agent, "/bin/sh", "-c", command, "/bin/sh", ...)
end
-----
-- Comfort routine also for user.
-- Returns true if 'String' starts with 'Start'
@ -2454,6 +2483,27 @@ function string.ends(String,End)
end
------
-- Replaces default os.execute with a warning message,
-- doing the execute nevertheless, if not in dryrun.
--
local os_execute = os.execute
os.execute = function(...)
log("Warn", "using os.execute makes Lsyncd splutter, use spawn() instead.")
if not settings.dryrun then
os_execute(...)
else
log("Normal", "would os.execute ", table.concat({...}, " "))
end
end
-----
-- An empty settings table optionally for the config to
-- expand instead of replace.
--
settings = {}
--============================================================================
-- Lsyncd default settings
--============================================================================