1 #include "dat.h" 2 #include "fns.h" 3 #include "error.h" 4 #include <interp.h> 5 6 #define NETTYPE(x) ((ulong)(x)&0x1f) 7 #define NETID(x) (((ulong)(x))>>5) 8 #define NETQID(i,t) (((i)<<5)|(t)) 9 10 typedef struct Pipe Pipe; 11 struct Pipe 12 { 13 QLock l; 14 Pipe* next; 15 int ref; 16 ulong path; 17 Queue* q[2]; 18 int qref[2]; 19 Dirtab* pipedir; 20 char* user; 21 }; 22 23 static struct 24 { 25 Lock l; 26 ulong path; 27 int pipeqsize; 28 } pipealloc; 29 30 enum 31 { 32 Qdir, 33 Qdata0, 34 Qdata1 35 }; 36 37 Dirtab pipedir[] = 38 { 39 ".", {Qdir,0,QTDIR}, 0, DMDIR|0500, 40 "data", {Qdata0}, 0, 0660, 41 "data1", {Qdata1}, 0, 0660, 42 }; 43 44 static void 45 freepipe(Pipe *p) 46 { 47 if(p != nil){ 48 free(p->user); 49 free(p->q[0]); 50 free(p->q[1]); 51 free(p->pipedir); 52 free(p); 53 } 54 } 55 56 static void 57 pipeinit(void) 58 { 59 pipealloc.pipeqsize = 32*1024; 60 } 61 62 /* 63 * create a pipe, no streams are created until an open 64 */ 65 static Chan* 66 pipeattach(char *spec) 67 { 68 Pipe *p; 69 Chan *c; 70 71 c = devattach('|', spec); 72 p = malloc(sizeof(Pipe)); 73 if(p == 0) 74 error(Enomem); 75 if(waserror()){ 76 freepipe(p); 77 nexterror(); 78 } 79 p->pipedir = malloc(sizeof(pipedir)); 80 if (p->pipedir == 0) 81 error(Enomem); 82 memmove(p->pipedir, pipedir, sizeof(pipedir)); 83 kstrdup(&p->user, up->env->user); 84 p->ref = 1; 85 86 p->q[0] = qopen(pipealloc.pipeqsize, 0, 0, 0); 87 if(p->q[0] == 0) 88 error(Enomem); 89 p->q[1] = qopen(pipealloc.pipeqsize, 0, 0, 0); 90 if(p->q[1] == 0) 91 error(Enomem); 92 poperror(); 93 94 lock(&pipealloc.l); 95 p->path = ++pipealloc.path; 96 unlock(&pipealloc.l); 97 98 c->qid.path = NETQID(2*p->path, Qdir); 99 c->qid.vers = 0; 100 c->qid.type = QTDIR; 101 c->aux = p; 102 c->dev = 0; 103 return c; 104 } 105 106 static int 107 pipegen(Chan *c, char *name, Dirtab *tab, int ntab, int i, Dir *dp) 108 { 109 int id, len; 110 Qid qid; 111 Pipe *p; 112 113 USED(name); 114 if(i == DEVDOTDOT){ 115 devdir(c, c->qid, "#|", 0, eve, 0555, dp); 116 return 1; 117 } 118 i++; /* skip . */ 119 if(tab==0 || i>=ntab) 120 return -1; 121 tab += i; 122 p = c->aux; 123 switch(NETTYPE(tab->qid.path)){ 124 case Qdata0: 125 len = qlen(p->q[0]); 126 break; 127 case Qdata1: 128 len = qlen(p->q[1]); 129 break; 130 default: 131 len = tab->length; 132 break; 133 } 134 id = NETID(c->qid.path); 135 qid.path = NETQID(id, tab->qid.path); 136 qid.vers = 0; 137 qid.type = QTFILE; 138 devdir(c, qid, tab->name, len, eve, tab->perm, dp); 139 return 1; 140 } 141 142 static Walkqid* 143 pipewalk(Chan *c, Chan *nc, char **name, int nname) 144 { 145 Walkqid *wq; 146 Pipe *p; 147 148 p = c->aux; 149 wq = devwalk(c, nc, name, nname, p->pipedir, nelem(pipedir), pipegen); 150 if(wq != nil && wq->clone != nil && wq->clone != c){ 151 qlock(&p->l); 152 p->ref++; 153 if(c->flag & COPEN){ 154 switch(NETTYPE(c->qid.path)){ 155 case Qdata0: 156 p->qref[0]++; 157 break; 158 case Qdata1: 159 p->qref[1]++; 160 break; 161 } 162 } 163 qunlock(&p->l); 164 } 165 return wq; 166 } 167 168 static int 169 pipestat(Chan *c, uchar *db, int n) 170 { 171 Pipe *p; 172 Dir dir; 173 Dirtab *tab; 174 175 p = c->aux; 176 tab = p->pipedir; 177 178 switch(NETTYPE(c->qid.path)){ 179 case Qdir: 180 devdir(c, c->qid, ".", 0, eve, DMDIR|0555, &dir); 181 break; 182 case Qdata0: 183 devdir(c, c->qid, tab[1].name, qlen(p->q[0]), eve, tab[1].perm, &dir); 184 break; 185 case Qdata1: 186 devdir(c, c->qid, tab[2].name, qlen(p->q[1]), eve, tab[2].perm, &dir); 187 break; 188 default: 189 panic("pipestat"); 190 } 191 n = convD2M(&dir, db, n); 192 if(n < BIT16SZ) 193 error(Eshortstat); 194 return n; 195 } 196 197 /* 198 * if the stream doesn't exist, create it 199 */ 200 static Chan* 201 pipeopen(Chan *c, int omode) 202 { 203 Pipe *p; 204 205 if(c->qid.type & QTDIR){ 206 if(omode != OREAD) 207 error(Ebadarg); 208 c->mode = omode; 209 c->flag |= COPEN; 210 c->offset = 0; 211 return c; 212 } 213 214 openmode(omode); /* check it */ 215 216 p = c->aux; 217 qlock(&p->l); 218 if(waserror()){ 219 qunlock(&p->l); 220 nexterror(); 221 } 222 switch(NETTYPE(c->qid.path)){ 223 case Qdata0: 224 devpermcheck(p->user, p->pipedir[1].perm, omode); 225 p->qref[0]++; 226 break; 227 case Qdata1: 228 devpermcheck(p->user, p->pipedir[2].perm, omode); 229 p->qref[1]++; 230 break; 231 } 232 poperror(); 233 qunlock(&p->l); 234 235 c->mode = openmode(omode); 236 c->flag |= COPEN; 237 c->offset = 0; 238 c->iounit = qiomaxatomic; 239 return c; 240 } 241 242 static void 243 pipeclose(Chan *c) 244 { 245 Pipe *p; 246 247 p = c->aux; 248 qlock(&p->l); 249 250 if(c->flag & COPEN){ 251 /* 252 * closing either side hangs up the stream 253 */ 254 switch(NETTYPE(c->qid.path)){ 255 case Qdata0: 256 p->qref[0]--; 257 if(p->qref[0] == 0){ 258 qhangup(p->q[1], 0); 259 qclose(p->q[0]); 260 } 261 break; 262 case Qdata1: 263 p->qref[1]--; 264 if(p->qref[1] == 0){ 265 qhangup(p->q[0], 0); 266 qclose(p->q[1]); 267 } 268 break; 269 } 270 } 271 272 273 /* 274 * if both sides are closed, they are reusable 275 */ 276 if(p->qref[0] == 0 && p->qref[1] == 0){ 277 qreopen(p->q[0]); 278 qreopen(p->q[1]); 279 } 280 281 /* 282 * free the structure on last close 283 */ 284 p->ref--; 285 if(p->ref == 0){ 286 qunlock(&p->l); 287 freepipe(p); 288 } else 289 qunlock(&p->l); 290 } 291 292 static long 293 piperead(Chan *c, void *va, long n, vlong junk) 294 { 295 Pipe *p; 296 297 p = c->aux; 298 299 USED(junk); 300 switch(NETTYPE(c->qid.path)){ 301 case Qdir: 302 return devdirread(c, va, n, p->pipedir, nelem(pipedir), pipegen); 303 case Qdata0: 304 return qread(p->q[0], va, n); 305 case Qdata1: 306 return qread(p->q[1], va, n); 307 default: 308 panic("piperead"); 309 } 310 return -1; /* not reached */ 311 } 312 313 static Block* 314 pipebread(Chan *c, long n, ulong offset) 315 { 316 Pipe *p; 317 318 p = c->aux; 319 320 switch(NETTYPE(c->qid.path)){ 321 case Qdata0: 322 return qbread(p->q[0], n); 323 case Qdata1: 324 return qbread(p->q[1], n); 325 } 326 327 return devbread(c, n, offset); 328 } 329 330 /* 331 * a write to a closed pipe causes an exception to be sent to 332 * the prog. 333 */ 334 static long 335 pipewrite(Chan *c, void *va, long n, vlong junk) 336 { 337 Pipe *p; 338 Prog *r; 339 340 USED(junk); 341 if(waserror()) { 342 /* avoid exceptions when pipe is a mounted queue */ 343 if((c->flag & CMSG) == 0) { 344 r = up->iprog; 345 if(r != nil && r->kill == nil) 346 r->kill = "write on closed pipe"; 347 } 348 nexterror(); 349 } 350 351 p = c->aux; 352 353 switch(NETTYPE(c->qid.path)){ 354 case Qdata0: 355 n = qwrite(p->q[1], va, n); 356 break; 357 358 case Qdata1: 359 n = qwrite(p->q[0], va, n); 360 break; 361 362 default: 363 panic("pipewrite"); 364 } 365 366 poperror(); 367 return n; 368 } 369 370 static long 371 pipebwrite(Chan *c, Block *bp, ulong junk) 372 { 373 long n; 374 Pipe *p; 375 Prog *r; 376 377 USED(junk); 378 if(waserror()) { 379 /* avoid exceptions when pipe is a mounted queue */ 380 if((c->flag & CMSG) == 0) { 381 r = up->iprog; 382 if(r != nil && r->kill == nil) 383 r->kill = "write on closed pipe"; 384 } 385 nexterror(); 386 } 387 388 p = c->aux; 389 switch(NETTYPE(c->qid.path)){ 390 case Qdata0: 391 n = qbwrite(p->q[1], bp); 392 break; 393 394 case Qdata1: 395 n = qbwrite(p->q[0], bp); 396 break; 397 398 default: 399 n = 0; 400 panic("pipebwrite"); 401 } 402 403 poperror(); 404 return n; 405 } 406 407 static int 408 pipewstat(Chan *c, uchar *dp, int n) 409 { 410 Dir *d; 411 Pipe *p; 412 int d1; 413 414 if (c->qid.type&QTDIR) 415 error(Eperm); 416 p = c->aux; 417 if(strcmp(up->env->user, p->user) != 0) 418 error(Eperm); 419 d = smalloc(sizeof(*d)+n); 420 if(waserror()){ 421 free(d); 422 nexterror(); 423 } 424 n = convM2D(dp, n, d, (char*)&d[1]); 425 if(n == 0) 426 error(Eshortstat); 427 d1 = NETTYPE(c->qid.path) == Qdata1; 428 if(!emptystr(d->name)){ 429 validwstatname(d->name); 430 if(strlen(d->name) >= KNAMELEN) 431 error(Efilename); 432 if(strcmp(p->pipedir[1+!d1].name, d->name) == 0) 433 error(Eexist); 434 kstrcpy(p->pipedir[1+d1].name, d->name, KNAMELEN); 435 } 436 if(d->mode != ~0UL) 437 p->pipedir[d1 + 1].perm = d->mode & 0777; 438 poperror(); 439 free(d); 440 return n; 441 } 442 443 Dev pipedevtab = { 444 '|', 445 "pipe", 446 447 pipeinit, 448 pipeattach, 449 pipewalk, 450 pipestat, 451 pipeopen, 452 devcreate, 453 pipeclose, 454 piperead, 455 pipebread, 456 pipewrite, 457 pipebwrite, 458 devremove, 459 pipewstat, 460 }; 461