Merge branch 'master' into next/arvo

This commit is contained in:
fang 2022-06-15 18:29:14 +02:00
commit 094396a26a
No known key found for this signature in database
GPG Key ID: EB035760C1BBA972
50 changed files with 25954 additions and 39245 deletions

View File

@ -1,40 +1,10 @@
# Notes:
#
# jobs.<job_id>
#
# A seperate job id results in a lot of duplication of nix/cachix work.
# The build will have to download any substituted derivations from cachix
# for the steps with each distinct job id and upload built derivations to
# cachix after each job has completed, either succesfully or on failure.
#
# jobs.<job_id>.steps.run
#
# build + test are distinct as each step entry results in a collapsable title
# within the log output, which makes it easier to view failing builds or
# tests independently.
#
# jobs.<job_id>.strategy.fail-fast
#
# Set to false so developers working on vere or king-haskell can have their
# respective builds proceed without the other causing failure.
#
# shell.nix
#
# mkShell doesn't allow you to build it - so instantiate all the subshells
# defined for the individual pkg/*/shell.nix as a sanity check and to create
# some artefacts suitable for developers to pull from cachix. The top-level
# shell.nix build time is dominated by Haskell dependencies so it's built as
# part of the haskell build steps.
#
# Syntax:
#
# https://docs.github.com/en/free-pro-team@latest/actions/reference/workflow-syntax-for-github-actions
name: build name: build
on: on:
push: push:
paths: paths:
- '.github/workflows/build.yml'
- '.github/workflows/vere.yml'
- 'pkg/arvo/**' - 'pkg/arvo/**'
- 'pkg/docker-image/**' - 'pkg/docker-image/**'
- 'pkg/ent/**' - 'pkg/ent/**'
@ -44,9 +14,11 @@ on:
- 'pkg/urcrypt/**' - 'pkg/urcrypt/**'
- 'bin/**' - 'bin/**'
- 'nix/**' - 'nix/**'
- default.nix - 'default.nix'
pull_request: pull_request:
paths: paths:
- '.github/workflows/build.yml'
- '.github/workflows/vere.yml'
- 'pkg/arvo/**' - 'pkg/arvo/**'
- 'pkg/docker-image/**' - 'pkg/docker-image/**'
- 'pkg/ent/**' - 'pkg/ent/**'
@ -56,64 +28,15 @@ on:
- 'pkg/urcrypt/**' - 'pkg/urcrypt/**'
- 'bin/**' - 'bin/**'
- 'nix/**' - 'nix/**'
- default.nix - 'default.nix'
jobs: jobs:
urbit: call-vere:
strategy: uses: ./.github/workflows/vere.yml
fail-fast: false with:
matrix: pace: 'often'
include: upload: >-
- { os: ubuntu-latest } ${{
- { os: macos-latest } (github.ref_name == 'next/vere' && github.ref_type == 'branch')
}}
runs-on: ${{ matrix.os }} secrets: inherit
steps:
- uses: actions/checkout@v2
# We only want the extra nix config on linux, where it is necessary
# for the docker build. We don't want in on Mac, where it isn't but
# it breaks the nix install. The two `if` clauses should be mutually
# exclusive
- uses: cachix/install-nix-action@v16
with:
extra_nix_config: |
system-features = nixos-test benchmark big-parallel kvm
if: ${{ matrix.os == 'ubuntu-latest' }}
- uses: cachix/install-nix-action@v16
if: ${{ matrix.os != 'ubuntu-latest' }}
- uses: cachix/cachix-action@v10
with:
name: ares
authToken: ${{ secrets.CACHIX_AUTH_TOKEN }}
- run: nix-build -A urbit --arg enableStatic true
- if: ${{ matrix.os == 'ubuntu-latest' }}
run: nix-build -A urbit-tests
- if: ${{ matrix.os == 'ubuntu-latest' }}
run: nix-build -A docker-image
mingw:
runs-on: windows-latest
defaults:
run:
shell: C:\msys64\msys2_shell.cmd -mingw64 -defterm -no-start -here -c ". <(cygpath '{0}')"
working-directory: ./pkg/urbit
steps:
- uses: actions/checkout@v2
with:
lfs: true
# echo suppresses pacman prompt
- run: echo|./configure
env:
CACHIX_CACHE: ares
CACHIX_AUTH_TOKEN: ${{ secrets.CACHIX_AUTH_TOKEN }}
- run: mingw32-make build/urbit
- run: build/urbit -l -d -B ../../bin/solid.pill -F bus && curl -f --data '{"source":{"dojo":"+hood/exit"},"sink":{"app":"hood"}}' http://localhost:12321

14
.github/workflows/pre-release.yml vendored Normal file
View File

@ -0,0 +1,14 @@
name: pre-release
on:
release: null
push:
tags: 'urbit-v[0-9]+.[0-9]+-rc[0-9]+'
jobs:
call-vere:
uses: ./.github/workflows/vere.yml
with:
pace: 'soon'
upload: true
secrets: inherit

View File

@ -3,42 +3,12 @@ name: release
on: on:
release: null release: null
push: push:
tags: ['*'] tags: 'urbit-v[0-9]+.[0-9]+'
jobs: jobs:
upload: call-vere:
strategy: uses: ./.github/workflows/vere.yml
matrix: with:
include: pace: 'live'
- { os: ubuntu-latest, system: x86_64-linux } upload: true
- { os: macos-latest, system: x86_64-darwin } secrets: inherit
runs-on: ${{ matrix.os }}
steps:
- uses: actions/checkout@v2
- uses: cachix/install-nix-action@v16
- uses: cachix/cachix-action@v10
with:
name: ${{ secrets.CACHIX_NAME }}
authToken: ${{ secrets.CACHIX_AUTH_TOKEN }}
- uses: google-github-actions/setup-gcloud@v0.2.0
with:
version: '290.0.1'
service_account_key: ${{ secrets.GCS_SERVICE_ACCOUNT_KEY }}
project_id: ${{ secrets.GCS_PROJECT }}
export_default_credentials: true
- run: nix-build -A tarball --arg enableStatic true
- name: Run upload to bootstrap.urbit.org
run: |
version="$(cat ./pkg/urbit/version)"
system="$(nix-instantiate --eval --expr 'builtins.currentSystem')"
system=${system:1:${#system}-2}
target="gs://bootstrap.urbit.org/ci/urbit-v${version}-${system}-${GITHUB_SHA:0:9}.tgz"
gsutil cp -n ./result "$target"
echo "upload to $target complete."

44
.github/workflows/tarballs.yml vendored Normal file
View File

@ -0,0 +1,44 @@
name: tarballs
on:
release: null
push:
tags: ['*']
jobs:
upload:
strategy:
matrix:
include:
- { os: ubuntu-latest, system: x86_64-linux }
- { os: macos-latest, system: x86_64-darwin }
runs-on: ${{ matrix.os }}
steps:
- uses: actions/checkout@v2
- uses: cachix/install-nix-action@v16
- uses: cachix/cachix-action@v10
with:
name: ${{ secrets.CACHIX_NAME }}
authToken: ${{ secrets.CACHIX_AUTH_TOKEN }}
- uses: google-github-actions/setup-gcloud@v0.2.0
with:
version: '290.0.1'
service_account_key: ${{ secrets.GCS_SERVICE_ACCOUNT_KEY }}
project_id: ${{ secrets.GCS_PROJECT }}
export_default_credentials: true
- run: nix-build -A tarball --arg enableStatic true
- name: Run upload to bootstrap.urbit.org
run: |
version="$(cat ./pkg/urbit/version)"
system="$(nix-instantiate --eval --expr 'builtins.currentSystem')"
system=${system:1:${#system}-2}
target="gs://bootstrap.urbit.org/ci/urbit-v${version}-${system}-${GITHUB_SHA:0:9}.tgz"
gsutil cp -n ./result "$target"
echo "upload to $target complete."

263
.github/workflows/vere.yml vendored Normal file
View File

@ -0,0 +1,263 @@
name: vere
on:
workflow_call:
inputs:
upload:
description: 'upload binaries to gcp'
type: boolean
default: false
required: false
pace:
description: 'release pace'
type: string
default: 'often'
required: false
secrets:
CACHIX_AUTH_TOKEN:
required: false
GCS_SERVICE_ACCOUNT_KEY:
required: false
GCS_PROJECT:
required: false
workflow_dispatch:
inputs:
upload:
description: 'upload binaries to gcp'
type: boolean
default: false
required: false
pace:
description: 'release pace'
type: choice
options:
- often
- soon
- live
env:
UPLOAD_BASE: bootstrap.urbit.org/vere
VERE_PACE: ${{ inputs.pace }}
VERSION_TYPE: ${{ (inputs.pace == 'soon' || inputs.pace == 'live') && 'real' || 'hash' }}
jobs:
urbit:
strategy:
fail-fast: false
matrix:
include:
- { os: ubuntu-latest }
- { os: macos-latest }
runs-on: ${{ matrix.os }}
steps:
- uses: actions/checkout@v2
# We only want the extra nix config on linux, where it is necessary
# for the docker build. We don't want in on Mac, where it isn't but
# it breaks the nix install. The two `if` clauses should be mutually
# exclusive
- uses: cachix/install-nix-action@v16
with:
extra_nix_config: |
system-features = nixos-test benchmark big-parallel kvm
if: ${{ matrix.os == 'ubuntu-latest' }}
- uses: cachix/install-nix-action@v16
if: ${{ matrix.os != 'ubuntu-latest' }}
- uses: cachix/cachix-action@v10
with:
name: ares
authToken: ${{ secrets.CACHIX_AUTH_TOKEN }}
- name: build static binary
run: |
nix-build -A urbit \
--arg enableStatic true \
--argstr verePace ${{ env.VERE_PACE }} > ./urbit-derivation
cat ./urbit-derivation
echo -n "urbit_static=" >> $GITHUB_ENV
cat ./urbit-derivation >> $GITHUB_ENV
cat ./urbit-derivation
- name: confirm binary is mostly static
if: matrix.os == 'macos-latest'
run: |
bin="${{ env.urbit_static }}/bin/urbit"
if [ ! -f "$bin" ]; then
echo "no binary at $bin"
exit 1;
fi
libs="$(otool -L "${{ env.urbit_static }}/bin/urbit" | tail -n +2)"
# XX CoreFoundation?
if [ -z "$(echo "$libs" | grep -v libSystem)" ]; then
echo "it's mostly static"
echo "$libs"
exit 0
else
echo "dynamic links found:"
echo "$libs"
exit 1
fi
- name: get version string
run: |
if [ "real" == "$VERSION_TYPE" ]; then
version="$(cat ./pkg/urbit/version)"
else
version="${GITHUB_SHA:0:9}"
fi
echo -n "$version" > ./version-string
- name: upload version string artifact
if: matrix.os == 'ubuntu-latest'
uses: actions/upload-artifact@v3
with:
name: version-string
path: version-string
- uses: google-github-actions/setup-gcloud@v0.2.0
if: inputs.upload
with:
version: '290.0.1'
service_account_key: ${{ secrets.GCS_SERVICE_ACCOUNT_KEY }}
project_id: ${{ secrets.GCS_PROJECT }}
export_default_credentials: true
- name: upload binary to bootstrap.urbit.org
if: inputs.upload
run: |
version="$(cat ./version-string)"
system="$(nix-instantiate --eval --expr 'builtins.currentSystem')"
system=${system:1:${#system}-2}
target="gs://${UPLOAD_BASE}/${VERE_PACE}/${version}/vere-v${version}-${system}"
gsutil cp -n "${{ env.urbit_static }}/bin/urbit" "$target"
exitcode=$?
test $exitcode -eq 0 &&
echo "upload to $target complete." ||
echo "upload to $target failed.";
exit $exitcode
- if: ${{ matrix.os == 'ubuntu-latest' }}
run: nix-build -A urbit-tests
- if: ${{ matrix.os == 'ubuntu-latest' }}
run: nix-build -A docker-image
mingw:
runs-on: windows-latest
defaults:
run:
shell: >
C:\msys64\msys2_shell.cmd -mingw64 -defterm -no-start -here -c
". <(cygpath '{0}')"
working-directory: ./pkg/urbit
steps:
- uses: actions/checkout@v2
with:
lfs: true
# echo suppresses pacman prompt
- run: echo|./configure
env:
CACHIX_CACHE: ares
CACHIX_AUTH_TOKEN: ${{ secrets.CACHIX_AUTH_TOKEN }}
- run: mingw32-make build/urbit
- run: >
build/urbit -l -d -B ../../bin/solid.pill -F bus &&
curl -f --data '{"source":{"dojo":"+hood/exit"},"sink":{"app":"hood"}}'
http://localhost:12321
- name: confirm binary is mostly static
run: |
if [ -z "$(ldd build/urbit | grep -vi "windows/system32")"]; then
echo "it's mostly static"
exit 0
else
echo "dynamic links found:"
ldd build/urbit
exit 1
fi
- uses: actions/setup-python@v2
if: inputs.upload
with:
python-version: 3.7
- uses: google-github-actions/setup-gcloud@v0.6.0
if: inputs.upload
env:
# see https://github.com/google-github-actions/setup-gcloud/issues/100
CLOUDSDK_PYTHON: ${{env.pythonLocation}}\python.exe
with:
service_account_key: ${{ secrets.GCS_SERVICE_ACCOUNT_KEY }}
project_id: ${{ secrets.GCS_PROJECT }}
export_default_credentials: true
- name: upload binary to bootstrap.urbit.org
if: inputs.upload
env:
CLOUDSDK_PYTHON: ${{env.pythonLocation}}\python.exe
shell: bash
run: |
if [ "real" == "$VERSION_TYPE" ]; then
version="$(cat ./version)"
else
version="${GITHUB_SHA:0:9}"
fi
system="x86_64-windows"
target="gs://${UPLOAD_BASE}/${VERE_PACE}/${version}/vere-v${version}-${system}.exe"
gsutil cp -n ./build/urbit.exe "$target"
exitcode=$?
test $exitcode -eq 0 &&
echo "upload to $target complete." ||
echo "upload to $target failed.";
exit $exitcode
after:
runs-on: ubuntu-latest
needs: [urbit, mingw]
# XX disabled due to missing storage.objects.delete access
if: false
# if: inputs.upload
steps:
- uses: google-github-actions/setup-gcloud@v0.2.0
with:
version: '290.0.1'
service_account_key: ${{ secrets.GCS_SERVICE_ACCOUNT_KEY }}
project_id: ${{ secrets.GCS_PROJECT }}
export_default_credentials: true
- name: download version-string
uses: actions/download-artifact@v3
with:
name: version-string
- name: update latest deployed version
run: |
target="gs://${UPLOAD_BASE}/${VERE_PACE}/last"
# *not* -n, as we want to overwrite the latest version-string
#
gsutil cp ./version-string "$target"
exitcode=$?
test $exitcode -eq 0 &&
echo "upload to $target complete." ||
echo "upload to $target failed.";
exit $exitcode

View File

@ -40,7 +40,9 @@
, crossOverlays ? [ ] , crossOverlays ? [ ]
# Whether to use pkgs.pkgsStatic.* to obtain statically linked package # Whether to use pkgs.pkgsStatic.* to obtain statically linked package
# dependencies - ie. when building fully-static libraries or executables. # dependencies - ie. when building fully-static libraries or executables.
, enableStatic ? false }: , enableStatic ? false
# release channel (when static)
, verePace ? "" }:
let let
@ -95,7 +97,7 @@ let
marsSources = callPackage ./nix/pkgs/marsSources { }; marsSources = callPackage ./nix/pkgs/marsSources { };
urbit = callPackage ./nix/pkgs/urbit { inherit enableStatic; }; urbit = callPackage ./nix/pkgs/urbit { inherit enableStatic verePace; };
urcrypt = callPackage ./nix/pkgs/urcrypt { inherit enableStatic; }; urcrypt = callPackage ./nix/pkgs/urcrypt { inherit enableStatic; };

View File

@ -27,6 +27,10 @@ in {
brotliSupport = false; brotliSupport = false;
}; };
# lies, all lies
openssl-static-osx = prev.openssl;
zlib-static-osx = prev.zlib;
lmdb = prev.lmdb.overrideAttrs (attrs: { lmdb = prev.lmdb.overrideAttrs (attrs: {
patches = patches =
optionalList attrs.patches ++ prev.lib.optional prev.stdenv.isDarwin [ optionalList attrs.patches ++ prev.lib.optional prev.stdenv.isDarwin [

View File

@ -16,12 +16,19 @@ let
in { in {
gmp = enableStatic prev.gmp; gmp = enableStatic prev.gmp;
curlUrbit = enableStatic prev.curlUrbit; curlUrbit = enableStatic (prev.curlUrbit.override { openssl = final.openssl-static-osx; zlib = final.zlib-static-osx; });
libuv = enableStatic prev.libuv; libuv = enableStatic prev.libuv;
libffi = enableStatic prev.libffi; libffi = enableStatic prev.libffi;
openssl-static-osx = prev.openssl.override {
static = true;
withPerl = false;
};
zlib-static-osx = if final.stdenv.isDarwin then prev.zlib.static else prev.zlib;
secp256k1 = enableStatic prev.secp256k1; secp256k1 = enableStatic prev.secp256k1;
lmdb = prev.lmdb.overrideAttrs (old: lmdb = prev.lmdb.overrideAttrs (old:

View File

@ -1,9 +1,11 @@
{ lib, stdenv, coreutils, pkgconfig # build/env { lib, stdenv, coreutils, pkgconfig # build/env
, cacert, ca-bundle, ivory # codegen , cacert, ca-bundle, ivory # codegen
, curlUrbit, ent, gmp, h2o, libsigsegv, libuv, lmdb # libs , curlUrbit, ent, gmp, h2o, libsigsegv, libuv, lmdb # libs
, murmur3, openssl, softfloat3, urcrypt, zlib # , murmur3, openssl, openssl-static-osx, softfloat3 #
, urcrypt, zlib, zlib-static-osx #
, enableStatic ? stdenv.hostPlatform.isStatic # opts , enableStatic ? stdenv.hostPlatform.isStatic # opts
, enableDebug ? false , enableDebug ? false
, verePace ? ""
, doCheck ? true , doCheck ? true
, enableParallelBuilding ? true , enableParallelBuilding ? true
, dontStrip ? true }: , dontStrip ? true }:
@ -40,10 +42,10 @@ in stdenv.mkDerivation {
libuv libuv
lmdb lmdb
murmur3 murmur3
openssl (if stdenv.isDarwin && enableStatic then openssl-static-osx else openssl)
softfloat3 softfloat3
urcrypt urcrypt
zlib (if stdenv.isDarwin && enableStatic then zlib-static-osx else zlib)
]; ];
# Ensure any `/usr/bin/env bash` shebang is patched. # Ensure any `/usr/bin/env bash` shebang is patched.
@ -69,6 +71,7 @@ in stdenv.mkDerivation {
MEMORY_DEBUG = enableDebug; MEMORY_DEBUG = enableDebug;
CPU_DEBUG = enableDebug; CPU_DEBUG = enableDebug;
EVENT_TIME_DEBUG = false; EVENT_TIME_DEBUG = false;
VERE_PACE = if enableStatic then verePace else "";
# See https://github.com/NixOS/nixpkgs/issues/18995 # See https://github.com/NixOS/nixpkgs/issues/18995
hardeningDisable = lib.optionals enableDebug [ "all" ]; hardeningDisable = lib.optionals enableDebug [ "all" ];

View File

@ -1,5 +1,5 @@
{ stdenv, autoreconfHook, pkgconfig { stdenv, autoreconfHook, pkgconfig
, libaes_siv, openssl, secp256k1 , libaes_siv, openssl, openssl-static-osx, secp256k1
, enableStatic ? stdenv.hostPlatform.isStatic }: , enableStatic ? stdenv.hostPlatform.isStatic }:
stdenv.mkDerivation rec { stdenv.mkDerivation rec {

View File

@ -1 +1 @@
16.14.0 14.19.0

View File

@ -0,0 +1 @@
nodejs 14.19.0

View File

@ -1,66 +1,71 @@
const path = require('path'); const path = require("path");
const HtmlWebpackPlugin = require('html-webpack-plugin'); const HtmlWebpackPlugin = require("html-webpack-plugin");
const { CleanWebpackPlugin } = require('clean-webpack-plugin'); const { CleanWebpackPlugin } = require("clean-webpack-plugin");
const MomentLocalesPlugin = require('moment-locales-webpack-plugin'); const MomentLocalesPlugin = require("moment-locales-webpack-plugin");
const webpack = require('webpack'); const webpack = require("webpack");
const { execSync } = require('child_process'); const { execSync } = require("child_process");
const GIT_DESC = execSync('git describe --always', { encoding: 'utf8' }).trim(); const GIT_DESC = execSync("git describe --always", { encoding: "utf8" }).trim();
module.exports = { module.exports = {
mode: 'production', mode: "production",
entry: { entry: {
app: './src/index.tsx', app: "./src/index.tsx",
serviceworker: './src/serviceworker.js' serviceworker: "./src/serviceworker.js",
}, },
module: { module: {
rules: [ rules: [
{ {
test: /\.(j|t)sx?$/, test: /\.(j|t)sx?$/,
use: { use: {
loader: 'babel-loader', loader: "babel-loader",
options: { options: {
presets: ['@babel/preset-env', '@babel/typescript', '@babel/preset-react'], presets: [
"@babel/preset-env",
"@babel/typescript",
"@babel/preset-react",
],
plugins: [ plugins: [
'lodash', "lodash",
'@babel/transform-runtime', "@babel/transform-runtime",
'@babel/plugin-proposal-object-rest-spread', "@babel/plugin-proposal-object-rest-spread",
'@babel/plugin-proposal-optional-chaining', "@babel/plugin-proposal-optional-chaining",
'@babel/plugin-proposal-class-properties' "@babel/plugin-proposal-class-properties",
] ],
} },
}, },
exclude: /node_modules\/(?!(@tlon\/indigo-dark|@tlon\/indigo-light|@tlon\/indigo-react|@urbit\/api)\/).*/ exclude:
/node_modules\/(?!(@tlon\/indigo-dark|@tlon\/indigo-light|@tlon\/indigo-react|@urbit\/api)\/).*/,
}, },
{ {
test: /\.css$/i, test: /\.css$/i,
use: [ use: [
// Creates `style` nodes from JS strings // Creates `style` nodes from JS strings
'style-loader', "style-loader",
// Translates CSS into CommonJS // Translates CSS into CommonJS
'css-loader', "css-loader",
// Compiles Sass to CSS // Compiles Sass to CSS
'sass-loader' "sass-loader",
] ],
}, },
{ {
test: /\.(woff(2)?|ttf|eot|svg)(\?v=\d+\.\d+\.\d+)?$/, test: /\.(woff(2)?|ttf|eot|svg)(\?v=\d+\.\d+\.\d+)?$/,
use: [ use: [
{ {
loader: 'file-loader', loader: "file-loader",
options: { options: {
name: '[name].[ext]', name: "[name].[ext]",
outputPath: 'fonts/' outputPath: "fonts/",
} },
} },
] ],
} },
] ],
}, },
resolve: { resolve: {
extensions: ['.js', '.ts', '.tsx'] extensions: [".js", ".ts", ".tsx"],
}, },
devtool: 'source-map', devtool: "source-map",
// devServer: { // devServer: {
// contentBase: path.join(__dirname, './'), // contentBase: path.join(__dirname, './'),
// hot: true, // hot: true,
@ -71,26 +76,30 @@ module.exports = {
new MomentLocalesPlugin(), new MomentLocalesPlugin(),
new CleanWebpackPlugin(), new CleanWebpackPlugin(),
new webpack.DefinePlugin({ new webpack.DefinePlugin({
'process.env.LANDSCAPE_STREAM': JSON.stringify(process.env.LANDSCAPE_STREAM), "process.env.LANDSCAPE_STREAM": JSON.stringify(
'process.env.LANDSCAPE_SHORTHASH': JSON.stringify(GIT_DESC), process.env.LANDSCAPE_STREAM
'process.env.LANDSCAPE_STORAGE_VERSION': Date.now().toString(), ),
'process.env.LANDSCAPE_LAST_WIPE': '2021-10-20', "process.env.LANDSCAPE_SHORTHASH": JSON.stringify(GIT_DESC),
"process.env.LANDSCAPE_STORAGE_VERSION": Date.now().toString(),
"process.env.LANDSCAPE_LAST_WIPE": "2021-10-20",
}), }),
new HtmlWebpackPlugin({ new HtmlWebpackPlugin({
title: 'Groups', title: "Groups",
template: './public/index.html', template: "./public/index.html",
favicon: './src/assets/img/Favicon.png' favicon: "./src/assets/img/favicon.png",
}) }),
], ],
output: { output: {
filename: (pathData) => { filename: (pathData) => {
return pathData.chunk.name === 'app' ? 'index.[contenthash].js' : '[name].js'; return pathData.chunk.name === "app"
? "index.[contenthash].js"
: "[name].js";
}, },
path: path.resolve(__dirname, '../dist'), path: path.resolve(__dirname, "../dist"),
publicPath: '/apps/landscape/' publicPath: "/apps/landscape/",
}, },
optimization: { optimization: {
minimize: true, minimize: true,
usedExports: true usedExports: true,
} },
}; };

File diff suppressed because it is too large Load Diff

View File

@ -5,21 +5,18 @@
"main": "index.js", "main": "index.js",
"private": true, "private": true,
"engines": { "engines": {
"node": "16.14.0" "node": "14.19.0"
}, },
"dependencies": { "dependencies": {
"@babel/runtime": "^7.12.5", "@babel/runtime": "^7.12.5",
"@fingerprintjs/fingerprintjs": "^3.3.3", "@fingerprintjs/fingerprintjs": "^3.3.3",
"@radix-ui/react-dialog": "^0.1.0", "@radix-ui/react-dialog": "^0.1.0",
"@reach/disclosure": "^0.10.5",
"@reach/menu-button": "^0.10.5",
"@reach/tabs": "^0.10.5",
"@react-spring/web": "^9.1.1", "@react-spring/web": "^9.1.1",
"@tlon/indigo-dark": "^1.0.6", "@tlon/indigo-dark": "^1.0.6",
"@tlon/indigo-light": "^1.0.7", "@tlon/indigo-light": "^1.0.7",
"@tlon/indigo-react": "^1.2.27", "@tlon/indigo-react": "^1.2.27",
"@tlon/sigil-js": "^1.4.3", "@tlon/sigil-js": "^1.4.5",
"@urbit/api": "^2.1.0", "@urbit/api": "^2.1.1",
"@urbit/http-api": "^2.1.0", "@urbit/http-api": "^2.1.0",
"any-ascii": "^0.1.7", "any-ascii": "^0.1.7",
"aws-sdk": "^2.830.0", "aws-sdk": "^2.830.0",
@ -36,15 +33,15 @@
"mousetrap": "^1.6.5", "mousetrap": "^1.6.5",
"mousetrap-global-bind": "^1.1.0", "mousetrap-global-bind": "^1.1.0",
"normalize-wheel": "1.0.1", "normalize-wheel": "1.0.1",
"oembed-parser": "^1.4.5", "oembed-parser": "^3.0.4",
"prop-types": "^15.7.2", "prop-types": "^15.7.2",
"querystring": "^0.2.0", "querystring": "^0.2.0",
"react": "^17.0.2", "react": "^17.0.2",
"react-codemirror2": "^6.0.1", "react-codemirror2": "git@github.com:scniro/react-codemirror2",
"react-dom": "^17.0.2", "react-dom": "^17.0.2",
"react-helmet": "^6.1.0", "react-helmet": "^6.1.0",
"react-markdown": "^4.3.1", "react-markdown": "^8.0.3",
"react-oembed-container": "^1.0.0", "react-oembed-container": "^1.0.1",
"react-router-dom": "^5.2.0", "react-router-dom": "^5.2.0",
"react-use-gesture": "^9.1.3", "react-use-gesture": "^9.1.3",
"react-virtuoso": "^0.20.3", "react-virtuoso": "^0.20.3",
@ -107,6 +104,7 @@
"lint-staged": "^11.0.0", "lint-staged": "^11.0.0",
"loki": "^0.28.1", "loki": "^0.28.1",
"moment-locales-webpack-plugin": "^1.2.0", "moment-locales-webpack-plugin": "^1.2.0",
"patch-package": "^6.4.7",
"react-refresh": "^0.11.0", "react-refresh": "^0.11.0",
"sass": "^1.32.5", "sass": "^1.32.5",
"sass-loader": "^8.0.2", "sass-loader": "^8.0.2",
@ -131,7 +129,8 @@
"storybook": "start-storybook -p 6006", "storybook": "start-storybook -p 6006",
"build-storybook": "build-storybook", "build-storybook": "build-storybook",
"chromatic": "chromatic --exit-zero-on-changes", "chromatic": "chromatic --exit-zero-on-changes",
"hook-lint": "eslint --cache --fix" "hook-lint": "eslint --cache --fix",
"postinstall": "patch-package"
}, },
"author": "", "author": "",
"license": "MIT", "license": "MIT",

View File

@ -4,7 +4,7 @@ export interface Suspender<T> {
read: () => T; read: () => T;
} }
export function suspend<T>(awaiting: Promise<T>): Suspender<T> { export function suspend<T>(awaiting: Promise<T>, defaultValue?: any): Suspender<T> {
let state: SuspendState = 'pending'; let state: SuspendState = 'pending';
let result: T | null = null; let result: T | null = null;
@ -22,8 +22,10 @@ export function suspend<T>(awaiting: Promise<T>): Suspender<T> {
read: () => { read: () => {
if (state === 'result') { if (state === 'result') {
return result!; return result!;
} else if (state === 'error') { } else if (state === 'error' && typeof defaultValue === 'undefined') {
throw result; throw result;
} else if (state === 'error' && typeof defaultValue !== 'undefined') {
return defaultValue;
} else { } else {
throw promise; throw promise;
} }

View File

@ -34,7 +34,8 @@ const useStorage = ({ accept = '*' } = { accept: '*' }): IuseStorage => {
} }
client.current = new S3Client({ client.current = new S3Client({
credentials: s3.credentials, credentials: s3.credentials,
endpoint: s3.credentials.endpoint endpoint: s3.credentials.endpoint,
signatureVersion: 'v4'
}); });
} }
}, [gcp.token, s3.credentials]); }, [gcp.token, s3.credentials]);

View File

@ -11,8 +11,7 @@ export function getTitleFromWorkspace(
case 'messages': case 'messages':
return 'Messages'; return 'Messages';
case 'group': case 'group':
const association = associations.groups[workspace.group]; return associations.groups[workspace.group]?.metadata?.title || 'Groups';
return association?.metadata?.title || '';
} }
} }

View File

@ -1,6 +1,6 @@
import { useCallback } from 'react'; import { useCallback } from 'react';
import create from 'zustand'; import create from 'zustand';
import { suspend, Suspender , suspendWithResult } from '../lib/suspend'; import { suspend, Suspender } from '../lib/suspend';
import { jsonFetch } from '~/logic/lib/util'; import { jsonFetch } from '~/logic/lib/util';
export interface EmbedState { export interface EmbedState {
@ -23,17 +23,19 @@ const useEmbedState = create<EmbedState>((set, get) => ({
const search = new URLSearchParams({ const search = new URLSearchParams({
url url
}); });
const embed = await jsonFetch(`${OEMBED_PROVIDER}?${search.toString()}`); const embed = await jsonFetch(`${OEMBED_PROVIDER}?${search.toString()}`);
const { embeds: es } = get();
set({ embeds: { ...es, [url]: embed } });
return embed; return embed;
}, },
getEmbed: (url: string): Suspender<any> => { getEmbed: (url: string): Suspender<any> => {
const { fetch, embeds } = get(); const { fetch, embeds } = get();
if(url in embeds) { if(url in embeds) {
return suspendWithResult(embeds[url]); return embeds[url];
} }
return suspend(fetch(url)); const { embeds: es } = get();
const embed = suspend(fetch(url), {});
set({ embeds: { ...es, [url]: embed } });
return embed;
} }
})); }));

View File

@ -1,5 +1,6 @@
import { acceptDm, cite, Content, declineDm, deSig, Post } from '@urbit/api'; import { acceptDm, cite, Content, declineDm, deSig, Post } from '@urbit/api';
import React, { useCallback, useEffect } from 'react'; import React, { useCallback, useEffect } from 'react';
import Helmet from 'react-helmet';
import _ from 'lodash'; import _ from 'lodash';
import bigInt from 'big-integer'; import bigInt from 'big-integer';
import { Box, Row, Col, Text, Center } from '@tlon/indigo-react'; import { Box, Row, Col, Text, Center } from '@tlon/indigo-react';
@ -49,6 +50,21 @@ function quoteReply(post: Post) {
return `${reply}\n\n~${post.author}:`; return `${reply}\n\n~${post.author}:`;
} }
export function DmHelmet(props: DmHelmetProps) {
const { ship } = props;
const hark = useHarkDm(ship);
const unreadCount = hark.count;
const contact = useContact(ship);
const { hideNicknames } = useSettingsState(selectCalmState);
const showNickname = !hideNicknames && Boolean(contact);
const nickname = showNickname ? contact!.nickname : cite(ship) ?? ship;
return(
<Helmet defer={false}>
<title>{unreadCount ? `(${String(unreadCount)}) ` : ''}{ nickname }</title>
</Helmet>
);
}
export function DmResource(props: DmResourceProps) { export function DmResource(props: DmResourceProps) {
const { ship } = props; const { ship } = props;
const dm = useDM(ship); const dm = useDM(ship);

View File

@ -1,18 +1,17 @@
import React from 'react'; import React from 'react';
import { import {
Icon, Icon,
Center,
Row, Row,
Text, Text,
Col, Col,
Box, Box,
CenterProps CenterProps
} from '@tlon/indigo-react'; } from '@tlon/indigo-react';
import { hasProvider } from 'oembed-parser'; import { AUDIO_REGEX, IMAGE_REGEX, validOembedCheck } from '~/views/components/RemoteContent';
import { AUDIO_REGEX, IMAGE_REGEX } from '~/views/components/RemoteContent';
import { AudioPlayer } from '~/views/components/AudioPlayer'; import { AudioPlayer } from '~/views/components/AudioPlayer';
import { useHistory } from 'react-router'; import { useHistory } from 'react-router';
import { useHovering } from '~/logic/lib/util'; import { useHovering } from '~/logic/lib/util';
import { useEmbed } from '~/logic/state/embed';
import Author from '~/views/components/Author'; import Author from '~/views/components/Author';
import { import {
GraphNode, GraphNode,
@ -38,8 +37,32 @@ export interface LinkBlockItemProps {
summary?: boolean; summary?: boolean;
} }
export function LinkBlockItem(props: LinkBlockItemProps & CenterProps) { export const LinkBlockItem = (props: LinkBlockItemProps & CenterProps) => {
const { node, summary, size, m, border = 1, objectFit, ...rest } = props; const { node, ...rest } = props;
const { post } = node;
const { contents } = post;
const [{ text: title }, ...content] = contents as [
TextContent,
UrlContent | ReferenceContent
];
let url = '';
if ('url' in content?.[0]) {
url = content[0].url;
}
return(
<AsyncFallback fallback={<RemoteContentEmbedFallback url={url} />}>
<LinkBlockItemInner
node={node}
{...rest}
/>
</AsyncFallback>
);
}
function LinkBlockItemInner(props: LinkBlockItemProps & CenterProps) {
const { node, summary, m, border = 1, objectFit, ...rest } = props;
const { post, children } = node; const { post, children } = node;
const { contents, index, author } = post; const { contents, index, author } = post;
@ -56,8 +79,9 @@ export function LinkBlockItem(props: LinkBlockItemProps & CenterProps) {
const isImage = IMAGE_REGEX.test(url); const isImage = IMAGE_REGEX.test(url);
const isAudio = AUDIO_REGEX.test(url); const isAudio = AUDIO_REGEX.test(url);
const oembed = useEmbed(url);
const isOembed = validOembedCheck(oembed, url);
const isOembed = hasProvider(url);
const history = useHistory(); const history = useHistory();
const { hovering, bind } = useHovering(); const { hovering, bind } = useHovering();
const onClick = () => { const onClick = () => {
@ -65,70 +89,67 @@ export function LinkBlockItem(props: LinkBlockItemProps & CenterProps) {
history.push(`${pathname}/index${index}${search}`); history.push(`${pathname}/index${index}${search}`);
}; };
return ( return (
<Center <Box
onClick={onClick} onClick={onClick}
position="relative"
m={m}
border={border} border={border}
borderColor="lightGray" borderColor="lightGray"
position="relative"
borderRadius="1" borderRadius="1"
height={size}
width={size}
m={m}
maxHeight="100%"
{...rest} {...rest}
{...bind} {...bind}
> >
<AsyncFallback fallback={<RemoteContentEmbedFallback url={url} />}> <Col height="100%" justifyContent="center" alignItems="center">
{isReference ? ( {isReference ? (
summary ? ( summary ? (
<RemoteContentPermalinkEmbed <RemoteContentPermalinkEmbed
reference={content[0] as ReferenceContent} reference={content[0] as ReferenceContent}
/> />
) : ( ) : (
<PermalinkEmbed <PermalinkEmbed
link={referenceToPermalink(content[0] as ReferenceContent).link} link={referenceToPermalink(content[0] as ReferenceContent).link}
transcluded={0} transcluded={0}
/> />
) )
) : isImage ? ( ) : isImage ? (
<RemoteContentImageEmbed <RemoteContentImageEmbed
url={url} url={url}
tall tall
stretch stretch
objectFit={objectFit ? objectFit : "cover"} objectFit={objectFit ? objectFit : "cover"}
/> />
) : isAudio ? ( ) : isAudio ? (
<AudioPlayer title={title} url={url} /> <AudioPlayer title={title} url={url} />
) : isOembed ? ( ) : isOembed ? (
<RemoteContentOembed tall={!summary} renderUrl={false} url={url} thumbnail={summary} /> <RemoteContentOembed tall={!summary} renderUrl={false} url={url} thumbnail={summary} oembed={oembed} />
) : ( ) : (
<RemoteContentEmbedFallback url={url} /> <RemoteContentEmbedFallback url={url} />
)} )}
</AsyncFallback> <Box
<Box backgroundColor="white"
backgroundColor="white" display={summary && hovering ? 'block' : 'none'}
display={summary && hovering ? 'block' : 'none'} width="100%"
width="100%" height="64px"
height="64px" position="absolute"
position="absolute" left="0"
left="0" bottom="0"
bottom="0" >
> <Col width="100%" height="100%" p="2" justifyContent="space-between">
<Col width="100%" height="100%" p="2" justifyContent="space-between"> <Row justifyContent="space-between" width="100%">
<Row justifyContent="space-between" width="100%"> <Text textOverflow="ellipsis" whiteSpace="nowrap" overflow="hidden">
<Text textOverflow="ellipsis" whiteSpace="nowrap" overflow="hidden"> {title}
{title} </Text>
</Text> <Row gapX="1" alignItems="center">
<Row gapX="1" alignItems="center"> <Icon icon="Chat" color="black" />
<Icon icon="Chat" color="black" /> <Text>{children.size}</Text>
<Text>{children.size}</Text> </Row>
</Row> </Row>
</Row> <Row width="100%">
<Row width="100%"> <Author ship={author} date={post['time-sent']} showImage></Author>
<Author ship={author} date={post['time-sent']} showImage></Author> </Row>
</Row> </Col>
</Col> </Box>
</Box> </Col>
</Center> </Box>
); );
} }

View File

@ -1,4 +1,4 @@
import { Col, Row, RowProps } from '@tlon/indigo-react'; import { Center, Col, Row, RowProps } from '@tlon/indigo-react';
import { Association, GraphNode, markEachAsRead, TextContent, UrlContent } from '@urbit/api'; import { Association, GraphNode, markEachAsRead, TextContent, UrlContent } from '@urbit/api';
import React, { useEffect } from 'react'; import React, { useEffect } from 'react';
import { useGroup } from '~/logic/state/group'; import { useGroup } from '~/logic/state/group';
@ -27,21 +27,13 @@ export function LinkDetail(props: LinkDetailProps) {
return ( return (
/* @ts-ignore indio props?? */ /* @ts-ignore indio props?? */
<Row height="100%" width="100%" flexDirection={['column', 'column', 'row']} {...rest}> <Row height="100%" width="100%" flexDirection={['column', 'column', 'row']} {...rest}>
<LinkBlockItem <Center flex="3 1 75%" overflowY="scroll" >
minWidth="0" <LinkBlockItem maxHeight="100%" border={0} node={node} objectFit="contain" />
minHeight="0" </Center>
height={["50%", "50%", "100%"]}
width={["100%", "100%", "calc(100% - 350px)"]}
flexGrow={0}
border={0}
node={node}
objectFit="contain"
/>
<Col <Col
minHeight="0" flex="1 25%"
flexShrink={1} maxWidth={['auto', 'auto', '45ch']}
width={['100%', '100%', '350px']} maxHeight={['50%', '50%', 'unset']}
flexGrow={0}
gapY={[2,4]} gapY={[2,4]}
borderLeft={[0, 0, 1]} borderLeft={[0, 0, 1]}
borderTop={[1, 1, 0]} borderTop={[1, 1, 0]}

View File

@ -90,33 +90,33 @@ export function EditProfile(props: any): ReactElement {
const onSubmit = async (values: any, actions: any) => { const onSubmit = async (values: any, actions: any) => {
try { try {
Object.keys(values).forEach((key) => { for (const key in values) {
const newValue = key !== 'color' ? values[key] : uxToHex(values[key]); const newValue = key !== 'color' ? values[key] : uxToHex(values[key]);
if (newValue !== contact[key]) { if (newValue === contact[key] || key === 'last-updated') {
if (key === 'isPublic') { continue;
airlock.poke(setPublic(newValue)); } else if (key === 'isPublic') {
return; await airlock.poke(setPublic(newValue));
} else if (key === 'groups') { } else if (key === 'groups') {
const toRemove: string[] = _.difference( const toRemove: string[] = _.difference(
contact?.groups || [], contact?.groups || [],
newValue newValue
); );
const toAdd: string[] = _.difference( const toAdd: string[] = _.difference(
newValue, newValue,
contact?.groups || [] contact?.groups || []
); );
toRemove.forEach(e => for (const i in toRemove) {
airlock.poke(editContact(ship, { 'remove-group': resourceFromPath(e) })) const group = resourceFromPath(toRemove[i]);
); await airlock.poke(editContact(ship, { 'remove-group': group }));
toAdd.forEach(e =>
airlock.poke(editContact(ship, { 'add-group': resourceFromPath(e) }))
);
} else if (key !== 'last-updated' && key !== 'isPublic') {
airlock.poke(editContact(ship, { [key]: newValue }));
return;
} }
for (const i in toAdd) {
const group = resourceFromPath(toAdd[i]);
await airlock.poke(editContact(ship, { 'add-group': group }));
}
} else {
await airlock.poke(editContact(ship, { [key]: newValue }));
} }
}); }
history.push(`/~profile/${ship}`); history.push(`/~profile/${ship}`);
} catch (e) { } catch (e) {
console.error(e); console.error(e);

View File

@ -23,6 +23,7 @@ interface MarkdownEditorProps {
value: string; value: string;
onChange: (s: string) => void; onChange: (s: string) => void;
onBlur?: (e: any) => void; onBlur?: (e: any) => void;
disabled?: boolean;
} }
const PromptIfDirty = () => { const PromptIfDirty = () => {
@ -39,7 +40,7 @@ const PromptIfDirty = () => {
export function MarkdownEditor( export function MarkdownEditor(
props: MarkdownEditorProps & PropFunc<typeof Box> props: MarkdownEditorProps & PropFunc<typeof Box>
) { ) {
const { onBlur, placeholder, value, onChange, ...boxProps } = props; const { onBlur, placeholder, value, onChange, disabled, ...boxProps } = props;
const options = { const options = {
mode: MARKDOWN_CONFIG, mode: MARKDOWN_CONFIG,
@ -56,7 +57,9 @@ export function MarkdownEditor(
const handleChange = useCallback( const handleChange = useCallback(
(_e, _d, v: string) => { (_e, _d, v: string) => {
onChange(v); if (!disabled) {
onChange(v);
}
}, },
[onChange] [onChange]
); );
@ -93,6 +96,7 @@ export function MarkdownEditor(
p={1} p={1}
border={1} border={1}
borderColor="lightGray" borderColor="lightGray"
backgroundColor={disabled ? '#eee' : '#fff'}
borderRadius={2} borderRadius={2}
height={['calc(100% - 22vh)', '100%']} height={['calc(100% - 22vh)', '100%']}
{...boxProps} {...boxProps}

View File

@ -6,14 +6,17 @@ import { MarkdownEditor } from './MarkdownEditor';
export const MarkdownField = ({ export const MarkdownField = ({
id, id,
disabled,
...rest ...rest
}: { id: string } & Parameters<typeof Box>[0]) => { }: { id: string; disabled?: boolean } & Parameters<typeof Box>[0]) => {
const [{ value, onBlur }, { error, touched }, { setValue }] = useField(id); const [{ value, onBlur }, { error, touched }, { setValue }] = useField(id);
const handleBlur = useCallback( const handleBlur = useCallback(
(e: any) => { (e: any) => {
_.set(e, 'target.id', id); if (!disabled) {
onBlur && onBlur(e); _.set(e, 'target.id', id);
onBlur && onBlur(e);
}
}, },
[onBlur, id] [onBlur, id]
); );
@ -23,7 +26,7 @@ export const MarkdownField = ({
return ( return (
<Box <Box
overflowY="hidden" overflowY="hidden"
height='100%' height="100%"
width="100%" width="100%"
display="flex" display="flex"
flexDirection="column" flexDirection="column"
@ -35,6 +38,7 @@ export const MarkdownField = ({
onBlur={handleBlur} onBlur={handleBlur}
value={value} value={value}
onChange={setValue} onChange={setValue}
disabled={disabled}
/> />
<ErrorLabel mt={2} hasError={Boolean(error && touched)}> <ErrorLabel mt={2} hasError={Boolean(error && touched)}>
{error} {error}

View File

@ -1,5 +1,7 @@
import { import {
Button, Col, ManagedTextInputField as Input, Button,
Col,
ManagedTextInputField as Input,
Row Row
} from '@tlon/indigo-react'; } from '@tlon/indigo-react';
import { Form, Formik, FormikHelpers } from 'formik'; import { Form, Formik, FormikHelpers } from 'formik';
@ -31,7 +33,8 @@ export interface PostFormSchema {
} }
export function PostForm(props: PostFormProps) { export function PostForm(props: PostFormProps) {
const { initial, onSubmit, submitLabel, loadingText, cancel, history } = props; const { initial, onSubmit, submitLabel, loadingText, cancel, history } =
props;
return ( return (
<Col width="100%" height="100%" p={[2, 4]}> <Col width="100%" height="100%" p={[2, 4]}>
@ -40,30 +43,49 @@ export function PostForm(props: PostFormProps) {
initialValues={initial} initialValues={initial}
onSubmit={onSubmit} onSubmit={onSubmit}
> >
<Form style={{ display: 'contents' }}> {({ isSubmitting }) => (
<Row flexShrink={0} flexDirection={['column-reverse', 'row']} mb={4} gapX={4} justifyContent='space-between'> <Form style={{ display: 'contents' }}>
<Input maxWidth='40rem' width='100%' flexShrink={[0, 1]} placeholder="Post Title" id="title" /> <Row
<Row flexDirection={['column', 'row']} mb={[4,0]}> flexShrink={0}
<AsyncButton flexDirection={['column-reverse', 'row']}
ml={[0,2]} mb={4}
flexShrink={0} gapX={4}
primary justifyContent="space-between"
loadingText={loadingText} >
> <Input
{submitLabel} maxWidth="40rem"
</AsyncButton> width="100%"
{cancel && <Button flexShrink={[0, 1]}
ml={[0,2]} placeholder="Post Title"
mt={[2,0]} id="title"
onClick={() => { disabled={isSubmitting}
history.goBack(); />
}} <Row flexDirection={['column', 'row']} mb={[4, 0]}>
type="button" <AsyncButton
>Cancel</Button>} ml={[0, 2]}
flexShrink={0}
primary
loadingText={loadingText}
>
{submitLabel}
</AsyncButton>
{cancel && (
<Button
ml={[0, 2]}
mt={[2, 0]}
onClick={() => {
history.goBack();
}}
type="button"
>
Cancel
</Button>
)}
</Row>
</Row> </Row>
</Row> <MarkdownField flexGrow={1} id="body" disabled={isSubmitting} />
<MarkdownField flexGrow={1} id="body" /> </Form>
</Form> )}
</Formik> </Formik>
</Col> </Col>
); );

View File

@ -3,7 +3,7 @@ import moment from 'moment';
import React, { ReactElement, ReactNode } from 'react'; import React, { ReactElement, ReactNode } from 'react';
import { Sigil } from '~/logic/lib/sigil'; import { Sigil } from '~/logic/lib/sigil';
import { useCopy } from '~/logic/lib/useCopy'; import { useCopy } from '~/logic/lib/useCopy';
import { cite, uxToHex } from '~/logic/lib/util'; import { cite, deSig, uxToHex } from '~/logic/lib/util';
import { useContact } from '~/logic/state/contact'; import { useContact } from '~/logic/state/contact';
import { useDark } from '~/logic/state/join'; import { useDark } from '~/logic/state/join';
import useSettingsState, { selectCalmState, useShowNickname } from '~/logic/state/settings'; import useSettingsState, { selectCalmState, useShowNickname } from '~/logic/state/settings';
@ -52,7 +52,7 @@ function Author(props: AuthorProps & PropFunc<typeof Box>): ReactElement {
const { hideAvatars } = useSettingsState(selectCalmState); const { hideAvatars } = useSettingsState(selectCalmState);
const name = showNickname && contact ? contact.nickname : cite(ship); const name = showNickname && contact ? contact.nickname : cite(ship);
const stamp = moment(date); const stamp = moment(date);
const { copyDisplay, doCopy } = useCopy(`~${ship}`, name); const { copyDisplay, doCopy } = useCopy(`~${deSig(ship)}`, name);
const sigil = fullNotIcon ? ( const sigil = fullNotIcon ? (
<Sigil ship={ship} size={size} color={color} padding={sigilPadding} /> <Sigil ship={ship} size={size} color={color} padding={sigilPadding} />

View File

@ -28,7 +28,7 @@ import { Link } from 'react-router-dom';
import { AppPermalink, referenceToPermalink } from '~/logic/lib/permalinks'; import { AppPermalink, referenceToPermalink } from '~/logic/lib/permalinks';
import useMetadataState from '~/logic/state/metadata'; import useMetadataState from '~/logic/state/metadata';
import { RemoteContentWrapper } from './wrapper'; import { RemoteContentWrapper } from './wrapper';
import { useEmbed } from '~/logic/state/embed'; import { Suspender } from '~/logic/lib/suspend';
import { IS_SAFARI } from '~/logic/lib/platform'; import { IS_SAFARI } from '~/logic/lib/platform';
import useDocketState, { useTreaty } from '~/logic/state/docket'; import useDocketState, { useTreaty } from '~/logic/state/docket';
import { AppTile } from '~/views/apps/permalinks/embed'; import { AppTile } from '~/views/apps/permalinks/embed';
@ -97,6 +97,7 @@ export function RemoteContentImageEmbed(
objectFit="cover" objectFit="cover"
borderRadius={2} borderRadius={2}
onError={onError} onError={onError}
style={{ imageRendering: '-webkit-optimize-contrast' }}
{...props} {...props}
/> />
</Box> </Box>
@ -319,6 +320,7 @@ type RemoteContentOembedProps = {
renderUrl?: boolean; renderUrl?: boolean;
thumbnail?: boolean; thumbnail?: boolean;
tall?: boolean; tall?: boolean;
oembed: Suspender<any>;
} & RemoteContentEmbedProps & } & RemoteContentEmbedProps &
PropFunc<typeof Box>; PropFunc<typeof Box>;
@ -332,10 +334,9 @@ export const RemoteContentOembed = React.forwardRef<
HTMLDivElement, HTMLDivElement,
RemoteContentOembedProps RemoteContentOembedProps
>((props, ref) => { >((props, ref) => {
const { url, renderUrl = false, thumbnail = false, ...rest } = props; const { url, oembed, renderUrl = false, thumbnail = false, ...rest } = props;
const oembed = useEmbed(url);
const embed = oembed.read(); const embed = oembed.read();
const fallbackError = new Error('fallback');
const [aspect, width, height] = useMemo(() => { const [aspect, width, height] = useMemo(() => {
if(!('height' in embed && typeof embed.height === 'number' if(!('height' in embed && typeof embed.height === 'number'
@ -373,11 +374,9 @@ export const RemoteContentOembed = React.forwardRef<
dangerouslySetInnerHTML={{ __html: embed.html }} dangerouslySetInnerHTML={{ __html: embed.html }}
></EmbedBox> ></EmbedBox>
</EmbedContainer> </EmbedContainer>
) : renderUrl ? ( ) : (
<RemoteContentEmbedFallback url={url} /> <RemoteContentEmbedFallback url={url} />
) : (() => { )
throw fallbackError;
})()
} }
</Col> </Col>
); );

View File

@ -1,12 +1,17 @@
import { hasProvider } from 'oembed-parser'; import {
Box,
} from '@tlon/indigo-react';
import React from 'react'; import React from 'react';
import useSettingsState from '~/logic/state/settings'; import useSettingsState from '~/logic/state/settings';
import { import {
RemoteContentAudioEmbed, RemoteContentAudioEmbed,
RemoteContentImageEmbed, RemoteContentImageEmbed,
RemoteContentOembed, RemoteContentOembed,
RemoteContentVideoEmbed RemoteContentVideoEmbed,
RemoteContentEmbedFallback
} from './embed'; } from './embed';
import { useEmbed } from '~/logic/state/embed';
import { Suspender } from '~/logic/lib/suspend';
import { TruncatedText } from '~/views/components/TruncatedText'; import { TruncatedText } from '~/views/components/TruncatedText';
import { RemoteContentWrapper } from './wrapper'; import { RemoteContentWrapper } from './wrapper';
import AsyncFallback from '../AsyncFallback'; import AsyncFallback from '../AsyncFallback';
@ -43,8 +48,34 @@ export const IMAGE_REGEX = new RegExp(
export const AUDIO_REGEX = new RegExp(/(\.mp3|\.wav|\.ogg|\.m4a)$/i); export const AUDIO_REGEX = new RegExp(/(\.mp3|\.wav|\.ogg|\.m4a)$/i);
export const VIDEO_REGEX = new RegExp(/(\.mov|\.mp4|\.ogv)$/i); export const VIDEO_REGEX = new RegExp(/(\.mov|\.mp4|\.ogv)$/i);
// This is used to prevent our oembed parser from
// trying to embed facebook/instagram links, which require an API key
const isFacebookGraphDependent = (url: string) => {
const caseDesensitizedURL = url.toLowerCase()
return (caseDesensitizedURL.includes('facebook.com') || caseDesensitizedURL.includes('instagram.com'))
}
export const validOembedCheck = (embed: Suspender<any>, url: string) => {
if (!isFacebookGraphDependent(url)) {
if (!embed.read().hasOwnProperty("error")) {
return true
}
}
return false
}
export const RemoteContent = (props: RemoteContentProps) => {
const {url, ...rest} = props
return(
<AsyncFallback fallback={<RemoteContentEmbedFallback url={url} />}>
<RemoteContentInner url={url} {...rest}/>
</AsyncFallback>
)
}
const emptyRef = () => {}; const emptyRef = () => {};
export function RemoteContent(props: RemoteContentProps) { function RemoteContentInner(props: RemoteContentProps) {
const { const {
url, url,
embedRef = emptyRef, embedRef = emptyRef,
@ -57,45 +88,51 @@ export function RemoteContent(props: RemoteContentProps) {
const isImage = IMAGE_REGEX.test(url); const isImage = IMAGE_REGEX.test(url);
const isAudio = AUDIO_REGEX.test(url); const isAudio = AUDIO_REGEX.test(url);
const isVideo = VIDEO_REGEX.test(url); const isVideo = VIDEO_REGEX.test(url);
const isOembed = hasProvider(url); const oembed = useEmbed(url);
const isOembed = validOembedCheck(oembed, url);
const wrapperProps = { const wrapperProps = {
url, url,
tall, tall,
embedOnly: !renderUrl || tall embedOnly: !renderUrl || tall
}; };
const fallback = !renderUrl ? null : ( const fallback = null;
<RemoteContentWrapper {...wrapperProps}>
<TruncatedText>{url}</TruncatedText>
</RemoteContentWrapper>
);
if (isImage && remoteContentPolicy.imageShown) { if (isImage && remoteContentPolicy.imageShown) {
return ( return (
<RemoteContentWrapper {...wrapperProps} noOp={transcluded} replaced> <Box mt={1} mb={2} flexShrink={0}>
<RemoteContentImageEmbed url={url} /> <RemoteContentWrapper {...wrapperProps} noOp={transcluded} replaced>
</RemoteContentWrapper> <RemoteContentImageEmbed url={url} />
</RemoteContentWrapper>
</Box>
); );
} else if (isAudio && remoteContentPolicy.audioShown) { } else if (isAudio && remoteContentPolicy.audioShown) {
return ( return (
<RemoteContentWrapper {...wrapperProps}> <Box mt={1} mb={2} flexShrink={0}>
<RemoteContentAudioEmbed url={url} /> <RemoteContentWrapper {...wrapperProps}>
</RemoteContentWrapper> <RemoteContentAudioEmbed url={url} />
</RemoteContentWrapper>
</Box>
); );
} else if (isVideo && remoteContentPolicy.videoShown) { } else if (isVideo && remoteContentPolicy.videoShown) {
return ( return (
<RemoteContentWrapper <Box mt={1} mb={2} flexShrink={0}>
{...wrapperProps} <RemoteContentWrapper
detail={<RemoteContentVideoEmbed url={url} />} {...wrapperProps}
> detail={<RemoteContentVideoEmbed url={url} />}
<TruncatedText>{url}</TruncatedText> >
</RemoteContentWrapper> <TruncatedText>{url}</TruncatedText>
</RemoteContentWrapper>
</Box>
); );
} else if (isOembed && remoteContentPolicy.oembedShown) { } else if (isOembed && remoteContentPolicy.oembedShown) {
return ( return (
<AsyncFallback fallback={fallback}> <Box mt={1} mb={2} flexShrink={0}>
<RemoteContentOembed ref={embedRef} url={url} renderUrl={renderUrl} /> <AsyncFallback fallback={fallback}>
</AsyncFallback> <RemoteContentOembed ref={embedRef} url={url} renderUrl={renderUrl} oembed={oembed} />
</AsyncFallback>
</Box>
); );
} }
return fallback; return fallback;

View File

@ -74,7 +74,7 @@ export function GraphPermissions(props: GraphPermissionsProps) {
const writers = _.get( const writers = _.get(
group?.tags, group?.tags,
['graph', association.resource, 'writers'], ['graph', association.resource, 'writers'],
new Set() []
); );
let [, , hostShip] = association.resource.split('/'); let [, , hostShip] = association.resource.split('/');
@ -91,7 +91,7 @@ export function GraphPermissions(props: GraphPermissionsProps) {
const initialValues = { const initialValues = {
writePerms, writePerms,
writers: Array.from(writers) writers: writers
.filter(x => x !== hostShip), .filter(x => x !== hostShip),
readerComments: association.metadata.vip === 'reader-comments' readerComments: association.metadata.vip === 'reader-comments'
}; };
@ -104,7 +104,7 @@ export function GraphPermissions(props: GraphPermissionsProps) {
resource: association.resource, resource: association.resource,
tag: 'writers' tag: 'writers'
}; };
const allWriters = Array.from(writers).map(w => `~${w}`); const allWriters = writers.map(w => `~${w}`);
if (values.readerComments !== readerComments) { if (values.readerComments !== readerComments) {
await airlock.poke(metadataEdit(association, { await airlock.poke(metadataEdit(association, {
vip: values.readerComments ? 'reader-comments' : '' vip: values.readerComments ? 'reader-comments' : ''
@ -170,7 +170,7 @@ export function GraphPermissions(props: GraphPermissionsProps) {
<Col> <Col>
<Label mb={2}>Permissions Summary</Label> <Label mb={2}>Permissions Summary</Label>
<PermissionsSummary <PermissionsSummary
writersSize={writers.size} writersSize={writers.length}
vip={association.metadata.vip} vip={association.metadata.vip}
/> />
</Col> </Col>

View File

@ -147,13 +147,14 @@ const contentToMdAst = (tall: boolean) => (
]; ];
} else if ('url' in content) { } else if ('url' in content) {
return [ return [
'block', 'inline',
{ {
type: 'root', type: 'root',
children: [ children: [
{ {
type: 'graph-url', type: 'link',
url: content.url url: content.url,
children: [{ type: 'text', value: content.url }]
} }
] ]
} }
@ -186,8 +187,20 @@ function stitchInline(a: any, b: any) {
if (!a?.children) { if (!a?.children) {
throw new Error('Bad stitchInline call: missing root'); throw new Error('Bad stitchInline call: missing root');
} }
const lastParaIdx = a.children.length - 1; const lastParaIdx = a.children.length - 1;
const last = a.children[lastParaIdx]; const last = a.children[lastParaIdx];
// wrap bare link in list-item inside a p node
// for better typography consistency
if (last?.type === 'listItem') {
if (last?.children.length === 0) {
last.children.push({
type: 'paragraph',
children: []
});
}
}
if (last?.children) { if (last?.children) {
const ros = { const ros = {
...a, ...a,
@ -217,7 +230,7 @@ function getChildren<T extends unknown>(node: T): AstContent[] {
} }
export function asParent<T extends BlockContent>(node: T): Parent | undefined { export function asParent<T extends BlockContent>(node: T): Parent | undefined {
return ['paragraph', 'heading', 'list', 'listItem', 'table'].includes( return ['paragraph', 'heading', 'list', 'listItem', 'table', 'blockquote'].includes(
node.type node.type
) )
? (node as Parent) ? (node as Parent)
@ -241,6 +254,7 @@ function stitchMerge(a: Root, b: Root) {
children: [...aChildren.slice(0, -1), mergedPara, ...bChildren.slice(1)] children: [...aChildren.slice(0, -1), mergedPara, ...bChildren.slice(1)]
}; };
} }
return { ...a, children: [...aChildren, ...bChildren] }; return { ...a, children: [...aChildren, ...bChildren] };
} }
@ -256,10 +270,10 @@ function stitchInlineAfterBlock(a: Root, b: GraphMentionNode[]) {
} }
function stitchAsts(asts: [StitchMode, GraphAstNode][]) { function stitchAsts(asts: [StitchMode, GraphAstNode][]) {
return _.reduce( const t = _.reduce(
asts, asts,
([prevMode, ast], [mode, val]): [StitchMode, GraphAstNode] => { ([prevMode, ast], [mode, val]): [StitchMode, GraphAstNode] => {
if (prevMode === 'block') { if (prevMode === 'block' || prevMode === 'inline') {
if (mode === 'inline') { if (mode === 'inline') {
return [mode, stitchInlineAfterBlock(ast, val?.children ?? [])]; return [mode, stitchInlineAfterBlock(ast, val?.children ?? [])];
} }
@ -283,6 +297,56 @@ function stitchAsts(asts: [StitchMode, GraphAstNode][]) {
}, },
['block', { type: 'root', children: [] }] as [StitchMode, GraphAstNode] ['block', { type: 'root', children: [] }] as [StitchMode, GraphAstNode]
); );
t[1].children.map((c, idx) => {
if (c.type === 'blockquote' && t[1].children[idx +1] !== undefined && t[1].children[idx +1].type === 'paragraph') {
const next = idx !== t[1].children.length -1
? t[1].children.splice(idx +1, 1)
: [];
if (next.length > 0) {
t[1].children[idx].children.push(next[0]);
}
}
const links = [];
function addRichEmbedURL(nodes) {
if (nodes?.children) {
nodes.children.filter((k) => {
if (k.type === 'link') {
links.push({
type: 'root',
children: [
{
type: 'graph-url',
url: k.url
}
]
});
} else if (k?.children) {
k.children.filter((o) => {
if (o.type === 'link') {
links.push({
type: 'root',
children: [
{
type: 'graph-url',
url: o.url
}
]
});
}
});
}
});
nodes.children.push(...links);
}
}
addRichEmbedURL(c);
});
return t;
} }
const header = ({ children, depth, ...rest }) => { const header = ({ children, depth, ...rest }) => {
const level = depth; const level = depth;
@ -408,7 +472,7 @@ const renderers = {
); );
}, },
list: ({ depth, ordered, children }) => { list: ({ depth, ordered, children }) => {
return ordered ? <Ol>{children}</Ol> : <Ul>{children}</Ul>; return ordered ? <Ol fontSize="1">{children}</Ol> : <Ul fontSize="1">{children}</Ul>;
}, },
'graph-mention': (obj) => { 'graph-mention': (obj) => {
return <Mention ship={obj.ship} emphasis={obj.emphasis} />; return <Mention ship={obj.ship} emphasis={obj.emphasis} />;
@ -419,9 +483,7 @@ const renderers = {
</Box> </Box>
), ),
'graph-url': ({ url, tall }) => ( 'graph-url': ({ url, tall }) => (
<Box mt={1} mb={2} flexShrink={0}> <RemoteContent key={url} url={url} tall={tall} />
<RemoteContent key={url} url={url} tall={tall} />
</Box>
), ),
'graph-reference': ({ reference, transcluded }) => { 'graph-reference': ({ reference, transcluded }) => {
const { link } = referenceToPermalink({ reference }); const { link } = referenceToPermalink({ reference });

View File

@ -1,128 +1,126 @@
/* eslint-disable */ /* eslint-disable */
/** pulled from remark-parse /** pulled from remark-parse
* *
* critical change is that blockquotes require a newline to be continued, see * critical change is that blockquotes require a newline to be continued, see
* the `if(!prefixed) conditional * the `if(!prefixed) conditional
*/ */
'use strict' "use strict";
var trim = require('trim') var trim = require("trim");
var interrupt = require('remark-parse/lib/util/interrupt')
module.exports = blockquote module.exports = blockquote;
var lineFeed = '\n' var lineFeed = "\n";
var tab = '\t' var tab = "\t";
var space = ' ' var space = " ";
var greaterThan = '>' var greaterThan = ">";
function blockquote(eat, value, silent) { function blockquote(eat, value, silent) {
var self = this var self = this;
var offsets = self.offset var offsets = self.offset;
var tokenizers = self.blockTokenizers var tokenizers = self.blockTokenizers;
var interruptors = self.interruptBlockquote var interruptors = self.interruptBlockquote;
var now = eat.now() var now = eat.now();
var currentLine = now.line var currentLine = now.line;
var length = value.length var length = value.length;
var values = [] var values = [];
var contents = [] var contents = [];
var indents = [] var indents = [];
var add var add;
var index = 0 var index = 0;
var character var character;
var rest var rest;
var nextIndex var nextIndex;
var content var content;
var line var line;
var startIndex var startIndex;
var prefixed var prefixed;
var exit var exit;
while (index < length) { while (index < length) {
character = value.charAt(index) character = value.charAt(index);
if (character !== space && character !== tab) { if (character !== space && character !== tab) {
break break;
} }
index++ index++;
} }
if (value.charAt(index) !== greaterThan) { if (value.charAt(index) !== greaterThan) {
return return;
} }
if (silent) { if (silent) {
return true return true;
} }
index = 0 index = 0;
while (index < length) { while (index < length) {
nextIndex = value.indexOf(lineFeed, index) nextIndex = value.indexOf(lineFeed, index);
startIndex = index startIndex = index;
prefixed = false prefixed = false;
if (nextIndex === -1) { if (nextIndex === -1) {
nextIndex = length nextIndex = length;
} }
while (index < length) { while (index < length) {
character = value.charAt(index) character = value.charAt(index);
if (character !== space && character !== tab) { if (character !== space && character !== tab) {
break break;
} }
index++ index++;
} }
if (value.charAt(index) === greaterThan) { if (value.charAt(index) === greaterThan) {
index++ index++;
prefixed = true prefixed = true;
if (value.charAt(index) === space) { if (value.charAt(index) === space) {
index++ index++;
} }
} else { } else {
index = startIndex index = startIndex;
} }
content = value.slice(index, nextIndex) content = value.slice(index, nextIndex);
if (!prefixed && !trim(content)) { if (!prefixed && !trim(content)) {
index = startIndex index = startIndex;
break break;
} }
if (!prefixed) { if (!prefixed) {
break; break;
} }
line = startIndex === index ? content : value.slice(startIndex, nextIndex) line = startIndex === index ? content : value.slice(startIndex, nextIndex);
indents.push(index - startIndex) indents.push(index - startIndex);
values.push(line) values.push(line);
contents.push(content) contents.push(content);
index = nextIndex + 1 index = nextIndex + 1;
} }
const trailingNewline = value.charAt(nextIndex) === '\n'; const trailingNewline = value.charAt(nextIndex) === "\n";
index = -1 index = -1;
length = indents.length length = indents.length;
add = eat(values.join(lineFeed)) add = eat(values.join(lineFeed));
while (++index < length) { while (++index < length) {
offsets[currentLine] = (offsets[currentLine] || 0) + indents[index] offsets[currentLine] = (offsets[currentLine] || 0) + indents[index];
currentLine++ currentLine++;
} }
exit = self.enterBlock() exit = self.enterBlock();
contents = self.tokenizeBlock(contents.join(lineFeed), now) contents = self.tokenizeBlock(contents.join(lineFeed), now);
console.log(values); exit();
exit()
const added = add({type: 'blockquote', children: contents}) const added = add({ type: "blockquote", children: contents });
return trailingNewline ? add({ type: 'paragraph', children: [] }) : added; return trailingNewline ? add({ type: "paragraph", children: [] }) : added;
} }

View File

@ -8,7 +8,7 @@ import {
} from 'react-router-dom'; } from 'react-router-dom';
import { useShortcut } from '~/logic/state/settings'; import { useShortcut } from '~/logic/state/settings';
import { useLocalStorageState } from '~/logic/lib/useLocalStorageState'; import { useLocalStorageState } from '~/logic/lib/useLocalStorageState';
import { getGroupFromWorkspace } from '~/logic/lib/workspace'; import { getGroupFromWorkspace, getTitleFromWorkspace } from '~/logic/lib/workspace';
import useGroupState from '~/logic/state/group'; import useGroupState from '~/logic/state/group';
import useHarkState from '~/logic/state/hark'; import useHarkState from '~/logic/state/hark';
import useMetadataState from '~/logic/state/metadata'; import useMetadataState from '~/logic/state/metadata';
@ -22,7 +22,7 @@ import { Skeleton } from './Skeleton';
import { EmptyGroupHome } from './Home/EmptyGroupHome'; import { EmptyGroupHome } from './Home/EmptyGroupHome';
import { Join } from './Join/Join'; import { Join } from './Join/Join';
import { Resource } from './Resource'; import { Resource } from './Resource';
import { DmResource } from '~/views/apps/chat/DmResource'; import { DmResource, DmHelmet } from '~/views/apps/chat/DmResource';
import { UnjoinedResource } from '~/views/components/UnjoinedResource'; import { UnjoinedResource } from '~/views/components/UnjoinedResource';
import { NewChannel } from './NewChannel'; import { NewChannel } from './NewChannel';
import { GroupHome } from './Home/GroupHome'; import { GroupHome } from './Home/GroupHome';
@ -126,16 +126,18 @@ export function GroupsPane(props: GroupsPaneProps) {
const { ship } = match.params as Record<string, string>; const { ship } = match.params as Record<string, string>;
return ( return (
<Skeleton <>
mobileHide <DmHelmet ship={ship} />
recentGroups={recentGroups} <Skeleton
selected={ship} mobileHide
{...props} recentGroups={recentGroups}
baseUrl={match.path} selected={ship}
> <DmResource ship={ship} /> {...props}
baseUrl={match.path}
</Skeleton> > <DmResource ship={ship} />
</Skeleton>
</>
); );
}} }}
/> />
@ -180,7 +182,7 @@ export function GroupsPane(props: GroupsPaneProps) {
const appPath = `/ship/${host}/${name}`; const appPath = `/ship/${host}/${name}`;
const association = associations.graph[appPath]; const association = associations.graph[appPath];
const resourceUrl = `${baseUrl}/join/${app}${appPath}`; const resourceUrl = `${baseUrl}/join/${app}${appPath}`;
let title = groupAssociation?.metadata?.title ?? 'Groups'; let title = getTitleFromWorkspace(associations, workspace);
if (!association) { if (!association) {
return <Loading />; return <Loading />;
@ -252,7 +254,7 @@ export function GroupsPane(props: GroupsPaneProps) {
render={(routeProps) => { render={(routeProps) => {
const shouldHideSidebar = const shouldHideSidebar =
routeProps.location.pathname.includes('/feed'); routeProps.location.pathname.includes('/feed');
const title = groupAssociation?.metadata?.title ?? 'Groups'; const title = getTitleFromWorkspace(associations, workspace);
return ( return (
<> <>
<Helmet defer={false}> <Helmet defer={false}>

View File

@ -352,7 +352,7 @@ function Participant(props: {
</Link> </Link>
</Action> </Action>
<Action bg="transparent"> <Action bg="transparent">
<Link to={`/~landscape/dm/${contact.patp}`}> <Link to={`/~landscape/messages/dm/~${contact.patp}`}>
<Text color="green">Send Message</Text> <Text color="green">Send Message</Text>
</Link> </Link>
</Action> </Action>

View File

@ -190,6 +190,11 @@
|% |%
++ pull-action pull-hook-action+!>([%add ship rid]) ++ pull-action pull-hook-action+!>([%add ship rid])
:: ::
++ listen-hark
|= gr=resource
%+ poke-our:pass:io %hark-graph-hook
hark-graph-hook-action+!>([%listen gr /])
::
++ watch-md (watch-our:(jn-pass-io /md) %metadata-store /updates) ++ watch-md (watch-our:(jn-pass-io /md) %metadata-store /updates)
++ watch-groups (watch-our:(jn-pass-io /groups) %group-store /groups) ++ watch-groups (watch-our:(jn-pass-io /groups) %group-store /groups)
++ watch-md-nacks (watch-our:(jn-pass-io /md-nacks) %metadata-pull-hook /nack) ++ watch-md-nacks (watch-our:(jn-pass-io /md-nacks) %metadata-pull-hook /nack)
@ -436,6 +441,9 @@
=? jn-core |(hidden autojoin.request) =? jn-core |(hidden autojoin.request)
%- emit-many %- emit-many
(turn graphs pull-gra:pass) (turn graphs pull-gra:pass)
=? jn-core hidden
%- emit-many
(turn graphs listen-hark:pass)
jn-core jn-core
:: ::
++ feed-rid ++ feed-rid

View File

@ -1,7 +1,7 @@
:~ title+'Groups' :~ title+'Groups'
info+'A suite of applications to communicate on Urbit' info+'A suite of applications to communicate on Urbit'
color+0xee.5432 color+0xee.5432
glob-http+['https://bootstrap.urbit.org/glob-0v7.bmftr.90ktq.cma0h.da190.bs8b1.glob' 0v7.bmftr.90ktq.cma0h.da190.bs8b1] glob-http+['https://bootstrap.urbit.org/glob-0v4.2se6m.fvv67.nn5e8.vfrv9.mmi88.glob' 0v4.2se6m.fvv67.nn5e8.vfrv9.mmi88]
base+'landscape' base+'landscape'
version+[1 0 11] version+[1 0 11]

View File

@ -35,6 +35,8 @@
(poke-our %group-store group-update-0+!>([%add-members rid (sy our.bowl ~)])) (poke-our %group-store group-update-0+!>([%add-members rid (sy our.bowl ~)]))
;< ~ bind:m ;< ~ bind:m
(poke-our %group-push-hook push-hook-act) (poke-our %group-push-hook push-hook-act)
;< ~ bind:m
(poke-our %hark-graph-hook hark-graph-hook-action+!>([%listen rid /]))
(pure:m rid) (pure:m rid)
-- --
:: ::

View File

@ -38,4 +38,6 @@
(raw-poke-our %contact-pull-hook pull-hook-act) (raw-poke-our %contact-pull-hook pull-hook-act)
;< ~ bind:m ;< ~ bind:m
(raw-poke-our %group-store remove) (raw-poke-our %group-store remove)
;< ~ bind:m
(raw-poke-our %group-view group-view-action+!>([%done rid]))
(pure:m !>(~)) (pure:m !>(~))

View File

@ -134,6 +134,16 @@ int err_win_to_posix(DWORD winerr)
return error; return error;
} }
int link(const char *path1, const char *path2)
{
if ( CreateHardLinkA(path2, path1, NULL) ) {
return 0;
}
errno = err_win_to_posix(GetLastError());
return -1;
}
// from msys2 mingw-packages-dev patches // from msys2 mingw-packages-dev patches
// ----------------------------------------------------------------------- // -----------------------------------------------------------------------

View File

@ -3,6 +3,7 @@
#define mkdir(A, B) mkdir(A) #define mkdir(A, B) mkdir(A)
int link(const char *path1, const char *path2);
char *realpath(const char *path, char *resolved_path); char *realpath(const char *path, char *resolved_path);
int fdatasync(int fd); int fdatasync(int fd);
int utimes(const char *path, const struct timeval times[2]); int utimes(const char *path, const struct timeval times[2]);

1
pkg/urbit/configure vendored
View File

@ -31,6 +31,7 @@ defmacro () {
} }
defmacro URBIT_VERSION "\"$URBIT_VERSION\"" defmacro URBIT_VERSION "\"$URBIT_VERSION\""
defmacro U3_VERE_PACE "\"${VERE_PACE:-once}\""
opt_debug= opt_debug=
opt_static= opt_static=

View File

@ -18,8 +18,10 @@
#include <curl/curl.h> #include <curl/curl.h>
#include <vere/db/lmdb.h> #include <vere/db/lmdb.h>
#include <getopt.h> #include <getopt.h>
#include <libgen.h>
#include "ca-bundle.h" #include "ca-bundle.h"
#include "whereami.h"
// serf module state // serf module state
// //
@ -36,6 +38,31 @@ static u3_cue_xeno* sil_u; // cue handle
STATIC_ASSERT(( 0 == CHAR_MIN && UCHAR_MAX == CHAR_MAX ), STATIC_ASSERT(( 0 == CHAR_MIN && UCHAR_MAX == CHAR_MAX ),
"unsigned char required"); "unsigned char required");
/* _main_self_path(): get binary self-path.
*/
static void
_main_self_path(void)
{
c3_c* pat_c;
c3_i len_i, pat_i;
if ( 0 < (len_i = wai_getExecutablePath(NULL, 0, &pat_i)) ) {
pat_c = c3_malloc( 1 + len_i );
wai_getExecutablePath(pat_c, len_i, &pat_i);
pat_c[len_i] = 0;
u3_Host.dem_c = pat_c;
}
else {
fprintf(stderr, "unable to get binary self path\r\n");
exit(1);
// XX continue?
//
// u3_Host.dem_c = strdup(bin_c);
}
}
/* _main_readw(): parse a word from a string. /* _main_readw(): parse a word from a string.
*/ */
static u3_noun static u3_noun
@ -105,13 +132,13 @@ _main_repath(c3_c* pax_c)
return rel_c; return rel_c;
} }
/* _main_getopt(): extract option map from command line. /* _main_init(): initialize globals
*/ */
static u3_noun static void
_main_getopt(c3_i argc, c3_c** argv) _main_init(void)
{ {
c3_i ch_i, lid_i; u3_Host.nex_o = c3n;
c3_w arg_w; u3_Host.pep_o = c3n;
u3_Host.ops_u.abo = c3n; u3_Host.ops_u.abo = c3n;
u3_Host.ops_u.dem = c3n; u3_Host.ops_u.dem = c3n;
@ -137,6 +164,37 @@ _main_getopt(c3_i argc, c3_c** argv)
u3_Host.ops_u.puf_c = "jam"; u3_Host.ops_u.puf_c = "jam";
u3_Host.ops_u.hap_w = 50000; u3_Host.ops_u.hap_w = 50000;
u3_Host.ops_u.kno_w = DefaultKernel; u3_Host.ops_u.kno_w = DefaultKernel;
}
/* _main_pier_run(): get pier from binary path (argv[0]), if appropriate
*/
static c3_c*
_main_pier_run(c3_c* bin_c)
{
c3_c* dir_c = 0;
c3_w bin_w = strlen(bin_c);
c3_w len_w = strlen(U3_BIN_ALIAS);
// no args, argv[0] == $pier/.run
//
if ( (len_w <= bin_w)
&& (0 == strcmp(bin_c + (bin_w - len_w), U3_BIN_ALIAS)) )
{
bin_c = strdup(bin_c); // dirname can modify
dir_c = _main_repath(dirname(bin_c));
c3_free(bin_c);
}
return dir_c;
}
/* _main_getopt(): extract option map from command line.
*/
static u3_noun
_main_getopt(c3_i argc, c3_c** argv)
{
c3_i ch_i, lid_i;
c3_w arg_w;
static struct option lop_u[] = { static struct option lop_u[] = {
{ "arvo", required_argument, NULL, 'A' }, { "arvo", required_argument, NULL, 'A' },
@ -166,6 +224,7 @@ _main_getopt(c3_i argc, c3_c** argv)
{ "http-port", required_argument, NULL, c3__http }, { "http-port", required_argument, NULL, c3__http },
{ "https-port", required_argument, NULL, c3__htls }, { "https-port", required_argument, NULL, c3__htls },
{ "no-conn", no_argument, NULL, c3__noco }, { "no-conn", no_argument, NULL, c3__noco },
{ "no-dock", no_argument, NULL, c3__nodo },
{ "quiet", no_argument, NULL, 'q' }, { "quiet", no_argument, NULL, 'q' },
{ "versions", no_argument, NULL, 'R' }, { "versions", no_argument, NULL, 'R' },
{ "replay-from", required_argument, NULL, 'r' }, { "replay-from", required_argument, NULL, 'r' },
@ -291,6 +350,10 @@ _main_getopt(c3_i argc, c3_c** argv)
u3_Host.ops_u.con = c3n; u3_Host.ops_u.con = c3n;
break; break;
} }
case c3__nodo: {
u3_Host.ops_u.doc = c3n;
break;
}
case 'R': { case 'R': {
u3_Host.ops_u.rep = c3y; u3_Host.ops_u.rep = c3y;
return c3y; return c3y;
@ -346,8 +409,9 @@ _main_getopt(c3_i argc, c3_c** argv)
if ( u3_Host.ops_u.who_c != 0 ) { if ( u3_Host.ops_u.who_c != 0 ) {
u3_Host.dir_c = strdup(1 + u3_Host.ops_u.who_c); u3_Host.dir_c = strdup(1 + u3_Host.ops_u.who_c);
} }
else { // no trailing positional arg, argv[0] != $pier/.run, invalid command
// XX not sure how this might be reachable //
else if ( !(u3_Host.dir_c = _main_pier_run(argv[0])) ) {
return c3n; return c3n;
} }
} }
@ -530,23 +594,37 @@ _setup_ssl_curl(void* arg)
/* _cw_usage(): print utility usage. /* _cw_usage(): print utility usage.
*/ */
static void static void
_cw_usage(c3_c* s) _cw_usage(c3_c* bin_c)
{ {
fprintf(stderr, c3_c *use_c[] = {
"\nutilities:\n" "utilities:\n",
" %s cram <pier> jam state:\n" " %s cram %.*s jam state:\n",
" %s grab <pier> measure memory usage:\n" " %s dock %.*s copy binary:\n",
" %s info <pier> print pier info:\n" " %s grab %.*s measure memory usage:\n",
" %s meld <pier> deduplicate snapshot:\n" " %s info %.*s print pier info:\n",
" %s pack <pier> defragment snapshot:\n" " %s meld %.*s deduplicate snapshot:\n",
" %s queu <pier> <at-event> cue state:\n" " %s pack %.*s defragment snapshot:\n",
"\n run as a 'serf':\n" " %s prep %.*s prepare for upgrade:\n",
" %s next %.*s request upgrade:\n",
" %s queu %.*s<at-event> cue state:\n",
" %s vere ARGS <output dir> download binary:\n",
"\n run as a 'serf':\n",
" %s serf <pier> <key> <flags> <cache-size> <at-event>" " %s serf <pier> <key> <flags> <cache-size> <at-event>"
#ifdef U3_OS_mingw #ifdef U3_OS_mingw
" <ctrlc-handle>" " <ctrlc-handle>"
#endif #endif
"\n", "\n",
s, s, s, s, s, s, s); 0
};
c3_c* d = _main_pier_run(bin_c);
c3_i i;
for ( i=0; use_c[i]; i++ ) {
fprintf(stderr, use_c[i], bin_c, d ? 0 : 7, "<pier> ");
}
c3_free(d);
} }
/* u3_ve_usage(): print usage and exit. /* u3_ve_usage(): print usage and exit.
@ -912,7 +990,7 @@ _cw_intr_win(c3_c* han_c)
} }
#endif #endif
/* _cw_serf_commence(); initialize and run serf /* _cw_serf_commence(): initialize and run serf
*/ */
static void static void
_cw_serf_commence(c3_i argc, c3_c* argv[]) _cw_serf_commence(c3_i argc, c3_c* argv[])
@ -1029,7 +1107,7 @@ _cw_serf_commence(c3_i argc, c3_c* argv[])
u3m_stop(); u3m_stop();
} }
/* _cw_disk_init(); open event log /* _cw_disk_init(): open event log
*/ */
static u3_disk* static u3_disk*
_cw_disk_init(c3_c* dir_c) _cw_disk_init(c3_c* dir_c)
@ -1045,18 +1123,62 @@ _cw_disk_init(c3_c* dir_c)
return log_u; return log_u;
} }
/* _cw_info(); print pier info /* _cw_dock(): copy binary into pier
*/
static void
_cw_dock(c3_i argc, c3_c* argv[])
{
switch ( argc ) {
case 2: {
if ( !(u3_Host.dir_c = _main_pier_run(argv[0])) ) {
fprintf(stderr, "unable to find pier\r\n");
exit (1);
}
} break;
case 3: {
u3_Host.dir_c = argv[2];
} break;
default: {
fprintf(stderr, "invalid command\r\n");
exit(1);
} break;
}
_main_self_path();
u3_king_dock(U3_VERE_PACE);
}
/* _cw_info(): print pier info
*/ */
static void static void
_cw_info(c3_i argc, c3_c* argv[]) _cw_info(c3_i argc, c3_c* argv[])
{ {
c3_assert( 3 <= argc ); switch ( argc ) {
case 2: {
if ( !(u3_Host.dir_c = _main_pier_run(argv[0])) ) {
fprintf(stderr, "unable to find pier\r\n");
exit (1);
}
} break;
c3_c* dir_c = argv[2]; case 3: {
c3_d eve_d = u3m_boot(dir_c); u3_Host.dir_c = argv[2];
u3_disk* log_u = _cw_disk_init(dir_c); } break;
fprintf(stderr, "\r\nurbit: %s at event %" PRIu64 "\r\n", dir_c, eve_d); default: {
fprintf(stderr, "invalid command\r\n");
exit(1);
} break;
}
c3_d eve_d = u3m_boot(u3_Host.dir_c);
u3_disk* log_u = _cw_disk_init(u3_Host.dir_c);
fprintf(stderr, "\r\nurbit: %s at event %" PRIu64 "\r\n",
u3_Host.dir_c, eve_d);
u3_disk_slog(log_u); u3_disk_slog(log_u);
printf("\n"); printf("\n");
@ -1066,35 +1188,65 @@ _cw_info(c3_i argc, c3_c* argv[])
u3m_stop(); u3m_stop();
} }
/* _cw_grab(); gc pier. /* _cw_grab(): gc pier.
*/ */
static void static void
_cw_grab(c3_i argc, c3_c* argv[]) _cw_grab(c3_i argc, c3_c* argv[])
{ {
c3_assert( 3 <= argc ); switch ( argc ) {
case 2: {
if ( !(u3_Host.dir_c = _main_pier_run(argv[0])) ) {
fprintf(stderr, "unable to find pier\r\n");
exit (1);
}
} break;
c3_c* dir_c = argv[2]; case 3: {
u3m_boot(dir_c); u3_Host.dir_c = argv[2];
} break;
default: {
fprintf(stderr, "invalid command\r\n");
exit(1);
} break;
}
u3m_boot(u3_Host.dir_c);
u3C.wag_w |= u3o_hashless; u3C.wag_w |= u3o_hashless;
u3_serf_grab(); u3_serf_grab();
u3m_stop(); u3m_stop();
} }
/* _cw_cram(); jam persistent state (rock), and exit. /* _cw_cram(): jam persistent state (rock), and exit.
*/ */
static void static void
_cw_cram(c3_i argc, c3_c* argv[]) _cw_cram(c3_i argc, c3_c* argv[])
{ {
c3_assert( 3 <= argc ); switch ( argc ) {
case 2: {
if ( !(u3_Host.dir_c = _main_pier_run(argv[0])) ) {
fprintf(stderr, "unable to find pier\r\n");
exit (1);
}
} break;
c3_c* dir_c = argv[2]; case 3: {
c3_d eve_d = u3m_boot(dir_c); u3_Host.dir_c = argv[2];
u3_disk* log_u = _cw_disk_init(dir_c); // XX s/b try_aquire lock } break;
default: {
fprintf(stderr, "invalid command\r\n");
exit(1);
} break;
}
c3_d eve_d = u3m_boot(u3_Host.dir_c);
u3_disk* log_u = _cw_disk_init(u3_Host.dir_c); // XX s/b try_aquire lock
c3_o ret_o; c3_o ret_o;
fprintf(stderr, "urbit: cram: preparing\r\n"); fprintf(stderr, "urbit: cram: preparing\r\n");
if ( c3n == (ret_o = u3u_cram(dir_c, eve_d)) ) { if ( c3n == (ret_o = u3u_cram(u3_Host.dir_c, eve_d)) ) {
fprintf(stderr, "urbit: cram: unable to jam state\r\n"); fprintf(stderr, "urbit: cram: unable to jam state\r\n");
} }
else { else {
@ -1113,33 +1265,50 @@ _cw_cram(c3_i argc, c3_c* argv[])
u3m_stop(); u3m_stop();
} }
/* _cw_queu(); cue rock, save, and exit. /* _cw_queu(): cue rock, save, and exit.
*/ */
static void static void
_cw_queu(c3_i argc, c3_c* argv[]) _cw_queu(c3_i argc, c3_c* argv[])
{ {
c3_assert( 4 <= argc ); c3_c* eve_c;
c3_c* dir_c = argv[2];
c3_c* eve_c = argv[3];
c3_d eve_d; c3_d eve_d;
switch ( argc ) {
case 3: {
if ( !(u3_Host.dir_c = _main_pier_run(argv[0])) ) {
fprintf(stderr, "unable to find pier\r\n");
exit (1);
}
eve_c = argv[2];
} break;
case 4: {
u3_Host.dir_c = argv[2];
eve_c = argv[3];
} break;
default: {
fprintf(stderr, "invalid command\r\n");
exit(1);
} break;
}
if ( 1 != sscanf(eve_c, "%" PRIu64 "", &eve_d) ) { if ( 1 != sscanf(eve_c, "%" PRIu64 "", &eve_d) ) {
fprintf(stderr, "urbit: queu: invalid number '%s'\r\n", eve_c); fprintf(stderr, "urbit: queu: invalid number '%s'\r\n", eve_c);
exit(1); exit(1);
} }
else { else {
u3_disk* log_u = _cw_disk_init(dir_c); // XX s/b try_aquire lock u3_disk* log_u = _cw_disk_init(u3_Host.dir_c); // XX s/b try_aquire lock
fprintf(stderr, "urbit: queu: preparing\r\n"); fprintf(stderr, "urbit: queu: preparing\r\n");
u3m_boot(dir_c); u3m_boot(u3_Host.dir_c);
// XX can spuriously fail do to corrupt memory-image checkpoint, // XX can spuriously fail do to corrupt memory-image checkpoint,
// need a u3m_half_boot equivalent // need a u3m_half_boot equivalent
// workaround is to delete/move the checkpoint in case of corruption // workaround is to delete/move the checkpoint in case of corruption
// //
if ( c3n == u3u_uncram(dir_c, eve_d) ) { if ( c3n == u3u_uncram(u3_Host.dir_c, eve_d) ) {
fprintf(stderr, "urbit: queu: failed\r\n"); fprintf(stderr, "urbit: queu: failed\r\n");
exit(1); exit(1);
} }
@ -1152,19 +1321,34 @@ _cw_queu(c3_i argc, c3_c* argv[])
} }
} }
/* _cw_uniq(); deduplicate persistent nouns /* _cw_uniq(): deduplicate persistent nouns
*/ */
static void static void
_cw_meld(c3_i argc, c3_c* argv[]) _cw_meld(c3_i argc, c3_c* argv[])
{ {
c3_assert( 3 <= argc ); switch ( argc ) {
case 2: {
if ( !(u3_Host.dir_c = _main_pier_run(argv[0])) ) {
fprintf(stderr, "unable to find pier\r\n");
exit (1);
}
} break;
c3_c* dir_c = argv[2]; case 3: {
u3_disk* log_u = _cw_disk_init(dir_c); // XX s/b try_aquire lock u3_Host.dir_c = argv[2];
} break;
default: {
fprintf(stderr, "invalid command\r\n");
exit(1);
} break;
}
u3_disk* log_u = _cw_disk_init(u3_Host.dir_c); // XX s/b try_aquire lock
c3_w pre_w; c3_w pre_w;
u3C.wag_w |= u3o_hashless; u3C.wag_w |= u3o_hashless;
u3m_boot(dir_c); u3m_boot(u3_Host.dir_c);
pre_w = u3a_open(u3R); pre_w = u3a_open(u3R);
u3u_meld(); u3u_meld();
@ -1175,17 +1359,83 @@ _cw_meld(c3_i argc, c3_c* argv[])
u3m_stop(); u3m_stop();
} }
/* _cw_pack(); compact memory, save, and exit. /* _cw_next(): request upgrade
*/
static void
_cw_next(c3_i argc, c3_c* argv[])
{
c3_i ch_i, lid_i;
c3_w arg_w;
static struct option lop_u[] = {
{ "arch", required_argument, NULL, 'a' },
{ NULL, 0, NULL, 0 }
};
u3_Host.dir_c = _main_pier_run(argv[0]);
while ( -1 != (ch_i=getopt_long(argc, argv, "a:", lop_u, &lid_i)) ) {
switch ( ch_i ) {
case 'a': {
u3_Host.arc_c = strdup(optarg);
} break;
case '?': {
exit(1);
} break;
}
}
// argv[optind] is always "next"
//
if ( !u3_Host.dir_c ) {
if ( optind + 1 < argc ) {
u3_Host.dir_c = argv[optind + 1];
}
else {
fprintf(stderr, "invalid command, pier required\r\n");
exit(1);
}
optind++;
}
if ( optind + 1 != argc ) {
fprintf(stderr, "invalid command\r\n");
exit(1);
}
u3_Host.pep_o = c3y;
u3_Host.nex_o = c3y;
}
/* _cw_pack(): compact memory, save, and exit.
*/ */
static void static void
_cw_pack(c3_i argc, c3_c* argv[]) _cw_pack(c3_i argc, c3_c* argv[])
{ {
c3_assert( 3 <= argc ); switch ( argc ) {
case 2: {
if ( !(u3_Host.dir_c = _main_pier_run(argv[0])) ) {
fprintf(stderr, "unable to find pier\r\n");
exit (1);
}
} break;
c3_c* dir_c = argv[2]; case 3: {
u3_disk* log_u = _cw_disk_init(dir_c); // XX s/b try_aquire lock u3_Host.dir_c = argv[2];
} break;
u3m_boot(dir_c); default: {
fprintf(stderr, "invalid command\r\n");
exit(1);
} break;
}
u3_disk* log_u = _cw_disk_init(u3_Host.dir_c); // XX s/b try_aquire lock
u3m_boot(u3_Host.dir_c);
u3a_print_memory(stderr, "urbit: pack: gained", u3m_pack()); u3a_print_memory(stderr, "urbit: pack: gained", u3m_pack());
u3e_save(); u3e_save();
@ -1193,6 +1443,145 @@ _cw_pack(c3_i argc, c3_c* argv[])
u3m_stop(); u3m_stop();
} }
/* _cw_prep(): prepare for upgrade
*/
static void
_cw_prep(c3_i argc, c3_c* argv[])
{
switch ( argc ) {
case 2: {
if ( !(u3_Host.dir_c = _main_pier_run(argv[0])) ) {
fprintf(stderr, "unable to find pier\r\n");
exit (1);
}
} break;
case 3: {
u3_Host.dir_c = argv[2];
} break;
default: {
fprintf(stderr, "invalid command\r\n");
exit(1);
} break;
}
u3_Host.pep_o = c3y;
}
/* _cw_vere(): download vere
*/
static void
_cw_vere(c3_i argc, c3_c* argv[])
{
c3_c* pac_c = "live";
c3_c* arc_c = 0;
c3_c* ver_c = 0;
c3_c* dir_c;
c3_i ch_i, lid_i;
c3_w arg_w;
static struct option lop_u[] = {
{ "arch", required_argument, NULL, 'a' },
{ "pace", required_argument, NULL, 'p' },
{ "version", required_argument, NULL, 'v' },
{ NULL, 0, NULL, 0 }
};
while ( -1 != (ch_i=getopt_long(argc, argv, "a:p:v:", lop_u, &lid_i)) ) {
switch ( ch_i ) {
case 'a': {
arc_c = strdup(optarg);
} break;
case 'p': {
pac_c = strdup(optarg);
} break;
case 'v': {
ver_c = strdup(optarg);
} break;
case '?': {
exit(1);
} break;
}
}
// argv[optind] is always "vere"/"fetch-vere"
//
if ( optind + 1 < argc ) {
dir_c = argv[optind + 1];
optind++;
}
else {
fprintf(stderr, "invalid command, output directory required\r\n");
exit(1);
}
if ( optind + 1 != argc ) {
fprintf(stderr, "invalid command\r\n");
exit(1);
}
if ( !arc_c ) {
#ifdef U3_OS_ARCH
arc_c = U3_OS_ARCH;
#else
fprintf(stderr, "unknown architecture, --arch required\r\n");
exit(1);
#endif
}
// Initialize OpenSSL for client and server
//
{
SSL_library_init();
SSL_load_error_strings();
}
// initialize curl
//
if ( 0 != curl_global_init(CURL_GLOBAL_DEFAULT) ) {
u3l_log("boot: curl initialization failed\r\n");
exit(1);
}
_setup_cert_store();
u3K.ssl_curl_f = _setup_ssl_curl;
u3K.ssl_x509_f = _setup_ssl_x509;
if ( !ver_c ) {
switch ( u3_king_next(pac_c, &ver_c) ) {
case -2: {
fprintf(stderr, "vere: unable to check for next version\n");
exit(1);
} break;
case -1: {
fprintf(stderr, "you're already running it!\n");
exit(0);
} break;
case 0: {
fprintf(stderr, "vere: next (%%%s): %s\n", pac_c, ver_c);
} break;
default: c3_assert(0);
}
}
if ( u3_king_vere(pac_c, ver_c, arc_c, dir_c, 0) ) {
u3l_log("vere: download failed\r\n");
exit(1);
}
u3l_log("vere: download succeeded\r\n");
}
/* _cw_utils(): "worker" utilities and "serf" entrypoint /* _cw_utils(): "worker" utilities and "serf" entrypoint
*/ */
static c3_i static c3_i
@ -1202,11 +1591,15 @@ _cw_utils(c3_i argc, c3_c* argv[])
// //
// $@ ~ :: usage // $@ ~ :: usage
// $% [%cram dir=@t] :: jam state // $% [%cram dir=@t] :: jam state
// [%grab dir=@t] :: gc // [%dock dir=@t] :: copy binary
// [?(%grab %mass) dir=@t] :: gc
// [%info dir=@t] :: print // [%info dir=@t] :: print
// [%meld dir=@t] :: deduplicate // [%meld dir=@t] :: deduplicate
// [?(%next %upgrade) dir=@t] :: upgrade
// [%pack dir=@t] :: defragment // [%pack dir=@t] :: defragment
// [%prep dir=@t] :: prep upgrade
// [%queu dir=@t eve=@ud] :: cue state // [%queu dir=@t eve=@ud] :: cue state
// [?(%vere %fetch-vere) dir=@t] :: download vere
// :: :: ipc: // :: :: ipc:
// [%serf dir=@t key=@t wag=@t hap=@ud eve=@ud] :: compute // [%serf dir=@t key=@t wag=@t hap=@ud eve=@ud] :: compute
// == // ==
@ -1214,22 +1607,37 @@ _cw_utils(c3_i argc, c3_c* argv[])
// NB: don't print to anything other than stderr; // NB: don't print to anything other than stderr;
// other streams may be used for ipc. // other streams may be used for ipc.
// //
if ( (2 < argc) && 4 == strlen(argv[1]) ) { c3_m mot_m = 0;
c3_m mot_m;
{
c3_c* s = argv[1]; mot_m = c3_s4(s[0], s[1], s[2], s[3]);
}
switch ( mot_m ) { if ( 2 <= argc ) {
case c3__cram: _cw_cram(argc, argv); return 1; if ( 4 == strlen(argv[1]) ) {
case c3__grab: _cw_grab(argc, argv); return 1; c3_c* s = argv[1];
case c3__info: _cw_info(argc, argv); return 1; mot_m = c3_s4(s[0], s[1], s[2], s[3]);
case c3__meld: _cw_meld(argc, argv); return 1;
case c3__pack: _cw_pack(argc, argv); return 1;
case c3__queu: _cw_queu(argc, argv); return 1;
case c3__serf: _cw_serf_commence(argc, argv); return 1;
} }
else if ( 0 == strcmp(argv[1], "upgrade") ) {
mot_m = c3__next;
}
else if ( 0 == strcmp(argv[1], "fetch-vere") ) {
mot_m = c3__vere;
}
}
switch ( mot_m ) {
case c3__cram: _cw_cram(argc, argv); return 1;
case c3__dock: _cw_dock(argc, argv); return 1;
case c3__mass:
case c3__grab: _cw_grab(argc, argv); return 1;
case c3__info: _cw_info(argc, argv); return 1;
case c3__meld: _cw_meld(argc, argv); return 1;
case c3__next: _cw_next(argc, argv); return 2; // continue on
case c3__pack: _cw_pack(argc, argv); return 1;
case c3__prep: _cw_prep(argc, argv); return 2; // continue on
case c3__queu: _cw_queu(argc, argv); return 1;
case c3__vere: _cw_vere(argc, argv); return 1;
case c3__serf: _cw_serf_commence(argc, argv); return 1;
} }
return 0; return 0;
@ -1239,18 +1647,48 @@ c3_i
main(c3_i argc, main(c3_i argc,
c3_c** argv) c3_c** argv)
{ {
// Parse options. if ( argc <= 0 ) {
// fprintf(stderr, "nice try, fbi\r\n");
if ( _cw_utils(argc, argv) ) { exit(1);
return 0;
}
else if ( c3n == _main_getopt(argc, argv) ) {
u3_ve_usage(argc, argv);
return 1;
} }
_main_init();
c3_c* bin_c = strdup(argv[0]);
// parse for subcommands
//
switch ( _cw_utils(argc, argv) ) {
default: c3_assert(0);
// no matching subcommand, parse arguments
//
case 0: {
if ( c3n == _main_getopt(argc, argv) ) {
u3_ve_usage(argc, argv);
return 1;
}
} break;
// ran subcommand
case 1: {
return 0;
}
// found subcommand, continue
//
case 2: break;
}
_main_self_path();
// XX add argument
//
if ( !u3_Host.wrk_c ) { if ( !u3_Host.wrk_c ) {
u3_Host.wrk_c = strdup(argv[0]); u3_Host.wrk_c = bin_c;
}
else {
c3_free(bin_c);
} }
if ( c3y == u3_Host.ops_u.dem ) { if ( c3y == u3_Host.ops_u.dem ) {

673
pkg/urbit/daemon/whereami.c Normal file
View File

@ -0,0 +1,673 @@
// (‑●‑●)> released under the WTFPL v2 license, by Gregory Pakosz (@gpakosz)
// https://github.com/gpakosz/whereami
// in case you want to #include "whereami.c" in a larger compilation unit
#if !defined(WHEREAMI_H)
#include "whereami.h"
#endif
#ifdef __cplusplus
extern "C" {
#endif
#if !defined(WAI_MALLOC) || !defined(WAI_FREE) || !defined(WAI_REALLOC)
#include <stdlib.h>
#endif
#if !defined(WAI_MALLOC)
#define WAI_MALLOC(size) malloc(size)
#endif
#if !defined(WAI_FREE)
#define WAI_FREE(p) free(p)
#endif
#if !defined(WAI_REALLOC)
#define WAI_REALLOC(p, size) realloc(p, size)
#endif
#ifndef WAI_NOINLINE
#if defined(_MSC_VER)
#define WAI_NOINLINE __declspec(noinline)
#elif defined(__GNUC__)
#define WAI_NOINLINE __attribute__((noinline))
#else
#error unsupported compiler
#endif
#endif
#if defined(_MSC_VER)
#define WAI_RETURN_ADDRESS() _ReturnAddress()
#elif defined(__GNUC__)
#define WAI_RETURN_ADDRESS() __builtin_extract_return_addr(__builtin_return_address(0))
#else
#error unsupported compiler
#endif
#if defined(_WIN32)
#define WIN32_LEAN_AND_MEAN
#if defined(_MSC_VER)
#pragma warning(push, 3)
#endif
#include <windows.h>
#include <intrin.h>
#if defined(_MSC_VER)
#pragma warning(pop)
#endif
static int WAI_PREFIX(getModulePath_)(HMODULE module, char* out, int capacity, int* dirname_length)
{
wchar_t buffer1[MAX_PATH];
wchar_t buffer2[MAX_PATH];
wchar_t* path = NULL;
int length = -1;
for (;;)
{
DWORD size;
int length_, length__;
size = GetModuleFileNameW(module, buffer1, sizeof(buffer1) / sizeof(buffer1[0]));
if (size == 0)
break;
else if (size == (DWORD)(sizeof(buffer1) / sizeof(buffer1[0])))
{
DWORD size_ = size;
do
{
wchar_t* path_;
path_ = (wchar_t*)WAI_REALLOC(path, sizeof(wchar_t) * size_ * 2);
if (!path_)
break;
size_ *= 2;
path = path_;
size = GetModuleFileNameW(module, path, size_);
}
while (size == size_);
if (size == size_)
break;
}
else
path = buffer1;
if (!_wfullpath(buffer2, path, MAX_PATH))
break;
length_ = (int)wcslen(buffer2);
length__ = WideCharToMultiByte(CP_UTF8, 0, buffer2, length_ , out, capacity, NULL, NULL);
if (length__ == 0)
length__ = WideCharToMultiByte(CP_UTF8, 0, buffer2, length_, NULL, 0, NULL, NULL);
if (length__ == 0)
break;
if (length__ <= capacity && dirname_length)
{
int i;
for (i = length__ - 1; i >= 0; --i)
{
if (out[i] == '\\')
{
*dirname_length = i;
break;
}
}
}
length = length__;
break;
}
if (path != buffer1)
WAI_FREE(path);
return length;
}
WAI_NOINLINE WAI_FUNCSPEC
int WAI_PREFIX(getExecutablePath)(char* out, int capacity, int* dirname_length)
{
return WAI_PREFIX(getModulePath_)(NULL, out, capacity, dirname_length);
}
WAI_NOINLINE WAI_FUNCSPEC
int WAI_PREFIX(getModulePath)(char* out, int capacity, int* dirname_length)
{
HMODULE module;
int length = -1;
#if defined(_MSC_VER)
#pragma warning(push)
#pragma warning(disable: 4054)
#endif
if (GetModuleHandleEx(GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS | GET_MODULE_HANDLE_EX_FLAG_UNCHANGED_REFCOUNT, (LPCTSTR)WAI_RETURN_ADDRESS(), &module))
#if defined(_MSC_VER)
#pragma warning(pop)
#endif
{
length = WAI_PREFIX(getModulePath_)(module, out, capacity, dirname_length);
}
return length;
}
#elif defined(__linux__) || defined(__CYGWIN__) || defined(__sun)
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#if defined(__linux__)
#include <linux/limits.h>
#else
#include <limits.h>
#endif
#ifndef __STDC_FORMAT_MACROS
#define __STDC_FORMAT_MACROS
#endif
#include <inttypes.h>
#if !defined(WAI_PROC_SELF_EXE)
#if defined(__sun)
#define WAI_PROC_SELF_EXE "/proc/self/path/a.out"
#else
#define WAI_PROC_SELF_EXE "/proc/self/exe"
#endif
#endif
WAI_FUNCSPEC
int WAI_PREFIX(getExecutablePath)(char* out, int capacity, int* dirname_length)
{
char buffer[PATH_MAX];
char* resolved = NULL;
int length = -1;
for (;;)
{
resolved = realpath(WAI_PROC_SELF_EXE, buffer);
if (!resolved)
break;
length = (int)strlen(resolved);
if (length <= capacity)
{
memcpy(out, resolved, length);
if (dirname_length)
{
int i;
for (i = length - 1; i >= 0; --i)
{
if (out[i] == '/')
{
*dirname_length = i;
break;
}
}
}
}
break;
}
return length;
}
#if !defined(WAI_PROC_SELF_MAPS_RETRY)
#define WAI_PROC_SELF_MAPS_RETRY 5
#endif
#if !defined(WAI_PROC_SELF_MAPS)
#if defined(__sun)
#define WAI_PROC_SELF_MAPS "/proc/self/map"
#else
#define WAI_PROC_SELF_MAPS "/proc/self/maps"
#endif
#endif
#if defined(__ANDROID__) || defined(ANDROID)
#include <fcntl.h>
#include <sys/mman.h>
#include <unistd.h>
#endif
WAI_NOINLINE WAI_FUNCSPEC
int WAI_PREFIX(getModulePath)(char* out, int capacity, int* dirname_length)
{
int length = -1;
FILE* maps = NULL;
for (int r = 0; r < WAI_PROC_SELF_MAPS_RETRY; ++r)
{
maps = fopen(WAI_PROC_SELF_MAPS, "r");
if (!maps)
break;
for (;;)
{
char buffer[PATH_MAX < 1024 ? 1024 : PATH_MAX];
uint64_t low, high;
char perms[5];
uint64_t offset;
uint32_t major, minor;
char path[PATH_MAX];
uint32_t inode;
if (!fgets(buffer, sizeof(buffer), maps))
break;
if (sscanf(buffer, "%" PRIx64 "-%" PRIx64 " %s %" PRIx64 " %x:%x %u %s\n", &low, &high, perms, &offset, &major, &minor, &inode, path) == 8)
{
uint64_t addr = (uintptr_t)WAI_RETURN_ADDRESS();
if (low <= addr && addr <= high)
{
char* resolved;
resolved = realpath(path, buffer);
if (!resolved)
break;
length = (int)strlen(resolved);
#if defined(__ANDROID__) || defined(ANDROID)
if (length > 4
&&buffer[length - 1] == 'k'
&&buffer[length - 2] == 'p'
&&buffer[length - 3] == 'a'
&&buffer[length - 4] == '.')
{
int fd = open(path, O_RDONLY);
char* begin;
char* p;
begin = (char*)mmap(0, offset, PROT_READ, MAP_SHARED, fd, 0);
p = begin + offset;
while (p >= begin) // scan backwards
{
if (*((uint32_t*)p) == 0x04034b50UL) // local file header found
{
uint16_t length_ = *((uint16_t*)(p + 26));
if (length + 2 + length_ < (int)sizeof(buffer))
{
memcpy(&buffer[length], "!/", 2);
memcpy(&buffer[length + 2], p + 30, length_);
length += 2 + length_;
}
break;
}
p -= 4;
}
munmap(begin, offset);
close(fd);
}
#endif
if (length <= capacity)
{
memcpy(out, resolved, length);
if (dirname_length)
{
int i;
for (i = length - 1; i >= 0; --i)
{
if (out[i] == '/')
{
*dirname_length = i;
break;
}
}
}
}
break;
}
}
}
fclose(maps);
maps = NULL;
if (length != -1)
break;
}
if (maps)
fclose(maps);
return length;
}
#elif defined(__APPLE__)
#define _DARWIN_BETTER_REALPATH
#include <mach-o/dyld.h>
#include <limits.h>
#include <stdlib.h>
#include <string.h>
#include <dlfcn.h>
WAI_FUNCSPEC
int WAI_PREFIX(getExecutablePath)(char* out, int capacity, int* dirname_length)
{
char buffer1[PATH_MAX];
char buffer2[PATH_MAX];
char* path = buffer1;
char* resolved = NULL;
int length = -1;
for (;;)
{
uint32_t size = (uint32_t)sizeof(buffer1);
if (_NSGetExecutablePath(path, &size) == -1)
{
path = (char*)WAI_MALLOC(size);
if (!_NSGetExecutablePath(path, &size))
break;
}
resolved = realpath(path, buffer2);
if (!resolved)
break;
length = (int)strlen(resolved);
if (length <= capacity)
{
memcpy(out, resolved, length);
if (dirname_length)
{
int i;
for (i = length - 1; i >= 0; --i)
{
if (out[i] == '/')
{
*dirname_length = i;
break;
}
}
}
}
break;
}
if (path != buffer1)
WAI_FREE(path);
return length;
}
WAI_NOINLINE WAI_FUNCSPEC
int WAI_PREFIX(getModulePath)(char* out, int capacity, int* dirname_length)
{
char buffer[PATH_MAX];
char* resolved = NULL;
int length = -1;
for(;;)
{
Dl_info info;
if (dladdr(WAI_RETURN_ADDRESS(), &info))
{
resolved = realpath(info.dli_fname, buffer);
if (!resolved)
break;
length = (int)strlen(resolved);
if (length <= capacity)
{
memcpy(out, resolved, length);
if (dirname_length)
{
int i;
for (i = length - 1; i >= 0; --i)
{
if (out[i] == '/')
{
*dirname_length = i;
break;
}
}
}
}
}
break;
}
return length;
}
#elif defined(__QNXNTO__)
#include <limits.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <dlfcn.h>
#if !defined(WAI_PROC_SELF_EXE)
#define WAI_PROC_SELF_EXE "/proc/self/exefile"
#endif
WAI_FUNCSPEC
int WAI_PREFIX(getExecutablePath)(char* out, int capacity, int* dirname_length)
{
char buffer1[PATH_MAX];
char buffer2[PATH_MAX];
char* resolved = NULL;
FILE* self_exe = NULL;
int length = -1;
for (;;)
{
self_exe = fopen(WAI_PROC_SELF_EXE, "r");
if (!self_exe)
break;
if (!fgets(buffer1, sizeof(buffer1), self_exe))
break;
resolved = realpath(buffer1, buffer2);
if (!resolved)
break;
length = (int)strlen(resolved);
if (length <= capacity)
{
memcpy(out, resolved, length);
if (dirname_length)
{
int i;
for (i = length - 1; i >= 0; --i)
{
if (out[i] == '/')
{
*dirname_length = i;
break;
}
}
}
}
break;
}
fclose(self_exe);
return length;
}
WAI_FUNCSPEC
int WAI_PREFIX(getModulePath)(char* out, int capacity, int* dirname_length)
{
char buffer[PATH_MAX];
char* resolved = NULL;
int length = -1;
for(;;)
{
Dl_info info;
if (dladdr(WAI_RETURN_ADDRESS(), &info))
{
resolved = realpath(info.dli_fname, buffer);
if (!resolved)
break;
length = (int)strlen(resolved);
if (length <= capacity)
{
memcpy(out, resolved, length);
if (dirname_length)
{
int i;
for (i = length - 1; i >= 0; --i)
{
if (out[i] == '/')
{
*dirname_length = i;
break;
}
}
}
}
}
break;
}
return length;
}
#elif defined(__DragonFly__) || defined(__FreeBSD__) || \
defined(__FreeBSD_kernel__) || defined(__NetBSD__)
#include <limits.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <sys/sysctl.h>
#include <dlfcn.h>
WAI_FUNCSPEC
int WAI_PREFIX(getExecutablePath)(char* out, int capacity, int* dirname_length)
{
char buffer1[PATH_MAX];
char buffer2[PATH_MAX];
char* path = buffer1;
char* resolved = NULL;
int length = -1;
for (;;)
{
int mib[4] = { CTL_KERN, KERN_PROC, KERN_PROC_PATHNAME, -1 };
size_t size = sizeof(buffer1);
if (sysctl(mib, (u_int)(sizeof(mib) / sizeof(mib[0])), path, &size, NULL, 0) != 0)
break;
resolved = realpath(path, buffer2);
if (!resolved)
break;
length = (int)strlen(resolved);
if (length <= capacity)
{
memcpy(out, resolved, length);
if (dirname_length)
{
int i;
for (i = length - 1; i >= 0; --i)
{
if (out[i] == '/')
{
*dirname_length = i;
break;
}
}
}
}
break;
}
if (path != buffer1)
WAI_FREE(path);
return length;
}
WAI_NOINLINE WAI_FUNCSPEC
int WAI_PREFIX(getModulePath)(char* out, int capacity, int* dirname_length)
{
char buffer[PATH_MAX];
char* resolved = NULL;
int length = -1;
for(;;)
{
Dl_info info;
if (dladdr(WAI_RETURN_ADDRESS(), &info))
{
resolved = realpath(info.dli_fname, buffer);
if (!resolved)
break;
length = (int)strlen(resolved);
if (length <= capacity)
{
memcpy(out, resolved, length);
if (dirname_length)
{
int i;
for (i = length - 1; i >= 0; --i)
{
if (out[i] == '/')
{
*dirname_length = i;
break;
}
}
}
}
}
break;
}
return length;
}
#else
#error unsupported platform
#endif
#ifdef __cplusplus
}
#endif

View File

@ -0,0 +1,65 @@
// (‑●‑●)> released under the WTFPL v2 license, by Gregory Pakosz (@gpakosz)
// https://github.com/gpakosz/whereami
#ifndef WHEREAMI_H
#define WHEREAMI_H
#ifdef __cplusplus
extern "C" {
#endif
#ifndef WAI_FUNCSPEC
#define WAI_FUNCSPEC
#endif
#ifndef WAI_PREFIX
#define WAI_PREFIX(function) wai_##function
#endif
/**
* Returns the path to the current executable.
*
* Usage:
* - first call `int length = wai_getExecutablePath(NULL, 0, NULL);` to
* retrieve the length of the path
* - allocate the destination buffer with `path = (char*)malloc(length + 1);`
* - call `wai_getExecutablePath(path, length, NULL)` again to retrieve the
* path
* - add a terminal NUL character with `path[length] = '\0';`
*
* @param out destination buffer, optional
* @param capacity destination buffer capacity
* @param dirname_length optional recipient for the length of the dirname part
* of the path.
*
* @return the length of the executable path on success (without a terminal NUL
* character), otherwise `-1`
*/
WAI_FUNCSPEC
int WAI_PREFIX(getExecutablePath)(char* out, int capacity, int* dirname_length);
/**
* Returns the path to the current module
*
* Usage:
* - first call `int length = wai_getModulePath(NULL, 0, NULL);` to retrieve
* the length of the path
* - allocate the destination buffer with `path = (char*)malloc(length + 1);`
* - call `wai_getModulePath(path, length, NULL)` again to retrieve the path
* - add a terminal NUL character with `path[length] = '\0';`
*
* @param out destination buffer, optional
* @param capacity destination buffer capacity
* @param dirname_length optional recipient for the length of the dirname part
* of the path.
*
* @return the length of the module path on success (without a terminal NUL
* character), otherwise `-1`
*/
WAI_FUNCSPEC
int WAI_PREFIX(getModulePath)(char* out, int capacity, int* dirname_length);
#ifdef __cplusplus
}
#endif
#endif // #ifndef WHEREAMI_H

View File

@ -328,6 +328,7 @@
# define c3__dmal c3_s4('d','m','a','l') # define c3__dmal c3_s4('d','m','a','l')
# define c3__do c3_s2('d','o') # define c3__do c3_s2('d','o')
# define c3__doc c3_s3('d','o','c') # define c3__doc c3_s3('d','o','c')
# define c3__dock c3_s4('d','o','c','k')
# define c3__docs c3_s4('d','o','c','s') # define c3__docs c3_s4('d','o','c','s')
# define c3__dogo c3_s4('d','o','g','o') # define c3__dogo c3_s4('d','o','g','o')
# define c3__dojo c3_s4('d','o','j','o') # define c3__dojo c3_s4('d','o','j','o')
@ -796,6 +797,7 @@
# define c3__noah c3_s4('n','o','a','h') # define c3__noah c3_s4('n','o','a','h')
# define c3__nock c3_s4('n','o','c','k') # define c3__nock c3_s4('n','o','c','k')
# define c3__noco c3_s4('n','o','c','o') # define c3__noco c3_s4('n','o','c','o')
# define c3__nodo c3_s4('n','o','d','o')
# define c3__none c3_s4('n','o','n','e') # define c3__none c3_s4('n','o','n','e')
# define c3__noop c3_s4('n','o','o','p') # define c3__noop c3_s4('n','o','o','p')
# define c3__nop c3_s3('n','o','p') # define c3__nop c3_s3('n','o','p')
@ -912,6 +914,7 @@
# define c3__post c3_s4('p','o','s','t') # define c3__post c3_s4('p','o','s','t')
# define c3__pray c3_s4('p','r','a','y') # define c3__pray c3_s4('p','r','a','y')
# define c3__prec c3_s4('p','r','e','c') # define c3__prec c3_s4('p','r','e','c')
# define c3__prep c3_s4('p','r','e','p')
# define c3__pret c3_s4('p','r','e','t') # define c3__pret c3_s4('p','r','e','t')
# define c3__prex c3_s4('p','r','e','x') # define c3__prex c3_s4('p','r','e','x')
# define c3__pril c3_s4('p','r','i','l') # define c3__pril c3_s4('p','r','i','l')
@ -1231,6 +1234,7 @@
# define c3__velt c3_s4('v','e','l','t') # define c3__velt c3_s4('v','e','l','t')
# define c3__vent c3_s4('v','e','n','t') # define c3__vent c3_s4('v','e','n','t')
# define c3__verb c3_s4('v','e','r','b') # define c3__verb c3_s4('v','e','r','b')
# define c3__vere c3_s4('v','e','r','e')
# define c3__vern c3_s4('v','e','r','n') # define c3__vern c3_s4('v','e','r','n')
# define c3__very c3_s4('v','e','r','y') # define c3__very c3_s4('v','e','r','y')
# define c3__view c3_s4('v','i','e','w') # define c3__view c3_s4('v','i','e','w')

View File

@ -21,6 +21,7 @@
# ifndef _XOPEN_SOURCE # ifndef _XOPEN_SOURCE
# define _XOPEN_SOURCE 700 # define _XOPEN_SOURCE 700
# endif # endif
# include <ctype.h>
# include <inttypes.h> # include <inttypes.h>
# include <stdlib.h> # include <stdlib.h>
# include <string.h> # include <string.h>
@ -35,8 +36,10 @@
# include <sys/time.h> # include <sys/time.h>
# include <sys/resource.h> # include <sys/resource.h>
# include <sys/mman.h> # include <sys/mman.h>
# include <sys/sendfile.h>
# elif defined(U3_OS_osx) # elif defined(U3_OS_osx)
# include <ctype.h>
# include <inttypes.h> # include <inttypes.h>
# include <stdlib.h> # include <stdlib.h>
# include <string.h> # include <string.h>
@ -53,8 +56,11 @@
# include <sys/resource.h> # include <sys/resource.h>
# include <sys/syslimits.h> # include <sys/syslimits.h>
# include <sys/mman.h> # include <sys/mman.h>
# include <sys/clonefile.h>
# include <copyfile.h>
# elif defined(U3_OS_bsd) # elif defined(U3_OS_bsd)
# include <ctype.h>
# include <inttypes.h> # include <inttypes.h>
# include <stdlib.h> # include <stdlib.h>
# include <string.h> # include <string.h>
@ -74,6 +80,7 @@
# define signal mingw_has_no_usable_signal # define signal mingw_has_no_usable_signal
# define raise mingw_has_no_usable_raise # define raise mingw_has_no_usable_raise
# define _POSIX # define _POSIX
# include <ctype.h>
# include <inttypes.h> # include <inttypes.h>
# include <stdlib.h> # include <stdlib.h>
# include <string.h> # include <string.h>
@ -101,6 +108,42 @@
# define ASAN_ENABLED # define ASAN_ENABLED
# endif # endif
/** Platform string.
**/
# if defined(U3_OS_linux)
# ifdef __LP64__
# ifdef U3_CPU_aarch64
// XX not yet
//# define U3_OS_ARCH "aarch64-linux"
# else
# define U3_OS_ARCH "x86_64-linux"
# endif
# endif
# elif defined(U3_OS_mingw)
# define U3_OS_ARCH "x86_64-windows"
# elif defined(U3_OS_osx)
# ifdef __LP64__
# ifdef U3_CPU_aarch64
// XX not yet
//# define U3_OS_ARCH "aarch64-darwin"
# else
# define U3_OS_ARCH "x86_64-darwin"
# endif
# endif
# endif
/** Binary alias.
**/
# ifdef U3_OS_mingw
# define U3_BIN_SUFFIX ".exe"
# else
# define U3_BIN_SUFFIX ""
# endif
# define U3_BIN_ALIAS ".run" U3_BIN_SUFFIX
/** Address space layout. /** Address space layout.
*** ***
*** NB: 2^29 words == 2GB *** NB: 2^29 words == 2GB

View File

@ -310,6 +310,7 @@
c3_c* puk_c; // -Y, scry result filename c3_c* puk_c; // -Y, scry result filename
c3_c* puf_c; // -Z, scry result format c3_c* puf_c; // -Z, scry result format
c3_o con; // run conn c3_o con; // run conn
c3_o doc; // dock binary in pier
} u3_opts; } u3_opts;
/* u3_host: entire host. /* u3_host: entire host.
@ -317,15 +318,19 @@
typedef struct _u3_host { typedef struct _u3_host {
c3_w kno_w; // current executing stage c3_w kno_w; // current executing stage
c3_c* dir_c; // pier path (no trailing /) c3_c* dir_c; // pier path (no trailing /)
c3_c* dem_c; // daemon executable path
c3_c* wrk_c; // worker executable path c3_c* wrk_c; // worker executable path
c3_d now_d; // event tick c3_d now_d; // event tick
uv_loop_t* lup_u; // libuv event loop uv_loop_t* lup_u; // libuv event loop
u3_usig* sig_u; // signal list u3_usig* sig_u; // signal list
#if defined(U3_OS_mingw) #if defined(U3_OS_mingw)
HANDLE cev_u; // Ctrl-C event handle HANDLE cev_u; // Ctrl-C event handle
#endif #endif
u3_utty* uty_u; // linked terminal list u3_utty* uty_u; // linked terminal list
c3_o nex_o; // upgrade requested
c3_c* arc_c; // upgrade to arch
u3_opts ops_u; // commandline options u3_opts ops_u; // commandline options
c3_o pep_o; // prep for upgrade
c3_i xit_i; // exit code for shutdown c3_i xit_i; // exit code for shutdown
u3_trac tra_u; // tracing information u3_trac tra_u; // tracing information
void (*bot_f)(); // call when chis is up void (*bot_f)(); // call when chis is up
@ -1409,6 +1414,11 @@
void void
u3_king_slog(void); u3_king_slog(void);
/* u3_king_dock(): copy binary into pier on boot.
*/
void
u3_king_dock(c3_c* pac_c);
/* u3_king_done(): all piers closed /* u3_king_done(): all piers closed
*/ */
void void
@ -1429,6 +1439,21 @@
void void
u3_king_grab(void* ptr_v); u3_king_grab(void* ptr_v);
/* u3_king_next(): get next vere version string, if it exists.
** return: 0 is success, -1 is no-op (same version), -2 is error
*/
c3_i
u3_king_next(c3_c* pac_c, c3_c** out_c);
/* u3_king_vere(): download binary as specified.
*/
c3_i
u3_king_vere(c3_c* pac_c, // pace
c3_c* ver_c, // version
c3_c* arc_c, // architecture
c3_c* dir_c, // output directory
c3_t lin_t); // link to $pier/.run
/* u3_daemon_init(): platform-specific daemon mode initialization. /* u3_daemon_init(): platform-specific daemon mode initialization.
*/ */
void void

View File

@ -9,6 +9,8 @@
#include <curl/curl.h> #include <curl/curl.h>
#include <uv.h> #include <uv.h>
static const c3_c* ver_hos_c = "https://bootstrap.urbit.org/vere";
// stash config flags for worker // stash config flags for worker
// //
static c3_w sag_w; static c3_w sag_w;
@ -231,54 +233,167 @@ _king_curl_alloc(void* dat_v, size_t uni_t, size_t mem_t, void* buf_v)
return siz_t; return siz_t;
} }
/* _king_get_atom(): HTTP GET url_c, produce the response body as an atom. /* _king_curl_bytes(): HTTP GET url_c, produce response body bytes.
** XX deduplicate with dawn.c ** XX deduplicate with dawn.c
*/ */
static u3_noun static c3_i
_king_get_atom(c3_c* url_c) _king_curl_bytes(c3_c* url_c, c3_w* len_w, c3_y** hun_y, c3_t veb_t)
{ {
CURL *curl; c3_i ret_i = 0;
CURLcode result; CURL *cul_u;
long cod_l; CURLcode res_i;
long cod_i;
uv_buf_t buf_u = uv_buf_init(c3_malloc(1), 0); uv_buf_t buf_u = uv_buf_init(c3_malloc(1), 0);
if ( !(curl = curl_easy_init()) ) { if ( !(cul_u = curl_easy_init()) ) {
u3l_log("failed to initialize libcurl\n"); u3l_log("failed to initialize libcurl\n");
exit(1); exit(1);
} }
u3K.ssl_curl_f(curl); u3K.ssl_curl_f(cul_u);
curl_easy_setopt(curl, CURLOPT_URL, url_c); curl_easy_setopt(cul_u, CURLOPT_URL, url_c);
curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, _king_curl_alloc); curl_easy_setopt(cul_u, CURLOPT_WRITEFUNCTION, _king_curl_alloc);
curl_easy_setopt(curl, CURLOPT_WRITEDATA, (void*)&buf_u); curl_easy_setopt(cul_u, CURLOPT_WRITEDATA, (void*)&buf_u);
result = curl_easy_perform(curl); res_i = curl_easy_perform(cul_u);
curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &cod_l); curl_easy_getinfo(cul_u, CURLINFO_RESPONSE_CODE, &cod_i);
// XX retry? // XX retry?
// //
if ( CURLE_OK != result ) { if ( CURLE_OK != res_i ) {
u3l_log("failed to fetch %s: %s\n", if ( veb_t ) {
url_c, curl_easy_strerror(result)); u3l_log("curl: failed %s: %s\n", url_c, curl_easy_strerror(res_i));
u3_king_bail(); }
exit(1); ret_i = -1;
} }
if ( 300 <= cod_l ) { if ( 300 <= cod_i ) {
u3l_log("error fetching %s: HTTP %ld\n", url_c, cod_l); if ( veb_t ) {
u3l_log("curl: error %s: HTTP %ld\n", url_c, cod_i);
}
ret_i = -2;
}
curl_easy_cleanup(cul_u);
*len_w = buf_u.len;
*hun_y = (c3_y*)buf_u.base;
return ret_i;
}
/* _king_get_atom(): HTTP GET url_c, produce response body as atom.
*/
static u3_noun
_king_get_atom(c3_c* url_c)
{
c3_w len_w;
c3_y* hun_y;
u3_noun pro;
if ( _king_curl_bytes(url_c, &len_w, &hun_y, 1) ) {
u3_king_bail(); u3_king_bail();
exit(1); exit(1);
} }
curl_easy_cleanup(curl); pro = u3i_bytes(len_w, hun_y);
c3_free(hun_y);
return pro;
}
{ /* _king_get_pace(): get "pace" (release channel name).
u3_noun pro = u3i_bytes(buf_u.len, (const c3_y*)buf_u.base); */
static c3_c*
_king_get_pace(void)
{
struct stat buf_u;
c3_c* pat_c;
c3_w red_w, len_w;
c3_i ret_i, fid_i;
c3_free(buf_u.base); ret_i = asprintf(&pat_c, "%s/.bin/pace", u3_Host.dir_c);
c3_assert( ret_i > 0 );
return pro; fid_i = c3_open(pat_c, O_RDONLY, 0644);
if ( (fid_i < 0) || (fstat(fid_i, &buf_u) < 0) ) {
c3_free(pat_c);
return strdup("live");
} }
c3_free(pat_c);
len_w = buf_u.st_size;
pat_c = c3_malloc(len_w + 1);
red_w = read(fid_i, pat_c, len_w);
close(fid_i);
if ( len_w != red_w ) {
c3_free(pat_c);
u3l_log("unable to read pace file, "
"falling back to default (\"live\")\n");
return strdup("live");
}
pat_c[len_w] = 0;
while ( len_w-- && isspace(pat_c[len_w]) ) {
pat_c[len_w] = 0;
}
return pat_c;
}
/* u3_king_next(): get next vere version string, if it exists.
** return: 0 is success, -1 is no-op (same version), -2 is error
*/
c3_i
u3_king_next(c3_c* pac_c, c3_c** out_c)
{
c3_c* ver_c;
c3_c* url_c;
c3_w len_w;
c3_y* hun_y;
c3_i ret_i;
ret_i = asprintf(&url_c, "%s/%s/%s/next", ver_hos_c, pac_c, URBIT_VERSION);
c3_assert( ret_i > 0 );
// skip printfs on failed requests (/next is usually not present)
//
if ( _king_curl_bytes(url_c, &len_w, &hun_y, 0) ) {
c3_free(url_c);
ret_i = asprintf(&url_c, "%s/%s/last", ver_hos_c, pac_c);
c3_assert( ret_i > 0 );
// enable printfs on failed requests (/last must be present)
// XX support channel redirections
//
if ( _king_curl_bytes(url_c, &len_w, &hun_y, 1) )
{
c3_free(url_c);
return -2;
}
}
c3_free(url_c);
// null-terminate
//
hun_y = c3_realloc(hun_y, 1 + len_w);
hun_y[len_w] = 0;
ver_c = (c3_c*)hun_y;
// XX trim ver_c ?
//
if ( 0 == strcmp(ver_c, URBIT_VERSION) ) {
c3_free(ver_c);
return -1;
}
*out_c = ver_c;
return 0;
} }
/* _get_cmd_output(): Run a shell command and capture its output. /* _get_cmd_output(): Run a shell command and capture its output.
@ -848,6 +963,527 @@ _king_forall_unlink(void (*pir_f)(u3_pier*))
} }
} }
/* _king_curl_file(): HTTP GET [url_c], write response body to [fil_u].
*/
static c3_i
_king_save_file(c3_c* url_c, FILE* fil_u)
{
c3_i ret_i = 0;
CURL *cul_u;
CURLcode res_i;
long cod_i;
if ( !(cul_u = curl_easy_init()) ) {
u3l_log("failed to initialize libcurl\n");
exit(1);
}
u3K.ssl_curl_f(cul_u);
curl_easy_setopt(cul_u, CURLOPT_URL, url_c);
curl_easy_setopt(cul_u, CURLOPT_WRITEDATA, (void*)fil_u);
res_i = curl_easy_perform(cul_u);
curl_easy_getinfo(cul_u, CURLINFO_RESPONSE_CODE, &cod_i);
// XX retry?
//
if ( CURLE_OK != res_i ) {
u3l_log("curl: failed %s: %s\n", url_c, curl_easy_strerror(res_i));
ret_i = -1;
}
if ( 300 <= cod_i ) {
u3l_log("curl: error %s: HTTP %ld\n", url_c, cod_i);
ret_i = -2;
}
curl_easy_cleanup(cul_u);
return ret_i;
}
/* _king_make_pace(): mkdir -p $pier/.bin/[pace]
*/
static c3_i
_king_make_pace(c3_c* pac_c)
{
c3_c* bin_c;
c3_i ret_i;
ret_i = asprintf(&bin_c, "%s/.bin", u3_Host.dir_c);
c3_assert( ret_i > 0 );
ret_i = c3_mkdir(bin_c, 0700);
if ( ret_i && (EEXIST != errno) ) {
fprintf(stderr, "vere: mkdir %s failed: %s\n", bin_c, strerror(errno));
c3_free(bin_c);
return -1;
}
c3_free(bin_c);
ret_i = asprintf(&bin_c, "%s/.bin/%s/", u3_Host.dir_c, pac_c);
c3_assert( ret_i > 0 );
// XX asserting wrapper conflicts here (and is bypassed for .urb)
//
ret_i = mkdir(bin_c, 0700);
if ( ret_i && (EEXIST != errno) ) {
fprintf(stderr, "vere: mkdir %s failed: %s\n", bin_c, strerror(errno));
c3_free(bin_c);
return -1;
}
c3_free(bin_c);
return 0;
}
static c3_i
_king_write_raw(c3_i fid_i, c3_y* buf_y, size_t len_i);
/* _king_init_pace(): save pace file if not present
*/
static c3_i
_king_init_pace(c3_c* pac_c)
{
c3_c* bin_c;
c3_i fid_i, ret_i = asprintf(&bin_c, "%s/.bin/pace", u3_Host.dir_c);
c3_assert( ret_i > 0 );
if ( (-1 == (fid_i = open(bin_c, O_WRONLY | O_CREAT | O_EXCL, 0644))) ) {
if ( EEXIST == errno ) {
c3_free(bin_c);
// XX print something here?
//
return 0;
}
else {
u3l_log("dock: init pace (%s): open %s\n", pac_c, strerror(errno));
c3_free(bin_c);
return -1;
}
}
if ( _king_write_raw(fid_i, (c3_y*)pac_c, strlen(pac_c)) ) {
u3l_log("dock: init pace (%s): write %s\n", pac_c, strerror(errno));
close(fid_i);
c3_free(bin_c);
return -1;
}
// XX sync first?
//
else if ( close(fid_i) ) {
u3l_log("dock: init pace (%s): close %s\n", pac_c, strerror(errno));
c3_free(bin_c);
return 1;
}
u3l_log("dock: pace (%s): configured at %s/.bin/pace\r\n",
pac_c, u3_Host.dir_c);
return 0;
}
/* _king_link_run(): ln [bin_c] $pier/.run
*/
static c3_i
_king_link_run(c3_c* bin_c)
{
c3_c* lin_c;
c3_i ret_i;
ret_i = asprintf(&lin_c, "%s/%s", u3_Host.dir_c, U3_BIN_ALIAS);
c3_assert( ret_i > 0 );
ret_i = unlink(lin_c);
if ( ret_i && (ENOENT != errno) ) {
fprintf(stderr, "vere: unlink %s failed: %s\n", lin_c, strerror(errno));
c3_free(lin_c);
return -1;
}
ret_i = link(bin_c, lin_c);
if ( ret_i ) {
fprintf(stderr, "vere: link %s -> %s failed: %s\n",
lin_c, bin_c, strerror(errno));
c3_free(lin_c);
return -1;
}
c3_free(lin_c);
return 0;
}
/* u3_king_vere(): download binary as specified.
*/
c3_i
u3_king_vere(c3_c* pac_c, // pace
c3_c* ver_c, // version
c3_c* arc_c, // architecture
c3_c* dir_c, // output directory
c3_t lin_t) // link to $pier/.run
{
c3_c* bin_c;
c3_c* url_c;
FILE* fil_u;
c3_i fid_i, ret_i;
ret_i = asprintf(&bin_c, "%s/vere-v%s-%s" U3_BIN_SUFFIX,
dir_c, ver_c, arc_c);
c3_assert( ret_i > 0 );
if ( (-1 == (fid_i = open(bin_c, O_WRONLY | O_CREAT | O_EXCL, 0755)))
|| !(fil_u = fdopen(fid_i, "wb")) )
{
if ( EEXIST == errno ) {
u3l_log("already installed\n");
c3_free(bin_c);
return 0;
}
else {
u3l_log("unable to open %s: %s\r\n", bin_c, strerror(errno));
c3_free(bin_c);
return -1;
}
}
ret_i = asprintf(&url_c, "%s/%s/%s/vere-v%s-%s",
ver_hos_c, pac_c, ver_c, ver_c, arc_c);
c3_assert( ret_i > 0 );
if ( (ret_i = _king_save_file(url_c, fil_u)) ) {
u3l_log("unable to save %s to %s: %d\r\n", url_c, bin_c, ret_i);
c3_free(url_c);
fclose(fil_u);
unlink(bin_c);
c3_free(bin_c);
return -1; // XX
}
// XX sync unnecessary here?
//
if ( fflush(fil_u) || c3_sync(fid_i) ) {
fprintf(stderr, "vere: sync %s failed: %s\n", bin_c, strerror(errno));
c3_free(url_c);
fclose(fil_u);
unlink(bin_c);
c3_free(bin_c);
return -1;
}
fclose(fil_u);
// XX if link fails wat do?
// XX set via cli option
//
if ( lin_t ) {
if ( _king_link_run(bin_c) ) {
fprintf(stderr, "vere: link %s/%s failed\n", u3_Host.dir_c, U3_BIN_ALIAS);
c3_free(url_c);
c3_free(bin_c);
return -1;
}
}
u3l_log("vere: saved to %s\n", bin_c);
c3_free(url_c);
c3_free(bin_c);
return 0;
}
/* _king_do_upgrade(): get arch-appropriate binary at [ver_c].
*/
static void
_king_do_upgrade(c3_c* pac_c, c3_c* ver_c)
{
c3_c* dir_c;
c3_c* arc_c;
#ifdef U3_OS_ARCH
arc_c = U3_OS_ARCH;
#else
if ( u3_Host.arc_c ) {
arc_c = u3_Host.arc_c;
}
else {
u3l_log("vere: --arch required\r\n");
return;
}
#endif
if ( _king_make_pace(pac_c) ) {
u3l_log("vere: unable to make pace (%s) directory in pier\n", pac_c);
u3_king_bail();
exit(1);
}
{
c3_i ret_i = asprintf(&dir_c, "%s/.bin/%s", u3_Host.dir_c, pac_c);
c3_assert( ret_i > 0 );
}
// XX get link option
//
if ( u3_king_vere(pac_c, ver_c, arc_c, dir_c, 1) ) {
u3l_log("vere: upgrade failed\r\n");
u3_king_bail();
exit(1);
}
c3_free(dir_c);
u3l_log("vere: upgrade succeeded\r\n");
// XX print restart instructions
}
/* _king_read_raw: read (up to) [len_i] from [fid_i] to [buf_y]
*/
static ssize_t
_king_read_raw(c3_i fid_i, c3_y* buf_y, size_t len_i)
{
ssize_t ret_i;
do {
ret_i = read(fid_i, buf_y, len_i);
}
while ( (ret_i < 0) && (errno == EINTR) );
return ret_i;
}
/* _king_read_raw: write [len_i] from [buf_y] to [fid_i].
*/
static c3_i
_king_write_raw(c3_i fid_i, c3_y* buf_y, size_t len_i)
{
ssize_t ret_i;
while ( len_i ) {
do {
ret_i = write(fid_i, buf_y, len_i);
}
while ( (ret_i < 0) && (errno == EINTR) );
if ( ret_i < 0 ) {
return -1;
}
else {
len_i -= ret_i;
buf_y += ret_i;
}
}
return 0;
}
static c3_i
_king_copy_raw(c3_i src_i, c3_i dst_i, c3_y* buf_y, size_t pag_i)
{
ssize_t red_i;
do {
if ( 0 > (red_i = _king_read_raw(src_i, buf_y, pag_i)) ) {
return -1;
}
if ( _king_write_raw(dst_i, buf_y, (size_t)red_i) ) {
return -1;
}
}
while ( red_i );
return 0;
}
#if defined(U3_OS_mingw)
int err_win_to_posix(DWORD winerr);
#endif
static c3_i
_king_copy_file(c3_c* src_c, c3_c* dst_c)
{
#if defined(U3_OS_mingw)
// XX try FSCTL_DUPLICATE_EXTENTS_TO_FILE
//
if ( CopyFileA(src_c, dst_c, TRUE) ) {
return 0;
}
// XX fallback on any?
//
errno = err_win_to_posix(GetLastError());
return -1;
#elif defined(U3_OS_osx)
if ( !clonefile(src_c, dst_c, 0) ) {
return 0;
}
// fallthru to copying bytes on some errors
//
else if ( (ENOTSUP != errno) && (EXDEV != errno) ) {
return -1;
}
#endif
{
c3_i src_i, dst_i, ret_i = 0, err_i = 0;
if ( -1 == (src_i = open(src_c, O_RDONLY, 0644)) ) {
err_i = errno;
ret_i = -1;
goto done1;
}
if ( -1 == (dst_i = open(dst_c, O_RDWR | O_CREAT, 0755)) ) {
err_i = errno;
ret_i = -1;
goto done2;
}
// XX try clone_file_range ?
//
#if defined(U3_OS_linux)
#if defined(FICLONE)
if ( !ioctl(dst_i, FICLONE, src_i) ) {
ret_i = 0;
goto done3;
}
// fallthru to copying bytes on some errors
//
else if ( (EOPNOTSUPP != errno) && (EXDEV != errno) ) {
err_i = errno;
ret_i = -1;
goto done3;
}
#endif
{
off_t off_i = 0;
ssize_t sen_i;
size_t len_i;
{
struct stat sat_u;
if ( -1 == fstat(src_i, &sat_u) ) {
err_i = errno;
ret_i = -1;
goto done3;
}
len_i = sat_u.st_size;
}
do {
// XX fallback on any errors?
//
if ( 0 > (sen_i = sendfile64(dst_i, src_i, &off_i, len_i)) ) {
err_i = errno;
ret_i = -1;
goto done3;
}
len_i -= off_i;
}
while ( len_i );
ret_i = 0;
goto done3;
}
#elif defined(U3_OS_osx)
if ( !fcopyfile(src_i, dst_i, NULL, COPYFILE_ALL) ) {
ret_i = 0;
goto done3;
}
// XX fallback on any errors?
//
#endif
{
size_t pag_i = 1 << 14;;
c3_y* buf_y = c3_malloc(pag_i);
ret_i = _king_copy_raw(src_i, dst_i, buf_y, pag_i);
err_i = errno;
c3_free(buf_y);
}
done3:
close(dst_i);
done2:
close(src_i);
done1:
errno = err_i;
return ret_i;
}
}
/* _king_copy_vere(): copy current binary into $pier/.bin (COW if possible)
*/
static c3_i
_king_copy_vere(c3_c* pac_c, c3_c* ver_c, c3_c* arc_c, c3_t lin_t)
{
c3_c* bin_c;
c3_i ret_i;
if ( _king_make_pace(pac_c) ) {
return -1; // XX
}
ret_i = asprintf(&bin_c, "%s/.bin/%s/vere-v%s-%s" U3_BIN_SUFFIX,
u3_Host.dir_c, pac_c, ver_c, arc_c);
c3_assert( ret_i > 0 );
ret_i = _king_copy_file(u3_Host.dem_c, bin_c);
if ( ret_i ) {
fprintf(stderr, "vere: copy %s -> %s failed: %s\r\n",
bin_c, u3_Host.dem_c, strerror(errno));
c3_free(bin_c);
return -1;
}
// XX option
//
if ( lin_t ) {
if ( _king_link_run(bin_c) ) {
fprintf(stderr, "vere: link %s/%s failed\n", u3_Host.dir_c, U3_BIN_ALIAS);
c3_free(bin_c);
return -1;
}
}
c3_free(bin_c);
return 0;
}
/* u3_king_dock(): copy binary into pier on boot.
*/
void
u3_king_dock(c3_c* pac_c)
{
c3_c* arc_c = "unknown";
#ifdef U3_OS_ARCH
arc_c = U3_OS_ARCH;
#endif
// XX get link option
//
if ( _king_copy_vere(pac_c, URBIT_VERSION, arc_c, 1) ) {
u3l_log("vere: binary copy failed\r\n");
u3_king_bail();
exit(1);
}
else {
// NB: failure ignored
//
_king_init_pace(pac_c);
u3l_log("vere: binary copy succeeded\r\n");
// XX print restart instructions
}
}
/* _king_done_cb(): /* _king_done_cb():
*/ */
static void static void
@ -871,6 +1507,53 @@ u3_king_done(void)
{ {
uv_handle_t* han_u = (uv_handle_t*)&u3K.tim_u; uv_handle_t* han_u = (uv_handle_t*)&u3K.tim_u;
// get next binary
//
if ( c3y == u3_Host.nex_o ) {
c3_c* pac_c;
c3_c* ver_c;
// hack to ensure we only try once
//
u3_Host.nex_o = c3n;
pac_c = _king_get_pace();
switch ( u3_king_next(pac_c, &ver_c) ) {
case -2: {
u3l_log("vere: unable to check for next version\n");
} break;
case -1: {
u3l_log("vere: up to date\n");
} break;
case 0: {
u3l_log("vere: next (%%%s): %s\n", pac_c, ver_c);
_king_do_upgrade(pac_c, ver_c);
c3_free(ver_c);
} break;
default: c3_assert(0);
}
c3_free(pac_c);
}
else if ( c3y == u3_Host.pep_o ) {
u3l_log("vere: ready for upgrade\n");
}
// copy binary into pier on boot
//
if ( (c3y == u3_Host.ops_u.nuu)
&& (c3y == u3_Host.ops_u.doc) )
{
// hack to ensure we only try once
//
u3_Host.ops_u.nuu = c3n;
u3_king_dock(U3_VERE_PACE);
}
// XX hack, if pier's are still linked, we're not actually done // XX hack, if pier's are still linked, we're not actually done
// //
if ( !u3K.pir_u && !uv_is_closing(han_u) ) { if ( !u3K.pir_u && !uv_is_closing(han_u) ) {
@ -880,6 +1563,10 @@ u3_king_done(void)
u3_term_log_exit(); u3_term_log_exit();
fflush(stdout); fflush(stdout);
} }
// XX remove move
//
exit(0);
} }
/* u3_king_exit(): shutdown gracefully /* u3_king_exit(): shutdown gracefully

View File

@ -469,14 +469,14 @@ static void
_pier_on_scry_done(void* ptr_v, u3_noun nun) _pier_on_scry_done(void* ptr_v, u3_noun nun)
{ {
u3_pier* pir_u = ptr_v; u3_pier* pir_u = ptr_v;
u3_weak res = u3r_at(7, nun); u3_weak res = u3r_at(7, nun);
if (u3_none == res) { if (u3_none == res) {
u3l_log("pier: scry failed\n"); u3l_log("pier: scry failed\n");
} }
else { else {
u3_weak out, pad; u3_weak out;
c3_c *ext_c, *pac_c; c3_c *ext_c, *pac_c;
u3l_log("pier: scry succeeded\n"); u3l_log("pier: scry succeeded\n");
@ -506,30 +506,13 @@ _pier_on_scry_done(void* ptr_v, u3_noun nun)
u3z(puf); u3z(puf);
} }
// try to build export target path
//
{
u3_noun pro = u3m_soft(0, _pier_stab, u3i_string(pac_c));
if ( 0 == u3h(pro) ) {
c3_w len_w = u3kb_lent(u3k(u3t(pro)));
pad = u3nt(c3_s4('.', 'u', 'r', 'b'),
c3_s3('p', 'u', 't'),
u3qb_scag(len_w - 1, u3t(pro)));
}
else {
u3l_log("pier: invalid export path %s\n", pac_c);
pad = u3_none;
}
u3z(pro);
}
// if serialization and export path succeeded, write to disk // if serialization and export path succeeded, write to disk
// //
if ( (u3_none != out) && (u3_none != pad) ) { if ( u3_none != out ) {
c3_c fil_c[256]; c3_c fil_c[256];
snprintf(fil_c, 256, "%s.%s", pac_c + 1, ext_c); snprintf(fil_c, 256, "%s.%s", pac_c + 1, ext_c);
u3_unix_save(fil_c, pad); u3_unix_save(fil_c, out);
u3l_log("pier: scry result in %s/.urb/put/%s\n", u3_Host.dir_c, fil_c); u3l_log("pier: scry result in %s/.urb/put/%s\n", u3_Host.dir_c, fil_c);
} }
} }
@ -1049,7 +1032,17 @@ _pier_play(u3_play* pay_u)
} }
else if ( pay_u->eve_d == log_u->dun_d ) { else if ( pay_u->eve_d == log_u->dun_d ) {
u3_lord_save(pir_u->god_u); u3_lord_save(pir_u->god_u);
_pier_wyrd_init(pir_u);
// early exit, preparing for upgrade
//
// XX check kelvins?
//
if ( c3y == u3_Host.pep_o ) {
u3_pier_exit(pir_u);
}
else {
_pier_wyrd_init(pir_u);
}
} }
} }
else { else {
@ -1431,7 +1424,16 @@ _pier_on_lord_live(void* ptr_v)
_pier_play_init(pir_u, eve_d); _pier_play_init(pir_u, eve_d);
} }
else { else {
_pier_wyrd_init(pir_u); // early exit, preparing for upgrade
//
// XX check kelvins?
//
if ( c3y == u3_Host.pep_o ) {
u3_pier_exit(pir_u);
}
else {
_pier_wyrd_init(pir_u);
}
} }
} }
} }

View File

@ -1 +1 @@
1.9-rc3 1.9