Posts tagged tutorial


Interacting with OneDrive in Nextcloud by using RClone

:: admin, microsoft, nextcloud, onedrive, system, tutorial

By: Maciej Barć

How it works

  • There will be a FUSE OneDrive filesystem mounted on the machine Nextcloud runs on,
  • You configure filesystem-local “External storage” and point to the mountpoint of a cloud drive (in this case OneDrive),
  • users connected via Nextcloud Client will have a option to sync any chosen files from the External storage as if they were Nextcloud-owned files.

Benefits

There are some benefits from connecting OneDrive to Nextcloud:

  • faster cached sync than by normal straight-to OneDrive connection - files will be pushed to Nextcloud and then uploaded to Nextcloud, this will take less time than uploading straight to OneDrive because of Microsoft OneDrive rate-limiting the uploads (especially in case of larger files),
  • clients of Nextcloud do not have to configure any OneDrive connection,
  • you will be able to have two-way sync of your OneDrive files (currently two-way sync on the RClone side is experimental, this will use Nextcloud’s two-way sync mechanism).

Set up RClone

First You will have to set up RClone. Connect to your cloud of choice and then copy the config to a location that will be readable by a service that mounts a given cloud drive.

For OneDrive I use /usr/local/share/rclone/config/rclone.conf that is accessible only to the apache user.

The config will look something like this:

1
2
3
4
5
6
[OneDrive]
type = onedrive
region = global
token = *REDACTED*
drive_id = *REDACTED*
drive_type = personal

Mounting OneDrive

I created this helper script (in /usr/local/bin/rclone-mount.sh):

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
#!/bin/sh

set -e
set -u
trap "exit 128" INT

conf_path="${1}"

local_user="${2}"
local_path="${3}"

cloud_name="${4}"
cloud_path="${5}"

log_dir="/var/log/rclone"
log_file="${log_dir}/rclone-mount-${local_user}-${cloud_name}.log"

mkdir -p "${log_dir}"
touch "${log_file}"

chmod a+r "${log_file}"
chown "${local_user}" "${log_file}"

exec rclone                                         \
     --default-permissions                          \
     --allow-other                                  \
     --verbose                                      \
     --vfs-cache-mode full                          \
     --config "${conf_path}"                        \
     mount                                          \
     "${cloud_name}:${cloud_path}" "${local_path}"  \
     >> "${log_file}" 2>&1

Then, I use it in a OpenRC service like this (/etc/init.d/mount-OneDrive):

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
#!/sbin/openrc-run

conf_path="/usr/local/share/rclone/config/rclone.conf"

cloud_name="OneDrive"
cloud_path="/"

local_user="apache"
local_path="/mnt/${cloud_name}"

command="/usr/local/bin/rclone-mount.sh"
command_args="${conf_path} ${local_user} ${local_path} ${cloud_name} ${cloud_path}"

command_background="false"
command_user="${local_user}:$(id -g -n ${local_user})"
supervisor="supervise-daemon"

depend() {
    need net
}

start_pre() {
    ebegin "Unmounting leftovers from ${local_path} before service start"
    umount "${local_path}"
    eend 0
}

stop_post() {
    ebegin "Unmounting leftovers from ${local_path} after service stop"
    umount "${local_path}"
    eend 0
}

Enabling the RClone service

Set up directories and correct permissions:

1
2
3
4
5
mkdir -p /usr/local/share/rclone/config
chown -R apache:apache /usr/local/share/rclone/config

mkdir -p /var/log/rclone
chown -R apache:apache /var/log/rclone

Do not forget to make the mount service script executable:

1
chmod +x /etc/init.d/mount-OneDrive

Enable and start this service on OpenRC:

1
2
rc-update add mount-OneDrive default
rc-service mount-OneDrive start

Drive permissions

RClone mounts cloud drives by using FUSE. To have the RClone option --allow-other available in order to allow root to access the drive you will have to modify the FUSE config file (/etc/fuse.conf) - add user_allow_other.

Nextcloud configuration

Download and enable the “External storage” app. Then, in “Administration” settings add a external storage:

  • name: ExternalStorage_OneDrive
  • type: Local
  • authentication: None
  • configuration: /mnt/OneDrive
  • available for: YOUR USER

nextcloud admin external storage onedrive

GHC 9.8 and Cabal 3.10.3 on Gentoo Linux

:: gentoo, haskell, programming language, system, tutorial

By: Maciej Barć

The official ::gentoo repository currently contains only GHC on version 9.2.8. To install newer GHC one has to either download/build themselves or use the ::haskell overlay (https://github.com/gentoo-haskell/gentoo-haskell).

Enable the ::haskell overlay

Enable:

1
eselect repository enable haskell

Sync:

1
2
3
emerge --sync haskell
egencache --update --repo haskell --jobs 12 --load 6
eix-update

Unmask needed packages

Add to /etc/portage/package.unmask/0000_hs.conf

1
2
3
4
5
6
7
8
<dev-lang/ghc-9.9

<dev-haskell/cabal-3.11
<dev-haskell/cabal-install-3.11
<dev-haskell/cabal-install-solver-3.11
<dev-haskell/cabal-syntax-3.11
<dev-haskell/text-2.2
<dev-haskell/parsec-3.1.18

Add to /etc/portage/package.accept_keywords/0000_hs.conf

1
2
3
app-admin/haskell-updater
dev-haskell/*
dev-lang/ghc

Install

1
emerge --ask --verbose ">=dev-lang/ghc-9.8" ">=dev-haskell/cabal-install-3.10"

Build of GHC 9.8 takes around ~2 hours on a 8-core laptop-grade CPU.

Bonus: masking packages from ::haskell

If you want to exclude a given version from the ::haskell overly from being installed/updated, then you can add a similar line(s) to /etc/portage/package.mask/0000_hs.conf:

1
app-emacs/haskell-mode::haskell

Safer Nix installation

:: linux, nix, packaging, sandbox, shell, system, test, testing, tutorial

By: Maciej Barć

Nix is useful for quickly testing out software and providing a strict environment that can be shared between people.

Today I’m trying out Nix again, this time I want to do it my way.

Installation process

Nix store

I know Nix needs “Nix store” installation on / (the system root).

Create it manually to prevent the installation script from calling sudo. 1st I switch to the root account, and then I run:

1
2
mkdir -p -m 0755 /nix
chown -R xy:xy /nix

Running the install script

Download the Nix install script and examine the contents.

1
curl -L https://nixos.org/nix/install > nix_install.sh

Then, run it with --no-daemon to prevent it running as system service.

1
sh ./nix_install.sh --no-daemon
performing a single-user installation of Nix...
copying Nix to /nix/store...
installing 'nix-2.20.1'
building '/nix/store/1ahlg3bviy174d6ig1gn393c23sqlki6-user-environment.drv'...
unpacking channels...
modifying /home/xy/.bash_profile...
modifying /home/xy/.zshenv...
placing /home/xy/.config/fish/conf.d/nix.fish...

Installation finished!  To ensure that the necessary environment
variables are set, either log in again, or type

. /home/xy/.nix-profile/etc/profile.d/nix.fish

in your shell.

Wait!

modifying /home/xy/.bash_profile...
modifying /home/xy/.zshenv...
placing /home/xy/.config/fish/conf.d/nix.fish...

That’s very rude!

Stopping Nix from making a mess

I need to prevent Nix from mess up with my environment when I do not want it to. Nix puts some code into the Bash, ZSH and Fish initialization files during installation to ease it’s use. I do not want that since I do not want Nix to meddle with my environment without me knowing it.

I keep my .bash_profile and .zshenv in a stow-managed git repo so I can just cd into my repo and do git reset --hard, but for you will have to revert those files to their old forms manually.

Playing with Nix

We do not have nix in PATH but we still can launch it. Nix executables are located inside ~/.nix-profile/bin/.

By invoking nix-shell one can create a ephemeral environment containing only packages specified after the -p flag. I always add -p nix to have the Nix tools available also inside the spawned environment.

I will test out chibi (small Scheme interpreter) + rlwrap (REPL support for software lacking it) inside a Nix ephemeral environment:

1
~/.nix-profile/bin/nix-shell -p nix chibi rlwrap

Inside the spawned shell:

1
rlwrap chibi-scheme

In the chibi REPL, let’s see the contents of the PATH environment variable:

1
(get-environment-variable "PATH")

And exit the Scheme REPL:

1
(exit)

After the playtime, run garbage collection:

1
~/.nix-profile/bin/nix-collect-garbage

Using gzexe for shipping Racket executables

:: lisp, racket, scheme, tutorial

By: Maciej Barć

Racket executables made by raco exe are known to be quite large. One of tools that can be used to help reduce the size of produced binaries is the gzexe program.

gzeze is a tool that can compress a executable binary. It can be acquired by installing gzip on most Linux distributions (included in the app-arch/gzip package on Gentoo).

Creating a hello-world executable with Racket

Write following contents to hello-world.rkt file:

1
2
3
4
5
6
7
#lang racket/base

(define (main)
  (displayln "It REPLs. Ship it!"))

(module+ main
  (main))

To make a binary run:

1
raco exe --orig-exe -v -o hello-world hello-world.rkt

The file hello-world will be produced.

This is what file hello-world says about it:

1
2
3
hello-world: ELF 64-bit LSB pie executable, x86-64, version 1 (SYSV),
dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2,
for GNU/Linux 3.2.0, stripped

This “small” executable weights 46 MB!

In comparison busybox weights around 2 MB.

Compressing with gzexe

Keep in mind that gzeze will overwrite the compressed file and create a backup with appended "~".

1
gzexe hello-world

And this gives us only 8,5 MB. Nice!

In comparison bazel, which is a single-binary build system written in JAVA, executable takes 33 MB on my Gentoo machine. I tried compressing it with gzexe and it reduces it only by 10%, to around 29 MB.

gzexeis not a silver bullet but with Racket exes it works very nicely.

Genkernel in 2023

:: gentoo, kernel, linux, sysadmin, system, tutorial

By: Maciej Barć

I really wanted to look into the new kernel building solutions for Gentoo and maybe migrate to dracut, but last time I tried, ~1.5 years ago, the initreamfs was now working for me.

And now in 2023 I’m still running genkernel for my personal boxes as well as other servers running Gentoo.

I guess some short term solutions really become defined tools :P

So this is how I rebuild my kernel nowadays:

  1. Copy old config

    1
    2
    cd /usr/src
    cp linux-6.1.38-gentoo/.config linux-6.1.41-gentoo/
    
  2. Remove old kernel build directories

    1
    rm -r linux-6.1.31-gentoo
    
  3. Run initial preparation

    1
    ( eselect kernel set 1 && cd /usr/src/linux && make olddefconfig )
    
  4. Call genkernel

     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    genkernel                                                       \
        --no-menuconfig                                             \
        --no-clean                                                  \
        --no-clear-cachedir                                         \
        --no-cleanup                                                \
        --no-mrproper                                               \
        --lvm                                                       \
        --luks                                                      \
        --mdadm                                                     \
        --nfs                                                       \
        --kernel-localversion="-$(hostname)-$(date '+%Y.%m.%d')"    \
        all
    
  5. Rebuild the modules

    If in your /etc/genkernel.conf you have MODULEREBUILD turned off, then also call emerge:

    1
    emerge -1 @module-rebuild
    

Debugging Frog blog with syntax macros

:: blog, language, lisp, macro, programming, racket, scheme, tutorial

By: Maciej Barć

Constructing debugging syntax

I wanted to echo parameter values when I set them in my blog’s frog.rkt config file.

Nothing simpler in Racket!

First I create this macro for echoing a single parameter value when it is set:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
(define-syntax-rule (verbose-set-parameter parameter-id parameter-value)
  (begin
    ;; Set the parameter.
    (parameter-id parameter-value)

    ;; Then call the parameter and print is's value.
    ;; The "'parameter-id" is special syntax
    ;; for turning a "parameter-id" identifier to a symbol.
    ;; We can also write it like:
    ;; > (quote parameter-id)
    ;; to be less confusing.
    (printf "[DEBUG] (~a ~v)\n" 'parameter-id (parameter-id))))

then, I create a wrapper for above macro that can take multiple parameter pairs:

1
2
3
4
5
(define-syntax-rule (verbose-set-parameters (parameter-id parameter-value) ...)
  (begin
    ;; Unpack a chain of "(parameter-id parameter-value)" pairs
    ;; using the "..." syntax.
    (verbose-set-parameter parameter-id parameter-value) ...))

Using the macro

Afterwards we can call it like so:

1
2
3
(verbose-set-parameters
 (current-title "XGQT's blog")
 (current-author "Maciej Barć"))

Notice that even the form of setting a parameter, that is (parameter-procedure "value"), remains the same, but in reality it is just similar to how the syntax macro pattern-matches on it.

Inspecting macro expansion

In racket-mode inside GNU Emacs we can inspect the macro expansion with racket-expand-region. Stepping through the expansion provided this result:

1
2
3
4
5
6
7
(begin
  (begin
    (current-title "XGQT's blog")
    (printf "[DEBUG] (~a ~v)\n" 'current-title (current-title)))
  (begin
    (current-author "Maciej Barć")
    (printf "[DEBUG] (~a ~v)\n" 'current-author (current-author))))

Comparing objects in Racket

:: lisp, programming language, racket, scheme, tutorial

By: Maciej Barć

Equality methods

By implementing a method for equality equal-to? and two extraction methods equal-hash-code-of and equal-secondary-hash-code-of we can define our own object comparison rules.

For more info see Object Equality and Hashing.

Consider the following example:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
(define integer%
  (class* object% (equal<%>)
    (super-new)

    (init-field [value 0])

    (define/public (equal-to? other-object recur)
      (= value (get-field value other-object)))

    (define/public (equal-hash-code-of hash-code)
      (hash-code value))

    (define/public (equal-secondary-hash-code-of hash-code)
      (hash-code value))))

If we create a new integer% object we can notice that it is not transparent (we can not inspect values of any of it’s fields).

1
2
(new integer%)
;;  => (object:integer% ...)

But if we compare two fresh integer% objects they will be equal.

1
2
(equal? (new integer%) (new integer%))
;;  => #true

Transparent class

A transparent cvlass is a class with the inspect expression valuye se to #false.

From Racket documentation Creating Classes:

Just as for structure types, an inspector controls access to the class’s fields, including private fields, and also affects comparisons using equal?.

Consider the following example:

1
2
3
4
5
6
7
8
(define integer%
  (class object%

    (super-new)

    (inspect #false)

    (init-field [value 0])))

If we create a new integer% object we can see it’s field values.

1
2
(new integer%)
;;  => (object:integer% 0)

And if we compare two fresh integer% objects they will be equal.

1
2
(equal? (new integer%) (new integer%))
;;  => #true