1/* 2 * sysread.c - interface to system read/write 3 * 4 * This file is part of zsh, the Z shell. 5 * 6 * Copyright (c) 1998-2003 Peter Stephenson 7 * All rights reserved. 8 * 9 * Permission is hereby granted, without written agreement and without 10 * license or royalty fees, to use, copy, modify, and distribute this 11 * software and to distribute modified versions of this software for any 12 * purpose, provided that the above copyright notice and the following 13 * two paragraphs appear in all copies of this software. 14 * 15 * In no event shall Peter Stephenson or the Zsh Development 16 * Group be liable to any party for direct, indirect, special, incidental, 17 * or consequential damages arising out of the use of this software and 18 * its documentation, even if Peter Stephenson, and the Zsh 19 * Development Group have been advised of the possibility of such damage. 20 * 21 * Peter Stephenson and the Zsh Development Group specifically 22 * disclaim any warranties, including, but not limited to, the implied 23 * warranties of merchantability and fitness for a particular purpose. The 24 * software provided hereunder is on an "as is" basis, and Peter Stephenson 25 * and the Zsh Development Group have no obligation to provide maintenance, 26 * support, updates, enhancements, or modifications. 27 * 28 */ 29 30#include "system.mdh" 31#include "system.pro" 32 33#ifdef HAVE_POLL_H 34# include <poll.h> 35#endif 36#if defined(HAVE_POLL) && !defined(POLLIN) 37# undef HAVE_POLL 38#endif 39 40#define SYSREAD_BUFSIZE 8192 41 42/**/ 43static int 44getposint(char *instr, char *nam) 45{ 46 char *eptr; 47 int ret; 48 49 ret = (int)zstrtol(instr, &eptr, 10); 50 if (*eptr || ret < 0) { 51 zwarnnam(nam, "integer expected: %s", instr); 52 return -1; 53 } 54 55 return ret; 56} 57 58 59/* 60 * Return values of bin_sysread: 61 * 0 Successfully read (and written if appropriate) 62 * 1 Error in parameters to command 63 * 2 Error on read, or polling read fd ) ERRNO set by 64 * 3 Error on write ) system 65 * 4 Timeout on read 66 * 5 Zero bytes read, end of file 67 */ 68 69/**/ 70static int 71bin_sysread(char *nam, char **args, Options ops, UNUSED(int func)) 72{ 73 int infd = 0, outfd = -1, bufsize = SYSREAD_BUFSIZE, count; 74 char *outvar = NULL, *countvar = NULL, *inbuf; 75 76 /* -i: input file descriptor if not stdin */ 77 if (OPT_ISSET(ops, 'i')) { 78 infd = getposint(OPT_ARG(ops, 'i'), nam); 79 if (infd < 0) 80 return 1; 81 } 82 83 /* -o: output file descriptor, else store in REPLY */ 84 if (OPT_ISSET(ops, 'o')) { 85 if (*args) { 86 zwarnnam(nam, "no argument allowed with -o"); 87 return 1; 88 } 89 outfd = getposint(OPT_ARG(ops, 'o'), nam); 90 if (outfd < 0) 91 return 1; 92 } 93 94 /* -s: buffer size if not default SYSREAD_BUFSIZE */ 95 if (OPT_ISSET(ops, 's')) { 96 bufsize = getposint(OPT_ARG(ops, 's'), nam); 97 if (bufsize < 0) 98 return 1; 99 } 100 101 /* -c: name of variable to store count of transferred bytes */ 102 if (OPT_ISSET(ops, 'c')) { 103 countvar = OPT_ARG(ops, 'c'); 104 if (!isident(countvar)) { 105 zwarnnam(nam, "not an identifier: %s", countvar); 106 return 1; 107 } 108 } 109 110 if (*args) { 111 /* 112 * Variable in which to store result if doing a plain read. 113 * Default variable if not specified is REPLY. 114 * If writing, only stuff we couldn't write is stored here, 115 * no default in that case (we just discard it if no variable). 116 */ 117 outvar = *args; 118 if (!isident(outvar)) { 119 zwarnnam(nam, "not an identifier: %s", outvar); 120 return 1; 121 } 122 } 123 124 inbuf = zhalloc(bufsize); 125 126#if defined(HAVE_POLL) || defined(HAVE_SELECT) 127 /* -t: timeout */ 128 if (OPT_ISSET(ops, 't')) 129 { 130# ifdef HAVE_POLL 131 struct pollfd poll_fd; 132 mnumber to_mn; 133 int to_int, ret; 134 135 poll_fd.fd = infd; 136 poll_fd.events = POLLIN; 137 138 to_mn = matheval(OPT_ARG(ops, 't')); 139 if (errflag) 140 return 1; 141 if (to_mn.type == MN_FLOAT) 142 to_int = (int) (1000 * to_mn.u.d); 143 else 144 to_int = 1000 * (int)to_mn.u.l; 145 146 while ((ret = poll(&poll_fd, 1, to_int)) < 0) { 147 if (errno != EINTR || errflag || retflag || breaks || contflag) 148 break; 149 } 150 if (ret <= 0) { 151 /* treat non-timeout error as error on read */ 152 return ret ? 2 : 4; 153 } 154# else 155 /* using select */ 156 struct timeval select_tv; 157 fd_set fds; 158 mnumber to_mn; 159 int ret; 160 161 FD_ZERO(&fds); 162 FD_SET(infd, &fds); 163 to_mn = matheval(OPT_ARG(ops, 't')); 164 if (errflag) 165 return 1; 166 167 if (to_mn.type == MN_FLOAT) { 168 select_tv.tv_sec = (int) to_mn.u.d; 169 select_tv.tv_usec = 170 (int) ((to_mn.u.d - select_tv.tv_sec) * 1e6); 171 } else { 172 select_tv.tv_sec = (int) to_mn.u.l; 173 select_tv.tv_usec = 0; 174 } 175 176 while ((ret = select(infd+1, (SELECT_ARG_2_T) &fds, 177 NULL, NULL,&select_tv)) < 1) { 178 if (errno != EINTR || errflag || retflag || breaks || contflag) 179 break; 180 } 181 if (ret <= 0) { 182 /* treat non-timeout error as error on read */ 183 return ret ? 2 : 4; 184 } 185# endif 186 } 187#endif 188 189 while ((count = read(infd, inbuf, bufsize)) < 0) { 190 if (errno != EINTR || errflag || retflag || breaks || contflag) 191 break; 192 } 193 if (countvar) 194 setiparam(countvar, count); 195 if (count < 0) 196 return 2; 197 198 if (outfd >= 0) { 199 if (!count) 200 return 5; 201 while (count > 0) { 202 int ret; 203 204 ret = write(outfd, inbuf, count); 205 if (ret < 0) { 206 if (errno == EINTR && !errflag && 207 !retflag && !breaks && !contflag) 208 continue; 209 if (outvar) 210 setsparam(outvar, metafy(inbuf, count, META_DUP)); 211 if (countvar) 212 setiparam(countvar, count); 213 return 3; 214 } 215 inbuf += ret; 216 count -= ret; 217 } 218 return 0; 219 } 220 221 if (!outvar) 222 outvar = "REPLY"; 223 /* do this even if we read zero bytes */ 224 setsparam(outvar, metafy(inbuf, count, META_DUP)); 225 226 return count ? 0 : 5; 227} 228 229 230/* 231 * Return values of bin_syswrite: 232 * 0 Successfully written 233 * 1 Error in parameters to command 234 * 2 Error on write, ERRNO set by system 235 */ 236 237/**/ 238static int 239bin_syswrite(char *nam, char **args, Options ops, UNUSED(int func)) 240{ 241 int outfd = 1, len, count, totcount; 242 char *countvar = NULL; 243 244 /* -o: output file descriptor if not stdout */ 245 if (OPT_ISSET(ops, 'o')) { 246 outfd = getposint(OPT_ARG(ops, 'o'), nam); 247 if (outfd < 0) 248 return 1; 249 } 250 251 /* -c: variable in which to store count of bytes written */ 252 if (OPT_ISSET(ops, 'c')) { 253 countvar = OPT_ARG(ops, 'c'); 254 if (!isident(countvar)) { 255 zwarnnam(nam, "not an identifier: %s", countvar); 256 return 1; 257 } 258 } 259 260 totcount = 0; 261 unmetafy(*args, &len); 262 while (len) { 263 while ((count = write(outfd, *args, len)) < 0) { 264 if (errno != EINTR || errflag || retflag || breaks || contflag) 265 { 266 if (countvar) 267 setiparam(countvar, totcount); 268 return 2; 269 } 270 } 271 *args += count; 272 totcount += count; 273 len -= count; 274 } 275 if (countvar) 276 setiparam(countvar, totcount); 277 278 return 0; 279} 280 281 282/* 283 * Return values of bin_syserror: 284 * 0 Successfully processed error 285 * (although if the number was invalid the string 286 * may not be useful) 287 * 1 Error in parameters 288 * 2 Name of error not recognised. 289 */ 290 291/**/ 292static int 293bin_syserror(char *nam, char **args, Options ops, UNUSED(int func)) 294{ 295 int num = 0; 296 char *errvar = NULL, *msg, *pfx = "", *str; 297 298 /* variable in which to write error message */ 299 if (OPT_ISSET(ops, 'e')) { 300 errvar = OPT_ARG(ops, 'e'); 301 if (!isident(errvar)) { 302 zwarnnam(nam, "not an identifier: %s", errvar); 303 return 1; 304 } 305 } 306 /* prefix for error message */ 307 if (OPT_ISSET(ops, 'p')) 308 pfx = OPT_ARG(ops, 'p'); 309 310 if (!*args) 311 num = errno; 312 else { 313 char *ptr = *args; 314 while (*ptr && idigit(*ptr)) 315 ptr++; 316 if (!*ptr && ptr > *args) 317 num = atoi(*args); 318 else { 319 const char **eptr; 320 for (eptr = sys_errnames; *eptr; eptr++) { 321 if (!strcmp(*eptr, *args)) { 322 num = (eptr - sys_errnames) + 1; 323 break; 324 } 325 } 326 if (!*eptr) 327 return 2; 328 } 329 } 330 331 msg = strerror(num); 332 if (errvar) { 333 str = (char *)zalloc(strlen(msg) + strlen(pfx) + 1); 334 sprintf(str, "%s%s", pfx, msg); 335 setsparam(errvar, str); 336 } else { 337 fprintf(stderr, "%s%s\n", pfx, msg); 338 } 339 340 return 0; 341} 342 343/**/ 344static int 345bin_zsystem_flock(char *nam, char **args, UNUSED(Options ops), UNUSED(int func)) 346{ 347 int cloexec = 1, unlock = 0, readlock = 0; 348 time_t timeout = 0; 349 char *fdvar = NULL; 350#ifdef HAVE_FCNTL_H 351 struct flock lck; 352 int flock_fd, flags; 353#endif 354 355 while (*args && **args == '-') { 356 int opt; 357 char *optptr = *args + 1, *optarg; 358 args++; 359 if (!*optptr || !strcmp(optptr, "-")) 360 break; 361 while ((opt = *optptr)) { 362 switch (opt) { 363 case 'e': 364 /* keep lock on "exec" */ 365 cloexec = 0; 366 break; 367 368 case 'f': 369 /* variable for fd */ 370 if (optptr[1]) { 371 fdvar = optptr + 1; 372 optptr += strlen(fdvar) - 1; 373 } else if (*args) { 374 fdvar = *args++; 375 } 376 if (fdvar == NULL || !isident(fdvar)) { 377 zwarnnam(nam, "flock: option %c requires a variable name", 378 opt); 379 return 1; 380 } 381 break; 382 383 case 'r': 384 /* read lock rather than read-write lock */ 385 readlock = 1; 386 break; 387 388 case 't': 389 /* timeout in seconds */ 390 if (optptr[1]) { 391 optarg = optptr + 1; 392 optptr += strlen(optarg) - 1; 393 } else if (!*args) { 394 zwarnnam(nam, "flock: option %c requires a numeric timeout", 395 opt); 396 return 1; 397 } else { 398 optarg = *args++; 399 } 400 timeout = (time_t)mathevali(optarg); 401 break; 402 403 case 'u': 404 /* unlock: argument is fd */ 405 unlock = 1; 406 break; 407 408 default: 409 zwarnnam(nam, "flock: unknown option: %c", *optptr); 410 return 1; 411 } 412 optptr++; 413 } 414 } 415 416 417 if (!args[0]) { 418 zwarnnam(nam, "flock: not enough arguments"); 419 return 1; 420 } 421 if (args[1]) { 422 zwarnnam(nam, "flock: too many arguments"); 423 return 1; 424 } 425 426#ifdef HAVE_FCNTL_H 427 if (unlock) { 428 flock_fd = (int)mathevali(args[0]); 429 if (zcloselockfd(flock_fd) < 0) { 430 zwarnnam(nam, "flock: file descriptor %d not in use for locking", 431 flock_fd); 432 return 1; 433 } 434 return 0; 435 } 436 437 if (readlock) 438 flags = O_RDONLY | O_NOCTTY; 439 else 440 flags = O_RDWR | O_NOCTTY; 441 if ((flock_fd = open(unmeta(args[0]), flags)) < 0) { 442 zwarnnam(nam, "failed to open %s for writing: %e", args[0], errno); 443 return 1; 444 } 445 flock_fd = movefd(flock_fd); 446 if (flock_fd == -1) 447 return 1; 448#ifdef FD_CLOEXEC 449 if (cloexec) 450 { 451 long fdflags = fcntl(flock_fd, F_GETFD, 0); 452 if (fdflags != (long)-1) 453 fcntl(flock_fd, F_SETFD, fdflags | FD_CLOEXEC); 454 } 455#endif 456 addlockfd(flock_fd, cloexec); 457 458 lck.l_type = readlock ? F_RDLCK : F_WRLCK; 459 lck.l_whence = SEEK_SET; 460 lck.l_start = 0; 461 lck.l_len = 0; /* lock the whole file */ 462 463 if (timeout > 0) { 464 time_t end = time(NULL) + (time_t)timeout; 465 while (fcntl(flock_fd, F_SETLK, &lck) < 0) { 466 if (errflag) 467 return 1; 468 if (errno != EINTR && errno != EACCES && errno != EAGAIN) { 469 zwarnnam(nam, "failed to lock file %s: %e", args[0], errno); 470 return 1; 471 } 472 if (time(NULL) >= end) 473 return 2; 474 sleep(1); 475 } 476 } else { 477 while (fcntl(flock_fd, F_SETLKW, &lck) < 0) { 478 if (errflag) 479 return 1; 480 if (errno == EINTR) 481 continue; 482 zwarnnam(nam, "failed to lock file %s: %e", args[0], errno); 483 return 1; 484 } 485 } 486 487 if (fdvar) 488 setiparam(fdvar, flock_fd); 489 490 return 0; 491#else /* HAVE_FCNTL_H */ 492 zwarnnam(nam, "flock: not implemented on this system"); 493 return 255; 494#endif /* HAVE_FCNTL_H */ 495} 496 497 498/* 499 * Return status zero if the zsystem feature is supported, else 1. 500 * Operates silently for future-proofing. 501 */ 502/**/ 503static int 504bin_zsystem_supports(char *nam, char **args, 505 UNUSED(Options ops), UNUSED(int func)) 506{ 507 if (!args[0]) { 508 zwarnnam(nam, "supports: not enough arguments"); 509 return 255; 510 } 511 if (args[1]) { 512 zwarnnam(nam, "supports: too many arguments"); 513 return 255; 514 } 515 516 /* stupid but logically this should work... */ 517 if (!strcmp(*args, "supports")) 518 return 0; 519#ifdef HAVE_FCNTL_H 520 if (!strcmp(*args, "flock")) 521 return 0; 522#endif 523 return 1; 524} 525 526 527/**/ 528static int 529bin_zsystem(char *nam, char **args, Options ops, int func) 530{ 531 /* If more commands are implemented, this can be more sophisticated */ 532 if (!strcmp(*args, "flock")) { 533 return bin_zsystem_flock(nam, args+1, ops, func); 534 } else if (!strcmp(*args, "supports")) { 535 return bin_zsystem_supports(nam, args+1, ops, func); 536 } 537 zwarnnam(nam, "unknown subcommand: %s", *args); 538 return 1; 539} 540 541static struct builtin bintab[] = { 542 BUILTIN("syserror", 0, bin_syserror, 0, 1, 0, "e:p:", NULL), 543 BUILTIN("sysread", 0, bin_sysread, 0, 1, 0, "c:i:o:s:t:", NULL), 544 BUILTIN("syswrite", 0, bin_syswrite, 1, 1, 0, "c:o:", NULL), 545 BUILTIN("zsystem", 0, bin_zsystem, 1, -1, 0, NULL, NULL) 546}; 547 548 549/* Functions for the errnos special parameter. */ 550 551/**/ 552static char ** 553errnosgetfn(UNUSED(Param pm)) 554{ 555 /* arrdup etc. should really take const pointers as arguments */ 556 return arrdup((char **)sys_errnames); 557} 558 559static const struct gsu_array errnos_gsu = 560{ errnosgetfn, arrsetfn, stdunsetfn }; 561 562 563/* Functions for the sysparams special parameter. */ 564 565/**/ 566static void 567fillpmsysparams(Param pm, const char *name) 568{ 569 char buf[DIGBUFSIZE]; 570 int num; 571 572 pm->node.nam = dupstring(name); 573 pm->node.flags = PM_SCALAR | PM_READONLY; 574 pm->gsu.s = &nullsetscalar_gsu; 575 if (!strcmp(name, "pid")) { 576 num = (int)getpid(); 577 } else if (!strcmp(name, "ppid")) { 578 num = (int)getppid(); 579 } else { 580 pm->u.str = dupstring(""); 581 pm->node.flags |= PM_UNSET; 582 return; 583 } 584 585 sprintf(buf, "%d", num); 586 pm->u.str = dupstring(buf); 587} 588 589 590/**/ 591static HashNode 592getpmsysparams(UNUSED(HashTable ht), const char *name) 593{ 594 Param pm; 595 596 pm = (Param) hcalloc(sizeof(struct param)); 597 fillpmsysparams(pm, name); 598 return &pm->node; 599} 600 601 602/**/ 603static void 604scanpmsysparams(UNUSED(HashTable ht), ScanFunc func, int flags) 605{ 606 struct param spm; 607 608 fillpmsysparams(&spm, "pid"); 609 func(&spm.node, flags); 610 fillpmsysparams(&spm, "ppid"); 611 func(&spm.node, flags); 612} 613 614 615static struct paramdef partab[] = { 616 SPECIALPMDEF("errnos", PM_ARRAY|PM_READONLY, 617 &errnos_gsu, NULL, NULL), 618 SPECIALPMDEF("sysparams", PM_READONLY, 619 NULL, getpmsysparams, scanpmsysparams) 620}; 621 622static struct features module_features = { 623 bintab, sizeof(bintab)/sizeof(*bintab), 624 NULL, 0, 625 NULL, 0, 626 partab, sizeof(partab)/sizeof(*partab), 627 0 628}; 629 630/* The load/unload routines required by the zsh library interface */ 631 632/**/ 633int 634setup_(UNUSED(Module m)) 635{ 636 return 0; 637} 638 639/**/ 640int 641features_(Module m, char ***features) 642{ 643 *features = featuresarray(m, &module_features); 644 return 0; 645} 646 647/**/ 648int 649enables_(Module m, int **enables) 650{ 651 return handlefeatures(m, &module_features, enables); 652} 653 654/**/ 655int 656boot_(Module m) 657{ 658 return 0; 659} 660 661 662/**/ 663int 664cleanup_(Module m) 665{ 666 return setfeatureenables(m, &module_features, NULL); 667} 668 669/**/ 670int 671finish_(UNUSED(Module m)) 672{ 673 return 0; 674} 675