1 #include <u.h>
2 #include <libc.h>
3 #include <thread.h>
4 #include <fcall.h>
5 #include "pool.h"
6 #include "playlist.h"
7
8 typedef struct Wmsg Wmsg;
9
10 enum {
11 Busy = 0x01,
12 Open = 0x02,
13 Trunc = 0x04,
14 Eof = 0x08,
15 };
16
17 File files[] = {
18 [Qdir] = {.dir = {0,0,{Qdir, 0,QTDIR}, 0555|DMDIR, 0,0,0, "."}},
19 [Qplayctl] = {.dir = {0,0,{Qplayctl, 0,QTFILE}, 0666, 0,0,0, "playctl"}},
20 [Qplaylist] = {.dir = {0,0,{Qplaylist, 0,QTFILE}, 0666|DMAPPEND, 0,0,0, "playlist"}},
21 [Qplayvol] = {.dir = {0,0,{Qplayvol, 0,QTFILE}, 0666, 0,0,0, "playvol"}},
22 [Qplaystat] = {.dir = {0,0,{Qplaystat, 0,QTFILE}, 0444, 0,0,0, "playstat"}},
23 };
24
25 Channel *reqs;
26 Channel *workers;
27 Channel *volumechan;
28 Channel *playchan;
29 Channel *playlistreq;
30 Playlist playlist;
31 int volume[8];
32
33 char *statetxt[] = {
34 [Nostate] = "panic!",
35 [Error] = "error",
36 [Stop] = "stop",
37 [Pause] = "pause",
38 [Play] = "play",
39 [Resume] = "resume",
40 [Skip] = "skip",
41 nil
42 };
43
44 // low-order bits: position in play list, high-order: play state:
45 Pmsg playstate = {Stop, 0};
46
47 char *rflush(Worker*), *rauth(Worker*),
48 *rattach(Worker*), *rwalk(Worker*),
49 *ropen(Worker*), *rcreate(Worker*),
50 *rread(Worker*), *rwrite(Worker*), *rclunk(Worker*),
51 *rremove(Worker*), *rstat(Worker*), *rwstat(Worker*),
52 *rversion(Worker*);
53
54 char *(*fcalls[])(Worker*) = {
55 [Tflush] rflush,
56 [Tversion] rversion,
57 [Tauth] rauth,
58 [Tattach] rattach,
59 [Twalk] rwalk,
60 [Topen] ropen,
61 [Tcreate] rcreate,
62 [Tread] rread,
63 [Twrite] rwrite,
64 [Tclunk] rclunk,
65 [Tremove] rremove,
66 [Tstat] rstat,
67 [Twstat] rwstat,
68 };
69
70 int messagesize = Messagesize;
71 Fid *fids;
72
73
74 char Eperm[] = "permission denied";
75 char Enotdir[] = "not a directory";
76 char Enoauth[] = "authentication not required";
77 char Enotexist[] = "file does not exist";
78 char Einuse[] = "file in use";
79 char Eexist[] = "file exists";
80 char Enotowner[] = "not owner";
81 char Eisopen[] = "file already open for I/O";
82 char Excl[] = "exclusive use file already open";
83 char Ename[] = "illegal name";
84 char Ebadctl[] = "unknown control message";
85 char Epast[] = "reading past eof";
86
87 Fid *oldfid(int);
88 Fid *newfid(int);
89 void volumeupdater(void*);
90 void playupdater(void*);
91
92 char *playerror;
93
94 static int
lookup(char * cmd,char * list[])95 lookup(char *cmd, char *list[])
96 {
97 int i;
98
99 for (i = 0; list[i] != nil; i++)
100 if (strcmp(cmd, list[i]) == 0)
101 return i;
102 return -1;
103 }
104
105 char*
rversion(Worker * w)106 rversion(Worker *w)
107 {
108 Req *r;
109 Fid *f;
110
111 r = w->r;
112 if(r->ifcall.msize < 256)
113 return "max messagesize too small";
114 if(r->ifcall.msize < messagesize)
115 messagesize = r->ifcall.msize;
116 r->ofcall.msize = messagesize;
117 if(strncmp(r->ifcall.version, "9P2000", 6) != 0)
118 return "unknown 9P version";
119 else
120 r->ofcall.version = "9P2000";
121 for(f = fids; f; f = f->next)
122 if(f->flags & Busy)
123 f->flags &= ~(Open|Busy);
124 return nil;
125 }
126
127 char*
rauth(Worker *)128 rauth(Worker*)
129 {
130 return Enoauth;
131 }
132
133 char*
rflush(Worker * w)134 rflush(Worker *w)
135 {
136 Wmsg m;
137 int i;
138 Req *r;
139
140 r = w->r;
141 m.cmd = Flush;
142 m.off = r->ifcall.oldtag;
143 m.arg = nil;
144 for(i = 1; i < nelem(files); i++)
145 bcastmsg(files[i].workers, &m);
146 if (debug & DbgWorker)
147 fprint(2, "flush done on tag %d\n", r->ifcall.oldtag);
148 return 0;
149 }
150
151 char*
rattach(Worker * w)152 rattach(Worker *w)
153 {
154 Fid *f;
155 Req *r;
156
157 r = w->r;
158 r->fid = newfid(r->ifcall.fid);
159 f = r->fid;
160 f->flags |= Busy;
161 f->file = &files[Qdir];
162 r->ofcall.qid = f->file->dir.qid;
163 if(!aflag && strcmp(r->ifcall.uname, user) != 0)
164 return Eperm;
165 return 0;
166 }
167
168 static Fid*
doclone(Fid * f,int nfid)169 doclone(Fid *f, int nfid)
170 {
171 Fid *nf;
172
173 nf = newfid(nfid);
174 if(nf->flags & Busy)
175 return nil;
176 nf->flags |= Busy;
177 nf->flags &= ~(Open);
178 nf->file = f->file;
179 return nf;
180 }
181
182 char*
dowalk(Fid * f,char * name)183 dowalk(Fid *f, char *name)
184 {
185 int t;
186
187 if (strcmp(name, ".") == 0)
188 return nil;
189 if (strcmp(name, "..") == 0){
190 f->file = &files[Qdir];
191 return nil;
192 }
193 if(f->file != &files[Qdir])
194 return Enotexist;
195 for (t = 1; t < Nqid; t++){
196 if(strcmp(name, files[t].dir.name) == 0){
197 f->file = &files[t];
198 return nil;
199 }
200 }
201 return Enotexist;
202 }
203
204 char*
rwalk(Worker * w)205 rwalk(Worker *w)
206 {
207 Fid *f, *nf;
208 char *rv;
209 int i;
210 File *savefile;
211 Req *r;
212
213 r = w->r;
214 r->fid = oldfid(r->ifcall.fid);
215 if((f = r->fid) == nil)
216 return Enotexist;
217 if(f->flags & Open)
218 return Eisopen;
219
220 r->ofcall.nwqid = 0;
221 nf = nil;
222 savefile = f->file;
223 /* clone if requested */
224 if(r->ifcall.newfid != r->ifcall.fid){
225 nf = doclone(f, r->ifcall.newfid);
226 if(nf == nil)
227 return "new fid in use";
228 f = nf;
229 }
230
231 /* if it's just a clone, return */
232 if(r->ifcall.nwname == 0 && nf != nil)
233 return nil;
234
235 /* walk each element */
236 rv = nil;
237 for(i = 0; i < r->ifcall.nwname; i++){
238 rv = dowalk(f, r->ifcall.wname[i]);
239 if(rv != nil){
240 if(nf != nil)
241 nf->flags &= ~(Open|Busy);
242 else
243 f->file = savefile;
244 break;
245 }
246 r->ofcall.wqid[i] = f->file->dir.qid;
247 }
248 r->ofcall.nwqid = i;
249
250 /* we only error out if no walk */
251 if(i > 0)
252 rv = nil;
253
254 return rv;
255 }
256
257 char *
ropen(Worker * w)258 ropen(Worker *w)
259 {
260 Fid *f, *ff;
261 Wmsg m;
262 Req *r;
263
264 r = w->r;
265 r->fid = oldfid(r->ifcall.fid);
266 if((f = r->fid) == nil)
267 return Enotexist;
268 if(f->flags & Open)
269 return Eisopen;
270
271 if(r->ifcall.mode != OREAD)
272 if((f->file->dir.mode & 0x2) == 0)
273 return Eperm;
274 if((r->ifcall.mode & OTRUNC) && f->file == &files[Qplaylist]){
275 playlist.nlines = 0;
276 playlist.ndata = 0;
277 free(playlist.lines);
278 free(playlist.data);
279 playlist.lines = nil;
280 playlist.data = nil;
281 f->file->dir.length = 0;
282 f->file->dir.qid.vers++;
283 /* Mark all fids for this file `Trunc'ed */
284 for(ff = fids; ff; ff = ff->next)
285 if(ff->file == &files[Qplaylist] && (ff->flags & Open))
286 ff->flags |= Trunc;
287 m.cmd = Check;
288 m.off = 0;
289 m.arg = nil;
290 bcastmsg(f->file->workers, &m);
291 }
292 r->ofcall.iounit = 0;
293 r->ofcall.qid = f->file->dir.qid;
294 f->flags |= Open;
295 return nil;
296 }
297
298 char *
rcreate(Worker *)299 rcreate(Worker*)
300 {
301 return Eperm;
302 }
303
304 int
readtopdir(Fid *,uchar * buf,long off,int cnt,int blen)305 readtopdir(Fid*, uchar *buf, long off, int cnt, int blen)
306 {
307 int i, m, n;
308 long pos;
309
310 n = 0;
311 pos = 0;
312 for (i = 1; i < Nqid; i++){
313 m = convD2M(&files[i].dir, &buf[n], blen-n);
314 if(off <= pos){
315 if(m <= BIT16SZ || m > cnt)
316 break;
317 n += m;
318 cnt -= m;
319 }
320 pos += m;
321 }
322 return n;
323 }
324
325 char*
rread(Worker * w)326 rread(Worker *w)
327 {
328 Fid *f;
329 Req *r;
330 long off, cnt;
331 int n, i;
332 Wmsg m;
333 char *p;
334
335 r = w->r;
336 r->fid = oldfid(r->ifcall.fid);
337 if((f = r->fid) == nil)
338 return Enotexist;
339 r->ofcall.count = 0;
340 off = r->ifcall.offset;
341 cnt = r->ifcall.count;
342
343 if(cnt > messagesize - IOHDRSZ)
344 cnt = messagesize - IOHDRSZ;
345
346 if(f->file == &files[Qdir]){
347 n = readtopdir(f, r->indata, off, cnt, messagesize - IOHDRSZ);
348 r->ofcall.count = n;
349 return nil;
350 }
351
352 if(f->file == files + Qplaystat){
353 p = getplaystat(r->ofcall.data, r->ofcall.data + sizeof r->indata);
354 readbuf(r, r->ofcall.data, p - r->ofcall.data);
355 return nil;
356 }
357
358 m.cmd = 0;
359 while(f->vers == f->file->dir.qid.vers && (f->flags & Eof)){
360 /* Wait until file state changes (f->file->dir.qid.vers is incremented) */
361 m = waitmsg(w, f->file->workers);
362 if(m.cmd == Flush && m.off == r->ifcall.tag)
363 return (char*)~0; /* no answer needed */
364 assert(m.cmd != Work);
365 yield();
366 }
367 if(f->file == files + Qplaylist){
368 f->flags &= ~Eof;
369 if((f->flags & Trunc) && r->ifcall.offset != 0){
370 f->flags &= ~Trunc;
371 return Epast;
372 }
373 f->flags &= ~Trunc;
374 if(r->ifcall.offset < playlist.ndata)
375 readbuf(r, playlist.data, playlist.ndata);
376 else if(r->ifcall.offset == playlist.ndata){
377 r->ofcall.count = 0;
378 /* Arrange for this fid to wait next time: */
379 f->vers = f->file->dir.qid.vers;
380 f->flags |= Eof;
381 }else{
382 /* Beyond eof, bad seek? */
383 return Epast;
384 }
385 }else if(f->file == files + Qplayctl){
386 f->flags &= ~Eof;
387 if(m.cmd == Error){
388 snprint(r->ofcall.data, sizeof r->indata, "%s %ud %q",
389 statetxt[m.cmd], m.off, m.arg);
390 free(m.arg);
391 }else if(f->vers == f->file->dir.qid.vers){
392 r->ofcall.count = 0;
393 /* Arrange for this fid to wait next time: */
394 f->flags |= Eof;
395 return nil;
396 }else{
397 snprint(r->ofcall.data, sizeof r->indata, "%s %ud",
398 statetxt[playstate.cmd], playstate.off);
399 f->vers = f->file->dir.qid.vers;
400 }
401 r->ofcall.count = strlen(r->ofcall.data);
402 if(r->ofcall.count > r->ifcall.count)
403 r->ofcall.count = r->ifcall.count;
404 }else if(f->file == files + Qplayvol){
405 f->flags &= ~Eof;
406 if(f->vers == f->file->dir.qid.vers){
407 r->ofcall.count = 0;
408 /* Arrange for this fid to wait next time: */
409 f->flags |= Eof;
410 }else{
411 p = seprint(r->ofcall.data, r->ofcall.data + sizeof r->indata, "volume '");
412 for(i = 0; i < nelem(volume); i++){
413 if(volume[i] == Undef)
414 break;
415 p = seprint(p, r->ofcall.data + sizeof r->indata, "%d ", volume[i]);
416 }
417 p = seprint(p, r->ofcall.data + sizeof r->indata, "'");
418 r->ofcall.count = p - r->ofcall.data;
419 if(r->ofcall.count > r->ifcall.count)
420 r->ofcall.count = r->ifcall.count;
421 f->vers = f->file->dir.qid.vers;
422 }
423 }else
424 abort();
425 return nil;
426 }
427
428 char*
rwrite(Worker * w)429 rwrite(Worker *w)
430 {
431 long cnt, i, nf, cmd;
432 Pmsg newstate;
433 char *fields[3], *p, *q;
434 Wmsg m;
435 Fid *f;
436 Req *r;
437
438 r = w->r;
439 r->fid = oldfid(r->ifcall.fid);
440 if((f = r->fid) == nil)
441 return Enotexist;
442 r->ofcall.count = 0;
443 cnt = r->ifcall.count;
444
445 if(cnt > messagesize - IOHDRSZ)
446 cnt = messagesize - IOHDRSZ;
447
448 if(f->file == &files[Qplayctl]){
449 r->ifcall.data[cnt] = '\0';
450 if (debug & DbgPlayer)
451 fprint(2, "rwrite playctl: %s\n", r->ifcall.data);
452 nf = tokenize(r->ifcall.data, fields, 4);
453 if (nf == 0){
454 r->ofcall.count = r->ifcall.count;
455 return nil;
456 }
457 if (nf == 2)
458 i = strtol(fields[1], nil, 0);
459 else
460 i = playstate.off;
461 newstate = playstate;
462 if ((cmd = lookup(fields[0], statetxt)) < 0)
463 return Ebadctl;
464 switch(cmd){
465 case Play:
466 newstate.cmd = cmd;
467 newstate.off = i;
468 break;
469 case Pause:
470 if (playstate.cmd != Play)
471 break;
472 // fall through
473 case Stop:
474 newstate.cmd = cmd;
475 newstate.off = playstate.off;
476 break;
477 case Resume:
478 if(playstate.cmd == Stop)
479 break;
480 newstate.cmd = Resume;
481 newstate.off = playstate.off;
482 break;
483 case Skip:
484 if (nf == 2)
485 i += playstate.off;
486 else
487 i = playstate.off +1;
488 if(i < 0)
489 i = 0;
490 else if (i >= playlist.nlines)
491 i = playlist.nlines - 1;
492 newstate.cmd = Play;
493 newstate.off = i;
494 }
495 if (newstate.off >= playlist.nlines){
496 newstate.cmd = Stop;
497 newstate.off = playlist.nlines;
498 }
499 if (debug & DbgPlayer)
500 fprint(2, "new state %s-%ud\n",
501 statetxt[newstate.cmd], newstate.off);
502 if (newstate.m != playstate.m)
503 sendul(playc, newstate.m);
504 f->file->dir.qid.vers++;
505 } else if(f->file == &files[Qplayvol]){
506 char *subfields[nelem(volume)];
507 int v[nelem(volume)];
508
509 r->ifcall.data[cnt] = '\0';
510 if (debug & DbgPlayer)
511 fprint(2, "rwrite playvol: %s\n", r->ifcall.data);
512 nf = tokenize(r->ifcall.data, fields, 4);
513 if (nf == 0){
514 r->ofcall.count = r->ifcall.count;
515 return nil;
516 }
517 if (nf != 2 || strcmp(fields[0], "volume") != 0)
518 return Ebadctl;
519 if (debug & DbgPlayer)
520 fprint(2, "new volume '");
521 nf = tokenize(fields[1], subfields, nelem(subfields));
522 if (nf <= 0 || nf > nelem(volume))
523 return "volume";
524 for (i = 0; i < nf; i++){
525 v[i] = strtol(subfields[i], nil, 0);
526 if (debug & DbgPlayer)
527 fprint(2, " %d", v[i]);
528 }
529 if (debug & DbgPlayer)
530 fprint(2, "'\n");
531 while (i < nelem(volume))
532 v[i++] = Undef;
533 volumeset(v);
534 r->ofcall.count = r->ifcall.count;
535 return nil;
536 } else if(f->file == &files[Qplaylist]){
537 if (debug & DbgPlayer){
538 fprint(2, "rwrite playlist: `");
539 write(2, r->ifcall.data, cnt);
540 fprint(2, "'\n");
541 }
542 playlist.data = realloc(playlist.data, playlist.ndata + cnt + 2);
543 if (playlist.data == 0)
544 sysfatal("realloc: %r");
545 memmove(playlist.data + playlist.ndata, r->ifcall.data, cnt);
546 if (playlist.data[playlist.ndata + cnt-1] != '\n')
547 playlist.data[playlist.ndata + cnt++] = '\n';
548 playlist.data[playlist.ndata + cnt] = '\0';
549 p = playlist.data + playlist.ndata;
550 while (*p){
551 playlist.lines = realloc(playlist.lines, (playlist.nlines+1)*sizeof(playlist.lines[0]));
552 if(playlist.lines == nil)
553 sysfatal("realloc: %r");
554 playlist.lines[playlist.nlines] = playlist.ndata;
555 q = strchr(p, '\n');
556 if (q == nil)
557 break;
558 if(debug & DbgPlayer)
559 fprint(2, "[%lud]: ", playlist.nlines);
560 playlist.nlines++;
561 q++;
562 if(debug & DbgPlayer)
563 write(2, p, q-p);
564 playlist.ndata += q - p;
565 p = q;
566 }
567 f->file->dir.length = playlist.ndata;
568 f->file->dir.qid.vers++;
569 }else
570 return Eperm;
571 r->ofcall.count = r->ifcall.count;
572 m.cmd = Check;
573 m.off = 0;
574 m.arg = nil;
575 bcastmsg(f->file->workers, &m);
576 return nil;
577 }
578
579 char *
rclunk(Worker * w)580 rclunk(Worker *w)
581 {
582 Fid *f;
583
584 f = oldfid(w->r->ifcall.fid);
585 if(f == nil)
586 return Enotexist;
587 f->flags &= ~(Open|Busy);
588 return 0;
589 }
590
591 char *
rremove(Worker *)592 rremove(Worker*)
593 {
594 return Eperm;
595 }
596
597 char *
rstat(Worker * w)598 rstat(Worker *w)
599 {
600 Req *r;
601
602 r = w->r;
603 r->fid = oldfid(r->ifcall.fid);
604 if(r->fid == nil)
605 return Enotexist;
606 r->ofcall.nstat = convD2M(&r->fid->file->dir, r->indata, messagesize - IOHDRSZ);
607 r->ofcall.stat = r->indata;
608 return 0;
609 }
610
611 char *
rwstat(Worker *)612 rwstat(Worker*)
613 {
614 return Eperm;
615 }
616
617 Fid *
oldfid(int fid)618 oldfid(int fid)
619 {
620 Fid *f;
621
622 for(f = fids; f; f = f->next)
623 if(f->fid == fid)
624 return f;
625 return nil;
626 }
627
628 Fid *
newfid(int fid)629 newfid(int fid)
630 {
631 Fid *f, *ff;
632
633 ff = nil;
634 for(f = fids; f; f = f->next)
635 if(f->fid == fid){
636 return f;
637 }else if(ff == nil && (f->flags & Busy) == 0)
638 ff = f;
639 if(ff == nil){
640 ff = mallocz(sizeof *ff, 1);
641 if (ff == nil)
642 sysfatal("malloc: %r");
643 ff->next = fids;
644 ff->readers = 0;
645 fids = ff;
646 }
647 ff->fid = fid;
648 ff->file = nil;
649 ff->vers = ~0;
650 return ff;
651 }
652
653 void
work(Worker * w)654 work(Worker *w)
655 {
656 Req *r;
657 char *err;
658 int n;
659
660 r = w->r;
661 r->ofcall.data = (char*)r->indata;
662 if(!fcalls[r->ifcall.type])
663 err = "bad fcall type";
664 else
665 err = (*fcalls[r->ifcall.type])(w);
666 if(err != (char*)~0){ /* ~0 indicates Flush received */
667 if(err){
668 r->ofcall.type = Rerror;
669 r->ofcall.ename = err;
670 }else{
671 r->ofcall.type = r->ifcall.type + 1;
672 r->ofcall.fid = r->ifcall.fid;
673 }
674 r->ofcall.tag = r->ifcall.tag;
675 if(debug & DbgFs)
676 fprint(2, "io:->%F\n", &r->ofcall);/**/
677 n = convS2M(&r->ofcall, r->outdata, messagesize);
678 if(write(srvfd[0], r->outdata, n) != n)
679 sysfatal("mount write");
680 }
681 reqfree(r);
682 w->r = nil;
683 }
684
685 void
worker(void * arg)686 worker(void *arg)
687 {
688 Worker *w;
689 Wmsg m;
690
691 w = arg;
692 recv(w->eventc, &m);
693 for(;;){
694 assert(m.cmd == Work);
695 w->r = m.arg;
696 if(debug & DbgWorker)
697 fprint(2, "worker 0x%p:<-%F\n", w, &w->r->ifcall);
698 work(w);
699 if(debug & DbgWorker)
700 fprint(2, "worker 0x%p wait for next\n", w);
701 m = waitmsg(w, workers);
702 }
703 }
704
705 void
allocwork(Req * r)706 allocwork(Req *r)
707 {
708 Worker *w;
709 Wmsg m;
710
711 m.cmd = Work;
712 m.off = 0;
713 m.arg = r;
714 if(sendmsg(workers, &m))
715 return;
716 /* No worker ready to accept request, allocate one */
717 w = malloc(sizeof(Worker));
718 w->eventc = chancreate(sizeof(Wmsg), 1);
719 if(debug & DbgWorker)
720 fprint(2, "new worker 0x%p\n", w);/**/
721 threadcreate(worker, w, STACKSIZE);
722 send(w->eventc, &m);
723 }
724
725 void
srvio(void * arg)726 srvio(void *arg)
727 {
728 char e[32];
729 int n;
730 Req *r;
731 Channel *dispatchc;
732
733 threadsetname("file server IO");
734 dispatchc = arg;
735
736 r = reqalloc();
737 for(;;){
738 /*
739 * reading from a pipe or a network device
740 * will give an error after a few eof reads
741 * however, we cannot tell the difference
742 * between a zero-length read and an interrupt
743 * on the processes writing to us,
744 * so we wait for the error
745 */
746 n = read9pmsg(srvfd[0], r->indata, messagesize);
747 if(n == 0)
748 continue;
749 if(n < 0){
750 rerrstr(e, sizeof e);
751 if (strcmp(e, "interrupted") == 0){
752 if (debug & DbgFs) fprint(2, "read9pmsg interrupted\n");
753 continue;
754 }
755 sysfatal("srvio: %s", e);
756 }
757 if(convM2S(r->indata, n, &r->ifcall) == 0)
758 continue;
759
760 if(debug & DbgFs)
761 fprint(2, "io:<-%F\n", &r->ifcall);
762 sendp(dispatchc, r);
763 r = reqalloc();
764 }
765 }
766
767 char *
getplaylist(int n)768 getplaylist(int n)
769 {
770 Wmsg m;
771
772 m.cmd = Preq;
773 m.off = n;
774 m.arg = nil;
775 send(playlistreq, &m);
776 recv(playlistreq, &m);
777 if(m.cmd == Error)
778 return nil;
779 assert(m.cmd == Prep);
780 assert(m.arg);
781 return m.arg;
782 }
783
784 void
playlistsrv(void *)785 playlistsrv(void*)
786 {
787 Wmsg m;
788 char *p, *q, *r;
789 char *fields[2];
790 int n;
791 /* Runs in the srv proc */
792
793 threadsetname("playlistsrv");
794 while(recv(playlistreq, &m)){
795 assert(m.cmd == Preq);
796 m.cmd = Error;
797 if(m.off < playlist.nlines){
798 p = playlist.data + playlist.lines[m.off];
799 q = strchr(p, '\n');
800 if (q == nil)
801 sysfatal("playlistsrv: no newline character found");
802 n = q-p;
803 r = malloc(n+1);
804 memmove(r, p, n);
805 r[n] = 0;
806 tokenize(r, fields, nelem(fields));
807 assert(fields[0] == r);
808 m.cmd = Prep;
809 m.arg = r;
810 }
811 send(playlistreq, &m);
812 }
813 }
814
815 void
srv(void *)816 srv(void*)
817 {
818 Req *r;
819 Channel *dispatchc;
820 /*
821 * This is the proc with all the action.
822 * When a file request comes in, it is dispatched to this proc
823 * for processing. Two extra threads field changes in play state
824 * and volume state.
825 * By keeping all the action in this proc, we won't need locks
826 */
827
828 threadsetname("srv");
829 close(srvfd[1]);
830
831 dispatchc = chancreate(sizeof(Req*), 1);
832 procrfork(srvio, dispatchc, STACKSIZE, RFFDG);
833
834 threadcreate(volumeupdater, nil, STACKSIZE);
835 threadcreate(playupdater, nil, STACKSIZE);
836 threadcreate(playlistsrv, nil, STACKSIZE);
837
838 while(r = recvp(dispatchc))
839 allocwork(r);
840
841 }
842
843 void
playupdater(void *)844 playupdater(void*)
845 {
846 Wmsg m;
847 /* This is a thread in the srv proc */
848
849 while(recv(playchan, &m)){
850 if(debug & DbgPlayer)
851 fprint(2, "playupdate: %s %d %s\n", statetxt[m.cmd], m.off, m.arg?m.arg:"");
852 if(playstate.m == m.m)
853 continue;
854 if(m.cmd == Stop && m.off == 0xffff)
855 m.off = playlist.nlines;
856 if(m.cmd != Error){
857 playstate.m = m.m;
858 m.cmd = Check;
859 assert(m.arg == nil);
860 }
861 files[Qplayctl].dir.qid.vers++;
862 bcastmsg(files[Qplayctl].workers, &m);
863 }
864 }
865
866 void
volumeupdater(void *)867 volumeupdater(void*)
868 {
869 Wmsg m;
870 int v[nelem(volume)];
871 /* This is a thread in the srv proc */
872
873 while(recv(volumechan, v)){
874 if(debug & DbgPlayer)
875 fprint(2, "volumeupdate: volume now %d %d %d %d\n", volume[0], volume[1], volume[2], volume[3]);
876 memmove(volume, v, sizeof(volume));
877 files[Qplayvol].dir.qid.vers++;
878 m.cmd = Check;
879 m.arg = nil;
880 bcastmsg(files[Qplayvol].workers, &m);
881 }
882 }
883
884 void
playupdate(Pmsg p,char * s)885 playupdate(Pmsg p, char *s)
886 {
887 Wmsg m;
888
889 m.m = p.m;
890 m.arg = s ? strdup(s) : nil;
891 send(playchan, &m);
892 }
893