5 R7 is a configuration management framework made to configure nodes with
6 configuration files and related operations versioned in a central repository.
7 To remain code versioning system agnostic, manual or automated versioning is
8 left to the user, via makefiles or triggered from r7 `site-config` configuration
11 The idea is to store hosts configurations in `nodes/hostname` directories that
12 maps each host's `/etc` and to assign those hosts to configuration groups
13 containing the directives to install the configuration and potential
16 _Nodes sub-directories are host names understood by the ssh client and
19 _Currently, r7 requires valid ssh configuration and key files in the repository
22 _Repositories can be initialized with `rset init` or `rset -w
23 path/to/new-repository init`, the user will then have to provide valid
24 configurations and keys for `.ssh/` in the newly created directory._
26 _See [Technical Notes](#technical-notes) for dependencies and supported
29 There is an example of r7 repository:
43 ./.ssh/id_ed25519_r7.pub
46 There is an example of r7 site configuration (`site-config` file):
49 prefix install state doc
51 group test0 hostB hostC
56 There is an example of the deployment of that configuration within the r7
61 - (ok) hostA:common::install_nothing
62 - (ok) hostA:common::state_uname
63 - (ok) hostA:common::doc_tutorial
64 - (ok) hostB:common::install_nothing
65 - (ok) hostB:common::state_uname
66 - (ok) hostB:common::doc_tutorial
67 - (ok) hostC:common::install_nothing
68 - (ok) hostC:common::state_uname
69 - (ok) hostC:common::doc_tutorial
70 - (ok) hostB:test0::state_test
71 - (ok) hostC:test0::state_test
74 In this example, everything goes fine, groups are executed on each related
75 nodes, note the `'.*'` matching all hosts present in `./nodes`. Groups are POSIX
76 shell scripts that are a collection of functions.
78 There is the `common` group:
82 echo Nothing installed
90 echo "## Welcome to the R7 tutorial!"
94 And there is the `test0` group:
102 Any function managed by `r7` needs to be declared in the format `^PREFIX_NAME()`.
104 Group files names can't contain spaces, tabs or newlines, as well for nodes
105 `dirnames`. _The ':' character is not recommended as well as it may reduce
106 readability of the interactive output._
108 Called groups and node directories are copied to related nodes in `hostname:/tmp/r7/`:
110 /tmp/r7/hostA/ # Contents of test.org/nodes/hostA/
111 /tmp/r7/common.dir # Contents of test.org/groups/common.dir/
112 /tmp/r7/test.dir # Contents of test.org/groups/test.dir
114 Groups may have no `.dir` directory, in this case, nothing is copied.
116 Once the group copy is done, functions are executed in the order of the defined
117 prefix (part of the `site-config` file shown before):
119 prefix install state doc
121 This means the functions matching `^install.*()`, `^state.*()` and `^doc.*()`
122 (the default `R7_PREFIX`) in the respective declaration order they're found in the
123 related group file will be executed.
125 Site configurations are shell scripts sourced by r7 after it's environment is
126 initialized, so `prefix`, `group`, `output-dump`, `summary` are the functions or
127 aliases provided by r7 itself. This allow for scripting from the `site-config`
128 file. Here `output-dump`, and `summary` are called to output potential errors,
129 diffs and permissions changes at the end of the interactive output obtained by
130 `r7 run-config`. If nothing happened, nothing is added to the output.
132 ### Errors during remote execution of groups
134 In our previous example, we modify `state_test` so the `test` fails.
142 And we re-run the configuration to see what's happening.
146 - (ok) hostA:common::install_nothing
147 - (ok) hostA:common::state_uname
148 - (ok) hostA:common::doc_tutorial
149 - (ok) hostB:common::install_nothing
150 - (ok) hostB:common::state_uname
151 - (ok) hostB:common::doc_tutorial
152 - (ok) hostC:common::install_nothing
153 - (ok) hostC:common::state_uname
154 - (ok) hostC:common::doc_tutorial
155 - (error 1) hostB:test0::state_test
156 - (error 1) hostC:test0::state_test
157 - (error 1) - in hostB test0 state_test (1748622980)
158 - (error 1) - in hostC test0 state_test (1748622980)
162 The last two lines are part of the `summary` output.
164 Let's modify the `state_test` function again to add some verbosity to it's
167 _Note that we also change the return value._
179 This will produce the following summary:
182 - (error 22) - in hostB test0 state_test (1748622980)
187 - (error 22) - in hostC test0 state_test (1748622980)
193 Diffs and permissions changes are also displayed by the `summary` command. When
194 they're are detected, the captured related output is shown.
196 ### Repository initialisation
198 Using the `init` command, we can specify the current or a specified
199 work directory to be created and act as an r7 repository.
201 _Automated versioning of outputs are currently let to the user, a recommended
202 place would be the end of the site configuration file._
204 This command initializes a new directory called `test.org`:
211 Once this step is done, it is required to:
213 - Copy or initialize the ssh key to the `.ssh` folder in the repository created
214 in `.ssh/id_ed25519-r7` and `./ssh/id_ed25519-r7.pub`
216 - Adjust the SSH client configuration file in the repository `.ssh/config`
218 _Note that it is possible to import additional SSH configurations, like the user
219 one from the SSH configuration, but it is advised to keep them separate._
221 ### SSH agent handling
223 The function `unlock` can be used to set the SSH agent environment, this will
224 be required for any automated operation later done by r7:
228 . $(command -v r7) # Sourcing r7
234 #### Tmux configuration
237 set -g update-environment -r
240 #### Optional Shell configuration
242 This is useful for handling multiple tmux sessions.
245 # Add this to the shell's rc
246 if [ -z "$TMUX" ]; then
247 if [ -n "$SSH_TTY" ]; then
248 if [ -z "$SSH_AUTH_SOCK" ]; then
249 export SSH_AUTH_SOCK="$HOME/.ssh/.auth_socket"
251 if [ ! -S "$SSH_AUTH_SOCK" ]; then
252 eval $(ssh-agent -a $SSH_AUTH_SOCK) >/dev/null 2>&1
253 echo $SSH_AGENT_PID >$HOME/.ssh/.auth_pid
255 if [ -z $SSH_AGENT_PID ]; then
256 export SSH_AGENT_PID=$(cat $HOME/.ssh/.auth_pid)
258 ssh-add "$SSH_ID" 2>/dev/null
266 For adding a new host, the simplest way is to have pre-configured
267 `authorized_keys` for the target user, it is then required to add it to the
268 `known_hosts` file. For that, it is possible to use r7 SSH functions:
271 . $(command -v r7) # Sourcing r7
272 ssh-accept-new hostname
276 If resetting the target's user `authorized_keys` file is needed, the following
277 function can be used:
280 . $(command -v r7) # Sourcing r7
281 ssh-authorized-keys-reset hostname
284 _**Warning:** This will resets the SSH authorized_keys file for the target user
285 (root, by default) and put the `.ssh/id_ed25519-r7.pub` in place._
288 Once the host is accepted in the `known_hosts` file, it's possible to import it
291 ### Importing hosts configurations
293 Avoiding to import the configuration manually is possible with the `import`
300 Most common configuration files will be copied in a newly created
301 `nodes/hostname` sub-directory.
303 ### Writing group files
305 Group files collects functions related to the configuration of the operating
306 system, networking, services, global and user configurations packages
307 installations and application deployments.
309 By specifying the execution prefix, the user determines the order of execution
310 of the functions declared in the group file.
330 echo "- [url](http://test.org/)"
335 _**file**: `groups/example1`_
337 With the default prefix, `install`, `state`, `doc`, the function that will be
338 executed are `state_A` and `doc_A`. To test that group file with all the
339 functions inside we can call r7 the following way:
342 r7 -P 'service state doc' group example1 hostname
345 Or using the `-a` flag which will create a prefix automatically with all
346 functions present in the group file:
349 r7 -a group example1 hostname
352 In a real situation, this example will have failing functions because the
353 `service` command may not be found or the services named `A` or `B` does not
357 ! (error 127) tools0:example1::service_A
358 ! (error 127) tools0:example1::service_B
359 ! (error 127) tools0:example1::state_A
360 - (ok) tools0:example1::doc_A
363 We could force the group execution to stop using a combination of the `-e` flag
364 and the `error` group function.
370 error unable to setup service A
376 error unable to setup service B
386 echo "- [url](http://test.org/)"
391 _**file:** `groups/example2`_
393 Let's run the updated example with the `-s` flag to trigger the summary output.
397 r7 -sea group example2 hostname
400 This will produce something like the following:
403 ! (error) tools0:example2::service_A
404 ! (exit) tools0:example2::service_A
405 - (error 127) - in tools0 example2 service_A (1748791986)
407 /bin/sh: <stdin>[155]: service: not found
408 :: 1748791986 tools0 ERROR unable to setup service A
412 ### Cleaning functions
414 To remove a function from a group, we need to ensure that this function returned
415 0 for the last execution.
418 state_function_to_remove() {
425 This would prevent for seeing potential past errors caused by this function in
428 ### Using the r7 install functions
430 The group library comes with the following functions that will allow
431 installation of files and triggering actions if necessary: `install`,
432 `groupinstall`, `nodeinstall`. They are all shortcuts to an internal
433 `r7_install` function, it's just the wrappers changes directory to respectively
434 `/tmp/r7`, `/tmp/r7/groupname.dir/` and `/tmp/r7/hostname/`.
436 If we want to install files from the group directory we will use something like:
441 groupinstall -m 755 -o root:bin my_program /usr/local/bin
445 _We are supposing to have the file `groups/example3.dir/my_program`_
447 And if it's something from node, we should use:
450 install_program_config() {
451 nodeinstall -m 644 -o root:wheel my_config /etc
455 We can now chain the `install` functions to configuration checking and
456 initialization of programs, services or network features like in the following
457 example, in which we install `ntp.conf` from the deployed node directory to
462 nodeinstall -m 644 -o root:wheel ntpd.conf /etc &&
469 ### Using the r7 node function
471 We may want to avoid executing a function or parts of it if the node has no
472 related files before executing the installation and actions handling parts. This
473 can be done with the help of the `node` function.
478 nodeinstall -m 644 -o root:wheel ntpd.conf /etc &&
485 Here, `node` will return `31` if there is no file or directory called
486 `ntpd.conf` inside the deployed node directory (`/tmp/r7/hostname`).
488 `0`, `30` and `31` exit codes are ignored when r7 will search for execution
489 errors. This means `30` and `31` exit code are reserved to r7.
491 In other terms, this will update the NTP daemon configuration only if
492 `ntpd.conf` is present in the node directory, otherwise it is kept by default,
493 which is the system provided default `ntpd.conf`.
495 ### Sourcing specific files
497 Here's an example how to source additional shell from the deployed node
498 directory, or elsewhere:
503 sysrc kld_list="vmm if_tuntap if_bridge nmdm"
504 sysrc microcode_update_enable=YES
505 sysrc mountd_enable=YES
506 sysrc nfs_server_enable=YES
507 sysrc ntpdate_enable=YES
508 sysrc powerd_enable=YES
509 sysrc rcshutdown_timeout=900
510 sysrc rpcbind_enable=YES
511 sysrc sshd_enable=YES
513 nodesource sysrc.local
518 In this FreeBSD example, we configure the system RC globally with
519 `service_sysrc` which apply, if `sysrc.local` is found in the root of the node
520 directory, it's sourcing in the current scope via the help of `nodesource
523 The file `nodes/hostname/sysrcl.local` contains the following:
526 sysrc ifconfig_igb0=DHCP
527 sysrc ifconfig_igb1=up
532 Inside the r7 release, a small example groups library can be found in the
533 `groups` directory, that can also be browsed in the
534 [repository](https://got.inda.re/?action=summary&path=r7.git).
536 #### System shell profile
538 Here we install the `profile` file provided in the `groupname.dir` directory or
539 the profile provided by the node directory:
543 groupinstall -m 644 -o root:wheel profile /etc
545 nodeinstall -m 644 -o root:wheel profile /etc
551 Here we add new users:
555 useradd user1 2>/dev/null || :
556 useradd user2 2>/dev/null || :
562 In the following example, we install a script and call it from a dedicated user crontab.
565 mail_admins=admins@test.org
568 groupinstall -o root:bin -m 755 myscript /usr/local/bin &&
569 useradd -d /var/empty -s /sbin/nologin _myscript
570 crontab -u _myscript - <<-/
577 ### Consulting the output
579 After the execution is done, the result are saved in the `_run` directory,
580 `output-dump` needs to be called to produce the current `_output` structure.
582 The r7 `output` command can then be used to show specific output groups or functions:
588 Will show the whole output of the latest groups deployments.
590 This format can be used to match specific host, group or function:
593 r7 output [hostname [groupname [funcname]]]
599 $ r7 output tools0 OpenBSD state_packages\*
600 - tools0 OpenBSD state_packages_upgrades
602 quirks-7.103 signed on 2025-05-30T01:12:03Z
604 - tools0 OpenBSD state_packages_repository
606 https://cdn.openbsd.org/pub/OpenBSD
609 _Note that the `output` function arguments are using shell's case glob patterns._
613 A `R7_WORKDIR/.active_nodes` file is automatically created when r7 tries to
614 connect to a list of hosts via the `group` command or function. This serves as a
615 cache of available hosts reachable via SSH login (the username is specified in
616 the SSH configuration file).
618 To add a previously non-available host, simply remove the file and use `group`
619 (here via `run-config`):
630 r7 [options] [command [arguments...]]
642 Specify the SSH identity file
645 Specify the SSH client configuration file
648 Set the work directory where `./nodes` and `./groups` can be found
651 Set the run directory (default: `./_run`)
654 Set the output directory (default: `./_output`)
657 Specify the site configuration file (default: `./site-config`)
660 Enable dry run mode; prevents any modification on hosts
663 Exit the group execution on the error function
666 Run each host in parallel per group call
669 Show output interactively
672 Specify a prefix, can contain whole function names
675 Select all available prefixes for a group command
678 Apply output-dump and summary after a group command
682 | Name | Description |
683 | ---- | ----------- |
684 | **run-config** | Run the site configuration |
685 | **group** | Deploy specified group to hosts |
686 | **output-dump** | Dump the run directory to the output directory |
687 | **output** | Consult the output |
688 | **summary** | Produce a summary of errors and changes |
689 | **digraph-host** | Produce a dot digraph for hosts |
690 | **digraph-ssh** | Produce a dot digraph of SSH ID's, users and nodes relations |
691 | **import** | Import TARGET host in the current nodes directory |
692 | **info** | Default command showing active nodes and summary |
693 | **init** | Initialize a new work directory |
697 | Name | Default value | Description |
698 | ---- | ------------- | ------------|
699 | R7_DEBUG | no | Enable debug mode |
700 | R7_DRYRUN | no | Enable dry-run mode |
701 | R7_GROUP_ALLPREFIX | no | Group selects all available prefix |
702 | R7_GROUP_SUMMARY | no | Enable output-dump and summary after group execution |
703 | R7_GROUP_EXITONERROR | no | Enable exit on error function |
704 | R7_PARALLEL | no | Enable group nodes parallel execution |
705 | R7_PREFIX | install state doc | Set the r7 group execution prefix |
706 | R7_SHOWOUTPUT | no | Show interactive output |
707 | R7_WORKDIR | PWD | Set the r7 repository path |
708 | R7_SITE_CONFIG | R7_WORKDIR/site-config | Set the r7 `site-config` path |
709 | R7_OUTPUTDIR | R7_WORKDIR/_output | Set the r7 output directory path |
710 | SSH_CONFIG_DIR | R7_WORKDIR/.ssh | Set the SSH configuration directory path |
711 | SSH_CONFIG_FILE | R7_WORKDIR/.ssh/config | Set the SSH configuration file path |
712 | SSH_CONTROL_DIR | SSH_CONFIG_DIR/control | Set the SSH control dir path |
713 | SSH_IDENTITY_FILE | R7_WORKDIR/.ssh/id_ed25519_r7 | Set the SSH identity file |
714 | SSH_KNOWN_HOSTS | R7_WORKDIR/.ssh/known_hosts | Set the SSH known hosts file |
715 | SSH_CONNECT_TIMEOUT | 3 | Set the SSH connect timeout |
720 Those variables are available during the execution of th group file's functions.
722 | Name | Description |
723 | ---- | ----------- |
724 | trace_id | Current run trace ID |
725 | groupname | Current group name being executed |
726 | exitonerror | Cause the error function to exit if equals `yes` |
727 | nodename | Contains the output of `nodename` |
728 | nodedir | Contains the output of `nodedir` |
729 | groupdir | Contains the output of `groupdir` |
734 Those functions are accessible during the execution of the group file's functions.
735 Non documented functions are reserved for internal use.
740 4. [nodename](#nodename)
741 5. [nodedir](#nodedir)
742 6. [groupdir](#groupdir)
743 7. [install](#install)
744 8. [groupinstall](#groupinstall)
745 9. [nodeinstall](#nodeinstall)
746 10. [installdir](#installdir)
747 11. [groupinstalldir](#groupinstalldir)
748 12. [nodeinstalldir](#nodeinstalldir)
749 13. [source](#source)
750 14. [groupsource](#groupsource)
751 15. [nodesource](#groupsource)
752 16. [template](#template)
753 17. [grouptemplate](#grouptemplate)
754 18. [nodetemplate](#nodetemplate)
761 Append a trace containing the current node name and `trace_id`
771 Trace an error message that will inform the user of an event in the summary
772 output, exit the group execution if `-e` is set
782 Returns true if the argument is an existing file or directory inside `/tmp/r7/$(nodename)`, else, returns 31
786 node [filename|dirname]
792 Returns the small host name of the current host
802 Returns `/tmp/r7/$(nodename)`
812 Returns `/tmp/r7/$groupname.dir`
822 Change directory to `/tmp/r7` and install files if changed and sets mode and
823 owner, returns true if the file changed, otherwise returns 30
827 install [-o owner] [-m mode] source destintaion
833 Change directory to the current group `.dir` directory in `/tmp/r7` then
834 performs like `install`
838 groupinstall [-o owner] [-m mode] source destintaion
844 Change directory to the current node directory in `/tmp/r7` then
845 performs like `install`
849 nodeinstall [-o owner] [-m mode] source destintaion
855 Install directory calling `install` for each file and checking for permissions
860 installdir [-o fileowner] [-m filemode] [-O dirowner] [-O dirmode] source
867 Change directory to the current group directory then call installdir
871 groupinstalldir [-o fileowner] [-m filemode] [-O dirowner] [-O dirmode] source
878 Change directory to the current group directory then call installdir
882 nodeinstalldir [-o fileowner] [-m filemode] [-O dirowner] [-O dirmode] source
889 Source a POSIX shell file in the current execution
900 Change directory to the current group directory then call fsource with the file
901 name as argument, produces an error if filename is not found from the group
912 Change directory to the current group directory then call node and fsource with the file
913 name as argument without producing an error if the filename is not found
923 Template the files passed as arguments with the current environment or by
924 sourcing a specified file
928 template [-s sourcefile] [file...]
934 Change directory to the current group directory before calling template
938 grouptemplate [-s sourcefile] [file...]
944 Change directory to the current node directory before calling template
948 nodetemplate [-s sourcefile] [file...]
953 Usable from the r7 `site-config` file or sourced in the current shell using:
959 Each functions are declared with underscore names and aliased with dashes.
960 Shorter aliases are also available, list all declared aliases with:
966 Non documented functions are reserved for internal use.
978 Default command showing active nodes and summary, ran when sourced
988 Shows the command line help
1000 4. [ssh-debug](#ssh-debug)
1001 5. [ssh-known-hosts](#ssh-known-hosts)
1002 6. [ssh-authorized-keys-reset](#ssh-authorized-keys-reset)
1003 7. [ssh-accept-new](#ssh-accept-new)
1004 8. [ssh-control-master-clean](#ssh-control-master-clean)
1005 9. [ssh-users](#ssh-users)
1006 10. [ssh-ids](#ssh-ids)
1007 11. [ssh-authorized-keys](#ssh-authorized-keys)
1008 12. [ssh-ids-hosts](#ssh-ids-hosts)
1015 Resets the SSH agent environment and issues ssh-add for SSH_IDENTITY_FILE
1025 r7 openrsync, rsync, scp wrapper; selected in order of availability
1035 r7 SSH wrapper; behaves like the standard OpenSSH command
1045 r7 SSH with debug connection mode for a specific HOST
1055 Calls ssh-keygen to dump the SSH known_hosts file
1062 ### ssh-authorized-keys-reset
1065 Resets SSH authorized_keys on the specified HOST
1069 ssh-authorized-keys-reset HOST
1075 Adds a new host entry in the SSH known_hosts file
1082 ### ssh-control-master-clean
1085 Cleans the SSH control master directory
1089 ssh-control-master-clean
1095 Searches nodes for authorized_keys_* and returns a list of users
1105 Returns sorted SSH IDs
1112 ### ssh-authorized-keys
1115 Returns the list of authorized_keys from nodes
1125 Returns SSH IDs along with matching key file and host name
1132 ## Deployment functions
1135 2. [nodes-active](#nodes-active)
1136 3. [run-config](#run-config)
1137 4. [members](#members)
1139 6. [group-functions](#group-functions)
1140 7. [group-functions-all](#group-functions-all)
1141 8. [prefix](#prefix)
1148 Returns a list of available nodes
1158 Returns a list of active nodes by detecting SSH availability (a login is done), writes
1159 `R7_WORKDIR/.active_nodes` containing the available hosts
1169 Executes `R7_SITE_CONFIG` in the loaded environment
1179 Returns a list of active nodes matching a list of grep REGEXES
1189 Applies GROUPNAME functions (in prefix order) to the provided MEMBERS list
1193 group GROUPNAME MEMBERS
1199 Returns a list of functions found in the specified GROUPNAME
1203 group-functions GROUPNAME
1206 ### group-functions-all
1209 Returns a list of all group functions found in the current `R7_WORKDIR/groups`
1210 directory with the `group_name::function_name` format
1222 Sets the current PREFIX for matching function names, can match for the whole
1223 unique name in the group, for executing one specified function only
1230 ## Text output functions
1232 1. [output-dump](#output-dump)
1233 2. [output-latest](#output-latest)
1234 3. [output-status](#output-status)
1235 4. [output-duration](#output-duration)
1236 5. [output](#output)
1237 6. [output-diffs](#output-diffs)
1238 7. [output-permissions](#output-permissions)
1239 8. [output-errors](#output-errors)
1240 9. [summary](#summary)
1241 10. [trace-id-last](#trace-id-last)
1248 Dumps R7_RUNDIR to R7_OUTPUTDIR
1258 Returns a list of outputs from the latest run
1268 Returns a list of current dumped functions exit status codes
1278 Returns a list of durations of executed groups
1288 Shows the dumped output matching HOST, GROUPNAME, and FUNCTION (or any if unspecified)
1292 output HOST GROUPNAME FUNCTION
1298 Returns the latest execution trace_id
1308 Returns the diffs from the latest execution
1315 ### output-permissions
1318 Returns the latest permission changes
1328 Returns the latest errors detected
1338 Produces a summary of errors, diffs, and permission outputs
1347 1. [digraph-host](#digraph-host)
1348 2. [digraph-ssh-id-hosts](#digraph-ssh-id-hosts)
1355 Returns a directed graph (in DOT format) showing relations among groups, functions, and nodes
1362 ### digraph-ssh-id-hosts
1365 Returns a directed graph (in DOT format) of relations between SSH IDs and hosts
1369 digraph-ssh-id-hosts
1379 Retunrs a report page with the content of `README.md` in HTML format
1390 1. [import](#import)
1395 Import configuration files from a target host
1406 - Supported shells are ksh93, oksh, yash and bash
1407 - Supported target platforms are POSIX compatible systems
1408 - `/bin/sh` or compatible must be set for the user's shell (which is
1409 usually the case by default)
1410 - `/tmp` must be writable by the user
1411 - Supported control node platforms are supposed to be POSIX compatible systems
1412 but currently limited to BSD's, Darwin and GNU/Linux
1413 - Dependencies are awk, OpenSSH and column
1414 - Optional dependencies are openrsync, rsync, column, graphviz and pandoc
1415 - The r7 importer supports the following platforms:
1439 The provided group library is shared as additional examples WITH NO WARRANTIES of the
1440 documentation and contain the following third-party files under the ISC License.
1441 Copyright information are indicated in the LICENSE section of related files.
1444 - `unbound-adblock.sh`
1447 The project is shared under the terms of the ISC license. Any version previous
1448 `0.1.0` needs to be considered obsolete and should not be redistributed. History
1449 in the GIT repository is kept for transparency purpose only.
1451 Below is a copy of the applicable license:
1455 Copyright 2025 xs <xs@inda.re>
1457 Permission to use, copy, modify, and/or distribute this software for any
1458 purpose with or without fee is hereby granted, provided that the above
1459 copyright notice and this permission notice appear in all copies.
1461 THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
1462 REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
1463 FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
1464 INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
1465 LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
1466 OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
1467 PERFORMANCE OF THIS SOFTWARE.