1implement Logs; 2 3include "sys.m"; 4 sys: Sys; 5 6include "draw.m"; 7 8include "bufio.m"; 9 bufio: Bufio; 10 Iobuf: import bufio; 11 12include "string.m"; 13 str: String; 14 15include "logs.m"; 16 17Hashsize: con 1024; 18Incr: con 500; 19 20init(bio: Bufio): string 21{ 22 sys = load Sys Sys->PATH; 23 bufio = bio; 24 str = load String String->PATH; 25 if(str == nil) 26 return sys->sprint("can't load %s: %r", String->PATH); 27 return nil; 28} 29 30Entry.read(in: ref Iobuf): (ref Entry, string) 31{ 32 if((s := in.gets('\n')) == nil) 33 return (nil, nil); 34 if(s[len s-1] == '\n') 35 s = s[0:len s-1]; 36 37 e := ref Entry; 38 e.x = -1; 39 40 l := str->unquoted(s); 41 fields := array[11] of string; 42 for(i := 0; l != nil; l = tl l) 43 fields[i++] = S(hd l); 44 45 # time gen verb path serverpath mode uid gid mtime length 46 # 1064889121 4 a sys/src/cmd/ip/httpd/webls.denied - 664 sys sys 1064887847 3 47 # time[0] gen[1] op[2] path[3] (serverpath|"-")[4] mode[5] uid[6] gid[7] mtime[8] length[9] 48 49 if(i < 10 || len fields[2] != 1) 50 return (nil, sys->sprint("bad log entry: %q", s)); 51 e.action = fields[2][0]; 52 case e.action { 53 'a' or 'c' or 'd' or 'm' => 54 ; 55 * => 56 return (nil, sys->sprint("bad log entry: %q", s)); 57 } 58 59 time := bigof(fields[0], 10); 60 sgen := bigof(fields[1], 10); 61 e.seq = (time << 32) | sgen; # for easier comparison 62 63 # time/gen check 64 # name check 65 66 if(fields[4] == "-") # undocumented 67 fields[4] = fields[3]; 68 e.path = fields[3]; 69 e.serverpath = fields[4]; 70 e.d = sys->nulldir; 71 { 72 e.d.mode = intof(fields[5], 8); 73 e.d.qid.qtype = e.d.mode>>24; 74 e.d.uid = fields[6]; 75 if(e.d.uid == "-") 76 e.d.uid = ""; 77 e.d.gid = fields[7]; 78 if(e.d.gid == "-") 79 e.d.gid = ""; 80 e.d.mtime = intof(fields[8], 10); 81 e.d.length = bigof(fields[9], 10); 82 }exception ex { 83 "log format:*" => 84 return (nil, sys->sprint("%s in log entry %q", ex, s)); 85 } 86 e.contents = fields[10] :: nil; # optional 87 return (e, nil); 88} 89 90rev[T](l: list of T): list of T 91{ 92 rl: list of T; 93 for(; l != nil; l = tl l) 94 rl = hd l :: rl; 95 return rl; 96} 97 98bigof(s: string, base: int): big 99{ 100 (b, r) := str->tobig(s, base); 101 if(r != nil) 102 raise "invalid integer field"; 103 return b; 104} 105 106intof(s: string, base: int): int 107{ 108 return int bigof(s, base); 109} 110 111mkpath(root: string, name: string): string 112{ 113 if(len root > 0 && root[len root-1] != '/' && (len name == 0 || name[0] != '/')) 114 return root+"/"+name; 115 return root+name; 116} 117 118contents(e: ref Entry): string 119{ 120 if(e.contents == nil) 121 return ""; 122 s := ""; 123 for(cl := e.contents; cl != nil; cl = tl cl) 124 s += " " + hd cl; 125 return s; # includes initial space 126} 127 128Entry.text(e: self ref Entry): string 129{ 130 a := e.action; 131 if(a == 0) 132 a = '?'; 133 return sys->sprint("%bd %bd %q [%d] %c m=%uo l=%bd t=%ud c=%q", e.seq>>32, e.seq & 16rFFFFFFFF, e.path, e.x, a, e.d.mode, e.d.length, e.d.mtime, contents(e)); 134} 135 136Entry.sumtext(e: self ref Entry): string 137{ 138 case e.action { 139 'a' or 'm' => 140 return sys->sprint("%c %q %uo %q %q %ud", e.action, e.path, e.d.mode, e.d.uid, e.d.gid, e.d.mtime); 141 'd' or 'c' => 142 return sys->sprint("%c %q", e.action, e.path); 143 * => 144 return sys->sprint("? %q", e.path); 145 } 146} 147 148Entry.dbtext(e: self ref Entry): string 149{ 150 # path dpath|"-" mode uid gid mtime length 151 return sys->sprint("%bd %bd %q - %uo %q %q %ud %bd%s", e.seq>>32, e.seq & 16rFFFFFFFF, e.path, e.d.mode, e.d.uid, e.d.gid, e.d.mtime, e.d.length, contents(e)); 152} 153 154Entry.logtext(e: self ref Entry): string 155{ 156 # gen n act path spath|"-" dpath|"-" mode uid gid mtime length 157 a := e.action; 158 if(a == 0) 159 a = '?'; 160 sf := e.serverpath; 161 if(sf == nil || sf == e.path) 162 sf = "-"; 163 return sys->sprint("%bd %bd %c %q %q %uo %q %q %ud %bd%s", e.seq>>32, e.seq & 16rFFFFFFFF, a, e.path, sf, e.d.mode, e.d.uid, e.d.gid, e.d.mtime, e.d.length, contents(e)); 164} 165 166Entry.remove(e: self ref Entry) 167{ 168 e.action = 'd'; 169} 170 171Entry.removed(e: self ref Entry): int 172{ 173 return e.action == 'd'; 174} 175 176Entry.update(e: self ref Entry, n: ref Entry) 177{ 178 if(n == nil) 179 return; 180 if(n.action == 'd') 181 e.contents = nil; 182 else 183 e.d = n.d; 184 if(n.action != 'm' || e.action == 'd') 185 e.action = n.action; 186 e.serverpath = S(n.serverpath); 187 for(nl := rev(n.contents); nl != nil; nl = tl nl) 188 e.contents = hd nl :: e.contents; 189 if(n.seq > e.seq) 190 e.seq = n.seq; 191} 192 193Db.new(name: string): ref Db 194{ 195 db := ref Db; 196 db.name = name; 197 db.stateht = array[Hashsize] of list of ref Entry; 198 db.nstate = 0; 199 db.state = array[50] of ref Entry; 200 return db; 201} 202 203Db.look(db: self ref Db, name: string): ref Entry 204{ 205 (b, nil) := hash(name, len db.stateht); 206 for(l := db.stateht[b]; l != nil; l = tl l) 207 if((hd l).path == name) 208 return hd l; 209 return nil; 210} 211 212Db.entry(db: self ref Db, seq: big, name: string, d: Sys->Dir): ref Entry 213{ 214 e := ref Entry; 215 e.action = 'a'; 216 e.seq = seq; 217 e.path = name; 218 e.d = d; 219 e.x = db.nstate++; 220 if(e.x >= len db.state){ 221 a := array[len db.state + Incr] of ref Entry; 222 a[0:] = db.state; 223 db.state = a; 224 } 225 db.state[e.x] = e; 226 (b, nil) := hash(name, len db.stateht); 227 db.stateht[b] = e :: db.stateht[b]; 228 return e; 229} 230 231Db.sort(db: self ref Db, key: int) 232{ 233 sortentries(db.state[0:db.nstate], key); 234} 235 236sortentries(a: array of ref Entry, key: int): (array of ref Entry, int) 237{ 238 mergesort(a, array[len a] of ref Entry, key); 239 return (a, len a); 240} 241 242mergesort(a, b: array of ref Entry, key: int) 243{ 244 r := len a; 245 if(r > 1) { 246 m := (r-1)/2 + 1; 247 mergesort(a[0:m], b[0:m], key); 248 mergesort(a[m:], b[m:], key); 249 b[0:] = a; 250 for((i, j, k) := (0, m, 0); i < m && j < r; k++) { 251 if(key==Byname && b[i].path > b[j].path || key==Byseq && b[i].seq > b[j].seq) 252 a[k] = b[j++]; 253 else 254 a[k] = b[i++]; 255 } 256 if(i < m) 257 a[k:] = b[i:m]; 258 else if(j < r) 259 a[k:] = b[j:r]; 260 } 261} 262 263strings: array of list of string; 264 265S(s: string): string 266{ 267 if(strings == nil) 268 strings = array[257] of list of string; 269 h := hash(s, len strings).t0; 270 for(sl := strings[h]; sl != nil; sl = tl sl) 271 if(hd sl == s) 272 return hd sl; 273 strings[h] = s :: strings[h]; 274 return s; 275} 276 277hash(s: string, n: int): (int, int) 278{ 279 # hashpjw 280 h := 0; 281 for(i:=0; i<len s; i++){ 282 h = (h<<4) + s[i]; 283 if((g := h & int 16rF0000000) != 0) 284 h ^= ((g>>24) & 16rFF) | g; 285 } 286 return ((h&~(1<<31))%n, h); 287} 288