diff --git a/Dockerfile b/Dockerfile index 030600b..96afa5e 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,7 +1,9 @@ -FROM rastasheep/ubuntu-sshd:18.04 +#FROM rastasheep/ubuntu-sshd:18.04 +FROM ubuntu:22.04 # Buildroot version to use -ARG BUILD_ROOT_RELEASE=2021.02-rc2 +#ARG BUILD_ROOT_RELEASE=2021.02-rc2 +ARG BUILD_ROOT_RELEASE=2024.02.8 # Root password for SSH ARG ROOT_PASSWORD=browser-vm @@ -23,6 +25,7 @@ RUN echo 'root:${ROOT_PASSWORD}' | chpasswd; \ WORKDIR /root RUN DEBIAN_FRONTEND=noninteractive apt-get -q -y install \ bc \ + file \ build-essential \ bzr \ cpio \ diff --git a/buildroot-v86/.config.busybox b/buildroot-v86/.config.busybox index d104550..aaddd8e 100644 --- a/buildroot-v86/.config.busybox +++ b/buildroot-v86/.config.busybox @@ -1,7 +1,7 @@ # # Automatically generated make config: don't edit -# Busybox version: 1.33.0 -# Mon Oct 14 15:09:24 2024 +# Busybox version: 1.36.1 +# Wed Dec 11 17:19:48 2024 # CONFIG_HAVE_DOT_CONFIG=y @@ -93,10 +93,16 @@ CONFIG_FEATURE_BUFFERS_USE_MALLOC=y # CONFIG_FEATURE_BUFFERS_GO_IN_BSS is not set CONFIG_PASSWORD_MINLEN=6 CONFIG_MD5_SMALL=1 +CONFIG_SHA1_SMALL=3 +# CONFIG_SHA1_HWACCEL is not set +# CONFIG_SHA256_HWACCEL is not set CONFIG_SHA3_SMALL=1 -# CONFIG_FEATURE_FAST_TOP is not set -# CONFIG_FEATURE_ETC_NETWORKS is not set -# CONFIG_FEATURE_ETC_SERVICES is not set +CONFIG_FEATURE_NON_POSIX_CP=y +# CONFIG_FEATURE_VERBOSE_CP_MESSAGE is not set +CONFIG_FEATURE_USE_SENDFILE=y +CONFIG_FEATURE_COPYBUF_KB=4 +CONFIG_MONOTONIC_SYSCALL=y +CONFIG_IOCTL_HEX2STR_ERROR=y CONFIG_FEATURE_EDITING=y CONFIG_FEATURE_EDITING_MAX_LEN=1024 CONFIG_FEATURE_EDITING_VI=y @@ -120,14 +126,9 @@ CONFIG_LAST_SUPPORTED_WCHAR=0 # CONFIG_UNICODE_BIDI_SUPPORT is not set # CONFIG_UNICODE_NEUTRAL_TABLE is not set # CONFIG_UNICODE_PRESERVE_BROKEN is not set -CONFIG_FEATURE_NON_POSIX_CP=y -# CONFIG_FEATURE_VERBOSE_CP_MESSAGE is not set -CONFIG_FEATURE_USE_SENDFILE=y -CONFIG_FEATURE_COPYBUF_KB=4 -CONFIG_FEATURE_SKIP_ROOTFS=y -CONFIG_MONOTONIC_SYSCALL=y -CONFIG_IOCTL_HEX2STR_ERROR=y -CONFIG_FEATURE_HWIB=y +# CONFIG_LOOP_CONFIGURE is not set +# CONFIG_NO_LOOP_CONFIGURE is not set +CONFIG_TRY_LOOP_CONFIGURE=y # # Applets @@ -162,6 +163,8 @@ CONFIG_FEATURE_BZIP2_DECOMPRESS=y CONFIG_CPIO=y # CONFIG_FEATURE_CPIO_O is not set # CONFIG_FEATURE_CPIO_P is not set +# CONFIG_FEATURE_CPIO_IGNORE_DEVNO is not set +# CONFIG_FEATURE_CPIO_RENUMBER_INODES is not set # CONFIG_DPKG is not set # CONFIG_DPKG_DEB is not set CONFIG_GZIP=y @@ -197,6 +200,22 @@ CONFIG_FEATURE_UNZIP_XZ=y # # Coreutils # +CONFIG_FEATURE_VERBOSE=y + +# +# Common options for date and touch +# +CONFIG_FEATURE_TIMEZONE=y + +# +# Common options for cp and mv +# +CONFIG_FEATURE_PRESERVE_HARDLINKS=y + +# +# Common options for df, du, ls +# +CONFIG_FEATURE_HUMAN_READABLE=y CONFIG_BASENAME=y CONFIG_CAT=y CONFIG_FEATURE_CATN=y @@ -207,11 +226,13 @@ CONFIG_CHOWN=y # CONFIG_FEATURE_CHOWN_LONG_OPTIONS is not set CONFIG_CHROOT=y CONFIG_CKSUM=y +CONFIG_CRC32=y # CONFIG_COMM is not set CONFIG_CP=y # CONFIG_FEATURE_CP_LONG_OPTIONS is not set # CONFIG_FEATURE_CP_REFLINK is not set CONFIG_CUT=y +CONFIG_FEATURE_CUT_REGEX=y CONFIG_DATE=y CONFIG_FEATURE_DATE_ISOFMT=y # CONFIG_FEATURE_DATE_NANO is not set @@ -223,6 +244,7 @@ CONFIG_FEATURE_DD_IBS_OBS=y CONFIG_FEATURE_DD_STATUS=y CONFIG_DF=y # CONFIG_FEATURE_DF_FANCY is not set +CONFIG_FEATURE_SKIP_ROOTFS=y CONFIG_DIRNAME=y CONFIG_DOS2UNIX=y CONFIG_UNIX2DOS=y @@ -316,13 +338,13 @@ CONFIG_TEST2=y CONFIG_FEATURE_TEST_64=y # CONFIG_TIMEOUT is not set CONFIG_TOUCH=y -# CONFIG_FEATURE_TOUCH_NODEREF is not set CONFIG_FEATURE_TOUCH_SUSV3=y CONFIG_TR=y CONFIG_FEATURE_TR_CLASSES=y CONFIG_FEATURE_TR_EQUIV=y CONFIG_TRUE=y CONFIG_TRUNCATE=y +CONFIG_TSORT=y CONFIG_TTY=y CONFIG_UNAME=y CONFIG_UNAME_OSNAME="GNU/Linux" @@ -342,21 +364,6 @@ CONFIG_W=y CONFIG_WHOAMI=y CONFIG_YES=y -# -# Common options -# -CONFIG_FEATURE_VERBOSE=y - -# -# Common options for cp and mv -# -CONFIG_FEATURE_PRESERVE_HARDLINKS=y - -# -# Common options for df, du, ls -# -CONFIG_FEATURE_HUMAN_READABLE=y - # # Console Utilities # @@ -424,6 +431,7 @@ CONFIG_VI=y CONFIG_FEATURE_VI_MAX_LEN=4096 CONFIG_FEATURE_VI_8BIT=y CONFIG_FEATURE_VI_COLON=y +CONFIG_FEATURE_VI_COLON_EXPAND=y CONFIG_FEATURE_VI_YANKMARK=y CONFIG_FEATURE_VI_SEARCH=y # CONFIG_FEATURE_VI_REGEX_SEARCH is not set @@ -437,6 +445,7 @@ CONFIG_FEATURE_VI_ASK_TERMINAL=y CONFIG_FEATURE_VI_UNDO=y CONFIG_FEATURE_VI_UNDO_QUEUE=y CONFIG_FEATURE_VI_UNDO_QUEUE_MAX=256 +CONFIG_FEATURE_VI_VERBOSE_STATUS=y CONFIG_FEATURE_ALLOW_EXEC=y # @@ -445,7 +454,11 @@ CONFIG_FEATURE_ALLOW_EXEC=y CONFIG_FIND=y CONFIG_FEATURE_FIND_PRINT0=y CONFIG_FEATURE_FIND_MTIME=y +CONFIG_FEATURE_FIND_ATIME=y +CONFIG_FEATURE_FIND_CTIME=y CONFIG_FEATURE_FIND_MMIN=y +CONFIG_FEATURE_FIND_AMIN=y +CONFIG_FEATURE_FIND_CMIN=y CONFIG_FEATURE_FIND_PERM=y CONFIG_FEATURE_FIND_TYPE=y CONFIG_FEATURE_FIND_EXECUTABLE=y @@ -453,6 +466,7 @@ CONFIG_FEATURE_FIND_XDEV=y CONFIG_FEATURE_FIND_MAXDEPTH=y CONFIG_FEATURE_FIND_NEWER=y # CONFIG_FEATURE_FIND_INUM is not set +CONFIG_FEATURE_FIND_SAMEFILE=y CONFIG_FEATURE_FIND_EXEC=y CONFIG_FEATURE_FIND_EXEC_PLUS=y CONFIG_FEATURE_FIND_USER=y @@ -743,14 +757,15 @@ CONFIG_FEATURE_VOLUMEID_UBIFS=y # Miscellaneous Utilities # # CONFIG_ADJTIMEX is not set +CONFIG_ASCII=y # CONFIG_BBCONFIG is not set # CONFIG_FEATURE_COMPRESS_BBCONFIG is not set CONFIG_BC=y -CONFIG_DC=y +# CONFIG_DC is not set CONFIG_FEATURE_DC_BIG=y # CONFIG_FEATURE_DC_LIBM is not set -CONFIG_FEATURE_BC_INTERACTIVE=y -CONFIG_FEATURE_BC_LONG_OPTIONS=y +# CONFIG_FEATURE_BC_INTERACTIVE is not set +# CONFIG_FEATURE_BC_LONG_OPTIONS is not set # CONFIG_BEEP is not set CONFIG_FEATURE_BEEP_FREQ=0 CONFIG_FEATURE_BEEP_LENGTH_MS=0 @@ -763,70 +778,72 @@ CONFIG_FEATURE_BEEP_LENGTH_MS=0 # CONFIG_FEATURE_CHAT_VAR_ABORT_LEN is not set # CONFIG_FEATURE_CHAT_CLR_ABORT is not set # CONFIG_CONSPY is not set -CONFIG_CROND=y +# CONFIG_CROND is not set # CONFIG_FEATURE_CROND_D is not set # CONFIG_FEATURE_CROND_CALL_SENDMAIL is not set -CONFIG_FEATURE_CROND_SPECIAL_TIMES=y -CONFIG_FEATURE_CROND_DIR="/var/spool/cron" -CONFIG_CRONTAB=y +# CONFIG_FEATURE_CROND_SPECIAL_TIMES is not set +CONFIG_FEATURE_CROND_DIR="" +# CONFIG_CRONTAB is not set # CONFIG_DEVFSD is not set # CONFIG_DEVFSD_MODLOAD is not set # CONFIG_DEVFSD_FG_NP is not set # CONFIG_DEVFSD_VERBOSE is not set # CONFIG_FEATURE_DEVFS is not set -CONFIG_DEVMEM=y +# CONFIG_DEVMEM is not set # CONFIG_FBSPLASH is not set # CONFIG_FLASH_ERASEALL is not set # CONFIG_FLASH_LOCK is not set # CONFIG_FLASH_UNLOCK is not set # CONFIG_FLASHCP is not set -CONFIG_HDPARM=y -CONFIG_FEATURE_HDPARM_GET_IDENTITY=y +# CONFIG_HDPARM is not set +# CONFIG_FEATURE_HDPARM_GET_IDENTITY is not set # CONFIG_FEATURE_HDPARM_HDIO_SCAN_HWIF is not set # CONFIG_FEATURE_HDPARM_HDIO_UNREGISTER_HWIF is not set # CONFIG_FEATURE_HDPARM_HDIO_DRIVE_RESET is not set # CONFIG_FEATURE_HDPARM_HDIO_TRISTATE_HWIF is not set # CONFIG_FEATURE_HDPARM_HDIO_GETSET_DMA is not set -CONFIG_HEXEDIT=y -CONFIG_I2CGET=y -CONFIG_I2CSET=y -CONFIG_I2CDUMP=y -CONFIG_I2CDETECT=y -CONFIG_I2CTRANSFER=y +# CONFIG_HEXEDIT is not set +# CONFIG_I2CGET is not set +# CONFIG_I2CSET is not set +# CONFIG_I2CDUMP is not set +# CONFIG_I2CDETECT is not set +# CONFIG_I2CTRANSFER is not set CONFIG_INOTIFYD=y CONFIG_LESS=y CONFIG_FEATURE_LESS_MAXLINES=9999999 CONFIG_FEATURE_LESS_BRACKETS=y CONFIG_FEATURE_LESS_FLAGS=y CONFIG_FEATURE_LESS_TRUNCATE=y -# CONFIG_FEATURE_LESS_MARKS is not set +CONFIG_FEATURE_LESS_MARKS=y CONFIG_FEATURE_LESS_REGEXP=y -# CONFIG_FEATURE_LESS_WINCH is not set -# CONFIG_FEATURE_LESS_ASK_TERMINAL is not set -# CONFIG_FEATURE_LESS_DASHCMD is not set -# CONFIG_FEATURE_LESS_LINENUMS is not set -# CONFIG_FEATURE_LESS_RAW is not set -# CONFIG_FEATURE_LESS_ENV is not set -CONFIG_LSSCSI=y +CONFIG_FEATURE_LESS_WINCH=y +CONFIG_FEATURE_LESS_ASK_TERMINAL=y +CONFIG_FEATURE_LESS_DASHCMD=y +CONFIG_FEATURE_LESS_LINENUMS=y +CONFIG_FEATURE_LESS_RAW=y +CONFIG_FEATURE_LESS_ENV=y +# CONFIG_LSSCSI is not set CONFIG_MAKEDEVS=y # CONFIG_FEATURE_MAKEDEVS_LEAF is not set CONFIG_FEATURE_MAKEDEVS_TABLE=y # CONFIG_MAN is not set -CONFIG_MICROCOM=y +# CONFIG_MICROCOM is not set CONFIG_MIM=y CONFIG_MT=y # CONFIG_NANDWRITE is not set # CONFIG_NANDDUMP is not set -CONFIG_PARTPROBE=y +# CONFIG_PARTPROBE is not set # CONFIG_RAIDAUTORUN is not set # CONFIG_READAHEAD is not set # CONFIG_RFKILL is not set CONFIG_RUNLEVEL=y # CONFIG_RX is not set +CONFIG_SEEDRNG=y CONFIG_SETFATTR=y CONFIG_SETSERIAL=y CONFIG_STRINGS=y CONFIG_TIME=y +CONFIG_TREE=y CONFIG_TS=y # CONFIG_TTYSIZE is not set # CONFIG_UBIATTACH is not set @@ -835,9 +852,10 @@ CONFIG_TS=y # CONFIG_UBIRMVOL is not set # CONFIG_UBIRSVOL is not set # CONFIG_UBIUPDATEVOL is not set -CONFIG_UBIRENAME=y +# CONFIG_UBIRENAME is not set # CONFIG_VOLNAME is not set CONFIG_WATCHDOG=y +# CONFIG_FEATURE_WATCHDOG_OPEN_TWICE is not set # # Networking Utilities @@ -846,6 +864,9 @@ CONFIG_FEATURE_IPV6=y # CONFIG_FEATURE_UNIX_LOCAL is not set CONFIG_FEATURE_PREFER_IPV4_ADDRESS=y # CONFIG_VERBOSE_RESOLUTION_ERRORS is not set +# CONFIG_FEATURE_ETC_NETWORKS is not set +# CONFIG_FEATURE_ETC_SERVICES is not set +CONFIG_FEATURE_HWIB=y # CONFIG_FEATURE_TLS_SHA1 is not set CONFIG_ARP=y CONFIG_ARPING=y @@ -864,6 +885,7 @@ CONFIG_ETHER_WAKE=y CONFIG_HOSTNAME=y CONFIG_DNSDOMAINNAME=y # CONFIG_HTTPD is not set +CONFIG_FEATURE_HTTPD_PORT_DEFAULT=0 # CONFIG_FEATURE_HTTPD_RANGES is not set # CONFIG_FEATURE_HTTPD_SETUID is not set # CONFIG_FEATURE_HTTPD_BASIC_AUTH is not set @@ -956,6 +978,7 @@ CONFIG_FEATURE_TELNET_AUTOLOGIN=y CONFIG_FEATURE_TELNET_WIDTH=y # CONFIG_TELNETD is not set # CONFIG_FEATURE_TELNETD_STANDALONE is not set +CONFIG_FEATURE_TELNETD_PORT_DEFAULT=0 # CONFIG_FEATURE_TELNETD_INETD_WAIT is not set CONFIG_TFTP=y # CONFIG_FEATURE_TFTP_PROGRESS_BAR is not set @@ -976,6 +999,7 @@ CONFIG_VCONFIG=y CONFIG_WGET=y CONFIG_FEATURE_WGET_LONG_OPTIONS=y CONFIG_FEATURE_WGET_STATUSBAR=y +CONFIG_FEATURE_WGET_FTP=y CONFIG_FEATURE_WGET_AUTHENTICATION=y CONFIG_FEATURE_WGET_TIMEOUT=y # CONFIG_FEATURE_WGET_HTTPS is not set @@ -992,6 +1016,7 @@ CONFIG_UDHCPC=y CONFIG_FEATURE_UDHCPC_ARPING=y CONFIG_FEATURE_UDHCPC_SANITIZEOPT=y CONFIG_UDHCPC_DEFAULT_SCRIPT="/usr/share/udhcpc/default.script" +CONFIG_UDHCPC6_DEFAULT_SCRIPT="" # CONFIG_UDHCPC6 is not set # CONFIG_FEATURE_UDHCPC6_RFC3646 is not set # CONFIG_FEATURE_UDHCPC6_RFC4704 is not set @@ -1001,6 +1026,7 @@ CONFIG_UDHCPC_DEFAULT_SCRIPT="/usr/share/udhcpc/default.script" # # Common options for DHCP applets # +CONFIG_UDHCPC_DEFAULT_INTERFACE="eth0" # CONFIG_FEATURE_UDHCP_PORT is not set CONFIG_UDHCP_DEBUG=0 CONFIG_UDHCPC_SLACK_FOR_BUGGY_SERVERS=80 @@ -1018,17 +1044,19 @@ CONFIG_IFUPDOWN_UDHCPC_CMD_OPTIONS="-R -n -O search" # # Mail Utilities # +CONFIG_FEATURE_MIME_CHARSET="" # CONFIG_MAKEMIME is not set # CONFIG_POPMAILDIR is not set # CONFIG_FEATURE_POPMAILDIR_DELIVERY is not set # CONFIG_REFORMIME is not set # CONFIG_FEATURE_REFORMIME_COMPAT is not set # CONFIG_SENDMAIL is not set -CONFIG_FEATURE_MIME_CHARSET="" # # Process Utilities # +# CONFIG_FEATURE_FAST_TOP is not set +# CONFIG_FEATURE_SHOW_THREADS is not set CONFIG_FREE=y CONFIG_FUSER=y # CONFIG_IOSTAT is not set @@ -1067,7 +1095,6 @@ CONFIG_FEATURE_TOP_CPU_GLOBAL_PERCENTS=y CONFIG_UPTIME=y # CONFIG_FEATURE_UPTIME_UTMP_SUPPORT is not set CONFIG_WATCH=y -# CONFIG_FEATURE_SHOW_THREADS is not set # # Runit Utilities @@ -1124,6 +1151,7 @@ CONFIG_ASH_IDLE_TIMEOUT=y CONFIG_ASH_ECHO=y CONFIG_ASH_PRINTF=y CONFIG_ASH_TEST=y +CONFIG_ASH_SLEEP=y CONFIG_ASH_HELP=y CONFIG_ASH_GETOPTS=y CONFIG_ASH_CMDCMD=y @@ -1132,8 +1160,8 @@ CONFIG_ASH_CMDCMD=y # CONFIG_SHELL_HUSH is not set # CONFIG_HUSH_BASH_COMPAT is not set # CONFIG_HUSH_BRACE_EXPANSION is not set -# CONFIG_HUSH_LINENO_VAR is not set # CONFIG_HUSH_BASH_SOURCE_CURDIR is not set +# CONFIG_HUSH_LINENO_VAR is not set # CONFIG_HUSH_INTERACTIVE is not set # CONFIG_HUSH_SAVEHISTORY is not set # CONFIG_HUSH_JOB is not set diff --git a/buildroot-v86/Config.in b/buildroot-v86/Config.in index 3903fe6..91ab7ac 100644 --- a/buildroot-v86/Config.in +++ b/buildroot-v86/Config.in @@ -1 +1,3 @@ source "$BR2_EXTERNAL_v86_PATH/package/nled/Config.in" +source "$BR2_EXTERNAL_v86_PATH/package/execfuse/Config.in" +source "$BR2_EXTERNAL_v86_PATH/package/fusescript/Config.in" diff --git a/buildroot-v86/board/v86/linux.config b/buildroot-v86/board/v86/linux.config index 50ddce8..9ffb1ee 100644 --- a/buildroot-v86/board/v86/linux.config +++ b/buildroot-v86/board/v86/linux.config @@ -261,3 +261,4 @@ CONFIG_SYSTEM_TRUSTED_KEYRING=y # CONFIG_VIRTUALIZATION is not set CONFIG_CRC_CCITT=y CONFIG_CRC16=y +CONFIG_FUSE_FS=m diff --git a/buildroot-v86/board/v86/rootfs_overlay/bin/bash b/buildroot-v86/board/v86/rootfs_overlay/bin/bash new file mode 120000 index 0000000..7fed9d7 --- /dev/null +++ b/buildroot-v86/board/v86/rootfs_overlay/bin/bash @@ -0,0 +1 @@ +sh \ No newline at end of file diff --git a/buildroot-v86/board/v86/rootfs_overlay/bin/httpfs b/buildroot-v86/board/v86/rootfs_overlay/bin/httpfs new file mode 100755 index 0000000..7a72092 Binary files /dev/null and b/buildroot-v86/board/v86/rootfs_overlay/bin/httpfs differ diff --git a/buildroot-v86/board/v86/rootfs_overlay/etc/inittab b/buildroot-v86/board/v86/rootfs_overlay/etc/inittab index d7a40bc..e4a4170 100644 --- a/buildroot-v86/board/v86/rootfs_overlay/etc/inittab +++ b/buildroot-v86/board/v86/rootfs_overlay/etc/inittab @@ -27,7 +27,7 @@ # Put a getty on the serial port #console::respawn:/sbin/getty -L console 0 vt100 # GENERIC_SERIAL -#console::respawn:-/bin/sh # vga console +console::respawn:-/bin/sh # vga console # Auto-login as root on ttyS0 ::respawn:/sbin/getty ttyS0 38400 xterm -n -l /bin/sh diff --git a/buildroot-v86/board/v86/rootfs_overlay/etc/motd b/buildroot-v86/board/v86/rootfs_overlay/etc/motd index 5ecbec9..b71fd96 100644 --- a/buildroot-v86/board/v86/rootfs_overlay/etc/motd +++ b/buildroot-v86/board/v86/rootfs_overlay/etc/motd @@ -1,6 +1,6 @@ Welcome to XRSH -Please hook up your (bluetooth) keyboard to use xrsh. +For an optimal experience, connect a (bluetooth) keyboard. Use Ctrl+a+0/1/2/3/.. to switch terminals. Use Ctrl+a+c to create a new terminal. Type 'save' to save session (survives tab-close) diff --git a/buildroot-v86/board/v86/rootfs_overlay/etc/profile b/buildroot-v86/board/v86/rootfs_overlay/etc/profile index 447045a..b0ce716 100644 --- a/buildroot-v86/board/v86/rootfs_overlay/etc/profile +++ b/buildroot-v86/board/v86/rootfs_overlay/etc/profile @@ -1,6 +1,8 @@ export HOME=/root export PATH=~/bin:$PATH:/etc +set +m # mute job control notifications + # source URL data from v86 (file created by v86 during boot) test -f /mnt/profile.browser && source /mnt/profile.browser @@ -35,7 +37,8 @@ help_tips(){ echo 'run 64bit linux app ' "type 'blink '" echo 'paste text ' "ctrl/cmd+shift+v'" echo 'paste text to file ' "ctrl/cmd+v'" - echo 'import file to scene ' "drag-drop or ctrl/cmd+v'" + echo 'import file to scene ' "drag-drop file or ctrl/cmd+v or type 'upload'" + echo 'import remote URL ' "type 'require '" echo 'reset scene & shell ' "type 'reset'" echo 'js run ' "type 'js 'alert(\"hello\")'" echo 'js console.log: ' "type 'console document.baseURI" @@ -44,6 +47,7 @@ help_tips(){ echo 'js capture console: ' "type 'tail -f /dev/browser/console'" echo 'js<->filesystem hooks:' "type 'see [executable] files in ~/hook.d'" echo 'include file into page' "type 'require '" + echo 'say something ' "type 'say hello'" echo '' echo 'type "help" or "man xrsh" to read the full manual' ONBOARDING=1 @@ -56,18 +60,25 @@ export PS1="\[\e[36m\]> \[\e\[0m\]" alias ls='ls -ha -w100' # interactive login -[[ -t 0 ]] && { - test -n "$STY" || { - resize # call twice - resize # otherwise COLUMNS/ROWS is 0 - # add URL-hash as extra screen session - test -z "$HASH" || { - grep 'screen -t #' /root/.screenrc || { - echo "screen -t xrsh+URL 3 /bin/sh -c '${HASH}; sh'" | sed "s/'#/'/g" >> /root/.screenrc +which screen &> /dev/null && { + [[ -t 0 ]] && { + test -n "$STY" || { + resize # call twice + resize # otherwise COLUMNS/ROWS is 0 + # add URL-hash as extra screen session + test -z "$HASH" || { + grep 'screen -t #' /root/.screenrc || { + echo "screen -t xrsh+URL 3 /bin/sh -c '${HASH}; sh'" | sed "s/'#/'/g" >> /root/.screenrc + } } + screen -Aa -R -T screen-256color -c /root/.screenrc + } + test -n "$STY" && { + test -f /root/motd && cat /root/motd || cat /etc/motd; + say "welcome to eex r, shell. For an optimal user experience, connect a Bluetooth keyboard." & } - screen -Aa -R -T screen-256color -c /root/.screenrc } - test -n "$STY" && cat /etc/motd } +cd $HOME + diff --git a/buildroot-v86/board/v86/rootfs_overlay/etc/profile.xrsh b/buildroot-v86/board/v86/rootfs_overlay/etc/profile.xrsh index 80770c9..c8c9d6a 100644 --- a/buildroot-v86/board/v86/rootfs_overlay/etc/profile.xrsh +++ b/buildroot-v86/board/v86/rootfs_overlay/etc/profile.xrsh @@ -3,7 +3,7 @@ test -d /dev/browser || { move_root_to_9pfs(){ - mkdir /mnt/run # for javascript<->unix pids + test -d /mnt/run || mkdir /mnt/run # for javascript<->unix pids test -d /mnt/root && return 0 mv /root /mnt/. ln -fs /mnt/root /root @@ -55,6 +55,12 @@ test -d /dev/browser || { /mnt/root/bin/watchfs & } + setup_mounts(){ + mkdir /mnt/helloworld + fusescript /root/fs/helloworld /mnt/helloworld + } + + modprobe fuse # load fuse move_root_to_9pfs setup_browser_dev setup_clipboard @@ -63,4 +69,6 @@ test -d /dev/browser || { setup_overlayfs setup_incoming_cmds & setup_listen_homedir + setup_mounts + cat /etc/motd } diff --git a/buildroot-v86/board/v86/rootfs_overlay/root/.config/webspeech/settings.json b/buildroot-v86/board/v86/rootfs_overlay/root/.config/webspeech/settings.json new file mode 100644 index 0000000..b24cefb --- /dev/null +++ b/buildroot-v86/board/v86/rootfs_overlay/root/.config/webspeech/settings.json @@ -0,0 +1,7 @@ +{ + "speak_rate": 0.88, + "speak_pitch": 0.01, + "speak_volume": 1, + "speak_voice": -1, + "speak_voices": 0 +} diff --git a/buildroot-v86/board/v86/rootfs_overlay/root/.inputrc b/buildroot-v86/board/v86/rootfs_overlay/root/.inputrc new file mode 100644 index 0000000..c06847f --- /dev/null +++ b/buildroot-v86/board/v86/rootfs_overlay/root/.inputrc @@ -0,0 +1,23 @@ +# ctrl-arrows jump between words / begin/end line +"\e[1;5D": shell-backward-word # seperate by spaces instead of: backward-word, forward-word +"\e[1;5C": shell-forward-word # + +# start bash recursive search from cursor using up/down keys +# prevents pressing up-key zillion times in bash/mysql/etc +"\e[A": history-search-backward +"\e[B": history-search-forward +"\C-R": history-search-forward + + +# always show completion +#set completion-query-items 1000 +TAB:menu-complete + +"\C-d":kill-word + +# completion improv +# for readability: put / after directory names and * after programs: +set visible-stats on + +"\e[1;5B": beginning-of-line +"\e[1;5A": end-of-line diff --git a/buildroot-v86/board/v86/rootfs_overlay/root/.profile b/buildroot-v86/board/v86/rootfs_overlay/root/.profile index e76e75f..8d06047 100644 --- a/buildroot-v86/board/v86/rootfs_overlay/root/.profile +++ b/buildroot-v86/board/v86/rootfs_overlay/root/.profile @@ -3,11 +3,29 @@ alias vi='echo -e "HINT: type :q (and press enter) to quit\n[press a key]";read; alias mg='echo -e "HINT: type Ctrl+c Ctrl-x to quit\n[press a key]";read;mg'; alias nano='echo -e "HINT: type Ctrl+x to quit\n[press a key]" ;read;nano'; -# start index.js -test -f index.js && chmod +x index.js && ./index.js | grep -v undefined +# save space in the iso image +alias curl="wget -q -O - " +alias make="mim" -# start index.html -test -f index.html && chmod +x index.html && ./index.html +# minimalist autoenv version [https://github.com/hyperupcall/autoenv] +#cd(){ +# test -f $(pwd)/.env.leave && source $(pwd)/.env.leave # cleanup environment +# command cd ${1:+"$@"} && { +# hook cd "$@" +# test -f $(pwd)/.env && source $(pwd)/.env # setup environment / run scripts e.g. +# ls # list directories +# } +#} + +# start index.js if /dev/browser/js filesize is not great than 0 +if [ ! -s /dev/browser/js ]; then + chmod +x /mnt/root/index.js && /mnt/root/index.js | grep -v undefined +fi + +# start index.html if /dev/browser/html filesize is not great than 0 +if [ ! -s /dev/browser/html ]; then + chmod +x /mnt/root/index.html && /mnt/root/index.html +fi # uncomment to set location (3D scene file [with XR Fragments] ) #require https://xrfragment.org/index.glb diff --git a/buildroot-v86/board/v86/rootfs_overlay/root/.screenrc b/buildroot-v86/board/v86/rootfs_overlay/root/.screenrc index 19a59bd..31bfa35 100644 --- a/buildroot-v86/board/v86/rootfs_overlay/root/.screenrc +++ b/buildroot-v86/board/v86/rootfs_overlay/root/.screenrc @@ -3,6 +3,7 @@ caption always "%{= kw}%-w%{= BW} %t %{-}%+w %-= $HOSTNAME" # skip the startup message startup_message off +msgwait 0 # go to home dir chdir /root @@ -14,7 +15,7 @@ autodetach on defscrollback 10000 # start with visual bell as default -vbell on +vbell off vbell_msg "bell on %t (%n)" diff --git a/buildroot-v86/board/v86/rootfs_overlay/root/bin/blink b/buildroot-v86/board/v86/rootfs_overlay/root/bin/blink deleted file mode 100755 index 510d21f..0000000 Binary files a/buildroot-v86/board/v86/rootfs_overlay/root/bin/blink and /dev/null differ diff --git a/buildroot-v86/board/v86/rootfs_overlay/root/bin/git b/buildroot-v86/board/v86/rootfs_overlay/root/bin/git new file mode 100755 index 0000000..3aa498b --- /dev/null +++ b/buildroot-v86/board/v86/rootfs_overlay/root/bin/git @@ -0,0 +1,27 @@ +#!/bin/sh +set -e +echo '[i] this is a git-stub' + +usage(){ + echo 'usage: git clone '; + exit 0; +} + +test -n "$1" || usage + +clone(){ + test -n "$2" || usage + name=$(basename $1) + branch=$2 + dir="$name/$branch" + + test -n "$DEBUG" && set -x + wget $1/archive/$branch.zip -O "${name}.zip" + unzip ${name}.zip + rm ${name}.zip + set +x + echo "" + echo "[i] unzipped repository to directory '$name'" +} + +"$@" diff --git a/buildroot-v86/board/v86/rootfs_overlay/root/bin/hello b/buildroot-v86/board/v86/rootfs_overlay/root/bin/hello new file mode 100755 index 0000000..f48b675 Binary files /dev/null and b/buildroot-v86/board/v86/rootfs_overlay/root/bin/hello differ diff --git a/buildroot-v86/board/v86/rootfs_overlay/root/bin/say b/buildroot-v86/board/v86/rootfs_overlay/root/bin/say new file mode 100755 index 0000000..9212b5e --- /dev/null +++ b/buildroot-v86/board/v86/rootfs_overlay/root/bin/say @@ -0,0 +1,57 @@ +#!/bin/js + +window.speak = async function(str, opts){ + + if( !str) return + let defaultOpts + let term = document.querySelector("[isoterminal]").components.isoterminal.term + + try{ + let buf = await term.worker.read_file("root/.config/webspeech/settings.json") + let jsonstr = new TextDecoder().decode(buf); + defaultOpts = JSON.parse(jsonstr) + }catch(e){ + defaultOpts = { + speaksigns:true, + override:true, + speak_rate: 1, + speak_pitch: 0.01, + speak_volume: 1, + speak_voice: -1, + speak_voices: 0 + } + } + opts = { ...defaultOpts, ...opts, speak: {} } + + if( opts.speaksigns ){ + str = str.replace(/\/\//,' ') + .replace(/:/,'') + .replace(/\//,' slash ') + .replace(/\./,' dot ') + .replace(/#/,' hash ') + .replace(/&/,' and ') + .replace(/=/,' is ') + } + if( str == opts.speak.lastStr ) return // no duplicates + opts.speak.lastStr = str + let speech = window.speechSynthesis + let utterance = new SpeechSynthesisUtterance( str ) + opts.speak_voices = speech.getVoices().length + if( opts.speak_voice != -1 && opts.speak_voice < opts.speak_voices ){ + utterance.voice = speech.getVoices()[ opts.speak_voice ]; + }else{ + let voices = speech.getVoices() + for(let i = 0; i < voices.length; i++ ){ + if( voices[i].lang == navigator.lang ) opts.speak_voice = i; + } + } + utterance.rate = opts.speak_rate + utterance.pitch = opts.speak_pitch + utterance.volume = opts.speak_volume + if( opts.override ) speech.cancel() + speech.speak(utterance) +} + +window.speak( args.slice(1).join(' ') ) + +return "" diff --git a/buildroot-v86/board/v86/rootfs_overlay/root/bin/tab b/buildroot-v86/board/v86/rootfs_overlay/root/bin/tab new file mode 100755 index 0000000..9a6af32 --- /dev/null +++ b/buildroot-v86/board/v86/rootfs_overlay/root/bin/tab @@ -0,0 +1,10 @@ +#!/bin/sh +test -n "$1" || { + echo "usage: tab " + echo -e "\ntabs: " + find /tmp/*.dtach | xargs -n1 basename | sed 's/\.dtach//g' + exit 0; +} + +# attach to a sessions [or create if it does not exist] +dtach -r ctrl_l -A /tmp/$1.dtach diff --git a/buildroot-v86/board/v86/rootfs_overlay/root/fs/helloworld b/buildroot-v86/board/v86/rootfs_overlay/root/fs/helloworld new file mode 100755 index 0000000..620c986 --- /dev/null +++ b/buildroot-v86/board/v86/rootfs_overlay/root/fs/helloworld @@ -0,0 +1,36 @@ +#!/bin/sh +LOG=~/.fuse.log + +file1="hello world" + +echo "$0 $*" >> $LOG +{ + case "$1" in + readdir) + echo "file1" + ;; + getattr) + # format: mode nlink uid gid size block + case "$2" in + /) + echo "16877 2 1000 1000 4096 8" && exit 0 # dir (mode 0755) + ;; + /file1) + echo "33188 1 1000 1000 ${#file1} 1" && exit 0 # file (mode 0644, size 6) + ;; + esac + exit 1 + ;; + read) + case "$2" in + /file1) + echo "$file1" + ;; + esac + ;; + *) + exit 1 + ;; + esac + +} | tee -a $LOG diff --git a/buildroot-v86/board/v86/rootfs_overlay/root/fs/helloworld.awk b/buildroot-v86/board/v86/rootfs_overlay/root/fs/helloworld.awk new file mode 100755 index 0000000..152cab3 --- /dev/null +++ b/buildroot-v86/board/v86/rootfs_overlay/root/fs/helloworld.awk @@ -0,0 +1,27 @@ +#!/usr/bin/awk -f + +BEGIN { + LOG = ENVIRON["HOME"] "/.fuse.log" + file1 = "hello world" + log_command() + + if (ARGC < 2) exit 1 + cmd = ARGV[1]; path = (ARGC > 2 ? ARGV[2] : "") + + if (cmd == "readdir") print "file1" + else if (cmd == "getattr") print_getattr(path) + else if (cmd == "read" && path == "/file1") print file1 + else exit 1 +} + +function log_command() { + for (i = 0; i < ARGC; i++) printf "%s ", ARGV[i] >> LOG + print "" >> LOG; close(LOG) +} + +function print_getattr(p) { + if (p == "/") print "16877 2 1000 1000 4096 8" + else if (p == "/file1") print "33188 1 1000 1000 " length(file1) " 1" + else exit 1 +} + diff --git a/buildroot-v86/board/v86/rootfs_overlay/root/fs/helloworld.js b/buildroot-v86/board/v86/rootfs_overlay/root/fs/helloworld.js new file mode 100755 index 0000000..216eba5 --- /dev/null +++ b/buildroot-v86/board/v86/rootfs_overlay/root/fs/helloworld.js @@ -0,0 +1,39 @@ +#!/bin/js + +// Set log file to the home directory +const file1 = "hello world"; + +// Function to handle getattr command +function getattr(path) { + if (path === '/') { + return "16877 2 1000 1000 4096 8" + } else if (path === '/file1') { + return `33188 1 1000 1000 ${file1.length} 1` + } else { + return '' + } +} + +// Main function to handle commands +function main(args) { + console.log(args.join(' ')); + + switch (args[0]) { + case 'readdir': + return 'file1' + break; + case 'getattr': + return getattr(args[1]); + break; + case 'read': + if (args[1] === '/file1') { + return file1; + } + break; + default: + return '' + } +} + +// Run the main function +return main(args.slice(1)); diff --git a/buildroot-v86/board/v86/rootfs_overlay/root/fs/helloworld.lua b/buildroot-v86/board/v86/rootfs_overlay/root/fs/helloworld.lua new file mode 100755 index 0000000..4f229a7 --- /dev/null +++ b/buildroot-v86/board/v86/rootfs_overlay/root/fs/helloworld.lua @@ -0,0 +1,23 @@ +#!/usr/bin/env lua + +LOG_PATH, file1 = os.getenv("HOME") .. "/.fuse.log", "hello world" + +local function log_command(args) + io.open(LOG_PATH, "a"):write(table.concat(args, " ") .. "\n"):close() +end + +local function getattr(path) + if path == "/" then print("16877 2 1000 1000 4096 8") + elseif path == "/file1" then print(string.format("33188 1 1000 1000 %d 1", #file1)) + else os.exit(1) end +end + +local function main(args) + log_command(args) + if args[1] == "readdir" then print("file1") + elseif args[1] == "getattr" then getattr(args[2]) + elseif args[1] == "read" and args[2] == "/file1" then print(file1) + else os.exit(1) end +end + +main({...}) diff --git a/buildroot-v86/board/v86/rootfs_overlay/root/fs/helloworld.py b/buildroot-v86/board/v86/rootfs_overlay/root/fs/helloworld.py new file mode 100755 index 0000000..884da83 --- /dev/null +++ b/buildroot-v86/board/v86/rootfs_overlay/root/fs/helloworld.py @@ -0,0 +1,17 @@ +#!/usr/bin/env micropython + +LOG_PATH = os.path.join(os.getenv("HOME", "/tmp"), ".fuse.log") +file1 = "hello world" + +def log(msg): open(LOG_PATH, "a").write(msg + "\n") +def getattr(path): return {"16877 2 1000 1000 4096 8" if path == "/" else f"33188 1 1000 1000 {len(file1)} 1"} if path in ["/", "/file1"] else None +def readdir(): print("file1") +def read(path): print(file1) if path == "/file1" else sys.exit(1) + +log(" ".join(sys.argv)) +if len(sys.argv) < 2: sys.exit(1) +cmd, path = sys.argv[1], sys.argv[2] if len(sys.argv) > 2 else None +if cmd == "readdir": readdir() +elif cmd == "getattr": print(getattr(path)) if getattr(path) else sys.exit(1) +elif cmd == "read": read(path) +else: sys.exit(1) diff --git a/buildroot-v86/board/v86/rootfs_overlay/root/fs/tags b/buildroot-v86/board/v86/rootfs_overlay/root/fs/tags new file mode 120000 index 0000000..1107866 --- /dev/null +++ b/buildroot-v86/board/v86/rootfs_overlay/root/fs/tags @@ -0,0 +1 @@ +/home/leon/.ctags.js \ No newline at end of file diff --git a/buildroot-v86/board/v86/rootfs_overlay/root/hook.d/mimetype/application/zip/extract_and_run.sh b/buildroot-v86/board/v86/rootfs_overlay/root/hook.d/mimetype/application/zip/extract_and_run.sh index aa17947..8d8fb5c 100755 --- a/buildroot-v86/board/v86/rootfs_overlay/root/hook.d/mimetype/application/zip/extract_and_run.sh +++ b/buildroot-v86/board/v86/rootfs_overlay/root/hook.d/mimetype/application/zip/extract_and_run.sh @@ -9,8 +9,8 @@ mkdir -p "/root/$dir" cd "/root/$dir" unzip "$file" -test -x app && { - logger "$0: detected $(pwd)/app executable" - logger "$0: running $(pwd)/app" - ./app +test -x .env && { + logger "$0: detected $(pwd)/.env [autoenv]" + logger "$0: running $(pwd)/.env" + source .env } diff --git a/buildroot-v86/board/v86/rootfs_overlay/root/manual.md b/buildroot-v86/board/v86/rootfs_overlay/root/manual.md index 2ce8b6a..d72ac4c 100644 --- a/buildroot-v86/board/v86/rootfs_overlay/root/manual.md +++ b/buildroot-v86/board/v86/rootfs_overlay/root/manual.md @@ -108,7 +108,8 @@ It can be loaded in various ways into [your own instance of] https://xrsh.isvery * drag-drop the zip from a filemanager to your XRSH-tab * download the zip, and type 'upload' in XRSH to import it -Currently, 'app' links to 'bin/app.sh', but there are also other scriptinglanguages it could link to as well (see bin-folder). +The package follows the popular `autoenv`-paradigm (a file called `.env` is automatically executed). +Currently, '.env' links to 'bin/app.sh', but there are also other scriptinglanguages it could link to as well (see bin-folder). > see example package at https://xrsh.isvery.ninja/package.zip @@ -156,6 +157,19 @@ But you can do it manually too: These are various ways to enable hybrid eventing between browser and terminal (languages). +## Wget: downloading remote files + +Internetrequests are limited to port 80 and 443 (as they are routed via javascript's fetch()): + +``` +$ wget xrsh.isvery.ninja # fetches https://xrsh.isvery.ninja (*) +$ wget http://xrsh.isvery.ninja # fetches http://xrsh.isvery.ninja +$ wget https://xrsh.isvery.ninja # does not work (*) +``` + +> \* = important for selfhosters: the browser cannot switch ports, therefore hostnames are considered HTTPS when the XRSH webapp is +hosted via **valid** HTTPS certificates. Serving the XRSH webapp via http will render https impossible. XRSH.com (the standalone binary) also launches +https://127.0.0.1 (instead of https://localhost) because the latter cripples the network-features. ## webrequests to the filesystem @@ -165,6 +179,9 @@ Javascript webrequests can read from the filesystem via the 'file://host/path' p fetch("file://xrsh/mnt/profile.browser") ``` + + NOTE: it's `file://xrsh` not `file:///` (basically it includes host `xrsh`) to prevent the browser security-model from blocking the request (and select the xrsh filesystem, not the native filesystem). + current [security] limitations: * only /mnt directory is exposed @@ -197,7 +214,8 @@ term.exec("ls -la") await term.worker.create_file("hello.txt", term.convert.toUint8Array("hello") ) await term.worker.update_file("hello.txt", term.convert.toUint8Array("hi") ) await term.worker.append_file("hello.txt", term.convert.toUint8Array("world") ) -const f = await term.worker.read_file("hello.txt") +const buf = await term.worker.read_file("hello.txt") +const str = new TextDecoder().decode(buf); ``` # Calling javascript from terminal diff --git a/buildroot-v86/configs/v86_defconfig b/buildroot-v86/configs/v86_defconfig index 57765e1..fecfdc7 100644 --- a/buildroot-v86/configs/v86_defconfig +++ b/buildroot-v86/configs/v86_defconfig @@ -1,7 +1,10 @@ BR2_x86_pentiumpro=y -BR2_CCACHE=y +BR2_TOOLCHAIN_BUILDROOT_MUSL=y BR2_PACKAGE_HOST_LINUX_HEADERS_CUSTOM_4_19=y -BR2_TOOLCHAIN_BUILDROOT_WCHAR=y +BR2_CCACHE=y +BR2_ENABLE_DEBUG=y +BR2_ENABLE_RUNTIME_DEBUG=y +BR2_OPTIMIZE_S=y # BR2_TARGET_GENERIC_GETTY is not set BR2_ROOTFS_OVERLAY="$(BR2_EXTERNAL_v86_PATH)/board/v86/rootfs_overlay/" BR2_ROOTFS_POST_IMAGE_SCRIPT="$(BR2_EXTERNAL_v86_PATH)/board/v86/post-image.sh" @@ -13,9 +16,12 @@ BR2_LINUX_KERNEL_CUSTOM_CONFIG_FILE="$(BR2_EXTERNAL_v86_PATH)/board/v86/linux.co BR2_PACKAGE_ZIP=y BR2_PACKAGE_LUA=y BR2_PACKAGE_MICROPYTHON=y +BR2_PACKAGE_ZLIB=y +BR2_PACKAGE_LIBFUSE3=y +BR2_PACKAGE_GMP=y BR2_PACKAGE_SCREEN=y BR2_PACKAGE_MG=y -BR2_PACKAGE_NANO=y BR2_TARGET_ROOTFS_INITRAMFS=y BR2_TARGET_ROOTFS_ISO9660=y BR2_TARGET_SYSLINUX=y +BR2_PACKAGE_FUSESCRIPT=y diff --git a/buildroot-v86/configs/v86_defconfig.bak b/buildroot-v86/configs/v86_defconfig.bak new file mode 100644 index 0000000..a766d25 --- /dev/null +++ b/buildroot-v86/configs/v86_defconfig.bak @@ -0,0 +1,27 @@ +BR2_x86_pentiumpro=y +BR2_PACKAGE_HOST_LINUX_HEADERS_CUSTOM_4_19=y +BR2_CCACHE=y +# BR2_TARGET_GENERIC_GETTY is not set +BR2_ROOTFS_OVERLAY="$(BR2_EXTERNAL_v86_PATH)/board/v86/rootfs_overlay/" +BR2_ROOTFS_POST_IMAGE_SCRIPT="$(BR2_EXTERNAL_v86_PATH)/board/v86/post-image.sh" +BR2_LINUX_KERNEL=y +BR2_LINUX_KERNEL_CUSTOM_VERSION=y +BR2_LINUX_KERNEL_CUSTOM_VERSION_VALUE="4.19.172" +BR2_LINUX_KERNEL_USE_CUSTOM_CONFIG=y +BR2_LINUX_KERNEL_CUSTOM_CONFIG_FILE="$(BR2_EXTERNAL_v86_PATH)/board/v86/linux.config" +BR2_PACKAGE_ZIP=y +BR2_PACKAGE_LUA=y +BR2_PACKAGE_MICROPYTHON=y +BR2_PACKAGE_SCREEN=y +BR2_PACKAGE_MG=y +BR2_PACKAGE_NANO=y +BR2_TARGET_ROOTFS_INITRAMFS=y +BR2_TARGET_ROOTFS_ISO9660=y +BR2_TARGET_SYSLINUX=y + + +# enable debugger by uncommenting the packages below +BR2_PACKAGE_HOST_GDB=y +BR2_PACKAGE_GDB=y +BR2_PACKAGE_GDB_SERVER=y +BR2_TOOLCHAIN_EXTERNAL_GDB_SERVER_COPY=y diff --git a/buildroot-v86/package/fusescript/.gitignore b/buildroot-v86/package/fusescript/.gitignore new file mode 100644 index 0000000..9964548 --- /dev/null +++ b/buildroot-v86/package/fusescript/.gitignore @@ -0,0 +1,6 @@ +*.o +*_test +execfuse +m +umltest.inner.sh +umltest.status diff --git a/buildroot-v86/package/fusescript/Config.in b/buildroot-v86/package/fusescript/Config.in new file mode 100644 index 0000000..cee4da6 --- /dev/null +++ b/buildroot-v86/package/fusescript/Config.in @@ -0,0 +1,4 @@ +config BR2_PACKAGE_FUSESCRIPT + bool "fusescript" + help + fusescript allows scriptable fuse filesystems [via awk/shellscript/micropython/lua e.g]. diff --git a/buildroot-v86/package/fusescript/LICENSE b/buildroot-v86/package/fusescript/LICENSE new file mode 100644 index 0000000..d86dd09 --- /dev/null +++ b/buildroot-v86/package/fusescript/LICENSE @@ -0,0 +1 @@ +Something like MIT or 2-clause BSD... diff --git a/buildroot-v86/package/fusescript/Makefile b/buildroot-v86/package/fusescript/Makefile new file mode 100644 index 0000000..ec2c1d1 --- /dev/null +++ b/buildroot-v86/package/fusescript/Makefile @@ -0,0 +1,10 @@ +all: fusescript + +CFLAGS=-O2 +#CFLAGS=-g3 -O0 -Wall + +FILES=fusescript.c + +fusescript: ${FILES} + ${CC} ${LDFLAGS} ${CFLAGS} ${FILES} $(shell pkg-config fuse3 --cflags --libs) -o fusescript + diff --git a/buildroot-v86/package/fusescript/README.md b/buildroot-v86/package/fusescript/README.md new file mode 100644 index 0000000..b91a53d --- /dev/null +++ b/buildroot-v86/package/fusescript/README.md @@ -0,0 +1,54 @@ +# Fusescript + +**scriptable** filesystems via python,lua,awk,shellscript etc. + +
+ +> Hackable & prototypable [FUSE3](https://github.com/libfuse/libfuse) script-bindings for your favorite programming language! + +## Usage + +```bash +$ mkdir /mnt/helloworld +$ fusescript fs/helloworld /mnt/helloworld +$ ls /mnt/helloworld +file1 +$ cat /mnt/helloworld/file1 +Hello world +``` + +Profit! The `helloworld`-script serves a 'virtual filesystem' on the fly ❤ + +> To see how this works, see the [fs/helloworld](fs/helloworld) shellscript, or the [python](fs/helloworld.py), [nodejs](fs/helloworld.js), [AWK](fs/helloworld.awk) script. + +## Compile + +* nix: `nix-build fusescript.nix` +* debian: `sudo apt-get install libfuse3-dev && make` +* buildroot [embedded]: put this repository in the `packages` directory and select it in 'External' (last menuitem when running `make menuconfig`) + +# Rationale + +* App-development usually adds various layers of tree-structures +* the filesystem is **already** a tree-structure + +**Fusescript** is an exploration into mapping things to a filesystem **asap**.
+In such case, we'd want to prototype with 1% FUSE-functionality, without being forced into building/debugging C (on an embedded device).
+ +> "Files are awesome, everything depends on it" + +#### Crazy ideas: + +* navigate database via the filesystem +* navigate REST api via the filesystem +* navigate fileformat-internals via the filesystem +* you name it! + +## Inspired by + +* https://github.com/vi/execfuse +* https://github.com/zevweiss/booze + +## Projects using fusescript + +* [XRSH](https://xrsh.isvery.ninja) diff --git a/buildroot-v86/package/fusescript/fusescript.c b/buildroot-v86/package/fusescript/fusescript.c new file mode 100644 index 0000000..efce91a --- /dev/null +++ b/buildroot-v86/package/fusescript/fusescript.c @@ -0,0 +1,218 @@ +#define FUSE_USE_VERSION 35 + +#include +#include +#include +#include +#include +#include + +static const char *script_path; + +static int call_script(const char *action, const char *path, char **output) { + char command[4096]; + FILE *fp; + + snprintf(command, sizeof(command), "%s %s '%s'", script_path, action, path); + fp = popen(command, "r"); + if (!fp) { + return -errno; + } + + size_t size = 0; + *output = NULL; + char line[1024]; + while (fgets(line, sizeof(line), fp)) { + size_t len = strlen(line); + *output = realloc(*output, size + len + 1); + memcpy(*output + size, line, len); + size += len; + } + + if (*output) { + (*output)[size] = '\0'; + } + + return pclose(fp); +} + +static int fusescript_getattr(const char *path, struct stat *stbuf, struct fuse_file_info *fi) { + (void) fi; + memset(stbuf, 0, sizeof(struct stat)); + + if( path == NULL ) return -EINVAL; + + if (strcmp(path, "/") == 0) { + // Root directory + stbuf->st_mode = S_IFDIR | 0755; // Directory with permissions 0755 + stbuf->st_nlink = 2; // Standard for directories + return 0; + } + + // Call the script for other paths + char *output = NULL; + int ret = call_script("getattr", path, &output); + if (ret != 0) { + free(output); + return -EIO; + } + + // Parse the script output + int mode, nlink, uid, gid, size, blocks; + if( output == NULL ) return -EIO; + if (sscanf(output, "%d %d %d %d %d %d", &mode, &nlink, &uid, &gid, &size, &blocks) != 6) { + free(output); + return -EIO; // Error parsing the output + } + + // Populate the stat structure + stbuf->st_mode = mode; // File mode (e.g., regular file, directory) + stbuf->st_nlink = nlink; // Number of hard links + stbuf->st_uid = uid; // User ID (owner) + stbuf->st_gid = gid; // Group ID + stbuf->st_size = size; // File size in bytes + stbuf->st_blocks = blocks; // Number of 512-byte blocks allocated + stbuf->st_blksize = 512; // Block size (optional) + + // Set default values for atime, mtime, ctime (if not handled by your script) + stbuf->st_atime = time(NULL); // Last access time + stbuf->st_mtime = time(NULL); // Last modification time + stbuf->st_ctime = time(NULL); // Last status change time + + free(output); + return 0; +} + +static int fusescript_access(const char *path, int mask) { + // Call the script for permission checks (optional) + char *output = NULL; + int ret = call_script("access", path, &output); + if (ret != 0) { + free(output); + return 0; //-EIO; + } + + free(output); + return 0; // Allow all accesses for simplicity +} + + +static int fusescript_readdir(const char *path, void *buf, fuse_fill_dir_t filler, + off_t offset, struct fuse_file_info *fi, enum fuse_readdir_flags flags) { + (void) offset; + (void) fi; + (void) flags; + + if (strcmp(path, "/") != 0) { + // If the path is not the root, return an error + return -ENOENT; + } + + // Add default entries for directories + filler(buf, ".", NULL, 0, 0); // Current directory + filler(buf, "..", NULL, 0, 0); // Parent directory + + // Call the script for additional entries + char *output = NULL; + int ret = call_script("readdir", path, &output); + if (ret != 0) { + free(output); + return -EIO; + } + + // Parse script output, assuming it provides one entry per line + char *line = strtok(output, "\n"); + while (line) { + filler(buf, line, NULL, 0, 0); + line = strtok(NULL, "\n"); + } + + free(output); + return 0; +} + + +static int fusescript_open(const char *path, struct fuse_file_info *fi) { + (void) fi; + char *output = NULL; + int ret = call_script("open", path, &output); + free(output); + return ret == 0 ? 0 : -EIO; +} + +static int fusescript_read(const char *path, char *buf, size_t size, off_t offset, struct fuse_file_info *fi) { + (void) fi; + char command[4096]; + snprintf(command, sizeof(command), "%s read '%s' %zu %jd", script_path, path, size, (intmax_t)offset); + + FILE *fp = popen(command, "r"); + if (!fp) { + return -errno; + } + + size_t bytes_read = fread(buf, 1, size, fp); + int ret = pclose(fp); + return ret == 0 ? bytes_read : -EIO; +} + +static int fusescript_write(const char *path, const char *buf, size_t size, off_t offset, struct fuse_file_info *fi) { + (void) fi; + char command[4096]; + snprintf(command, sizeof(command), "echo -n '%.*s' | %s write '%s' %zu %jd", (int)size, buf, script_path, path, size, (intmax_t)offset); + + int ret = system(command); + return ret == 0 ? size : -EIO; +} + +static int fusescript_mkdir(const char *path, mode_t mode) { + char command[4096]; + snprintf(command, sizeof(command), "%s mkdir '%s' %o", script_path, path, mode); + + int ret = system(command); + return ret == 0 ? 0 : -EIO; +} + +static int fusescript_unlink(const char *path) { + char command[4096]; + snprintf(command, sizeof(command), "%s unlink '%s'", script_path, path); + + int ret = system(command); + return ret == 0 ? 0 : -EIO; +} + +static int fusescript_rmdir(const char *path) { + char command[4096]; + snprintf(command, sizeof(command), "%s rmdir '%s'", script_path, path); + + int ret = system(command); + return ret == 0 ? 0 : -EIO; +} + +static const struct fuse_operations fusescript_ops = { + .getattr = fusescript_getattr, + .readdir = fusescript_readdir, + .open = fusescript_open, + .access = fusescript_access, + .read = fusescript_read, + .write = fusescript_write, + .mkdir = fusescript_mkdir, + .unlink = fusescript_unlink, + .rmdir = fusescript_rmdir, +}; + +int main(int argc, char *argv[]) { + if (argc < 3) { + fprintf(stderr, "Usage: %s