123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866 |
- #include <u.h>
- #include <libc.h>
- #include <thread.h>
- #include <fcall.h>
- #include "pool.h"
- #include "playlist.h"
- typedef struct Wmsg Wmsg;
- enum {
- Busy = 0x01,
- Open = 0x02,
- Trunc = 0x04,
- Eof = 0x08,
- };
- File files[] = {
- [Qdir] = {.dir = {0,0,{Qdir, 0,QTDIR}, 0555|DMDIR, 0,0,0, "."}},
- [Qplayctl] = {.dir = {0,0,{Qplayctl, 0,QTFILE}, 0666, 0,0,0, "playctl"}},
- [Qplaylist] = {.dir = {0,0,{Qplaylist, 0,QTFILE}, 0666|DMAPPEND, 0,0,0, "playlist"}},
- [Qplayvol] = {.dir = {0,0,{Qplayvol, 0,QTFILE}, 0666, 0,0,0, "playvol"}},
- [Qplaystat] = {.dir = {0,0,{Qplaystat, 0,QTFILE}, 0444, 0,0,0, "playstat"}},
- };
- Channel *reqs;
- Channel *workers;
- Channel *volumechan;
- Channel *playchan;
- Channel *playlistreq;
- Playlist playlist;
- int volume[8];
- char *statetxt[] = {
- [Nostate] = "panic!",
- [Error] = "error",
- [Stop] = "stop",
- [Pause] = "pause",
- [Play] = "play",
- [Resume] = "resume",
- [Skip] = "skip",
- nil
- };
- // low-order bits: position in play list, high-order: play state:
- Pmsg playstate = {Stop, 0};
- char *rflush(Worker*), *rauth(Worker*),
- *rattach(Worker*), *rwalk(Worker*),
- *ropen(Worker*), *rcreate(Worker*),
- *rread(Worker*), *rwrite(Worker*), *rclunk(Worker*),
- *rremove(Worker*), *rstat(Worker*), *rwstat(Worker*),
- *rversion(Worker*);
- char *(*fcalls[])(Worker*) = {
- [Tflush] rflush,
- [Tversion] rversion,
- [Tauth] rauth,
- [Tattach] rattach,
- [Twalk] rwalk,
- [Topen] ropen,
- [Tcreate] rcreate,
- [Tread] rread,
- [Twrite] rwrite,
- [Tclunk] rclunk,
- [Tremove] rremove,
- [Tstat] rstat,
- [Twstat] rwstat,
- };
- int messagesize = Messagesize;
- Fid *fids;
- char Eperm[] = "permission denied";
- char Enotdir[] = "not a directory";
- char Enoauth[] = "authentication not required";
- char Enotexist[] = "file does not exist";
- char Einuse[] = "file in use";
- char Eexist[] = "file exists";
- char Enotowner[] = "not owner";
- char Eisopen[] = "file already open for I/O";
- char Excl[] = "exclusive use file already open";
- char Ename[] = "illegal name";
- char Ebadctl[] = "unknown control message";
- char Epast[] = "reading past eof";
- Fid *newfid(int);
- void volumeupdater(void*);
- void playupdater(void*);
- char *playerror;
- static int
- lookup(char *cmd, char *list[])
- {
- int i;
- for (i = 0; list[i] != nil; i++)
- if (strcmp(cmd, list[i]) == 0)
- return i;
- return -1;
- }
- char*
- rversion(Worker *w)
- {
- Req *r;
- Fid *f;
- r = w->r;
- if(r->ifcall.msize < 256)
- return "max messagesize too small";
- if(r->ifcall.msize < messagesize)
- messagesize = r->ifcall.msize;
- r->ofcall.msize = messagesize;
- if(strncmp(r->ifcall.version, "9P2000", 6) != 0)
- return "unknown 9P version";
- else
- r->ofcall.version = "9P2000";
- for(f = fids; f; f = f->next)
- if(f->flags & Busy)
- f->flags &= ~(Open|Busy);
- return nil;
- }
- char*
- rauth(Worker*)
- {
- return Enoauth;
- }
- char*
- rflush(Worker *w)
- {
- Wmsg m;
- int i;
- Req *r;
- r = w->r;
- m.cmd = Flush;
- m.off = r->ifcall.oldtag;
- m.arg = nil;
- for(i = 1; i < nelem(files); i++)
- bcastmsg(files[i].workers, &m);
- if (debug & DbgWorker)
- fprint(2, "flush done on tag %d\n", r->ifcall.oldtag);
- return 0;
- }
- char*
- rattach(Worker *w)
- {
- Fid *f;
- Req *r;
- r = w->r;
- f = r->fid;
- f->flags |= Busy;
- f->file = &files[Qdir];
- r->ofcall.qid = f->file->dir.qid;
- if(!aflag && strcmp(r->ifcall.uname, user) != 0)
- return Eperm;
- return 0;
- }
- static Fid*
- doclone(Fid *f, int nfid)
- {
- Fid *nf;
- nf = newfid(nfid);
- if(nf->flags & Busy)
- return nil;
- nf->flags |= Busy;
- nf->flags &= ~(Open);
- nf->file = f->file;
- return nf;
- }
- char*
- dowalk(Fid *f, char *name)
- {
- int t;
- if (strcmp(name, ".") == 0)
- return nil;
- if (strcmp(name, "..") == 0){
- f->file = &files[Qdir];
- return nil;
- }
- if(f->file != &files[Qdir])
- return Enotexist;
- for (t = 1; t < Nqid; t++){
- if(strcmp(name, files[t].dir.name) == 0){
- f->file = &files[t];
- return nil;
- }
- }
- return Enotexist;
- }
- char*
- rwalk(Worker *w)
- {
- Fid *f, *nf;
- char *rv;
- int i;
- File *savefile;
- Req *r;
- r = w->r;
- f = r->fid;
- if(f->flags & Open)
- return Eisopen;
- r->ofcall.nwqid = 0;
- nf = nil;
- savefile = f->file;
- /* clone if requested */
- if(r->ifcall.newfid != r->ifcall.fid){
- nf = doclone(f, r->ifcall.newfid);
- if(nf == nil)
- return "new fid in use";
- f = nf;
- }
- /* if it's just a clone, return */
- if(r->ifcall.nwname == 0 && nf != nil)
- return nil;
- /* walk each element */
- rv = nil;
- for(i = 0; i < r->ifcall.nwname; i++){
- rv = dowalk(f, r->ifcall.wname[i]);
- if(rv != nil){
- if(nf != nil)
- nf->flags &= ~(Open|Busy);
- else
- f->file = savefile;
- break;
- }
- r->ofcall.wqid[i] = f->file->dir.qid;
- }
- r->ofcall.nwqid = i;
- /* we only error out if no walk */
- if(i > 0)
- rv = nil;
- return rv;
- }
- char *
- ropen(Worker *w)
- {
- Fid *f, *ff;
- Wmsg m;
- Req *r;
- r = w->r;
- f = r->fid;
- if(f->flags & Open)
- return Eisopen;
- if(r->ifcall.mode != OREAD)
- if((f->file->dir.mode & 0x2) == 0)
- return Eperm;
- if((r->ifcall.mode & OTRUNC) && f->file == &files[Qplaylist]){
- playlist.nlines = 0;
- playlist.ndata = 0;
- free(playlist.lines);
- free(playlist.data);
- playlist.lines = nil;
- playlist.data = nil;
- f->file->dir.length = 0;
- f->file->dir.qid.vers++;
- /* Mark all fids for this file `Trunc'ed */
- for(ff = fids; ff; ff = ff->next)
- if(ff->file == &files[Qplaylist] && (ff->flags & Open))
- ff->flags |= Trunc;
- m.cmd = Check;
- m.off = 0;
- m.arg = nil;
- bcastmsg(f->file->workers, &m);
- }
- r->ofcall.iounit = 0;
- r->ofcall.qid = f->file->dir.qid;
- f->flags |= Open;
- return nil;
- }
- char *
- rcreate(Worker*)
- {
- return Eperm;
- }
- int
- readtopdir(Fid*, uchar *buf, long off, int cnt, int blen)
- {
- int i, m, n;
- long pos;
- n = 0;
- pos = 0;
- for (i = 1; i < Nqid; i++){
- m = convD2M(&files[i].dir, &buf[n], blen-n);
- if(off <= pos){
- if(m <= BIT16SZ || m > cnt)
- break;
- n += m;
- cnt -= m;
- }
- pos += m;
- }
- return n;
- }
- char*
- rread(Worker *w)
- {
- Fid *f;
- Req *r;
- long off, cnt;
- int n, i;
- Wmsg m;
- char *p;
- r = w->r;
- f = r->fid;
- r->ofcall.count = 0;
- off = r->ifcall.offset;
- cnt = r->ifcall.count;
- if(cnt > messagesize - IOHDRSZ)
- cnt = messagesize - IOHDRSZ;
- if(f->file == &files[Qdir]){
- n = readtopdir(f, r->indata, off, cnt, messagesize - IOHDRSZ);
- r->ofcall.count = n;
- return nil;
- }
- if(f->file == files + Qplaystat){
- p = getplaystat(r->ofcall.data, r->ofcall.data + sizeof r->indata);
- readbuf(r, r->ofcall.data, p - r->ofcall.data);
- return nil;
- }
- m.cmd = 0;
- while(f->vers == f->file->dir.qid.vers && (f->flags & Eof)){
- /* Wait until file state changes (f->file->dir.qid.vers is incremented) */
- m = waitmsg(w, f->file->workers);
- if(m.cmd == Flush && m.off == r->ifcall.tag)
- return (char*)~0; /* no answer needed */
- assert(m.cmd != Work);
- yield();
- }
- if(f->file == files + Qplaylist){
- f->flags &= ~Eof;
- if((f->flags & Trunc) && r->ifcall.offset != 0){
- f->flags &= ~Trunc;
- return Epast;
- }
- f->flags &= ~Trunc;
- if(r->ifcall.offset < playlist.ndata)
- readbuf(r, playlist.data, playlist.ndata);
- else if(r->ifcall.offset == playlist.ndata){
- r->ofcall.count = 0;
- /* Arrange for this fid to wait next time: */
- f->vers = f->file->dir.qid.vers;
- f->flags |= Eof;
- }else{
- /* Beyond eof, bad seek? */
- return Epast;
- }
- }else if(f->file == files + Qplayctl){
- f->flags &= ~Eof;
- if(m.cmd == Error){
- snprint(r->ofcall.data, sizeof r->indata, "%s %ud %q",
- statetxt[m.cmd], m.off, m.arg);
- free(m.arg);
- }else if(f->vers == f->file->dir.qid.vers){
- r->ofcall.count = 0;
- /* Arrange for this fid to wait next time: */
- f->flags |= Eof;
- return nil;
- }else{
- snprint(r->ofcall.data, sizeof r->indata, "%s %ud",
- statetxt[playstate.cmd], playstate.off);
- f->vers = f->file->dir.qid.vers;
- }
- r->ofcall.count = strlen(r->ofcall.data);
- if(r->ofcall.count > r->ifcall.count)
- r->ofcall.count = r->ifcall.count;
- }else if(f->file == files + Qplayvol){
- f->flags &= ~Eof;
- if(f->vers == f->file->dir.qid.vers){
- r->ofcall.count = 0;
- /* Arrange for this fid to wait next time: */
- f->flags |= Eof;
- }else{
- p = seprint(r->ofcall.data, r->ofcall.data + sizeof r->indata, "volume '");
- for(i = 0; i < nelem(volume); i++){
- if(volume[i] == Undef)
- break;
- p = seprint(p, r->ofcall.data + sizeof r->indata, "%d ", volume[i]);
- }
- p = seprint(p, r->ofcall.data + sizeof r->indata, "'");
- r->ofcall.count = p - r->ofcall.data;
- if(r->ofcall.count > r->ifcall.count)
- r->ofcall.count = r->ifcall.count;
- f->vers = f->file->dir.qid.vers;
- }
- }else
- abort();
- return nil;
- }
- char*
- rwrite(Worker *w)
- {
- long cnt, i, nf, cmd;
- Pmsg newstate;
- char *fields[3], *p, *q;
- Wmsg m;
- Fid *f;
- Req *r;
- r = w->r;
- f = r->fid;
- r->ofcall.count = 0;
- cnt = r->ifcall.count;
- if(cnt > messagesize - IOHDRSZ)
- cnt = messagesize - IOHDRSZ;
- if(f->file == &files[Qplayctl]){
- r->ifcall.data[cnt] = '\0';
- if (debug & DbgPlayer)
- fprint(2, "rwrite playctl: %s\n", r->ifcall.data);
- nf = tokenize(r->ifcall.data, fields, 4);
- if (nf == 0){
- r->ofcall.count = r->ifcall.count;
- return nil;
- }
- if (nf == 2)
- i = strtol(fields[1], nil, 0);
- else
- i = playstate.off;
- newstate = playstate;
- if ((cmd = lookup(fields[0], statetxt)) < 0)
- return Ebadctl;
- switch(cmd){
- case Play:
- newstate.cmd = cmd;
- newstate.off = i;
- break;
- case Pause:
- if (playstate.cmd != Play)
- break;
- // fall through
- case Stop:
- newstate.cmd = cmd;
- newstate.off = playstate.off;
- break;
- case Resume:
- if(playstate.cmd == Stop)
- break;
- newstate.cmd = Resume;
- newstate.off = playstate.off;
- break;
- case Skip:
- if (nf == 2)
- i += playstate.off;
- else
- i = playstate.off +1;
- if(i < 0)
- i = 0;
- else if (i >= playlist.nlines)
- i = playlist.nlines - 1;
- newstate.cmd = Play;
- newstate.off = i;
- }
- if (newstate.off >= playlist.nlines){
- newstate.cmd = Stop;
- newstate.off = playlist.nlines;
- }
- if (debug & DbgPlayer)
- fprint(2, "new state %s-%ud\n",
- statetxt[newstate.cmd], newstate.off);
- if (newstate.m != playstate.m)
- sendul(playc, newstate.m);
- f->file->dir.qid.vers++;
- } else if(f->file == &files[Qplayvol]){
- char *subfields[nelem(volume)];
- int v[nelem(volume)];
- r->ifcall.data[cnt] = '\0';
- if (debug & DbgPlayer)
- fprint(2, "rwrite playvol: %s\n", r->ifcall.data);
- nf = tokenize(r->ifcall.data, fields, 4);
- if (nf == 0){
- r->ofcall.count = r->ifcall.count;
- return nil;
- }
- if (nf != 2 || strcmp(fields[0], "volume") != 0)
- return Ebadctl;
- if (debug & DbgPlayer)
- fprint(2, "new volume '");
- nf = tokenize(fields[1], subfields, nelem(subfields));
- if (nf <= 0 || nf > nelem(volume))
- return "volume";
- for (i = 0; i < nf; i++){
- v[i] = strtol(subfields[i], nil, 0);
- if (debug & DbgPlayer)
- fprint(2, " %d", v[i]);
- }
- if (debug & DbgPlayer)
- fprint(2, "'\n");
- while (i < nelem(volume))
- v[i++] = Undef;
- volumeset(v);
- r->ofcall.count = r->ifcall.count;
- return nil;
- } else if(f->file == &files[Qplaylist]){
- if (debug & DbgPlayer){
- fprint(2, "rwrite playlist: `");
- write(2, r->ifcall.data, cnt);
- fprint(2, "'\n");
- }
- playlist.data = realloc(playlist.data, playlist.ndata + cnt + 2);
- if (playlist.data == 0)
- sysfatal("realloc: %r");
- memmove(playlist.data + playlist.ndata, r->ifcall.data, cnt);
- if (playlist.data[playlist.ndata + cnt-1] != '\n')
- playlist.data[playlist.ndata + cnt++] = '\n';
- playlist.data[playlist.ndata + cnt] = '\0';
- p = playlist.data + playlist.ndata;
- while (*p){
- playlist.lines = realloc(playlist.lines, (playlist.nlines+1)*sizeof(playlist.lines[0]));
- if(playlist.lines == nil)
- sysfatal("realloc: %r");
- playlist.lines[playlist.nlines] = playlist.ndata;
- q = strchr(p, '\n');
- if (q == nil)
- break;
- if(debug & DbgPlayer)
- fprint(2, "[%lud]: ", playlist.nlines);
- playlist.nlines++;
- q++;
- if(debug & DbgPlayer)
- write(2, p, q-p);
- playlist.ndata += q - p;
- p = q;
- }
- f->file->dir.length = playlist.ndata;
- f->file->dir.qid.vers++;
- }else
- return Eperm;
- r->ofcall.count = r->ifcall.count;
- m.cmd = Check;
- m.off = 0;
- m.arg = nil;
- bcastmsg(f->file->workers, &m);
- return nil;
- }
- char *
- rclunk(Worker *w)
- {
- w->r->fid->flags &= ~(Open|Busy);
- return 0;
- }
- char *
- rremove(Worker*)
- {
- return Eperm;
- }
- char *
- rstat(Worker *w)
- {
- Req *r;
- r = w->r;
- r->ofcall.nstat = convD2M(&r->fid->file->dir, r->indata, messagesize - IOHDRSZ);
- r->ofcall.stat = r->indata;
- return 0;
- }
- char *
- rwstat(Worker*)
- {
- return Eperm;
- }
- Fid *
- newfid(int fid)
- {
- Fid *f, *ff;
- ff = nil;
- for(f = fids; f; f = f->next)
- if(f->fid == fid){
- return f;
- }else if(ff == nil && (f->flags & Busy) == 0)
- ff = f;
- if(ff == nil){
- ff = malloc(sizeof *ff);
- if (ff == nil)
- sysfatal("malloc: %r");
- memset(ff, 0, sizeof *ff);
- ff->next = fids;
- ff->readers = 0;
- fids = ff;
- }
- ff->fid = fid;
- ff->file = nil;
- ff->vers = ~0;
- return ff;
- }
- void
- work(Worker *w)
- {
- Req *r;
- char *err;
- int n;
- r = w->r;
- r->ofcall.data = (char*)r->indata;
- if(!fcalls[r->ifcall.type])
- err = "bad fcall type";
- else {
- r->fid = newfid(r->ifcall.fid);
- err = (*fcalls[r->ifcall.type])(w);
- }
- if(err != (char*)~0){ /* ~0 indicates Flush received */
- if(err){
- r->ofcall.type = Rerror;
- r->ofcall.ename = err;
- }else{
- r->ofcall.type = r->ifcall.type + 1;
- r->ofcall.fid = r->ifcall.fid;
- }
- r->ofcall.tag = r->ifcall.tag;
- if(debug & DbgFs)
- fprint(2, "io:->%F\n", &r->ofcall);/**/
- n = convS2M(&r->ofcall, r->outdata, messagesize);
- if(write(srvfd[0], r->outdata, n) != n)
- sysfatal("mount write");
- }
- reqfree(r);
- w->r = nil;
- }
- void
- worker(void *arg)
- {
- Worker *w;
- Wmsg m;
- w = arg;
- recv(w->eventc, &m);
- for(;;){
- assert(m.cmd == Work);
- w->r = m.arg;
- if(debug & DbgWorker)
- fprint(2, "worker 0x%p:<-%F\n", w, &w->r->ifcall);
- work(w);
- if(debug & DbgWorker)
- fprint(2, "worker 0x%p wait for next\n", w);
- m = waitmsg(w, workers);
- }
- }
- void
- allocwork(Req *r)
- {
- Worker *w;
- Wmsg m;
- m.cmd = Work;
- m.off = 0;
- m.arg = r;
- if(sendmsg(workers, &m))
- return;
- /* No worker ready to accept request, allocate one */
- w = malloc(sizeof(Worker));
- w->eventc = chancreate(sizeof(Wmsg), 1);
- if(debug & DbgWorker)
- fprint(2, "new worker 0x%p\n", w);/**/
- threadcreate(worker, w, 4096);
- send(w->eventc, &m);
- }
- void
- srvio(void *arg)
- {
- char e[32];
- int n;
- Req *r;
- Channel *dispatchc;
- threadsetname("file server IO");
- dispatchc = arg;
- r = reqalloc();
- for(;;){
- /*
- * reading from a pipe or a network device
- * will give an error after a few eof reads
- * however, we cannot tell the difference
- * between a zero-length read and an interrupt
- * on the processes writing to us,
- * so we wait for the error
- */
- n = read9pmsg(srvfd[0], r->indata, messagesize);
- if(n == 0)
- continue;
- if(n < 0){
- rerrstr(e, sizeof e);
- if (strcmp(e, "interrupted") == 0){
- if (debug & DbgFs) fprint(2, "read9pmsg interrupted\n");
- continue;
- }
- sysfatal("srvio: %s", e);
- }
- if(convM2S(r->indata, n, &r->ifcall) == 0)
- continue;
- if(debug & DbgFs)
- fprint(2, "io:<-%F\n", &r->ifcall);
- sendp(dispatchc, r);
- r = reqalloc();
- }
- }
- char *
- getplaylist(int n)
- {
- Wmsg m;
- m.cmd = Preq;
- m.off = n;
- m.arg = nil;
- send(playlistreq, &m);
- recv(playlistreq, &m);
- if(m.cmd == Error)
- return nil;
- assert(m.cmd == Prep);
- assert(m.arg);
- return m.arg;
- }
- void
- playlistsrv(void*)
- {
- Wmsg m;
- char *p, *q, *r;
- char *fields[2];
- int n;
- /* Runs in the srv proc */
- threadsetname("playlistsrv");
- while(recv(playlistreq, &m)){
- assert(m.cmd == Preq);
- m.cmd = Error;
- if(m.off < playlist.nlines){
- p = playlist.data + playlist.lines[m.off];
- q = strchr(p, '\n');
- if (q == nil)
- sysfatal("playlistsrv: no newline character found");
- n = q-p;
- r = malloc(n+1);
- memmove(r, p, n);
- r[n] = 0;
- tokenize(r, fields, nelem(fields));
- assert(fields[0] == r);
- m.cmd = Prep;
- m.arg = r;
- }
- send(playlistreq, &m);
- }
- }
- void
- srv(void*)
- {
- Req *r;
- Channel *dispatchc;
- /*
- * This is the proc with all the action.
- * When a file request comes in, it is dispatched to this proc
- * for processing. Two extra threads field changes in play state
- * and volume state.
- * By keeping all the action in this proc, we won't need locks
- */
- threadsetname("srv");
- close(srvfd[1]);
- dispatchc = chancreate(sizeof(Req*), 1);
- procrfork(srvio, dispatchc, 4096, RFFDG);
- threadcreate(volumeupdater, nil, 4096);
- threadcreate(playupdater, nil, 4096);
- threadcreate(playlistsrv, nil, 4096);
- while(r = recvp(dispatchc))
- allocwork(r);
-
- }
- void
- playupdater(void*)
- {
- Wmsg m;
- /* This is a thread in the srv proc */
- while(recv(playchan, &m)){
- if(debug & DbgPlayer)
- fprint(2, "playupdate: %s %d %s\n", statetxt[m.cmd], m.off, m.arg?m.arg:"");
- if(playstate.m == m.m)
- continue;
- if(m.cmd == Stop && m.off == 0xffff)
- m.off = playlist.nlines;
- if(m.cmd != Error){
- playstate.m = m.m;
- m.cmd = Check;
- assert(m.arg == nil);
- }
- files[Qplayctl].dir.qid.vers++;
- bcastmsg(files[Qplayctl].workers, &m);
- }
- }
- void
- volumeupdater(void*)
- {
- Wmsg m;
- int v[nelem(volume)];
- /* This is a thread in the srv proc */
- while(recv(volumechan, v)){
- if(debug & DbgPlayer)
- fprint(2, "volumeupdate: volume now %d %d %d %d\n", volume[0], volume[1], volume[2], volume[3]);
- memmove(volume, v, sizeof(volume));
- files[Qplayvol].dir.qid.vers++;
- m.cmd = Check;
- m.arg = nil;
- bcastmsg(files[Qplayvol].workers, &m);
- }
- }
- void
- playupdate(Pmsg p, char *s)
- {
- Wmsg m;
- m.m = p.m;
- m.arg = s ? strdup(s) : nil;
- send(playchan, &m);
- }
|