table of contents
| RPM-LUA(7) | Miscellaneous Information Manual | RPM-LUA(7) |
NAME¶
rpm-lua - RPM embedded Lua interpreter
SYNOPSIS¶
%scriptlet -p <lua>
%{lua:...}
DESCRIPTION¶
Lua is a general purpose programming language specifically designed for embedding in other programs, and RPM includes an embedded Lua interpreter for use in advanced rpm-macros(7) and transaction scriptlets.
The embedded Lua interpreter makes possible various things that are hard or impossible with plain macros or external shell scripts, such as help eliminate dependency loops from package scriptlets.
MACROS¶
Accessing macros¶
The rpm extension has various functions for dealing with macros, but the most convenient way to access rpm-macroproc(7) from the RPM Lua environment is via the global macros table.
Lua makes no difference between table index and field name syntaxes so macros.foo and macros['foo'] are equivalent, use what better suits the purpose.
Like any real Lua table, non-existent items are returned as nil, and assignment can be used to define or undefine macros.
Example:
if not macros.yours then
macros.my = 'my macro' end local v = { '_libdir', '_bindir', '_xbindir' } for _, v in ipairs(v) do
if not macros[v] then
macros[v] = 'default'
end end
All macros share the the same global Lua execution environment.
Calling parametric macros¶
Parametric macros (including all built-in macros) can be called in a Lua native manner via the macros table, with either macros.name() or macros[name]() syntax.
Arguments are passed through a single argument, which is either
- a single string, in which case it's expanded and split with the macro-native rules
- a table, in which case the table contents are used as literal arguments that are not expanded in any way
Example 1:
macros.with('foo')
Example 2:
macros.dostuff({'one', 'two', 'three'})
Returning data¶
By definition, anything print()'ed in Lua will end up in the macro expansion. Lua macros can also also return their output, which makes programming helper macros look more natural.
Example:
%sum() %{lua:
local v = 0
for _, a in ipairs(arg) do
v = v + tonumber(a)
end
return v
}
Options and arguments¶
Parametric Lua macros receive their options and arguments as two local tables opt and arg, where opt holds processed option values keyed by the option character, and arg contains arguments numerically indexed.
These tables are always present regardless of whether options or arguments were actually passed to simplify use.
Example:
%foo(a:b) %{lua:
if opt.b then
print('do b')
else
print('or not')
end
if opt.a == 's' then
print('do s')
end
if #arg == 0 then
print('no arguments :(')
else
for i = 1, #arg do
print(arg[i])
end
end
}
SCRIPTLETS¶
The internal Lua can be used as the interpreter of RPM any transaction scriptlets, including triggers and file triggers:
Example:
%pre -p <lua>
print('Hello from Lua')
While the venerable /bin/sh is usually more convenient for packaging related scripting activities, the embedded Lua interpreter has some unique advantages for transaction scriptlets: they add no extra dependencies to the packages and so can help eliminate dependency loops. This can be a crucial difference in the early "bootstrap" package set in an initial install.
In particular, an embedded Lua script is the only generally usable option in %pretrans scriplets during the initial installation of a system.
Embedded Lua is also much faster than executing a potentially heavyweight interpreter just to run a couple of lines of shell script.
Note: scriptlets using the internal Lua should not make assumptions about sharing the execution environment with other scriptlets.
Arguments¶
Scriptlet arguments are accessible from a global arg table.
Note: in Lua, indexes customarily start at 1 (one) instead of 0 (zero), and for the better or worse, the RPM implementation follows this practise. Thus the scriptlet arg indexes are off by one from general expectation based on traditional scriptlet arguments. The argument containing number of installed package instances is arg[2] and the similar argument for trigger targets is arg[3], vs the traditional $1 and $2 in shell scripts.
Example:
%postun -p <lua> if arg[2] == 0 then
print("erasing") end
Relocatable packages¶
Scriptlets of relocatable packages additionally carry a global RPM_INSTALL_PREFIX table containing all the possible prefixes of the package.
Added: 4.18.0
Exit status¶
While scriptlets shouldn't be allowed to fail normally, you can signal scriptlet failure status by using Lua's error(msg, [level]) function if you need to.
SPEC FILES¶
In the context of a rpm-spec(5) file parse with rpmbuild(1) or rpmspec(1), the RPM Lua environment contains the following spec specific global tables:
patches
patch_nums
sources
source_nums
Example:
for i, p in ipairs(patches) do
print(string.format("echo %d: %sn", patch_nums[i], patches[i])) end
EXTENSIONS¶
In addition to Lua standard libraries (subject to the Lua version RPM is linked to), the following extensions are available in the RPM internal Lua interpreter. These can be used in all contexts where the internal Lua can be used.
rpm extension¶
The following RPM specific functions are available:
b64decode(arg)
Example:
blob = 'binary data' print(blob) e = rpm.b64encode(blob) print(e) d = rpm.b64decode(e) print(d)
b64encode(arg [, linelen])
define("name body")
Example:
rpm.define('foo 1')
execute(path [, arg1 [,...])
For a better control over the process execution and output, see rpm.spawn().
Added: 4.15.0
Example:
rpm.execute('ls', '-l', '/')
Example:
rpm.expand('%{_libdir}/mydir')
glob(pattern, [flags])
Example:
for i, p in ipairs(rpm.glob('*')) do
print(p)
end
interactive()
Example:
rpm --eval "%{lua: rpm.interactive()}"
isdefined(name)
Example:
if rpm.isdefined('_libdir') then
...
end
load(path)
Example:
rpm.load('my.macros')
open(path, [mode[.flags]])
path is file name string, optionally followed with mode string to specify open behavior:
- a: open for append
- w: open for writing, truncate
- r: open for reading (default)
- +: open for reading and writing
- x: fail if file exists
and optionally followed by rpm-payloadflags(7) for compression and decompression.
Added: 4.17.0
Example:
f = rpm.open('some.txt.gz', 'r.gzdio')
print(f:read())
The returned rpm.fd object has the following methods:
fd:close()
Close the file stream.
Example:
f = rpm.open('file')
f:close()
fd:flush()
Flush the file stream.
Example:
f = rpm.open('file', 'w')
f:write('foo')
f:flush()
f:close()
fd:read([len])
Read data from the file stream up to len bytes or if not specified, the entire file.
Example:
f = rpm.open('/some/file')
print(f:read())
fd:seek(mode, offset)
Reposition the file offset of the stream. mode is one of set, cur and end, and offset is relative to the mode: absolute, relative to current or relative to end. Not all streams support seeking.
Returns file offset after the operation.
See also lseek(3).
Example:
f = rpm.open('newfile', 'w')
f:seek('set', 555)
f:close()
fd:write(buf [, len])
Write data in buf to the file stream, either in its entirety or up to len bytes if specified.
Example:
f = rpm.open('newfile', 'w')
f:write('data data')
f:close()
fd:reopen(mode)
Reopen a stream with a new mode (see rpm.open()).
Example:
rpm.open('some.txt.gz')
f = f:reopen('r.gzdio')
print(f:read())}
redirect2null(fdno) (OBSOLETE)
This function is obsolete and only available for RPM v4 packages for backwards compatibility. Use `rpm.spawn()` or `rpm.execute()` instead.
pid = posix.fork() if pid == 0 then
posix.redirect2null(2)
assert(posix.exec('/bin/awk')) elseif pid > 0 then
posix.wait(pid) end
spawn({command} [, {actions}])
{command} is a table consisting of the command and its arguments. An optional second table can be used to pass various actions related to the command execution, currently supported are:
| Action | Argument(s) | Description |---------|---------------------- | *stdin* | path | Redirect standard input to path | *stdout*| path | Redirect standard output to path | *stderr*| path | Redirect standard error to path
Returns the command exit status: zero on success, or a tuplet of (nil, message, code) on failure.
Added: 4.20
Example:
rpm.spawn({'systemctl', 'restart', 'httpd'}, {stderr='/dev/null'})
undefine(name)
Note that this is only pops the most recent macro definition by the given name from stack, ie there may be still macro definitions by the same name after an undefine operation.
Example:
rpm.undefine('zzz')
vercmp(v1, v2)
Note: in RPM < 4.16 this operated on version segments only, which does not produce correct results on full EVR strings.
Example:
rpm.vercmp('1.2-1', '2.0-1')
ver(evr), ver(e, v, r)
Added: 4.17.0
Example:
v1 = rpm.ver('5:1.0-2)
v2 = rpm.ver(3, '5a', 1)
if v1 < v2 then
...
end
if v1.e then
...
end
posix extension¶
Lua standard library offers fairly limited set of io operations. The posix extension greatly enhances what can be done from Lua.
The following functions are available in the posix namespace, ie to call them use posix.function(). This documentation concentrates on the Lua API conventions, for further information on the corresponding system calls refer to the system manual, eg access(3) for posix.access().
access(path [, mode])
- r: readable
- w: writable
- x: executable
- f: exists
Example:
if posix.access('/bin/rpm', 'x') then
...
end
chdir(path)
Example:
posix.chdir('/tmp')
chmod(path, mode)
Example:
posix.chmod('aa', 600)
posix.chmod('bb', 'rw-')
posix.chmod('cc', 'u+x')
chown(path, user, group)
Note: This is a privileged operation.
Example:
posix.chown('aa', 0, 0)
posix.chown('bb', 'nobody', 'nobody')
ctermid()
dir([path])
Example:
for i,p in pairs(posix.dir('/')) do
print(p..'n')
end
errno()
Example:
f = '/zzz' if not posix.chmod(f, 100) then
s, n = posix.errno()
print(f, s) end
exec(path [, args...]) (OBSOLETE)
This function is obsolete and only available for RPM v4 packages for backwards compatibility. Use rpm.spawn() or rpm.execute() instead.
files([path])
Example:
for f in posix.files('/') do
print(f..'n')
end
fork() (OBSOLETE)
This function is obsolete and only available for RPM v4 packages for backwards compatibility. Use rpm.spawn() or rpm.execute() instead.
Example:
pid = posix.fork() if pid == 0 then
posix.exec('/foo/bar') elseif pid > 0 then
posix.wait(pid) end
getcwd()
getenv(name)
Example:
if posix.getenv('HOME') ~= posix.getcwd() then
print('not at home')
end
getgroup(group)
Example:
print(posix.getgroup('wheel').gid)
getlogin()
getpasswd([user [, selector]])
- name
- uid
- gid
- dir
- shell
- gecos
- passwd
If omitted, a table with all these fields is returned.
Example:
pw = posix.getpasswd(posix.getlogin(), 'shell')|
getprocessid([selector])
- egid: effective group id
- euid: effective user id
- gid: group id
- uid: user id
- pgrp: parent group id
- pid: parent user id
- ppid: parent pid
If omitted, a table with all these fields is returned.
Example:
if posix.getprocessid('pid') == 1 then
...
end
kill(pid [, signal])
Example:
posix.kill(posix.getprocessid('pid'))
link(oldpath, newpath)
Example:
f = rpm.open('aaa', 'w')
posix.link('aaa', 'bbb')
mkdir(path)
mkfifo(path)
pathconf(path [, selector])
- link_max
- max_canon
- max_input
- name_max
- path_max
- pipe_buf
- chown_restricted
- no_trunc
- vdisable.
If omitted, a table with all these fields is returned.
Example:
posix.pathconf('/', 'path_max')
putenv(string)
readlink(path)
Example:
posix.mkdir('aaa')
posix.symlink('aaa', 'bbb')
print(posix.readlink('bbb'))
rmdir(path)
setgid(group)
Note: This is a privileged operation.
setuid(user)
Note: This is a privileged operation.
Example:
posix.setuid('nobody')
sleep(seconds)
stat(path [, selector])
- mode
- ino
- dev
- nlink
- uid
- gid
- size
- atime
- mtime
- ctime
- type.
If omitted, a table with all these fields is returned.
Example:
print(posix.stat('/tmp', 'mode'))|
s1 = posix.stat('f1')
s2 = posix.stat('f2')
if s1.ino == s2.ino and s1.dev == s2.dev then
...
end
symlink(oldpath, newpath)
Example:
posix.mkdir('aaa')
posix.symlink('aaa', 'bbb')
sysconf([selector])
- arg_max
- child_max
- clk_tck
- ngroups_max
- stream_max
- tzname_max
- open_max
- job_control
- saved_ids
- version.
If omitted, a table with all these fields is returned.
Example:
posix.sysconf('open_max')|
times([selector])
- utime
- stime
- cutime
- cstime
- elapsed
If omitted, a table with all these fields is returned.
Example:
t = posix.times() print(t.utime, t.stime)
ttyname([fd])
Example:
if not posix.ttyname() then
... endif
umask([mode])
Example:
print(posix.umask())
posix.umask(222)
posix.umask('ug-w')
posix.umask('rw-rw-r--')
uname(format)
- %m: Name of the hardware type
- %n: Name of this node
- %r: Current release level of this implementation
- %s: Name of this operation system
- %v: Current version level of this implementation
Example:
print(posix.uname('%s %r'))
utime(path [, mtime [, ctime]])
If mtime or ctime are omitted, current time is used, similar to touch(1).
Example:
posix.mkdir('aaa')
posix.utime('aaa', 0, 0)
wait([pid]) (DEPRECATED)
This function is obsolete and only available for RPM v4 packages for backwards compatibility. Use rpm.spawn() or rpm.execute() instead.
Example:
pid = posix.fork() if pid == 0 then
posix.exec('/bin/ls')) elseif pid > 0 then
posix.wait(pid) end
setenv(name, value [, overwrite])
Example:
posix.setenv('HOME', '/me', true)
unsetenv(name)
EXTENDING AND CUSTOMIZING¶
On initialization, RPM executes a global init.lua Lua initialization script from the directory %getconfdir expands to, typically /usr/lib/rpm/init.lua. This can be used to customize the rpm Lua environment without recompiling RPM.
For the embedded Lua interpreter, module's loaded with require are primarily searched from %{getconfdir}/lua/. %_rpmluadir is a shorthand for this path.
SEE ALSO¶
| 2025-11-07 | RPM 6.0.0 |