<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0" xmlns:cc="http://cyber.law.harvard.edu/rss/creativeCommonsRssModule.html">
    <channel>
        <title><![CDATA[Stories by Vincent Wang on Medium]]></title>
        <description><![CDATA[Stories by Vincent Wang on Medium]]></description>
        <link>https://medium.com/@vwangsf?source=rss-74e55b4e61cd------2</link>
        <image>
            <url>https://cdn-images-1.medium.com/fit/c/150/150/0*ioqRVvCmcoxm5_2u</url>
            <title>Stories by Vincent Wang on Medium</title>
            <link>https://medium.com/@vwangsf?source=rss-74e55b4e61cd------2</link>
        </image>
        <generator>Medium</generator>
        <lastBuildDate>Tue, 23 Jun 2026 10:37:07 GMT</lastBuildDate>
        <atom:link href="https://medium.com/@vwangsf/feed" rel="self" type="application/rss+xml"/>
        <webMaster><![CDATA[yourfriends@medium.com]]></webMaster>
        <atom:link href="http://medium.superfeedr.com" rel="hub"/>
        <item>
            <title><![CDATA[The State of MAVLink in 2021]]></title>
            <link>https://vwangsf.medium.com/the-state-of-mavlink-in-2021-5f33c4fbde92?source=rss-74e55b4e61cd------2</link>
            <guid isPermaLink="false">https://medium.com/p/5f33c4fbde92</guid>
            <dc:creator><![CDATA[Vincent Wang]]></dc:creator>
            <pubDate>Sun, 26 Dec 2021 08:13:22 GMT</pubDate>
            <atom:updated>2021-12-26T08:14:18.362Z</atom:updated>
            <content:encoded><![CDATA[<p>It’s about the end of the year, a perfect time for a ranting post on the state of certain open-source projects.</p><p>MAVLink has been the standard protocol for drone communication and control for a long time now. It’s been updated and extended significantly for over a decade, and is currently in use by both the PX4 and Ardupilot open-source autopilot projects, as well as others. I want to make it clear that this post is not an attack or insult on MAVLink at all; it overall does its job well.</p><p>That being said, this post wouldn’t be here if there weren’t major issues surrounding the project (at least in my view). This post aims to summarize the current state of the protocol, pain points, and issues that I have run into while using the protocol.</p><p>This is a disclaimer that the contents of this article are written entirely off of my own understanding and experiences with MAVLink, and does not come with a guarantee of accuracy. With that out of the way, here is the actual content.</p><h3>How the MAVLink Protocol Works</h3><p>In order to understand some of the issues with MAVLink, we should first describe how the MAVLink protocol works.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/0*ZoYhD2BdboIExyj-.jpg" /><figcaption>A standard MAVLink v2 data frame.</figcaption></figure><p>MAVLink is a very simple <a href="https://en.wikipedia.org/wiki/Publish%E2%80%93subscribe_pattern">publish-subscribe</a> protocol with a <a href="https://mavlink.io/en/messages/common.html">fixed standard message set</a> (which can be customized, but rely on all devices on the network understanding that message set).</p><p>A MAVLink network is comprised of <em>systems</em>, which represent physical systems such as drones, ground stations etc.. Each device in the system is identified with a <em>component ID, </em>which has <a href="https://mavlink.io/en/messages/common.html#MAV_COMPONENT">standard values</a> to describe the type of the component.</p><p>Each device will give itself a system and component ID, and <a href="https://mavlink.io/en/messages/common.html#HEARTBEAT">publish <em>heartbeats </em>on the network</a> (as in most protocols) to let other devices know it’s there. This heartbeat contains <em>the type of the device (IMPORTANT FOR LATER!), </em>autopilot info if applicable, and some other information.</p><p>Devices can <em>publish</em> messages (e.g. for telemetry). Messages have fixed <em>message IDs</em>, and other devices can listen for a certain message ID to <em>subscribe</em> to the message. Other devices can also read the system and component ID from the message to figure out where the message came from.</p><h4>Services</h4><p>Although MAVLink is a publish-subscribe protocol, it is capable of emulating the traditional service-client (1 server, 1 caller) architecture. The most common example of this is the <a href="https://mavlink.io/en/services/command.html">Command Protocol</a>, which is used to execute commands such as arming, takeoff etc.</p><figure><img alt="" src="https://cdn-images-1.medium.com/proxy/1*b31hiO4ynbDLRrXWEFF4aQ.png" /><figcaption>The protocol flow for the Command Protocol. GCS means ground station, drone means drone.</figcaption></figure><p>There is a whole collection of <a href="https://mavlink.io/en/services/">microservices</a> supported by MAVLink, which involve exchanging a series of messages to support services such as writing parameters to a vehicle.</p><h4>An Addendum on Routing</h4><p>Routing is perhaps one of the more complicated parts of MAVLink, and given that even the standard protocol libraries don’t seem to implement it properly, it deserves a section here.</p><p>All devices on a MAVLink system have a <em>system ID </em>and a<em> component ID. </em>The system ID identifies a complete system, while the component ID identifies a component on the system; so for example, a drone’s autopilot might be (<em>sysid: 1, compid: 1</em>), while that drone’s gimbal might be (<em>sysid: 1, compid: 154</em>).</p><p>Realistically the only commonly used compids are 1 (autopilot), 190 (ground control), and <em>rarely </em>154 (gimbal).</p><p>One important issue to note is that <em>one connection != one system.</em> Multiple drones/systems can exist over one UDP/serial/whatever connection. Strangely all of the existing common libraries assume that there is only one drone per connection, something that will be ranted about down below.</p><p>Speaking of rants:</p><h3>The Ranting Section</h3><p>Now that you have some background on the protocol, let’s talk about what’s wrong with it and the library ecosystem surrounding it.</p><h4>Just Routing, like, the whole thing</h4><p>It is <strong><em>very</em></strong> rare that will you find a system that uses two IDs to identify devices instead of 1. That being said, this is excusable because we want to know which system the device is part of, and encoding that into every message is a reliable way of doing so.</p><p>That’s a small nitpick, however. The more pressing issue is the <strong>none of the current largest MAVLink libraries implement the routing protocol properly.</strong> Technically, the raw C header library does, but it literally only sends and receives messages, so we can’t count it. Here’s a rundown:</p><ul><li><a href="https://www.ardusub.com/developers/pymavlink.html">Pymavlink</a>, the simple reference Python library, assigns a master.target_system and master.target_component based on the first heartbeat it reads. That being said, you can also manually feed in your own system and component IDs so it technically works, but like the C header library it’s very low level.</li><li><a href="https://github.com/dronekit/dronekit-python/issues/516">Dronekit</a> still has not managed to implement it in master. The issue seems stale as of last year.</li><li><a href="https://github.com/mavlink/MAVSDK/blob/main/examples/fly_multiple_drones/fly_multiple_drones.cpp">MAVSDK technically has multi-drone support</a> in C++ but, like DroneKit, it is one-system-one-connection. Its bindings require a (simple) workaround for multi-drone support — set the mavsdk_server port to different ports, and it’ll start separate servers, allowing multi-drone control; but again, it is still one system per connection. Its heavily abstracted design removes the concept of system and component IDs for the user for the most part.</li></ul><p>MAVLink also follows the practice of hard-fixing meanings to component IDs — like having gimbals be 154, etc. Technically, this makes it easier to deconflict IDs, but it comes with a number of disadvantages:</p><ul><li>We’re assigning semantic meaning to the IDs, a role that’s already fulfilled by MAV_TYPE and a decision that limits the flexibility.</li><li>Having two sources of component type information causes confusion. Some libraries may check the component ID instead of the MAV_TYPE, which is incorrect.</li><li>Multiple of the same device will have the same default component ID and require deconfliction anyway.</li></ul><p>Node deconfliction is a somewhat complex topic, but there are a number of easy ways in which MAVLink could implement it; a crude method is to simply listen for other nodes with the same ID for a period, and reassign.</p><p>Basically, the TL;DR of routing is that libraries don’t implement it properly, and it is not super flexible. The protocol itself is acceptable, but library support is meh.</p><h4>Inconsistency</h4><p>MAVLink is a standard. That means industry standard open-source autopilots should implement the standard consistently, but we’ve seen that they often don’t.</p><p>For instance, take the simple <a href="https://mavlink.io/en/messages/common.html#MAV_CMD_NAV_TAKEOFF">MAV_CMD_NAV_TAKEOFF command</a>:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*JxIfAowcrAl0vz4qk6k0Mw.png" /></figure><p>That altitude parameter is completely ignored by PX4, which instead only pays attention to the MIS_TAKEOFF_ALT parameter. Ardupilot implements the altitude parameter.</p><p>Where is this documented? Nowhere, except if you dig through the <a href="https://github.com/mavlink/MAVSDK/blob/5d2a21f38b7f29664fcd64332b33a1d278811287/src/mavsdk/plugins/action/action_impl.cpp">MAVSDK implementation for takeoff</a>.</p><p>Ardupilot and PX4 also implement different sets of modes, even though they are functionally the same thing. This is the standard DO_SET_MODE command:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*0xBPSA7CwplK6re-NBxpyA.png" /><figcaption>Simple? NO.</figcaption></figure><p>That first parameter is a standard enum of modes:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*mW8SDaID9_IyGto6oI9sVQ.png" /><figcaption>Simple vehicle modes.</figcaption></figure><p>PX4 and Ardupilot promptly ignore this first field entirely and instead implement custom modes, but ones that do the same thing (Mission, Guided/Offboard, etc.) While custom modes are useful, it would benefit the community to be able to have a standard set of modes for vehicles, instead of completely separate modes that largely have the same features.</p><h4>Microservice Protocols</h4><p>MAVLink has a large collection of <a href="https://mavlink.io/en/services/">microservices</a> that implement certain functionality, such as sending mission descriptions, writing parameters, etc.</p><p>There are issues with some of the microservice protocols. Most of the issues boil down to three points:</p><ul><li>Overcomplicated</li><li>Scope creep</li><li>Not implemented consistently or properly</li></ul><h4>Camera Protocol</h4><p>The <a href="https://mavlink.io/en/services/camera.html">MAVLink Camera Protocol</a> is a protocol for generally exposing camera streams and image servers. It’s…okay, in that it fulfills the job of exposing the stream/image server, but it contains a <em>lot</em> of redundant information. Take, for example, the CAMERA_INFORMATION message:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*CtLmytfp5LpRD4j34T7tNA.png" /><figcaption>The CAMERA_INFORMATION message has a lot of (redundant) fields.</figcaption></figure><p>This doesn’t seem terrible, except all of that information is also encoded in the <a href="https://mavlink.io/en/services/camera_def.html#full_example">camera definition file</a>. Then, the VIDEO_STREAM_INFORMATION has similar data:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*MFGWVz2esfM1swCJw6rwQA.png" /><figcaption>More fields.</figcaption></figure><p>except resolution, bitrate etc. are all available via the RTSP/RTP/whatever stream itself, usually.</p><p>The camera protocol is honestly not that bad of an offender; it is just rather complex — it requires a companion computer to function correctly, and preferably a dedicated radio (MAVLink FTP is very, very slow). There’s a <a href="https://github.com/Dronecode/camera-manager">reference implementation</a>, but is archived.</p><p>My major gripe with this protocol is that it depends on an external HTTP/FTP server + companion computer for the definition and stream anyway. MAVLink is clearly not the optimal protocol for this application; it would likely be better to standardize an HTTP-based API for requesting camera information, and use MAVLink only to point ground control software at the server.</p><h4>Gimbal Protocol (v1 and v2)</h4><p>The Gimbal Protocol was updated with a v2 a while back to address issues with the v1 protocol, mainly its ambiguous message set and performance issues. V2 made major changes, the most important of which is an explicit separation between <em>gimbal device</em> and <em>gimbal manager:</em></p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*C1hy6--4mDQ-oWPRt60YyA.png" /><figcaption>An example where the autopilot is the gimbal manager.</figcaption></figure><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*xyKWfk19jTNVZ6rgAcErgw.png" /><figcaption>An example where the gimbal is its own manager. The gimbal device doesn’t exist here because it is the same thing as the gimbal manager.</figcaption></figure><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*tY1kSlb4e2aNn7glULzFzA.png" /><figcaption>An example where a companion computer manages the gimbal device.</figcaption></figure><p>The separation is a good idea; v1 struggled because both the autopilot and ground station would often try to control the gimble directly, which would cause conflicts. The gimbal manager aims to solve this; its primary job is to implement higher-level functionality and deconflict control.</p><p>The way it deconflicts control is by assigning a primary and secondary control system+component ID; the implementation of this mixing is not defined, which is a minor issue. The behavior of primary and secondary control is not defined to be consistent across platforms, which could lead to portability issues, although this is solvable.</p><p>There are also a large number of manager commands, some of which will not be supported by all systems. While GIMBAL_MANAGER_INFORMATION contains some facilities for reporting capabilities, I think more fine-grained capability reporting (e.g. can track ROI but not WPNEXT) would be useful.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*EbhDypDcmkvtkNQOXEDVXg.png" /><figcaption>That’s a lot of messages.</figcaption></figure><p>There is also a somewhat confusing gimbal manager to device relationship. The protocol calls for one gimbal manager to one device; messages such as GIMBAL_MANAGER_INFORMATION use a gimbal device ID directly rather than calling it a manager ID, so the wording is confusing.</p><p>Additionally, gimbal managers have no component ID. They are instead attached to another device (autopilot, gimbal device etc.) but which device they are attached to is generally unclear.</p><p>It would be much clearer if gimbal managers were considered their own devices; it would simplify addressing gimbal managers (no need for a separate gimbal device ID in the control message!) and clear up confusion.</p><p>Better yet, remove the gimbal device from the MAVLink network. This seems to me that moving the gimbal device out of the main MAVLink network and having it be isolated through the gimbal manager greatly reduces confusion; the gimbal manager <em>is</em> the representation of the gimbal on the main network. Additionally, it opens up fun possibilities for ideas such as chaining gimbal managers; a lower-level gimbal manager implemented on a physical gimbal that can only control angle can be chained into an internal autopilot or companion gimbal manager, which allows less advanced hardware to take advantage of autopilot sensors and systems.</p><h3>Conclusion</h3><p>MAVLink in 2021 is a fairly usable protocol that suffers from some incorrectly implemented libraries and feature creep. The core protocol does a great job of controlling drones; auxiliary protocols such as the gimbal and camera protocol may benefit from moving some functionality outside of MAVLink. Multi-drone control and correct routing is an issue that libraries sorely need to address.</p><p>Here’s to hoping that 2022 will bring further improvements to the protocol and the ecosystem surrounding it.</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=5f33c4fbde92" width="1" height="1" alt="">]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Setting up an Ubuntu chroot on your Linux distro]]></title>
            <link>https://vwangsf.medium.com/setting-up-an-ubuntu-chroot-on-your-linux-distro-250844da6573?source=rss-74e55b4e61cd------2</link>
            <guid isPermaLink="false">https://medium.com/p/250844da6573</guid>
            <category><![CDATA[chroot]]></category>
            <category><![CDATA[ubuntu]]></category>
            <category><![CDATA[roses]]></category>
            <dc:creator><![CDATA[Vincent Wang]]></dc:creator>
            <pubDate>Fri, 06 Nov 2020 06:41:33 GMT</pubDate>
            <atom:updated>2021-04-25T04:56:58.393Z</atom:updated>
            <content:encoded><![CDATA[<h3>Setting up an Ubuntu chroot on your Linux distro with schroot</h3><p>Sometimes, you just need Ubuntu.</p><p>In my daily life, I usually use Arch Linux as my daily driver (barring <a href="https://gnuless-november.gitlab.io">GNUless November</a>). It works amazing, the AUR is a blessing from Harambe, etc etc, all the stuff you’ve probably already heard from Arch users. The problem is that unfortunately, not all things work on Arch; for example, I occasionally need to mess with ROS (Robot Operating System), and the community-maintained AUR distribution seems to always have dependency issues, so I wanted to install the official Ubuntu distribution but still be able to use it cleanly within my Arch system.</p><p>This article assumes you’re using an amd64/x86_64 system.</p><h4>Why not a container?</h4><p>Yes, containers are nice and pretty epic, but for my use case, I thought it was too much of a hassle — containers are <em>too</em> isolated in this case for my tastes. So for this specific purpose, I wanted to run something a bit lighter and less isolated than a container; chroots are perfect for that.</p><h4>Enter: schroot+debootstrap</h4><p>schroot and debootstrap are 2 tools that make managing Debian-based chroots incredibly simple. schroot manages setting up the chroot (it can even mount your home directory, so all your configs are there!), while debootstrap allows you to basically bootstrap your chroot with a Debian derivative with one command.</p><p>First, you’ll need to actually install the two tools. Since i use Arch:</p><p>pacman -S schroot debootstrap</p><p>Next, set up the chroot definition. Edit /etc/schroot/chroot.d/&lt;name&gt;.conf (you can name the chroot whatever you want). Next, put in this content:</p><pre>[&lt;chroot name&gt;]<br>description=&lt;description&gt;<br>directory=/srv/chroot/&lt;chroot name&gt;<br>root-users=&lt;your user&gt;<br>type=directory<br>users=&lt;your user&gt;</pre><p>This will set up a new chroot at /srv/chroot/&lt;chroot-name&gt;. We can actually make as many chroots as we want; just put another config file in the directory.</p><p>Next, make the chroot directory:</p><pre>sudo mkdir -p /srv/chroot/&lt;chroot name&gt;</pre><p>And bootstrap it using debootstrap.</p><pre>sudo debootstrap --arch=amd64 focal /srv/chroot/&lt;chroot name&gt;/ http://archive.ubuntu.com/ubuntu/</pre><p>This command will actually bootstrap from any Debian derivative, not just Ubuntu. You just need to change the mirror (http://archive.ubuntu.com/ubuntu/) and distribution version (focal). This specific command bootstraps Ubuntu 20.04 (focal).</p><p>That’s more or less it! You now have a working Ubuntu chroot. You can run schroot -c &lt;chroot name&gt; to enter the chroot. However, we still need to set some stuff up before it works properly.</p><h4>Post Setup</h4><p>After the chroot is set up, we need to do a few more things:</p><ul><li>Fix network config (do only once)</li><li>Add Ubuntu sandbox users and groups on host system (so apt works) (do only once)</li><li>Enable repos</li><li>Give your user sudo</li></ul><p>Fix the network config by commenting out networks in /etc/schroot/default/nssdatabases. This tells schroot to not copy the networks from host system. If you leave this on in Arch it breaks the network.</p><p>Add the sandbox users and groups (run this on the Arch host, <strong>not</strong> inside the chroot):</p><pre>sudo useradd -u 124 _apt<br>sudo useradd -u 939 geoclue<br>sudo useradd -u 694 man<br>sudo groupadd crontab<br>sudo groupadd messagebus</pre><p>(For the astute, we’re specifying UIDs under 1000 so they don’t show up on your login screen. They’re random numbers.)</p><p>Next, actually enter your chroot as root with sudo schroot -c &lt;chroot name&gt; , and add your user to sudo with</p><pre>usermod -a -G sudo &lt;user&gt;</pre><p>Next, exit and re-enter your chroot as your normal user (sudo schroot -c &lt;chroot name&gt;), and add the universe, multiverse and restricted repos (you don’t have to do this but you probably want to in order to get most of Ubuntu’s packages):</p><pre>sudo apt update<br>sudo apt install software-properties-common<br>sudo add-apt-repository universe<br>sudo add-apt-repository multiverse<br>sudo add-apt-repository restricted</pre><p>You probably want to add focal-security as well, for more up to date packages. Run this command:</p><pre>echo &quot;deb http://security.ubuntu.com/ubuntu focal-security main&quot; &gt;&gt; /etc/apt/sources.list</pre><p>as root (sudo -s).</p><p>That should be it! Now your Ubuntu chroot will basically work exactly like an actual Ubuntu installation, and you can install all the Ubuntu programs you need without ever leaving your distro of choice. Hope this guide helped!</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=250844da6573" width="1" height="1" alt="">]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Please Stop Hijacking Our Scroll Wheels]]></title>
            <link>https://vwangsf.medium.com/please-stop-hijacking-our-scroll-wheels-4e1ce68aa13e?source=rss-74e55b4e61cd------2</link>
            <guid isPermaLink="false">https://medium.com/p/4e1ce68aa13e</guid>
            <category><![CDATA[ui]]></category>
            <category><![CDATA[ux]]></category>
            <category><![CDATA[scroll]]></category>
            <dc:creator><![CDATA[Vincent Wang]]></dc:creator>
            <pubDate>Sat, 18 Jul 2020 05:49:30 GMT</pubDate>
            <atom:updated>2020-07-18T05:49:30.789Z</atom:updated>
            <content:encoded><![CDATA[<p>Web developers are no strangers to hijacking regular browser functions in order to do something they consider “cooler”. Overall, this is probably a plus; without overriding regular browser functions, modern website functionality we take for granted today (for example, Single Page Applications) wouldn’t be possible. However, this ability has also been turned towards more insidious applications — namely, hijacking the scroll wheel to do “cool” animations and other actions.</p><p>What am I talking about? Just take a look at <a href="https://www.apple.com/iphone-se/">Apple’s product pages</a>. Try scrolling. The function of the scroll wheel bounces around all over the place. Is it playing an animation? Scrolling the actual page? No, wait — now it’s changing the background. Oh, wait, it’s scrolling again.<em> The scroll wheel just won’t stay put.</em> The end result? The page is extremely hard to navigate — look at how much you have to scroll to get to the bottom of the page:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*QXYMG8uUslUMaTNtpFW9sA.png" /><figcaption>Just look at the size of that scrollbar!</figcaption></figure><p>Those who have followed web design for a while might know that this problem is not exactly new — the problem has existed for years, with multiple other voices loudly complaining about the usability nightmare it poses.</p><p>But if web designers have known about the problem for years, then why does it still exist?</p><p>It mainly comes down to web designers over-designing in order to make a website look <strong>edgy </strong>and <strong>cool. </strong>The product page above is a classic example of this; in an effort to replicate the slick in-your-face editing shots of the Apple commercials, the product page takes the scroll wheel and throws its regular function out of the window, using it to play through a bunch of in-your-face animations and changes.</p><p>Apple, of course, is not the only offender in this design trend; countless websites use some form of scroll-jacking, ranging from simple full-page scrolling to using the scroll wheel to play through all sorts of fancy animations and effects. However, just because a lot of websites use scroll-jacking <strong>does not make it a good practice.</strong></p><p>Seriously, don’t do it.</p><p>Need some reasons? Let’s discuss a few:</p><h3>Intuitiveness</h3><p>This is probably the most important issue in regard to scroll hijacking — and, to be honest, UX design in general.</p><p>When we use a function, we expect the function to do what we expect it to. In the case of the scroll wheel, that’s scrolling. If the page doesn’t scroll when the user scrolls, it creates a break between what the user expects and what the page does; this leads to an unintuitive interface that feels cumbersome and confusing to navigate.</p><p>Different scroll hijacking methods violate this rule to different degrees; for example, most simple full-page scroll websites are relatively unobtrusive, simply making the site scroll screen-by-screen instead of the regular behavior. Therefore, if you <em>really really want </em>some full-page scrolling, it’s OK to implement, as long as it doesn’t disrupt the user experience too much. Which brings me to my second point…</p><h3>Consistency</h3><p>Some websites allow you to scroll naturally at first, but then decide halfway down the page that it wants to take control of your scroll wheel.</p><p>Please don’t do this.</p><p>If you’re going to change the scroll wheel functionality, change it in a consistent manner — don’t have a section that scrolls naturally and then suddenly transitions into full-page scrolling, or vice versa. If you’re going to scroll-jack, do it all the way. A sudden change in the function of the scroll wheel is jarring and distracting.</p><h3>Navigability</h3><p>Is that a word?</p><p>Doesn’t matter — the point is that on a page, being able to navigate up and down the page is essential. Scroll hijacking interferes with this (although, again, some methods affect UX less than others). Full-page scrolling, for example, limits the rate at which you can scroll to a constant animation speed, making it frustrating to scroll to the bottom of the page. On the other hand, scrolling tied to animation a la Apple leads to unreasonably long scroll bars, forcing you to partake in a forced <a href="https://the100meterscroll.com/">100 meter scroll race</a> to get to the bottom. Either way, changing the scroll functionality will inevitably lead to some issues with site navigation, ranging from mildly annoying to downright unusable.</p><h3>Summary</h3><p>TL;DR: The best option is to just leave the default scrolling in place. You probably don’t need scroll hijacking.</p><p>If you’re really, really sure that you do want to change the scroll functionality of your site, consider these few tips:</p><ul><li>Be consistent about it. If you’re changing the scroll functionality, make sure it does one thing only; don’t transition between natural scrolling and full-page scrolling.</li><li>Keep your site navigation in mind. Try and control the size of your scrollbar, and if you’re using full-page scrolling, don’t set the animation speed to an unbearably slow pace.</li><li>Avoid tying scrolling to animation. Not only is this unintuitive, but it also doesn’t even look that good — for instance, on a desktop, some scroll wheels only scroll a chunk at a time, which means that the animation will play, pause, then play in fits and starts as the user scrolls down the page. A better solution may be to implement a sort of full-page scrolling for animations — instead of tying the entire animation to the scroll wheel (which leads to the animation starting and stopping), the animation simply plays when the user scrolls past a certain threshold, in order to transition between two screens. (Note: we’re talking here about animations tied to scroll positions a la Apple website; <a href="https://michalsnik.github.io/aos/">animating elements on scroll-in </a>is fine.)</li></ul><p>Thanks for reading!</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=4e1ce68aa13e" width="1" height="1" alt="">]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Bibify: Building an open-source citation service]]></title>
            <link>https://vwangsf.medium.com/bibify-building-an-open-source-citation-service-af0769ae0dea?source=rss-74e55b4e61cd------2</link>
            <guid isPermaLink="false">https://medium.com/p/af0769ae0dea</guid>
            <category><![CDATA[citations]]></category>
            <category><![CDATA[open-source]]></category>
            <category><![CDATA[citation-generator]]></category>
            <category><![CDATA[citeproc-js]]></category>
            <dc:creator><![CDATA[Vincent Wang]]></dc:creator>
            <pubDate>Tue, 19 May 2020 04:16:36 GMT</pubDate>
            <atom:updated>2020-05-19T22:39:40.131Z</atom:updated>
            <content:encoded><![CDATA[<p><a href="https://bibify.org">bibify</a></p><p>DISCLAIMER: This article is shameless self promo. The links for the frontend and backend source code are here: <a href="https://gitlab.com/bibify">https://gitlab.com/bibify.</a> If you want to read about how we did it, go on.</p><p>We’ve all had to do it: a teacher assigns a research project and says that it needs a full bibliography, formatted in MLA or APA or whatever. Now, most of us don’t bother to remember how to cite a website in MLA (because how often do you actually need to do that in life?), so we usually turn to a citation generator.</p><p>These things usually suck.</p><p>Most of these citation generators are slow and laden with ads. The ones that aren’t usually don’t work properly. We put up with most of them because the alternative would be to do it by hand, and doing it by hand is painful. So what if we built an free and open source citation generator?</p><h4>Step 1 — the frontend</h4><p>Our citation generator needs a frontend. Easy job. Grab some React, <a href="https://material-ui.com/">your favorite components library</a>, and slap them together. Bing bang bong, add some <a href="https://www.npmjs.com/package/axios">axios</a> for making HTTP requests, a <a href="https://www.npmjs.com/package/qs">querystring</a> library, and you’re done!</p><p>The source for the frontend is here: <a href="https://gitlab.com/bibify/bibify">https://gitlab.com/bibify/bibify</a>.</p><h4>Step 2 — the backend</h4><p>The backend side is a bit harder. In order to match current citation generators in features, our app needs to be able to:</p><ul><li>Generate accurate citations for all common citation styles</li><li>Get book and website info to auto-cite (because entering data by hand is lame)</li></ul><p>Let’s break down these problems.</p><h4>Fetching Website Info</h4><p>Fetching website info is pretty easy. Grab a <a href="https://www.npmjs.com/package/metascraper">metadata scraping library</a> and point it at the website you want to get the info of. That’s about it!</p><h4>Fetching Book Info</h4><p>Fetching accurate book information for free is a bit harder than fetching websites; <a href="http://isbndb.com/">the book database that most people use</a> is locked behind a subscription fee. However, the Google Books API is free, and there’s no limit! (Although they do request that you stay under 10,000 requests per day as a courtesy limit.) All we need to do is grab a good <a href="https://www.npmjs.com/package/bookify">wrapper library</a>, give it a search query, and we’re good to go.</p><h4>Generating accurate citations</h4><p>Generating accurate citations for every common citation style is difficult, especially because doing it yourself would mean reading through every citation style guide’s rules on how to cite every media type. Luckily, we don’t have to! The <a href="https://citationstyles.org/">CSL (Citation Style Language) project</a> already contains 9000+ citation styles that we can use. Combine this with the <a href="https://citeproc-js.readthedocs.io/">citeproc-js processor</a>, which takes these citation styles and spits out a citation, and we’re in business!</p><p>Except it’s not that simple.</p><p>Being able to access 9000 styles through citeproc-js is great, but getting it to work is a bit of a slog. In order to use the citeproc-js engine, you need to write your own <em>sys </em>object which provides the functions retrieveItem() and retrieveLocale(); while it isn’t really that hard to write these functions yourself, it is still a good amount of boring boilerplate. So, to solve this, we s̶t̶e̶a̶l̶ borrow<a href="https://github.com/zotero/citeproc-js-server/blob/master/lib/citeprocnode.js"> this nice wrapper</a> (as well as <a href="https://github.com/zotero/citeproc-js-server/blob/master/lib/json_walker.js">this helper script</a>). Now, instead of writing our own sys object, we can just let sys = citeprocnode.simpleSys(). Much easier, isn’t it?</p><p>Once we have our sys object, it’s a simple matter of giving it an item and calling makeBibliography:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/657/1*nw0r-zowixDtiGePZMhP-g.png" /><figcaption>Just load in the CSL style file, the locale, and the bibliography item (formatted in <a href="https://citeproc-js.readthedocs.io/en/latest/csl-json/markup.html">CSL-JSON</a>), and it spits out a bibliography!</figcaption></figure><h4>Side Note: CSL-JSON</h4><p>In order to generate a bibliography, you need to load your info into citeproc-js in CSL-JSON format. It varies based on type, but it more or less looks something like this:</p><pre>{<br>  &#39;id&#39;: &#39;random-id-aihwgew&#39;,<br>  &#39;type&#39;: &#39;book&#39;, // any one of the CSL types listed <a href="https://aurimasv.github.io/z2csl/typeMap.xml">here</a><br>  &#39;title&#39;: &#39;A Book about Something&#39;,<br>  &#39;publisher&#39;: &#39;Random Publisher Inc.&#39;,<br>  <a href="https://aurimasv.github.io/z2csl/typeMap.xml">// other type-specific fields</a>,<br>  &#39;authors&#39;: [<br>    { &#39;family&#39;: &#39;Last&#39;, &#39;given&#39;: &#39;First&#39; }<br>  ]<br>}</pre><p>The full docs for CSL-JSON are <a href="https://citeproc-js.readthedocs.io/en/latest/csl-json/markup.html">here</a>.</p><h4>Pitfalls</h4><p>One thing about CSL is it has 2 categories of styles: independent and dependent. Basically, the dependent styles are different names for an independent style — for example, Harvard Educational Review links to APA. Unfortunately, that’s the only information that’s available in the dependent style:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/913/1*6HTZf-paz1M_LSju3mFWUg.png" /><figcaption>An example of a dependent CSL style. Note that only the parent style and some metadata are available.</figcaption></figure><p>This means that trying to load this style directly into citeproc-js won’t work, because citeproc-js is expecting a full independent CSL style:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/936/1*f78Z1gmdwszkVDKze1rQvQ.png" /><figcaption>An example of an independent style. Note how there’s a lot more information on how to actually create the citation.</figcaption></figure><p>So, we need to grab the linked independent style from the dependent style and load <strong>that </strong>instead:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/619/1*-Bpyn1uLc-TJTKmwaCkUfQ.png" /><figcaption>Here we use xpath to grab the path of the linked independent CSL style and load that instead.</figcaption></figure><p>Now we’re in business!</p><p>That’s the main CSL issue we need to get out of the way. Here’s some other minor inconveniences:</p><ul><li>This is more of a side note and less of a pitfall, but it turns out that the “Harvard” style that most popular citation services provide doesn’t really exist in CSL. There’s a (deprecated) harvard1.csl reference Harvard style, which links to the Harvard Cite Them Right style. However, most universities actually have their own Harvard variant.</li><li>MLA 7 and 8 are both shortened to “MLA”, so when you display the short titles side by side (as bibify does), both of them show up as “MLA”. This is easily fixed by going into each CSL file (modern-language-association.csl and modern-language-association-7th-edition.csl) and changing the &lt;title-short&gt; content.</li></ul><h4>The Future</h4><p>While bibify currently has feature parity with other popular citation generators, there’s some things still planned in the works:</p><ul><li>Bibify currently scrapes websites for metadata in real time. While this approach generally works, it also means that slow websites will take longer to cite, and websites that are down won’t be able to be cited at all. To solve this, we’re planning on maintaining a cache of cited websites, as well as <a href="https://archive.org/help/wayback_api.php">integrating with The Wayback Machine.</a> (NOTE: As of update 2020.05.18, Bibify now caches website fetch results with superagent-cache. While this speeds things up, websites that are down still can’t be cited.)</li><li>Autocitation only currently works with books and websites. We can improve on this by adding autocitation for other media types.</li><li>Like most citation generators, Bibify struggles to handle autociting author names with more than two words (e.g. “Bartolome de las Casas”). Currently, Bibify simply treats author names with more than two words as a literal, meaning that the name is put in as is; this is generally not compliant with most citation styles.</li><li>The frontend UI can definitely be improved.</li></ul><h4>Come and contribute!</h4><p>This open-source citation generator project lives at <a href="https://gitlab.com/bibify">https://gitlab.com/bibify</a>. Come on over and contribute! File an issue or two, maybe fix some bugs or add some new features. New additions are always welcome.</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=af0769ae0dea" width="1" height="1" alt="">]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Creating a D-Bus Service with dbus-python and Polkit Authentication]]></title>
            <link>https://vwangsf.medium.com/creating-a-d-bus-service-with-dbus-python-and-polkit-authentication-4acc9bc5ed29?source=rss-74e55b4e61cd------2</link>
            <guid isPermaLink="false">https://medium.com/p/4acc9bc5ed29</guid>
            <category><![CDATA[programming]]></category>
            <category><![CDATA[dbus]]></category>
            <category><![CDATA[python]]></category>
            <category><![CDATA[tutorial]]></category>
            <dc:creator><![CDATA[Vincent Wang]]></dc:creator>
            <pubDate>Sat, 09 Nov 2019 10:20:24 GMT</pubDate>
            <atom:updated>2019-11-09T10:20:24.316Z</atom:updated>
            <content:encoded><![CDATA[<p>D-Bus is the standard for inter-process communication for Linux desktop applications. Both <a href="https://doc.qt.io/qt-5/qtdbus-index.html">Qt</a> and <a href="https://developer.gnome.org/gio/stable/gdbus-convenience.html">GLib</a> have high-level abstractions for D-Bus communication, and many of the desktop services we rely on export D-Bus protocols. However, D-Bus has its shortcomings — namely a lack of documentation. Let’s explore how to write our own D-Bus Service in Python and connect it to Freedesktop.org’s PolicyKit API to provide user authentication.</p><h4>What is D-Bus?</h4><p>D-Bus is a standard IPC/RPC protocol introduced by Freedesktop.org as a way of unifying the messy landscape of inter-process communication on Linux desktops under one standard. In other words, it’s a way for programs to communicate with each other.</p><h4>How is D-Bus organized?</h4><p>D-Bus is organized into <em>objects</em>. These objects can be published on one of two “buses” (the system bus, in which there is one object for the whole system, or the session bus, in which each user session can have its own object). Objects play a double role as both an RPC object (you can call methods on the object) and as a publish/subscribe interface (you can subscribe to signals on the object). Each object defines <em>interfaces</em>, which describe and organize what each object can do.</p><p>Objects published on a bus are identified by a unique <em>bus name</em> (often written in reverse-DNS format, e.g. “org.freedesktop.NetworkManager”) and an <em>object path</em>, describing (e.g. “/org/freedesktop/NetworkManager”). <a href="https://dbus.freedesktop.org/doc/dbus-faq.html#idm39">If you’re confused about the difference between an object path and a bus name, this probably explains it better than me.</a> In this article, we’ll explore how to export our own object onto a bus under an object path of our choosing.</p><h4>Why use D-Bus?</h4><p>While D-Bus has its shortcomings, it still remains the standard for Linux desktops today. All of Freedesktop.org’s APIs (that is to say, maybe half of the average Linux desktop in general) are published through D-Bus. D-Bus is well suited to the needs of an IPC system for the Linux desktop; it can handle publish/subscribe interfaces as well as methods, allowing for robust architectures.</p><h4>Okay, can we build the thing?</h4><p>Yes.</p><p>We’ll be using dbus-python to build our service. This means it’s primarily targeted towards GLib/GTK apps. If you’re looking to integrate your Qt app with D-Bus, <a href="https://doc.qt.io/qt-5/qtdbus-index.html">there’s great first-party docs on that</a>.</p><p>Step 1 is to install the dbus-python bindings. Most package managers should have a package for it; it might already be installed by something else. If your package manager’s dbus-python version is out of date, you can always pip install it.</p><p>While you’re at it, install <a href="https://wiki.gnome.org/action/show/Apps/DFeet?action=show&amp;redirect=DFeet#Latest_stable_release">D-Feet</a> for easy GUI D-Bus debugging.</p><p>Oh, and you’ll also want to grab <a href="https://pypi.org/project/PyGObject/">PyGObject</a> so the service has a mainloop, otherwise it won’t listen continuously.</p><p>Now that we have the dbus-python bindings, let’s start by creating a simple service:</p><p>service.py</p><pre>import dbus<br>import dbus.service<br>import dbus.mainloop.glib</pre><pre>from gi.repository import GLib</pre><pre>class HelloWorld(dbus.service.Object):<br>    def __init__(self, conn=None, object_path=None, bus_name=None):<br>        dbus.service.Object.__init__(self, conn, object_path, bus_name)<br>    </pre><pre>if __name__ == &quot;__main__&quot;:<br>    dbus.mainloop.glib.DBusGMainLoop(set_as_default=True)<br>    bus = dbus.SessionBus()<br>    name = dbus.service.BusName(&quot;com.example.HelloWorld&quot;, bus)<br>    helloworld = HelloWorld(bus, &quot;/HelloWorld&quot;)<br>    mainloop = GLib.MainLoop()<br>    mainloop.run()</pre><p>Let’s break it down line by line:</p><p>1–5: imports</p><p>7: We declare a subclass of dbus.service.Object. This will define the object we export onto the bus.</p><p>8: We define an init function. It doesn’t have to follow this as long as the superclass constructor in line 9 gets the proper arguments, but I think this is probably the cleanest way.</p><p>9: We call the superclass constructor. We pass in self , conn (the bus connection), object_path (the object path we want to use, as str), and the bus name we want to export under.</p><p>11: It’s an if __name__ == &quot;__main__&quot; block. Python devs should probably know what this is.</p><p>12: We tell D-Bus to use a mainloop. This allows the service to listen for requests.</p><p>13: We get a connection to the Session Bus (if you want SystemBus instead, just replace SessionBus with SystemBus; everything’s the same).</p><p>14: We create a BusName to export our object under (“com.example.HelloWorld”) under the Session Bus.</p><p>15: We create the actual service object, passing in the bus connection, the object path and the bus name.</p><p>That is basically all you need to create a D-Bus service. Open up D-Feet and you should see something like this:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*Vh3Es4NenJ8Gq5Jolu6f0Q.png" /></figure><p>As you can see, our object shows up under /HelloWorld in com.example.HelloWorld, but it doesn’t have any content in it (besides the default Introspectable interface). Let’s fix that by adding a method to our class:</p><pre><a href="http://twitter.com/dbus">@dbus</a>.service.method(dbus_interface=&quot;com.example.HelloWorldInterface&quot;, in_signature=&quot;s&quot;, out_signature=&quot;s&quot;, sender_keyword=&quot;sender&quot;, connection_keyword=&quot;conn&quot;)<br>def SayHello(self, name, sender=None, conn=None):<br>    return &quot;Hello &quot; + name</pre><p>It’s technically only 3 lines, but there’s a lot to break down here. Let’s go line by line again:</p><p>1: the @dbus.service.method decorator tells D-Bus that this is a callable method that can be called on the object. This decorator takes several named arguments:</p><ul><li>dbus_interface is the interface name we publish the method under. For methods, interfaces are merely a way of grouping functionality.</li><li>in_signature is a string of characters representing the datatypes of the parameters passed in; for a comprehensive guide, see <a href="https://dbus.freedesktop.org/doc/dbus-specification.html#type-system">the official D-Bus docs.</a> Each piece of data is passed in as an argument to the method; in this example, we take in 1 string argument, name .</li><li>out_signature is the same as in_signature, but it represents return datatype instead of parameter datatype.</li><li>sender_keyword and connection_keyword are optional; basically, sender_keyword=“sender” basically means the ID of the user who called the method will be stored in the sender argument, and connection_keyword means the connection will be stored in conn .</li></ul><p>2: Here we actually declare the function. Note that the types have to match the in_signature declared in the decorator. (P.S. In most D-Bus services, methods are in PascalCase; that’s just how it is, don’t ask me why.)</p><p>3: We return a result. Note that the type has to match the out_signature declared in the decorator.</p><p>Let’s see the result:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*1r12JQjxJBctdH1-x3MkIw.png" /></figure><p>The SayHello method shows up under com.example.HelloWorldInterface as expected and takes the input and output expected. Great!</p><p>Now, let’s spice things up a bit.</p><h4>Polkit Authentication</h4><p>For the uninitiated, PolicyKit (Polkit for short) is the authorization system used by most of the Linux desktop today. Opened your software center and got prompted for a password? That’s polkit. Tried running GParted and you need root privileges? Polkit. Polkit automatically manages showing those nice authorization popups, so it’s less work for the dev and more ease of use for the user.</p><h4>How do I use Polkit?</h4><p>This is where the docs start to fall short. There’s not a lot of actual documentation on integrating your own app with Polkit; <a href="https://www.freedesktop.org/software/polkit/docs/latest/">you could hunt through the docs all day to find out how it works</a> but TL;DR: it’s a D-Bus API (and a C library, but of course we want to do Python, so D-Bus it is).</p><p>First, you need to configure a polkit auth level. Write this file to /usr/share/polkit-1/actions :</p><p>com.example.HelloWorld.policy</p><pre>&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot;?&gt;<br>&lt;!DOCTYPE policyconfig PUBLIC<br> &quot;-//freedesktop//DTD PolicyKit Policy Configuration 1.0//EN&quot;<br> &quot;<a href="http://www.freedesktop.org/standards/PolicyKit/1.0/policyconfig.dtd">http://www.freedesktop.org/standards/PolicyKit/1.0/policyconfig.dtd</a>&quot;&gt;<br>&lt;policyconfig&gt;</pre><pre>&lt;vendor&gt;Example&lt;/vendor&gt;<br>  &lt;vendor_url&gt;<a href="https://example.com/example">https://example.com/example</a>&lt;/vendor_url&gt;</pre><pre>&lt;action id=&quot;com.example.HelloWorld.auth&quot;&gt;<br>    &lt;description gettext-domain=&quot;systemd&quot;&gt;Authorization&lt;/description&gt;<br>    &lt;message gettext-domain=&quot;systemd&quot;&gt;Authentication is needed to perform this action.&lt;/message&gt;<br>    &lt;defaults&gt;<br>        &lt;!--These describe the auth level needed to do this.<br>            Auth_admin, the current one, requires admin authentication every time.<br>            Auth_admin_keep behaves like sudo, saving the password for a few minutes.</pre><pre>Allow_inactive allows it to be accessed from SSH etc. Allow_active allows it to be accessed from the desktop.<br>            Allow_any is a combo of both.<br>        --&gt;<br>      &lt;allow_any&gt;auth_admin&lt;/allow_any&gt;<br>      &lt;allow_inactive&gt;auth_admin&lt;/allow_inactive&gt;<br>      &lt;allow_active&gt;auth_admin&lt;/allow_active&gt;<br>    &lt;/defaults&gt;<br>  &lt;/action&gt;<br>&lt;/policyconfig&gt;</pre><p>This XML configuration basically defines a polkit privilege called com.example.HelloWorld.auth which requires the user to enter admin credentials to gain authorization.</p><p>Of course, you also need to write the code to check privileges. To save time, here’s a convenience function for checking polkit privileges:</p><p><a href="https://pastebin.com/URUKRGex">Pastebin</a></p><figure><img alt="" src="https://cdn-images-1.medium.com/max/597/1*UAI4lld-fvZXrguOiPskBA.png" /><figcaption>Vim screenshot because I couldn’t get it to format</figcaption></figure><p>Make sure to add self.dbus_info = None and self.polkit = None to the __init__ function.</p><p>Here’s a general summary:</p><p>1–8: Get a reference to the DBus object and use it to get the Unix Process ID of the user who called the method (we’ll need this for polkit).</p><p>10–15: Get a reference to the Polkit object.</p><p>17–32: Use the Polkit object to check authorization. If it times out, try again. The polkit CheckAuthorization method takes these arguments:</p><ul><li>Subject (aka type — “unix-process” here — and a dictionary of details (pid as UInt32 and start time of 0 here ))</li><li>Privilege — a string describing the Polkit privilege we defined earlier.</li><li>Details — details describing the action; for example, you can set a message for the authorization dialog that overrides the default one (here we leave it empty)</li><li>CheckAuthorizationFlags — aka 0 = no attempt to authenticate user, or 1 = show them an authentication dialog</li><li>CancellationID — just leave this one empty</li><li>timeout=seconds until timeout</li></ul><p>CheckAuthorization will return a set of values in the format (bool is_authorized, bool is_challenge, dict details). is_authorized is likely the one you want to look at; it tells you whether the user is authenticated or not. We return false if is_auth is false, true otherwise; if we choose, we can also substitute returning true or false for raising an exception when is_auth is false.</p><p>Let’s try it out:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*9hOd9dNSQHf8SUhP9AgSEg.png" /></figure><p>Whoops — it gives us an error. Only services running as root or <a href="https://www.freedesktop.org/software/polkit/docs/latest/polkit.8.html">running as an action owner (that we have to specify in the config file)</a> can actually check for authorization, which makes sense given that non-privileged services would have little use for authorization. We could just run the service as root, but because it’s on a SessionBus, the service won’t be accessible to regular users, which ruins the point of authentication. We’ll need to convert this service into a SystemBus service.</p><h4>Converting to SystemBus</h4><p>SystemBus services are a little more involved than SessionBus services. Unlike SessionBus services, SystemBus services require a config file to specify the service; if you don’t have it, you’ll get an error. Write this file to /etc/dbus-1/system.d/ :</p><p>com.example.HelloWorld.conf</p><pre>&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot;?&gt;<br>&lt;!DOCTYPE busconfig PUBLIC &quot;-//freedesktop//DTD D-BUS Bus Configuration 1.0//EN&quot;<br>&quot;<a href="http://www.freedesktop.org/standards/dbus/1.0/busconfig.dtd">http://www.freedesktop.org/standards/dbus/1.0/busconfig.dtd</a>&quot;&gt;<br>&lt;busconfig&gt;<br>  &lt;type&gt;system&lt;/type&gt;<br>  &lt;!-- Only root can own the service --&gt;<br>  &lt;policy user=&quot;root&quot;&gt;<br>    &lt;allow own=&quot;com.example.HelloWorld&quot;/&gt;<br>    &lt;allow send_destination=&quot;com.example.HelloWorld&quot;/&gt;<br>    &lt;allow send_interface=&quot;com.example.HelloWorld&quot;/&gt;<br>  &lt;/policy&gt;</pre><pre>&lt;!-- Allow anyone to invoke methods on the interfaces --&gt;<br>  &lt;policy context=&quot;default&quot;&gt;<br>    &lt;allow send_destination=&quot;com.example.HelloWorld&quot;/&gt;<br>    &lt;allow send_interface=&quot;com.example.HelloWorld&quot;/&gt;<br>  &lt;/policy&gt;<br>&lt;/busconfig&gt;</pre><p>Once we have written the XML config file, D-Bus should allow us to run our service with sudo: sudo python3 service.py</p><p>Now does it work?</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*iMtmbCPWMIxU_o0mPuG-9g.png" /></figure><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*x_bW7MFLTc890XOCnoH-UQ.png" /></figure><p>Yes it does.</p><p>Congratulations — you’ve just built a D-Bus service with polkit authentication! If you want further D-Bus guidance, check out <a href="https://dbus.freedesktop.org/doc/dbus-python/tutorial.html">the official dbus-python docs.</a></p><h4>Gotchas — debugging</h4><p>Polkit can be finicky sometimes; I lost several hours debugging why I wasn’t getting an authorization dialog before I realized I needed to have my authentication agent running first (to be fair, that particular case was more my own fault than polkit’s, but the point stands). Some useful tips for debugging a polkit service:</p><ul><li>Print out is_challenge. If is_challenge is true, you probably don’t have your authentication agent running; while this shouldn’t be a problem on most major DEs, you may have issues if you configured your own WM setup.</li><li>Print out details; details can show you things you might have missed, such as an incorrect argument placement.</li></ul><h4>Credits</h4><p>A huge thanks to <a href="https://ubuntuforums.org/showthread.php?t=1359397">this guy on Ubuntu Forums</a> from 2009 for providing example code; it really helped me figure out how polkit and D-Bus work (the _check_polkit_privilege() function is mostly copied from his code, with some modifications).</p><p>Also thanks to the D-Bus and dbus-python official documentation.</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=4acc9bc5ed29" width="1" height="1" alt="">]]></content:encoded>
        </item>
    </channel>
</rss>