ftrigger.c (5900B)
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 14 struct 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; 21 int n; 22 23 void sighandler(int signum) { 24 int status; 25 (void)signum; 26 wait(&status); 27 } 28 29 int v; 30 31 int 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 (;;) { 92 again: 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 208 goodevent: 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 }