xref: /plan9/sys/src/games/music/playlistfs/fs.c (revision 6ca6a3e703ee2ec4aed99c2177f71d7f127da6d9)
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