| .\" |
| .\" This file and its contents are supplied under the terms of the |
| .\" Common Development and Distribution License ("CDDL"), version 1.0. |
| .\" You may only use this file in accordance with the terms of version |
| .\" 1.0 of the CDDL. |
| .\" |
| .\" A full copy of the text of the CDDL should have accompanied this |
| .\" source. A copy of the CDDL is also available via the Internet at |
| .\" http://www.illumos.org/license/CDDL. |
| .\" |
| .\" Copyright (c) 2016, 2019 by Delphix. All Rights Reserved. |
| .\" Copyright (c) 2019, 2020 by Christian Schwarz. All Rights Reserved. |
| .\" Copyright 2020 Joyent, Inc. |
| .\" |
| .Dd May 27, 2021 |
| .Dt ZFS-PROGRAM 8 |
| .Os |
| . |
| .Sh NAME |
| .Nm zfs-program |
| .Nd execute ZFS channel programs |
| .Sh SYNOPSIS |
| .Nm zfs |
| .Cm program |
| .Op Fl jn |
| .Op Fl t Ar instruction-limit |
| .Op Fl m Ar memory-limit |
| .Ar pool |
| .Ar script |
| .Op Ar script arguments |
| . |
| .Sh DESCRIPTION |
| The ZFS channel program interface allows ZFS administrative operations to be |
| run programmatically as a Lua script. |
| The entire script is executed atomically, with no other administrative |
| operations taking effect concurrently. |
| A library of ZFS calls is made available to channel program scripts. |
| Channel programs may only be run with root privileges. |
| .Pp |
| A modified version of the Lua 5.2 interpreter is used to run channel program |
| scripts. |
| The Lua 5.2 manual can be found at |
| .Lk http://www.lua.org/manual/5.2/ |
| .Pp |
| The channel program given by |
| .Ar script |
| will be run on |
| .Ar pool , |
| and any attempts to access or modify other pools will cause an error. |
| . |
| .Sh OPTIONS |
| .Bl -tag -width "-t" |
| .It Fl j |
| Display channel program output in JSON format. |
| When this flag is specified and standard output is empty - |
| channel program encountered an error. |
| The details of such an error will be printed to standard error in plain text. |
| .It Fl n |
| Executes a read-only channel program, which runs faster. |
| The program cannot change on-disk state by calling functions from the |
| zfs.sync submodule. |
| The program can be used to gather information such as properties and |
| determining if changes would succeed (zfs.check.*). |
| Without this flag, all pending changes must be synced to disk before a |
| channel program can complete. |
| .It Fl t Ar instruction-limit |
| Limit the number of Lua instructions to execute. |
| If a channel program executes more than the specified number of instructions, |
| it will be stopped and an error will be returned. |
| The default limit is 10 million instructions, and it can be set to a maximum of |
| 100 million instructions. |
| .It Fl m Ar memory-limit |
| Memory limit, in bytes. |
| If a channel program attempts to allocate more memory than the given limit, it |
| will be stopped and an error returned. |
| The default memory limit is 10 MB, and can be set to a maximum of 100 MB. |
| .El |
| .Pp |
| All remaining argument strings will be passed directly to the Lua script as |
| described in the |
| .Sx LUA INTERFACE |
| section below. |
| . |
| .Sh LUA INTERFACE |
| A channel program can be invoked either from the command line, or via a library |
| call to |
| .Fn lzc_channel_program . |
| . |
| .Ss Arguments |
| Arguments passed to the channel program are converted to a Lua table. |
| If invoked from the command line, extra arguments to the Lua script will be |
| accessible as an array stored in the argument table with the key 'argv': |
| .Bd -literal -compact -offset indent |
| args = ... |
| argv = args["argv"] |
| -- argv == {1="arg1", 2="arg2", ...} |
| .Ed |
| .Pp |
| If invoked from the libZFS interface, an arbitrary argument list can be |
| passed to the channel program, which is accessible via the same |
| "..." syntax in Lua: |
| .Bd -literal -compact -offset indent |
| args = ... |
| -- args == {"foo"="bar", "baz"={...}, ...} |
| .Ed |
| .Pp |
| Note that because Lua arrays are 1-indexed, arrays passed to Lua from the |
| libZFS interface will have their indices incremented by 1. |
| That is, the element |
| in |
| .Va arr[0] |
| in a C array passed to a channel program will be stored in |
| .Va arr[1] |
| when accessed from Lua. |
| . |
| .Ss Return Values |
| Lua return statements take the form: |
| .Dl return ret0, ret1, ret2, ... |
| .Pp |
| Return statements returning multiple values are permitted internally in a |
| channel program script, but attempting to return more than one value from the |
| top level of the channel program is not permitted and will throw an error. |
| However, tables containing multiple values can still be returned. |
| If invoked from the command line, a return statement: |
| .Bd -literal -compact -offset indent |
| a = {foo="bar", baz=2} |
| return a |
| .Ed |
| .Pp |
| Will be output formatted as: |
| .Bd -literal -compact -offset indent |
| Channel program fully executed with return value: |
| return: |
| baz: 2 |
| foo: 'bar' |
| .Ed |
| . |
| .Ss Fatal Errors |
| If the channel program encounters a fatal error while running, a non-zero exit |
| status will be returned. |
| If more information about the error is available, a singleton list will be |
| returned detailing the error: |
| .Dl error: \&"error string, including Lua stack trace" |
| .Pp |
| If a fatal error is returned, the channel program may have not executed at all, |
| may have partially executed, or may have fully executed but failed to pass a |
| return value back to userland. |
| .Pp |
| If the channel program exhausts an instruction or memory limit, a fatal error |
| will be generated and the program will be stopped, leaving the program partially |
| executed. |
| No attempt is made to reverse or undo any operations already performed. |
| Note that because both the instruction count and amount of memory used by a |
| channel program are deterministic when run against the same inputs and |
| filesystem state, as long as a channel program has run successfully once, you |
| can guarantee that it will finish successfully against a similar size system. |
| .Pp |
| If a channel program attempts to return too large a value, the program will |
| fully execute but exit with a nonzero status code and no return value. |
| .Pp |
| .Em Note : |
| ZFS API functions do not generate Fatal Errors when correctly invoked, they |
| return an error code and the channel program continues executing. |
| See the |
| .Sx ZFS API |
| section below for function-specific details on error return codes. |
| . |
| .Ss Lua to C Value Conversion |
| When invoking a channel program via the libZFS interface, it is necessary to |
| translate arguments and return values from Lua values to their C equivalents, |
| and vice-versa. |
| .Pp |
| There is a correspondence between nvlist values in C and Lua tables. |
| A Lua table which is returned from the channel program will be recursively |
| converted to an nvlist, with table values converted to their natural |
| equivalents: |
| .TS |
| cw3 l c l . |
| string -> string |
| number -> int64 |
| boolean -> boolean_value |
| nil -> boolean (no value) |
| table -> nvlist |
| .TE |
| .Pp |
| Likewise, table keys are replaced by string equivalents as follows: |
| .TS |
| cw3 l c l . |
| string -> no change |
| number -> signed decimal string ("%lld") |
| boolean -> "true" | "false" |
| .TE |
| .Pp |
| Any collision of table key strings (for example, the string "true" and a |
| true boolean value) will cause a fatal error. |
| .Pp |
| Lua numbers are represented internally as signed 64-bit integers. |
| . |
| .Sh LUA STANDARD LIBRARY |
| The following Lua built-in base library functions are available: |
| .TS |
| cw3 l l l l . |
| assert rawlen collectgarbage rawget |
| error rawset getmetatable select |
| ipairs setmetatable next tonumber |
| pairs tostring rawequal type |
| .TE |
| .Pp |
| All functions in the |
| .Em coroutine , |
| .Em string , |
| and |
| .Em table |
| built-in submodules are also available. |
| A complete list and documentation of these modules is available in the Lua |
| manual. |
| .Pp |
| The following functions base library functions have been disabled and are |
| not available for use in channel programs: |
| .TS |
| cw3 l l l l l l . |
| dofile loadfile load pcall print xpcall |
| .TE |
| . |
| .Sh ZFS API |
| . |
| .Ss Function Arguments |
| Each API function takes a fixed set of required positional arguments and |
| optional keyword arguments. |
| For example, the destroy function takes a single positional string argument |
| (the name of the dataset to destroy) and an optional "defer" keyword boolean |
| argument. |
| When using parentheses to specify the arguments to a Lua function, only |
| positional arguments can be used: |
| .Dl Sy zfs.sync.destroy Ns Pq \&"rpool@snap" |
| .Pp |
| To use keyword arguments, functions must be called with a single argument that |
| is a Lua table containing entries mapping integers to positional arguments and |
| strings to keyword arguments: |
| .Dl Sy zfs.sync.destroy Ns Pq {1="rpool@snap", defer=true} |
| .Pp |
| The Lua language allows curly braces to be used in place of parenthesis as |
| syntactic sugar for this calling convention: |
| .Dl Sy zfs.sync.snapshot Ns {"rpool@snap", defer=true} |
| . |
| .Ss Function Return Values |
| If an API function succeeds, it returns 0. |
| If it fails, it returns an error code and the channel program continues |
| executing. |
| API functions do not generate Fatal Errors except in the case of an |
| unrecoverable internal file system error. |
| .Pp |
| In addition to returning an error code, some functions also return extra |
| details describing what caused the error. |
| This extra description is given as a second return value, and will always be a |
| Lua table, or Nil if no error details were returned. |
| Different keys will exist in the error details table depending on the function |
| and error case. |
| Any such function may be called expecting a single return value: |
| .Dl errno = Sy zfs.sync.promote Ns Pq dataset |
| .Pp |
| Or, the error details can be retrieved: |
| .Bd -literal -compact -offset indent |
| .No errno, details = Sy zfs.sync.promote Ns Pq dataset |
| if (errno == EEXIST) then |
| assert(details ~= Nil) |
| list_of_conflicting_snapshots = details |
| end |
| .Ed |
| .Pp |
| The following global aliases for API function error return codes are defined |
| for use in channel programs: |
| .TS |
| cw3 l l l l l l l . |
| EPERM ECHILD ENODEV ENOSPC ENOENT EAGAIN ENOTDIR |
| ESPIPE ESRCH ENOMEM EISDIR EROFS EINTR EACCES |
| EINVAL EMLINK EIO EFAULT ENFILE EPIPE ENXIO |
| ENOTBLK EMFILE EDOM E2BIG EBUSY ENOTTY ERANGE |
| ENOEXEC EEXIST ETXTBSY EDQUOT EBADF EXDEV EFBIG |
| .TE |
| . |
| .Ss API Functions |
| For detailed descriptions of the exact behavior of any ZFS administrative |
| operations, see the main |
| .Xr zfs 8 |
| manual page. |
| .Bl -tag -width "xx" |
| .It Fn zfs.debug msg |
| Record a debug message in the zfs_dbgmsg log. |
| A log of these messages can be printed via mdb's "::zfs_dbgmsg" command, or |
| can be monitored live by running |
| .Dl dtrace -n 'zfs-dbgmsg{trace(stringof(arg0))}' |
| .Pp |
| .Bl -tag -compact -width "property (string)" |
| .It Ar msg Pq string |
| Debug message to be printed. |
| .El |
| .It Fn zfs.exists dataset |
| Returns true if the given dataset exists, or false if it doesn't. |
| A fatal error will be thrown if the dataset is not in the target pool. |
| That is, in a channel program running on rpool, |
| .Sy zfs.exists Ns Pq \&"rpool/nonexistent_fs" |
| returns false, but |
| .Sy zfs.exists Ns Pq \&"somepool/fs_that_may_exist" |
| will error. |
| .Pp |
| .Bl -tag -compact -width "property (string)" |
| .It Ar dataset Pq string |
| Dataset to check for existence. |
| Must be in the target pool. |
| .El |
| .It Fn zfs.get_prop dataset property |
| Returns two values. |
| First, a string, number or table containing the property value for the given |
| dataset. |
| Second, a string containing the source of the property (i.e. the name of the |
| dataset in which it was set or nil if it is readonly). |
| Throws a Lua error if the dataset is invalid or the property doesn't exist. |
| Note that Lua only supports int64 number types whereas ZFS number properties |
| are uint64. |
| This means very large values (like GUIDs) may wrap around and appear negative. |
| .Pp |
| .Bl -tag -compact -width "property (string)" |
| .It Ar dataset Pq string |
| Filesystem or snapshot path to retrieve properties from. |
| .It Ar property Pq string |
| Name of property to retrieve. |
| All filesystem, snapshot and volume properties are supported except for |
| .Sy mounted |
| and |
| .Sy iscsioptions . |
| Also supports the |
| .Sy written@ Ns Ar snap |
| and |
| .Sy written# Ns Ar bookmark |
| properties and the |
| .Ao Sy user Ns | Ns Sy group Ac Ns Ao Sy quota Ns | Ns Sy used Ac Ns Sy @ Ns Ar id |
| properties, though the id must be in numeric form. |
| .El |
| .El |
| .Bl -tag -width "xx" |
| .It Sy zfs.sync submodule |
| The sync submodule contains functions that modify the on-disk state. |
| They are executed in "syncing context". |
| .Pp |
| The available sync submodule functions are as follows: |
| .Bl -tag -width "xx" |
| .It Sy zfs.sync.destroy Ns Pq Ar dataset , Op Ar defer Ns = Ns Sy true Ns | Ns Sy false |
| Destroy the given dataset. |
| Returns 0 on successful destroy, or a nonzero error code if the dataset could |
| not be destroyed (for example, if the dataset has any active children or |
| clones). |
| .Pp |
| .Bl -tag -compact -width "newbookmark (string)" |
| .It Ar dataset Pq string |
| Filesystem or snapshot to be destroyed. |
| .It Op Ar defer Pq boolean |
| Valid only for destroying snapshots. |
| If set to true, and the snapshot has holds or clones, allows the snapshot to be |
| marked for deferred deletion rather than failing. |
| .El |
| .It Fn zfs.sync.inherit dataset property |
| Clears the specified property in the given dataset, causing it to be inherited |
| from an ancestor, or restored to the default if no ancestor property is set. |
| The |
| .Nm zfs Cm inherit Fl S |
| option has not been implemented. |
| Returns 0 on success, or a nonzero error code if the property could not be |
| cleared. |
| .Pp |
| .Bl -tag -compact -width "newbookmark (string)" |
| .It Ar dataset Pq string |
| Filesystem or snapshot containing the property to clear. |
| .It Ar property Pq string |
| The property to clear. |
| Allowed properties are the same as those for the |
| .Nm zfs Cm inherit |
| command. |
| .El |
| .It Fn zfs.sync.promote dataset |
| Promote the given clone to a filesystem. |
| Returns 0 on successful promotion, or a nonzero error code otherwise. |
| If EEXIST is returned, the second return value will be an array of the clone's |
| snapshots whose names collide with snapshots of the parent filesystem. |
| .Pp |
| .Bl -tag -compact -width "newbookmark (string)" |
| .It Ar dataset Pq string |
| Clone to be promoted. |
| .El |
| .It Fn zfs.sync.rollback filesystem |
| Rollback to the previous snapshot for a dataset. |
| Returns 0 on successful rollback, or a nonzero error code otherwise. |
| Rollbacks can be performed on filesystems or zvols, but not on snapshots |
| or mounted datasets. |
| EBUSY is returned in the case where the filesystem is mounted. |
| .Pp |
| .Bl -tag -compact -width "newbookmark (string)" |
| .It Ar filesystem Pq string |
| Filesystem to rollback. |
| .El |
| .It Fn zfs.sync.set_prop dataset property value |
| Sets the given property on a dataset. |
| Currently only user properties are supported. |
| Returns 0 if the property was set, or a nonzero error code otherwise. |
| .Pp |
| .Bl -tag -compact -width "newbookmark (string)" |
| .It Ar dataset Pq string |
| The dataset where the property will be set. |
| .It Ar property Pq string |
| The property to set. |
| .It Ar value Pq string |
| The value of the property to be set. |
| .El |
| .It Fn zfs.sync.snapshot dataset |
| Create a snapshot of a filesystem. |
| Returns 0 if the snapshot was successfully created, |
| and a nonzero error code otherwise. |
| .Pp |
| Note: Taking a snapshot will fail on any pool older than legacy version 27. |
| To enable taking snapshots from ZCP scripts, the pool must be upgraded. |
| .Pp |
| .Bl -tag -compact -width "newbookmark (string)" |
| .It Ar dataset Pq string |
| Name of snapshot to create. |
| .El |
| .It Fn zfs.sync.bookmark source newbookmark |
| Create a bookmark of an existing source snapshot or bookmark. |
| Returns 0 if the new bookmark was successfully created, |
| and a nonzero error code otherwise. |
| .Pp |
| Note: Bookmarking requires the corresponding pool feature to be enabled. |
| .Pp |
| .Bl -tag -compact -width "newbookmark (string)" |
| .It Ar source Pq string |
| Full name of the existing snapshot or bookmark. |
| .It Ar newbookmark Pq string |
| Full name of the new bookmark. |
| .El |
| .El |
| .It Sy zfs.check submodule |
| For each function in the |
| .Sy zfs.sync |
| submodule, there is a corresponding |
| .Sy zfs.check |
| function which performs a "dry run" of the same operation. |
| Each takes the same arguments as its |
| .Sy zfs.sync |
| counterpart and returns 0 if the operation would succeed, |
| or a non-zero error code if it would fail, along with any other error details. |
| That is, each has the same behavior as the corresponding sync function except |
| for actually executing the requested change. |
| For example, |
| .Fn zfs.check.destroy \&"fs" |
| returns 0 if |
| .Fn zfs.sync.destroy \&"fs" |
| would successfully destroy the dataset. |
| .Pp |
| The available |
| .Sy zfs.check |
| functions are: |
| .Bl -tag -compact -width "xx" |
| .It Sy zfs.check.destroy Ns Pq Ar dataset , Op Ar defer Ns = Ns Sy true Ns | Ns Sy false |
| .It Fn zfs.check.promote dataset |
| .It Fn zfs.check.rollback filesystem |
| .It Fn zfs.check.set_property dataset property value |
| .It Fn zfs.check.snapshot dataset |
| .El |
| .It Sy zfs.list submodule |
| The zfs.list submodule provides functions for iterating over datasets and |
| properties. |
| Rather than returning tables, these functions act as Lua iterators, and are |
| generally used as follows: |
| .Bd -literal -compact -offset indent |
| .No for child in Fn zfs.list.children \&"rpool" No do |
| ... |
| end |
| .Ed |
| .Pp |
| The available |
| .Sy zfs.list |
| functions are: |
| .Bl -tag -width "xx" |
| .It Fn zfs.list.clones snapshot |
| Iterate through all clones of the given snapshot. |
| .Pp |
| .Bl -tag -compact -width "snapshot (string)" |
| .It Ar snapshot Pq string |
| Must be a valid snapshot path in the current pool. |
| .El |
| .It Fn zfs.list.snapshots dataset |
| Iterate through all snapshots of the given dataset. |
| Each snapshot is returned as a string containing the full dataset name, |
| e.g. "pool/fs@snap". |
| .Pp |
| .Bl -tag -compact -width "snapshot (string)" |
| .It Ar dataset Pq string |
| Must be a valid filesystem or volume. |
| .El |
| .It Fn zfs.list.children dataset |
| Iterate through all direct children of the given dataset. |
| Each child is returned as a string containing the full dataset name, |
| e.g. "pool/fs/child". |
| .Pp |
| .Bl -tag -compact -width "snapshot (string)" |
| .It Ar dataset Pq string |
| Must be a valid filesystem or volume. |
| .El |
| .It Fn zfs.list.bookmarks dataset |
| Iterate through all bookmarks of the given dataset. |
| Each bookmark is returned as a string containing the full dataset name, |
| e.g. "pool/fs#bookmark". |
| .Pp |
| .Bl -tag -compact -width "snapshot (string)" |
| .It Ar dataset Pq string |
| Must be a valid filesystem or volume. |
| .El |
| .It Fn zfs.list.holds snapshot |
| Iterate through all user holds on the given snapshot. |
| Each hold is returned |
| as a pair of the hold's tag and the timestamp (in seconds since the epoch) at |
| which it was created. |
| .Pp |
| .Bl -tag -compact -width "snapshot (string)" |
| .It Ar snapshot Pq string |
| Must be a valid snapshot. |
| .El |
| .It Fn zfs.list.properties dataset |
| An alias for zfs.list.user_properties (see relevant entry). |
| .Pp |
| .Bl -tag -compact -width "snapshot (string)" |
| .It Ar dataset Pq string |
| Must be a valid filesystem, snapshot, or volume. |
| .El |
| .It Fn zfs.list.user_properties dataset |
| Iterate through all user properties for the given dataset. |
| For each step of the iteration, output the property name, its value, |
| and its source. |
| Throws a Lua error if the dataset is invalid. |
| .Pp |
| .Bl -tag -compact -width "snapshot (string)" |
| .It Ar dataset Pq string |
| Must be a valid filesystem, snapshot, or volume. |
| .El |
| .It Fn zfs.list.system_properties dataset |
| Returns an array of strings, the names of the valid system (non-user defined) |
| properties for the given dataset. |
| Throws a Lua error if the dataset is invalid. |
| .Pp |
| .Bl -tag -compact -width "snapshot (string)" |
| .It Ar dataset Pq string |
| Must be a valid filesystem, snapshot or volume. |
| .El |
| .El |
| .El |
| . |
| .Sh EXAMPLES |
| . |
| .Ss Example 1 |
| The following channel program recursively destroys a filesystem and all its |
| snapshots and children in a naive manner. |
| Note that this does not involve any error handling or reporting. |
| .Bd -literal -offset indent |
| function destroy_recursive(root) |
| for child in zfs.list.children(root) do |
| destroy_recursive(child) |
| end |
| for snap in zfs.list.snapshots(root) do |
| zfs.sync.destroy(snap) |
| end |
| zfs.sync.destroy(root) |
| end |
| destroy_recursive("pool/somefs") |
| .Ed |
| . |
| .Ss Example 2 |
| A more verbose and robust version of the same channel program, which |
| properly detects and reports errors, and also takes the dataset to destroy |
| as a command line argument, would be as follows: |
| .Bd -literal -offset indent |
| succeeded = {} |
| failed = {} |
| |
| function destroy_recursive(root) |
| for child in zfs.list.children(root) do |
| destroy_recursive(child) |
| end |
| for snap in zfs.list.snapshots(root) do |
| err = zfs.sync.destroy(snap) |
| if (err ~= 0) then |
| failed[snap] = err |
| else |
| succeeded[snap] = err |
| end |
| end |
| err = zfs.sync.destroy(root) |
| if (err ~= 0) then |
| failed[root] = err |
| else |
| succeeded[root] = err |
| end |
| end |
| |
| args = ... |
| argv = args["argv"] |
| |
| destroy_recursive(argv[1]) |
| |
| results = {} |
| results["succeeded"] = succeeded |
| results["failed"] = failed |
| return results |
| .Ed |
| . |
| .Ss Example 3 |
| The following function performs a forced promote operation by attempting to |
| promote the given clone and destroying any conflicting snapshots. |
| .Bd -literal -offset indent |
| function force_promote(ds) |
| errno, details = zfs.check.promote(ds) |
| if (errno == EEXIST) then |
| assert(details ~= Nil) |
| for i, snap in ipairs(details) do |
| zfs.sync.destroy(ds .. "@" .. snap) |
| end |
| elseif (errno ~= 0) then |
| return errno |
| end |
| return zfs.sync.promote(ds) |
| end |
| .Ed |