unix/minit

ftrigger.c in master
Repositories | Summary | Log | Files | README | COPYING

ftrigger.c (5900B) download


  1#include <sys/types.h>
  2#include <unistd.h>
  3#include <sys/stat.h>
  4#include <fcntl.h>
  5#include <sys/inotify.h>
  6#include <stdlib.h>
  7#include <string.h>
  8#include <buffer.h>
  9#include <errno.h>
 10#include <sys/poll.h>
 11#include <sys/wait.h>
 12#include <sys/signal.h>
 13
 14struct trigger {
 15  const char* filename,* command;
 16  char* dironly,* fileonly;
 17  struct stat ss;
 18  int idd,idf,created;	/* inotify-deskriptor */
 19  /* created = 1: got a create event on the dir, added a watch on the file, but no modify events on the file yet */
 20}* root;
 21int n;
 22
 23void sighandler(int signum) {
 24  int status;
 25  (void)signum;
 26  wait(&status);
 27}
 28
 29int v;
 30
 31int main(int argc,char* argv[]) {
 32  int i,in;
 33  const char* command="make";
 34  struct stat ss;
 35  char buf[2048];
 36  struct inotify_event* ie=(struct inotify_event*)buf;
 37  struct pollfd p;
 38  static struct sigaction sa;
 39
 40  sa.sa_flags=SA_RESTART|SA_NOCLDSTOP;
 41  sa.sa_handler=SIG_IGN;
 42  sigaction(SIGCHLD,&sa,0);
 43  in=inotify_init();
 44  root=(struct trigger*)alloca(sizeof(struct trigger)*argc);
 45  memset(root,0,sizeof(struct trigger)*argc);
 46  for (i=1; i<argc; ++i) {
 47    if (!strcmp(argv[i],"-v")) {
 48      v=1;
 49      continue;
 50    }
 51    if (argv[i][0]=='@') {
 52      command=argv[i]+1;
 53      ++i;
 54    }
 55    if (argv[i]) {
 56      char* c;
 57      root[n].filename=argv[i];
 58      root[n].command=command;
 59      root[n].dironly=alloca(strlen(root[n].filename)+3);
 60      c=strrchr(root[n].filename,'/');
 61      if (c) {
 62	size_t m=c-root[n].filename;
 63	strcpy(root[n].dironly,root[n].filename);
 64	root[n].fileonly=root[n].dironly+m+1;
 65	root[n].fileonly[-1]=0;
 66      } else {
 67	root[n].dironly[0]='.';
 68	root[n].dironly[1]=0;
 69	root[n].fileonly=root[n].dironly+2;
 70	strcpy(root[n].fileonly,root[n].filename);
 71      }
 72      ++n;
 73    }
 74  }
 75
 76  for (i=0; i<n; ++i) {
 77    root[i].idf=-1;
 78    if (stat(root[i].filename,&root[i].ss)!=0)
 79      buffer_putmflush(buffer_2,"warning: could not stat file \"",root[i].filename,"\": ",strerror(errno),"\n");
 80    else
 81      root[i].idf=inotify_add_watch(in,root[i].filename,
 82				    IN_CLOSE_WRITE|IN_MOVE_SELF|IN_DELETE_SELF|IN_MODIFY);
 83    root[i].idd=inotify_add_watch(in,root[i].dironly,
 84				  IN_MOVED_TO|IN_CREATE);
 85    root[i].created=0;
 86  }
 87
 88  p.fd=in;
 89  p.events=POLLIN;
 90
 91  for (;;) {
 92again:
 93    switch (poll(&p,1,1000)) {
 94    case -1:
 95      if (errno==EINTR) {
 96	int status;
 97	wait(&status);
 98	break;
 99      }
100      return 1;
101    case 0:
102      {
103	int foundone=0;
104	for (i=0; i<n; ++i)
105	  if (root[i].created==1) {
106	    memset(&root[i].ss,0,sizeof(root[i].ss));
107	    root[i].created=0;
108	    foundone=1;
109	  }
110	if (foundone) goto goodevent;
111      }
112      continue;
113    case 1: break;
114    };
115    if (read(in,buf,sizeof(buf)) < (ssize_t)sizeof(*ie)) {
116      buffer_putmflush(buffer_2,"error reading inotify notification: ",strerror(errno),"\n");
117      exit(111);
118    }
119
120#if 0
121    buffer_puts(buffer_1,"got event for wd ");
122    buffer_putulong(buffer_1,ie->wd);
123    if (ie->len)
124      buffer_putmflush(buffer_1," with filename \"",ie->name,"\"\n");
125    else
126      buffer_putsflush(buffer_1," with no associated filename\n");
127    struct {
128      unsigned int mask;
129      const char* string;
130    } strings[] = {
131      { 0x00000001,"IN_ACCESS" },
132      { 0x00000002,"IN_MODIFY" },
133      { 0x00000004,"IN_ATTRIB" },
134      { 0x00000008,"IN_CLOSE_WRITE" },
135      { 0x00000010,"IN_CLOSE_NOWRITE" },
136      { 0x00000020,"IN_OPEN" },
137      { 0x00000040,"IN_MOVED_FROM" },
138      { 0x00000080,"IN_MOVED_TO" },
139      { 0x00000100,"IN_CREATE" },
140      { 0x00000200,"IN_DELETE" },
141      { 0x00000400,"IN_DELETE_SELF" },
142      { 0x00000800,"IN_MOVE_SELF" },
143      { 0x00002000,"IN_UNMOUNT" },
144      { 0x00004000,"IN_Q_OVERFLOW" },
145      { 0x00008000,"IN_IGNORED" },
146    };
147
148    for (i=0; i<(int)(sizeof(strings)/sizeof(strings[0])); ++i) {
149      if (ie->mask&strings[i].mask)
150	buffer_putm(buffer_1," ",strings[i].string);
151    }
152    buffer_putnlflush(buffer_1);
153
154#endif
155
156    for (i=0; i<n; ++i) {
157      if (root[i].idf==ie->wd) {
158	if (ie->mask & IN_MODIFY) {
159	  root[i].created=0;
160	  root[i].idf=inotify_add_watch(in,root[i].filename,
161				  IN_CLOSE_WRITE|IN_MOVE_SELF|IN_DELETE_SELF);
162	  continue;
163	}
164	if (ie->mask & (IN_DELETE_SELF|IN_MOVE_SELF)) {
165#if 0
166	  buffer_putmflush(buffer_1,root[i].filename," was ",
167			   ie->mask&IN_DELETE_SELF ? "deleted" : "renamed",
168			   ", cancelling subscription\n");
169#endif
170	  inotify_rm_watch(in,ie->wd);
171	  root[i].idf=-1;
172	  goto again;
173	}
174	goto goodevent;
175      } else if (root[i].idd==ie->wd) {
176	if (!strcmp(ie->name,root[i].fileonly)) {
177#if 0
178	  buffer_putmflush(buffer_1,"the file we were interested in, ",
179			    root[i].filename,", has been created. Adding subscription.\n");
180#endif
181	  if (root[i].idf!=-1)
182	    inotify_rm_watch(in,root[i].idf);
183	  root[i].idf=inotify_add_watch(in,root[i].filename,
184				  IN_CLOSE_WRITE|IN_MOVE_SELF|IN_DELETE_SELF|IN_MODIFY);
185	  root[i].created=1;
186	  /* if the file was created, it will be empty now, wait for
187	    * the IN_CLOSE_WRITE event. */
188	  if (ie->mask & IN_CREATE) {
189#if 0
190	    buffer_putmflush(buffer_1,"file was created, so it will be empty now. Skipping until we get a close event.\n");
191#endif
192	    goto again;
193	  }
194	  goto goodevent;
195	  /* if the file was moved, this is a genuine event we are
196	    * interested in. fall through */
197	} else goto again;
198#if 0
199	buffer_putmflush(buffer_1,"(directory event for \"",root[i].filename,"\")\n");
200#endif
201      }
202    }
203#if 0
204    buffer_putmflush(buffer_1,"ignoring irrelevant event.\n");
205#endif
206    goto again;
207
208goodevent:
209#if 0
210    buffer_putmflush(buffer_1,"got through to stat\n");
211#endif
212    for (i=0; i<n; ++i) {
213      if (stat(root[i].filename,&ss)==0 && memcmp(&ss,&root[i].ss,sizeof(ss))) {
214	memcpy(&root[i].ss,&ss,sizeof(ss));
215	if (v)
216	  buffer_putmflush(buffer_1,"file \"",root[i].filename,"\" changed, running command \"",root[i].command,"\"\n");
217	if (vfork()==0) {
218	  exit(system(root[i].command));
219	}
220      }
221    }
222  }
223  return 0;
224}