Skip to main content

the avatar of FreeAptitude

Upgrading openSUSE with zypper

With no doubt, an installation from scratch allows to get rid of all misconfigurations, packages installed once and never used, and broken or unneeded dependencies that most of the times we accumulate from time to time while playing new applications or system settings, upgrading openSUSE Leap through zypper instead, might perform enough well and, at the same time, avoiding to repeat the standard, several, boring configuration steps, to save time.

the avatar of Federico Mena-Quintero

Containing mutability in GObjects

Traditionally, GObject implementations in C are mutable: you instantiate a GObject and then change its state via method calls. Sometimes this is expected and desired; a GtkCheckButton widget certainly can change its internal state from pressed to not pressed, for example.

Other times, objects are mutable while they are being "assembled" or "configured", and only yield a final immutable result until later. This is the case for RsvgHandle from librsvg.

Please bear with me while I write about the history of the RsvgHandle API and why it ended up with different ways of doing the same thing.

The traditional RsvgHandle API

The final purpose of an RsvgHandle is to represent an SVG document loaded in memory. Once it is loaded, the SVG document does not change, as librsvg does not support animation or creating/removing SVG elements; it is a static renderer.

However, before an RsvgHandle achieves its immutable state, it has to be loaded first. Loading can be done in two ways:

  • The historical/deprecated way, using the rsvg_handle_write() and rsvg_handle_close() APIs. Plenty of code in GNOME used this write/close idiom before GLib got a good abstraction for streams; you can see another example in GdkPixbufLoader. The idea is that applications do this:
file = open a file...;
handle = rsvg_handle_new ();

while (file has more data) {
   rsvg_handle_write(handle, a bit of data);
}

rsvg_handle_close (handle);

// now the handle is fully loaded and immutable

rsvg_handle_render (handle, ...);
file = g_file_new_for_path ("/foo/bar.svg");
stream = g_file_read (file, ...);
handle = rsvg_handle_new ();

rsvg_handle_read_stream_sync (handle, stream, ...);

// now the handle is fully loaded and immutable

rsvg_handle_render (handle, ...);

A bit of history

Let's consider a few of RsvgHandle's functions.

Constructors:

  • rsvg_handle_new()
  • rsvg_handle_new_with_flags()

Configure the handle for loading:

  • rsvg_handle_set_base_uri()
  • rsvg_handle_set_base_gfile()

Deprecated loading API:

  • rsvg_handle_write()
  • rsvg_handle_close()

Streaming API:

  • rsvg_handle_read_stream_sync()

When librsvg first acquired the concept of an RsvgHandle, it just had rsvg_handle_new() with no arguments. About 9 years later, it got rsvg_handle_new_with_flags() to allow more options, but it took another 2 years to actually add some usable flags — the first one was to configure the parsing limits in the underlying calls to libxml2.

About 3 years after RsvgHandle appeared, it got rsvg_handle_set_base_uri() to configure the "base URI" against which relative references in the SVG document get resolved. For example, if you are reading /foo/bar.svg and it contains an element like <image xlink:ref="smiley.png"/>, then librsvg needs to be able to produce the path /foo/smiley.png and that is done relative to the base URI. (The base URI is implicit when reading from a specific SVG file, but it needs to be provided when reading from an arbitrary stream that may not even come from a file.)

Initially RsvgHandle had the write/close APIs, and 8 years later it got the streaming functions once GIO appeared. Eventually the streaming API would be the preferred one, instead of just being a convenience for those brave new apps that started using GIO.

A summary of librsvg's API may be something like:

  • librsvg gets written initially; it doesn't even have an RsvgHandle, and just provides a single function which takes a FILE * and renders it to a GdkPixbuf.

  • That gets replaced with RsvgHandle, its single rsvg_handle_new() constructor, and the write/close API to feed it data progressively.

  • GIO appears, we get the first widespread streaming APIs in GNOME, and RsvgHandle gets the ability to read from streams.

  • RsvgHandle gets rsvg_handle_new_with_flags() because now apps may want to configure extra stuff for libxml2.

  • When Cairo appears and librsvg is ported to it, RsvgHandle gets an extra flag so that SVGs rendered to PDF can embed image data efficiently.

It's a convoluted history, but git log -- rsvg.h makes it accessible.

Where is the mutability?

An RsvgHandle gets created, with flags or without. It's empty, and doesn't know if it will be given data with the write/close API or with the streaming API. Also, someone may call set_base_uri() on it. So, the handle must remain mutable while it is being populated with data. After that, it can say, "no more changes, I'm done".

In C, this doesn't even have a name. Everything is mutable by default all the time. This monster was the private data of RsvgHandle before it got ported to Rust:

struct RsvgHandlePrivate {
    // set during construction
    RsvgHandleFlags flags;

    // GObject-ism
    gboolean is_disposed;

    // Extra crap for a deprecated API
    RsvgSizeFunc size_func;
    gpointer user_data;
    GDestroyNotify user_data_destroy;

    // Data only used while parsing an SVG
    RsvgHandleState state;
    RsvgDefs *defs;
    guint nest_level;
    RsvgNode *currentnode;
    RsvgNode *treebase;
    GHashTable *css_props;
    RsvgSaxHandler *handler;
    int handler_nest;
    GHashTable *entities;
    xmlParserCtxtPtr ctxt;
    GError **error;
    GCancellable *cancellable;
    GInputStream *compressed_input_stream;

    // Data only used while rendering
    double dpi_x;
    double dpi_y;

    // The famous base URI, set before loading
    gchar *base_uri;
    GFile *base_gfile;

    // Some internal stuff
    gboolean in_loop;
    gboolean is_testing;
};

"Single responsibility principle"? This is a horror show. That RsvgHandlePrivate struct has all of these:

  • Data only settable during construction (flags)
  • Data set after construction, but which may only be set before loading (base URI)
  • Highly mutable data used only during the loading stage: state machines, XML parsers, a stack of XML elements, CSS properties...
  • The DPI (dots per inch) values only used during rendering.
  • Assorted fields used at various stages of the handle's life.

It took a lot of refactoring to get the code to a point where it was clear that an RsvgHandle in fact has distinct stages during its lifetime, and that some of that data should only live during a particular stage. Before, everything seemed a jumble of fields, used at various unclear points in the code (for the struct listing above, I've grouped related fields together — they were somewhat shuffled in the original code!).

What would a better separation look like?

In the master branch, now librsvg has this:

/// Contains all the interior mutability for a RsvgHandle to be called
/// from the C API.
pub struct CHandle {
    dpi: Cell<Dpi>,
    load_flags: Cell<LoadFlags>,

    base_url: RefCell<Option<Url>>,
    // needed because the C api returns *const char
    base_url_cstring: RefCell<Option<CString>>,

    size_callback: RefCell<SizeCallback>,
    is_testing: Cell<bool>,
    load_state: RefCell<LoadState>,
}

Internally, that CHandle struct is now the private data of the public RsvgHandle object. Note that all of CHandle's fields are a Cell<> or RefCell<>: in Rust terms, this means that those fields allow for "interior mutability" in the CHandle struct: they can be modified after intialization.

The last field's cell, load_state, contains this type:

enum LoadState {
    Start,

    // Being loaded using the legacy write()/close() API
    Loading { buffer: Vec<u8> },

    // Fully loaded, with a Handle to an SVG document
    ClosedOk { handle: Handle },

    ClosedError,
}

A CHandle starts in the Start state, where it doesn't know if it will be loaded with a stream, or with the legacy write/close API.

If the caller uses the write/close API, the handle moves to the Loading state, which has a buffer where it accumulates the data being fed to it.

But if the caller uses the stream API, the handle tries to parse an SVG document from the stream, and it moves either to the ClosedOk state, or to the ClosedError state if there is a parse error.

Correspondingly, when using the write/close API, when the caller finally calls rsvg_handle_close(), the handle creates a stream for the buffer, parses it, and also gets either into the ClosedOk or ClosedError state.

If you look at the variant ClosedOk { handle: Handle }, it contains a fully loaded Handle inside, which right now is just a wrapper around a reference-counted Svg object:

pub struct Handle {
    svg: Rc<Svg>,
}

The reason why LoadState::ClosedOk does not contain an Rc<Svg> directly, and instead wraps it with a Handle, is that this is just the first pass at refactoring. Also, Handle contains some API-level logic which I'm not completely sure makes sense as a lower-level Svg object. We'll see.

Couldn't you move more of CHandle's fields into LoadState?

Sort of, kind of, but the public API still lets one do things like call rsvg_handle_get_base_uri() after the handle is fully loaded, even though its result will be of little value. So, the fields that hold the base_uri information are kept in the longer-lived CHandle, not in the individual variants of LoadState.

How does this look from the Rust API?

CHandle implements the public C API of librsvg. Internally, Handle implements the basic "load from stream", "get the geometry of an SVG element", and "render to a Cairo context" functionality.

This basic functionality gets exported in a cleaner way through the Rust API, discussed previously. There is no interior mutability in there at all; that API uses a builder pattern to gradually configure an SVG loader, which returns a fully loaded SvgHandle, out of which you can create a CairoRenderer.

In fact, it may be possible to refactor all of this a bit and implement CHandle directly in terms of the new Rust API: in effect, use CHandle as the "holding space" while the SVG loader gets configured, and later turned into a fully loaded SvgHandle internally.

Conclusion

The C version of RsvgHandle's private structure used to have a bunch of fields. Without knowing the code, it was hard to know that they belonged in groups, and each group corresponded roughtly to a stage in the handle's lifetime.

It took plenty of refactoring to get the fields split up cleanly in librsvg's internals. The process of refactoring RsvgHandle's fields, and ensuring that the various states of a handle are consistent, in fact exposed a few bugs where the state was not being checked appropriately. The public C API remains the same as always, but has better internal checks now.

GObject APIs tend to allow for a lot of mutability via methods that change the internal state of objects. For RsvgHandle, it was possible to change this into a single CHandle that maintains the mutable data in a contained fashion, and later translates it internally into an immutable Handle that represents a fully-loaded SVG document. This scheme ties in well with the new Rust API for librsvg, which keeps everything immutable after creation.

the avatar of Jim Fehlig

Let’s Brew!

Where to start? There are many blogs on homebrewing from folks with lots of experience so I won’t be providing any earth-shattering information for knowledgeable brewers, but I can provide a perspective from a novice that might be interesting for others considering diving into homebrewing. Along the way perhaps I’ll be lucky enough to also get some feedback and tips from the pros! I’ll first talk briefly about the ingredients used to make beer and then discuss the process of turning those ingredients into the tasty beverage we all love using the Clawhammer BIAB system I discussed in a previous post.

Beer is actually made from a simple list of ingredients: water, barley, hops, and yeast. All are very important to the quality of the final product. Although the ingredient list is simple, the chemical characteristics of water, the types of barley and the degree of malting, the families of hops, and the strains of yeast vary greatly, which accounts for the seemingly endless varieties of beer we see. Some beers include ingredients like fruit, honey, and other sweeteners, but IMO those are for amateurs and kids and will not be considered in this blog post. We’ll save those ingredients for the ice cream and pies!

The beer we are brewing here is a recipe from a good friend and fellow brewer, which he named Baldy Chutes Pale Ale in honor of the famed ski run at Alta Ski area. BTW, in the winters when not brewing or working I can often be spotted at Alta :-). Ok, back to making beer. Let’s review the ingredients: water, malted barley, hops, and yeast. Many brewers say that yeast is the secrete sauce to a beer. I don’t have enough experience to confirm or deny this rumor, but understand its role in the process enough to know it is important. Therefore I make a yeast starter three days before brewing. Being one that likes simplicity, I use a canned yeast starter instead of preparing one traditionally. I buy the bagged yeast, adding it, the yeast starter, and water to a flask and drop in a magnetic stir stick.

The flask is placed on a stir plate that slowly stirs the yeast in the starter to ensure it has oxygen to flourish. I try to keep the concoction at a constant temperature, ideally 68F, but it really at the mercy of the temperature of my office. On the morning of brewing, I place the flask in the refrigerator so the yeast will separate from the starter and settle to the bottom.

Another task I’ve learned to do the night before brewing is filling the boil pot with cold tap water, particularly in the winter months when the water is very cold. This allows raising it to room temperature without using electricity. It saves a few electrons for the conservation-minded folks. One other item to consider the night before brewing, or even before starting your yeast starter, is that brewing takes some time. I’ve found I need to reserve 5-6 hours for preparation, actual brewing, and cleanup. I don’t start a brew session unless I have the day free, or at least 8 hours to give some buffer in case of catastrophe.

For my last brew, I did all the preparation the night before. I installed the boil pot heating element, inserted the grain basket, connected the circulation pump, setup the controller and temperature probe, filled the pot with water per recipe, and added a campden tablet per gallon of water to help neutralize the city water chlorine. On brew day I only had to turn on the controller and heat the water to mash-in temperature.

Once water is at mash-in temperature, it is time to slowly stir in the milled barlies of your recipe. Avoid dumping to much of that grains at once, otherwise they are likely to form dough balls that will act like little safes for the grain’s sugars. We wont be able to extract the sugar locked inside those dough balls. So slowly pour in your grains, giving the mixture a good stir each time grains are added, being careful not to damage the grain basket. After all the grain is thoroughly mixed with the water in the grain basket and the mixture has returned to mash-in temperature, start circulating the water over the grains using the system’s pump. Typical beer recipes generally call for an hour or so of mashing, a process meant to extract the grain’s sugars and produce our prized wort.

The Clawhammer system makes mash-out easy by providing the grain basket and hooks that make a temporary ledge for the basket to drain as much of the sugar-saturated water from the grains. While the grains are draining, the controller can be adjusted to begin bringing the wort to boil temperature. After the grains have mostly drained they can be removed from the pot and placed in a 5 gallon bucket for further draining.

This allows covering the boil pot with lid and a towel or other insulation to help accelerate our goal of boil temperature. I’ve found it can take 15-20 minutes to reach boil temperature with the 110 volt element. You should experiment with boil temperature. I’ve found most recipes I brew boil around 200F at my house, which is 4600 feet elevation. The primary advice I can give is to keep a close eye on the pot once you’ve reached 195F to avoid the dreaded boil-over. Wort boil-over can make an awful mess and extend your brew time with extra cleaning, not to mention the hazard of erupting sugar water at boil temperature!

Most of the recipes I’ve attempted thus far call for 60 minutes boil time. At various intervals of the boil your recipe may call for adding hops, yeast nutrient, and whirlfloc tablets or Irish moss. The latter is used to help clarify your beer. Hops not only add unique flavors to beer but also bitter the beer to offset the sweetness of the barley sugars. I enjoy taking the occasional whiff of the hops before adding them to the boil. There’s something about the smell of fresh hop pellets that’s hard to resist. Interestingly, hops are a relative of marijuana and are grown similarly, where only the female plants are allowed to thrive and all male plants are removed as soon as they are identified.

It’s all downhill after the boil is complete. There is still lots of cleaning to do, but the real time-consuming part of the brew is done. It is time to chill the wort to fermenting temperature, which is dictated by your recipe. Most ales are fermented at 68F. Lagering beer is a more time-consuming process where the fermentation is done slowly at various temperatures. I have not yet done any lagering since you really need another chest freezer or refrigerator with a dual stage controller to precisely control the temperature of the fermenting beer.

Sanitation of all equipment is a must after the boil to avoid contaminating and spoiling your beer. I use Star San non-rinse sanitizer for all of my post-boil brew equipment: carboys, kegs, siphon tubes, etc. After thoroughly sanitizing a carboy and siphon tube I transfer the wort into the carboy for fermentation, collecting a bit of the wort along the way to take a starting gravity (SG) reading.

Before adding the yeast, I like to aerate the beer with a small pump similar to a fish tank aerator. This ensures there is plenty of oxygen in the wort for the yeast to thrive and turn all that sweet sugar to alcohol! Before adding the yeast I decant most of the yeast starer leaving just enough to thin the thick yeast layer on the bottom of the flask to ensure the all-important yeast is completely emptied from the flask.

After the yeast is added to the wort it is time to seal the carboy with an airlock and let the fermentation begin! I wrap my carboy in a heater to keep the temperature consistent. For ales I can always find a place in my house that will be at or below 68F, even in the summer months.

I always like to keep an eye on my beer for a few days into the fermentation process. Once the yeast really gets after the sugars, the mixture will look very cloudy and hopefully you’ll have lots of air bubbles in the airlock, indicating the yeast is happy and doing its job. I always find fermentation fastinating. To me, it is a bit of magic. I agree with the old addage that brewers make wort and yeast makes beer!

About half way through the fermenation process I like to transfer the beer to a secondary carboy. A lot of yeast waste, hop bits, and small particles of barley will settle to the bottom of the primary fermentor. This is a good time to transfer the contents to another fermentor and leave the baggage behind. Strictly speaking it is not needed, but I like a non-cloudy beer so put in a bit of extra time with the secondary fermentor.

After the second stage fermentation is complete it is time to transfer the beer to a keg for carbonation. This is also the time to collect a bit of the flat, fermented beer for a final gravity (FG) reading. This allows us to determine the alcohol content of the beer. The formula for Alcohol By Volume (ABV) is: ABV% = (SG-FG)/0.776. Or for the pedantic math nerds out there: ABV% = (SG-FG)*131.

If there are no existing kegs on tap, I carbonate a freshly kegged beer at 22 PSI for a week. If my gas system is in use for pouring (10-12 PSI) I’ll let the new keg carbonate for two weeks, at which point we reap the rewards of our labor! Fresh beer with no bullshit sweeteners, additives, or preservatives. Beer as it was meant to be enjoyed!

the avatar of Jim Fehlig

BIAB Homebrewing

Being a beer lover, I always wanted to brew my own beer but lacked time, money, and space required form making my own liquid bread. Over my travels and many places I’ve called home I’ve met several homebrewers and even spent afternoons helping brew batches of beer. While lending a helping hand I quickly realized the equipment needed to make beer from all-grain ingredients was the barrier for me. That all changed last fall when I helped a good fried make a batch of Alaska Amber clone. He had recently invested in a new Brew In A Bag (BIAB) system from Clawhammer Supply and I was immediately impressed with the functionality and usability of the system. I decided that afternoon it was time to start brewing my own beer!  I had found a brew system that provided the balance of simplicity and functionality I was looking for. Along with more free time in my life now that kids are older and more independent, it’s a great time to start a new hobby!

The Clawhammer BIAB system includes all the components you need for creating your favorite all-grain beer. It consists of a 10 gallon stainless steel boil pot, a 110 volt heating element (220 volt option available at substantially more cost), a controller for heat and pump, a temperature probe, a 110 volt pump, a plate wort chiller, grain and hop baskets, high temperature, restaurant-grade rubber hose, and all the quick-release fittings and valves needed to connect the system components. The system requires assembly but that was made easy by the helpful videos provided by Clawhammer Supply. That leads me to the only complaint about the system: lack of written documentation. Videos are fine, but I’m old school and generally like to read shit :-).

20190405_123138

When deciding on the 110 vs 220 volt system, I read many reviews stating the 110 system was adequate if the boil pot was insulated. Clawhammer even sells an insulation kit for their boil pots. So I opted for the 110 volt element and fabricated my own insulation wrap for the pot with some insulation bought at the local hardware store.

I already mentioned how I liked the Clawhammer system simplicity. You have a boil pot that heats water with an electric element, a large grain basket that fits inside the pot, and a pump to circulate hot water over the grains to extract their all-important sugars. After mashing the grains and boiling the wort, simply insert the wort chiller in the system to cool the wort before transferring to fermentation storage. The system is also relatively mobile, opening the possibility to take it to the Henry’s Lake place for some Idaho brewing! Perhaps this picture of the entire system in action chilling a pale ale wort will help you understand my excitement around this system. In a followup post I’ll describe the steps for creating an all-natural, preservative and additive free beer with the Clawhammer BIAB system. Until then, enjoy a few cold ones!

20190216_194637-1

the avatar of Nathan Wolf

the avatar of Bernhard M. Wiedemann

experimental openSUSE mirror via IPFS

The InterPlanetary File System (IPFS) can be used to provide files in a more efficient and distributed way than HTTP.

Our filesystem repo already has the go-ipfs client.

You use it with
ipfs daemon --init

And then you can add my Tumbleweed mirror with
zypper ar http://127.0.0.1:8080/ipns/opensuse.zq1.de./tumbleweed/repo/oss/ ipfs-oss

You can also browse the content online at
http://opensuse.zq1.de./tumbleweed/repo/oss/ . During my testing I found that the results are sometimes inappropriately cached on the Cloudflare CDN, so if you used it under this URL without the ipfs client, this might throw signature errors in zypper.

On the server side, the mirror is updated using the syncopensuse script from
https://github.com/bmwiedemann/opensusearchive and consistency of the repo is verified with checkrepo

When a complete repo was synced, dynaname updates a DNS entry to point to the new head:

> host -t txt _dnslink.opensuse.zq1.de.
_dnslink.opensuse.zq1.de is an alias for tumbleweedipfs.d.zq1.de.
tumbleweedipfs.d.zq1.de descriptive text “Last update: 2019-04-03 12:23:43 UTC”
tumbleweedipfs.d.zq1.de descriptive text “dnslink=/ipfs/QmSXEVuU5z23rDxMyFYDhSAUaGRUPswuSXD3aVsBEzucjE”

If you got spare bandwidth and 300 GB disk on some public server, you could also host a mirror of today’s version, simply by doing ipfs pin add QmSXEVuU5z23rDxMyFYDhSAUaGRUPswuSXD3aVsBEzucjE

This is a permalink: http://127.0.0.1:8080/ipfs/QmSXEVuU5z23rDxMyFYDhSAUaGRUPswuSXD3aVsBEzucjE also browsable via any public IPFS gateway. This means, it will always remain on the 20190401 version of Tumbleweed and no changes in content are possible – similar to how a git commit ID always refers to the same data.

So why did I create this IPFS mirror? That is related to my work on reproducible builds for openSUSE. There it regularly happened that published Tumbleweed binaries were built with libraries, compilers and toolchains that were no longer available in current Tumbleweed. This prevented me from verifying that the published binaries were indeed built correctly without manipulation on the OBS build workers.

Now, with this archive of rpms easily available, it was possible to verify many more Tumbleweed packages than before. And most importantly, it remains possible to independently verify even after Tumbleweed moves on to newer versions. This data is going to stay available as long as anyone pins it on a reachable server. I’m going to pin it as long as it remains relevant to me, so probably a bit until after the next full Tumbleweed rebuild – maybe 6 to 12 months.

Thus, it now is even less easy to sneak in binary backdoors during our package build process.

a silhouette of a person's head and shoulders, used as a default avatar

Enabling scroll wheel emulation for the Logitech Trackman Marble using Wayland and GNOME 3

For ergonomic reasons, I’ve long used Trackballs as pointing devices instead of regular mice. For several years now, I have been using a Logitech Trackman Marble and a libinput tweak to use the trackball for scrolling by holding down one of the buttons while spinning the ball.

Top view of a Logitech Trackman Marble Trackball

This worked well until some distributions decided to switch to using Wayland as a replacement for X.org. In the past, I have been reverting back to using X.org (by setting WaylandEnable=True in /etc/gdm/custom.conf), as Wayland does not support the required libinput configurations. I found a workaround that creates a shared library that can be preloaded to implement this, but that looked somewhat hacky to me.

But as Wayland seems to be the way forward and my latest distribution upgrade caused some weird X.org issues (my dual-screen setup did no longer work properly), I caved in and switched to Wayland again. At least all of my screens were properly detected afterward, but the scroll wheel emulation was broken. I did some research to see if the libinput support in Wayland had improved in that regard in the meanwhile, but it seems it doesn’t.

However, I found a solution for enabling mouse wheel emulation in Wayland/GNOME3 on the Arch Linux Wiki: simply run the following command in a terminal window:

gsettings set org.gnome.desktop.peripherals.trackball scroll-wheel-emulation-button 8

Now button 8 (the small button above the left button) acts both as a “back” button (e.g., when browsing web pages) as well as the modifier that turns the trackball into a scroll wheel, just like before. Nifty!

the avatar of Michal Čihař

translation-finder 1.1

The translation-finder module has been released in version 1.1. It is used by Weblate to detect translatable files in the repository making setup of translation components in Weblate much easier. This release brings lot of improvements based on feedback from our users, making the detection more reliable and accurate.

Full list of changes:

  • Improved detection of translation with full language code.
  • Improved detection of language code in directory and file name.
  • Improved detection of language code separated by full stop.
  • Added detection for app store metadata files.
  • Added detection for JSON files.
  • Ignore symlinks during discovery.
  • Improved detection of matching pot files in several corner cases.
  • Improved detection of monolingual Gettext.

Filed under: Debian English SUSE Weblate

the avatar of Nathan Wolf