/* Program : watchd Created : 20.08.2001 Modified : $Date: 2005/05/10 04:08:15 $ Author : Peter Turczak Syntax : watchd 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; either version 2 of the License, 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; if not, write to the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ #define __USE_BSD #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "inifile.h" #include "chain.h" #include "filedb.h" #define FL_ACT_CHANGE 1 #define FL_ACT_EXISTENCE 2 #define FL_ACT_NOMOVE 4 #define FL_ACT_COPY 8 #define FL_ACT_DELETE 16 #define FL_LOG_SYSLOG 64 #define FL_LOG_FILE 128 #define FL_LOG_MAIL 256 #define FL_LOG_ALWAYS 512 typedef struct Twatchfolder { char* runprg; char* dir; element *filechain; uid_t user; int flags; char *notify; char *logfile; int gracetime; // Remaining time 'till scanchain int interval, curcount; // Interval will stay constant, // curcount-- until exec then curcount=interval } Twatchfolder; Twatchfolder defaultfolder; Twatchfolder *curfolder=&defaultfolder; element *folders; //Chain of folders int interval=1; // Global rescan interval in seconds int timeofgrace;// Time in intervals between calls to scanchain char *tmpdir; // Directory where the processing dirs are created. Default=/tmp char *mvprg; // Program that is used to move the files around. Default=mv char *cpprg; // Program that is used to move the files around. Default=cp char *rmprg; // Program that is used to remove the files. Default=rm char *config_file = NULL; static char default_config_filename[] = "/usr/local/etc/watchd.conf"; #ifdef HAS_NO_MKSTEMP int mkstemp(char *template) { int r; char *dir=strdup(template); char *prefix=NULL; if (strrchr(dir, '/')!=0) { file=strrchr(dir, '/'); file=0x00; file++; if (strstr(file, "XXXXXX")) { (strstr(file, "XXXXXX"))[0]=0x00; } } r=open(tempnam(dir, prefix), O_CREAT | O_RDWR | S_IRWXU); return(r); } #endif int get_filestate(char *fn) { int fd=open(fn,O_RDWR); /* l_type l_whence l_start l_len l_pid */ struct flock fl = { F_WRLCK, SEEK_SET, 0, 0, 0 }; if (fd>0) { fl.l_pid = getpid(); fl.l_type = F_WRLCK; if (fcntl(fd, F_SETLK, &fl) == -1) { close(fd); #ifdef DEBUG printf("get_filestate(%s)=%d\n",fn,0); #endif return(0); } else { close(fd); #ifdef DEBUG printf("get_filestate(%s)=%d\n",fn,1); #endif fl.l_type = F_UNLCK; /* set to unlock same region */ if (fcntl(fd, F_SETLK, &fl) == -1) { close(fd); #ifdef DEBUG printf("Strange unlock behavior..\n"); #endif } return(1); } } else // open() failed... { if (isdir(fn)) { #ifdef DEBUG printf("Directory %s not processed...\n",fn); #endif return(2); } else return(1); } } int splits(char *s, char* p1, char* p2) { char *equal=strchr(s,'='); char *p2tmp; int pos1 = (((equal==NULL)||(equalcur; curfolder->filechain=newchain(); f=curfolder; } if (prg!=NULL) f->runprg=strdup(prg); if (dir!=NULL) f->dir=strdup(dir); if (uid!=-1) f->user=uid; if (flags!=0) f->flags=flags; if (interval!=-1) { f->interval=interval; f->curcount=interval; } } int flags2int(char *c) { int r=0; char *i=(char *)malloc(strlen(c)+3); char *s=i; char *p; strcpy(s, c); strcat(s, " "); while ((p=strstr(s," "))!=NULL) { p[0]=0x00; if (strncasecmp("CHANGE",s, 6)==0) r|=FL_ACT_CHANGE; if (strncasecmp("EXIST",s, 5)==0) r|=FL_ACT_EXISTENCE; if (strncasecmp("COPY",s, 4)==0) r|=FL_ACT_COPY; if (strncasecmp("DELETE",s, 6)==0) r|=FL_ACT_DELETE; if (strncasecmp("SYSLOG",s, 6)==0) r|=FL_LOG_SYSLOG; if (strncasecmp("FILELOG",s, 7)==0) r|=FL_LOG_FILE; if (strncasecmp("MAILLOG",s, 7)==0) { r|=FL_LOG_MAIL; printf("While parsing logfile: Warning: MAILLOG is not yet implemented!\n"); } if (strncasecmp("ALWAYSLOG",s, 9)==0) r|=FL_LOG_ALWAYS; #ifdef DEBUG printf("flags2int('%s') : parsed '%s', r=%d\n", c, s, r); #endif s=++p; } free(i); return(r); } void lowercase(char *c) { int i; for (i=0;i0) { addfolder(curfolder, NULL, NULL, -1, tmpi, 0); } // set interval if the value is not completely // brain-damaged! #ifdef DEBUG printf("set to %d!\n",interval); #endif } if (strncmp(tmpc1,"tempdir",7)==0) { #ifdef DEBUG printf("'tempdir' line found. Trying to set tempdir..."); #endif strcpy(tmpdir,tmpc2); if (tmpdir[strlen(tmpdir)-1]!='/') {strcat(tmpdir,"/");} #ifdef DEBUG printf("set to %s!\n",tmpdir); #endif } if (strncmp(tmpc1,"mv",2)==0) { #ifdef DEBUG printf("'mv' line found. Trying to set mvprg..."); #endif strcpy(mvprg,tmpc2); #ifdef DEBUG printf("set to %s!\n",mvprg); #endif } if (strncmp(tmpc1,"rm",2)==0) { #ifdef DEBUG printf("'rm' line found. Trying to set rmprg..."); #endif strcpy(rmprg,tmpc2); #ifdef DEBUG printf("set to %s!\n",rmprg); #endif } if (strncmp(tmpc1,"cp",2)==0) { #ifdef DEBUG printf("'cp' line found. Trying to set cpprg..."); #endif strcpy(cpprg,tmpc2); #ifdef DEBUG printf("set to %s!\n",cpprg); #endif } if (strncmp(tmpc1,"mail",4)==0) { #ifdef DEBUG printf("'mail' line found. Trying to set notify..."); #endif curfolder->notify=strdup(tmpc2); #ifdef DEBUG printf("set to %s!\n",cpprg); #endif } if (strncmp(tmpc1,"logfile",7)==0) { #ifdef DEBUG printf("'logfile' line found. Trying to set logfile..."); #endif curfolder->logfile=strdup(tmpc2); #ifdef DEBUG printf("set to %s!\n",curfolder->logfile); #endif } free(tmpc1); free(tmpc2); } void defraglst() { // FOO BAR } void parsecommon(inifile *i) { char *s=NULL; while ((s=ini_nextline(i))!=NULL) { if (strchr(s, '=')!=NULL) parseequal(s); free(s); } } void dumpfolders() { element *c=head(folders); while (c->next!=NULL) { c=c->next; curfolder=(Twatchfolder *)c->cur; if (c->cur!=NULL) printf("Will watch dir '%s' for files and run '%s' on them with uid='%d' flags: %d\n",curfolder->dir, curfolder->runprg, curfolder->user, curfolder->flags); } } void consolidate() { element *c=head(folders); while (c->next!=NULL) { c=c->next; curfolder=(Twatchfolder *)c->cur; if (((Twatchfolder *)c->cur)->dir==NULL) { folders=unchain(c); c=head(folders); } } } void init() { inifile *f; char *buf; int i; tmpdir=malloc(1024); strcpy(tmpdir,"/tmp/"); mvprg=malloc(1024); strcpy(mvprg,"/bin/mv"); rmprg=malloc(1024); strcpy(rmprg,"/bin/rm"); cpprg=malloc(1024); strcpy(cpprg,"/bin/cp"); timeofgrace=30; defaultfolder.flags=(FL_ACT_EXISTENCE | FL_ACT_COPY | FL_ACT_DELETE); defaultfolder.gracetime=timeofgrace; defaultfolder.logfile=strdup("/dev/stdout"); folders=newchain(); buf=malloc(1024); f=ini_open(config_file); if (!f) { printf("Error: Could not open /usr/local/etc/watchd.conf (%s) , exiting..\n", strerror(errno)); free (defaultfolder.logfile); free (buf); free (cpprg); free (rmprg); free (mvprg); free (tmpdir); clearlst (folders); exit(1); } #ifdef DEBUG printf("Stage 1 : Read common options..\n"); #endif i=ini_goto_grp(f, "common"); if (i!=-1) { parsecommon(f); } #ifdef DEBUG else printf("Waring: could't read common options\n"); #endif #ifdef DEBUG printf("Stage 2 : Read folders..\n"); #endif ini_rewind(f); while ((ini_nextgrp(f)!=-1)) { addfolder(NULL, NULL, NULL, -1, -1, 0); if (strcasecmp(f->sts.cur_grp,"common")!=0) while ((buf=ini_nextline(f))!=NULL) { if (strstr(buf,"=")!=NULL) parseequal(buf); free(buf); } } ini_close(f); consolidate(); #ifdef DEBUG dumpfolders(); #endif } void deinit() { element *e=head(folders); Twatchfolder *w; #ifdef DEBUG printf("deinit"); #endif while (e!=NULL) { #ifdef DEBUG printf("."); #endif w=(Twatchfolder *)e->cur; if (w!=NULL) { free(w->runprg); free(w->dir); killfilechain(w->filechain); } e=unchain(e); } #ifdef DEBUG printf("deinited\n"); #endif } int run(char* cmd, char* arg1, char* arg2, char* arg3, char* arg4, uid_t uid, int logfd) { char **args; int f; int r; #ifdef DEBUG printf("run(cmd=%s,arg1=%s,arg2=%s,arg3=%s,arg4=%s)\n",cmd, arg1, arg2, arg3, arg4); #endif f=fork(); if (f==0) { if (logfd>0) { dup2(logfd, STDOUT_FILENO); dup2(STDOUT_FILENO , STDERR_FILENO); close(logfd); } args=malloc(sizeof(args)*5); // allocate ram for 4 arguments... args[0]=strdup(cmd); args[1]=arg1!=NULL?strdup(arg1):NULL; args[2]=arg2!=NULL?strdup(arg2):NULL; args[3]=arg3!=NULL?strdup(arg3):NULL; args[4]=arg4!=NULL?strdup(arg4):NULL; deinit(); cmd=args[0]; #ifdef DEBUG fprintf(stderr,"forked of, uid=%d...\n", uid); #endif if (uid!=-1) { #ifdef DEBUG fprintf(stderr, "Doing setuid(%d)...",uid); #endif if (setuid(uid)!=0) { #ifdef DEBUG printf("failed!\n"); #endif syslog(LOG_WARNING||LOG_DAEMON,"Could not setuid(%d): %s", uid, strerror(errno)); } #ifdef DEBUG printf("ok..\n"); #endif } #ifdef DEBUG printf("Executing %s...\n", cmd); #endif execvp(cmd,args); printf("Something went wrong! Could not execute %s... (%s) ",cmd, strerror(errno)); syslog(LOG_WARNING||LOG_DAEMON,"Could not execute %s",cmd); exit(0); // Should never occur, but we are paranoid... } else { #ifdef DEBUG printf("pid of child=%d\n",f); #endif waitpid(f,&r,0); // wait for process to finish.. #ifdef DEBUG printf("child died: pid=%d return=%d\n", f, r); #endif } return(r); } char *prepare_logfd(Twatchfolder *folder, int *fd) { int r=-1; char *name=strdup("/tmp/watchdrun.XXXXXX"); if ((folder->flags & (FL_LOG_SYSLOG|FL_LOG_FILE|FL_LOG_MAIL))!=0) { r=mkstemp(name); } *fd=r; return(name); } void finalize_logfd(int fd, int r, Twatchfolder *folder, char *logname) { int log,l; char buf[512]; FILE *f; #ifdef DEBUG printf("Entering finalize_logfd(fd=%i, r=%d, folder, logname='%s');\n", fd, r, logname); #endif if (fd>0) { if ((folder->flags&FL_LOG_ALWAYS) | (r!=0)) { if (folder->flags&FL_LOG_FILE) { log=open(curfolder->logfile, O_CREAT | O_WRONLY | O_APPEND ); if (log>0) { lseek(fd, SEEK_SET, 0); // Rewind temp log file.. snprintf(buf, 511, "Program %s returned %d, stdout/err follows:\n", folder->runprg, r); write(log, buf, strlen(buf)); l=222222; while (l>0) { l=read(fd, buf, 512); write(log, buf, l); } if (close(fd)) // Close the tempfile { #ifdef DEBUG printf("Error closing fd=%d!\n",fd); #endif } fd=0; close(log); } } if (folder->flags&FL_LOG_SYSLOG) { lseek(fd, SEEK_SET, 0); // Rewind temp log file.. snprintf(buf, 511, "Program %s returned %d, stdout/err follows:\n", folder->runprg, r); openlog(folder->runprg, LOG_PID, LOG_DAEMON); f=fdopen(fd, "r"); while (!feof(f)) { if (fgets(buf, 511, f)!=NULL) syslog((r==0)?LOG_NOTICE:LOG_WARNING, "%s\n", buf); } closelog(); fclose(f); if (close(fd)) // Close the tempfile { #ifdef DEBUG printf("Error closing fd=%d!\n",fd); #endif } fd=0; } } if (fd!=0) close(fd); unlink(logname); // Kill it! } } //void processfile(char *file, char *srcdir, char *cmd, uid_t uid) int processfile(char *file, Twatchfolder *folder) { char *complete; char *srcfile; char *logname; struct timeval tp; Twatchfolder w; int logfd,r; memcpy(&w,folder,sizeof(w)); #ifdef DEBUG printf("processfile called! (folder=%s, flags=%d\n)\n", w.dir, w.flags); #endif complete=malloc(1024); srcfile=malloc(1024); strcpy(srcfile,w.dir); strcat(srcfile,"/"); strcat(srcfile,file); #ifdef DEBUG printf("calling checkfile...\n"); #endif if ((w.flags & FL_ACT_CHANGE)!=0) if (checkfile(srcfile, 0, w.filechain)!=2) { // Happens in case a folder should be watched for changes // an there is no demand of processing free(complete); free(srcfile); return(0); } if ((r=get_filestate(srcfile))==1) { syslog(LOG_NOTICE||LOG_DAEMON,"processing %s with %s",file,w.runprg); if ((w.flags & FL_ACT_COPY)!=0) { gettimeofday(&tp,NULL); sprintf(complete, "%s/watchdtmp.%d%d", tmpdir, tp.tv_sec, tp.tv_usec); mkdir(complete,0700); #ifdef DEBUG printf("created tempdir : %s\n",complete); #endif chown(complete, w.user, 0); #ifdef DEBUG printf("doing chown(%d, %d)", w.user, 0); #endif #ifdef DEBUG printf("running cp...\n"); #endif run(cpprg,srcfile,complete, NULL, NULL, w.user, -1); if ((w.flags & FL_ACT_DELETE)!=0) { #ifdef DEBUG printf("running rm...\n"); #endif run(rmprg, srcfile, NULL, NULL, NULL, w.user, -1); } strcpy(srcfile,complete); strcat(srcfile,"/"); strcat(srcfile,file); } logname=prepare_logfd(folder, &logfd); r=run(w.runprg, srcfile, NULL, NULL, NULL, w.user, logfd); finalize_logfd(logfd, r, folder, logname); if ((w.flags & FL_ACT_COPY) == FL_ACT_COPY) run(rmprg,"-rf",complete, NULL, NULL, w.user, -1); if ((w.flags & FL_ACT_DELETE)!=0) run(rmprg,"-rf",srcfile, NULL, NULL, w.user, -1); } else { if (r!=2) syslog(LOG_NOTICE||LOG_DAEMON,"file %s is locked, not processed",file,w.runprg); } free(complete); free(srcfile); //printf("flushing...\n"); fflush(stdout); fflush(stdin); fflush(stderr); return 0; } int isdir(char *name) { struct stat st; #ifdef DEBUG printf("isdir(%s)=", name); #endif stat(name, &st); if (S_ISDIR(st.st_mode)) { #ifdef DEBUG printf("1\n"); #endif return(1); } else { #ifdef DEBUG printf("0\n"); #endif return(0); } } void processdir(Twatchfolder *f) { DIR* dir=NULL; struct dirent *de; char* tmpS; // int child; #ifdef DEBUG printf("Processing dir '%s' prg='%s' uid='%d' interval='%d' curcount='%d'...\n", f->dir, f->runprg, f->user, f->interval, f->curcount); #endif if (f->dir!=NULL) dir=opendir(f->dir); else dir=NULL; tmpS=malloc(1024); if (dir!=NULL) { de=readdir(dir); while (de!=NULL) { if ((strcmp(de->d_name,".")!=0) && (strcmp(de->d_name,"..")!=0)) { // we don't process dirs, yet... #ifdef DEBUG printf("Processing '%s' (de->d_name).\n ", de->d_name); #endif processfile(de->d_name,f); // if (isdir(de->d_name)==0) { #ifdef DEBUG printf("Entering processdir()..."); #endif // processfile(de->d_name,f->dir,f->runprg, f->user); /* } else { syslog(LOG_WARNING||LOG_DAEMON,"sorry, cannot read dirs, currently ",folders[n].dir); printf("Found a dir!\n"); //processdir(de->d_name); } */ } de=readdir(dir); } closedir(dir); } else { printf("Warning: Could not read directory '%s'\n",f->dir); syslog(LOG_WARNING||LOG_DAEMON,"could not read directory %s",f->dir); } free(tmpS); } void sighandler(int signal) { if (signal==SIGHUP) { init(); // Reread config file! } } int process_args (int argc, char *argv[]) { if (argc < 2) { printf ("Usage: %s [config file]\n", argv[0]); config_file = default_config_filename; printf ("Using config file: %s\n", config_file); // exit (1); } else { config_file = argv[1]; printf ("Using config file: %s\n", config_file); } return 0; } int main(int argc, char *argv[]) { // int i; element *c; process_args (argc, argv); // signal(SIGHUP, sighandler); init(); #ifndef DEBUG if (fork()==0) { #endif while (1) { c=head(folders); while (c->next!=NULL) { c=c->next; if (c->cur!=NULL) { if (((Twatchfolder *)c->cur)->gracetime--<1) { #ifdef DEBUG printf("scanchain()\n"); #endif ((Twatchfolder *)c->cur)->filechain=scanchain(((Twatchfolder *)c->cur)->filechain); ((Twatchfolder *)c->cur)->gracetime=timeofgrace; } if (((Twatchfolder *)c->cur)->curcount--<1) { processdir((Twatchfolder *)c->cur); ((Twatchfolder *)c->cur)->curcount=((Twatchfolder *)c->cur)->interval-1; } } } sleep(interval); } #ifndef DEBUG } return(0); #endif }