The Boston Diaries

The ongoing saga of a programmer who doesn't live in Boston, nor does he even like Boston, but yet named his weblog/journal “The Boston Diaries.”

Go figure.

Friday, September 29, 2023

YAML config file? Pain? Try Lua

Several articles about using YAML for configuration have been making the rounds, yet rarely do I see Lua being mentioned as an alternative for configuration files.

Yes, it's a language, but it started out life as a configuration format. It's also small for a language, easy to embed, and easy to sandbox for the paranoid. Here's an example:

lua_State *gL;

bool config_read(char const *conf)
{
  int rc;
  
  assert(conf != NULL);
  
  /*---------------------------------------------------
  ; Create the Lua state, which includes NO predefined
  ; functions or values.  This is literally an empty
  ; slate.  
  ;----------------------------------------------------*/
  
  gL = luaL_newstate();
  if (gL == NULL)
  {
    fprintf(stderr,"cannot create Lua state");
    return false;
  }
  
  /*-----------------------------------------------------
  ; For the truly paranoid about sandboxing, enable the
  ; following code, which removes the string library,
  ; which some people find problematic to leave un-sand-
  ; boxed. But in my opinion, if you are worried about
  ; such attacks in a configuration file, you have bigger
  ; security issues to worry about than this.
  ;------------------------------------------------------*/
  
#ifdef PARANOID
  lua_pushliteral(gL,"x");
  lua_pushnil(gL);
  lua_setmetatable(gL,-2);
  lua_pop(gL,1);
#endif
  
  /*-----------------------------------------------------
  ; Lua 5.2+ can restrict scripts to being text only,
  ; to avoid a potential problem with loading pre-compiled
  ; Lua scripts that may have malformed Lua VM code that
  ; could possibly lead to an exploit, but again, if you
  ; have to worry about that, you have bigger security
  ; issues to worry about.  But in any case, here I'm
  ; restricting the file to "text" only.
  ;------------------------------------------------------*/
  
  rc = luaL_loadfilex(gL,conf,"t");
  if (rc != LUA_OK)
  {
    fprintf(stderr,"Lua error: (%d) %s",rc,lua_tostring(gL,-1));
    return false;
  }
  
  rc = lua_pcall(gL,0,0,0);
  if (rc != LUA_OK)
  {
    fprintf(stderr,"Lua error: (%d) %s",rc,lua_tostring(gL,-1));
    return false;
  }
  
  /*--------------------------------------------
  ; the Lua state gL contains our configuration,
  ; we can now query it for values
  ;---------------------------------------------*/
  
  /* ... */
  return true;
}

Yes, it's all too possible for someone to write:

(function() while true do end end)()

in the configuration and block the process with 100% CPU utilization, but as I stated in the code example, if that's a worry, you have bigger security issues to worry about.

Another nice benefit of using Lua is string management. If you are only using Lua for the configuration file, and once read, don't execute any more Lua code, then there's no need to duplicate the strings for your codebase—just keep using the strings directly from Lua. As long as you close the Lua state at the end of the program, they'll be cleaned up for you. And speaking of strings, you'll also have Lua's “long strings:”

long_string = [[
This is a long Lua string that can
span several lines.  Escapes like '\n' don't work in this,
but then again,
you don't really need the '\n' here because they're part of the 
string.
]]

long_string_2 = [=[
And if you want to embed a literal ']]' in a long string,
you can, no problems here.
]=]

I use Lua for the configuration file for my blogging engine (to see how I pull the configuration from Lua) which looks like:

name        = "A Blog Grows in Cyberspace"
description = "A place where I talk about stuff in cyperspace."
class       = "blog, rants, random stuff, programming"
basedir     = "."
webdir      = "htdocs"
lockfile    = ".modblog.lock"
url         = "http://www.example.com/blog/"
adtag       = "programming"
conversion  = "html"
prehook     = "./prehook_script"
posthook    = "./posthook_script"

author =
{
  name   = "Joe Blog" ,
  email  = "joe@example.com",
}

templates =
{
  {
    template = "html",
    output   = webdir .. "/index.html",
    items    = "7d",
    reverse  = true,
    posthook = "posthook_template_script"
  },
  {
    template = "atom",
    output   = webdir .. "/index.atom",
    items    = 15,
    reverse  = true,
  },
}

But if you think it'll be too complicated to instruct devops as to when to use a comma and not, you can always include semicolons at the end of each line:

name        = "A Blog Grows in Cyberspace";
description = "A place where I talk about stuff in cyperspace.";
class       = "blog, rants, random stuff, programming";
basedir     = ".";
webdir      = "htdocs";
lockfile    = ".modblog.lock";
url         = "http://www.example.com/blog/";
adtag       = "programming";
conversion  = "html";
prehook     = "./prehook_script";
posthook    = "./posthook_script";

author =
{
  name   = "Joe Blog";
  email  = "joe@example.com";
};

templates =
{
  {
    template = "html";
    output   = webdir .. "/index.html";
    items    = "7d";
    reverse  = true;
    posthook = "posthook_template_script";
  };
  {
    template = "atom";
    output   = webdir .. "/index.atom";
    items    = 15;
    reverse  = true;
  };
};

to simplify the configuration instructions (“just add a semicolon to the end of each line … ”). One other benefit—comments. That's one of the biggest complaints about using JSON as a configuration file format—a lack of comments.

Also, you don't even have to mention you are using Lua as a configuration file—most likely, no one will really notice anyway. I used Lua to configure “Project: Sippy-Cup” and “Project: Cleese” at The Enterprise and no one said anything about the format. It was also used for “Project: Bradenburg” (written by another team member) and there were no issues with that either.


Discussions about this entry

Obligatory Picture

An abstract representation of where you're coming from]

Obligatory Contact Info

Obligatory Feeds

Obligatory Links

Obligatory Miscellaneous

Obligatory AI Disclaimer

No AI was used in the making of this site, unless otherwise noted.

You have my permission to link freely to any entry here. Go ahead, I won't bite. I promise.

The dates are the permanent links to that day's entries (or entry, if there is only one entry). The titles are the permanent links to that entry only. The format for the links are simple: Start with the base link for this site: https://boston.conman.org/, then add the date you are interested in, say 2000/08/01, so that would make the final URL:

https://boston.conman.org/2000/08/01

You can also specify the entire month by leaving off the day portion. You can even select an arbitrary portion of time.

You may also note subtle shading of the links and that's intentional: the “closer” the link is (relative to the page) the “brighter” it appears. It's an experiment in using color shading to denote the distance a link is from here. If you don't notice it, don't worry; it's not all that important.

It is assumed that every brand name, slogan, corporate name, symbol, design element, et cetera mentioned in these pages is a protected and/or trademarked entity, the sole property of its owner(s), and acknowledgement of this status is implied.

Copyright © 1999-2024 by Sean Conner. All Rights Reserved.