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