|  | #include "stdinc.h" | 
|  |  | 
|  | #include "9.h" | 
|  |  | 
|  | static struct { | 
|  | QLock	lock; | 
|  |  | 
|  | Con*	con; | 
|  | int	confd[2]; | 
|  | ushort	tag; | 
|  | } cbox; | 
|  |  | 
|  | static ulong | 
|  | cmd9pStrtoul(char* s) | 
|  | { | 
|  | if(strcmp(s, "~0") == 0) | 
|  | return ~0UL; | 
|  | return strtoul(s, 0, 0); | 
|  | } | 
|  |  | 
|  | static uvlong | 
|  | cmd9pStrtoull(char* s) | 
|  | { | 
|  | if(strcmp(s, "~0") == 0) | 
|  | return ~0ULL; | 
|  | return strtoull(s, 0, 0); | 
|  | } | 
|  |  | 
|  | static int | 
|  | cmd9pTag(Fcall* f, int i, char **argv) | 
|  | { | 
|  | USED(f); | 
|  | USED(i); | 
|  | cbox.tag = strtoul(argv[0], 0, 0)-1; | 
|  |  | 
|  | return 1; | 
|  | } | 
|  |  | 
|  | static int | 
|  | cmd9pTwstat(Fcall* f, int i, char **argv) | 
|  | { | 
|  | Dir d; | 
|  | static uchar buf[DIRMAX]; | 
|  |  | 
|  | USED(i); | 
|  | memset(&d, 0, sizeof d); | 
|  | nulldir(&d); | 
|  | d.name = argv[1]; | 
|  | d.uid = argv[2]; | 
|  | d.gid = argv[3]; | 
|  | d.mode = cmd9pStrtoul(argv[4]); | 
|  | d.mtime = cmd9pStrtoul(argv[5]); | 
|  | d.length = cmd9pStrtoull(argv[6]); | 
|  |  | 
|  | f->fid = strtol(argv[0], 0, 0); | 
|  | f->stat = buf; | 
|  | f->nstat = convD2M(&d, buf, sizeof buf); | 
|  | if(f->nstat < BIT16SZ){ | 
|  | werrstr("Twstat: convD2M failed (internal error)"); | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | return 1; | 
|  | } | 
|  |  | 
|  | static int | 
|  | cmd9pTstat(Fcall* f, int i, char** argv) | 
|  | { | 
|  | USED(i); | 
|  | f->fid = strtol(argv[0], 0, 0); | 
|  |  | 
|  | return 1; | 
|  | } | 
|  |  | 
|  | static int | 
|  | cmd9pTremove(Fcall* f, int i, char** argv) | 
|  | { | 
|  | USED(i); | 
|  | f->fid = strtol(argv[0], 0, 0); | 
|  |  | 
|  | return 1; | 
|  | } | 
|  |  | 
|  | static int | 
|  | cmd9pTclunk(Fcall* f, int i, char** argv) | 
|  | { | 
|  | USED(i); | 
|  | f->fid = strtol(argv[0], 0, 0); | 
|  |  | 
|  | return 1; | 
|  | } | 
|  |  | 
|  | static int | 
|  | cmd9pTwrite(Fcall* f, int i, char** argv) | 
|  | { | 
|  | USED(i); | 
|  | f->fid = strtol(argv[0], 0, 0); | 
|  | f->offset = strtoll(argv[1], 0, 0); | 
|  | f->data = argv[2]; | 
|  | f->count = strlen(argv[2]); | 
|  |  | 
|  | return 1; | 
|  | } | 
|  |  | 
|  | static int | 
|  | cmd9pTread(Fcall* f, int i, char** argv) | 
|  | { | 
|  | USED(i); | 
|  | f->fid = strtol(argv[0], 0, 0); | 
|  | f->offset = strtoll(argv[1], 0, 0); | 
|  | f->count = strtol(argv[2], 0, 0); | 
|  |  | 
|  | return 1; | 
|  | } | 
|  |  | 
|  | static int | 
|  | cmd9pTcreate(Fcall* f, int i, char** argv) | 
|  | { | 
|  | USED(i); | 
|  | f->fid = strtol(argv[0], 0, 0); | 
|  | f->name = argv[1]; | 
|  | f->perm = strtol(argv[2], 0, 8); | 
|  | f->mode = strtol(argv[3], 0, 0); | 
|  |  | 
|  | return 1; | 
|  | } | 
|  |  | 
|  | static int | 
|  | cmd9pTopen(Fcall* f, int i, char** argv) | 
|  | { | 
|  | USED(i); | 
|  | f->fid = strtol(argv[0], 0, 0); | 
|  | f->mode = strtol(argv[1], 0, 0); | 
|  |  | 
|  | return 1; | 
|  | } | 
|  |  | 
|  | static int | 
|  | cmd9pTwalk(Fcall* f, int argc, char** argv) | 
|  | { | 
|  | int i; | 
|  |  | 
|  | if(argc < 2){ | 
|  | werrstr("usage: Twalk tag fid newfid [name...]"); | 
|  | return 0; | 
|  | } | 
|  | f->fid = strtol(argv[0], 0, 0); | 
|  | f->newfid = strtol(argv[1], 0, 0); | 
|  | f->nwname = argc-2; | 
|  | if(f->nwname > MAXWELEM){ | 
|  | werrstr("Twalk: too many names"); | 
|  | return 0; | 
|  | } | 
|  | for(i = 0; i < argc-2; i++) | 
|  | f->wname[i] = argv[2+i]; | 
|  |  | 
|  | return 1; | 
|  | } | 
|  |  | 
|  | static int | 
|  | cmd9pTflush(Fcall* f, int i, char** argv) | 
|  | { | 
|  | USED(i); | 
|  | f->oldtag = strtol(argv[0], 0, 0); | 
|  |  | 
|  | return 1; | 
|  | } | 
|  |  | 
|  | static int | 
|  | cmd9pTattach(Fcall* f, int i, char** argv) | 
|  | { | 
|  | USED(i); | 
|  | f->fid = strtol(argv[0], 0, 0); | 
|  | f->afid = strtol(argv[1], 0, 0); | 
|  | f->uname = argv[2]; | 
|  | f->aname = argv[3]; | 
|  |  | 
|  | return 1; | 
|  | } | 
|  |  | 
|  | static int | 
|  | cmd9pTauth(Fcall* f, int i, char** argv) | 
|  | { | 
|  | USED(i); | 
|  | f->afid = strtol(argv[0], 0, 0); | 
|  | f->uname = argv[1]; | 
|  | f->aname = argv[2]; | 
|  |  | 
|  | return 1; | 
|  | } | 
|  |  | 
|  | static int | 
|  | cmd9pTversion(Fcall* f, int i, char** argv) | 
|  | { | 
|  | USED(i); | 
|  | f->msize = strtoul(argv[0], 0, 0); | 
|  | if(f->msize > cbox.con->msize){ | 
|  | werrstr("msize too big"); | 
|  | return 0; | 
|  | } | 
|  | f->version = argv[1]; | 
|  |  | 
|  | return 1; | 
|  | } | 
|  |  | 
|  | typedef struct Cmd9p Cmd9p; | 
|  | struct Cmd9p { | 
|  | char*	name; | 
|  | int	type; | 
|  | int	argc; | 
|  | char*	usage; | 
|  | int	(*f)(Fcall*, int, char**); | 
|  | }; | 
|  |  | 
|  | static Cmd9p cmd9pTmsg[] = { | 
|  | "Tversion", Tversion, 2, "msize version", cmd9pTversion, | 
|  | "Tauth", Tauth, 3, "afid uname aname", cmd9pTauth, | 
|  | "Tflush", Tflush, 1, "oldtag", cmd9pTflush, | 
|  | "Tattach", Tattach, 4, "fid afid uname aname", cmd9pTattach, | 
|  | "Twalk", Twalk, 0, "fid newfid [name...]", cmd9pTwalk, | 
|  | "Topen", Topen, 2, "fid mode", cmd9pTopen, | 
|  | "Tcreate", Tcreate, 4, "fid name perm mode", cmd9pTcreate, | 
|  | "Tread", Tread, 3, "fid offset count", cmd9pTread, | 
|  | "Twrite", Twrite, 3, "fid offset data", cmd9pTwrite, | 
|  | "Tclunk", Tclunk, 1, "fid", cmd9pTclunk, | 
|  | "Tremove", Tremove, 1, "fid", cmd9pTremove, | 
|  | "Tstat", Tstat, 1, "fid", cmd9pTstat, | 
|  | "Twstat", Twstat, 7, "fid name uid gid mode mtime length", cmd9pTwstat, | 
|  | "nexttag", 0, 0, "", cmd9pTag, | 
|  | }; | 
|  |  | 
|  | static int | 
|  | cmd9p(int argc, char* argv[]) | 
|  | { | 
|  | int i, n; | 
|  | Fcall f, t; | 
|  | uchar *buf; | 
|  | char *usage; | 
|  | u32int msize; | 
|  |  | 
|  | usage = "usage: 9p T-message ..."; | 
|  |  | 
|  | ARGBEGIN{ | 
|  | default: | 
|  | return cliError(usage); | 
|  | }ARGEND | 
|  | if(argc < 1) | 
|  | return cliError(usage); | 
|  |  | 
|  | for(i = 0; i < nelem(cmd9pTmsg); i++){ | 
|  | if(strcmp(cmd9pTmsg[i].name, argv[0]) == 0) | 
|  | break; | 
|  | } | 
|  | if(i == nelem(cmd9pTmsg)) | 
|  | return cliError(usage); | 
|  | argc--; | 
|  | argv++; | 
|  | if(cmd9pTmsg[i].argc && argc != cmd9pTmsg[i].argc){ | 
|  | werrstr("usage: %s %s", | 
|  | cmd9pTmsg[i].name, cmd9pTmsg[i].usage); | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | memset(&t, 0, sizeof(t)); | 
|  | t.type = cmd9pTmsg[i].type; | 
|  | if(t.type == Tversion) | 
|  | t.tag = NOTAG; | 
|  | else | 
|  | t.tag = ++cbox.tag; | 
|  | msize = cbox.con->msize; | 
|  | if(!cmd9pTmsg[i].f(&t, argc, argv)) | 
|  | return 0; | 
|  | buf = vtmalloc(msize); | 
|  | n = convS2M(&t, buf, msize); | 
|  | if(n <= BIT16SZ){ | 
|  | werrstr("%s: convS2M error", cmd9pTmsg[i].name); | 
|  | vtfree(buf); | 
|  | return 0; | 
|  | } | 
|  | if(write(cbox.confd[0], buf, n) != n){ | 
|  | werrstr("%s: write error: %r", cmd9pTmsg[i].name); | 
|  | vtfree(buf); | 
|  | return 0; | 
|  | } | 
|  | consPrint("\t-> %F\n", &t); | 
|  |  | 
|  | if((n = read9pmsg(cbox.confd[0], buf, msize)) <= 0){ | 
|  | werrstr("%s: read error: %r", cmd9pTmsg[i].name); | 
|  | vtfree(buf); | 
|  | return 0; | 
|  | } | 
|  | if(convM2S(buf, n, &f) == 0){ | 
|  | werrstr("%s: convM2S error", cmd9pTmsg[i].name); | 
|  | vtfree(buf); | 
|  | return 0; | 
|  | } | 
|  | consPrint("\t<- %F\n", &f); | 
|  |  | 
|  | vtfree(buf); | 
|  | return 1; | 
|  | } | 
|  |  | 
|  | static int | 
|  | cmdDot(int argc, char* argv[]) | 
|  | { | 
|  | long l; | 
|  | Dir *dir; | 
|  | int fd, r; | 
|  | vlong length; | 
|  | char *f, *p, *s, *usage; | 
|  |  | 
|  | usage = "usage: . file"; | 
|  |  | 
|  | ARGBEGIN{ | 
|  | default: | 
|  | return cliError(usage); | 
|  | }ARGEND | 
|  | if(argc != 1) | 
|  | return cliError(usage); | 
|  |  | 
|  | if((dir = dirstat(argv[0])) == nil) | 
|  | return cliError(". dirstat %s: %r", argv[0]); | 
|  | length = dir->length; | 
|  | free(dir); | 
|  |  | 
|  | r = 1; | 
|  | if(length != 0){ | 
|  | /* | 
|  | * Read the whole file in. | 
|  | */ | 
|  | if((fd = open(argv[0], OREAD)) < 0) | 
|  | return cliError(". open %s: %r", argv[0]); | 
|  | f = vtmalloc(dir->length+1); | 
|  | if((l = read(fd, f, length)) < 0){ | 
|  | vtfree(f); | 
|  | close(fd); | 
|  | return cliError(". read %s: %r", argv[0]); | 
|  | } | 
|  | close(fd); | 
|  | f[l] = '\0'; | 
|  |  | 
|  | /* | 
|  | * Call cliExec() for each line. | 
|  | */ | 
|  | for(p = s = f; *p != '\0'; p++){ | 
|  | if(*p == '\n'){ | 
|  | *p = '\0'; | 
|  | if(cliExec(s) == 0){ | 
|  | r = 0; | 
|  | consPrint("%s: %r\n", s); | 
|  | } | 
|  | s = p+1; | 
|  | } | 
|  | } | 
|  | vtfree(f); | 
|  | } | 
|  |  | 
|  | if(r == 0) | 
|  | werrstr("errors in . %#q", argv[0]); | 
|  | return r; | 
|  | } | 
|  |  | 
|  | static int | 
|  | cmdDflag(int argc, char* argv[]) | 
|  | { | 
|  | char *usage; | 
|  |  | 
|  | usage = "usage: dflag"; | 
|  |  | 
|  | ARGBEGIN{ | 
|  | default: | 
|  | return cliError(usage); | 
|  | }ARGEND | 
|  | if(argc) | 
|  | return cliError(usage); | 
|  |  | 
|  | Dflag ^= 1; | 
|  | consPrint("dflag %d\n", Dflag); | 
|  |  | 
|  | return 1; | 
|  | } | 
|  |  | 
|  | static int | 
|  | cmdEcho(int argc, char* argv[]) | 
|  | { | 
|  | char *usage; | 
|  | int i, nflag; | 
|  |  | 
|  | nflag = 0; | 
|  | usage = "usage: echo [-n] ..."; | 
|  |  | 
|  | ARGBEGIN{ | 
|  | default: | 
|  | return cliError(usage); | 
|  | case 'n': | 
|  | nflag = 1; | 
|  | break; | 
|  | }ARGEND | 
|  |  | 
|  | for(i = 0; i < argc; i++){ | 
|  | if(i != 0) | 
|  | consPrint(" %s", argv[i]); | 
|  | else | 
|  | consPrint(argv[i]); | 
|  | } | 
|  | if(!nflag) | 
|  | consPrint("\n"); | 
|  |  | 
|  | return 1; | 
|  | } | 
|  |  | 
|  | static int | 
|  | cmdBind(int argc, char* argv[]) | 
|  | { | 
|  | ulong flag = 0; | 
|  | char *usage; | 
|  |  | 
|  | usage = "usage: bind [-b|-a|-c|-bc|-ac] new old"; | 
|  |  | 
|  | ARGBEGIN{ | 
|  | case 'a': | 
|  | flag |= MAFTER; | 
|  | break; | 
|  | case 'b': | 
|  | flag |= MBEFORE; | 
|  | break; | 
|  | case 'c': | 
|  | flag |= MCREATE; | 
|  | break; | 
|  | default: | 
|  | return cliError(usage); | 
|  | }ARGEND | 
|  |  | 
|  | if(argc != 2 || (flag&MAFTER)&&(flag&MBEFORE)) | 
|  | return cliError(usage); | 
|  |  | 
|  | #ifndef PLAN9PORT | 
|  | if(bind(argv[0], argv[1], flag) < 0){ | 
|  | /* try to give a less confusing error than the default */ | 
|  | if(access(argv[0], 0) < 0) | 
|  | return cliError("bind: %s: %r", argv[0]); | 
|  | else if(access(argv[1], 0) < 0) | 
|  | return cliError("bind: %s: %r", argv[1]); | 
|  | else | 
|  | return cliError("bind %s %s: %r", argv[0], argv[1]); | 
|  | } | 
|  | #endif | 
|  | return 1; | 
|  | } | 
|  |  | 
|  | int | 
|  | cmdInit(void) | 
|  | { | 
|  | cbox.confd[0] = cbox.confd[1] = -1; | 
|  |  | 
|  | cliAddCmd(".", cmdDot); | 
|  | cliAddCmd("9p", cmd9p); | 
|  | cliAddCmd("dflag", cmdDflag); | 
|  | cliAddCmd("echo", cmdEcho); | 
|  | cliAddCmd("bind", cmdBind); | 
|  |  | 
|  | if(pipe(cbox.confd) < 0) | 
|  | return 0; | 
|  | if((cbox.con = conAlloc(cbox.confd[1], "console", 0)) == nil){ | 
|  | close(cbox.confd[0]); | 
|  | close(cbox.confd[1]); | 
|  | cbox.confd[0] = cbox.confd[1] = -1; | 
|  | return 0; | 
|  |  | 
|  | } | 
|  | cbox.con->isconsole = 1; | 
|  |  | 
|  | return 1; | 
|  | } |