<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Mehmet YILMAZ</title>
    <description>The latest articles on DEV Community by Mehmet YILMAZ (@myilmaz).</description>
    <link>https://dev.to/myilmaz</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F1536001%2F49e03eb9-d342-410f-b57e-ecb06fa453a5.png</url>
      <title>DEV Community: Mehmet YILMAZ</title>
      <link>https://dev.to/myilmaz</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/myilmaz"/>
    <language>en</language>
    <item>
      <title>Manage Ubuntu Linux Networking Programmatically with Netplan and D-Bus in C/C++</title>
      <dc:creator>Mehmet YILMAZ</dc:creator>
      <pubDate>Tue, 30 Jul 2024 15:08:49 +0000</pubDate>
      <link>https://dev.to/myilmaz/manage-ubuntu-linux-networking-programmatically-with-netplan-and-d-bus-in-cc-4f45</link>
      <guid>https://dev.to/myilmaz/manage-ubuntu-linux-networking-programmatically-with-netplan-and-d-bus-in-cc-4f45</guid>
      <description>&lt;p&gt;When we need to manage network adapter settings programmatically, we often encounter the challenge of using system calls to override static configurations. These attempts frequently lead to panic and crashes in the network services. At best, we might temporarily achieve our objectives until the next reboot, and even then, a correct implementation is rare.&lt;/p&gt;

&lt;p&gt;If DHCP enters the picture, things become even more complicated. In addition to handling other complexities, we might need to implement or interact with a DHCP client.&lt;/p&gt;

&lt;p&gt;As we've all seen, getting sidetracked by unrelated and complex issues during development often leads to major setbacks and failures.&lt;/p&gt;

&lt;p&gt;In light of these challenges, we need to consider alternative approaches. Given that network management is operated by OS-provided services, we should aim to communicate correctly with these services instead of attempting to take over their roles.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/mehmet-yilmaz/netplan-dbus-example" rel="noopener noreferrer"&gt;https://github.com/mehmet-yilmaz/netplan-dbus-example&lt;/a&gt;&lt;/p&gt;




&lt;blockquote&gt;
&lt;p&gt;PS: If you work on a remote host, remember that changing the network settings may cause the connection lost. Do not apply any changes to your active connection interface if you are not sure what are you doing.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  How Do OS Services Communicate with Each Other?
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Inter-Process Communication
&lt;/h3&gt;

&lt;p&gt;Operating systems consist of multiple services and processes that need to work together smoothly. IPC is the communication layer that allows these services to exchange data and synchronize actions effectively.&lt;/p&gt;

&lt;h3&gt;
  
  
  What is D-Bus?
&lt;/h3&gt;

&lt;p&gt;D-Bus (Desktop Bus) is a message-oriented bus system and Remote Procedure Call (RPC) mechanism in Linux that enables communication between different processes on the same machine. It is designed to be simple and lightweight, suitable for both desktop and embedded environments.&lt;/p&gt;

&lt;h4&gt;
  
  
  Key Features of D-Bus:
&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;Message Passing: Processes can send and receive messages, including signals, method calls, and return values.&lt;/li&gt;
&lt;li&gt;Service Registration: Processes can register services on the bus, making functionalities available to others.&lt;/li&gt;
&lt;li&gt;Introspection: D-Bus supports dynamic discovery of services and their capabilities.&lt;/li&gt;
&lt;li&gt;Security: Includes mechanisms for authenticating and authorizing messages.&lt;/li&gt;
&lt;li&gt;Efficiency: Designed to be efficient in both performance and resource usage.&lt;/li&gt;
&lt;li&gt;Using Netplan on Ubuntu&lt;/li&gt;
&lt;li&gt;For this article, we will use Ubuntu Server 22.04 LTS and its default network configuration service, Netplan. However, you can choose any OS and network service that provides a D-Bus API.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  What is Netplan?
&lt;/h3&gt;

&lt;p&gt;Netplan is a tool that provides a high-level configuration interface for network settings using YAML files. It abstracts the network configuration by communicating with supported network services like &lt;code&gt;NetworkManager&lt;/code&gt; and &lt;code&gt;systemd-networkd&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;For more information: &lt;a href="https://netplan.readthedocs.io" rel="noopener noreferrer"&gt;Netplan Documentation&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Netplan also provides a simple D-Bus API and a CLI example for communication with the Netplan service over D-Bus:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://netplan.readthedocs.io/en/stable/netplan-dbus/" rel="noopener noreferrer"&gt;Netplan D-Bus API&lt;/a&gt;&lt;br&gt;
&lt;a href="https://netplan.readthedocs.io/en/stable/dbus-config/" rel="noopener noreferrer"&gt;CLI Example&lt;/a&gt;&lt;/p&gt;
&lt;h2&gt;
  
  
  How to use D-Bus configuration API
&lt;/h2&gt;

&lt;p&gt;See also: Netplan D-Bus reference, busctl reference. Copy the current state from/{etc,run,lib}/netplan/*.yaml by…&lt;br&gt;
netplan.readthedocs.io&lt;/p&gt;
&lt;h3&gt;
  
  
  DBus CLI Example:
&lt;/h3&gt;

&lt;p&gt;Before diving in, please try the following CLI example to get the current configuration for ensuring that all services working correctly.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;busctl is a DBus communication CLI tool already installed in the Ubuntu system, there are some other alternatives that you can install if needed.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;We will use the following command to call the &lt;code&gt;Config()&lt;/code&gt; method, which starts a session and returns the &lt;code&gt;Netplan-DBus&lt;/code&gt; configuration path.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Netplan-DBus service runs on the System Session, so you will need sudo privileges to communicate with it.&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;sudo busctl call io.netplan.Netplan /io/netplan/Netplan io.netplan.Netplan Config
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Expected output:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;o "/io/netplan/Netplan/config/ULJIU0"
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;o&lt;/code&gt; represents the output type &lt;code&gt;OBJECT_PATH&lt;/code&gt; and the &lt;code&gt;”/io/netplan/Netplan/config/ULJIU0"&lt;/code&gt; as value.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;OBJECT_PATH value is a session-based dynamic value, we will do all the configuration with this path during the session, but it will be changed on the next session after the timeout or if you call the Config method again.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Now we can call the &lt;code&gt;Get()&lt;/code&gt; method to this configuration path as &lt;code&gt;busctl call io.netplan.Netplan [ConfigurationPath] io.netplan.Netplan.Config&lt;/code&gt; Get to read the current configuration as examples here.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;sudo busctl call io.netplan.Netplan /io/netplan/Netplan/config/ULJIU0 io.netplan.Netplan.Config Get
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Expected output depends on your existing network configuration differs:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;s "network:\n  ethernets:\n    eth0:\n      dhcp4: true\n  renderer: networkd\n  version: 2\n"
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;s&lt;/code&gt; represents the output type &lt;code&gt;STRING&lt;/code&gt; and the value is the string parse output of the Netplans &lt;code&gt;.yaml&lt;/code&gt; current configuration file.&lt;/p&gt;

&lt;p&gt;Now we can move to the programming.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;PS: If you work on a remote host, remember that changing the network settings may cause the connection lost. Do not apply any changes to your active connection interface if you are not sure what are you doing.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Lets Coding…
&lt;/h2&gt;

&lt;p&gt;After this introduction, we can focus on implementing this in our C++ Program.&lt;/p&gt;

&lt;h3&gt;
  
  
  Dependencies
&lt;/h3&gt;

&lt;p&gt;There are different DBus libraries you can choose from. I prefer the dbus-cxx library because it provides the native C++ implementation with Smart pointers support.&lt;/p&gt;

&lt;p&gt;Please take a look at the project: &lt;a href="https://dbus-cxx.github.io" rel="noopener noreferrer"&gt;https://dbus-cxx.github.io&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Installation of the &lt;code&gt;dbus-cxx&lt;/code&gt; library on &lt;code&gt;Ubuntu 22.04 LTS&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Install the pre-requirements&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;≥ C++17&lt;/li&gt;
&lt;li&gt;cmake(≥3.8)&lt;/li&gt;
&lt;li&gt;make&lt;/li&gt;
&lt;li&gt;g++&lt;/li&gt;
&lt;li&gt;libsig++(≥3.0)&lt;/li&gt;
&lt;li&gt;expat&lt;/li&gt;
&lt;li&gt;libdbus
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;sudo apt install build-essentials cmake libdbus-1-dev libexpat1-dev

git clone https://github.com/dbus-cxx/libsigc--3.0.git
cd libsigc--3.0
cd build
cmake ..
make install
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Please refer to the documentation for your operation system.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://dbus-cxx.github.io" rel="noopener noreferrer"&gt;dbus-cxx: dbus-cxx Library - dbus-cxx.github.io&lt;/a&gt;&lt;br&gt;
Do not forget to link the library &lt;code&gt;dbus-cxx&lt;/code&gt; to your compiler with -&lt;code&gt;ldbus-cxx&lt;/code&gt; flag for using it.&lt;/p&gt;
&lt;h3&gt;
  
  
  Finally, we are ready to hands-on.
&lt;/h3&gt;

&lt;p&gt;I will create a service class that will interface the Netplan-DBus API and a basic CLI to interact with.&lt;/p&gt;

&lt;p&gt;Project Structure&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;netplan-dbus-example/
├─ netplan.service.hpp
├─ cli.class.hpp
├─ main.cpp
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;First of all, I will create the &lt;code&gt;netplan.service.hpp&lt;/code&gt; file&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;#ifndef ___NETPLAN_DBUS_SERVICE_HPP___
#define ___NETPLAN_DBUS_SERVICE_HPP___

#include &amp;lt;memory&amp;gt;
#include &amp;lt;string&amp;gt;
#include &amp;lt;dbus-cxx-2.0/dbus-cxx.h&amp;gt;
#include &amp;lt;dbus-cxx-2.0/dbus-cxx/path.h&amp;gt;
#include &amp;lt;dbus-cxx-2.0/dbus-cxx/dispatcher.h&amp;gt;
#include &amp;lt;dbus-cxx-2.0/dbus-cxx/connection.h&amp;gt;
#include &amp;lt;dbus-cxx-2.0/dbus-cxx/objectproxy.h&amp;gt;
#include &amp;lt;dbus-cxx-2.0/dbus-cxx/methodproxybase.h&amp;gt;

class NetplanService
{
public:
    NetplanService()
    {
        this-&amp;gt;m_path.clear(); // Ensure that it will return "True" when we call ".empty()" method as default to start a new session!
        this-&amp;gt;setConfigurationFile();
        this-&amp;gt;connect();
    };

    void connect()
    {
        try
        {
            this-&amp;gt;m_dispatcher = DBus::StandaloneDispatcher::create();

            // Netplan DBus service runs on the System Bus.
            // For connecting to Netplan, we have to create a connection in the System Bus
            // !!! IMPORTANT: System Bus requires ROOT Privilages!!!
            this-&amp;gt;m_connection = this-&amp;gt;m_dispatcher-&amp;gt;create_connection(DBus::BusType::SYSTEM);
            this-&amp;gt;m_object_proxy = this-&amp;gt;m_connection-&amp;gt;create_object_proxy("io.netplan.Netplan", "/io/netplan/Netplan");

            this-&amp;gt;m_path.clear(); // Ensure that it will return "True" when we call ".empty()" method as default to start a new session!
        }
        catch (const DBus::Error &amp;amp;err)
        {
            this-&amp;gt;handleDBusError(err);
        }
    };

    // Confguration .yaml file will be stored and processed with this filename under the default netplan configuration folder.
    void setConfigurationFile(const char *filename = "dbus-test-config")
    {
        this-&amp;gt;m_filename = filename;
    };

    const std::string configurationPath()
    {
        if (this-&amp;gt;m_path.empty())
            this-&amp;gt;initConfigurationPath();
        return this-&amp;gt;m_path;
    };

    const std::string getConfiguration()
    {
        try
        {
            const std::string path = this-&amp;gt;configurationPath();
            this-&amp;gt;m_object_proxy-&amp;gt;set_path(path);
            DBus::MethodProxy&amp;lt;std::string()&amp;gt; &amp;amp;Get = *(this-&amp;gt;m_object_proxy-&amp;gt;create_method&amp;lt;std::string()&amp;gt;("io.netplan.Netplan.Config", "Get"));
            return Get();
        }
        catch (const DBus::Error &amp;amp;err)
        {
            this-&amp;gt;handleDBusError(err);
        }
    };

    const bool setConfiguration(const std::string &amp;amp;target, const std::string &amp;amp;value)
    {
        try
        {
            const std::string path = this-&amp;gt;configurationPath();
            this-&amp;gt;m_object_proxy-&amp;gt;set_path(path);
            std::string config = target + "=" + value;
            DBus::MethodProxy&amp;lt;bool(std::string, std::string)&amp;gt; &amp;amp;Set = *(this-&amp;gt;m_object_proxy-&amp;gt;create_method&amp;lt;bool(std::string, std::string)&amp;gt;("io.netplan.Netplan.Config", "Set"));
            return Set(config, this-&amp;gt;m_filename);
        }
        catch (const DBus::Error &amp;amp;err)
        {
            this-&amp;gt;handleDBusError(err);
        }
    };
    bool tryConfiguration(const uint32_t &amp;amp;timeout)
    {
        try
        {
            const std::string path = this-&amp;gt;configurationPath();
            this-&amp;gt;m_object_proxy-&amp;gt;set_path(path);
            DBus::MethodProxy&amp;lt;bool(uint32_t)&amp;gt; &amp;amp;Try = *(this-&amp;gt;m_object_proxy-&amp;gt;create_method&amp;lt;bool(uint32_t)&amp;gt;("io.netplan.Netplan.Config", "Try"));
            return Try(timeout);
        }
        catch (DBus::Error &amp;amp;err)
        {
            this-&amp;gt;handleDBusError(err);
        };
    };
    bool applyConfiguration()
    {
        try
        {
            const std::string path = this-&amp;gt;configurationPath();
            this-&amp;gt;m_object_proxy-&amp;gt;set_path(path);
            DBus::MethodProxy&amp;lt;bool()&amp;gt; &amp;amp;Apply = *(this-&amp;gt;m_object_proxy-&amp;gt;create_method&amp;lt;bool()&amp;gt;("io.netplan.Netplan.Config", "Apply"));
            return Apply();
        }
        catch (DBus::Error &amp;amp;err)
        {
            this-&amp;gt;handleDBusError(err);
        };
    };

    bool cancelConfiguration()
    {
        try
        {
            const std::string path = this-&amp;gt;configurationPath();
            this-&amp;gt;m_object_proxy-&amp;gt;set_path(path);
            DBus::MethodProxy&amp;lt;bool()&amp;gt; &amp;amp;Cancel = *(this-&amp;gt;m_object_proxy-&amp;gt;create_method&amp;lt;bool()&amp;gt;("io.netplan.Netplan.Config", "Cancel"));
            const bool result = Cancel();
            this-&amp;gt;m_path.clear();
            return result;
        }
        catch (DBus::Error &amp;amp;err)
        {
            this-&amp;gt;handleDBusError(err);
        };
    };

private:
    std::shared_ptr&amp;lt;DBus::Dispatcher&amp;gt; m_dispatcher{nullptr};
    std::shared_ptr&amp;lt;DBus::Connection&amp;gt; m_connection{nullptr};
    std::shared_ptr&amp;lt;DBus::ObjectProxy&amp;gt; m_object_proxy{nullptr};
    std::string m_path{NULL};
    std::string m_filename{NULL};

    void initConfigurationPath()
    {
        try
        {
            // Ensure that object path corrected!
            this-&amp;gt;m_object_proxy-&amp;gt;set_path("/io/netplan/Netplan");
            // Create a Method Proxy to represent Config Method of the Netplan-DBus API
            DBus::MethodProxy&amp;lt;DBus::Path()&amp;gt; &amp;amp;Config = *(this-&amp;gt;m_object_proxy-&amp;gt;create_method&amp;lt;DBus::Path()&amp;gt;("io.netplan.Netplan", "Config"));
            // Run the proxy method to start a new Session and get the Configuration path as a return value and assing it to the m_configuratoin_path
            this-&amp;gt;m_path = Config();
        }
        catch (const DBus::Error &amp;amp;err)
        {
            this-&amp;gt;handleDBusError(err);
        }
    };
    void handleDBusError(const DBus::Error &amp;amp;err)
    {
        // Reset the session in case of error
        this-&amp;gt;m_path.clear();

        // Print out the error.
        printf("ERROR \n\tDBus Error: %s \n\tType: %s \n\tMessage: %s\n", err.name().c_str(), err.what().c_str(), err.message().c_str());
    };
};

#endif // !___NETPLAN_DBUS_SERVICE_HPP___
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;then the cli.hpp file;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;#ifndef ___CLI_HPP___
#define ___CLI_HPP___

#include &amp;lt;iostream&amp;gt;
#include &amp;lt;stdio.h&amp;gt;
#include "netplan.service.hpp"

class Cli
{
public:
    Cli() {};
    void run()
    {
        this-&amp;gt;m_running = true;
        while (this-&amp;gt;m_running)
        {
            this-&amp;gt;printmenu();
        }
    };
    void printmenu()
    {
        printf("\n------------MENU-------------\n");
        for (const auto &amp;amp;item : menu)
        {
            printf("\n%c \t %s", item.first, item.second.c_str());
        }
        printf("\nPlease Enter Your Selection: ");
        this-&amp;gt;readmenu();
    };

    void readmenu()
    {
        char c;
        std::cin &amp;gt;&amp;gt; c;
        switch (c)
        {
        case 'P':
            this-&amp;gt;configurationPath();
            break;
        case 'G':
            this-&amp;gt;getConfiguration();
            break;
        case 'S':
            this-&amp;gt;setMenu();
            break;
        case 'Q':
            this-&amp;gt;stop();
            break;
        default:
            break;
        }
    };

private:
    std::atomic_bool m_running = true;
    std::unique_ptr&amp;lt;NetplanService&amp;gt;
        netplanService = std::make_unique&amp;lt;NetplanService&amp;gt;();
    std::map&amp;lt;const char, const std::string&amp;gt; menu{
        {'P', "Configuration Path"},
        {'G', "Get Configuration"},
        {'S', "Set Configuration"},
        {'Q', "Quit"}};

    void stop()
    {
        this-&amp;gt;m_running = false;
    };

    void configurationPath()
    {
        const std::string path = this-&amp;gt;netplanService-&amp;gt;configurationPath();
        printf("\n\t\tConfigiration Path: %s", path.c_str());
    };

    void getConfiguration()
    {
        const std::string configuration = this-&amp;gt;netplanService-&amp;gt;getConfiguration();
        printf("\nConfiguration\n%s\n", configuration.c_str());
    };

    void setConfiguration(const std::string &amp;amp;target, const std::string &amp;amp;value)
    {
        this-&amp;gt;netplanService-&amp;gt;setConfiguration(target, value);
    };

    void setMenu()
    {
        printf("\n------------------");
        this-&amp;gt;getConfiguration();
        printf("\n\tSet Configuration");
        printf("\n\tEnter the configuration target: ");
        std::string target;
        std::cin &amp;gt;&amp;gt; target;
        printf("\n\tEnter the configuration value: ");
        std::string value;
        std::cin &amp;gt;&amp;gt; value;
        this-&amp;gt;setConfiguration(target, value);
    };

protected:
};

#endif // !___CLI_HPP___
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;and the main.cpp file&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;#include "cli.hpp"
int main(int argc, char *argv[])
{

    const auto cli = std::make_unique&amp;lt;Cli&amp;gt;();
    if (cli)
        cli-&amp;gt;run();
    return 0;
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;Since the Netplan runs on the System Session, we have to run our application with sudo privileges&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Running Application Output:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;------------MENU-------------

G        Get Configuration
P        Configuration Path
Q        Quit
S        Set Configuration
Please Enter Your Selection: 
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Configuration Path Selection:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;------------MENU-------------

G        Get Configuration
P        Configuration Path
Q        Quit
S        Set Configuration
Please Enter Your Selection: P

                Configiration Path: /io/netplan/Netplan/config/EF2OR2
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Get Configuration Selection:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;------------MENU-------------

G        Get Configuration
P        Configuration Path
Q        Quit
S        Set Configuration
Please Enter Your Selection: G

Configuration
network:
  version: 2
  ethernets:
    enxf8e43b893689:
      dhcp4: true
    eno1:
      dhcp4: true
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Set Configuration:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;------------MENU-------------

G        Get Configuration
P        Configuration Path
Q        Quit
S        Set Configuration
Please Enter Your Selection: S

------------------
Configuration
network:
  version: 2
  ethernets:
    enxf8e43b893689:
      dhcp4: true
    eno1:
      dhcp4: true


        Set Configuration
        Enter the configuration target: 

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Here we can test and change the interface.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;I have plug a USB-ETH adaptor for testing, if you are not sure which interface you are working on, do not try this.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;code&gt;ethernets.enxf8e43b893689.dhcp4&lt;/code&gt; value to &lt;code&gt;false&lt;/code&gt; .&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;------------MENU-------------

G        Get Configuration
P        Configuration Path
Q        Quit
S        Set Configuration
Please Enter Your Selection: S

------------------
Configuration
network:
  version: 2
  ethernets:
    enxf8e43b893689:
      dhcp4: true
    eno1:
      dhcp4: true


        Set Configuration
        Enter the configuration target: ethernets.enxf8e43b893689.dhcp4

        Enter the configuration value: false
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It will change the interface &lt;code&gt;enxf8e43b893689.dhcp4&lt;/code&gt; to &lt;code&gt;false&lt;/code&gt; but since we do not apply this. It will not affect anything in this application.&lt;/p&gt;

&lt;p&gt;Complete example could be inspected on github.&lt;br&gt;
&lt;a href="https://github.com/mehmet-yilmaz/netplan-dbus-example" rel="noopener noreferrer"&gt;mehmet-yilmaz/netplan-dbus-example&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This was all, you can implement your requirements and communicate with Netplan or any other service over DBus.&lt;/p&gt;

&lt;p&gt;I hope this helps you.&lt;/p&gt;

</description>
      <category>cpp</category>
      <category>networking</category>
      <category>dbus</category>
      <category>netplan</category>
    </item>
    <item>
      <title>Serving Static Files and Single Page Applications on Oat++ (OatPP)</title>
      <dc:creator>Mehmet YILMAZ</dc:creator>
      <pubDate>Tue, 23 Jul 2024 21:16:57 +0000</pubDate>
      <link>https://dev.to/myilmaz/serving-static-files-and-single-page-applications-on-oat-oatpp-30ag</link>
      <guid>https://dev.to/myilmaz/serving-static-files-and-single-page-applications-on-oat-oatpp-30ag</guid>
      <description>&lt;p&gt;&lt;a href="https://oatpp.io/" rel="noopener noreferrer"&gt;Oat++(OatPP)&lt;/a&gt; is a lightweight C++ Web framework. Out of the box, it provides &lt;code&gt;REST API&lt;/code&gt; with built-in &lt;code&gt;JSON&lt;/code&gt; serialization/deserialization features, which could be interfaced with your DTOs.&lt;/p&gt;

&lt;p&gt;It also offers various modules that could be easily added and used in your application in a plug-in manner.&lt;/p&gt;

&lt;p&gt;Since the main concept of the framework is RestAPI, it does not provide static file serving as default. It means that you must implement your solution to serve your static resources over the Oat++ Web server.&lt;/p&gt;

&lt;p&gt;Despite sounding a little scary, it is not difficult at all. With the help of the frameworks &lt;code&gt;Oatpp::String::loadFromFile&lt;/code&gt; method, we can easily implement our solution.&lt;/p&gt;

&lt;p&gt;Before getting started we can list the steps that we will follow below;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Create a public static folder and files&lt;/li&gt;
&lt;li&gt;File Mime Content Type Mapping&lt;/li&gt;
&lt;li&gt;Define Endpoints&lt;/li&gt;
&lt;/ol&gt;




&lt;h2&gt;
  
  
  1- Create a public static folder and files
&lt;/h2&gt;

&lt;p&gt;This folder will be used to serve static sources and will be accessible over the HTTP Protocol. So we can store our HTML, JS, CSS files, images, etc.&lt;/p&gt;

&lt;p&gt;I will create my folder directly under the project root folder and name it as public in my example. Create a simple HTML file, a simple JS file, a simple CSS file, a favicon, and an image to simulate the widely used content types in order the following folder tree.&lt;/p&gt;

&lt;p&gt;Folder tree&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;ProjectRoot/
├─ public/
│  ├─ favicon.ico
│  ├─ index.html
│  ├─ index.js
│  ├─ style.css
│  ├─ images/
│  │  ├─ logo.svg
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;index.html&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;lt;!DOCTYPE html&amp;gt;
&amp;lt;html lang="en"&amp;gt;
&amp;lt;head&amp;gt;
    &amp;lt;meta charset="UTF-8"&amp;gt;
    &amp;lt;meta name="viewport" content="width=device-width, initial-scale=1.0"&amp;gt;
    &amp;lt;link rel="icon" href="favicon.ico"&amp;gt;
    &amp;lt;script src="index.js"&amp;gt;&amp;lt;/script&amp;gt;
    &amp;lt;link rel="stylesheet" href="style.css"&amp;gt;
    &amp;lt;title&amp;gt;Oat++ Static File Serving Example&amp;lt;/title&amp;gt;
&amp;lt;/head&amp;gt;
&amp;lt;body&amp;gt;
    &amp;lt;div class="container"&amp;gt;
        &amp;lt;div class="header"&amp;gt;
            &amp;lt;img src="images/logo.png" alt="Logo"&amp;gt;
            &amp;lt;h1&amp;gt;
                Oat++
            &amp;lt;/h1&amp;gt;
            &amp;lt;p id="counter"&amp;gt; &amp;lt;/p&amp;gt;
        &amp;lt;/div&amp;gt;
        &amp;lt;div class="main"&amp;gt;
            &amp;lt;button id="clicker" onclick="onCount()"&amp;gt;Count&amp;lt;/button&amp;gt;
        &amp;lt;/div&amp;gt;
        &amp;lt;div class="footer"&amp;gt;&amp;lt;/div&amp;gt;
    &amp;lt;/div&amp;gt;
&amp;lt;/body&amp;gt;
&amp;lt;/html&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;index.js&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;var iterator = 0;
var counter = document.getElementById("counter");
function onCount() {
  counter.innerText = iterator++;
  console.log("Counter Increased: ", iterator);
};
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;style.css&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;body {
  background-color: bisque;
  color: black;
}

.header {
display: block;
}

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Images&lt;/p&gt;

&lt;p&gt;An .ico file as a favicon, and an image as a logo.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;favicon&lt;/code&gt; would be stored in the static folder according to my HTML file and &lt;code&gt;logo&lt;/code&gt; under the images folder which is a sub-folder of the public.&lt;/p&gt;

&lt;h2&gt;
  
  
  2. File Mime Content Type Mapping
&lt;/h2&gt;

&lt;p&gt;Web servers usually provide a public folder, like &lt;code&gt;www&lt;/code&gt; where you can store your static resources via S/FTP, WebUI, SSH, etc.&lt;br&gt;
Or you can set your dedicated folder as a static resources folder accessible over HTTP.&lt;/p&gt;

&lt;p&gt;So, if you have an &lt;code&gt;index.html&lt;/code&gt; file in your static folder, you can access it over HTTP via &lt;code&gt;http://localhost:port/index.html&lt;/code&gt;. All the other files located in the static folder would be accessible with the same path structure as well. For example, we can access the logo.png file under the images folder via this URL: &lt;code&gt;http://localhost:port/images/logo.png&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;This is what we are looking for, right? We are almost there.&lt;br&gt;
We also need a content-type mapper to respond correctly with files regarding its extension.&lt;/p&gt;

&lt;p&gt;I will shortly create a sample function that expects the path and finds the extension by easily looking after the last. in the path and return the correct content type.&lt;/p&gt;

&lt;p&gt;To create a full mime content type, you can reference NGINX’s map linked below.&lt;br&gt;
&lt;a href="https://github.com/nginx/nginx/blob/master/conf/mime.types" rel="noopener noreferrer"&gt;https://github.com/nginx/nginx/blob/master/conf/mime.types&lt;/a&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;std::string* getContentType(const std::string &amp;amp;path) {
  const size_t i = path.find_last_of(".");
  if (i != std::string::npos) {
    const std::string extension = path.substr(i+1); 
    if(extension == "html" || extension == "htm" || extension == "shtml")
      return "text/html";
    if (extension == "js")
      return "application/javascript";
    if (extension == "css")
      return "text/css";
    if (extension == "jpg" || extension == "jpg")
      return "image/jpeg";
    if (extension == "gif")
      return "image/gif";
    if (extension == "ico")
      return "image/x-icon";
    if (extension == "png")
      return "image/png";
    if (extension == "svg" || extension == "svgz")
      return "image/svg+xml";
  }
    return nullptr; // if the path has no "." or has unknow extension we can not find the correct mime as well.
};
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  3. Defining Endpoints
&lt;/h2&gt;

&lt;p&gt;The best way of serving static files is by defining a &lt;code&gt;wildcard&lt;/code&gt; endpoint at the end of the routes.&lt;/p&gt;

&lt;p&gt;Open your last controller or better create one for static routes and add it to the router as the last controller to prevent any unexpected bypass of API endpoints.&lt;br&gt;
I am working on async but you can easily refactor the code in your regular endpoint.&lt;/p&gt;

&lt;p&gt;In the following example, we will accept the wildcard route and check;&lt;/p&gt;

&lt;p&gt;We will respond with the &lt;code&gt;index.html&lt;/code&gt; for route (&lt;code&gt;/&lt;/code&gt;) endpoint.&lt;br&gt;
Try to read the file, and respond with &lt;code&gt;404&lt;/code&gt; in case of not found.&lt;br&gt;
If we find the file, then check the content type regarding its extension and add the content-type header with its mapped mime in the response.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;You can also change the code and respond with the index.html for not-found files to Single-Page Application compatibility.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;static-controller&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;ENDPOINT_ASYNC("GET", "*", Wildcard)
  {
      ENDPOINT_ASYNC_INIT(Wildcard)
      Action act() override
      {
          std::string path = request-&amp;gt;getPathTail();
          // Load the home page in case of no path, it means calling root like: http://localhost/
          if (path.empty()) {
             path = "index.html"; // This is my default home file, you can set your own home page as well.
          }
          // We will check the file if exist and send the index.html in case of not 
          auto file = oatpp::String::loadFromFile(path.c_srt());
          // Send 404 not found in case of no file
          OATPP_ASSERT_HTTP(file.get() != nullptr, Status::CODE_404, "File not found");

          // As file already found, we can search the content type
          const std::string* contentType = getContentType(path);

          // Creating the response
          auto response = this-&amp;gt;controller-&amp;gt;createResponse(Status::CODE_200, file);
          if(contentType != nullptr) // Add the content-type header only if we have a known mime
            res-&amp;gt;putHeader(Header::CONTENT_TYPE, *contentType);
          return _return(response);
      };
  };
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;p&gt;This is the basic implementation and simple example of serving static files over Oat++ and I hope will help you.&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>cpp</category>
      <category>web</category>
      <category>oatpp</category>
    </item>
    <item>
      <title>Installing and Using The OatPP (Oat++) Library</title>
      <dc:creator>Mehmet YILMAZ</dc:creator>
      <pubDate>Mon, 22 Jul 2024 21:09:29 +0000</pubDate>
      <link>https://dev.to/myilmaz/installing-and-using-the-oatpp-oat-library-1n93</link>
      <guid>https://dev.to/myilmaz/installing-and-using-the-oatpp-oat-library-1n93</guid>
      <description>&lt;p&gt;&lt;a href="https://oatpp.io/" rel="noopener noreferrer"&gt;OatPP(Oat++)&lt;/a&gt; is an open-source lightweight C++ Web Framework.&lt;/p&gt;

&lt;p&gt;OatPP is a well-structured, relatively easy-to-use, multithreaded, modern C++ web framework.&lt;/p&gt;

&lt;p&gt;The learning curve is narrower and the project shares the same approach with all the modern language web frameworks like Node.JS, Go, Java, etc. which helps developers to adapt easily.&lt;/p&gt;

&lt;p&gt;Project website: &lt;a href="https://oatpp.io/" rel="noopener noreferrer"&gt;https://oatpp.io/&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Github Repo: &lt;a href="https://github.com/oatpp/oatpp" rel="noopener noreferrer"&gt;https://github.com/oatpp/oatpp&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Although installing and building the Oat++ library is straightforward, the default installation folder structure can cause some dependency errors in gcc/g++ on (as I experienced) Linux systems.&lt;/p&gt;

&lt;p&gt;You can follow the instructions to solve this issue whenever you face it.&lt;/p&gt;

&lt;p&gt;1- Install pre-requirements&lt;br&gt;
OatPP does not offer pre-built binaries. We must download the source code from the repository and compile it on the target computer. This process requires some dependencies, which we must install on our system before this compilation process.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;apt install git cmake build-essential libatomic -y
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;2- Download the source code&lt;br&gt;
Download the source code from the OatPP (Oat++) GitHub repository to any folder in your system. I prefer a temp folder for this purpose which we could delete completely after the installation.&lt;br&gt;
Download the git repository in your preferred folder.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;mkdir temp &amp;amp;&amp;amp; cd temp
git clone https://github.com/oatpp/oatpp.git
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;3- Build the OatPP (Oat++) Library&lt;br&gt;
Open the &lt;code&gt;oatpp&lt;/code&gt; folder and create a build folder under this to collect the CMake build outputs. If you want to customize the building process please consider the OatPP (Oat++) CMake Options.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;cd oatpp
mkdir build &amp;amp;&amp;amp; cd build
cmake ..
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;4- Install the OatPP (Oat++) Library&lt;br&gt;
Finally use make to start installation.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;make install
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This will complete the installation and, normally you are ready to use it including the header files to your project and link the library to your compiler.&lt;/p&gt;

&lt;p&gt;If you have compiler errors in finding OatPP references like me, follow the next steps to change the system's static library and header files folder structures.&lt;/p&gt;

&lt;p&gt;OatPP Installation script targets the &lt;code&gt;/usr/local/include&lt;/code&gt; and &lt;code&gt;/usr/local/lib&lt;/code&gt; folders as default but it uses version numbered root folder and deeply nested folder structures in the destination. Cause of this folder structure, header files in the library could not be found as default. We will bring the library files to the upper level in the folder structure to solve this problem.&lt;/p&gt;

&lt;p&gt;Open the &lt;code&gt;/usr/local/lib/oatpp-[version.number]&lt;/code&gt; folder and copy the &lt;code&gt;oatpp&lt;/code&gt; folder under the &lt;code&gt;/usr/local/lib&lt;/code&gt; folder with sudo previlages.&lt;br&gt;
After copying the library folder, you can delete the bad structured library folder.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;cd /usr/loca/lib/oatpp-[version.number]
sudo cp -R oatpp /usr/local/lib/

cd /usr/local/lib
sudo rm -R /usr/local/lib/oatpp-[version.number]
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Open the &lt;code&gt;/usr/local/include/oatpp-[version]/oatpp/&lt;/code&gt; folder and copy the oatpp folder under the &lt;code&gt;/usr/local/include/&lt;/code&gt; folder with sudo previlages.&lt;br&gt;
After copying the header folder, you can delete the bad structured header folder.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;cd /usr/local/include/oatpp-[version.number]/oatpp/oatpp
sudo cp -R oatpp /usr/local/include/

cd /usr/local/include
sudo rm -R /usr/local/include/oatpp-[version.number]
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This will solve the issue you face with “unknown reference” during the compilation.&lt;/p&gt;

</description>
      <category>cpp</category>
      <category>webdev</category>
      <category>api</category>
      <category>tutorial</category>
    </item>
  </channel>
</rss>
