/* Scheme48/scsh Unix system interface. ** Routines that require custom C support. ** Copyright (c) 1993,1994 by Olin Shivers. */ #include "sysdep.h" #include #include #include #include #include #include #include /* for O_RDWR */ #include #include /* For gethostname() */ #include /* This lossage brought to you by Solaris and BIND */ /* We thank Solaris for forcing users to get a new BIND */ /* We thank BIND for blowing away the Solaris includea for MAXHOSTNAMELEN */ #ifndef MAXHOSTNAMELEN #include #ifndef MAXHOSTNAMELEN #define MAXHOSTNAMELEN MAXDNAME #endif #endif #include #include #include #include #include #include #ifdef HAVE_CRYPT_H #include #endif #include #include "cstuff.h" #include "machine/stdio_dep.h" /* Make sure our exports match up w/the implementation: */ #include "syscalls1.h" extern int errno; extern char **environ; /* Sux because it's dependent on 32-bitness. */ #define hi8(i) (((i)>>24) & 0xff) #define lo24(i) ((i) & 0xffffff) #define comp8_24(hi, lo) (((hi)<<24) + (lo)) /* Process stuff ******************************************************************************* ** wait, exec */ /* Args: pid, flags; returns [retval, status] */ s48_value wait_pid(s48_value s48_pid, s48_value s48_flags) { int status=0; pid_t pid = (pid_t) s48_extract_integer (s48_pid); int flags = s48_extract_integer (s48_flags); pid_t result_pid; result_pid = waitpid(pid, &status, flags); return s48_cons ((result_pid == -1) ? s48_enter_fixnum(errno) : S48_FALSE, s48_cons (s48_enter_integer (result_pid), s48_cons (s48_enter_integer (status), S48_NULL))); } /* env: Scheme vector of Scheme strings, e.g., #("TERM=vt100" ...) or #T. ** argv: Scheme vector of Scheme strings. ** prog: String. ** ** We don't typecheck the args. You must do the typechecking ** on the Scheme side. */ s48_value scheme_exec(s48_value prog, s48_value argv, s48_value env) { int i, j, e; int argc = S48_VECTOR_LENGTH(argv); char **unix_argv = Malloc(char*, argc+1); char **unix_env; if( unix_argv == NULL ) s48_raise_os_error_3(errno, prog, argv, env); /* Scheme->Unix convert the argv parameter. */ for(i=0; iUnix convert the env parameter. */ if( env == S48_TRUE ) unix_env = environ; else { int envlen = S48_VECTOR_LENGTH(env); unix_env = Malloc(char*, envlen+1); if( !unix_env ){ Free(unix_argv); s48_raise_os_error_3(errno, prog, argv, env); } for(j=0; j 0 and 1 -> -1 */ /* If it's already what we want, just return. */ if( (flags & FD_CLOEXEC) == (FD_CLOEXEC & val) ) return S48_FALSE; flags = (flags & ~FD_CLOEXEC) | (val & FD_CLOEXEC); if (fcntl(fd, F_SETFD, flags) == -1) s48_raise_os_error_2(errno, _fd, _val); else return S48_FALSE; } s48_value scsh_chdir(s48_value directory){ int retval = chdir (s48_extract_string (directory)); if (retval == -1) s48_raise_os_error_1(errno, directory); return S48_TRUE; } /* Two versions of CWD ******************************************************************************* */ /* Posix rules: If PATH_MAX is defined, it's the length of longest path. ** Otherwise, _POSIX_PATH_MAX = 255, and is a lower bound on said length. ** I'm writing out 255 as a literal because HP-UX isn't finding ** _POSIX_PATH_MAX. */ #ifdef PATH_MAX #define scsh_path_max (PATH_MAX) #else #define scsh_path_max (255) #endif /* Simple-minded POSIX version. */ s48_value scheme_cwd() { char *buf; int size = scsh_path_max + 1; /* +1 for terminating nul byte... */ buf = Malloc(char,size); if(!buf) goto lose; while( !getcwd(buf, size) ) if( errno != ERANGE ) goto lose; else { /* Double the buf and retry. */ char *nbuf = Realloc(char, buf, size += size); if( !nbuf ) goto lose; buf = nbuf; } return s48_enter_string(buf); /* win */ lose: {int e = errno; Free(buf); s48_raise_os_error(e);} } /* Process times ******************************************************************************* */ /* Sleazing on the types here -- the ret values should be clock_t, not int, ** but cig can't handle it. */ int process_times(clock_t *utime, clock_t *stime, clock_t *cutime, clock_t *cstime) { struct tms tms; clock_t t = times(&tms); if (t == -1) return -1; *utime = tms.tms_utime; *stime = tms.tms_stime; *cutime = tms.tms_cutime; *cstime = tms.tms_cstime; return t; } int cpu_clock_ticks_per_sec() { #ifdef _SC_CLK_TCK static long clock_tick = 0; if (clock_tick == 0) clock_tick = sysconf(_SC_CLK_TCK); /* POSIX.1, POSIX.2 */ return clock_tick; #else #ifdef CLK_TCK return CLK_TCK; #else return 60; #endif #endif } /* Reading and writing ******************************************************************************* */ /* Return a char, #f (EOF), or errno. */ s48_value read_fdes_char(int fd) { int i; char c; if( (i=read(fd, &c, 1)) < 0 ) return s48_enter_fixnum(errno); if(i==0) return S48_FALSE; return s48_enter_char(c); } int write_fdes_char(char c, int fd) {return write(fd, &c, 1);} ssize_t read_fdes_substring(s48_value buf, size_t start, size_t end, int fd) { return read(fd, StrByte(buf,start), end-start); } ssize_t write_fdes_substring(s48_value buf, size_t start, size_t end, int fd) { return write(fd, StrByte(buf,start), end-start); } /* S_ISSOCK(mode) and S_ISLNK(mode) are not POSIX. You lose on a NeXT. Ugh. */ #ifndef S_ISSOCK #define S_ISSOCK(mode) (((mode) & S_IFMT) == S_IFSOCK) #endif #ifndef S_ISLNK #define S_ISLNK(mode) (((mode) & S_IFMT) == S_IFLNK) #endif #define low24(x) ((x) & 0xffffff) #define hi_but24(x) (((x) >> 24) & 0xff) /* Note that hi_but24 assumes value is a *32 bit* signed value. We have to ** do this, because C's right-shift operator exposes word width. A suckful ** language. */ /* Internal aux function -- loads stat values into Scheme vector: */ s48_value really_stat(struct stat *s, s48_value vec) { int modes, typecode = -1; S48_DECLARE_GC_PROTECT(1); S48_GC_PROTECT_1(vec); modes = s->st_mode; if( S_ISBLK(modes) ) typecode = 0; else if( S_ISCHR(modes) ) typecode = 1; else if( S_ISDIR(modes) ) typecode = 2; else if( S_ISFIFO(modes) ) typecode = 3; else if( S_ISREG(modes) ) typecode = 4; else if( S_ISSOCK(modes) ) typecode = 5; else if( S_ISLNK(modes) ) typecode = 6; S48_VECTOR_SET(vec,0,s48_enter_fixnum(typecode)); S48_VECTOR_SET(vec,1, s48_enter_integer(s->st_dev)); S48_VECTOR_SET(vec,2, s48_enter_integer(s->st_ino)); S48_VECTOR_SET(vec,3, s48_enter_integer(s->st_mode)); S48_VECTOR_SET(vec,4, s48_enter_integer(s->st_nlink)); S48_VECTOR_SET(vec,5, s48_enter_integer(s->st_uid)); S48_VECTOR_SET(vec,6, s48_enter_integer(s->st_gid)); S48_VECTOR_SET(vec,7, s48_enter_integer(s->st_size)); S48_VECTOR_SET(vec,8, s48_enter_integer(s->st_atime)); S48_VECTOR_SET(vec,9, s48_enter_integer(s->st_mtime)); S48_VECTOR_SET(vec,10, s48_enter_integer(s->st_ctime)); /* We also used to do st_rdev, st_blksize, and st_blocks. These aren't POSIX, and, e.g., are not around on SGI machines. Too bad -- blksize is useful. Unix sux. */ S48_GC_UNPROTECT(); return S48_UNSPECIFIC; } s48_value scheme_stat(s48_value path, s48_value vec, s48_value chase_p) { struct stat s; const char * cp_path = s48_extract_string(path); int retval = (chase_p != S48_FALSE) ? stat(cp_path, &s) : lstat(cp_path, &s); if (retval == -1) s48_raise_os_error_2 (errno, path, chase_p); return really_stat (&s, vec); } s48_value scheme_fstat(s48_value fd, s48_value vec) { struct stat s; int retval = fstat (s48_extract_fixnum (fd), &s); if (retval == -1) s48_raise_os_error_1 (errno, fd); return really_stat (&s, vec); } /* Supplementary groups access ******************************************************************************* */ s48_value scsh_getgid() { return s48_enter_integer(getgid()); } s48_value scsh_getegid() { return s48_enter_integer(getegid()); } s48_value scsh_setgid(s48_value gid) { int retval = setgid (s48_extract_integer (gid)); if (retval == -1) s48_raise_os_error_1(errno, gid); return S48_UNSPECIFIC; } s48_value scsh_setegid(s48_value gid) { int retval = setegid (s48_extract_integer (gid)); if (retval == -1) s48_raise_os_error_1(errno, gid); return S48_UNSPECIFIC; } /* Load the supplementary groups into a list */ s48_value get_groups() { int retval; int veclen = getgroups(0,NULL); int i = veclen; s48_value l = S48_NULL; gid_t gvec0[20], *gp = gvec0; if( veclen > 20 ) if (NULL == (gp=Malloc(gid_t,veclen))) s48_raise_os_error(errno); retval = getgroups(veclen, gp); if (retval == -1){ if (veclen > 20) Free(gp); s48_raise_os_error(errno); } else if (retval != veclen){ // paranoia... get_groups(); } else{ while (i > 0){ l = s48_cons(s48_enter_integer(gp[--i]), l); } if (veclen > 20) Free(gp); return l; } } s48_value scsh_getuid() { return s48_enter_integer(getuid()); } s48_value scsh_geteuid() { return s48_enter_integer(geteuid()); } s48_value scsh_setuid(s48_value uid) { int retval = setuid (s48_extract_integer (uid)); if (retval == -1) s48_raise_os_error_1(errno, uid); return S48_UNSPECIFIC; } s48_value scsh_seteuid(s48_value uid) { int retval = seteuid (s48_extract_integer (uid)); if (retval == -1) s48_raise_os_error_1(errno, uid); return S48_UNSPECIFIC; } /* Environment hackery ******************************************************************************* */ struct envvec { char **env; /* Null-terminated vector of strings. Malloc'd. */ int size; /* Length of env. */ int revealed; /* True => exported to C code */ int gcable; /* True => no pointers to us from Scheme heap. */ }; /* Note that the SIZE field tells you how many words of memory are allocated ** for the block of memory to which the ENV field points. This includes the ** terminating null pointer, and may include more words beyond that. It is ** *not* the number of strings stored in ENV; it is always greater than this ** value. ** ** The REVEALED field is incremented if either the ENV block or the entire ** struct is handed out to C code. ** ** If the structure becomes gc'able, but is REVEALED (hence cannot be freed), ** the GC simply sets GCABLE and forgets about it. */ /* The envvec corresponding to the current environment. ** Null if the current environment has no corresponding envvec struct ** (which should only be true of the initial environment at process ** startup time.) That is, ** !current_env || current_env->env == environ */ struct envvec *current_env = 0; void align_env(s48_value pointer_to_struct) { struct envvec* thread_env; thread_env = (struct envvec*) s48_extract_integer(pointer_to_struct); environ = thread_env->env; current_env = thread_env; } s48_value free_envvec (s48_value pointer_to_struct) { struct envvec* envv = (struct envvec*) s48_extract_integer(pointer_to_struct); int i; if (envv->revealed) { envv->gcable = 1; return S48_FALSE; } for (i=0; isize; i++) Free(envv->env[i]); Free(envv->env); Free(envv); return S48_TRUE; } s48_value envvec_setenv(s48_value scheme_name, s48_value entry){ char * name = s48_extract_string(scheme_name); int namelen = strlen(name); char **ptr = environ; char ** newenv; int size; int number_of_entries = 0; char * newentry = Malloc(char, S48_STRING_LENGTH(entry) + 1); if ( !newentry) return s48_enter_fixnum(errno); if (!current_env) { fprintf(stderr, "no current_env, giving up" ); exit (1); } size = current_env->size; while (*ptr){ if ( ( strncmp(*ptr, name, namelen) == 0) && (*ptr)[namelen] == '=') { *ptr = strcpy(newentry,s48_extract_string(entry)); return S48_FALSE; } ptr++; number_of_entries++; } if (number_of_entries >= size) { // I never had this problem, but... fprintf(stderr, "currupt env, giving up %d %d", number_of_entries,size); exit (1); } else if (number_of_entries < (size - 1)) // is space left after the NULL ? { *ptr = strcpy(newentry,s48_extract_string(entry)); *++ptr = NULL; return S48_FALSE; } else // number_of_entries == (size - 1) { int newsize = size + 1; // TODO: add more char ** newenv = Malloc (char *, newsize); if( !newenv) return s48_enter_fixnum(errno); current_env->env = newenv; current_env->size = newsize; memcpy(newenv, environ, number_of_entries * sizeof (char *)); newenv[number_of_entries] = strcpy(newentry, s48_extract_string(entry)); newenv[number_of_entries + 1] = NULL; environ = newenv; return S48_FALSE; } } s48_value scm_envvec(){ return char_pp_2_string_list(environ); } /* Load the (Scheme) strings in the (Scheme) vector VEC into environ. ** Somewhat wasteful of memory: we do not free any of the memory ** in the old environ -- don't know if it is being shared elsewhere. */ int install_env(s48_value vec) { int i, envsize; char **newenv; envsize = S48_VECTOR_LENGTH(vec); if (envsize >= (current_env->size)) { newenv = Malloc(char*, envsize+1); if( !newenv ) return errno; Free(current_env->env); current_env->env = newenv; } else newenv = current_env->env; for( i=0; ienv = newenv; thread_env->size = envsize + 1; thread_env->revealed = 0; thread_env->gcable = 0; for( i=0; i char *errno_msg(int i) { #ifdef HAVE_STRERROR return(strerror(i)); #else /* temp hack until we figure out what to do about losing sys_errlist's */ extern #ifdef HAVE_CONST_SYS_ERRLIST const #endif char *sys_errlist[]; extern int sys_nerr; return ( i < 0 || i > sys_nerr ) ? NULL /* i.e., #f */ : (char*) sys_errlist[i]; #endif /* !HAVE_STRERROR */ } /* Some of fcntl() ****************** */ s48_value fcntl_read(s48_value fd, s48_value command) { int ret = fcntl(s48_extract_fixnum (fd), s48_extract_integer (command)); if (ret == -1) s48_raise_os_error_2(errno, fd, command); else return s48_enter_fixnum (ret); } s48_value fcntl_write(s48_value fd, s48_value command, s48_value value) { int ret = fcntl(s48_extract_fixnum (fd), s48_extract_integer (command), s48_extract_integer (value)); if (ret == -1) s48_raise_os_error_3(errno, fd, command, value); else return s48_enter_fixnum (ret); } /* crypt() ****************** */ s48_value scm_crypt(s48_value key, s48_value salt) { return s48_enter_string (crypt ( s48_extract_string (key), s48_extract_string(salt))); } /* syslog ******************* */ enum scsh_syslog_option {SCSH_LOG_OPTION_DEFAULT, SCSH_LOG_CONS, SCSH_LOG_NDELAY, SCSH_LOG_PID}; int extract_option(s48_value _option) { int option; switch (s48_extract_fixnum (_option)){ case SCSH_LOG_OPTION_DEFAULT: option = 0; break; case SCSH_LOG_CONS: option = LOG_CONS; break; case SCSH_LOG_NDELAY: option = LOG_NDELAY; break; case SCSH_LOG_PID: option = LOG_PID; break; default: s48_raise_argtype_error (_option);} return option; } enum scsh_syslog_facility{SCSH_LOG_FACILITY_DEFAULT, SCSH_LOG_AUTH, SCSH_LOG_DAEMON, SCSH_LOG_KERN, SCSH_LOG_LOCAL0, SCSH_LOG_LOCAL1, SCSH_LOG_LOCAL2, SCSH_LOG_LOCAL3, SCSH_LOG_LOCAL4, SCSH_LOG_LOCAL5, SCSH_LOG_LOCAL6, SCSH_LOG_LOCAL7, SCSH_LOG_LPR, SCSH_LOG_MAIL, SCSH_LOG_USER}; int extract_facility(s48_value _facility) { int facility; switch (s48_extract_fixnum(_facility)){ case SCSH_LOG_FACILITY_DEFAULT: facility = 0; break; case SCSH_LOG_AUTH: facility = LOG_AUTH; break; case SCSH_LOG_DAEMON: facility = LOG_DAEMON; break; case SCSH_LOG_KERN: facility = LOG_KERN; break; case SCSH_LOG_LOCAL0: facility = LOG_LOCAL0; break; case SCSH_LOG_LOCAL1: facility = LOG_LOCAL1; break; case SCSH_LOG_LOCAL2: facility = LOG_LOCAL2; break; case SCSH_LOG_LOCAL3: facility = LOG_LOCAL3; break; case SCSH_LOG_LOCAL4: facility = LOG_LOCAL4; break; case SCSH_LOG_LOCAL5: facility = LOG_LOCAL5; break; case SCSH_LOG_LOCAL6: facility = LOG_LOCAL6; break; case SCSH_LOG_LOCAL7: facility = LOG_LOCAL7; break; case SCSH_LOG_LPR: facility = LOG_LPR; break; case SCSH_LOG_MAIL: facility = LOG_MAIL; break; case SCSH_LOG_USER: facility = LOG_USER; break; default: s48_raise_argtype_error (_facility);} return facility; } enum scsh_syslog_level{SCSH_LOG_LEVEL_DEFAULT, SCSH_LOG_EMERG, SCSH_LOG_ALERT, SCSH_LOG_CRIT, SCSH_LOG_ERR, SCSH_LOG_WARNING, SCSH_LOG_NOTICE, SCSH_LOG_INFO, SCSH_LOG_DEBUG}; int extract_level(s48_value _level) { int level; switch (s48_extract_fixnum (_level)){ case SCSH_LOG_LEVEL_DEFAULT: level = 0; break; case SCSH_LOG_EMERG: level = LOG_EMERG; break; case SCSH_LOG_ALERT: level = LOG_ALERT; break; case SCSH_LOG_CRIT: level = LOG_CRIT; break; case SCSH_LOG_ERR: level = LOG_ERR; break; case SCSH_LOG_WARNING: level = LOG_WARNING; break; case SCSH_LOG_NOTICE: level = LOG_NOTICE; break; case SCSH_LOG_INFO: level = LOG_INFO; break; case SCSH_LOG_DEBUG: level = LOG_DEBUG; break; default: s48_raise_argtype_error (_level);} return level; } s48_value scm_openlog (s48_value _ident, s48_value _option, s48_value _facility) { openlog(s48_extract_string(_ident), extract_option (_option), extract_facility (_facility)); return S48_UNSPECIFIC; } s48_value scm_syslog (s48_value _facility, s48_value _level, s48_value _message) { int facility = extract_facility (_facility); int level = extract_level (_level); syslog (facility | level, s48_extract_string (_message)); return S48_UNSPECIFIC; } s48_value scm_closelog () { closelog(); return S48_UNSPECIFIC; }