Zsh Mailing List Archive
Messages sorted by:
Reverse Date,
Date,
Thread,
Author
preliminary patch for zsh/random module
- X-seq: zsh-workers 50807
- From: Clinton Bunch <cdb_zsh@xxxxxxxxxxx>
- To: "zsh-workers@xxxxxxx" <zsh-workers@xxxxxxx>
- Subject: preliminary patch for zsh/random module
- Date: Sat, 22 Oct 2022 22:43:11 -0500
- Archived-at: <https://zsh.org/workers/50807>
- List-id: <zsh-workers.zsh.org>
It still needs testing on more platforms and additional error-checking
as well as documentation, but I was hoping for some feedback on the
general ideas.
diff --git a/Src/Modules/random.c b/Src/Modules/random.c
new file mode 100644
index 000000000..038a56c1f
--- /dev/null
+++ b/Src/Modules/random.c
@@ -0,0 +1,350 @@
+/*
+ * random.c - module to access kernel randome sources.
+ *
+ * This file is part of zsh, the Z shell.
+ *
+ * Copyright (c) 2022 Clinton Bunch
+ * 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 Zoltán Hidvégi 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 Zoltán Hidvégi and the Zsh Development Group have been
advised of
+ * the possibility of such damage.
+ *
+ * Clinton Bunch 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 Zoltán Hidvégi and the
+ * Zsh Development Group have no obligation to provide maintenance,
+ * support, updates, enhancements, or modifications.
+ *
+ */
+
+#include "random.mdh"
+#include "random.pro"
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <stdio.h>
+
+#ifdef HAVE_SYS_RANDOM_H
+#include <sys/random.h>
+#endif
+
+/* Simplify select URANDOM specific code */
+#if !defined(HAVE_ARC4RANDOM) && !defined(HAVE_GETRANDOM)
+#define USE_URANDOM
+#endif
+
+/* Define a buffer type for 32 bit integers */
+#ifdef UINT32_MAX
+typedef uint32_t zuint32;
+#else
+#define UINT32_MAX UINT_MAX
+typedef unsigned int zuint32;
+#endif
+
+/* buffer to pre-load integers for SRANDOM to lessen the context
switches */
+zuint32 rand_buff[8];
+static int buf_cnt = -1;
+
+#ifdef USE_URANDOM
+/* File descriptor for /dev/urandom */
+int randfd = -1;
+#endif /* USE_URANDOM */
+
+/*
+ * Function takes an input buffer and converts the characters to hex and
+ * places them in an outbut buffer twice the size. Returns -1 if it can't.
+ * Returns the size of the output otherwise.
+ */
+
+/**/
+static int
+ztr_to_hex(char *out, size_t outlen, unsigned char *input, size_t inlen)
+{
+ int i = 0, j = 0;
+
+ if (outlen < (inlen * 2 + 1))
+ return -1;
+
+ for(i = 0, j = 0; i < inlen; i++, j+=2) {
+ if (j < outlen) {
+ sprintf((char *)(out + j),"%02X",input[i]);
+ } else {
+ return -1;
+ }
+ }
+ out[j]='\0';
+ return j;
+}
+
+/**/
+static int
+getrandom_buffer(void *buf, size_t len)
+{
+ int ret;
+
+#ifdef HAVE_ARC4RANDOM
+ arc4random_buf(buf,len);
+ ret = len;
+#elif defined(HAVE_GETRANDOM)
+ ret=getrandom(buf,len,0);
+#else
+ ret=read(randfd,buf,len);
+#endif
+ return ret;
+}
+
+/*
+ * Implements the getrandom builtin to return a string of random bytes of
+ * user-specified length. It either prints them to stdout or returns them
+ * in a parameter passed on the command line.
+ *
+ */
+
+/**/
+static int
+bin_getrandom(char *name, char **argv, Options ops, int func)
+{
+
+ size_t len=8;
+ size_t byte_len=0, outlen;
+ int integer_out=0, i;
+ char int2str[11]; /*maximum string length of a 32-bit integer +
null */
+
+ unsigned char *buf;
+ zuint32 *int_buf;
+ char *scalar = NULL, *arrname= NULL, *endptr, *len_arg, *outbuff;
+ char **array;
+
+ if (OPT_ISSET(ops, 's')) {
+ scalar = OPT_ARG(ops, 's');
+ if (!isident(scalar)) {
+ zwarnnam(name, "argument to -s not an identifier: %s", scalar);
+ return 1;
+ }
+ }
+
+ if (OPT_ISSET(ops,'a')) {
+ arrname = OPT_ARG(ops, 'a');
+ if (!isident(arrname)) {
+ zwarnnam(name,"argument to -a not an identifier: %s", arrname);
+ return 1;
+ }
+ }
+
+ if (OPT_ISSET(ops, 'i')) {
+ if (!OPT_ISSET(ops,'a')) {
+ zwarnnam(name,"-i requires -a");
+ return 1;
+ }
+ integer_out=1;
+ }
+
+ if (OPT_ISSET(ops, 'l')) {
+ errno = 0;
+
+ len_arg=OPT_ARG(ops, 'l');
+ len = strtoul(len_arg, &endptr, 10);
+ if (errno != 0) {
+ zwarnnam(name, "%s: %e", len_arg, errno);
+ return 1;
+ } else if (*len_arg == '\0' || *endptr != '\0') {
+ zwarnnam(name, "%s: invalid decimal number", len_arg);
+ return 1;
+ } else if (len > 64 || (integer_out && len > 16) || len <= 0) {
+ zwarnnam(name,"length must be between 1 and %d you specified: %d",
+ (integer_out?16:64), len);
+ return 1;
+ }
+
+ }
+
+ if (!byte_len)
+ byte_len = len;
+ if (integer_out)
+ byte_len = len*sizeof(zuint32);
+
+ buf = zalloc(byte_len);
+
+ if (getrandom_buffer(buf, byte_len) < 0) {
+ zwarnnam(name,"Couldn't get random data");
+ return 1;
+ }
+
+ if (OPT_ISSET(ops, 'r')) {
+ outbuff = (char *) buf;
+ outlen = byte_len;
+ } else {
+ outlen=byte_len*2+1;
+ outbuff = (char*) zalloc(outlen);
+ ztr_to_hex(outbuff, outlen, buf, byte_len);
+ }
+
+ if (scalar) {
+ setsparam(scalar, metafy(outbuff, outlen, META_DUP));
+ }
+
+ if(arrname) {
+ array = (char **) zalloc((len+1)*sizeof(char *));
+ array[len] = NULL;
+
+ if(integer_out)
+ int_buf=(zuint32 *)buf;
+
+ for(i=0;i < len;i++) {
+ if(integer_out) {
+ sprintf(int2str,"%u",int_buf[i]);
+ } else {
+ sprintf(int2str,"%d",buf[i]);
+ }
+ array[i]=ztrdup(int2str);
+ }
+ setaparam(arrname,array);
+ }
+
+ if (!scalar && !arrname) {
+ fwrite(outbuff,1,outlen,stdout);
+ if (outbuff != (void *) buf) {
+ zfree(outbuff,outlen);
+ }
+ }
+
+
+ zfree(buf, len);
+ return 0;
+}
+
+
+
+/**/
+static zlong
+get_srandom(UNUSED(Param pm)) {
+
+ if(buf_cnt < 0) {
+ getrandom_buffer((void*) rand_buff,sizeof(rand_buff));
+ buf_cnt=7;
+ }
+ return rand_buff[buf_cnt--];
+}
+
+/**/
+static mnumber
+math_zrandom(UNUSED(char *name), UNUSED(int argc), UNUSED(mnumber *argv),
+ UNUSED(int id))
+{
+ mnumber ret;
+ uint32_t r;
+
+ r=get_srandom(NULL);
+ ret.type = MN_FLOAT;
+ ret.u.d = r/(UINT32_MAX+0.0);
+
+ return ret;
+}
+
+static struct builtin bintab[] = {
+ BUILTIN("getrandom", 0, bin_getrandom, 0, 5, 0, "ria:s:l:%", NULL),
+};
+
+static const struct gsu_integer srandom_gsu =
+{ get_srandom, nullintsetfn, stdunsetfn };
+
+static struct paramdef patab[] = {
+ SPECIALPMDEF("SRANDOM", PM_INTEGER|PM_READONLY,
&srandom_gsu,NULL,NULL),
+};
+
+static struct mathfunc mftab[] = {
+ NUMMATHFUNC("zrandom", math_zrandom, 0, 0, 0),
+};
+
+static struct features module_features = {
+ bintab, sizeof(bintab)/sizeof(*bintab),
+ NULL, 0,
+ mftab, sizeof(mftab)/sizeof(*mftab),
+ patab, sizeof(patab)/sizeof(*patab),
+ 0
+};
+
+/**/
+int
+setup_(UNUSED(Module m))
+{
+#ifdef USE_URANDOM
+ struct stat st;
+
+ if (lstat("/dev/urandom",&st) < 0) {
+ zwarn("No kernel random pool found.");
+ return 1;
+ }
+
+ if (!(S_ISCHR(st.st_mode)) ) {
+ zwarn("No kernel random pool found.");
+ return 1;
+ }
+#endif /* USE_URANDOM */
+ return 0;
+}
+
+/**/
+int
+features_(Module m, char ***features)
+{
+ *features = featuresarray(m, &module_features);
+ return 0;
+}
+
+/**/
+int
+enables_(Module m, int **enables)
+{
+ return handlefeatures(m, &module_features, enables);
+}
+
+/**/
+int
+boot_(Module m)
+{
+#ifdef USE_URANDOM
+ int tmpfd=-1;
+
+ if ((tmpfd = open("/dev/urandom", O_RDONLY)) < 0) {
+ zwarnnam("Could not access kernel random pool");
+ return 1;
+ }
+ randfd = movefd(tmpfd);
+ if (randfd < 0)
+ zwarnnam("Could not access kernel random pool");
+ return 1;
+ }
+#endif
+ return 0;
+}
+
+/**/
+int
+cleanup_(Module m)
+{
+ return setfeatureenables(m, &module_features, NULL);
+}
+
+/**/
+int
+finish_(UNUSED(Module m))
+{
+#ifdef USE_URANDOM
+ if (randfd >= 0)
+ zclose(randfd);
+#endif /* USE_URANDOM */
+ return 0;
+}
diff --git a/Src/Modules/random.mdd b/Src/Modules/random.mdd
new file mode 100644
index 000000000..3803a4533
--- /dev/null
+++ b/Src/Modules/random.mdd
@@ -0,0 +1,7 @@
+name=zsh/random
+link=either
+load=yes
+
+autofeatures="b:getrandom p:SRANDOM f:zrandom"
+
+objects="random.o"
diff --git a/configure.ac b/configure.ac
index 074141d38..f1fa01274 100644
--- a/configure.ac
+++ b/configure.ac
@@ -675,6 +675,7 @@ fi
AC_CHECK_HEADERS(sys/time.h sys/times.h sys/select.h termcap.h termio.h \
termios.h sys/param.h sys/filio.h string.h memory.h \
limits.h fcntl.h libc.h sys/utsname.h sys/resource.h \
+ sys/random.h \
locale.h errno.h stdio.h stdarg.h varargs.h stdlib.h \
unistd.h sys/capability.h \
utmp.h utmpx.h sys/types.h pwd.h grp.h poll.h sys/mman.h \
@@ -1337,6 +1338,7 @@ AC_CHECK_FUNCS(strftime strptime mktime timelocal \
cygwin_conv_path \
nanosleep \
srand_deterministic \
+ getrandom arc4random \
setutxent getutxent endutxent getutent)
AC_FUNC_STRCOLL
Messages sorted by:
Reverse Date,
Date,
Thread,
Author