Subversion Repositories planix.SVN

Rev

Rev 2 | Details | Compare with Previous | Last modification | View Log | RSS feed

Rev Author Line No. Line
2 - 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
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*
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*
128
rauth(Worker*)
129
{
130
	return Enoauth;
131
}
132
 
133
char*
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*
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*
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*
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*
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 *
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 *
299
rcreate(Worker*)
300
{
301
	return Eperm;
302
}
303
 
304
int
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*
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*
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 *
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 *
592
rremove(Worker*)
593
{
594
	return Eperm;
595
}
596
 
597
char *
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 *
612
rwstat(Worker*)
613
{
614
	return Eperm;
615
}
616
 
617
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 *
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
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
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
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
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 *
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
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
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
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
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
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
}