Index: build-common.sh ================================================================== --- build-common.sh +++ build-common.sh @@ -9,8 +9,8 @@ rm -f tuapi.tcl.h exit 0 ;; esac -tuapi_version="$(grep Tcl_PkgProvide system.c | awk '{ print $3 }' | sed 's@[");]*@@g')" +tuapi_version="$(grep Tcl_PkgProvide tuapi.c | awk '{ print $3 }' | sed 's@[");]*@@g')" -./stringify.tcl system.tcl > tuapi.tcl.h +./stringify.tcl tuapi.tcl > tuapi.tcl.h Index: build-dyn.sh ================================================================== --- build-dyn.sh +++ build-dyn.sh @@ -6,9 +6,9 @@ # Compile using the same options as Tcl TCLCONFIGSH="$(find /usr/lib /usr/lib64 /usr/local/lib /usr/local/lib64 /lib /lib64 -name tclConfig.sh -print -quit)" . "${TCLCONFIGSH}" -echo "${TCL_CC} -ggdb3 -fPIC -DPIC -Wall -DUSE_TCL_STUBS=1 ${TCL_DEFS} ${TCL_INCLUDE_SPEC} ${TCL_STUB_LIB_SPEC} -shared -rdynamic -o tuapi.so system.c" -eval ${TCL_CC} -ggdb3 -fPIC -DPIC -Wall -DUSE_TCL_STUBS=1 ${TCL_DEFS} ${TCL_INCLUDE_SPEC} ${TCL_STUB_LIB_SPEC} -shared -rdynamic -o tuapi.so system.c +echo "${TCL_CC} -ggdb3 -fPIC -DPIC -Wall -DUSE_TCL_STUBS=1 ${TCL_DEFS} ${TCL_INCLUDE_SPEC} ${TCL_STUB_LIB_SPEC} -shared -rdynamic -o tuapi.so tuapi.c" +eval ${TCL_CC} -ggdb3 -fPIC -DPIC -Wall -DUSE_TCL_STUBS=1 ${TCL_DEFS} ${TCL_INCLUDE_SPEC} ${TCL_STUB_LIB_SPEC} -shared -rdynamic -o tuapi.so tuapi.c echo 'package ifneeded tuapi '"${tuapi_version}"' [list load [file join $dir tuapi.so]]' > pkgIndex.tcl Index: build-static.sh ================================================================== --- build-static.sh +++ build-static.sh @@ -9,10 +9,10 @@ # Compile using the same options as Tcl TCLCONFIGSH='/usr/lib64/tclConfig.sh' . "${TCLCONFIGSH}" -echo "${TCL_CC} ${TCL_DEFS} ${TCL_INCLUDE_SPEC} -o tuapi.o -c system.c" -eval ${TCL_CC} ${TCL_DEFS} ${TCL_INCLUDE_SPEC} -o tuapi.o -c system.c +echo "${TCL_CC} ${TCL_DEFS} ${TCL_INCLUDE_SPEC} -o tuapi.o -c tuapi.c" +eval ${TCL_CC} ${TCL_DEFS} ${TCL_INCLUDE_SPEC} -o tuapi.o -c tuapi.c ar rcu libtuapi.a tuapi.o ranlib libtuapi.a echo 'package ifneeded tuapi '"${tuapi_version}"' [list load {} tuapi]' > pkgIndex.tcl Index: build.sh ================================================================== --- build.sh +++ build.sh @@ -9,16 +9,16 @@ # Compile using the same options as Tcl TCLCONFIGSH="${KITCREATORROOT}/tcl/inst/lib/tclConfig.sh" . "${TCLCONFIGSH}" -echo "${TCL_CC} ${TCL_DEFS} ${TCL_INCLUDE_SPEC} -o tuapi.o -c system.c" -eval ${TCL_CC} ${TCL_DEFS} ${TCL_INCLUDE_SPEC} -o tuapi.o -c system.c +echo "${TCL_CC} ${TCL_DEFS} ${TCL_INCLUDE_SPEC} -o tuapi.o -c tuapi.c" +eval ${TCL_CC} ${TCL_DEFS} ${TCL_INCLUDE_SPEC} -o tuapi.o -c tuapi.c ar rcu libtuapi.a tuapi.o ranlib libtuapi.a echo 'package ifneeded tuapi '"${tuapi_version}"' [list load {} tuapi]' > pkgIndex.tcl mkdir -p inst/lib/tuapi-0.1 mkdir -p out/lib/tuapi-0.1 cp libtuapi.a inst/lib/tuapi-0.1 cp pkgIndex.tcl out/lib/tuapi-0.1 DELETED system.c Index: system.c ================================================================== --- system.c +++ system.c @@ -1,2624 +0,0 @@ -#define _LINUX_SOURCE 1 -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include -#include -#include - -#ifndef HOST_NAME_MAX -/* SUSv2 Limit */ -#define HOST_NAME_MAX 255 -#endif - -/* From Linux 2.6 */ -#ifndef MNT_DETACH -#define MNT_DETACH 2 -#endif -#ifndef MNT_EXPIRE -#define MNT_EXPIRE 4 -#endif -#ifndef MS_MOVE -#define MS_MOVE 8192 -#endif - -/* Simple macros */ -#ifndef MAX -#define MAX(a, b) (((a) < (b)) ? (b) : (a)) -#endif -#ifndef MIN -#define MIN(a, b) (((a) < (b)) ? (a) : (b)) -#endif - -/* User environment, for execve */ -extern char **environ; - -/* Re-implement these if needed */ -#ifdef SYS_init_module -static int init_module(void *val, unsigned long len, const char *args) { - return(syscall(SYS_init_module, val, len, args)); -} -#endif -#ifdef SYS_pivot_root -static int pivot_root(const char *new_root, const char *put_old) { - return(syscall(SYS_pivot_root, new_root, put_old)); -} -#endif - -/* - * Simple hash routine to enable switching on a string to be implemented - */ -static unsigned long tuapi_internal_simplehash(const void *databuf, int datalen) { - unsigned long retval = 0; - const unsigned char *data; - - data = databuf; - - for (; datalen > 0; datalen--,data++) { - retval ^= (retval >> 25) & 0x7F; - retval <<= 7; - retval &= (0xFFFFFFFFUL); - retval ^= *data; - } - - return(retval); -} - -static unsigned long tuapi_internal_simplehash_obj(Tcl_Obj *tcl_data) { - unsigned long retval; - char *data; - int datalen = -1; - - data = Tcl_GetStringFromObj(tcl_data, &datalen); - - retval = tuapi_internal_simplehash(data, datalen); - - return(retval); -} - -#if 0 -/* NOTUSED: Uncomment when needed: */ -static unsigned long tuapi_internal_simplehash_str(const char *data) { - unsigned long retval; - int datalen; - - datalen = strlen(data); - - retval = tuapi_internal_simplehash(data, datalen); - - return(retval); -} -#endif - -static int tuapi_internalproc_simplehash(ClientData cd, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[]) { - unsigned long hashval; - Tcl_Obj *hashval_obj; - - if (objc != 2) { - Tcl_SetObjResult(interp, Tcl_NewStringObj("wrong # args: should be \"::tuapi::internal::hash value\"", -1)); - - return(TCL_ERROR); - } - - hashval = tuapi_internal_simplehash_obj(objv[1]); - - hashval_obj = Tcl_NewObj(); - Tcl_SetWideIntObj(hashval_obj, hashval); - - Tcl_SetObjResult(interp, hashval_obj); - - return(TCL_OK); -} - -static int tuapi_internal_getsock(int *sock_v4_out, int *sock_v6_out) { - int sock_v4 = -1, sock_v6 = -1; - int sock; - - if (sock_v4_out == NULL && sock_v6_out == NULL) { - return(-1); - } - - if (sock_v4_out != NULL) { - /* - * Check for IPv4 support before trying to create an IPv4 socket to - * avoid demand-loading IPv4 (XXX: TODO) - */ - sock_v4 = socket(AF_INET, SOCK_DGRAM, 0); - } - - if (sock_v6_out != NULL) { - /* - * Check for IPv6 support before trying to create an IPv6 socket to - * avoid demand-loading IPv6 (XXX: TODO) - */ - sock_v6 = socket(AF_INET6, SOCK_DGRAM, 0); - } - - /* Pick a socket to query for the interface list */ - if (sock_v4 == -1 && sock_v6 == -1) { - return(-1); - } - - if (sock_v6 != -1) { - sock = sock_v6; - } else { - sock = sock_v4; - } - - if (sock_v4_out != NULL) { - *sock_v4_out = sock_v4; - } - - if (sock_v6_out != NULL) { - *sock_v6_out = sock_v6; - } - - return(sock); -} - -/* - * Low-level System Call Wrapper Procedures - * - * These procedures should minimally wrap Linux or UNIX system calls to - * expose to the Tcl-level. Where possible accept symbolic names rather - * than numeric values (.e.g, list of values to OR together to get flags). - */ -static int tuapi_mount(ClientData cd, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[]) { - Tcl_Obj *mountflags_obj, **mountflags_list, *mountflag; - int mountflags_list_len; - char *source, *target, *fstype; - unsigned long mountflags = 0; - void *data = NULL; - int mount_ret, tcl_ret; - - if (objc < 5 || objc > 6) { - Tcl_SetObjResult(interp, Tcl_NewStringObj("wrong # args: should be \"::tuapi::syscall::mount source target fstype mountflags ?data?\"", -1)); - - return(TCL_ERROR); - } - - source = Tcl_GetString(objv[1]); - target = Tcl_GetString(objv[2]); - fstype = Tcl_GetString(objv[3]); - mountflags_obj = objv[4]; - - if (objc == 6) { - data = Tcl_GetString(objv[5]); - } - - tcl_ret = Tcl_ListObjGetElements(interp, mountflags_obj, &mountflags_list_len, &mountflags_list); - if (tcl_ret != TCL_OK) { - return(tcl_ret); - } - - for (; mountflags_list_len > 0; mountflags_list_len--,mountflags_list++) { - mountflag = mountflags_list[0]; - - switch (tuapi_internal_simplehash_obj(mountflag)) { -#ifdef MS_BIND - case 0x8526744: /* BIND */ - mountflags |= MS_BIND; - break; -#endif -#ifdef MS_DIRSYNC - case 0x2aff41c3: /* DIRSYNC */ - mountflags |= MS_DIRSYNC; - break; -#endif -#ifdef MS_MANDLOCK - case 0x410dbcb: /* MANDLOCK */ - mountflags |= MS_MANDLOCK; - break; -#endif -#ifdef MS_MOVE - case 0x9b3eb45: /* MOVE */ - mountflags |= MS_MOVE; - break; -#endif -#ifdef MS_NOATIME - case 0x1a0f58c5: /* NOATIME */ - mountflags |= MS_NOATIME; - break; -#endif -#ifdef MS_NODEV - case 0xe9f120d6: /* NODEV */ - mountflags |= MS_NODEV; - break; -#endif -#ifdef MS_NODIRATIME - case 0xde08ff45: /* NODIRATIME */ - mountflags |= MS_NODIRATIME; - break; -#endif -#ifdef MS_NOEXEC - case 0xf8b718c3: /* NOEXEC */ - mountflags |= MS_NOEXEC; - break; -#endif -#ifdef MS_NOSUID - case 0xfa745ec4: /* NOSUID */ - mountflags |= MS_NOSUID; - break; -#endif -#ifdef MS_RDONLY - case 0x49f2ec59: /* RDONLY */ - mountflags |= MS_RDONLY; - break; -#endif -#ifdef MS_RELATIME - case 0x481954c5: /* RELATIME */ - mountflags |= MS_RELATIME; - break; -#endif -#ifdef MS_REMOUNT - case 0xd9507154: /* REMOUNT */ - mountflags |= MS_REMOUNT; - break; -#endif -#ifdef MS_SILENT - case 0x99902954: /* SILENT */ - mountflags |= MS_SILENT; - break; -#endif -#ifdef MS_STRICTATIME - case 0x562fa045: /* STRICTATIME */ - mountflags |= MS_STRICTATIME; - break; -#endif -#ifdef MS_SYNCHRONOUS - case 0xbf799353: /* SYNCHRONOUS */ - case 0xa766743: /* SYNC */ - mountflags |= MS_SYNCHRONOUS; - break; -#endif - default: - Tcl_SetObjResult(interp, Tcl_ObjPrintf("unknown element in mountflags: \"%s\"", Tcl_GetString(mountflag))); - - return(TCL_ERROR); - } - } - - mount_ret = mount(source, target, fstype, mountflags, data); - if (mount_ret != 0) { - Tcl_SetObjResult(interp, Tcl_NewStringObj(strerror(errno), -1)); - - return(TCL_ERROR); - } - - Tcl_SetObjResult(interp, Tcl_NewStringObj(target, -1)); - - return(TCL_OK); -} - -static int tuapi_umount(ClientData cd, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[]) { - Tcl_Obj **flags, *flag; - Tcl_Obj *pathname_obj; - char *pathname; - int umount2_flags = 0; - int flags_cnt; - int chk_ret, tcl_ret; - - if (objc < 2 || objc > 3) { - Tcl_SetObjResult(interp, Tcl_NewStringObj("wrong # args: should be \"tuapi::syscall::umount dir ?flags?\"", -1)); - - return(TCL_ERROR); - } - - pathname_obj = objv[1]; - pathname = Tcl_GetString(pathname_obj); - - /* Set a default return value */ - Tcl_SetObjResult(interp, pathname_obj); - - if (objc == 3) { - tcl_ret = Tcl_ListObjGetElements(interp, objv[2], &flags_cnt, &flags); - if (tcl_ret != TCL_OK) { - return(tcl_ret); - } - - for (; flags_cnt > 0; flags_cnt--,flags++) { - flag = flags[0]; - - switch (tuapi_internal_simplehash_obj(flag)) { - case 0x69f4a3c5: /* FORCE */ - umount2_flags |= MNT_FORCE; - - break; - case 0x5a9173c8: /* DETACH */ - umount2_flags |= MNT_DETACH; - - break; - case 0x8a137fc5: /* EXPIRE */ - umount2_flags |= MNT_EXPIRE; - - break; - default: - Tcl_SetObjResult(interp, Tcl_ObjPrintf("unknown flag \"%s\" specified", Tcl_GetString(flag))); - - return(TCL_ERROR); - } - } - - chk_ret = umount2(pathname, umount2_flags); - - /* Do not return an error for this case, since it is apparently not exceptional */ - if (chk_ret != 0 && (umount2_flags & MNT_EXPIRE) == MNT_EXPIRE && errno == EAGAIN) { - Tcl_SetObjResult(interp, Tcl_NewStringObj("AGAIN", -1)); - - chk_ret = 0; - } - } else { - chk_ret = umount(pathname); - } - - if (chk_ret != 0) { - Tcl_SetObjResult(interp, Tcl_NewStringObj(strerror(errno), -1)); - - return(TCL_ERROR); - } - - return(TCL_OK); -} - -static int tuapi_swapon(ClientData cd, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[]) { - char *pathname; - int chk_ret; - - if (objc != 2) { - Tcl_SetObjResult(interp, Tcl_NewStringObj("wrong # args: should be \"tuapi::syscall::swapon pathname\"", -1)); - - return(TCL_ERROR); - } - - pathname = Tcl_GetString(objv[1]); - - chk_ret = swapon(pathname, 0); - if (chk_ret != 0) { - Tcl_SetObjResult(interp, Tcl_NewStringObj(strerror(errno), -1)); - - return(TCL_ERROR); - } - - return(TCL_OK); -} - -static int tuapi_swapoff(ClientData cd, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[]) { - char *pathname; - int chk_ret; - - if (objc != 2) { - Tcl_SetObjResult(interp, Tcl_NewStringObj("wrong # args: should be \"tuapi::syscall::swapoff pathname\"", -1)); - - return(TCL_ERROR); - } - - pathname = Tcl_GetString(objv[1]); - - chk_ret = swapoff(pathname); - if (chk_ret != 0) { - Tcl_SetObjResult(interp, Tcl_NewStringObj(strerror(errno), -1)); - - return(TCL_ERROR); - } - - return(TCL_OK); -} - -static int tuapi_insmod(ClientData cd, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[]) { - Tcl_Channel fd; - Tcl_Obj *module_filename, *module_data; - void *module_data_val; - int module_data_len; - int read_ret, chk_ret; - - if (objc < 2) { - Tcl_SetObjResult(interp, Tcl_NewStringObj("wrong # args: should be \"tuapi::syscall::insmod filename ?args ...?\"", -1)); - - return(TCL_ERROR); - } - - module_filename = objv[1]; - - fd = Tcl_FSOpenFileChannel(interp, module_filename, "r", 0600); - if (fd == NULL) { - return(TCL_ERROR); - } - - chk_ret = Tcl_SetChannelOption(interp, fd, "-translation", "binary"); - if (chk_ret != TCL_OK) { - Tcl_Close(interp, fd); - - return(chk_ret); - } - - module_data = Tcl_NewObj(); - - read_ret = Tcl_ReadChars(fd, module_data, -1, 0); - - Tcl_Close(interp, fd); - - if (read_ret <= 0) { - Tcl_SetObjResult(interp, Tcl_NewStringObj("read failed", -1)); - - return(TCL_ERROR); - } - - module_data_val = Tcl_GetByteArrayFromObj(module_data, &module_data_len); - - chk_ret = init_module(module_data_val, module_data_len, ""); - if (chk_ret != 0) { - Tcl_SetObjResult(interp, Tcl_NewStringObj(strerror(errno), -1)); - - return(TCL_ERROR); - } - - return(TCL_OK); -} - -static int tuapi_rmmod(ClientData cd, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[]) { - Tcl_SetObjResult(interp, Tcl_NewStringObj("not implemented", -1)); - - return(TCL_ERROR); -} - -static int tuapi_lsmod(ClientData cd, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[]) { - Tcl_SetObjResult(interp, Tcl_NewStringObj("not implemented", -1)); - - return(TCL_ERROR); -} - -static int tuapi_hostname(ClientData cd, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[]) { - char hostname[HOST_NAME_MAX + 1]; - int chk_ret; - - if (objc == 1) { - /* No arguments given, just return the hostname */ - chk_ret = gethostname(hostname, sizeof(hostname)); - if (chk_ret != 0) { - Tcl_SetObjResult(interp, Tcl_NewStringObj(strerror(errno), -1)); - - return(TCL_ERROR); - } - - hostname[sizeof(hostname) - 1] = '\0'; - - Tcl_SetObjResult(interp, Tcl_NewStringObj(hostname, -1)); - - return(TCL_OK); - } - - if (objc == 2) { - /* Exactly one argument given, set the hostname */ - strncpy(hostname, Tcl_GetString(objv[1]), sizeof(hostname)); - hostname[sizeof(hostname) - 1] = '\0'; - - chk_ret = sethostname(hostname, strlen(hostname)); - if (chk_ret != 0) { - Tcl_SetObjResult(interp, Tcl_NewStringObj(strerror(errno), -1)); - - return(TCL_ERROR); - } - - Tcl_SetObjResult(interp, Tcl_NewStringObj(hostname, -1)); - - return(TCL_OK); - } - - Tcl_SetObjResult(interp, Tcl_NewStringObj("wrong # args: should be \"hostname ?hostname?\"", -1)); - - return(TCL_ERROR); -} - -static int tuapi_domainname(ClientData cd, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[]) { - Tcl_SetObjResult(interp, Tcl_NewStringObj("not implemented", -1)); - - return(TCL_ERROR); -} - -static int tuapi_chroot(ClientData cd, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[]) { - char *pathname; - int chk_ret; - - if (objc != 2) { - Tcl_SetObjResult(interp, Tcl_NewStringObj("wrong # args: should be \"::tuapi::syscall:chroot pathname\"", -1)); - - return(TCL_ERROR); - } - - pathname = Tcl_GetString(objv[1]); - - chk_ret = chroot(pathname); - if (chk_ret != 0) { - Tcl_SetObjResult(interp, Tcl_NewStringObj(strerror(errno), -1)); - - return(TCL_ERROR); - } - - return(TCL_OK); -} - -static int tuapi_pivot_root(ClientData cd, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[]) { - char *new_root, *put_old; - int chk_ret; - - if (objc != 3) { - Tcl_SetObjResult(interp, Tcl_NewStringObj("wrong # args: should be \"::tuapi::syscall::pivot_root new_root put_old\"", -1)); - - return(TCL_ERROR); - } - - new_root = Tcl_GetString(objv[1]); - put_old = Tcl_GetString(objv[2]); - - chk_ret = pivot_root(new_root, put_old); - if (chk_ret != 0) { - Tcl_SetObjResult(interp, Tcl_NewStringObj(strerror(errno), -1)); - - return(TCL_ERROR); - } - - return(TCL_OK); -} - -static int tuapi_mknod(ClientData cd, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[]) { - Tcl_SetObjResult(interp, Tcl_NewStringObj("not implemented", -1)); - - return(TCL_ERROR); -} - -static int tuapi_getuid(ClientData cd, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[]) { - Tcl_SetObjResult(interp, Tcl_NewStringObj("not implemented", -1)); - - return(TCL_ERROR); -} - -static int tuapi_kill(ClientData cd, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[]) { - Tcl_Obj *signal_obj; - - Tcl_WideInt pid_wide, sig_wide; - pid_t pid; - int sig; - int kill_ret, tcl_ret; - - if (objc != 3) { - Tcl_SetObjResult(interp, Tcl_NewStringObj("wrong # args: should be \"::tuapi::syscall::kill pid sig\"", -1)); - - return(TCL_ERROR); - } - - tcl_ret = Tcl_GetWideIntFromObj(interp, objv[1], &pid_wide); - if (tcl_ret != TCL_OK) { - return(tcl_ret); - } - pid = pid_wide; - - signal_obj = objv[2]; - - tcl_ret = Tcl_GetWideIntFromObj(interp, signal_obj, &sig_wide); - if (tcl_ret != TCL_OK) { - switch (tuapi_internal_simplehash_obj(signal_obj)) { - case 0x122ad0: /* HUP */ - case 0x98f364d0: /* SIGHUP */ - sig = SIGHUP; - break; - case 0x126754: /* INT */ - case 0x98f32954: /* SIGINT */ - sig = SIGINT; - break; - case 0xa3564d4: /* QUIT */ - case 0x7a9242d4: /* SIGQUIT */ - sig = SIGQUIT; - break; - case 0x12664c: /* ILL */ - case 0x98f3284c: /* SIGILL */ - sig = SIGILL; - break; - case 0xa94a0d0: /* TRAP */ - case 0x7a3386d0: /* SIGTRAP */ - sig = SIGTRAP; - break; - case 0x830a954: /* ABRT */ - case 0x78978f54: /* SIGABRT */ - sig = SIGABRT; - break; - case 0x1267d4: /* IOT */ - case 0x98f329d4: /* SIGIOT */ - sig = SIGIOT; - break; - case 0x10aad3: /* BUS */ - case 0x98f1e4d3: /* SIGBUS */ - sig = SIGBUS; - break; - case 0x11a845: /* FPE */ - case 0x98f0e645: /* SIGFPE */ - sig = SIGFPE; - break; - case 0x972664c: /* KILL */ - case 0x79d5404c: /* SIGKILL */ - sig = SIGKILL; - break; - case 0xab4e931: /* USR1 */ - case 0x7a13cf31: /* SIGUSR1 */ - sig = SIGUSR1; - break; - case 0xa7163d6: /* SEGV */ - case 0x7ad645d6: /* SIGSEGV */ - sig = SIGSEGV; - break; - case 0xab4e932: /* USR2 */ - case 0x7a13cf32: /* SIGUSR2 */ - sig = SIGUSR2; - break; - case 0xa126845: /* PIPE */ - case 0x7ab54e45: /* SIGPIPE */ - sig = SIGPIPE; - break; - case 0x833294d: /* ALRM */ - case 0x78940f4d: /* SIGALRM */ - sig = SIGALRM; - break; - case 0xa91694d: /* TERM */ - case 0x7a364f4d: /* SIGTERM */ - sig = SIGTERM; - break; - case 0x4970e8d4: /* STKFLT */ - case 0x80fefc54: /* SIGSTKFLT */ - sig = SIGSTKFLT; - break; - case 0x8722644: /* CHLD */ - case 0x78d50044: /* SIGCHLD */ - sig = SIGCHLD; - break; - case 0x873e754: /* CONT */ - case 0x78d4c154: /* SIGCONT */ - sig = SIGCONT; - break; - case 0xa7527d0: /* STOP */ - case 0x7ad201d0: /* SIGSTOP */ - sig = SIGSTOP; - break; - case 0xa94ea50: /* TSTP */ - case 0x7a33cc50: /* SIGTSTP */ - sig = SIGTSTP; - break; - case 0xa9524ce: /* TTIN */ - case 0x7a3202ce: /* SIGTTIN */ - sig = SIGTTIN; - break; - case 0xa9527d5: /* TTOU */ - case 0x7a3201d5: /* SIGTTOU */ - sig = SIGTTOU; - break; - case 0x156947: /* URG */ - case 0x98f42747: /* SIGURG */ - sig = SIGURG; - break; - case 0xb10e855: /* XCPU */ - case 0x7bb7ce55: /* SIGXCPU */ - sig = SIGXCPU; - break; - case 0xb11a9da: /* XFSZ */ - case 0x7bb68fda: /* SIGXFSZ */ - sig = SIGXFSZ; - break; - case 0x483273cd: /* VTALRM */ - case 0x81bc674d: /* SIGVTALRM */ - sig = SIGVTALRM; - break; - case 0xa14a7c6: /* PROF */ - case 0x7ab381c6: /* SIGPROF */ - sig = SIGPROF; - break; - case 0x7933a348: /* WINCH */ - case 0x2aa0bf48: /* SIGWINCH */ - sig = SIGWINCH; - break; - case 0x24cf: /* IO */ - case 0x3931e64f: /* SIGIO */ - sig = SIGIO; - break; - case 0x142bd2: /* PWR */ - case 0x98f565d2: /* SIGPWR */ - sig = SIGPWR; - break; - case 0x14ecd3: /* SYS */ - case 0x98f5a2d3: /* SIGSYS */ - sig = SIGSYS; - break; - default: - Tcl_SetObjResult(interp, Tcl_ObjPrintf("unknown signal \"%s\"", Tcl_GetString(signal_obj))); - - return(TCL_ERROR); - } - } else { - sig = sig_wide; - } - - kill_ret = kill(pid, sig); - if (kill_ret != 0) { - Tcl_SetObjResult(interp, Tcl_NewStringObj(strerror(errno), -1)); - - return(TCL_ERROR); - } - - return(TCL_OK); -} - -static int tuapi_ps(ClientData cd, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[]) { - Tcl_SetObjResult(interp, Tcl_NewStringObj("not implemented", -1)); - - return(TCL_ERROR); -} - -static int tuapi_execve(ClientData cd, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[]) { - char **argv = NULL; - char *file; - int idx; - - if (objc < 2) { - Tcl_SetObjResult(interp, Tcl_NewStringObj("wrong # args: should be \"::tuapi::syscall::execve file ?args ...?\"", -1)); - - return(TCL_ERROR); - } - - /* Find executable */ - file = Tcl_GetString(objv[1]); - - /* Generate argument array */ - argv = malloc(sizeof(*argv) * (objc - 1)); - - for (idx = 2; idx < objc; idx++) { - argv[idx - 2] = Tcl_GetString(objv[idx]); - } - argv[objc - 2] = NULL; - - /* Pass execution to new file */ - execve(file, argv, environ); - - /* If the new image could not take over, something went wrong -- report error */ - Tcl_SetObjResult(interp, Tcl_NewStringObj(strerror(errno), -1)); - - return(TCL_ERROR); -} - -static int tuapi_losetup(ClientData cd, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[]) { - char *file, *loopdev; - int chk_ret; - int loopfd, filefd; - - if (objc != 3) { - Tcl_SetObjResult(interp, Tcl_NewStringObj("wrong # args: should be \"::tuapi::syscall::losetup loopdev file\"", -1)); - - return(TCL_ERROR); - } - - loopdev = Tcl_GetString(objv[1]); - file = Tcl_GetString(objv[2]); - - loopfd = open(loopdev, O_RDONLY); - if (loopfd < 0) { - Tcl_SetObjResult(interp, Tcl_NewStringObj(strerror(errno), -1)); - - return(TCL_ERROR); - } - - if (file[0] != '\0') { - filefd = open(file, O_RDONLY); - if (filefd < 0) { - close(loopfd); - - Tcl_SetObjResult(interp, Tcl_NewStringObj(strerror(errno), -1)); - - return(TCL_ERROR); - } - - chk_ret = ioctl(loopfd, LOOP_SET_FD, filefd); - - close(filefd); - } else { - chk_ret = ioctl(loopfd, LOOP_CLR_FD, 0); - } - - close(loopfd); - - if (chk_ret != 0) { - Tcl_SetObjResult(interp, Tcl_NewStringObj(strerror(errno), -1)); - - return(TCL_ERROR); - } - - return(TCL_OK); -} - -static void tuapi_private_append_sockaddr_to_tclobj(Tcl_Interp *interp, Tcl_Obj *list, char *header, struct sockaddr *addr) { - char addr_buf[INET6_ADDRSTRLEN + INET_ADDRSTRLEN + 1], *chk_inp; - - switch (addr->sa_family) { - case AF_INET: /* IPv4 */ - case AF_INET6: /* IPv6 */ - switch (addr->sa_family) { - case AF_INET: /* IPv4 */ - chk_inp = (char *) inet_ntop(addr->sa_family, &((struct sockaddr_in *) addr)->sin_addr, addr_buf, sizeof(addr_buf)); - break; - case AF_INET6: /* IPv6 */ - chk_inp = (char *) inet_ntop(addr->sa_family, &((struct sockaddr_in6 *) addr)->sin6_addr, addr_buf, sizeof(addr_buf)); - break; - } - - if (chk_inp == NULL) { - break; - } - - if (header) { - Tcl_ListObjAppendElement(interp, list, Tcl_NewStringObj(header, -1)); - } - - Tcl_ListObjAppendElement(interp, list, Tcl_NewStringObj(addr_buf, -1)); - - break; - } - - return; -} - -static int tuapi_private_get_sockaddr_from_obj(Tcl_Obj *value, void *target) { - struct sockaddr_in local_v4; - struct sockaddr_in6 local_v6; - const char *addr_str; - int inetpton_ret; - - addr_str = Tcl_GetString(value); - - memset(&local_v4, 0, sizeof(local_v4)); - inetpton_ret = inet_pton(AF_INET, addr_str, &local_v4.sin_addr); - if (inetpton_ret == 1) { - local_v4.sin_family = AF_INET; - - memcpy(target, &local_v4, sizeof(local_v4)); - - return(0); - } - - memset(&local_v6, 0, sizeof(local_v6)); - inetpton_ret = inet_pton(AF_INET6, addr_str, &local_v6.sin6_addr); - if (inetpton_ret == 1) { - local_v6.sin6_family = AF_INET6; - - memcpy(target, &local_v6, sizeof(local_v6)); - - return(0); - } - - return(-1); -} - -static int tuapi_ifconfig_list(ClientData cd, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[], int sock) { - Tcl_Obj *tcl_iface_list; - struct ifconf ifaces_cfg; - struct ifreq *iface_req = NULL; - int iface_req_cnt = 224, iface_req_len; - int idx, iface_cnt; - int ioctl_ret, tcl_ret; - - iface_req_len = iface_req_cnt * sizeof(*iface_req); - iface_req = malloc(iface_req_len); - if (iface_req == NULL) { - /* Report failure */ - Tcl_SetObjResult(interp, Tcl_NewStringObj("unable to allocate memory", -1)); - - return(TCL_ERROR); - } - - ifaces_cfg.ifc_req = iface_req; - ifaces_cfg.ifc_len = iface_req_len; - ioctl_ret = ioctl(sock, SIOCGIFCONF, &ifaces_cfg); - if (ioctl_ret != 0) { - Tcl_SetObjResult(interp, Tcl_NewStringObj("ioctl failed", -1)); - - free(iface_req); - - return(TCL_ERROR); - } - - iface_cnt = ifaces_cfg.ifc_len / sizeof(*iface_req); - - tcl_iface_list = Tcl_NewObj(); - - for (idx = 0; idx < iface_cnt; idx++) { - tcl_ret = Tcl_ListObjAppendElement(interp, tcl_iface_list, Tcl_NewStringObj(iface_req[idx].ifr_name, -1)); - if (tcl_ret != TCL_OK) { - Tcl_SetObjResult(interp, Tcl_NewStringObj("unable to append to list", -1)); - - free(iface_req); - - return(TCL_ERROR); - } - } - - free(iface_req); - - Tcl_SetObjResult(interp, tcl_iface_list); - - return(TCL_OK); -} - -static int tuapi_ifconfig_info(ClientData cd, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[], int sock, int sock_v4, int sock_v6) { - Tcl_Obj *retlist, *flags; - struct ifreq iface_req; - unsigned char *addr_data; - const char *link_encap; - const char *iface; - int flags_bitmask, flag_broadcast = 0, flag_pointopoint = 0; - int ioctl_ret; - - retlist = Tcl_NewObj(); - - iface = Tcl_GetString(objv[1]); - - if ((strlen(iface) + 1) >= IFNAMSIZ) { - Tcl_SetObjResult(interp, Tcl_NewStringObj("interface name too long", -1)); - - return(TCL_ERROR); - } - - strcpy(iface_req.ifr_name, iface); - - /* - * All interfaces should have flags, so use it as a check for interface - * existance - */ - ioctl_ret = ioctl(sock, SIOCGIFFLAGS, &iface_req); - if (ioctl_ret != 0) { - Tcl_SetObjResult(interp, Tcl_NewStringObj("invalid interface", -1)); - - return(TCL_ERROR); - } - - /* Create list of flags */ - flags = Tcl_NewObj(); - flags_bitmask = iface_req.ifr_flags; - - if ((flags_bitmask & IFF_UP) == IFF_UP) { - Tcl_ListObjAppendElement(interp, flags, Tcl_NewStringObj("UP", -1)); - } - if ((flags_bitmask & IFF_BROADCAST) == IFF_BROADCAST) { - flag_broadcast = 1; - - Tcl_ListObjAppendElement(interp, flags, Tcl_NewStringObj("BROADCAST", -1)); - } - if ((flags_bitmask & IFF_POINTOPOINT) == IFF_POINTOPOINT) { - flag_pointopoint = 1; - - Tcl_ListObjAppendElement(interp, flags, Tcl_NewStringObj("POINTOPOINT", -1)); - } - if ((flags_bitmask & IFF_DEBUG) == IFF_DEBUG) { - Tcl_ListObjAppendElement(interp, flags, Tcl_NewStringObj("DEBUG", -1)); - } - if ((flags_bitmask & IFF_LOOPBACK) == IFF_LOOPBACK) { - Tcl_ListObjAppendElement(interp, flags, Tcl_NewStringObj("LOOPBACK", -1)); - } - if ((flags_bitmask & IFF_NOTRAILERS) == IFF_NOTRAILERS) { - Tcl_ListObjAppendElement(interp, flags, Tcl_NewStringObj("NOTRAILERS", -1)); - } - if ((flags_bitmask & IFF_RUNNING) == IFF_RUNNING) { - Tcl_ListObjAppendElement(interp, flags, Tcl_NewStringObj("RUNNING", -1)); - } - if ((flags_bitmask & IFF_NOARP) == IFF_NOARP) { - Tcl_ListObjAppendElement(interp, flags, Tcl_NewStringObj("NOARP", -1)); - } - if ((flags_bitmask & IFF_PROMISC) == IFF_PROMISC) { - Tcl_ListObjAppendElement(interp, flags, Tcl_NewStringObj("PROMISC", -1)); - } - if ((flags_bitmask & IFF_ALLMULTI) == IFF_ALLMULTI) { - Tcl_ListObjAppendElement(interp, flags, Tcl_NewStringObj("ALLMULTI", -1)); - } - if ((flags_bitmask & IFF_MASTER) == IFF_MASTER) { - Tcl_ListObjAppendElement(interp, flags, Tcl_NewStringObj("MASTER", -1)); - } - if ((flags_bitmask & IFF_SLAVE) == IFF_SLAVE) { - Tcl_ListObjAppendElement(interp, flags, Tcl_NewStringObj("SLAVE", -1)); - } - if ((flags_bitmask & IFF_MULTICAST) == IFF_MULTICAST) { - Tcl_ListObjAppendElement(interp, flags, Tcl_NewStringObj("MULTICAST", -1)); - } - if ((flags_bitmask & IFF_PORTSEL) == IFF_PORTSEL) { - Tcl_ListObjAppendElement(interp, flags, Tcl_NewStringObj("PORTSEL", -1)); - } - if ((flags_bitmask & IFF_AUTOMEDIA) == IFF_AUTOMEDIA) { - Tcl_ListObjAppendElement(interp, flags, Tcl_NewStringObj("AUTOMEDIA", -1)); - } - if ((flags_bitmask & IFF_DYNAMIC) == IFF_DYNAMIC) { - Tcl_ListObjAppendElement(interp, flags, Tcl_NewStringObj("DYNAMIC", -1)); - } - if ((flags_bitmask & IFF_LOWER_UP) == IFF_LOWER_UP) { - Tcl_ListObjAppendElement(interp, flags, Tcl_NewStringObj("LOWER_UP", -1)); - } - if ((flags_bitmask & IFF_DORMANT) == IFF_DORMANT) { - Tcl_ListObjAppendElement(interp, flags, Tcl_NewStringObj("DORMANT", -1)); - } -#ifdef IFF_ECHO - if ((flags_bitmask & IFF_ECHO) == IFF_ECHO) { - Tcl_ListObjAppendElement(interp, flags, Tcl_NewStringObj("ECHO", -1)); - } -#endif - - /* Add array-compliant/dict entry to the return list */ - Tcl_ListObjAppendElement(interp, retlist, Tcl_NewStringObj("flags", -1)); - Tcl_ListObjAppendElement(interp, retlist, flags); - - /* Fetch other attributes from the interface */ - ioctl_ret = ioctl(sock, SIOCGIFHWADDR, &iface_req); - if (ioctl_ret == 0) { - link_encap = "unknown"; - - addr_data = (unsigned char *) iface_req.ifr_hwaddr.sa_data; - switch (iface_req.ifr_hwaddr.sa_family) { - case ARPHRD_ETHER: - link_encap = "ethernet"; - - Tcl_ListObjAppendElement(interp, retlist, Tcl_NewStringObj("hwaddr", -1)); - Tcl_ListObjAppendElement(interp, retlist, - Tcl_ObjPrintf("%02x:%02x:%02x:%02x:%02x:%02x", - addr_data[0], - addr_data[1], - addr_data[2], - addr_data[3], - addr_data[4], - addr_data[5] - ) - ); - - break; - case ARPHRD_AX25: - link_encap = "ax25"; - break; - case ARPHRD_PRONET: - link_encap = "pronet"; - break; - case ARPHRD_CHAOS: - link_encap = "chaos"; - break; - case ARPHRD_IEEE802: - link_encap = "ieee802"; - break; - case ARPHRD_ARCNET: - link_encap = "arcnet"; - break; - case ARPHRD_APPLETLK: - link_encap = "appletlk"; - break; - case ARPHRD_DLCI: - link_encap = "dlci"; - break; - case ARPHRD_ATM: - link_encap = "atm"; - break; - case ARPHRD_METRICOM: - link_encap = "metricom"; - break; - case ARPHRD_IEEE1394: - link_encap = "ieee1394"; - break; - case ARPHRD_EUI64: - link_encap = "eui64"; - break; - case ARPHRD_INFINIBAND: - link_encap = "infiniband"; - break; - case ARPHRD_SLIP: - link_encap = "slip"; - break; - case ARPHRD_CSLIP: - link_encap = "cslip"; - break; - case ARPHRD_SLIP6: - link_encap = "slip6"; - break; - case ARPHRD_CSLIP6: - link_encap = "cslip6"; - break; - case ARPHRD_RSRVD: - link_encap = "rsrvd"; - break; - case ARPHRD_ADAPT: - link_encap = "adapt"; - break; - case ARPHRD_ROSE: - link_encap = "rose"; - break; - case ARPHRD_X25: - link_encap = "x25"; - break; - case ARPHRD_HWX25: - link_encap = "hwx25"; - break; - case ARPHRD_CAN: - link_encap = "can"; - break; - case ARPHRD_PPP: - link_encap = "ppp"; - break; - case ARPHRD_CISCO: - link_encap = "cisco"; - break; - case ARPHRD_LAPB: - link_encap = "lapb"; - break; - case ARPHRD_DDCMP: - link_encap = "ddcmp"; - break; - case ARPHRD_RAWHDLC: - link_encap = "rawhdlc"; - break; - case ARPHRD_TUNNEL: - link_encap = "tunnel"; - break; - case ARPHRD_TUNNEL6: - link_encap = "tunnel6"; - break; - case ARPHRD_FRAD: - link_encap = "frad"; - break; - case ARPHRD_SKIP: - link_encap = "skip"; - break; - case ARPHRD_LOOPBACK: - link_encap = "loopback"; - break; - case ARPHRD_LOCALTLK: - link_encap = "localtalk"; - break; - case ARPHRD_FDDI: - link_encap = "fddi"; - break; - case ARPHRD_BIF: - link_encap = "bif"; - break; - case ARPHRD_SIT: - link_encap = "sit"; - break; - case ARPHRD_IPDDP: - link_encap = "ipddp"; - break; - case ARPHRD_IPGRE: - link_encap = "gre"; - break; - case ARPHRD_PIMREG: - link_encap = "pimreg"; - break; - case ARPHRD_HIPPI: - link_encap = "hippi"; - break; - case ARPHRD_ASH: - link_encap = "ash"; - break; - case ARPHRD_ECONET: - link_encap = "econet"; - break; - case ARPHRD_IRDA: - link_encap = "irda"; - break; - } - - Tcl_ListObjAppendElement(interp, retlist, Tcl_NewStringObj("link_encap", -1)); - Tcl_ListObjAppendElement(interp, retlist, Tcl_NewStringObj(link_encap, -1)); - } - - ioctl_ret = ioctl(sock, SIOCGIFMETRIC, &iface_req); - if (ioctl_ret == 0) { - Tcl_ListObjAppendElement(interp, retlist, Tcl_NewStringObj("metric", -1)); - Tcl_ListObjAppendElement(interp, retlist, Tcl_NewWideIntObj(iface_req.ifr_metric + 1)); - } - - ioctl_ret = ioctl(sock, SIOCGIFMTU, &iface_req); - if (ioctl_ret == 0) { - Tcl_ListObjAppendElement(interp, retlist, Tcl_NewStringObj("mtu", -1)); - Tcl_ListObjAppendElement(interp, retlist, Tcl_NewWideIntObj(iface_req.ifr_mtu)); - } - - ioctl_ret = ioctl(sock, SIOCGIFINDEX, &iface_req); - if (ioctl_ret == 0) { - Tcl_ListObjAppendElement(interp, retlist, Tcl_NewStringObj("index", -1)); - Tcl_ListObjAppendElement(interp, retlist, Tcl_NewWideIntObj(iface_req.ifr_ifindex)); - } - - if (sock_v4 != -1) { - ioctl_ret = ioctl(sock_v4, SIOCGIFADDR, &iface_req); - if (ioctl_ret == 0) { - tuapi_private_append_sockaddr_to_tclobj(interp, retlist, "address", &iface_req.ifr_addr); - } - - if (flag_pointopoint) { - /* Point-to-Point interfaces */ - ioctl_ret = ioctl(sock_v4, SIOCGIFDSTADDR, &iface_req); - if (ioctl_ret == 0) { - tuapi_private_append_sockaddr_to_tclobj(interp, retlist, "destination", &iface_req.ifr_addr); - } - } - - if (flag_broadcast) { - /* Broadcast interfaces */ - ioctl_ret = ioctl(sock_v4, SIOCGIFBRDADDR, &iface_req); - if (ioctl_ret == 0) { - tuapi_private_append_sockaddr_to_tclobj(interp, retlist, "broadcast", &iface_req.ifr_addr); - } - } - - ioctl_ret = ioctl(sock_v4, SIOCGIFNETMASK, &iface_req); - if (ioctl_ret == 0) { - tuapi_private_append_sockaddr_to_tclobj(interp, retlist, "netmask", &iface_req.ifr_addr); - } - } - - Tcl_SetObjResult(interp, retlist); - - return(TCL_OK); -} - -static int tuapi_ifconfig_conf(ClientData cd, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[], int sock, int sock_v4, int sock_v6) { - Tcl_Obj *option_name_obj, *option_val_obj; - Tcl_Obj **flags_objv; - struct ifreq iface_req; - struct sockaddr *tmp_ioctl_addr; - const char *iface; - short flags; - int flags_objc; - int tmp_sock, tmp_ioctl; - int ioctl_ret, tcl_ret, parse_ret; - - iface = Tcl_GetString(objv[1]); - - if ((strlen(iface) + 1) >= IFNAMSIZ) { - Tcl_SetObjResult(interp, Tcl_NewStringObj("interface name too long", -1)); - - return(TCL_ERROR); - } - - objc -= 2; - objv += 2; - - for (; objc > 0; objc--,objv++) { - /* Prepare for an ioctl() */ - strcpy(iface_req.ifr_name, iface); - tmp_ioctl = -1; - - option_name_obj = objv[0]; - - if (objc == 1) { - Tcl_SetObjResult(interp, Tcl_ObjPrintf("option \"%s\" requires an argument", Tcl_GetString(option_name_obj))); - - return(TCL_ERROR); - } - - objc--; - objv++; - - option_val_obj = objv[0]; - - switch (tuapi_internal_simplehash_obj(option_name_obj)) { - case 0x6d9870f3: /* flags */ - flags = 0; - - tcl_ret = Tcl_ListObjGetElements(interp, option_val_obj, &flags_objc, &flags_objv); - if (tcl_ret != TCL_OK) { - return(tcl_ret); - } - - for (; flags_objc > 0; flags_objc--,flags_objv++) { - switch (tuapi_internal_simplehash_obj(flags_objv[0])) { - case 0x2ad0: /* UP */ - flags |= IFF_UP; - break; - case 0x1aef7f54: /* BROADCAST */ - flags |= IFF_BROADCAST; - break; - case 0xc252abd4: /* POINTOPOINT */ - flags |= IFF_POINTOPOINT; - break; - case 0x48b0a8c7: /* DEBUG */ - flags |= IFF_DEBUG; - break; - case 0x4d3dbcd3: /* NOTRAILERS */ - flags |= IFF_NOTRAILERS; - break; - case 0xe9773147: /* RUNNING */ - flags |= IFF_RUNNING; - break; - case 0xe9f06b50: /* NOARP */ - flags |= IFF_NOARP; - break; - case 0xf91323c3: /* PROMISC */ - flags |= IFF_PROMISC; - break; - case 0x9b2a1849: /* ALLMULTI */ - flags |= IFF_ALLMULTI; - break; - case 0x1a7414d2: /* MASTER */ - flags |= IFF_MASTER; - break; - case 0x399069c5: /* SLAVE */ - flags |= IFF_SLAVE; - break; - case 0x4de928d4: /* MULTICAST */ - flags |= IFF_MULTICAST; - break; - case 0x2a35dc4c: /* PORTSEL */ - flags |= IFF_PORTSEL; - break; - case 0xd180ac1: /* AUTOMEDIA */ - flags |= IFF_AUTOMEDIA; - break; - case 0xe8ba02c3: /* DYNAMIC */ - flags |= IFF_DYNAMIC; - break; - case 0x16c8b4d0: /* LOWER_UP */ - flags |= IFF_LOWER_UP; - break; - case 0x293959d4: /* DORMANT */ - flags |= IFF_DORMANT; - break; -#ifdef IFF_ECHO - case 0x8b0e44f: /* ECHO */ - flags |= IFF_ECHO; - break; -#endif - } - } - - iface_req.ifr_flags = flags; - - ioctl_ret = ioctl(sock, SIOCSIFFLAGS, &iface_req); - if (ioctl_ret != 0) { - Tcl_SetObjResult(interp, Tcl_NewStringObj(strerror(errno), -1)); - - return(TCL_ERROR); - } - - break; - case 0x5e9d03e3: /* metric */ - case 0x1b7a75: /* mtu */ - case 0x7c3891f2: /* hwaddr */ - case 0xbf72a969: /* addmulti */ - case 0xba708969: /* delmulti */ - case 0xdd876e5: /* name */ - Tcl_SetObjResult(interp, Tcl_ObjPrintf("option \"%s\" unsupported", Tcl_GetString(option_name_obj))); - - return(TCL_ERROR); - break; - case 0x4e9aeaf3: /* address */ - if (tmp_ioctl == -1) { - tmp_ioctl = SIOCSIFADDR; - tmp_ioctl_addr = &iface_req.ifr_addr; - } - - case 0xec05706e: /* destination */ - if (tmp_ioctl == -1) { - tmp_ioctl = SIOCSIFDSTADDR; - tmp_ioctl_addr = &iface_req.ifr_dstaddr; - } - - case 0x3ea7e674: /* broadcast */ - if (tmp_ioctl == -1) { - tmp_ioctl = SIOCSIFBRDADDR; - tmp_ioctl_addr = &iface_req.ifr_broadaddr; - } - - case 0x4d65ee6b: /* netmask */ - if (tmp_ioctl == -1) { - tmp_ioctl = SIOCSIFNETMASK; - tmp_ioctl_addr = &iface_req.ifr_netmask; - } - - parse_ret = tuapi_private_get_sockaddr_from_obj(option_val_obj, tmp_ioctl_addr); - if (parse_ret != 0) { - Tcl_SetObjResult(interp, Tcl_ObjPrintf("unable to parse \"%s\" as an address", Tcl_GetString(option_val_obj))); - - return(TCL_ERROR); - } - - switch (tmp_ioctl_addr->sa_family) { - case AF_INET: - tmp_sock = sock_v4; - - break; - case AF_INET6: - tmp_sock = sock_v6; - - break; - default: - Tcl_SetObjResult(interp, Tcl_NewStringObj("unable to determine address family of sockaddr", -1)); - return(TCL_ERROR); - } - - ioctl_ret = ioctl(tmp_sock, tmp_ioctl, &iface_req); - if (ioctl_ret != 0) { - Tcl_SetObjResult(interp, Tcl_NewStringObj(strerror(errno), -1)); - - return(TCL_ERROR); - } - - break; - default: - Tcl_SetObjResult(interp, Tcl_ObjPrintf("unknown option \"%s\"", Tcl_GetString(option_name_obj))); - - return(TCL_ERROR); - } - } - - return(TCL_OK); -} - -static int tuapi_ifconfig(ClientData cd, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[]) { - int sock_v4, sock_v6, sock; - int retval = TCL_ERROR; - - sock = tuapi_internal_getsock(&sock_v4, &sock_v6); - if (sock == -1) { - Tcl_SetObjResult(interp, Tcl_NewStringObj("unable to create socket", -1)); - - return(TCL_ERROR); - } - - switch (objc) { - case 0: - case 1: /* No arguments, list all interfaces */ - retval = tuapi_ifconfig_list(cd, interp, objc, objv, sock); - - break; - case 2: /* One argument, give information about the interface */ - retval = tuapi_ifconfig_info(cd, interp, objc, objv, sock, sock_v4, sock_v6); - - break; - default: - /* Otherwise, configure the interace */ - retval = tuapi_ifconfig_conf(cd, interp, objc, objv, sock, sock_v4, sock_v6); - - break; - } - - /* Cleanup */ - if (sock_v4 != -1) { - close(sock_v4); - } - - if (sock_v6 != -1) { - close(sock_v6); - } - - return(retval); -} - -static int tuapi_route_list(ClientData cd, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[], int sock_v4, int sock_v6) { - Tcl_SetObjResult(interp, Tcl_NewStringObj("not implemented", -1)); - - return(TCL_ERROR); -} - -static int tuapi_route_conf(ClientData cd, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[], int sock_v4, int sock_v6) { - Tcl_WideInt option_val_wide; - Tcl_Obj *operation_obj, *dest_obj, *destmask_obj; - Tcl_Obj *option_name_obj, *option_val_obj; - struct rtentry route; - int sock; - int ioctl_id; - int tcl_ret, ioctl_ret, parse_ret; - - if (objc < 4) { - Tcl_SetObjResult(interp, Tcl_NewStringObj("wrong # args: should be \"::tuapi::syscall::route operation destination destination_mask ?options?\"", -1)); - - return(TCL_ERROR); - } - - /* Clear object values */ - memset(&route, 0, sizeof(route)); - - /* Determine operation */ - operation_obj = objv[1]; - switch (tuapi_internal_simplehash_obj(operation_obj)) { - case 0x187264: /* add */ - ioctl_id = SIOCADDRT; - break; - case 0x1932ec: /* del */ - case 0x5d98e965: /* delete */ - ioctl_id = SIOCDELRT; - break; - default: - Tcl_SetObjResult(interp, Tcl_ObjPrintf("bad option \"%s\": must be add, or delete", Tcl_GetString(operation_obj))); - - return(TCL_ERROR); - } - - /* Set default flags */ - route.rt_flags = RTF_UP; - - /* Parse destination address */ - dest_obj = objv[2]; - parse_ret = tuapi_private_get_sockaddr_from_obj(dest_obj, &route.rt_dst); - if (parse_ret != 0) { - Tcl_SetObjResult(interp, Tcl_ObjPrintf("unable to parse \"%s\" as an address", Tcl_GetString(dest_obj))); - - return(TCL_ERROR); - } - - /* Parse destination netmask */ - destmask_obj = objv[3]; - parse_ret = tuapi_private_get_sockaddr_from_obj(destmask_obj, &route.rt_genmask); - if (parse_ret != 0) { - Tcl_SetObjResult(interp, Tcl_ObjPrintf("unable to parse \"%s\" as an address", Tcl_GetString(destmask_obj))); - - return(TCL_ERROR); - } - - if (route.rt_dst.sa_family != route.rt_genmask.sa_family) { - Tcl_SetObjResult(interp, - Tcl_ObjPrintf("destination (\"%s\") and destination_mask (\"%s\") are different classes", - Tcl_GetString(dest_obj), - Tcl_GetString(destmask_obj) - ) - ); - - return(TCL_ERROR); - } - - switch (route.rt_dst.sa_family) { - case AF_INET: /* IPv4 */ - if (sock_v4 == -1) { - Tcl_SetObjResult(interp, Tcl_ObjPrintf("address \"%s\" is IPv4, but unable to create IPv4 socket", Tcl_GetString(dest_obj))); - - return(TCL_ERROR); - } - - if (((struct sockaddr_in *) &route.rt_genmask)->sin_addr.s_addr == INADDR_BROADCAST) { - route.rt_flags |= RTF_HOST; - } - - sock = sock_v4; - - break; - case AF_INET6: /* IPv6 */ - if (sock_v6 == -1) { - Tcl_SetObjResult(interp, Tcl_ObjPrintf("address \"%s\" is IPv6, but unable to create IPv6 socket", Tcl_GetString(dest_obj))); - - return(TCL_ERROR); - } - - sock = sock_v6; - - break; - default: - Tcl_SetObjResult(interp, Tcl_ObjPrintf("unable to determine type of address for \"%s\"", Tcl_GetString(dest_obj))); - - return(TCL_ERROR); - } - - /* Parse remaining options */ - objc -= 4; - objv += 4; - - for (; objc > 0; objc--,objv++) { - option_name_obj = objv[0]; - - if (objc < 2) { - Tcl_SetObjResult(interp, Tcl_ObjPrintf("option \"%s\" requires an argument", Tcl_GetString(option_name_obj))); - - return(TCL_ERROR); - } - - objc--; - objv++; - - option_val_obj = objv[0]; - - switch (tuapi_internal_simplehash_obj(option_name_obj)) { - case 0x4c727779: /* gateway */ - parse_ret = tuapi_private_get_sockaddr_from_obj(option_val_obj, &route.rt_gateway); - if (parse_ret != 0) { - Tcl_SetObjResult(interp, Tcl_ObjPrintf("unable to parse \"%s\" as an address", Tcl_GetString(option_val_obj))); - - return(TCL_ERROR); - } - - route.rt_flags &= (~RTF_HOST); - route.rt_flags |= RTF_GATEWAY; - - break; - case 0x1b7a75: /* mtu */ - tcl_ret = Tcl_GetWideIntFromObj(interp, option_val_obj, &option_val_wide); - if (tcl_ret != TCL_OK) { - return(tcl_ret); - } - - route.rt_flags |= RTF_MTU; - route.rt_mtu = option_val_wide; - - break; - case 0x5e9d03e3: /* metric */ - tcl_ret = Tcl_GetWideIntFromObj(interp, option_val_obj, &option_val_wide); - if (tcl_ret != TCL_OK) { - return(tcl_ret); - } - - route.rt_metric = option_val_wide; - - break; - case 0x9dd8e8f7: /* window */ - tcl_ret = Tcl_GetWideIntFromObj(interp, option_val_obj, &option_val_wide); - if (tcl_ret != TCL_OK) { - return(tcl_ret); - } - - route.rt_flags |= RTF_WINDOW; - route.rt_window = option_val_wide; - - break; - case 0x1932f6: /* dev */ - case 0x5edbe2e5: /* device */ - route.rt_dev = strdup(Tcl_GetString(option_val_obj)); - - break; - default: - Tcl_SetObjResult(interp, Tcl_ObjPrintf("bad option \"%s\": must be gateway, mtu, metric, device, or window", Tcl_GetString(option_name_obj))); - - return(TCL_ERROR); - } - } - - /* Request route change */ - ioctl_ret = ioctl(sock, ioctl_id, &route); - if (ioctl_ret != 0) { - Tcl_SetObjResult(interp, Tcl_NewStringObj(strerror(errno), -1)); - - return(TCL_ERROR); - } - - return(TCL_OK); -} - -static int tuapi_route(ClientData cd, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[]) { - int sock_v4, sock_v6, sock; - int retval = TCL_ERROR; - - sock = tuapi_internal_getsock(&sock_v4, &sock_v6); - if (sock == -1) { - Tcl_SetObjResult(interp, Tcl_NewStringObj("unable to create socket", -1)); - - return(TCL_ERROR); - } - - switch (objc) { - case 0: - case 1: /* No arguments, list all interfaces */ - retval = tuapi_route_list(cd, interp, objc, objv, sock_v4, sock_v6); - - break; - default: - /* Otherwise, modify routes */ - retval = tuapi_route_conf(cd, interp, objc, objv, sock_v4, sock_v6); - - break; - } - - /* Cleanup */ - if (sock_v4 != -1) { - close(sock_v4); - } - - if (sock_v6 != -1) { - close(sock_v6); - } - - return(retval); -} - -static int tuapi_brctl_list(ClientData cd, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[], int sock) { - Tcl_SetObjResult(interp, Tcl_NewStringObj("not implemented", -1)); - - return(TCL_ERROR); -} - -static int tuapi_brctl_conf(ClientData cd, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[], int sock) { - Tcl_Obj *operation_obj, *bridge_name_obj, *interface_name_obj; - unsigned long arg[4]; - struct ifreq ifr; - int ioctl_ret, ioctl_id; - int add = 0; - - /* Determine operation */ - operation_obj = objv[1]; - switch (tuapi_internal_simplehash_obj(operation_obj)) { - case 0x1c993272: /* addbr */ - add = 1; - case 0x4cbb3272: /* delbr */ - if (objc != 3) { - if (add) { - Tcl_SetObjResult(interp, Tcl_NewStringObj("wrong # args: should be \"::tuapi::syscall::brctl addbr bridge\"", -1)); - } else { - Tcl_SetObjResult(interp, Tcl_NewStringObj("wrong # args: should be \"::tuapi::syscall::brctl delbr bridge\"", -1)); - } - - return(TCL_ERROR); - } - - bridge_name_obj = objv[2]; - - if (add) { - arg[0] = BRCTL_ADD_BRIDGE; - } else { - arg[0] = BRCTL_DEL_BRIDGE; - } - - arg[1] = (unsigned long) Tcl_GetString(bridge_name_obj); - arg[2] = 0; - - ioctl_ret = ioctl(sock, SIOCGIFBR, &arg); - - break; - case 0x1C9937E6: /* addif */ - add = 1; - case 0x4cbb37e6: /* delif */ - if (objc != 4) { - if (add) { - Tcl_SetObjResult(interp, Tcl_NewStringObj("wrong # args: should be \"::tuapi::syscall::brctl addif bridge interface\"", -1)); - } else { - Tcl_SetObjResult(interp, Tcl_NewStringObj("wrong # args: should be \"::tuapi::syscall::brctl delif bridge interface\"", -1)); - } - - return(TCL_ERROR); - } - - if (add) { - ioctl_id = SIOCBRADDIF; - } else { - ioctl_id = SIOCBRDELIF; - } - - bridge_name_obj = objv[2]; - interface_name_obj = objv[3]; - - memset(&ifr, 0, sizeof(ifr)); - snprintf(ifr.ifr_name, IFNAMSIZ, "%s", Tcl_GetString(interface_name_obj)); - - ioctl_ret = ioctl(sock, SIOCGIFINDEX, (void *) &ifr); - if (ioctl_ret == 0) { - snprintf(ifr.ifr_name, IFNAMSIZ, "%s", Tcl_GetString(bridge_name_obj)); - ioctl_ret = ioctl(sock, ioctl_id, (void *) &ifr); - } - - break; - } - - if (ioctl_ret < 0) { - Tcl_SetObjResult(interp, Tcl_NewStringObj(strerror(errno), -1)); - - return(TCL_ERROR); - } - - return(TCL_OK); -} - -static int tuapi_brctl(ClientData cd, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[]) { - int sock_v4, sock_v6, sock; - int retval = TCL_ERROR; - - sock = tuapi_internal_getsock(&sock_v4, &sock_v6); - if (sock == -1) { - Tcl_SetObjResult(interp, Tcl_NewStringObj("unable to create socket", -1)); - - return(TCL_ERROR); - } - - switch (objc) { - case 0: - case 1: /* No arguments, list all bridges */ - retval = tuapi_brctl_list(cd, interp, objc, objv, sock); - - break; - default: - /* Otherwise, modify routes */ - retval = tuapi_brctl_conf(cd, interp, objc, objv, sock); - - break; - } - - /* Cleanup */ - if (sock_v4 != -1) { - close(sock_v4); - } - - if (sock_v6 != -1) { - close(sock_v6); - } - - return(retval); -} - -static int tuapi_vconfig(ClientData cd, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[]) { - int sock_v4, sock_v6, sock; - int retval = TCL_ERROR; - - sock = tuapi_internal_getsock(&sock_v4, &sock_v6); - if (sock == -1) { - Tcl_SetObjResult(interp, Tcl_NewStringObj("unable to create socket", -1)); - - return(TCL_ERROR); - } - - Tcl_SetObjResult(interp, Tcl_NewStringObj("not implemented", -1)); - - /* Cleanup */ - if (sock_v4 != -1) { - close(sock_v4); - } - - if (sock_v6 != -1) { - close(sock_v6); - } - - return(retval); -} - -#ifndef DISABLE_UNIX_SOCKETS -struct tuapi_socket_unix__chan_id { - int fd; - Tcl_Channel chan; -}; - -static int tuapi_socket_unix__chan_close(ClientData id_p, Tcl_Interp *interp) { - struct tuapi_socket_unix__chan_id *id; - int fd; - - id = id_p; - - fd = id->fd; - - close(fd); - - free(id); - - return(0); -} - -static int tuapi_socket_unix__chan_read(ClientData id_p, char *buf, int bufsize, int *errorCodePtr) { - struct tuapi_socket_unix__chan_id *id; - ssize_t read_ret; - int fd; - int retval; - - id = id_p; - - fd = id->fd; - - read_ret = read(fd, buf, bufsize); - if (read_ret < 0) { - *errorCodePtr = errno; - - return(-1); - } - - retval = read_ret; - - return(retval); -} - -static int tuapi_socket_unix__chan_write(ClientData id_p, const char *buf, int toWrite, int *errorCodePtr) { - struct tuapi_socket_unix__chan_id *id; - ssize_t write_ret; - int fd; - int bytesWritten; - - id = id_p; - - fd = id->fd; - - bytesWritten = 0; - while (toWrite) { - write_ret = write(fd, buf, toWrite); - if (write_ret == 0) { - break; - } - - if (write_ret < 0) { - *errorCodePtr = errno; - - return(-1); - } - - toWrite -= write_ret; - buf += write_ret; - bytesWritten += write_ret; - } - - if (bytesWritten == 0) { - *errorCodePtr = EAGAIN; - - return(-1); - } - - return(bytesWritten); -} - -static void tuapi_socket_unix__chan_eventhandler(ClientData id_p, int mask) { - struct tuapi_socket_unix__chan_id *id; - Tcl_Channel chan; - - id = id_p; - - chan = id->chan; - - if (!chan) { - return; - } - - Tcl_NotifyChannel(chan, mask); -} - -static void tuapi_socket_unix__chan_watch(ClientData id_p, int mask) { - struct tuapi_socket_unix__chan_id *id; - int fd; - - id = id_p; - - fd = id->fd; - - Tcl_CreateFileHandler(fd, mask, tuapi_socket_unix__chan_eventhandler, id); - - return; -} - -static int tuapi_socket_unix__chan_gethandle(ClientData id_p, int direction, ClientData *handlePtr) { - struct tuapi_socket_unix__chan_id *id; - int fd; - ClientData fd_cd; - - id = id_p; - - fd = id->fd; - - memcpy(&fd_cd, &fd, sizeof(fd)); - - *handlePtr = fd_cd; - - return(TCL_OK); -} - -static Tcl_Channel tuapi_socket_unix_sock2tclchan(int sock) { - struct tuapi_socket_unix__chan_id *id; - static Tcl_ChannelType tcl_chan_type; - static int tcl_chan_type_init = 0; - Tcl_Channel tcl_chan; - char chan_name[32]; - int sock_flags; - - if (!tcl_chan_type_init) { - tcl_chan_type.typeName = "socket"; - tcl_chan_type.version = TCL_CHANNEL_VERSION_2; - tcl_chan_type.closeProc = tuapi_socket_unix__chan_close; - tcl_chan_type.inputProc = tuapi_socket_unix__chan_read; - tcl_chan_type.outputProc = tuapi_socket_unix__chan_write; - tcl_chan_type.watchProc = tuapi_socket_unix__chan_watch; - tcl_chan_type.getHandleProc = tuapi_socket_unix__chan_gethandle; - tcl_chan_type.seekProc = NULL; - tcl_chan_type.setOptionProc = NULL; - tcl_chan_type.getOptionProc = NULL; - tcl_chan_type.close2Proc = NULL; - tcl_chan_type.blockModeProc = NULL; - tcl_chan_type.flushProc = NULL; - tcl_chan_type.handlerProc = NULL; - tcl_chan_type.wideSeekProc = NULL; - tcl_chan_type.threadActionProc = NULL; - tcl_chan_type.truncateProc = NULL; - - tcl_chan_type_init = 1; - } - - snprintf(chan_name, sizeof(chan_name), "sock%u", sock); - - id = malloc(sizeof(*id)); - if (id == NULL) { - return(NULL); - } - - id->fd = sock; - id->chan = NULL; - - /* Configure socket as non-blocking */ - sock_flags = fcntl(sock, F_GETFL, 0); - if (sock_flags == -1) { - sock_flags = O_NONBLOCK; - } else { - sock_flags |= O_NONBLOCK; - } - fcntl(sock, F_SETFL, (long) sock_flags); - - /* Create the channel */ - tcl_chan = Tcl_CreateChannel(&tcl_chan_type, chan_name, id, TCL_READABLE | TCL_WRITABLE); - - /* Update the structure passed to each function to include the channel name */ - id->chan = tcl_chan; - - return(tcl_chan); -} - -struct tuapi_socket_unix__chan_accept_cd { - int fd; - Tcl_Interp *interp; - Tcl_Obj *command; -}; - -static void tuapi_socket_unix__chan_accept(ClientData cd_p, int mask) { - struct tuapi_socket_unix__chan_accept_cd *cd; - Tcl_Interp *interp; - Tcl_Channel chan; - Tcl_Obj *command, *command_to_run_objs[5], *command_to_run; - int setsockopt_ret; - int pass_creds_true = 1; - int fd; - int sock; - - if ((mask & TCL_READABLE) != TCL_READABLE) { - return; - } - - cd = cd_p; - - fd = cd->fd; - interp = cd->interp; - command = cd->command; - - sock = accept(fd, NULL, NULL); - if (sock < 0) { - return; - } - - chan = tuapi_socket_unix_sock2tclchan(sock); - if (chan == NULL) { - close(sock); - - return; - } - - setsockopt_ret = setsockopt(sock, SOL_SOCKET, SO_PASSCRED, &pass_creds_true, sizeof(pass_creds_true)); - if (setsockopt_ret != 0) { - close(sock); - - return; - } - - Tcl_RegisterChannel(interp, chan); - - command_to_run_objs[0] = command; - command_to_run_objs[1] = Tcl_NewStringObj(Tcl_GetChannelName(chan), -1); - command_to_run_objs[2] = Tcl_NewStringObj("...uid...", -1); /* XXX: TODO */ - command_to_run_objs[3] = Tcl_NewStringObj("...gid...", -1); /* XXX: TODO */ - command_to_run_objs[4] = Tcl_NewStringObj("...pid...", -1); /* XXX: TODO */ - command_to_run = Tcl_ConcatObj(sizeof(command_to_run_objs) / sizeof(command_to_run_objs[0]), command_to_run_objs); - - Tcl_EvalObjEx(interp, command_to_run, TCL_EVAL_GLOBAL); - - return; -} - -static int tuapi_socket_unix_server(ClientData cd, Tcl_Interp *interp, int sock, const char *path, Tcl_Obj *command) { - struct tuapi_socket_unix__chan_accept_cd *accept_cd; - struct sockaddr_un dest; - ssize_t pathlen; - int bind_ret, listen_ret; - - pathlen = strlen(path) + 1; - if (pathlen <= 0) { - Tcl_SetObjResult(interp, Tcl_NewStringObj("path too short", -1)); - - return(TCL_ERROR); - } - - if (pathlen > sizeof(dest.sun_path)) { - Tcl_SetObjResult(interp, Tcl_NewStringObj("path too long", -1)); - - return(TCL_ERROR); - } - - dest.sun_family = AF_UNIX; - memcpy(dest.sun_path, path, pathlen); - - bind_ret = bind(sock, (struct sockaddr *) &dest, sizeof(dest)); - if (bind_ret != 0) { - Tcl_SetObjResult(interp, Tcl_NewStringObj(strerror(errno), -1)); - - return(TCL_ERROR); - } - - listen_ret = listen(sock, 2); - if (listen_ret != 0) { - Tcl_SetObjResult(interp, Tcl_NewStringObj(strerror(errno), -1)); - - return(TCL_ERROR); - } - - accept_cd = malloc(sizeof(*accept_cd)); - if (accept_cd == NULL) { - Tcl_SetObjResult(interp, Tcl_NewStringObj(strerror(errno), -1)); - - return(TCL_ERROR); - } - - accept_cd->fd = sock; - accept_cd->interp = interp; - accept_cd->command = command; - - Tcl_IncrRefCount(command); - - Tcl_CreateFileHandler(sock, TCL_READABLE, tuapi_socket_unix__chan_accept, accept_cd); - - return(TCL_OK); -} - -static int tuapi_socket_unix_client(ClientData cd, Tcl_Interp *interp, int sock, const char *path) { - Tcl_Channel chan; - struct sockaddr_un dest; - ssize_t pathlen; - int connect_ret, setsockopt_ret; - int pass_creds_true = 1; - - pathlen = strlen(path) + 1; - if (pathlen <= 0) { - Tcl_SetObjResult(interp, Tcl_NewStringObj("path too short", -1)); - - return(TCL_ERROR); - } - - if (pathlen > sizeof(dest.sun_path)) { - Tcl_SetObjResult(interp, Tcl_NewStringObj("path too long", -1)); - - return(TCL_ERROR); - } - - dest.sun_family = AF_UNIX; - memcpy(dest.sun_path, path, pathlen); - - connect_ret = connect(sock, (struct sockaddr *) &dest, sizeof(dest)); - if (connect_ret != 0) { - Tcl_SetObjResult(interp, Tcl_NewStringObj(strerror(errno), -1)); - - return(TCL_ERROR); - } - - setsockopt_ret = setsockopt(sock, SOL_SOCKET, SO_PASSCRED, &pass_creds_true, sizeof(pass_creds_true)); - if (setsockopt_ret != 0) { - Tcl_SetObjResult(interp, Tcl_NewStringObj(strerror(errno), -1)); - - return(TCL_ERROR); - } - - chan = tuapi_socket_unix_sock2tclchan(sock); - if (chan == NULL) { - Tcl_SetObjResult(interp, Tcl_NewStringObj("unable to create Tcl channel", -1)); - - return(TCL_ERROR); - } - - Tcl_RegisterChannel(interp, chan); - - Tcl_SetObjResult(interp, Tcl_NewStringObj(Tcl_GetChannelName(chan), -1)); - - return(TCL_OK); -} - -static int tuapi_socket_unix(ClientData cd, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[]) { - Tcl_Obj *path_obj, *command_obj; - char *path; - int retval; - int sock; - - if (objc < 2) { - Tcl_SetObjResult(interp, Tcl_NewStringObj("wrong # args: should be \"::tuapi::syscall::socket_unix path\" or \"::tuapi::syscall::socket_unix -server command path\"", -1)); - - return(TCL_ERROR); - } - - path_obj = objv[1]; - path = Tcl_GetString(path_obj); - - sock = socket(AF_UNIX, SOCK_STREAM, 0); - if (sock == -1) { - Tcl_SetObjResult(interp, Tcl_NewStringObj(strerror(errno), -1)); - - return(TCL_ERROR); - } - - if (strcmp(path, "-server") == 0) { - if (objc != 4) { - Tcl_SetObjResult(interp, Tcl_NewStringObj("wrong # args: should be \"::tuapi::syscall::socket_unix -server command path\"", -1)); - - close(sock); - - return(TCL_ERROR); - } - - command_obj = objv[2]; - path_obj = objv[3]; - - path = Tcl_GetString(path_obj); - - retval = tuapi_socket_unix_server(cd, interp, sock, path, command_obj); - } else { - if (objc != 2) { - Tcl_SetObjResult(interp, Tcl_NewStringObj("wrong # args: should be \"::tuapi::syscall::socket_unix path\"", -1)); - - close(sock); - - return(TCL_ERROR); - } - - retval = tuapi_socket_unix_client(cd, interp, sock, path); - } - - if (retval != TCL_OK) { - close(sock); - } - - return(retval); -} -#else -static int tuapi_socket_unix(ClientData cd, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[]) { - Tcl_SetObjResult(interp, Tcl_NewStringObj("not implemented", -1)); - return(TCL_ERROR) -} -#endif - -static int tuapi_tsmf_start_svc(ClientData cd, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[]) { - struct timeval select_timeout; - Tcl_WideInt umask_val, timeout_val, uid_val, gid_val; - Tcl_Obj *filename_obj, *env_obj, *logfile_obj, **env_entry_objv, *cwd_obj, *umask_obj, *uid_obj, *gid_obj; - Tcl_Obj *sri_obj, *timeout_obj; - pid_t child, child_pgid = -1, waitpid_ret; - ssize_t read_ret; - time_t currtime; - char *argv[3], *envv[512]; - char *logfile, *filename, *cwd; - char logmsg[2048]; - fd_set read_fdset; - int pipe_ret, setsid_ret, execve_ret, tcl_ret, select_ret, chdir_ret; - int null_fd, log_fd, tmp_fd, max_fd; - int env_entry_objc; - int fds[2], fd; - int status; - int idx; - - /* 1. Parse arguments */ - /* 1.a. Ensure the correct number of arguments were passed */ - if (objc != 10) { - Tcl_SetObjResult(interp, Tcl_NewStringObj("wrong # args: should be \"::tuapi::syscall::tsmf_start_svc sri filename logfile env cwd umask uid gid timeout\"", -1)); - - return(TCL_ERROR); - } - - /* 1.b. Identify Tcl_Objs to use for each argument */ - sri_obj = objv[1]; - filename_obj = objv[2]; - logfile_obj = objv[3]; - env_obj = objv[4]; - cwd_obj = objv[5]; - umask_obj = objv[6]; - uid_obj = objv[7]; - gid_obj = objv[8]; - timeout_obj = objv[9]; - - /* 1.c. Store string arguments */ - filename = Tcl_GetString(filename_obj); - logfile = Tcl_GetString(logfile_obj); - cwd = Tcl_GetString(cwd_obj); - - /* 1.d. Integer objects */ - tcl_ret = Tcl_GetWideIntFromObj(interp, umask_obj, &umask_val); - if (tcl_ret != TCL_OK) { - return(tcl_ret); - } - - tcl_ret = Tcl_GetWideIntFromObj(interp, timeout_obj, &timeout_val); - if (tcl_ret != TCL_OK) { - return(tcl_ret); - } - - tcl_ret = Tcl_GetWideIntFromObj(interp, uid_obj, &uid_val); - if (tcl_ret != TCL_OK) { - return(tcl_ret); - } - - tcl_ret = Tcl_GetWideIntFromObj(interp, gid_obj, &gid_val); - if (tcl_ret != TCL_OK) { - return(tcl_ret); - } - - /* 1.e. Process environment */ - tcl_ret = Tcl_ListObjGetElements(interp, env_obj, &env_entry_objc, &env_entry_objv); - if (tcl_ret != TCL_OK) { - return(tcl_ret); - } - - for (idx = 0; idx < MIN(env_entry_objc, sizeof(envv) / sizeof(envv[0]) - 1); idx++) { - envv[idx] = Tcl_GetString(env_entry_objv[idx]); - } - envv[idx] = NULL; - - /* 2. Create a pipe for communication between the processes */ - pipe_ret = pipe(fds); - if (pipe_ret != 0) { - Tcl_SetObjResult(interp, Tcl_NewStringObj("pipe failed", -1)); - - return(TCL_ERROR); - } - - /* 3. Fork into a new process */ - child = fork(); - if (child == -1) { - Tcl_SetObjResult(interp, Tcl_NewStringObj("fork failed", -1)); - - return(TCL_ERROR); - } - - if (child != 0) { - /* 4.parent. Get PGID from child */ - /* 4.parent.a. Close write end of pipe -- we are read-only */ - close(fds[1]); - fd = fds[0]; - - /* 4.parent.b. Read process group ID of child from pipe */ - select_timeout.tv_sec = timeout_val; - select_timeout.tv_usec = 0; - - FD_ZERO(&read_fdset); - FD_SET(fd, &read_fdset); - - select_ret = select(fd + 1, &read_fdset, NULL, NULL, &select_timeout); - if (select_ret == 0) { - /* On timeout, terminate starting process */ - child_pgid = getpgid(child); - if (child_pgid != -1) { - kill(-child_pgid, SIGKILL); - } - - Tcl_SetObjResult(interp, Tcl_NewStringObj("timeout", -1)); - - return(TCL_ERROR); - } - - if (select_ret > 0) { - read_ret = read(fd, &child_pgid, sizeof(child_pgid)); - } - - /* 4.parent.c. Close read end of pipe */ - close(fd); - - /* 4.parent.d. Verify read was meaningful */ - if (read_ret != sizeof(child_pgid)) { - Tcl_SetObjResult(interp, Tcl_NewStringObj("failed to communicate with started service", -1)); - - return(TCL_ERROR); - } - - /* 4.parent.e. If the PGID given is actually an error, return error */ - if (child_pgid == -1) { - Tcl_SetObjResult(interp, Tcl_NewStringObj("service failed to start", -1)); - - return(TCL_ERROR); - } - - /* 4.parent.f. Return PGID to Tcl */ - Tcl_SetObjResult(interp, Tcl_NewWideIntObj((Tcl_WideInt) child_pgid)); - - return(TCL_OK); - } - - /* 4.child.a. Close read end of pipe -- we only write to it */ - close(fds[0]); - fd = fds[1]; - - /* 5. Create a new session */ - setsid_ret = setsid(); - if (setsid_ret == -1) { - write(fd, &child_pgid, sizeof(child_pgid)); - - _exit(0); - } - - /* 6. Setup environment */ - /* 6.a. Set umask */ - umask(umask_val); - - /* 6.b. Set working directory */ - chdir_ret = chdir(cwd); - if (chdir_ret != 0) { - write(fd, &child_pgid, sizeof(child_pgid)); - - _exit(0); - } - - /* 6.c. Open log file for stderr and stdout */ - log_fd = open(logfile, O_WRONLY | O_CREAT | O_APPEND, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH); - - /* 6.d. Open "/dev/null" for stdin */ - null_fd = open("/dev/null", O_RDONLY); - if (null_fd < 0 || log_fd <0) { - write(fd, &child_pgid, sizeof(child_pgid)); - - _exit(0); - } - - /* 6.e. Redirect stdin, stdout, and stderr to null, logs */ - dup2(null_fd, STDIN_FILENO); - dup2(log_fd, STDOUT_FILENO); - dup2(log_fd, STDERR_FILENO); - - close(null_fd); - close(log_fd); - - /* 6.f. Close stray file descriptors */ - max_fd = MAX(MAX(MAX(1024, STDIN_FILENO), STDOUT_FILENO), STDERR_FILENO); - for (tmp_fd = 0; tmp_fd < max_fd; tmp_fd++) { - if (tmp_fd == STDIN_FILENO || tmp_fd == STDOUT_FILENO || tmp_fd == STDERR_FILENO) { - continue; - } - - if (tmp_fd == fd) { - continue; - } - - close(tmp_fd); - } - - /* 6.g. Switch to appropriate user/group */ - /* 6.g.i. Group */ - setgid(gid_val); - - /* 6.g.ii. User */ - setuid(uid_val); - - /* 7. Create a new process to actually spawn the process */ - child = fork(); - if (child == -1) { - write(fd, &child_pgid, sizeof(child_pgid)); - - _exit(0); - } - - if (child != 0) { - /* 7.parent.a. Wait for child process to terminate and collect status */ - waitpid_ret = waitpid(child, &status, 0); - if (waitpid_ret == -1) { - status = -1; - } - - /* 7.parent.b. Set PGID (if successful, -1 otherwise) to pass back to TSMF */ - if (status == 0) { - child_pgid = getpgid(getpid()); - } - write(fd, &child_pgid, sizeof(child_pgid)); - - close(fd); - - /* 7.parent.c. Write log of result */ - /* Note: We avoid ANSI I/O here in case there is already something in the buffer */ - currtime = time(NULL); - strftime(logmsg, sizeof(logmsg), "[ %b %e %H:%M:%S ", localtime(&currtime)); - write(STDERR_FILENO, logmsg, strlen(logmsg)); - - snprintf(logmsg, sizeof(logmsg), "Method \"start\" exited with status %i ]\n", WEXITSTATUS(status)); - write(STDERR_FILENO, logmsg, strlen(logmsg)); - - _exit(0); - } - - /* 7.child.a. Close channel to parent */ - close(fd); - - /* 8. Log attempt to run start method */ - currtime = time(NULL); - strftime(logmsg, sizeof(logmsg), "[ %b %e %H:%M:%S ", localtime(&currtime)); - write(STDERR_FILENO, logmsg, strlen(logmsg)); - - snprintf(logmsg, sizeof(logmsg), "Executing start method (\"%s\") ]\n", filename); - write(STDERR_FILENO, logmsg, strlen(logmsg)); - - /* 9. execve() new image */ - argv[0] = filename; - argv[1] = "start"; - argv[2] = NULL; - execve_ret = execve(filename, argv, envv); - - /* 10. Abort if something has gone wrong */ - _exit(execve_ret); - - /* Handle lint-ness */ - return(TCL_ERROR); - sri_obj = sri_obj; -} - -int Tuapi_Init(Tcl_Interp *interp) { -#ifdef USE_TCL_STUBS - const char *tclInitStubs_ret; - - /* Initialize Stubs */ - tclInitStubs_ret = Tcl_InitStubs(interp, "8.4", 0); - if (!tclInitStubs_ret) { - return(TCL_ERROR); - } -#endif - - /* Kernel maintenance related commands */ - Tcl_CreateObjCommand(interp, "::tuapi::syscall::insmod", tuapi_insmod, NULL, NULL); - Tcl_CreateObjCommand(interp, "::tuapi::syscall::rmmod", tuapi_rmmod, NULL, NULL); - Tcl_CreateObjCommand(interp, "::tuapi::syscall::lsmod", tuapi_lsmod, NULL, NULL); - Tcl_CreateObjCommand(interp, "::tuapi::syscall::hostname", tuapi_hostname, NULL, NULL); - Tcl_CreateObjCommand(interp, "::tuapi::syscall::domainname", tuapi_domainname, NULL, NULL); - - /* Block or char device related commands */ - Tcl_CreateObjCommand(interp, "::tuapi::syscall::losetup", tuapi_losetup, NULL, NULL); - - /* Filesystem related commands */ - Tcl_CreateObjCommand(interp, "::tuapi::syscall::mount", tuapi_mount, NULL, NULL); - Tcl_CreateObjCommand(interp, "::tuapi::syscall::umount", tuapi_umount, NULL, NULL); - Tcl_CreateObjCommand(interp, "::tuapi::syscall::swapon", tuapi_swapon, NULL, NULL); - Tcl_CreateObjCommand(interp, "::tuapi::syscall::swapoff", tuapi_swapoff, NULL, NULL); - Tcl_CreateObjCommand(interp, "::tuapi::syscall::mknod", tuapi_mknod, NULL, NULL); - - /* Process related commands */ - Tcl_CreateObjCommand(interp, "::tuapi::syscall::getuid", tuapi_getuid, NULL, NULL); - Tcl_CreateObjCommand(interp, "::tuapi::syscall::chroot", tuapi_chroot, NULL, NULL); - Tcl_CreateObjCommand(interp, "::tuapi::syscall::pivot_root", tuapi_pivot_root, NULL, NULL); - Tcl_CreateObjCommand(interp, "::tuapi::syscall::kill", tuapi_kill, NULL, NULL); - Tcl_CreateObjCommand(interp, "::tuapi::syscall::ps", tuapi_ps, NULL, NULL); - Tcl_CreateObjCommand(interp, "::tuapi::syscall::execve", tuapi_execve, NULL, NULL); - - /* Network related commands */ - Tcl_CreateObjCommand(interp, "::tuapi::syscall::ifconfig", tuapi_ifconfig, NULL, NULL); - Tcl_CreateObjCommand(interp, "::tuapi::syscall::route", tuapi_route, NULL, NULL); - Tcl_CreateObjCommand(interp, "::tuapi::syscall::brctl", tuapi_brctl, NULL, NULL); - Tcl_CreateObjCommand(interp, "::tuapi::syscall::vconfig", tuapi_vconfig, NULL, NULL); - - /* Needed commands for basic services Tcl lacks */ - Tcl_CreateObjCommand(interp, "::tuapi::syscall::socket_unix", tuapi_socket_unix, NULL, NULL); - - /* Service (TSMF) related commands */ - Tcl_CreateObjCommand(interp, "::tuapi::syscall::tsmf_start_svc", tuapi_tsmf_start_svc, NULL, NULL); - - /* Internal functions */ - Tcl_CreateObjCommand(interp, "::tuapi::internal::hash", tuapi_internalproc_simplehash, NULL, NULL); - - /* Define constants */ - /** Create parent namespace **/ - Tcl_CreateNamespace(interp, "::tuapi::const", NULL, NULL); - - /** Define constants, for real **/ - Tcl_ObjSetVar2(interp, Tcl_NewStringObj("::tuapi::const::HOST_NAME_MAX", -1), NULL, Tcl_NewWideIntObj(HOST_NAME_MAX), TCL_GLOBAL_ONLY); - - /* Create high-level user functions */ - Tcl_Eval(interp, -#include "tuapi.tcl.h" - ); - - Tcl_PkgProvide(interp, "tuapi", "0.1"); - - return(TCL_OK); -} DELETED system.tcl Index: system.tcl ================================================================== --- system.tcl +++ system.tcl @@ -1,256 +0,0 @@ -#! /usr/bin/env tclsh - -namespace eval ::tuapi {} -namespace eval ::tuapi::helper {} - -set ::tuapi::_mount_flags(bind) BIND -set ::tuapi::_mount_flags(move) MOVE -set ::tuapi::_mount_flags(remount) REMOUNT -set ::tuapi::_mount_flags(mandlock) MANDLOCK -set ::tuapi::_mount_flags(dirsync) DIRSYNC -set ::tuapi::_mount_flags(noatime) NOATIME -set ::tuapi::_mount_flags(nodiratime) NODIRATIME -set ::tuapi::_mount_flags(relatime) RELATIME -set ::tuapi::_mount_flags(strictatime) STRICTATIME -set ::tuapi::_mount_flags(nodev) NODEV -set ::tuapi::_mount_flags(noexec) NOEXEC -set ::tuapi::_mount_flags(nosuid) NOSUID -set ::tuapi::_mount_flags(ro) RDONLY -set ::tuapi::_mount_flags(silent) SILENT -set ::tuapi::_mount_flags(synchronous) SYNCHRONOUS -set ::tuapi::_mount_flags(sync) SYNCHRONOUS - - -# Determine where to mount a given device (usually by checking "/etc/fstab") -proc ::tuapi::helper::find_mountpoint {device} { - set data "" - catch { - set fd [open "/etc/fstab"] - set data [read -nonewline $fd] - close $fd - } - - foreach line [split $data "\n"] { - set line [string trim [regsub {#.*$} $line ""]] - set line [regsub -all {[ \t][ \t][ \t]*} $line " "] - - set work [split $line] - - set curr_device [lindex $work 0] - set curr_mountpoint [lindex $work 1] - set curr_fstype [lindex $work 2] - set curr_opts [split [lindex $work 3] ","] - set curr_dumpfreq [lindex $work 4] - set curr_fsckpass [lindex $work 5] - - - if {$curr_device == $device || $curr_mountpoint == $device} { - return [list source $curr_device target $curr_mountpoint fstype $curr_fstype options $curr_opts dumpfreq $curr_dumpfreq fsckpass $curr_fsckpass] - } - } - - return -code error "no entry found in \"/etc/fstab\" for \"$device\"" -} - -proc ::tuapi::mount args { - set options_list [list] - - for {set idx 0} {$idx < [llength $args]} {incr idx} { - set curr_arg [lindex $args $idx] - - switch -glob -- $curr_arg { - "-t" { - incr idx - set fstype [lindex $args $idx] - } - "-r" { - lappend options_list "RDONLY" - } - "-w" { - set idx [lsearch -exact $options_list "RDONLY"] - if {$idx != -1} { - set options_list [lreplace $options_list $idx $idx] - } - } - "-o" { - incr idx - set options [lindex $args $idx] - } - "--" { - incr idx - - break - } - "-*" { - return -code error "unknown option \"$curr_arg\"" - } - default { - break - } - } - } - - set args [lrange $args $idx end] - - if {[llength $args] < 1 || [llength $args] > 2} { - return -code error "wrong # args: should be \"::tuapi::mount ?options? source ?target?\"" - } - - set source [lindex $args 0] - - if {[llength $args] == 2} { - set target [lindex $args 1] - } else { - array set mountinfo [::tuapi::helper::find_mountpoint $source] - set source $mountinfo(source) - set target $mountinfo(target) - - if {![info exists fstype]} { - set fstype $mountinfo(fstype) - } - - if {![info exists options]} { - set options $mountinfo(options) - } - } - - # Ensure all mount-related parameters have been computed - if {![info exists fstype]} { - set fstype "auto" - } - - if {![info exists options]} { - set options [list] - } - - # Process options - foreach option $options { - set option_lc [string tolower $option] - - # Special option handling - switch -- $option_lc { - "defaults" { - set options_list [list] - unset -nocomplain unknown_options - - continue - } - "rw" { - set option_lc "noro" - } - "norw" { - set option_lc "ro" - } - } - - # Example: noatime - if {[info exists ::tuapi::_mount_flags($option_lc)]} { - lappend options_list $::tuapi::_mount_flags($option_lc) - - continue - } - - # Example: atime - if {[info exists ::tuapi::_mount_flags(no$option_lc)]} { - set idx [lsearch -exact $options_list $::tuapi::_mount_flags(no$option_lc)] - if {$idx != -1} { - set options_list [lreplace $options_list $idx $idx] - } - - continue - } - - # Example: norelatime - if {[string match "no*" $option_lc]} { - set neg_option_lc [string range $option_lc 2 end] - - if {[info exists ::tuapi::_mount_flags($neg_option_lc)]} { - set idx [lsearch -exact $options_list $::tuapi::_mount_flags($neg_option_lc)] - if {$idx != -1} { - set options_list [lreplace $options_list $idx $idx] - } - - continue - } - } - - # Accumulate unknown options - lappend unknown_options $option - } - - # Use "swapon" if this is swap - if {$fstype == "swap"} { - return [::tuapi::syscall::swapon $source] - } - - # Otherwise, call "mount" system call - ## If we have accumulated any unknown options, pass them as a - ## comma-seperated value string - if {[info exists unknown_options]} { - set data [join $unknown_options ","] - - return [::tuapi::syscall::mount $source $target $fstype $options_list $data] - } - - return [::tuapi::syscall::mount $source $target $fstype $options_list] -} - -proc ::tuapi::umount {dir {flags ""}} { - return [::tuapi::syscall::umount $dir [string toupper $flags]] -} - -proc ::tuapi::kill {pid sig} { - return [::tuapi::syscall::kill $pid [string toupper $sig]] -} - -proc ::tuapi::killpg {pgroup sig} { - if {$pgroup <= 1} { - return -code error "invalid process group specified (must be greater than 1)" - } - - return [::tuapi::syscall::kill -$pgroup [string toupper $sig]] -} - -proc ::tuapi::ifconfig args { - if {[llength $args] == 0} { - # Return information on all interfaces - set retlist [list] - foreach interface [::tuapi::syscall::ifconfig] { - lappend retlist $interface [::tuapi::syscall::ifconfig $interface] - } - - return $retlist - } - - set interface [lindex $args 0] - set args [lrange $args 1 end] - - array set ifaceinfo [::tuapi::syscall::ifconfig $interface] - - if {[llength $args] == 0} { - return [array get ifaceinfo] - } - - for {set idx 0} {$idx < [llength $args]} {incr idx} { - set opt [lindex $args $idx] - - switch -- $opt { - "up" { - if {[info exists ifaceinfo(flags)]} { - set flags $ifaceinfo(flags) - } else { - set flags "" - } - - foreach newflag [list UP RUNNING] { - if {[lsearch -exact $flags $newflag] == -1} { - lappend flags $newflag - } - } - - ::tuapi::syscall::ifconfig $interface flags $flags - } - - } - } -} ADDED tuapi.c Index: tuapi.c ================================================================== --- tuapi.c +++ tuapi.c @@ -0,0 +1,2624 @@ +#define _LINUX_SOURCE 1 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#ifndef HOST_NAME_MAX +/* SUSv2 Limit */ +#define HOST_NAME_MAX 255 +#endif + +/* From Linux 2.6 */ +#ifndef MNT_DETACH +#define MNT_DETACH 2 +#endif +#ifndef MNT_EXPIRE +#define MNT_EXPIRE 4 +#endif +#ifndef MS_MOVE +#define MS_MOVE 8192 +#endif + +/* Simple macros */ +#ifndef MAX +#define MAX(a, b) (((a) < (b)) ? (b) : (a)) +#endif +#ifndef MIN +#define MIN(a, b) (((a) < (b)) ? (a) : (b)) +#endif + +/* User environment, for execve */ +extern char **environ; + +/* Re-implement these if needed */ +#ifdef SYS_init_module +static int init_module(void *val, unsigned long len, const char *args) { + return(syscall(SYS_init_module, val, len, args)); +} +#endif +#ifdef SYS_pivot_root +static int pivot_root(const char *new_root, const char *put_old) { + return(syscall(SYS_pivot_root, new_root, put_old)); +} +#endif + +/* + * Simple hash routine to enable switching on a string to be implemented + */ +static unsigned long tuapi_internal_simplehash(const void *databuf, int datalen) { + unsigned long retval = 0; + const unsigned char *data; + + data = databuf; + + for (; datalen > 0; datalen--,data++) { + retval ^= (retval >> 25) & 0x7F; + retval <<= 7; + retval &= (0xFFFFFFFFUL); + retval ^= *data; + } + + return(retval); +} + +static unsigned long tuapi_internal_simplehash_obj(Tcl_Obj *tcl_data) { + unsigned long retval; + char *data; + int datalen = -1; + + data = Tcl_GetStringFromObj(tcl_data, &datalen); + + retval = tuapi_internal_simplehash(data, datalen); + + return(retval); +} + +#if 0 +/* NOTUSED: Uncomment when needed: */ +static unsigned long tuapi_internal_simplehash_str(const char *data) { + unsigned long retval; + int datalen; + + datalen = strlen(data); + + retval = tuapi_internal_simplehash(data, datalen); + + return(retval); +} +#endif + +static int tuapi_internalproc_simplehash(ClientData cd, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[]) { + unsigned long hashval; + Tcl_Obj *hashval_obj; + + if (objc != 2) { + Tcl_SetObjResult(interp, Tcl_NewStringObj("wrong # args: should be \"::tuapi::internal::hash value\"", -1)); + + return(TCL_ERROR); + } + + hashval = tuapi_internal_simplehash_obj(objv[1]); + + hashval_obj = Tcl_NewObj(); + Tcl_SetWideIntObj(hashval_obj, hashval); + + Tcl_SetObjResult(interp, hashval_obj); + + return(TCL_OK); +} + +static int tuapi_internal_getsock(int *sock_v4_out, int *sock_v6_out) { + int sock_v4 = -1, sock_v6 = -1; + int sock; + + if (sock_v4_out == NULL && sock_v6_out == NULL) { + return(-1); + } + + if (sock_v4_out != NULL) { + /* + * Check for IPv4 support before trying to create an IPv4 socket to + * avoid demand-loading IPv4 (XXX: TODO) + */ + sock_v4 = socket(AF_INET, SOCK_DGRAM, 0); + } + + if (sock_v6_out != NULL) { + /* + * Check for IPv6 support before trying to create an IPv6 socket to + * avoid demand-loading IPv6 (XXX: TODO) + */ + sock_v6 = socket(AF_INET6, SOCK_DGRAM, 0); + } + + /* Pick a socket to query for the interface list */ + if (sock_v4 == -1 && sock_v6 == -1) { + return(-1); + } + + if (sock_v6 != -1) { + sock = sock_v6; + } else { + sock = sock_v4; + } + + if (sock_v4_out != NULL) { + *sock_v4_out = sock_v4; + } + + if (sock_v6_out != NULL) { + *sock_v6_out = sock_v6; + } + + return(sock); +} + +/* + * Low-level System Call Wrapper Procedures + * + * These procedures should minimally wrap Linux or UNIX system calls to + * expose to the Tcl-level. Where possible accept symbolic names rather + * than numeric values (.e.g, list of values to OR together to get flags). + */ +static int tuapi_mount(ClientData cd, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[]) { + Tcl_Obj *mountflags_obj, **mountflags_list, *mountflag; + int mountflags_list_len; + char *source, *target, *fstype; + unsigned long mountflags = 0; + void *data = NULL; + int mount_ret, tcl_ret; + + if (objc < 5 || objc > 6) { + Tcl_SetObjResult(interp, Tcl_NewStringObj("wrong # args: should be \"::tuapi::syscall::mount source target fstype mountflags ?data?\"", -1)); + + return(TCL_ERROR); + } + + source = Tcl_GetString(objv[1]); + target = Tcl_GetString(objv[2]); + fstype = Tcl_GetString(objv[3]); + mountflags_obj = objv[4]; + + if (objc == 6) { + data = Tcl_GetString(objv[5]); + } + + tcl_ret = Tcl_ListObjGetElements(interp, mountflags_obj, &mountflags_list_len, &mountflags_list); + if (tcl_ret != TCL_OK) { + return(tcl_ret); + } + + for (; mountflags_list_len > 0; mountflags_list_len--,mountflags_list++) { + mountflag = mountflags_list[0]; + + switch (tuapi_internal_simplehash_obj(mountflag)) { +#ifdef MS_BIND + case 0x8526744: /* BIND */ + mountflags |= MS_BIND; + break; +#endif +#ifdef MS_DIRSYNC + case 0x2aff41c3: /* DIRSYNC */ + mountflags |= MS_DIRSYNC; + break; +#endif +#ifdef MS_MANDLOCK + case 0x410dbcb: /* MANDLOCK */ + mountflags |= MS_MANDLOCK; + break; +#endif +#ifdef MS_MOVE + case 0x9b3eb45: /* MOVE */ + mountflags |= MS_MOVE; + break; +#endif +#ifdef MS_NOATIME + case 0x1a0f58c5: /* NOATIME */ + mountflags |= MS_NOATIME; + break; +#endif +#ifdef MS_NODEV + case 0xe9f120d6: /* NODEV */ + mountflags |= MS_NODEV; + break; +#endif +#ifdef MS_NODIRATIME + case 0xde08ff45: /* NODIRATIME */ + mountflags |= MS_NODIRATIME; + break; +#endif +#ifdef MS_NOEXEC + case 0xf8b718c3: /* NOEXEC */ + mountflags |= MS_NOEXEC; + break; +#endif +#ifdef MS_NOSUID + case 0xfa745ec4: /* NOSUID */ + mountflags |= MS_NOSUID; + break; +#endif +#ifdef MS_RDONLY + case 0x49f2ec59: /* RDONLY */ + mountflags |= MS_RDONLY; + break; +#endif +#ifdef MS_RELATIME + case 0x481954c5: /* RELATIME */ + mountflags |= MS_RELATIME; + break; +#endif +#ifdef MS_REMOUNT + case 0xd9507154: /* REMOUNT */ + mountflags |= MS_REMOUNT; + break; +#endif +#ifdef MS_SILENT + case 0x99902954: /* SILENT */ + mountflags |= MS_SILENT; + break; +#endif +#ifdef MS_STRICTATIME + case 0x562fa045: /* STRICTATIME */ + mountflags |= MS_STRICTATIME; + break; +#endif +#ifdef MS_SYNCHRONOUS + case 0xbf799353: /* SYNCHRONOUS */ + case 0xa766743: /* SYNC */ + mountflags |= MS_SYNCHRONOUS; + break; +#endif + default: + Tcl_SetObjResult(interp, Tcl_ObjPrintf("unknown element in mountflags: \"%s\"", Tcl_GetString(mountflag))); + + return(TCL_ERROR); + } + } + + mount_ret = mount(source, target, fstype, mountflags, data); + if (mount_ret != 0) { + Tcl_SetObjResult(interp, Tcl_NewStringObj(strerror(errno), -1)); + + return(TCL_ERROR); + } + + Tcl_SetObjResult(interp, Tcl_NewStringObj(target, -1)); + + return(TCL_OK); +} + +static int tuapi_umount(ClientData cd, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[]) { + Tcl_Obj **flags, *flag; + Tcl_Obj *pathname_obj; + char *pathname; + int umount2_flags = 0; + int flags_cnt; + int chk_ret, tcl_ret; + + if (objc < 2 || objc > 3) { + Tcl_SetObjResult(interp, Tcl_NewStringObj("wrong # args: should be \"tuapi::syscall::umount dir ?flags?\"", -1)); + + return(TCL_ERROR); + } + + pathname_obj = objv[1]; + pathname = Tcl_GetString(pathname_obj); + + /* Set a default return value */ + Tcl_SetObjResult(interp, pathname_obj); + + if (objc == 3) { + tcl_ret = Tcl_ListObjGetElements(interp, objv[2], &flags_cnt, &flags); + if (tcl_ret != TCL_OK) { + return(tcl_ret); + } + + for (; flags_cnt > 0; flags_cnt--,flags++) { + flag = flags[0]; + + switch (tuapi_internal_simplehash_obj(flag)) { + case 0x69f4a3c5: /* FORCE */ + umount2_flags |= MNT_FORCE; + + break; + case 0x5a9173c8: /* DETACH */ + umount2_flags |= MNT_DETACH; + + break; + case 0x8a137fc5: /* EXPIRE */ + umount2_flags |= MNT_EXPIRE; + + break; + default: + Tcl_SetObjResult(interp, Tcl_ObjPrintf("unknown flag \"%s\" specified", Tcl_GetString(flag))); + + return(TCL_ERROR); + } + } + + chk_ret = umount2(pathname, umount2_flags); + + /* Do not return an error for this case, since it is apparently not exceptional */ + if (chk_ret != 0 && (umount2_flags & MNT_EXPIRE) == MNT_EXPIRE && errno == EAGAIN) { + Tcl_SetObjResult(interp, Tcl_NewStringObj("AGAIN", -1)); + + chk_ret = 0; + } + } else { + chk_ret = umount(pathname); + } + + if (chk_ret != 0) { + Tcl_SetObjResult(interp, Tcl_NewStringObj(strerror(errno), -1)); + + return(TCL_ERROR); + } + + return(TCL_OK); +} + +static int tuapi_swapon(ClientData cd, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[]) { + char *pathname; + int chk_ret; + + if (objc != 2) { + Tcl_SetObjResult(interp, Tcl_NewStringObj("wrong # args: should be \"tuapi::syscall::swapon pathname\"", -1)); + + return(TCL_ERROR); + } + + pathname = Tcl_GetString(objv[1]); + + chk_ret = swapon(pathname, 0); + if (chk_ret != 0) { + Tcl_SetObjResult(interp, Tcl_NewStringObj(strerror(errno), -1)); + + return(TCL_ERROR); + } + + return(TCL_OK); +} + +static int tuapi_swapoff(ClientData cd, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[]) { + char *pathname; + int chk_ret; + + if (objc != 2) { + Tcl_SetObjResult(interp, Tcl_NewStringObj("wrong # args: should be \"tuapi::syscall::swapoff pathname\"", -1)); + + return(TCL_ERROR); + } + + pathname = Tcl_GetString(objv[1]); + + chk_ret = swapoff(pathname); + if (chk_ret != 0) { + Tcl_SetObjResult(interp, Tcl_NewStringObj(strerror(errno), -1)); + + return(TCL_ERROR); + } + + return(TCL_OK); +} + +static int tuapi_insmod(ClientData cd, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[]) { + Tcl_Channel fd; + Tcl_Obj *module_filename, *module_data; + void *module_data_val; + int module_data_len; + int read_ret, chk_ret; + + if (objc < 2) { + Tcl_SetObjResult(interp, Tcl_NewStringObj("wrong # args: should be \"tuapi::syscall::insmod filename ?args ...?\"", -1)); + + return(TCL_ERROR); + } + + module_filename = objv[1]; + + fd = Tcl_FSOpenFileChannel(interp, module_filename, "r", 0600); + if (fd == NULL) { + return(TCL_ERROR); + } + + chk_ret = Tcl_SetChannelOption(interp, fd, "-translation", "binary"); + if (chk_ret != TCL_OK) { + Tcl_Close(interp, fd); + + return(chk_ret); + } + + module_data = Tcl_NewObj(); + + read_ret = Tcl_ReadChars(fd, module_data, -1, 0); + + Tcl_Close(interp, fd); + + if (read_ret <= 0) { + Tcl_SetObjResult(interp, Tcl_NewStringObj("read failed", -1)); + + return(TCL_ERROR); + } + + module_data_val = Tcl_GetByteArrayFromObj(module_data, &module_data_len); + + chk_ret = init_module(module_data_val, module_data_len, ""); + if (chk_ret != 0) { + Tcl_SetObjResult(interp, Tcl_NewStringObj(strerror(errno), -1)); + + return(TCL_ERROR); + } + + return(TCL_OK); +} + +static int tuapi_rmmod(ClientData cd, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[]) { + Tcl_SetObjResult(interp, Tcl_NewStringObj("not implemented", -1)); + + return(TCL_ERROR); +} + +static int tuapi_lsmod(ClientData cd, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[]) { + Tcl_SetObjResult(interp, Tcl_NewStringObj("not implemented", -1)); + + return(TCL_ERROR); +} + +static int tuapi_hostname(ClientData cd, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[]) { + char hostname[HOST_NAME_MAX + 1]; + int chk_ret; + + if (objc == 1) { + /* No arguments given, just return the hostname */ + chk_ret = gethostname(hostname, sizeof(hostname)); + if (chk_ret != 0) { + Tcl_SetObjResult(interp, Tcl_NewStringObj(strerror(errno), -1)); + + return(TCL_ERROR); + } + + hostname[sizeof(hostname) - 1] = '\0'; + + Tcl_SetObjResult(interp, Tcl_NewStringObj(hostname, -1)); + + return(TCL_OK); + } + + if (objc == 2) { + /* Exactly one argument given, set the hostname */ + strncpy(hostname, Tcl_GetString(objv[1]), sizeof(hostname)); + hostname[sizeof(hostname) - 1] = '\0'; + + chk_ret = sethostname(hostname, strlen(hostname)); + if (chk_ret != 0) { + Tcl_SetObjResult(interp, Tcl_NewStringObj(strerror(errno), -1)); + + return(TCL_ERROR); + } + + Tcl_SetObjResult(interp, Tcl_NewStringObj(hostname, -1)); + + return(TCL_OK); + } + + Tcl_SetObjResult(interp, Tcl_NewStringObj("wrong # args: should be \"hostname ?hostname?\"", -1)); + + return(TCL_ERROR); +} + +static int tuapi_domainname(ClientData cd, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[]) { + Tcl_SetObjResult(interp, Tcl_NewStringObj("not implemented", -1)); + + return(TCL_ERROR); +} + +static int tuapi_chroot(ClientData cd, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[]) { + char *pathname; + int chk_ret; + + if (objc != 2) { + Tcl_SetObjResult(interp, Tcl_NewStringObj("wrong # args: should be \"::tuapi::syscall:chroot pathname\"", -1)); + + return(TCL_ERROR); + } + + pathname = Tcl_GetString(objv[1]); + + chk_ret = chroot(pathname); + if (chk_ret != 0) { + Tcl_SetObjResult(interp, Tcl_NewStringObj(strerror(errno), -1)); + + return(TCL_ERROR); + } + + return(TCL_OK); +} + +static int tuapi_pivot_root(ClientData cd, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[]) { + char *new_root, *put_old; + int chk_ret; + + if (objc != 3) { + Tcl_SetObjResult(interp, Tcl_NewStringObj("wrong # args: should be \"::tuapi::syscall::pivot_root new_root put_old\"", -1)); + + return(TCL_ERROR); + } + + new_root = Tcl_GetString(objv[1]); + put_old = Tcl_GetString(objv[2]); + + chk_ret = pivot_root(new_root, put_old); + if (chk_ret != 0) { + Tcl_SetObjResult(interp, Tcl_NewStringObj(strerror(errno), -1)); + + return(TCL_ERROR); + } + + return(TCL_OK); +} + +static int tuapi_mknod(ClientData cd, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[]) { + Tcl_SetObjResult(interp, Tcl_NewStringObj("not implemented", -1)); + + return(TCL_ERROR); +} + +static int tuapi_getuid(ClientData cd, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[]) { + Tcl_SetObjResult(interp, Tcl_NewStringObj("not implemented", -1)); + + return(TCL_ERROR); +} + +static int tuapi_kill(ClientData cd, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[]) { + Tcl_Obj *signal_obj; + + Tcl_WideInt pid_wide, sig_wide; + pid_t pid; + int sig; + int kill_ret, tcl_ret; + + if (objc != 3) { + Tcl_SetObjResult(interp, Tcl_NewStringObj("wrong # args: should be \"::tuapi::syscall::kill pid sig\"", -1)); + + return(TCL_ERROR); + } + + tcl_ret = Tcl_GetWideIntFromObj(interp, objv[1], &pid_wide); + if (tcl_ret != TCL_OK) { + return(tcl_ret); + } + pid = pid_wide; + + signal_obj = objv[2]; + + tcl_ret = Tcl_GetWideIntFromObj(interp, signal_obj, &sig_wide); + if (tcl_ret != TCL_OK) { + switch (tuapi_internal_simplehash_obj(signal_obj)) { + case 0x122ad0: /* HUP */ + case 0x98f364d0: /* SIGHUP */ + sig = SIGHUP; + break; + case 0x126754: /* INT */ + case 0x98f32954: /* SIGINT */ + sig = SIGINT; + break; + case 0xa3564d4: /* QUIT */ + case 0x7a9242d4: /* SIGQUIT */ + sig = SIGQUIT; + break; + case 0x12664c: /* ILL */ + case 0x98f3284c: /* SIGILL */ + sig = SIGILL; + break; + case 0xa94a0d0: /* TRAP */ + case 0x7a3386d0: /* SIGTRAP */ + sig = SIGTRAP; + break; + case 0x830a954: /* ABRT */ + case 0x78978f54: /* SIGABRT */ + sig = SIGABRT; + break; + case 0x1267d4: /* IOT */ + case 0x98f329d4: /* SIGIOT */ + sig = SIGIOT; + break; + case 0x10aad3: /* BUS */ + case 0x98f1e4d3: /* SIGBUS */ + sig = SIGBUS; + break; + case 0x11a845: /* FPE */ + case 0x98f0e645: /* SIGFPE */ + sig = SIGFPE; + break; + case 0x972664c: /* KILL */ + case 0x79d5404c: /* SIGKILL */ + sig = SIGKILL; + break; + case 0xab4e931: /* USR1 */ + case 0x7a13cf31: /* SIGUSR1 */ + sig = SIGUSR1; + break; + case 0xa7163d6: /* SEGV */ + case 0x7ad645d6: /* SIGSEGV */ + sig = SIGSEGV; + break; + case 0xab4e932: /* USR2 */ + case 0x7a13cf32: /* SIGUSR2 */ + sig = SIGUSR2; + break; + case 0xa126845: /* PIPE */ + case 0x7ab54e45: /* SIGPIPE */ + sig = SIGPIPE; + break; + case 0x833294d: /* ALRM */ + case 0x78940f4d: /* SIGALRM */ + sig = SIGALRM; + break; + case 0xa91694d: /* TERM */ + case 0x7a364f4d: /* SIGTERM */ + sig = SIGTERM; + break; + case 0x4970e8d4: /* STKFLT */ + case 0x80fefc54: /* SIGSTKFLT */ + sig = SIGSTKFLT; + break; + case 0x8722644: /* CHLD */ + case 0x78d50044: /* SIGCHLD */ + sig = SIGCHLD; + break; + case 0x873e754: /* CONT */ + case 0x78d4c154: /* SIGCONT */ + sig = SIGCONT; + break; + case 0xa7527d0: /* STOP */ + case 0x7ad201d0: /* SIGSTOP */ + sig = SIGSTOP; + break; + case 0xa94ea50: /* TSTP */ + case 0x7a33cc50: /* SIGTSTP */ + sig = SIGTSTP; + break; + case 0xa9524ce: /* TTIN */ + case 0x7a3202ce: /* SIGTTIN */ + sig = SIGTTIN; + break; + case 0xa9527d5: /* TTOU */ + case 0x7a3201d5: /* SIGTTOU */ + sig = SIGTTOU; + break; + case 0x156947: /* URG */ + case 0x98f42747: /* SIGURG */ + sig = SIGURG; + break; + case 0xb10e855: /* XCPU */ + case 0x7bb7ce55: /* SIGXCPU */ + sig = SIGXCPU; + break; + case 0xb11a9da: /* XFSZ */ + case 0x7bb68fda: /* SIGXFSZ */ + sig = SIGXFSZ; + break; + case 0x483273cd: /* VTALRM */ + case 0x81bc674d: /* SIGVTALRM */ + sig = SIGVTALRM; + break; + case 0xa14a7c6: /* PROF */ + case 0x7ab381c6: /* SIGPROF */ + sig = SIGPROF; + break; + case 0x7933a348: /* WINCH */ + case 0x2aa0bf48: /* SIGWINCH */ + sig = SIGWINCH; + break; + case 0x24cf: /* IO */ + case 0x3931e64f: /* SIGIO */ + sig = SIGIO; + break; + case 0x142bd2: /* PWR */ + case 0x98f565d2: /* SIGPWR */ + sig = SIGPWR; + break; + case 0x14ecd3: /* SYS */ + case 0x98f5a2d3: /* SIGSYS */ + sig = SIGSYS; + break; + default: + Tcl_SetObjResult(interp, Tcl_ObjPrintf("unknown signal \"%s\"", Tcl_GetString(signal_obj))); + + return(TCL_ERROR); + } + } else { + sig = sig_wide; + } + + kill_ret = kill(pid, sig); + if (kill_ret != 0) { + Tcl_SetObjResult(interp, Tcl_NewStringObj(strerror(errno), -1)); + + return(TCL_ERROR); + } + + return(TCL_OK); +} + +static int tuapi_ps(ClientData cd, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[]) { + Tcl_SetObjResult(interp, Tcl_NewStringObj("not implemented", -1)); + + return(TCL_ERROR); +} + +static int tuapi_execve(ClientData cd, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[]) { + char **argv = NULL; + char *file; + int idx; + + if (objc < 2) { + Tcl_SetObjResult(interp, Tcl_NewStringObj("wrong # args: should be \"::tuapi::syscall::execve file ?args ...?\"", -1)); + + return(TCL_ERROR); + } + + /* Find executable */ + file = Tcl_GetString(objv[1]); + + /* Generate argument array */ + argv = malloc(sizeof(*argv) * (objc - 1)); + + for (idx = 2; idx < objc; idx++) { + argv[idx - 2] = Tcl_GetString(objv[idx]); + } + argv[objc - 2] = NULL; + + /* Pass execution to new file */ + execve(file, argv, environ); + + /* If the new image could not take over, something went wrong -- report error */ + Tcl_SetObjResult(interp, Tcl_NewStringObj(strerror(errno), -1)); + + return(TCL_ERROR); +} + +static int tuapi_losetup(ClientData cd, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[]) { + char *file, *loopdev; + int chk_ret; + int loopfd, filefd; + + if (objc != 3) { + Tcl_SetObjResult(interp, Tcl_NewStringObj("wrong # args: should be \"::tuapi::syscall::losetup loopdev file\"", -1)); + + return(TCL_ERROR); + } + + loopdev = Tcl_GetString(objv[1]); + file = Tcl_GetString(objv[2]); + + loopfd = open(loopdev, O_RDONLY); + if (loopfd < 0) { + Tcl_SetObjResult(interp, Tcl_NewStringObj(strerror(errno), -1)); + + return(TCL_ERROR); + } + + if (file[0] != '\0') { + filefd = open(file, O_RDONLY); + if (filefd < 0) { + close(loopfd); + + Tcl_SetObjResult(interp, Tcl_NewStringObj(strerror(errno), -1)); + + return(TCL_ERROR); + } + + chk_ret = ioctl(loopfd, LOOP_SET_FD, filefd); + + close(filefd); + } else { + chk_ret = ioctl(loopfd, LOOP_CLR_FD, 0); + } + + close(loopfd); + + if (chk_ret != 0) { + Tcl_SetObjResult(interp, Tcl_NewStringObj(strerror(errno), -1)); + + return(TCL_ERROR); + } + + return(TCL_OK); +} + +static void tuapi_private_append_sockaddr_to_tclobj(Tcl_Interp *interp, Tcl_Obj *list, char *header, struct sockaddr *addr) { + char addr_buf[INET6_ADDRSTRLEN + INET_ADDRSTRLEN + 1], *chk_inp; + + switch (addr->sa_family) { + case AF_INET: /* IPv4 */ + case AF_INET6: /* IPv6 */ + switch (addr->sa_family) { + case AF_INET: /* IPv4 */ + chk_inp = (char *) inet_ntop(addr->sa_family, &((struct sockaddr_in *) addr)->sin_addr, addr_buf, sizeof(addr_buf)); + break; + case AF_INET6: /* IPv6 */ + chk_inp = (char *) inet_ntop(addr->sa_family, &((struct sockaddr_in6 *) addr)->sin6_addr, addr_buf, sizeof(addr_buf)); + break; + } + + if (chk_inp == NULL) { + break; + } + + if (header) { + Tcl_ListObjAppendElement(interp, list, Tcl_NewStringObj(header, -1)); + } + + Tcl_ListObjAppendElement(interp, list, Tcl_NewStringObj(addr_buf, -1)); + + break; + } + + return; +} + +static int tuapi_private_get_sockaddr_from_obj(Tcl_Obj *value, void *target) { + struct sockaddr_in local_v4; + struct sockaddr_in6 local_v6; + const char *addr_str; + int inetpton_ret; + + addr_str = Tcl_GetString(value); + + memset(&local_v4, 0, sizeof(local_v4)); + inetpton_ret = inet_pton(AF_INET, addr_str, &local_v4.sin_addr); + if (inetpton_ret == 1) { + local_v4.sin_family = AF_INET; + + memcpy(target, &local_v4, sizeof(local_v4)); + + return(0); + } + + memset(&local_v6, 0, sizeof(local_v6)); + inetpton_ret = inet_pton(AF_INET6, addr_str, &local_v6.sin6_addr); + if (inetpton_ret == 1) { + local_v6.sin6_family = AF_INET6; + + memcpy(target, &local_v6, sizeof(local_v6)); + + return(0); + } + + return(-1); +} + +static int tuapi_ifconfig_list(ClientData cd, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[], int sock) { + Tcl_Obj *tcl_iface_list; + struct ifconf ifaces_cfg; + struct ifreq *iface_req = NULL; + int iface_req_cnt = 224, iface_req_len; + int idx, iface_cnt; + int ioctl_ret, tcl_ret; + + iface_req_len = iface_req_cnt * sizeof(*iface_req); + iface_req = malloc(iface_req_len); + if (iface_req == NULL) { + /* Report failure */ + Tcl_SetObjResult(interp, Tcl_NewStringObj("unable to allocate memory", -1)); + + return(TCL_ERROR); + } + + ifaces_cfg.ifc_req = iface_req; + ifaces_cfg.ifc_len = iface_req_len; + ioctl_ret = ioctl(sock, SIOCGIFCONF, &ifaces_cfg); + if (ioctl_ret != 0) { + Tcl_SetObjResult(interp, Tcl_NewStringObj("ioctl failed", -1)); + + free(iface_req); + + return(TCL_ERROR); + } + + iface_cnt = ifaces_cfg.ifc_len / sizeof(*iface_req); + + tcl_iface_list = Tcl_NewObj(); + + for (idx = 0; idx < iface_cnt; idx++) { + tcl_ret = Tcl_ListObjAppendElement(interp, tcl_iface_list, Tcl_NewStringObj(iface_req[idx].ifr_name, -1)); + if (tcl_ret != TCL_OK) { + Tcl_SetObjResult(interp, Tcl_NewStringObj("unable to append to list", -1)); + + free(iface_req); + + return(TCL_ERROR); + } + } + + free(iface_req); + + Tcl_SetObjResult(interp, tcl_iface_list); + + return(TCL_OK); +} + +static int tuapi_ifconfig_info(ClientData cd, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[], int sock, int sock_v4, int sock_v6) { + Tcl_Obj *retlist, *flags; + struct ifreq iface_req; + unsigned char *addr_data; + const char *link_encap; + const char *iface; + int flags_bitmask, flag_broadcast = 0, flag_pointopoint = 0; + int ioctl_ret; + + retlist = Tcl_NewObj(); + + iface = Tcl_GetString(objv[1]); + + if ((strlen(iface) + 1) >= IFNAMSIZ) { + Tcl_SetObjResult(interp, Tcl_NewStringObj("interface name too long", -1)); + + return(TCL_ERROR); + } + + strcpy(iface_req.ifr_name, iface); + + /* + * All interfaces should have flags, so use it as a check for interface + * existance + */ + ioctl_ret = ioctl(sock, SIOCGIFFLAGS, &iface_req); + if (ioctl_ret != 0) { + Tcl_SetObjResult(interp, Tcl_NewStringObj("invalid interface", -1)); + + return(TCL_ERROR); + } + + /* Create list of flags */ + flags = Tcl_NewObj(); + flags_bitmask = iface_req.ifr_flags; + + if ((flags_bitmask & IFF_UP) == IFF_UP) { + Tcl_ListObjAppendElement(interp, flags, Tcl_NewStringObj("UP", -1)); + } + if ((flags_bitmask & IFF_BROADCAST) == IFF_BROADCAST) { + flag_broadcast = 1; + + Tcl_ListObjAppendElement(interp, flags, Tcl_NewStringObj("BROADCAST", -1)); + } + if ((flags_bitmask & IFF_POINTOPOINT) == IFF_POINTOPOINT) { + flag_pointopoint = 1; + + Tcl_ListObjAppendElement(interp, flags, Tcl_NewStringObj("POINTOPOINT", -1)); + } + if ((flags_bitmask & IFF_DEBUG) == IFF_DEBUG) { + Tcl_ListObjAppendElement(interp, flags, Tcl_NewStringObj("DEBUG", -1)); + } + if ((flags_bitmask & IFF_LOOPBACK) == IFF_LOOPBACK) { + Tcl_ListObjAppendElement(interp, flags, Tcl_NewStringObj("LOOPBACK", -1)); + } + if ((flags_bitmask & IFF_NOTRAILERS) == IFF_NOTRAILERS) { + Tcl_ListObjAppendElement(interp, flags, Tcl_NewStringObj("NOTRAILERS", -1)); + } + if ((flags_bitmask & IFF_RUNNING) == IFF_RUNNING) { + Tcl_ListObjAppendElement(interp, flags, Tcl_NewStringObj("RUNNING", -1)); + } + if ((flags_bitmask & IFF_NOARP) == IFF_NOARP) { + Tcl_ListObjAppendElement(interp, flags, Tcl_NewStringObj("NOARP", -1)); + } + if ((flags_bitmask & IFF_PROMISC) == IFF_PROMISC) { + Tcl_ListObjAppendElement(interp, flags, Tcl_NewStringObj("PROMISC", -1)); + } + if ((flags_bitmask & IFF_ALLMULTI) == IFF_ALLMULTI) { + Tcl_ListObjAppendElement(interp, flags, Tcl_NewStringObj("ALLMULTI", -1)); + } + if ((flags_bitmask & IFF_MASTER) == IFF_MASTER) { + Tcl_ListObjAppendElement(interp, flags, Tcl_NewStringObj("MASTER", -1)); + } + if ((flags_bitmask & IFF_SLAVE) == IFF_SLAVE) { + Tcl_ListObjAppendElement(interp, flags, Tcl_NewStringObj("SLAVE", -1)); + } + if ((flags_bitmask & IFF_MULTICAST) == IFF_MULTICAST) { + Tcl_ListObjAppendElement(interp, flags, Tcl_NewStringObj("MULTICAST", -1)); + } + if ((flags_bitmask & IFF_PORTSEL) == IFF_PORTSEL) { + Tcl_ListObjAppendElement(interp, flags, Tcl_NewStringObj("PORTSEL", -1)); + } + if ((flags_bitmask & IFF_AUTOMEDIA) == IFF_AUTOMEDIA) { + Tcl_ListObjAppendElement(interp, flags, Tcl_NewStringObj("AUTOMEDIA", -1)); + } + if ((flags_bitmask & IFF_DYNAMIC) == IFF_DYNAMIC) { + Tcl_ListObjAppendElement(interp, flags, Tcl_NewStringObj("DYNAMIC", -1)); + } + if ((flags_bitmask & IFF_LOWER_UP) == IFF_LOWER_UP) { + Tcl_ListObjAppendElement(interp, flags, Tcl_NewStringObj("LOWER_UP", -1)); + } + if ((flags_bitmask & IFF_DORMANT) == IFF_DORMANT) { + Tcl_ListObjAppendElement(interp, flags, Tcl_NewStringObj("DORMANT", -1)); + } +#ifdef IFF_ECHO + if ((flags_bitmask & IFF_ECHO) == IFF_ECHO) { + Tcl_ListObjAppendElement(interp, flags, Tcl_NewStringObj("ECHO", -1)); + } +#endif + + /* Add array-compliant/dict entry to the return list */ + Tcl_ListObjAppendElement(interp, retlist, Tcl_NewStringObj("flags", -1)); + Tcl_ListObjAppendElement(interp, retlist, flags); + + /* Fetch other attributes from the interface */ + ioctl_ret = ioctl(sock, SIOCGIFHWADDR, &iface_req); + if (ioctl_ret == 0) { + link_encap = "unknown"; + + addr_data = (unsigned char *) iface_req.ifr_hwaddr.sa_data; + switch (iface_req.ifr_hwaddr.sa_family) { + case ARPHRD_ETHER: + link_encap = "ethernet"; + + Tcl_ListObjAppendElement(interp, retlist, Tcl_NewStringObj("hwaddr", -1)); + Tcl_ListObjAppendElement(interp, retlist, + Tcl_ObjPrintf("%02x:%02x:%02x:%02x:%02x:%02x", + addr_data[0], + addr_data[1], + addr_data[2], + addr_data[3], + addr_data[4], + addr_data[5] + ) + ); + + break; + case ARPHRD_AX25: + link_encap = "ax25"; + break; + case ARPHRD_PRONET: + link_encap = "pronet"; + break; + case ARPHRD_CHAOS: + link_encap = "chaos"; + break; + case ARPHRD_IEEE802: + link_encap = "ieee802"; + break; + case ARPHRD_ARCNET: + link_encap = "arcnet"; + break; + case ARPHRD_APPLETLK: + link_encap = "appletlk"; + break; + case ARPHRD_DLCI: + link_encap = "dlci"; + break; + case ARPHRD_ATM: + link_encap = "atm"; + break; + case ARPHRD_METRICOM: + link_encap = "metricom"; + break; + case ARPHRD_IEEE1394: + link_encap = "ieee1394"; + break; + case ARPHRD_EUI64: + link_encap = "eui64"; + break; + case ARPHRD_INFINIBAND: + link_encap = "infiniband"; + break; + case ARPHRD_SLIP: + link_encap = "slip"; + break; + case ARPHRD_CSLIP: + link_encap = "cslip"; + break; + case ARPHRD_SLIP6: + link_encap = "slip6"; + break; + case ARPHRD_CSLIP6: + link_encap = "cslip6"; + break; + case ARPHRD_RSRVD: + link_encap = "rsrvd"; + break; + case ARPHRD_ADAPT: + link_encap = "adapt"; + break; + case ARPHRD_ROSE: + link_encap = "rose"; + break; + case ARPHRD_X25: + link_encap = "x25"; + break; + case ARPHRD_HWX25: + link_encap = "hwx25"; + break; + case ARPHRD_CAN: + link_encap = "can"; + break; + case ARPHRD_PPP: + link_encap = "ppp"; + break; + case ARPHRD_CISCO: + link_encap = "cisco"; + break; + case ARPHRD_LAPB: + link_encap = "lapb"; + break; + case ARPHRD_DDCMP: + link_encap = "ddcmp"; + break; + case ARPHRD_RAWHDLC: + link_encap = "rawhdlc"; + break; + case ARPHRD_TUNNEL: + link_encap = "tunnel"; + break; + case ARPHRD_TUNNEL6: + link_encap = "tunnel6"; + break; + case ARPHRD_FRAD: + link_encap = "frad"; + break; + case ARPHRD_SKIP: + link_encap = "skip"; + break; + case ARPHRD_LOOPBACK: + link_encap = "loopback"; + break; + case ARPHRD_LOCALTLK: + link_encap = "localtalk"; + break; + case ARPHRD_FDDI: + link_encap = "fddi"; + break; + case ARPHRD_BIF: + link_encap = "bif"; + break; + case ARPHRD_SIT: + link_encap = "sit"; + break; + case ARPHRD_IPDDP: + link_encap = "ipddp"; + break; + case ARPHRD_IPGRE: + link_encap = "gre"; + break; + case ARPHRD_PIMREG: + link_encap = "pimreg"; + break; + case ARPHRD_HIPPI: + link_encap = "hippi"; + break; + case ARPHRD_ASH: + link_encap = "ash"; + break; + case ARPHRD_ECONET: + link_encap = "econet"; + break; + case ARPHRD_IRDA: + link_encap = "irda"; + break; + } + + Tcl_ListObjAppendElement(interp, retlist, Tcl_NewStringObj("link_encap", -1)); + Tcl_ListObjAppendElement(interp, retlist, Tcl_NewStringObj(link_encap, -1)); + } + + ioctl_ret = ioctl(sock, SIOCGIFMETRIC, &iface_req); + if (ioctl_ret == 0) { + Tcl_ListObjAppendElement(interp, retlist, Tcl_NewStringObj("metric", -1)); + Tcl_ListObjAppendElement(interp, retlist, Tcl_NewWideIntObj(iface_req.ifr_metric + 1)); + } + + ioctl_ret = ioctl(sock, SIOCGIFMTU, &iface_req); + if (ioctl_ret == 0) { + Tcl_ListObjAppendElement(interp, retlist, Tcl_NewStringObj("mtu", -1)); + Tcl_ListObjAppendElement(interp, retlist, Tcl_NewWideIntObj(iface_req.ifr_mtu)); + } + + ioctl_ret = ioctl(sock, SIOCGIFINDEX, &iface_req); + if (ioctl_ret == 0) { + Tcl_ListObjAppendElement(interp, retlist, Tcl_NewStringObj("index", -1)); + Tcl_ListObjAppendElement(interp, retlist, Tcl_NewWideIntObj(iface_req.ifr_ifindex)); + } + + if (sock_v4 != -1) { + ioctl_ret = ioctl(sock_v4, SIOCGIFADDR, &iface_req); + if (ioctl_ret == 0) { + tuapi_private_append_sockaddr_to_tclobj(interp, retlist, "address", &iface_req.ifr_addr); + } + + if (flag_pointopoint) { + /* Point-to-Point interfaces */ + ioctl_ret = ioctl(sock_v4, SIOCGIFDSTADDR, &iface_req); + if (ioctl_ret == 0) { + tuapi_private_append_sockaddr_to_tclobj(interp, retlist, "destination", &iface_req.ifr_addr); + } + } + + if (flag_broadcast) { + /* Broadcast interfaces */ + ioctl_ret = ioctl(sock_v4, SIOCGIFBRDADDR, &iface_req); + if (ioctl_ret == 0) { + tuapi_private_append_sockaddr_to_tclobj(interp, retlist, "broadcast", &iface_req.ifr_addr); + } + } + + ioctl_ret = ioctl(sock_v4, SIOCGIFNETMASK, &iface_req); + if (ioctl_ret == 0) { + tuapi_private_append_sockaddr_to_tclobj(interp, retlist, "netmask", &iface_req.ifr_addr); + } + } + + Tcl_SetObjResult(interp, retlist); + + return(TCL_OK); +} + +static int tuapi_ifconfig_conf(ClientData cd, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[], int sock, int sock_v4, int sock_v6) { + Tcl_Obj *option_name_obj, *option_val_obj; + Tcl_Obj **flags_objv; + struct ifreq iface_req; + struct sockaddr *tmp_ioctl_addr; + const char *iface; + short flags; + int flags_objc; + int tmp_sock, tmp_ioctl; + int ioctl_ret, tcl_ret, parse_ret; + + iface = Tcl_GetString(objv[1]); + + if ((strlen(iface) + 1) >= IFNAMSIZ) { + Tcl_SetObjResult(interp, Tcl_NewStringObj("interface name too long", -1)); + + return(TCL_ERROR); + } + + objc -= 2; + objv += 2; + + for (; objc > 0; objc--,objv++) { + /* Prepare for an ioctl() */ + strcpy(iface_req.ifr_name, iface); + tmp_ioctl = -1; + + option_name_obj = objv[0]; + + if (objc == 1) { + Tcl_SetObjResult(interp, Tcl_ObjPrintf("option \"%s\" requires an argument", Tcl_GetString(option_name_obj))); + + return(TCL_ERROR); + } + + objc--; + objv++; + + option_val_obj = objv[0]; + + switch (tuapi_internal_simplehash_obj(option_name_obj)) { + case 0x6d9870f3: /* flags */ + flags = 0; + + tcl_ret = Tcl_ListObjGetElements(interp, option_val_obj, &flags_objc, &flags_objv); + if (tcl_ret != TCL_OK) { + return(tcl_ret); + } + + for (; flags_objc > 0; flags_objc--,flags_objv++) { + switch (tuapi_internal_simplehash_obj(flags_objv[0])) { + case 0x2ad0: /* UP */ + flags |= IFF_UP; + break; + case 0x1aef7f54: /* BROADCAST */ + flags |= IFF_BROADCAST; + break; + case 0xc252abd4: /* POINTOPOINT */ + flags |= IFF_POINTOPOINT; + break; + case 0x48b0a8c7: /* DEBUG */ + flags |= IFF_DEBUG; + break; + case 0x4d3dbcd3: /* NOTRAILERS */ + flags |= IFF_NOTRAILERS; + break; + case 0xe9773147: /* RUNNING */ + flags |= IFF_RUNNING; + break; + case 0xe9f06b50: /* NOARP */ + flags |= IFF_NOARP; + break; + case 0xf91323c3: /* PROMISC */ + flags |= IFF_PROMISC; + break; + case 0x9b2a1849: /* ALLMULTI */ + flags |= IFF_ALLMULTI; + break; + case 0x1a7414d2: /* MASTER */ + flags |= IFF_MASTER; + break; + case 0x399069c5: /* SLAVE */ + flags |= IFF_SLAVE; + break; + case 0x4de928d4: /* MULTICAST */ + flags |= IFF_MULTICAST; + break; + case 0x2a35dc4c: /* PORTSEL */ + flags |= IFF_PORTSEL; + break; + case 0xd180ac1: /* AUTOMEDIA */ + flags |= IFF_AUTOMEDIA; + break; + case 0xe8ba02c3: /* DYNAMIC */ + flags |= IFF_DYNAMIC; + break; + case 0x16c8b4d0: /* LOWER_UP */ + flags |= IFF_LOWER_UP; + break; + case 0x293959d4: /* DORMANT */ + flags |= IFF_DORMANT; + break; +#ifdef IFF_ECHO + case 0x8b0e44f: /* ECHO */ + flags |= IFF_ECHO; + break; +#endif + } + } + + iface_req.ifr_flags = flags; + + ioctl_ret = ioctl(sock, SIOCSIFFLAGS, &iface_req); + if (ioctl_ret != 0) { + Tcl_SetObjResult(interp, Tcl_NewStringObj(strerror(errno), -1)); + + return(TCL_ERROR); + } + + break; + case 0x5e9d03e3: /* metric */ + case 0x1b7a75: /* mtu */ + case 0x7c3891f2: /* hwaddr */ + case 0xbf72a969: /* addmulti */ + case 0xba708969: /* delmulti */ + case 0xdd876e5: /* name */ + Tcl_SetObjResult(interp, Tcl_ObjPrintf("option \"%s\" unsupported", Tcl_GetString(option_name_obj))); + + return(TCL_ERROR); + break; + case 0x4e9aeaf3: /* address */ + if (tmp_ioctl == -1) { + tmp_ioctl = SIOCSIFADDR; + tmp_ioctl_addr = &iface_req.ifr_addr; + } + + case 0xec05706e: /* destination */ + if (tmp_ioctl == -1) { + tmp_ioctl = SIOCSIFDSTADDR; + tmp_ioctl_addr = &iface_req.ifr_dstaddr; + } + + case 0x3ea7e674: /* broadcast */ + if (tmp_ioctl == -1) { + tmp_ioctl = SIOCSIFBRDADDR; + tmp_ioctl_addr = &iface_req.ifr_broadaddr; + } + + case 0x4d65ee6b: /* netmask */ + if (tmp_ioctl == -1) { + tmp_ioctl = SIOCSIFNETMASK; + tmp_ioctl_addr = &iface_req.ifr_netmask; + } + + parse_ret = tuapi_private_get_sockaddr_from_obj(option_val_obj, tmp_ioctl_addr); + if (parse_ret != 0) { + Tcl_SetObjResult(interp, Tcl_ObjPrintf("unable to parse \"%s\" as an address", Tcl_GetString(option_val_obj))); + + return(TCL_ERROR); + } + + switch (tmp_ioctl_addr->sa_family) { + case AF_INET: + tmp_sock = sock_v4; + + break; + case AF_INET6: + tmp_sock = sock_v6; + + break; + default: + Tcl_SetObjResult(interp, Tcl_NewStringObj("unable to determine address family of sockaddr", -1)); + return(TCL_ERROR); + } + + ioctl_ret = ioctl(tmp_sock, tmp_ioctl, &iface_req); + if (ioctl_ret != 0) { + Tcl_SetObjResult(interp, Tcl_NewStringObj(strerror(errno), -1)); + + return(TCL_ERROR); + } + + break; + default: + Tcl_SetObjResult(interp, Tcl_ObjPrintf("unknown option \"%s\"", Tcl_GetString(option_name_obj))); + + return(TCL_ERROR); + } + } + + return(TCL_OK); +} + +static int tuapi_ifconfig(ClientData cd, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[]) { + int sock_v4, sock_v6, sock; + int retval = TCL_ERROR; + + sock = tuapi_internal_getsock(&sock_v4, &sock_v6); + if (sock == -1) { + Tcl_SetObjResult(interp, Tcl_NewStringObj("unable to create socket", -1)); + + return(TCL_ERROR); + } + + switch (objc) { + case 0: + case 1: /* No arguments, list all interfaces */ + retval = tuapi_ifconfig_list(cd, interp, objc, objv, sock); + + break; + case 2: /* One argument, give information about the interface */ + retval = tuapi_ifconfig_info(cd, interp, objc, objv, sock, sock_v4, sock_v6); + + break; + default: + /* Otherwise, configure the interace */ + retval = tuapi_ifconfig_conf(cd, interp, objc, objv, sock, sock_v4, sock_v6); + + break; + } + + /* Cleanup */ + if (sock_v4 != -1) { + close(sock_v4); + } + + if (sock_v6 != -1) { + close(sock_v6); + } + + return(retval); +} + +static int tuapi_route_list(ClientData cd, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[], int sock_v4, int sock_v6) { + Tcl_SetObjResult(interp, Tcl_NewStringObj("not implemented", -1)); + + return(TCL_ERROR); +} + +static int tuapi_route_conf(ClientData cd, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[], int sock_v4, int sock_v6) { + Tcl_WideInt option_val_wide; + Tcl_Obj *operation_obj, *dest_obj, *destmask_obj; + Tcl_Obj *option_name_obj, *option_val_obj; + struct rtentry route; + int sock; + int ioctl_id; + int tcl_ret, ioctl_ret, parse_ret; + + if (objc < 4) { + Tcl_SetObjResult(interp, Tcl_NewStringObj("wrong # args: should be \"::tuapi::syscall::route operation destination destination_mask ?options?\"", -1)); + + return(TCL_ERROR); + } + + /* Clear object values */ + memset(&route, 0, sizeof(route)); + + /* Determine operation */ + operation_obj = objv[1]; + switch (tuapi_internal_simplehash_obj(operation_obj)) { + case 0x187264: /* add */ + ioctl_id = SIOCADDRT; + break; + case 0x1932ec: /* del */ + case 0x5d98e965: /* delete */ + ioctl_id = SIOCDELRT; + break; + default: + Tcl_SetObjResult(interp, Tcl_ObjPrintf("bad option \"%s\": must be add, or delete", Tcl_GetString(operation_obj))); + + return(TCL_ERROR); + } + + /* Set default flags */ + route.rt_flags = RTF_UP; + + /* Parse destination address */ + dest_obj = objv[2]; + parse_ret = tuapi_private_get_sockaddr_from_obj(dest_obj, &route.rt_dst); + if (parse_ret != 0) { + Tcl_SetObjResult(interp, Tcl_ObjPrintf("unable to parse \"%s\" as an address", Tcl_GetString(dest_obj))); + + return(TCL_ERROR); + } + + /* Parse destination netmask */ + destmask_obj = objv[3]; + parse_ret = tuapi_private_get_sockaddr_from_obj(destmask_obj, &route.rt_genmask); + if (parse_ret != 0) { + Tcl_SetObjResult(interp, Tcl_ObjPrintf("unable to parse \"%s\" as an address", Tcl_GetString(destmask_obj))); + + return(TCL_ERROR); + } + + if (route.rt_dst.sa_family != route.rt_genmask.sa_family) { + Tcl_SetObjResult(interp, + Tcl_ObjPrintf("destination (\"%s\") and destination_mask (\"%s\") are different classes", + Tcl_GetString(dest_obj), + Tcl_GetString(destmask_obj) + ) + ); + + return(TCL_ERROR); + } + + switch (route.rt_dst.sa_family) { + case AF_INET: /* IPv4 */ + if (sock_v4 == -1) { + Tcl_SetObjResult(interp, Tcl_ObjPrintf("address \"%s\" is IPv4, but unable to create IPv4 socket", Tcl_GetString(dest_obj))); + + return(TCL_ERROR); + } + + if (((struct sockaddr_in *) &route.rt_genmask)->sin_addr.s_addr == INADDR_BROADCAST) { + route.rt_flags |= RTF_HOST; + } + + sock = sock_v4; + + break; + case AF_INET6: /* IPv6 */ + if (sock_v6 == -1) { + Tcl_SetObjResult(interp, Tcl_ObjPrintf("address \"%s\" is IPv6, but unable to create IPv6 socket", Tcl_GetString(dest_obj))); + + return(TCL_ERROR); + } + + sock = sock_v6; + + break; + default: + Tcl_SetObjResult(interp, Tcl_ObjPrintf("unable to determine type of address for \"%s\"", Tcl_GetString(dest_obj))); + + return(TCL_ERROR); + } + + /* Parse remaining options */ + objc -= 4; + objv += 4; + + for (; objc > 0; objc--,objv++) { + option_name_obj = objv[0]; + + if (objc < 2) { + Tcl_SetObjResult(interp, Tcl_ObjPrintf("option \"%s\" requires an argument", Tcl_GetString(option_name_obj))); + + return(TCL_ERROR); + } + + objc--; + objv++; + + option_val_obj = objv[0]; + + switch (tuapi_internal_simplehash_obj(option_name_obj)) { + case 0x4c727779: /* gateway */ + parse_ret = tuapi_private_get_sockaddr_from_obj(option_val_obj, &route.rt_gateway); + if (parse_ret != 0) { + Tcl_SetObjResult(interp, Tcl_ObjPrintf("unable to parse \"%s\" as an address", Tcl_GetString(option_val_obj))); + + return(TCL_ERROR); + } + + route.rt_flags &= (~RTF_HOST); + route.rt_flags |= RTF_GATEWAY; + + break; + case 0x1b7a75: /* mtu */ + tcl_ret = Tcl_GetWideIntFromObj(interp, option_val_obj, &option_val_wide); + if (tcl_ret != TCL_OK) { + return(tcl_ret); + } + + route.rt_flags |= RTF_MTU; + route.rt_mtu = option_val_wide; + + break; + case 0x5e9d03e3: /* metric */ + tcl_ret = Tcl_GetWideIntFromObj(interp, option_val_obj, &option_val_wide); + if (tcl_ret != TCL_OK) { + return(tcl_ret); + } + + route.rt_metric = option_val_wide; + + break; + case 0x9dd8e8f7: /* window */ + tcl_ret = Tcl_GetWideIntFromObj(interp, option_val_obj, &option_val_wide); + if (tcl_ret != TCL_OK) { + return(tcl_ret); + } + + route.rt_flags |= RTF_WINDOW; + route.rt_window = option_val_wide; + + break; + case 0x1932f6: /* dev */ + case 0x5edbe2e5: /* device */ + route.rt_dev = strdup(Tcl_GetString(option_val_obj)); + + break; + default: + Tcl_SetObjResult(interp, Tcl_ObjPrintf("bad option \"%s\": must be gateway, mtu, metric, device, or window", Tcl_GetString(option_name_obj))); + + return(TCL_ERROR); + } + } + + /* Request route change */ + ioctl_ret = ioctl(sock, ioctl_id, &route); + if (ioctl_ret != 0) { + Tcl_SetObjResult(interp, Tcl_NewStringObj(strerror(errno), -1)); + + return(TCL_ERROR); + } + + return(TCL_OK); +} + +static int tuapi_route(ClientData cd, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[]) { + int sock_v4, sock_v6, sock; + int retval = TCL_ERROR; + + sock = tuapi_internal_getsock(&sock_v4, &sock_v6); + if (sock == -1) { + Tcl_SetObjResult(interp, Tcl_NewStringObj("unable to create socket", -1)); + + return(TCL_ERROR); + } + + switch (objc) { + case 0: + case 1: /* No arguments, list all interfaces */ + retval = tuapi_route_list(cd, interp, objc, objv, sock_v4, sock_v6); + + break; + default: + /* Otherwise, modify routes */ + retval = tuapi_route_conf(cd, interp, objc, objv, sock_v4, sock_v6); + + break; + } + + /* Cleanup */ + if (sock_v4 != -1) { + close(sock_v4); + } + + if (sock_v6 != -1) { + close(sock_v6); + } + + return(retval); +} + +static int tuapi_brctl_list(ClientData cd, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[], int sock) { + Tcl_SetObjResult(interp, Tcl_NewStringObj("not implemented", -1)); + + return(TCL_ERROR); +} + +static int tuapi_brctl_conf(ClientData cd, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[], int sock) { + Tcl_Obj *operation_obj, *bridge_name_obj, *interface_name_obj; + unsigned long arg[4]; + struct ifreq ifr; + int ioctl_ret, ioctl_id; + int add = 0; + + /* Determine operation */ + operation_obj = objv[1]; + switch (tuapi_internal_simplehash_obj(operation_obj)) { + case 0x1c993272: /* addbr */ + add = 1; + case 0x4cbb3272: /* delbr */ + if (objc != 3) { + if (add) { + Tcl_SetObjResult(interp, Tcl_NewStringObj("wrong # args: should be \"::tuapi::syscall::brctl addbr bridge\"", -1)); + } else { + Tcl_SetObjResult(interp, Tcl_NewStringObj("wrong # args: should be \"::tuapi::syscall::brctl delbr bridge\"", -1)); + } + + return(TCL_ERROR); + } + + bridge_name_obj = objv[2]; + + if (add) { + arg[0] = BRCTL_ADD_BRIDGE; + } else { + arg[0] = BRCTL_DEL_BRIDGE; + } + + arg[1] = (unsigned long) Tcl_GetString(bridge_name_obj); + arg[2] = 0; + + ioctl_ret = ioctl(sock, SIOCGIFBR, &arg); + + break; + case 0x1C9937E6: /* addif */ + add = 1; + case 0x4cbb37e6: /* delif */ + if (objc != 4) { + if (add) { + Tcl_SetObjResult(interp, Tcl_NewStringObj("wrong # args: should be \"::tuapi::syscall::brctl addif bridge interface\"", -1)); + } else { + Tcl_SetObjResult(interp, Tcl_NewStringObj("wrong # args: should be \"::tuapi::syscall::brctl delif bridge interface\"", -1)); + } + + return(TCL_ERROR); + } + + if (add) { + ioctl_id = SIOCBRADDIF; + } else { + ioctl_id = SIOCBRDELIF; + } + + bridge_name_obj = objv[2]; + interface_name_obj = objv[3]; + + memset(&ifr, 0, sizeof(ifr)); + snprintf(ifr.ifr_name, IFNAMSIZ, "%s", Tcl_GetString(interface_name_obj)); + + ioctl_ret = ioctl(sock, SIOCGIFINDEX, (void *) &ifr); + if (ioctl_ret == 0) { + snprintf(ifr.ifr_name, IFNAMSIZ, "%s", Tcl_GetString(bridge_name_obj)); + ioctl_ret = ioctl(sock, ioctl_id, (void *) &ifr); + } + + break; + } + + if (ioctl_ret < 0) { + Tcl_SetObjResult(interp, Tcl_NewStringObj(strerror(errno), -1)); + + return(TCL_ERROR); + } + + return(TCL_OK); +} + +static int tuapi_brctl(ClientData cd, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[]) { + int sock_v4, sock_v6, sock; + int retval = TCL_ERROR; + + sock = tuapi_internal_getsock(&sock_v4, &sock_v6); + if (sock == -1) { + Tcl_SetObjResult(interp, Tcl_NewStringObj("unable to create socket", -1)); + + return(TCL_ERROR); + } + + switch (objc) { + case 0: + case 1: /* No arguments, list all bridges */ + retval = tuapi_brctl_list(cd, interp, objc, objv, sock); + + break; + default: + /* Otherwise, modify routes */ + retval = tuapi_brctl_conf(cd, interp, objc, objv, sock); + + break; + } + + /* Cleanup */ + if (sock_v4 != -1) { + close(sock_v4); + } + + if (sock_v6 != -1) { + close(sock_v6); + } + + return(retval); +} + +static int tuapi_vconfig(ClientData cd, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[]) { + int sock_v4, sock_v6, sock; + int retval = TCL_ERROR; + + sock = tuapi_internal_getsock(&sock_v4, &sock_v6); + if (sock == -1) { + Tcl_SetObjResult(interp, Tcl_NewStringObj("unable to create socket", -1)); + + return(TCL_ERROR); + } + + Tcl_SetObjResult(interp, Tcl_NewStringObj("not implemented", -1)); + + /* Cleanup */ + if (sock_v4 != -1) { + close(sock_v4); + } + + if (sock_v6 != -1) { + close(sock_v6); + } + + return(retval); +} + +#ifndef DISABLE_UNIX_SOCKETS +struct tuapi_socket_unix__chan_id { + int fd; + Tcl_Channel chan; +}; + +static int tuapi_socket_unix__chan_close(ClientData id_p, Tcl_Interp *interp) { + struct tuapi_socket_unix__chan_id *id; + int fd; + + id = id_p; + + fd = id->fd; + + close(fd); + + free(id); + + return(0); +} + +static int tuapi_socket_unix__chan_read(ClientData id_p, char *buf, int bufsize, int *errorCodePtr) { + struct tuapi_socket_unix__chan_id *id; + ssize_t read_ret; + int fd; + int retval; + + id = id_p; + + fd = id->fd; + + read_ret = read(fd, buf, bufsize); + if (read_ret < 0) { + *errorCodePtr = errno; + + return(-1); + } + + retval = read_ret; + + return(retval); +} + +static int tuapi_socket_unix__chan_write(ClientData id_p, const char *buf, int toWrite, int *errorCodePtr) { + struct tuapi_socket_unix__chan_id *id; + ssize_t write_ret; + int fd; + int bytesWritten; + + id = id_p; + + fd = id->fd; + + bytesWritten = 0; + while (toWrite) { + write_ret = write(fd, buf, toWrite); + if (write_ret == 0) { + break; + } + + if (write_ret < 0) { + *errorCodePtr = errno; + + return(-1); + } + + toWrite -= write_ret; + buf += write_ret; + bytesWritten += write_ret; + } + + if (bytesWritten == 0) { + *errorCodePtr = EAGAIN; + + return(-1); + } + + return(bytesWritten); +} + +static void tuapi_socket_unix__chan_eventhandler(ClientData id_p, int mask) { + struct tuapi_socket_unix__chan_id *id; + Tcl_Channel chan; + + id = id_p; + + chan = id->chan; + + if (!chan) { + return; + } + + Tcl_NotifyChannel(chan, mask); +} + +static void tuapi_socket_unix__chan_watch(ClientData id_p, int mask) { + struct tuapi_socket_unix__chan_id *id; + int fd; + + id = id_p; + + fd = id->fd; + + Tcl_CreateFileHandler(fd, mask, tuapi_socket_unix__chan_eventhandler, id); + + return; +} + +static int tuapi_socket_unix__chan_gethandle(ClientData id_p, int direction, ClientData *handlePtr) { + struct tuapi_socket_unix__chan_id *id; + int fd; + ClientData fd_cd; + + id = id_p; + + fd = id->fd; + + memcpy(&fd_cd, &fd, sizeof(fd)); + + *handlePtr = fd_cd; + + return(TCL_OK); +} + +static Tcl_Channel tuapi_socket_unix_sock2tclchan(int sock) { + struct tuapi_socket_unix__chan_id *id; + static Tcl_ChannelType tcl_chan_type; + static int tcl_chan_type_init = 0; + Tcl_Channel tcl_chan; + char chan_name[32]; + int sock_flags; + + if (!tcl_chan_type_init) { + tcl_chan_type.typeName = "socket"; + tcl_chan_type.version = TCL_CHANNEL_VERSION_2; + tcl_chan_type.closeProc = tuapi_socket_unix__chan_close; + tcl_chan_type.inputProc = tuapi_socket_unix__chan_read; + tcl_chan_type.outputProc = tuapi_socket_unix__chan_write; + tcl_chan_type.watchProc = tuapi_socket_unix__chan_watch; + tcl_chan_type.getHandleProc = tuapi_socket_unix__chan_gethandle; + tcl_chan_type.seekProc = NULL; + tcl_chan_type.setOptionProc = NULL; + tcl_chan_type.getOptionProc = NULL; + tcl_chan_type.close2Proc = NULL; + tcl_chan_type.blockModeProc = NULL; + tcl_chan_type.flushProc = NULL; + tcl_chan_type.handlerProc = NULL; + tcl_chan_type.wideSeekProc = NULL; + tcl_chan_type.threadActionProc = NULL; + tcl_chan_type.truncateProc = NULL; + + tcl_chan_type_init = 1; + } + + snprintf(chan_name, sizeof(chan_name), "sock%u", sock); + + id = malloc(sizeof(*id)); + if (id == NULL) { + return(NULL); + } + + id->fd = sock; + id->chan = NULL; + + /* Configure socket as non-blocking */ + sock_flags = fcntl(sock, F_GETFL, 0); + if (sock_flags == -1) { + sock_flags = O_NONBLOCK; + } else { + sock_flags |= O_NONBLOCK; + } + fcntl(sock, F_SETFL, (long) sock_flags); + + /* Create the channel */ + tcl_chan = Tcl_CreateChannel(&tcl_chan_type, chan_name, id, TCL_READABLE | TCL_WRITABLE); + + /* Update the structure passed to each function to include the channel name */ + id->chan = tcl_chan; + + return(tcl_chan); +} + +struct tuapi_socket_unix__chan_accept_cd { + int fd; + Tcl_Interp *interp; + Tcl_Obj *command; +}; + +static void tuapi_socket_unix__chan_accept(ClientData cd_p, int mask) { + struct tuapi_socket_unix__chan_accept_cd *cd; + Tcl_Interp *interp; + Tcl_Channel chan; + Tcl_Obj *command, *command_to_run_objs[5], *command_to_run; + int setsockopt_ret; + int pass_creds_true = 1; + int fd; + int sock; + + if ((mask & TCL_READABLE) != TCL_READABLE) { + return; + } + + cd = cd_p; + + fd = cd->fd; + interp = cd->interp; + command = cd->command; + + sock = accept(fd, NULL, NULL); + if (sock < 0) { + return; + } + + chan = tuapi_socket_unix_sock2tclchan(sock); + if (chan == NULL) { + close(sock); + + return; + } + + setsockopt_ret = setsockopt(sock, SOL_SOCKET, SO_PASSCRED, &pass_creds_true, sizeof(pass_creds_true)); + if (setsockopt_ret != 0) { + close(sock); + + return; + } + + Tcl_RegisterChannel(interp, chan); + + command_to_run_objs[0] = command; + command_to_run_objs[1] = Tcl_NewStringObj(Tcl_GetChannelName(chan), -1); + command_to_run_objs[2] = Tcl_NewStringObj("...uid...", -1); /* XXX: TODO */ + command_to_run_objs[3] = Tcl_NewStringObj("...gid...", -1); /* XXX: TODO */ + command_to_run_objs[4] = Tcl_NewStringObj("...pid...", -1); /* XXX: TODO */ + command_to_run = Tcl_ConcatObj(sizeof(command_to_run_objs) / sizeof(command_to_run_objs[0]), command_to_run_objs); + + Tcl_EvalObjEx(interp, command_to_run, TCL_EVAL_GLOBAL); + + return; +} + +static int tuapi_socket_unix_server(ClientData cd, Tcl_Interp *interp, int sock, const char *path, Tcl_Obj *command) { + struct tuapi_socket_unix__chan_accept_cd *accept_cd; + struct sockaddr_un dest; + ssize_t pathlen; + int bind_ret, listen_ret; + + pathlen = strlen(path) + 1; + if (pathlen <= 0) { + Tcl_SetObjResult(interp, Tcl_NewStringObj("path too short", -1)); + + return(TCL_ERROR); + } + + if (pathlen > sizeof(dest.sun_path)) { + Tcl_SetObjResult(interp, Tcl_NewStringObj("path too long", -1)); + + return(TCL_ERROR); + } + + dest.sun_family = AF_UNIX; + memcpy(dest.sun_path, path, pathlen); + + bind_ret = bind(sock, (struct sockaddr *) &dest, sizeof(dest)); + if (bind_ret != 0) { + Tcl_SetObjResult(interp, Tcl_NewStringObj(strerror(errno), -1)); + + return(TCL_ERROR); + } + + listen_ret = listen(sock, 2); + if (listen_ret != 0) { + Tcl_SetObjResult(interp, Tcl_NewStringObj(strerror(errno), -1)); + + return(TCL_ERROR); + } + + accept_cd = malloc(sizeof(*accept_cd)); + if (accept_cd == NULL) { + Tcl_SetObjResult(interp, Tcl_NewStringObj(strerror(errno), -1)); + + return(TCL_ERROR); + } + + accept_cd->fd = sock; + accept_cd->interp = interp; + accept_cd->command = command; + + Tcl_IncrRefCount(command); + + Tcl_CreateFileHandler(sock, TCL_READABLE, tuapi_socket_unix__chan_accept, accept_cd); + + return(TCL_OK); +} + +static int tuapi_socket_unix_client(ClientData cd, Tcl_Interp *interp, int sock, const char *path) { + Tcl_Channel chan; + struct sockaddr_un dest; + ssize_t pathlen; + int connect_ret, setsockopt_ret; + int pass_creds_true = 1; + + pathlen = strlen(path) + 1; + if (pathlen <= 0) { + Tcl_SetObjResult(interp, Tcl_NewStringObj("path too short", -1)); + + return(TCL_ERROR); + } + + if (pathlen > sizeof(dest.sun_path)) { + Tcl_SetObjResult(interp, Tcl_NewStringObj("path too long", -1)); + + return(TCL_ERROR); + } + + dest.sun_family = AF_UNIX; + memcpy(dest.sun_path, path, pathlen); + + connect_ret = connect(sock, (struct sockaddr *) &dest, sizeof(dest)); + if (connect_ret != 0) { + Tcl_SetObjResult(interp, Tcl_NewStringObj(strerror(errno), -1)); + + return(TCL_ERROR); + } + + setsockopt_ret = setsockopt(sock, SOL_SOCKET, SO_PASSCRED, &pass_creds_true, sizeof(pass_creds_true)); + if (setsockopt_ret != 0) { + Tcl_SetObjResult(interp, Tcl_NewStringObj(strerror(errno), -1)); + + return(TCL_ERROR); + } + + chan = tuapi_socket_unix_sock2tclchan(sock); + if (chan == NULL) { + Tcl_SetObjResult(interp, Tcl_NewStringObj("unable to create Tcl channel", -1)); + + return(TCL_ERROR); + } + + Tcl_RegisterChannel(interp, chan); + + Tcl_SetObjResult(interp, Tcl_NewStringObj(Tcl_GetChannelName(chan), -1)); + + return(TCL_OK); +} + +static int tuapi_socket_unix(ClientData cd, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[]) { + Tcl_Obj *path_obj, *command_obj; + char *path; + int retval; + int sock; + + if (objc < 2) { + Tcl_SetObjResult(interp, Tcl_NewStringObj("wrong # args: should be \"::tuapi::syscall::socket_unix path\" or \"::tuapi::syscall::socket_unix -server command path\"", -1)); + + return(TCL_ERROR); + } + + path_obj = objv[1]; + path = Tcl_GetString(path_obj); + + sock = socket(AF_UNIX, SOCK_STREAM, 0); + if (sock == -1) { + Tcl_SetObjResult(interp, Tcl_NewStringObj(strerror(errno), -1)); + + return(TCL_ERROR); + } + + if (strcmp(path, "-server") == 0) { + if (objc != 4) { + Tcl_SetObjResult(interp, Tcl_NewStringObj("wrong # args: should be \"::tuapi::syscall::socket_unix -server command path\"", -1)); + + close(sock); + + return(TCL_ERROR); + } + + command_obj = objv[2]; + path_obj = objv[3]; + + path = Tcl_GetString(path_obj); + + retval = tuapi_socket_unix_server(cd, interp, sock, path, command_obj); + } else { + if (objc != 2) { + Tcl_SetObjResult(interp, Tcl_NewStringObj("wrong # args: should be \"::tuapi::syscall::socket_unix path\"", -1)); + + close(sock); + + return(TCL_ERROR); + } + + retval = tuapi_socket_unix_client(cd, interp, sock, path); + } + + if (retval != TCL_OK) { + close(sock); + } + + return(retval); +} +#else +static int tuapi_socket_unix(ClientData cd, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[]) { + Tcl_SetObjResult(interp, Tcl_NewStringObj("not implemented", -1)); + return(TCL_ERROR) +} +#endif + +static int tuapi_tsmf_start_svc(ClientData cd, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[]) { + struct timeval select_timeout; + Tcl_WideInt umask_val, timeout_val, uid_val, gid_val; + Tcl_Obj *filename_obj, *env_obj, *logfile_obj, **env_entry_objv, *cwd_obj, *umask_obj, *uid_obj, *gid_obj; + Tcl_Obj *sri_obj, *timeout_obj; + pid_t child, child_pgid = -1, waitpid_ret; + ssize_t read_ret; + time_t currtime; + char *argv[3], *envv[512]; + char *logfile, *filename, *cwd; + char logmsg[2048]; + fd_set read_fdset; + int pipe_ret, setsid_ret, execve_ret, tcl_ret, select_ret, chdir_ret; + int null_fd, log_fd, tmp_fd, max_fd; + int env_entry_objc; + int fds[2], fd; + int status; + int idx; + + /* 1. Parse arguments */ + /* 1.a. Ensure the correct number of arguments were passed */ + if (objc != 10) { + Tcl_SetObjResult(interp, Tcl_NewStringObj("wrong # args: should be \"::tuapi::syscall::tsmf_start_svc sri filename logfile env cwd umask uid gid timeout\"", -1)); + + return(TCL_ERROR); + } + + /* 1.b. Identify Tcl_Objs to use for each argument */ + sri_obj = objv[1]; + filename_obj = objv[2]; + logfile_obj = objv[3]; + env_obj = objv[4]; + cwd_obj = objv[5]; + umask_obj = objv[6]; + uid_obj = objv[7]; + gid_obj = objv[8]; + timeout_obj = objv[9]; + + /* 1.c. Store string arguments */ + filename = Tcl_GetString(filename_obj); + logfile = Tcl_GetString(logfile_obj); + cwd = Tcl_GetString(cwd_obj); + + /* 1.d. Integer objects */ + tcl_ret = Tcl_GetWideIntFromObj(interp, umask_obj, &umask_val); + if (tcl_ret != TCL_OK) { + return(tcl_ret); + } + + tcl_ret = Tcl_GetWideIntFromObj(interp, timeout_obj, &timeout_val); + if (tcl_ret != TCL_OK) { + return(tcl_ret); + } + + tcl_ret = Tcl_GetWideIntFromObj(interp, uid_obj, &uid_val); + if (tcl_ret != TCL_OK) { + return(tcl_ret); + } + + tcl_ret = Tcl_GetWideIntFromObj(interp, gid_obj, &gid_val); + if (tcl_ret != TCL_OK) { + return(tcl_ret); + } + + /* 1.e. Process environment */ + tcl_ret = Tcl_ListObjGetElements(interp, env_obj, &env_entry_objc, &env_entry_objv); + if (tcl_ret != TCL_OK) { + return(tcl_ret); + } + + for (idx = 0; idx < MIN(env_entry_objc, sizeof(envv) / sizeof(envv[0]) - 1); idx++) { + envv[idx] = Tcl_GetString(env_entry_objv[idx]); + } + envv[idx] = NULL; + + /* 2. Create a pipe for communication between the processes */ + pipe_ret = pipe(fds); + if (pipe_ret != 0) { + Tcl_SetObjResult(interp, Tcl_NewStringObj("pipe failed", -1)); + + return(TCL_ERROR); + } + + /* 3. Fork into a new process */ + child = fork(); + if (child == -1) { + Tcl_SetObjResult(interp, Tcl_NewStringObj("fork failed", -1)); + + return(TCL_ERROR); + } + + if (child != 0) { + /* 4.parent. Get PGID from child */ + /* 4.parent.a. Close write end of pipe -- we are read-only */ + close(fds[1]); + fd = fds[0]; + + /* 4.parent.b. Read process group ID of child from pipe */ + select_timeout.tv_sec = timeout_val; + select_timeout.tv_usec = 0; + + FD_ZERO(&read_fdset); + FD_SET(fd, &read_fdset); + + select_ret = select(fd + 1, &read_fdset, NULL, NULL, &select_timeout); + if (select_ret == 0) { + /* On timeout, terminate starting process */ + child_pgid = getpgid(child); + if (child_pgid != -1) { + kill(-child_pgid, SIGKILL); + } + + Tcl_SetObjResult(interp, Tcl_NewStringObj("timeout", -1)); + + return(TCL_ERROR); + } + + if (select_ret > 0) { + read_ret = read(fd, &child_pgid, sizeof(child_pgid)); + } + + /* 4.parent.c. Close read end of pipe */ + close(fd); + + /* 4.parent.d. Verify read was meaningful */ + if (read_ret != sizeof(child_pgid)) { + Tcl_SetObjResult(interp, Tcl_NewStringObj("failed to communicate with started service", -1)); + + return(TCL_ERROR); + } + + /* 4.parent.e. If the PGID given is actually an error, return error */ + if (child_pgid == -1) { + Tcl_SetObjResult(interp, Tcl_NewStringObj("service failed to start", -1)); + + return(TCL_ERROR); + } + + /* 4.parent.f. Return PGID to Tcl */ + Tcl_SetObjResult(interp, Tcl_NewWideIntObj((Tcl_WideInt) child_pgid)); + + return(TCL_OK); + } + + /* 4.child.a. Close read end of pipe -- we only write to it */ + close(fds[0]); + fd = fds[1]; + + /* 5. Create a new session */ + setsid_ret = setsid(); + if (setsid_ret == -1) { + write(fd, &child_pgid, sizeof(child_pgid)); + + _exit(0); + } + + /* 6. Setup environment */ + /* 6.a. Set umask */ + umask(umask_val); + + /* 6.b. Set working directory */ + chdir_ret = chdir(cwd); + if (chdir_ret != 0) { + write(fd, &child_pgid, sizeof(child_pgid)); + + _exit(0); + } + + /* 6.c. Open log file for stderr and stdout */ + log_fd = open(logfile, O_WRONLY | O_CREAT | O_APPEND, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH); + + /* 6.d. Open "/dev/null" for stdin */ + null_fd = open("/dev/null", O_RDONLY); + if (null_fd < 0 || log_fd <0) { + write(fd, &child_pgid, sizeof(child_pgid)); + + _exit(0); + } + + /* 6.e. Redirect stdin, stdout, and stderr to null, logs */ + dup2(null_fd, STDIN_FILENO); + dup2(log_fd, STDOUT_FILENO); + dup2(log_fd, STDERR_FILENO); + + close(null_fd); + close(log_fd); + + /* 6.f. Close stray file descriptors */ + max_fd = MAX(MAX(MAX(1024, STDIN_FILENO), STDOUT_FILENO), STDERR_FILENO); + for (tmp_fd = 0; tmp_fd < max_fd; tmp_fd++) { + if (tmp_fd == STDIN_FILENO || tmp_fd == STDOUT_FILENO || tmp_fd == STDERR_FILENO) { + continue; + } + + if (tmp_fd == fd) { + continue; + } + + close(tmp_fd); + } + + /* 6.g. Switch to appropriate user/group */ + /* 6.g.i. Group */ + setgid(gid_val); + + /* 6.g.ii. User */ + setuid(uid_val); + + /* 7. Create a new process to actually spawn the process */ + child = fork(); + if (child == -1) { + write(fd, &child_pgid, sizeof(child_pgid)); + + _exit(0); + } + + if (child != 0) { + /* 7.parent.a. Wait for child process to terminate and collect status */ + waitpid_ret = waitpid(child, &status, 0); + if (waitpid_ret == -1) { + status = -1; + } + + /* 7.parent.b. Set PGID (if successful, -1 otherwise) to pass back to TSMF */ + if (status == 0) { + child_pgid = getpgid(getpid()); + } + write(fd, &child_pgid, sizeof(child_pgid)); + + close(fd); + + /* 7.parent.c. Write log of result */ + /* Note: We avoid ANSI I/O here in case there is already something in the buffer */ + currtime = time(NULL); + strftime(logmsg, sizeof(logmsg), "[ %b %e %H:%M:%S ", localtime(&currtime)); + write(STDERR_FILENO, logmsg, strlen(logmsg)); + + snprintf(logmsg, sizeof(logmsg), "Method \"start\" exited with status %i ]\n", WEXITSTATUS(status)); + write(STDERR_FILENO, logmsg, strlen(logmsg)); + + _exit(0); + } + + /* 7.child.a. Close channel to parent */ + close(fd); + + /* 8. Log attempt to run start method */ + currtime = time(NULL); + strftime(logmsg, sizeof(logmsg), "[ %b %e %H:%M:%S ", localtime(&currtime)); + write(STDERR_FILENO, logmsg, strlen(logmsg)); + + snprintf(logmsg, sizeof(logmsg), "Executing start method (\"%s\") ]\n", filename); + write(STDERR_FILENO, logmsg, strlen(logmsg)); + + /* 9. execve() new image */ + argv[0] = filename; + argv[1] = "start"; + argv[2] = NULL; + execve_ret = execve(filename, argv, envv); + + /* 10. Abort if something has gone wrong */ + _exit(execve_ret); + + /* Handle lint-ness */ + return(TCL_ERROR); + sri_obj = sri_obj; +} + +int Tuapi_Init(Tcl_Interp *interp) { +#ifdef USE_TCL_STUBS + const char *tclInitStubs_ret; + + /* Initialize Stubs */ + tclInitStubs_ret = Tcl_InitStubs(interp, "8.4", 0); + if (!tclInitStubs_ret) { + return(TCL_ERROR); + } +#endif + + /* Kernel maintenance related commands */ + Tcl_CreateObjCommand(interp, "::tuapi::syscall::insmod", tuapi_insmod, NULL, NULL); + Tcl_CreateObjCommand(interp, "::tuapi::syscall::rmmod", tuapi_rmmod, NULL, NULL); + Tcl_CreateObjCommand(interp, "::tuapi::syscall::lsmod", tuapi_lsmod, NULL, NULL); + Tcl_CreateObjCommand(interp, "::tuapi::syscall::hostname", tuapi_hostname, NULL, NULL); + Tcl_CreateObjCommand(interp, "::tuapi::syscall::domainname", tuapi_domainname, NULL, NULL); + + /* Block or char device related commands */ + Tcl_CreateObjCommand(interp, "::tuapi::syscall::losetup", tuapi_losetup, NULL, NULL); + + /* Filesystem related commands */ + Tcl_CreateObjCommand(interp, "::tuapi::syscall::mount", tuapi_mount, NULL, NULL); + Tcl_CreateObjCommand(interp, "::tuapi::syscall::umount", tuapi_umount, NULL, NULL); + Tcl_CreateObjCommand(interp, "::tuapi::syscall::swapon", tuapi_swapon, NULL, NULL); + Tcl_CreateObjCommand(interp, "::tuapi::syscall::swapoff", tuapi_swapoff, NULL, NULL); + Tcl_CreateObjCommand(interp, "::tuapi::syscall::mknod", tuapi_mknod, NULL, NULL); + + /* Process related commands */ + Tcl_CreateObjCommand(interp, "::tuapi::syscall::getuid", tuapi_getuid, NULL, NULL); + Tcl_CreateObjCommand(interp, "::tuapi::syscall::chroot", tuapi_chroot, NULL, NULL); + Tcl_CreateObjCommand(interp, "::tuapi::syscall::pivot_root", tuapi_pivot_root, NULL, NULL); + Tcl_CreateObjCommand(interp, "::tuapi::syscall::kill", tuapi_kill, NULL, NULL); + Tcl_CreateObjCommand(interp, "::tuapi::syscall::ps", tuapi_ps, NULL, NULL); + Tcl_CreateObjCommand(interp, "::tuapi::syscall::execve", tuapi_execve, NULL, NULL); + + /* Network related commands */ + Tcl_CreateObjCommand(interp, "::tuapi::syscall::ifconfig", tuapi_ifconfig, NULL, NULL); + Tcl_CreateObjCommand(interp, "::tuapi::syscall::route", tuapi_route, NULL, NULL); + Tcl_CreateObjCommand(interp, "::tuapi::syscall::brctl", tuapi_brctl, NULL, NULL); + Tcl_CreateObjCommand(interp, "::tuapi::syscall::vconfig", tuapi_vconfig, NULL, NULL); + + /* Needed commands for basic services Tcl lacks */ + Tcl_CreateObjCommand(interp, "::tuapi::syscall::socket_unix", tuapi_socket_unix, NULL, NULL); + + /* Service (TSMF) related commands */ + Tcl_CreateObjCommand(interp, "::tuapi::syscall::tsmf_start_svc", tuapi_tsmf_start_svc, NULL, NULL); + + /* Internal functions */ + Tcl_CreateObjCommand(interp, "::tuapi::internal::hash", tuapi_internalproc_simplehash, NULL, NULL); + + /* Define constants */ + /** Create parent namespace **/ + Tcl_CreateNamespace(interp, "::tuapi::const", NULL, NULL); + + /** Define constants, for real **/ + Tcl_ObjSetVar2(interp, Tcl_NewStringObj("::tuapi::const::HOST_NAME_MAX", -1), NULL, Tcl_NewWideIntObj(HOST_NAME_MAX), TCL_GLOBAL_ONLY); + + /* Create high-level user functions */ + Tcl_Eval(interp, +#include "tuapi.tcl.h" + ); + + Tcl_PkgProvide(interp, "tuapi", "0.1"); + + return(TCL_OK); +} ADDED tuapi.tcl Index: tuapi.tcl ================================================================== --- tuapi.tcl +++ tuapi.tcl @@ -0,0 +1,256 @@ +#! /usr/bin/env tclsh + +namespace eval ::tuapi {} +namespace eval ::tuapi::helper {} + +set ::tuapi::_mount_flags(bind) BIND +set ::tuapi::_mount_flags(move) MOVE +set ::tuapi::_mount_flags(remount) REMOUNT +set ::tuapi::_mount_flags(mandlock) MANDLOCK +set ::tuapi::_mount_flags(dirsync) DIRSYNC +set ::tuapi::_mount_flags(noatime) NOATIME +set ::tuapi::_mount_flags(nodiratime) NODIRATIME +set ::tuapi::_mount_flags(relatime) RELATIME +set ::tuapi::_mount_flags(strictatime) STRICTATIME +set ::tuapi::_mount_flags(nodev) NODEV +set ::tuapi::_mount_flags(noexec) NOEXEC +set ::tuapi::_mount_flags(nosuid) NOSUID +set ::tuapi::_mount_flags(ro) RDONLY +set ::tuapi::_mount_flags(silent) SILENT +set ::tuapi::_mount_flags(synchronous) SYNCHRONOUS +set ::tuapi::_mount_flags(sync) SYNCHRONOUS + + +# Determine where to mount a given device (usually by checking "/etc/fstab") +proc ::tuapi::helper::find_mountpoint {device} { + set data "" + catch { + set fd [open "/etc/fstab"] + set data [read -nonewline $fd] + close $fd + } + + foreach line [split $data "\n"] { + set line [string trim [regsub {#.*$} $line ""]] + set line [regsub -all {[ \t][ \t][ \t]*} $line " "] + + set work [split $line] + + set curr_device [lindex $work 0] + set curr_mountpoint [lindex $work 1] + set curr_fstype [lindex $work 2] + set curr_opts [split [lindex $work 3] ","] + set curr_dumpfreq [lindex $work 4] + set curr_fsckpass [lindex $work 5] + + + if {$curr_device == $device || $curr_mountpoint == $device} { + return [list source $curr_device target $curr_mountpoint fstype $curr_fstype options $curr_opts dumpfreq $curr_dumpfreq fsckpass $curr_fsckpass] + } + } + + return -code error "no entry found in \"/etc/fstab\" for \"$device\"" +} + +proc ::tuapi::mount args { + set options_list [list] + + for {set idx 0} {$idx < [llength $args]} {incr idx} { + set curr_arg [lindex $args $idx] + + switch -glob -- $curr_arg { + "-t" { + incr idx + set fstype [lindex $args $idx] + } + "-r" { + lappend options_list "RDONLY" + } + "-w" { + set idx [lsearch -exact $options_list "RDONLY"] + if {$idx != -1} { + set options_list [lreplace $options_list $idx $idx] + } + } + "-o" { + incr idx + set options [lindex $args $idx] + } + "--" { + incr idx + + break + } + "-*" { + return -code error "unknown option \"$curr_arg\"" + } + default { + break + } + } + } + + set args [lrange $args $idx end] + + if {[llength $args] < 1 || [llength $args] > 2} { + return -code error "wrong # args: should be \"::tuapi::mount ?options? source ?target?\"" + } + + set source [lindex $args 0] + + if {[llength $args] == 2} { + set target [lindex $args 1] + } else { + array set mountinfo [::tuapi::helper::find_mountpoint $source] + set source $mountinfo(source) + set target $mountinfo(target) + + if {![info exists fstype]} { + set fstype $mountinfo(fstype) + } + + if {![info exists options]} { + set options $mountinfo(options) + } + } + + # Ensure all mount-related parameters have been computed + if {![info exists fstype]} { + set fstype "auto" + } + + if {![info exists options]} { + set options [list] + } + + # Process options + foreach option $options { + set option_lc [string tolower $option] + + # Special option handling + switch -- $option_lc { + "defaults" { + set options_list [list] + unset -nocomplain unknown_options + + continue + } + "rw" { + set option_lc "noro" + } + "norw" { + set option_lc "ro" + } + } + + # Example: noatime + if {[info exists ::tuapi::_mount_flags($option_lc)]} { + lappend options_list $::tuapi::_mount_flags($option_lc) + + continue + } + + # Example: atime + if {[info exists ::tuapi::_mount_flags(no$option_lc)]} { + set idx [lsearch -exact $options_list $::tuapi::_mount_flags(no$option_lc)] + if {$idx != -1} { + set options_list [lreplace $options_list $idx $idx] + } + + continue + } + + # Example: norelatime + if {[string match "no*" $option_lc]} { + set neg_option_lc [string range $option_lc 2 end] + + if {[info exists ::tuapi::_mount_flags($neg_option_lc)]} { + set idx [lsearch -exact $options_list $::tuapi::_mount_flags($neg_option_lc)] + if {$idx != -1} { + set options_list [lreplace $options_list $idx $idx] + } + + continue + } + } + + # Accumulate unknown options + lappend unknown_options $option + } + + # Use "swapon" if this is swap + if {$fstype == "swap"} { + return [::tuapi::syscall::swapon $source] + } + + # Otherwise, call "mount" system call + ## If we have accumulated any unknown options, pass them as a + ## comma-seperated value string + if {[info exists unknown_options]} { + set data [join $unknown_options ","] + + return [::tuapi::syscall::mount $source $target $fstype $options_list $data] + } + + return [::tuapi::syscall::mount $source $target $fstype $options_list] +} + +proc ::tuapi::umount {dir {flags ""}} { + return [::tuapi::syscall::umount $dir [string toupper $flags]] +} + +proc ::tuapi::kill {pid sig} { + return [::tuapi::syscall::kill $pid [string toupper $sig]] +} + +proc ::tuapi::killpg {pgroup sig} { + if {$pgroup <= 1} { + return -code error "invalid process group specified (must be greater than 1)" + } + + return [::tuapi::syscall::kill -$pgroup [string toupper $sig]] +} + +proc ::tuapi::ifconfig args { + if {[llength $args] == 0} { + # Return information on all interfaces + set retlist [list] + foreach interface [::tuapi::syscall::ifconfig] { + lappend retlist $interface [::tuapi::syscall::ifconfig $interface] + } + + return $retlist + } + + set interface [lindex $args 0] + set args [lrange $args 1 end] + + array set ifaceinfo [::tuapi::syscall::ifconfig $interface] + + if {[llength $args] == 0} { + return [array get ifaceinfo] + } + + for {set idx 0} {$idx < [llength $args]} {incr idx} { + set opt [lindex $args $idx] + + switch -- $opt { + "up" { + if {[info exists ifaceinfo(flags)]} { + set flags $ifaceinfo(flags) + } else { + set flags "" + } + + foreach newflag [list UP RUNNING] { + if {[lsearch -exact $flags $newflag] == -1} { + lappend flags $newflag + } + } + + ::tuapi::syscall::ifconfig $interface flags $flags + } + + } + } +}