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