1 /*
2 * generate a list of files and their metadata
3 * using a given proto file.
4 */
5 #include "all.h"
6
7 int changesonly;
8 char *uid;
9 Db *db;
10 Biobuf blog;
11 ulong now;
12 int n;
13 char **x;
14 int nx;
15 int justlog;
16 char *root=".";
17 char **match;
18 int nmatch;
19
20 int
ismatch(char * s)21 ismatch(char *s)
22 {
23 int i, len;
24
25 if(nmatch == 0)
26 return 1;
27 for(i=0; i<nmatch; i++){
28 if(strcmp(s, match[i]) == 0)
29 return 1;
30 len = strlen(match[i]);
31 if(strncmp(s, match[i], len) == 0 && s[len]=='/')
32 return 1;
33 }
34 return 0;
35 }
36
37 void
xlog(int c,char * name,Dir * d)38 xlog(int c, char *name, Dir *d)
39 {
40 char *dname;
41
42 dname = d->name;
43 if(strcmp(dname, name) == 0)
44 dname = "-";
45 if(!justlog)
46 Bprint(&blog, "%lud %d ", now, n++);
47 Bprint(&blog, "%c %q %q %luo %q %q %lud %lld\n",
48 c, name, dname, d->mode, uid ? uid : d->uid, d->gid, d->mtime, d->length);
49 }
50
51 void
walk(char * new,char * old,Dir * xd,void *)52 walk(char *new, char *old, Dir *xd, void*)
53 {
54 int i, change, len;
55 Dir od, d;
56
57 new = unroot(new, "/");
58 old = unroot(old, root);
59
60 if(!ismatch(new))
61 return;
62 for(i=0; i<nx; i++){
63 if(strcmp(new, x[i]) == 0)
64 return;
65 len = strlen(x[i]);
66 if(strncmp(new, x[i], len)==0 && new[len]=='/')
67 return;
68 }
69
70 d = *xd;
71 d.name = old;
72 memset(&od, 0, sizeof od);
73 change = 0;
74 if(markdb(db, new, &od) < 0){
75 if(!changesonly){
76 xlog('a', new, &d);
77 change = 1;
78 }
79 }else{
80 if((d.mode&DMDIR)==0 && (od.mtime!=d.mtime || od.length!=d.length)){
81 xlog('c', new, &d);
82 change = 1;
83 }
84 if((!uid&&strcmp(od.uid,d.uid)!=0)
85 || strcmp(od.gid,d.gid)!=0
86 || od.mode!=d.mode){
87 xlog('m', new, &d);
88 change = 1;
89 }
90 }
91 if(!justlog && change){
92 if(uid)
93 d.uid = uid;
94 d.muid = "mark"; /* mark bit */
95 insertdb(db, new, &d);
96 }
97 }
98
99 void
warn(char * msg,void *)100 warn(char *msg, void*)
101 {
102 char *p;
103
104 fprint(2, "warning: %s\n", msg);
105
106 /* find the %r in "can't open foo: %r" */
107 p = strstr(msg, ": ");
108 if(p)
109 p += 2;
110
111 /*
112 * if the error is about a remote server failing,
113 * then there's no point in continuing to look
114 * for changes -- we'll think everything got deleted!
115 *
116 * actual errors i see are:
117 * "i/o on hungup channel" for a local hangup
118 * "i/o on hungup channel" for a timeout (yank the network wire)
119 * "'/n/sources/plan9' Hangup" for a remote hangup
120 * the rest is paranoia.
121 */
122 if(p){
123 if(cistrstr(p, "hungup") || cistrstr(p, "Hangup")
124 || cistrstr(p, "rpc error")
125 || cistrstr(p, "shut down")
126 || cistrstr(p, "i/o")
127 || cistrstr(p, "connection"))
128 sysfatal("suspected network or i/o error - bailing out");
129 }
130 }
131
132 void
usage(void)133 usage(void)
134 {
135 fprint(2, "usage: replica/updatedb [-c] [-p proto] [-r root] [-t now n] [-u uid] [-x path]... db [paths]\n");
136 exits("usage");
137 }
138
139 void
main(int argc,char ** argv)140 main(int argc, char **argv)
141 {
142 char *proto;
143 Avlwalk *w;
144 Dir d;
145 Entry *e;
146
147 quotefmtinstall();
148 proto = "/sys/lib/sysconfig/proto/allproto";
149 now = time(0);
150 Binit(&blog, 1, OWRITE);
151 ARGBEGIN{
152 case 'c':
153 changesonly = 1;
154 break;
155 case 'l':
156 justlog = 1;
157 break;
158 case 'p':
159 proto = EARGF(usage());
160 break;
161 case 'r':
162 root = EARGF(usage());
163 break;
164 case 't':
165 now = strtoul(EARGF(usage()), 0, 0);
166 n = atoi(EARGF(usage()));
167 break;
168 case 'u':
169 uid = EARGF(usage());
170 break;
171 case 'x':
172 if(nx%16 == 0)
173 x = erealloc(x, (nx+16)*sizeof(x[0]));
174 x[nx++] = EARGF(usage());
175 break;
176 default:
177 usage();
178 }ARGEND
179
180 if(argc <1)
181 usage();
182
183 match = argv+1;
184 nmatch = argc-1;
185
186 db = opendb(argv[0]);
187 if(rdproto(proto, root, walk, warn, nil) < 0)
188 sysfatal("rdproto: %r");
189
190 if(!changesonly){
191 w = avlwalk(db->avl);
192 while(e = (Entry*)avlprev(w)){
193 if(!ismatch(e->name))
194 continue;
195 if(!e->d.mark){ /* not visited during walk */
196 memset(&d, 0, sizeof d);
197 d.name = e->d.name;
198 d.uid = e->d.uid;
199 d.gid = e->d.gid;
200 d.mtime = e->d.mtime;
201 d.mode = e->d.mode;
202 xlog('d', e->name, &d);
203 if(!justlog)
204 removedb(db, e->name);
205 }
206 }
207 }
208
209 if(Bterm(&blog) < 0)
210 sysfatal("writing output: %r");
211
212 exits(nil);
213 }
214
215