From e74702b467171dbdafb56dfe354794a212e020d9 Mon Sep 17 00:00:00 2001 From: Tanaka Akira Date: Thu, 15 Apr 1999 18:05:38 +0000 Subject: Initial revision --- Src/Builtins/.cvsignore | 11 + Src/Builtins/.distfiles | 5 + Src/Builtins/.exrc | 2 + Src/Builtins/rlimits.awk | 72 ++++++ Src/Builtins/rlimits.c | 593 +++++++++++++++++++++++++++++++++++++++++++++++ Src/Builtins/rlimits.mdd | 18 ++ Src/Builtins/sched.c | 214 +++++++++++++++++ Src/Builtins/sched.mdd | 3 + 8 files changed, 918 insertions(+) create mode 100644 Src/Builtins/.cvsignore create mode 100644 Src/Builtins/.distfiles create mode 100644 Src/Builtins/.exrc create mode 100644 Src/Builtins/rlimits.awk create mode 100644 Src/Builtins/rlimits.c create mode 100644 Src/Builtins/rlimits.mdd create mode 100644 Src/Builtins/sched.c create mode 100644 Src/Builtins/sched.mdd (limited to 'Src/Builtins') diff --git a/Src/Builtins/.cvsignore b/Src/Builtins/.cvsignore new file mode 100644 index 000000000..ff73d86c4 --- /dev/null +++ b/Src/Builtins/.cvsignore @@ -0,0 +1,11 @@ +Makefile +Makefile.in +*.pro +*.o +*.o.c +*.so +*.mdh +*.mdhi +*.mdhs +*.mdh.tmp +rlimits.h diff --git a/Src/Builtins/.distfiles b/Src/Builtins/.distfiles new file mode 100644 index 000000000..cd36388ef --- /dev/null +++ b/Src/Builtins/.distfiles @@ -0,0 +1,5 @@ +DISTFILES_SRC=' + .cvsignore .distfiles .exrc + rlimits.mdd rlimits.c rlimits.awk + sched.mdd sched.c +' diff --git a/Src/Builtins/.exrc b/Src/Builtins/.exrc new file mode 100644 index 000000000..91d0b39ef --- /dev/null +++ b/Src/Builtins/.exrc @@ -0,0 +1,2 @@ +set ai +set sw=4 diff --git a/Src/Builtins/rlimits.awk b/Src/Builtins/rlimits.awk new file mode 100644 index 000000000..e2500582a --- /dev/null +++ b/Src/Builtins/rlimits.awk @@ -0,0 +1,72 @@ +# +# rlimits.awk: {g,n}awk script to generate rlimits.h +# +# NB: On SunOS 4.1.3 - user-functions don't work properly, also \" problems +# Without 0 + hacks some nawks compare numbers as strings +# +BEGIN {limidx = 0} + +/^[\t ]*(#[\t ]*define[\t _]*RLIMIT_[A-Z]*[\t ]*[0-9][0-9]*|RLIMIT_[A-Z]*,[\t ]*)/ { + limindex = index($0, "RLIMIT_") + limtail = substr($0, limindex, 80) + split(limtail, tmp) + limnam = substr(tmp[1], 8, 20) + limnum = tmp[2] + # in this case I assume GNU libc resourcebits.h + if (limnum == "") { + limnum = limidx++ + limindex = index($0, ",") + limnam = substr(limnam, 1, limindex-1) + } + limrev[limnam] = limnum + if (lim[limnum] == "") { + lim[limnum] = limnam + if (limnum ~ /^[0-9]*$/) { + if (limnam == "MEMLOCK") { msg[limnum] = "memorylocked" } + if (limnam == "RSS") { msg[limnum] = "resident" } + if (limnam == "VMEM") { msg[limnum] = "vmemorysize" } + if (limnam == "NOFILE") { msg[limnum] = "descriptors" } + if (limnam == "OFILE") { msg[limnum] = "descriptors" } + if (limnam == "CORE") { msg[limnum] = "coredumpsize" } + if (limnam == "STACK") { msg[limnum] = "stacksize" } + if (limnam == "DATA") { msg[limnum] = "datasize" } + if (limnam == "FSIZE") { msg[limnum] = "filesize" } + if (limnam == "CPU") { msg[limnum] = "cputime" } + if (limnam == "NPROC") { msg[limnum] = "maxproc" } + if (limnam == "AS") { msg[limnum] = "addressspace" } + if (limnam == "TCACHE") { msg[limnum] = "cachedthreads" } + } + } +} +/^[\t ]*#[\t ]*define[\t _]*RLIM_NLIMITS[\t ]*[0-9][0-9]*/ { + limindex = index($0, "RLIM_") + limtail = substr($0, limindex, 80) + split(limtail, tmp) + nlimits = tmp[2] +} +# in case of GNU libc +/^[\t ]*RLIM_NLIMITS[\t ]*=[\t ]*RLIMIT_NLIMITS/ { + nlimits = limidx +} + +END { + if (limrev["MEMLOCK"] != "") { + irss = limrev["RSS"] + msg[irss] = "memoryuse" + } + ps = "%s" + + printf("%s\n%s\n\n", "/** rlimits.h **/", "/** architecture-customized limits for zsh **/") + printf("#define ZSH_NLIMITS %d\n\nstatic char *recs[ZSH_NLIMITS+1] = {\n", 0 + nlimits) + + for (i = 0; i < 0 + nlimits; i++) + if (msg[i] == "") { + badlimit++ + printf("\t%c%s%c,\n", 34, lim[i], 34) + } else + printf("\t%c%s%c,\n", 34, msg[i], 34) + print "\tNULL" + print "};" + print "" + exit(badlimit) +} diff --git a/Src/Builtins/rlimits.c b/Src/Builtins/rlimits.c new file mode 100644 index 000000000..20b8d663d --- /dev/null +++ b/Src/Builtins/rlimits.c @@ -0,0 +1,593 @@ +/* + * rlimits.c - resource limit builtins + * + * This file is part of zsh, the Z shell. + * + * Copyright (c) 1992-1997 Paul Falstad + * All rights reserved. + * + * Permission is hereby granted, without written agreement and without + * license or royalty fees, to use, copy, modify, and distribute this + * software and to distribute modified versions of this software for any + * purpose, provided that the above copyright notice and the following + * two paragraphs appear in all copies of this software. + * + * In no event shall Paul Falstad or the Zsh Development Group be liable + * to any party for direct, indirect, special, incidental, or consequential + * damages arising out of the use of this software and its documentation, + * even if Paul Falstad and the Zsh Development Group have been advised of + * the possibility of such damage. + * + * Paul Falstad and the Zsh Development Group specifically disclaim any + * warranties, including, but not limited to, the implied warranties of + * merchantability and fitness for a particular purpose. The software + * provided hereunder is on an "as is" basis, and Paul Falstad and the + * Zsh Development Group have no obligation to provide maintenance, + * support, updates, enhancements, or modifications. + * + */ + +#include "rlimits.mdh" +#include "rlimits.pro" + +#if defined(HAVE_GETRLIMIT) && defined(RLIM_INFINITY) + +/* Generated rec array containing limits required for the limit builtin. * + * They must appear in this array in numerical order of the RLIMIT_* macros. */ + +# include "rlimits.h" + +# if defined(RLIM_T_IS_QUAD_T) || defined(RLIM_T_IS_UNSIGNED) +static rlim_t +zstrtorlimt(const char *s, char **t, int base) +{ + rlim_t ret = 0; + + if (!base) + if (*s != '0') + base = 10; + else if (*++s == 'x' || *s == 'X') + base = 16, s++; + else + base = 8; + + if (base <= 10) + for (; *s >= '0' && *s < ('0' + base); s++) + ret = ret * base + *s - '0'; + else + for (; idigit(*s) || (*s >= 'a' && *s < ('a' + base - 10)) + || (*s >= 'A' && *s < ('A' + base - 10)); s++) + ret = ret * base + (idigit(*s) ? (*s - '0') : (*s & 0x1f) + 9); + if (t) + *t = (char *)s; + return ret; +} +# else /* !RLIM_T_IS_QUAD_T && !RLIM_T_IS_UNSIGNED */ +# define zstrtorlimt(a, b, c) zstrtol((a), (b), (c)) +# endif /* !RLIM_T_IS_QUAD_T && !RLIM_T_IS_UNSIGNED */ + +/* Display resource limits. hard indicates whether `hard' or `soft' * + * limits should be displayed. lim specifies the limit, or may be -1 * + * to show all. */ + +/**/ +static void +showlimits(int hard, int lim) +{ + int rt; + rlim_t val; + + /* main loop over resource types */ + for (rt = 0; rt != ZSH_NLIMITS; rt++) + if (rt == lim || lim == -1) { + /* display limit for resource number rt */ + printf("%-16s", recs[rt]); + val = (hard) ? limits[rt].rlim_max : limits[rt].rlim_cur; + if (val == RLIM_INFINITY) + printf("unlimited\n"); + else if (rt==RLIMIT_CPU) + /* time-type resource -- display as hours, minutes and + seconds. */ + printf("%d:%02d:%02d\n", (int)(val / 3600), + (int)(val / 60) % 60, (int)(val % 60)); +# ifdef RLIMIT_NPROC + else if (rt == RLIMIT_NPROC) + /* pure numeric resource */ + printf("%d\n", (int)val); +# endif /* RLIMIT_NPROC */ +# ifdef RLIMIT_NOFILE + else if (rt == RLIMIT_NOFILE) + /* pure numeric resource */ + printf("%d\n", (int)val); +# endif /* RLIMIT_NOFILE */ + else if (val >= 1024L * 1024L) + /* memory resource -- display with `K' or `M' modifier */ +# ifdef RLIM_T_IS_QUAD_T + printf("%qdMB\n", val / (1024L * 1024L)); + else + printf("%qdkB\n", val / 1024L); +# else + printf("%ldMB\n", val / (1024L * 1024L)); + else + printf("%ldkB\n", val / 1024L); +# endif /* RLIM_T_IS_QUAD_T */ + } +} + +/* Display a resource limit, in ulimit style. lim specifies which * + * limit should be displayed, and hard indicates whether the hard or * + * soft limit should be displayed. */ + +/**/ +static void +printulimit(int lim, int hard, int head) +{ + rlim_t limit; + + /* get the limit in question */ + limit = (hard) ? limits[lim].rlim_max : limits[lim].rlim_cur; + /* display the appropriate heading */ + switch (lim) { + case RLIMIT_CPU: + if (head) + printf("cpu time (seconds) "); + break; + case RLIMIT_FSIZE: + if (head) + printf("file size (blocks) "); + if (limit != RLIM_INFINITY) + limit /= 512; + break; + case RLIMIT_DATA: + if (head) + printf("data seg size (kbytes) "); + if (limit != RLIM_INFINITY) + limit /= 1024; + break; + case RLIMIT_STACK: + if (head) + printf("stack size (kbytes) "); + if (limit != RLIM_INFINITY) + limit /= 1024; + break; + case RLIMIT_CORE: + if (head) + printf("core file size (blocks) "); + if (limit != RLIM_INFINITY) + limit /= 512; + break; +# ifdef RLIMIT_RSS + case RLIMIT_RSS: + if (head) + printf("resident set size (kbytes) "); + if (limit != RLIM_INFINITY) + limit /= 1024; + break; +# endif /* RLIMIT_RSS */ +# ifdef RLIMIT_MEMLOCK + case RLIMIT_MEMLOCK: + if (head) + printf("locked-in-memory size (kb) "); + if (limit != RLIM_INFINITY) + limit /= 1024; + break; +# endif /* RLIMIT_MEMLOCK */ +# ifdef RLIMIT_NPROC + case RLIMIT_NPROC: + if (head) + printf("processes "); + break; +# endif /* RLIMIT_NPROC */ +# ifdef RLIMIT_NOFILE + case RLIMIT_NOFILE: + if (head) + printf("file descriptors "); + break; +# endif /* RLIMIT_NOFILE */ +# ifdef RLIMIT_VMEM + case RLIMIT_VMEM: + if (head) + printf("virtual memory size (kb) "); + if (limit != RLIM_INFINITY) + limit /= 1024; + break; +# endif /* RLIMIT_VMEM */ +# if defined RLIMIT_AS && RLIMIT_AS != RLIMIT_VMEM + case RLIMIT_AS: + if (head) + printf("address space (kb) "); + if (limit != RLIM_INFINITY) + limit /= 1024; + break; +# endif /* RLIMIT_AS */ +# ifdef RLIMIT_TCACHE + case RLIMIT_TCACHE: + if (head) + printf("cached threads "); + break; +# endif /* RLIMIT_TCACHE */ + } + /* display the limit */ + if (limit == RLIM_INFINITY) + printf("unlimited\n"); + else + printf("%ld\n", (long)limit); +} + +/* limit: set or show resource limits. The variable hard indicates * + * whether `hard' or `soft' resource limits are being set/shown. */ + +/**/ +static int +bin_limit(char *nam, char **argv, char *ops, int func) +{ + char *s; + int hard, limnum, lim; + rlim_t val; + int ret = 0; + + hard = ops['h']; + if (ops['s'] && !*argv) + return setlimits(NULL); + /* without arguments, display limits */ + if (!*argv) { + showlimits(hard, -1); + return 0; + } + while ((s = *argv++)) { + /* Search for the appropriate resource name. When a name matches (i.e. * + * starts with) the argument, the lim variable changes from -1 to the * + * number of the resource. If another match is found, lim goes to -2. */ + for (lim = -1, limnum = 0; limnum < ZSH_NLIMITS; limnum++) + if (!strncmp(recs[limnum], s, strlen(s))) { + if (lim != -1) + lim = -2; + else + lim = limnum; + } + /* lim==-1 indicates that no matches were found. * + * lim==-2 indicates that multiple matches were found. */ + if (lim < 0) { + zwarnnam("limit", + (lim == -2) ? "ambiguous resource specification: %s" + : "no such resource: %s", s, 0); + return 1; + } + /* without value for limit, display the current limit */ + if (!(s = *argv++)) { + showlimits(hard, lim); + return 0; + } + if (lim==RLIMIT_CPU) { + /* time-type resource -- may be specified as seconds, or minutes or * + * hours with the `m' and `h' modifiers, and `:' may be used to add * + * together more than one of these. It's easier to understand from * + * the code: */ + val = zstrtorlimt(s, &s, 10); + if (*s) + if ((*s == 'h' || *s == 'H') && !s[1]) + val *= 3600L; + else if ((*s == 'm' || *s == 'M') && !s[1]) + val *= 60L; + else if (*s == ':') + val = val * 60 + zstrtorlimt(s + 1, &s, 10); + else { + zwarnnam("limit", "unknown scaling factor: %s", s, 0); + return 1; + } + } +# ifdef RLIMIT_NPROC + else if (lim == RLIMIT_NPROC) + /* pure numeric resource -- only a straight decimal number is + permitted. */ + val = zstrtorlimt(s, &s, 10); +# endif /* RLIMIT_NPROC */ +# ifdef RLIMIT_NOFILE + else if (lim == RLIMIT_NOFILE) + /* pure numeric resource -- only a straight decimal number is + permitted. */ + val = zstrtorlimt(s, &s, 10); +# endif /* RLIMIT_NOFILE */ + else { + /* memory-type resource -- `k' and `M' modifiers are permitted, + meaning (respectively) 2^10 and 2^20. */ + val = zstrtorlimt(s, &s, 10); + if (!*s || ((*s == 'k' || *s == 'K') && !s[1])) + val *= 1024L; + else if ((*s == 'M' || *s == 'm') && !s[1]) + val *= 1024L * 1024; + else { + zwarnnam("limit", "unknown scaling factor: %s", s, 0); + return 1; + } + } + /* new limit is valid and has been interpreted; apply it to the + specified resource */ + if (hard) { + /* can only raise hard limits if running as root */ + if (val > current_limits[lim].rlim_max && geteuid()) { + zwarnnam("limit", "can't raise hard limits", NULL, 0); + return 1; + } else { + limits[lim].rlim_max = val; + if (val < limits[lim].rlim_cur) + limits[lim].rlim_cur = val; + } + } else if (val > limits[lim].rlim_max) { + zwarnnam("limit", "limit exceeds hard limit", NULL, 0); + return 1; + } else + limits[lim].rlim_cur = val; + if (ops['s'] && zsetlimit(lim, "limit")) + ret++; + } + return ret; +} + +/* unlimit: remove resource limits. Much of this code is the same as * + * that in bin_limit(). */ + +/**/ +static int +bin_unlimit(char *nam, char **argv, char *ops, int func) +{ + int hard, limnum, lim; + int ret = 0; + uid_t euid = geteuid(); + + hard = ops['h']; + /* Without arguments, remove all limits. */ + if (!*argv) { + for (limnum = 0; limnum != RLIM_NLIMITS; limnum++) { + if (hard) + if (euid && current_limits[limnum].rlim_max != RLIM_INFINITY) + ret++; + else + limits[limnum].rlim_max = RLIM_INFINITY; + else + limits[limnum].rlim_cur = limits[limnum].rlim_max; + } + if (ops['s']) + ret += setlimits(nam); + if (ret) + zwarnnam(nam, "can't remove hard limits", NULL, 0); + } else { + for (; *argv; argv++) { + /* Search for the appropriate resource name. When a name * + * matches (i.e. starts with) the argument, the lim variable * + * changes from -1 to the number of the resource. If another * + * match is found, lim goes to -2. */ + for (lim = -1, limnum = 0; limnum < ZSH_NLIMITS; limnum++) + if (!strncmp(recs[limnum], *argv, strlen(*argv))) { + if (lim != -1) + lim = -2; + else + lim = limnum; + } + /* lim==-1 indicates that no matches were found. * + * lim==-2 indicates that multiple matches were found. */ + if (lim < 0) { + zwarnnam(nam, + (lim == -2) ? "ambiguous resource specification: %s" + : "no such resource: %s", *argv, 0); + return 1; + } + /* remove specified limit */ + if (hard) + if (euid && current_limits[lim].rlim_max != RLIM_INFINITY) { + zwarnnam(nam, "can't remove hard limits", NULL, 0); + ret++; + } else + limits[lim].rlim_max = RLIM_INFINITY; + else + limits[lim].rlim_cur = limits[lim].rlim_max; + if (ops['s'] && zsetlimit(lim, nam)) + ret++; + } + } + return ret; +} + +/* ulimit: set or display resource limits */ + +/**/ +static int +bin_ulimit(char *name, char **argv, char *ops, int func) +{ + int res, resmask = 0, hard = 0, soft = 0, nres = 0; + char *options; + + do { + options = *argv; + if (options && *options == '-' && !options[1]) { + zwarnnam(name, "missing option letter", NULL, 0); + return 1; + } + res = -1; + if (options && *options == '-') { + argv++; + while (*++options) { + if(*options == Meta) + *++options ^= 32; + res = -1; + switch (*options) { + case 'H': + hard = 1; + continue; + case 'S': + soft = 1; + continue; + case 'a': + if (*argv || options[1] || resmask) { + zwarnnam(name, "no arguments required after -a", + NULL, 0); + return 1; + } + resmask = (1 << RLIM_NLIMITS) - 1; + nres = RLIM_NLIMITS; + continue; + case 't': + res = RLIMIT_CPU; + break; + case 'f': + res = RLIMIT_FSIZE; + break; + case 'd': + res = RLIMIT_DATA; + break; + case 's': + res = RLIMIT_STACK; + break; + case 'c': + res = RLIMIT_CORE; + break; +# ifdef RLIMIT_RSS + case 'm': + res = RLIMIT_RSS; + break; +# endif /* RLIMIT_RSS */ +# ifdef RLIMIT_MEMLOCK + case 'l': + res = RLIMIT_MEMLOCK; + break; +# endif /* RLIMIT_MEMLOCK */ +# ifdef RLIMIT_NOFILE + case 'n': + res = RLIMIT_NOFILE; + break; +# endif /* RLIMIT_NOFILE */ +# ifdef RLIMIT_NPROC + case 'u': + res = RLIMIT_NPROC; + break; +# endif /* RLIMIT_NPROC */ +# ifdef RLIMIT_VMEM + case 'v': + res = RLIMIT_VMEM; + break; +# endif /* RLIMIT_VMEM */ + default: + /* unrecognised limit */ + zwarnnam(name, "bad option: -%c", NULL, *options); + return 1; + } + if (options[1]) { + resmask |= 1 << res; + nres++; + } + } + } + if (!*argv || **argv == '-') { + if (res < 0) + if (*argv || nres) + continue; + else + res = RLIMIT_FSIZE; + resmask |= 1 << res; + nres++; + continue; + } + if (res < 0) + res = RLIMIT_FSIZE; + if (strcmp(*argv, "unlimited")) { + /* set limit to specified value */ + rlim_t limit; + + limit = zstrtorlimt(*argv, NULL, 10); + /* scale appropriately */ + switch (res) { + case RLIMIT_FSIZE: + case RLIMIT_CORE: + limit *= 512; + break; + case RLIMIT_DATA: + case RLIMIT_STACK: +# ifdef RLIMIT_RSS + case RLIMIT_RSS: +# endif /* RLIMIT_RSS */ +# ifdef RLIMIT_MEMLOCK + case RLIMIT_MEMLOCK: +# endif /* RLIMIT_MEMLOCK */ +# ifdef RLIMIT_VMEM + case RLIMIT_VMEM: +# endif /* RLIMIT_VMEM */ + limit *= 1024; + break; + } + if (hard) { + /* can't raise hard limit unless running as root */ + if (limit > current_limits[res].rlim_max && geteuid()) { + zwarnnam(name, "can't raise hard limits", NULL, 0); + return 1; + } + limits[res].rlim_max = limit; + if (limit < limits[res].rlim_cur) + limits[res].rlim_cur = limit; + } + if (!hard || soft) { + /* can't raise soft limit above hard limit */ + if (limit > limits[res].rlim_max) { + if (limit > current_limits[res].rlim_max && geteuid()) { + zwarnnam(name, "value exceeds hard limit", NULL, 0); + return 1; + } + limits[res].rlim_max = limits[res].rlim_cur = limit; + } else + limits[res].rlim_cur = limit; + } + } else { + /* remove specified limit */ + if (hard) { + /* can't remove hard limit unless running as root */ + if (current_limits[res].rlim_max != RLIM_INFINITY && geteuid()) { + zwarnnam(name, "can't remove hard limits", NULL, 0); + return 1; + } + limits[res].rlim_max = RLIM_INFINITY; + } + if (!hard || soft) + /* `removal' of soft limit means setting it equal to the + corresponding hard limit */ + limits[res].rlim_cur = limits[res].rlim_max; + } + if (zsetlimit(res, name)) + return 1; + argv++; + } while (*argv); + for (res = 0; res < RLIM_NLIMITS; res++, resmask >>= 1) + if (resmask & 1) + printulimit(res, hard, nres > 1); + return 0; +} + +#else /* !HAVE_GETRLIMIT || !RLIM_INFINITY */ + +# define bin_limit bin_notavail +# define bin_ulimit bin_notavail +# define bin_unlimit bin_notavail + +#endif /* !HAVE_GETRLIMIT || !RLIM_INFINITY */ + +static struct builtin bintab[] = { + BUILTIN("limit", 0, bin_limit, 0, -1, 0, "sh", NULL), + BUILTIN("ulimit", 0, bin_ulimit, 0, -1, 0, NULL, NULL), + BUILTIN("unlimit", 0, bin_unlimit, 0, -1, 0, "hs", NULL), +}; + +/**/ +int +boot_rlimits(Module m) +{ + return !addbuiltins(m->nam, bintab, sizeof(bintab)/sizeof(*bintab)); +} + +#ifdef MODULE + +/**/ +int +cleanup_rlimits(Module m) +{ + deletebuiltins(m->nam, bintab, sizeof(bintab)/sizeof(*bintab)); + return 0; +} +#endif diff --git a/Src/Builtins/rlimits.mdd b/Src/Builtins/rlimits.mdd new file mode 100644 index 000000000..f0e41b73e --- /dev/null +++ b/Src/Builtins/rlimits.mdd @@ -0,0 +1,18 @@ +autobins="limit ulimit unlimit" + +objects="rlimits.o" + +:<<\Make +rlimits.o rlimits..o: rlimits.h + +# this file will not be made if limits are unavailable: +# silent so the warning doesn't appear unless necessary +rlimits.h: rlimits.awk @RLIMITS_INC_H@ + @echo '$(AWK) -f $(sdir)/rlimits.awk @RLIMITS_INC_H@ > rlimits.h'; \ + $(AWK) -f $(sdir)/rlimits.awk @RLIMITS_INC_H@ > rlimits.h || \ + echo WARNING: unknown limits: mail rlimits.h to developers + +clean-here: clean.rlimits +clean.rlimits: + rm -f rlimits.h +Make diff --git a/Src/Builtins/sched.c b/Src/Builtins/sched.c new file mode 100644 index 000000000..b4914899e --- /dev/null +++ b/Src/Builtins/sched.c @@ -0,0 +1,214 @@ +/* + * sched.c - execute commands at scheduled times + * + * This file is part of zsh, the Z shell. + * + * Copyright (c) 1992-1997 Paul Falstad + * All rights reserved. + * + * Permission is hereby granted, without written agreement and without + * license or royalty fees, to use, copy, modify, and distribute this + * software and to distribute modified versions of this software for any + * purpose, provided that the above copyright notice and the following + * two paragraphs appear in all copies of this software. + * + * In no event shall Paul Falstad or the Zsh Development Group be liable + * to any party for direct, indirect, special, incidental, or consequential + * damages arising out of the use of this software and its documentation, + * even if Paul Falstad and the Zsh Development Group have been advised of + * the possibility of such damage. + * + * Paul Falstad and the Zsh Development Group specifically disclaim any + * warranties, including, but not limited to, the implied warranties of + * merchantability and fitness for a particular purpose. The software + * provided hereunder is on an "as is" basis, and Paul Falstad and the + * Zsh Development Group have no obligation to provide maintenance, + * support, updates, enhancements, or modifications. + * + */ + +#include "sched.mdh" +#include "sched.pro" + +/* node in sched list */ + +typedef struct schedcmd *Schedcmd; + +struct schedcmd { + struct schedcmd *next; + char *cmd; /* command to run */ + time_t time; /* when to run it */ +}; + +/* the list of sched jobs pending */ + +static struct schedcmd *schedcmds; + +/**/ +static int +bin_sched(char *nam, char **argv, char *ops, int func) +{ + char *s = *argv++; + time_t t; + long h, m; + struct tm *tm; + struct schedcmd *sch, *sch2, *schl; + int sn; + + /* If the argument begins with a -, remove the specified item from the + schedule. */ + if (s && *s == '-') { + sn = atoi(s + 1); + + if (!sn) { + zwarnnam("sched", "usage for delete: sched -.", NULL, 0); + return 1; + } + for (schl = (struct schedcmd *)&schedcmds, sch = schedcmds, sn--; + sch && sn; sch = (schl = sch)->next, sn--); + if (!sch) { + zwarnnam("sched", "not that many entries", NULL, 0); + return 1; + } + schl->next = sch->next; + zsfree(sch->cmd); + zfree(sch, sizeof(struct schedcmd)); + + return 0; + } + + /* given no arguments, display the schedule list */ + if (!s) { + char tbuf[40]; + + for (sn = 1, sch = schedcmds; sch; sch = sch->next, sn++) { + t = sch->time; + tm = localtime(&t); + ztrftime(tbuf, 20, "%a %b %e %k:%M:%S", tm); + printf("%3d %s %s\n", sn, tbuf, sch->cmd); + } + return 0; + } else if (!*argv) { + /* other than the two cases above, sched * + *requires at least two arguments */ + zwarnnam("sched", "not enough arguments", NULL, 0); + return 1; + } + + /* The first argument specifies the time to schedule the command for. The + remaining arguments form the command. */ + if (*s == '+') { + /* + introduces a relative time. The rest of the argument is an + hour:minute offset from the current time. Once the hour and minute + numbers have been extracted, and the format verified, the resulting + offset is simply added to the current time. */ + h = zstrtol(s + 1, &s, 10); + if (*s != ':') { + zwarnnam("sched", "bad time specifier", NULL, 0); + return 1; + } + m = zstrtol(s + 1, &s, 10); + if (*s) { + zwarnnam("sched", "bad time specifier", NULL, 0); + return 1; + } + t = time(NULL) + h * 3600 + m * 60; + } else { + /* If there is no +, an absolute time of day must have been given. + This is in hour:minute format, optionally followed by a string starting + with `a' or `p' (for a.m. or p.m.). Characters after the `a' or `p' + are ignored. */ + h = zstrtol(s, &s, 10); + if (*s != ':') { + zwarnnam("sched", "bad time specifier", NULL, 0); + return 1; + } + m = zstrtol(s + 1, &s, 10); + if (*s && *s != 'a' && *s != 'A' && *s != 'p' && *s != 'P') { + zwarnnam("sched", "bad time specifier", NULL, 0); + return 1; + } + t = time(NULL); + tm = localtime(&t); + t -= tm->tm_sec + tm->tm_min * 60 + tm->tm_hour * 3600; + if (*s == 'p' || *s == 'P') + h += 12; + t += h * 3600 + m * 60; + /* If the specified time is before the current time, it must refer to + tomorrow. */ + if (t < time(NULL)) + t += 3600 * 24; + } + /* The time has been calculated; now add the new entry to the linked list + of scheduled commands. */ + sch = (struct schedcmd *) zcalloc(sizeof *sch); + sch->time = t; + PERMALLOC { + sch->cmd = zjoin(argv, ' '); + } LASTALLOC; + sch->next = NULL; + for (sch2 = (struct schedcmd *)&schedcmds; sch2->next; sch2 = sch2->next); + sch2->next = sch; + return 0; +} + +/* Check scheduled commands; call this function from time to time. */ + +/**/ +static void +checksched(void) +{ + time_t t; + struct schedcmd *sch, *schl; + + if(!schedcmds) + return; + t = time(NULL); + for (schl = (struct schedcmd *)&schedcmds, sch = schedcmds; sch; + sch = (schl = sch)->next) { + if (sch->time <= t) { + execstring(sch->cmd, 0, 0); + schl->next = sch->next; + zsfree(sch->cmd); + zfree(sch, sizeof(struct schedcmd)); + sch = schl; + } + } +} + +static void (*p_checksched) _((void)) = checksched; +static struct linknode n_checksched = { NULL, NULL, &p_checksched }; + +static struct builtin bintab[] = { + BUILTIN("sched", 0, bin_sched, 0, -1, 0, NULL, NULL), +}; + +/**/ +int +boot_sched(Module m) +{ + if(!addbuiltins(m->nam, bintab, sizeof(bintab)/sizeof(*bintab))) + return 1; + uaddlinknode(prepromptfns, &n_checksched); + return 0; +} + +#ifdef MODULE + +/**/ +int +cleanup_sched(Module m) +{ + struct schedcmd *sch, *schn; + + for (sch = schedcmds; sch; sch = schn) { + schn = sch->next; + zsfree(sch->cmd); + zfree(sch, sizeof(*sch)); + } + uremnode(prepromptfns, &n_checksched); + deletebuiltins(m->nam, bintab, sizeof(bintab)/sizeof(*bintab)); + return 0; +} + +#endif diff --git a/Src/Builtins/sched.mdd b/Src/Builtins/sched.mdd new file mode 100644 index 000000000..6ed749f32 --- /dev/null +++ b/Src/Builtins/sched.mdd @@ -0,0 +1,3 @@ +autobins="sched" + +objects="sched.o" -- cgit 1.4.1