Subversion Repositories public

Compare Revisions

Ignore whitespace Rev 60 → Rev 61

/incron/trunk/incrontab.5
1,4 → 1,4
.TH "incrontab" "5" "0.3.1" "Lukas Jelinek" "incron documentation"
.TH "incrontab" "5" "0.3.3" "Lukas Jelinek" "incron documentation"
.SH "NAME"
incrontab \- tables for driving inotify cron (incron)
.SH "DESCRIPTION"
/incron/trunk/ict-main.cpp
103,22 → 103,8
/// Program arguments
static struct argp argp = { options, parse_opt, args_doc, doc };
 
/// Unlink a file with temporarily changed UID.
/**
* \param[in] file file to unlink
* \param[in] uid UID for unlink processing
*
* \attention No error checking is done!
*/
void unlink_suid(const char* file, uid_t uid)
{
uid_t iu = geteuid();
seteuid(uid);
if (unlink(file) != 0)
perror("cannot remove temporary file");
seteuid(iu);
}
 
 
/// Copies a file to an user table.
/**
* \param[in] path path to file
207,15 → 193,18
fprintf(stderr, "cannot find user %s: %s\n", user, strerror(errno));
return false;
}
uid_t uid = ppwd->pw_uid;
uid_t gid = ppwd->pw_gid;
char s[NAME_MAX];
strcpy(s, "/tmp/incron.table-XXXXXX");
uid_t iu = geteuid();
uid_t ig = getegid();
 
if (seteuid(uid) != 0) {
fprintf(stderr, "cannot change effective UID to %i: %s\n", (int) uid, strerror(errno));
if (seteuid(uid) != 0 || setegid(gid) != 0) {
fprintf(stderr, "cannot change effective UID/GID for user %s: %s\n", user, strerror(errno));
return false;
}
225,29 → 214,32
return false;
}
bool ok = false;
FILE* out = NULL;
FILE* in = NULL;
time_t mt = (time_t) 0;
const char* e = NULL;
if (fchmod(fd, 0644) != 0) {
fprintf(stderr, "cannot change mode of temporary file: %s\n", strerror(errno));
close(fd);
unlink_suid(s, uid);
return false;
goto end;
}
if (seteuid(iu) != 0) {
fprintf(stderr, "cannot change effective UID: %s\n", strerror(errno));
if (seteuid(iu) != 0 || setegid(ig) != 0) {
fprintf(stderr, "cannot change effective UID/GID: %s\n", strerror(errno));
close(fd);
unlink_suid(s, uid);
return false;
goto end;
}
FILE* out = fdopen(fd, "w");
out = fdopen(fd, "w");
if (out == NULL) {
fprintf(stderr, "cannot write to temporary file: %s\n", strerror(errno));
close(fd);
unlink_suid(s, uid);
return false;
goto end;
}
FILE* in = fopen(tp.c_str(), "r");
in = fopen(tp.c_str(), "r");
if (in == NULL) {
if (errno == ENOENT) {
in = fopen("/dev/null", "r");
254,15 → 246,13
if (in == NULL) {
fprintf(stderr, "cannot get empty table for %s: %s\n", user, strerror(errno));
fclose(out);
unlink_suid(s, uid);
return false;
goto end;
}
}
else {
fprintf(stderr, "cannot read old table for %s: %s\n", user, strerror(errno));
fclose(out);
unlink_suid(s, uid);
return false;
goto end;
}
}
276,68 → 266,69
struct stat st;
if (stat(s, &st) != 0) {
fprintf(stderr, "cannot stat temporary file: %s\n", strerror(errno));
unlink_suid(s, uid);
return false;
goto end;
}
time_t mt = st.st_mtime;
mt = st.st_mtime;
const char* e = getenv("EDITOR");
e = getenv("EDITOR");
if (e == NULL)
e = INCRON_DEFAULT_EDITOR;
pid_t pid = fork();
if (pid == 0) {
if (setuid(uid) != 0) {
fprintf(stderr, "cannot set user %s: %s\n", user, strerror(errno));
return false;
}
execlp(e, e, s, NULL);
_exit(1);
}
else if (pid > 0) {
int status;
if (wait(&status) != pid) {
perror("error while waiting for editor");
unlink_suid(s, uid);
return false;
{
pid_t pid = fork();
if (pid == 0) {
if (setuid(uid) != 0 || setgid(gid) != 0) {
fprintf(stderr, "cannot set user %s: %s\n", user, strerror(errno));
goto end;
}
execlp(e, e, s, NULL);
_exit(1);
}
if (!(WIFEXITED(status)) || WEXITSTATUS(status) != 0) {
perror("editor finished with error");
unlink_suid(s, uid);
return false;
else if (pid > 0) {
int status;
if (wait(&status) != pid) {
perror("error while waiting for editor");
goto end;
}
if (!(WIFEXITED(status)) || WEXITSTATUS(status) != 0) {
perror("editor finished with error");
goto end;
}
}
else {
perror("cannot start editor");
goto end;
}
}
else {
perror("cannot start editor");
unlink_suid(s, uid);
return false;
}
if (stat(s, &st) != 0) {
fprintf(stderr, "cannot stat temporary file: %s\n", strerror(errno));
unlink_suid(s, uid);
return false;
goto end;
}
if (st.st_mtime == mt) {
fprintf(stderr, "table unchanged\n");
unlink_suid(s, uid);
return true;
ok = true;
goto end;
}
InCronTab ict;
if (!ict.Load(s) || !ict.Save(tp)) {
fprintf(stderr, "cannot move temporary table: %s\n", strerror(errno));
unlink(s);
return false;
{
InCronTab ict;
if (!ict.Load(s) || !ict.Save(tp)) {
fprintf(stderr, "cannot move temporary table: %s\n", strerror(errno));
goto end;
}
}
ok = true;
fprintf(stderr, "table updated\n");
unlink_suid(s, uid);
return true;
end:
unlink(s);
return ok;
}
 
 
/incron/trunk/CHANGELOG
1,3 → 1,10
0.3.3 2006-12-15
* based on inotify-cxx 0.5.3
* "GID ignorance" bug fixed (#0000109)
* "zombie bug" fixed (#0000105)
* code cleanup and simplifying
 
 
0.3.2 2006-12-02
* problems with reiserfs fixed (#0000104)
 
/incron/trunk/incrond.8
1,4 → 1,4
.TH "incrond" "8" "0.3.1" "Lukas Jelinek" "incron documentation"
.TH "incrond" "8" "0.3.3" "Lukas Jelinek" "incron documentation"
.SH "NAME"
incrond \- inotify cron (incron) daemon
 
/incron/trunk/icd-main.cpp
16,6 → 16,7
#include <map>
#include <signal.h>
#include <wait.h>
#include <fcntl.h>
#include <pwd.h>
#include <dirent.h>
#include <syslog.h>
48,17 → 49,39
/// Finish program yes/no
volatile bool g_fFinish = false;
 
/// Pipe for notifying about dead children
int g_cldPipe[2];
 
// Buffer for emptying child pipe
#define CHILD_PIPE_BUF_LEN 32
char g_cldPipeBuf[CHILD_PIPE_BUF_LEN];
 
 
/// Handles a signal.
/**
* For SIGTERM and SIGINT it sets the program finish variable.
* For SIGCHLD it writes a character into the notification pipe
* (this is a workaround made due to disability to reliably
* wait for dead children).
*
* \param[in] signo signal number
*/
void on_signal(int signo)
{
if (signo == SIGTERM || signo == SIGINT)
g_fFinish = true;
switch (signo) {
case SIGTERM:
case SIGINT:
g_fFinish = true;
break;
case SIGCHLD:
// first empty pipe (to prevent internal buffer overflow)
do {} while (read(g_cldPipe[0], g_cldPipeBuf, CHILD_PIPE_BUF_LEN) > 0);
// now write one character
write(g_cldPipe[1], "X", 1);
break;
default:;
}
}
 
/// Checks whether an user exists and has permission to use incron.
125,6 → 148,31
closedir(d);
}
 
/// Prepares a 'dead/done child' notification pipe.
/**
* This function returns no value at all and on error it
* throws an exception.
*/
void prepare_pipe()
{
g_cldPipe[0] = -1;
g_cldPipe[1] = -1;
if (pipe(g_cldPipe) != 0)
throw InotifyException("cannot create notification pipe", errno, NULL);
for (int i=0; i<2; i++) {
int res = fcntl(g_cldPipe[i], F_GETFL);
if (res == -1)
throw InotifyException("cannot get pipe flags", errno, NULL);
res |= O_NONBLOCK;
if (fcntl(g_cldPipe[i], F_SETFL, res) == -1)
throw InotifyException("cannot set pipe flags", errno, NULL);
}
}
 
/// Main application function.
/**
* \param[in] argc argument count
155,13 → 203,15
return 1;
}
if (DAEMON)
daemon(0, 0);
prepare_pipe();
signal(SIGTERM, on_signal);
signal(SIGINT, on_signal);
signal(SIGCHLD, on_signal);
if (DAEMON)
daemon(0, 0);
uint32_t wm = IN_CLOSE_WRITE | IN_DELETE | IN_MOVE | IN_DELETE_SELF | IN_UNMOUNT;
InotifyWatch watch(INCRON_TABLE_BASE, wm);
in.Add(watch);
170,16 → 220,27
InotifyEvent e;
struct pollfd pfd;
pfd.fd = in.GetDescriptor();
pfd.events = (short) POLLIN;
pfd.revents = (short) 0;
struct pollfd pfd[2];
pfd[0].fd = in.GetDescriptor();
pfd[0].events = (short) POLLIN;
pfd[0].revents = (short) 0;
pfd[1].fd = g_cldPipe[0];
pfd[1].events = (short) POLLIN;
pfd[1].revents = (short) 0;
while (!g_fFinish) {
int res = poll(&pfd, 1, -1);
int res = poll(pfd, 2, -1);
if (res > 0) {
in.WaitForEvents(true);
if (pfd[1].revents & ((short) POLLIN)) {
char c;
while (read(pfd[1].fd, &c, 1) > 0) {}
UserTable::FinishDone();
}
else {
in.WaitForEvents(true);
}
}
else if (res < 0) {
if (errno != EINTR)
186,8 → 247,6
throw InotifyException("polling failed", errno, NULL);
}
UserTable::FinishDone();
while (in.GetEvent(e)) {
if (e.GetWatch() == &watch) {
223,8 → 282,14
else {
ed.DispatchEvent(e);
}
}
}
if (g_cldPipe[0] != -1)
close(g_cldPipe[0]);
if (g_cldPipe[1] != -1)
close(g_cldPipe[1]);
} catch (InotifyException e) {
int err = e.GetErrorNumber();
syslog(LOG_CRIT, "*** unhandled exception occurred ***");
/incron/trunk/inotify-cxx.cpp
397,12 → 397,12
len = read(m_fd, m_buf, INOTIFY_BUFLEN);
} while (fNoIntr && len == -1 && errno == EINTR);
if (errno == EWOULDBLOCK)
if (len == -1 && !(errno == EWOULDBLOCK || errno == EINTR))
throw InotifyException(IN_EXC_MSG("reading events failed"), errno, this);
if (len == -1)
return;
if (len < 0)
throw InotifyException(IN_EXC_MSG("reading events failed"), errno, this);
IN_WRITE_BEGIN
ssize_t i = 0;
/incron/trunk/TODO
1,4 → 1,3
Currently pending tasks:
 
#0000101 - Temporary files not removed
#0000105 - Zombie child processes
*** nothing to do ***
/incron/trunk/usertable.cpp
211,6 → 211,7
struct passwd* pwd = getpwnam(m_user.c_str());
if ( pwd == NULL // user not found
|| setuid(pwd->pw_uid) != 0 // setting UID failed
|| setgid(pwd->pw_gid) != 0 // setting GID failed
|| execvp(argv[0], argv) != 0) // exec failed
{
_exit(1);
/incron/trunk/Makefile
66,7 → 66,10
rm -f $(MANPATH)/man5/incrontab.5
rm -f $(MANPATH)/man8/incrontab.8
 
update: uninstall install
 
release:
doxygen
mkdir -p $(RELEASEDIR)
cp -r doc $(RELEASEDIR)
cp *.h $(RELEASEDIR)
94,7 → 97,7
rm -f $(RELEASE).zip
rm -f sha1.txt
 
.PHONY: all clean distclean install install-man uninstall uninstall-man release release-clean
.PHONY: all clean distclean install install-man uninstall uninstall-man release release-clean update
 
.POSIX:
 
/incron/trunk/incrontab.1
1,4 → 1,4
.TH "incrontab" "1" "0.3.1" "Lukas Jelinek" "incron documentation"
.TH "incrontab" "1" "0.3.3" "Lukas Jelinek" "incron documentation"
.SH "NAME"
incrontab \- table manipulator for inotify cron (incron)
.SH "SYNOPSIS"
/incron/trunk/incron.h
27,7 → 27,7
#define INCRON_TAB_NAME "incrontab"
 
/// Application version (release)
#define INCRON_VERSION "0.3.2"
#define INCRON_VERSION "0.3.3"
 
/// Address for sending bugs
#define INCRON_BUG_ADDRESS "<bugs@aiken.cz>"