1implement Styxchat; 2 3# 4# Copyright © 2002,2003 Vita Nuova Holdings Limited. All rights reserved. 5# 6 7include "sys.m"; 8 sys: Sys; 9 10include "draw.m"; 11 12include "styx.m"; 13 styx: Styx; 14 Tmsg, Rmsg: import styx; 15 16include "string.m"; 17 str: String; 18 19include "bufio.m"; 20 bufio: Bufio; 21 Iobuf: import bufio; 22 23include "dial.m"; 24 dial: Dial; 25 26include "arg.m"; 27 28Styxchat: module 29{ 30 init: fn(nil: ref Draw->Context, nil: list of string); 31}; 32 33msgsize := 64*1024; 34nexttag := 1; 35verbose := 0; 36 37stdin: ref Sys->FD; 38 39init(nil: ref Draw->Context, args: list of string) 40{ 41 sys = load Sys Sys->PATH; 42 styx = load Styx Styx->PATH; 43 str = load String String->PATH; 44 bufio = load Bufio Bufio->PATH; 45 dial = load Dial Dial->PATH; 46 styx->init(); 47 48 client := 1; 49 addr := 0; 50 arg := load Arg Arg->PATH; 51 arg->init(args); 52 arg->setusage("styxchat [-nsv] [-m messagesize] [dest]"); 53 while((o := arg->opt()) != 0) 54 case o { 55 'm' => 56 msgsize = atoi(arg->earg()); 57 's' => 58 client = 0; 59 'n' => 60 addr = 1; 61 'v' => 62 verbose++; 63 * => 64 arg->usage(); 65 } 66 args = arg->argv(); 67 arg = nil; 68 fd: ref Sys->FD; 69 if(args == nil){ 70 fd = sys->fildes(0); 71 stdin = sys->open("/dev/cons", Sys->ORDWR); 72 if (stdin == nil) 73 err(sys->sprint("can't open /dev/cons: %r")); 74 sys->dup(stdin.fd, 1); 75 }else{ 76 if(tl args != nil) 77 arg->usage(); 78 stdin = sys->fildes(0); 79 dest := hd args; 80 if(addr){ 81 dest = dial->netmkaddr(dest, "net", "styx"); 82 if (client){ 83 c := dial->dial(dest, nil); 84 if(c == nil) 85 err(sys->sprint("can't dial %s: %r", dest)); 86 fd = c.dfd; 87 }else{ 88 lc := dial->announce(dest); 89 if(lc == nil) 90 err(sys->sprint("can't announce %s: %r", dest)); 91 c := dial->listen(lc); 92 if(c == nil) 93 err(sys->sprint("can't listen on %s: %r", dest)); 94 fd = dial->accept(c); 95 if(fd == nil) 96 err(sys->sprint("can't open %s/data: %r", c.dir)); 97 } 98 }else{ 99 fd = sys->open(dest, Sys->ORDWR); 100 if(fd == nil) 101 err(sys->sprint("can't open %s: %r", dest)); 102 } 103 } 104 sys->pctl(Sys->NEWPGRP, nil); 105 if(client){ 106 spawn Rreader(fd); 107 Twriter(fd); 108 }else{ 109 spawn Treader(fd); 110 Rwriter(fd); 111 } 112} 113 114quit(e: int) 115{ 116 fd := sys->open("/prog/"+string sys->pctl(0, nil)+"/ctl", Sys->OWRITE); 117 if(fd != nil) 118 sys->fprint(fd, "killgrp"); 119 if(e) 120 raise "fail:error"; 121 exit; 122} 123 124Rreader(fd: ref Sys->FD) 125{ 126 while((m := Rmsg.read(fd, msgsize)) != nil){ 127 sys->print("<- %s\n%s", m.text(), Rdump(m)); 128 if(tagof m == tagof Rmsg.Readerror) 129 quit(1); 130 } 131 sys->print("styxchat: server hungup\n"); 132} 133 134Twriter(fd: ref Sys->FD) 135{ 136 in := bufio->fopen(stdin, Sys->OREAD); 137 while((l := in.gets('\n')) != nil){ 138 if(l != nil && l[0] == '#') 139 continue; 140 (t, err) := Tparse(l); 141 if(t == nil){ 142 if(err != nil) 143 sys->print("?%s\n", err); 144 }else{ 145 if(t.tag == 0) 146 t.tag = nexttag; 147 a := t.pack(); 148 if(a != nil){ 149 sys->print("-> %s\n%s", t.text(), Tdump(t)); 150 n := len a; 151 if(n <= msgsize){ 152 if(sys->write(fd, a, len a) != len a) 153 sys->print("?write error to server: %r\n"); 154 if(t.tag != Styx->NOTAG && t.tag != ~0) 155 nexttag++; 156 }else 157 sys->print("?message bigger than agreed: %d bytes\n", n); 158 }else 159 sys->fprint(sys->fildes(2), "styxchat: T-message conversion failed\n"); 160 } 161 } 162} 163 164Rdump(m: ref Rmsg): string 165{ 166 if(!verbose) 167 return ""; 168 pick r :=m { 169 Read => 170 return dump(r.data, len r.data, verbose>1); 171 * => 172 return ""; 173 } 174} 175 176Tdump(m: ref Tmsg): string 177{ 178 if(!verbose) 179 return ""; 180 pick t := m { 181 Write => 182 return dump(t.data, len t.data, verbose>1); 183 * => 184 return ""; 185 } 186} 187 188isprint(c: int): int 189{ 190 return c >= 16r20 && c < 16r7F || c == '\n' || c == '\t' || c == '\r'; 191} 192 193textdump(a: array of byte, lim: int): string 194{ 195 s := "\ttext(\""; 196 for(i := 0; i < lim; i++) 197 case c := int a[i] { 198 '\t' => 199 s += "\\t"; 200 '\n' => 201 s += "\\n"; 202 '\r' => 203 s += "\\r"; 204 '"' => 205 s += "\\\""; 206 * => 207 if(isprint(c)) 208 s[len s] = c; 209 else 210 s += sys->sprint("\\u%4.4ux", c); 211 } 212 s += "\")\n"; 213 return s; 214} 215 216dump(a: array of byte, lim: int, text: int): string 217{ 218 if(a == nil) 219 return ""; 220 if(len a < lim) 221 lim = len a; 222 printable := 1; 223 for(i := 0; i < lim; i++) 224 if(!isprint(int a[i])){ 225 printable = 0; 226 break; 227 } 228 if(printable) 229 return textdump(a, lim); 230 s := "\tdump("; 231 for(i = 0; i < lim; i++) 232 s += sys->sprint("%2.2ux", int a[i]); 233 s += ")\n"; 234 if(text) 235 s += textdump(a, lim); 236 return s; 237} 238 239val(s: string): int 240{ 241 if(s == "~0") 242 return ~0; 243 return atoi(s); 244} 245 246bigval(s: string): big 247{ 248 if(s == "~0") 249 return ~ big 0; 250 return atob(s); 251} 252 253fid(s: string): int 254{ 255 if(s == "nofid" || s == "NOFID") 256 return Styx->NOFID; 257 return val(s); 258} 259 260tag(s: string): int 261{ 262 if(s == "~0" || s == "notag" || s == "NOTAG") 263 return Styx->NOTAG; 264 return atoi(s); 265} 266 267dir(name: string, uid: string, gid: string, mode: int, mtime: int, length: big): Sys->Dir 268{ 269 d := sys->zerodir; 270 d.name = name; 271 d.uid = uid; 272 d.gid = gid; 273 d.mode = mode; 274 d.mtime = mtime; 275 d.length = length; 276 return d; 277} 278 279Tparse(s: string): (ref Tmsg, string) 280{ 281 args := str->unquoted(s); 282 if(args == nil) 283 return (nil, nil); 284 argc := len args; 285 av := array[argc] of string; 286 for(i:=0; args != nil; args = tl args) 287 av[i++] = hd args; 288 case av[0] { 289 "Tversion" => 290 if(argc != 3) 291 return (nil, "usage: Tversion messagesize version"); 292 return (ref Tmsg.Version(Styx->NOTAG, atoi(av[1]), av[2]), nil); 293 "Tauth" => 294 if(argc != 4) 295 return (nil, "usage: Tauth afid uname aname"); 296 return (ref Tmsg.Auth(0, fid(av[1]), av[2], av[3]), nil); 297 "Tflush" => 298 if(argc != 2) 299 return (nil, "usage: Tflush oldtag"); 300 return (ref Tmsg.Flush(0, tag(av[1])), nil); 301 "Tattach" => 302 if(argc != 5) 303 return (nil, "usage: Tattach fid afid uname aname"); 304 return (ref Tmsg.Attach(0, fid(av[1]), fid(av[2]), av[3], av[4]), nil); 305 "Twalk" => 306 if(argc < 3) 307 return (nil, "usage: Twalk fid newfid [name...]"); 308 names: array of string; 309 if(argc > 3) 310 names = av[3:]; 311 return (ref Tmsg.Walk(0, fid(av[1]), fid(av[2]), names), nil); 312 "Topen" => 313 if(argc != 3) 314 return (nil, "usage: Topen fid mode"); 315 return (ref Tmsg.Open(0, fid(av[1]), atoi(av[2])), nil); 316 "Tcreate" => 317 if(argc != 5) 318 return (nil, "usage: Tcreate fid name perm mode"); 319 return (ref Tmsg.Create(0, fid(av[1]), av[2], atoi(av[3]), atoi(av[4])), nil); 320 "Tread" => 321 if(argc != 4) 322 return (nil, "usage: Tread fid offset count"); 323 return (ref Tmsg.Read(0, fid(av[1]), atob(av[2]), atoi(av[3])), nil); 324 "Twrite" => 325 if(argc != 4) 326 return (nil, "usage: Twrite fid offset data"); 327 return (ref Tmsg.Write(0, fid(av[1]), atob(av[2]), array of byte av[3]), nil); 328 "Tclunk" => 329 if(argc != 2) 330 return (nil, "usage: Tclunk fid"); 331 return (ref Tmsg.Clunk(0, fid(av[1])), nil); 332 "Tremove" => 333 if(argc != 2) 334 return (nil, "usage: Tremove fid"); 335 return (ref Tmsg.Remove(0, fid(av[1])), nil); 336 "Tstat" => 337 if(argc != 2) 338 return (nil, "usage: Tstat fid"); 339 return (ref Tmsg.Stat(0, fid(av[1])), nil); 340 "Twstat" => 341 if(argc != 8) 342 return (nil, "usage: Twstat fid name uid gid mode mtime length"); 343 return (ref Tmsg.Wstat(0, fid(av[1]), dir(av[2], av[3], av[4], val(av[5]), val(av[6]), bigval(av[7]))), nil); 344 "nexttag" => 345 if(argc < 2) 346 return (nil, sys->sprint("next tag is %d", nexttag)); 347 nexttag = tag(av[1]); 348 return (nil, nil); 349 "dump" => 350 verbose++; 351 return (nil, nil); 352 * => 353 return (nil, "unknown message type"); 354 } 355} 356 357# 358# server side 359# 360 361Treader(fd: ref Sys->FD) 362{ 363 while((m := Tmsg.read(fd, msgsize)) != nil){ 364 sys->print("<- %s\n", m.text()); 365 if(tagof m == tagof Tmsg.Readerror) 366 quit(1); 367 } 368 sys->print("styxchat: clients hungup\n"); 369} 370 371Rwriter(fd: ref Sys->FD) 372{ 373 in := bufio->fopen(stdin, Sys->OREAD); 374 while((l := in.gets('\n')) != nil){ 375 if(l != nil && l[0] == '#') 376 continue; 377 (r, err) := Rparse(l); 378 if(r == nil){ 379 if(err != nil) 380 sys->print("?%s\n", err); 381 }else{ 382 a := r.pack(); 383 if(a != nil){ 384 sys->print("-> %s\n", r.text()); 385 n := len a; 386 if(n <= msgsize){ 387 if(sys->write(fd, a, len a) != len a) 388 sys->print("?write error to clients: %r\n"); 389 }else 390 sys->print("?message bigger than agreed: %d bytes\n", n); 391 }else 392 sys->fprint(sys->fildes(2), "styxchat: R-message conversion failed\n"); 393 } 394 } 395} 396 397qid(s: string): Sys->Qid 398{ 399 (nf, flds) := sys->tokenize(s, "."); 400 q := Sys->Qid(big 0, 0, 0); 401 if(nf < 1) 402 return q; 403 q.path = atob(hd flds); 404 if(nf < 2) 405 return q; 406 q.vers = atoi(hd tl flds); 407 if(nf < 3) 408 return q; 409 q.qtype = mode(hd tl tl flds); 410 return q; 411} 412 413mode(s: string): int 414{ 415 if(len s > 0 && s[0] >= '0' && s[0] <= '9') 416 return atoi(s); 417 mode := 0; 418 for(i := 0; i < len s; i++){ 419 case s[i] { 420 'd' => 421 mode |= Sys->QTDIR; 422 'a' => 423 mode |= Sys->QTAPPEND; 424 'u' => 425 mode |= Sys->QTAUTH; 426 'l' => 427 mode |= Sys->QTEXCL; 428 'f' => 429 ; 430 * => 431 sys->fprint(sys->fildes(2), "styxchat: unknown mode character %c, ignoring\n", s[i]); 432 } 433 } 434 return mode; 435} 436 437rdir(a: array of string): Sys->Dir 438{ 439 d := sys->zerodir; 440 d.qid = qid(a[0]); 441 d.mode = atoi(a[1]) | (d.qid.qtype<<24); 442 d.atime = atoi(a[2]); 443 d.mtime = atoi(a[3]); 444 d.length = atob(a[4]); 445 d.name = a[5]; 446 d.uid = a[6]; 447 d.gid = a[7]; 448 d.muid = a[8]; 449 return d; 450} 451 452Rparse(s: string): (ref Rmsg, string) 453{ 454 args := str->unquoted(s); 455 if(args == nil) 456 return (nil, nil); 457 argc := len args; 458 av := array[argc] of string; 459 for(i:=0; args != nil; args = tl args) 460 av[i++] = hd args; 461 case av[0] { 462 "Rversion" => 463 if(argc != 4) 464 return (nil, "usage: Rversion tag messagesize version"); 465 return (ref Rmsg.Version(tag(av[1]), atoi(av[2]), av[3]), nil); 466 "Rauth" => 467 if(argc != 3) 468 return (nil, "usage: Rauth tag aqid"); 469 return (ref Rmsg.Auth(tag(av[1]), qid(av[2])), nil); 470 "Rflush" => 471 if(argc != 2) 472 return (nil, "usage: Rflush tag"); 473 return (ref Rmsg.Flush(tag(av[1])), nil); 474 "Rattach" => 475 if(argc != 3) 476 return (nil, "usage: Rattach tag qid"); 477 return (ref Rmsg.Attach(tag(av[1]), qid(av[2])), nil); 478 "Rwalk" => 479 if(argc < 2) 480 return (nil, "usage: Rwalk tag [qid ...]"); 481 qids := array[argc-2] of Sys->Qid; 482 for(i = 0; i < len qids; i++) 483 qids[i] = qid(av[i+2]); 484 return (ref Rmsg.Walk(tag(av[1]), qids), nil); 485 "Ropen" => 486 if(argc != 4) 487 return (nil, "usage: Ropen tag qid iounit"); 488 return (ref Rmsg.Open(tag(av[1]), qid(av[2]), atoi(av[3])), nil); 489 "Rcreate" => 490 if(argc != 4) 491 return (nil, "usage: Rcreate tag qid iounit"); 492 return (ref Rmsg.Create(tag(av[1]), qid(av[2]), atoi(av[3])), nil); 493 "Rread" => 494 if(argc != 3) 495 return (nil, "usage: Rread tag data"); 496 return (ref Rmsg.Read(tag(av[1]), array of byte av[2]), nil); 497 "Rwrite" => 498 if(argc != 3) 499 return (nil, "usage: Rwrite tag count"); 500 return (ref Rmsg.Write(tag(av[1]), atoi(av[2])), nil); 501 "Rclunk" => 502 if(argc != 2) 503 return (nil, "usage: Rclunk tag"); 504 return (ref Rmsg.Clunk(tag(av[1])), nil); 505 "Rremove" => 506 if(argc != 2) 507 return (nil, "usage: Rremove tag"); 508 return (ref Rmsg.Remove(tag(av[1])), nil); 509 "Rstat" => 510 if(argc != 11) 511 return (nil, "usage: Rstat tag qid mode atime mtime length name uid gid muid"); 512 return (ref Rmsg.Stat(tag(av[1]), rdir(av[2:])), nil); 513 "Rwstat" => 514 if(argc != 8) 515 return (nil, "usage: Rwstat tag"); 516 return (ref Rmsg.Wstat(tag(av[1])), nil); 517 "Rerror" => 518 if(argc != 3) 519 return (nil, "usage: Rerror tag ename"); 520 return (ref Rmsg.Error(tag(av[1]), av[2]), nil); 521 "dump" => 522 verbose++; 523 return (nil, nil); 524 * => 525 return (nil, "unknown message type"); 526 } 527} 528 529atoi(s: string): int 530{ 531 (i, nil) := str->toint(s, 0); 532 return i; 533} 534 535# atoi with traditional unix semantics for octal and hex. 536atob(s: string): big 537{ 538 (b, nil) := str->tobig(s, 0); 539 return b; 540} 541 542err(s: string) 543{ 544 sys->fprint(sys->fildes(2), "styxchat: %s\n", s); 545 raise "fail:error"; 546} 547