Close Menu

    Subscribe to Updates

    Get the latest creative news from FooBar about art, design and business.

    What's Hot

    U Mobile deploys ULTRA5G in Kota Kinabalu

    AKASO Launches Keychain 2: A Pocket-Sized 4K Action Camera Built for Creators on the Move

    Huawei Malaysia beings preorders for Pura 80

    Facebook X (Twitter) Instagram
    • Artificial Intelligence
    • Business Technology
    • Cryptocurrency
    • Gadgets
    • Gaming
    • Health
    • Software and Apps
    • Technology
    Facebook X (Twitter) Instagram Pinterest Vimeo
    Tech AI Verse
    • Home
    • Artificial Intelligence

      Blue-collar jobs are gaining popularity as AI threatens office work

      August 17, 2025

      Man who asked ChatGPT about cutting out salt from his diet was hospitalized with hallucinations

      August 15, 2025

      What happens when chatbots shape your reality? Concerns are growing online

      August 14, 2025

      Scientists want to prevent AI from going rogue by teaching it to be bad first

      August 8, 2025

      AI models may be accidentally (and secretly) learning each other’s bad behaviors

      July 30, 2025
    • Business

      Why Certified VMware Pros Are Driving the Future of IT

      August 24, 2025

      Murky Panda hackers exploit cloud trust to hack downstream customers

      August 23, 2025

      The rise of sovereign clouds: no data portability, no party

      August 20, 2025

      Israel is reportedly storing millions of Palestinian phone calls on Microsoft servers

      August 6, 2025

      AI site Perplexity uses “stealth tactics” to flout no-crawl edicts, Cloudflare says

      August 5, 2025
    • Crypto

      Japan Auto Parts Maker Invests US Stablecoin Firm and Its Stock Soars

      August 29, 2025

      Stablecoin Card Firm Rain Raise $58M from Samsung and Sapphire

      August 29, 2025

      Shark Tank Star Kevin O’Leary Expands to Bitcoin ETF

      August 29, 2025

      BitMine Stock Moves Opposite to Ethereum — What Are Analysts Saying?

      August 29, 2025

      Argentina’s Opposition Parties Reactivate LIBRA Investigation Into President Milei

      August 29, 2025
    • Technology

      It’s time we blow up PC benchmarking

      August 29, 2025

      If my Wi-Fi’s not working, here’s how I find answers

      August 29, 2025

      Asus ROG NUC 2025 review: Mini PC in size, massive in performance

      August 29, 2025

      20 free ‘hidden gem’ apps I install on every Windows PC

      August 29, 2025

      Lowest price ever: Microsoft Office at $25 over Labor Day weekend

      August 29, 2025
    • Others
      • Gadgets
      • Gaming
      • Health
      • Software and Apps
    Check BMI
    Tech AI Verse
    You are at:Home»Technology»Writing a basic service for GNU Guix
    Technology

    Writing a basic service for GNU Guix

    TechAiVerseBy TechAiVerseAugust 3, 2025No Comments11 Mins Read2 Views
    Facebook Twitter Pinterest Telegram LinkedIn Tumblr Email Reddit
    Share
    Facebook Twitter LinkedIn Pinterest WhatsApp Email

    BMI Calculator – Check your Body Mass Index for free!

    Writing a basic service for GNU Guix

    Table of Contents

    • Reading the docs
    • Reading some source
    • Writing kmonad-service-type
    • Using kmonad-service-type
    • A simpler daemon?
    • References

    Let’s write a service for GNU Guix that will automatically start
    kmonad at boot, and keep it running until shutdown.

    Note: before we proceed, we could make a kmonad “daemon” without
    going to the trouble of writing a Guix-specific service: we could
    start kmonad in a shell profile, write a cron job, or something
    else. The user experience of these methods might be the same, but
    writing a service keeps things organized and seems like the “right”
    way. If nothing else, it’s a great learning opportunity.

    Reading the docs

    First let’s read the documentation of GNU Guix. We see that Guix
    offers “services” which extend the functionality of the operating
    system. The manual says:

    Guix system services are connected by extensions. For instance, the
    secure shell service extends the Shepherd—the initialization system,
    running as PID 1—by giving it the command lines to start and stop the
    secure shell daemon (see openssh-service-type); the UPower service
    extends the D-Bus service by passing it its .service specification,
    and extends the udev service by passing it device management rules
    (see upower-service); the Guix daemon service extends the Shepherd by
    passing it the command lines to start and stop the daemon, and extends
    the account service by passing it a list of required build user
    accounts (see Base Services).

    The Guix daemon sounds very similar to what we’re looking to
    construct. Nearby, we see that the Guix daemon defines its own
    “service type”:

    (define guix-service-type
      (service-type
       (name 'guix)
       (extensions
        (list (service-extension shepherd-root-service-type guix-shepherd-service)
              (service-extension account-service-type guix-accounts)
              (service-extension activation-service-type guix-activation)))
       (default-value (guix-configuration))))
    

    Hm. I didn’t expect we’d need to instatiate our own data type just to
    create a simple init daemon. After all, kmonad is simpler than the
    guix daemon – it just needs to start at boot, and that’s it. The
    documentation is terse and it’s not clear whether there’s a better way
    to proceed. Maybe if we find a simple example of service, we can build
    off of it.

    Reading some source

    I saw that “Game Services” were listed in the manual. Maybe there’s a
    simple one we can make a template of. First, we find the definition of
    the games services /gnu/services/games.scm:

    ;;; GNU Guix --- Functional package management for GNU
    ;;; Copyright © 2018 Arun Isaac 
    ;;;
    ;;; This file is part of GNU Guix.
    ;;;
    ;;; GNU Guix is free software; you can redistribute it and/or modify it
    ;;; under the terms of the GNU General Public License as published by
    ;;; the Free Software Foundation; either version 3 of the License, or (at
    ;;; your option) any later version.
    ;;;
    ;;; GNU Guix is distributed in the hope that it will be useful, but
    ;;; WITHOUT ANY WARRANTY; without even the implied warranty of
    ;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    ;;; GNU General Public License for more details.
    ;;;
    ;;; You should have received a copy of the GNU General Public License
    ;;; along with GNU Guix.  If not, see .
    
    (define-module (gnu services games)
      #:use-module (gnu services)
      #:use-module (gnu services shepherd)
      #:use-module (gnu packages admin)
      #:use-module (gnu packages games)
      #:use-module (gnu system shadow)
      #:use-module (guix gexp)
      #:use-module (guix modules)
      #:use-module (guix records)
      #:use-module (ice-9 match)
      #:export (wesnothd-configuration
                wesnothd-configuration?
                wesnothd-service-type))
    
    ;;;
    ;;; The Battle for Wesnoth server
    ;;;
    
    (define-record-type* 
      wesnothd-configuration make-wesnothd-configuration wesnothd-configuration?
      (package wesnothd-configuration-package
               (default wesnoth-server))
      (port wesnothd-configuration-port
            (default 15000)))
    
    (define %wesnothd-accounts
      (list (user-account
             (name "wesnothd")
             (group "wesnothd")
             (system? #t)
             (comment "Wesnoth daemon user")
             (home-directory "/var/empty")
             (shell (file-append shadow "/sbin/nologin")))
            (user-group
             (name "wesnothd")
             (system? #t))))
    
    (define wesnothd-shepherd-service
      (match-lambda
        (($  package port)
         (with-imported-modules (source-module-closure
                                 '((gnu build shepherd)))
           (shepherd-service
            (documentation "The Battle for Wesnoth server")
            (provision '(wesnoth-daemon))
            (requirement '(networking))
            (modules '((gnu build shepherd)))
            (start #~(make-forkexec-constructor/container
                      (list #$(file-append package "/bin/wesnothd")
                            "-p" #$(number->string port))
                      #:user "wesnothd" #:group "wesnothd"))
            (stop #~(make-kill-destructor)))))))
    
    (define wesnothd-service-type
      (service-type
       (name 'wesnothd)
       (description
        "Run The Battle for Wesnoth server @command{wesnothd}.")
       (extensions
        (list (service-extension account-service-type
                                 (const %wesnothd-accounts))
              (service-extension shepherd-root-service-type
                                 (compose list wesnothd-shepherd-service))))
       (default-value (wesnothd-configuration))))
    

    There’s only one game contained in (gnu services games) and that is
    The Battle for Wesnoth Server. The Game Services reference says

    To run wesnothd in the default configuration, instantiate it as:

    (service wesnothd-service-type)

    So far, we have a module that exports wesnothd-service-type and a
    way to use that binding in our system config to declare a service.
    Apparently, a new service-type is what we’re looking for after
    all. Let’s start by implementing a kmonad-service-type and see where
    that takes us.

    Writing kmonad-service-type

    A service-type (see Service Reference) takes a name, description,
    and a list of service-extension objects, each of which describes how
    to extend existing services. So what exactly is a
    object? The service-extension documentation
    says:

    Scheme Procedure: service-extension target-type compute

    Return a new extension for services of type target-type. compute must
    be a one-argument procedure: fold-services calls it, passing it the
    value associated with the service that provides the extension; it must
    return a valid value for the target service.

    So in the context of (define kmonad-service-type ...), a compute
    function will receive the (single) value associated with
    kmonad-service-type (a value which we have yet to define) and will
    return the value required by target-type, which is the service being
    extended. The wesnothd-service-type extends two services: the
    account-service-type which is extended by a list of user-group and
    user-account objects; and the shepherd-root-service-type which is
    extended by a list of objects.

    So which services should kmonad extend? It’s recommended to make a
    dedicated user and group for a daemon (see stackoverflow), so let’s
    extend the account-service-type with a new user and group to run the
    kmonad daemon. Let’s also extend the shepherd-root-service-type
    because we want our daemon managed by the init system, Shepherd:

    (define kmonad-service-type
      (service-type
       (name 'kmonad)
       (description
        "Run kmonad as a daemon.")
       (extensions
        (list (service-extension account-service-type
                                 (const %kmonad-daemon-accounts))
              (service-extension shepherd-root-service-type
                                 (compose list kmonad-shepherd-service))))))
    

    It looks pretty similar to wesnothd-service-type, but without the
    default-value, which doesn’t really apply to kmonad. Next, we’ve got
    to choose how to extend the account-service-type and the
    shepherd-root-service-type.

    Extending the account-service-type

    As noted above, (const %kmonad-daemon-accounts) needs to evaluate to a
    function of a single argument that returns a list of users and
    groups. Well, const makes a function that takes some argument, and
    then just returns whatever was passed to const–in this case,
    %kmonad-daemon-accounts. All we’ve got to do is define
    %kmonad-daemon-accounts.

    We need one daemon user and one group. Our daemon user won’t require
    login, so we’ll use /sbin/nologin (see man 8 nologin) like
    %wesnothd-accounts. Similarly, it won’t require a home directory. We
    can pretty much copy the %wesnothd-accounts to make
    %kmonad-daemon-accounts:

    (define %kmonad-daemon-accounts
      (list (user-account
             (name "kmonad-daemon")
             (group "kmonad-daemon")
             (system? #t)
             (comment "kmonad daemon user")
             (home-directory "/var/empty")
             (shell (file-append shadow "/sbin/nologin")))
            (user-group
             (name "kmonad-daemon")
             (system? #t))))
    

    Extending the shepherd-root-service-type

    As noted above, (compose list kmonad-shepherd-service) needs to
    evaluate to a function of a single argument (a single argument of our
    choice) and return a list of objects. That means
    that kmonad-shepherd-service must take a single argument, and return
    a single shepherd-service! Before we write
    kmonad-shepherd-service, let’s decide what we want to pass
    it. Minimally, kmonad needs a .kbd file to run. In theory, a user
    could also specify other stuff (e.g. a log level), but let’s just
    start with the .kbd path.

    If we’re using wesnoth-shepherd-service as an example, we’ll need to
    think about the requirement, start, and stop fields.

    requirement field

    We definitely need to wait for udev, but maybe we should also wait
    for user-processes like the other daemons in the shepherd graph
    do. Looking at the source for user-processes:

    This is a synchronization point used to make sure user processes and daemons
    get started only after crucial initial services have been started—file
    system mounts, etc. This is similar to the ‘sysvinit’ target in systemd.

    In theory we could explicitly wait for other shepherd services, but it
    seems like this catch-all applies perfectly to our use case. Come to
    think of it, I wonder why wesnothd doesn’t depend on
    user-processes too.

    start and stop fields

    According to the Shepherd Services documentation, the start and
    stop fields of shepherd-service take G-Expressions. But what’s a
    g-expression? Well, because Guix uses Scheme for both higher-level
    actions–like defining packages–and lower-level actions–like
    building derivations generated by packages– it needs a faculty for
    embedding lower-level code in higher-level code. So in the start
    field of wesnoth-shepherd-service:

    (start #~(make-forkexec-constructor/container
              (list #$(file-append package "/bin/wesnothd")
                    "-p" #$(number->string port))
              #:user "wesnothd" #:group "wesnothd"))
    

    Some lower-level code is passed with #~(...), within which
    higher-level code is escaped with #$(...) which the compiler is able
    to “lower” to lower-level code. Looking at the source for
    make-forkexec-constructor/container, we see:

    This is a variant of ‘make-forkexec-constructor’ that starts COMMAND in
    NAMESPACES, a list of Linux namespaces such as ‘(mnt ipc). MAPPINGS is the
    list of to make in the case of a separate mount
    namespace, in addition to essential bind-mounts such /proc.

    And the documentation for make-forkexec-contructor reads:

    Return a procedure that forks a child process, closes all file
    descriptors except the standard output and standard error descriptors,
    sets the current directory to directory, sets the umask to
    file-creation-mask unless it is #f, changes the environment to
    environment-variables (using the environ procedure), sets the current
    user to user and the current group to group unless they are #f, and
    executes command (a list of strings.) The result of the procedure will
    be the PID of the child process. Note that this will not work as
    expected if the process “daemonizes” (forks); in that case, you will
    need to pass #:pid-file, as explained below.

    When pid-file is true, it must be the name of a PID file associated
    with the process being launched; the return value is the PID once that
    file has been created. If pid-file does not show up in less than
    pid-file-timeout seconds, the service is considered as failing to
    start.

    When log-file is true, it names the file to which the service’s
    standard output and standard error are redirected. log-file is created
    if it does not exist, otherwise it is appended to.

    The wesnoth source appears not to use the NAMESPACES feature of
    the containerized-version, so we’ll stick with
    make-forkexec-constructor as is used in the syslogd example.

    kmonad-shepherd-service

    Let’s put it all together:

    (define (kmonad-shepherd-service kbd-path)
      (shepherd-service
       (documentation "Run the kmonad daemon (kmonad-daemon)." )
       (provision '(kmonad-daemon))
       (requirement '(udev user-processes))
       (start #~(make-forkexec-constructor
                 (list #$(file-append kmonad "/bin/kmonad")
                       #$kbd-path "-l info")
                 #:user "kmonad-daemon" #:group "kmonad-daemon"
                 #:log-file "/var/log/kmonad.log"))
       (stop #~(make-kill-destructor))))
    

    Making a module

    Now let’s package our code into a module. If the location where we
    keep local guix modules is ~/local-guix, then we can add our module
    at ~/local-guix/my/services/kmonad.scm as:

    (define-module (my services kmonad)
      #:use-module (gnu services)
      #:use-module (gnu services shepherd)
      #:use-module (gnu packages haskell-apps)
      #:use-module (gnu system shadow)
      #:use-module (guix gexp)
      #:export (kmonad-service-type))
    
    (define %kmonad-daemon-accounts
      (list (user-account
             (name "kmonad-daemon")
             (group "kmonad-daemon")
             (system? #t)
             (comment "kmonad daemon user")
             (home-directory "/var/empty")
             (shell (file-append shadow "/sbin/nologin")))
            (user-group
             (name "kmonad-daemon")
             (system? #t))))
    
    (define (kmonad-shepherd-service kbd-path)
      (shepherd-service
       (documentation "Run the kmonad daemon (kmonad-daemon)." )
       (provision '(kmonad-daemon))
       (requirement '(udev user-processes))
       (start #~(make-forkexec-constructor
                 (list #$(file-append kmonad "/bin/kmonad")
                       #$kbd-path "-l info")
                 #:user "kmonad-daemon" #:group "kmonad-daemon"
                 #:log-file "/var/log/kmonad.log"))
       (stop #~(make-kill-destructor))))
    
    (define kmonad-service-type
      (service-type
       (name 'kmonad)
       (description
        "Run kmonad as a daemon.")
       (extensions
        (list (service-extension account-service-type
                                 (const %kmonad-daemon-accounts))
              (service-extension shepherd-root-service-type
                                 (compose list kmonad-shepherd-service))))))
    

    Using kmonad-service-type

    Now adding a kmonad service is straightforward. Let’s add some other
    kmonad-specific configuration to our system while we’re at it:

    (use-modules (my services kmonad))
    ;; more modules
    
    (operating-system
      (users
        (append (list (user-account
                        (supplementary-groups
                          '("input" ;; needed by kmonad
                            ;; more groups
                           ))
                        ;; more fields
                      )
                      ;; more users
                )
                %base-user-accounts))
      (packages
        (append (list
                 "kmonad"
                 ;; more packages
                )
                %base-packages))
      (services
        (append (list (service kmonad-service-type "/path/to/config.kbd")
                      ;; more services
                )
                (modify-services %desktop-services ;; needed to add kmonad udev rules
                  (udev-service-type config =>
                    (udev-configuration (inherit config)
                      (rules (cons kmonad
                        (udev-configuration-rules config))))))))
      ;; more fields
    )
    

    And reconfigure:

    sudo guix system -L ~/local-guix reconfigure /path/to/config.scm
    

    A simpler daemon?

    I think it would be possible to add kmonad as a simple-service on top of
    shepherd-root-service-type. After looking at the simple-service
    source, we can try:

    (simple-service 'kmonad-service shepherd-root-service-type
                    (list (shepherd-service
                           (documentation "Run the kmonad daemon (kmonad-daemon)." )
                           (provision '(kmonad-daemon))
                           (requirement '(udev user-processes))
                           (start #~(make-forkexec-constructor
                                     (list #$(file-append kmonad "/bin/kmonad")
                                           #$kbd-path "-l info")
                                     #:log-file "/var/log/kmonad.log"))
                           (stop #~(make-kill-destructor)))))
    
    

    References

    BMI Calculator – Check your Body Mass Index for free!

    Share. Facebook Twitter Pinterest LinkedIn Reddit WhatsApp Telegram Email
    Previous ArticleParsing without ASTs and Optimizing with Sea of Nodes [video]
    Next Article Seed7 – The Extensible Programming Language
    TechAiVerse
    • Website

    Jonathan is a tech enthusiast and the mind behind Tech AI Verse. With a passion for artificial intelligence, consumer tech, and emerging innovations, he deliver clear, insightful content to keep readers informed. From cutting-edge gadgets to AI advancements and cryptocurrency trends, Jonathan breaks down complex topics to make technology accessible to all.

    Related Posts

    It’s time we blow up PC benchmarking

    August 29, 2025

    If my Wi-Fi’s not working, here’s how I find answers

    August 29, 2025

    Asus ROG NUC 2025 review: Mini PC in size, massive in performance

    August 29, 2025
    Leave A Reply Cancel Reply

    Top Posts

    Ping, You’ve Got Whale: AI detection system alerts ships of whales in their path

    April 22, 2025166 Views

    6.7 Cummins Lifter Failure: What Years Are Affected (And Possible Fixes)

    April 14, 202548 Views

    New Akira ransomware decryptor cracks encryptions keys using GPUs

    March 16, 202530 Views

    Is Libby Compatible With Kobo E-Readers?

    March 31, 202528 Views
    Don't Miss
    Gadgets August 29, 2025

    U Mobile deploys ULTRA5G in Kota Kinabalu

    U Mobile deploys ULTRA5G in Kota Kinabalu After unveiling its new ULTRA5G network for in-building…

    AKASO Launches Keychain 2: A Pocket-Sized 4K Action Camera Built for Creators on the Move

    Huawei Malaysia beings preorders for Pura 80

    It’s time we blow up PC benchmarking

    Stay In Touch
    • Facebook
    • Twitter
    • Pinterest
    • Instagram
    • YouTube
    • Vimeo

    Subscribe to Updates

    Get the latest creative news from SmartMag about art & design.

    About Us
    About Us

    Welcome to Tech AI Verse, your go-to destination for everything technology! We bring you the latest news, trends, and insights from the ever-evolving world of tech. Our coverage spans across global technology industry updates, artificial intelligence advancements, machine learning ethics, and automation innovations. Stay connected with us as we explore the limitless possibilities of technology!

    Facebook X (Twitter) Pinterest YouTube WhatsApp
    Our Picks

    U Mobile deploys ULTRA5G in Kota Kinabalu

    August 29, 20252 Views

    AKASO Launches Keychain 2: A Pocket-Sized 4K Action Camera Built for Creators on the Move

    August 29, 20252 Views

    Huawei Malaysia beings preorders for Pura 80

    August 29, 20252 Views
    Most Popular

    Xiaomi 15 Ultra Officially Launched in China, Malaysia launch to follow after global event

    March 12, 20250 Views

    Apple thinks people won’t use MagSafe on iPhone 16e

    March 12, 20250 Views

    French Apex Legends voice cast refuses contracts over “unacceptable” AI clause

    March 12, 20250 Views
    © 2025 TechAiVerse. Designed by Divya Tech.
    • Home
    • About Us
    • Contact Us
    • Privacy Policy
    • Terms & Conditions

    Type above and press Enter to search. Press Esc to cancel.