Architecture specific change in rpms/teem.git
by githook-noreply@fedoraproject.org
The package rpms/teem.git has added or updated architecture specific content in its
spec file (ExclusiveArch/ExcludeArch or %ifarch/%ifnarch) in commit(s):
https://src.fedoraproject.org/cgit/rpms/teem.git/commit/?id=593f4aa56a75b....
Change:
+%ifarch %{arm64} ppc64le s390x
Thanks.
Full change:
============
commit 78c8bfa63ee9200e45b439952cccee2508142cdc
Author: Benjamin A. Beasley <code(a)musicinmybrain.net>
Date: Sat Aug 21 11:07:13 2021 -0400
Remove EPEL workarounds from Fedora spec file
diff --git a/teem.spec b/teem.spec
index a7f8e03..1913581 100644
--- a/teem.spec
+++ b/teem.spec
@@ -141,7 +141,7 @@ Source961: unu-save.1
Patch0: lib_install_dir.patch
BuildRequires: gcc-c++
-BuildRequires: cmake3
+BuildRequires: cmake
BuildRequires: make
BuildRequires: zlib-devel
@@ -247,16 +247,16 @@ rm -rvf python
%build
-%cmake3 \
+%cmake \
-DCMAKE_SKIP_INSTALL_RPATH=ON \
-DTeem_USE_LIB_INSTALL_SUBDIR=ON \
-DTeem_FFTW3=ON \
-DTeem_LEVMAR=ON
-%cmake3_build
+%cmake_build
%install
-%cmake3_install
+%cmake_install
%if %{with man_pages}
install -d %{buildroot}%{_mandir}/man1
@@ -296,12 +296,7 @@ install -t %{buildroot}%{_mandir}/man1 -p -m 0644 \
# Tests are not parallel-safe
%global _smp_mflags -j1
-%ctest3 %{?ctest_excludes}
-
-
-%if 0%{?epel} == 7
-%ldconfig_scriptlets libs
-%endif
+%ctest %{?ctest_excludes}
%files
commit 2d230660a820d87784de6faa0fa2c9c025de4659
Author: Benjamin A. Beasley <code(a)musicinmybrain.net>
Date: Sat Aug 21 11:04:34 2021 -0400
Finish complete set of man pages (adds unu-* man pages)
diff --git a/ilk.1 b/ilk.1
index e88f365..ec96d51 100644
--- a/ilk.1
+++ b/ilk.1
@@ -33,7 +33,7 @@ Applies linear (homogenous coordinate) transforms to a given image, using the
given kernel for resampling.
.SH OPTIONS
.TP
-.IR @file \ ...
+.IR @file \ ...\:
response file(s) containing command-line arguments
.TP
.B \-i\fR <\fIimage\fR>
diff --git a/miter.1 b/miter.1
index 254f8b8..c95895c 100644
--- a/miter.1
+++ b/miter.1
@@ -79,7 +79,7 @@ miter \- a simple but effective little volume renderer
.RI < filename >]
.SH OPTIONS
.TP
-.IR @file \ ...
+.IR @file \ ...\:
response file(s) containing command-line arguments
.TP
.B \-i\fR <\fInsin\fR>
diff --git a/mrender.1 b/mrender.1
index c58da9e..b263ee1 100644
--- a/mrender.1
+++ b/mrender.1
@@ -69,7 +69,7 @@ in the output
image.
.SH OPTIONS
.TP
-.IR @file \ ...
+.IR @file \ ...\:
response file(s) containing command-line arguments
.TP
.B \-i\fR <\fInin\fR>
diff --git a/puller.1 b/puller.1
index 45b7635..ec9f9cf 100644
--- a/puller.1
+++ b/puller.1
@@ -139,7 +139,7 @@ puller \- command-line interface to the \(lqpull\(rq library
.RI < base >]
.SH OPTIONS
.TP
-.IR @file \ ...
+.IR @file \ ...\:
response file(s) containing command-line arguments
.TP
.B \-int\fR <\fIint\fR>
diff --git a/teem.spec b/teem.spec
index aded044..a7f8e03 100644
--- a/teem.spec
+++ b/teem.spec
@@ -4,7 +4,8 @@ Version: 1.11.0
Release: %autorelease -b 13
Summary: Libraries for processing and visualizing scientific raster data
-# The entire source is LGPLv2+, except files noted at the License fields of certain subpackages.
+# The entire source is LGPLv2+, except files noted at the License fields of
+# certain subpackages.
License: LGPLv2+
# Additionally, the following are removed in %%prep to assert that they do not
# contribute to the built package:
@@ -71,68 +72,68 @@ Source837: tend-shrink.1
Source838: tend-bfit.1
Source839: tend-satin.1
-#Source900: unu-about.1
-#Source901: unu-env.1
-#Source902: unu-i2w.1
-#Source903: unu-w2i.1
-#Source904: unu-make.1
-#Source905: unu-head.1
-#Source906: unu-data.1
-#Source907: unu-convert.1
-#Source908: unu-resample.1
-#Source909: unu-fft.1
-#Source910: unu-cmedian.1
-#Source911: unu-dist.1
-#Source912: unu-minmax.1
-#Source913: unu-cksum.1
-#Source914: unu-diff.1
-#Source915: unu-quantize.1
-#Source916: unu-unquantize.1
-#Source917: unu-project.1
-#Source918: unu-slice.1
-#Source919: unu-sselect.1
-#Source920: unu-dice.1
-#Source921: unu-splice.1
-#Source922: unu-join.1
-#Source923: unu-crop.1
-#Source924: unu-acrop.1
-#Source925: unu-inset.1
-#Source926: unu-pad.1
-#Source927: unu-reshape.1
-#Source928: unu-permute.1
-#Source929: unu-swap.1
-#Source930: unu-shuffle.1
-#Source931: unu-flip.1
-#Source932: unu-unorient.1
-#Source933: unu-axinfo.1
-#Source934: unu-axinsert.1
-#Source935: unu-axsplit.1
-#Source936: unu-axdelete.1
-#Source937: unu-axmerge.1
-#Source938: unu-tile.1
-#Source939: unu-untile.1
-#Source940: unu-histo.1
-#Source941: unu-dhisto.1
-#Source942: unu-jhisto.1
-#Source943: unu-histax.1
-#Source944: unu-heq.1
-#Source945: unu-gamma.1
-#Source946: unu-1op.1
-#Source947: unu-2op.1
-#Source948: unu-3op.1
-#Source949: unu-affine.1
-#Source950: unu-lut.1
-#Source951: unu-mlut.1
-#Source952: unu-subst.1
-#Source953: unu-rmap.1
-#Source954: unu-mrmap.1
-#Source955: unu-imap.1
-#Source956: unu-lut2.1
-#Source957: unu-ccfind.1
-#Source958: unu-ccadj.1
-#Source959: unu-ccmerge.1
-#Source960: unu-ccsettle.1
-#Source961: unu-save.1
+Source900: unu-about.1
+Source901: unu-env.1
+Source902: unu-i2w.1
+Source903: unu-w2i.1
+Source904: unu-make.1
+Source905: unu-head.1
+Source906: unu-data.1
+Source907: unu-convert.1
+Source908: unu-resample.1
+Source909: unu-fft.1
+Source910: unu-cmedian.1
+Source911: unu-dist.1
+Source912: unu-minmax.1
+Source913: unu-cksum.1
+Source914: unu-diff.1
+Source915: unu-quantize.1
+Source916: unu-unquantize.1
+Source917: unu-project.1
+Source918: unu-slice.1
+Source919: unu-sselect.1
+Source920: unu-dice.1
+Source921: unu-splice.1
+Source922: unu-join.1
+Source923: unu-crop.1
+Source924: unu-acrop.1
+Source925: unu-inset.1
+Source926: unu-pad.1
+Source927: unu-reshape.1
+Source928: unu-permute.1
+Source929: unu-swap.1
+Source930: unu-shuffle.1
+Source931: unu-flip.1
+Source932: unu-unorient.1
+Source933: unu-axinfo.1
+Source934: unu-axinsert.1
+Source935: unu-axsplit.1
+Source936: unu-axdelete.1
+Source937: unu-axmerge.1
+Source938: unu-tile.1
+Source939: unu-untile.1
+Source940: unu-histo.1
+Source941: unu-dhisto.1
+Source942: unu-jhisto.1
+Source943: unu-histax.1
+Source944: unu-heq.1
+Source945: unu-gamma.1
+Source946: unu-1op.1
+Source947: unu-2op.1
+Source948: unu-3op.1
+Source949: unu-affine.1
+Source950: unu-lut.1
+Source951: unu-mlut.1
+Source952: unu-subst.1
+Source953: unu-rmap.1
+Source954: unu-mrmap.1
+Source955: unu-imap.1
+Source956: unu-lut2.1
+Source957: unu-ccfind.1
+Source958: unu-ccadj.1
+Source959: unu-ccmerge.1
+Source960: unu-ccsettle.1
+Source961: unu-save.1
# Patch CMakeLists.txt to fix library install paths to match what Fedora
# expects. It is not immediately obvious if this patch is suitable for
@@ -269,20 +270,20 @@ install -t %{buildroot}%{_mandir}/man1 -p -m 0644 \
%{SOURCE820} %{SOURCE821} %{SOURCE822} %{SOURCE823} %{SOURCE824} \
%{SOURCE825} %{SOURCE826} %{SOURCE827} %{SOURCE828} %{SOURCE829} \
%{SOURCE830} %{SOURCE831} %{SOURCE832} %{SOURCE833} %{SOURCE834} \
- %{SOURCE835} %{SOURCE836} %{SOURCE837} %{SOURCE838} %{SOURCE839}
- #{SOURCE900} #{SOURCE901} #{SOURCE902} #{SOURCE903} #{SOURCE904} \
- #{SOURCE905} #{SOURCE906} #{SOURCE907} #{SOURCE908} #{SOURCE909} \
- #{SOURCE910} #{SOURCE911} #{SOURCE912} #{SOURCE913} #{SOURCE914} \
- #{SOURCE915} #{SOURCE916} #{SOURCE917} #{SOURCE918} #{SOURCE919} \
- #{SOURCE920} #{SOURCE921} #{SOURCE922} #{SOURCE923} #{SOURCE924} \
- #{SOURCE925} #{SOURCE926} #{SOURCE927} #{SOURCE928} #{SOURCE929} \
- #{SOURCE930} #{SOURCE931} #{SOURCE932} #{SOURCE933} #{SOURCE934} \
- #{SOURCE935} #{SOURCE936} #{SOURCE937} #{SOURCE938} #{SOURCE939} \
- #{SOURCE940} #{SOURCE941} #{SOURCE942} #{SOURCE943} #{SOURCE944} \
- #{SOURCE945} #{SOURCE946} #{SOURCE947} #{SOURCE948} #{SOURCE949} \
- #{SOURCE950} #{SOURCE951} #{SOURCE952} #{SOURCE953} #{SOURCE954} \
- #{SOURCE955} #{SOURCE956} #{SOURCE957} #{SOURCE958} #{SOURCE959} \
- #{SOURCE960} #{SOURCE961}
+ %{SOURCE835} %{SOURCE836} %{SOURCE837} %{SOURCE838} %{SOURCE839} \
+ %{SOURCE900} %{SOURCE901} %{SOURCE902} %{SOURCE903} %{SOURCE904} \
+ %{SOURCE905} %{SOURCE906} %{SOURCE907} %{SOURCE908} %{SOURCE909} \
+ %{SOURCE910} %{SOURCE911} %{SOURCE912} %{SOURCE913} %{SOURCE914} \
+ %{SOURCE915} %{SOURCE916} %{SOURCE917} %{SOURCE918} %{SOURCE919} \
+ %{SOURCE920} %{SOURCE921} %{SOURCE922} %{SOURCE923} %{SOURCE924} \
+ %{SOURCE925} %{SOURCE926} %{SOURCE927} %{SOURCE928} %{SOURCE929} \
+ %{SOURCE930} %{SOURCE931} %{SOURCE932} %{SOURCE933} %{SOURCE934} \
+ %{SOURCE935} %{SOURCE936} %{SOURCE937} %{SOURCE938} %{SOURCE939} \
+ %{SOURCE940} %{SOURCE941} %{SOURCE942} %{SOURCE943} %{SOURCE944} \
+ %{SOURCE945} %{SOURCE946} %{SOURCE947} %{SOURCE948} %{SOURCE949} \
+ %{SOURCE950} %{SOURCE951} %{SOURCE952} %{SOURCE953} %{SOURCE954} \
+ %{SOURCE955} %{SOURCE956} %{SOURCE957} %{SOURCE958} %{SOURCE959} \
+ %{SOURCE960} %{SOURCE961}
%endif
@@ -327,7 +328,7 @@ install -t %{buildroot}%{_mandir}/man1 -p -m 0644 \
%{_mandir}/man1/tend.1*
%{_mandir}/man1/tend-*.1*
%{_mandir}/man1/unu.1*
-#{_mandir}/man1/unu-*.1*
+%{_mandir}/man1/unu-*.1*
%{_mandir}/man1/vprobe.1*
%endif
diff --git a/tend-bfit.1 b/tend-bfit.1
index 9269f75..3e72390 100644
--- a/tend-bfit.1
+++ b/tend-bfit.1
@@ -27,7 +27,7 @@ to the range of DWI values along input axis 0, as a function of changing
values.
.SH OPTIONS
.TP
-.IR @file \ ...
+.IR @file \ ...\:
response file(s) containing command-line arguments
.TP
.B \-i\fR <\fInin\fR>
diff --git a/tend-glyph.1 b/tend-glyph.1
index c0ee791..5f8d28a 100644
--- a/tend-glyph.1
+++ b/tend-glyph.1
@@ -124,7 +124,7 @@ and/or
.RB \(lq unu\ quantize\ \-b\ 8 \(rq.
.SH OPTIONS
.TP
-.IR @file \ ...
+.IR @file \ ...\:
response file(s) containing command-line arguments
.TP
.B \-rt
diff --git a/tend-unmf.1 b/tend-unmf.1
index d97b5ce..c2fefd6 100644
--- a/tend-unmf.1
+++ b/tend-unmf.1
@@ -18,7 +18,9 @@ world space, and remove the measurement frame from the
.B \-i\fR <\fInin\fR>
input diffusion tensor volume (sorry, can\(cqt use usual default of
.RB \(lq \- \(rq
-for stdin because of hest quirk)
+for
+.BR stdin (3)
+because of hest quirk)
.TP
.B \-o\fR <\fInout\fR>
output tensor volume
diff --git a/tend.1 b/tend.1
index 831950d..0dc61a9 100644
--- a/tend.1
+++ b/tend.1
@@ -4,7 +4,7 @@ tend \- diffusion image processing and analysis
.SH SYNOPSIS
.B tend
.I COMMAND
-.RI [ OPTION ]...
+.RI [ OPTION ]...\:
.SH DESCRIPTION
.BR tend (1)
is a command-line interface to much of the functionality in \(lqten\(rq, a C
diff --git a/unu-1op.1 b/unu-1op.1
new file mode 100644
index 0000000..9a7fd67
--- /dev/null
+++ b/unu-1op.1
@@ -0,0 +1,177 @@
+.TH UNU\-1OP "1" "August 2021" "" "User Commands"
+.SH NAME
+unu\-1op \- unary operation on a
+.I nrrd
+.SH SYNOPSIS
+.B unu\ 1op
+.RI < operator >
+.RB [ \-s , \-\-seed
+.RI < seed >]
+.RB [ \-t , \-\-type
+.RI < type >]
+.RB [ \-i , \-\-input
+.RI < nin >]
+.RB [ \-o , \-\-output
+.RI < nout >]
+.SH DESCRIPTION
+Unary operation on a
+.IR nrrd .
+.IP \(bu +2n
+Uses
+.B nrrdArithUnaryOp
+.SH OPTIONS
+.TP
+.I operator
+Unary operator.
+Possibilities include:
+.RS +7n
+.IP \(bu 2
+.RB \(lq \- \(rq:
+negative (multiply by
+.BR \-1.0 )
+.IP \(bu 2
+.RB \(lq r \(rq:
+reciprocal
+.RB ( 1.0 / \fIvalue )
+.IP \(bu 2
+.RB \(lq sin \(rq,
+.RB \(lq cos \(rq,
+.RB \(lq tan \(rq,
+.RB \(lq asin \(rq,
+.RB \(lq acos \(rq,
+.RB \(lq atan \(rq:
+same as in C
+.IP \(bu 2
+.RB \(lq exp \(rq,
+.RB \(lq log \(rq,
+.RB \(lq log10 \(rq:
+same as in C
+.IP \(bu 2
+.RB \(lq log1p \(rq,
+.RB \(lq expm1 \(rq:
+accurate
+.BI log( x +1)
+and
+.BI exp( x )\-1
+.IP \(bu 2
+.RB \(lq log2 \(rq:
+log base 2
+.IP \(bu 2
+.RB \(lq sqrt \(rq,
+.RB \(lq cbrt \(rq,
+.RB \(lq ceil \(rq,
+.RB \(lq floor \(rq:
+same as in C
+.IP \(bu 2
+.RB \(lq erf \(rq:
+error function (integral of Gaussian)
+.IP \(bu 2
+.RB \(lq rup \(rq,
+.RB \(lq rdn \(rq:
+round up or down to integral value
+.IP \(bu 2
+.RB \(lq abs \(rq:
+absolute value
+.IP \(bu 2
+.RB \(lq sgn \(rq:
+.BR \-1 ,
+.BR 0 ,
+.BR 1
+if value is
+.RB < 0 ,
+.RB == 0 ,
+or
+.RB > 0
+.IP \(bu 2
+.RB \(lq exists \(rq:
+.B 1
+iff not
+.B NaN
+or
+.BR + / \-Inf ,
+.B 0
+otherwise
+.IP \(bu 2
+.RB \(lq rand \(rq:
+random value in
+.BR [0.0,1.0) ,
+no relation to input
+.IP \(bu 2
+.RB \(lq nrand \(rq:
+random sample from normal distribution with mean
+.B 0.0
+and stdvi
+.BR 1.0 ,
+no relation to input
+.IP \(bu 2
+.RB \(lq if \(rq:
+if input is non-zero,
+.BR 1 ,
+else
+.B 0
+.IP \(bu 2
+.RB \(lq 0 \(rq:
+output always
+.B 0
+.IP \(bu 2
+.RB \(lq 1 \(rq:
+output always
+.B 1
+.RE
+.TP
+.B \-s\fR\ <\fIseed\fR>\ ,\ \fB\-\-seed\fR\ <\fIseed\fR>
+seed value for RNG for
+.B rand
+and
+.BR nrand ,
+so that you can get repeatable results between runs, or, by not using this
+option, the RNG seeding will be based on the current time
+.RI ( string )
+.TP
+.B \-t\fR\ <\fItype\fR>\ ,\ \fB\-\-type\fR\ <\fItype\fR>
+convert input
+.I nrrd
+to this type prior to doing operation.
+Useful when desired output is
+.I float
+(e.g., with
+.BR log1p ),
+but input is integral.
+By default (not using this option), the types of the input
+.IR nrrd s
+are left unchanged.
+.TP
+.B \-i\fR\ <\fInin\fR>\ ,\ \fB\-\-input\fR\ <\fInin\fR>
+input
+.I nrrd
+.TP
+.B \-o\fR\ <\fInout\fR>\ ,\ \fB\-\-output\fR\ <\fInout\fR>
+output
+.I nrrd
+.RI ( string );
+default:
+.RB \(lq \- \(rq
+.SH "SEE\ ALSO"
+.BR unu (1)
+.P
+.BR abs (3),
+.BR acos (3),
+.BR asin (3),
+.BR atan (3),
+.BR cbrt (3),
+.BR ceil (3),
+.BR cos (3),
+.BR erf (3),
+.BR exp (3),
+.BR expm1 (3),
+.BR fabs (3),
+.BR floor (3),
+.BR isfinite (3),
+.BR log (3),
+.BR log10 (3),
+.BR log1p (3),
+.BR log2 (3),
+.BR rand (3),
+.BR sin (3),
+.BR sqrt (3),
+.BR tan (3)
diff --git a/unu-2op.1 b/unu-2op.1
new file mode 100644
index 0000000..a6678fb
--- /dev/null
+++ b/unu-2op.1
@@ -0,0 +1,180 @@
+.TH UNU\-2OP "1" "August 2021" "" "User Commands"
+.SH NAME
+unu\-2op \- binary operation on two
+.IR nrrd s,
+or on a
+.I nrrd
+and a constant
+.SH SYNOPSIS
+.B unu\ 2op
+.RI < operator >
+.RI < in1 >
+.RI < in2 >
+.RB [ \-s , \-\-seed
+.RI < seed >]
+.RB [ \-t , \-\-type
+.RI < type >]
+.RB [ \-w , \-\-which
+.RI < arg >]
+.RB [ \-o , \-\-output
+.RI < nout >]
+.SH DESCRIPTION
+Binary operation on two
+.IR nrrd s,
+or on a
+.I nrrd
+and a constant.
+Either the first or second operand can be a
+.I float
+constant, but not both.
+Use
+.RB \(lq \- \(rq
+for an operand to signify a
+.I nrrd
+to be read from
+.I stdin
+(a pipe).
+Note, however, that
+.RB \(lq \- \(rq
+can probably only be used once (reliably).
+.IP \(bu +2n
+Uses
+.B nrrdArithIterBinaryOp
+or (with
+.BR \-w )
+.B nrrdArithIterBinaryOpSelect
+.SH OPTIONS
+.TP
+.I operator
+Binary operator.
+Possibilities include:
+.RS +7n
+.IP \(bu +2n
+.RB \(lq + \(rq,
+.RB \(lq \- \(rq
+.RB \(lq x \(rq
+.RB \(lq / \(rq:
+add, subtract, multiply, divide
+.IP \(bu +2n
+.RB \(lq ^ \(rq:
+exponentiation
+.RB ( pow (3))
+.IP \(bu +2n
+.RB \(lq spow \(rq:
+signed exponentiation:
+.BI sgn( x )pow(abs( x ), p )
+.IP \(bu +2n
+.RB \(lq fpow \(rq:
+like spow but with curves flipped
+.IP \(bu +2n
+.RB \(lq % \(rq:
+integer modulo
+.IP \(bu +2n
+.RB \(lq fmod \(rq:
+same as
+.BR fmod (3)
+in C
+.IP \(bu +2n
+.RB \(lq atan2 \(rq:
+same as
+.BR atan2 (3)
+in C
+.IP \(bu +2n
+.RB \(lq min \(rq
+.RB \(lq max \(rq:
+minimum, maximum
+.IP \(bu +2n
+.RB \(lq lt \(rq
+.RB \(lq lte \(rq
+.RB \(lq gt \(rq
+.RB \(lq gte \(rq:
+same as C's
+.BR < ,
+.BR <= ,
+.BR > ,
+.B <=
+.IP \(bu +2n
+.RB \(lq eq \(rq
+.RB \(lq neq \(rq:
+same as C's
+.B ==
+and
+.B !=
+.IP \(bu +2n
+.RB \(lq comp \(rq:
+.BR \-1 ,
+.BR 0 ,
+or
+.B 1
+if 1st value is less than, equal to, or greater than 2nd value
+.IP \(bu +2n
+.RB \(lq if \(rq:
+if 1st value is non-zero, use it, else use 2nd value
+.IP \(bu +2n
+.RB \(lq exists \(rq:
+if 1st value exists, use it, else use 2nd value
+.IP \(bu +2n
+.RB \(lq nrand \(rq:
+scale unit-stdv Gaussian noise by 2nd value and add to first value
+.IP \(bu +2n
+.RB \(lq rrand \(rq:
+sample Rician distribution with 1st value for \(lqtrue\(rq mean, and 2nd value
+for sigma
+.RE
+.TP
+.I in1
+First input.
+Can be a single value or a
+.IR nrrd .
+.TP
+.I in2
+Second input.
+Can be a single value or a
+.IR nrrd .
+.TP
+.B \-s\fR\ <\fIseed\fR>\ ,\ \fB\-\-seed\fR\ <\fIseed\fR>
+seed value for RNG for
+.BR nrand ,
+so that you can get repeatable results between runs, or, by not using this
+option, the RNG seeding will be based on the current time
+.RI ( string )
+.TP
+.B \-t\fR\ <\fItype\fR>\ ,\ \fB\-\-type\fR\ <\fItype\fR>
+type to convert all
+.B INPUT
+.IR nrrd s
+to, prior to doing operation, useful for doing, for instance, the difference
+between two
+.I unsigned\ char
+.IR nrrd s.
+This will also determine output type.
+By default (not using this option), the types of the input
+.IR nrrd s
+are left unchanged.
+.TP
+.B \-w\fR\ <\fIarg\fR>\ ,\ \fB\-\-which\fR\ <\fIarg\fR>
+Which argument
+.RB ( 0
+or
+.BR 1 )
+should be used to determine the shape of the output
+.IR nrrd .
+By default (not using this option), the first non-constant argument is used.
+.RI ( int );
+default:
+.RB \(lq \-1 \(rq
+.TP
+.B \-o\fR\ <\fInout\fR>\ ,\ \fB\-\-output\fR\ <\fInout\fR>
+output
+.I nrrd
+.RI ( string );
+default:
+.RB \(lq \- \(rq
+.SH "SEE\ ALSO"
+.BR unu (1)
+.P
+.BR abs (1),
+.BR atan2 (1),
+.BR fabs (1),
+.BR fmod (1),
+.BR pow (1)
diff --git a/unu-3op.1 b/unu-3op.1
new file mode 100644
index 0000000..3204054
--- /dev/null
+++ b/unu-3op.1
@@ -0,0 +1,171 @@
+.TH UNU\-3OP "1" "August 2021" "" "User Commands"
+.SH NAME
+unu\-3op \- ternary operation on three
+.IR nrrd s
+or constants
+.SH SYNOPSIS
+.B unu\ 3op
+.RI < operator >
+.RI < in1 >
+.RI < in2 >
+.RI < in3 >
+.RB [ \-t , \-\-type
+.RI < type >]
+.RB [ \-w , \-\-which
+.RI < arg >]
+.RB [ \-o , \-\-output
+.RI < nout >]
+.SH DESCRIPTION
+Ternary operation on three
+.IR nrrd s
+or constants.
+Can have one, two, or three
+.IR nrrd s,
+but not zero.
+Use
+.RB \(lq \- \(rq
+for an operand to signify a
+.I nrrd
+to be read from
+.I stdin
+(a pipe).
+Note, however, that
+.RB \(lq \- \(rq
+can probably only be used once (reliably).
+.IP \(bu +2n
+Uses
+.B nrrdArithIterTernaryOp
+or (with
+.BR \-w )
+.B nrrdArithIterTernaryOpSelect
+.SH OPTIONS
+.TP
+.I operator
+Ternary operator.
+Possibilities include:
+.RS +7n
+.IP \(bu +2n
+.RB \(lq + \(rq,
+.RB \(lq x \(rq
+sum or product of three values
+.IP \(bu +2n
+.RB \(lq min \(rq,
+.RB \(lq max \(rq
+minimum, maximum
+.IP \(bu +2n
+.RB \(lq min_sm \(rq
+smoothed minimum function;
+.BI min_sm( x ,\ w ,\ M )
+is like
+.BI min( x , M )
+but for
+.IR x \ >\ M \- w
+(with
+.IR w \ >\ \fB0 )
+there is a smooth transition from
+.I x
+to asymptotic to
+.I M
+.IP \(bu +2n
+.RB \(lq max_sm \(rq
+smoothed maximum function;
+max_sm(m, w, x)
+.BI max_sm( m ,\ w ,\ x )
+is like
+.BI max( m , x )
+but for
+.IR x \ <\ m + w
+(with
+.IR w \ >\ m )
+there is a smooth transition from
+.I x
+to asymptotic to
+.I m
+.IP \(bu +2n
+.RB \(lq lt_sm \(rq
+1st less than 3rd, smoothed by 2nd
+.IP \(bu +2n
+.RB \(lq gt_sm \(rq
+1st greater than 3rd, smoothed by 2nd
+.IP \(bu +2n
+.RB \(lq clamp \(rq
+2nd value is clamped to range between the 1st and the 3rd
+.IP \(bu +2n
+.RB \(lq ifelse \(rq
+if 1st value non-zero, then 2nd value, else 3rd value
+.IP \(bu +2n
+.RB \(lq lerp \(rq
+linear interpolation between the 2nd and 3rd values, as the 1st value varies
+between
+.B 0.0
+and
+.BR 1.0 ,
+respectively
+.IP \(bu +2n
+.RB \(lq exists \(rq
+if the 1st value exists, use the 2nd value, otherwise use the 3rd
+.IP \(bu +2n
+.RB \(lq in_op \(rq
+.B 1
+iff 2nd value is > 1st and < 3rd,
+.B 0
+otherwise
+.IP \(bu +2n
+.RB \(lq in_cl \(rq
+.B 1
+iff 2nd value is >= 1st and <= 3rd,
+.B 0
+otherwise
+.IP \(bu +2n
+.RB \(lq gauss \(rq
+evaluate (at 1st value) Gaussian with mean=2nd and stdv=3rd value
+.IP \(bu +2n
+.RB \(lq rician \(rq
+evaluate (at 1st value) Rician with mean=2nd and stdv=3rd value
+.RE
+.TP
+.I in1
+First input.
+Can be a single value or a
+.IR nrrd .
+.TP
+.I in2
+Second input.
+Can be a single value or a
+.IR nrrd .
+.TP
+.I in3
+Third input.
+Can be a single value or a
+.IR nrrd .
+.TP
+.B \-t\fR\ <\fItype\fR>\ ,\ \fB\-\-type\fR\ <\fItype\fR>
+type to convert all
+.IR nrrd
+inputs to, prior to doing operation.
+This also determines output type.
+By default (not using this option), the types of the input
+.IR nrrd s
+are left unchanged.
+.TP
+.B \-w\fR\ <\fIarg\fR>\ ,\ \fB\-\-which\fR\ <\fIarg\fR>
+Which argument
+.RB ( 0 ,
+.BR 1 ,
+or
+.BR 2 )
+should be used to determine the shape of the output
+.IR nrrd .
+By default (not using this option), the first non-constant argument is used.
+.RI ( int );
+default:
+.RB \(lq \-1 \(rq
+.TP
+.B \-o\fR\ <\fInout\fR>\ ,\ \fB\-\-output\fR\ <\fInout\fR>
+output
+.I nrrd
+.RI ( string );
+default:
+.RB \(lq \- \(rq
+.SH "SEE\ ALSO"
+.BR unu (1)
diff --git a/unu-about.1 b/unu-about.1
new file mode 100644
index 0000000..9df6bee
--- /dev/null
+++ b/unu-about.1
@@ -0,0 +1,9 @@
+.TH UNU\-ABOUT "1" "April 2021" "" "User Commands"
+.SH NAME
+unu\-about \- information about
+.BR unu (1)
+and its use
+.SH SYNOPSIS
+.B unu\ about
+.SH "SEE\ ALSO"
+.BR unu (1)
diff --git a/unu-acrop.1 b/unu-acrop.1
new file mode 100644
index 0000000..cbc595f
--- /dev/null
+++ b/unu-acrop.1
@@ -0,0 +1,115 @@
+.TH UNU\-ACROP "1" "August 2021" "" "User Commands"
+.SH NAME
+unu\-acrop \- automatically crop axes based on given measure
+.SH SYNOPSIS
+.B unu\ acrop
+.RB [ \-a , \-\-axes
+.RI [< ax0 \ ...>]]
+.BR \-m , \-\-measure
+.RI < measr >
+.RB [ \-f , \-\-frac
+.RI < frac >]
+.RB [ \-off , \-\-offset
+.RI < offset >]
+.RB [ \-b , \-\-bounds
+.RI < filename >]
+.RB [ \-i , \-\-input
+.RI < nin >]
+.RB [ \-o , \-\-output
+.RI < nout >]
+.SH DESCRIPTION
+Automatically crop axes based on given measure.
+For the axes that are to be cropped, the slices perpendicular to that axis are
+projected down to a scalar with the specified measure.
+The resulting 1\-D array is analyzed by determining what portions at the
+beginning and end constitute less than some portion of the cumulative array
+sum; these ends are cropped off.
+The cropping bounds determined here can be saved and applied to other arrays
+via the
+.RB \(lq \-b \(rq
+option.
+.IP \(bu +2n
+Uses
+.B nrrdCropAuto
+.SH OPTIONS
+.TP
+.B \-a\fR\ [<\fIax0\fR\ ...>]\ ,\ \fB\-\-axes\fR\ [<\fIax0\fR\ ...>]
+the axes (if any) that should
+.B NOT
+be cropped (0 or more
+.IR unsigned\ int s);
+default: \(lq \(rq
+.TP
+.B \-m\fR\ <\fImeasr\fR>\ ,\ \fB\-\-measure\fR\ <\fImeasr\fR>
+How to measure slices (along axes to crop) as scalars, to form 1\-D array
+analyzed to determine cropping extent.
+All the measures from
+.BR unu\-project (1)
+can be used, but those that make more sense here include:
+.RS +7n
+.IP \(bu 2
+.RB \(lq max \(rq,
+.RB \(lq mean \(rq,
+.RB \(lq median \(rq,
+.RB \(lq variance \(rq:
+(self-explanatory)
+.IP \(bu 2
+.RB \(lq stdv \(rq:
+standard deviation
+.IP \(bu 2
+.RB \(lq cov \(rq:
+coefficient of variation
+.IP \(bu 2
+.RB \(lq product \(rq,
+.RB \(lq sum \(rq:
+product or sum of all values
+.IP \(bu 2
+.RB \(lq L1 \(rq,
+.RB \(lq L2 \(rq,
+.RB \(lq NL2 \(rq,
+.RB \(lq RMS \(rq,
+.RB \(lq Linf \(rq:
+different norms
+.RE
+.TP
+.B \-f\fR\ <\fIfrac\fR>\ ,\ \fB\-\-frac\fR\ <\fIfrac\fR>
+threshold of cumulative sum of 1\-D array at which to crop.
+Needs to be in interval
+.BR [0.0,0.5) .
+.RI ( double );
+default:
+.RB \(lq 0.1 \(rq
+.TP
+.B \-off\fR\ <\fIoffset\fR>\ ,\ \fB\-\-offset\fR\ <\fIoffset\fR>
+how much to offset the numerically determined cropping; positive offsets means
+expanding the interval of kept indices (less cropping)
+.RI ( int );
+default:
+.RB \(lq 1 \(rq
+.TP
+.B \-b\fR\ <\fIfilename\fR>\ ,\ \fB\-\-bounds\fR\ <\fIfilename\fR>
+if a filename is given here, the automatically determined min and max bounds
+for cropping are saved to this file as a 2\-D array; first scanline is for
+.BR \-min ,
+second is for
+.BR \-max .
+Unfortunately nothing using the
+.RB \(lq m \(rq
+and
+.RB \(lq M \(rq
+semantics (above) can currently be saved in the bounds file.
+.RI ( string )
+.TP
+.B \-i\fR\ <\fInin\fR>\ ,\ \fB\-\-input\fR\ <\fInin\fR>
+input
+.I nrrd
+.TP
+.B \-o\fR\ <\fInout\fR>\ ,\ \fB\-\-output\fR\ <\fInout\fR>
+output
+.I nrrd
+.RI ( string );
+default:
+.RB \(lq \- \(rq
+.SH "SEE\ ALSO"
+.BR unu (1),
+.BR unu\-project (1)
diff --git a/unu-affine.1 b/unu-affine.1
new file mode 100644
index 0000000..5765707
--- /dev/null
+++ b/unu-affine.1
@@ -0,0 +1,84 @@
+.TH UNU\-AFFINE "1" "August 2021" "" "User Commands"
+.SH NAME
+unu\-affine \- affine (lerp) mapping on 5
+.IR nrrd s
+or constants
+.SH SYNOPSIS
+.B unu\ affine
+.RI < minIn >
+.RI < in >
+.RI < maxIn >
+.RI < minOut >
+.RI < maxOut >
+.RB [ \-t , \-\-type
+.RI < type >]
+.RB [ \-clamp
+.RI < bool >]
+.RB [ \-o , \-\-output
+.RI < nout >]
+.SH DESCRIPTION
+Affine (lerp) mapping on 5
+.IR nrrd s
+or constants.
+All the 5 arguments can be either
+.IR nrrd s
+or single floating-point values.
+When all args are single values, this is subsuming the functionality of the
+previous stand-alone
+.BR affine (1)
+program.
+Use
+.RB \(lq \- \(rq
+for an operand to signify a
+.I nrrd
+to be read from
+.I stdin
+(a pipe).
+Note, however, that
+.RB \(lq \- \(rq
+can probably only be used once (reliably).
+.IP \(bu +2n
+Uses
+.B nrrdArithAffine
+or
+.B nrrdArithIterAffine
+.SH OPTIONS
+.TP
+.I minIn
+Lower end of input value range.
+.TP
+.I in
+Input value.
+.TP
+.I maxIn
+Upper end of input value range.
+.TP
+.I minOut
+Lower end of output value range.
+.TP
+.I maxOut
+Upper end of output value range.
+.TP
+.B \-t\fR\ <\fItype\fR>\ ,\ \fB\-\-type\fR\ <\fItype\fR>
+type to convert all
+.IR nrrd
+inputs to, prior to doing operation.
+This also determines output type.
+By default (not using this option), the types of the input
+.IR nrrd s
+are left unchanged.
+.TP
+.B \-clamp\fR\ <\fIbool\fR>
+clamp output values to specified output range
+.RI ( bool );
+default:
+.RB \(lq false \(rq
+.TP
+.B \-o\fR\ <\fInout\fR>\ ,\ \fB\-\-output\fR\ <\fInout\fR>
+output
+.I nrrd
+.RI ( string );
+default:
+.RB \(lq \- \(rq
+.SH "SEE\ ALSO"
+.BR unu (1)
diff --git a/unu-axdelete.1 b/unu-axdelete.1
new file mode 100644
index 0000000..947c921
--- /dev/null
+++ b/unu-axdelete.1
@@ -0,0 +1,42 @@
+.TH UNU\-AXDELETE "1" "August 2021" "" "User Commands"
+.SH NAME
+unu\-axdelete \- remove one or more singleton axes from a
+.I nrrd
+.SH SYNOPSIS
+.B unu\ axdelete
+.BR \-a , \-\-axis
+.RI < axis >
+.RB [ \-i , \-\-input
+.RI < nin >]
+.RB [ \-o , \-\-output
+.RI < nout >]
+.SH DESCRIPTION
+Remove one or more singleton axes from a
+.IR nrrd .
+Singleton axes have only a single sample along them.
+The underlying linear ordering of the samples is unchanged, and the information
+about the other axes is shifted downwards as needed.
+As a total hack, if you give \-1 as the axis, this will do a matlab-style
+\(lqsqueeze\(rq, in which any and all singleton axes are removed.
+.IP \(bu +2n
+Uses
+.B nrrdAxesDelete
+.SH OPTIONS
+.TP
+.B \-a\fR\ <\fIaxis\fR>\ ,\ \fB\-\-axis\fR\ <\fIaxis\fR>
+dimension (axis index) of the axis to remove
+.RI ( unsigned\ int )
+.TP
+.B \-i\fR\ <\fInin\fR>\ ,\ \fB\-\-input\fR\ <\fInin\fR>
+input
+.I nrrd
+.TP
+.B \-o\fR\ <\fInout\fR>\ ,\ \fB\-\-output\fR\ <\fInout\fR>
+output
+.I nrrd
+.RI ( string );
+default:
+.RB \(lq \- \(rq
+.SH "SEE\ ALSO"
+.BR unu (1),
+.BR unu\-axmerge (1)
diff --git a/unu-axinfo.1 b/unu-axinfo.1
new file mode 100644
index 0000000..00dd7e9
--- /dev/null
+++ b/unu-axinfo.1
@@ -0,0 +1,80 @@
+.TH UNU\-AXINFO "1" "August 2021" "" "User Commands"
+.SH NAME
+unu\-axinfo \- modify attributes of one or more axes
+.SH SYNOPSIS
+.B unu\ axinfo
+.BR \-a , \-\-axes
+.RI < ax0 \ ax1 \ ...>
+.RB [ \-l , \-\-label
+.RI < label >]
+.RB [ \-u , \-\-units
+.RI < units >]
+.RB [ \-mm , \-\-minmax
+.RI < min \ max >]
+.RB [ \-sp , \-\-spacing
+.RI < spacing >]
+.RB [ \-c , \-\-center
+.RI < center >]
+.RB [ \-k , \-\-kind
+.RI < kind >]
+.RB [ \-i , \-\-input
+.RI < nin >]
+.RB [ \-o , \-\-output
+.RI < nout >]
+.SH DESCRIPTION
+Modify attributes of one or more axes.
+The only attributes which are set are those for which command-line options are
+given.
+.IP \(bu +2n
+Uses no particular function; just sets fields in
+.B NrrdAxisInfo
+.SH OPTIONS
+.TP
+.B \-a\fR\ <\fIax0\fR \fIax1\fR\ ...>\ ,\ \fB\-\-axes\fR\ <\fIax0\fR \fIax1\fR\ ...>
+the one or more axes that should be modified
+(1 or more
+.IR unsigned\ int s)
+.TP
+.B \-l\fR\ <\fIlabel\fR>\ ,\ \fB\-\-label\fR\ <\fIlabel\fR>
+label to associate with axis
+.RI ( string )
+.TP
+.B \-u\fR\ <\fIunits\fR>\ ,\ \fB\-\-units\fR\ <\fIunits\fR>
+units of measurement
+.RI ( string )
+.TP
+.B \-mm\fR\ <\fImin\fR\ \fImax\fR>\ ,\ \fB\-\-minmax\fR\ <\fImin\fR\ \fImax\fR>
+units of measurement
+min and max values along axis
+( 2
+.IR double s)
+.TP
+.B \-sp\fR\ <\fIspacing\fR>\ ,\ \fB\-\-spacing\fR\ <\fIspacing\fR>
+spacing between samples along axis
+.RI ( double )
+.TP
+.B \-c\fR\ <\fIcenter\fR>\ ,\ \fB\-\-center\fR\ <\fIcenter\fR>
+axis centering:
+.RB \(lq cell \(rq
+or
+.RB \(lq node \(rq.
+Not using this option leaves the centering as it is on input
+.RI ( string )
+.TP
+.B \-k\fR\ <\fIkind\fR>\ ,\ \fB\-\-kind\fR\ <\fIkind\fR>
+axis kind.
+Not using this option leaves the kind as it is on input
+.RI ( string )
+.TP
+.B \-i\fR\ <\fInin\fR>\ ,\ \fB\-\-input\fR\ <\fInin\fR>
+input
+.I nrrd
+.TP
+.B \-o\fR\ <\fInout\fR>\ ,\ \fB\-\-output\fR\ <\fInout\fR>
+output
+.I nrrd
+.RI ( string );
+default:
+.RB \(lq \- \(rq
+.SH "SEE\ ALSO"
+.BR unu (1)
diff --git a/unu-axinsert.1 b/unu-axinsert.1
new file mode 100644
index 0000000..62f1e0a
--- /dev/null
+++ b/unu-axinsert.1
@@ -0,0 +1,45 @@
+.TH UNU\-AXINSERT "1" "August 2021" "" "User Commands"
+.SH NAME
+unu\-axinsert \- add a \(lqstub\(rq (length 1) axis to a
+.I nrrd
+.SH SYNOPSIS
+.B unu\ axinsert
+.BR \-a , \-\-axis
+.RI < axis >
+.RB [ \-l , \-\-label
+.RI < label >]
+.RB [ \-i , \-\-input
+.RI < nin >]
+.RB [ \-o , \-\-output
+.RI < nout >]
+.SH DESCRIPTION
+Add a \(lqstub\(rq (length 1) axis to a
+.IR nrrd .
+The underlying linear ordering of the samples is unchanged, and the information
+about the other axes is shifted upwards as needed.
+.IP \(bu +2n
+Uses
+.B nrrdAxesInsert
+.SH OPTIONS
+.TP
+.B \-a\fR\ <\fIaxis\fR>\ ,\ \fB\-\-axes\fR\ <\fIaxis\fR>
+dimension (axis index) at which to insert the new axis
+.RI ( unsigned\ int )
+.TP
+.B \-l\fR\ <\fIlabel\fR>\ ,\ \fB\-\-label\fR\ <\fIlabel\fR>
+label to associate with axis
+.RI ( string )
+.TP
+.B \-i\fR\ <\fInin\fR>\ ,\ \fB\-\-input\fR\ <\fInin\fR>
+input
+.I nrrd
+.TP
+.B \-o\fR\ <\fInout\fR>\ ,\ \fB\-\-output\fR\ <\fInout\fR>
+output
+.I nrrd
+.RI ( string );
+default:
+.RB \(lq \- \(rq
+.SH "SEE\ ALSO"
+.BR unu (1),
+.BR unu\-axsplit (1)
diff --git a/unu-axmerge.1 b/unu-axmerge.1
new file mode 100644
index 0000000..50e8831
--- /dev/null
+++ b/unu-axmerge.1
@@ -0,0 +1,47 @@
+.TH UNU\-AXMERGE "1" "August 2021" "" "User Commands"
+.SH NAME
+unu\-axmerge \- merge two adjacent axes into one
+.SH SYNOPSIS
+.B unu\ axmerge
+.BR \-a , \-\-axis
+.RI < ax0 \ ...>
+.RB [ \-i , \-\-input
+.RI < nin >]
+.RB [ \-o , \-\-output
+.RI < nout >]
+.SH DESCRIPTION
+Merge two adjacent axes into one.
+A more general version of
+.BR unu\-axdelete (1).
+The underlying linear ordering of the samples is unchanged, and the information
+about the other axes is shifted downwards as needed.
+.IP \(bu +2n
+Uses
+.B nrrdAxesMerge
+.SH OPTIONS
+.TP
+.B \-a\fR\ <\fIax0\fR\ ...>\ ,\ \fB\-\-axis\fR\ <\fIax0\fR\ ...>
+axis (or axes) to merge.
+Each axis index identified is the lower of the pair of axes that will be
+merged.
+Saying
+.RB \(lq \-a\ 2 \(rq
+means to merge axis 2 and axis 3 into axis 2.
+If multiple merges are to be done, the indices listed here are for the axes
+prior to any merging.
+(1 or more
+.IR int s)
+.TP
+.B \-i\fR\ <\fInin\fR>\ ,\ \fB\-\-input\fR\ <\fInin\fR>
+input
+.I nrrd
+.TP
+.B \-o\fR\ <\fInout\fR>\ ,\ \fB\-\-output\fR\ <\fInout\fR>
+output
+.I nrrd
+.RI ( string );
+default:
+.RB \(lq \- \(rq
+.SH "SEE\ ALSO"
+.BR unu (1),
+.BR unu\-axdelete (1)
diff --git a/unu-axsplit.1 b/unu-axsplit.1
new file mode 100644
index 0000000..2577ae8
--- /dev/null
+++ b/unu-axsplit.1
@@ -0,0 +1,47 @@
+.TH UNU\-AXSPLIT "1" "August 2021" "" "User Commands"
+.SH NAME
+unu\-axsplit \- split one axis into two axes
+.SH SYNOPSIS
+.B unu\ axsplit
+.BR \-a , \-\-axis
+.RI < axis >
+.BR \-s , \-\-size
+.RI < fast ,\ slow \ sizes>
+.RB [ \-i , \-\-input
+.RI < nin >]
+.RB [ \-o , \-\-output
+.RI < nout >]
+.SH DESCRIPTION
+Split one axis into two axes.
+More general version of
+.BR unu\-axinsert (1),
+since a given axis can be split into fast and slow axes of arbitrary size, as
+long as the product of the fast and slow sizes is the same as the original
+size.
+.IP \(bu +2n
+Uses
+.B nrrdAxesSplit
+.SH OPTIONS
+.TP
+.B \-a\fR\ <\fIaxis\fR>\ ,\ \fB\-\-axis\fR\ <\fIaxis\fR>
+dimension (axis index) to split at
+.RI ( unsigned\ int )
+.TP
+.B \-s\fR\ <\fIfast\fR,\ \fIslow\fR\ sizes>\ ,\ \fB\-\-size\fR\ <\fIfast\fR,\ \fIslow\fR\ sizes>
+fast and slow axis sizes to produce as result of splitting given axis.
+(2
+.IR size_t s)
+.TP
+.B \-i\fR\ <\fInin\fR>\ ,\ \fB\-\-input\fR\ <\fInin\fR>
+input
+.I nrrd
+.TP
+.B \-o\fR\ <\fInout\fR>\ ,\ \fB\-\-output\fR\ <\fInout\fR>
+output
+.I nrrd
+.RI ( string );
+default:
+.RB \(lq \- \(rq
+.SH "SEE\ ALSO"
+.BR unu (1),
+.BR unu\-axinsert (1)
diff --git a/unu-ccadj.1 b/unu-ccadj.1
new file mode 100644
index 0000000..ec09d85
--- /dev/null
+++ b/unu-ccadj.1
@@ -0,0 +1,55 @@
+.TH UNU\-CCADJ "1" "August 2021" "" "User Commands"
+.SH NAME
+unu\-ccadj \- form adjacency matrix of connected components
+.SH SYNOPSIS
+.B unu\ ccadj
+.BR \-c , \-\-connect
+.RI < connectivity >
+.RB [ \-i , \-\-input
+.RI < nin >]
+.RB [ \-o , \-\-output
+.RI < nout >]
+.SH DESCRIPTION
+Form adjecency matrix of connected components.
+This operates on
+the output of
+.BR unu\-ccfind (1).
+Output is
+.I unsigned\ char
+array containing
+.B 1
+at locations
+.RI ( I , J )
+and
+.RI ( J , I )
+if CCs with ids
+.I I
+and
+.I J
+are adjacent, according to the chosen style of adjacency.
+.IP \(bu +2n
+Uses
+.B nrrdCCAdjacency
+.SH OPTIONS
+.TP
+.B \-c\fR\ <\fIconnectivity\fR>\ ,\ \fB\-\-connect\fR\ <\fIconnectivity\fR>
+what kind of connectivity to use: the number of coordinates that vary in order
+to traverse the neighborhood of a given sample.
+In 2D:
+.RB \(lq 1 \(rq:
+4-connected,
+.RB \(lq 2 \(rq:
+8-connected
+.RI ( unsigned\ int )
+.TP
+.B \-o\fR\ <\fInout\fR>\ ,\ \fB\-\-output\fR\ <\fInout\fR>
+output
+.I nrrd
+.RI ( string );
+default:
+.RB \(lq \- \(rq
+.SH "SEE\ ALSO"
+.BR unu (1),
+.BR unu\-ccfind (1),
+.BR unu\-ccmerge (1),
+.BR unu\-ccsettle (1)
diff --git a/unu-ccfind.1 b/unu-ccfind.1
new file mode 100644
index 0000000..2c4169e
--- /dev/null
+++ b/unu-ccfind.1
@@ -0,0 +1,64 @@
+.TH UNU\-CCFIND "1" "August 2021" "" "User Commands"
+.SH NAME
+unu\-ccfind \- find connected components (CCs)
+.SH SYNOPSIS
+.B unu\ ccfind
+.RB [ \-v , \-\-values
+.RI < filename >]
+.RB [ \-t , \-\-type
+.RI < type >]
+.BR \-c , \-\-connect
+.RI < connectivity >
+.RB [ \-i , \-\-input
+.RI < nin >]
+.RB [ \-o , \-\-output
+.RI < nout >]
+.SH DESCRIPTION
+Find connected components (CCs).
+This works on 1-byte and 2-byte integral values, as well as 4-byte
+.IR int s.
+.IP \(bu +2n
+Uses
+.B nrrdCCFind
+.SH OPTIONS
+.TP
+.B \-v\fR\ <\fIfilename\fR>\ ,\ \fB\-\-values\fR\ <\fIfilename\fR>
+Giving a
+.I filename
+here allows you to save out the values associated with each connect component.
+This can be used later with
+.BR unu\-ccmerge (1) \ \-d .
+By default, no record of the original CC values is kept.
+.RI ( string )
+.TP
+.B \-t\fR\ <\fItype\fR>\ ,\ \fB\-\-type\fR\ <\fItype\fR>
+type to use for output, to store the CC ID values.
+By default (not using this option), the type used will be the smallest of
+.IR uchar ,
+.IR ushort ,
+or
+.IR int ,
+that can represent all the CC ID values.
+Using this option allows one to specify the integral type to be used.
+.TP
+.B \-c\fR\ <\fIconnectivity\fR>\ ,\ \fB\-\-connect\fR\ <\fIconnectivity\fR>
+what kind of connectivity to use: the number of coordinates that vary in order
+to traverse the neighborhood of a given sample.
+In 2D:
+.RB \(lq 1 \(rq:
+4-connected,
+.RB \(lq 2 \(rq:
+8-connected
+.RI ( unsigned\ int )
+.TP
+.B \-o\fR\ <\fInout\fR>\ ,\ \fB\-\-output\fR\ <\fInout\fR>
+output
+.I nrrd
+.RI ( string );
+default:
+.RB \(lq \- \(rq
+.SH "SEE\ ALSO"
+.BR unu (1),
+.BR unu\-ccadj (1),
+.BR unu\-ccmerge (1),
+.BR unu\-ccsettle (1)
diff --git a/unu-ccmerge.1 b/unu-ccmerge.1
new file mode 100644
index 0000000..2b263f5
--- /dev/null
+++ b/unu-ccmerge.1
@@ -0,0 +1,111 @@
+.TH UNU\-CCMERGE "1" "August 2021" "" "User Commands"
+.SH NAME
+unu\-ccmerge \- merge CCs with their neighbors, under various constraints
+.SH SYNOPSIS
+.B unu\ ccmerge
+.RB [ \-d , \-\-directed
+.RI < dir >]
+.RB [ \-s , \-\-size
+.RI < max\ size >]
+.RB [ \-n , \-\-neighbor
+.RI < max\ #\ neigh >]
+.BR \-c , \-\-connect
+.RI < connectivity >
+.RB [ \-revalue ]
+.RB [ \-i , \-\-input
+.RI < nin >]
+.RB [ \-v , \-\-values
+.RI < values >]
+.RB [ \-o , \-\-output
+.RI < nout >]
+.SH DESCRIPTION
+Merge CCs with their neighbors, under various constraints.
+This operates on the output of
+.BR unu\-ccfind (1).
+Merging of a CC is always done into its largest neighbor.
+Whether or not to merge can be constrained by one or more of: CC size
+.RB (\(lq \-s \(rq),
+original CC value being brighter or darker
+.RB (\(lq \-d \(rq),
+and number of neighbors
+.RB (\(lq \-n \(rq).
+.IP \(bu +2n
+Uses
+.B nrrdCCMerge
+.SH OPTIONS
+.TP
+.B \-d\fR\ <\fIdir\fR>\ ,\ \fB\-\-directed\fR\ <\fIdir\fR>
+do value-driven merging.
+Using (positive)
+.RB \(lq 1 \(rq
+says that dark islands get merged with bright surrounds, while
+.RB \(lq \-1 \(rq
+says the opposite.
+By default, merging can go either way.
+.RI ( int );
+default:
+.RB \(lq 0 \(rq
+.TP
+.B \-s\fR\ <\fImax\ size\fR>\ ,\ \fB\-\-size\fR\ <\fImax\ size\fR>
+a cap on the CC size that will be absorbed into its surround.
+CCs larger than this are deemed too significant to mess with.
+Or, use
+.RB \(lq 0 \(rq
+to remove any such restriction on merging.
+.RI ( int );
+default:
+.RB \(lq 0 \(rq
+.TP
+.B \-n\fR\ <\fImax\ #\ neigh\fR>\ ,\ \fB\-\-neighbor\fR\ <\fImax\ #\ neigh\fR>
+a cap on the number of neighbors that a CC may have if it is to be be merged.
+.RB \(lq 1 \(rq
+allows only islands to be merged,
+.RB \(lq 2 \(rq
+does merging with bigger of two neighbors, etc, while
+.RB \(lq 0 \(rq
+says that number of neighbors is no constraint
+.RI ( int );
+default:
+.RB \(lq 1 \(rq
+.TP
+.B \-c\fR\ <\fIconnectivity\fR>\ ,\ \fB\-\-connect\fR\ <\fIconnectivity\fR>
+what kind of connectivity to use: the number of coordinates that vary in order
+to traverse the neighborhood of a given sample.
+In 2D:
+.RB \(lq 1 \(rq:
+4-connected,
+.RB \(lq 2 \(rq:
+8-connected
+.RI ( unsigned\ int )
+.TP
+.B \-revalue
+If this option is given, then after the merging, the CCs are re-assigned their
+original datavalues, as given by the
+.RB \(lq \-v \(rq
+option
+.TP
+.B \-i\fR\ <\fInin\fR>\ ,\ \fB\-\-input\fR\ <\fInin\fR>
+input
+.I nrrd
+.TP
+.B \-v\fR\ <\fIvalues\fR>\ ,\ \fB\-\-values\fR\ <\fIvalues\fR>
+result of using
+.BR unu\-ccfind (1) \ \-v ,
+the record of which values were originally associated with each CC.
+This is required for value-directed merging (with non-zero
+.RB \(lq \-d \(rq
+option), or if the
+.B \-revalue
+option is given, but is not needed otherwise
+.TP
+.B \-o\fR\ <\fInout\fR>\ ,\ \fB\-\-output\fR\ <\fInout\fR>
+output
+.I nrrd
+.RI ( string );
+default:
+.RB \(lq \- \(rq
+.SH "SEE\ ALSO"
+.BR unu (1),
+.BR unu\-ccfind (1),
+.BR unu\-ccadj (1),
+.BR unu\-ccsettle (1)
diff --git a/unu-ccsettle.1 b/unu-ccsettle.1
new file mode 100644
index 0000000..e5aa503
--- /dev/null
+++ b/unu-ccsettle.1
@@ -0,0 +1,40 @@
+.TH UNU\-CCSETTLE "1" "August 2021" "" "User Commands"
+.SH NAME
+unu\-ccsettle \- remap CC values down to lowest contiguous values
+.SH SYNOPSIS
+.B unu\ ccsettle
+.BR \-i , \-\-input
+.RI < nin >
+.RB [ \-v , \-\-values
+.RI < filename >]
+.RB [ \-o , \-\-output
+.RI < nout >]
+.SH DESCRIPTION
+Remap CC values down to lowest contiguous values.
+.IP \(bu +2n
+Uses
+.B nrrdCCSettle
+.SH OPTIONS
+.TP
+.B \-i\fR\ <\fInin\fR>\ ,\ \fB\-\-input\fR\ <\fInin\fR>
+input
+.I nrrd
+.TP
+.B \-v\fR\ <\fIfilename\fR>\ ,\ \fB\-\-values\fR\ <\fIfilename\fR>
+Giving a
+.I filename
+here allows you to save out the mapping from new (settled) values to old
+values, in the form of a 1-D lookup table
+.RI ( string )
+.TP
+.B \-o\fR\ <\fInout\fR>\ ,\ \fB\-\-output\fR\ <\fInout\fR>
+output
+.I nrrd
+.RI ( string );
+default:
+.RB \(lq \- \(rq
+.SH "SEE\ ALSO"
+.BR unu (1),
+.BR unu\-ccfind (1),
+.BR unu\-ccadj (1),
+.BR unu\-ccmerge (1)
diff --git a/unu-cksum.1 b/unu-cksum.1
new file mode 100644
index 0000000..af6d4b8
--- /dev/null
+++ b/unu-cksum.1
@@ -0,0 +1,55 @@
+.TH UNU\-CKSUM "1" "May 2021" "" "User Commands"
+.SH NAME
+unu\-cksum \- compute 32-bit CRC of
+.I nrrd
+data (same as via
+.BR cksum (1))
+.SH SYNOPSIS
+.B unu\ cksum
+.BR \-en , \-\-endian
+.RI < end >
+.RB [ \-pen , \-\-printendian
+.RI < bool >]
+.RI < nin1 \ ...>
+.SH DESCRIPTION
+Compute 32-bit CRC of
+.I nrrd
+data (same as via
+.BR cksum (1)).
+Unlike other commands, this doesn\(cqt produce a
+.IR nrrd .
+It only prints to standard out the CRC and byte counts for the input
+.IR nrrd (s),
+seeking to emulate the formatting of
+.BR cksum (1)
+output.
+.IP \(bu +2n
+Uses
+.B nrrdCRC32
+.SH OPTIONS
+.TP
+.B \-en\fR\ <\fIendian\fR>\ ,\ \fB\-\-endian\fR\ <\fIendian\fR>
+Endianness in which to compute CRC;
+.RB \(lq little \(rq
+for Intel and friends;
+.RB \(lq big \(rq
+for everyone else.
+Defaults to endianness of this machine.
+.TP
+.B \-pen\fR\ <\fIbool\fR>\ ,\ \fB\-\-printendian\fR\ <\fIbool\fR>
+whether or not to indicate after the CRC value the endianness with which the
+CRC was computed; doing so clarifies that the CRC result depends on endianness
+and may remove confusion in comparing results on platforms of different
+endianness
+.RI ( bool );
+default:
+.RB \(lq false \(rq
+.TP
+.RI < nin \ ...>
+input
+.IR nrrd (s)
+(1 or more
+.IR string s)
+.SH "SEE\ ALSO"
+.BR unu (1),
+.BR cksum (1)
diff --git a/unu-cmedian.1 b/unu-cmedian.1
new file mode 100644
index 0000000..685059c
--- /dev/null
+++ b/unu-cmedian.1
@@ -0,0 +1,98 @@
+.TH UNU\-CMEDIAN "1" "May 2021" "" "User Commands"
+.SH NAME
+unu\-cmedian \- cheap histogram-based median/mode filtering
+.SH SYNOPSIS
+.B unu\ cmedian
+.BR \-r , \-\-radius
+.RI < radius >
+.RB [ \-mode ]
+.RB [ \-b , \-\-bins
+.RI < num >]
+.RB [ \-w , \-\-weight
+.RI < weight >]
+.RB [ \-p , \-\-pad ]
+.RB [ \-c , \-\-channel ]
+.RB [ \-i , \-\-input
+.RI < nin >]
+.RB [ \-o , \-\-output
+.RI < nout >]
+.SH DESCRIPTION
+Cheap histogram-based median/mode filtering.
+Only works on 1, 2, or 3 dimensions.
+The window over which filtering is done is always square, and only a simplistic
+weighting scheme is available.
+The method is cheap because it does the median or mode based on a histogram,
+which enforces a quantization to the number of bins in the histogram, which
+probably means a loss of precision for anything except 8-bit data.
+Also, integral values can be
+recovered exactly only when the number of bins is exactly
+.IB min \- max +1
+(as reported by
+.BR unu\-minmax (1)).
+.IP \(bu +2n
+Uses
+.BR nrrdCheapMedian ,
+plus
+.B nrrdSlice
+and
+.B nrrdJoin
+in case of
+.RB \(lq \-c \(rq
+.SH OPTIONS
+.TP
+.B \-r\fR\ <\fIradius\fR>\ ,\ \fB\-\-radius\fR\ <\fIradius\fR>
+how big a window to filter over.
+.RB \(lq \-r\ 1 \(rq
+leads to a 3x3 window in an image, and a 3x3x3 window in a volume
+.RI ( unsigned\ int )
+.TP
+.B \-mode
+By default, median filtering is done.
+Using this option enables mode filtering, in which the most common value is
+used as output
+.TP
+.B \-b\fR\ <\fInum\fR>\ ,\ \fB\-\-bins\fR\ <\fInum\fR>
+# of bins in histogram.
+It is in your interest to minimize this number, since big histograms mean
+slower execution times.
+8-bit data needs at most 256 bins.
+.RI ( unsigned\ int );
+default:
+.RB \(lq 256 \(rq
+.TP
+.B \-w\fR\ <\fIweight\fR>\ ,\ \fB\-\-weight\fR\ <\fIweight\fR>
+How much higher to preferentially weight samples that are closer to the center
+of the window.
+.RB \(lq 1.0 \(rq
+weight means that all samples are uniformly weighted over the window, which
+facilitates a simple speed-up.
+.RI ( float );
+default:
+.RB \(lq 1.0 \(rq
+.TP
+.B \-p\fR\ ,\ \fB\-\-pad
+Pad the input (with boundary method
+.RB \(lq bleed \(rq),
+and crop the output, so as to overcome our cheapness and correctly handle the
+border.
+Obviously, this takes more memory.
+.TP
+.B \-c\fR\ ,\ \fB\-\-channel
+Slice the input along axis 0, run filtering on all slices, and join the results
+back together.
+This is the way you\(cqd want to process color (multi-channel) images or
+volumes.
+.TP
+.B \-i\fR\ <\fInin\fR>\ ,\ \fB\-\-input\fR\ <\fInin\fR>
+input
+.I nrrd
+.TP
+.B \-o\fR\ <\fInout\fR>\ ,\ \fB\-\-output\fR\ <\fInout\fR>
+output
+.I nrrd
+.RI ( string );
+default:
+.RB \(lq \- \(rq
+.SH "SEE\ ALSO"
+.BR unu (1),
+.BR unu\-minmax (1)
diff --git a/unu-convert.1 b/unu-convert.1
new file mode 100644
index 0000000..f3588cd
--- /dev/null
+++ b/unu-convert.1
@@ -0,0 +1,57 @@
+.TH UNU\-CONVERT "1" "May 2021" "" "User Commands"
+.SH NAME
+unu\-convert \- convert to another type (as if by cast, w/ optional clamp)
+.SH SYNOPSIS
+.B unu\ convert
+.BR \-t , \-\-type
+.RI < type >
+.RB [ \-i , \-\-input
+.RI < nin >]
+.RB [ \-clamp ]
+.RB [ \-o , \-\-output
+.RI < nout >]
+.SH DESCRIPTION
+Convert to another type (as if by cast, w/ optional clamp).
+By default this does not transform, scale, or intelligently quantize values; it
+just copies them from one type to another, which replicates exactly what
+you\(cqd get in C when you assign from a variable of one type to another, or
+when you cast to a different type.
+However, clamping values to the representable range
+of the output type is possible with
+.RB \(lq \-clamp \(rq.
+See also
+.BR unu\-quantize (1),
+.BR unu\-2op (1)
+.BR x ,
+and
+.BR unu\-3op (1)
+.BR clamp .
+.IP \(bu +2n
+Uses
+.B nrrdConvert
+or
+.B nrrdClampConvert
+.SH OPTIONS
+.TP
+.B \-t\fR\ <\fItype\fR>\ ,\ \fB\-\-type\fR\ <\fItype\fR>
+type to convert to
+.TP
+.B \-i\fR\ <\fInin\fR>\ ,\ \fB\-\-input\fR\ <\fInin\fR>
+input
+.I nrrd
+.TP
+.B \-clamp
+clamp input values to representable range of values of output type, to avoid
+wrap-around problems
+.TP
+.B \-o\fR\ <\fInout\fR>\ ,\ \fB\-\-output\fR\ <\fInout\fR>
+output
+.I nrrd
+.RI ( string );
+default:
+.RB \(lq \- \(rq
+.SH "SEE\ ALSO"
+.BR unu (1),
+.BR unu\-2op (1),
+.BR unu\-3op (1),
+.BR unu\-quantize (1)
diff --git a/unu-crop.1 b/unu-crop.1
new file mode 100644
index 0000000..b0f9ac8
--- /dev/null
+++ b/unu-crop.1
@@ -0,0 +1,85 @@
+.TH UNU\-CROP "1" "August 2021" "" "User Commands"
+.SH NAME
+unu\-crop \- crop along each axis to make a smaller
+.I nrrd
+.SH SYNOPSIS
+.B unu\ crop
+.RB [ \-min , \-\-minimum
+.RI [< pos0 \ ...>]]
+.RB [ \-max , \-\-maximum
+.RI [< pos0 \ ...>]]
+.RB [ \-b , \-\-bounds
+.RI < filename >]
+.RB [ \-i , \-\-input
+.RI < nin >]
+.RB [ \-o , \-\-output
+.RI < nout >]
+.SH DESCRIPTION
+Crop along each axis to make a smaller
+.IR nrrd .
+.IP \(bu +2n
+Uses
+.B nrrdCrop
+.SH OPTIONS
+.TP
+.B \-min\fR\ [<\fIpos0\fR\ ...>]\ ,\ \fB\-\-minimum\fR\ [<\fIpos0\fR\ ...>]
+low corner of bounding box.
+.RS +7n
+.IP \(bu +2n
+.RI < int >
+gives 0-based index
+.IP \(bu +2n
+.BR M ,
+.BR M+ <\fIint\fR>,
+.BR M\- <\fIint\fR>
+give index relative to the last sample on the axis
+.RB ( M== \fI#samples \-1 ).
+.RE
+.IP
+(0 or more
+.IR position s).
+default:
+.RB \(lq 0 \(rq
+.TP
+.B \-max\fR\ [<\fIpos0\fR\ ...>]\ ,\ \fB\-\-maximum\fR\ [<\fIpos0\fR\ ...>]
+high corner of bounding box.
+Besides the specification styles described above, there\(cqs also:
+.RS +7n
+.IP \(bu +2n
+.BR m+ <\fIint\fR>
+give index relative to minimum.
+.RE
+.IP
+(0 or more
+.IR position s).
+default:
+.RB \(lq 0 \(rq
+.TP
+.B \-b\fR\ <\fIfilename\fR>\ ,\ \fB\-\-bounds\fR\ <\fIfilename\fR>
+a filename given here overrides the
+.B \-min
+and
+.B \-max
+options (they don\(cqt need to be used) and provides the cropping bounds as a
+2\-D array; first scanline is for
+.BR \-min ,
+second is for
+.BR \-max .
+Unfortunately the
+.RB \(lq m \(rq
+and
+.RB \(lq M \(rq
+semantics (above) are currently not supported in the bounds file.
+.TP
+.B \-i\fR\ <\fInin\fR>\ ,\ \fB\-\-input\fR\ <\fInin\fR>
+input
+.I nrrd
+.TP
+.B \-o\fR\ <\fInout\fR>\ ,\ \fB\-\-output\fR\ <\fInout\fR>
+output
+.I nrrd
+.RI ( string );
+default:
+.RB \(lq \- \(rq
+.SH "SEE\ ALSO"
+.BR unu (1)
diff --git a/unu-data.1 b/unu-data.1
new file mode 100644
index 0000000..e04dc57
--- /dev/null
+++ b/unu-data.1
@@ -0,0 +1,55 @@
+.TH UNU\-DATA "1" "May 2021" "" "User Commands"
+.SH NAME
+unu\-data \- print data segment of a \fInrrd\fR file
+.SH SYNOPSIS
+.B unu\ data
+.RI < nin >
+.SH DESCRIPTION
+Print data segment of a
+.I nrrd
+file.
+The value of this is to pass the data segment in isolation to a stand-alone
+decoder, in case this Teem build lacks an optional data encoding required for a
+given nrrd file.
+.SS CAVEATS
+Will start copying characters from the datafile until EOF is hit, so this
+won\(cqt work correctly if the datafile has extraneous content at the end.
+Will skip lines (as per
+.RB \(lq line\ skip: \(rq
+header field) if needed, but can only skip bytes (as per
+.RB \(lq byte\ skip: \(rq
+if the encoding is NOT a compression.
+.IP \(bu +2n
+Uses
+.B nrrdLoad
+with
+.B nio->skipData
+and
+.B nio->keepNrrdDataFileOpen
+both
+.B true
+in the
+.I NrrdIoState
+.BR nio .
+.SH OPTIONS
+.TP
+.RI < nin >
+input
+.I nrrd
+.RI ( string )
+.SH EXAMPLES
+To make
+.B vol.raw
+contain the uncompressed data from
+.B vol.nrrd
+which uses
+.RB \(lq gz \(rq
+encoding:
+.in +4n
+.EX
+.B unu data vol.nrrd | gunzip > vol.raw
+.EE
+.in
+.SH "SEE\ ALSO"
+.BR unu (1),
+.BR unu\-head (1)
diff --git a/unu-dhisto.1 b/unu-dhisto.1
new file mode 100644
index 0000000..90057d3
--- /dev/null
+++ b/unu-dhisto.1
@@ -0,0 +1,55 @@
+.TH UNU\-DHISTO "1" "August 2021" "" "User Commands"
+.SH NAME
+unu\-dhisto \- create image of 1\-D value histogram
+.SH SYNOPSIS
+.B unu\ dhisto
+.BR \-h , \-\-height
+.RI < height >
+.RB [ \-nolog ]
+.RB [ \-notick ]
+.RB [ \-max , \-\-maximum
+.RI < max\ #\ hits >]
+.RB [ \-i , \-\-input
+.RI < nin >]
+.RB [ \-o , \-\-output
+.RI < nout >]
+.SH DESCRIPTION
+Create image of 1\-D value histogram.
+With
+.RB \(lq \-nolog \(rq,
+this becomes a quick & dirty way of plotting a function.
+.IP \(bu +2n
+Uses
+.B nrrdHistoDraw
+.SH OPTIONS
+.TP
+.B \-h\fR\ <\fIheight\fR>\ ,\ \fB\-\-height\fR\ <\fIheight\fR>
+height of output image (horizontal size is determined by number of bins in
+input histogram).
+.RI ( unsigned\ int )
+.TP
+.B \-nolog
+do not show the log-scaled histogram with decade tick-marks
+.TP
+.B \-notick
+do not draw the log decade tick marks
+.TP
+.B \-max\fR\ <\fImax\ #\ hits\fR>\ ,\ \fB\-\-maximum\fR\ <\fImax\ #\ hits\fR>
+constrain the top of the drawn histogram to be at this number of hits.
+This will either scale the drawn histogram downward or clip its top, depending
+on whether the given max is higher or lower than the actual maximum bin count.
+By not using this option (the default), the actual maximum bin count is used
+.RI \(lq double \(rq
+.TP
+.B \-i\fR\ <\fInin\fR>\ ,\ \fB\-\-input\fR\ <\fInin\fR>
+input
+.I nrrd
+.TP
+.B \-o\fR\ <\fInout\fR>\ ,\ \fB\-\-output\fR\ <\fInout\fR>
+output
+.I nrrd
+.RI ( string );
+default:
+.RB \(lq \- \(rq
+.SH "SEE\ ALSO"
+.BR unu (1)
diff --git a/unu-dice.1 b/unu-dice.1
new file mode 100644
index 0000000..ec458f3
--- /dev/null
+++ b/unu-dice.1
@@ -0,0 +1,80 @@
+.TH UNU\-DICE "1" "August 2021" "" "User Commands"
+.SH NAME
+unu\-dice \- save all slices along one axis into separate files
+.SH SYNOPSIS
+.B unu\ dice
+.BR \-a , \-\-axis
+.RI < axis >
+.RB [ \-i , \-\-input
+.RI < nin >]
+.RB [ \-s , \-\-start
+.RI < start >]
+.RB [ \-ff , \-\-format
+.RI < form >]
+.RB [ \-l , \-\-limit
+.RI < max# >]
+.RB [ \-o , \-\-output
+.RI < above \ below >]
+.SH DESCRIPTION
+Save all slices along one axis into separate files.
+Calls
+.BR unu\-slice (1)
+for each position along the indicated axis, and saves out a different
+file for each sample along that axis.
+.IP \(bu +2n
+Uses repeated calls to
+.B nrrdSlice
+and
+.B nrrdSave
+.SH OPTIONS
+.TP
+.B \-a\fR\ <\fIaxis\fR>\ ,\ \fB\-\-axis\fR\ <\fIaxis\fR>
+axis to slice along
+.RI ( unsigned\ int )
+.TP
+.B \-i\fR\ <\fInin\fR>\ ,\ \fB\-\-input\fR\ <\fInin\fR>
+input
+.I nrrd
+.TP
+.B \-s\fR\ <\fIstart\fR>\ ,\ \fB\-\-start\fR\ <\fIstart\fR>
+integer value to start numbering with
+.RI ( unsigned\ int );
+default:
+.RB \(lq 0 \(rq
+.TP
+.B \-ff\fR\ <\fIform\fR>\ ,\ \fB\-\-format\fR\ <\fIform\fR>
+a
+.BR printf (3)-style
+format to use for generating all filenames.
+Use this to override the number of characters used to represent the slice
+position, or the file format of the output, e.g.
+.RB \(lq \-ff\ %03d.ppm \(rq
+for
+.BR 000.ppm ,
+.BR 001.ppm ,
+etc.
+By default (not using this option), slices are saved in NRRD format (or PNM or
+PNG where possible) with shortest possible filenames.
+.RI ( string )
+.TP
+.B \-l\fR\ <\fImax#\fR>\ ,\ \fB\-\-limit\fR\ <\fImax#\fR>
+a sanity check on how many slice files should be saved out, to prevent
+accidentally dicing the wrong axis or the wrong array.
+Can raise this value if needed.
+.RI ( unsigned\ int );
+default:
+.RB \(lq 9999 \(rq
+.TP
+.B \-o\fR\ <\fIprefix\fR>\ ,\ \fB\-\-output\fR\ <\fIprefix\fR>
+output filename
+.I prefix
+(excluding info set via
+.RB \(lq \-ff \(rq,
+basically to set path of output files (so be sure to end with
+.RB \(lq / \(rq).
+.RI ( string )
+.SH "SEE\ ALSO"
+.BR unu (1),
+.BR unu\-slice (1)
+.P
+.BR printf (3)
diff --git a/unu-diff.1 b/unu-diff.1
new file mode 100644
index 0000000..a8e7359
--- /dev/null
+++ b/unu-diff.1
@@ -0,0 +1,46 @@
+.TH UNU\-DIFF "1" "May 2021" "" "User Commands"
+.SH NAME
+unu\-diff \- sees if two
+.IR nrrd s
+are different in any way
+.SH SYNOPSIS
+.B unu\ diff
+.RI < ninA >
+.RI < ninB >
+.RB [ \-eps , \-\-epsilon
+.RI < eps >]
+.RB [ \-od , \-\-onlydata ]
+.SH DESCRIPTION
+Sees if two
+.IR nrrd s
+are different in any way.
+Looks through all fields to see if two given
+.IR nrrd s
+contain the same information.
+Or, array meta-data can be excluded, and comparison only on the data values is
+done with the
+.B \-od
+flag.
+.IP \(bu +2n
+Uses
+.B nrrdCompare
+.SH OPTIONS
+.TP
+.RI < ninA >
+First input
+.IR nrrd .
+.TP
+.RI < ninB >
+Second input
+.IR nrrd .
+.TP
+.B \-eps\fR\ <\fIeps\fR>\ ,\ \fB\-\-epsilon\fR\ <\fIeps\fR>
+threshold for allowable difference in values in data values
+.RI ( double );
+default:
+.RB \(lq 0.0 \(rq
+.TP
+.B \-od\fR\ ,\ \fB\-\-onlydata
+Compare data values only, excluding array meta-data
+.SH "SEE\ ALSO"
+.BR unu (1)
diff --git a/unu-dist.1 b/unu-dist.1
new file mode 100644
index 0000000..fb0a97c
--- /dev/null
+++ b/unu-dist.1
@@ -0,0 +1,71 @@
+.TH UNU\-DIST "1" "May 2021" "" "User Commands"
+.SH NAME
+unu\-dist \- Euclidean distance transform
+.SH SYNOPSIS
+.B unu\ dist
+.BR \-th , \-\-thresh
+.RI < val >
+.RB [ \-b , \-\-bias
+.RI < val >]
+.RB [ \-t , \-\-type
+.RI < type >]
+.RB [ \-sgn ]
+.RB [ \-inv ]
+.RB [ \-i , \-\-input
+.RI < nin >]
+.RB [ \-o , \-\-output
+.RI < nout >]
+.SH DESCRIPTION
+Euclidean distance transform.
+Based on \(lqDistance Transforms of Sampled Functions\(rq
+by Pedro F. Felzenszwalb and Daniel P. Huttenlocher,
+\fICornell Computing and Information Science\fR TR2004\-1963.
+This function first thresholds at the specified value and then does the
+distance transform of the resulting binary image.
+The signed distance (negative values inside object) is also available.
+Distances between non-isotropic samples are handled correctly.
+.IP \(bu +2n
+Uses
+.B nrrdDistanceL2
+or
+.B nrrdDistanceL2Signed
+.SH OPTIONS
+.TP
+.B \-th\fR\ <\fIval\fR>\ ,\ \fB\-\-thresh\fR\ <\fIval\fR>
+threshold value to separate inside from outside
+.RI ( double )
+.TP
+.B \-b\fR\ <\fIval\fR>\ ,\ \fB\-\-bias\fR\ <\fIval\fR>
+if non-zero, bias the distance transform by this amount times the difference in
+value from the threshold
+.RI ( double );
+default:
+.RB \(lq 0.0 \(rq
+.TP
+.B \-t\fR\ <\fItype\fR>\ ,\ \fB\-\-type\fR\ <\fItype\fR>
+type to save output in; default:
+.RB \(lq float \(rq
+.TP
+.B \-sgn
+also compute signed (negative) distances inside objects, instead of leaving
+them as zero
+.TP
+.B \-inv
+values
+.B below
+threshold are considered interior to object.
+By default (not using this option), values above threshold are considered
+interior.
+.TP
+.B \-i\fR\ <\fInin\fR>\ ,\ \fB\-\-input\fR\ <\fInin\fR>
+input
+.I nrrd
+.TP
+.B \-o\fR\ <\fInout\fR>\ ,\ \fB\-\-output\fR\ <\fInout\fR>
+output
+.I nrrd
+.RI ( string );
+default:
+.RB \(lq \- \(rq
+.SH "SEE\ ALSO"
+.BR unu (1)
diff --git a/unu-env.1 b/unu-env.1
new file mode 100644
index 0000000..2b3e8a6
--- /dev/null
+++ b/unu-env.1
@@ -0,0 +1,184 @@
+.TH UNU\-ENV "1" "April 2021" "" "User Commands"
+.SH NAME
+unu\-env \- list relevant environment variables and their values
+.SH SYNOPSIS
+.B unu\ env
+.SH DESCRIPTION
+List relevant environment variables and their values.
+These environment variables provide a way of setting global variables that can
+affect the way Nrrd (and
+.BR unu (1))
+operates.
+.IP \(bu +2n
+Uses
+.BR nrrdGetenvBool ,
+.BR nrrdGetenvEnum ,
+.BR nrrdGetenvInt ,
+and
+.B nrrdGetenvUInt
+.IP
+Each variable in the listing below starts with the name of the environment
+variable
+.RB (\(lq NRRD_ ...\(rq),
+what type of value it represents (e.g.\:
+.RI \(lq int \(rq,
+.RI \(lq bool \(rq),
+what the corresponding Nrrd global variable is set to, and a description of the
+variable.
+In the
+.BR unu\-env (1)
+command output, the listing also includes what the environment variable is
+currently set to.
+.IP
+.I Bool
+variables may be set to true simply by setting the environment variable;
+setting the value to
+.RI \(lq true \(rq
+or
+.RI \(lq false \(rq
+sets the
+.I bool
+accordingly.
+.I Enum
+variables may be set by setting the environment variable to any string that
+parses as one of the
+.I enum
+values.
+.I Int
+and
+.I unsigned\ int
+variables are set via a string parse-able as a numeric value.
+.IP
+In addition to the the
+.RB \(lq NRRD_ \(rq
+environment variables, there is this one,
+.BR UNRRDU_QUIET_QUIT ;
+see the
+.B ENVIRONMENT
+section below.
+.SH ENVIRONMENT
+.TP
+.B UNRRDU_QUIET_QUIT
+Determines whether
+.BR unu (1)
+exits quietly (without error and usage info) when it fails because an input
+.I nrrd
+read immediately hit
+.B EOF
+(as happens when many
+.BR unu (1)
+invocations are piped together).
+This is currently detected by seeing if the error message ends with
+.RB \(lq [nrrd]\ _nrrdRead:\ immediately\ hit\ EOF \(rq.
+.TP
+.B NRRD_STATE_KEYVALUEPAIRS_PROPAGATE \fR(\fIbool\fR)
+.RB ( nrrdStateKeyValuePairsPropagate \ ==\ true )
+.IP
+When
+.BR true ,
+key/value pairs are copied from input
+.I nrrd
+to output
+.I nrrd
+just like other basic info that hasn\(cqt just been modified (e.g. type,
+dimension, block size).
+.TP
+.B NRRD_DEFAULT_CENTER \fR(\fIcentering\ enum\fR)
+.RB ( nrrdDefaultCenter \ ==\ cell )
+.IP
+The type of sample centering to use when none has been set but one has to be
+chosen for some operation (e.g. resampling).
+.TP
+.B NRRD_DEFAULT_WRITE_ENCODING_TYPE \fR(\fIencoding\ enum\fR)
+.RB ( nrrdDefaultWriteEncodingType \ ==\ raw )
+.IP
+When writing
+.IR nrrd s,
+what encoding to use.
+Only
+.BR unu\-save (1)
+affords explicit control of output encoding.
+.TP
+.B NRRD_STATE_KIND_NOOP \fR(\fIbool\fR)
+.RB ( nrrdStateKindNoop \ ==\ false )
+.IP
+When true, Nrrd makes not even the slightest effort to be smart about setting
+the
+.RB \(lq kind \(rq
+field of an axis after some operation that modified its samples.
+.TP
+.B NRRD_STATE_VERBOSE_IO \fR(\fIint\fR)
+.RB ( nrrdStateVerboseIO \ ==\ 1 )
+.IP
+The verbosity level of Nrrd input/output operations.
+.TP
+.B NRRD_DEFAULT_WRITE_BARE_TEXT \fR(\fIbool\fR)
+.RB ( nrrdDefaultWriteBareText \ ==\ 1 )
+.IP
+When
+.BR false ,
+text files used for saving
+.IR nrrd s
+start with comment
+.RB (\(lq #\ ... \(rq)
+lines containing
+.I nrrd
+fields.
+.TP
+.B NRRD_STATE_MEASURE_TYPE \fR(\fIenum\fR)
+.RB ( nrrdStateMeasureType \ ==\ float )
+.IP
+For measurements
+.RB ( unu\-project (1))
+like sum and product, the type of the output result, when one hasn\(cqt been
+explicitly requested.
+.TP
+.B NRRD_STATE_MEASURE_MODE_BINS \fR(\fIint\fR)
+.RB ( nrrdStateMeasureModeBins \ ==\ 1024 )
+.IP
+When measuring mode but without a given histogram, how many bins to use in the
+temporary internal histogram.
+.TP
+.B NRRD_STATE_MEASURE_HISTO_TYPE \fR(\fIenum\fR)
+.RB ( nrrdStateMeasureHistoType \ ==\ float )
+.IP
+Output type for most measurements of histograms, when one hasn\(cqt been
+explicitly requested
+.TP
+.B NRRD_STATE_ALWAYS_SET_CONTENT \fR(\fIbool\fR)
+.RB ( nrrdStateAlwaysSetContent \ ==\ true )
+.IP
+If
+.BR true ,
+the output content string is set even when the input content string is not set.
+.TP
+.B NRRD_STATE_DISABLE_CONTENT \fR(\fIbool\fR)
+.RB ( nrrdStateDisableContent \ ==\ false )
+.IP
+If
+.BR true ,
+output content is never set.
+.TP
+.B NRRD_DEFAULT_WRITE_CHARS_PER_LINE \fR(\fIunsigned\ int\fR)
+.RB ( nrrdDefaultWriteCharsPerLine \ ==\ 75 )
+.IP
+When using text encoding, maximum # characters allowed per line.
+.TP
+.B NRRD_DEFAULT_WRITE_VALS_PER_LINE \fR(\fIunsigned\ int\fR)
+.RB ( nrrdDefaultWriteValsPerLine \ ==\ 8 )
+.IP
+When using text encoding, maximum # values allowed per line
+.TP
+.B NRRD_STATE_GRAYSCALE_IMAGE_3D \fR(\fIbool\fR)
+.RB ( nrrdStateGrayscaleImage3D \ ==\ false )
+.IP
+If
+.BR true ,
+reading a 2-D grayscale image results in a 3-D image with a single
+sample
+.RB ( size = 1 )
+on the first (fastest) axis.
+.SH "SEE\ ALSO"
+.BR unu (1),
+.BR unu\-project (1),
+.BR unu\-save (1)
diff --git a/unu-fft.1 b/unu-fft.1
new file mode 100644
index 0000000..3be7157
--- /dev/null
+++ b/unu-fft.1
@@ -0,0 +1,107 @@
+.TH UNU\-FFT "1" "May 2021" "" "User Commands"
+.SH NAME
+unu\-fft \- Fast Fourier Transform of selected axes
+.SH SYNOPSIS
+.B unu\ fft
+.RI < dir >
+.BR \-a , \-\-axes
+.RI < ax0 \ ...>
+.RB [ \-pr , \-\-planrigor
+.RI < pr >]
+.RB [ \-r , \-\-rescale
+.RI < bool >]
+.RB [ \-w , \-\-wisdom
+.RI < filename >]
+.RB [ \-i , \-\-input
+.RI < nin >]
+.RB [ \-ri , \-\-realinput ]
+.RB [ \-o , \-\-output
+.RI < nout >]
+.SH DESCRIPTION
+Fast Fourier Transform of selected axes.
+Initial attempt at wrapping the FFTW3 library; options are likely to change in
+Teem 2.0.
+.IP \(bu +2n
+Uses
+.B nrrdFFT
+.SH OPTIONS
+.TP
+.RI < dir >
+forward
+.RB (\(lq forw \(rq,
+.RB \(lq f \(rq)
+or backward/inverse ("back", "b")
+.RB (\(lq back \(rq,
+.RB \(lq b \(rq)
+transform
+.TP
+.B \-a\fR\ <\fIax0\fR\ ...>\ ,\ \fB\-\-axes\fR\ <\fIax0\fR\ ...>
+the one or more axes that should be transformed (1 or more
+.IR unsigned\ int s)
+.TP
+.B \-pr\fR\ <\fIpr\fR>\ ,\ \fB\-\-planrigor\fR\ <\fIpr\fR>
+rigor with which fftw plan is constructed.
+Options include:
+.RS +7n
+.IP \(bu 2
+.RB \(lq e \(rq,
+.RB \(lq est \(rq,
+.RB \(lq estimate \(rq:
+only an estimate
+.IP \(bu 2
+.RB \(lq m \(rq,
+.RB \(lq meas \(rq,
+.RB \(lq measure \(rq:
+standard amount of measurements of system properties
+.IP \(bu 2
+.RB \(lq p \(rq,
+.RB \(lq pat \(rq,
+.RB \(lq patient \(rq:
+slower, more measurements
+.IP \(bu 2
+.RB \(lq x \(rq,
+.RB \(lq ex \(rq,
+.RB \(lq exhaustive \(rq:
+slowest, most measurements
+.P
+default:
+.RB \(lq est \(rq
+.RE
+.TP
+.B \-r\fR\ <\fIbool\fR>\ ,\ \fB\-\-rescale\fR\ <\fIbool\fR>
+scale fftw output (by sqrt(1/N)) so that forward and backward transforms will
+get back to original values
+.RI ( bool );
+default:
+.RB \(lq true \(rq
+.TP
+.B \-w\fR\ <\fIfilename\fR>\ ,\ \fB\-\-wisdom\fR\ <\fIfilename\fR>
+A filename here is used to read in fftw wisdom (if the file exists already),
+and is used to save out updated wisdom after the transform.
+By default (not using this option), no wisdom is read or saved.
+.IR Note :
+no wisdom is gained (that is, learned by FFTW) with planning rigor
+.RB \(lq estimate \(rq.
+.RI ( string )
+.TP
+.B \-i\fR\ <\fInin\fR>\ ,\ \fB\-\-input\fR\ <\fInin\fR>
+input
+.I nrrd
+.TP
+.B \-ri\fR\ ,\ \fB\-\-realinput
+input is real-valued, so insert new length-2 axis
+.B 0
+and set complex component to
+.BR 0.0 .
+Axes to transform (indicated by
+.RB \(lq \-a \(rq
+will be incremented accordingly.
+.TP
+.B \-o\fR\ <\fInout\fR>\ ,\ \fB\-\-output\fR\ <\fInout\fR>
+output
+.I nrrd
+.RI ( string );
+default:
+.RB \(lq \- \(rq
+.SH "SEE\ ALSO"
+.BR unu (1)
diff --git a/unu-flip.1 b/unu-flip.1
new file mode 100644
index 0000000..63c86d5
--- /dev/null
+++ b/unu-flip.1
@@ -0,0 +1,37 @@
+.TH UNU\-FLIP "1" "August 2021" "" "User Commands"
+.SH NAME
+unu\-flip \- reverse order of slices along one axis
+.SH SYNOPSIS
+.B unu\ flip
+.BR \-a , \-\-axis
+.RI < axis >
+.RB [ \-i , \-\-input
+.RI < nin >]
+.RB [ \-o , \-\-output
+.RI < nout >]
+.SH DESCRIPTION
+Reverse order of slices along one axis.
+Special case of
+.BR unu\-shuffle (1).
+.IP \(bu +2n
+Uses
+.B nrrdFlip
+.SH OPTIONS
+.TP
+.B \-a\fR\ <\fIaxis\fR>\ ,\ \fB\-\-axis\fR\ <\fIaxis\fR>
+axis to flip along
+.RI ( unsigned\ int )
+.TP
+.B \-i\fR\ <\fInin\fR>\ ,\ \fB\-\-input\fR\ <\fInin\fR>
+input
+.I nrrd
+.TP
+.B \-o\fR\ <\fInout\fR>\ ,\ \fB\-\-output\fR\ <\fInout\fR>
+output
+.I nrrd
+.RI ( string );
+default:
+.RB \(lq \- \(rq
+.SH "SEE\ ALSO"
+.BR unu (1),
+.BR unu\-shuffle (1)
diff --git a/unu-gamma.1 b/unu-gamma.1
new file mode 100644
index 0000000..a536802
--- /dev/null
+++ b/unu-gamma.1
@@ -0,0 +1,81 @@
+.TH UNU\-GAMMA "1" "August 2021" "" "User Commands"
+.SH NAME
+unu\-gamma \- brighten or darken values with a gamma
+.SH SYNOPSIS
+.B unu\ gamma
+.BR \-g , \-\-gamma
+.RI < gamma >
+.RB [ \-min , \-\-minimum
+.RI < value >]
+.RB [ \-max , \-\-maximum
+.RI < value >]
+.RB [ \-blind8
+.RI < bool >]
+.RB [ \-i , \-\-input
+.RI < nin >]
+.RB [ \-o , \-\-output
+.RI < nout >]
+.SH DESCRIPTION
+Brighten or darken values with a gamma.
+Just as in
+.BR xv ,
+the gamma value here is actually the reciprocal of the exponent actually used
+to transform the values.
+.IP \(bu +2n
+Uses
+.B nrrdArithGamma
+.SH OPTIONS
+.TP
+.B \-g\fR\ <\fIgamma\fR>\ ,\ \fB\-\-gamma\fR\ <\fIgamma\fR>
+.B 1.0
+brightens; gamma <
+.B 1.0
+darkens.
+Negative gammas invert values (like in
+.BR xv ).
+.RI ( double )
+.TP
+.B \-min\fR\ [<\fIvalue\fR>]\ ,\ \fB\-\-minimum\fR\ [<\fIvalue\fR>]
+Value to implicitly map to
+.B 0.0
+prior to calling
+.BR pow (3).
+Defaults to lowest value found in input
+.IR nrrd .
+.RI ( double )
+.TP
+.B \-max\fR\ [<\fIvalue\fR>]\ ,\ \fB\-\-maximum\fR\ [<\fIvalue\fR>]
+Value to implicitly map to
+.B 1.0
+prior to calling
+.BR pow (3).
+Defaults to highest value found in input
+.IR nrrd .
+.RI ( double )
+.TP
+.B \-blind8\fR\ <\fIbool\fR>
+Whether to know the range of 8-bit data blindly
+.RI ( uchar
+is always
+.BR [0,255] ,
+.I signed\ char
+is
+.BR [-128,127] ).
+.RI ( bool );
+default:
+.RB \(lq true \(rq
+.TP
+.B \-i\fR\ <\fInin\fR>\ ,\ \fB\-\-input\fR\ <\fInin\fR>
+input
+.I nrrd
+.TP
+.B \-o\fR\ <\fInout\fR>\ ,\ \fB\-\-output\fR\ <\fInout\fR>
+output
+.I nrrd
+.RI ( string );
+default:
+.RB \(lq \- \(rq
+.SH "SEE\ ALSO"
+.BR unu (1)
+.P
+http://www.trilon.com/xv/
diff --git a/unu-head.1 b/unu-head.1
new file mode 100644
index 0000000..0a3e691
--- /dev/null
+++ b/unu-head.1
@@ -0,0 +1,34 @@
+.TH UNU\-HEAD "1" "May 2021" "" "User Commands"
+.SH NAME
+unu\-head \- print header of one or more \fInrrd\fR files
+.SH SYNOPSIS
+.B unu\ head
+.RI < nin1 \ ...>
+.SH DESCRIPTION
+Print header of one or more
+.I nrrd
+files.
+The value of this is simply to print the contents of
+.I nrrd
+headers.
+This avoids the use of
+.RB \(lq head\ \-\fIN \(rq,
+where
+.I N
+has to be determined manually, which always risks printing raw binary data
+(following the header) to screen, which tends to clobber terminal settings,
+make pointless beeps, and be annoying.
+.IP \(bu +2n
+Uses
+.B nrrdOneLine
+.SH OPTIONS
+.TP
+.RI < nin1 \ ...>
+input
+.IR nrrd (s)
+(1 or more
+.IR string s)
+.SH "SEE\ ALSO"
+.BR unu (1),
+.BR unu\-data (1),
+.BR head (1)
diff --git a/unu-heq.1 b/unu-heq.1
new file mode 100644
index 0000000..2d7a955
--- /dev/null
+++ b/unu-heq.1
@@ -0,0 +1,82 @@
+.TH UNU\-HEQ "1" "August 2021" "" "User Commands"
+.SH NAME
+unu\-heq \- perform histogram equalization
+.SH SYNOPSIS
+.B unu\ heq
+.BR \-b , \-\-bin
+.RI < bins >
+.RB [ \-s , \-\-smart
+.RI [< bins >]]
+.RB [ \-a , \-\-amount
+.RI < amount >]
+.RB [ \-m , \-\-map
+.RI < filename >]
+.RB [ \-i , \-\-input
+.RI < nin >]
+.RB [ \-o , \-\-output
+.RI < nout >]
+.SH DESCRIPTION
+Perform histogram equalization.
+If this seems to be doing nothing, try increasing the number of histograms bins
+by an order of magnitude or two (or more).
+Or, use
+.BR unu\-gamma (1)
+to warp the values in the direction you know they need to go.
+Either of these might work because extremely tall and narrow peaks in the
+equalization histogram will produce poor results.
+.IP \(bu +2n
+Uses
+.B nrrdHistoEq
+.SH OPTIONS
+.TP
+.B \-b\fR\ <\fIbins\fR>\ ,\ \fB\-\-bin\fR\ <\fIbins\fR>
+# bins to use in histogram that is created in order to calculate the mapping
+that achieves the equalization.
+.RI ( int )
+.TP
+.B \-s\fR\ [<\fIbins\fR>]\ ,\ \fB\-\-smart\fR\ [<\fIbins\fR>]
+# bins in value histogram to ignore in calculating the mapping.
+Bins are ignored when they get more hits than other bins, and when the values
+that fall in them are constant.
+This is an effective way to prevent large regions of background value from
+distorting the equalization mapping.
+(optional
+.IR int );
+default:
+.RB \(lq 0 \(rq
+.TP
+.B \-a\fR\ <\fIamount\fR>\ ,\ \fB\-\-amount\fR\ <\fIamount\fR>
+extent to which the histogram equalizing mapping should be applied;
+.BR 0.0 :
+no change,
+.BR 1.0 :
+full equalization
+.RI ( float );
+default:
+.B \-t\fR\ <\fItype\fR>\ ,\ \fB\-\-type\fR\ <\fItype\fR>
+output type; default:
+.RB \(lq 1.0 \(rq
+.TP
+.B \-m\fR\ <\fIfilename\fR>\ ,\ \fB\-\-map\fR\ <\fIfilename\fR>
+The value mapping used to achieve histogram equalization is represented by a
+univariate regular map.
+By giving a filename here, that map can be saved out and applied to other
+.IR nrrd s
+with
+.BR unu\-rmap (1)
+.RI ( string )
+.TP
+.B \-i\fR\ <\fInin\fR>\ ,\ \fB\-\-input\fR\ <\fInin\fR>
+input
+.I nrrd
+.TP
+.B \-o\fR\ <\fInout\fR>\ ,\ \fB\-\-output\fR\ <\fInout\fR>
+output
+.I nrrd
+.RI ( string );
+default:
+.RB \(lq \- \(rq
+.SH "SEE\ ALSO"
+.BR unu (1),
+.BR unu\-gamma (1),
+.BR unu\-rmap (1)
diff --git a/unu-histax.1 b/unu-histax.1
new file mode 100644
index 0000000..4c76380
--- /dev/null
+++ b/unu-histax.1
@@ -0,0 +1,94 @@
+.TH UNU\-HISTAX "1" "August 2021" "" "User Commands"
+.SH NAME
+unu\-histax \- replace each scanline along an axis with its histogram
+.SH SYNOPSIS
+.B unu\ histax
+.BR \-a , \-\-axis
+.RI < axis >
+.BR \-b , \-\-bin
+.RI < bins >
+.RB [ \-t , \-\-type
+.RI < type >]
+.RB [ \-min , \-\-minimum
+.RI < value >]
+.RB [ \-max , \-\-maximum
+.RI < value >]
+.RB [ \-blind8
+.RI < bool >]
+.RB [ \-i , \-\-input
+.RI < nin >]
+.RB [ \-o , \-\-output
+.RI < nout >]
+.SH DESCRIPTION
+Replace each scanline along an axis with its histogram.
+.IP \(bu +2n
+Uses
+.B nrrdHistoAxis
+.SH OPTIONS
+.TP
+.B \-a\fR\ <\fIaxis\fR>\ ,\ \fB\-\-axis\fR\ <\fIaxis\fR>
+axis to histogram along
+.RI ( unsigned\ int )
+.TP
+.B \-b\fR\ <\fIbins\fR>\ ,\ \fB\-\-bin\fR\ <\fIbins\fR>
+# of bins in histogram
+.RI ( unsigned\ int )
+.B \-t\fR\ <\fItype\fR>\ ,\ \fB\-\-type\fR\ <\fItype\fR>
+output type; default:
+.RB \(lq uint \(rq
+.TP
+.B \-min\fR\ <\fIvalue\fR>\ ,\ \fB\-\-minimum\fR\ <\fIvalue\fR>
+The value to map to zero, given explicitly as a regular number,
+.BR or ,
+if the number is given with a
+.RB \(lq % \(rq
+suffix, this minimum is specified in terms of the percentage of samples in
+input that are lower.
+.RB \(lq 0% \(rq
+means the lowest input value is used,
+.RB \(lq 1% \(rq
+means that the 1% of the lowest values are all mapped to zero.
+By default (not using this option), the lowest input value is used.
+.RI ( string );
+default:
+.RB \(lq nan \(rq
+.TP
+.B \-max\fR\ <\fIvalue\fR>\ ,\ \fB\-\-maximum\fR\ <\fIvalue\fR>
+The value to map to the highest unsigned integral value, given explicitly as a
+regular number,
+.BR or ,
+if the number is given with a
+.RB \(lq % \(rq
+suffix, this maximum is specified in terms of the percentage of samples in
+input that are higher.
+.RB \(lq 0% \(rq
+means the highest input value is used, which is also the default behavior (same
+as not using this option).
+.RI ( string );
+default:
+.RB \(lq nan \(rq
+.TP
+.B \-blind8\fR\ <\fIbool\fR>
+Whether to know the range of 8-bit data blindly
+.RI ( uchar
+is always
+.BR [0,255] ,
+.I signed\ char
+is
+.BR [-128,127] ).
+.RI ( bool );
+default:
+.RB \(lq true \(rq
+.TP
+.B \-i\fR\ <\fInin\fR>\ ,\ \fB\-\-input\fR\ <\fInin\fR>
+input
+.I nrrd
+.TP
+.B \-o\fR\ <\fInout\fR>\ ,\ \fB\-\-output\fR\ <\fInout\fR>
+output
+.I nrrd
+.RI ( string );
+default:
+.RB \(lq \- \(rq
+.SH "SEE\ ALSO"
+.BR unu (1)
diff --git a/unu-histo.1 b/unu-histo.1
new file mode 100644
index 0000000..e0b34b8
--- /dev/null
+++ b/unu-histo.1
@@ -0,0 +1,98 @@
+.TH UNU\-HISTO "1" "August 2021" "" "User Commands"
+.SH NAME
+unu\-histo \- create 1\-D histogram of values in a
+.I nrrd
+.SH SYNOPSIS
+.B unu\ histo
+.BR \-b , \-\-bins
+.RI < num >
+.RB [ \-w , \-\-weight
+.RI < nweight >]
+.RB [ \-min , \-\-minimum
+.RI < value >]
+.RB [ \-max , \-\-maximum
+.RI < value >]
+.RB [ \-blind8
+.RI < bool >]
+.RB [ \-t , \-\-type
+.RI < type >]
+.RB [ \-i , \-\-input
+.RI < nin >]
+.RB [ \-o , \-\-output
+.RI < nout >]
+.SH DESCRIPTION
+Create 1\-D histogram of values in a
+.IR nrrd .
+Can explicitly set bounds of histogram domain or can learn these from the data.
+.IP \(bu +2n
+Uses
+.B nrrdHisto
+.SH OPTIONS
+.TP
+.B \-b\fR\ <\fInum\fR>\ ,\ \fB\-\-bins\fR\ <\fInum\fR>
+# of bins in histogram
+.RI ( unsigned\ int )
+.TP
+.B \-w\fR\ <\fInweight\fR>\ ,\ \fB\-\-weight\fR\ <\fInweight\fR>
+how to weigh contributions to histogram.
+By default (not using this option), the increment is one bin count per sample,
+but by giving a
+.IR nrrd ,
+the value in the
+.I nrrd
+at the corresponding location will be the bin count increment
+.TP
+.B \-min\fR\ <\fIvalue\fR>\ ,\ \fB\-\-minimum\fR\ <\fIvalue\fR>
+Value at low end of histogram, given explicitly as a regular number,
+.BR or ,
+if the number is given with a
+.RB \(lq % \(rq
+suffix, this minimum is specified in terms of the percentage of samples in
+input that are lower.
+By default (not using this option), the lowest value found in input
+.IR nrrd .
+.RI ( string );
+default:
+.RB \(lq nan \(rq
+.TP
+.B \-max\fR\ <\fIvalue\fR>\ ,\ \fB\-\-maximum\fR\ <\fIvalue\fR>
+Value at high end of histogram, given explicitly as a regular number,
+.BR or ,
+if the number is given with a
+.RB \(lq % \(rq
+suffix, this maximum is specified in terms of the percentage of samples in
+input that are higher.
+Defaults to highest value found in input
+.IR nrrd .
+.RI ( string );
+default:
+.RB \(lq nan \(rq
+.TP
+.B \-blind8\fR\ <\fIbool\fR>
+Whether to know the range of 8-bit data blindly
+.RI ( uchar
+is always
+.BR [0,255] ,
+.I signed\ char
+is
+.BR [-128,127] ).
+.RI ( bool );
+default:
+.RB \(lq true \(rq
+.TP
+.B \-t\fR\ <\fItype\fR>\ ,\ \fB\-\-type\fR\ <\fItype\fR>
+type to use for bins in output histogram; default:
+.RB \(lq uint \(rq
+.TP
+.B \-i\fR\ <\fInin\fR>\ ,\ \fB\-\-input\fR\ <\fInin\fR>
+input
+.I nrrd
+.TP
+.B \-o\fR\ <\fInout\fR>\ ,\ \fB\-\-output\fR\ <\fInout\fR>
+output
+.I nrrd
+.RI ( string );
+default:
+.RB \(lq \- \(rq
+.SH "SEE\ ALSO"
+.BR unu (1)
diff --git a/unu-i2w.1 b/unu-i2w.1
new file mode 100644
index 0000000..3b29e31
--- /dev/null
+++ b/unu-i2w.1
@@ -0,0 +1,58 @@
+.TH UNU\-I2W "1" "April 2021" "" "User Commands"
+.SH NAME
+unu\-i2w \- converts from 1-D index to world position
+.SH SYNOPSIS
+.B unu\ i2w
+.RI < center >
+.RI < minPos >
+.RI < maxPos >
+.RI < num >
+.RI < index >
+.SH DESCRIPTION
+Converts from 1-D index to world position, given the centering of the data
+.RB ( cell
+vs.
+.BR node ),
+the range of positions, and the number of intervals into which position has
+been quantized.
+This is a demo/utility, which does not actually operate on any
+.IR nrrd s.
+Previously available as the stand-alone
+.B idx2pos
+binary.
+.P
+Uses
+.B NRRD_POS
+macro
+.SH OPTIONS
+.TP
+.RI < center >
+which centering applies to the quantized position.
+Possibilities are:
+.RS +7n
+.IP \(bu 2
+.RB \(lq cell \(rq
+for histogram bins, quantized values, and pixels-as-squares
+.IP \(bu 2
+.RB \(lq node \(rq
+for non-trivially interpolated sample points
+.RE
+.TP
+.RI < minPos >
+smallest position associated with index
+.B 0
+.RI ( double )
+.TP
+.RI < maxPos >
+highest position associated with highest index
+.RI ( double )
+.TP
+.RI < num >
+number of intervals into which position has been quantized
+.RI ( double )
+.TP
+.RI < index >
+the input index position, to be converted to world
+.RI ( double )
+.SH "SEE\ ALSO"
+.BR unu (1)
diff --git a/unu-imap.1 b/unu-imap.1
new file mode 100644
index 0000000..c79205f
--- /dev/null
+++ b/unu-imap.1
@@ -0,0 +1,118 @@
+.TH UNU\-IMAP "1" "August 2021" "" "User Commands"
+.SH NAME
+unu\-imap \- map
+.I nrrd
+through
+.B irregular
+univariate map (\(lqcolormap\(rq)
+.SH SYNOPSIS
+.B unu\ rmap
+.BR \-m , \-\-map
+.RI < map >
+.RB [ \-l , \-\-length
+.RI < aclLen >]
+.RB [ \-r , \-\-rescale ]
+.RB [ \-min , \-\-minimum
+.RI < value >]
+.RB [ \-max , \-\-maximum
+.RI < value >]
+.RB [ \-blind8
+.RI < bool >]
+.RB [ \-t , \-\-type
+.RI < type >]
+.RB [ \-i , \-\-input
+.RI < nin >]
+.RB [ \-o , \-\-output
+.RI < nout >]
+.SH DESCRIPTION
+Map
+.I nrrd
+through
+.B irregular
+univariate map (\(lqcolormap\(rq).
+A map is irregular if the control points are not evenly spaced along the
+domain, and hence their position must be explicitly represented in the map.
+As
+.IR nrrd s,
+these maps are necessarily 2D.
+Along axis
+.BR 0 ,
+the first value is the location of the control point, and the remaining values
+give are the range of the map for that control point.
+The output value(s) is the result of linearly interpolating between value(s)
+from the map.
+.IP \(bu +2n
+Uses
+.B nrrdApply1DIrregMap
+.SH OPTIONS
+.TP
+.B \-m\fR\ <\fImap\fR>\ ,\ \fB\-\-map\fR\ <\fImap\fR>
+irregular map to map input
+.I nrrd
+through
+.TP
+.B \-l\fR\ <\fIaclLen\fR>\ ,\ \fB\-\-length\fR\ <\fIaclLen\fR>
+length of accelerator array, used to try to speed-up task of finding between
+which pair of control points a given value lies.
+Not terribly useful for small maps (about 10 points or less).
+Use
+.B 0
+to turn accelorator off.
+.RI ( unsigned\ int );
+default:
+.RB \(lq 0 \(rq
+.TP
+.B \-r\fR ,\ \fB\-\-rescale
+rescale the input values from the input range to the map domain.
+.TP
+.B \-min\fR\ <\fIvalue\fR>\ ,\ \fB\-\-minimum\fR\ <\fIvalue\fR>
+Low end of input range.
+Defaults to lowest value found in input
+.IR nrrd .
+Explicitly setting this is useful only with rescaling
+.RB (\(lq \-r \(rq)
+.RI ( double )
+.TP
+.B \-max\fR\ <\fIvalue\fR>\ ,\ \fB\-\-maximum\fR\ <\fIvalue\fR>
+High end of input range.
+Defaults to highest value found in input
+.IR nrrd .
+Explicitly setting this is useful only with rescaling
+.RB (\(lq \-r \(rq)
+.RI ( double )
+.TP
+.B \-blind8\fR\ <\fIbool\fR>
+Whether to know the range of 8-bit data blindly
+.RI ( uchar
+is always
+.BR [0,255] ,
+.I signed\ char
+is
+.BR [-128,127] ).
+Explicitly setting this is useful only with rescaling
+.RB (\(lq \-r \(rq)
+.RI ( bool );
+default:
+.RB \(lq true \(rq
+.TP
+.B \-t\fR\ <\fItype\fR>\ ,\ \fB\-\-type\fR\ <\fItype\fR>
+specify the type
+.RB (\(lq int \(rq,
+.RB \(lq float \(rq,
+etc.) of the output
+.IR nrrd .
+By default (not using this option), the output type is the map\(cqs type.
+.TP
+.B \-i\fR\ <\fInin\fR>\ ,\ \fB\-\-input\fR\ <\fInin\fR>
+input
+.I nrrd
+.TP
+.B \-o\fR\ <\fInout\fR>\ ,\ \fB\-\-output\fR\ <\fInout\fR>
+output
+.I nrrd
+.RI ( string );
+default:
+.RB \(lq \- \(rq
+.SH "SEE\ ALSO"
+.BR unu (1),
+.BR unu\-rmap (1)
diff --git a/unu-inset.1 b/unu-inset.1
new file mode 100644
index 0000000..38059f7
--- /dev/null
+++ b/unu-inset.1
@@ -0,0 +1,61 @@
+.TH UNU\-INSET "1" "August 2021" "" "User Commands"
+.SH NAME
+unu\-inset \- replace a sub-region with a different
+.I nrrd
+.SH SYNOPSIS
+.B unu\ inset
+.BR \-min , \-\-minimum
+.RI < pos0 \ ...>
+.BR \-s , \-\-subset
+.RI < nsub >
+.RB [ \-i , \-\-input
+.RI < nin >]
+.RB [ \-o , \-\-output
+.RI < nout >]
+.SH DESCRIPTION
+Replace a sub-region with a different
+.IR nrrd .
+This is functionally the opposite of
+.RB \(lq crop \(rq.
+.IP \(bu +2n
+Uses
+.B nrrdInset
+.SH OPTIONS
+.TP
+.B \-min\fR\ [<\fIpos0\fR\ ...>]\ ,\ \fB\-\-minimum\fR\ [<\fIpos0\fR\ ...>]
+coordinates of where to locate sub-volume within input
+.IR nrrd .
+.RS +7n
+.IP \(bu 2
+.RI < int >
+gives 0\-based index
+.IP \(bu 2
+.BR M ,
+.BR M\+ <\fIint\fR>,
+.BR M\- <\fIint\fR>
+give index relative to the last sample on the axis
+.RB ( M== \fI#samples \-1 ).
+.RE
+.IP
+(1 or more
+.IR position s)
+.TP
+.B \-s\fR\ <\fInsub\fR>\ ,\ \fB\-\-subset\fR\ <\fInsub\fR>
+sub-region
+.IR nrrd .
+This is the data to be inset in
+.I nin
+.TP
+.B \-i\fR\ <\fInin\fR>\ ,\ \fB\-\-input\fR\ <\fInin\fR>
+input
+.I nrrd
+.TP
+.B \-o\fR\ <\fInout\fR>\ ,\ \fB\-\-output\fR\ <\fInout\fR>
+output
+.I nrrd
+.RI ( string );
+default:
+.RB \(lq \- \(rq
+.SH "SEE\ ALSO"
+.BR unu (1),
+.BR unu\-crop (1)
diff --git a/unu-jhisto.1 b/unu-jhisto.1
new file mode 100644
index 0000000..861e581
--- /dev/null
+++ b/unu-jhisto.1
@@ -0,0 +1,99 @@
+.TH UNU\-JHISTO "1" "August 2021" "" "User Commands"
+.SH NAME
+unu\-jhisto \- create joint histogram of two or more
+.IR nrrd s
+.SH SYNOPSIS
+.B unu\ jhisto
+.BR \-b , \-\-bin
+.RI < bins0 \ bins1 \ ...>
+.RB [ \-w , \-\-weight
+.RI < nweight >]
+.RB [ \-min , \-\-minimum
+.RI < min0 \ min1 \ ...>]
+.RB [ \-max , \-\-maximum
+.RI < max0 \ max1 \ ...>]
+.RB [ \-t , \-\-type
+.RI < type >]
+.RB [ \-i , \-\-input
+.RI < nin0 \ nin1 \ ...>]
+.RB [ \-a , \-\-axis
+.RI < axis >]
+.RB [ \-o , \-\-output
+.RI < nout >]
+.SH DESCRIPTION
+Create joint histogram of two or more
+.IR nrrd s.
+Each axis of the output corresponds to one of the input
+.IR nrrd s,
+and each bin in the output records the number of corresponding positions in the
+inputs with a combination of values represented by the coordinates of the bin.
+.IP \(bu +2n
+Uses
+.B nrrdHistoJoint
+.SH OPTIONS
+.TP
+.B \-b\fR\ <\fIbins0\fR\ \fIbins1\fR\ ...>\ ,\ \fB\-\-bin\fR\ <\fIbins0\fR\ \fIbins1\fR\ ...>
+.IR bins < i >
+is the number of bins to use along axis
+.I i
+(of joint histogram), which
+ represents the values of
+.IR nin < i >
+(2 or more
+.IR size_t s)
+.TP
+.B \-w\fR\ <\fInweight\fR>\ ,\ \fB\-\-weight\fR\ <\fInweight\fR>
+how to weigh contributions to joint histogram.
+By default (not using this option), the increment is one bin count per sample,
+but by giving a
+.IR nrrd ,
+the value in the
+.I nrrd
+at the corresponding location will be the bin count increment
+.TP
+.B \-min\fR\ <\fImin0\fR\ min1\fR\ ...>\ ,\ \fB\-\-minimum\fR\ <\fImin0\fR\ min1\fR\ ...>
+.IR min < i >
+is the low range of values to be quantized along axis
+.IR i ;
+use
+.RB \(lq nan \(rq
+to represent lowest value present
+(2 or more
+.IR double s)
+.TP
+.B \-max\fR\ <\fImax0\fR\ max1\fR\ ...>\ ,\ \fB\-\-maximum\fR\ <\fImax0\fR\ max1\fR\ ...>
+.IR max < i >
+is the high range of values to be quantized along axis
+.IR i ;
+use
+.RB \(lq nan \(rq
+to represent highest value present
+(2 or more
+.IR double s)
+.TP
+.B \-t\fR\ <\fItype\fR>\ ,\ \fB\-\-type\fR\ <\fItype\fR>
+type to use for output (the type used to store hit counts in the joint
+histogram).
+Clamping is done on hit counts so that they never overflow a fixed-point type;
+default:
+.RB \(lq uint \(rq
+.TP
+.B \-i\fR\ <\fInin0\fR\ [\fInin1\fR]\ ...>\ ,\ \fB\-\-input\fR\ <\fInin0\fR\ [\fInin1\fR]\ ...>
+list of
+.IR nrrd s
+(one for each axis of joint histogram), or, single
+.I nrrd
+that will be sliced along specified axis.
+(1 or more
+.IR nrrd s);
+default:
+.RB \(lq \- \(rq
+.TP
+.B \-o\fR\ <\fInout\fR>\ ,\ \fB\-\-output\fR\ <\fInout\fR>
+output
+.I nrrd
+.RI ( string );
+default:
+.RB \(lq \- \(rq
+.SH "SEE\ ALSO"
+.BR unu (1)
diff --git a/unu-join.1 b/unu-join.1
new file mode 100644
index 0000000..011a0ae
--- /dev/null
+++ b/unu-join.1
@@ -0,0 +1,89 @@
+.TH UNU\-JOIN "1" "August 2021" "" "User Commands"
+.SH NAME
+unu\-join \- connect slices and/or slabs into a bigger
+.I nrrd
+.SH SYNOPSIS
+.B unu\ join
+.RI [ @file \ ...]
+.BR \-i , \-\-input
+.RI < nin0 \ ...>
+.BR \-a , \-\-axis
+.RI < axis >
+.RB [ \-incr ]
+.RB [ \-l , \-\-label
+.RI < label >]
+.RB [ \-mm , \-\-minmax
+.RI < min \ max >]
+.RB [ \-sp , \-\-spacing
+.RI < spc >]
+.RB [ \-o , \-\-output
+.RI < nout >]
+.SH DESCRIPTION
+Connect slices and/or slabs into a bigger
+.IR nrrd .
+Can stich images into volumes, or tile images side by side, or attach images
+onto volumes.
+If
+there are many many files to name in the
+.RB \(lq \-i \(rq
+option, and using wildcards won\(cqt work, consider putting the list of
+filenames into a separate text file (e.g.
+.RB \(lq slices.txt \(rq),
+and then name this file as a response file (e.g.
+.RB \(lq \-i\ s(a)lices.txt \(rq).
+This command now allows you to set the same pieces of
+information that previously had to be set with
+.BR unu\-axinfo (1):
+label, spacing, and min/max.
+These can be use whether the join axis is new (because of
+.RB \(lq \-incr \(rq)
+or not.
+.IP \(bu +2n
+Uses
+.B nrrdJoin
+.SH OPTIONS
+.TP
+.IR @file \ ...
+response file(s) containing command-line arguments
+.TP
+.B \-i\fR\ <\fInin0\fR\ ...>\ ,\ \fB\-\-input\fR\ <\fInin0\fR\ ...>
+everything to be joined together (1 or more
+.IR nrrd s)
+.TP
+.B \-a\fR\ <\fIaxis\fR>\ ,\ \fB\-\-axis\fR\ <\fIaxis\fR>
+axis to join along
+.RI ( unsigned\ int )
+.TP
+.B \-incr
+in situations where the join axis is
+.B not
+among the existing axes of the input
+.IR nrrd s,
+then this flag signifies that the join axis should be
+.BR inserted ,
+and the output dimension should be one greater than input dimension.
+Without this flag, the
+.IR nrrd s
+are joined side-by-side, along an existing axis.
+.TP
+.B \-l\fR\ <\fIlabel\fR>\ ,\ \fB\-\-label\fR\ <\fIlabel\fR>
+label to associate with join axis
+.RI ( string )
+.TP
+.B \-mm\fR\ <\fImin\fR\ \fImax\fR>\ ,\ \fB\-\-minmax\fR\ <\fImin\fR\ \fImax\fR>
+min and max values along join axis (2
+.IR double s)
+.TP
+.B \-sp\fR\ <\fIspc\fR>\ ,\ \fB\-\-spacing\fR\ <\fIspc\fR>
+spacing between samples along join axis
+.RI ( double )
+.TP
+.B \-o\fR\ <\fInout\fR>\ ,\ \fB\-\-output\fR\ <\fInout\fR>
+output
+.I nrrd
+.RI ( string );
+default:
+.RB \(lq \- (rq
+.SH "SEE\ ALSO"
+.BR unu (1),
+.BR unu\-axinfo (1)
diff --git a/unu-lut.1 b/unu-lut.1
new file mode 100644
index 0000000..1812278
--- /dev/null
+++ b/unu-lut.1
@@ -0,0 +1,104 @@
+.TH UNU\-LUT "1" "August 2021" "" "User Commands"
+.SH NAME
+unu\-lut \- map
+.I nrrd
+through one univariate lookup table
+.SH SYNOPSIS
+.B unu\ lut
+.BR \-m , \-\-map
+.RI < lut >
+.RB [ \-r , \-\-rescale ]
+.RB [ \-min , \-\-minimum
+.RI < value >]
+.RB [ \-max , \-\-maximum
+.RI < value >]
+.RB [ \-blind8
+.RI < bool >]
+.RB [ \-t , \-\-type
+.RI < type >]
+.RB [ \-i , \-\-input
+.RI < nin >]
+.RB [ \-o , \-\-output
+.RI < nout >]
+.SH DESCRIPTION
+Map
+.I nrrd
+through one univariate lookup table (itself represented as a
+.IR nrrd ).
+The lookup table can be 1D, in which case the output has the same dimension as
+the input, or 2D, in which case the output has one more dimension than the
+input, and each value is mapped to a scanline (along axis
+.BR 0 )
+from the lookup table.
+.IP \(bu +2n
+Uses
+.B nrrdApply1DLut
+.SH OPTIONS
+.TP
+.B \-m\fR\ <\fIlut\fR>\ ,\ \fB\-\-map\fR\ <\fIlut\fR>
+lookup table to map input
+.I nrrd
+through
+.TP
+.B \-r\fR ,\ \fB\-\-rescale
+rescale the input values from the input range to the lut domain.
+The lut domain is either explicitly defined by the axis
+.IR min , max
+along axis
+.B 0
+or
+.BR 1 ,
+or, it is implicitly defined as zero to the length of that axis minus one.
+.TP
+.B \-min\fR\ <\fIvalue\fR>\ ,\ \fB\-\-minimum\fR\ <\fIvalue\fR>
+Low end of input range.
+Defaults to lowest value found in input
+.IR nrrd .
+Explicitly setting this is useful only with rescaling
+.RB (\(lq \-r \(rq)
+.RI ( double )
+.TP
+.B \-max\fR\ <\fIvalue\fR>\ ,\ \fB\-\-maximum\fR\ <\fIvalue\fR>
+High end of input range.
+Defaults to highest value found in input
+.IR nrrd .
+Explicitly setting this is useful only with rescaling
+.RB (\(lq \-r \(rq)
+.RI ( double )
+.TP
+.B \-blind8\fR\ <\fIbool\fR>
+Whether to know the range of 8-bit data blindly
+.RI ( uchar
+is always
+.BR [0,255] ,
+.I signed\ char
+is
+.BR [-128,127] ).
+Explicitly setting this is useful only with rescaling
+.RB (\(lq \-r \(rq)
+.RI ( bool );
+default:
+.RB \(lq true \(rq
+.TP
+.B \-t\fR\ <\fItype\fR>\ ,\ \fB\-\-type\fR\ <\fItype\fR>
+specify the type
+.RB (\(lq int \(rq,
+.RB \(lq float \(rq,
+etc.) of the output
+.IR nrrd .
+By default (not using this option), the output type is the lut\(cqs type.
+.TP
+.B \-i\fR\ <\fInin\fR>\ ,\ \fB\-\-input\fR\ <\fInin\fR>
+input
+.I nrrd
+.TP
+.B \-o\fR\ <\fInout\fR>\ ,\ \fB\-\-output\fR\ <\fInout\fR>
+output
+.I nrrd
+.RI ( string );
+default:
+.RB \(lq \- \(rq
+.SH "SEE\ ALSO"
+.BR unu (1),
+.BR unu\-mlut (1),
+.BR unu\-lut2 (1)
diff --git a/unu-lut2.1 b/unu-lut2.1
new file mode 100644
index 0000000..f6871c2
--- /dev/null
+++ b/unu-lut2.1
@@ -0,0 +1,114 @@
+.TH UNU\-LUT2 "1" "August 2021" "" "User Commands"
+.SH NAME
+unu\-lut2 \- map
+.I nrrd
+through a bivariate lookup table
+.SH SYNOPSIS
+.B unu\ lut2
+.BR \-m , \-\-map
+.RI < lut >
+.RB [ \-r , \-\-rescale
+.RI < bool \ bool >]
+.RB [ \-min , \-\-minimum
+.RI < value >]
+.RB [ \-max , \-\-maximum
+.RI < value >]
+.RB [ \-blind8
+.RI < bool >]
+.RB [ \-t , \-\-type
+.RI < type >]
+.RB [ \-i , \-\-input
+.RI < nin >]
+.RB [ \-o , \-\-output
+.RI < nout >]
+.SH DESCRIPTION
+Map
+.I nrrd
+through a bivariate lookup table (itself represented as a
+.IR nrrd ).
+The lookup table can be 2D, in which case the output has the same dimension as
+the input, or 3D, in which case the output has one more dimension than the
+input, and each value is mapped to a scanline (along axis
+.BR 0 )
+from the lookup table.
+In any case, axis
+.B 0
+of the input must have length two.
+.IP \(bu +2n
+Uses
+.B nrrdApply2DLut
+.SH OPTIONS
+.TP
+.B \-m\fR\ <\fIlut\fR>\ ,\ \fB\-\-map\fR\ <\fIlut\fR>
+lookup table to map input
+.I nrrd
+through
+.TP
+.B \-r\fR\ <\fIbool\fR\ \fIbool\fR>\ ,\ \fB\-\-rescale\fR\ <\fIbool\fR\ \fIbool\fR>
+rescale one or both of the input values from the input range to the lut domain.
+The lut domain is either explicitly defined by the axis
+.IR min , max
+along axis
+.B 0
+or
+.BR 1 ,
+or, it is implicitly defined as zero to the length of that axis minus one.
+(2
+.IR bool s);
+default:
+.RB \(lq false\ false \(rq
+.TP
+.B \-min\fR\ <\fImin0\fR\ \fImin1\fR>\ ,\ \fB\-\-minimum\fR\ <\fImin0\fR\ \fImin1\fR>
+Low end of input range.
+Defaults to lowest values found in input
+.IR nrrd .
+Explicitly setting this is useful only with rescaling
+.RB (\(lq \-r \(rq)
+(2
+.IR double s)
+.TP
+.B \-max\fR\ <\fImax0\fR\ \fImax1\fR>\ ,\ \fB\-\-maximum\fR\ <\fImax0\fR\ \fImax1\fR>
+High end of input range.
+Defaults to highest values found in input
+.IR nrrd .
+Explicitly setting this is useful only with rescaling
+.RB (\(lq \-r \(rq)
+(2
+.IR double s)
+.TP
+.B \-blind8\fR\ <\fIbool\fR>
+Whether to know the range of 8-bit data blindly
+.RI ( uchar
+is always
+.BR [0,255] ,
+.I signed\ char
+is
+.BR [-128,127] ).
+Explicitly setting this is useful only with rescaling
+.RB (\(lq \-r \(rq)
+.RI ( bool );
+default:
+.RB \(lq true \(rq
+.TP
+.B \-t\fR\ <\fItype\fR>\ ,\ \fB\-\-type\fR\ <\fItype\fR>
+specify the type
+.RB (\(lq int \(rq,
+.RB \(lq float \(rq,
+etc.) of the output
+.IR nrrd .
+By default (not using this option), the output type is the lut\(cqs type.
+.TP
+.B \-i\fR\ <\fInin\fR>\ ,\ \fB\-\-input\fR\ <\fInin\fR>
+input
+.I nrrd
+.TP
+.B \-o\fR\ <\fInout\fR>\ ,\ \fB\-\-output\fR\ <\fInout\fR>
+output
+.I nrrd
+.RI ( string );
+default:
+.RB \(lq \- \(rq
+.SH "SEE\ ALSO"
+.BR unu (1),
+.BR unu\-lut (1),
+.BR unu\-mlut (1)
diff --git a/unu-make.1 b/unu-make.1
new file mode 100644
index 0000000..d5a93b3
--- /dev/null
+++ b/unu-make.1
@@ -0,0 +1,383 @@
+.TH UNU\-MAKE "1" "May 2021" "" "User Commands"
+.SH NAME
+unu\-make \- create a \fInrrd\fR (or \fInrrd\fR header) from scratch
+.SH SYNOPSIS
+.B unu\ make
+.RI [ @file \ ...]
+.RB [ \-h ]
+.RB [ \-i , \-\-input
+.RI < file \ ...>]
+.BR \-t , \-\-type
+.RI < type >
+.BR \-s , \-\-size
+.RI < sz0 \ sz1 \ ...>
+.RB [ \-fd , \-\-filedim
+.RI < dim >]
+.RB [ \-sp , \-\-spacing
+.RI < sp0 \ sp1 \ ...>]
+.RB [ \-th , \-\-thickness
+.RI < th0 \ th1 \ ...>]
+.RB [ \-k , \-\-kind
+.RI < k0 \ k1 \ ...>]
+.RB [ \-cn , \-\-centering
+.RI < c0 \ c1 \ ...>]
+.RB [ \-l , \-\-label
+.RI < lb0 \ lb1 \ ...>]
+.RB [ \-u , \-\-unit
+.RI < un0 \ un1 \ ...>]
+.RB [ \-c , \-\-content
+.RI < content >]
+.RB [ \-ls , \-\-lineskip
+.RI < num >]
+.RB [ \-bs , \-\-byteskip
+.RI < num >]
+.RB [ \-e , \-\-encoding
+.RI < enc >]
+.RB [ \-en , \-\-endian
+.RI < end >]
+.RB [ \-kv , \-\-keyvalue
+.RI < key/val \ ...>]
+.RB [ \-spc , \-\-space
+.RI < space >]
+.RB [ \-orig , \-\-origin
+.RI < origin >]
+.RB [ \-dirs , \-\-directions
+.RI < v0 \ v1 \ ...>]
+.RB [ \-mf , \-\-measurementframe
+.RI < v0 \ v1 \ ...>]
+.RB [ \-spu , \-\-spaceunit
+.RI < su0 \ su1 \ ...>]
+.RB [ \-o , \-\-output
+.RI < nout >]
+.RB [ \-od , \-\-outputdata
+.RI < name >]
+.SH DESCRIPTION
+Create a
+.I nrrd
+(or
+.I nrrd
+header) from scratch.
+The data can be in one or more files, or coming from
+.BR stdin (3).
+This provides an easy way of specifying the information about some data as to
+wrap it in a NRRD file, either to pass on for further
+.BR unu (1)
+processing, or to save to disk.
+Note that with
+.RB \(lq \-h \(rq,
+this creates a detached
+.I nrrd
+header file, without ever reading or writing data files.
+.P
+When using multiple datafiles, the data from each is simply concatenated in
+memory (as opposed to interleaving along a faster axis).
+Keep in mind that all the options below refer to the finished data segment
+resulting from joining all the data pieces together, except for
+.RB \(lq \-ls \(rq,
+.RB \(lq \-bs \(rq,
+and
+.RB \(lq \-e \(rq,
+which apply (uniformly) to the individual data files.
+Use the
+.RB \(lq \-fd \(rq,
+option when the things being joined together are not slices of the final
+result, but slabs or scanlines.
+It may be easier to put multiple filenames in a response file; there can be one
+or more filenames per line of the response file. You can also use a
+.BR sprintf (3)-style
+format to identify a numbered range of files, so for example
+.RB \(lq \-i\ I.%03d\ 1\ 90\ 1 \(rq
+refers to
+.BR I.001 ,\ I.002 ,\ ...\ I.090 ,
+using the inclusive range from the first to the second integer (following the
+.BR sprintf (3)-style
+format), in steps of the third.
+Can optionally give a fourth integer to serve same role as
+.RB \(lq \-fd \(rq.
+.TP
+.B NOTE:
+For the
+.RB \(lq \-l \(rq
+(labels),
+.RB \(lq \-u \(rq
+(units), and
+.RB \(lq \-spu \(rq
+(space units) options below, you can use a single unquoted period
+.RB ( . )
+to signify an empty string.
+This creates a convenient way to convey something that the shell doesn\(cqt
+make it easy to convey.
+Shell expansion weirdness also requires the use of
+quotes
+around the arguments to
+.RB \(lq \-orig \(rq
+(space origin),
+.RB \(lq \-dirs \(rq
+(space directions), and
+.RB \(lq \-mf \(rq
+(measurement frame).
+.P
+Uses various components of file and data IO, but currently there is no library
+function that encapsulates the functionality here.
+.SH OPTIONS
+.TP
+.IR @file \ ...\:
+response file(s) containing command-line arguments
+.TP
+.B \-h
+Generate header
+.BR ONLY :
+don\(cqt write out the whole
+.IR nrrd ,
+don\(cqt even bother reading the input data, just output the detached
+.I nrrd
+header file (usually with a
+.RB \(lq .nhdr \(rq
+extension) determined by the options below.
+The single constraint is that detached headers are incompatible with using
+.BR stdin (3)
+as the data source.
+.TP
+.B \-i\fR\ <\fIfile\fR\ ...>\ ,\ \fB\-\-input\fR\ <\fIfile\fR\ ...>
+Filename(s) of data file(s); use
+.RB \(lq \- \(rq
+for
+.BR stdin (3).
+.BR OR ,
+can use
+.BR sprintf (3)-style
+format for identifying a range of numbered files, see above for details.
+(1 or more
+.IR string s);
+default:
+.RB \(lq \- \(rq
+.TP
+.B \-t\fR\ <\fItype\fR>\ ,\ \fB\-\-type\fR\ <\fItype\fR>
+type of data (e.g.\:
+.RB \(lq uchar \(rq,
+.RB \(lq int \(rq,
+.RB \(lq float \(rq,
+.RB \(lq double \(rq,
+etc.)
+.TP
+.B \-s\fR\ <\fIsz0\fR\ \fIsz1\fR\ ...>\ ,\ \fB\-\-size\fR\ <\fIsz0\fR\ \fIsz1\fR\ ...>
+number of samples along each axis (and implicit indicator of dimension of
+.IR nrrd )
+(1 or more
+.IR size_t s)
+.TP
+.B \-fd\fR\ <\fIdim\fR>\ ,\ \fB\-\-filedim\fR\ <\fIdim\fR>
+When using
+.B multiple
+input data files (to
+.RB \(lq \-i \(rq),
+what is the dimension of the array data in each individual file.
+By default (not using this option), this dimension is assumed to be one less
+than the whole data dimension.
+.RI ( unsigned\ int );
+default:
+.RB \(lq 0 \(rq)
+.TP
+.B \-sp\fR\ <\fIsp0\fR\ \fIsp1\fR\ ...>\ ,\ \fB\-\-spacing\fR\ <\fIsp0\fR\ \fIsp1\fR\ ...>
+spacing between samples on each axis.
+Use
+.RB \(lq nan \(rq
+for any non-spatial axes (e.g. spacing between red, green, and blue along axis
+0 of interleaved RGB image data) (1 or more
+.IR double s)
+.TP
+.B \-th\fR\ <\fIth0\fR\ \fIth1\fR\ ...>\ ,\ \fB\-\-thickness\fR\ <\fIth0\fR\ \fIth1\fR\ ...>
+thickness of region represented by one sample along each axis.
+As with spacing, use
+.RB \(lq nan \(rq
+for any non-spatial axes.
+(1 or more
+.IR double s)
+.TP
+.B \-k\fR\ <\fIk0\fR\ \fIk1\fR\ ...>\ ,\ \fB\-\-kind\fR\ <\fIk0\fR\ \fIk1\fR\ ...>
+what \(lqkind\(rq is each axis, from the
+.I nrrdKind
+.I airEnum
+(e.g.
+.BR space ,
+.BR time ,
+.BR 3-vector ,
+.BR 3D-masked-symmetric-matrix ,
+or
+.RB \(lq none \(rq
+to signify no kind) (1 or more
+.IR string s)
+.TP
+.B \-cn\fR\ <\fIc0\fR\ \fIc1\fR\ ...>\ ,\ \fB\-\-centering\fR\ <\fIc0\fR\ \fIc1\fR\ ...>
+kind of centering
+.RB ( node
+or
+.RB cell )
+for each axis, or
+.RB \(lq none \(rq
+to signify no centering) (1 or more
+.IR string s)
+.TP
+.B \-l\fR\ <\fIlb0\fR\ \fIlb1\fR\ ...>\ ,\ \fB\-\-label\fR\ <\fIlb0\fR\ \fIlb1\fR\ ...>
+short string labels for each of the axes (1 or more
+.IR string s)
+.TP
+.B \-u\fR\ <\fIun0\fR\ \fIun1\fR\ ...>\ ,\ \fB\-\-unit\fR\ <\fIun0\fR\ \fIun1\fR\ ...>
+short strings giving units for each of the axes (1 or more
+.TP
+.B \-c\fR\ <\fIcontent\fR>\ ,\ \fB\-\-content\fR\ <\fIcontent\fR>
+Specifies the content string of the
+.IR nrrd ,
+which is built upon by many
+.I nrrd
+functions to record a history of operations
+.RI ( string )
+.TP
+.B \-ls\fR\ <\fInum\fR>\ ,\ \fB\-\-lineskip\fR\ <\fInum\fR>
+number of ascii lines to skip before reading data
+.RI ( int )
+default:
+.RB \(lq 0 \(rq
+.TP
+.B \-bs\fR\ <\fInum\fR>\ ,\ \fB\-\-byteskip\fR\ <\fInum\fR>
+number of bytes to skip (after skipping ascii lines, if any) before reading
+data.
+Can use
+.RB \(lq \-bs\ \-1 \(rq
+to skip a binary header of unknown length in raw-encoded data
+.RI ( long int )
+default:
+.RB \(lq 0 \(rq
+.TP
+.B \-e\fR\ <\fIenc\fR>\ ,\ \fB\-\-encoding\fR\ <\fIenc\fR>
+encoding of input data.
+Possibilities include:
+.RS +7n
+.IP \(bu 2
+.RB \(lq raw \(rq
+raw encoding
+.IP \(bu 2
+.RB \(lq ascii \(rq
+ascii values, one scanline per line of text, values within line are delimited
+by space, tab, or comma
+.IP \(bu 2
+.RB \(lq hex \(rq
+two hex digits per byte
+.IP \(bu 2
+.RB \(lq gzip \(rq,\ \(lq gz \(rq
+gzip compressed raw data
+.IP \(bu 2
+.RB \(lq bzip2 \(rq,\ \(lq bz2 \(rq
+bzip2 compressed raw data
+.P
+default:
+.RB \(lq raw \(rq
+.RE
+.TP
+.B \-kv\fR\ <\fIkey/val\fR\ ...>\ ,\ \fB\-\-keyvalue\fR\ <\fIkey/val\fR\ ...>
+key/value string pairs to be stored in
+.IR nrrd .
+Each key/value pair must be a single string (put it in
+.BR \(dq\(dq s
+if the key or the value contain spaces).
+The format of each pair is
+.RI \(lq key\fB:=\fIvalue \(rq,
+with no spaces before or after
+.RB \(lq := \(rq.
+(1 or more
+.IR string s)
+.TP
+.B \-spc\fR\ <\fIspace\fR>\ ,\ \fB\-\-space\fR\ <\fIspace\fR>
+identify the space (e.g.\:
+.RB \(lq RAS \(rq,
+.RB \(lq LPS \(rq)
+in which the array conceptually lives, from the
+.I nrrdSpace
+.IR airEnum ,
+which in turn determines the dimension of the space.
+Or, use an
+.RB integer> 0
+to give the dimension of a space that
+.I nrrdSpace
+doesn\(cqt know about.
+By default (not using this option), the enclosing space is set as unknown.
+.RI ( string )
+.TP
+.B \-orig\fR\ <\fIorigin\fR>\ ,\ \fB\-\-origin\fR\ <\fIorigin\fR>
+.RB ( NOTE:
+must quote vector)
+the origin in space of the array: the location of the center of the first
+sample, of the form
+.RB \(lq (x,y,z) \(rq
+(or however many coefficients are needed for the chosen space).
+Quoting the vector is needed to stop interpretation from the shell
+.RI ( string )
+.TP
+.B \-dirs\fR\ <\fIv0\fR\ \fIv1\fR\ ...>\ ,\ \fB\-\-directions\fR\ <\fI\fIv0\fR\ \fIv1\fR\ ...>
+.RB ( NOTE:
+must quote whole vector list)
+The \(lqspace directions\(rq: the vectors in space spanned by incrementing (by
+one) each axis index (the column vectors of the index-to-world matrix
+transform),
+.BR OR ,
+.RB \(lq none \(rq
+for non-spatial axes.
+Give one vector per axis.
+(Quoting around whole vector list, not individually, is needed because of
+limitations in the parser)
+.RI ( string )
+.TP
+.B \-mf\fR\ <\fIv0\fR\ \fIv1\fR\ ...>\ ,\ \fB\-\-measurementframe\fR\ <\fI\fIv0\fR\ \fIv1\fR\ ...>
+.RB ( NOTE:
+must quote whole vector list)
+Each vector is a
+.I column
+vector of the matrix which transforms from coordinates in measurement frame (in
+which the coefficients of vectors and tensors are given) to coordinates of
+world space (given with
+.RB \(lq \-spc \(rq).
+This is not a per-axis field: the column vectors comprise a
+.IR D -by- D
+square matrix, where
+.I D
+is the dimension of world space.
+.RI ( string )
+.TP
+.B \-spu\fR\ <\fIsu0\fR\ \fIsu1\fR\ ...>\ ,\ \fB\-\-spaceunit\fR\ <\fI\fIsu0\fR\ \fIsu1\fR\ ...>
+short
+.IR string s
+giving units with which the coefficients of the space origin and direction
+vectors are measured.
+(1 or more
+.IR string s)
+.TP
+.B \-o\fR\ <\fInout\fR>\ ,\ \fB\-\-output\fR\ <\fInout\fR>
+output filename.
+If
+.RB \(lq \-h \(rq
+has been used, the output file is always a detached header.
+Otherwise, use extension
+.RB \(lq .nrrd \(rq
+to signal creation of self-contained
+.IR nrrd ,
+and
+.RB \(lq .nhdr \(rq
+to signal creating of a detached header with (single) data file.
+.RI ( string );
+default:
+.RB \(lq \- \(rq
+.TP
+.B \-od\fR\ <\fIname\fR>\ ,\ \fB\-\-outputdata\fR\ <\fIname\fR>
+when
+.I not
+using
+.RB \(lq \-h \(rq
+and saving to a
+.RB \(lq .nhdr \(rq
+file, using this option allows you to explicitly name the data file, instead of
+(by default, not using this option) having it be the same filename base as the
+header file.
+.RI ( string )
+.SH "SEE\ ALSO"
+.BR unu (1),
+.BR sprintf (3)
diff --git a/unu-minmax.1 b/unu-minmax.1
new file mode 100644
index 0000000..11f0533
--- /dev/null
+++ b/unu-minmax.1
@@ -0,0 +1,49 @@
+.TH UNU\-MINMAX "1" "May 2021" "" "User Commands"
+.SH NAME
+unu\-minmax \- print out min and max values in one or more
+.IR nrrd s
+.SH SYNOPSIS
+.B unu\ minmax
+.BR \-blind8
+.RI < bool >
+.RI < nin1 \ ...>
+.SH DESCRIPTION
+Print out min and max values in one or more
+.IR nrrd s.
+Unlike other commands, this doesn\(cqt produce a
+.IRnrrd .
+It only prints to standard out the min and max values found in the input
+.IR nrrd (s),
+and it also indicates if there are non-existent values.
+.IP \(bu +2n
+Uses
+.B nrrdRangeNewSet
+.SH OPTIONS
+.TP
+.B \-blind8\fR\ <\fIbool\fR>
+whether to blindly assert the range of 8-bit data, without actually going
+through the data values, i.e.
+.I uchar
+is always
+.BR [0,255] ,
+.I signed\ char
+is
+.BR [\-128,127] .
+Note that even if you do not use this option, the default
+.RB ( false )
+is potentialy over-riding the effect of environment variable
+.BR NRRD_STATE_BLIND_8_BIT_RANGE ;
+see
+.BR unu\-env (1)
+.RI ( bool );
+default:
+.RB \(lq false \(rq
+.TP
+.RI < nin1 \ ...>
+input
+.IR nrrd (s)
+(1 or more
+.IR string s)
+.SH "SEE\ ALSO"
+.BR unu (1),
+.BR unu\-env (1)
diff --git a/unu-mlut.1 b/unu-mlut.1
new file mode 100644
index 0000000..e4f0b8a
--- /dev/null
+++ b/unu-mlut.1
@@ -0,0 +1,115 @@
+.TH UNU\-MLUT "1" "August 2021" "" "User Commands"
+.SH NAME
+unu\-mlut \- map
+.I nrrd
+through whole
+.I nrrd
+of univariate lookup tables
+.SH SYNOPSIS
+.B unu\ mlut
+.BR \-m , \-\-map
+.RI < mlut \ ...>
+.RB [ \-r , \-\-rescale ]
+.RB [ \-min , \-\-minimum
+.RI < value >]
+.RB [ \-max , \-\-maximum
+.RI < value >]
+.RB [ \-blind8
+.RI < bool >]
+.RB [ \-t , \-\-type
+.RI < type >]
+.RB [ \-i , \-\-input
+.RI < nin >]
+.RB [ \-o , \-\-output
+.RI < nout >]
+.SH DESCRIPTION
+Map
+.I nrrd
+through whole
+.I nrrd
+of univariate lookup tables, with one lookup table per element of input
+.IR nrrd .
+The multiple tables are stored in a
+.I nrrd
+with a dimension which is either 1 or 2 more than the dimension of the input
+.IR nrrd ,
+resulting in an output which has either the same or one more dimension than the
+input, resptectively.
+.IP \(bu +2n
+Uses
+.B nrrdApplyMulti1DLut
+.SH OPTIONS
+.TP
+.B \-m\fR\ <\fIlut\fR>\ ,\ \fB\-\-map\fR\ <\fIlut\fR>
+one
+.I nrrd
+of lookup tables to map input
+.I nrrd
+through, or, list of
+.IR nrrd s
+which contain the individual entries of the lookup table at each voxel, which
+will be joined together.
+(1 or more
+.IR nrrd s)
+.TP
+.B \-r\fR ,\ \fB\-\-rescale
+rescale the input values from the input range to the lut domain.
+The lut domain is either explicitly defined by the axis
+.IR min , max
+along axis
+.B 0
+or
+.BR 1 ,
+or, it is implicitly defined as zero to the length of that axis minus one.
+.TP
+.B \-min\fR\ <\fIvalue\fR>\ ,\ \fB\-\-minimum\fR\ <\fIvalue\fR>
+Low end of input range.
+Defaults to lowest value found in input
+.IR nrrd .
+Explicitly setting this is useful only with rescaling
+.RB (\(lq \-r \(rq)
+.RI ( double )
+.TP
+.B \-max\fR\ <\fIvalue\fR>\ ,\ \fB\-\-maximum\fR\ <\fIvalue\fR>
+High end of input range.
+Defaults to highest value found in input
+.IR nrrd .
+Explicitly setting this is useful only with rescaling
+.RB (\(lq \-r \(rq)
+.RI ( double )
+.TP
+.B \-blind8\fR\ <\fIbool\fR>
+Whether to know the range of 8-bit data blindly
+.RI ( uchar
+is always
+.BR [0,255] ,
+.I signed\ char
+is
+.BR [-128,127] ).
+Explicitly setting this is useful only with rescaling
+.RB (\(lq \-r \(rq)
+.RI ( bool );
+default:
+.RB \(lq true \(rq
+.TP
+.B \-t\fR\ <\fItype\fR>\ ,\ \fB\-\-type\fR\ <\fItype\fR>
+specify the type
+.RB (\(lq int \(rq,
+.RB \(lq float \(rq,
+etc.) of the output
+.IR nrrd .
+By default (not using this option), the output type is the lut\(cqs type.
+.TP
+.B \-i\fR\ <\fInin\fR>\ ,\ \fB\-\-input\fR\ <\fInin\fR>
+input
+.I nrrd
+.TP
+.B \-o\fR\ <\fInout\fR>\ ,\ \fB\-\-output\fR\ <\fInout\fR>
+output
+.I nrrd
+.RI ( string );
+default:
+.RB \(lq \- \(rq
+.SH "SEE\ ALSO"
+.BR unu (1),
+.BR unu\-lut (1)
diff --git a/unu-mrmap.1 b/unu-mrmap.1
new file mode 100644
index 0000000..e5ddc68
--- /dev/null
+++ b/unu-mrmap.1
@@ -0,0 +1,123 @@
+.TH UNU\-MRMAP "1" "August 2021" "" "User Commands"
+.SH NAME
+unu\-mrmap \- map
+.I nrrd
+through whole
+.I nrrd
+of regular univariate maps
+.SH SYNOPSIS
+.B unu\ mrmap
+.BR \-m , \-\-map
+.RI < mmap \ ...>
+.RB [ \-r , \-\-rescale ]
+.RB [ \-min , \-\-minimum
+.RI < value >]
+.RB [ \-max , \-\-maximum
+.RI < value >]
+.RB [ \-blind8
+.RI < bool >]
+.RB [ \-t , \-\-type
+.RI < type >]
+.RB [ \-i , \-\-input
+.RI < nin >]
+.RB [ \-o , \-\-output
+.RI < nout >]
+.SH DESCRIPTION
+Map
+.I nrrd
+through whole
+.I nrrd
+of regular univariate maps, one map per sample in input.
+The
+.BI \(lq mmap \(rq
+.I nrrd
+has the same dimensional constraints as the
+.BI \(lq mlut \(rq
+.I nrrd
+for
+.BR unu\-mlut (1).
+This functionality is a generalization of
+.BR unu\-3op (1) \ lerp :
+it allows you to lerp through multiple
+.IR nrrd s,
+instead of just two.
+.IP \(bu +2n
+Uses
+.B nrrdApplyMulti1DRegMap
+.SH OPTIONS
+.TP
+.B \-m\fR\ <\fImmap\fR>\ ,\ \fB\-\-map\fR\ <\fImmap\fR>
+one
+.I nrrd
+of regular maps to map input
+.I nrrd
+through, or, list of
+.IR nrrd s
+which contain the individual entries of the map at each voxel, which will be
+joined together.
+(1 or more
+.IR nrrd s)
+.TP
+.B \-r\fR ,\ \fB\-\-rescale
+rescale the input values from the input range to the map domain.
+The map domain is either explicitly defined by the axis
+.IR min , max
+along axis
+.B 0
+or
+.BR 1 ,
+or, it is implicitly defined as zero to the length of that axis minus one.
+.TP
+.B \-min\fR\ <\fIvalue\fR>\ ,\ \fB\-\-minimum\fR\ <\fIvalue\fR>
+Low end of input range.
+Defaults to lowest value found in input
+.IR nrrd .
+Explicitly setting this is useful only with rescaling
+.RB (\(lq \-r \(rq)
+.RI ( double )
+.TP
+.B \-max\fR\ <\fIvalue\fR>\ ,\ \fB\-\-maximum\fR\ <\fIvalue\fR>
+High end of input range.
+Defaults to highest value found in input
+.IR nrrd .
+Explicitly setting this is useful only with rescaling
+.RB (\(lq \-r \(rq)
+.RI ( double )
+.TP
+.B \-blind8\fR\ <\fIbool\fR>
+Whether to know the range of 8-bit data blindly
+.RI ( uchar
+is always
+.BR [0,255] ,
+.I signed\ char
+is
+.BR [-128,127] ).
+Explicitly setting this is useful only with rescaling
+.RB (\(lq \-r \(rq)
+.RI ( bool );
+default:
+.RB \(lq true \(rq
+.TP
+.B \-t\fR\ <\fItype\fR>\ ,\ \fB\-\-type\fR\ <\fItype\fR>
+specify the type
+.RB (\(lq int \(rq,
+.RB \(lq float \(rq,
+etc.) of the output
+.IR nrrd .
+By default (not using this option), the output type is the map\(cqs type.
+.TP
+.B \-i\fR\ <\fInin\fR>\ ,\ \fB\-\-input\fR\ <\fInin\fR>
+input
+.I nrrd
+.TP
+.B \-o\fR\ <\fInout\fR>\ ,\ \fB\-\-output\fR\ <\fInout\fR>
+output
+.I nrrd
+.RI ( string );
+default:
+.RB \(lq \- \(rq
+.SH "SEE\ ALSO"
+.BR unu (1),
+.BR unu\-rmap (1),
+.BR unu\-mlut (1),
+.BR unu\-3op (1)
diff --git a/unu-pad.1 b/unu-pad.1
new file mode 100644
index 0000000..608c2fb
--- /dev/null
+++ b/unu-pad.1
@@ -0,0 +1,92 @@
+.TH UNU\-PAD "1" "August 2021" "" "User Commands"
+.SH NAME
+unu\-pad \- pad along each axis to make a bigger
+.I nrrd
+.SH SYNOPSIS
+.B unu\ pad
+.BR \-min , \-\-minimum
+.RI < pos0 \ ...>
+.BR \-max , \-\-maximum
+.RI < pos0 \ ...>
+.RB [ \-b , \-\-boundary
+.RI < behavior >]
+.RB [ \-v , \-\-value
+.RI < val >]
+.RB [ \-i , \-\-input
+.RI < nin >]
+.RB [ \-o , \-\-output
+.RI < nout >]
+.SH DESCRIPTION
+Pad along each axis to make a bigger
+.IR nrrd .
+.IP \(bu +2n
+Uses
+.B nrrdPad_nva
+.SH OPTIONS
+.TP
+.B \-min\fR\ <\fIpos0\fR\ ...>\ ,\ \fB\-\-minimum\fR\ <\fIpos0\fR\ ...>
+low corner of bounding box.
+.RS +7n
+.IP \(bu 2
+.RI < int >
+gives 0\-based index
+.IP \(bu 2
+.BR M ,
+.BR M\+ <\fIint\fR>,
+.BR M\- <\fIint\fR>
+give index relative to the last sample on the axis
+.RB ( M== \fI#samples \-1 ).
+.RE
+.IP
+(1 or more
+.IR position s)
+.TP
+.B \-max\fR\ <\fIpos0\fR\ ...>\ ,\ \fB\-\-maximum\fR\ <\fIpos0\fR\ ...>
+high corner of bounding box.
+.RS +7n
+.IP \(bu 2
+.RI < int >
+gives 0\-based index
+.IP \(bu 2
+.BR M ,
+.BR M\+ <\fIint\fR>,
+.BR M\- <\fIint\fR>
+give index relative to the last sample on the axis
+.RB ( M== \fI#samples \-1 ).
+.RE
+.IP
+(1 or more
+.IR position s)
+.TP
+.B \-b\fR\ <\fIbehavior\fR>\ ,\ \fB\-\-boundary\fR\ <\fIbehavior\fR>
+How to handle samples beyond the input bounds:
+.RS +7n
+.IP \(bu 2
+.RB \(lq pad \(rq:
+use some specified value
+.IP \(bu 2
+.RB \(lq bleed \(rq:
+extend border values outward
+.IP \(bu 2
+.RB \(lq mirror \(rq:
+repeated reflections
+.IP \(bu 2
+.RB \(lq wrap \(rq:
+wrap-around to other side
+.RE
+.IP
+default:
+.RB \(lq bleed \(rq
+.TP
+.B \-i\fR\ <\fInin\fR>\ ,\ \fB\-\-input\fR\ <\fInin\fR>
+input
+.I nrrd
+.TP
+.B \-o\fR\ <\fInout\fR>\ ,\ \fB\-\-output\fR\ <\fInout\fR>
+output
+.I nrrd
+.RI ( string );
+default:
+.RB \(lq \- \(rq
+.SH "SEE\ ALSO"
+.BR unu (1)
diff --git a/unu-permute.1 b/unu-permute.1
new file mode 100644
index 0000000..5a0eff6
--- /dev/null
+++ b/unu-permute.1
@@ -0,0 +1,38 @@
+.TH UNU\-PERMUTE "1" "August 2021" "" "User Commands"
+.SH NAME
+unu\-permute \- permute ordering of axes
+.SH SYNOPSIS
+.B unu\ permute
+.BR \-p , \-\-permute
+.RI < ax0 \ ax1 \ ...>
+.RB [ \-i , \-\-input
+.RI < nin >]
+.RB [ \-o , \-\-output
+.RI < nout >]
+.SH DESCRIPTION
+Permute ordering of axes.
+The permutation gives the new ordering of the old axes (in 0-based numbering).
+For example, the permutation 0->1, 1->2, 2->0 would be
+.RB \(lq 2\ 0\ 1 \(rq.
+.IP \(bu +2n
+Uses
+.B nrrdAxesPermute
+.SH OPTIONS
+.TP
+.B \-p\fR\ <\fIax0\fR \fIax1\fR\ ...>\ ,\ \fB\-\-permute\fR\ <\fIax0\fR \fIax1\fR\ ...>
+new axis ordering
+(1 or more
+.IR unsigned\ int s)
+.TP
+.B \-i\fR\ <\fInin\fR>\ ,\ \fB\-\-input\fR\ <\fInin\fR>
+input
+.I nrrd
+.TP
+.B \-o\fR\ <\fInout\fR>\ ,\ \fB\-\-output\fR\ <\fInout\fR>
+output
+.I nrrd
+.RI ( string );
+default:
+.RB \(lq \- \(rq
+.SH "SEE\ ALSO"
+.BR unu (1)
diff --git a/unu-project.1 b/unu-project.1
new file mode 100644
index 0000000..a246329
--- /dev/null
+++ b/unu-project.1
@@ -0,0 +1,103 @@
+.TH UNU\-PROJECT "1" "August 2021" "" "User Commands"
+.SH NAME
+unu\-project \- collapse scanlines to scalars along some axis
+.SH SYNOPSIS
+.B unu\ project
+.BR \-a , \-\-axis
+.RI < axis >
+.BR \-m , \-\-measure
+.RI < measr >
+.RB [ \-t , \-\-type
+.RI < type >]
+.RB [ \-i , \-\-input
+.RI < nin >]
+.RB [ \-o , \-\-output
+.RI < nout >]
+.SH DESCRIPTION
+Collapse scanlines to scalars along some axis.
+The scanline is reduced to a single scalar by \(lqmeasuring\(rq all the values
+in the scanline with some measure.
+The output
+.I nrrd
+has dimension one less than input (except when the input is itself 1\-D); the
+output type depends on the measure in a non-trivial way, or it can be set
+explicitly with the
+.RB \(lq \-t \(rq
+option.
+.IP \(bu +2n
+Uses
+.B nrrdProject
+.SH OPTIONS
+.TP
+.B \-a\fR\ <\fIaxis\fR>\ ,\ \fB\-\-axis\fR\ <\fIaxis\fR>
+axis to project along
+.RI ( unsigned\ int )
+.TP
+.B \-m\fR\ <\fImeasr\fR>\ ,\ \fB\-\-measure\fR\ <\fImeasr\fR>
+How to \(lqmeasure\(rq a scanline, by summarizing all its values with a single
+scalar.
+Possibilities include:
+.RS +7n
+.IP \(bu 2
+.RB \(lq min \(rq,
+.RB \(lq max \(rq,
+.RB \(lq mean \(rq,
+.RB \(lq median \(rq,
+.RB \(lq mode \(rq,
+.RB \(lq variance \(rq,
+.RB \(lq skew \(rq:
+(self-explanatory)
+.IP \(bu 2
+.RB \(lq intc \(rq,
+.RB \(lq slope \(rq,
+.RB \(lq error \(rq:
+intercept, slope, and error from line fitting
+.IP \(bu 2
+.RB \(lq stdv \(rq:
+standard deviation
+.IP \(bu 2
+.RB \(lq cov \(rq:
+coefficient of variation
+.IP \(bu 2
+.RB \(lq product \(rq,
+.RB \(lq sum \(rq:
+product or sum of all values
+.IP \(bu 2
+.RB \(lq L1 \(rq,
+.RB \(lq L2 \(rq,
+.RB \(lq NL2 \(rq,
+.RB \(lq RMS \(rq,
+.RB \(lq Linf \(rq:
+different norms
+.IP \(bu 2
+.RB \(lq histo-min \(rq,
+.RB \(lq histo-max \(rq,
+.RB \(lq histo-mean \(rq,
+.RB \(lq histo-median \(rq,
+.RB \(lq histo-mode \(rq,
+.RB \(lq histo-product \(rq,
+.RB \(lq histo-l2 \(rq,
+.RB \(lq histo-sum \(rq,
+.RB \(lq histo-variance \(rq,
+.RB \(lq histo-sd \(rq:
+same measures, but for situations where we\(cqre given not the original values,
+but a histogram of them.
+.RE
+.TP
+.B \-t\fR\ <\fItype\fR>\ ,\ \fB\-\-type\fR\ <\fItype\fR>
+type to use for output.
+By default (not using this option), the output type is determined
+auto-magically.
+.TP
+.B \-i\fR\ <\fInin\fR>\ ,\ \fB\-\-input\fR\ <\fInin\fR>
+input
+.I nrrd
+.TP
+.B \-o\fR\ <\fInout\fR>\ ,\ \fB\-\-output\fR\ <\fInout\fR>
+output
+.I nrrd
+.RI ( string );
+default:
+.RB \(lq \- \(rq
+.SH "SEE\ ALSO"
+.BR unu (1)
diff --git a/unu-quantize.1 b/unu-quantize.1
new file mode 100644
index 0000000..4686f3e
--- /dev/null
+++ b/unu-quantize.1
@@ -0,0 +1,135 @@
+.TH UNU\-QUANTIZE "1" "August 2021" "" "User Commands"
+.SH NAME
+unu\-quantize \- quantize values to 8, 16, or 32 bits
+.SH SYNOPSIS
+.B unu\ quantize
+.BR \-b , \-\-bits
+.RI < bits >
+.RB [ \-min , \-\-minimum
+.RI < value >]
+.RB [ \-max , \-\-maximum
+.RI < value >]
+.RB [ \-hb , \-\-bins
+.RI < bins >]
+.RB [ \-blind8
+.RI < bool >]
+.RB [ \-i , \-\-input
+.RI < nin >]
+.RB [ \-o , \-\-output
+.RI < nout >]
+.SH DESCRIPTION
+Quantize values to 8, 16, or 32 bits.
+Input values can be fixed point (e.g. quantizing ushorts down to uchars) or
+floating point.
+Values are clamped to the min and max before they are quantized, so there is no
+risk of getting
+.B 255
+where you expect
+.B 0
+(with
+.I unsigned char
+output, for example).
+The min and max can be specified explicitly (as a regular number), or in terms
+of percentiles (a number suffixed with
+.RB \(lq % \(rq,
+no space in between).
+This does only linear quantization.
+See also
+.BR unu\-convert (1),
+.BR unu\-2op (1) \ x ,
+and
+.BR unu\-3op (1) \ clamp .
+.IP \(bu +2n
+Uses
+.B nrrdQuantize
+.SH OPTIONS
+.TP
+.B \-b\fR\ <\fIbits\fR>\ ,\ \fB\-\-bits\fR\ <\fIbits\fR>
+Number of bits to quantize down to; determines the type of the output
+.IR nrrd :
+.RS +7n
+.IP \(bu 2
+.RB \(lq 8 \(rq:
+.I unsigned char
+.IP \(bu 2
+.RB \(lq 16 \(rq:
+.I unsigned short
+.IP \(bu 2
+.RB \(lq 32 \(rq:
+.I unsigned int
+.RE
+.TP
+.B \-min\fR\ <\fIvalue\fR>\ ,\ \fB\-\-minimum\fR\ <\fIvalue\fR>
+The value to map to zero, given explicitly as a regular number,
+.BR or ,
+if the number is given with a
+.RB \(lq % \(rq
+suffix, this minimum is specified in terms of the percentage of samples in
+input that are lower.
+.RB \(lq 0% \(rq
+means the lowest input value is used,
+.RB \(lq 1% \(rq
+means that the 1% of the lowest values are all mapped to zero.
+By default (not using this option), the lowest input value is used.
+.RI ( string );
+default:
+.RB \(lq nan \(rq
+.TP
+.B \-max\fR\ <\fIvalue\fR>\ ,\ \fB\-\-maximum\fR\ <\fIvalue\fR>
+The value to map to the highest unsigned integral value, given explicitly as a
+regular number,
+.BR or ,
+if the number is given with a
+.RB \(lq % \(rq
+suffix, this maximum is specified in terms of the percentage of samples in
+input that are higher.
+.RB \(lq 0% \(rq
+means the highest input value is used, which is also the default behavior (same
+as not using this option).
+.RI ( string );
+default:
+.RB \(lq nan \(rq
+.TP
+.B \-hb\fR\ <\fIbins\fR>\ ,\ \fB\-\-bins\fR\ <\fIbins\fR>
+number of bins in histogram of values, for determining min or max by
+percentiles.
+This has to be large enough so that any errant very high or very low values do
+not compress the interesting part of the histogram to an inscrutably small
+number of bins.
+.RI ( unsigned\ int );
+default:
+.RB \(lq 5000 \(rq
+.TP
+.B \-blind8\fR\ <\fIbins\fR>
+if not using
+.RB \(lq \-min \(rq
+or
+.RB \(lq \-max \(rq,
+whether to know the range of 8-bit data blindly
+.RI ( uchar
+is always
+.BR [0,255] ,
+.I signed char
+is
+.BR [-128,127] )
+.RI ( bool );
+default:
+.RB \(lq true \(rq,
+"true"
+.TP
+.B \-i\fR\ <\fInin\fR>\ ,\ \fB\-\-input\fR\ <\fInin\fR>
+input
+.I nrrd
+.TP
+.B \-o\fR\ <\fInout\fR>\ ,\ \fB\-\-output\fR\ <\fInout\fR>
+output
+.I nrrd
+.RI ( string );
+default:
+.RB \(lq \- \(rq
+.SH "SEE\ ALSO"
+.BR unu (1),
+.BR unu\-unquantize (1),
+.BR unu\-convert (1),
+.BR unu\-2op (1) \ x ,
+.BR unu\-3op (1) \ clamp
diff --git a/unu-resample.1 b/unu-resample.1
new file mode 100644
index 0000000..e08f625
--- /dev/null
+++ b/unu-resample.1
@@ -0,0 +1,344 @@
+.TH UNU\-RESAMPLE "1" "May 2021" "" "User Commands"
+.SH NAME
+unu\-resample \- filtering and {up,down}sampling with a separable kernel
+.SH SYNOPSIS
+.B unu\ resample
+.RB [ \-old ]
+.BR \-s , \-\-size
+.RI < sz0 \ ...>
+.RB [ \-off , \-\-offset
+.RI [< off0 \ ...>]]
+.RB [ \-min , \-\-minimum
+.RI [< min0 \ ...>]]
+.RB [ \-max , \-\-maximum
+.RI [< max0 \ ...>]]
+.RB [ \-k , \-\-kernel
+.RI < kern \ ...>]
+.RB [ \-nrn ]
+.RB [ \-ne , \-\-nonexistent
+.RI < behavior >]
+.RB [ \-b , \-\-boundary
+.RI < behavior >]
+.RB [ \-v , \-\-value
+.RI < value >]
+.RB [ \-t , \-\-type
+.RI < type >]
+.RB [ \-cheap ]
+.RB [ \-c , \-\-center
+.RI < center >]
+.RB [ \-co , \-\-center\-override ]
+.RB [ \-verbose
+.RI < v >]
+.RB [ \-i , \-\-input
+.RI < nin >]
+.RB [ \-o , \-\-output
+.RI < nout >]
+.SH DESCRIPTION
+Filtering and {up,down}sampling with a separable kernel.
+Simplifies access to the
+.B NrrdResampleContext
+functions by assuming (among other things) that the same kernel is used for
+resampling every axis that is being resampled.
+Only required option is
+.RB \(lq \-s \(rq
+to specify which axes to resample and how many output samples to generate.
+Resampling kernel
+.RB \(lq \-k \(rq
+defaults to an interpolating cubic, but many other choices are available.
+By default, resampling an axis resamples the full extent of its samples, but it
+is possible to offset this range via
+.RB \(lq \-off \(rq,
+or to crop and/or pad via
+.RB \(lq \-min \(rq
+and
+.RB \(lq \-max \(rq.
+The resampling respects the difference between cell- and node-centered data,
+but you can over-ride known centering with
+.RB \(lq \-co \(rq.
+.IP \(bu +2n
+Uses the many
+.B nrrdResample*
+functions operating on a
+.B nrrdResampleContext
+.SH OPTIONS
+.TP
+.B \-old
+instead of using the new
+.B nrrdResampleContext
+implementation, use the old
+.B nrrdSpatialResample
+implementation
+.TP
+.B \-s\fR\ <\fIsz0\fR\ ...>\ ,\ \fB\-\-size\fR\ <\fIsz0\fR\ ...>
+For each axis, information about how many samples in output:
+.RS +7n
+.IP \(bu 2
+.RB \(lq = \(rq:
+leave this axis completely untouched: no resampling whatsoever
+.IP \(bu 2
+.RB \(lq x\fIfloat \(rq:
+multiply the number of input samples by
+.IR float ,
+and round to the nearest integer, to get the number of output samples.
+Use
+.RB \(lq x1 \(rq
+to resample the axis but leave the number of samples unchanged
+.IP \(bu 2
+.RB \(lq /\fIfloat \(rq:
+divide number of input samples by
+.I float
+.IP \(bu 2
+.RB \(lq +=\fIuint \(rq,
+.RB \(lq \-=\fIuint \(rq:
+add
+.I uint
+to or subtract
+.I uint
+from number input samples to get number output samples
+.IP \(bu 2
+.RI \(lq uint \(rq:
+exact number of output samples
+.IP \(bu 2
+.RI \(lq a \(rq:
+resample this axis to whatever number of samples preserves the aspect ratio of
+other resampled axes.
+Currently needs to be used on all but one of the resampled axes, if at all.
+.RE
+.IP
+(1 or more sampling specifications)
+.TP
+.B \-off\fR\ [<\fIoff0\fR\ ...>]\ ,\ \fB\-\-offset\fR\ [<\fIoff0\fR\ ...>]
+For each axis, an offset or shift to the position (in index space) of the lower
+end of the sampling domain.
+Either
+.B \-off
+can be used, or
+.B \-min
+and
+.B \-max
+together, or none of these (so that, by default, the full domain of the axis is
+resampled).
+(0 or more
+.IR double s);
+default: \(lq\(rq
+.TP
+.B \-min\fR\ [<\fImin0\fR\ ...>]\ ,\ \fB\-\-minimum\fR\ [<\fImin0\fR\ ...>]
+For each axis, the lower end (in index space) of the domain of the resampling.
+Either
+.B \-off
+can be used, or
+.B \-min
+and
+.B \-max
+together, or none of these (so that, by default, the full domain of the axis is
+resampled).
+(0 or more
+.IR double s);
+default: \(lq\(rq
+.TP
+.B \-max\fR\ [<\fImax0\fR\ ...>]\ ,\ \fB\-\-maximum\fR\ [<\fImax0\fR\ ...>]
+For each axis, the upper end (in index space) of the domain of the resampling.
+Either
+.B \-off
+can be used, or
+.B \-max
+and
+.B \-max
+together, or none of these (so that, by default, the full domain of the axis is
+resampled).
+(0 or more
+.IR double s);
+default: \(lq\(rq
+.TP
+.B \-k\fR\ <\fIkern\fR>\ , \fB\-\-kernel\fR\ <\fIkern\fR>
+The kernel to use for resampling.
+Kernels logically live in the input index space for upsampling, and in the
+output index space for downsampling.
+Possibilities include:
+.RS +7n
+.IP \(bu 2
+.RB \(lq box \(rq:
+nearest neighbor interpolation on upsampling, and uniform averaging on
+downsampling
+.IP \(bu 2
+.RB \(lq cheap \(rq:
+nearest neighbor interpolation for upsampling, and non-blurring sub-sampling
+(pick subset of input samples) on downsampling
+.IP \(bu 2
+.RB \(lq tent \(rq:
+linear interpolation
+.IP \(bu 2
+.RB \(lq cubic:\fIB\fR,\fIC \(rq:
+Mitchell/Netravali BC-family of cubics:
+.IP
+.RB \(lq cubic:1,0 \(rq:
+B-spline; maximal blurring
+.IP
+.RB \(lq cubic:0,0.5 \(rq:
+Catmull-Rom; good interpolating kernel
+.IP \(bu 2
+.RB \(lq c4h \(rq:
+6-sample-support, C^4 continuous, accurate
+.IP \(bu 2
+.RB \(lq c4hai \(rq:
+discrete pre-filter to make
+.B c4h
+interpolate
+.IP \(bu 2
+.RB \(lq bspl3 \(rq,
+.RB \(lq bspl5 \(rq,
+.RB \(lq bspl7 \(rq:
+cubic (same as
+.BR cubic:1,0 ),
+quintic, and 7th order B-spline
+.IP \(bu 2
+.RB \(lq bspl3ai \(rq,
+.RB \(lq bspl5ai \(rq,
+.RB \(lq bspl7ai \(rq:
+discrete pre-filters to make
+.BR bspl3 ,
+.BR bspl5 ,
+.B bspl7
+interpolate
+.IP \(bu 2
+.RB \(lq hann:\fIR \(rq:
+Hann (cosine bell) windowed sinc, radius
+.I R
+.IP \(bu 2
+.RB \(lq black:\fIR \(rq:
+Blackman windowed sinc, radius
+.I R
+.IP \(bu 2
+.RB \(lq gauss:\fIS\fB,\fIC \(rq:
+Gaussian blurring, with standard deviation
+.I S
+and cut-off at
+.I C
+standard deviations
+.IP \(bu 2
+.RB \(lq dgauss:\fIS\fB,\fIC \(rq:
+Lindeberg\(cqs discrete Gaussian.
+.RE
+.IP
+default:
+.RB \(lq cubic:0,0.5 \(rq
+.TP
+.B \-nrn
+do NOT do per-pass kernel weight renormalization.
+Doing the renormalization is not a performance hit (hence is enabled by
+default), and the renormalization is sometimes needed to avoid \(lqgrating\(rq
+on non-integral down-sampling.
+Disabling the renormalization is needed for correct results with artificially
+narrow kernels.
+.TP
+.B \-ne\fR\ <\fIbehavior\fR>\ , \fB\-\-nonexistent\fR\ <\fIbehavior\fR>
+When resampling floating-point values, how to handle non-existent values within
+kernel support:
+.RS +7n
+.IP \(bu 2
+.RB \(lq noop \(rq:
+do nothing; let them pollute result
+.IP \(bu 2
+.RB \(lq renorm \(rq:
+ignore them and renormalize weights of existent values
+.IP \(bu 2
+.RB \(lq wght \(rq:
+ignore them and simply use weights of existent values
+.RE
+.IP
+default:
+.RB \(lq noop \(rq
+.TP
+.B \-b\fR\ <\fIbehavior\fR>\ , \fB\-\-boundary\fR\ <\fIbehavior\fR>
+How to handle samples beyond the input bounds:
+.RS +7n
+.IP \(bu 2
+.RB \(lq pad \(rq:
+use some specified value
+.IP \(bu 2
+.RB \(lq bleed \(rq:
+extend border values outward
+.IP \(bu 2
+.RB \(lq mirror \(rq:
+repeated reflections
+.IP \(bu 2
+.RB \(lq wrap \(rq:
+wrap-around to other side
+.RE
+.IP
+default:
+.RB \(lq noop \(rq
+.TP
+.B \-v\fR\ <\fIvalue\fR>\ , \fB\-\-value\fR\ <\fIvalue\fR>
+for
+.RB \(lq pad \(rq
+boundary behavior, pad with this value
+.RI ( double );
+default:
+.RB \(lq 0.0 \(rq
+.TP
+.B \-t\fR\ <\fItype\fR>\ , \fB\-\-type\fR\ <\fItype\fR>
+type to save output as.
+By default (not using this option), the output type is the same as the input
+type; default:
+.RB \(lq default \(rq
+.TP
+.B \-cheap
+.RI [ DEPRECATED :
+the
+.RB \(lq \-k\ cheap \(rq
+option is the new (and more reliable) way to access this functionality.
+.RB \(lq \-cheap \(rq
+is only here for legacy use in combination with
+.RB \(lq \-old \(rq.]
+.IP
+When downsampling (reducing number of samples), don\(cqt try to do correct
+filtering by scaling kernel to match new (stretched) index space; keep it in
+old index space.
+When used in conjunction with
+.RB \(lq \-k\ box \(rq,
+this can implement subsampling which chooses every Nth value.
+.TP
+.B \-c\fR\ <\fIcenter\fR>\ , \fB\-\-center\fR\ <\fIcenter\fR>
+(not available with
+.RB \(lq \-old \(rq)
+default centering of axes when input
+.I nrrd
+axes don\(cqt have a known centering:
+.RB \(lq cell \(rq
+or
+.RB \(lq node \(rq;
+default:
+.RB \(lq cell \(rq
+.TP
+.B \-co\fR\ , \fB\-\-center-override
+(not available with
+.RB \(lq \-old \(rq)
+centering info specified via
+.RB \(lq \-c \(rq
+should
+.B over-ride
+known centering, rather than simply be used when centering is unknown.
+.TP
+.B \-verbose\fR\ <\fIv\fR>
+(not available with
+.RB \(lq \-old \(rq)
+verbosity level
+.RI ( int );
+default:
+.RB \(lq 0 \(rq
+.TP
+.B \-i\fR\ <\fInin\fR>\ , \fB\-\-input\fR\ <\fInin\fR>
+input
+.IR nrrd ;
+default:
+.RB \(lq \- \(rq
+.TP
+.B \-o\fR\ <\fInout\fR>\ , \fB\-\-output\fR\ <\fInout\fR>
+output
+.I nrrd
+.RI ( string );
+default:
+.RB \(lq \- \(rq
+.SH "SEE\ ALSO"
+.BR unu (1)
diff --git a/unu-reshape.1 b/unu-reshape.1
new file mode 100644
index 0000000..f9660dd
--- /dev/null
+++ b/unu-reshape.1
@@ -0,0 +1,40 @@
+.TH UNU\-RESHAPE "1" "August 2021" "" "User Commands"
+.SH NAME
+unu\-reshape \- superficially change dimension and/or axes sizes
+.SH SYNOPSIS
+.B unu\ reshape
+.BR \-s , \-\-size
+.RI < sz0 \ sz1 \ ...>
+.RB [ \-i , \-\-input
+.RI < nin >]
+.RB [ \-o , \-\-output
+.RI < nout >]
+.SH DESCRIPTION
+Superficially change dimension and/or axes sizes.
+The underlying linear ordering of the samples is unchanged, but the reported
+dimension or axes sizes are changed.
+Identical in concept to Matlab\(cqs
+.RB \(lq reshape \(rq
+command.
+.IP \(bu +2n
+Uses
+.B nrrdReshape_nva
+.SH OPTIONS
+.TP
+.B \-s\fR\ <\fIsz0\fR \fIsz1\fR\ ...>\ ,\ \fB\-\-size\fR\ <\fI\fIsz0\fR \fIsz1\fR\ ...>
+new axes sizes
+(1 or more
+.IR size_t s)
+.TP
+.B \-i\fR\ <\fInin\fR>\ ,\ \fB\-\-input\fR\ <\fInin\fR>
+input
+.I nrrd
+.TP
+.B \-o\fR\ <\fInout\fR>\ ,\ \fB\-\-output\fR\ <\fInout\fR>
+output
+.I nrrd
+.RI ( string );
+default:
+.RB \(lq \- \(rq
+.SH "SEE\ ALSO"
+.BR unu (1)
diff --git a/unu-rmap.1 b/unu-rmap.1
new file mode 100644
index 0000000..ab2a69c
--- /dev/null
+++ b/unu-rmap.1
@@ -0,0 +1,119 @@
+.TH UNU\-RMAP "1" "August 2021" "" "User Commands"
+.SH NAME
+unu\-rmap \- map
+.I nrrd
+through one
+.B regular
+univariate map (\(lqcolormap\(rq)
+.SH SYNOPSIS
+.B unu\ rmap
+.BR \-m , \-\-map
+.RI < map >
+.RB [ \-r , \-\-rescale ]
+.RB [ \-min , \-\-minimum
+.RI < value >]
+.RB [ \-max , \-\-maximum
+.RI < value >]
+.RB [ \-blind8
+.RI < bool >]
+.RB [ \-t , \-\-type
+.RI < type >]
+.RB [ \-i , \-\-input
+.RI < nin >]
+.RB [ \-o , \-\-output
+.RI < nout >]
+.SH DESCRIPTION
+Map
+.I nrrd
+through one
+.B regular
+univariate map (\(lqcolormap\(rq).
+A map is regular if the control points are evenly spaced along the domain, and
+hence their position isn\(cqt explicitly represented in the map; the axis
+.IR min ,
+axis
+.IR max ,
+and number of points determine their location.
+The map can be a 1D
+.I nrrd
+(for \(lqgrayscale\(rq), in which case the output has the same dimension as the
+input, or a 2D
+.I nrrd
+(for \(lqcolor\(rq), in which case the output has one more dimension than the
+input.
+In either case, the output is the result of linearly interpolating between map
+points, either scalar values (\(lqgrayscale\(rq), or scanlines along axis
+.B 0
+(\(lqcolor\(rq).
+.IP \(bu +2n
+Uses
+.B nrrdApply1DRegMap
+.SH OPTIONS
+.TP
+.B \-m\fR\ <\fImap\fR>\ ,\ \fB\-\-map\fR\ <\fImap\fR>
+regular map to map input
+.I nrrd
+through
+.TP
+.B \-r\fR ,\ \fB\-\-rescale
+rescale the input values from the input range to the map domain.
+The map domain is either explicitly defined by the axis
+.IR min , max
+along axis
+.B 0
+or
+.BR 1 ,
+or, it is implicitly defined as zero to the length of that axis minus one.
+.TP
+.B \-min\fR\ <\fIvalue\fR>\ ,\ \fB\-\-minimum\fR\ <\fIvalue\fR>
+Low end of input range.
+Defaults to lowest value found in input
+.IR nrrd .
+Explicitly setting this is useful only with rescaling
+.RB (\(lq \-r \(rq)
+.RI ( double )
+.TP
+.B \-max\fR\ <\fIvalue\fR>\ ,\ \fB\-\-maximum\fR\ <\fIvalue\fR>
+High end of input range.
+Defaults to highest value found in input
+.IR nrrd .
+Explicitly setting this is useful only with rescaling
+.RB (\(lq \-r \(rq)
+.RI ( double )
+.TP
+.B \-blind8\fR\ <\fIbool\fR>
+Whether to know the range of 8-bit data blindly
+.RI ( uchar
+is always
+.BR [0,255] ,
+.I signed\ char
+is
+.BR [-128,127] ).
+Explicitly setting this is useful only with rescaling
+.RB (\(lq \-r \(rq)
+.RI ( bool );
+default:
+.RB \(lq true \(rq
+.TP
+.B \-t\fR\ <\fItype\fR>\ ,\ \fB\-\-type\fR\ <\fItype\fR>
+specify the type
+.RB (\(lq int \(rq,
+.RB \(lq float \(rq,
+etc.) of the output
+.IR nrrd .
+By default (not using this option), the output type is the map\(cqs type.
+.TP
+.B \-i\fR\ <\fInin\fR>\ ,\ \fB\-\-input\fR\ <\fInin\fR>
+input
+.I nrrd
+.TP
+.B \-o\fR\ <\fInout\fR>\ ,\ \fB\-\-output\fR\ <\fInout\fR>
+output
+.I nrrd
+.RI ( string );
+default:
+.RB \(lq \- \(rq
+.SH "SEE\ ALSO"
+.BR unu (1),
+.BR unu\-mrmap (1),
+.BR unu\-imap (1)
diff --git a/unu-save.1 b/unu-save.1
new file mode 100644
index 0000000..ac0d8f8
--- /dev/null
+++ b/unu-save.1
@@ -0,0 +1,150 @@
+.TH UNU\-SAVE
+.SH NAME
+unu\-save \- write
+.I nrrd
+with specific format, encoding, or endianness
+.SH SYNOPSIS
+.B unu\ save
+.BR \-f , \-\-format
+.RI < form >
+.RB [ \-e , \-\-encoding
+.RI < enc >]
+.RB [ \-en , \-\-endian
+.RI < end >]
+.RB [ \-i , \-\-input
+.RI < nin >]
+.RB [ \-o , \-\-output
+.RI < nout >]
+.RB [ \-od , \-\-ouputdata
+.RI < name >]
+.SH DESCRIPTION
+Write
+.I nrrd
+with specific format, encoding, or endianness.
+Use
+.in +4n
+.EX
+.B unu\ save\ \-f\ pnm\ |\ xv\ \-
+.EE
+.in
+to view PPM- or PGM-compatible
+.IR nrrd s on unix.
+EPS output is a EPSF\-3.0 file with
+.B BoundingBox
+and
+.B HiResBoundingBox
+DSC comments, and is suitable for inclusion into other PostScript documents.
+As a stand-alone file, the image is conveniently centered on an 8.5x11 inch
+page, with 0.5 inch margins.
+.IP \(bu +2n
+Uses various fields in the
+.B NrrdIOState
+passed to
+.B nrrdSave
+.SH OPTIONS
+.TP
+.B \-f\fR\ <\fIform\fR>\ ,\ \fB\-\-format\fR\ <\fIform\fR>
+output file format.
+Possibilities include:
+.RS +7n
+.IP \(bu +2n
+.RB \(lq nrrd \(rq:
+standard
+.I nrrd
+format
+.IP \(bu +2n
+.RB \(lq pnm \(rq:
+PNM image; PPM for color, PGM for grayscale
+.IP \(bu +2n
+.RB \(lq text \(rq:
+plain ASCII text for 1\-D and 2\-D data
+.IP \(bu +2n
+.RB \(lq vtk \(rq:
+VTK
+.RB \(lq STRUCTURED_POINTS \(rq
+dataset
+.IP \(bu +2n
+.RB \(lq png \(rq:
+PNG image
+.IP \(bu +2n
+.RB \(lq eps \(rq:
+EPS file
+.RE
+.TP
+.B \-e\fR\ <\fIenc\fR>\ ,\ \fB\-\-encoding\fR\ <\fIenc\fR>
+encoding of data in file.
+Not all encodings are supported in a given format.
+Possibilities include:
+.RS +7n
+.IP \(bu +2n
+.RB \(lq raw \(rq:
+raw encoding
+.IP \(bu +2n
+.RB \(lq ascii \(rq:
+print data in ascii
+.IP \(bu +2n
+.RB \(lq hex \(rq:
+two hex digits per byte
+.IP \(bu +2n
+.RB \(lq gzip \(rq,\ \(lq gz \(rq:
+gzip compressed raw data
+.IP \(bu +2n
+.RB \(lq bzip2 \(rq,\ \(lq bz2 \(rq:
+bzip2 compressed raw data
+.P
+The specifiers for compressions may be followed by a colon
+.RB \(lq : \(rq,
+followed by an optional digit giving compression \(lqlevel\(rq (for
+.BR gzip )
+or \(lqblock size\(rq (for
+.BR bzip2 ).
+For
+.BR gzip ,
+this can be followed by an optional character for a compression strategy:
+.IP \(bu +2n
+.RB \(lq d \(rq:
+default, Huffman with string match
+.IP \(bu +2n
+.RB \(lq h \(rq:
+Huffman alone
+.IP \(bu +2n
+.RB \(lq f \(rq:
+specialized for filtered data
+.P
+For example,
+.RB \(lq gz \(rq,
+.RB \(lq gz:9 \(rq,
+.RB \(lq gz:9f \(rq
+are all valid
+.RE
+.TP
+.B \-en\fR\ <\fIend\fR>\ ,\ \fB\-\-endian\fR\ <\fIend\fR>
+Endianness to save data out as;
+.RB \(lq little \(rq
+for Intel and friends;
+.RB \(lq big \(rq
+for everyone else.
+Defaults to endianness of this machine
+.TP
+.B \-i\fR\ <\fInin\fR>\ ,\ \fB\-\-input\fR\ <\fInin\fR>
+input
+.I nrrd
+.TP
+.B \-o\fR\ <\fInout\fR>\ ,\ \fB\-\-output\fR\ <\fInout\fR>
+output
+.I nrrd
+.RI ( string );
+default:
+.RB \(lq \- \(rq
+.TP
+.B \-od\fR\ <\fIname\fR>\ ,\ \fB\-\-ouputdata\fR\ <\fIname\fR>
+when saving to a
+.RB \(lq .nhdr \(rq
+file, this option allows you to explicitly name the data file, instead of (by
+default, not using this option) having it be the same filename base as the
+header file.
+.RI ( string )
+.SH "SEE\ ALSO"
+.BR unu (1)
+.P
+http://www.trilon.com/xv/
diff --git a/unu-shuffle.1 b/unu-shuffle.1
new file mode 100644
index 0000000..b2bd05e
--- /dev/null
+++ b/unu-shuffle.1
@@ -0,0 +1,57 @@
+.TH UNU\-SHUFFLE "1" "August 2021" "" "User Commands"
+.SH NAME
+unu\-shuffle \- permute slices along one axis
+.SH SYNOPSIS
+.B unu\ shuffle
+.RI [ @file \ ...]
+.BR \-p , \-\-permute
+.RI < slc0 \ slc1 \ ...>
+.RB [ \-inv , \-\-inverse ]
+.BR \-a , \-\-axis
+.RI < axis >
+.RB [ \-i , \-\-input
+.RI < nin >]
+.RB [ \-o , \-\-output
+.RI < nout >]
+.SH DESCRIPTION
+Permute slices along one axis.
+Slices along one axis are re-arranged as units according to the given
+permutation (or its inverse).
+The permutation tells which old slice to put at each new position.
+For example, the shuffle 0->1, 1->2, 2->0 would be
+.RB \(lq 2\ 0\ 1 \(rq.
+Obviously, if you have to rearrange the many slices of a large dataset, you
+should probably store the permutation in a plain text file and use it as a
+\(lqresponse file\rq).
+.IP \(bu +2n
+Uses
+.B nrrdShuffle
+.SH OPTIONS
+.TP
+.IR @file \ ...
+response file(s) containing command-line arguments
+.TP
+.B \-p\fR\ <\fIslc0\fR\ \fIslc1\fR\ ...>\ ,\ \fB\-\-permute\fR\ <\fIslc0\fR\ \fIslc1\fR\ ...>
+new slice ordering (1 or more
+.IR unsigned\ int s)
+.TP
+.B \-inv\fR\ ,\ \fB\-\-inverse
+use inverse of given permutation
+.TP
+.B \-a\fR\ <\fIaxis\fR>\ ,\ \fB\-\-axis\fR\ <\fIaxis\fR>
+axis to shuffle along
+.RI ( unsigned\ int )
+.TP
+.B \-i\fR\ <\fInin\fR>\ ,\ \fB\-\-input\fR\ <\fInin\fR>
+input
+.I nrrd
+.TP
+.B \-o\fR\ <\fInout\fR>\ ,\ \fB\-\-output\fR\ <\fInout\fR>
+output
+.I nrrd
+.RI ( string );
+default:
+.RB \(lq \- \(rq
+.SH "SEE\ ALSO"
+.BR unu (1),
+.BR unu\-flip (1)
diff --git a/unu-slice.1 b/unu-slice.1
new file mode 100644
index 0000000..fd6a2de
--- /dev/null
+++ b/unu-slice.1
@@ -0,0 +1,66 @@
+.TH UNU\-SLICE "1" "August 2021" "" "User Commands"
+.SH NAME
+unu\-slice \- slice along one or more axes at given positions
+.SH SYNOPSIS
+.B unu\ slice
+.BR \-a , \-\-axis
+.RI < axis \ ...>
+.BR \-p , \-\-position
+.RI < pos \ ...>
+.RB [ \-i , \-\-input
+.RI < nin >]
+.RB [ \-o , \-\-output
+.RI < nout >]
+.SH DESCRIPTION
+Slice along one or more axes at given positions.
+Output
+.I nrrd
+dimension is less than input
+.I nrrd
+dimension by the number of slice axes (except when the input is or gets down to
+1\-D).
+Can slice on all axes in order to sample a single value from the array.
+Per-axis information is preserved.
+.IP \(bu +2n
+Uses
+.B nrrdSlice
+(possibly called multiple times)
+.SH OPTIONS
+.TP
+.B \-a\fR\ <\fIaxis\fR\ ...>\ ,\ \fB\-\-axis\fR\ <\fIaxis\fR\ ...>
+single axis or multiple axes to slice along.
+Giving multiple axes here leads to doing multiple slices (at the corresponding
+positions given with
+.RB \(lq \-p \(rq.
+Multiple axes should be identified in terms of the axis numbering of the
+original
+.IR nrrd ;
+as the slices are done (in the given ordering) the actual slice axis will be
+different if previous slices were on lower-numbered (faster) axes.
+(1 or more
+.IR unsigned\ ints )
+.TP
+.B \-p\fR\ <\fIpos\fR>\ ,\ \fB\-\-position\fR\ <\fIpos\fR>
+position(s) to slice at:
+.RS +7n
+.IP \(bu 2
+.RI < int >
+gives 0-based index
+.IP \(bu 2
+.BR M\- <\fIint\fR>
+give index relative to the last sample on the axis
+.RB ( M== \fI#samples \-1 ).
+.RE
+.TP
+.B \-i\fR\ <\fInin\fR>\ ,\ \fB\-\-input\fR\ <\fInin\fR>
+input
+.I nrrd
+.TP
+.B \-o\fR\ <\fInout\fR>\ ,\ \fB\-\-output\fR\ <\fInout\fR>
+output
+.I nrrd
+.RI ( string );
+default:
+.RB \(lq \- \(rq
+.SH "SEE\ ALSO"
+.BR unu (1)
diff --git a/unu-splice.1 b/unu-splice.1
new file mode 100644
index 0000000..86d0ca5
--- /dev/null
+++ b/unu-splice.1
@@ -0,0 +1,56 @@
+.TH UNU\-SPLICE "1" "August 2021" "" "User Commands"
+.SH NAME
+unu\-splice \- replace a slice with a different \fInrrd
+.SH SYNOPSIS
+.B unu\ splice
+.BR \-a , \-\-axis
+.RI < axis >
+.BR \-p , \-\-position
+.RI < pos >
+.BR \-s , \-\-slice
+.RI < nslice >
+.RB [ \-i , \-\-input
+.RI < nin >]
+.RB [ \-o , \-\-output
+.RI < nout >]
+.SH DESCRIPTION
+Replace a slice with a different
+.IR nrrd .
+This is functionally the opposite of
+.RB \(lq slice \(rq.
+.IP \(bu +2n
+Uses
+.B nrrdSplice
+.SH OPTIONS
+.TP
+.B \-a\fR\ <\fIaxis\fR>\ ,\ \fB\-\-axis\fR\ <\fIaxis\fR>
+axis to splice along
+.RI ( unsigned\ int )
+.TP
+.B \-p\fR\ <\fIpos\fR>\ ,\ \fB\-\-position\fR\ <\fIpos\fR>
+position to splice at:
+.RS +7n
+.IP \(bu 2
+.RI < int >
+gives 0-based index
+.IP \(bu 2
+.BR M\- <\fIint\fR>
+give index relative to the last sample on the axis
+.RB ( M== \fI#samples \-1 ).
+.RE
+.TP
+.B \-i\fR\ <\fInin\fR>\ ,\ \fB\-\-input\fR\ <\fInin\fR>
+input
+.IR nrrd .
+This is the slice to insert into
+.I nin
+.TP
+.B \-o\fR\ <\fInout\fR>\ ,\ \fB\-\-output\fR\ <\fInout\fR>
+output
+.I nrrd
+.RI ( string );
+default:
+.RB \(lq \- (rq
+.SH "SEE\ ALSO"
+.BR unu (1),
+.BR unu\-slice (1)
diff --git a/unu-sselect.1 b/unu-sselect.1
new file mode 100644
index 0000000..84e21ff
--- /dev/null
+++ b/unu-sselect.1
@@ -0,0 +1,52 @@
+.TH UNU\-SSELECT "1" "August 2021" "" "User Commands"
+.SH NAME
+unu\-sselect \- select subset of slices along an axis
+.SH SYNOPSIS
+.B unu\ sselect
+.RB [ \-i , \-\-input
+.RI < nin >]
+.BR \-a , \-\-axis
+.RI < axis >
+.BR \-s , \-\-selector
+.RI < nline >
+.BR \-th
+.RI < thresh >
+.RB [ \-o , \-\-output
+.RI < above \ below >]
+.SH DESCRIPTION
+Select subset of slices along an axis.
+The choice to keep or nix a slice is determined by whether the values in a
+given 1\-D line of values is above or below a given threshold.
+.IP \(bu +2n
+Uses
+.B nrrdSliceSelect
+.SH OPTIONS
+.TP
+.B \-i\fR\ <\fInin\fR>\ ,\ \fB\-\-input\fR\ <\fInin\fR>
+input
+.I nrrd
+.TP
+.B \-a\fR\ <\fIaxis\fR>\ ,\ \fB\-\-axis\fR\ <\fIaxis\fR>
+axis to slice along
+.RI ( unsigned\ int )
+.TP
+.B \-s\fR\ <\fInline\fR>\ ,\ \fB\-\-selector\fR\ <\fInline\fR>
+the 1\-D
+.I nrrd
+of values to compare with threshold
+.TP
+.B \-th\fR\ <\fIthresh\fR>
+threshold on selector line
+.RI ( double )
+.TP
+.B \-o\fR\ <\fIabove\fR\ \fIbelow\fR>\ ,\ \fB\-\-output\fR\ <\fIabove\fR\ \fIbelow\fR>
+outputs for slices corresponding to values above (first) and below (second) given threshold.
+Use
+.RB \(lq x \(rq
+to say that no output is desired.
+(2
+.IR string s);
+default:
+.RB \(lq \-\ x \(rq
+.SH "SEE\ ALSO"
+.BR unu (1)
diff --git a/unu-subst.1 b/unu-subst.1
new file mode 100644
index 0000000..20e639a
--- /dev/null
+++ b/unu-subst.1
@@ -0,0 +1,55 @@
+.TH UNU\-SUBST "1" "August 2021" "" "User Commands"
+.SH NAME
+unu\-subst \- map
+.I nrrd
+through one univariate substitution table
+.SH SYNOPSIS
+.B unu\ subst
+.BR \-s , \-\-subst
+.RI < subst >
+.RB [ \-i , \-\-input
+.RI < nin >]
+.RB [ \-o , \-\-output
+.RI < nout >]
+.SH DESCRIPTION
+Map
+.I nrrd
+through one univariate substitution table (itself represented as a
+.IR nrrd ).
+The substitution table is a
+.RI 2\-by\- N
+table: a list of pairs giving the old value (to replace) and the the new value
+(to change to), in that order.
+A plain text file makes this easy.
+Unlike with
+.BR unu\-lut (1),
+.BR unu\-rmap (1),
+and
+.BR unu\-imap (1),
+the output type is the same as the input type, rather than the type of the lut
+or map.
+.IP \(bu +2n
+Uses
+.B nrrdApply1DSubstitution
+.SH OPTIONS
+.TP
+.B \-s\fR\ <\fIsubst\fR>\ ,\ \fB\-\-subst\fR\ <\fIsubst\fR>
+substitution table to map input
+.I nrrd
+through
+.TP
+.B \-i\fR\ <\fInin\fR>\ ,\ \fB\-\-input\fR\ <\fInin\fR>
+input
+.I nrrd
+.TP
+.B \-o\fR\ <\fInout\fR>\ ,\ \fB\-\-output\fR\ <\fInout\fR>
+output
+.I nrrd
+.RI ( string );
+default:
+.RB \(lq \- \(rq
+.SH "SEE\ ALSO"
+.BR unu (1),
+.BR unu\-lut (1),
+.BR unu\-rmap (1),
+.BR unu\-imap (1)
diff --git a/unu-swap.1 b/unu-swap.1
new file mode 100644
index 0000000..0189d7f
--- /dev/null
+++ b/unu-swap.1
@@ -0,0 +1,36 @@
+.TH UNU\-SWAP "1" "August 2021" "" "User Commands"
+.SH NAME
+unu\-swap \- interchange ordering of two axes
+.SH SYNOPSIS
+.B unu\ swap
+.BR \-a , \-\-axis
+.RI < axisA \ axisB >
+.RB [ \-i , \-\-input
+.RI < nin >]
+.RB [ \-o , \-\-output
+.RI < nout >]
+.SH DESCRIPTION
+Interchange ordering of two axes. Special case of
+.BR unu\-permute (1).
+.IP \(bu +2n
+Uses
+.B nrrdAxesSwap
+.SH OPTIONS
+.TP
+.B \-a\fR\ <\fIaxisA\fR\ \fIaxisB\fR>\ ,\ \fB\-\-axis\fR\ <\fIaxisA\fR\ \fIaxisB\fR>
+the two axes to switch (0-based numbering) (2
+.IR unsigned\ int s)
+.TP
+.B \-i\fR\ <\fInin\fR>\ ,\ \fB\-\-input\fR\ <\fInin\fR>
+input
+.I nrrd
+.TP
+.B \-o\fR\ <\fInout\fR>\ ,\ \fB\-\-output\fR\ <\fInout\fR>
+output
+.I nrrd
+.RI ( string );
+default:
+.RB \(lq \- \(rq
+.SH "SEE\ ALSO"
+.BR unu (1),
+.BR unu\-permute (1)
diff --git a/unu-tile.1 b/unu-tile.1
new file mode 100644
index 0000000..7346ecc
--- /dev/null
+++ b/unu-tile.1
@@ -0,0 +1,57 @@
+.TH UNU\-TILE "1" "August 2021" "" "User Commands"
+.SH NAME
+unu\-tile \- tile slices of one axis into two other axes
+.SH SYNOPSIS
+.B unu\ tile
+.BR \-a , \-\-axis
+.RI < axSplit \ ax0 \ ax1 >
+.BR \-s , \-\-size
+.RI < fast \ slow >
+.RB [ \-i , \-\-input
+.RI < nin >]
+.RB [ \-o , \-\-output
+.RI < nout >]
+.SH DESCRIPTION
+Tile slices of one axis into two other axes.
+Tiling an array means splitting one axis into fast and slow parts, and then
+interleaving those parts into other (existing) axes by doing two axis merges,
+which combine an existing axis with part of the split axis.
+This reduces the dimension by one.
+The three axis arguments all identify axes in the input array as is.
+This provides, for example, a simple way of viewing the 128 slices along the
+slow axis of a 3\-D volume as a 16x8 tiled array of 2\-D slices, as with
+.RB \(lq \-a\ 2\ 0\ 1\ \-s\ 16\ 8 \(rq.
+.IP \(bu +2n
+Uses
+.B nrrdTile2D
+.SH OPTIONS
+.TP
+.B \-a\fR\ <\fIaxSplit\fR\ \fIax0\fR\ \fIax1\fR>\ ,\ \fB\-\-axis\fR\ <\fIaxSplit\fR\ \fIax0\fR\ \fIax1\fR>
+.I axSplit
+is divided and merged with
+.I ax0
+and
+.I ax1
+(3
+.IR unsigned\ int s)
+.TP
+.B \-s\fR\ <\fIfast\fR\ \fIslow\fR>\ ,\ \fB\-\-size\fR\ <\fIfast\fR\ \fIslow\fR>
+fast and slow axis sizes to produce as result of splitting the
+.I axSplit
+ axis.
+(2
+.IR size_t s)
+.TP
+.B \-i\fR\ <\fInin\fR>\ ,\ \fB\-\-input\fR\ <\fInin\fR>
+input
+.I nrrd
+.TP
+.B \-o\fR\ <\fInout\fR>\ ,\ \fB\-\-output\fR\ <\fInout\fR>
+output
+.I nrrd
+.RI ( string );
+default:
+.RB \(lq \- \(rq
+.SH "SEE\ ALSO"
+.BR unu (1),
+.BR unu\-untile (1)
diff --git a/unu-unorient.1 b/unu-unorient.1
new file mode 100644
index 0000000..7a078db
--- /dev/null
+++ b/unu-unorient.1
@@ -0,0 +1,38 @@
+.TH UNU\-UNORIENT "1" "August 2021" "" "User Commands"
+.SH NAME
+unu\-unorient \- make image orientation be axis-aligned
+.SH SYNOPSIS
+.B unu\ unorient
+.BR \-i , \-\-input
+.RI < nin >
+.RB [ \-smfo ]
+.RB [ \-o , \-\-output
+.RI < nout >]
+.SH DESCRIPTION
+Make image orientation be axis-aligned.
+Does various tricks.
+.IP \(bu +2n
+Uses
+.B nrrdOrientationReduce
+.SH OPTIONS
+.TP
+.B \-i\fR\ <\fInin\fR>\ ,\ \fB\-\-input\fR\ <\fInin\fR>
+input
+.I nrrd
+(sorry, can’t use usual default of
+.RB \(lq \- \(rq
+for
+.I stdin
+because of hest quirk)
+.TP
+.B \-smfo
+set some axis mins based on space origin (hack)
+.TP
+.B \-o\fR\ <\fInout\fR>\ ,\ \fB\-\-output\fR\ <\fInout\fR>
+output
+.I nrrd
+.RI ( string );
+default:
+.RB \(lq \- \(rq
+.SH "SEE\ ALSO"
+.BR unu (1)
diff --git a/unu-unquantize.1 b/unu-unquantize.1
new file mode 100644
index 0000000..9c81558
--- /dev/null
+++ b/unu-unquantize.1
@@ -0,0 +1,83 @@
+.TH UNU\-UNQUANTIZE "1" "August 2021" "" "User Commands"
+.SH NAME
+unu\-unquantize \- recover floating point values from quantized data
+.SH SYNOPSIS
+.B unu\ unquantize
+.BR \-i , \-\-input
+.RI < nin >
+.RB [ \-min , \-\-minimum
+.RI < value >]
+.RB [ \-max , \-\-maximum
+.RI < value >]
+.RB [ \-double ]
+.RB [ \-o , \-\-output
+.RI < nout >]
+.SH DESCRIPTION
+Recover floating point values from quantized data.
+Uses the
+.B oldMin
+and
+.B oldMax
+fields in the
+.I nrrd
+of quantized values to regenerate approximate versions of the original
+unquantized values.
+Can also override these with
+.RB \(lq \-min \(rq
+and
+.RB \(lq \-max \(rq.
+.IP \(bu +2n
+Uses
+.B nrrdUnquantize
+.SH OPTIONS
+.TP
+.B \-i\fR\ <\fInin\fR>\ ,\ \fB\-\-input\fR\ <\fInin\fR>
+input
+.IR nrrd .
+That this argument is required instead of optional, as with most
+.BR unu (1)
+commands, is a quirk caused by the need to have
+.BR unu\-unquantize (1)
+generate usage info, combined with the fact that all the other arguments have
+sensible defaults.
+.TP
+.B \-min\fR\ <\fIvalue\fR>\ ,\ \fB\-\-minimum\fR\ <\fIvalue\fR>
+Defaults to
+.IB nin ->oldMin
+if it exists, otherwise
+.B 0.0
+.RI ( double )
+.TP
+.B \-max\fR\ <\fIvalue\fR>\ ,\ \fB\-\-maximum\fR\ <\fIvalue\fR>
+Defaults to
+.IB nin ->oldMax
+if it exists, otherwise
+.B 1.0
+.RI ( double )
+.TP
+.B \-double
+If not using
+.RB \(lq \-min \(rq
+or
+.RB \(lq \-max \(rq,
+whether to know the range of 8-bit data blindly
+.RI ( uchar
+is always
+.BR [0,255] ,
+.I signed char
+is
+.BR [-128,127] )
+.RI ( bool );
+default:
+.RB \(lq true \(rq,
+"true"
+.TP
+.B \-o\fR\ <\fInout\fR>\ ,\ \fB\-\-output\fR\ <\fInout\fR>
+output
+.I nrrd
+.RI ( string );
+default:
+.RB \(lq \- \(rq
+.SH "SEE\ ALSO"
+.BR unu (1),
+.BR unu\-quantize (1)
diff --git a/unu-untile.1 b/unu-untile.1
new file mode 100644
index 0000000..f1674af
--- /dev/null
+++ b/unu-untile.1
@@ -0,0 +1,90 @@
+.TH UNU\-UNTILE "1" "August 2021" "" "User Commands"
+.SH NAME
+unu\-untile \- undo
+.BR unu\-tile (1):
+merge slow parts of two axis splits
+.SH SYNOPSIS
+.B unu\ untile
+.BR \-a , \-\-axis
+.RI < axMerge \ ax0 \ ax1 >
+.BR \-s , \-\-size
+.RI < size0 \ size1 >
+.RB [ \-i , \-\-input
+.RI < nin >]
+.RB [ \-o , \-\-output
+.RI < nout >]
+.SH DESCRIPTION
+Undo
+.BR unu\-tile (1):
+merge slow parts of two axis splits.
+Untiling an array means spliting two axes, permuting the slow parts of those
+axes to be adjecent in the axis ordering, and then merging them.
+This increases the dimension by one.
+Undoing a
+.BR unu\-tile (1)
+uses the same
+.RB \(lq \-s \(rq
+argument, and
+sometimes a different
+.RB \(lq \-a \(rq
+argument, as demonstrated here for a 3\-D array:
+.IP
+.RB \(lq unu\ untile\ \-a\ 2\ 0\ 1 \(rq
+undoes
+.RB \(lq unu\ tile\ \-a\ 2\ 0\ 1 \(rq
+.IP
+.RB \(lq unu\ untile\ \-a\ 1\ 0\ 1 \(rq
+undoes
+.RB \(lq unu\ tile\ \-a\ 1\ 0\ 2 \(rq
+.IP
+.RB \(lq unu\ untile\ \-a\ 0\ 0\ 1 \(rq
+undoes
+.RB \(lq unu\ tile\ \-a\ 0\ 1\ 2 \(rq.
+.IP \(bu +2n
+Uses
+.B nrrdUntile2D
+.SH OPTIONS
+.TP
+.B \-a\fR\ <\fIaxMerge\fR\ \fIax0\fR\ \fIax1\fR>\ ,\ \fB\-\-axis\fR\ <\fIaxMerge\fR\ \fIax0\fR\ \fIax1\fR>
+the slow parts of axes
+.I ax0
+and
+.I ax1
+are merged into a (new) axis
+.IR axMerge ,
+with the axis
+.I ax0
+part being faster than
+.IR ax1 .
+(3
+.IR unsigned\ int s)
+.TP
+.B \-s\fR\ <\fIsize0\fR\ \fIsize1\fR>\ ,\ \fB\-\-size\fR\ <\fIsize0\fR\ \fIsize1\fR>
+the slow parts of axes
+.I ax0
+and
+.I ax1
+are taken to have size
+.I size0
+and
+.IR size1 ,
+respectively, and axis
+.I axMerge
+will have size
+.IB size0 * size1\fR.
+(2
+.IR size_t s)
+.TP
+.B \-i\fR\ <\fInin\fR>\ ,\ \fB\-\-input\fR\ <\fInin\fR>
+input
+.I nrrd
+.TP
+.B \-o\fR\ <\fInout\fR>\ ,\ \fB\-\-output\fR\ <\fInout\fR>
+output
+.I nrrd
+.RI ( string );
+default:
+.RB \(lq \- \(rq
+.SH "SEE\ ALSO"
+.BR unu (1),
+.BR unu\-tile (1)
diff --git a/unu-w2i.1 b/unu-w2i.1
new file mode 100644
index 0000000..17dd885
--- /dev/null
+++ b/unu-w2i.1
@@ -0,0 +1,58 @@
+.TH UNU\-W2I "1" "April 2021" "" "User Commands"
+.SH NAME
+unu\-w2i \- converts from 1-D world to index position
+.SH SYNOPSIS
+.B unu\ w2i
+.RI < center >
+.RI < minPos >
+.RI < maxPos >
+.RI < num >
+.RI < world >
+.SH DESCRIPTION
+Converts from 1-D world to index position, given the centering of the data
+.RB ( cell
+vs.
+.BR node ),
+the range of positions, and the number of intervals into which position has
+been quantized.
+This is a demo/utility, which does not actually operate on any
+.IR nrrd s.
+Previously available as the stand-alone
+.B pos2idx
+binary.
+.P
+Uses
+.B NRRD_IDX
+macro
+.SH OPTIONS
+.TP
+.RI < center >
+which centering applies to the quantized position.
+Possibilities are:
+.RS +7n
+.IP \(bu 2
+.RB \(lq cell \(rq
+for histogram bins, quantized values, and pixels-as-squares
+.IP \(bu 2
+.RB \(lq node \(rq
+for non-trivially interpolated sample points
+.RE
+.TP
+.RI < minPos >
+smallest position associated with index
+.B 0
+.RI ( double )
+.TP
+.RI < maxPos >
+highest position associated with highest index
+.RI ( double )
+.TP
+.RI < num >
+number of intervals into which position has been quantized
+.RI ( double )
+.TP
+.RI < world >
+the input world position, to be converted to index
+.RI ( double )
+.SH "SEE\ ALSO"
+.BR unu (1)
diff --git a/unu.1 b/unu.1
index 6f8b611..cb49b63 100644
--- a/unu.1
+++ b/unu.1
@@ -4,7 +4,7 @@ unu \- Utah Nrrd Utilities command-line interface
.SH SYNOPSIS
.B unu
.I COMMAND
-.RI [ OPTION ]...
+.RI [ OPTION ]...\:
.SH DESCRIPTION
.BR unu (1)
is a command-line interface to much of the functionality in \(lqnrrd\(rq, a C
@@ -116,11 +116,11 @@ Formats available:
Nrrd data encodings available:
.B raw ascii hex gz bz2
.SH COMMANDS
-\" .P
-\" Each of the following has a corresponding man page: for example,
-\" .BR unu\-about (1)
-\" for
-\" .RB \(lq unu\ about \(rq.
+.P
+Each of the following has a corresponding man page: for example,
+.BR unu\-about (1)
+for
+.RB \(lq unu\ about \(rq.
.SS unu about
Information about this program and its use
.SS unu env
@@ -303,6 +303,9 @@ Remap CC values down to lowest contiguous values
Write
.I nrrd
with specific format, encoding, or endianness
+.SH ENVIRONMENT
+See
+.BR unu\-env (1).
.SH "SEE\ ALSO"
.BR gprobe (1),
.BR ilk (1),
@@ -312,67 +315,66 @@ with specific format, encoding, or endianness
.BR overrgb (1),
.BR puller (1),
.BR tend (1),
-.BR vprobe (1)
-\" .BR vprobe (1),
-\" .BR unu-1op (1),
-\" .BR unu-2op (1),
-\" .BR unu-3op (1),
-\" .BR unu-about (1),
-\" .BR unu-acrop (1),
-\" .BR unu-affine (1),
-\" .BR unu-axdelete (1),
-\" .BR unu-axinfo (1),
-\" .BR unu-axinsert (1),
-\" .BR unu-axmerge (1),
-\" .BR unu-axsplit (1),
-\" .BR unu-ccadj (1),
-\" .BR unu-ccfind (1),
-\" .BR unu-ccmerge (1),
-\" .BR unu-ccsettle (1),
-\" .BR unu-cksum (1),
-\" .BR unu-cmedian (1),
-\" .BR unu-convert (1),
-\" .BR unu-crop (1),
-\" .BR unu-data (1),
-\" .BR unu-dhisto (1),
-\" .BR unu-dice (1),
-\" .BR unu-diff (1),
-\" .BR unu-dist (1),
-\" .BR unu-env (1),
-\" .BR unu-fft (1),
-\" .BR unu-flip (1),
-\" .BR unu-gamma (1),
-\" .BR unu-head (1),
-\" .BR unu-heq (1),
-\" .BR unu-histax (1),
-\" .BR unu-histo (1),
-\" .BR unu-i2w (1),
-\" .BR unu-imap (1),
-\" .BR unu-inset (1),
-\" .BR unu-jhisto (1),
-\" .BR unu-join (1),
-\" .BR unu-lut (1),
-\" .BR unu-lut2 (1),
-\" .BR unu-make (1),
-\" .BR unu-minmax (1),
-\" .BR unu-mlut (1),
-\" .BR unu-mrmap (1),
-\" .BR unu-pad (1),
-\" .BR unu-permute (1),
-\" .BR unu-project (1),
-\" .BR unu-quantize (1),
-\" .BR unu-resample (1),
-\" .BR unu-reshape (1),
-\" .BR unu-rmap (1),
-\" .BR unu-save (1),
-\" .BR unu-shuffle (1),
-\" .BR unu-slice (1),
-\" .BR unu-splice (1),
-\" .BR unu-sselect (1),
-\" .BR unu-subst (1),
-\" .BR unu-swap (1),
-\" .BR unu-tile (1),
-\" .BR unu-unorient (1),
-\" .BR unu-unquantize (1),
-\" .BR unu-untile (1),
-\" .BR unu-w2i (1)
+.BR vprobe (1),
+.BR unu-1op (1),
+.BR unu-2op (1),
+.BR unu-3op (1),
+.BR unu-about (1),
+.BR unu-acrop (1),
+.BR unu-affine (1),
+.BR unu-axdelete (1),
+.BR unu-axinfo (1),
+.BR unu-axinsert (1),
+.BR unu-axmerge (1),
+.BR unu-axsplit (1),
+.BR unu-ccadj (1),
+.BR unu-ccfind (1),
+.BR unu-ccmerge (1),
+.BR unu-ccsettle (1),
+.BR unu-cksum (1),
+.BR unu-cmedian (1),
+.BR unu-convert (1),
+.BR unu-crop (1),
+.BR unu-data (1),
+.BR unu-dhisto (1),
+.BR unu-dice (1),
+.BR unu-diff (1),
+.BR unu-dist (1),
+.BR unu-env (1),
+.BR unu-fft (1),
+.BR unu-flip (1),
+.BR unu-gamma (1),
+.BR unu-head (1),
+.BR unu-heq (1),
+.BR unu-histax (1),
+.BR unu-histo (1),
+.BR unu-i2w (1),
+.BR unu-imap (1),
+.BR unu-inset (1),
+.BR unu-jhisto (1),
+.BR unu-join (1),
+.BR unu-lut (1),
+.BR unu-lut2 (1),
+.BR unu-make (1),
+.BR unu-minmax (1),
+.BR unu-mlut (1),
+.BR unu-mrmap (1),
+.BR unu-pad (1),
+.BR unu-permute (1),
+.BR unu-project (1),
+.BR unu-quantize (1),
+.BR unu-resample (1),
+.BR unu-reshape (1),
+.BR unu-rmap (1),
+.BR unu-save (1),
+.BR unu-shuffle (1),
+.BR unu-slice (1),
+.BR unu-splice (1),
+.BR unu-sselect (1),
+.BR unu-subst (1),
+.BR unu-swap (1),
+.BR unu-tile (1),
+.BR unu-unorient (1),
+.BR unu-unquantize (1),
+.BR unu-untile (1),
+.BR unu-w2i (1)
commit 516324379066d7660be0b0eaa7e3ff0bd06b27c9
Author: Benjamin A. Beasley <code(a)musicinmybrain.net>
Date: Wed Jul 21 16:06:33 2021 -0400
Opt in to rpmautospec
diff --git a/changelog b/changelog
new file mode 100644
index 0000000..833cd80
--- /dev/null
+++ b/changelog
@@ -0,0 +1,60 @@
+* Tue Apr 13 2021 Benjamin A. Beasley <code(a)musicinmybrain.net> - 1.11.0-16
+- Finish man pages, except “unu” sub-pages. Start installing man pages.
+
+* Sat Apr 03 2021 Benjamin A. Beasley <code(a)musicinmybrain.net> - 1.11.0-15
+- Fix typo in lib_install_dir.patch
+- Update download URL from HTTP to HTTPS
+- Improved package summaries and descriptions
+- Adjust whitespace to personal preference
+- Fix stray line continuation backslash
+- Support EPEL7
+- Build with “levmar” support enabled
+- Use stricter file globs, including specifying the so-version
+- Add an -examples subpackage so the BSD and zlib licenses can be “contained”
+ to that subpackage; remove certain unused files with other licenses in %%prep
+- Require cmake-filesystem instead of all of CMake in -devel
+- More precisely skip overly-strict floating-point tests where required
+- Start work on adding a complete set of man pages
+
+* Wed Jan 27 2021 Fedora Release Engineering <releng(a)fedoraproject.org> - 1.11.0-14
+- Rebuilt for https://fedoraproject.org/wiki/Fedora_34_Mass_Rebuild
+
+* Fri Sep 04 2020 Ankur Sinha <ankursinha AT fedoraproject DOT org> - 1.11.0-13
+- Use cmake macros to fix build
+
+* Sat Aug 01 2020 Fedora Release Engineering <releng(a)fedoraproject.org> - 1.11.0-12
+- Second attempt - Rebuilt for
+ https://fedoraproject.org/wiki/Fedora_33_Mass_Rebuild
+
+* Wed Jul 29 2020 Fedora Release Engineering <releng(a)fedoraproject.org> - 1.11.0-11
+- Rebuilt for https://fedoraproject.org/wiki/Fedora_33_Mass_Rebuild
+
+* Fri Jan 31 2020 Fedora Release Engineering <releng(a)fedoraproject.org> - 1.11.0-10
+- Rebuilt for https://fedoraproject.org/wiki/Fedora_32_Mass_Rebuild
+
+* Sat Jul 27 2019 Fedora Release Engineering <releng(a)fedoraproject.org> - 1.11.0-9
+- Rebuilt for https://fedoraproject.org/wiki/Fedora_31_Mass_Rebuild
+
+* Sun Feb 03 2019 Fedora Release Engineering <releng(a)fedoraproject.org> - 1.11.0-8
+- Rebuilt for https://fedoraproject.org/wiki/Fedora_30_Mass_Rebuild
+
+* Sat Jul 14 2018 Fedora Release Engineering <releng(a)fedoraproject.org> - 1.11.0-7
+- Rebuilt for https://fedoraproject.org/wiki/Fedora_29_Mass_Rebuild
+
+* Fri Feb 09 2018 Fedora Release Engineering <releng(a)fedoraproject.org> - 1.11.0-6
+- Rebuilt for https://fedoraproject.org/wiki/Fedora_28_Mass_Rebuild
+
+* Thu Aug 03 2017 Fedora Release Engineering <releng(a)fedoraproject.org> - 1.11.0-5
+- Rebuilt for https://fedoraproject.org/wiki/Fedora_27_Binutils_Mass_Rebuild
+
+* Thu Jul 27 2017 Fedora Release Engineering <releng(a)fedoraproject.org> - 1.11.0-4
+- Rebuilt for https://fedoraproject.org/wiki/Fedora_27_Mass_Rebuild
+
+* Sat Feb 11 2017 Fedora Release Engineering <releng(a)fedoraproject.org> - 1.11.0-3
+- Rebuilt for https://fedoraproject.org/wiki/Fedora_26_Mass_Rebuild
+
+* Fri Feb 05 2016 Fedora Release Engineering <releng(a)fedoraproject.org> - 1.11.0-2
+- Rebuilt for https://fedoraproject.org/wiki/Fedora_24_Mass_Rebuild
+
+* Wed Nov 25 2015 Dmitry Mikhirev <mikhirev(a)gmail.com> 1.11.0-1
+- Initial package
diff --git a/teem.spec b/teem.spec
index 5c62687..aded044 100644
--- a/teem.spec
+++ b/teem.spec
@@ -1,7 +1,7 @@
Name: teem
Version: 1.11.0
%global so_version 1
-Release: 16%{?dist}
+Release: %autorelease -b 13
Summary: Libraries for processing and visualizing scientific raster data
# The entire source is LGPLv2+, except files noted at the License fields of certain subpackages.
@@ -351,63 +351,4 @@ install -t %{buildroot}%{_mandir}/man1 -p -m 0644 \
%changelog
-* Tue Apr 13 2021 Benjamin A. Beasley <code(a)musicinmybrain.net> - 1.11.0-16
-- Finish man pages, except “unu” sub-pages. Start installing man pages.
-
-* Sat Apr 03 2021 Benjamin A. Beasley <code(a)musicinmybrain.net> - 1.11.0-15
-- Fix typo in lib_install_dir.patch
-- Update download URL from HTTP to HTTPS
-- Improved package summaries and descriptions
-- Adjust whitespace to personal preference
-- Fix stray line continuation backslash
-- Support EPEL7
-- Build with “levmar” support enabled
-- Use stricter file globs, including specifying the so-version
-- Add an -examples subpackage so the BSD and zlib licenses can be “contained”
- to that subpackage; remove certain unused files with other licenses in %%prep
-- Require cmake-filesystem instead of all of CMake in -devel
-- More precisely skip overly-strict floating-point tests where required
-- Start work on adding a complete set of man pages
-
-* Wed Jan 27 2021 Fedora Release Engineering <releng(a)fedoraproject.org> - 1.11.0-14
-- Rebuilt for https://fedoraproject.org/wiki/Fedora_34_Mass_Rebuild
-
-* Fri Sep 04 2020 Ankur Sinha <ankursinha AT fedoraproject DOT org> - 1.11.0-13
-- Use cmake macros to fix build
-
-* Sat Aug 01 2020 Fedora Release Engineering <releng(a)fedoraproject.org> - 1.11.0-12
-- Second attempt - Rebuilt for
- https://fedoraproject.org/wiki/Fedora_33_Mass_Rebuild
-
-* Wed Jul 29 2020 Fedora Release Engineering <releng(a)fedoraproject.org> - 1.11.0-11
-- Rebuilt for https://fedoraproject.org/wiki/Fedora_33_Mass_Rebuild
-
-* Fri Jan 31 2020 Fedora Release Engineering <releng(a)fedoraproject.org> - 1.11.0-10
-- Rebuilt for https://fedoraproject.org/wiki/Fedora_32_Mass_Rebuild
-
-* Sat Jul 27 2019 Fedora Release Engineering <releng(a)fedoraproject.org> - 1.11.0-9
-- Rebuilt for https://fedoraproject.org/wiki/Fedora_31_Mass_Rebuild
-
-* Sun Feb 03 2019 Fedora Release Engineering <releng(a)fedoraproject.org> - 1.11.0-8
-- Rebuilt for https://fedoraproject.org/wiki/Fedora_30_Mass_Rebuild
-
-* Sat Jul 14 2018 Fedora Release Engineering <releng(a)fedoraproject.org> - 1.11.0-7
-- Rebuilt for https://fedoraproject.org/wiki/Fedora_29_Mass_Rebuild
-
-* Fri Feb 09 2018 Fedora Release Engineering <releng(a)fedoraproject.org> - 1.11.0-6
-- Rebuilt for https://fedoraproject.org/wiki/Fedora_28_Mass_Rebuild
-
-* Thu Aug 03 2017 Fedora Release Engineering <releng(a)fedoraproject.org> - 1.11.0-5
-- Rebuilt for https://fedoraproject.org/wiki/Fedora_27_Binutils_Mass_Rebuild
-
-* Thu Jul 27 2017 Fedora Release Engineering <releng(a)fedoraproject.org> - 1.11.0-4
-- Rebuilt for https://fedoraproject.org/wiki/Fedora_27_Mass_Rebuild
-
-* Sat Feb 11 2017 Fedora Release Engineering <releng(a)fedoraproject.org> - 1.11.0-3
-- Rebuilt for https://fedoraproject.org/wiki/Fedora_26_Mass_Rebuild
-
-* Fri Feb 05 2016 Fedora Release Engineering <releng(a)fedoraproject.org> - 1.11.0-2
-- Rebuilt for https://fedoraproject.org/wiki/Fedora_24_Mass_Rebuild
-
-* Wed Nov 25 2015 Dmitry Mikhirev <mikhirev(a)gmail.com> 1.11.0-1
-- Initial package
+%autochangelog
commit 7dd3eb9d51ce2c2984ed8f6c467d7483da181c65
Author: Benjamin A. Beasley <code(a)musicinmybrain.net>
Date: Tue Apr 13 12:04:10 2021 -0400
Finish man pages, except “unu” sub-pages. Start installing man pages.
diff --git a/ilk.1 b/ilk.1
index 51b2915..e88f365 100644
--- a/ilk.1
+++ b/ilk.1
@@ -33,7 +33,7 @@ Applies linear (homogenous coordinate) transforms to a given image, using the
given kernel for resampling.
.SH OPTIONS
.TP
-.BR @file \ ...
+.IR @file \ ...
response file(s) containing command-line arguments
.TP
.B \-i\fR <\fIimage\fR>
diff --git a/miter.1 b/miter.1
index 75d7ef4..254f8b8 100644
--- a/miter.1
+++ b/miter.1
@@ -79,7 +79,7 @@ miter \- a simple but effective little volume renderer
.RI < filename >]
.SH OPTIONS
.TP
-.BR @file \ ...
+.IR @file \ ...
response file(s) containing command-line arguments
.TP
.B \-i\fR <\fInsin\fR>
diff --git a/mrender.1 b/mrender.1
index f619ac2..c58da9e 100644
--- a/mrender.1
+++ b/mrender.1
@@ -69,7 +69,7 @@ in the output
image.
.SH OPTIONS
.TP
-.BR @file \ ...
+.IR @file \ ...
response file(s) containing command-line arguments
.TP
.B \-i\fR <\fInin\fR>
diff --git a/overrgb.1 b/overrgb.1
index 538bf57..a9e2a0c 100644
--- a/overrgb.1
+++ b/overrgb.1
@@ -31,9 +31,6 @@ have to be the same size as the input image; it will be resampled (with linear
interpolation) to fit.
.SH OPTIONS
.TP
-.BR @file \ ...
-response file(s) containing command-line arguments
-.TP
.B \-i\fR <\fInin\fR>
input nrrd to composite
.RI ( nrrd )
diff --git a/puller.1 b/puller.1
index fb03592..45b7635 100644
--- a/puller.1
+++ b/puller.1
@@ -3,7 +3,7 @@
puller \- command-line interface to the \(lqpull\(rq library
.SH SYNOPSIS
.B puller
-.RB [ @file \ ...]
+.RI [ @file \ ...]
.RB [ \-int
.RI < int >]
.RB [ \-enr
@@ -139,7 +139,7 @@ puller \- command-line interface to the \(lqpull\(rq library
.RI < base >]
.SH OPTIONS
.TP
-.BR @file \ ...
+.IR @file \ ...
response file(s) containing command-line arguments
.TP
.B \-int\fR <\fIint\fR>
diff --git a/teem.spec b/teem.spec
index 61764d0..5c62687 100644
--- a/teem.spec
+++ b/teem.spec
@@ -1,11 +1,7 @@
-# Writing man pages is a work in progress. We do not install them until we have
-# a complete set.
-%bcond_with man_pages
-
Name: teem
Version: 1.11.0
%global so_version 1
-Release: 15%{?dist}
+Release: 16%{?dist}
Summary: Libraries for processing and visualizing scientific raster data
# The entire source is LGPLv2+, except files noted at the License fields of certain subpackages.
@@ -45,36 +41,36 @@ Source807: tend-mconv.1
Source808: tend-msim.1
Source809: tend-make.1
Source810: tend-avg.1
-#Source811: tend-helix.1
-#Source812: tend-sten.1
-#Source813: tend-glyph.1
-#Source814: tend-ellipse.1
-#Source815: tend-anplot.1
-#Source816: tend-anvol.1
-#Source817: tend-anscale.1
-#Source818: tend-anhist.1
-#Source819: tend-triple.1
-#Source820: tend-tconv.1
-#Source821: tend-point.1
-#Source822: tend-slice.1
-#Source823: tend-norm.1
-#Source824: tend-fiber.1
-#Source825: tend-eval.1
-#Source826: tend-evalpow.1
-#Source827: tend-evalclamp.1
-#Source828: tend-evaladd.1
-#Source829: tend-evalmul.1
-#Source830: tend-log.1
-#Source831: tend-exp.1
-#Source832: tend-evec.1
-#Source833: tend-evecrgb.1
-#Source834: tend-evq.1
-#Source835: tend-unmf.1
-#Source836: tend-expand.1
-#Source837: tend-shrink.1
-#Source838: tend-bfit.1
-#Source839: tend-satin.1
-#
+Source811: tend-helix.1
+Source812: tend-sten.1
+Source813: tend-glyph.1
+Source814: tend-ellipse.1
+Source815: tend-anplot.1
+Source816: tend-anvol.1
+Source817: tend-anscale.1
+Source818: tend-anhist.1
+Source819: tend-triple.1
+Source820: tend-tconv.1
+Source821: tend-point.1
+Source822: tend-slice.1
+Source823: tend-norm.1
+Source824: tend-fiber.1
+Source825: tend-eval.1
+Source826: tend-evalpow.1
+Source827: tend-evalclamp.1
+Source828: tend-evaladd.1
+Source829: tend-evalmult.1
+Source830: tend-log.1
+Source831: tend-exp.1
+Source832: tend-evec.1
+Source833: tend-evecrgb.1
+Source834: tend-evq.1
+Source835: tend-unmf.1
+Source836: tend-expand.1
+Source837: tend-shrink.1
+Source838: tend-bfit.1
+Source839: tend-satin.1
+
#Source900: unu-about.1
#Source901: unu-env.1
#Source902: unu-i2w.1
@@ -273,20 +269,20 @@ install -t %{buildroot}%{_mandir}/man1 -p -m 0644 \
%{SOURCE820} %{SOURCE821} %{SOURCE822} %{SOURCE823} %{SOURCE824} \
%{SOURCE825} %{SOURCE826} %{SOURCE827} %{SOURCE828} %{SOURCE829} \
%{SOURCE830} %{SOURCE831} %{SOURCE832} %{SOURCE833} %{SOURCE834} \
- %{SOURCE835} %{SOURCE836} %{SOURCE837} %{SOURCE838} %{SOURCE839} \
- %{SOURCE900} %{SOURCE901} %{SOURCE902} %{SOURCE903} %{SOURCE904} \
- %{SOURCE905} %{SOURCE906} %{SOURCE907} %{SOURCE908} %{SOURCE909} \
- %{SOURCE910} %{SOURCE911} %{SOURCE912} %{SOURCE913} %{SOURCE914} \
- %{SOURCE915} %{SOURCE916} %{SOURCE917} %{SOURCE918} %{SOURCE919} \
- %{SOURCE920} %{SOURCE921} %{SOURCE922} %{SOURCE923} %{SOURCE924} \
- %{SOURCE925} %{SOURCE926} %{SOURCE927} %{SOURCE928} %{SOURCE929} \
- %{SOURCE930} %{SOURCE931} %{SOURCE932} %{SOURCE933} %{SOURCE934} \
- %{SOURCE935} %{SOURCE936} %{SOURCE937} %{SOURCE938} %{SOURCE939} \
- %{SOURCE940} %{SOURCE941} %{SOURCE942} %{SOURCE943} %{SOURCE944} \
- %{SOURCE945} %{SOURCE946} %{SOURCE947} %{SOURCE948} %{SOURCE949} \
- %{SOURCE950} %{SOURCE951} %{SOURCE952} %{SOURCE953} %{SOURCE954} \
- %{SOURCE955} %{SOURCE956} %{SOURCE957} %{SOURCE958} %{SOURCE959} \
- %{SOURCE960} %{SOURCE961}
+ %{SOURCE835} %{SOURCE836} %{SOURCE837} %{SOURCE838} %{SOURCE839}
+ #{SOURCE900} #{SOURCE901} #{SOURCE902} #{SOURCE903} #{SOURCE904} \
+ #{SOURCE905} #{SOURCE906} #{SOURCE907} #{SOURCE908} #{SOURCE909} \
+ #{SOURCE910} #{SOURCE911} #{SOURCE912} #{SOURCE913} #{SOURCE914} \
+ #{SOURCE915} #{SOURCE916} #{SOURCE917} #{SOURCE918} #{SOURCE919} \
+ #{SOURCE920} #{SOURCE921} #{SOURCE922} #{SOURCE923} #{SOURCE924} \
+ #{SOURCE925} #{SOURCE926} #{SOURCE927} #{SOURCE928} #{SOURCE929} \
+ #{SOURCE930} #{SOURCE931} #{SOURCE932} #{SOURCE933} #{SOURCE934} \
+ #{SOURCE935} #{SOURCE936} #{SOURCE937} #{SOURCE938} #{SOURCE939} \
+ #{SOURCE940} #{SOURCE941} #{SOURCE942} #{SOURCE943} #{SOURCE944} \
+ #{SOURCE945} #{SOURCE946} #{SOURCE947} #{SOURCE948} #{SOURCE949} \
+ #{SOURCE950} #{SOURCE951} #{SOURCE952} #{SOURCE953} #{SOURCE954} \
+ #{SOURCE955} #{SOURCE956} #{SOURCE957} #{SOURCE958} #{SOURCE959} \
+ #{SOURCE960} #{SOURCE961}
%endif
@@ -331,7 +327,7 @@ install -t %{buildroot}%{_mandir}/man1 -p -m 0644 \
%{_mandir}/man1/tend.1*
%{_mandir}/man1/tend-*.1*
%{_mandir}/man1/unu.1*
-%{_mandir}/man1/unu-*.1*
+#{_mandir}/man1/unu-*.1*
%{_mandir}/man1/vprobe.1*
%endif
@@ -355,6 +351,9 @@ install -t %{buildroot}%{_mandir}/man1 -p -m 0644 \
%changelog
+* Tue Apr 13 2021 Benjamin A. Beasley <code(a)musicinmybrain.net> - 1.11.0-16
+- Finish man pages, except “unu” sub-pages. Start installing man pages.
+
* Sat Apr 03 2021 Benjamin A. Beasley <code(a)musicinmybrain.net> - 1.11.0-15
- Fix typo in lib_install_dir.patch
- Update download URL from HTTP to HTTPS
diff --git a/tend-anhist.1 b/tend-anhist.1
new file mode 100644
index 0000000..a475647
--- /dev/null
+++ b/tend-anhist.1
@@ -0,0 +1,61 @@
+.TH TEND\-ANHIST "1" "April 2021" "" "User Commands"
+.SH NAME
+tend\-anhist \- generate barycentric histograms of anisotropy
+.SH SYNOPSIS
+.B tend\ anhist
+.RB [ \-v
+.RI < westin\ version >]
+.RB [ \-w
+.RI < nweight >]
+.B \-r
+.RI < res >
+.RB [ \-right ]
+.RB [ \-i
+.RI < nin >]
+.RB [ \-o
+.RI < nout >]
+.SH DESCRIPTION
+Generate barycentric histograms of anisotropy.
+The barycentric space used is either one of Westin\(cqs triple of spherical,
+linear, and planar anisotropy.
+The bin counts in the histogram are weighted by the confidence value.
+.SH OPTIONS
+.TP
+.B \-v\fR <\fIwestin\ version\fR>
+Which version of Westin\(cqs anisotropy metric triple to use, either
+.RB \(lq 1 \(rq
+or
+.RB \(lq 2 \(rq
+.RI ( int );
+default:
+.RB \(lq 1 \(rq
+.TP
+.B \-w\fR <\fInweight\fR>
+how to weigh contributions to histogram.
+By default (not using this option), the increment is one bin count per sample,
+but by giving a
+.IR nrrd ,
+the value in the
+.I nrrd
+at the corresponding location will be the bin count increment; default:
+\(lq\(rq
+.TP
+.B \-r\fR <\fIres\fR>
+resolution of anisotropy plot
+.RI ( int )
+.TP
+.B \-right
+sample a right-triangle-shaped region, instead of a roughly equilateral
+triangle.
+.TP
+.B \-i\fR <\fInin\fR>
+input diffusion tensor volume; default:
+.RB \(lq \- \(rq
+.TP
+.B \-o\fR <\fInout\fR>
+output image (floating point)
+.RI ( string )
+default:
+.RB \(lq \- \(rq
+.SH "SEE\ ALSO"
+.BR tend (1)
diff --git a/tend-anplot.1 b/tend-anplot.1
new file mode 100644
index 0000000..44024a6
--- /dev/null
+++ b/tend-anplot.1
@@ -0,0 +1,106 @@
+.TH TEND\-ANPLOT "1" "April 2021" "" "User Commands"
+.SH NAME
+tend\-anplot \- graph anisotropy metric in barycentric coords
+.SH SYNOPSIS
+.B tend\ anplot
+.RB [ \-r
+.RI < res >]
+.RB [ \-w ]
+.RB [ \-hflip ]
+.RB [ \-nan ]
+.B \-a
+.RI < aniso >
+.RB [ \-o
+.RI < nout >]
+.SH DESCRIPTION
+Graph anisotropy metric in barycentric coordinates.
+The metrics all vary from
+.B 0.0
+to
+.BR 1.0 ,
+and will be sampled in the lower right half of the image.
+The plane on which they are sampled is a surface of constant trace.
+You may want to use
+.in +4n
+.EX
+.B unu\ resample\ \-s\ =\ x0.57735\ \-k\ tent
+.in
+.EE
+to transform the triangle into a 30-60-90 triangle, and
+.in +4n
+.EX
+.B ilk\ \-t\ 1,\-0.5,0,0,0.866,0\ \-k\ tent\ \-0\ u:0,1\ \-b\ pad\ \-bg\ 0
+.EE
+.in
+(possibly followed by
+.BR teem/src/limntest/triimg )
+to transform the domain into an equilateral triangle.
+.SH OPTIONS
+.TP
+.B \-res\fR <\fIres\fR>
+resolution of anisotropy plot
+.RI ( int );
+default:
+.RB \(lq 256 \(rq
+.TP
+.B \-w
+sample the whole triangle of constant trace, instead of just the sixth of it in
+which the eigenvalues have the traditional sorted order.
+.TP
+.B \-hflip
+flip the two bottom corners (swapping the place of linear and planar)
+.TP
+.B \-nan
+set the pixel values outside the triangle to be
+.BR NaN ,
+instead of
+.B 0
+.TP
+.B \-a\fR <\fIaniso\fR>
+Which anisotropy metric to plot.
+All the Westin metrics come in two versions.
+Currently supported:
+.RS +7n
+.IP \(bu 2
+.RB \(lq cl1 \(rq,
+.RB \(lq cl2 \(rq:
+Westin\(cqs linear
+.IP \(bu 2
+.RB \(lq cp1 \(rq,
+.RB \(lq cp2 \(rq:
+Westin\(cqs planar
+.IP \(bu 2
+.RB \(lq ca1 \(rq,
+.RB \(lq ca2 \(rq:
+Westin\(cqs linear + planar
+.IP \(bu 2
+.RB \(lq cs1 \(rq,
+.RB \(lq cs2 \(rq:
+Westin\(cqs spherical
+.IP \(bu 2
+.RB \(lq ct1 \(rq,
+.RB \(lq ct2 \(rq:
+GK\(cqs anisotropy type (cp/ca)
+.IP \(bu 2
+.RB \(lq ra \(rq:
+Basser/Pierpaoli relative anisotropy/sqrt(2)
+.IP \(bu 2
+.RB \(lq fa \(rq:
+Basser/Pierpaoli fractional anisotropy
+.IP \(bu 2
+.RB \(lq vf \(rq:
+volume fraction = 1\-(Basser/Pierpaoli volume ratio)
+.IP \(bu 2
+.RB \(lq tr \(rq:
+trace
+.RE
+.TP
+.B \-o\fR <\fInout\fR>
+output image (floating point)
+.RI ( string )
+default:
+.RB \(lq \- \(rq
+.SH "SEE\ ALSO"
+.BR tend (1),
+.BR ilk (1),
+.BR unu\-resample (1)
diff --git a/tend-anscale.1 b/tend-anscale.1
new file mode 100644
index 0000000..740157d
--- /dev/null
+++ b/tend-anscale.1
@@ -0,0 +1,44 @@
+.TH TEND\-ANSCALE "1" "April 2021" "" "User Commands"
+.SH NAME
+tend\-anscale \- scale the anisotropic component of the tensors
+.SH SYNOPSIS
+.B tend\ anscale
+.B \-s
+.RI < scale >
+.RB [ \-fd ]
+.RB [ \-mp ]
+.RB [ \-i
+.RI < nin >]
+.RB [ \-o
+.RI < nout >]
+.SH DESCRIPTION
+Scale the anisotropic component of the tensors.
+This maintains the isotropic component of the tensor, and fixes either the
+trace or determinant, while scaling up (or down) the \(lqdeviatoric\(rq
+component of the tensor.
+Good for exaggerating the shape of nearly isotropic tensors.
+.SH OPTIONS
+.TP
+.B \-s\fR <\fIscale\fR>
+Amount by which to scale deviatoric component of tensor.
+.RI ( float )
+.TP
+.B \-fd
+instead of fixing the per-sample trace (the default), fix the determinant
+(ellipsoid volume)
+.TP
+.B \-mp
+after changing the eigenvalues of the tensor, enforce their non-negative-ness.
+By default, no such constraint is imposed.
+.TP
+.B \-i\fR <\fInin\fR>
+input diffusion tensor volume; default:
+.RB \(lq \- \(rq
+.TP
+.B \-o\fR <\fInout\fR>
+output image (floating point)
+.RI ( string )
+default:
+.RB \(lq \- \(rq
+.SH "SEE\ ALSO"
+.BR tend (1)
diff --git a/tend-anvol.1 b/tend-anvol.1
new file mode 100644
index 0000000..6c06441
--- /dev/null
+++ b/tend-anvol.1
@@ -0,0 +1,75 @@
+.TH TEND\-ANVOL "1" "April 2021" "" "User Commands"
+.SH NAME
+tend\-anvol \- apply an anisotropy metric to a DT volume
+.SH SYNOPSIS
+.B tend\ anvol
+.B \-a
+.RI < aniso >
+.RB [ \-t
+.RI < thresh >]
+.RB [ \-i
+.RI < nin >]
+.RB [ \-o
+.RI < nout >]
+.SH DESCRIPTION
+Apply an anisotropy metric to a DT volume.
+The anisotropy value will be zero in the locations which don\(cqt meet the
+given confidence threshold.
+.SH OPTIONS
+.TP
+.B \-a\fR <\fIaniso\fR>
+Which anisotropy metric to plot.
+All the Westin metrics come in two versions.
+Currently supported:
+.RS +7n
+.IP \(bu 2
+.RB \(lq cl1 \(rq,
+.RB \(lq cl2 \(rq:
+Westin\(cqs linear
+.IP \(bu 2
+.RB \(lq cp1 \(rq,
+.RB \(lq cp2 \(rq:
+Westin\(cqs planar
+.IP \(bu 2
+.RB \(lq ca1 \(rq,
+.RB \(lq ca2 \(rq:
+Westin\(cqs linear + planar
+.IP \(bu 2
+.RB \(lq cs1 \(rq,
+.RB \(lq cs2 \(rq:
+Westin\(cqs spherical
+.IP \(bu 2
+.RB \(lq ct1 \(rq,
+.RB \(lq ct2 \(rq:
+GK\(cqs anisotropy type (cp/ca)
+.IP \(bu 2
+.RB \(lq ra \(rq:
+Basser/Pierpaoli relative anisotropy/sqrt(2)
+.IP \(bu 2
+.RB \(lq fa \(rq:
+Basser/Pierpaoli fractional anisotropy
+.IP \(bu 2
+.RB \(lq vf \(rq:
+volume fraction = 1\-(Basser/Pierpaoli volume ratio)
+.IP \(bu 2
+.RB \(lq tr \(rq:
+trace
+.RE
+.TP
+.B \-t\fR <\fIthresh\fR>
+confidence threshold
+.RI ( float )
+default:
+.RB \(lq 0.5 \(rq
+.TP
+.B \-i\fR <\fInin\fR>
+input diffusion tensor volume; default:
+.RB \(lq \- \(rq
+.TP
+.B \-o\fR <\fInout\fR>
+output image (floating point)
+.RI ( string )
+default:
+.RB \(lq \- \(rq
+.SH "SEE\ ALSO"
+.BR tend (1)
diff --git a/tend-bfit.1 b/tend-bfit.1
new file mode 100644
index 0000000..9269f75
--- /dev/null
+++ b/tend-bfit.1
@@ -0,0 +1,72 @@
+.TH TEND\-BFIT "1" "April 2021" "" "User Commands"
+.SH NAME
+tend\-bfit \- non-linear least-squares fitting of \fIb\fR-value curves
+.SH SYNOPSIS
+.B tend\ bfit
+.RI [ @file \ ...]
+.RB [ \-i
+.RI < nin >]
+.B \-b
+.RI < b1 \ b2 \ ...>
+.RB [ \-w
+.RI < w1 \ w2 \ ...>]
+.RB [ \-imax
+.RI < #\ iter >]
+.RB [ \-eps
+.RI < epsilon >]
+.RB [ \-o
+.RI < nout >]
+.SH DESCRIPTION
+Non-linear least-squares fitting of \fIb\fR-value curves.
+Axis 0 is replaced by three values:
+.IR amp ,\ dec ,\ err ,
+based on a non-linear least-squares fit of
+.IB amp *exp(\- b * dec )
+to the range of DWI values along input axis 0, as a function of changing
+.I b
+values.
+.SH OPTIONS
+.TP
+.IR @file \ ...
+response file(s) containing command-line arguments
+.TP
+.B \-i\fR <\fInin\fR>
+Input
+.IR nrrd .
+List of DWIs from different
+.IR b -values
+must be along axis 0; default:
+.RB \(lq \- \(rq
+.TP
+.B \-b\fR <\fIb1\fR\ \fIb2\fR\ ...>
+.I b
+values across axis 0 of input
+.I nrrd
+(2 or more
+.IR double s)
+.TP
+.B \-w\fR <\fIw1\fR\ \fIw2\fR\ ...>
+weights for samples in non-linear fitting (2 or more
+.IR double s)
+.TP
+.B \-imax\fR <\fI#\ iter\fR>
+max number of iterations to use in non-linear fitting, or, use
+.B 0
+to do only initial linear fit
+.RI ( int );
+default:
+.RB \(lq 10 \(rq
+.TP
+.B \-eps\fR <\fIepsilon\fR>
+epsilon convergence threshold for non-linear fitting
+.RI ( double );
+default:
+.RB \(lq 1 \(rq
+.TP
+.B \-o\fR <\fInout\fR>
+output tensor volume
+.RI ( string );
+default:
+.RB \(lq \- \(rq
+.SH "SEE\ ALSO"
+.BR tend (1)
diff --git a/tend-ellipse.1 b/tend-ellipse.1
new file mode 100644
index 0000000..13f7913
--- /dev/null
+++ b/tend-ellipse.1
@@ -0,0 +1,99 @@
+.TH TEND\-ELLIPSE "1" "April 2021" "" "User Commands"
+.SH NAME
+tend\-ellipse \- generate postscript renderings of 2D glyphs
+.SH SYNOPSIS
+.B tend\ ellipse
+.RB [ \-ctr
+.RI < conf\ thresh >]
+.RB [ \-gsc
+.RI < scale >]
+.RB [ \-dot
+.RI < radius >]
+.RB [ \-wid
+.RI < width >]
+.RB [ \-inv ]
+.RB [ \-min
+.RI < minX \ minY >]
+.RB [ \-max
+.RI < maxX \ maxY >]
+.RB [ \-i
+.RI < nin >]
+.RB [ \-p
+.RI < pos\ array >]
+.RB [ \-s
+.RI < stn\ array >]
+.RB [ \-o
+.RI < nout >]
+.SH DESCRIPTION
+Generate postscript renderings of 2D glyphs.
+Not much to look at here.
+.SH OPTIONS
+.TP
+.B \-ctr\fR <\fIconf\ thresh\fR>
+Glyphs will be drawn only for tensors with confidence values greater than this
+threshold
+.RI ( float );
+default:
+.RB \(lq 0.5 \(rq
+.TP
+.B \-gsc\fR <\fIscale\fR>
+over-all glyph size
+.RI ( float );
+default:
+.RB \(lq 1 \(rq
+.TP
+.B \-dot\fR <\fIradius\fR>
+radius of little dot to put in middle of ellipse, or
+.RB \(lq 0 \(rq
+for no such dot
+.RI ( float );
+default:
+.RB \(lq 0.0 \(rq
+.TP
+.B \-wid\fR <\fIwidth\fR>
+width of lines for tractlets
+.RI ( float );
+default:
+.RB \(lq 0.0 \(rq
+.TP
+.B \-inv
+use white ellipses on black background, instead of reverse
+.TP
+.B \-min\fR <\fIminX\fR \fIminY\fR>
+when using
+.RB \(lq \-p \(rq,
+minimum corner (2
+.IR float s);
+default:
+.RB \(lq \-1\ \-1 \(rq
+.TP
+.B \-max\fR <\fImaxX\fR \fImaxY\fR>
+when using
+.RB \(lq \-p \(rq,
+maximum corner (2
+.IR float s);
+default:
+.RB \(lq 1\ 1 \(rq
+.TP
+.B \-i\fR <\fInin\fR>
+image of 2D tensors; default:
+.RB \(lq \- \(rq
+.TP
+.B \-p\fR <\fIpos\ array\fR>
+Instead of being on a grid, tensors are at arbitrary locations, as defined by
+this 2-by-N array of
+.IR float s);
+default: \(lq\(rq
+.TP
+.B \-s\fR <\fIstn\ array\fR>
+Locations given by
+.RB \(lq \-p \(rq
+have this connectivity; default: \(lq\(rq
+.TP
+.B \-o\fR <\fInout\fR>
+output PostScript file
+.RI ( string )
+default:
+.RB \(lq \- \(rq
+.SH "SEE\ ALSO"
+.BR tend (1)
diff --git a/tend-eval.1 b/tend-eval.1
new file mode 100644
index 0000000..de9dc27
--- /dev/null
+++ b/tend-eval.1
@@ -0,0 +1,47 @@
+.TH TEND\-EVAL "1" "April 2021" "" "User Commands"
+.SH NAME
+tend\-eval \- calculate one or more eigenvalues in a DT volume
+.SH SYNOPSIS
+.B tend\ eval
+.B \-c
+.RI < c0 \ ...>
+.RB [ \-t
+.RI < thresh >]
+.RB [ \-i
+.RI < nin >]
+.RB [ \-o
+.RI < nout >]
+.SH OPTIONS
+.TP
+.B \-c\fR <\fIc0\fR\ ...>
+which eigenvalues should be saved out.
+.RB \(lq 0 \(rq
+for the largest,
+.RB \(lq 1 \(rq
+for the middle,
+.RB \(lq 2 \(rq
+for the smallest,
+.RB \(lq 0\ 1 \(rq,
+.RB \(lq 1\ 2 \(rq,
+.RB \(lq 0\ 1\ 2 \(rq
+or similar for more than one (1..3
+.IR int s)
+.TP
+.B \-t\fR <\fIthresh\fR>
+confidence threshold
+.RI ( float );
+default:
+.RB \(lq 0.5 \(rq
+.TP
+.B \-i\fR <\fInin\fR>
+input diffusion tensor volume; default:
+.RB \(lq \- \(rq
+.TP
+.B \-o\fR <\fInout\fR>
+output image (floating point)
+.RI ( string )
+default:
+.RB \(lq \- \(rq
+.SH "SEE\ ALSO"
+.BR tend (1),
+.BR tend\-evec (1)
diff --git a/tend-evaladd.1 b/tend-evaladd.1
new file mode 100644
index 0000000..e8754f9
--- /dev/null
+++ b/tend-evaladd.1
@@ -0,0 +1,35 @@
+.TH TEND\-EVALADD "1" "April 2021" "" "User Commands"
+.SH NAME
+tend\-evaladd \- modify shape by adding a constant to all eigenvalues
+.SH SYNOPSIS
+.B tend\ evaladd
+.B \-v
+.RI < value >
+.RB [ \-i
+.RI < nin >]
+.RB [ \-o
+.RI < nout >]
+.SH DESCRIPTION
+Modify shape by adding a constant to all eigenvalues.
+The orientation of the tensor is unchanged.
+Note that unlike
+.BR tend\-anscale (1),
+this operation can completely change the shape of the tensor.
+.SH OPTIONS
+.TP
+.B \-v\fR <\fIvalue\fR>
+Value to add to all eigenvalues
+.RI ( float )
+.TP
+.B \-i\fR <\fInin\fR>
+input diffusion tensor volume; default:
+.RB \(lq \- \(rq
+.TP
+.B \-o\fR <\fInout\fR>
+output image (floating point)
+.RI ( string )
+default:
+.RB \(lq \- \(rq
+.SH "SEE\ ALSO"
+.BR tend (1),
+.BR tend\-anscale (1)
diff --git a/tend-evalclamp.1 b/tend-evalclamp.1
new file mode 100644
index 0000000..b88bd6e
--- /dev/null
+++ b/tend-evalclamp.1
@@ -0,0 +1,47 @@
+.TH TEND\-EVALCLAMP "1" "April 2021" "" "User Commands"
+.SH NAME
+tend\-evalclamp \- modify shape by clamping eigenvalues in some range
+.SH SYNOPSIS
+.B tend\ evalclamp
+.B \-min
+.RI < min >
+.RB [ \-max
+.RI < max >]
+.RB [ \-i
+.RI < nin >]
+.RB [ \-o
+.RI < nout >]
+.SH DESCRIPTION
+Modify shape by clamping eigenvalues in some range.
+The orientation of the tensor is unchanged.
+Note that unlike
+.BR tend\-anscale (1),
+this operation can completely change the shape of the tensor.
+.SH OPTIONS
+.TP
+.B \-min\fR <\fImin\fR>
+Eigenvalues are clamped from below by this (the minimum output eigenvalue).
+Use
+.RB \(lq nan \(rq
+to signify that no minimum clamping should be done.
+.RI ( float )
+.TP
+.B \-max\fR <\fImax\fR>
+Eigenvalues are clamped from above by this (the maximum output eigenvalue).
+Use
+.RB \(lq nan \(rq
+to signify that no maximum clamping should be done.
+.RI ( float )
+.TP
+.B \-i\fR <\fInin\fR>
+input diffusion tensor volume; default:
+.RB \(lq \- \(rq
+.TP
+.B \-o\fR <\fInout\fR>
+output image (floating point)
+.RI ( string )
+default:
+.RB \(lq \- \(rq
+.SH "SEE\ ALSO"
+.BR tend (1),
+.BR tend\-anscale (1)
diff --git a/tend-evalmult.1 b/tend-evalmult.1
new file mode 100644
index 0000000..3bf86b0
--- /dev/null
+++ b/tend-evalmult.1
@@ -0,0 +1,31 @@
+.TH TEND\-EVALMULT "1" "April 2021" "" "User Commands"
+.SH NAME
+tend\-evalmult \- modify shape by multiplying eigenvalues by a constant
+.SH SYNOPSIS
+.B tend\ evalmult
+.B \-v
+.RI < value >
+.RB [ \-i
+.RI < nin >]
+.RB [ \-o
+.RI < nout >]
+.SH DESCRIPTION
+Modify shape by multiplying eigenvalues by a constant.
+The orientation of the tensor is unchanged.
+.SH OPTIONS
+.TP
+.B \-v\fR <\fIvalue\fR>
+Value to multiply eigenvalues by
+.RI ( float )
+.TP
+.B \-i\fR <\fInin\fR>
+input diffusion tensor volume; default:
+.RB \(lq \- \(rq
+.TP
+.B \-o\fR <\fInout\fR>
+output image (floating point)
+.RI ( string )
+default:
+.RB \(lq \- \(rq
+.SH "SEE\ ALSO"
+.BR tend (1)
diff --git a/tend-evalpow.1 b/tend-evalpow.1
new file mode 100644
index 0000000..9f1adf5
--- /dev/null
+++ b/tend-evalpow.1
@@ -0,0 +1,31 @@
+.TH TEND\-EVALPOW "1" "April 2021" "" "User Commands"
+.SH NAME
+tend\-evalpow \- modify shape by raising eigenvalues to some power
+.SH SYNOPSIS
+.B tend\ evalpow
+.B \-p
+.RI < power >
+.RB [ \-i
+.RI < nin >]
+.RB [ \-o
+.RI < nout >]
+.SH DESCRIPTION
+Modify shape by raising eigenvalues to some power.
+The orientation of the tensor is unchanged.
+.SH OPTIONS
+.TP
+.B \-p\fR <\fIpower\fR>
+Power to which to raise all the eigenvalues.
+.RI ( float )
+.TP
+.B \-i\fR <\fInin\fR>
+input diffusion tensor volume; default:
+.RB \(lq \- \(rq
+.TP
+.B \-o\fR <\fInout\fR>
+output tensor volume
+.RI ( string )
+default:
+.RB \(lq \- \(rq
+.SH "SEE\ ALSO"
+.BR tend (1)
diff --git a/tend-evec.1 b/tend-evec.1
new file mode 100644
index 0000000..74e7988
--- /dev/null
+++ b/tend-evec.1
@@ -0,0 +1,47 @@
+.TH TEND\-EVEC "1" "April 2021" "" "User Commands"
+.SH NAME
+tend\-evec \- calculate one or more eigenvectors in a DT volume
+.SH SYNOPSIS
+.B tend\ evec
+.B \-c
+.RI < c0 \ ...>
+.RB [ \-t
+.RI < thresh >]
+.RB [ \-i
+.RI < nin >]
+.RB [ \-o
+.RI < nout >]
+.SH OPTIONS
+.TP
+.B \-c\fR <\fIc0\fR\ ...>
+which eigenvectors should be saved out.
+.RB \(lq 0 \(rq
+for the largest,
+.RB \(lq 1 \(rq
+for the middle,
+.RB \(lq 2 \(rq
+for the smallest,
+.RB \(lq 0\ 1 \(rq,
+.RB \(lq 1\ 2 \(rq,
+.RB \(lq 0\ 1\ 2 \(rq
+or similar for more than one (1..3
+.IR int s)
+.TP
+.B \-t\fR <\fIthresh\fR>
+confidence threshold
+.RI ( float );
+default:
+.RB \(lq 0.5 \(rq
+.TP
+.B \-i\fR <\fInin\fR>
+input diffusion tensor volume; default:
+.RB \(lq \- \(rq
+.TP
+.B \-o\fR <\fInout\fR>
+output image (floating point)
+.RI ( string )
+default:
+.RB \(lq \- \(rq
+.SH "SEE\ ALSO"
+.BR tend (1),
+.BR tend\-eval (1)
diff --git a/tend-evecrgb.1 b/tend-evecrgb.1
new file mode 100644
index 0000000..94e209c
--- /dev/null
+++ b/tend-evecrgb.1
@@ -0,0 +1,108 @@
+.TH TEND\-EVECRGB "1" "April 2021" "" "User Commands"
+.SH NAME
+tend\-evecrgb \- make an RGB volume from an eigenvector and an anisotropy
+.SH SYNOPSIS
+.B tend\ evecrgb
+.B \-c
+.RI < evec\ index >
+.B \-a
+.RI < aniso >
+.RB [ \-t
+.RI < thresh >]
+.RB [ \-bg
+.RI < background >]
+.RB [ \-gr
+.RI < gray >]
+.RB [ \-gam
+.RI < gamma >]
+.RB [ \-i
+.RI < nin >]
+.RB [ \-o
+.RI < nout >]
+.SH OPTIONS
+.TP
+.B \-c\fR <\fIevec\ index\fR>
+which eigenvector will be colored.
+.RB \(lq 0 \(rq
+for the principal,
+.RB \(lq 1 \(rq
+for the middle,
+.RB \(lq 2 \(rq
+for the minor
+.RI ( unsigned\ int )
+.TP
+.B \-a\fR <\fIaniso\fR>
+Which anisotropy to use for modulating the saturation of the colors.
+All the Westin metrics come in two versions.
+Currently supported:
+.RS +7n
+.IP \(bu 2
+.RB \(lq cl1 \(rq,
+.RB \(lq cl2 \(rq:
+Westin\(cqs linear
+.IP \(bu 2
+.RB \(lq cp1 \(rq,
+.RB \(lq cp2 \(rq:
+Westin\(cqs planar
+.IP \(bu 2
+.RB \(lq ca1 \(rq,
+.RB \(lq ca2 \(rq:
+Westin\(cqs linear + planar
+.IP \(bu 2
+.RB \(lq cs1 \(rq,
+.RB \(lq cs2 \(rq:
+Westin\(cqs spherical
+.IP \(bu 2
+.RB \(lq ct1 \(rq,
+.RB \(lq ct2 \(rq:
+GK\(cqs anisotropy type (cp/ca)
+.IP \(bu 2
+.RB \(lq ra \(rq:
+Basser/Pierpaoli relative anisotropy/sqrt(2)
+.IP \(bu 2
+.RB \(lq fa \(rq:
+Basser/Pierpaoli fractional anisotropy
+.IP \(bu 2
+.RB \(lq vf \(rq:
+volume fraction = 1\-(Basser/Pierpaoli volume ratio)
+.IP \(bu 2
+.RB \(lq tr \(rq:
+trace
+.RE
+.TP
+.B \-t\fR <\fIthresh\fR>
+confidence threshold
+.RI ( float )
+default:
+.RB \(lq 0.5 \(rq
+.TP
+.B \-bg\fR <\fIbackground\fR>
+gray level to use for voxels whose confidence is zero
+.RI ( double )
+default:
+.RB \(lq 0 \(rq
+.TP
+.B \-gr\fR <\fIgray\fR>
+the gray level to desaturate towards as anisotropy decreases (while confidence
+remains 1.0)
+.RI ( double )
+default:
+.RB \(lq 0 \(rq
+.TP
+.B \-gam\fR <\fIgamma\fR>
+gamma to use on color components
+.RI ( double )
+default:
+.RB \(lq 1 \(rq
+.TP
+.B \-i\fR <\fInin\fR>
+input diffusion tensor volume; default:
+.RB \(lq \- \(rq
+.TP
+.B \-o\fR <\fInout\fR>
+output image (floating point)
+.RI ( string )
+default:
+.RB \(lq \- \(rq
+.SH "SEE\ ALSO"
+.BR tend (1)
diff --git a/tend-evq.1 b/tend-evq.1
new file mode 100644
index 0000000..2266f75
--- /dev/null
+++ b/tend-evq.1
@@ -0,0 +1,93 @@
+.TH TEND\-EVQ "1" "April 2021" "" "User Commands"
+.SH NAME
+tend\-evq \- quantize directions of diffusion
+.SH SYNOPSIS
+.B tend\ evq
+.RB [ \-c
+.RI < evec\ index >]
+.B \-a
+.RI < aniso >
+.RB [ \-ns ]
+.RB [ \-i
+.RI < nin >]
+.RB [ \-o
+.RI < nout >]
+.SH DESCRIPTION
+Quantize directions of diffusion.
+Because VTK doesn\(cqt do multi-dimensional colormaps, we have to quantize
+directions of diffusion (usually just the principal eigenvector) in order to
+create the usual XYZ<->RGB coloring.
+Because eigenvector directions are poorly defined in regions of low anisotropy,
+the length of the vector (pre-quantization) is modulated by anisotropy,
+requiring the selection of some anisotropy metric.
+.SH OPTIONS
+.TP
+.B \-c\fR <\fIevec\ index\fR>
+Which eigenvector should be quantized:
+
+.RB \(lq 0 \(rq
+for the direction of fastest diffusion (eigenvector associated with largest
+eigenvalue),
+.RB \(lq 1 \(rq
+or
+.RB \(lq 2 \(rq
+for other two eigenvectors (associated with middle and smallest eigenvalue)
+.RI ( int );
+default:
+.RB \(lq 0 \(rq
+.TP
+.B \-a\fR <\fIaniso\fR>
+Which anisotropy metric to scale the eigenvector with.
+All the Westin metrics come in two versions.
+Currently supported:
+.RS +7n
+.IP \(bu 2
+.RB \(lq cl1 \(rq,
+.RB \(lq cl2 \(rq:
+Westin\(cqs linear
+.IP \(bu 2
+.RB \(lq cp1 \(rq,
+.RB \(lq cp2 \(rq:
+Westin\(cqs planar
+.IP \(bu 2
+.RB \(lq ca1 \(rq,
+.RB \(lq ca2 \(rq:
+Westin\(cqs linear + planar
+.IP \(bu 2
+.RB \(lq cs1 \(rq,
+.RB \(lq cs2 \(rq:
+Westin\(cqs spherical
+.IP \(bu 2
+.RB \(lq ct1 \(rq,
+.RB \(lq ct2 \(rq:
+GK\(cqs anisotropy type (cp/ca)
+.IP \(bu 2
+.RB \(lq ra \(rq:
+Basser/Pierpaoli relative anisotropy/sqrt(2)
+.IP \(bu 2
+.RB \(lq fa \(rq:
+Basser/Pierpaoli fractional anisotropy
+.IP \(bu 2
+.RB \(lq vf \(rq:
+volume fraction = 1\-(Basser/Pierpaoli volume ratio)
+.IP \(bu 2
+.RB \(lq tr \(rq:
+trace
+.RE
+.TP
+.B \-ns
+Don\(cqt attenuate the color by anisotropy.
+By default (not using this option), regions with low or no anisotropy are very
+dark colors or black
+.TP
+.B \-i\fR <\fInin\fR>
+input diffusion tensor volume; default:
+.RB \(lq \- \(rq
+.TP
+.B \-o\fR <\fInout\fR>
+output image (floating point)
+.RI ( string )
+default:
+.RB \(lq \- \(rq
+.SH "SEE\ ALSO"
+.BR tend (1)
diff --git a/tend-exp.1 b/tend-exp.1
new file mode 100644
index 0000000..e01f780
--- /dev/null
+++ b/tend-exp.1
@@ -0,0 +1,28 @@
+.TH TEND\-EXP "1" "April 2021" "" "User Commands"
+.SH NAME
+tend\-exp \- calculates \fBexp()\fR of the tensor
+.SH SYNOPSIS
+.B tend\ exp
+.RB [ \-i
+.RI < nin >]
+.B \-o
+.RI < nout >
+.SH DESCRIPTION
+Calculates
+.B exp()
+of the tensor, which is based on
+.B exp()
+of the eigenvalues.
+.SH OPTIONS
+.TP
+.B \-i\fR <\fInin\fR>
+input diffusion tensor volume; default:
+.RB \(lq \- \(rq
+.TP
+.B \-o\fR <\fInout\fR>
+output image
+.RI ( string )
+default:
+.RB \(lq \- \(rq
+.SH "SEE\ ALSO"
+.BR tend (1)
diff --git a/tend-expand.1 b/tend-expand.1
new file mode 100644
index 0000000..7025c54
--- /dev/null
+++ b/tend-expand.1
@@ -0,0 +1,72 @@
+.TH TEND\-EXPAND "1" "April 2021" "" "User Commands"
+.SH NAME
+tend\-expand \- converts masked non-redundant tensor images to redundant
+.SH SYNOPSIS
+.B tend\ expand
+.RB [ \-t
+.RI < thresh >]
+.RB [ \-s
+.RI < scale >]
+.B [ \-unmf ]
+.B [ \-ro ]
+.B [ \-roo ]
+.RB [ \-i
+.RI < nin >]
+.RB [ \-o
+.RI < nout >]
+.SH DESCRIPTION
+Converts masked non-redundant tensor images to redundant.
+For
+images of 3D tensors, this converts from a 7-value tensor starting with the
+confidence/mask value
+.BI ( conf ,\ Dxx ,\ Dxy ,\ Dxz ,\ Dyy ,\ Dyz ,\ Dzz )
+to a 9-value tensor
+with the full matrix
+.BI ( Dxx ,\ Dxy ,\ Dxz ,\ Dxy ,\ Dyy ,\ Dyz ,\ Dxz ,\ Dyz ,\ Dzz ).
+This is set to all zeros when the confidence is below the given threshold.
+For
+images
+of 2D tensors, the conversion is from
+.BI ( conf ,\ Dxx ,\ Dxy ,\ Dyy )
+to
+.BI ( Dxx ,\ Dxy ,\ Dxy ,\ Dyy ).
+.SH OPTIONS
+.TP
+.B \-t\fR <\fIthresh\fR>
+confidence level to threshold output tensors at.
+Should be between
+.B 0.0
+and
+.BR 1.0 .
+.RI ( float );
+default:
+.RB \(lq 0.5 \(rq
+.TP
+.B \-s\fR <\fIscale\fR>
+how to scale values before saving as 9-value tensor.
+Useful for visualization tools which assume certain characteristic ranges of
+eigenvalues
+.RI ( float );
+default:
+.RB \(lq 1.0 \(rq
+.TP
+.B \-unmf
+apply and remove the measurement frame, if it exists
+.TP
+.B \-ro
+reduce general image orientation to axis-aligned spacings
+.TP
+.B \-roo
+reduce general image orientation to axis-aligned spacings, while also making
+some effort to set axis mins from space origin
+.TP
+.B \-i\fR <\fInin\fR>
+input diffusion tensor volume, with 7 values per sample; default:
+.RB \(lq \- \(rq
+.TP
+.B \-o\fR <\fInout\fR>
+output tensor volume, with the 9 matrix components per sample
+.RI ( string )
+.SH "SEE\ ALSO"
+.BR tend (1),
+.BR tend\-shrink (1)
diff --git a/tend-fiber.1 b/tend-fiber.1
new file mode 100644
index 0000000..8733a8a
--- /dev/null
+++ b/tend-fiber.1
@@ -0,0 +1,163 @@
+.TH TEND\-FIBER "1" "April 2021" "" "User Commands"
+.SH NAME
+tend\-fiber \- fiber tractography, from one or more seeds
+.SH SYNOPSIS
+.B tend\ fiber
+.RB [ \-i
+.RI < nin >]
+.RB [ \-dwi ]
+.RB [ \-s
+.RI < seed\ point >]
+.RB [ \-ns
+.RI < seed\ nrrd >]
+.RB [ \-wsp ]
+.RB [ \-t
+.RI < type >]
+.RB [ \-n
+.RI < intg >]
+.RB [ \-k
+.RI < kernel >]
+.RB [ \-wp
+.RI < which >]
+.RB [ \-ap ]
+.RB [ \-wspo ]
+.RB [ \-step
+.RI < step\ size >]
+.B \-stop
+.RI < stop1 \ ...>
+.RB [ \-v
+.RI < verbose >]
+.RB [ \-nmat
+.RI < transform >]
+.RB [ \-o
+.RI < nout >]
+.SH DESCRIPTION
+Fiber tractography, from one or more seeds.
+A fairly complete command-line interface to the
+.B tenFiber
+API.
+.SH OPTIONS
+.TP
+.B \-i\fR <\fInin\fR>
+input volume; default:
+.RB \(lq \- \(rq
+.TP
+.B \-dwi
+input volume is a DWI volume, not a single tensor volume
+.TP
+.B \-s\fR <\fIseed\ point\fR>
+seed point for fiber; it will propogate in two opposite directions starting
+from here (3
+.IR double s);
+default:
+.RB \(lq 0\ 0\ 0 \(rq
+.TP
+.B \-ns\fR <\fIseed\ nrrd\fR>
+3-by-N
+.I nrrd
+of seedpoints; default: \(lq\(rq
+.TP
+.B \-wsp
+define seedpoint and output path in worldspace.
+Otherwise, (without using this option) everything is in index space
+.TP
+.B \-t\fR <\fItype\fR>
+fiber type; defaults to something
+.RI ( string )
+.TP
+.B \-n\fR <\fIintg\fR>
+integration method for fiber tracking; default:
+.RB \(lq rk4 \(rq
+.TP
+.B \-k\fR <\fIkernel\fR>
+kernel for reconstructing tensor field; default:
+.RB \(lq tent \(rq
+.TP
+.B \-wp\fR <\fIwhich\fR>
+when doing multi-tensor tracking, index of path to follow (made moot by
+.RB \(lq \-ap \(rq)
+.RI ( unsigned\ int );
+default:
+.RB \(lq 0 \(rq
+.TP
+.B \-ap
+follow all paths from (all) seedpoint(s), output will be
+.IR polydata ,
+rather than a single 3-by-N
+.IR nrrd ,
+even if only a single path is generated
+.TP
+.B \-wspo
+output should be in worldspace, even if input is not (this feature is unstable
+and/or confusing)
+.TP
+.B \-s\fR <\fIstep\ size\fR>
+stepsize along fiber, in world space
+.RI ( double );
+default:
+.RB \(lq 0.01 \(rq
+.TP
+.B \-stop\fR <\fIstop1\fR\ ...>
+the conditions that should signify the end of a fiber, or when to discard a
+fiber that is done propagating.
+Multiple stopping criteria are logically OR-ed and tested at every point along
+the fiber.
+Possibilities include:
+.RS +7n
+.IP \(bu 2
+.RB \(lq aniso:\fR<\fItype\fR>\fB,\fR<\fIthresh >\(rq:
+require anisotropy to be above the given threshold.
+Which anisotropy type is given as with
+.BR tend-anvol (1)
+.IP \(bu 2
+.RB \(lq len:\fR<\fIlength >\(rq:
+limits the length, in world space, of each fiber half
+.IP \(bu 2
+.RB \(lq steps:\fR<\fIN >\(rq:
+the number of steps in each fiber half is capped at
+.I N
+.IP \(bu 2
+.RB \(lq conf:\fR<\fIthresh >\(rq:
+requires the tensor confidence value to be above the given
+.I thresh
+.IP \(bu 2
+.RB \(lq radius:\fR<\fIthresh >\(rq:
+requires that the radius of curvature of the fiber stay above given
+.I thresh
+.IP \(bu 2
+.RB \(lq frac:\fR<\fIF >\(rq:
+in multi-tensor tracking, the fraction of the tracked component must stay above
+.I F
+.IP \(bu 2
+.RB \(lq minlen:\fR<\fIlen >\(rq:
+discard fiber if its final whole length is below
+.I len
+(not really a termination criterion)
+.IP \(bu 2
+.RB \(lq minsteps:\fR<\fIN >\(rq:
+discard fiber if its final number of steps is below
+.I N
+(not really a termination criterion)
+(1 or more fiber stops)
+.RE
+.TP
+.B \-v\fR <\fIverbose\fR>
+verbosity level
+.RI ( int )
+default:
+.RB \(lq 0 \(rq
+.TP
+.B \-nmat\fR <\fItransform\fR>
+a 4x4 homogenous transform matrix (as a
+.IR nrrd ,
+or just a text file) given with this option will be applied to the output
+tractography vertices just prior to output; default: \(lq\(rq
+.TP
+.B \-o\fR <\fInout\fR>
+output fiber(s)
+.RI ( string )
+default:
+.RB \(lq \- \(rq
+.SH "SEE\ ALSO"
+.BR tend (1),
+.BR tend\-anvol (1)
diff --git a/tend-glyph.1 b/tend-glyph.1
new file mode 100644
index 0000000..c0ee791
--- /dev/null
+++ b/tend-glyph.1
@@ -0,0 +1,462 @@
+.TH TEND\-GLYPH "1" "April 2021" "" "User Commands"
+.SH NAME
+tend\-glyph \- generate postscript or ray-traced renderings of 3D glyphs
+.SH SYNOPSIS
+.B tend\ glyph
+.RI [ @file \ ...]
+.RB [ \-rt ]
+.RB [ \-v
+.RI < level >]
+.RB [ \-ctr
+.RI < conf\ thresh >]
+.RB [ \-a
+.RI < aniso >]
+.RB [ \-atr
+.RI < aniso\ thresh >]
+.RB [ \-p
+.RI < pos\ array >]
+.RB [ \-m
+.RI < mask\ vol >]
+.RB [ \-mtr
+.RI < mask\ thresh >]
+.RB [ \-g
+.RI < glyph\ shape >]
+.RB [ \-sh
+.RI < sharpness >]
+.RB [ \-gsc
+.RI < scale >]
+.RB [ \-c
+.RI < evector\ # >]
+.RB [ \-sat
+.RI < saturation >]
+.RB [ \-ga
+.RI < aniso >]
+.RB [ \-am
+.RI < aniso\ mod >]
+.RB [ \-gg
+.RI < gray >]
+.RB [ \-gam
+.RI < gamma >]
+.RB [ \-emap
+.RI < env\ map >]
+.RB [ \-adsp
+.RI < phong >]
+.RB [ \-bg
+.RI < background >]
+.RB [ \-ec
+.RI < edge\ rgb >]
+.RB [ \-slc
+.RI < axis\ pos >]
+.RB [ \-si
+.RI < slice\ image >]
+.RB [ \-off
+.RI < slice\ offset >]
+.RB [ \-sg
+.RI < slice\ gamma >]
+.RB [ \-sb
+.RI < slice\ bias >]
+.B \-fr
+.RI < from\ point >
+.RB [ \-at
+.RI < at\ point >]
+.RB [ \-up
+.RI < up\ vector >]
+.RB [ \-rh ]
+.RB [ \-dn
+.RI < near\ clip >]
+.RB [ \-df
+.RI < far\ clip >]
+.RB [ \-or ]
+.RB [ \-ur
+.RI < uMin \ uMax >]
+.RB [ \-vr
+.RI < vMin \ vMax >]
+.RB [ \-fv
+.RI < fov >]
+.RB [ \-gr
+.RI < glyph\ res >]
+.RB [ \-wd
+.RI < 3\ widths >]
+.RB [ \-psc
+.RI < scale >]
+.RB [ \-ca
+.RI < angle >]
+.RB [ \-nobg ]
+.RB [ \-concave ]
+.RB [ \-is
+.RI < nx \ ny >]
+.RB [ \-ns
+.RI < #\ samp >]
+.RB [ \-nt
+.RI < #\ threads >]
+.RB [ \-al
+.RI < B \ U \ V \ N \ E >]
+.RB [ \-ao ]
+.RB [ \-shadow
+.RI < s >]
+.RB [ \-hack
+.RI < hack >]
+.RB [ \-i
+.RI < nin >]
+.RB [ \-o
+.RI < nout >]
+.SH DESCRIPTION
+Generate postscript or ray-traced renderings of 3D glyphs.
+Whether the output is postscript or a ray-traced image is controlled by the
+initial
+.RB \(lq \-rt \(rq
+flag (by default, the output is postscript).
+Because this is doing viz/graphics, many parameters need to be set.
+Use a response file to simplify giving the command-line options which aren\(cqt
+changing between invocations.
+The postscript output is an EPS file, suitable for including as a figure in
+LaTeX, or viewing with ghostview, or distilling into PDF.
+The
+ray-traced output is a 5 channel
+.RI ( R , G , B , A , T )
+.I float
+.IR nrrd ,
+suitable for
+.RB \(lq unu\ crop\ \-min\ 0\ 0\ 0\ \-max\ 2\ M\ M \(rq
+followed by
+.RB \(lq unu\ gamma \(rq
+and/or
+.RB \(lq unu\ quantize\ \-b\ 8 \(rq.
+.SH OPTIONS
+.TP
+.IR @file \ ...
+response file(s) containing command-line arguments
+.TP
+.B \-rt
+generate ray-traced output.
+By default (not using this option), postscript output is generated.
+.TP
+.B \-v\fR <\fIlevel\fR>
+verbosity level
+.RI ( int )
+default:
+.RB \(lq 0 \(rq
+.TP
+.B \-ctr\fR <\fIconf\ thresh\fR>
+Glyphs will be drawn only for tensors with confidence values greater than this
+threshold
+.RI ( float )
+default:
+.RB \(lq 0.5 \(rq
+.TP
+.B \-a\fR <\fIaniso\fR>
+Which anisotropy metric to use for thresholding the data points to be drawn;
+default:
+.RB \(lq fa \(rq
+.TP
+.B \-p\fR <\fIpos\ array\fR>
+Instead of being on a grid, tensors are at arbitrary locations, as defined by
+this 3-by-N array of floats.
+Doing this makes various other options moot; default: \(lq\(rq
+.TP
+.B \-m\fR <\fImask\ vol\fR>
+Scalar volume (if any) for masking region in which glyphs are drawn, in
+conjunction with
+.RB \(lq \-mtr \(rq
+flag; default: \(lq\(rq
+.TP
+.B \-mtr\fR <\fImask\ thresh\fR>
+Glyphs will be drawn only for tensors with mask value greater than this
+threshold
+.RI ( float )
+default:
+.RB \(lq 0.5 \(rq
+.TP
+.B \-g\fR <\fIglyph\ shape\fR>
+shape of glyph to use for display. Possibilities include
+.RB \(lq box \(rq,
+.RB \(lq sphere \(rq,
+.RB \(lq cylinder \(rq,
+and
+.RB \(lq superquad \(rq;
+default:
+.RB \(lq box \(rq
+.TP
+.B \-sh\fR <\fIsharpness\fR>
+for superquadric glyphs, how much to sharp edges form as a function of
+differences between eigenvalues.
+Higher values mean that edges form more easily
+.RI ( float )
+default:
+.RB \(lq 3.0 \(rq
+.TP
+.B \-gsc\fR <\fIscale\fR>
+over-all glyph size in world-space
+.RI ( float )
+default:
+.RB \(lq 0.01 \(rq
+.TP
+.B \-c\fR <\fIevector\ #\fR>
+which eigenvector should determine coloring. (formally
+.RB \(lq v \(rq)
+.RB \(lq 0 \(rq,
+.RB \(lq 1 \(rq,
+.RB \(lq 2 \(rq,
+are principal, medium, and minor
+.RI ( int )
+default:
+.RB \(lq 0 \(rq
+.TP
+.B \-sat\fR <\fIsaturation\fR>
+maximal saturation to use on glyph colors (use
+.B 0.0
+to create a black and white image)
+.RI ( float )
+default:
+.RB \(lq 1.0 \(rq
+.TP
+.B \-ga\fR <\fIaniso\fR>
+Which anisotropy metric to use for modulating the saturation of the glyph
+color; default:
+.RB \(lq fa \(rq
+.TP
+.B \-am\fR <\fIaniso\ mod\fR>
+How much to modulate glyph color saturation by anisotropy (as chosen by
+.RB \(lq \-ga \(rq.
+If
+.BR 1.0 ,
+then glyphs for zero anisotropy data points will have no hue.
+.RI ( float )
+default:
+.RB \(lq 0.0 \(rq
+.TP
+.B \-gg\fR <\fIgray\fR>
+desaturating glyph color due to low anisotropy tends towards this gray level
+.RI ( float )
+default:
+.RB \(lq 1.0 \(rq
+.TP
+.B \-gam\fR <\fIgamma\fR>
+gamma to use on color components (after saturation)
+.RI ( float )
+default:
+.RB \(lq 0.7 \(rq
+.TP
+.B \-emap\fR <\fIenv\ map\fR>
+environment map to use for shading glyphs.
+By default, there is no shading; default: \(lq\(rq
+.TP
+.B \-adsp\fR <\fIphong\fR>
+phong ambient, diffuse, specular components, and specular power (4
+.IR float s);
+default:
+.RB \(lq 0\ 1\ 0\ 30 \(rq
+.TP
+.B \-bg\fR <\fIbackground\fR>
+background RGB color; each component in range
+.B [0.0,1.0]
+(3
+.IR float s);
+default:
+.RB \(lq 1\ 1\ 1 \(rq
+.TP
+.B \-ec\fR <\fIedge\ rgb\fR>
+edge RGB color; each component in range
+.B [0.0,1.0]
+(3
+.IR float s);
+default:
+.RB \(lq 0\ 0\ 0 \(rq
+.TP
+.B \-slc\fR <\fIaxis\ pos\fR>
+For showing a gray-scale slice of anisotropy: the axis and position along which
+to slice.
+Use
+.RB \(lq \-1\ \-1 \(rq
+to signify that no slice should be shown (2
+.IR int s);
+default:
+.RB \(lq \-1\ \-1 \(rq
+.TP
+.B \-si\fR <\fIslice\ image\fR>
+Instead of showing a slice of the anisotropy used to cull glyphs, show
+something else; default: \(lq\(rq
+.TP
+.B \-off\fR <\fIslice\ offset\fR>
+Offset from slice position to render slice at (so that it doesn\(cqt occlude
+glyphs).
+.RI ( float )
+default:
+.RB \(lq 0.0 \(rq
+.TP
+.B \-sg\fR <\fIslice\ gamma\fR>
+Gamma to apply to values on slice.
+.RI ( float )
+default:
+.RB \(lq 1.7 \(rq
+.TP
+.B \-sb\fR <\fIslice\ bias\fR>
+amount by which to bump up slice gray values prior to gamma.
+.RI ( float )
+default:
+.RB \(lq 0.05 \(rq
+.TP
+.B \-fr\fR <\fIfrom\ point\fR>
+position of camera, used to determine view vector (3
+.IR double s)
+.TP
+.B \-at\fR <\fIat\ point\fR>
+camera look-at point, used to determine view vector (3
+.IR double s)
+.TP
+.B \-up\fR <\fIup\ vector\fR>
+camera pseudo-up vector, used to determine view coordinates (3
+.IR double s);
+default:
+.RB \(lq 0\ 0\ 1 \(rq
+.TP
+.B \-rh
+use a right-handed UVN frame (V points down)
+.TP
+.B \-dn\fR <\fInear\ clip\fR>
+position of near clipping plane, relative to look-at point
+.RI ( double );
+default:
+.RB \(lq \-2 \(rq
+.TP
+.B \-df\fR <\fIfar\ clip\fR>
+position of far clipping plane, relative to look-at point
+.RI ( double );
+default:
+.RB \(lq 2 \(rq
+.TP
+.B \-or
+use orthogonal projection
+.TP
+.B \-ur\fR <\fIuMin\fR\ \fIuMax\fR>
+range in U direction of image plane (2
+.IR double s);
+default:
+.RB \(lq \-1\ 1 \(rq
+.TP
+.B \-vr\fR <\fIvMin\fR\ \fIvMax\fR>
+range in V direction of image plane (2
+.IR double s);
+default:
+.RB \(lq \-1\ 1 \(rq
+.TP
+.B \-fv\fR <\fIfov\fR>
+if not
+.BR NaN ,
+vertical field-of-view, in degrees
+.RI ( double )
+.TP
+.B \-gr\fR <\fIglyph\ res\fR>
+.B postscript only
+.IP
+resolution of polygonalization of glyphs (all glyphs other than the default
+box)
+.RI ( int );
+default:
+.RB \(lq 10 \(rq
+.TP
+.B \-wd\fR <\fI3\ widths\fR>
+.B postscript only
+.IP
+width of edges drawn for three kinds of glyph edges: silohuette, crease,
+non-crease (3
+.IR float s);
+default:
+.RB \(lq 0.8\ 0.4\ 0.0 \(rq
+.TP
+.B \-psc\fR <\fIscale\fR>
+.B postscript only
+.IP
+scaling from screen space units to postscript units (in points)
+.RI ( float );
+default:
+.RB \(lq 300 \(rq
+.TP
+.B \-ca\fR <\fIangle\fR>
+.B postscript only
+.IP
+minimum crease angle
+.RI ( float );
+default:
+.RB \(lq 70 \(rq
+.TP
+.B \-nobg
+.B postscript only
+.IP
+don\(cqt initially fill with background color
+.TP
+.B \-concave
+use slightly buggy rendering method suitable for concave or self-occluding
+objects
+.TP
+.B \-is\fR <\fInx\fR\ \fIny\fR>
+.B ray-traced only
+.IP
+image size (resolution) to render (2
+.IR int s);
+default:
+.RB \(lq 256\ 256 \(rq
+.TP
+.B \-ns\fR <\fI#\ samp\fR>
+.B ray-traced only
+.IP
+number of samples per pixel (must be a square number)
+.RI ( int );
+default:
+.RB \(lq 4 \(rq
+.TP
+.B \-nt\fR <\fI#\ threads\fR>
+.B ray-traced only
+.IP
+number of threads to be used for rendering
+.RI ( int );
+default:
+.RB \(lq 1 \(rq
+.TP
+.B \-al\fR <\fIB\fR\ \fIU\fR\ \fIV\fR\ \fIN\fR\ \fIE\fR>
+.B ray-traced only
+.IP
+brightness
+.RI ( B ),
+view-space location
+.RI ( U \ V \ N ),
+and length of edge
+.RI ( E )
+of a square area light source, for getting soft shadows.
+Requires lots more samples
+.RB \(lq \-ns \(rq
+to converge.
+Use brightness
+.B 0
+(the default) to turn this off, and use environment map-based shading
+.RB (\(lq \-emap \(rq)
+instead.
+(5
+.IR float s);
+default:
+.RB \(lq 0\ \-1\ \-1\ \-4\ 0.7 \(rq
+.TP
+.B \-ao
+set up 6 area lights in a box to approximate ambient occlusion
+.TP
+.B \-shadow\fR <\fIs\fR>
+the extent to which shadowing occurs
+.RI ( float );
+default:
+.RB \(lq 1.0 \(rq
+.TP
+.B \-hack\fR <\fIhack\fR>
+don\(cqt mind me
+.RI ( string )
+.TP
+.B \-i\fR <\fInin\fR>
+input diffusion tensor volume; default:
+.RB \(lq \- \(rq
+.TP
+.B \-o\fR <\fInout\fR>
+output file
+.RI ( string )
+default:
+.RB \(lq \- \(rq
+.SH "SEE\ ALSO"
+.BR tend (1)
diff --git a/tend-helix.1 b/tend-helix.1
new file mode 100644
index 0000000..79326f5
--- /dev/null
+++ b/tend-helix.1
@@ -0,0 +1,133 @@
+.TH TEND\-HELIX "1" "April 2021" "" "User Commands"
+.SH NAME
+tend\-helix \- generate twisting helical tensor field
+.SH SYNOPSIS
+.B tend\ helix
+.RB [ \-s
+.RI < size >]
+.RB [ \-ip
+.RI < image\ orientation >]
+.RB [ \-mp
+.RI < measurement\ orientation >]
+.RB [ \-b
+.RI < boundary >]
+.RB [ \-r
+.RI < little\ radius >]
+.RB [ \-R
+.RI < big\ radius >]
+.RB [ \-S
+.RI < spacing >]
+.RB [ \-a
+.RI < angle >]
+.RB [ \-nit ]
+.RB [ \-ev
+.RI < eigenvalues >]
+.RB [ \-bg
+.RI < background >]
+.RB [ \-v
+.RI < verbose >]
+.RB [ \-o
+.RI < nout >]
+.SH DESCRIPTION
+Generate twisting helical tensor field.
+The main utility of such a field is to debug handling of coordinate systems in
+tensor field visualization.
+The \(lqspace directions\(rq and \(lqspace origin\(rq fields of the NRRD header
+determines the mapping from coordinates in the index space of the image to
+coordinates in the world space in which the image is sampled.
+The \(lqmeasurement frame\(rq field determines the mapping from the coordinates
+of the tensor itself, to coordinates of the world space.
+When these are correctly handled, the region of high anisotropy is a
+right-handed helix (same as DNA).
+Using differing axes sizes (via
+.RB \(lq \-s \(rq)
+helps make sure that the raster ordering of axes is correct.
+In addition, the tensors twist relative to the helix, which exposes handling of
+the measurement frame.
+If you trace paths guided by the principal eigenvector of the tensors, along
+the surface of the helical cylinder, you get another right-handed helix, as if
+the the tensor field is modeling the result of twisting a set of fibers into
+single-stranded helical bundle.
+.SH OPTIONS
+.TP
+.B \-s\fR <\fIsize\fR>
+sizes along fast, medium, and slow axes of the sampled volume, often called
+\(lqX\(rq, \(lqY\(rq, and \(lqZ\(rq.
+It is best to use slightly different sizes here, to expose errors in
+interpreting axis ordering (e.g. \(lq\fB\-s\ 39\ 40\ 41\fR\(rq) (3 \fIint\fRs)
+.TP
+.B \-ip\fR <\fIimage\ orientation\fR>
+quaternion quotient space orientation of image (3 \fIdouble\fRs); default:
+.RB \(lq 0\ 0\ 0 \(rq
+.TP
+.B \-mp\fR <\fImeasurement\ orientation\fR>
+quaternion quotient space orientation of measurement frame (3 \fIdouble\fRs);
+default:
+.RB \(lq 0\ 0\ 0 \(rq
+.TP
+.B \-b\fR <\fIboundary\fR>
+parameter governing how fuzzy the boundary between high and low anisotropy is.
+Use
+.RB \(lq \-b\ 0 \(rq
+for no fuzziness
+.RI ( double );
+default:
+.RB \(lq 10 \(rq
+.TP
+.B \-r\fR <\fIlittle\ radius\fR>
+(minor) radius of cylinder tracing helix
+.RI ( double );
+default:
+.RB \(lq 30 \(rq
+.TP
+.B \-R\fR <\fIbig\ radius\fR>
+(major) radius of helical turns
+.RI ( double );
+default:
+.RB \(lq 50 \(rq
+.TP
+.B \-S\fR <\fIspacing\fR>
+spacing between turns of helix (along its axis)
+.RI ( double );
+default:
+.RB \(lq 100 \(rq
+.TP
+.B \-a\fR <\fIangle\fR>
+maximal angle of twist of tensors along path.
+There is no twist at helical core of path, and twist increases linearly with
+radius around this path.
+Positive twist angle with positive spacing resulting in a right-handed twist
+around a right-handed helix.
+.RI ( double );
+default:
+.RB \(lq 60 \(rq
+.TP
+.B \-nit
+changes behavior of twist angle as function of distance from center of helical
+core: instead of increasing linearly as describe above, be at a constant angle
+.TP
+.B \-ev\fR <\fIeigenvalues\fR>
+eigenvalues of tensors (in order) along direction of coil, circumferential
+around coil, and radial around coil.
+(3 \fIdouble\fRs); default:
+.RB \(lq 0.006\ 0.002\ 0.001 \(rq
+.TP
+.B \-bg\fR <\fIbackground\fR>
+eigenvalue of isotropic background
+.RI ( double );
+default:
+.RB \(lq 0.5 \(rq
+.TP
+.B \-v\fR <\fIverbose\fR>
+verbose output
+.RI ( int );
+default:
+.RB \(lq 1 \(rq
+.TP
+.B \-o\fR <\fInout\fR>
+output file
+.RI ( string )
+default:
+.RB \(lq \- \(rq
+.SH "SEE\ ALSO"
+.BR tend (1)
diff --git a/tend-log.1 b/tend-log.1
new file mode 100644
index 0000000..07d328a
--- /dev/null
+++ b/tend-log.1
@@ -0,0 +1,25 @@
+.TH TEND\-LOG "1" "April 2021" "" "User Commands"
+.SH NAME
+tend\-log \- calculates logarithm of the tensor
+.SH SYNOPSIS
+.B tend\ log
+.RB [ \-i
+.RI < nin >]
+.B \-o
+.RI < nout >
+.SH DESCRIPTION
+Calculates logarithm of the tensor, which is based on finding the log of the
+eigenvalues.
+.SH OPTIONS
+.TP
+.B \-i\fR <\fInin\fR>
+input diffusion tensor volume; default:
+.RB \(lq \- \(rq
+.TP
+.B \-o\fR <\fInout\fR>
+output image
+.RI ( string )
+default:
+.RB \(lq \- \(rq
+.SH "SEE\ ALSO"
+.BR tend (1)
diff --git a/tend-msim.1 b/tend-msim.1
index 2d0b76a..3f37f0b 100644
--- a/tend-msim.1
+++ b/tend-msim.1
@@ -63,7 +63,8 @@ gradient list, one row per diffusion-weighted image
.B \-b0\fR <\fIb0\ image\fR>
reference non-diffusion-weighted
.RB (\(lq B0 \(rq)
-image, which may be needed if it isn't part of the given model parameter image
+image, which may be needed if it isn\(cqt part of the given model parameter
+image
.TP
.B \-m\fR <\fImodel\fR>
model with which to simulate DWIs, which must be specified if it is not
@@ -74,7 +75,7 @@ indicated by the first axis in input model image.
insert a non-DW
.B B0
image at the beginning of the experiment specification (useful if the given
-gradient list doesn't already have one) and hence also insert a
+gradient list doesn\(cqt already have one) and hence also insert a
.B B0
image at the beginning of the output simulated DWIs
.RB \(lq bool \(rq
diff --git a/tend-norm.1 b/tend-norm.1
new file mode 100644
index 0000000..9d74928
--- /dev/null
+++ b/tend-norm.1
@@ -0,0 +1,48 @@
+.TH TEND\-NORM "1" "April 2021" "" "User Commands"
+.SH NAME
+tend\-norm \- normalize tensor size
+.SH SYNOPSIS
+.B tend\ norm
+.B \-w
+.RI < w0 \ w1 \ w2 >
+.RB [ \-a
+.RI < amount >]
+.RB [ \-t
+.RI < target >]
+.RB [ \-i
+.RI < nin >]
+.RB [ \-o
+.RI < nout >]
+.SH OPTIONS
+.TP
+.B \-w\fR <\fIw0\fR\ \fIw1\fR\ \fIw2\fR>
+relative weights to put on major, medium, and minor eigenvalue when performing
+normalization (internally rescaled to have a 1.0 L1 norm).
+These weightings determine the tensors\(cqs \(lqsize\(rq.
+(3
+.IR double s)
+.TP
+.B \-a\fR <\fIamount\fR>
+how much of the normalization to perform
+.RI ( float )
+default:
+.RB \(lq 1.0 \(rq
+.TP
+.B \-t\fR <\fItarget\fR>
+target size, post normalization
+.RI ( float )
+default:
+.RB \(lq 1.0 \(rq
+.TP
+.TP
+.B \-i\fR <\fInin\fR>
+input diffusion tensor volume; default:
+.RB \(lq \- \(rq
+.TP
+.B \-o\fR <\fInout\fR>
+output image (floating point)
+.RI ( string )
+default:
+.RB \(lq \- \(rq
+.SH "SEE\ ALSO"
+.BR tend (1)
diff --git a/tend-point.1 b/tend-point.1
new file mode 100644
index 0000000..7e2e55e
--- /dev/null
+++ b/tend-point.1
@@ -0,0 +1,27 @@
+.TH TEND\-POINT "1" "April 2021" "" "User Commands"
+.SH NAME
+tend\-point \- describe everything about one sample in a DT volume
+.SH SYNOPSIS
+.B tend\ point
+.B \-p
+.RI < x \ y \ z >
+.RB [ \-i
+.RI < nin >]
+.SH OPTIONS
+.TP
+.TP
+.B \-p\fR <\fIx\fR\ \fIy\fR\ \fIz\fR>
+coordinates of sample to be described (3
+.IR int s)
+.TP
+.B \-i\fR <\fInin\fR>
+input diffusion tensor volume; default:
+.RB \(lq \- \(rq
+.TP
+.B \-o\fR <\fInout\fR>
+output array
+.RI ( string )
+default:
+.RB \(lq \- \(rq
+.SH "SEE\ ALSO"
+.BR tend (1)
diff --git a/tend-satin.1 b/tend-satin.1
new file mode 100644
index 0000000..5b6b85f
--- /dev/null
+++ b/tend-satin.1
@@ -0,0 +1,113 @@
+.TH TEND\-SATIN "1" "April 2021" "" "User Commands"
+.SH NAME
+tend\-satin \- generate a pretty synthetic DT volume
+.SH SYNOPSIS
+.B tend\ satin
+.RI [ \-t ]
+.B \-p
+.RI < aniso\ param >
+.RB [ \-max
+.RI < max\ cal >]
+.RB [ \-min
+.RI < min\ cal >]
+.RB [ \-b
+.RI < boundary >]
+.RB [ \-br
+.RI < ramp >]
+.RB [ \-th
+.RI < thickness >]
+.RB [ \-evsc
+.RI < eval\ scale >]
+.RB [ \-s
+.RI < size >]
+.RB [ \-o
+.RI < nout >]
+.SH DESCRIPTION
+Generate a pretty synthetic DT volume.
+The surface of a sphere or torus is covered with either linear or planar
+anisotropic tensors, or somewhere in between.
+.SH OPTIONS
+.TP
+.B \-t
+generate a torus dataset, instead of the default spherical
+.TP
+.B \-p\fR <\fIaniso\ param\fR>
+anisotropy parameter.
+.B 0.0
+for one direction of linear (along the equator for spheres, or along the larger
+circumference for toruses),
+.B 1.0
+for planar,
+.B 2.0
+for the other direction of linear (from pole to pole for spheres, or along the
+smaller circumference for toruses)
+.RI ( float )
+.TP
+.B \-max\fR <\fImax\ cal\fR>
+maximum anisotropy in dataset, according to the
+.RB \(lq ca1 \(rq
+anisotropy metric.
+.RB \(lq 1.0 \(rq
+means completely linear or completely planar anisotropy
+.RI ( float );
+default:
+.RB \(lq 1.0 \(rq
+.TP
+.B \-min\fR <\fImin\ cal\fR>
+minimum anisotropy in dataset
+.RI ( float );
+default:
+.RB \(lq 0.0 \(rq
+.TP
+.B \-b\fR <\fIboundary\fR>
+parameter governing how fuzzy the boundary between high and low anisotropy is.
+Use
+.RB \(lq \-b\ 0 \(rq
+for no fuzziness
+.RI ( float );
+default:
+.RB \(lq 0.05 \(rq
+.TP
+.B \-br\fR <\fIramp\fR>
+how much to ramp up effective
+.RI \(lq \-b \(rq
+along Y axis.
+Use
+.RB \(lq \-b\ 0 \(rq
+for no such ramping.
+.RI ( float );
+default:
+.RB \(lq 0.0 \(rq
+.TP
+.B \-th\fR <\fIthickness\fR>
+parameter governing how thick region of high anisotropy is
+.RI ( float );
+default:
+.RB \(lq 0.3 \(rq
+.TP
+.B \-evsc\fR <\fIeval\ scale\fR>
+scaling of eigenvalues
+.RI ( float );
+default:
+.RB \(lq 1.0 \(rq
+.TP
+.B \-s\fR <\fIsize\fR>
+dimensions of output volume.
+For size
+.IR N ,
+the output is
+.IB N \ x\ N \ x\ N
+for spheres, and
+.BI 2 N \ x\ 2 N \ x\ N
+for toruses
+.RI ( int );
+default:
+.RB \(lq 32 \(rq
+.TP
+.B \-o\fR <\fInout\fR>
+output filename
+.RI ( string );
+default:
+.RB \(lq \- \(rq
+.SH "SEE\ ALSO"
+.BR tend (1)
diff --git a/tend-shrink.1 b/tend-shrink.1
new file mode 100644
index 0000000..8c504e1
--- /dev/null
+++ b/tend-shrink.1
@@ -0,0 +1,32 @@
+.TH TEND\-SHRINK "1" "April 2021" "" "User Commands"
+.SH NAME
+tend\-shrink \- converts a 9-value DT volume to a 7-value DT volume
+.SH SYNOPSIS
+.B tend\ shrink
+.RB [ \-i
+.RI < nin >]
+.RB [ \-o
+.RI < nout >]
+.SH DESCRIPTION
+Converts a 9-value DT volume to a 7-value DT volume.
+The confidence value is set to
+.B 1.0
+everywhere.
+You can
+.BR unu\-splice (1)
+or
+.B nrrdSplice()
+something else in its place later.
+.SH OPTIONS
+.TP
+.B \-i\fR <\fInin\fR>
+input diffusion tensor volume, with 9 matrix components per sample; default:
+.RB \(lq \- \(rq
+.TP
+.B \-o\fR <\fInout\fR>
+output tensor volume, with the 7 values per sample
+.RI ( string )
+.SH "SEE\ ALSO"
+.BR tend (1),
+.BR tend\-expand (1),
+.BR unu\-splice (1)
diff --git a/tend-sim.1 b/tend-sim.1
index 3761398..c40a192 100644
--- a/tend-sim.1
+++ b/tend-sim.1
@@ -45,7 +45,7 @@ measurement frame.
.SH OPTIONS
.TP
.B \-old
-don't use the new
+don\(cqt use the new
.B tenEstimateContext
functionality
.TP
diff --git a/tend-slice.1 b/tend-slice.1
new file mode 100644
index 0000000..a42335a
--- /dev/null
+++ b/tend-slice.1
@@ -0,0 +1,45 @@
+.TH TEND\-SLICE "1" "April 2021" "" "User Commands"
+.SH NAME
+tend\-slice \- slice 3D tensors to get slab/image of 3D/2D tensors
+.SH SYNOPSIS
+.B tend\ slice
+.B \-a
+.RI < axis >
+.B \-p
+.RI < pos >
+.RB [ \-d
+.RI < dim >]
+.RB [ \-i
+.RI < nin >]
+.RB [ \-o
+.RI < nout >]
+.SH OPTIONS
+.TP
+.B \-a\fR <\fIaxis\fR>
+axis along which to slice
+.RI ( int )
+.TP
+.B \-p\fR <\fIpos\fR>
+position to slice at
+.RI ( int )
+.TP
+.B \-d\fR <\fIdim\fR>
+dimension of desired tensor output, can be either
+.B 2
+or
+.B 3
+.RI ( int );
+default:
+.RB \(lq 3 \(rq
+.TP
+.B \-i\fR <\fInin\fR>
+input diffusion tensor volume; default:
+.RB \(lq \- \(rq
+.TP
+.B \-o\fR <\fInout\fR>
+output tensor slice
+.RI ( string )
+default:
+.RB \(lq \- \(rq
+.SH "SEE\ ALSO"
+.BR tend (1)
diff --git a/tend-sten.1 b/tend-sten.1
new file mode 100644
index 0000000..e5a13a5
--- /dev/null
+++ b/tend-sten.1
@@ -0,0 +1,51 @@
+.TH TEND\-STEN "1" "April 2021" "" "User Commands"
+.SH NAME
+tend\-sten \- calculate structure tensors from a scalar field
+.SH SYNOPSIS
+.B tend\ sten
+.RB [ \-ds
+.RI < diff.\ scale >]
+.RB [ \-is
+.RI < int.\ scale >]
+.RB [ \-df
+.RI < downsample\ factor >]
+.RB [ \-i
+.RI < nin >]
+.RB [ \-o
+.RI < nout >]
+.SH DESCRIPTION
+Calculate structure tensors from a scalar field.
+Not a diffusion tensor, but it is symmetric and positive-definite.
+.SH OPTIONS
+.TP
+.B \-ds\fR <\fIdiff.\ scale\fR>
+differentiation scale, in pixels: the radius of the kernel used for
+differentation to compute gradient vectors
+.RI ( int );
+default:
+.RB \(lq 1 \(rq
+.TP
+.B \-is\fR <\fIint.\ scale\fR>
+integration scale, in pixels: the radius of the kernel used for blurring outer
+products of gradients in order compute structure tensors
+.RI ( int );
+default:
+.RB \(lq 2 \(rq
+.TP
+.B \-df\fR <\fIdownsample\ factor\fR>
+the factor by which to downsample when creating volume of structure tensors
+.RI ( int );
+default:
+.RB \(lq 1 \(rq
+.TP
+.B \-i\fR <\fInin\fR>
+input scalar volume; default:
+.RB \(lq \- \(rq
+.TP
+.B \-o\fR <\fInout\fR>
+output filename
+.RI ( string )
+default:
+.RB \(lq \- \(rq
+.SH "SEE\ ALSO"
+.BR tend (1)
diff --git a/tend-tconv.1 b/tend-tconv.1
new file mode 100644
index 0000000..7949634
--- /dev/null
+++ b/tend-tconv.1
@@ -0,0 +1,34 @@
+.TH TEND\-TCONV "1" "April 2021" "" "User Commands"
+.SH NAME
+tend\-tconv \- convert between different shape triples
+.SH SYNOPSIS
+.B tend\ tconv
+.B \-t
+.RI < inType \ outType >
+.RB [ \-i
+.RI < nin >]
+.RB [ \-o
+.RI < nout >]
+.SH DESCRIPTION
+Convert between different shape triples.
+The triple can be eigenvalues,
+invariants
+.BI ( J ,\ K ,\ R )\fR,
+and lots of other things.
+.SH OPTIONS
+.TP
+.B \-t\fR <\fIinType\fR\ \fIoutType\fR>
+given input and desired output type of triples (2
+.IR tenTripleType s)
+.TP
+.B \-i\fR <\fInin\fR>
+input array of triples; default:
+.RB \(lq \- \(rq
+.TP
+.B \-o\fR <\fInout\fR>
+output array
+.RI ( string )
+default:
+.RB \(lq \- \(rq
+.SH "SEE\ ALSO"
+.BR tend (1)
diff --git a/tend-triple.1 b/tend-triple.1
new file mode 100644
index 0000000..21fe16e
--- /dev/null
+++ b/tend-triple.1
@@ -0,0 +1,33 @@
+.TH TEND\-TRIPLE "1" "April 2021" "" "User Commands"
+.SH NAME
+tend\-triple \- compute volume of shape triples
+.SH SYNOPSIS
+.B tend\ triple
+.B \-t
+.RI < type >
+.RB [ \-i
+.RI < nin >]
+.RB [ \-o
+.RI < nout >]
+.SH DESCRIPTION
+Compute volume of shape triples.
+The triple can be eigenvalues,
+invariants
+.BI ( J ,\ K ,\ R )\fR,
+and lots of other things.
+.SH OPTIONS
+.TP
+.B \-t\fR <\fItype\fR>
+desired output triple type
+.TP
+.B \-i\fR <\fInin\fR>
+input diffusion tensor volume; default:
+.RB \(lq \- \(rq
+.TP
+.B \-o\fR <\fInout\fR>
+output triple volume
+.RI ( string )
+default:
+.RB \(lq \- \(rq
+.SH "SEE\ ALSO"
+.BR tend (1)
diff --git a/tend-unmf.1 b/tend-unmf.1
new file mode 100644
index 0000000..d97b5ce
--- /dev/null
+++ b/tend-unmf.1
@@ -0,0 +1,29 @@
+.TH TEND\-UNMF "1" "April 2021" "" "User Commands"
+.SH NAME
+tend\-unmf \- applies and removes the measurement frame
+.SH SYNOPSIS
+.B tend\ unmf
+.RB [ \-i
+.RI < nin >]
+.RB [ \-o
+.RI < nout >]
+.SH DESCRIPTION
+Applies and removes the measurement frame.
+When the given tensor volume has a measurement frame associated with it, this
+will apply the measurement frame transform to all tensors to convert them into
+world space, and remove the measurement frame from the
+.IR nrrd .
+.SH OPTIONS
+.TP
+.B \-i\fR <\fInin\fR>
+input diffusion tensor volume (sorry, can\(cqt use usual default of
+.RB \(lq \- \(rq
+for stdin because of hest quirk)
+.TP
+.B \-o\fR <\fInout\fR>
+output tensor volume
+.RI ( string )
+default:
+.RB \(lq \- \(rq
+.SH "SEE\ ALSO"
+.BR tend (1)
diff --git a/tend.1 b/tend.1
index e61040a..831950d 100644
--- a/tend.1
+++ b/tend.1
@@ -64,206 +64,92 @@ eigenvector, and then quantizes it to an 8-bit PNG:
.EE
.in
.SH OPTIONS
+.SH COMMANDS
+.P
+Each of the following has a corresponding man page: for example,
+.BR tend\-about (1)
+for
+.RB \(lq tend\ about \(rq.
.SS tend\ about
Information about this program and its use
-.P
-See
-.BR tend\-about (1).
.SS tend\ grads
Calculate balanced gradient directions for DWI acquisition
-.P
-See
-.BR tend\-grads (1).
.SS tend\ epireg
Register diffusion-weighted echo-planar images
-.P
-See
-.BR tend\-epireg (1).
.SS tend\ bmat
Calculate B-matrix given gradient directions
-.P
-See
-.BR tend\-bmat (1).
.SS tend\ estim
Estimate tensors from a set of DW images
-.P
-See
-.BR tend\-estim (1).
.SS tend\ sim
Simulate DW images from a tensor field
-.P
-See
-.BR tend\-sim (1).
.SS tend\ mfit
Estimate models from a set of DW images
-.P
-See
-.BR tend\-mfit (1).
.SS tend\ mconv
Convert from one model to another
-.P
-See
-.BR tend\-mconv (1).
.SS tend\ msim
Simulate DW images from an image of models
-.P
-See
-.BR tend\-msim (1).
.SS tend\ make
Create DT volume from confidence and eigensystem
-.P
-See
-.BR tend\-make (1).
.SS tend\ avg
Average across tensor volumes
-.P
-See
-.BR tend\-avg (1).
.SS tend\ helix
Generate twisting helical tensor field
-.P
-See
-.BR tend\-helix (1).
.SS tend\ sten
Calculate structure tensors from a scalar field
-.P
-See
-.BR tend\-sten (1).
.SS tend\ glyph
Generate postscript or ray-traced renderings of 3D glyphs
-.P
-See
-.BR tend\-glyph (1).
.SS tend\ ellipse
Generate postscript renderings of 2D glyphs
-.P
-See
-.BR tend\-ellipse (1).
.SS tend\ anplot
Graph anisotropy metric in barycentric coords
-.P
-See
-.BR tend\-anplot (1).
.SS tend\ anvol
Apply an anisotropy metric to a DT volume
-.P
-See
-.BR tend\-anvol (1).
.SS tend\ anscale
Scale the anisotropic component of the tensors
-.P
-See
-.BR tend\-anscale (1).
.SS tend\ anhist
Generate barycentric histograms of anisotropy
-.P
-See
-.BR tend\-anhist (1).
.SS tend\ triple
Compute volume of shape triples
-.P
-See
-.BR tend\-triple (1).
.SS tend\ tconv
Convert between different shape triples
-.P
-See
-.BR tend\-tconv (1).
.SS tend\ point
Describe everything about one sample in a DT volume
-.P
-See
-.BR tend\-point (1).
.SS tend\ slice
Slice 3D tensors to get slab/image of 3D/2D tensors
-.P
-See
-.BR tend\-slice (1).
.SS tend\ norm
Normalize tensor size
-.P
-See
-.BR tend\-norm (1).
.SS tend\ fiber
Fiber tractography, from one or more seeds
-.P
-See
-.BR tend\-fiber (1).
.SS tend\ eval
Calculate one or more eigenvalues in a DT volume
-.P
-See
-.BR tend\-eval (1).
.SS tend\ evalpow
Modify shape by raising eigenvalues to some power
-.P
-See
-.BR tend\-evalpow (1).
.SS tend\ evalclamp
Modify shape by clamping eigenvalues in some range
-.P
-See
-.BR tend\-evalclamp (1).
.SS tend\ evaladd
Modify shape by adding a constant to all eigenvalues
-.P
-See
-.BR tend\-evaladd (1).
-.SS tend\ evalmul
+.SS tend\ evalmult
Modify shape by multiplying eigenvalues by a constant
-.P
-See
-.BR tend\-evalmul (1).
.SS tend\ log
Calculates logarithm of the tensor
-.P
-See
-.BR tend\-log (1).
.SS tend\ exp
Calculates \fBexp()\fR of the tensor
-.P
-See
-.BR tend\-exp (1).
.SS tend\ evec
Calculate one or more eigenvectors in a DT volume
-.P
-See
-.BR tend\-evec (1).
.SS tend\ evecrgb
Make an RGB volume from an eigenvector and an anisotropy
-.P
-See
-.BR tend\-evecrgb (1).
.SS tend\ evq
Quantize directions of diffusion
-.P
-See
-.BR tend\-evq (1).
.SS tend\ unmf
Applies and removes the measurement frame
-.P
-See
-.BR tend\-unmf (1).
.SS tend\ expand
Converts masked non-redundant tensor images to redundant
-.P
-See
-.BR tend\-expand (1).
.SS tend\ shrink
Converts a 9-value DT volume to a 7-value DT volume
-.P
-See
-.BR tend\-shrink (1).
.SS tend\ bfit
Non-linear least-squares fitting of b-value curves
-.P
-See
-.BR tend\-bfit (1).
.SS tend\ satin
Generate a pretty synthetic DT volume
-.P
-See
-.BR tend\-satin (1).
.SH "SEE\ ALSO"
.BR gprobe (1),
.BR ilk (1),
@@ -288,7 +174,7 @@ See
.BR tend\-eval (1),
.BR tend\-evaladd (1),
.BR tend\-evalclamp (1),
-.BR tend\-evalmul (1),
+.BR tend\-evalmult (1),
.BR tend\-evalpow (1),
.BR tend\-evec (1),
.BR tend\-evecrgb (1),
diff --git a/unu.1 b/unu.1
index ef710be..6f8b611 100644
--- a/unu.1
+++ b/unu.1
@@ -115,6 +115,194 @@ Formats available:
.P
Nrrd data encodings available:
.B raw ascii hex gz bz2
+.SH COMMANDS
+\" .P
+\" Each of the following has a corresponding man page: for example,
+\" .BR unu\-about (1)
+\" for
+\" .RB \(lq unu\ about \(rq.
+.SS unu about
+Information about this program and its use
+.SS unu env
+List relevant environment variables and their values
+.SS unu i2w
+Convert from 1-D index to world position
+.SS unu w2i
+Convert from 1-D world to index position
+.SS unu make
+Create a
+.I nrrd
+(or
+.I nrrd
+header) from scratch
+.SS unu head
+Print header of one or more
+.I nrrd
+files
+.SS unu data
+Print data segment of a
+.I nrrd
+file
+.SS unu convert
+Convert to another type (as if by cast, w/ optional clamp)
+.SS unu resample
+Filtering and {up,down}sampling with a separable kernel
+.SS unu fft
+Fast Fourier Transform of selected axes
+.SS unu cmedian
+Cheap histogram-based median/mode filtering
+.SS unu dist
+Euclidean distance transform
+.SS unu minmax
+Print out min and max values in one or more
+.IR nrrd s
+.SS unu cksum
+Compute 32-bit CRC of
+.I nrrd
+data (same as via
+.BR cksum (1))
+.SS unu diff
+Sees if two
+.IR nrrd s
+are different in any way
+.SS unu quantize
+Quantize values to 8, 16, or 32 bits
+.SS unu unquantize
+Recover floating point values from quantized data
+.SS unu project
+Collapse scanlines to scalars along some axis
+.SS unu slice
+Slice along one or more axes at given positions
+.SS unu sselect
+Select subset of slices along an axis
+.SS unu dice
+Save all slices along one axis into separate files
+.SS unu splice
+Replace a slice with a different
+.I nrrd
+.SS unu join
+Connect slices and/or slabs into a bigger
+.I nrrd
+.SS unu crop
+Crop along each axis to make a smaller
+.I nrrd
+.SS unu acrop
+Automatically crop axes based on given measure
+.SS unu inset
+Replace a sub-region with a different
+.I nrrd
+.SS unu pad
+Pad along each axis to make a bigger
+.I nrrd
+.SS unu reshape
+Superficially change dimension and/or axes sizes
+.SS unu permute
+Permute ordering of axes
+.SS unu swap
+Interchange ordering of two axes
+.SS unu shuffle
+Permute slices along one axis
+.SS unu flip
+Reverse order of slices along one axis
+.SS unu unorient
+Make image orientation be axis-aligned
+.SS unu axinfo
+Modify attributes of one or more axes
+.SS unu axinsert
+Add a \(lqstub\(rq (length 1) axis to a
+.I nrrd
+.SS unu axsplit
+Split one axis into two axes
+.SS unu axdelete
+Remove one or more singleton axes from a
+.I nrrd
+.SS unu axmerge
+Merge two adjacent axes into one
+.SS unu tile
+Tile slices of one axis into two other axes
+.SS unu untile
+Undo
+.BR unu\-tile (1):
+merge slow parts of two axis splits
+.SS unu histo
+Create 1-D histogram of values in a
+.I nrrd
+.SS unu dhisto
+Create image of 1-D value histogram
+.SS unu jhisto
+Create joint histogram of two or more
+.IR nrrd s
+.SS unu histax
+Replace each scanline along an axis with its histogram
+.SS unu heq
+Perform histogram equalization
+.SS unu gamma
+Brighten or darken values with a gamma
+.SS unu 1op
+Unary operation on a
+.I nrrd
+.SS unu 2op
+Binary operation on two
+.IR nrrd s,
+or on a
+.I nrrd
+and a constant
+.SS unu 3op
+Ternary operation on three
+.IR nrrd s
+or constants
+.SS unu affine
+Affine (lerp) mapping on 5
+.IR nrrd s
+or constants
+.SS unu lut
+Map
+.I nrrd
+through one univariate lookup table
+.SS unu mlut
+Map
+.I nrrd
+through whole
+.I nrrd
+of univariate lookup tables
+.SS unu subst
+Map
+.I nrrd
+through a univariate substitution table
+.SS unu rmap
+Map
+.I nrrd
+through one
+.B regular
+univariate map (\(lqcolormap\(rq)
+.SS unu mrmap
+Map
+.I nrrd
+through a whole
+.I nrrd
+of regular univariate maps
+.SS unu imap
+Map
+.I nrrd
+through
+.B irregular
+univariate map (\(lqcolormap\(rq)
+.SS unu lut2
+Map
+.I nrrd
+through a bivariate lookup table
+.SS unu ccfind
+Find connected components (CCs)
+.SS unu ccadj
+Form adjecency matrix of connected components
+.SS unu ccmerge
+Merge CCs with their neighbors, under various constraints
+.SS unu ccsettle
+Remap CC values down to lowest contiguous values
+.SS unu save
+Write
+.I nrrd
+with specific format, encoding, or endianness
.SH "SEE\ ALSO"
.BR gprobe (1),
.BR ilk (1),
@@ -125,3 +313,66 @@ Nrrd data encodings available:
.BR puller (1),
.BR tend (1),
.BR vprobe (1)
+\" .BR vprobe (1),
+\" .BR unu-1op (1),
+\" .BR unu-2op (1),
+\" .BR unu-3op (1),
+\" .BR unu-about (1),
+\" .BR unu-acrop (1),
+\" .BR unu-affine (1),
+\" .BR unu-axdelete (1),
+\" .BR unu-axinfo (1),
+\" .BR unu-axinsert (1),
+\" .BR unu-axmerge (1),
+\" .BR unu-axsplit (1),
+\" .BR unu-ccadj (1),
+\" .BR unu-ccfind (1),
+\" .BR unu-ccmerge (1),
+\" .BR unu-ccsettle (1),
+\" .BR unu-cksum (1),
+\" .BR unu-cmedian (1),
+\" .BR unu-convert (1),
+\" .BR unu-crop (1),
+\" .BR unu-data (1),
+\" .BR unu-dhisto (1),
+\" .BR unu-dice (1),
+\" .BR unu-diff (1),
+\" .BR unu-dist (1),
+\" .BR unu-env (1),
+\" .BR unu-fft (1),
+\" .BR unu-flip (1),
+\" .BR unu-gamma (1),
+\" .BR unu-head (1),
+\" .BR unu-heq (1),
+\" .BR unu-histax (1),
+\" .BR unu-histo (1),
+\" .BR unu-i2w (1),
+\" .BR unu-imap (1),
+\" .BR unu-inset (1),
+\" .BR unu-jhisto (1),
+\" .BR unu-join (1),
+\" .BR unu-lut (1),
+\" .BR unu-lut2 (1),
+\" .BR unu-make (1),
+\" .BR unu-minmax (1),
+\" .BR unu-mlut (1),
+\" .BR unu-mrmap (1),
+\" .BR unu-pad (1),
+\" .BR unu-permute (1),
+\" .BR unu-project (1),
+\" .BR unu-quantize (1),
+\" .BR unu-resample (1),
+\" .BR unu-reshape (1),
+\" .BR unu-rmap (1),
+\" .BR unu-save (1),
+\" .BR unu-shuffle (1),
+\" .BR unu-slice (1),
+\" .BR unu-splice (1),
+\" .BR unu-sselect (1),
+\" .BR unu-subst (1),
+\" .BR unu-swap (1),
+\" .BR unu-tile (1),
+\" .BR unu-unorient (1),
+\" .BR unu-unquantize (1),
+\" .BR unu-untile (1),
+\" .BR unu-w2i (1)
commit 8d05a3a34a29ea41d5b2ca9712f366ef4f57a1b2
Author: Benjamin A. Beasley <code(a)musicinmybrain.net>
Date: Tue Apr 6 12:03:06 2021 -0400
Add an rpmlintrc file
diff --git a/teem.rpmlintrc b/teem.rpmlintrc
new file mode 100644
index 0000000..890462c
--- /dev/null
+++ b/teem.rpmlintrc
@@ -0,0 +1,5 @@
+# These are not real spelling errors...
+addFilter(r'spelling-error .*(\(en_US\)| en_US) (resample|datasets|'
+ r'tractography|unu) ')
+# Indeed, there is no dedicated documentation for some subpackages.
+addFilter(r' no-documentation$')
commit d09ed97c6dc65490fb502eeb6baaae9b3b304a14
Author: Benjamin A. Beasley <code(a)musicinmybrain.net>
Date: Tue Apr 6 11:59:36 2021 -0400
rewrap description
diff --git a/teem.spec b/teem.spec
index 563e3b5..61764d0 100644
--- a/teem.spec
+++ b/teem.spec
@@ -182,8 +182,8 @@ Strengths of Teem
Slicer.
• Teem is light-weight: The libraries are written with an eye towards
minimizing the annoyance of getting data in an out, by using the simplest
- possible constructs for the job, and by supporting combinations of operations
- that arise in common practice.
+ possible constructs for the job, and by supporting combinations of
+ operations that arise in common practice.
• Teem is coherent: There is a consistent logic to how information is
represented, and uniformity in the APIs across libraries.
• Teem is portable: All the code is written in plain ANSI C, so it compiles
commit b3cb082255b1984b8f5a864a4576729446f37aee
Author: Benjamin A. Beasley <code(a)musicinmybrain.net>
Date: Tue Apr 6 11:36:04 2021 -0400
Fix single-threading tests
diff --git a/teem.spec b/teem.spec
index b69288c..563e3b5 100644
--- a/teem.spec
+++ b/teem.spec
@@ -298,7 +298,8 @@ install -t %{buildroot}%{_mandir}/man1 -p -m 0644 \
%endif
# Tests are not parallel-safe
-%ctest3 %{?ctest_excludes} -j1
+%global _smp_mflags -j1
+%ctest3 %{?ctest_excludes}
%if 0%{?epel} == 7
commit ba847837bef28947baf576882c1463ada7d0ae7f
Author: Benjamin A. Beasley <code(a)musicinmybrain.net>
Date: Tue Apr 6 11:34:57 2021 -0400
Start work on adding a complete set of man pages
diff --git a/gprobe.1 b/gprobe.1
new file mode 100644
index 0000000..4f35b12
--- /dev/null
+++ b/gprobe.1
@@ -0,0 +1,264 @@
+.TH GPROBE "1" "April 2021" "" "User Commands"
+.SH NAME
+gprobe \- shows off the functionality of the gage library
+.SH SYNOPSIS
+.B gprobe
+.B \-i
+.RI < nin >
+.B \-k
+.RI < kind >
+.RB [ \-v
+.RI < verbosity >]
+.B \-q
+.RI < query >
+.RB [ \-gmc
+.RI < min\ gradmag >]
+.RB [ \-ofs ]
+.RB [ \-seed
+.RI < N >]
+.RB [ \-c
+.RI < bool >]
+.RB [ \-k00
+.RI < kern00 >]
+.RB [ \-k11
+.RI < kern11 >]
+.RB [ \-k22
+.RI < kern22 >]
+.RB [ \-rn ]
+.RB [ \-ssn
+.RI < SS\ # >]
+.RB [ \-ssr
+.RI < scale\ range >]
+.RB [ \-kssb
+.RI < kernel >]
+.RB [ \-kssr
+.RI < kernel >]
+.RB [ \-ssu ]
+.RB [ \-sso ]
+.RB [ \-ssnd ]
+.RB [ \-ssnb
+.RI < bias >]
+.RB [ \-ssf
+.RI < SS\ read/save\ format >]
+.RB [ \-s
+.RI < sclX \ xclY \ sxlZ >]
+.RB [ \-ssp
+.RI < pos >]
+.RB [ \-pg
+.RI < nrrd >]
+.RB [ \-pi
+.RI < nrrd >]
+.RB [ \-pp
+.RI < pos \ ...>]
+.RB [ \-eps
+.RI < epsilon >]
+.RB [ \-psi
+.RI < p >]
+.RB [ \-t
+.RI < type >]
+.RB [ \-o
+.RI < nout >]
+.SH DESCRIPTION
+.P
+Shows off the functionality of the gage library.
+Uses gageProbe() to query various kinds of volumes to learn various measured or
+derived quantities.
+.SH OPTIONS
+.TP
+.B \-i\fR\ <\fInin\fR>
+input volume
+.TP
+.B \-k\fR\ <\fIkind\fR>
+\(lqkind\(rq of volume
+.RB (\(lq scalar \(rq,
+.RB \(lq vector \(rq,
+.RB \(lq tensor \(rq,
+or
+.RB \(lq dwi \(rq)
+.TP
+.B \-v\fR\ <\fIverbosity\fR>
+verbosity level
+.RI ( int );
+default:
+.RB \(lq 1 \(rq
+.TP
+.B \-q\fR\ <\fIquery\fR>
+the quantity
+.RB ( scalar ,
+.BR vector ,
+or
+.BR matrix )
+to learn by probing
+.RI ( string )
+.TP
+.B \-gmc\fR\ <\fImin\ gradmag\fR>
+For curvature-based queries, use zero when gradient magnitude is below this
+.RI ( double );
+default:
+.RB \(lq 0.0 \(rq
+.TP
+.B \-ofs
+If only per-axis spacing is available, use that to contrive full orientation
+info
+.TP
+.B \-seed\fR\ <\fIN\fR>
+RNG seed; mostly for debugging
+.RI ( unsigned\ int );
+default:
+.RB \(lq 42 \(rq
+.TP
+.B \-c\fR\ <\fIbool\fR>
+clamp positions as part of probing
+.RI ( bool );
+default:
+.RB \(lq false \(rq
+.TP
+.B \-k00\fR\ <\fIkern00\fR>
+kernel for
+.BR gageKernel00 ;
+default:
+.RB \(lq tent \(rq
+.TP
+.B \-k11\fR\ <\fIkern11\fR>
+kernel for
+.BR gageKernel11 ;
+default:
+.RB \(lq cubicd:1,0 \(rq
+.TP
+.B \-k22\fR\ <\fIkern22\fR>
+kernel for
+.BR gageKernel22 ;
+default:
+.RB \(lq cubicdd:1,0 \(rq
+.TP
+.B \-rn
+renormalize kernel weights at each new sample location.
+\(lqAccurate\(rq kernels don\(cqt need this; doing it always makes things go
+slower
+.TP
+.B \-ssn\fR\ <\fISS\ #\fR>
+how many scale-space samples to evaluate, or,
+.B 0
+to turn-off all scale-space behavior
+.RI ( unsigned\ int );
+default:
+.RB \(lq 0 \(rq
+.TP
+.B \-ssr\fR\ <\fIscale\ range\fR>
+range of scales in scale-space (2
+.IR double s);
+default:
+.RB \(lq nan\ nan \(rq
+.TP
+.B \-kssb\fR\ <\fIkernel\fR>
+blurring kernel, to sample scale space; default:
+.RB \(lq dgauss:1,5 \(rq
+.TP
+.B \-kssr\fR\ <\fIkernel\fR>
+kernel for reconstructing from scale space samples
+.RB \(lq hermite \(rq
+.TP
+.B \-ssu
+do uniform samples along sigma, and not (by default) samples according to the
+effective diffusion scale
+.TP
+.B \-sso
+if not using \(lq\-ssu\(rq, use pre-computed optimal sigmas when possible
+.TP
+.B \-ssnd
+normalize derivatives by scale
+.TP
+.B \-ssnb\fR\ <\fIbias\fR>
+bias on scale-based derivative normalization
+.RI ( double );
+default:
+.RB \(lq 0.0 \(rq
+.TP
+.B \-ssf\fR\ <\fISS\ read/save\ format\fR>
+.BR printf (3)\-style
+format (including a
+.RB \(lq %u \(rq)
+for the filenames from which to read pre-blurred volumes computed for the
+stack, if they exist and match the stack parameters, and where to save them if
+they had to be re-computed.
+Leave this as empty string to disable this.
+.RI ( string );
+default: \(lq\(rq
+.TP
+.B \-s\fR\ <\fIsclX\fR\ \fIsclY\fR\ \fIsxlZ\fR>
+scaling factor for resampling on each axis
+.RB (> 1.0 :
+supersampling); use
+.RB \(lq \-ssp \(rq
+(and
+.RB \(lq \-psi \(rq)
+to specify scale position of sampling (3
+.IR double s);
+default:
+.RB \(lq 1\ 1\ 1 \(rq
+.TP
+.B \-ssp\fR\ <\fIpos\fR>
+when using scale-space, scale-position at which to probe
+.RI ( double );
+default:
+.RB \(lq 0 \(rq
+.TP
+.B \-pg\fR\ <\fInrrd\fR>
+overrides
+.RB \(lq \-s \(rq:
+2-D
+.I nrrd
+which specifies origin and direction vectors for sampling grid; default:
+\(lq\(rq
+.TP
+.B \-pi\fR\ <\fInrrd\fR>
+overrides
+.RB \(lq \-pv \(rq:
+probes at this list of 3-vec or 4-vec positions; default:
+\(lq\(rq
+.TP
+.B \-pp\fR\ <\fIpos\fR ...>
+overrides
+.RB \(lq \-pi \(rq:
+only sample at this specified point (3..4 \fIdouble\fRs); default:
+.RB \(lq nan\ nan \(rq
+.TP
+.B \-eps\fR\ <\fIepsilon\fR>
+if non-zero, and if query is a scalar, and if using
+.RB \(lq \-pp \(rq
+to query at a single point, then do
+.I epsilon
+offset probes to calculate discrete differences, to find the numerical gradient
+and hessian (for debugging)
+.RI ( double );
+default:
+.RB \(lq 0 \(rq
+.TP
+.B \-psi\fR\ <\fIp\fR>
+whether the probe location specification (by any of the four previous flags)
+are in index space
+.RI ( bool );
+default:
+.RB \(lq false \(rq
+.TP
+.B \-t\fR\ <\fItype\fR>
+type of output volume
+.RI ( type );
+default:
+.RB \(lq float \(rq
+.TP
+.B \-o\fR\ <\fInout\fR>
+output volume
+.RI ( string );
+default:
+.RB \(lq \- \(rq
+.SH "SEE\ ALSO"
+.BR ilk (1),
+.BR miter (1),
+.BR mrender (1),
+.BR nrrdSanity (1),
+.BR overrgb (1),
+.BR puller (1),
+.BR tend (1),
+.BR unu (1),
+.BR vprobe (1)
diff --git a/ilk.1 b/ilk.1
new file mode 100644
index 0000000..51b2915
--- /dev/null
+++ b/ilk.1
@@ -0,0 +1,180 @@
+.TH ILK "1" "April 2021" "" "User Commands"
+.SH NAME
+ilk \- (I)mage (L)inear Trans(X\-\->K)forms
+.SH SYNOPSIS
+.B ilk
+.RI [ @file \ ...]
+.RB [ \-i
+.RI < image >]
+.RB [ \-0
+.RI < origin >]
+.B \-t
+.RI < xform0 \ ...>
+.RB [ \-k
+.RI < kernel >]
+.RB [ \-min
+.RI < xMin \ yMin >]
+.RB [ \-max
+.RI < xMax \ yMax >]
+.RB [ \-b
+.RI < boundary >]
+.RB [ \-bg
+.RI < bg0 \ bg1 \ ...>]
+.RB [ \-s
+.RI < xSize \ ySize >]
+.RB [ \-a
+.RI < avg\ # >]
+.RB [ \-o
+.RI < filename >]
+.SH DESCRIPTION
+.P
+(I)mage (L)inear Trans(X\-\->K)forms.
+Applies linear (homogenous coordinate) transforms to a given image, using the
+given kernel for resampling.
+.SH OPTIONS
+.TP
+.BR @file \ ...
+response file(s) containing command-line arguments
+.TP
+.B \-i\fR <\fIimage\fR>
+input image; default:
+.RB \(lq \- \(rq
+.TP
+.B \-0\fR <\fIorigin\fR>
+where to locate
+.B (0,0)
+prior to applying transforms.
+.RS +7n
+.IP \(bu 2
+.RB \(lq u:<\fIfloat\fB>,<\fIfloat\fB> \(rq
+locate origin in a unit box
+.B [0,1]x[0,1]
+which covers the original image
+.IP \(bu 2
+.RB \(lq p:<\fIfloat\fB>,<\fIfloat\fB> \(rq
+locate origin at a particular pixel location, in the index space of the image
+.P
+default:
+.RB \(lq p:0,0 \(rq
+.RE
+.TP
+.B \-t\fR <\fIxform0\fR\ ...>
+transform(s) to apply to image.
+Transforms are applied in the order in which they appear.
+.RS +7n
+.IP \(bu 2
+.RB \(lq identity \(rq:
+no geometric transform, just resampling
+.IP \(bu 2
+.RB \(lq translate:\fIx\fB,\fIy\fB \(rq:
+shift image by vector
+.BR (\fIx\fB,\fIy\fB) ,
+as measured in pixels
+.IP \(bu 2
+.RB \(lq rotate:\fIang\fB \(rq:
+rotate CCW by
+.I ang
+degrees
+.IP \(bu 2
+.RB \(lq scale:\fIxs\fB,\fIys\fB \(rq:
+scale by
+.I xs
+in X, and
+.I ys
+in Y
+.IP \(bu 2
+.RB \(lq shear:\fIfix\fB,\fIamnt\fB \(rq:
+shear by
+.IR amnt ,
+keeping fixed the pixels along a direction
+.RI < fix >
+degrees from the X axis
+.IP \(bu 2
+.RB \(lq flip:\fIang\fB \(rq:
+flip along axis an angle
+.RI < ang >
+degrees from the X axis
+.IP \(bu 2
+.RB \(lq \fIa\fB,\fIb\fB,\fItx\fB,\fIc\fB,\fId\fB,\fIty\fB \(rq:
+specify the transform explicitly in row-major order (opposite of PostScript)
+(1 or more 2D transforms)
+.RE
+.TP
+.B \-k\fR <\fIkernel\fR>
+reconstruction kernel; default:
+.RB \(lq cubic:0,0.5 \(rq
+.TP
+.B \-min\fR <\fIxMin\fR\ \fIyMin\fR>
+lower bounding corner of output image.
+Default (by not using this option) is the lower corner of input image.
+(2 \fIdouble\fRs)
+.TP
+.B \-max\fR <\fIxMax\fR\ \fIyMax\fR>
+upper bounding corner of output image.
+Default (by not using this option) is the upper corner of input image.
+(2 \fIdouble\fRs)
+.TP
+.B \-b\fR <\fIboundary\fR>
+what to do when sampling outside original image.
+.RS +7n
+.IP \(bu 2
+.RB \(lq bleed \(rq:
+copy values at image border outward
+.IP \(bu 2
+.RB \(lq wrap \(rq:
+do wrap-around on image locations
+.IP \(bu 2
+.RB \(lq pad \(rq:
+use a given background value (via
+.RB \(lq \-bg \(rq)
+.P
+default:
+.RB \(lq bleed \(rq
+.RE
+.TP
+.B \-bg\fR <\fIbg0\fR\ \fIbg1\fR\ ...>
+background color to use with boundary behavior
+.RB \(lq pad \(rq.
+Defaults to all zeroes.
+(1 or more \fIfloat\fRs)
+.TP
+.B \-s\fR <\fIxSize\fR\ \fIySize\fR>
+For each axis, information about how many samples in output:
+.RS +7n
+.IP \(bu 2
+.RB \(lq x\fR<\fIfloat\fR> \(rq:
+number of output samples is some scaling of the number input of samples;
+multiplied by
+.RI < float >
+.IP \(bu 2
+.RB \(lq \fR<\fIint\fR> \(rq:
+specify exact number of samples
+(2 sampling specifications)
+.P
+default:
+.RB \(lq x1\ x1 \(rq
+.RE
+.TP
+.B \-a\fR <\fIavg\ #\fR>
+number of averages (if there there is only one rotation)
+.RI ( unsigned\ int );
+default:
+.RB \(lq 0 \(rq
+.TP
+.B \-o\fR <\fIfilename\fR>
+file to write output
+.I nrrd
+to
+.RI ( string );
+default:
+.RB \(lq \- \(rq
+.SH "SEE\ ALSO"
+.BR gprobe (1),
+.BR miter (1),
+.BR mrender (1),
+.BR nrrdSanity (1),
+.BR overrgb (1),
+.BR puller (1),
+.BR tend (1),
+.BR unu (1),
+.BR vprobe (1)
diff --git a/miter.1 b/miter.1
new file mode 100644
index 0000000..75d7ef4
--- /dev/null
+++ b/miter.1
@@ -0,0 +1,287 @@
+.TH MITER "1" "April 2021" "" "User Commands"
+.SH NAME
+miter \- a simple but effective little volume renderer
+.SH SYNOPSIS
+.B miter
+.RI [ @file \ ...]
+.RB [ \-i
+.RI < nsin >]
+.RB [ \-vi
+.RI < nvin >]
+.RB [ \-ti
+.RI < ntin >]
+.B \-txf
+.RI < nin \ ...>
+.B \-fr
+.RI < eye\ pos >
+.RB [ \-at
+.RI < at\ pos >]
+.RB [ \-up
+.RI < up\ dir >]
+.RB [ \-rh ]
+.RB [ \-or ]
+.B \-dn
+.RI < near >
+.B \-di
+.RI < image >
+.B \-df
+.RI < far >
+.RB [ \-ar ]
+.RB [ \-ur
+.RI < uMin\fR\ \fIuMax >]
+.RB [ \-vr
+.RI < vMin\fR\ \fIvMax >]
+.RB [ \-fv
+.RI < field\ of\ view >]
+.RB [ \-offfr ]
+.RB [ \-fr
+.RI < fake\ from >]
+.RB [ \-turn
+.RI < angle >]
+.RB [ \-am
+.RI < ambient >]
+.RB [ \-ld
+.RI < light\ pos >]
+.RB [ \-is
+.RI < image\ size >]
+.RB [ \-iss
+.RI < scale >]
+.RB [ \-ads
+.RI < ka\fR\ \fIkd\fR\ \fIks >]
+.RB [ \-sp
+.RI < spec\ pow >]
+.RB [ \-k00
+.RI < kernel >]
+.RB [ \-k11
+.RI < kernel >]
+.RB [ \-k22
+.RI < kernel >]
+.RB [ \-ss
+.RI < shading\ spec >]
+.RB [ \-ns
+.RI < normal\ spec >]
+.RB [ \-side
+.RI < normal\ side >]
+.RB [ \-rn ]
+.RB [ \-gmc
+.RI < min\ gradmag >]
+.RB [ \-step
+.RI < size >]
+.RB [ \-ref
+.RI < size >]
+.RB [ \-vp
+.RI < verbose pixel >]
+.RB [ \-n1
+.RI < near1 >]
+.RB [ \-nt
+.RI < #\ threads >]
+.RB [ \-o
+.RI < filename >]
+.SH OPTIONS
+.TP
+.BR @file \ ...
+response file(s) containing command-line arguments
+.TP
+.B \-i\fR <\fInsin\fR>
+input scalar volume to render
+.RI ( nrrd );
+default: \(lq\(rq
+.TP
+.B \-vi\fR <\fInvin\fR>
+input vector volume to render
+.RI ( nrrd );
+default: \(lq\(rq
+.TP
+.B \-ti\fR <\fIntin\fR>
+input tensor volume to render
+.RI ( nrrd );
+default: \(lq\(rq
+.TP
+.B \-txf\fR <\fInin\fR\ ...>
+one or more transfer functions (1 or more
+.IR nrrd s)
+.TP
+.B \-fr\fR <\fIeye\ pos\fR>
+camera eye point (3 \fIdouble\fRs)
+.TP
+.B \-at\fR <\fIat\ pos\fR>
+camera look-at point (3 \fIdouble\fRs); default:
+.RB \(lq 0\ 0\ 0 \(rq
+.TP
+.B \-up\fR <\fIup\ dir\fR>
+camera pseudo-up vector (3 \fIdouble\fRs); default:
+.RB \(lq 0\ 0\ 1 \(rq
+.TP
+.B \-rh
+use a right-handed UVN frame (V points down)
+.TP
+.B \-or
+orthogonal (not perspective) projection
+.TP
+.B \-dn\fR <\fInear\fR>
+distance to near clipping plane
+.RI ( double )
+.TP
+.B \-di\fR <\fIimage\fR>
+distance to image plane
+.RI ( double )
+.TP
+.B \-df\fR <\fIfar\fR>
+distance to far clipping plane
+.RI ( double )
+.TP
+.B \-ur\fR <\fIuMin\fR\ \fIuMax\fR>
+range in U direction of image plane (2
+.IR double s)
+.TP
+.B \-vr\fR <\fIvMin\fR\ \fIvMax\fR>
+range in V direction of image plane (2
+.IR double s)
+.TP
+.B \-fv\fR <\fIfield\ of\ view\fR>
+angle (in degrees) vertically subtended by view window
+.RI ( double );
+default:
+.RB \(lq 20 \(rq
+.TP
+.B \-offfr
+the given eye point
+.RB (\(lq \-fr \(rq)
+is to be interpreted as an offset from the at point.
+.TP
+.B \-ffr\fR <\fIfake\ from\fR>
+eye point to use for view-dependent transfer functions.
+By default (not using this option), the point used is the normally specified
+camera eye point. (3 \fIdouble\fRs)
+.TP
+.B \-turn\fR <\fIangle\fR>
+angle (degrees) by which to rotate the from point around true up, for making
+stereo pairs.
+Positive means move towards positive U (the right)
+.RI ( double );
+default:
+.RB \(lq 0.0 \(rq
+.TP
+.B \-am\fR <\fIambient\fR>
+ambient light color (3 \fIfloat\fRs); default:
+.RB \(lq 1\ 1\ 1 \(rq
+.TP
+.B \-ld\fR <\fIlight\ pos\fR>
+view space light position (extended to infinity) (3 \fIfloat\fRs); default:
+.RB \(lq 0\ 0\ \-1 \(rq
+.TP
+.B \-is\fR <\fIimage\ size\fR>
+image dimensions (2 \fIint\fRs); default:
+.RB \(lq 256\ 256 \(rq
+.TP
+.B \-iss\fR <\fIscale\fR>
+scaling of image size (from
+.RB \(lq \-is \(rq)
+.RI ( float );
+default:
+.RB \(lq 1.0 \(rq
+.TP
+.B \-ads\fR <\fIka\fR\ \fIkd\fR\ \fIks\fR>
+phong components (3 \fIfloat\fRs); default:
+.RB \(lq 0.1\ 0.6\ 0.3 \(rq
+.TP
+.B \-sp\fR <\fIka\fR\ \fIkd\fR\ \fIks\fR>
+phong specular power
+.RI ( double ); default:
+.RB \(lq 30 \(rq
+.TP
+.B \-k00\fR <\fIkernel\fR>
+value reconstruction kernel (\fIkernel specification\fR); default:
+.RB \(lq tent \(rq
+.TP
+.B \-k11\fR <\fIkernel\fR>
+first derivative kernel (\fIkernel specification\fR); default:
+.RB \(lq cubicd:1,0 \(rq
+.TP
+.B \-k22\fR <\fIkernel\fR>
+second derivative kernel (\fIkernel specification\fR); default:
+.RB \(lq cubicdd:1,0 \(rq
+.TP
+.B \-ss\fR <\fIshading\ spec\fR>
+how to do shading
+.RI ( string );
+default:
+.RB \(lq phong:gage(scalar:n) \(rq
+.TP
+.B \-ns\fR <\fInormal\ spec\fR>
+\(lqnormal\(rq to use for those miteVal\(cqs that need one
+.RI ( string );
+default: \(lq\(rq
+.TP
+.B \-side\fR <\fInormal\ side\fR>
+how to interpret gradients as normals:
+.RS +7n
+.IP \(bu 2
+.RB \(lq 1 \(rq:
+normal points to lower values (higher == more \(lqinside\(rq)
+.IP \(bu 2
+.RB \(lq 0 \(rq:
+\(lqtwo-sided\(rq: dot-products are \fBabs()\fR\(cqed
+.IP \(bu 2
+.RB \(lq \-1 \(rq:
+normal points to higher values (lower == more \(lqinside\(rq)
+.P
+default:
+.RB \(lq 1 \(rq
+.RE
+.TP
+.B \-rn
+renormalize kernel weights at each new sample location.
+\(lqAccurate\(rq kernels don\(cqt need this; doing it always makes things go
+slower
+.B \-gmc\fR\ <\fImin\ gradmag\fR>
+For curvature-based transfer functions, set curvature to zero when gradient
+magnitude is below this
+.RI ( double );
+default:
+.RB \(lq 0.0 \(rq
+.TP
+.B \-step\fR\ <\fIsize\fR>
+step size along ray in world space
+.RI ( double );
+default:
+.RB \(lq 0.01 \(rq
+.TP
+.B \-ref\fR\ <\fIsize\fR>
+\(lqreference\(rq step size (world space) for doing opacity correction in
+compositing
+.RI ( double );
+default:
+.RB \(lq 0.01 \(rq
+.TP
+.B \-vp\fR\ <\fIverbose\ pixel\fR>
+pixel for which to turn on verbose messages (2 \fIint\fRs); default:
+.RB \(lq \-1\ \-1 \(rq
+.TP
+.B \-n1\fR\ <\fInear1\fR>
+opacity close enough to 1.0 to terminate ray
+.RI ( double );
+default:
+.RB \(lq 0.99 \(rq
+.TP
+.B \-nt\fR\ <\fI#\ threads\fR>
+number of threads hoover should use
+.RI ( int );
+default:
+.RB \(lq 1 \(rq
+.TP
+.B \-o\fR <\fIfilename\fR>
+file to write output nrrd to
+.RI ( string );
+default:
+.RB \(lq \- \(rq
+.SH "SEE\ ALSO"
+.BR gprobe (1),
+.BR ilk (1),
+.BR mrender (1),
+.BR nrrdSanity (1),
+.BR overrgb (1),
+.BR puller (1),
+.BR tend (1),
+.BR unu (1),
+.BR vprobe (1)
diff --git a/mrender.1 b/mrender.1
new file mode 100644
index 0000000..f619ac2
--- /dev/null
+++ b/mrender.1
@@ -0,0 +1,294 @@
+.TH MRENDER "1" "April 2021" "" "User Commands"
+.SH NAME
+mrender \- a demonstration of hoover, gage, and nrrd measures
+.SH SYNOPSIS
+.B mrender
+.RI [ @file \ ...]
+.RB [ \-i
+.RI < nin >]
+.B \-k
+.RI < kind >
+.B \-fr
+.RI < eye\ pos >
+.RB [ \-at
+.RI < at\ pos >]
+.RB [ \-up
+.RI < up\ dir >]
+.RB [ \-rh ]
+.RB [ \-or ]
+.B \-dn
+.RI < near >
+.B \-di
+.RI < image >
+.B \-df
+.RI < far >
+.RB [ \-ar ]
+.RB [ \-ur
+.RI < uMin\fR\ \fIuMax >]
+.RB [ \-vr
+.RI < vMin\fR\ \fIvMax >]
+.RB [ \-fv
+.RI < field\ of\ view >]
+.RB [ \-offfr ]
+.RB [ \-turn
+.RI < angle >]
+.RB [ \-is
+.RI < image\ size >]
+.RB [ \-iss
+.RI < scale >]
+.RB [ \-k00
+.RI < kernel >]
+.RB [ \-k11
+.RI < kernel >]
+.RB [ \-k22
+.RI < kernel >]
+.RB [ \-rn ]
+.B \-q
+.RI < query >
+.B \-m
+.RI < measure >
+.RB [ \-gmc
+.RI < min\ gradmag >]
+.RB [ \-fn
+.RI < from\ nan >]
+.RB [ \-step
+.RB [ \-nt
+.RI < #\ threads >]
+.RB [ \-vp
+.RI < img\ coords >]
+.RB [ \-o
+.RI < filename >]
+.SH DESCRIPTION
+.P
+Uses hoover to cast rays through a volume (scalar, vector, or tensor), gage to
+measure one of various quantities along the rays, and a specified
+.I nrrd
+measure to reduce all the values along a ray down to one scalar, which is saved
+in the output
+.RI ( double )
+image.
+.SH OPTIONS
+.TP
+.BR @file \ ...
+response file(s) containing command-line arguments
+.TP
+.B \-i\fR <\fInin\fR>
+input
+.I nrrd
+to render
+.RI ( nrrd );
+default: \(lq\(rq
+.TP
+.B \-k\fR <\fIkind\fR>
+\(lqkind\(rq of volume
+.RB (\(lq scalar \(rq,
+.RB \(lq vector \(rq,
+or
+.RB \(lq tensor \(rq)
+.RI ( gageKind )
+.TP
+.B \-fr\fR <\fIeye\ pos\fR>
+camera eye point (3 \fIdouble\fRs)
+.TP
+.B \-at\fR <\fIat\ pos\fR>
+camera look-at point (3 \fIdouble\fRs); default:
+.RB \(lq 0\ 0\ 0 \(rq
+.TP
+.B \-up\fR <\fIup\ dir\fR>
+camera pseudo-up vector (3 \fIdouble\fRs); default:
+.RB \(lq 0\ 0\ 1 \(rq
+.TP
+.B \-rh
+use a right-handed UVN frame (V points down)
+.TP
+.B \-or
+orthogonal (not perspective) projection
+.TP
+.B \-dn\fR <\fInear\fR>
+distance to near clipping plane
+.RI ( double )
+.TP
+.B \-di\fR <\fIimage\fR>
+distance to image plane
+.RI ( double )
+.TP
+.B \-df\fR <\fIfar\fR>
+distance to far clipping plane
+.RI ( double )
+.TP
+.B \-ar
+near, image, and far plane distances are relative to the
+.B at
+point, instead of the eye point
+.TP
+.B \-ur\fR <\fIuMin\fR\ \fIuMax\fR>
+range in U direction of image plane (2
+.IR double s);
+default:
+.RB \(lq nan\ nan \(rq
+.TP
+.B \-vr\fR <\fIvMin\fR\ \fIvMax\fR>
+range in V direction of image plane (2
+.IR double s);
+default:
+.RB \(lq nan\ nan \(rq
+.TP
+.B \-fv\fR <\fIfield\ of\ view\fR>
+angle (in degrees) vertically subtended by view window
+.RI ( double );
+default:
+.RB \(lq 20 \(rq
+.TP
+.B \-offfr
+the given eye point
+.RB (\(lq \-fr \(rq)
+is to be interpreted as an offset from the at point.
+.TP
+.B \-turn\fR <\fIangle\fR>
+angle (degrees) by which to rotate the from point around true up, for making
+stereo pairs.
+Positive means move towards positive U (the right)
+.RI ( double );
+default:
+.RB \(lq 0.0 \(rq
+.TP
+.B \-is\fR <\fIimage\ size\fR>
+image dimensions (2 \fIint\fRs); default:
+.RB \(lq 256\ 256 \(rq
+.TP
+.B \-iss\fR <\fIscale\fR>
+scaling of image size (from
+.RB \(lq \-is \(rq)
+.RI ( float );
+default:
+.RB \(lq 1.0 \(rq
+.TP
+.B \-k00\fR <\fIkernel\fR>
+value reconstruction kernel (\fIkernel specification\fR); default:
+.RB \(lq tent \(rq
+.TP
+.B \-k11\fR <\fIkernel\fR>
+first derivative kernel (\fIkernel specification\fR); default:
+.RB \(lq cubicd:1,0 \(rq
+.TP
+.B \-k22\fR <\fIkernel\fR>
+second derivative kernel (\fIkernel specification\fR); default:
+.RB \(lq cubicdd:1,0 \(rq
+.TP
+.B \-rn
+renormalize kernel weights at each new sample location.
+\(lqAccurate\(rq kernels don\(cqt need this; doing it always makes things go
+slower
+.TP
+.B \-q\fR <\fIquery\fR>
+the quantity
+.RB ( scalar ,
+.BR vector ,
+or
+.BR matrix)
+to learn by probing
+.RI ( string )
+.TP
+.B \-m\fR <\fImeasure\fR>
+how to collapse list of ray samples into one scalar.
+Possibilities include:
+.RS +7n
+.IP \(bu 2
+.RB \(lq min \(rq,
+.RB \(lq max \(rq,
+.RB \(lq mean \(rq,
+.RB \(lq median \(rq,
+.RB \(lq mode \(rq,
+.RB \(lq variance \(rq,
+.RB \(lq skew \(rq:
+(self-explanatory)
+.IP \(bu 2
+.RB \(lq intc \(rq,
+.RB \(lq slope \(rq,
+.RB \(lq error \(rq:
+intercept, slope, and error from line fitting
+.IP \(bu 2
+.RB \(lq stdv \(rq:
+standard deviation
+.IP \(bu 2
+.RB \(lq cov \(rq:
+coefficient of variation
+.IP \(bu 2
+.RB \(lq product \(rq,
+.RB \(lq sum \(rq:
+product or sum of all values
+.IP \(bu 2
+.RB \(lq L1 \(rq,
+.RB \(lq L2 \(rq,
+.RB \(lq NL2 \(rq,
+.RB \(lq RMS \(rq,
+.RB \(lq Linf \(rq:
+different norms
+.IP \(bu 2
+.RB \(lq histo\-min \(rq,
+.RB \(lq histo\-max \(rq,
+.RB \(lq histo\-mean \(rq,
+.RB \(lq histo\-median \(rq,
+.RB \(lq histo\-mode \(rq,
+.RB \(lq histo\-product \(rq,
+.RB \(lq histo\-l2 \(rq,
+.RB \(lq histo\-sum \(rq,
+.RB \(lq histo\-variance \(rq,
+.RB \(lq histo\-sd \(rq:
+same measures, but for situations where we\(cqre given not the original values,
+but a histogram of them.
+.RI ( measure )
+.TP
+.B \-gmc\fR\ <\fImin\ gradmag\fR>
+For curvature-based queries, set answer to zero when gradient magnitude is
+below this
+.RI ( double );
+default:
+.RB \(lq 0.0 \(rq
+.TP
+.B \-fn\fR\ <\fIfrom\ nan\fR>
+When histo-based measures generate
+.B NaN
+answers, the value that should be substituted for
+.BR NaN .
+.RI ( double );
+default:
+.RB \(lq nan \(rq
+.TP
+.B \-step\fR\ <\fIsize\fR>
+step size along ray in world space
+.RI ( double );
+default:
+.RB \(lq 0.01 \(rq
+.TP
+.B \-nt\fR\ <\fI#\ threads\fR>
+number of threads hoover should use
+.RI ( int );
+default:
+.RB \(lq 1 \(rq
+.TP
+.B \-vp\fR\ <\fIimg\ coords\fR>
+pixel coordinates for which to turn on verbose debugging messages, or
+.RB \(lq \-1\ \-1 \(rq
+to disable this.
+(2 \fIint\fRs);
+default:
+.RB \(lq \-1\ \-1 \(rq
+.TP
+.B \-o\fR <\fIfilename\fR>
+file to write output
+.I nrrd
+to
+.RI ( string );
+default:
+.RB \(lq \- \(rq
+.SH "SEE\ ALSO"
+.BR gprobe (1),
+.BR ilk (1),
+.BR miter (1),
+.BR nrrdSanity (1),
+.BR overrgb (1),
+.BR puller (1),
+.BR tend (1),
+.BR unu (1),
+.BR vprobe (1)
diff --git a/nrrdSanity.1 b/nrrdSanity.1
new file mode 100644
index 0000000..70c577a
--- /dev/null
+++ b/nrrdSanity.1
@@ -0,0 +1,32 @@
+.TH NRRDSANITY "1" "April 2021" "" "User Commands"
+.SH NAME
+nrrdSanity \- nrrd sanity check
+.SH SYNOPSIS
+.B nrrdSanity
+.SH DESCRIPTION
+.P
+.BR nrrdSanity (1)
+calls the
+.B nrrdSanity()
+check to verify the correctness of all the information (set at compile-time)
+about the architecture, such as endianness, 32/64-bit, and the size of various
+types, as well as running sanity checks on the global default
+.RB ( nrrdDefault* )
+and state
+.RB ( nrrdState* )
+variables.
+.P
+As a convenience,
+.BR nrrdSanity (1)
+also lists the availability of the different formats and data encodings (for
+Nrrd files) supported by this build.
+.SH "SEE\ ALSO"
+.BR gprobe (1),
+.BR ilk (1),
+.BR miter (1),
+.BR mrender (1),
+.BR overrgb (1),
+.BR puller (1),
+.BR tend (1),
+.BR unu (1),
+.BR vprobe (1)
diff --git a/overrgb.1 b/overrgb.1
new file mode 100644
index 0000000..538bf57
--- /dev/null
+++ b/overrgb.1
@@ -0,0 +1,92 @@
+.TH OVERRGB "1" "April 2021" "" "User Commands"
+.SH NAME
+overrgb \- composites an RGBA nrrd over a background color (or image)
+.SH SYNOPSIS
+.B overrgb
+.B \-i
+.RI < nin >
+.RB [ \-c
+.RI < contrast >]
+.RB [ \-cfp
+.RI < fixed\ point >]
+.RB [ \-g
+.RI < gamma >]
+.RB [ \-b
+.RI < background >]
+.RB [ \-bi
+.RI < nbg >]
+.B \-o
+.RI < filename >
+.SH DESCRIPTION
+.P
+Composites an RGBA nrrd over a background color (or image), after doing gamma
+correction, then quantizes to an 8-bit image.
+Actually, the input nrrd can have more than 4 values per pixel, but only the
+first four are used.
+If the RGBA nrrd is floating point, the values are taken at face value; if it
+is fixed point, the values interpreted as having been quantized (so that 8-bit
+RGBA images will act as you expect).
+When compositing with a background image, the given background image does not
+have to be the same size as the input image; it will be resampled (with linear
+interpolation) to fit.
+.SH OPTIONS
+.TP
+.BR @file \ ...
+response file(s) containing command-line arguments
+.TP
+.B \-i\fR <\fInin\fR>
+input nrrd to composite
+.RI ( nrrd )
+.TP
+.B \-c\fR <\fIcontrast\fR>
+contrast to apply to RGB values, before gamma.
+.RB \(lq 0.0 \(rq
+means no change,
+.RB \(lq 1.0 \(rq
+means thresholding,
+.RB \(lq \-1.0 \(rq
+means a complete washout.
+.RI ( double );
+default:
+.RB \(lq 0.0 \(rq
+.TP
+.B \-cfp\fR <\fIfixed\ point\fR>
+component level that doesn\(cqt change with contrast
+.RI ( double );
+default:
+.RB \(lq 0.5 \(rq
+.TP
+.B \-g\fR <\fIgamma\fR>
+gamma to apply to image data, after contrast
+.RI ( double );
+default:
+.RB \(lq 1.0 \(rq
+.TP
+.B \-b\fR <\fIbackground\fR>
+background color to composite against; white is
+.BR 1\ 1\ 1 ,
+not
+.BR 255\ 255\ 255 .
+(3 \fIdouble\fRs); default:
+.RB \(lq 0\ 0\ 0 \(rq
+.TP
+.B \-bi\fR <\fInbg\fR>
+8-bit RGB background image to composite against
+.RI ( nrrd );
+default: \(lq\(rq
+.TP
+.B \-o\fR <\fIfilename\fR>
+file to write output PPM image to
+.RI ( string );
+default:
+.RB \(lq \- \(rq
+.SH "SEE\ ALSO"
+.BR gprobe (1),
+.BR ilk (1),
+.BR mrender (1),
+.BR miter (1),
+.BR nrrdSanity (1),
+.BR puller (1),
+.BR tend (1),
+.BR unu (1),
+.BR vprobe (1)
diff --git a/puller.1 b/puller.1
new file mode 100644
index 0000000..fb03592
--- /dev/null
+++ b/puller.1
@@ -0,0 +1,591 @@
+.TH PULLER "1" "April 2021" "" "User Commands"
+.SH NAME
+puller \- command-line interface to the \(lqpull\(rq library
+.SH SYNOPSIS
+.B puller
+.RB [ @file \ ...]
+.RB [ \-int
+.RI < int >]
+.RB [ \-enr
+.RI < spec >]
+.RB [ \-ens
+.RI < spec >]
+.RB [ \-enw
+.RI < spec >]
+.RB [ \-efs
+.RI < bool >]
+.RB [ \-nave
+.RI < bool >]
+.RB [ \-cbst
+.RI < bool >]
+.RB [ \-noadd ]
+.RB [ \-usa
+.RI < bool >]
+.RB [ \-pcet
+.RI < bool >]
+.RB [ \-nobin ]
+.RB [ \-lti
+.RI < bool >]
+.RB [ \-por
+.RI < bool >]
+.RB [ \-npcwza
+.RI < bool >]
+.RB [ \-ubfgl
+.RI < bool >]
+.RB [ \-ratb
+.RI < bool >]
+.RB [ \-svec
+.RI < vec >]
+.RB [ \-gssr
+.RI < rad >]
+.RB [ \-v
+.RI < verbosity >]
+.B \-vol
+.RI < vol0\ vol1 \ ...>
+.B \-info
+.RI < info0\ info1 \ ...>
+.RB [ \-k00
+.RI < kern00 >]
+.RB [ \-k11
+.RI < kern11 >]
+.RB [ \-k22
+.RI < kern22 >]
+.RB [ \-sscp
+.RI < path >]
+.RB [ \-kssb
+.RI < kernel >]
+.RB [ \-kssr
+.RI < kernel >]
+.RB [ \-nss
+.RI < #\ scl\ smpls >]
+.RB [ \-np
+.RI < #\ points >]
+.RB [ \-halton ]
+.RB [ \-ppv
+.RI < #\ pnts/vox >]
+.RB [ \-ppvzr
+.RI < z\ range >]
+.RB [ \-jit
+.RI < jitter >]
+.RB [ \-pi
+.RI < npos >]
+.RB [ \-step
+.RI < step >]
+.RB [ \-csm
+.RI < step >]
+.RB [ \-snap
+.RI < #\ iters >]
+.RB [ \-maxi
+.RI < #\ iters >]
+.RB [ \-stim
+.RI < #\ iters >]
+.RB [ \-maxci
+.RI < #\ iters >]
+.RB [ \-irad
+.RI < scale >]
+.RB [ \-srad
+.RI < scale >]
+.RB [ \-bws
+.RI < bin\ width >]
+.RB [ \-alpha
+.RI < alpha >]
+.RB [ \-beta
+.RI < beta >]
+.RB [ \-gamma
+.RI < gamma >]
+.RB [ \-theta
+.RI < theta >]
+.RB [ \-wall
+.RI < k >]
+.RB [ \-eip
+.RI < k >]
+.RB [ \-ess
+.RI < scl >]
+.RB [ \-oss
+.RI < scl >]
+.RB [ \-edmin
+.RI < frac >]
+.RB [ \-edpcmin
+.RI < frac >]
+.RB [ \-fnnm
+.RI < frac >]
+.RB [ \-pcp
+.RI < period >]
+.RB [ \-iad
+.RI < #\ iters >]
+.RB [ \-icb
+.RI < #\ iters >]
+.RB [ \-ac3c
+.RI < ac3c >]
+.RB [ \-sit
+.RI < sit >]
+.RB [ \-rng
+.RI < seed >]
+.RB [ \-pbm
+.RI < mod >]
+.RB [ \-eiphl
+.RI < hl >]
+.RB [ \-nt
+.RI < #\ threads >]
+.RB [ \-nprob
+.RI < prob >]
+.RB [ \-pprob
+.RI < prob >]
+.RB [ \-addlog
+.RI < fname >]
+.RB [ \-o
+.RI < nout >]
+.RB [ \-eob
+.RI < base >]
+.SH OPTIONS
+.TP
+.BR @file \ ...
+response file(s) containing command-line arguments
+.TP
+.B \-int\fR <\fIint\fR>
+inter-particle energy type
+.RI ( interaction\ type );
+default:
+.RB \(lq justr \(rq
+.TP
+.B \-enr\fR <\fIspec\fR>
+inter-particle energy, radial component
+.RI ( energy\ specification );
+default:
+.RB \(lq cotan \(rq
+.TP
+.B \-ens\fR <\fIspec\fR>
+inter-particle energy, scale component
+.RI ( energy\ specification );
+default:
+.RB \(lq zero \(rq
+.TP
+.B \-enw\fR <\fIspec\fR>
+windowing to create locality with additive scale-space interaction
+.RB (\(lq \-int\ add \(rq)
+.RI ( energy\ specification );
+default:
+.RB \(lq butter:16,0.8 \(rq
+.TP
+.B \-efs\fR <\fIbool\fR>
+whether or not strength contributes to particle-image energy
+.RI ( bool );
+default:
+.RB \(lq false \(rq
+.TP
+.B \-nave\fR <\fIbool\fR>
+whether or not to nix points at edge of volume, where gage had to invent values
+for kernel support
+.RI ( bool );
+default:
+.RB \(lq false \(rq
+.TP
+.B \-cbst\fR <\fIbool\fR>
+during initialization, try constraint satisfaction before testing
+.B seedThresh
+.RI ( bool );
+default:
+.RB \(lq false \(rq
+.TP
+.B \-noadd
+turn off adding during population control
+.TP
+.B \-usa\fR <\fIbool\fR>
+allow volumes to have different shapes
+.RB ( false
+is safe as different volume sizes are often accidental)
+.RI ( bool );
+default:
+.RB \(lq false \(rq
+.TP
+.B \-pcet\fR <\fIbool\fR>
+use neighbor-counting \(lqenough\(rq heuristic to bail out of population
+control
+.RI ( bool );
+default:
+.RB \(lq false \(rq
+.TP
+.B \-nobin
+turn off spatial binning (which prevents multi-threading from being useful),
+for debugging or speed-up measurement
+.TP
+.B \-lti\fR <\fIbool\fR>
+impose
+.B liveThresh
+on initialization
+.RI ( bool );
+default:
+.RB \(lq true \(rq
+.TP
+.B \-por\fR <\fIbool\fR>
+permute points during rebinning
+.RI ( bool );
+default:
+.RB \(lq true \(rq
+.TP
+.B \-npcwza\fR <\fIbool\fR>
+no population control with zero alpha
+.RI ( bool );
+default:
+.RB \(lq false \(rq
+.TP
+.B \-ubfgl\fR <\fIbool\fR>
+use beta for gamma learning
+.RI ( bool );
+default:
+.RB \(lq false \(rq
+.TP
+.B \-ratb\fR <\fIbool\fR>
+be choosy when adding points to bins to avoid overlap
+.RI ( bool );
+default:
+.RB \(lq true \(rq
+.TP
+.B \-svec\fR <\fIvec\fR>
+if non-zero (length), vector to use for displaying scale in 3-space (3
+\fIdouble\fRs); default:
+.RB \(lq 0\ 0\ 0 \(rq
+.TP
+.B \-gssr\fR <\fIrad\fR>
+if non-zero (length), scaling of scale to cylindrical tensors
+.RI ( double );
+default:
+.RB \(lq 0.0 \(rq
+.TP
+.B \-v\fR <\fIverbosity\fR>
+verbosity level
+.RI ( int );
+default:
+.RB \(lq 1 \(rq
+.TP
+.B \-vol\fR <\fIvol0\fR\ \fIvol1\fR\ ...>
+input volumes, in format
+<\fIfilename\fR>\fB:\fR<\fIkind\fR>\fB:\fR<\fIvolname\fR>
+(1 or more \fImeetPullVol\fRs)
+.TP
+.B \-info\fR <\fIinfo0\fR\ \fIinfo1\fR\ ...>
+info definitions, in format
+.RI < info >[\fB\-c\fR]\fB:\fR< volname >\fB:\fR< item >[\fB:\fR< zero >\fB:\fR< scale >]
+(1 or more \fImeetPullInfo\fRs)
+.TP
+.B \-k00\fR <\fIkern00\fR>
+kernel for
+.B gageKernel00
+.RI ( kernel\ specification );
+default:
+.RB \(lq cubic:1,0 \(rq
+.TP
+.B \-k11\fR <\fIkern11\fR>
+kernel for
+.B gageKernel11
+.RI ( kernel\ specification );
+default:
+.RB \(lq cubicd:1,0 \(rq
+.TP
+.B \-k22\fR <\fIkern22\fR>
+kernel for
+.B gageKernel22
+.RI ( kernel\ specification );
+default:
+.RB \(lq cubicdd:1,0 \(rq
+.TP
+.B \-sscp\fR <\fIpath\fR>
+path (without trailing
+.BR / )
+for where to read/write pre-blurred volumes for scale-space
+.RI ( string );
+default:
+.RB \(lq ./ \(rq
+.TP
+.B \-kssb\fR <\fIkernel\fR>
+blurring kernel, to sample scale space
+.RI ( kernel\ specification );
+default:
+.RB \(lq ds:1,5 \(rq
+.TP
+.B \-kssr\fR <\fIkernel\fR>
+kernel for reconstructing from scale space samples
+.RI ( kernel\ specification );
+default:
+.RB \(lq hermite \(rq
+.TP
+.B \-nssr\fR <\fI#\ scl\ smpls\fR>
+if using
+.RB \(lq \-ppv \(rq;
+number of samples along scale axis for each spatial position
+.RI ( unsigned\ int );
+default:
+.RB \(lq 1 \(rq
+.TP
+.B \-np\fR <\fI#\ points\fR>
+number of points to start in system
+.RI ( unsigned\ int );
+default:
+.RB \(lq 1000 \(rq
+.TP
+.B \-halton
+use Halton sequence initialization instead of uniform random
+.TP
+.B \-ppv\fR <\fI#\ p[nts/vox\fR>
+number of points per voxel to start in simulation (need to have a seed
+threshold volume, overrides
+.RB \(lq \-np \(rq)
+.RI ( int );
+default:
+.RB \(lq 0 \(rq
+.TP
+.B \-ppvzr\fR <\fIz\ range\fR>
+range of Z slices (1st num < 2nd num) to do
+.B ppv
+in, or,
+.RB \(lq 1\ 0 \(rq)
+for whole volume (2 \fIunsigned\ int\fRs); default:
+.RB \(lq 1\ 0 \(rq
+.TP
+.B \-jit\fR <\fIjitter\fR>
+amount of jittering to do with
+.B ppv
+.RI ( double );
+default:
+.RB \(lq 0 \(rq
+.TP
+.B \-pi\fR <\fInpos\fR>
+4-by-N array of positions to start at (overrides
+.RB \(lq \-np \(rq
+.RI ( nrrd );
+default: \(lq\(rq
+.TP
+.B \-step\fR <\fIstep\fR>
+initial step size for gradient descent
+.RI ( double );
+default:
+.RB \(lq 1 \(rq
+.TP
+.B \-csm\fR <\fIstep\fR>
+convergence criterion for constraint satisfaction
+.RI ( double );
+default:
+.RB \(lq 0.0001 \(rq
+.TP
+.B \-snap\fR <\fI#\ iters\fR>
+if non-zero, # iters between saved snapshots
+.RI ( unsigned\ int );
+default:
+.RB \(lq 0 \(rq
+.TP
+.B \-maxi\fR <\fI#\ iters\fR>
+if non-zero, max # iterations to run whole system
+.RI ( unsigned\ int );
+default:
+.RB \(lq 0 \(rq
+.TP
+.B \-stim\fR <\fI#\ iters\fR>
+if non-zero, max # iterations to allow a particle to be stuck before nixing
+.RI ( unsigned\ int );
+default:
+.RB \(lq 5 \(rq
+.TP
+.B \-maxci\fR <\fI#\ iters\fR>
+if non-zero, max # iterations for contraint enforcement
+.RI ( unsigned\ int );
+default:
+.RB \(lq 15 \(rq
+.TP
+.B \-irad\fR <\fIscale\fR>
+particle radius in spatial domain
+.RI ( double );
+default:
+.RB \(lq 1 \(rq
+.TP
+.B \-srad\fR <\fIscale\fR>
+particle radius in scale domain
+.RI ( double );
+default:
+.RB \(lq 1 \(rq
+.TP
+.B \-bws\fR <\fIbin\ width\fR>
+spatial bin width as multiple of spatial radius
+.RI ( double );
+default:
+.RB \(lq 1.001 \(rq
+.TP
+.B \-alpha\fR <\fIalpha\fR>
+blend between particle-image (\fIalpha\fR=\fB0\fR) and inter-particle
+(\fIalpha\fR=\fB1\fR) energies
+.RI ( double );
+default:
+.RB \(lq 0.5 \(rq
+.TP
+.B \-beta\fR <\fIbeta\fR>
+when using
+.B Phi2
+energy, blend between pure space repulsion (\fIbeta\fR=\fB0\fR) and scale
+attraction (\fIbeta\fR=\fB1\fR)
+.RI ( double );
+default:
+.RB \(lq 1.0 \(rq
+.TP
+.B \-gamma\fR <\fIgamma\fR>
+scaling factor on energy from strength
+.RI ( double );
+default:
+.RB \(lq 1.0 \(rq
+.TP
+.B \-theta\fR <\fItheta\fR>
+slope of increasing
+.B livethresh
+with respect to scale
+.RI ( double );
+default:
+.RB \(lq 0.0 \(rq
+.TP
+.B \-wall\fR <\fIk\fR>
+spring constant on walls
+.RI ( double );
+default:
+.RB \(lq 0.0 \(rq
+.TP
+.B \-eip\fR <\fIk\fR>
+amount by which it\(cqs okay for
+.B per-particle
+energy to increase during gradient descent process
+.RI ( double );
+default:
+.RB \(lq 0.0 \(rq
+.TP
+.B \-ess\fR <\fIscl\fR>
+when energy goes up instead of down, scale step size by this
+.RI ( double );
+default:
+.RB \(lq 0.5 \(rq
+.TP
+.B \-oss\fR <\fIscl\fR>
+opportunistic scaling (hopefully up, >1) of step size on every iteration
+.RI ( double );
+default:
+.RB \(lq 1.0 \(rq
+.TP
+.B \-edmin\fR <\fIfrac\fR>
+convergence threshold: stop when fractional improvement (decrease) in energy
+dips below this
+.RI ( double );
+default:
+.RB \(lq 0.0001 \(rq
+.TP
+.B \-edpcmin\fR <\fIfrac\fR>
+population control is triggered when energy improvement goes below this
+threshold
+.RI ( double );
+default:
+.RB \(lq 0.01 \(rq
+.TP
+.B \-fnnm\fR <\fIfrac\fR>
+don\(cqt nix if this fraction (or more) of neighbors have been nixed
+.RI ( double );
+default:
+.RB \(lq 0.25 \(rq
+.TP
+.B \-pcp\fR <\fIperiod\fR>
+# iters to wait between attempts at population control
+.RI ( unsigned\ int );
+default:
+.RB \(lq 20 \(rq
+.TP
+.B \-iad\fR <\fI#\ iters\fR>
+# iters to run descent on tentative new points during PC
+.RI ( unsigned\ int );
+default:
+.RB \(lq 10 \(rq
+.TP
+.B \-icb\fR <\fI#\ iters\fR>
+periodicity of calling rendering callback
+.RI ( unsigned\ int );
+default:
+.RB \(lq 1 \(rq
+.TP
+.B \-ac3c\fR <\fIac3c\fR>
+allow codimensions 3 constraints
+.RI ( bool );
+default:
+.RB \(lq false \(rq
+.TP
+.B \-sit\fR <\fIsit\fR>
+scale is tau
+.RI ( bool );
+default:
+.RB \(lq false \(rq
+.TP
+.B \-rng\fR <\fIseed\fR>
+base seed value for RNGs
+.RI ( unsigned\ int );
+default:
+.RB \(lq 42 \(rq
+.TP
+.B \-pbm\fR <\fImod\fR>
+progress bin mod
+.RI ( unsigned\ int );
+default:
+.RB \(lq 50 \(rq
+.TP
+.B \-eiphl\fR <\fIhl\fR>
+half-life of
+.BR energyIncreasePermute (\(lq \-eip \(rq)
+.RI ( unsigned\ int );
+default:
+.RB \(lq 0 \(rq
+.TP
+.B \-nt\fR <\fI#\ threads\fR>
+number of threads hoover should use
+.RI ( int );
+default:
+.RB \(lq 1 \(rq
+.TP
+.B \-nprob\fR <\fIprob\fR>
+do full neighbor discovery with this probability
+.RI ( double );
+default:
+.RB \(lq 1.0 \(rq
+.TP
+.B \-pprob\fR <\fIprob\fR>
+probe local image values with this probability
+.RI ( double );
+default:
+.RB \(lq 1.0 \(rq
+.TP
+.B \-addlog\fR <\fIfname\fR>
+name of file in which to log all particle additions
+.RI ( string );
+default: \(lq\(rq
+.TP
+.B \-o\fR <\fInout\fR>
+filename for saving computed positions
+.RI ( string );
+default:
+.RB \(lq \- \(rq
+.TP
+.B \-eob\fR <\fIbase\fR>
+save extra info (besides position), and use this string as the base of the
+filenames.
+Not using this eans the extra info is not saved.
+.RI ( string );
+default: \(lq\(rq
+.SH NOTES
+.P
+Published research using this tool or the \(lqpull\(rq library should cite the
+paper:
+.in +4n
+\fBGordon L. Kindlmann, Ra{\(rs\(aqu}l San Jos{\(rs\(aqe} Est{\(rs\(aqe}par,
+Stephen M. Smith, Carl-Fredrik Westin.
+Sampling and Visualizing Creases with Scale-Space Particles.
+IEEE Trans. on Visualization and Computer Graphics, 15(6):1415-1424 (2009).\fR
+.in
+.SH "SEE\ ALSO"
+.BR gprobe (1),
+.BR ilk (1),
+.BR mrender (1),
+.BR miter (1),
+.BR nrrdSanity (1),
+.BR overrgb (1),
+.BR tend (1),
+.BR unu (1),
+.BR vprobe (1)
diff --git a/teem.spec b/teem.spec
index 2ebe7a6..b69288c 100644
--- a/teem.spec
+++ b/teem.spec
@@ -1,3 +1,7 @@
+# Writing man pages is a work in progress. We do not install them until we have
+# a complete set.
+%bcond_with man_pages
+
Name: teem
Version: 1.11.0
%global so_version 1
@@ -16,8 +20,124 @@ License: LGPLv2+
# python/ctypes/pullDemo.py
# python/ctypes/teem-gen.py
# python/ctypes/teem.py
-URL: https://teem.sourceforge.net
+URL: http://teem.sourceforge.net
Source0: https://downloads.sourceforge.net/project/teem/teem/%{version}/%{name}-%{...
+
+Source1: gprobe.1
+Source2: ilk.1
+Source3: miter.1
+Source4: mrender.1
+Source5: nrrdSanity.1
+Source6: overrgb.1
+Source7: puller.1
+Source8: tend.1
+Source9: unu.1
+Source10: vprobe.1
+
+Source800: tend-about.1
+Source801: tend-grads.1
+Source802: tend-epireg.1
+Source803: tend-bmat.1
+Source804: tend-estim.1
+Source805: tend-sim.1
+Source806: tend-mfit.1
+Source807: tend-mconv.1
+Source808: tend-msim.1
+Source809: tend-make.1
+Source810: tend-avg.1
+#Source811: tend-helix.1
+#Source812: tend-sten.1
+#Source813: tend-glyph.1
+#Source814: tend-ellipse.1
+#Source815: tend-anplot.1
+#Source816: tend-anvol.1
+#Source817: tend-anscale.1
+#Source818: tend-anhist.1
+#Source819: tend-triple.1
+#Source820: tend-tconv.1
+#Source821: tend-point.1
+#Source822: tend-slice.1
+#Source823: tend-norm.1
+#Source824: tend-fiber.1
+#Source825: tend-eval.1
+#Source826: tend-evalpow.1
+#Source827: tend-evalclamp.1
+#Source828: tend-evaladd.1
+#Source829: tend-evalmul.1
+#Source830: tend-log.1
+#Source831: tend-exp.1
+#Source832: tend-evec.1
+#Source833: tend-evecrgb.1
+#Source834: tend-evq.1
+#Source835: tend-unmf.1
+#Source836: tend-expand.1
+#Source837: tend-shrink.1
+#Source838: tend-bfit.1
+#Source839: tend-satin.1
+#
+#Source900: unu-about.1
+#Source901: unu-env.1
+#Source902: unu-i2w.1
+#Source903: unu-w2i.1
+#Source904: unu-make.1
+#Source905: unu-head.1
+#Source906: unu-data.1
+#Source907: unu-convert.1
+#Source908: unu-resample.1
+#Source909: unu-fft.1
+#Source910: unu-cmedian.1
+#Source911: unu-dist.1
+#Source912: unu-minmax.1
+#Source913: unu-cksum.1
+#Source914: unu-diff.1
+#Source915: unu-quantize.1
+#Source916: unu-unquantize.1
+#Source917: unu-project.1
+#Source918: unu-slice.1
+#Source919: unu-sselect.1
+#Source920: unu-dice.1
+#Source921: unu-splice.1
+#Source922: unu-join.1
+#Source923: unu-crop.1
+#Source924: unu-acrop.1
+#Source925: unu-inset.1
+#Source926: unu-pad.1
+#Source927: unu-reshape.1
+#Source928: unu-permute.1
+#Source929: unu-swap.1
+#Source930: unu-shuffle.1
+#Source931: unu-flip.1
+#Source932: unu-unorient.1
+#Source933: unu-axinfo.1
+#Source934: unu-axinsert.1
+#Source935: unu-axsplit.1
+#Source936: unu-axdelete.1
+#Source937: unu-axmerge.1
+#Source938: unu-tile.1
+#Source939: unu-untile.1
+#Source940: unu-histo.1
+#Source941: unu-dhisto.1
+#Source942: unu-jhisto.1
+#Source943: unu-histax.1
+#Source944: unu-heq.1
+#Source945: unu-gamma.1
+#Source946: unu-1op.1
+#Source947: unu-2op.1
+#Source948: unu-3op.1
+#Source949: unu-affine.1
+#Source950: unu-lut.1
+#Source951: unu-mlut.1
+#Source952: unu-subst.1
+#Source953: unu-rmap.1
+#Source954: unu-mrmap.1
+#Source955: unu-imap.1
+#Source956: unu-lut2.1
+#Source957: unu-ccfind.1
+#Source958: unu-ccadj.1
+#Source959: unu-ccmerge.1
+#Source960: unu-ccsettle.1
+#Source961: unu-save.1
+
# Patch CMakeLists.txt to fix library install paths to match what Fedora
# expects. It is not immediately obvious if this patch is suitable for
# sending upstream or not.
@@ -141,6 +261,34 @@ rm -rvf python
%install
%cmake3_install
+%if %{with man_pages}
+install -d %{buildroot}%{_mandir}/man1
+install -t %{buildroot}%{_mandir}/man1 -p -m 0644 \
+ %{SOURCE1} %{SOURCE2} %{SOURCE3} %{SOURCE4} %{SOURCE5} %{SOURCE6} \
+ %{SOURCE7} %{SOURCE8} %{SOURCE9} %{SOURCE10} \
+ %{SOURCE800} %{SOURCE801} %{SOURCE802} %{SOURCE803} %{SOURCE804} \
+ %{SOURCE805} %{SOURCE806} %{SOURCE807} %{SOURCE808} %{SOURCE809} \
+ %{SOURCE810} %{SOURCE811} %{SOURCE812} %{SOURCE813} %{SOURCE814} \
+ %{SOURCE815} %{SOURCE816} %{SOURCE817} %{SOURCE818} %{SOURCE819} \
+ %{SOURCE820} %{SOURCE821} %{SOURCE822} %{SOURCE823} %{SOURCE824} \
+ %{SOURCE825} %{SOURCE826} %{SOURCE827} %{SOURCE828} %{SOURCE829} \
+ %{SOURCE830} %{SOURCE831} %{SOURCE832} %{SOURCE833} %{SOURCE834} \
+ %{SOURCE835} %{SOURCE836} %{SOURCE837} %{SOURCE838} %{SOURCE839} \
+ %{SOURCE900} %{SOURCE901} %{SOURCE902} %{SOURCE903} %{SOURCE904} \
+ %{SOURCE905} %{SOURCE906} %{SOURCE907} %{SOURCE908} %{SOURCE909} \
+ %{SOURCE910} %{SOURCE911} %{SOURCE912} %{SOURCE913} %{SOURCE914} \
+ %{SOURCE915} %{SOURCE916} %{SOURCE917} %{SOURCE918} %{SOURCE919} \
+ %{SOURCE920} %{SOURCE921} %{SOURCE922} %{SOURCE923} %{SOURCE924} \
+ %{SOURCE925} %{SOURCE926} %{SOURCE927} %{SOURCE928} %{SOURCE929} \
+ %{SOURCE930} %{SOURCE931} %{SOURCE932} %{SOURCE933} %{SOURCE934} \
+ %{SOURCE935} %{SOURCE936} %{SOURCE937} %{SOURCE938} %{SOURCE939} \
+ %{SOURCE940} %{SOURCE941} %{SOURCE942} %{SOURCE943} %{SOURCE944} \
+ %{SOURCE945} %{SOURCE946} %{SOURCE947} %{SOURCE948} %{SOURCE949} \
+ %{SOURCE950} %{SOURCE951} %{SOURCE952} %{SOURCE953} %{SOURCE954} \
+ %{SOURCE955} %{SOURCE956} %{SOURCE957} %{SOURCE958} %{SOURCE959} \
+ %{SOURCE960} %{SOURCE961}
+%endif
+
%check
# We must exclude probeSS_ctmr04 and probeSS_ctmr10 on certain architectures
@@ -171,6 +319,21 @@ rm -rvf python
%{_bindir}/unu
%{_bindir}/vprobe
+%if %{with man_pages}
+%{_mandir}/man1/gprobe.1*
+%{_mandir}/man1/ilk.1*
+%{_mandir}/man1/miter.1*
+%{_mandir}/man1/mrender.1*
+%{_mandir}/man1/nrrdSanity.1*
+%{_mandir}/man1/overrgb.1*
+%{_mandir}/man1/puller.1*
+%{_mandir}/man1/tend.1*
+%{_mandir}/man1/tend-*.1*
+%{_mandir}/man1/unu.1*
+%{_mandir}/man1/unu-*.1*
+%{_mandir}/man1/vprobe.1*
+%endif
+
%files libs
%license LICENSE.txt
@@ -193,7 +356,7 @@ rm -rvf python
%changelog
* Sat Apr 03 2021 Benjamin A. Beasley <code(a)musicinmybrain.net> - 1.11.0-15
- Fix typo in lib_install_dir.patch
-- Update URLs from HTTP to HTTPS
+- Update download URL from HTTP to HTTPS
- Improved package summaries and descriptions
- Adjust whitespace to personal preference
- Fix stray line continuation backslash
@@ -204,6 +367,7 @@ rm -rvf python
to that subpackage; remove certain unused files with other licenses in %%prep
- Require cmake-filesystem instead of all of CMake in -devel
- More precisely skip overly-strict floating-point tests where required
+- Start work on adding a complete set of man pages
* Wed Jan 27 2021 Fedora Release Engineering <releng(a)fedoraproject.org> - 1.11.0-14
- Rebuilt for https://fedoraproject.org/wiki/Fedora_34_Mass_Rebuild
diff --git a/tend-about.1 b/tend-about.1
new file mode 100644
index 0000000..21d8320
--- /dev/null
+++ b/tend-about.1
@@ -0,0 +1,9 @@
+.TH TEND\-ABOUT "1" "April 2021" "" "User Commands"
+.SH NAME
+tend\-about \- information about
+.BR tend (1)
+and its use
+.SH SYNOPSIS
+.B tend\ about
+.SH "SEE\ ALSO"
+.BR tend (1)
diff --git a/tend-avg.1 b/tend-avg.1
new file mode 100644
index 0000000..8b67389
--- /dev/null
+++ b/tend-avg.1
@@ -0,0 +1,32 @@
+.TH TEND\-AVG "1" "April 2021" "" "User Commands"
+.SH NAME
+tend\-avg \- average across tensor volumes
+.SH SYNOPSIS
+.B tend\ avg
+.B \-i
+.RI < nin1 \ nin2 \ ...>
+.RB [ \-t
+.RI < type >]
+.RB [ \-o
+.RI < nout >]
+.SH DESCRIPTION
+Average across tensor volumes.
+The output is the same size as the any one of the inputs.
+The individual tensors may be averaged in various ways.
+.SH OPTIONS
+.TP
+.B \-i\fR <\fInin1\fR\ \fInin2\fR\ ...>
+list of input diffusion tensor volumes (2 or more
+.IR nrrd s)
+.TP
+.B \-t\fR <\fItype\fR>
+averaging method; default:
+.RB \(lq linear \(rq
+.TP
+.B \-o\fR <\fInout\fR>
+output image (floating point)
+.RI ( string )
+default:
+.RB \(lq \- \(rq
+.SH "SEE\ ALSO"
+.BR tend (1)
diff --git a/tend-bmat.1 b/tend-bmat.1
new file mode 100644
index 0000000..e0a8c3b
--- /dev/null
+++ b/tend-bmat.1
@@ -0,0 +1,43 @@
+.TH TEND\-BMAT "1" "April 2021" "" "User Commands"
+.SH NAME
+tend\-bmat \- calculate B-matrix given gradient directions
+.SH SYNOPSIS
+.B tend\ bmat
+.B \-i
+.RI < grads >
+.RB [ \-o
+.RI < nout >]
+.SH DESCRIPTION
+.P
+Calculate B-matrix given gradient directions, assuming no diffusion weighting
+from the other imaging gradients.
+The input is a 3-by-N array of floats or doubles, each row being one of the
+gradient directions used for diffusion-weighted imaging.
+A plain text file with one gradient per line, no punctuation, is an easy way to
+specify this information.
+The gradient vector coefficients are used as is, without normalization (since
+different gradient strengths are sometimes desired).
+The output has one row of the
+B-matrix per line, with coefficient ordering
+.BR Bxx ,
+.BR Bxy ,
+.BR Bxz ,
+.BR Byy ,
+.BR Byz ,
+.BR Bzz ,
+and with the off-diagonal elements NOT pre-multiplied by 2.
+.SH OPTIONS
+.TP
+.B \-i\fR <\fIgrads\fR>
+array of gradient directions
+.TP
+.B \-o\fR <\fInout\fR>
+output B matrix
+.RI ( string )
+default:
+.RB \(lq \- \(rq
+.SH "SEE\ ALSO"
+.BR tend (1),
+.BR tend\-estim (1),
+.BR tend\-msim (1),
+.BR tend\-sim (1)
diff --git a/tend-epireg.1 b/tend-epireg.1
new file mode 100644
index 0000000..cb45cb6
--- /dev/null
+++ b/tend-epireg.1
@@ -0,0 +1,139 @@
+.TH TEND\-EPIREG "1" "April 2021" "" "User Commands"
+.SH NAME
+tend\-epireg \- register diffusion-weighted echo-planar images
+.SH SYNOPSIS
+.B tend\ epireg
+.B \-i
+.RI < dwi0\fR\ \fBdwi1 \ ...>
+.B \-g
+.RI < grads >
+.RB [ \-r
+.RI < reference >]
+.RB [ \-nv ]
+.RB [ \-p ]
+.RB [ \-bw
+.RI < x\fB,\fIy\fB\ blur >]
+.RB [ \-t
+.RI < DWI\ thresh >]
+.RB [ \-ncc ]
+.RB [ \-f
+.RI < fit\ frac >]
+.RB [ \-k
+.RI < kernel >]
+.RB [ \-s
+.RI < start\ # >]
+.RB [ \-o
+.RI < output/prefix >]
+.SH DESCRIPTION
+.P
+Register diffusion-weighted echo-planar images.
+This registration corrects the shear, scale, and translate along the phase
+encoding direction (assumed to be the Y (second) axis of the image) caused by
+eddy currents from the diffusion-encoding gradients with echo-planar imaging.
+The method is based on calculating moments of segmented images, where the
+segmentation is a simple procedure based on blurring (optional), thresholding
+and connected component analysis.
+The registered DWIs are resampled with the chosen kernel, with the separate
+DWIs stacked along axis 0.
+.SH OPTIONS
+.TP
+.B \-i\fR <\fIdwi0\fR\ \fIdwi1\fR\ ...>
+all the diffusion-weighted images (DWIs), as separate 3D
+.IR nrrd s,
+.BR OR :
+one 4D
+.I nrrd
+of all DWIs stacked along axis 0 (1 or more
+.IR nrrd s)
+.TP
+.B \-g\fR <\fIgrads\fR>
+array of gradient directions, in the same order as the associated DWIs were
+given to
+.RB \(lq \-i \(rq,
+.B OR
+.RB \(lq \-g\ kvp \(rq
+signifies that gradient directions should be read from the key/value pairs of
+the DWI
+.RI ( string )
+.TP
+.B \-r\fR <\fIreference\fR>
+which of the DW volumes (zero-based numbering) should be used as the standard,
+to which all other images are transformed.
+Using
+.B \-1
+(the default) means that 9 intrinsic parameters governing the relationship
+between the gradient direction and the resulting distortion are estimated and
+fitted, ensuring good registration with the non-diffusion-weighted T2 image
+(which is never explicitly used in registration).
+Otherwise, by picking a specific DWI, no distortion parameter estimation is
+done.
+.RI ( int );
+default:
+.RB \(lq \-1 \(rq
+.TP
+.B \-nv
+turn OFF verbose mode, and have no idea what stage processing is at.
+.TP
+.B \-p
+save out intermediate steps of processing
+.TP
+.B \-bw\fR <\fIx\fB,\fIy\ blur\fR>
+standard deviations in X and Y directions of gaussian filter used to blur the
+DWIs prior to doing segmentation.
+This blurring does not effect the final resampling of registered DWIs.
+Use
+.RB \(lq 0.0\ 0.0 \(rq
+to say \(lqno blurring\(rq (2
+.IR float s);
+default:
+.RB \(lq 1.0\ 2.0 \(rq
+.TP
+.B \-t\fR <\fIDWI\ thresh\fR>
+Threshold value to use on DWIs, to do initial separation of brain and
+non-brain.
+By default, the threshold is determined automatically by histogram analysis.
+.RI ( float )
+.TP
+.B \-ncc
+do
+.B NOT
+do connected component (CC) analysis, after thresholding and before moment
+calculation.
+Doing CC analysis usually gives better results because it converts the
+thresholding output into something much closer to a real segmentation
+.TP
+.B \-f\fR <\fIfit\ frac\fR>
+(only meaningful with
+.RB \(lq \-r\ \-1 \(rq)
+When doing linear fitting of the intrinsic distortion parameters, it is good to
+ignore the slices for which the segmentation was poor.
+A heuristic is used to rank the slices according to segmentation quality.
+This option controls how many of the (best) slices contribute to the fitting.
+Use
+.RB \(lq 0 \(rq
+to disable distortion parameter fitting.
+.RI ( float )
+default:
+.RB \(lq 0.70 \(rq
+.TP
+.B \-k\fR <\fIkernel\fR>
+kernel for resampling DWIs along the phase-encoding direction during final
+registration stage; default:
+.RB \(lq cubic:0,0.5 \(rq
+.TP
+.B \-s\fR <\fIstart\ #\fR>
+first number to use in numbered sequence of output files.
+.RI ( int );
+default:
+.RB \(lq 1 \(rq
+.TP
+.B \-o\fR <\fIoutput/prefix\ #\fR>
+For separate 3D DWI volume inputs: prefix for output filenames; will save out
+one (registered) DWI for each input DWI, using the same type as the input.
+.BR OR :
+For single 4D DWI input: output file name.
+.RI ( string );
+default:
+.RB \(lq \- \(rq
+.SH "SEE\ ALSO"
+.BR tend (1)
diff --git a/tend-estim.1 b/tend-estim.1
new file mode 100644
index 0000000..649a1e1
--- /dev/null
+++ b/tend-estim.1
@@ -0,0 +1,196 @@
+.TH TEND\-ESTIM "1" "April 2021" "" "User Commands"
+.SH NAME
+tend\-estim \- estimate tensors from a set of DW images
+.SH SYNOPSIS
+.B tend\ estim
+.RB [ \-old ]
+.RB [ \-sigma
+.RI < sigma >]
+.RB [ \-v
+.RI < verbose >]
+.RB [ \-est
+.RI < estimate\ method >]
+.RB [ \-wlsi
+.RI < WLS\ iters >]
+.RB [ \-fixneg ]
+.RB [ \-ee
+.RI < filename >]
+.RB [ \-eb
+.RI < filename >]
+.RB [ \-t
+.RI < thresh >]
+.RB [ \-soft
+.RI < soft >]
+.RB [ \-scale
+.RI < scale >]
+.RB [ \-mv
+.RI < min\ val >]
+.B \-B
+.RI < B\-list >
+.RB [ \-b
+.RI < b >]
+.B \-knownB0
+.RI < bool >
+.RB [ \-i
+.RI < dwi0\fR\ \fIdwi1\fR\ ...>]
+.RB [ \-o
+.RI < nout >]
+.SH DESCRIPTION
+.P
+Estimate tensors from a set of DW images.
+The tensor coefficient weightings associated with each of the DWIs, the
+B-matrix, is given either as a separate array, (see
+.BR tend\-bmat (1) for
+details), or by the key-value pairs in the DWI
+.I nrrd
+header.
+A \(lqconfidence\(rq value is computed with the tensor, based on a soft
+thresholding of the sum of all the DWIs, according to the threshold and
+softness parameters.
+.SH OPTIONS
+.TP
+.B \-old
+instead of the new
+.B tenEstimateContext
+code, use the old
+.B tenEstimateLinear
+code
+.TP
+.B \-sigma\fR <\fIsigma\fR>
+Rician noise parameter
+.RI ( float )
+.TP
+.B \-v\fR <\fIverbose\fR>
+verbosity level
+.RI ( int )
+default:
+.RB \(lq 0 \(rq
+.TP
+.B \-est\fR <\fIestimate\ method\fR>
+estimation method to use.
+.RB \(lq lls \(rq:
+linear-least squares; default:
+.RB \(lq lls \(rq
+.TP
+.B \-wlsi\fR <\fIWLS\ iters\fR>
+when using weighted-least-squares
+.RB (\(lq \-est\ wls \(rq),
+how many iterations to do after the initial weighted fit.
+.RI ( unsigned\ int )
+default:
+.RB \(lq 1 \(rq
+.TP
+.B \-fixneg
+after estimating the tensor, ensure that there are no negative eigenvalues by
+adding (to all eigenvalues) the amount by which the smallest is negative
+(corresponding to increasing the non-DWI image value).
+.TP
+.B \-ee\fR <\fIfilename\fR>
+Giving a filename here allows you to save out the tensor estimation error: a
+value which measures how much error there is between the tensor model and the
+given diffusion weighted measurements for each sample.
+By default, no such error calculation is saved.
+.RI ( string )
+.TP
+.B \-eb\fR <\fIfilename\fR>
+In those cases where there is no B=0 reference image given
+.RB (\(lq \-knownB0\ false \(rq),
+giving a filename here allows you to save out the
+.B B=0
+image which is estimated from the data.
+By default, this image value is estimated but not saved.
+.RI ( string )
+.TP
+.B \-t\fR <\fIthresh\fR>
+value at which to threshold the mean DWI value per pixel in order to generate
+the \(lqconfidence\(rq mask.
+By default, the threshold value is calculated automatically, based on histogram
+analysis.
+.RI ( double )
+.TP
+.B \-soft\fR <\fIsoft\fR>
+how fuzzy the confidence boundary should be.
+By default, confidence boundary is perfectly sharp
+.RI ( float )
+default:
+.RB \(lq 0 \(rq
+.TP
+.B \-scale\fR <\fIscale\fR>
+After estimating the tensor, scale all of its elements (but not the confidence
+value) by this amount.
+Can help with downstream numerical precision if values are very large or small.
+.RI ( float )
+default:
+.RB \(lq 1 \(rq
+.TP
+.B \-mv\fR <\fImin\ val\fR>
+minimum plausible value (especially important for linear least squares
+estimation)
+.RI ( double )
+default:
+.RB \(lq 1.0 \(rq
+.TP
+.B \-B\fR <\fIB\-list\fR>
+6-by-N list of B-matrices characterizing the diffusion weighting for each
+image.
+.BR tend\-bmat (1)
+is one source for such a matrix; see its usage info for specifics on how the
+coefficients of the B-matrix are ordered.
+An unadorned plain text file is a great way to specify the B-matrix.
+.IP
+.B OR
+.IP
+Can say just
+.RB \(lq \-B\ kvp \(rq
+to try to learn B matrices from key/value pair information in input images.
+.IP
+.RI ( string )
+.TP
+.B \-b\fR <\fIb\fR>
+\(lqb\(rq diffusion-weighting factor (units of sec/mm^2)
+.RI ( double )
+.TP
+.B \-knownB0\fR <\fIbool\fR>
+Indicates if the
+.B B=0
+non-diffusion-weighted reference image is known, or if it has to be estimated
+along with the tensor elements
+.RS +7n
+.IP \(bu 2
+if
+.RB \(lq true \(rq:
+in the given list of diffusion gradients or B-matrices, there are one or more
+with zero norm, which are simply averaged to find the
+.B B=0
+reference image value
+.IP \(bu 2
+if
+.RB \(lq false \(rq:
+there may or may not be diffusion-weighted images among the input; the
+.B B=0
+image value is going to be estimated along with the diffusion model
+.P
+.RI ( bool )
+.RE
+.TP
+.B \-i\fR <\fIdwi0\fR\ \fIdwi1\fR\ ...>
+all the diffusion-weighted images (DWIs), as separate 3D
+.IR nrrd s,
+.BR OR :
+One 4D
+.I nrrd
+of all DWIs stacked along axis 0 (1 or more
+.IR nrrd s);
+default:
+.RB \(lq \- \(rq
+.TP
+.B \-o\fR <\fInout\fR>
+output tensor volume
+.RI ( string )
+default:
+.RB \(lq \- \(rq
+.SH "SEE\ ALSO"
+.BR tend (1)
+.BR tend\-bmat (1),
+.BR tend\-msim (1),
+.BR tend\-sim (1)
diff --git a/tend-grads.1 b/tend-grads.1
new file mode 100644
index 0000000..e92bc8f
--- /dev/null
+++ b/tend-grads.1
@@ -0,0 +1,162 @@
+.TH TEND\-GRADS "1" "April 2021" "" "User Commands"
+.SH NAME
+tend\-grads \- calculate balanced gradient directions for DWI acquisition
+.SH SYNOPSIS
+.B tend\ grads
+.RB [ \-n
+.RI < #\ dir >]
+.RB [ \-i
+.RI < grads >]
+.RB [ \-seed
+.RI < value >]
+.RB [ \-step
+.RI < step >]
+.RB [ \-single ]
+.RB [ \-snap
+.RI < interval >]
+.RB [ \-jitter
+.RI < jitter >]
+.RB [ \-miniter
+.RI < #\ iters >]
+.RB [ \-maxiter
+.RI < #\ iters >]
+.RB [ \-minvelo
+.RI < vel >]
+.RB [ \-exp
+.RI < exponent >]
+.RB [ \-dp
+.RI < potential\ change >]
+.RB [ \-minimprov
+.RI < delta >]
+.RB [ \-minmean
+.RI < len >]
+.RB [ \-izv
+.RI < insert >]
+.RB [ \-o
+.RI < filename >]
+.SH DESCRIPTION
+.P
+Composites an RGBA
+.I nrrd
+over a background color (or image), after doing gamma correction, then
+quantizes to an 8-bit image.
+Actually, the input
+.I nrrd
+can have more than 4 values per pixel, but only the first four are used.
+If the RGBA
+.I nrrd
+s floating point, the values are taken at face value; if it is fixed point, the
+values interpreted as having been quantized (so that 8-bit RGBA images will act
+as you expect).
+When compositing with a background image, the given background image does not
+have to be the same size as the input image; it will be resampled (with linear
+interpolation) to fit.
+.SH OPTIONS
+.TP
+.B \-n\fR <\fI#\ dir\fR>
+desired number of diffusion gradient directions
+.RI ( int )
+default:
+.RB \(lq 6 \(rq
+.TP
+.B \-i\fR <\fIgrads\fR>
+initial gradient directions to start with, instead of default random initial
+directions (overrides
+.RB \(lq \-n \(rq);
+default: \(lq\(rq
+.TP
+.B \-seed\fR <\fIvalue\fR>
+seed value to used with
+.B airSrandMT()
+.RI ( unsigned\ int )
+default:
+.RB \(lq 42 \(rq
+.TP
+.B \-step\fR <\fIstep\fR>
+time increment in solver
+.RI ( double )
+default:
+.RB \(lq 1.0 \(rq
+.TP
+.B \-single
+instead of the default behavior of tracking a pair of antipodal points
+(appropriate for determining DWI gradients), use only single points
+(appropriate for who knows what).
+.TP
+.B \-snap\fR <\fIinterval\fR>
+specifies an interval between which snapshots of the point positions should be
+saved out.
+By default (not using this option), there is no such snapshot behavior
+.RI ( int )
+default:
+.RB \(lq 0 \(rq
+.TP
+.B \-jitter\fR <\fIjitter\fR>
+amount by which to perturb points when given an input
+.I nrrd
+.RI ( double )
+default:
+.RB \(lq 0.1 \(rq
+.TP
+.B \-miniter\fR <\fI#\ iters\fR>
+min number of iterations for which to run the simulation
+.RI ( int )
+default:
+.RB \(lq 0 \(rq
+.TP
+.B \-maxiter\fR <\fI#\ iters\fR>
+max number of iterations for which to run the simulation
+.RI ( int )
+default:
+.RB \(lq 1000000 \(rq
+.TP
+.B \-minvelo\fR <\fIvel\fR>
+low threshold on mean velocity of repelling points, at which point repulsion
+phase of algorithm terminates.
+.RI ( double )
+default:
+.RB \(lq 0.00001 \(rq
+.TP
+.B \-exp\fR <\fIexponent\fR>
+the exponent n that determines the potential energy
+.BR 1/r^n .
+.RI ( double )
+default:
+.RB \(lq 1 \(rq
+.TP
+.B \-dp\fR <\fIpotential\ change\fR>
+low threshold on fractional change of potential at which point repulsion phase
+of algorithm terminates.
+.RI ( double )
+default:
+.RB \(lq 0.000000001 \(rq
+.TP
+.B \-minimprov\fR <\fIdelta\fR>
+in the second phase of the algorithm, when stochastically balancing the sign of
+the gradients, the (small) improvement in length of mean gradient which
+triggers termination (as further improvements are unlikely.
+.RI ( double )
+default:
+.RB \(lq 0.00005 \(rq
+.TP
+.B \-minmean\fR <\fIlen\fR>
+if length of mean gradient falls below this, finish the balancing phase
+.RI ( double )
+default:
+.RB \(lq 0.0001 \(rq
+.TP
+.B \-izv\fR <\fIinsert\fR>
+adding zero vector at beginning of grads
+.RI ( bool )
+default:
+.RB \(lq false \(rq
+.TP
+.B \-o\fR <\fIfilename\fR>
+file to write output
+.I nrrd
+to
+.RI ( string );
+default:
+.RB \(lq \- \(rq
+.SH "SEE\ ALSO"
+.BR tend (1)
diff --git a/tend-make.1 b/tend-make.1
new file mode 100644
index 0000000..c4b7b08
--- /dev/null
+++ b/tend-make.1
@@ -0,0 +1,28 @@
+.TH TEND\-MAKE "1" "April 2021" "" "User Commands"
+.SH NAME
+tend\-make \- create DT volume from confidence and eigensystem
+.SH SYNOPSIS
+.B tend\ make
+.B \-i
+.RI < conf \ evals \ evecs >
+.RB [ \-o
+.RI < nout >]
+.SH DESCRIPTION
+Create DT volume from confidence and eigensystem.
+The input is in the form of three
+.IR nrrd s,
+one for confidence values (3D), one for eigenvalues (4D, three evals per
+voxel), and one for eigenvectors (4D, nine evec components per voxel).
+.SH OPTIONS
+.TP
+.B \-i\fR <\fIconf\fR\ \fIevals\fR\ \fIevecs\fR>
+input diffusion tensor volume (3
+.IR nrrd s)
+.TP
+.B \-o\fR <\fInout\fR>
+output image (floating point)
+.RI ( string )
+default:
+.RB \(lq \- \(rq
+.SH "SEE\ ALSO"
+.BR tend (1)
diff --git a/tend-mconv.1 b/tend-mconv.1
new file mode 100644
index 0000000..54a6f07
--- /dev/null
+++ b/tend-mconv.1
@@ -0,0 +1,39 @@
+.TH TEND\-MCONV "1" "April 2021" "" "User Commands"
+.SH NAME
+tend\-mconv \- convert from one model to another
+.SH SYNOPSIS
+.B tend\ mconv
+.B \-mo
+.RI < model >
+.RB [ \-mi
+.RI < model >]
+.RB [ \-i
+.RI < nin >]
+.RB [ \-o
+.RI < nout >]
+.SH OPTIONS
+.TP
+.B \-mo\fR <\fImodel\fR>
+which model to convert to
+.RI ( string )
+.TP
+.B \-mi\fR <\fImodel\fR>
+model converting from; if not set, will try to determine from input
+.I nrrd
+.RI ( string )
+.TP
+.B \-i\fR <\fInin\fR>
+input
+.I nrrd
+of model parms; default:
+.RB \(lq \- \(rq
+.TP
+.B \-o\fR <\fInout\fR>
+output
+.I nrrd
+of model parms
+.RI ( string );
+default:
+.RB \(lq \- \(rq
+.SH "SEE\ ALSO"
+.BR tend (1)
diff --git a/tend-mfit.1 b/tend-mfit.1
new file mode 100644
index 0000000..fc220b4
--- /dev/null
+++ b/tend-mfit.1
@@ -0,0 +1,129 @@
+.TH TEND\-MFIT "1" "April 2021" "" "User Commands"
+.SH NAME
+tend\-mfit \- estimate models from a set of DW images
+.SH SYNOPSIS
+.B tend\ mfit
+.RB [ \-v
+.RI < verbose >]
+.B \-m
+.RI < model >
+.RB [ \-ns
+.RI < #\ starts >]
+.RB [ \-ml ]
+.RB [ \-sigma
+.RI < sigma >]
+.RB [ \-eps
+.RI < eps >]
+.RB [ \-mini
+.RI < min\ iters >]
+.RB [ \-maxi
+.RI < max\ iters >]
+.B \-knownB0
+.RI < bool >
+.RB [ \-t
+.RI < type >]
+.RB [ \-i
+.RI < dwi >]
+.RB [ \-o
+.RI < nout >]
+.RB [ \-eo
+.RI < filename >]
+.RB [ \-co
+.RI < filename >]
+.RB [ \-io
+.RI < filename >]
+.SH OPTIONS
+.TP
+.B \-v\fR <\fIverbose\fR>
+verbosity level
+.RI ( int )
+default:
+.RB \(lq 0 \(rq
+.TP
+.B \-m\fR <\fImodel\fR>
+which model to fit.
+Use optional
+.RB \(lq b0+ \(rq
+prefix to indicate that the
+.B B0
+image should also be saved (independent of whether it was known or had to be
+estimated, according to
+.RB \(lq \-knownB0 \(rq).
+.RI ( string )
+.TP
+.B \-ns\fR <\fI#\ starts\fR>
+number of random starting points at which to initialize fitting
+.RI ( unsigned\ int )
+default:
+.RB \(lq 1 \(rq
+.TP
+.B \-ml
+do ML fitting, rather than least-squares, which also requires setting
+.RB \(lq \-sigma \(rq
+.TP
+.B \-sigma\fR <\fIsigma\fR>
+Gaussian/Rician noise parameter
+.RI ( double )
+.TP
+.B \-eps\fR <\fIeps\fR>
+convergence epsilon
+.RI ( double )
+default:
+.RB \(lq 0.01 \(rq
+.TP
+.B \-mini\fR <\fImin\ iters\fR>
+minimum required # iterations for fitting.
+.RI ( unsigned\ int )
+default:
+.RB \(lq 3 \(rq
+.TP
+.B \-maxi\fR <\fImax\ iters\fR>
+maximum allowable # iterations for fitting.
+.RI ( unsigned\ int )
+default:
+.RB \(lq 100 \(rq
+.TP
+.B \-knownB0\fR <\fIbool\fR>
+Indicates if the
+.B B=0
+non-diffusion-weighted reference image is known
+.RB (\(lq true \(rq)
+because it appears one or more times amongst the DWIs, or, if it has to be
+estimated along with the other model parameters
+.RB (\(lq false \(rq)
+.RI ( bool )
+.TP
+.B \-t\fR <\fItype\fR>
+output type of model parameters; default:
+.RB \(lq float \(rq
+.TP
+.B \-i\fR <\fIdwi\fR>
+all the diffusion-weighted images in one 4D
+.IR nrrd ;
+default:
+.RB \(lq \- \(rq
+.TP
+.B \-o\fR <\fInout\fR>
+output parameter vector image
+.RI ( string )
+default:
+.RB \(lq \- \(rq
+.TP
+.B \-eo\fR <\fIfilename\fR>
+Giving a filename here allows you to save out the per-sample fitting error.
+By default, no such error is saved.
+.RI ( string )
+.TP
+.B \-co\fR <\fIfilename\fR>
+Giving a filename here allows you to save out the per-sample convergence
+fraction.
+By default, no such error is saved.
+.RI ( string )
+.TP
+.B \-io\fR <\fIfilename\fR>
+Giving a filename here allows you to save out the per-sample number of
+iterations needed for fitting.
+By default, no such error is saved.
+.RI ( string )
+.SH "SEE\ ALSO"
+.BR tend (1)
diff --git a/tend-msim.1 b/tend-msim.1
new file mode 100644
index 0000000..2d0b76a
--- /dev/null
+++ b/tend-msim.1
@@ -0,0 +1,111 @@
+.TH TEND\-MSIM "1" "April 2021" "" "User Commands"
+.SH NAME
+tend\-msim \- simulate DW images from an image of models
+.SH SYNOPSIS
+.B tend\ msim
+.RB [ \-sigma
+.RI < sigma >]
+.RB [ \-seed
+.RI < seed >]
+.B \-g
+.RI < grad\ list >
+.RB [ \-b0
+.RI < b0\ image >]
+.RB [ \-i
+.RI < model\ image >]
+.B \-m
+.RI < model >
+.RB [ \-ib0
+.RI < bool >]
+.RB [ \-b
+.RI < b >]
+.RB [ \-kvp
+.RI < bool >]
+.RB [ \-t
+.RI < type >]
+.RB [ \-o
+.RI < nout >]
+.SH DESCRIPTION
+Simulate DW images from an image of models.
+The output will be in the same form as the input to
+.BR tend\-estim (1).
+The B-matrices
+.RB (\(lq \-B \(rq)
+can be the output from
+.BR tend\-bmat (1),
+or the gradients can be given directly
+.RB (\(lq \-g \(rq);
+one of these is required.
+Note that the input tensor image
+.RB (\(lq \-i \(rq)
+is the basis of the output per-axis fields and image orientation.
+.B NOTE:
+this includes the measurement frame used in the input tensor image, which
+implies that the given gradients or B-matrices are already expressed in that
+measurement frame.
+.SH OPTIONS
+.TP
+.B \-sigma\fR <\fIsigma\fR>
+Gaussian/Rician noise parameter
+.RI ( double )
+default:
+.RB \(lq 0.0 \(rq
+.TP
+.B \-seed\fR <\fIseed\fR>
+seed value for RNG which creates noise
+.RI ( int )
+default:
+.RB \(lq 42 \(rq
+.TP
+.B \-g\fR <\fIgrad\ list\fR>
+gradient list, one row per diffusion-weighted image
+.TP
+.B \-b0\fR <\fIb0\ image\fR>
+reference non-diffusion-weighted
+.RB (\(lq B0 \(rq)
+image, which may be needed if it isn't part of the given model parameter image
+.TP
+.B \-m\fR <\fImodel\fR>
+model with which to simulate DWIs, which must be specified if it is not
+indicated by the first axis in input model image.
+.RB \(lq string \(rq
+.TP
+.B \-ib0\fR <\fIbool\fR>
+insert a non-DW
+.B B0
+image at the beginning of the experiment specification (useful if the given
+gradient list doesn't already have one) and hence also insert a
+.B B0
+image at the beginning of the output simulated DWIs
+.RB \(lq bool \(rq
+default:
+.RB \(lq false \(rq
+.TP
+.B \-b\fR <\fIb\fR>
+.B b
+value for simulated scan
+.RB \(lq double \(rq
+default:
+.RB \(lq 1000 \(rq
+.TP
+.B \-kvp\fR <\fIbool\fR>
+generate key/value pairs in the NRRD header corresponding to the input
+.BR b -value
+and gradients.
+.RB \(lq bool \(rq
+default:
+.RB \(lq true \(rq
+.TP
+.B \-t\fR <\fItype\fR>
+output type of DWIs; default:
+.RB \(lq float \(rq
+.TP
+.B \-o\fR <\fInout\fR>
+output dwis
+.RI ( string )
+default:
+.RB \(lq \- \(rq
+.SH "SEE\ ALSO"
+.BR tend (1),
+.BR tend\-bmat (1),
+.BR tend\-estim (1)
diff --git a/tend-sim.1 b/tend-sim.1
new file mode 100644
index 0000000..3761398
--- /dev/null
+++ b/tend-sim.1
@@ -0,0 +1,101 @@
+.TH TEND\-SIM "1" "April 2021" "" "User Commands"
+.SH NAME
+tend\-sim \- simulate DW images from a tensor field
+.SH SYNOPSIS
+.B tend\ sim
+.RB [ \-old ]
+.RB [ \-sigma
+.RI < sigma >]
+.RB [ \-seed
+.RI < seed >]
+.RB [ \-g
+.RI < grad\ list >]
+.RB [ \-B
+.RI < B\ matrix >]
+.B \-r
+.RI < reference\ field >
+.RB [ \-i
+.RI < tensor\ field >]
+.RB [ \-b
+.RI < b >]
+.RB [ \-kvp ]
+.RB [ \-t
+.RI < type >]
+.RB [ \-o
+.RI < nout >]
+.SH DESCRIPTION
+.P
+Simulate DW images from a tensor field.
+The output will be in the same form as the input to
+.BR tend\-estim (1).
+The B-matrices
+.RB (\(lq \-B \(rq)
+can be the output from
+.BR tend\-bmat (1),
+or the gradients can be given directly
+.RB (\(lq \-g \(rq);
+one of these is required.
+Note that the input tensor field
+.RB (\(lq \-i \(rq)
+is the basis of the output per-axis fields and image orientation.
+.B NOTE:
+this includes the measurement frame used in the input tensor field, which
+implies that the given gradients or B-matrices are already expressed in that
+measurement frame.
+.SH OPTIONS
+.TP
+.B \-old
+don't use the new
+.B tenEstimateContext
+functionality
+.TP
+.B \-sigma\fR <\fIsigma\fR>
+Rician noise parameter
+.RI ( float )
+default:
+.RB \(lq 0.0 \(rq
+.TP
+.B \-seed\fR <\fIseed\fR>
+seed value for RNG which creates noise
+.RI ( int )
+default:
+.RB \(lq 42 \(rq
+.TP
+.B \-g\fR <\fIgrad\ list\fR>
+gradient list, one row per diffusion-weighted image
+.TP
+.B \-B\fR <\fIB\ matrix\fR>
+B matrix, one row per diffusion-weighted image.
+Using this overrides the gradient list input via
+.RB \(lq \-g \(rq
+.TP
+.B \-r\fR <\fIreference\ field\fR>
+reference anatomical scan, with no diffusion weighting
+.TP
+.B \-i\fR <\fItensor\ field\fR>
+input diffusion tensor field
+.TP
+.B \-b\fR <\fIb\fR>
+.B b
+value for simulated scan
+.RI ( float )
+default:
+.RB \(lq 1000 \(rq
+.TP
+.B \-kvp
+generate key/value pairs in the NRRD header corresponding to the input b-value
+and gradients or B-matrices.
+.TP
+.B \-t\fR <\fItype\fR>
+output type of DWIs; default:
+.RB \(lq float \(rq
+.TP
+.B \-o\fR <\fInout\fR>
+output image (floating point)
+.RI ( string )
+default:
+.RB \(lq \- \(rq
+.SH "SEE\ ALSO"
+.BR tend (1),
+.BR tend\-bmat (1),
+.BR tend\-estim (1)
diff --git a/tend.1 b/tend.1
new file mode 100644
index 0000000..e61040a
--- /dev/null
+++ b/tend.1
@@ -0,0 +1,316 @@
+.TH TEND "1" "April 2021" "" "User Commands"
+.SH NAME
+tend \- diffusion image processing and analysis
+.SH SYNOPSIS
+.B tend
+.I COMMAND
+.RI [ OPTION ]...
+.SH DESCRIPTION
+.BR tend (1)
+is a command-line interface to much of the functionality in \(lqten\(rq, a C
+library for diffusion image processing.
+Ten is one library in the \(lqTeem\(rq collection of libraries.
+More information about Teem is at
+.UR http://teem.sf.net
+http://teem.sf.net
+.UE .
+A checkout of Teem source is available via:
+.in +4n
+.EX
+.B svn co https://svn.code.sf.net/p/teem/code/teem/trunk teem
+.EE
+.in
+.P
+Long-term maintenance of this software depends on funding, and funding depends
+on being able to document who is using it for what.
+If
+.BR tend (1)
+or Ten has helped in your research, including for simple one-off experiments or
+mundane data hacking, the developers of Teem would love to know.
+There are multiple ways of communicating this.
+In your publications, consider adding a line such as this in the
+Acknowledgments:
+.in +4n
+Data processing performed with the tend tool, part of the Teem toolkit
+available at
+.UR http://teem.sf.net
+http://teem.sf.net
+.UE .
+.in
+Alternatively, please email
+.MT glk(a)uchicago.edu
+glk(a)uchicago.edu
+.ME
+and briefly describe how Teem software has helped in your work.
+Please also consider joining the
+.UR https://lists.sourceforge.net/lists/listinfo/teem-users
+teem\-users mailing list
+.UE .
+This is the primary forum for feedback, questions, and feature requests.
+.P
+Like
+.BR unu (1),
+another Teem command-line binary, it is often useful to chain together invocations of
+.BR tend (1)
+with pipes, as in the following, which estimates tensors from DWIs, takes a
+slice of the tensor volume, computes the standard RGB colormap of the principal
+eigenvector, and then quantizes it to an 8-bit PNG:
+.in +4n
+.EX
+\fBtend\ estim\ \-i\ dwi.nhdr\ \-B\ kvp\ \-knownB0\ true\ \(rs
+\ |\ tend\ slice\ \-a\ 2\ \-p\ 30\ \(rs
+\ |\ tend\ evecrgb\ \-c\ 0\ \-a\ cl2\ \-gam\ 1.2\ \(rs
+\ |\ unu\ quantize\ \-b\ 8\ \-min\ 0\ \-max\ 1\ \-o\ z30\-rgb.png
+.EE
+.in
+.SH OPTIONS
+.SS tend\ about
+Information about this program and its use
+.P
+See
+.BR tend\-about (1).
+.SS tend\ grads
+Calculate balanced gradient directions for DWI acquisition
+.P
+See
+.BR tend\-grads (1).
+.SS tend\ epireg
+Register diffusion-weighted echo-planar images
+.P
+See
+.BR tend\-epireg (1).
+.SS tend\ bmat
+Calculate B-matrix given gradient directions
+.P
+See
+.BR tend\-bmat (1).
+.SS tend\ estim
+Estimate tensors from a set of DW images
+.P
+See
+.BR tend\-estim (1).
+.SS tend\ sim
+Simulate DW images from a tensor field
+.P
+See
+.BR tend\-sim (1).
+.SS tend\ mfit
+Estimate models from a set of DW images
+.P
+See
+.BR tend\-mfit (1).
+.SS tend\ mconv
+Convert from one model to another
+.P
+See
+.BR tend\-mconv (1).
+.SS tend\ msim
+Simulate DW images from an image of models
+.P
+See
+.BR tend\-msim (1).
+.SS tend\ make
+Create DT volume from confidence and eigensystem
+.P
+See
+.BR tend\-make (1).
+.SS tend\ avg
+Average across tensor volumes
+.P
+See
+.BR tend\-avg (1).
+.SS tend\ helix
+Generate twisting helical tensor field
+.P
+See
+.BR tend\-helix (1).
+.SS tend\ sten
+Calculate structure tensors from a scalar field
+.P
+See
+.BR tend\-sten (1).
+.SS tend\ glyph
+Generate postscript or ray-traced renderings of 3D glyphs
+.P
+See
+.BR tend\-glyph (1).
+.SS tend\ ellipse
+Generate postscript renderings of 2D glyphs
+.P
+See
+.BR tend\-ellipse (1).
+.SS tend\ anplot
+Graph anisotropy metric in barycentric coords
+.P
+See
+.BR tend\-anplot (1).
+.SS tend\ anvol
+Apply an anisotropy metric to a DT volume
+.P
+See
+.BR tend\-anvol (1).
+.SS tend\ anscale
+Scale the anisotropic component of the tensors
+.P
+See
+.BR tend\-anscale (1).
+.SS tend\ anhist
+Generate barycentric histograms of anisotropy
+.P
+See
+.BR tend\-anhist (1).
+.SS tend\ triple
+Compute volume of shape triples
+.P
+See
+.BR tend\-triple (1).
+.SS tend\ tconv
+Convert between different shape triples
+.P
+See
+.BR tend\-tconv (1).
+.SS tend\ point
+Describe everything about one sample in a DT volume
+.P
+See
+.BR tend\-point (1).
+.SS tend\ slice
+Slice 3D tensors to get slab/image of 3D/2D tensors
+.P
+See
+.BR tend\-slice (1).
+.SS tend\ norm
+Normalize tensor size
+.P
+See
+.BR tend\-norm (1).
+.SS tend\ fiber
+Fiber tractography, from one or more seeds
+.P
+See
+.BR tend\-fiber (1).
+.SS tend\ eval
+Calculate one or more eigenvalues in a DT volume
+.P
+See
+.BR tend\-eval (1).
+.SS tend\ evalpow
+Modify shape by raising eigenvalues to some power
+.P
+See
+.BR tend\-evalpow (1).
+.SS tend\ evalclamp
+Modify shape by clamping eigenvalues in some range
+.P
+See
+.BR tend\-evalclamp (1).
+.SS tend\ evaladd
+Modify shape by adding a constant to all eigenvalues
+.P
+See
+.BR tend\-evaladd (1).
+.SS tend\ evalmul
+Modify shape by multiplying eigenvalues by a constant
+.P
+See
+.BR tend\-evalmul (1).
+.SS tend\ log
+Calculates logarithm of the tensor
+.P
+See
+.BR tend\-log (1).
+.SS tend\ exp
+Calculates \fBexp()\fR of the tensor
+.P
+See
+.BR tend\-exp (1).
+.SS tend\ evec
+Calculate one or more eigenvectors in a DT volume
+.P
+See
+.BR tend\-evec (1).
+.SS tend\ evecrgb
+Make an RGB volume from an eigenvector and an anisotropy
+.P
+See
+.BR tend\-evecrgb (1).
+.SS tend\ evq
+Quantize directions of diffusion
+.P
+See
+.BR tend\-evq (1).
+.SS tend\ unmf
+Applies and removes the measurement frame
+.P
+See
+.BR tend\-unmf (1).
+.SS tend\ expand
+Converts masked non-redundant tensor images to redundant
+.P
+See
+.BR tend\-expand (1).
+.SS tend\ shrink
+Converts a 9-value DT volume to a 7-value DT volume
+.P
+See
+.BR tend\-shrink (1).
+.SS tend\ bfit
+Non-linear least-squares fitting of b-value curves
+.P
+See
+.BR tend\-bfit (1).
+.SS tend\ satin
+Generate a pretty synthetic DT volume
+.P
+See
+.BR tend\-satin (1).
+.SH "SEE\ ALSO"
+.BR gprobe (1),
+.BR ilk (1),
+.BR mrender (1),
+.BR miter (1),
+.BR nrrdSanity (1),
+.BR overrgb (1),
+.BR puller (1),
+.BR unu (1),
+.BR vprobe (1),
+.BR tend\-about (1),
+.BR tend\-anhist (1),
+.BR tend\-anplot (1),
+.BR tend\-anscale (1),
+.BR tend\-anvol (1),
+.BR tend\-avg (1),
+.BR tend\-bfit (1),
+.BR tend\-bmat (1),
+.BR tend\-ellipse (1),
+.BR tend\-epireg (1),
+.BR tend\-estim (1),
+.BR tend\-eval (1),
+.BR tend\-evaladd (1),
+.BR tend\-evalclamp (1),
+.BR tend\-evalmul (1),
+.BR tend\-evalpow (1),
+.BR tend\-evec (1),
+.BR tend\-evecrgb (1),
+.BR tend\-evq (1),
+.BR tend\-exp (1),
+.BR tend\-expand (1),
+.BR tend\-fiber (1),
+.BR tend\-glyph (1),
+.BR tend\-grads (1),
+.BR tend\-helix (1),
+.BR tend\-log (1),
+.BR tend\-make (1),
+.BR tend\-mconv (1),
+.BR tend\-mfit (1),
+.BR tend\-msim (1),
+.BR tend\-norm (1),
+.BR tend\-point (1),
+.BR tend\-satin (1),
+.BR tend\-shrink (1),
+.BR tend\-sim (1),
+.BR tend\-slice (1),
+.BR tend\-sten (1),
+.BR tend\-tconv (1),
+.BR tend\-triple (1),
+.BR tend\-unmf (1)
diff --git a/unu.1 b/unu.1
new file mode 100644
index 0000000..ef710be
--- /dev/null
+++ b/unu.1
@@ -0,0 +1,127 @@
+.TH UNU "1" "April 2021" "" "User Commands"
+.SH NAME
+unu \- Utah Nrrd Utilities command-line interface
+.SH SYNOPSIS
+.B unu
+.I COMMAND
+.RI [ OPTION ]...
+.SH DESCRIPTION
+.BR unu (1)
+is a command-line interface to much of the functionality in \(lqnrrd\(rq, a C
+library for raster data processing.
+Nrrd is one library in the \(lqTeem\(rq collection of libraries.
+More information about Teem is at
+.UR http://teem.sf.net
+http://teem.sf.net
+.UE .
+A checkout of Teem source is available via:
+.in +4n
+.EX
+.B svn co https://svn.code.sf.net/p/teem/code/teem/trunk teem
+.EE
+.in
+.P
+Long-term maintenance of this software depends on funding, and funding depends
+on being able to document who is using it for what.
+If
+.BR unu (1)
+or Nrrd has helped in your research, including for simple one-off experiments
+or mundane data hacking, the developers of Teem would love to know.
+There are multiple ways of communicating this.
+In your publications, consider adding a line such as this in the
+Acknowledgments:
+.in +4n
+Data processing performed with the unu tool, part of the Teem toolkit available
+at
+.UR http://teem.sf.net
+http://teem.sf.net
+.UE .
+.in
+Alternatively, please email
+.MT glk(a)uchicago.edu
+glk(a)uchicago.edu
+.ME
+and briefly describe how Teem software has helped in your work.
+Please also consider joining the
+.UR https://lists.sourceforge.net/lists/listinfo/teem-users
+teem\-users mailing list
+.UE .
+This is the primary forum for feedback, questions, and feature requests.
+.P
+A summary list of unu commands is generated by running simply
+.RB \(lq unu \(rq.
+Running a
+.BR unu (1)
+command without additional arguments (e.g. \(lq\fBunu\ slice\fR\(rq)
+generates its description and usage information.
+This includes information (marked with
+.RB \(lq *\ Uses\ ... \(rq)
+about the underling Nrrd library calls (e.g. \fBnrrdSlice()\fR) that implement
+the basic functionality in that
+.BR unu (1)
+command.
+.P
+The utility of
+.BR unu (1)
+is mainly as a pre-processing tool for getting data into a type, encoding,
+format, or dimensions best suited for some visualization or rendering task.
+Also, slices and projections are effective ways to visually inspect the
+contents of a dataset.
+Especially useful
+ commands include
+.BR make ,
+.BR resample ,
+.BR crop ,
+.BR slice ,
+.BR project ,
+.BR histo ,
+.BR dhisto ,
+.BR quantize ,
+and
+.BR save .
+.BR unu (1)
+can process CT and MRI volume datasets, grayscale and color images,
+time-varying volumes of vector fields (5-D arrays), and more.
+Currently supported formats are plain text files (2-D float arrays), NRRD, VTK
+structured points, and PNG and PNM images.
+.RB \(lq unu\ make\ \-bs\ \-1 \(rq
+can read from most DICOM files.
+.RB \(lq unu\ save \(rq
+can generate EPS files.
+Supported encodings are raw, ascii, hex, gzip, and bzip2.
+.P
+Much of the functionality of
+.BR unu (1)
+derives from chaining multiple invocations together with pipes
+.RB (\(lq | \(rq),
+minimizing the need to save out intermediate files.
+For example, if
+.RB (\(lq data.raw.gz \(rq),
+is a gzip\(cqed 256\ x\ 256\ x\ 80 volume of raw floats written from a PC, then
+the following will save to
+.RB (\(lq zsum.png \(rq)
+a histogram equalized summation projection along the slowest axis:
+.in +4n
+.EX
+\fBunu\ make\ \-i\ data.raw.gz\ \-t\ float\ \-s\ 256\ 256\ 80\ \-e\ gzip\ \-en\ little\ \(rs
+\ |\ unu\ project\ \-a\ 2\ \-m\ sum\ \(rs
+\ |\ unu\ heq\ \-b\ 2000\ \-s\ 1\ \(rs
+\ |\ unu\ quantize\ \-b\ 8\ \-o\ zsum.png
+.EE
+.in
+.P
+Formats available:
+.B nrrd pnm png vtk text eps
+.P
+Nrrd data encodings available:
+.B raw ascii hex gz bz2
+.SH "SEE\ ALSO"
+.BR gprobe (1),
+.BR ilk (1),
+.BR mrender (1),
+.BR miter (1),
+.BR nrrdSanity (1),
+.BR overrgb (1),
+.BR puller (1),
+.BR tend (1),
+.BR vprobe (1)
diff --git a/vprobe.1 b/vprobe.1
new file mode 100644
index 0000000..bdee507
--- /dev/null
+++ b/vprobe.1
@@ -0,0 +1,214 @@
+.TH VPROBE "1" "April 2021" "" "User Commands"
+.SH NAME
+vprobe \- shows off the functionality of the gage library
+.SH SYNOPSIS
+.B vprobe
+.B \-i
+.RI < nin >
+.B \-k
+.RI < kind >
+.RB [ \-v
+.RI < verbosity >]
+.B \-q
+.RI < query >
+.RB [ \-s
+.RI < sclX \ xclY \ sxlZ >]
+.RB [ \-k00
+.RI < kern00 >]
+.RB [ \-k11
+.RI < kern11 >]
+.RB [ \-k22
+.RI < kern22 >]
+.RB [ \-seed
+.RI < N >]
+.RB [ \-ssn
+.RI < SS\ # >]
+.RB [ \-ssr
+.RI < scale\ range >]
+.RB [ \-ssrf
+.RI < SS\ read\ format >]
+.RB [ \-sssf
+.RI < SS\ save\ format >]
+.RB [ \-ssw
+.RI < SS\ pos >]
+.RB [ \-kssb
+.RI < kernel >]
+.RB [ \-kssr
+.RI < kernel >]
+.RB [ \-ssu ]
+.RB [ \-sso ]
+.RB [ \-ssnd ]
+.RB [ \-rn ]
+.RB [ \-gmc
+.RI < min\ gradmag >]
+.RB [ \-ofs ]
+.RB [ \-t
+.RI < type >]
+.RB [ \-o
+.RI < nout >]
+.SH DESCRIPTION
+.P
+Shows off the functionality of the gage library.
+Uses gageProbe() to query various kinds of volumes to learn various measured or
+derived quantities.
+Can set environment variable
+.B TEEM_VPROBE_HACK_ZI
+to limit probing to a single z slice.
+.SH OPTIONS
+.TP
+.B \-i\fR\ <\fInin\fR>
+input volume
+.TP
+.B \-k\fR\ <\fIkind\fR>
+\(lqkind\(rq of volume
+.RB (\(lq scalar \(rq,
+.RB \(lq vector \(rq,
+.RB \(lq tensor \(rq,
+or
+.RB \(lq dwi \(rq)
+.TP
+.B \-v\fR\ <\fIverbosity\fR>
+verbosity level
+.RI ( int );
+default:
+.RB \(lq 1 \(rq
+.TP
+.B \-q\fR\ <\fIquery\fR>
+the quantity
+.RB ( scalar ,
+.BR vector ,
+or
+.BR matrix )
+to learn by probing
+.RI ( string )
+.TP
+.B \-s\fR\ <\fIsclX\fR\ \fIsclY\fR\ \fIsxlZ\fR>
+scaling factor for resampling on each axis
+.RB (> 1.0 :
+supersampling); (3
+.IR double s);
+default:
+.RB \(lq 1.0\ 1.0\ 1.0 \(rq
+.TP
+.B \-k00\fR\ <\fIkern00\fR>
+kernel for
+.BR gageKernel00 ;
+default:
+.RB \(lq tent \(rq
+.TP
+.B \-k11\fR\ <\fIkern11\fR>
+kernel for
+.BR gageKernel11 ;
+default:
+.RB \(lq cubicd:1,0 \(rq
+.TP
+.B \-k22\fR\ <\fIkern22\fR>
+kernel for
+.BR gageKernel22 ;
+default:
+.RB \(lq cubicdd:1,0 \(rq
+.TP
+.B \-seed\fR\ <\fIN\fR>
+RNG seed; mostly for debugging
+.RI ( unsigned\ int );
+default:
+.RB \(lq 42 \(rq
+.TP
+.B \-ssn\fR\ <\fISS\ #\fR>
+how many scale-space samples to evaluate, or,
+.B 0
+to turn-off all scale-space behavior
+.RI ( unsigned\ int );
+default:
+.RB \(lq 0 \(rq
+.TP
+.B \-ssr\fR\ <\fIscale\ range\fR>
+range of scales in scale-space (2
+.IR double s);
+default:
+.RB \(lq nan\ nan \(rq
+.TP
+.B \-ssrf\fR\ <\fISS\ read\ format\fR>
+.BR printf (3)\-style
+format (including a
+.RB \(lq %u \(rq)
+for the filenames from which to
+.I read
+pre-blurred volumes computed for the stack
+.RI ( string );
+default: \(lq\(rq
+.TP
+.B \-sssf\fR\ <\fISS\ save\ format\fR>
+.BR printf (3)\-style
+format (including a
+.RB \(lq %u \(rq)
+for the filenames from which to
+.I save
+pre-blurred volumes computed for the stack
+.RI ( string );
+default: \(lq\(rq
+.TP
+.B \-ssw\fR\ <\fISS\ pos\fR>
+\(lqworld\(rq)-space position (true sigma) at which to sample in scale-space
+.RI ( double );
+default:
+.RB \(lq 0 \(rq
+.TP
+.B \-kssb\fR\ <\fIkernel\fR>
+blurring kernel, to sample scale space; default:
+.RB \(lq dgauss:1,5 \(rq
+.TP
+.B \-kssr\fR\ <\fIkernel\fR>
+kernel for reconstructing from scale space samples
+.RB \(lq hermite \(rq
+.TP
+.B \-ssu
+do uniform samples along sigma, and not (by default) samples according to the
+effective diffusion scale
+.TP
+.B \-sso
+if not using \(lq\-ssu\(rq, use pre-computed optimal sigmas when possible
+.TP
+.B \-ssnd
+normalize derivatives by scale
+.TP
+.B \-rn
+renormalize kernel weights at each new sample location.
+\(lqAccurate\(rq kernels don\(cqt need this; doing it always makes things go
+slower
+.TP
+.B \-gmc\fR\ <\fImin\ gradmag\fR>
+For curvature-based queries, use zero when gradient magnitude is below this
+.RI ( double );
+default:
+.RB \(lq 0.0 \(rq
+.TP
+.B \-ofs
+If only per-axis spacing is available, use that to contrive full orientation
+info
+.TP
+.B \-t\fR\ <\fItype\fR>
+type of output volume
+.RI ( type );
+default:
+.RB \(lq float \(rq
+.TP
+.B \-o\fR\ <\fInout\fR>
+output volume
+.RI ( string );
+default:
+.RB \(lq \- \(rq
+.SH ENVIRONMENT
+.TP
+.B TEEM_VPROBE_HACK_ZI
+If set, probing is limited to a single z slice.
+.SH "SEE\ ALSO"
+.BR gprobe (1),
+.BR ilk (1),
+.BR miter (1),
+.BR mrender (1),
+.BR nrrdSanity (1),
+.BR overrgb (1),
+.BR puller (1),
+.BR tend (1),
+.BR unu (1)
commit 593f4aa56a75b955f0a6b62a4a248954e702945d
Author: Benjamin A. Beasley <code(a)musicinmybrain.net>
Date: Sat Apr 3 12:41:10 2021 -0400
More precisely skip overly-strict floating-point tests where required
diff --git a/teem.spec b/teem.spec
index f97e08f..2ebe7a6 100644
--- a/teem.spec
+++ b/teem.spec
@@ -143,13 +143,15 @@ rm -rvf python
%check
-# tests fail on 32-bit arches
-# seems that computation accuracy is arch-dependant
-%global _smp_mflags -j1
-%ifarch x86_64
-%ctest3
+# We must exclude probeSS_ctmr04 and probeSS_ctmr10 on certain architectures
+# due to overly-strict rounding requirements.
+%ifarch %{arm64} ppc64le s390x
+%global ctest_excludes --exclude-regex '^(probeSS_ctmr(04|10))$'
%endif
+# Tests are not parallel-safe
+%ctest3 %{?ctest_excludes} -j1
+
%if 0%{?epel} == 7
%ldconfig_scriptlets libs
@@ -201,6 +203,7 @@ rm -rvf python
- Add an -examples subpackage so the BSD and zlib licenses can be “contained”
to that subpackage; remove certain unused files with other licenses in %%prep
- Require cmake-filesystem instead of all of CMake in -devel
+- More precisely skip overly-strict floating-point tests where required
* Wed Jan 27 2021 Fedora Release Engineering <releng(a)fedoraproject.org> - 1.11.0-14
- Rebuilt for https://fedoraproject.org/wiki/Fedora_34_Mass_Rebuild
commit 23e9f5a00538ba4ca386c4cf3cb6384c16caa08a
Author: Benjamin A. Beasley <code(a)musicinmybrain.net>
Date: Sat Apr 3 09:49:36 2021 -0400
Add explicit BR on make (implied by cmake/cmake3)
diff --git a/teem.spec b/teem.spec
index d1fa1e0..f97e08f 100644
--- a/teem.spec
+++ b/teem.spec
@@ -25,6 +25,7 @@ Patch0: lib_install_dir.patch
BuildRequires: gcc-c++
BuildRequires: cmake3
+BuildRequires: make
BuildRequires: zlib-devel
BuildRequires: libpng-devel
commit db87fd2490d946f17a6d9fc4b18e47d37bebb4b3
Author: Benjamin A. Beasley <code(a)musicinmybrain.net>
Date: Sat Apr 3 09:43:41 2021 -0400
Require cmake-filesystem instead of all of CMake in -devel
diff --git a/teem.spec b/teem.spec
index d4c3d79..d1fa1e0 100644
--- a/teem.spec
+++ b/teem.spec
@@ -92,7 +92,7 @@ applications that use Teem.
%package devel
Summary: Development files for %{name}
Requires: %{name}-libs%{?_isa} = %{version}-%{release}
-Requires: cmake%{?_isa}
+Requires: cmake-filesystem
%description devel %{common_description}
@@ -199,6 +199,7 @@ rm -rvf python
- Use stricter file globs, including specifying the so-version
- Add an -examples subpackage so the BSD and zlib licenses can be “contained”
to that subpackage; remove certain unused files with other licenses in %%prep
+- Require cmake-filesystem instead of all of CMake in -devel
* Wed Jan 27 2021 Fedora Release Engineering <releng(a)fedoraproject.org> - 1.11.0-14
- Rebuilt for https://fedoraproject.org/wiki/Fedora_34_Mass_Rebuild
commit e6982c7d45b51e73e424b3235560d9c3d6314908
Author: Benjamin A. Beasley <code(a)musicinmybrain.net>
Date: Sat Apr 3 09:42:16 2021 -0400
Add an -examples subpackage so the BSD and zlib licenses can be “contained” to that subpackage; remove certain unused files with other licenses in %%prep
diff --git a/teem.spec b/teem.spec
index b3f50d4..d4c3d79 100644
--- a/teem.spec
+++ b/teem.spec
@@ -4,7 +4,18 @@ Version: 1.11.0
Release: 15%{?dist}
Summary: Libraries for processing and visualizing scientific raster data
+# The entire source is LGPLv2+, except files noted at the License fields of certain subpackages.
License: LGPLv2+
+# Additionally, the following are removed in %%prep to assert that they do not
+# contribute to the built package:
+#
+# zlib:
+# arch/win32/include/zlib.h
+# MIT:
+# python/ctypes/Nrrd.py
+# python/ctypes/pullDemo.py
+# python/ctypes/teem-gen.py
+# python/ctypes/teem.py
URL: https://teem.sourceforge.net
Source0: https://downloads.sourceforge.net/project/teem/teem/%{version}/%{name}-%{...
# Patch CMakeLists.txt to fix library install paths to match what Fedora
@@ -89,9 +100,33 @@ The %{name}-devel package contains libraries and header files for developing
applications that use Teem.
+%package examples
+Summary: Examples for developing with for %{name}
+# The entire contents of this subpackage are LGPLv2+, except:
+#
+# zlib:
+# UseTeemCMakeDemo/sanity.c
+# BSD:
+# Examples/sanity/sanity.c
+License: LGPLv2+ and zlib and BSD
+BuildArch: noarch
+
+%description examples %{common_description}
+
+The %{name}-examples package contains examples for developing applications that
+use Teem.
+
+
%prep
%autosetup -n %{name}-%{version}-src -p1
+# Remove files that, while they have acceptable licenses, we want to assert do
+# not contribute to the built RPMs:
+# - Not applicable to this platform
+rm -rvf arch/win32
+# - Nothing here appears suitable for packaging:
+rm -rvf python
+
%build
%cmake3 \
@@ -141,12 +176,17 @@ applications that use Teem.
%files devel
-%doc Examples/sanity/
%{_includedir}/teem/
%{_libdir}/libteem.so
%{_libdir}/cmake/Teem-%{version}/
+%files examples
+%license LICENSE.txt
+%doc Examples
+%doc UseTeemCMakeDemo
+
+
%changelog
* Sat Apr 03 2021 Benjamin A. Beasley <code(a)musicinmybrain.net> - 1.11.0-15
- Fix typo in lib_install_dir.patch
@@ -156,6 +196,9 @@ applications that use Teem.
- Fix stray line continuation backslash
- Support EPEL7
- Build with “levmar” support enabled
+- Use stricter file globs, including specifying the so-version
+- Add an -examples subpackage so the BSD and zlib licenses can be “contained”
+ to that subpackage; remove certain unused files with other licenses in %%prep
* Wed Jan 27 2021 Fedora Release Engineering <releng(a)fedoraproject.org> - 1.11.0-14
- Rebuilt for https://fedoraproject.org/wiki/Fedora_34_Mass_Rebuild
commit f879b4ba5748261a4e94736d94a9aecf9e63a24b
Author: Benjamin A. Beasley <code(a)musicinmybrain.net>
Date: Sat Apr 3 09:22:50 2021 -0400
Use stricter file globs, including specifying the so-version
diff --git a/teem.spec b/teem.spec
index be5871f..b3f50d4 100644
--- a/teem.spec
+++ b/teem.spec
@@ -1,5 +1,6 @@
Name: teem
Version: 1.11.0
+%global so_version 1
Release: 15%{?dist}
Summary: Libraries for processing and visualizing scientific raster data
@@ -121,12 +122,22 @@ applications that use Teem.
%files
%doc README.txt
-%{_bindir}/*
+%{_bindir}/gprobe
+%{_bindir}/ilk
+%{_bindir}/miter
+%{_bindir}/mrender
+%{_bindir}/nrrdSanity
+%{_bindir}/overrgb
+%{_bindir}/puller
+%{_bindir}/tend
+%{_bindir}/unu
+%{_bindir}/vprobe
%files libs
%license LICENSE.txt
-%{_libdir}/libteem.so.*
+%{_libdir}/libteem.so.%{so_version}
+%{_libdir}/libteem.so.%{so_version}.*
%files devel
commit 8e9d83ec6f4083e2e472511cf71e6a4cb303f6f6
Author: Benjamin A. Beasley <code(a)musicinmybrain.net>
Date: Sat Apr 3 09:19:43 2021 -0400
Build with “levmar” support enabled
diff --git a/teem.spec b/teem.spec
index 5f11edb..be5871f 100644
--- a/teem.spec
+++ b/teem.spec
@@ -18,6 +18,7 @@ BuildRequires: zlib-devel
BuildRequires: libpng-devel
BuildRequires: bzip2-devel
BuildRequires: fftw-devel
+BuildRequires: levmar-devel
%global common_description %{expand:
What Is Teem?
@@ -95,7 +96,8 @@ applications that use Teem.
%cmake3 \
-DCMAKE_SKIP_INSTALL_RPATH=ON \
-DTeem_USE_LIB_INSTALL_SUBDIR=ON \
- -DTeem_FFTW3=ON
+ -DTeem_FFTW3=ON \
+ -DTeem_LEVMAR=ON
%cmake3_build
@@ -142,6 +144,7 @@ applications that use Teem.
- Adjust whitespace to personal preference
- Fix stray line continuation backslash
- Support EPEL7
+- Build with “levmar” support enabled
* Wed Jan 27 2021 Fedora Release Engineering <releng(a)fedoraproject.org> - 1.11.0-14
- Rebuilt for https://fedoraproject.org/wiki/Fedora_34_Mass_Rebuild
commit 3d52b904785970205228eea38b1f59eab1e5f7e8
Author: Benjamin A. Beasley <code(a)musicinmybrain.net>
Date: Sat Apr 3 09:18:27 2021 -0400
Rearrange BR’s, and drop gcc (implied by gcc-c++)
diff --git a/teem.spec b/teem.spec
index 31cd485..5f11edb 100644
--- a/teem.spec
+++ b/teem.spec
@@ -12,12 +12,12 @@ Source0: https://downloads.sourceforge.net/project/teem/teem/%{version}/%
Patch0: lib_install_dir.patch
BuildRequires: gcc-c++
-BuildRequires: gcc
BuildRequires: cmake3
+
+BuildRequires: zlib-devel
+BuildRequires: libpng-devel
BuildRequires: bzip2-devel
BuildRequires: fftw-devel
-BuildRequires: libpng-devel
-BuildRequires: zlib-devel
%global common_description %{expand:
What Is Teem?
commit b4222b2431c0b4975ed3ddbc8bf009cd7af182b3
Author: Benjamin A. Beasley <code(a)musicinmybrain.net>
Date: Sat Apr 3 09:15:50 2021 -0400
Support EPEL7
diff --git a/teem.spec b/teem.spec
index a6f7829..31cd485 100644
--- a/teem.spec
+++ b/teem.spec
@@ -13,7 +13,7 @@ Patch0: lib_install_dir.patch
BuildRequires: gcc-c++
BuildRequires: gcc
-BuildRequires: cmake
+BuildRequires: cmake3
BuildRequires: bzip2-devel
BuildRequires: fftw-devel
BuildRequires: libpng-devel
@@ -92,15 +92,15 @@ applications that use Teem.
%build
-%cmake \
+%cmake3 \
-DCMAKE_SKIP_INSTALL_RPATH=ON \
-DTeem_USE_LIB_INSTALL_SUBDIR=ON \
-DTeem_FFTW3=ON
-%cmake_build
+%cmake3_build
%install
-%cmake_install
+%cmake3_install
%check
@@ -108,11 +108,13 @@ applications that use Teem.
# seems that computation accuracy is arch-dependant
%global _smp_mflags -j1
%ifarch x86_64
-%ctest
+%ctest3
%endif
+%if 0%{?epel} == 7
%ldconfig_scriptlets libs
+%endif
%files
@@ -139,6 +141,7 @@ applications that use Teem.
- Improved package summaries and descriptions
- Adjust whitespace to personal preference
- Fix stray line continuation backslash
+- Support EPEL7
* Wed Jan 27 2021 Fedora Release Engineering <releng(a)fedoraproject.org> - 1.11.0-14
- Rebuilt for https://fedoraproject.org/wiki/Fedora_34_Mass_Rebuild
commit 59bc546adcb1c818b1406486ff585c265ec7d975
Author: Benjamin A. Beasley <code(a)musicinmybrain.net>
Date: Sat Apr 3 09:06:20 2021 -0400
Fix stray line continuation backslash
diff --git a/teem.spec b/teem.spec
index 96ea77c..a6f7829 100644
--- a/teem.spec
+++ b/teem.spec
@@ -95,7 +95,7 @@ applications that use Teem.
%cmake \
-DCMAKE_SKIP_INSTALL_RPATH=ON \
-DTeem_USE_LIB_INSTALL_SUBDIR=ON \
- -DTeem_FFTW3=ON \
+ -DTeem_FFTW3=ON
%cmake_build
@@ -138,6 +138,7 @@ applications that use Teem.
- Update URLs from HTTP to HTTPS
- Improved package summaries and descriptions
- Adjust whitespace to personal preference
+- Fix stray line continuation backslash
* Wed Jan 27 2021 Fedora Release Engineering <releng(a)fedoraproject.org> - 1.11.0-14
- Rebuilt for https://fedoraproject.org/wiki/Fedora_34_Mass_Rebuild
commit 1a749c45beb6a651404240b06296b5561d55e673
Author: Benjamin A. Beasley <code(a)musicinmybrain.net>
Date: Sat Apr 3 09:01:56 2021 -0400
Adjust whitespace to personal preference
diff --git a/teem.spec b/teem.spec
index 8f24e62..96ea77c 100644
--- a/teem.spec
+++ b/teem.spec
@@ -1,23 +1,23 @@
-Name: teem
-Version: 1.11.0
-Release: 15%{?dist}
-Summary: Libraries for processing and visualizing scientific raster data
-
-License: LGPLv2+
-URL: https://teem.sourceforge.net
-Source0: https://downloads.sourceforge.net/project/teem/teem/%{version}/%{name}-%{...
+Name: teem
+Version: 1.11.0
+Release: 15%{?dist}
+Summary: Libraries for processing and visualizing scientific raster data
+
+License: LGPLv2+
+URL: https://teem.sourceforge.net
+Source0: https://downloads.sourceforge.net/project/teem/teem/%{version}/%{name}-%{...
# Patch CMakeLists.txt to fix library install paths to match what Fedora
# expects. It is not immediately obvious if this patch is suitable for
# sending upstream or not.
-Patch0: lib_install_dir.patch
+Patch0: lib_install_dir.patch
BuildRequires: gcc-c++
-BuildRequires: gcc
-BuildRequires: cmake
-BuildRequires: bzip2-devel
-BuildRequires: fftw-devel
-BuildRequires: libpng-devel
-BuildRequires: zlib-devel
+BuildRequires: gcc
+BuildRequires: cmake
+BuildRequires: bzip2-devel
+BuildRequires: fftw-devel
+BuildRequires: libpng-devel
+BuildRequires: zlib-devel
%global common_description %{expand:
What Is Teem?
@@ -66,6 +66,7 @@ Strengths of Teem
The %{name} package contains the command-line tools included with Teem.
+
%package libs
Summary: Libraries files for %{name}
@@ -74,10 +75,11 @@ Summary: Libraries files for %{name}
The %{name}-libs package contains libraries that may be required at runtime by
applications that use Teem.
+
%package devel
Summary: Development files for %{name}
-Requires: %{name}-libs%{?_isa} = %{version}-%{release}
-Requires: cmake%{?_isa}
+Requires: %{name}-libs%{?_isa} = %{version}-%{release}
+Requires: cmake%{?_isa}
%description devel %{common_description}
@@ -100,6 +102,7 @@ applications that use Teem.
%install
%cmake_install
+
%check
# tests fail on 32-bit arches
# seems that computation accuracy is arch-dependant
@@ -108,16 +111,20 @@ applications that use Teem.
%ctest
%endif
+
%ldconfig_scriptlets libs
+
%files
%doc README.txt
%{_bindir}/*
+
%files libs
%license LICENSE.txt
%{_libdir}/libteem.so.*
+
%files devel
%doc Examples/sanity/
%{_includedir}/teem/
@@ -130,6 +137,7 @@ applications that use Teem.
- Fix typo in lib_install_dir.patch
- Update URLs from HTTP to HTTPS
- Improved package summaries and descriptions
+- Adjust whitespace to personal preference
* Wed Jan 27 2021 Fedora Release Engineering <releng(a)fedoraproject.org> - 1.11.0-14
- Rebuilt for https://fedoraproject.org/wiki/Fedora_34_Mass_Rebuild
commit 68faf7af85f2054b1476dc60655e208092e76cac
Author: Benjamin A. Beasley <code(a)musicinmybrain.net>
Date: Sat Apr 3 09:00:17 2021 -0400
Improved package summaries and descriptions
diff --git a/teem.spec b/teem.spec
index 04a9438..8f24e62 100644
--- a/teem.spec
+++ b/teem.spec
@@ -1,7 +1,7 @@
Name: teem
Version: 1.11.0
Release: 15%{?dist}
-Summary: Libraries for scientific raster data processing and visualizing
+Summary: Libraries for processing and visualizing scientific raster data
License: LGPLv2+
URL: https://teem.sourceforge.net
@@ -19,41 +19,70 @@ BuildRequires: fftw-devel
BuildRequires: libpng-devel
BuildRequires: zlib-devel
-%description
-Teem is a coordinated group of libraries for representing, processing,
-and visualizing scientific raster data. Teem includes command-line tools
-that permit the library functions to be quickly applied to files and streams,
-without having to write any code. The most important and useful libraries
-in Teem are:
-
-- Nrrd (and the unu command-line tool on top of it) supports a range
- of operations for transforming N-dimensional raster data (resample, crop,
- slice, project, histogram, etc.), as well as the NRRD file format
- for storing arrays and their meta-information.
-- Gage: fast convolution-based measurements at arbitrary point locations
- in volume datasets (scalar, vector, tensor, etc.)
-- Mite: a multi-threaded ray-casting volume render with transfer functions
- based on any quantity Gage can measure
-- Ten: for estimating, processing, and visualizing diffusion tensor fields,
- including fiber tractography methods.
-
+%global common_description %{expand:
+What Is Teem?
+
+Teem is a coordinated group of libraries for representing, processing, and
+visualizing scientific raster data. Teem includes command-line tools that
+permit the library functions to be quickly applied to files and streams,
+without having to write any code. The most important and useful libraries in
+Teem are:
+
+ • Nrrd (and the unu command-line tool on top of it) supports a range of
+ operations for transforming N-dimensional raster data (resample, crop,
+ slice, project, histogram, etc.), as well as the NRRD file format for
+ storing arrays and their meta-information.
+ • Gage: fast convolution-based measurements at arbitrary point locations in
+ volume datasets (scalar, vector, tensor, etc.)
+ • Mite: a multi-threaded ray-casting volume render with transfer functions
+ based on any quantity Gage can measure
+ • Ten: for estimating, processing, and visualizing diffusion tensor fields,
+ including fiber tractography methods.
+
+Strengths of Teem
+
+ • Teem works: Its purpose is to enable research in visualization and image
+ processing, and research is enabled when simple things are simple to do.
+ Teem’s functionality and its ease of use have allowed it to become a
+ component of larger research software projects, such as SCIRun and 3D
+ Slicer.
+ • Teem is light-weight: The libraries are written with an eye towards
+ minimizing the annoyance of getting data in an out, by using the simplest
+ possible constructs for the job, and by supporting combinations of operations
+ that arise in common practice.
+ • Teem is coherent: There is a consistent logic to how information is
+ represented, and uniformity in the APIs across libraries.
+ • Teem is portable: All the code is written in plain ANSI C, so it compiles
+ everywhere, including Windows, using either CMake or GNU Make. A Dashboard
+ is used to monitor compiler errors and warnings.
+ • Teem is growing: Some Teem libraries were created years ago and have
+ remained stable, but new libraries and new functionality are continually
+ being added.
+ • Teem is open source: Anyone can use it, and contributions are welcome. Teem
+ is licensed under the GNU Lesser General Public License, plus exceptions
+ which facilitate linking Teem into binary-only applications.}
+
+%description %{common_description}
+
+The %{name} package contains the command-line tools included with Teem.
%package libs
-Summary: Libraries for scientific raster data processing and visualizing
+Summary: Libraries files for %{name}
+
+%description libs %{common_description}
-%description libs
-Teem is a coordinated group of libraries for representing, processing,
-and visualizing scientific raster data.
+The %{name}-libs package contains libraries that may be required at runtime by
+applications that use Teem.
%package devel
-Summary: Libraries for scientific raster data processing and visualizing
+Summary: Development files for %{name}
Requires: %{name}-libs%{?_isa} = %{version}-%{release}
Requires: cmake%{?_isa}
-%description devel
-Teem is a coordinated group of libraries for representing, processing,
-and visualizing scientific raster data. This package contains development
-files.
+%description devel %{common_description}
+
+The %{name}-devel package contains libraries and header files for developing
+applications that use Teem.
%prep
@@ -100,6 +129,7 @@ files.
* Sat Apr 03 2021 Benjamin A. Beasley <code(a)musicinmybrain.net> - 1.11.0-15
- Fix typo in lib_install_dir.patch
- Update URLs from HTTP to HTTPS
+- Improved package summaries and descriptions
* Wed Jan 27 2021 Fedora Release Engineering <releng(a)fedoraproject.org> - 1.11.0-14
- Rebuilt for https://fedoraproject.org/wiki/Fedora_34_Mass_Rebuild
commit 45277db33360db4931556c3c678c8df5a42d11f9
Author: Benjamin A. Beasley <code(a)musicinmybrain.net>
Date: Sat Apr 3 08:50:07 2021 -0400
Update hash in sources file from MD5 to SHA-512
diff --git a/sources b/sources
index f09a911..4a5f2e5 100644
--- a/sources
+++ b/sources
@@ -1 +1 @@
-6b9737e8b7640e18eaf281e830fe59d1 teem-1.11.0-src.tar.gz
+SHA512 (teem-1.11.0-src.tar.gz) = 48b171a12db0f02dcfdaa87aa84464c651d661fa66201dc966b3cd5a8134c5bad1dad8987ffcc5d7c21c5d14c2eb617d48200410a1bda19008ef743c093ed575
commit ea786f8048639405a91a5ebb7ef9ed932b514674
Author: Benjamin A. Beasley <code(a)musicinmybrain.net>
Date: Sat Apr 3 08:48:37 2021 -0400
Update URLs from HTTP to HTTPS
diff --git a/teem.spec b/teem.spec
index 272c4b9..04a9438 100644
--- a/teem.spec
+++ b/teem.spec
@@ -4,8 +4,8 @@ Release: 15%{?dist}
Summary: Libraries for scientific raster data processing and visualizing
License: LGPLv2+
-URL: http://teem.sourceforge.net
-Source0: http://downloads.sourceforge.net/project/teem/teem/%{version}/%{name}-%{v...
+URL: https://teem.sourceforge.net
+Source0: https://downloads.sourceforge.net/project/teem/teem/%{version}/%{name}-%{...
# Patch CMakeLists.txt to fix library install paths to match what Fedora
# expects. It is not immediately obvious if this patch is suitable for
# sending upstream or not.
@@ -99,6 +99,7 @@ files.
%changelog
* Sat Apr 03 2021 Benjamin A. Beasley <code(a)musicinmybrain.net> - 1.11.0-15
- Fix typo in lib_install_dir.patch
+- Update URLs from HTTP to HTTPS
* Wed Jan 27 2021 Fedora Release Engineering <releng(a)fedoraproject.org> - 1.11.0-14
- Rebuilt for https://fedoraproject.org/wiki/Fedora_34_Mass_Rebuild
commit 5e008cd008b4422253f4aa2edc846e49efcb41cb
Author: Benjamin A. Beasley <code(a)musicinmybrain.net>
Date: Sat Apr 3 08:48:27 2021 -0400
Add a comment about lib_install_dir.patch
diff --git a/teem.spec b/teem.spec
index 6c490a4..272c4b9 100644
--- a/teem.spec
+++ b/teem.spec
@@ -6,6 +6,9 @@ Summary: Libraries for scientific raster data processing and visualizing
License: LGPLv2+
URL: http://teem.sourceforge.net
Source0: http://downloads.sourceforge.net/project/teem/teem/%{version}/%{name}-%{v...
+# Patch CMakeLists.txt to fix library install paths to match what Fedora
+# expects. It is not immediately obvious if this patch is suitable for
+# sending upstream or not.
Patch0: lib_install_dir.patch
BuildRequires: gcc-c++
commit 427091f9585922ee66a43470d65ea1a17726f334
Author: Benjamin A. Beasley <code(a)musicinmybrain.net>
Date: Sat Apr 3 08:35:58 2021 -0400
Fix typo in lib_install_dir.patch
diff --git a/lib_install_dir.patch b/lib_install_dir.patch
index ba4a5be..c39fed0 100644
--- a/lib_install_dir.patch
+++ b/lib_install_dir.patch
@@ -6,7 +6,7 @@ Index: teem-1.11.0-src/CMakeLists.txt
# RPATH stuff, and mac will ingore the INSTALL_RPATH. Make sure for
# macs to set BUILD_WITH_INSTALL_RPATH OFF and set INSTALL_NAME_DIR.
# I belive INSTALL_NAME_DIR will be ignored on linux.
-+SET(LIB_INSTALL_DIR ${CMAKE_INSTALL_PREFIX}/lib CACHE PATH "Lirary installation directory.")
++SET(LIB_INSTALL_DIR ${CMAKE_INSTALL_PREFIX}/lib CACHE PATH "Library installation directory.")
SET_TARGET_PROPERTIES(teem PROPERTIES
BUILD_WITH_INSTALL_RPATH OFF
- INSTALL_RPATH ${CMAKE_INSTALL_PREFIX}/lib
diff --git a/teem.spec b/teem.spec
index 77f8aa7..6c490a4 100644
--- a/teem.spec
+++ b/teem.spec
@@ -1,6 +1,6 @@
Name: teem
Version: 1.11.0
-Release: 14%{?dist}
+Release: 15%{?dist}
Summary: Libraries for scientific raster data processing and visualizing
License: LGPLv2+
@@ -94,6 +94,9 @@ files.
%changelog
+* Sat Apr 03 2021 Benjamin A. Beasley <code(a)musicinmybrain.net> - 1.11.0-15
+- Fix typo in lib_install_dir.patch
+
* Wed Jan 27 2021 Fedora Release Engineering <releng(a)fedoraproject.org> - 1.11.0-14
- Rebuilt for https://fedoraproject.org/wiki/Fedora_34_Mass_Rebuild
commit 6fa79ed60a962b9e204a527bfb1f2de902914f9a
Author: Fedora Release Engineering <releng(a)fedoraproject.org>
Date: Wed Jan 27 21:44:00 2021 +0000
- Rebuilt for https://fedoraproject.org/wiki/Fedora_34_Mass_Rebuild
Signed-off-by: Fedora Release Engineering <releng(a)fedoraproject.org>
diff --git a/teem.spec b/teem.spec
index 2ceb967..77f8aa7 100644
--- a/teem.spec
+++ b/teem.spec
@@ -1,6 +1,6 @@
Name: teem
Version: 1.11.0
-Release: 13%{?dist}
+Release: 14%{?dist}
Summary: Libraries for scientific raster data processing and visualizing
License: LGPLv2+
@@ -94,6 +94,9 @@ files.
%changelog
+* Wed Jan 27 2021 Fedora Release Engineering <releng(a)fedoraproject.org> - 1.11.0-14
+- Rebuilt for https://fedoraproject.org/wiki/Fedora_34_Mass_Rebuild
+
* Fri Sep 04 2020 Ankur Sinha <ankursinha AT fedoraproject DOT org> - 1.11.0-13
- Use cmake macros to fix build
2 years, 9 months
Architecture specific change in rpms/teem.git
by githook-noreply@fedoraproject.org
The package rpms/teem.git has added or updated architecture specific content in its
spec file (ExclusiveArch/ExcludeArch or %ifarch/%ifnarch) in commit(s):
https://src.fedoraproject.org/cgit/rpms/teem.git/commit/?id=593f4aa56a75b....
Change:
+%ifarch %{arm64} ppc64le s390x
Thanks.
Full change:
============
commit 78c8bfa63ee9200e45b439952cccee2508142cdc
Author: Benjamin A. Beasley <code(a)musicinmybrain.net>
Date: Sat Aug 21 11:07:13 2021 -0400
Remove EPEL workarounds from Fedora spec file
diff --git a/teem.spec b/teem.spec
index a7f8e03..1913581 100644
--- a/teem.spec
+++ b/teem.spec
@@ -141,7 +141,7 @@ Source961: unu-save.1
Patch0: lib_install_dir.patch
BuildRequires: gcc-c++
-BuildRequires: cmake3
+BuildRequires: cmake
BuildRequires: make
BuildRequires: zlib-devel
@@ -247,16 +247,16 @@ rm -rvf python
%build
-%cmake3 \
+%cmake \
-DCMAKE_SKIP_INSTALL_RPATH=ON \
-DTeem_USE_LIB_INSTALL_SUBDIR=ON \
-DTeem_FFTW3=ON \
-DTeem_LEVMAR=ON
-%cmake3_build
+%cmake_build
%install
-%cmake3_install
+%cmake_install
%if %{with man_pages}
install -d %{buildroot}%{_mandir}/man1
@@ -296,12 +296,7 @@ install -t %{buildroot}%{_mandir}/man1 -p -m 0644 \
# Tests are not parallel-safe
%global _smp_mflags -j1
-%ctest3 %{?ctest_excludes}
-
-
-%if 0%{?epel} == 7
-%ldconfig_scriptlets libs
-%endif
+%ctest %{?ctest_excludes}
%files
commit 2d230660a820d87784de6faa0fa2c9c025de4659
Author: Benjamin A. Beasley <code(a)musicinmybrain.net>
Date: Sat Aug 21 11:04:34 2021 -0400
Finish complete set of man pages (adds unu-* man pages)
diff --git a/ilk.1 b/ilk.1
index e88f365..ec96d51 100644
--- a/ilk.1
+++ b/ilk.1
@@ -33,7 +33,7 @@ Applies linear (homogenous coordinate) transforms to a given image, using the
given kernel for resampling.
.SH OPTIONS
.TP
-.IR @file \ ...
+.IR @file \ ...\:
response file(s) containing command-line arguments
.TP
.B \-i\fR <\fIimage\fR>
diff --git a/miter.1 b/miter.1
index 254f8b8..c95895c 100644
--- a/miter.1
+++ b/miter.1
@@ -79,7 +79,7 @@ miter \- a simple but effective little volume renderer
.RI < filename >]
.SH OPTIONS
.TP
-.IR @file \ ...
+.IR @file \ ...\:
response file(s) containing command-line arguments
.TP
.B \-i\fR <\fInsin\fR>
diff --git a/mrender.1 b/mrender.1
index c58da9e..b263ee1 100644
--- a/mrender.1
+++ b/mrender.1
@@ -69,7 +69,7 @@ in the output
image.
.SH OPTIONS
.TP
-.IR @file \ ...
+.IR @file \ ...\:
response file(s) containing command-line arguments
.TP
.B \-i\fR <\fInin\fR>
diff --git a/puller.1 b/puller.1
index 45b7635..ec9f9cf 100644
--- a/puller.1
+++ b/puller.1
@@ -139,7 +139,7 @@ puller \- command-line interface to the \(lqpull\(rq library
.RI < base >]
.SH OPTIONS
.TP
-.IR @file \ ...
+.IR @file \ ...\:
response file(s) containing command-line arguments
.TP
.B \-int\fR <\fIint\fR>
diff --git a/teem.spec b/teem.spec
index aded044..a7f8e03 100644
--- a/teem.spec
+++ b/teem.spec
@@ -4,7 +4,8 @@ Version: 1.11.0
Release: %autorelease -b 13
Summary: Libraries for processing and visualizing scientific raster data
-# The entire source is LGPLv2+, except files noted at the License fields of certain subpackages.
+# The entire source is LGPLv2+, except files noted at the License fields of
+# certain subpackages.
License: LGPLv2+
# Additionally, the following are removed in %%prep to assert that they do not
# contribute to the built package:
@@ -71,68 +72,68 @@ Source837: tend-shrink.1
Source838: tend-bfit.1
Source839: tend-satin.1
-#Source900: unu-about.1
-#Source901: unu-env.1
-#Source902: unu-i2w.1
-#Source903: unu-w2i.1
-#Source904: unu-make.1
-#Source905: unu-head.1
-#Source906: unu-data.1
-#Source907: unu-convert.1
-#Source908: unu-resample.1
-#Source909: unu-fft.1
-#Source910: unu-cmedian.1
-#Source911: unu-dist.1
-#Source912: unu-minmax.1
-#Source913: unu-cksum.1
-#Source914: unu-diff.1
-#Source915: unu-quantize.1
-#Source916: unu-unquantize.1
-#Source917: unu-project.1
-#Source918: unu-slice.1
-#Source919: unu-sselect.1
-#Source920: unu-dice.1
-#Source921: unu-splice.1
-#Source922: unu-join.1
-#Source923: unu-crop.1
-#Source924: unu-acrop.1
-#Source925: unu-inset.1
-#Source926: unu-pad.1
-#Source927: unu-reshape.1
-#Source928: unu-permute.1
-#Source929: unu-swap.1
-#Source930: unu-shuffle.1
-#Source931: unu-flip.1
-#Source932: unu-unorient.1
-#Source933: unu-axinfo.1
-#Source934: unu-axinsert.1
-#Source935: unu-axsplit.1
-#Source936: unu-axdelete.1
-#Source937: unu-axmerge.1
-#Source938: unu-tile.1
-#Source939: unu-untile.1
-#Source940: unu-histo.1
-#Source941: unu-dhisto.1
-#Source942: unu-jhisto.1
-#Source943: unu-histax.1
-#Source944: unu-heq.1
-#Source945: unu-gamma.1
-#Source946: unu-1op.1
-#Source947: unu-2op.1
-#Source948: unu-3op.1
-#Source949: unu-affine.1
-#Source950: unu-lut.1
-#Source951: unu-mlut.1
-#Source952: unu-subst.1
-#Source953: unu-rmap.1
-#Source954: unu-mrmap.1
-#Source955: unu-imap.1
-#Source956: unu-lut2.1
-#Source957: unu-ccfind.1
-#Source958: unu-ccadj.1
-#Source959: unu-ccmerge.1
-#Source960: unu-ccsettle.1
-#Source961: unu-save.1
+Source900: unu-about.1
+Source901: unu-env.1
+Source902: unu-i2w.1
+Source903: unu-w2i.1
+Source904: unu-make.1
+Source905: unu-head.1
+Source906: unu-data.1
+Source907: unu-convert.1
+Source908: unu-resample.1
+Source909: unu-fft.1
+Source910: unu-cmedian.1
+Source911: unu-dist.1
+Source912: unu-minmax.1
+Source913: unu-cksum.1
+Source914: unu-diff.1
+Source915: unu-quantize.1
+Source916: unu-unquantize.1
+Source917: unu-project.1
+Source918: unu-slice.1
+Source919: unu-sselect.1
+Source920: unu-dice.1
+Source921: unu-splice.1
+Source922: unu-join.1
+Source923: unu-crop.1
+Source924: unu-acrop.1
+Source925: unu-inset.1
+Source926: unu-pad.1
+Source927: unu-reshape.1
+Source928: unu-permute.1
+Source929: unu-swap.1
+Source930: unu-shuffle.1
+Source931: unu-flip.1
+Source932: unu-unorient.1
+Source933: unu-axinfo.1
+Source934: unu-axinsert.1
+Source935: unu-axsplit.1
+Source936: unu-axdelete.1
+Source937: unu-axmerge.1
+Source938: unu-tile.1
+Source939: unu-untile.1
+Source940: unu-histo.1
+Source941: unu-dhisto.1
+Source942: unu-jhisto.1
+Source943: unu-histax.1
+Source944: unu-heq.1
+Source945: unu-gamma.1
+Source946: unu-1op.1
+Source947: unu-2op.1
+Source948: unu-3op.1
+Source949: unu-affine.1
+Source950: unu-lut.1
+Source951: unu-mlut.1
+Source952: unu-subst.1
+Source953: unu-rmap.1
+Source954: unu-mrmap.1
+Source955: unu-imap.1
+Source956: unu-lut2.1
+Source957: unu-ccfind.1
+Source958: unu-ccadj.1
+Source959: unu-ccmerge.1
+Source960: unu-ccsettle.1
+Source961: unu-save.1
# Patch CMakeLists.txt to fix library install paths to match what Fedora
# expects. It is not immediately obvious if this patch is suitable for
@@ -269,20 +270,20 @@ install -t %{buildroot}%{_mandir}/man1 -p -m 0644 \
%{SOURCE820} %{SOURCE821} %{SOURCE822} %{SOURCE823} %{SOURCE824} \
%{SOURCE825} %{SOURCE826} %{SOURCE827} %{SOURCE828} %{SOURCE829} \
%{SOURCE830} %{SOURCE831} %{SOURCE832} %{SOURCE833} %{SOURCE834} \
- %{SOURCE835} %{SOURCE836} %{SOURCE837} %{SOURCE838} %{SOURCE839}
- #{SOURCE900} #{SOURCE901} #{SOURCE902} #{SOURCE903} #{SOURCE904} \
- #{SOURCE905} #{SOURCE906} #{SOURCE907} #{SOURCE908} #{SOURCE909} \
- #{SOURCE910} #{SOURCE911} #{SOURCE912} #{SOURCE913} #{SOURCE914} \
- #{SOURCE915} #{SOURCE916} #{SOURCE917} #{SOURCE918} #{SOURCE919} \
- #{SOURCE920} #{SOURCE921} #{SOURCE922} #{SOURCE923} #{SOURCE924} \
- #{SOURCE925} #{SOURCE926} #{SOURCE927} #{SOURCE928} #{SOURCE929} \
- #{SOURCE930} #{SOURCE931} #{SOURCE932} #{SOURCE933} #{SOURCE934} \
- #{SOURCE935} #{SOURCE936} #{SOURCE937} #{SOURCE938} #{SOURCE939} \
- #{SOURCE940} #{SOURCE941} #{SOURCE942} #{SOURCE943} #{SOURCE944} \
- #{SOURCE945} #{SOURCE946} #{SOURCE947} #{SOURCE948} #{SOURCE949} \
- #{SOURCE950} #{SOURCE951} #{SOURCE952} #{SOURCE953} #{SOURCE954} \
- #{SOURCE955} #{SOURCE956} #{SOURCE957} #{SOURCE958} #{SOURCE959} \
- #{SOURCE960} #{SOURCE961}
+ %{SOURCE835} %{SOURCE836} %{SOURCE837} %{SOURCE838} %{SOURCE839} \
+ %{SOURCE900} %{SOURCE901} %{SOURCE902} %{SOURCE903} %{SOURCE904} \
+ %{SOURCE905} %{SOURCE906} %{SOURCE907} %{SOURCE908} %{SOURCE909} \
+ %{SOURCE910} %{SOURCE911} %{SOURCE912} %{SOURCE913} %{SOURCE914} \
+ %{SOURCE915} %{SOURCE916} %{SOURCE917} %{SOURCE918} %{SOURCE919} \
+ %{SOURCE920} %{SOURCE921} %{SOURCE922} %{SOURCE923} %{SOURCE924} \
+ %{SOURCE925} %{SOURCE926} %{SOURCE927} %{SOURCE928} %{SOURCE929} \
+ %{SOURCE930} %{SOURCE931} %{SOURCE932} %{SOURCE933} %{SOURCE934} \
+ %{SOURCE935} %{SOURCE936} %{SOURCE937} %{SOURCE938} %{SOURCE939} \
+ %{SOURCE940} %{SOURCE941} %{SOURCE942} %{SOURCE943} %{SOURCE944} \
+ %{SOURCE945} %{SOURCE946} %{SOURCE947} %{SOURCE948} %{SOURCE949} \
+ %{SOURCE950} %{SOURCE951} %{SOURCE952} %{SOURCE953} %{SOURCE954} \
+ %{SOURCE955} %{SOURCE956} %{SOURCE957} %{SOURCE958} %{SOURCE959} \
+ %{SOURCE960} %{SOURCE961}
%endif
@@ -327,7 +328,7 @@ install -t %{buildroot}%{_mandir}/man1 -p -m 0644 \
%{_mandir}/man1/tend.1*
%{_mandir}/man1/tend-*.1*
%{_mandir}/man1/unu.1*
-#{_mandir}/man1/unu-*.1*
+%{_mandir}/man1/unu-*.1*
%{_mandir}/man1/vprobe.1*
%endif
diff --git a/tend-bfit.1 b/tend-bfit.1
index 9269f75..3e72390 100644
--- a/tend-bfit.1
+++ b/tend-bfit.1
@@ -27,7 +27,7 @@ to the range of DWI values along input axis 0, as a function of changing
values.
.SH OPTIONS
.TP
-.IR @file \ ...
+.IR @file \ ...\:
response file(s) containing command-line arguments
.TP
.B \-i\fR <\fInin\fR>
diff --git a/tend-glyph.1 b/tend-glyph.1
index c0ee791..5f8d28a 100644
--- a/tend-glyph.1
+++ b/tend-glyph.1
@@ -124,7 +124,7 @@ and/or
.RB \(lq unu\ quantize\ \-b\ 8 \(rq.
.SH OPTIONS
.TP
-.IR @file \ ...
+.IR @file \ ...\:
response file(s) containing command-line arguments
.TP
.B \-rt
diff --git a/tend-unmf.1 b/tend-unmf.1
index d97b5ce..c2fefd6 100644
--- a/tend-unmf.1
+++ b/tend-unmf.1
@@ -18,7 +18,9 @@ world space, and remove the measurement frame from the
.B \-i\fR <\fInin\fR>
input diffusion tensor volume (sorry, can\(cqt use usual default of
.RB \(lq \- \(rq
-for stdin because of hest quirk)
+for
+.BR stdin (3)
+because of hest quirk)
.TP
.B \-o\fR <\fInout\fR>
output tensor volume
diff --git a/tend.1 b/tend.1
index 831950d..0dc61a9 100644
--- a/tend.1
+++ b/tend.1
@@ -4,7 +4,7 @@ tend \- diffusion image processing and analysis
.SH SYNOPSIS
.B tend
.I COMMAND
-.RI [ OPTION ]...
+.RI [ OPTION ]...\:
.SH DESCRIPTION
.BR tend (1)
is a command-line interface to much of the functionality in \(lqten\(rq, a C
diff --git a/unu-1op.1 b/unu-1op.1
new file mode 100644
index 0000000..9a7fd67
--- /dev/null
+++ b/unu-1op.1
@@ -0,0 +1,177 @@
+.TH UNU\-1OP "1" "August 2021" "" "User Commands"
+.SH NAME
+unu\-1op \- unary operation on a
+.I nrrd
+.SH SYNOPSIS
+.B unu\ 1op
+.RI < operator >
+.RB [ \-s , \-\-seed
+.RI < seed >]
+.RB [ \-t , \-\-type
+.RI < type >]
+.RB [ \-i , \-\-input
+.RI < nin >]
+.RB [ \-o , \-\-output
+.RI < nout >]
+.SH DESCRIPTION
+Unary operation on a
+.IR nrrd .
+.IP \(bu +2n
+Uses
+.B nrrdArithUnaryOp
+.SH OPTIONS
+.TP
+.I operator
+Unary operator.
+Possibilities include:
+.RS +7n
+.IP \(bu 2
+.RB \(lq \- \(rq:
+negative (multiply by
+.BR \-1.0 )
+.IP \(bu 2
+.RB \(lq r \(rq:
+reciprocal
+.RB ( 1.0 / \fIvalue )
+.IP \(bu 2
+.RB \(lq sin \(rq,
+.RB \(lq cos \(rq,
+.RB \(lq tan \(rq,
+.RB \(lq asin \(rq,
+.RB \(lq acos \(rq,
+.RB \(lq atan \(rq:
+same as in C
+.IP \(bu 2
+.RB \(lq exp \(rq,
+.RB \(lq log \(rq,
+.RB \(lq log10 \(rq:
+same as in C
+.IP \(bu 2
+.RB \(lq log1p \(rq,
+.RB \(lq expm1 \(rq:
+accurate
+.BI log( x +1)
+and
+.BI exp( x )\-1
+.IP \(bu 2
+.RB \(lq log2 \(rq:
+log base 2
+.IP \(bu 2
+.RB \(lq sqrt \(rq,
+.RB \(lq cbrt \(rq,
+.RB \(lq ceil \(rq,
+.RB \(lq floor \(rq:
+same as in C
+.IP \(bu 2
+.RB \(lq erf \(rq:
+error function (integral of Gaussian)
+.IP \(bu 2
+.RB \(lq rup \(rq,
+.RB \(lq rdn \(rq:
+round up or down to integral value
+.IP \(bu 2
+.RB \(lq abs \(rq:
+absolute value
+.IP \(bu 2
+.RB \(lq sgn \(rq:
+.BR \-1 ,
+.BR 0 ,
+.BR 1
+if value is
+.RB < 0 ,
+.RB == 0 ,
+or
+.RB > 0
+.IP \(bu 2
+.RB \(lq exists \(rq:
+.B 1
+iff not
+.B NaN
+or
+.BR + / \-Inf ,
+.B 0
+otherwise
+.IP \(bu 2
+.RB \(lq rand \(rq:
+random value in
+.BR [0.0,1.0) ,
+no relation to input
+.IP \(bu 2
+.RB \(lq nrand \(rq:
+random sample from normal distribution with mean
+.B 0.0
+and stdvi
+.BR 1.0 ,
+no relation to input
+.IP \(bu 2
+.RB \(lq if \(rq:
+if input is non-zero,
+.BR 1 ,
+else
+.B 0
+.IP \(bu 2
+.RB \(lq 0 \(rq:
+output always
+.B 0
+.IP \(bu 2
+.RB \(lq 1 \(rq:
+output always
+.B 1
+.RE
+.TP
+.B \-s\fR\ <\fIseed\fR>\ ,\ \fB\-\-seed\fR\ <\fIseed\fR>
+seed value for RNG for
+.B rand
+and
+.BR nrand ,
+so that you can get repeatable results between runs, or, by not using this
+option, the RNG seeding will be based on the current time
+.RI ( string )
+.TP
+.B \-t\fR\ <\fItype\fR>\ ,\ \fB\-\-type\fR\ <\fItype\fR>
+convert input
+.I nrrd
+to this type prior to doing operation.
+Useful when desired output is
+.I float
+(e.g., with
+.BR log1p ),
+but input is integral.
+By default (not using this option), the types of the input
+.IR nrrd s
+are left unchanged.
+.TP
+.B \-i\fR\ <\fInin\fR>\ ,\ \fB\-\-input\fR\ <\fInin\fR>
+input
+.I nrrd
+.TP
+.B \-o\fR\ <\fInout\fR>\ ,\ \fB\-\-output\fR\ <\fInout\fR>
+output
+.I nrrd
+.RI ( string );
+default:
+.RB \(lq \- \(rq
+.SH "SEE\ ALSO"
+.BR unu (1)
+.P
+.BR abs (3),
+.BR acos (3),
+.BR asin (3),
+.BR atan (3),
+.BR cbrt (3),
+.BR ceil (3),
+.BR cos (3),
+.BR erf (3),
+.BR exp (3),
+.BR expm1 (3),
+.BR fabs (3),
+.BR floor (3),
+.BR isfinite (3),
+.BR log (3),
+.BR log10 (3),
+.BR log1p (3),
+.BR log2 (3),
+.BR rand (3),
+.BR sin (3),
+.BR sqrt (3),
+.BR tan (3)
diff --git a/unu-2op.1 b/unu-2op.1
new file mode 100644
index 0000000..a6678fb
--- /dev/null
+++ b/unu-2op.1
@@ -0,0 +1,180 @@
+.TH UNU\-2OP "1" "August 2021" "" "User Commands"
+.SH NAME
+unu\-2op \- binary operation on two
+.IR nrrd s,
+or on a
+.I nrrd
+and a constant
+.SH SYNOPSIS
+.B unu\ 2op
+.RI < operator >
+.RI < in1 >
+.RI < in2 >
+.RB [ \-s , \-\-seed
+.RI < seed >]
+.RB [ \-t , \-\-type
+.RI < type >]
+.RB [ \-w , \-\-which
+.RI < arg >]
+.RB [ \-o , \-\-output
+.RI < nout >]
+.SH DESCRIPTION
+Binary operation on two
+.IR nrrd s,
+or on a
+.I nrrd
+and a constant.
+Either the first or second operand can be a
+.I float
+constant, but not both.
+Use
+.RB \(lq \- \(rq
+for an operand to signify a
+.I nrrd
+to be read from
+.I stdin
+(a pipe).
+Note, however, that
+.RB \(lq \- \(rq
+can probably only be used once (reliably).
+.IP \(bu +2n
+Uses
+.B nrrdArithIterBinaryOp
+or (with
+.BR \-w )
+.B nrrdArithIterBinaryOpSelect
+.SH OPTIONS
+.TP
+.I operator
+Binary operator.
+Possibilities include:
+.RS +7n
+.IP \(bu +2n
+.RB \(lq + \(rq,
+.RB \(lq \- \(rq
+.RB \(lq x \(rq
+.RB \(lq / \(rq:
+add, subtract, multiply, divide
+.IP \(bu +2n
+.RB \(lq ^ \(rq:
+exponentiation
+.RB ( pow (3))
+.IP \(bu +2n
+.RB \(lq spow \(rq:
+signed exponentiation:
+.BI sgn( x )pow(abs( x ), p )
+.IP \(bu +2n
+.RB \(lq fpow \(rq:
+like spow but with curves flipped
+.IP \(bu +2n
+.RB \(lq % \(rq:
+integer modulo
+.IP \(bu +2n
+.RB \(lq fmod \(rq:
+same as
+.BR fmod (3)
+in C
+.IP \(bu +2n
+.RB \(lq atan2 \(rq:
+same as
+.BR atan2 (3)
+in C
+.IP \(bu +2n
+.RB \(lq min \(rq
+.RB \(lq max \(rq:
+minimum, maximum
+.IP \(bu +2n
+.RB \(lq lt \(rq
+.RB \(lq lte \(rq
+.RB \(lq gt \(rq
+.RB \(lq gte \(rq:
+same as C's
+.BR < ,
+.BR <= ,
+.BR > ,
+.B <=
+.IP \(bu +2n
+.RB \(lq eq \(rq
+.RB \(lq neq \(rq:
+same as C's
+.B ==
+and
+.B !=
+.IP \(bu +2n
+.RB \(lq comp \(rq:
+.BR \-1 ,
+.BR 0 ,
+or
+.B 1
+if 1st value is less than, equal to, or greater than 2nd value
+.IP \(bu +2n
+.RB \(lq if \(rq:
+if 1st value is non-zero, use it, else use 2nd value
+.IP \(bu +2n
+.RB \(lq exists \(rq:
+if 1st value exists, use it, else use 2nd value
+.IP \(bu +2n
+.RB \(lq nrand \(rq:
+scale unit-stdv Gaussian noise by 2nd value and add to first value
+.IP \(bu +2n
+.RB \(lq rrand \(rq:
+sample Rician distribution with 1st value for \(lqtrue\(rq mean, and 2nd value
+for sigma
+.RE
+.TP
+.I in1
+First input.
+Can be a single value or a
+.IR nrrd .
+.TP
+.I in2
+Second input.
+Can be a single value or a
+.IR nrrd .
+.TP
+.B \-s\fR\ <\fIseed\fR>\ ,\ \fB\-\-seed\fR\ <\fIseed\fR>
+seed value for RNG for
+.BR nrand ,
+so that you can get repeatable results between runs, or, by not using this
+option, the RNG seeding will be based on the current time
+.RI ( string )
+.TP
+.B \-t\fR\ <\fItype\fR>\ ,\ \fB\-\-type\fR\ <\fItype\fR>
+type to convert all
+.B INPUT
+.IR nrrd s
+to, prior to doing operation, useful for doing, for instance, the difference
+between two
+.I unsigned\ char
+.IR nrrd s.
+This will also determine output type.
+By default (not using this option), the types of the input
+.IR nrrd s
+are left unchanged.
+.TP
+.B \-w\fR\ <\fIarg\fR>\ ,\ \fB\-\-which\fR\ <\fIarg\fR>
+Which argument
+.RB ( 0
+or
+.BR 1 )
+should be used to determine the shape of the output
+.IR nrrd .
+By default (not using this option), the first non-constant argument is used.
+.RI ( int );
+default:
+.RB \(lq \-1 \(rq
+.TP
+.B \-o\fR\ <\fInout\fR>\ ,\ \fB\-\-output\fR\ <\fInout\fR>
+output
+.I nrrd
+.RI ( string );
+default:
+.RB \(lq \- \(rq
+.SH "SEE\ ALSO"
+.BR unu (1)
+.P
+.BR abs (1),
+.BR atan2 (1),
+.BR fabs (1),
+.BR fmod (1),
+.BR pow (1)
diff --git a/unu-3op.1 b/unu-3op.1
new file mode 100644
index 0000000..3204054
--- /dev/null
+++ b/unu-3op.1
@@ -0,0 +1,171 @@
+.TH UNU\-3OP "1" "August 2021" "" "User Commands"
+.SH NAME
+unu\-3op \- ternary operation on three
+.IR nrrd s
+or constants
+.SH SYNOPSIS
+.B unu\ 3op
+.RI < operator >
+.RI < in1 >
+.RI < in2 >
+.RI < in3 >
+.RB [ \-t , \-\-type
+.RI < type >]
+.RB [ \-w , \-\-which
+.RI < arg >]
+.RB [ \-o , \-\-output
+.RI < nout >]
+.SH DESCRIPTION
+Ternary operation on three
+.IR nrrd s
+or constants.
+Can have one, two, or three
+.IR nrrd s,
+but not zero.
+Use
+.RB \(lq \- \(rq
+for an operand to signify a
+.I nrrd
+to be read from
+.I stdin
+(a pipe).
+Note, however, that
+.RB \(lq \- \(rq
+can probably only be used once (reliably).
+.IP \(bu +2n
+Uses
+.B nrrdArithIterTernaryOp
+or (with
+.BR \-w )
+.B nrrdArithIterTernaryOpSelect
+.SH OPTIONS
+.TP
+.I operator
+Ternary operator.
+Possibilities include:
+.RS +7n
+.IP \(bu +2n
+.RB \(lq + \(rq,
+.RB \(lq x \(rq
+sum or product of three values
+.IP \(bu +2n
+.RB \(lq min \(rq,
+.RB \(lq max \(rq
+minimum, maximum
+.IP \(bu +2n
+.RB \(lq min_sm \(rq
+smoothed minimum function;
+.BI min_sm( x ,\ w ,\ M )
+is like
+.BI min( x , M )
+but for
+.IR x \ >\ M \- w
+(with
+.IR w \ >\ \fB0 )
+there is a smooth transition from
+.I x
+to asymptotic to
+.I M
+.IP \(bu +2n
+.RB \(lq max_sm \(rq
+smoothed maximum function;
+max_sm(m, w, x)
+.BI max_sm( m ,\ w ,\ x )
+is like
+.BI max( m , x )
+but for
+.IR x \ <\ m + w
+(with
+.IR w \ >\ m )
+there is a smooth transition from
+.I x
+to asymptotic to
+.I m
+.IP \(bu +2n
+.RB \(lq lt_sm \(rq
+1st less than 3rd, smoothed by 2nd
+.IP \(bu +2n
+.RB \(lq gt_sm \(rq
+1st greater than 3rd, smoothed by 2nd
+.IP \(bu +2n
+.RB \(lq clamp \(rq
+2nd value is clamped to range between the 1st and the 3rd
+.IP \(bu +2n
+.RB \(lq ifelse \(rq
+if 1st value non-zero, then 2nd value, else 3rd value
+.IP \(bu +2n
+.RB \(lq lerp \(rq
+linear interpolation between the 2nd and 3rd values, as the 1st value varies
+between
+.B 0.0
+and
+.BR 1.0 ,
+respectively
+.IP \(bu +2n
+.RB \(lq exists \(rq
+if the 1st value exists, use the 2nd value, otherwise use the 3rd
+.IP \(bu +2n
+.RB \(lq in_op \(rq
+.B 1
+iff 2nd value is > 1st and < 3rd,
+.B 0
+otherwise
+.IP \(bu +2n
+.RB \(lq in_cl \(rq
+.B 1
+iff 2nd value is >= 1st and <= 3rd,
+.B 0
+otherwise
+.IP \(bu +2n
+.RB \(lq gauss \(rq
+evaluate (at 1st value) Gaussian with mean=2nd and stdv=3rd value
+.IP \(bu +2n
+.RB \(lq rician \(rq
+evaluate (at 1st value) Rician with mean=2nd and stdv=3rd value
+.RE
+.TP
+.I in1
+First input.
+Can be a single value or a
+.IR nrrd .
+.TP
+.I in2
+Second input.
+Can be a single value or a
+.IR nrrd .
+.TP
+.I in3
+Third input.
+Can be a single value or a
+.IR nrrd .
+.TP
+.B \-t\fR\ <\fItype\fR>\ ,\ \fB\-\-type\fR\ <\fItype\fR>
+type to convert all
+.IR nrrd
+inputs to, prior to doing operation.
+This also determines output type.
+By default (not using this option), the types of the input
+.IR nrrd s
+are left unchanged.
+.TP
+.B \-w\fR\ <\fIarg\fR>\ ,\ \fB\-\-which\fR\ <\fIarg\fR>
+Which argument
+.RB ( 0 ,
+.BR 1 ,
+or
+.BR 2 )
+should be used to determine the shape of the output
+.IR nrrd .
+By default (not using this option), the first non-constant argument is used.
+.RI ( int );
+default:
+.RB \(lq \-1 \(rq
+.TP
+.B \-o\fR\ <\fInout\fR>\ ,\ \fB\-\-output\fR\ <\fInout\fR>
+output
+.I nrrd
+.RI ( string );
+default:
+.RB \(lq \- \(rq
+.SH "SEE\ ALSO"
+.BR unu (1)
diff --git a/unu-about.1 b/unu-about.1
new file mode 100644
index 0000000..9df6bee
--- /dev/null
+++ b/unu-about.1
@@ -0,0 +1,9 @@
+.TH UNU\-ABOUT "1" "April 2021" "" "User Commands"
+.SH NAME
+unu\-about \- information about
+.BR unu (1)
+and its use
+.SH SYNOPSIS
+.B unu\ about
+.SH "SEE\ ALSO"
+.BR unu (1)
diff --git a/unu-acrop.1 b/unu-acrop.1
new file mode 100644
index 0000000..cbc595f
--- /dev/null
+++ b/unu-acrop.1
@@ -0,0 +1,115 @@
+.TH UNU\-ACROP "1" "August 2021" "" "User Commands"
+.SH NAME
+unu\-acrop \- automatically crop axes based on given measure
+.SH SYNOPSIS
+.B unu\ acrop
+.RB [ \-a , \-\-axes
+.RI [< ax0 \ ...>]]
+.BR \-m , \-\-measure
+.RI < measr >
+.RB [ \-f , \-\-frac
+.RI < frac >]
+.RB [ \-off , \-\-offset
+.RI < offset >]
+.RB [ \-b , \-\-bounds
+.RI < filename >]
+.RB [ \-i , \-\-input
+.RI < nin >]
+.RB [ \-o , \-\-output
+.RI < nout >]
+.SH DESCRIPTION
+Automatically crop axes based on given measure.
+For the axes that are to be cropped, the slices perpendicular to that axis are
+projected down to a scalar with the specified measure.
+The resulting 1\-D array is analyzed by determining what portions at the
+beginning and end constitute less than some portion of the cumulative array
+sum; these ends are cropped off.
+The cropping bounds determined here can be saved and applied to other arrays
+via the
+.RB \(lq \-b \(rq
+option.
+.IP \(bu +2n
+Uses
+.B nrrdCropAuto
+.SH OPTIONS
+.TP
+.B \-a\fR\ [<\fIax0\fR\ ...>]\ ,\ \fB\-\-axes\fR\ [<\fIax0\fR\ ...>]
+the axes (if any) that should
+.B NOT
+be cropped (0 or more
+.IR unsigned\ int s);
+default: \(lq \(rq
+.TP
+.B \-m\fR\ <\fImeasr\fR>\ ,\ \fB\-\-measure\fR\ <\fImeasr\fR>
+How to measure slices (along axes to crop) as scalars, to form 1\-D array
+analyzed to determine cropping extent.
+All the measures from
+.BR unu\-project (1)
+can be used, but those that make more sense here include:
+.RS +7n
+.IP \(bu 2
+.RB \(lq max \(rq,
+.RB \(lq mean \(rq,
+.RB \(lq median \(rq,
+.RB \(lq variance \(rq:
+(self-explanatory)
+.IP \(bu 2
+.RB \(lq stdv \(rq:
+standard deviation
+.IP \(bu 2
+.RB \(lq cov \(rq:
+coefficient of variation
+.IP \(bu 2
+.RB \(lq product \(rq,
+.RB \(lq sum \(rq:
+product or sum of all values
+.IP \(bu 2
+.RB \(lq L1 \(rq,
+.RB \(lq L2 \(rq,
+.RB \(lq NL2 \(rq,
+.RB \(lq RMS \(rq,
+.RB \(lq Linf \(rq:
+different norms
+.RE
+.TP
+.B \-f\fR\ <\fIfrac\fR>\ ,\ \fB\-\-frac\fR\ <\fIfrac\fR>
+threshold of cumulative sum of 1\-D array at which to crop.
+Needs to be in interval
+.BR [0.0,0.5) .
+.RI ( double );
+default:
+.RB \(lq 0.1 \(rq
+.TP
+.B \-off\fR\ <\fIoffset\fR>\ ,\ \fB\-\-offset\fR\ <\fIoffset\fR>
+how much to offset the numerically determined cropping; positive offsets means
+expanding the interval of kept indices (less cropping)
+.RI ( int );
+default:
+.RB \(lq 1 \(rq
+.TP
+.B \-b\fR\ <\fIfilename\fR>\ ,\ \fB\-\-bounds\fR\ <\fIfilename\fR>
+if a filename is given here, the automatically determined min and max bounds
+for cropping are saved to this file as a 2\-D array; first scanline is for
+.BR \-min ,
+second is for
+.BR \-max .
+Unfortunately nothing using the
+.RB \(lq m \(rq
+and
+.RB \(lq M \(rq
+semantics (above) can currently be saved in the bounds file.
+.RI ( string )
+.TP
+.B \-i\fR\ <\fInin\fR>\ ,\ \fB\-\-input\fR\ <\fInin\fR>
+input
+.I nrrd
+.TP
+.B \-o\fR\ <\fInout\fR>\ ,\ \fB\-\-output\fR\ <\fInout\fR>
+output
+.I nrrd
+.RI ( string );
+default:
+.RB \(lq \- \(rq
+.SH "SEE\ ALSO"
+.BR unu (1),
+.BR unu\-project (1)
diff --git a/unu-affine.1 b/unu-affine.1
new file mode 100644
index 0000000..5765707
--- /dev/null
+++ b/unu-affine.1
@@ -0,0 +1,84 @@
+.TH UNU\-AFFINE "1" "August 2021" "" "User Commands"
+.SH NAME
+unu\-affine \- affine (lerp) mapping on 5
+.IR nrrd s
+or constants
+.SH SYNOPSIS
+.B unu\ affine
+.RI < minIn >
+.RI < in >
+.RI < maxIn >
+.RI < minOut >
+.RI < maxOut >
+.RB [ \-t , \-\-type
+.RI < type >]
+.RB [ \-clamp
+.RI < bool >]
+.RB [ \-o , \-\-output
+.RI < nout >]
+.SH DESCRIPTION
+Affine (lerp) mapping on 5
+.IR nrrd s
+or constants.
+All the 5 arguments can be either
+.IR nrrd s
+or single floating-point values.
+When all args are single values, this is subsuming the functionality of the
+previous stand-alone
+.BR affine (1)
+program.
+Use
+.RB \(lq \- \(rq
+for an operand to signify a
+.I nrrd
+to be read from
+.I stdin
+(a pipe).
+Note, however, that
+.RB \(lq \- \(rq
+can probably only be used once (reliably).
+.IP \(bu +2n
+Uses
+.B nrrdArithAffine
+or
+.B nrrdArithIterAffine
+.SH OPTIONS
+.TP
+.I minIn
+Lower end of input value range.
+.TP
+.I in
+Input value.
+.TP
+.I maxIn
+Upper end of input value range.
+.TP
+.I minOut
+Lower end of output value range.
+.TP
+.I maxOut
+Upper end of output value range.
+.TP
+.B \-t\fR\ <\fItype\fR>\ ,\ \fB\-\-type\fR\ <\fItype\fR>
+type to convert all
+.IR nrrd
+inputs to, prior to doing operation.
+This also determines output type.
+By default (not using this option), the types of the input
+.IR nrrd s
+are left unchanged.
+.TP
+.B \-clamp\fR\ <\fIbool\fR>
+clamp output values to specified output range
+.RI ( bool );
+default:
+.RB \(lq false \(rq
+.TP
+.B \-o\fR\ <\fInout\fR>\ ,\ \fB\-\-output\fR\ <\fInout\fR>
+output
+.I nrrd
+.RI ( string );
+default:
+.RB \(lq \- \(rq
+.SH "SEE\ ALSO"
+.BR unu (1)
diff --git a/unu-axdelete.1 b/unu-axdelete.1
new file mode 100644
index 0000000..947c921
--- /dev/null
+++ b/unu-axdelete.1
@@ -0,0 +1,42 @@
+.TH UNU\-AXDELETE "1" "August 2021" "" "User Commands"
+.SH NAME
+unu\-axdelete \- remove one or more singleton axes from a
+.I nrrd
+.SH SYNOPSIS
+.B unu\ axdelete
+.BR \-a , \-\-axis
+.RI < axis >
+.RB [ \-i , \-\-input
+.RI < nin >]
+.RB [ \-o , \-\-output
+.RI < nout >]
+.SH DESCRIPTION
+Remove one or more singleton axes from a
+.IR nrrd .
+Singleton axes have only a single sample along them.
+The underlying linear ordering of the samples is unchanged, and the information
+about the other axes is shifted downwards as needed.
+As a total hack, if you give \-1 as the axis, this will do a matlab-style
+\(lqsqueeze\(rq, in which any and all singleton axes are removed.
+.IP \(bu +2n
+Uses
+.B nrrdAxesDelete
+.SH OPTIONS
+.TP
+.B \-a\fR\ <\fIaxis\fR>\ ,\ \fB\-\-axis\fR\ <\fIaxis\fR>
+dimension (axis index) of the axis to remove
+.RI ( unsigned\ int )
+.TP
+.B \-i\fR\ <\fInin\fR>\ ,\ \fB\-\-input\fR\ <\fInin\fR>
+input
+.I nrrd
+.TP
+.B \-o\fR\ <\fInout\fR>\ ,\ \fB\-\-output\fR\ <\fInout\fR>
+output
+.I nrrd
+.RI ( string );
+default:
+.RB \(lq \- \(rq
+.SH "SEE\ ALSO"
+.BR unu (1),
+.BR unu\-axmerge (1)
diff --git a/unu-axinfo.1 b/unu-axinfo.1
new file mode 100644
index 0000000..00dd7e9
--- /dev/null
+++ b/unu-axinfo.1
@@ -0,0 +1,80 @@
+.TH UNU\-AXINFO "1" "August 2021" "" "User Commands"
+.SH NAME
+unu\-axinfo \- modify attributes of one or more axes
+.SH SYNOPSIS
+.B unu\ axinfo
+.BR \-a , \-\-axes
+.RI < ax0 \ ax1 \ ...>
+.RB [ \-l , \-\-label
+.RI < label >]
+.RB [ \-u , \-\-units
+.RI < units >]
+.RB [ \-mm , \-\-minmax
+.RI < min \ max >]
+.RB [ \-sp , \-\-spacing
+.RI < spacing >]
+.RB [ \-c , \-\-center
+.RI < center >]
+.RB [ \-k , \-\-kind
+.RI < kind >]
+.RB [ \-i , \-\-input
+.RI < nin >]
+.RB [ \-o , \-\-output
+.RI < nout >]
+.SH DESCRIPTION
+Modify attributes of one or more axes.
+The only attributes which are set are those for which command-line options are
+given.
+.IP \(bu +2n
+Uses no particular function; just sets fields in
+.B NrrdAxisInfo
+.SH OPTIONS
+.TP
+.B \-a\fR\ <\fIax0\fR \fIax1\fR\ ...>\ ,\ \fB\-\-axes\fR\ <\fIax0\fR \fIax1\fR\ ...>
+the one or more axes that should be modified
+(1 or more
+.IR unsigned\ int s)
+.TP
+.B \-l\fR\ <\fIlabel\fR>\ ,\ \fB\-\-label\fR\ <\fIlabel\fR>
+label to associate with axis
+.RI ( string )
+.TP
+.B \-u\fR\ <\fIunits\fR>\ ,\ \fB\-\-units\fR\ <\fIunits\fR>
+units of measurement
+.RI ( string )
+.TP
+.B \-mm\fR\ <\fImin\fR\ \fImax\fR>\ ,\ \fB\-\-minmax\fR\ <\fImin\fR\ \fImax\fR>
+units of measurement
+min and max values along axis
+( 2
+.IR double s)
+.TP
+.B \-sp\fR\ <\fIspacing\fR>\ ,\ \fB\-\-spacing\fR\ <\fIspacing\fR>
+spacing between samples along axis
+.RI ( double )
+.TP
+.B \-c\fR\ <\fIcenter\fR>\ ,\ \fB\-\-center\fR\ <\fIcenter\fR>
+axis centering:
+.RB \(lq cell \(rq
+or
+.RB \(lq node \(rq.
+Not using this option leaves the centering as it is on input
+.RI ( string )
+.TP
+.B \-k\fR\ <\fIkind\fR>\ ,\ \fB\-\-kind\fR\ <\fIkind\fR>
+axis kind.
+Not using this option leaves the kind as it is on input
+.RI ( string )
+.TP
+.B \-i\fR\ <\fInin\fR>\ ,\ \fB\-\-input\fR\ <\fInin\fR>
+input
+.I nrrd
+.TP
+.B \-o\fR\ <\fInout\fR>\ ,\ \fB\-\-output\fR\ <\fInout\fR>
+output
+.I nrrd
+.RI ( string );
+default:
+.RB \(lq \- \(rq
+.SH "SEE\ ALSO"
+.BR unu (1)
diff --git a/unu-axinsert.1 b/unu-axinsert.1
new file mode 100644
index 0000000..62f1e0a
--- /dev/null
+++ b/unu-axinsert.1
@@ -0,0 +1,45 @@
+.TH UNU\-AXINSERT "1" "August 2021" "" "User Commands"
+.SH NAME
+unu\-axinsert \- add a \(lqstub\(rq (length 1) axis to a
+.I nrrd
+.SH SYNOPSIS
+.B unu\ axinsert
+.BR \-a , \-\-axis
+.RI < axis >
+.RB [ \-l , \-\-label
+.RI < label >]
+.RB [ \-i , \-\-input
+.RI < nin >]
+.RB [ \-o , \-\-output
+.RI < nout >]
+.SH DESCRIPTION
+Add a \(lqstub\(rq (length 1) axis to a
+.IR nrrd .
+The underlying linear ordering of the samples is unchanged, and the information
+about the other axes is shifted upwards as needed.
+.IP \(bu +2n
+Uses
+.B nrrdAxesInsert
+.SH OPTIONS
+.TP
+.B \-a\fR\ <\fIaxis\fR>\ ,\ \fB\-\-axes\fR\ <\fIaxis\fR>
+dimension (axis index) at which to insert the new axis
+.RI ( unsigned\ int )
+.TP
+.B \-l\fR\ <\fIlabel\fR>\ ,\ \fB\-\-label\fR\ <\fIlabel\fR>
+label to associate with axis
+.RI ( string )
+.TP
+.B \-i\fR\ <\fInin\fR>\ ,\ \fB\-\-input\fR\ <\fInin\fR>
+input
+.I nrrd
+.TP
+.B \-o\fR\ <\fInout\fR>\ ,\ \fB\-\-output\fR\ <\fInout\fR>
+output
+.I nrrd
+.RI ( string );
+default:
+.RB \(lq \- \(rq
+.SH "SEE\ ALSO"
+.BR unu (1),
+.BR unu\-axsplit (1)
diff --git a/unu-axmerge.1 b/unu-axmerge.1
new file mode 100644
index 0000000..50e8831
--- /dev/null
+++ b/unu-axmerge.1
@@ -0,0 +1,47 @@
+.TH UNU\-AXMERGE "1" "August 2021" "" "User Commands"
+.SH NAME
+unu\-axmerge \- merge two adjacent axes into one
+.SH SYNOPSIS
+.B unu\ axmerge
+.BR \-a , \-\-axis
+.RI < ax0 \ ...>
+.RB [ \-i , \-\-input
+.RI < nin >]
+.RB [ \-o , \-\-output
+.RI < nout >]
+.SH DESCRIPTION
+Merge two adjacent axes into one.
+A more general version of
+.BR unu\-axdelete (1).
+The underlying linear ordering of the samples is unchanged, and the information
+about the other axes is shifted downwards as needed.
+.IP \(bu +2n
+Uses
+.B nrrdAxesMerge
+.SH OPTIONS
+.TP
+.B \-a\fR\ <\fIax0\fR\ ...>\ ,\ \fB\-\-axis\fR\ <\fIax0\fR\ ...>
+axis (or axes) to merge.
+Each axis index identified is the lower of the pair of axes that will be
+merged.
+Saying
+.RB \(lq \-a\ 2 \(rq
+means to merge axis 2 and axis 3 into axis 2.
+If multiple merges are to be done, the indices listed here are for the axes
+prior to any merging.
+(1 or more
+.IR int s)
+.TP
+.B \-i\fR\ <\fInin\fR>\ ,\ \fB\-\-input\fR\ <\fInin\fR>
+input
+.I nrrd
+.TP
+.B \-o\fR\ <\fInout\fR>\ ,\ \fB\-\-output\fR\ <\fInout\fR>
+output
+.I nrrd
+.RI ( string );
+default:
+.RB \(lq \- \(rq
+.SH "SEE\ ALSO"
+.BR unu (1),
+.BR unu\-axdelete (1)
diff --git a/unu-axsplit.1 b/unu-axsplit.1
new file mode 100644
index 0000000..2577ae8
--- /dev/null
+++ b/unu-axsplit.1
@@ -0,0 +1,47 @@
+.TH UNU\-AXSPLIT "1" "August 2021" "" "User Commands"
+.SH NAME
+unu\-axsplit \- split one axis into two axes
+.SH SYNOPSIS
+.B unu\ axsplit
+.BR \-a , \-\-axis
+.RI < axis >
+.BR \-s , \-\-size
+.RI < fast ,\ slow \ sizes>
+.RB [ \-i , \-\-input
+.RI < nin >]
+.RB [ \-o , \-\-output
+.RI < nout >]
+.SH DESCRIPTION
+Split one axis into two axes.
+More general version of
+.BR unu\-axinsert (1),
+since a given axis can be split into fast and slow axes of arbitrary size, as
+long as the product of the fast and slow sizes is the same as the original
+size.
+.IP \(bu +2n
+Uses
+.B nrrdAxesSplit
+.SH OPTIONS
+.TP
+.B \-a\fR\ <\fIaxis\fR>\ ,\ \fB\-\-axis\fR\ <\fIaxis\fR>
+dimension (axis index) to split at
+.RI ( unsigned\ int )
+.TP
+.B \-s\fR\ <\fIfast\fR,\ \fIslow\fR\ sizes>\ ,\ \fB\-\-size\fR\ <\fIfast\fR,\ \fIslow\fR\ sizes>
+fast and slow axis sizes to produce as result of splitting given axis.
+(2
+.IR size_t s)
+.TP
+.B \-i\fR\ <\fInin\fR>\ ,\ \fB\-\-input\fR\ <\fInin\fR>
+input
+.I nrrd
+.TP
+.B \-o\fR\ <\fInout\fR>\ ,\ \fB\-\-output\fR\ <\fInout\fR>
+output
+.I nrrd
+.RI ( string );
+default:
+.RB \(lq \- \(rq
+.SH "SEE\ ALSO"
+.BR unu (1),
+.BR unu\-axinsert (1)
diff --git a/unu-ccadj.1 b/unu-ccadj.1
new file mode 100644
index 0000000..ec09d85
--- /dev/null
+++ b/unu-ccadj.1
@@ -0,0 +1,55 @@
+.TH UNU\-CCADJ "1" "August 2021" "" "User Commands"
+.SH NAME
+unu\-ccadj \- form adjacency matrix of connected components
+.SH SYNOPSIS
+.B unu\ ccadj
+.BR \-c , \-\-connect
+.RI < connectivity >
+.RB [ \-i , \-\-input
+.RI < nin >]
+.RB [ \-o , \-\-output
+.RI < nout >]
+.SH DESCRIPTION
+Form adjecency matrix of connected components.
+This operates on
+the output of
+.BR unu\-ccfind (1).
+Output is
+.I unsigned\ char
+array containing
+.B 1
+at locations
+.RI ( I , J )
+and
+.RI ( J , I )
+if CCs with ids
+.I I
+and
+.I J
+are adjacent, according to the chosen style of adjacency.
+.IP \(bu +2n
+Uses
+.B nrrdCCAdjacency
+.SH OPTIONS
+.TP
+.B \-c\fR\ <\fIconnectivity\fR>\ ,\ \fB\-\-connect\fR\ <\fIconnectivity\fR>
+what kind of connectivity to use: the number of coordinates that vary in order
+to traverse the neighborhood of a given sample.
+In 2D:
+.RB \(lq 1 \(rq:
+4-connected,
+.RB \(lq 2 \(rq:
+8-connected
+.RI ( unsigned\ int )
+.TP
+.B \-o\fR\ <\fInout\fR>\ ,\ \fB\-\-output\fR\ <\fInout\fR>
+output
+.I nrrd
+.RI ( string );
+default:
+.RB \(lq \- \(rq
+.SH "SEE\ ALSO"
+.BR unu (1),
+.BR unu\-ccfind (1),
+.BR unu\-ccmerge (1),
+.BR unu\-ccsettle (1)
diff --git a/unu-ccfind.1 b/unu-ccfind.1
new file mode 100644
index 0000000..2c4169e
--- /dev/null
+++ b/unu-ccfind.1
@@ -0,0 +1,64 @@
+.TH UNU\-CCFIND "1" "August 2021" "" "User Commands"
+.SH NAME
+unu\-ccfind \- find connected components (CCs)
+.SH SYNOPSIS
+.B unu\ ccfind
+.RB [ \-v , \-\-values
+.RI < filename >]
+.RB [ \-t , \-\-type
+.RI < type >]
+.BR \-c , \-\-connect
+.RI < connectivity >
+.RB [ \-i , \-\-input
+.RI < nin >]
+.RB [ \-o , \-\-output
+.RI < nout >]
+.SH DESCRIPTION
+Find connected components (CCs).
+This works on 1-byte and 2-byte integral values, as well as 4-byte
+.IR int s.
+.IP \(bu +2n
+Uses
+.B nrrdCCFind
+.SH OPTIONS
+.TP
+.B \-v\fR\ <\fIfilename\fR>\ ,\ \fB\-\-values\fR\ <\fIfilename\fR>
+Giving a
+.I filename
+here allows you to save out the values associated with each connect component.
+This can be used later with
+.BR unu\-ccmerge (1) \ \-d .
+By default, no record of the original CC values is kept.
+.RI ( string )
+.TP
+.B \-t\fR\ <\fItype\fR>\ ,\ \fB\-\-type\fR\ <\fItype\fR>
+type to use for output, to store the CC ID values.
+By default (not using this option), the type used will be the smallest of
+.IR uchar ,
+.IR ushort ,
+or
+.IR int ,
+that can represent all the CC ID values.
+Using this option allows one to specify the integral type to be used.
+.TP
+.B \-c\fR\ <\fIconnectivity\fR>\ ,\ \fB\-\-connect\fR\ <\fIconnectivity\fR>
+what kind of connectivity to use: the number of coordinates that vary in order
+to traverse the neighborhood of a given sample.
+In 2D:
+.RB \(lq 1 \(rq:
+4-connected,
+.RB \(lq 2 \(rq:
+8-connected
+.RI ( unsigned\ int )
+.TP
+.B \-o\fR\ <\fInout\fR>\ ,\ \fB\-\-output\fR\ <\fInout\fR>
+output
+.I nrrd
+.RI ( string );
+default:
+.RB \(lq \- \(rq
+.SH "SEE\ ALSO"
+.BR unu (1),
+.BR unu\-ccadj (1),
+.BR unu\-ccmerge (1),
+.BR unu\-ccsettle (1)
diff --git a/unu-ccmerge.1 b/unu-ccmerge.1
new file mode 100644
index 0000000..2b263f5
--- /dev/null
+++ b/unu-ccmerge.1
@@ -0,0 +1,111 @@
+.TH UNU\-CCMERGE "1" "August 2021" "" "User Commands"
+.SH NAME
+unu\-ccmerge \- merge CCs with their neighbors, under various constraints
+.SH SYNOPSIS
+.B unu\ ccmerge
+.RB [ \-d , \-\-directed
+.RI < dir >]
+.RB [ \-s , \-\-size
+.RI < max\ size >]
+.RB [ \-n , \-\-neighbor
+.RI < max\ #\ neigh >]
+.BR \-c , \-\-connect
+.RI < connectivity >
+.RB [ \-revalue ]
+.RB [ \-i , \-\-input
+.RI < nin >]
+.RB [ \-v , \-\-values
+.RI < values >]
+.RB [ \-o , \-\-output
+.RI < nout >]
+.SH DESCRIPTION
+Merge CCs with their neighbors, under various constraints.
+This operates on the output of
+.BR unu\-ccfind (1).
+Merging of a CC is always done into its largest neighbor.
+Whether or not to merge can be constrained by one or more of: CC size
+.RB (\(lq \-s \(rq),
+original CC value being brighter or darker
+.RB (\(lq \-d \(rq),
+and number of neighbors
+.RB (\(lq \-n \(rq).
+.IP \(bu +2n
+Uses
+.B nrrdCCMerge
+.SH OPTIONS
+.TP
+.B \-d\fR\ <\fIdir\fR>\ ,\ \fB\-\-directed\fR\ <\fIdir\fR>
+do value-driven merging.
+Using (positive)
+.RB \(lq 1 \(rq
+says that dark islands get merged with bright surrounds, while
+.RB \(lq \-1 \(rq
+says the opposite.
+By default, merging can go either way.
+.RI ( int );
+default:
+.RB \(lq 0 \(rq
+.TP
+.B \-s\fR\ <\fImax\ size\fR>\ ,\ \fB\-\-size\fR\ <\fImax\ size\fR>
+a cap on the CC size that will be absorbed into its surround.
+CCs larger than this are deemed too significant to mess with.
+Or, use
+.RB \(lq 0 \(rq
+to remove any such restriction on merging.
+.RI ( int );
+default:
+.RB \(lq 0 \(rq
+.TP
+.B \-n\fR\ <\fImax\ #\ neigh\fR>\ ,\ \fB\-\-neighbor\fR\ <\fImax\ #\ neigh\fR>
+a cap on the number of neighbors that a CC may have if it is to be be merged.
+.RB \(lq 1 \(rq
+allows only islands to be merged,
+.RB \(lq 2 \(rq
+does merging with bigger of two neighbors, etc, while
+.RB \(lq 0 \(rq
+says that number of neighbors is no constraint
+.RI ( int );
+default:
+.RB \(lq 1 \(rq
+.TP
+.B \-c\fR\ <\fIconnectivity\fR>\ ,\ \fB\-\-connect\fR\ <\fIconnectivity\fR>
+what kind of connectivity to use: the number of coordinates that vary in order
+to traverse the neighborhood of a given sample.
+In 2D:
+.RB \(lq 1 \(rq:
+4-connected,
+.RB \(lq 2 \(rq:
+8-connected
+.RI ( unsigned\ int )
+.TP
+.B \-revalue
+If this option is given, then after the merging, the CCs are re-assigned their
+original datavalues, as given by the
+.RB \(lq \-v \(rq
+option
+.TP
+.B \-i\fR\ <\fInin\fR>\ ,\ \fB\-\-input\fR\ <\fInin\fR>
+input
+.I nrrd
+.TP
+.B \-v\fR\ <\fIvalues\fR>\ ,\ \fB\-\-values\fR\ <\fIvalues\fR>
+result of using
+.BR unu\-ccfind (1) \ \-v ,
+the record of which values were originally associated with each CC.
+This is required for value-directed merging (with non-zero
+.RB \(lq \-d \(rq
+option), or if the
+.B \-revalue
+option is given, but is not needed otherwise
+.TP
+.B \-o\fR\ <\fInout\fR>\ ,\ \fB\-\-output\fR\ <\fInout\fR>
+output
+.I nrrd
+.RI ( string );
+default:
+.RB \(lq \- \(rq
+.SH "SEE\ ALSO"
+.BR unu (1),
+.BR unu\-ccfind (1),
+.BR unu\-ccadj (1),
+.BR unu\-ccsettle (1)
diff --git a/unu-ccsettle.1 b/unu-ccsettle.1
new file mode 100644
index 0000000..e5aa503
--- /dev/null
+++ b/unu-ccsettle.1
@@ -0,0 +1,40 @@
+.TH UNU\-CCSETTLE "1" "August 2021" "" "User Commands"
+.SH NAME
+unu\-ccsettle \- remap CC values down to lowest contiguous values
+.SH SYNOPSIS
+.B unu\ ccsettle
+.BR \-i , \-\-input
+.RI < nin >
+.RB [ \-v , \-\-values
+.RI < filename >]
+.RB [ \-o , \-\-output
+.RI < nout >]
+.SH DESCRIPTION
+Remap CC values down to lowest contiguous values.
+.IP \(bu +2n
+Uses
+.B nrrdCCSettle
+.SH OPTIONS
+.TP
+.B \-i\fR\ <\fInin\fR>\ ,\ \fB\-\-input\fR\ <\fInin\fR>
+input
+.I nrrd
+.TP
+.B \-v\fR\ <\fIfilename\fR>\ ,\ \fB\-\-values\fR\ <\fIfilename\fR>
+Giving a
+.I filename
+here allows you to save out the mapping from new (settled) values to old
+values, in the form of a 1-D lookup table
+.RI ( string )
+.TP
+.B \-o\fR\ <\fInout\fR>\ ,\ \fB\-\-output\fR\ <\fInout\fR>
+output
+.I nrrd
+.RI ( string );
+default:
+.RB \(lq \- \(rq
+.SH "SEE\ ALSO"
+.BR unu (1),
+.BR unu\-ccfind (1),
+.BR unu\-ccadj (1),
+.BR unu\-ccmerge (1)
diff --git a/unu-cksum.1 b/unu-cksum.1
new file mode 100644
index 0000000..af6d4b8
--- /dev/null
+++ b/unu-cksum.1
@@ -0,0 +1,55 @@
+.TH UNU\-CKSUM "1" "May 2021" "" "User Commands"
+.SH NAME
+unu\-cksum \- compute 32-bit CRC of
+.I nrrd
+data (same as via
+.BR cksum (1))
+.SH SYNOPSIS
+.B unu\ cksum
+.BR \-en , \-\-endian
+.RI < end >
+.RB [ \-pen , \-\-printendian
+.RI < bool >]
+.RI < nin1 \ ...>
+.SH DESCRIPTION
+Compute 32-bit CRC of
+.I nrrd
+data (same as via
+.BR cksum (1)).
+Unlike other commands, this doesn\(cqt produce a
+.IR nrrd .
+It only prints to standard out the CRC and byte counts for the input
+.IR nrrd (s),
+seeking to emulate the formatting of
+.BR cksum (1)
+output.
+.IP \(bu +2n
+Uses
+.B nrrdCRC32
+.SH OPTIONS
+.TP
+.B \-en\fR\ <\fIendian\fR>\ ,\ \fB\-\-endian\fR\ <\fIendian\fR>
+Endianness in which to compute CRC;
+.RB \(lq little \(rq
+for Intel and friends;
+.RB \(lq big \(rq
+for everyone else.
+Defaults to endianness of this machine.
+.TP
+.B \-pen\fR\ <\fIbool\fR>\ ,\ \fB\-\-printendian\fR\ <\fIbool\fR>
+whether or not to indicate after the CRC value the endianness with which the
+CRC was computed; doing so clarifies that the CRC result depends on endianness
+and may remove confusion in comparing results on platforms of different
+endianness
+.RI ( bool );
+default:
+.RB \(lq false \(rq
+.TP
+.RI < nin \ ...>
+input
+.IR nrrd (s)
+(1 or more
+.IR string s)
+.SH "SEE\ ALSO"
+.BR unu (1),
+.BR cksum (1)
diff --git a/unu-cmedian.1 b/unu-cmedian.1
new file mode 100644
index 0000000..685059c
--- /dev/null
+++ b/unu-cmedian.1
@@ -0,0 +1,98 @@
+.TH UNU\-CMEDIAN "1" "May 2021" "" "User Commands"
+.SH NAME
+unu\-cmedian \- cheap histogram-based median/mode filtering
+.SH SYNOPSIS
+.B unu\ cmedian
+.BR \-r , \-\-radius
+.RI < radius >
+.RB [ \-mode ]
+.RB [ \-b , \-\-bins
+.RI < num >]
+.RB [ \-w , \-\-weight
+.RI < weight >]
+.RB [ \-p , \-\-pad ]
+.RB [ \-c , \-\-channel ]
+.RB [ \-i , \-\-input
+.RI < nin >]
+.RB [ \-o , \-\-output
+.RI < nout >]
+.SH DESCRIPTION
+Cheap histogram-based median/mode filtering.
+Only works on 1, 2, or 3 dimensions.
+The window over which filtering is done is always square, and only a simplistic
+weighting scheme is available.
+The method is cheap because it does the median or mode based on a histogram,
+which enforces a quantization to the number of bins in the histogram, which
+probably means a loss of precision for anything except 8-bit data.
+Also, integral values can be
+recovered exactly only when the number of bins is exactly
+.IB min \- max +1
+(as reported by
+.BR unu\-minmax (1)).
+.IP \(bu +2n
+Uses
+.BR nrrdCheapMedian ,
+plus
+.B nrrdSlice
+and
+.B nrrdJoin
+in case of
+.RB \(lq \-c \(rq
+.SH OPTIONS
+.TP
+.B \-r\fR\ <\fIradius\fR>\ ,\ \fB\-\-radius\fR\ <\fIradius\fR>
+how big a window to filter over.
+.RB \(lq \-r\ 1 \(rq
+leads to a 3x3 window in an image, and a 3x3x3 window in a volume
+.RI ( unsigned\ int )
+.TP
+.B \-mode
+By default, median filtering is done.
+Using this option enables mode filtering, in which the most common value is
+used as output
+.TP
+.B \-b\fR\ <\fInum\fR>\ ,\ \fB\-\-bins\fR\ <\fInum\fR>
+# of bins in histogram.
+It is in your interest to minimize this number, since big histograms mean
+slower execution times.
+8-bit data needs at most 256 bins.
+.RI ( unsigned\ int );
+default:
+.RB \(lq 256 \(rq
+.TP
+.B \-w\fR\ <\fIweight\fR>\ ,\ \fB\-\-weight\fR\ <\fIweight\fR>
+How much higher to preferentially weight samples that are closer to the center
+of the window.
+.RB \(lq 1.0 \(rq
+weight means that all samples are uniformly weighted over the window, which
+facilitates a simple speed-up.
+.RI ( float );
+default:
+.RB \(lq 1.0 \(rq
+.TP
+.B \-p\fR\ ,\ \fB\-\-pad
+Pad the input (with boundary method
+.RB \(lq bleed \(rq),
+and crop the output, so as to overcome our cheapness and correctly handle the
+border.
+Obviously, this takes more memory.
+.TP
+.B \-c\fR\ ,\ \fB\-\-channel
+Slice the input along axis 0, run filtering on all slices, and join the results
+back together.
+This is the way you\(cqd want to process color (multi-channel) images or
+volumes.
+.TP
+.B \-i\fR\ <\fInin\fR>\ ,\ \fB\-\-input\fR\ <\fInin\fR>
+input
+.I nrrd
+.TP
+.B \-o\fR\ <\fInout\fR>\ ,\ \fB\-\-output\fR\ <\fInout\fR>
+output
+.I nrrd
+.RI ( string );
+default:
+.RB \(lq \- \(rq
+.SH "SEE\ ALSO"
+.BR unu (1),
+.BR unu\-minmax (1)
diff --git a/unu-convert.1 b/unu-convert.1
new file mode 100644
index 0000000..f3588cd
--- /dev/null
+++ b/unu-convert.1
@@ -0,0 +1,57 @@
+.TH UNU\-CONVERT "1" "May 2021" "" "User Commands"
+.SH NAME
+unu\-convert \- convert to another type (as if by cast, w/ optional clamp)
+.SH SYNOPSIS
+.B unu\ convert
+.BR \-t , \-\-type
+.RI < type >
+.RB [ \-i , \-\-input
+.RI < nin >]
+.RB [ \-clamp ]
+.RB [ \-o , \-\-output
+.RI < nout >]
+.SH DESCRIPTION
+Convert to another type (as if by cast, w/ optional clamp).
+By default this does not transform, scale, or intelligently quantize values; it
+just copies them from one type to another, which replicates exactly what
+you\(cqd get in C when you assign from a variable of one type to another, or
+when you cast to a different type.
+However, clamping values to the representable range
+of the output type is possible with
+.RB \(lq \-clamp \(rq.
+See also
+.BR unu\-quantize (1),
+.BR unu\-2op (1)
+.BR x ,
+and
+.BR unu\-3op (1)
+.BR clamp .
+.IP \(bu +2n
+Uses
+.B nrrdConvert
+or
+.B nrrdClampConvert
+.SH OPTIONS
+.TP
+.B \-t\fR\ <\fItype\fR>\ ,\ \fB\-\-type\fR\ <\fItype\fR>
+type to convert to
+.TP
+.B \-i\fR\ <\fInin\fR>\ ,\ \fB\-\-input\fR\ <\fInin\fR>
+input
+.I nrrd
+.TP
+.B \-clamp
+clamp input values to representable range of values of output type, to avoid
+wrap-around problems
+.TP
+.B \-o\fR\ <\fInout\fR>\ ,\ \fB\-\-output\fR\ <\fInout\fR>
+output
+.I nrrd
+.RI ( string );
+default:
+.RB \(lq \- \(rq
+.SH "SEE\ ALSO"
+.BR unu (1),
+.BR unu\-2op (1),
+.BR unu\-3op (1),
+.BR unu\-quantize (1)
diff --git a/unu-crop.1 b/unu-crop.1
new file mode 100644
index 0000000..b0f9ac8
--- /dev/null
+++ b/unu-crop.1
@@ -0,0 +1,85 @@
+.TH UNU\-CROP "1" "August 2021" "" "User Commands"
+.SH NAME
+unu\-crop \- crop along each axis to make a smaller
+.I nrrd
+.SH SYNOPSIS
+.B unu\ crop
+.RB [ \-min , \-\-minimum
+.RI [< pos0 \ ...>]]
+.RB [ \-max , \-\-maximum
+.RI [< pos0 \ ...>]]
+.RB [ \-b , \-\-bounds
+.RI < filename >]
+.RB [ \-i , \-\-input
+.RI < nin >]
+.RB [ \-o , \-\-output
+.RI < nout >]
+.SH DESCRIPTION
+Crop along each axis to make a smaller
+.IR nrrd .
+.IP \(bu +2n
+Uses
+.B nrrdCrop
+.SH OPTIONS
+.TP
+.B \-min\fR\ [<\fIpos0\fR\ ...>]\ ,\ \fB\-\-minimum\fR\ [<\fIpos0\fR\ ...>]
+low corner of bounding box.
+.RS +7n
+.IP \(bu +2n
+.RI < int >
+gives 0-based index
+.IP \(bu +2n
+.BR M ,
+.BR M+ <\fIint\fR>,
+.BR M\- <\fIint\fR>
+give index relative to the last sample on the axis
+.RB ( M== \fI#samples \-1 ).
+.RE
+.IP
+(0 or more
+.IR position s).
+default:
+.RB \(lq 0 \(rq
+.TP
+.B \-max\fR\ [<\fIpos0\fR\ ...>]\ ,\ \fB\-\-maximum\fR\ [<\fIpos0\fR\ ...>]
+high corner of bounding box.
+Besides the specification styles described above, there\(cqs also:
+.RS +7n
+.IP \(bu +2n
+.BR m+ <\fIint\fR>
+give index relative to minimum.
+.RE
+.IP
+(0 or more
+.IR position s).
+default:
+.RB \(lq 0 \(rq
+.TP
+.B \-b\fR\ <\fIfilename\fR>\ ,\ \fB\-\-bounds\fR\ <\fIfilename\fR>
+a filename given here overrides the
+.B \-min
+and
+.B \-max
+options (they don\(cqt need to be used) and provides the cropping bounds as a
+2\-D array; first scanline is for
+.BR \-min ,
+second is for
+.BR \-max .
+Unfortunately the
+.RB \(lq m \(rq
+and
+.RB \(lq M \(rq
+semantics (above) are currently not supported in the bounds file.
+.TP
+.B \-i\fR\ <\fInin\fR>\ ,\ \fB\-\-input\fR\ <\fInin\fR>
+input
+.I nrrd
+.TP
+.B \-o\fR\ <\fInout\fR>\ ,\ \fB\-\-output\fR\ <\fInout\fR>
+output
+.I nrrd
+.RI ( string );
+default:
+.RB \(lq \- \(rq
+.SH "SEE\ ALSO"
+.BR unu (1)
diff --git a/unu-data.1 b/unu-data.1
new file mode 100644
index 0000000..e04dc57
--- /dev/null
+++ b/unu-data.1
@@ -0,0 +1,55 @@
+.TH UNU\-DATA "1" "May 2021" "" "User Commands"
+.SH NAME
+unu\-data \- print data segment of a \fInrrd\fR file
+.SH SYNOPSIS
+.B unu\ data
+.RI < nin >
+.SH DESCRIPTION
+Print data segment of a
+.I nrrd
+file.
+The value of this is to pass the data segment in isolation to a stand-alone
+decoder, in case this Teem build lacks an optional data encoding required for a
+given nrrd file.
+.SS CAVEATS
+Will start copying characters from the datafile until EOF is hit, so this
+won\(cqt work correctly if the datafile has extraneous content at the end.
+Will skip lines (as per
+.RB \(lq line\ skip: \(rq
+header field) if needed, but can only skip bytes (as per
+.RB \(lq byte\ skip: \(rq
+if the encoding is NOT a compression.
+.IP \(bu +2n
+Uses
+.B nrrdLoad
+with
+.B nio->skipData
+and
+.B nio->keepNrrdDataFileOpen
+both
+.B true
+in the
+.I NrrdIoState
+.BR nio .
+.SH OPTIONS
+.TP
+.RI < nin >
+input
+.I nrrd
+.RI ( string )
+.SH EXAMPLES
+To make
+.B vol.raw
+contain the uncompressed data from
+.B vol.nrrd
+which uses
+.RB \(lq gz \(rq
+encoding:
+.in +4n
+.EX
+.B unu data vol.nrrd | gunzip > vol.raw
+.EE
+.in
+.SH "SEE\ ALSO"
+.BR unu (1),
+.BR unu\-head (1)
diff --git a/unu-dhisto.1 b/unu-dhisto.1
new file mode 100644
index 0000000..90057d3
--- /dev/null
+++ b/unu-dhisto.1
@@ -0,0 +1,55 @@
+.TH UNU\-DHISTO "1" "August 2021" "" "User Commands"
+.SH NAME
+unu\-dhisto \- create image of 1\-D value histogram
+.SH SYNOPSIS
+.B unu\ dhisto
+.BR \-h , \-\-height
+.RI < height >
+.RB [ \-nolog ]
+.RB [ \-notick ]
+.RB [ \-max , \-\-maximum
+.RI < max\ #\ hits >]
+.RB [ \-i , \-\-input
+.RI < nin >]
+.RB [ \-o , \-\-output
+.RI < nout >]
+.SH DESCRIPTION
+Create image of 1\-D value histogram.
+With
+.RB \(lq \-nolog \(rq,
+this becomes a quick & dirty way of plotting a function.
+.IP \(bu +2n
+Uses
+.B nrrdHistoDraw
+.SH OPTIONS
+.TP
+.B \-h\fR\ <\fIheight\fR>\ ,\ \fB\-\-height\fR\ <\fIheight\fR>
+height of output image (horizontal size is determined by number of bins in
+input histogram).
+.RI ( unsigned\ int )
+.TP
+.B \-nolog
+do not show the log-scaled histogram with decade tick-marks
+.TP
+.B \-notick
+do not draw the log decade tick marks
+.TP
+.B \-max\fR\ <\fImax\ #\ hits\fR>\ ,\ \fB\-\-maximum\fR\ <\fImax\ #\ hits\fR>
+constrain the top of the drawn histogram to be at this number of hits.
+This will either scale the drawn histogram downward or clip its top, depending
+on whether the given max is higher or lower than the actual maximum bin count.
+By not using this option (the default), the actual maximum bin count is used
+.RI \(lq double \(rq
+.TP
+.B \-i\fR\ <\fInin\fR>\ ,\ \fB\-\-input\fR\ <\fInin\fR>
+input
+.I nrrd
+.TP
+.B \-o\fR\ <\fInout\fR>\ ,\ \fB\-\-output\fR\ <\fInout\fR>
+output
+.I nrrd
+.RI ( string );
+default:
+.RB \(lq \- \(rq
+.SH "SEE\ ALSO"
+.BR unu (1)
diff --git a/unu-dice.1 b/unu-dice.1
new file mode 100644
index 0000000..ec458f3
--- /dev/null
+++ b/unu-dice.1
@@ -0,0 +1,80 @@
+.TH UNU\-DICE "1" "August 2021" "" "User Commands"
+.SH NAME
+unu\-dice \- save all slices along one axis into separate files
+.SH SYNOPSIS
+.B unu\ dice
+.BR \-a , \-\-axis
+.RI < axis >
+.RB [ \-i , \-\-input
+.RI < nin >]
+.RB [ \-s , \-\-start
+.RI < start >]
+.RB [ \-ff , \-\-format
+.RI < form >]
+.RB [ \-l , \-\-limit
+.RI < max# >]
+.RB [ \-o , \-\-output
+.RI < above \ below >]
+.SH DESCRIPTION
+Save all slices along one axis into separate files.
+Calls
+.BR unu\-slice (1)
+for each position along the indicated axis, and saves out a different
+file for each sample along that axis.
+.IP \(bu +2n
+Uses repeated calls to
+.B nrrdSlice
+and
+.B nrrdSave
+.SH OPTIONS
+.TP
+.B \-a\fR\ <\fIaxis\fR>\ ,\ \fB\-\-axis\fR\ <\fIaxis\fR>
+axis to slice along
+.RI ( unsigned\ int )
+.TP
+.B \-i\fR\ <\fInin\fR>\ ,\ \fB\-\-input\fR\ <\fInin\fR>
+input
+.I nrrd
+.TP
+.B \-s\fR\ <\fIstart\fR>\ ,\ \fB\-\-start\fR\ <\fIstart\fR>
+integer value to start numbering with
+.RI ( unsigned\ int );
+default:
+.RB \(lq 0 \(rq
+.TP
+.B \-ff\fR\ <\fIform\fR>\ ,\ \fB\-\-format\fR\ <\fIform\fR>
+a
+.BR printf (3)-style
+format to use for generating all filenames.
+Use this to override the number of characters used to represent the slice
+position, or the file format of the output, e.g.
+.RB \(lq \-ff\ %03d.ppm \(rq
+for
+.BR 000.ppm ,
+.BR 001.ppm ,
+etc.
+By default (not using this option), slices are saved in NRRD format (or PNM or
+PNG where possible) with shortest possible filenames.
+.RI ( string )
+.TP
+.B \-l\fR\ <\fImax#\fR>\ ,\ \fB\-\-limit\fR\ <\fImax#\fR>
+a sanity check on how many slice files should be saved out, to prevent
+accidentally dicing the wrong axis or the wrong array.
+Can raise this value if needed.
+.RI ( unsigned\ int );
+default:
+.RB \(lq 9999 \(rq
+.TP
+.B \-o\fR\ <\fIprefix\fR>\ ,\ \fB\-\-output\fR\ <\fIprefix\fR>
+output filename
+.I prefix
+(excluding info set via
+.RB \(lq \-ff \(rq,
+basically to set path of output files (so be sure to end with
+.RB \(lq / \(rq).
+.RI ( string )
+.SH "SEE\ ALSO"
+.BR unu (1),
+.BR unu\-slice (1)
+.P
+.BR printf (3)
diff --git a/unu-diff.1 b/unu-diff.1
new file mode 100644
index 0000000..a8e7359
--- /dev/null
+++ b/unu-diff.1
@@ -0,0 +1,46 @@
+.TH UNU\-DIFF "1" "May 2021" "" "User Commands"
+.SH NAME
+unu\-diff \- sees if two
+.IR nrrd s
+are different in any way
+.SH SYNOPSIS
+.B unu\ diff
+.RI < ninA >
+.RI < ninB >
+.RB [ \-eps , \-\-epsilon
+.RI < eps >]
+.RB [ \-od , \-\-onlydata ]
+.SH DESCRIPTION
+Sees if two
+.IR nrrd s
+are different in any way.
+Looks through all fields to see if two given
+.IR nrrd s
+contain the same information.
+Or, array meta-data can be excluded, and comparison only on the data values is
+done with the
+.B \-od
+flag.
+.IP \(bu +2n
+Uses
+.B nrrdCompare
+.SH OPTIONS
+.TP
+.RI < ninA >
+First input
+.IR nrrd .
+.TP
+.RI < ninB >
+Second input
+.IR nrrd .
+.TP
+.B \-eps\fR\ <\fIeps\fR>\ ,\ \fB\-\-epsilon\fR\ <\fIeps\fR>
+threshold for allowable difference in values in data values
+.RI ( double );
+default:
+.RB \(lq 0.0 \(rq
+.TP
+.B \-od\fR\ ,\ \fB\-\-onlydata
+Compare data values only, excluding array meta-data
+.SH "SEE\ ALSO"
+.BR unu (1)
diff --git a/unu-dist.1 b/unu-dist.1
new file mode 100644
index 0000000..fb0a97c
--- /dev/null
+++ b/unu-dist.1
@@ -0,0 +1,71 @@
+.TH UNU\-DIST "1" "May 2021" "" "User Commands"
+.SH NAME
+unu\-dist \- Euclidean distance transform
+.SH SYNOPSIS
+.B unu\ dist
+.BR \-th , \-\-thresh
+.RI < val >
+.RB [ \-b , \-\-bias
+.RI < val >]
+.RB [ \-t , \-\-type
+.RI < type >]
+.RB [ \-sgn ]
+.RB [ \-inv ]
+.RB [ \-i , \-\-input
+.RI < nin >]
+.RB [ \-o , \-\-output
+.RI < nout >]
+.SH DESCRIPTION
+Euclidean distance transform.
+Based on \(lqDistance Transforms of Sampled Functions\(rq
+by Pedro F. Felzenszwalb and Daniel P. Huttenlocher,
+\fICornell Computing and Information Science\fR TR2004\-1963.
+This function first thresholds at the specified value and then does the
+distance transform of the resulting binary image.
+The signed distance (negative values inside object) is also available.
+Distances between non-isotropic samples are handled correctly.
+.IP \(bu +2n
+Uses
+.B nrrdDistanceL2
+or
+.B nrrdDistanceL2Signed
+.SH OPTIONS
+.TP
+.B \-th\fR\ <\fIval\fR>\ ,\ \fB\-\-thresh\fR\ <\fIval\fR>
+threshold value to separate inside from outside
+.RI ( double )
+.TP
+.B \-b\fR\ <\fIval\fR>\ ,\ \fB\-\-bias\fR\ <\fIval\fR>
+if non-zero, bias the distance transform by this amount times the difference in
+value from the threshold
+.RI ( double );
+default:
+.RB \(lq 0.0 \(rq
+.TP
+.B \-t\fR\ <\fItype\fR>\ ,\ \fB\-\-type\fR\ <\fItype\fR>
+type to save output in; default:
+.RB \(lq float \(rq
+.TP
+.B \-sgn
+also compute signed (negative) distances inside objects, instead of leaving
+them as zero
+.TP
+.B \-inv
+values
+.B below
+threshold are considered interior to object.
+By default (not using this option), values above threshold are considered
+interior.
+.TP
+.B \-i\fR\ <\fInin\fR>\ ,\ \fB\-\-input\fR\ <\fInin\fR>
+input
+.I nrrd
+.TP
+.B \-o\fR\ <\fInout\fR>\ ,\ \fB\-\-output\fR\ <\fInout\fR>
+output
+.I nrrd
+.RI ( string );
+default:
+.RB \(lq \- \(rq
+.SH "SEE\ ALSO"
+.BR unu (1)
diff --git a/unu-env.1 b/unu-env.1
new file mode 100644
index 0000000..2b3e8a6
--- /dev/null
+++ b/unu-env.1
@@ -0,0 +1,184 @@
+.TH UNU\-ENV "1" "April 2021" "" "User Commands"
+.SH NAME
+unu\-env \- list relevant environment variables and their values
+.SH SYNOPSIS
+.B unu\ env
+.SH DESCRIPTION
+List relevant environment variables and their values.
+These environment variables provide a way of setting global variables that can
+affect the way Nrrd (and
+.BR unu (1))
+operates.
+.IP \(bu +2n
+Uses
+.BR nrrdGetenvBool ,
+.BR nrrdGetenvEnum ,
+.BR nrrdGetenvInt ,
+and
+.B nrrdGetenvUInt
+.IP
+Each variable in the listing below starts with the name of the environment
+variable
+.RB (\(lq NRRD_ ...\(rq),
+what type of value it represents (e.g.\:
+.RI \(lq int \(rq,
+.RI \(lq bool \(rq),
+what the corresponding Nrrd global variable is set to, and a description of the
+variable.
+In the
+.BR unu\-env (1)
+command output, the listing also includes what the environment variable is
+currently set to.
+.IP
+.I Bool
+variables may be set to true simply by setting the environment variable;
+setting the value to
+.RI \(lq true \(rq
+or
+.RI \(lq false \(rq
+sets the
+.I bool
+accordingly.
+.I Enum
+variables may be set by setting the environment variable to any string that
+parses as one of the
+.I enum
+values.
+.I Int
+and
+.I unsigned\ int
+variables are set via a string parse-able as a numeric value.
+.IP
+In addition to the the
+.RB \(lq NRRD_ \(rq
+environment variables, there is this one,
+.BR UNRRDU_QUIET_QUIT ;
+see the
+.B ENVIRONMENT
+section below.
+.SH ENVIRONMENT
+.TP
+.B UNRRDU_QUIET_QUIT
+Determines whether
+.BR unu (1)
+exits quietly (without error and usage info) when it fails because an input
+.I nrrd
+read immediately hit
+.B EOF
+(as happens when many
+.BR unu (1)
+invocations are piped together).
+This is currently detected by seeing if the error message ends with
+.RB \(lq [nrrd]\ _nrrdRead:\ immediately\ hit\ EOF \(rq.
+.TP
+.B NRRD_STATE_KEYVALUEPAIRS_PROPAGATE \fR(\fIbool\fR)
+.RB ( nrrdStateKeyValuePairsPropagate \ ==\ true )
+.IP
+When
+.BR true ,
+key/value pairs are copied from input
+.I nrrd
+to output
+.I nrrd
+just like other basic info that hasn\(cqt just been modified (e.g. type,
+dimension, block size).
+.TP
+.B NRRD_DEFAULT_CENTER \fR(\fIcentering\ enum\fR)
+.RB ( nrrdDefaultCenter \ ==\ cell )
+.IP
+The type of sample centering to use when none has been set but one has to be
+chosen for some operation (e.g. resampling).
+.TP
+.B NRRD_DEFAULT_WRITE_ENCODING_TYPE \fR(\fIencoding\ enum\fR)
+.RB ( nrrdDefaultWriteEncodingType \ ==\ raw )
+.IP
+When writing
+.IR nrrd s,
+what encoding to use.
+Only
+.BR unu\-save (1)
+affords explicit control of output encoding.
+.TP
+.B NRRD_STATE_KIND_NOOP \fR(\fIbool\fR)
+.RB ( nrrdStateKindNoop \ ==\ false )
+.IP
+When true, Nrrd makes not even the slightest effort to be smart about setting
+the
+.RB \(lq kind \(rq
+field of an axis after some operation that modified its samples.
+.TP
+.B NRRD_STATE_VERBOSE_IO \fR(\fIint\fR)
+.RB ( nrrdStateVerboseIO \ ==\ 1 )
+.IP
+The verbosity level of Nrrd input/output operations.
+.TP
+.B NRRD_DEFAULT_WRITE_BARE_TEXT \fR(\fIbool\fR)
+.RB ( nrrdDefaultWriteBareText \ ==\ 1 )
+.IP
+When
+.BR false ,
+text files used for saving
+.IR nrrd s
+start with comment
+.RB (\(lq #\ ... \(rq)
+lines containing
+.I nrrd
+fields.
+.TP
+.B NRRD_STATE_MEASURE_TYPE \fR(\fIenum\fR)
+.RB ( nrrdStateMeasureType \ ==\ float )
+.IP
+For measurements
+.RB ( unu\-project (1))
+like sum and product, the type of the output result, when one hasn\(cqt been
+explicitly requested.
+.TP
+.B NRRD_STATE_MEASURE_MODE_BINS \fR(\fIint\fR)
+.RB ( nrrdStateMeasureModeBins \ ==\ 1024 )
+.IP
+When measuring mode but without a given histogram, how many bins to use in the
+temporary internal histogram.
+.TP
+.B NRRD_STATE_MEASURE_HISTO_TYPE \fR(\fIenum\fR)
+.RB ( nrrdStateMeasureHistoType \ ==\ float )
+.IP
+Output type for most measurements of histograms, when one hasn\(cqt been
+explicitly requested
+.TP
+.B NRRD_STATE_ALWAYS_SET_CONTENT \fR(\fIbool\fR)
+.RB ( nrrdStateAlwaysSetContent \ ==\ true )
+.IP
+If
+.BR true ,
+the output content string is set even when the input content string is not set.
+.TP
+.B NRRD_STATE_DISABLE_CONTENT \fR(\fIbool\fR)
+.RB ( nrrdStateDisableContent \ ==\ false )
+.IP
+If
+.BR true ,
+output content is never set.
+.TP
+.B NRRD_DEFAULT_WRITE_CHARS_PER_LINE \fR(\fIunsigned\ int\fR)
+.RB ( nrrdDefaultWriteCharsPerLine \ ==\ 75 )
+.IP
+When using text encoding, maximum # characters allowed per line.
+.TP
+.B NRRD_DEFAULT_WRITE_VALS_PER_LINE \fR(\fIunsigned\ int\fR)
+.RB ( nrrdDefaultWriteValsPerLine \ ==\ 8 )
+.IP
+When using text encoding, maximum # values allowed per line
+.TP
+.B NRRD_STATE_GRAYSCALE_IMAGE_3D \fR(\fIbool\fR)
+.RB ( nrrdStateGrayscaleImage3D \ ==\ false )
+.IP
+If
+.BR true ,
+reading a 2-D grayscale image results in a 3-D image with a single
+sample
+.RB ( size = 1 )
+on the first (fastest) axis.
+.SH "SEE\ ALSO"
+.BR unu (1),
+.BR unu\-project (1),
+.BR unu\-save (1)
diff --git a/unu-fft.1 b/unu-fft.1
new file mode 100644
index 0000000..3be7157
--- /dev/null
+++ b/unu-fft.1
@@ -0,0 +1,107 @@
+.TH UNU\-FFT "1" "May 2021" "" "User Commands"
+.SH NAME
+unu\-fft \- Fast Fourier Transform of selected axes
+.SH SYNOPSIS
+.B unu\ fft
+.RI < dir >
+.BR \-a , \-\-axes
+.RI < ax0 \ ...>
+.RB [ \-pr , \-\-planrigor
+.RI < pr >]
+.RB [ \-r , \-\-rescale
+.RI < bool >]
+.RB [ \-w , \-\-wisdom
+.RI < filename >]
+.RB [ \-i , \-\-input
+.RI < nin >]
+.RB [ \-ri , \-\-realinput ]
+.RB [ \-o , \-\-output
+.RI < nout >]
+.SH DESCRIPTION
+Fast Fourier Transform of selected axes.
+Initial attempt at wrapping the FFTW3 library; options are likely to change in
+Teem 2.0.
+.IP \(bu +2n
+Uses
+.B nrrdFFT
+.SH OPTIONS
+.TP
+.RI < dir >
+forward
+.RB (\(lq forw \(rq,
+.RB \(lq f \(rq)
+or backward/inverse ("back", "b")
+.RB (\(lq back \(rq,
+.RB \(lq b \(rq)
+transform
+.TP
+.B \-a\fR\ <\fIax0\fR\ ...>\ ,\ \fB\-\-axes\fR\ <\fIax0\fR\ ...>
+the one or more axes that should be transformed (1 or more
+.IR unsigned\ int s)
+.TP
+.B \-pr\fR\ <\fIpr\fR>\ ,\ \fB\-\-planrigor\fR\ <\fIpr\fR>
+rigor with which fftw plan is constructed.
+Options include:
+.RS +7n
+.IP \(bu 2
+.RB \(lq e \(rq,
+.RB \(lq est \(rq,
+.RB \(lq estimate \(rq:
+only an estimate
+.IP \(bu 2
+.RB \(lq m \(rq,
+.RB \(lq meas \(rq,
+.RB \(lq measure \(rq:
+standard amount of measurements of system properties
+.IP \(bu 2
+.RB \(lq p \(rq,
+.RB \(lq pat \(rq,
+.RB \(lq patient \(rq:
+slower, more measurements
+.IP \(bu 2
+.RB \(lq x \(rq,
+.RB \(lq ex \(rq,
+.RB \(lq exhaustive \(rq:
+slowest, most measurements
+.P
+default:
+.RB \(lq est \(rq
+.RE
+.TP
+.B \-r\fR\ <\fIbool\fR>\ ,\ \fB\-\-rescale\fR\ <\fIbool\fR>
+scale fftw output (by sqrt(1/N)) so that forward and backward transforms will
+get back to original values
+.RI ( bool );
+default:
+.RB \(lq true \(rq
+.TP
+.B \-w\fR\ <\fIfilename\fR>\ ,\ \fB\-\-wisdom\fR\ <\fIfilename\fR>
+A filename here is used to read in fftw wisdom (if the file exists already),
+and is used to save out updated wisdom after the transform.
+By default (not using this option), no wisdom is read or saved.
+.IR Note :
+no wisdom is gained (that is, learned by FFTW) with planning rigor
+.RB \(lq estimate \(rq.
+.RI ( string )
+.TP
+.B \-i\fR\ <\fInin\fR>\ ,\ \fB\-\-input\fR\ <\fInin\fR>
+input
+.I nrrd
+.TP
+.B \-ri\fR\ ,\ \fB\-\-realinput
+input is real-valued, so insert new length-2 axis
+.B 0
+and set complex component to
+.BR 0.0 .
+Axes to transform (indicated by
+.RB \(lq \-a \(rq
+will be incremented accordingly.
+.TP
+.B \-o\fR\ <\fInout\fR>\ ,\ \fB\-\-output\fR\ <\fInout\fR>
+output
+.I nrrd
+.RI ( string );
+default:
+.RB \(lq \- \(rq
+.SH "SEE\ ALSO"
+.BR unu (1)
diff --git a/unu-flip.1 b/unu-flip.1
new file mode 100644
index 0000000..63c86d5
--- /dev/null
+++ b/unu-flip.1
@@ -0,0 +1,37 @@
+.TH UNU\-FLIP "1" "August 2021" "" "User Commands"
+.SH NAME
+unu\-flip \- reverse order of slices along one axis
+.SH SYNOPSIS
+.B unu\ flip
+.BR \-a , \-\-axis
+.RI < axis >
+.RB [ \-i , \-\-input
+.RI < nin >]
+.RB [ \-o , \-\-output
+.RI < nout >]
+.SH DESCRIPTION
+Reverse order of slices along one axis.
+Special case of
+.BR unu\-shuffle (1).
+.IP \(bu +2n
+Uses
+.B nrrdFlip
+.SH OPTIONS
+.TP
+.B \-a\fR\ <\fIaxis\fR>\ ,\ \fB\-\-axis\fR\ <\fIaxis\fR>
+axis to flip along
+.RI ( unsigned\ int )
+.TP
+.B \-i\fR\ <\fInin\fR>\ ,\ \fB\-\-input\fR\ <\fInin\fR>
+input
+.I nrrd
+.TP
+.B \-o\fR\ <\fInout\fR>\ ,\ \fB\-\-output\fR\ <\fInout\fR>
+output
+.I nrrd
+.RI ( string );
+default:
+.RB \(lq \- \(rq
+.SH "SEE\ ALSO"
+.BR unu (1),
+.BR unu\-shuffle (1)
diff --git a/unu-gamma.1 b/unu-gamma.1
new file mode 100644
index 0000000..a536802
--- /dev/null
+++ b/unu-gamma.1
@@ -0,0 +1,81 @@
+.TH UNU\-GAMMA "1" "August 2021" "" "User Commands"
+.SH NAME
+unu\-gamma \- brighten or darken values with a gamma
+.SH SYNOPSIS
+.B unu\ gamma
+.BR \-g , \-\-gamma
+.RI < gamma >
+.RB [ \-min , \-\-minimum
+.RI < value >]
+.RB [ \-max , \-\-maximum
+.RI < value >]
+.RB [ \-blind8
+.RI < bool >]
+.RB [ \-i , \-\-input
+.RI < nin >]
+.RB [ \-o , \-\-output
+.RI < nout >]
+.SH DESCRIPTION
+Brighten or darken values with a gamma.
+Just as in
+.BR xv ,
+the gamma value here is actually the reciprocal of the exponent actually used
+to transform the values.
+.IP \(bu +2n
+Uses
+.B nrrdArithGamma
+.SH OPTIONS
+.TP
+.B \-g\fR\ <\fIgamma\fR>\ ,\ \fB\-\-gamma\fR\ <\fIgamma\fR>
+.B 1.0
+brightens; gamma <
+.B 1.0
+darkens.
+Negative gammas invert values (like in
+.BR xv ).
+.RI ( double )
+.TP
+.B \-min\fR\ [<\fIvalue\fR>]\ ,\ \fB\-\-minimum\fR\ [<\fIvalue\fR>]
+Value to implicitly map to
+.B 0.0
+prior to calling
+.BR pow (3).
+Defaults to lowest value found in input
+.IR nrrd .
+.RI ( double )
+.TP
+.B \-max\fR\ [<\fIvalue\fR>]\ ,\ \fB\-\-maximum\fR\ [<\fIvalue\fR>]
+Value to implicitly map to
+.B 1.0
+prior to calling
+.BR pow (3).
+Defaults to highest value found in input
+.IR nrrd .
+.RI ( double )
+.TP
+.B \-blind8\fR\ <\fIbool\fR>
+Whether to know the range of 8-bit data blindly
+.RI ( uchar
+is always
+.BR [0,255] ,
+.I signed\ char
+is
+.BR [-128,127] ).
+.RI ( bool );
+default:
+.RB \(lq true \(rq
+.TP
+.B \-i\fR\ <\fInin\fR>\ ,\ \fB\-\-input\fR\ <\fInin\fR>
+input
+.I nrrd
+.TP
+.B \-o\fR\ <\fInout\fR>\ ,\ \fB\-\-output\fR\ <\fInout\fR>
+output
+.I nrrd
+.RI ( string );
+default:
+.RB \(lq \- \(rq
+.SH "SEE\ ALSO"
+.BR unu (1)
+.P
+http://www.trilon.com/xv/
diff --git a/unu-head.1 b/unu-head.1
new file mode 100644
index 0000000..0a3e691
--- /dev/null
+++ b/unu-head.1
@@ -0,0 +1,34 @@
+.TH UNU\-HEAD "1" "May 2021" "" "User Commands"
+.SH NAME
+unu\-head \- print header of one or more \fInrrd\fR files
+.SH SYNOPSIS
+.B unu\ head
+.RI < nin1 \ ...>
+.SH DESCRIPTION
+Print header of one or more
+.I nrrd
+files.
+The value of this is simply to print the contents of
+.I nrrd
+headers.
+This avoids the use of
+.RB \(lq head\ \-\fIN \(rq,
+where
+.I N
+has to be determined manually, which always risks printing raw binary data
+(following the header) to screen, which tends to clobber terminal settings,
+make pointless beeps, and be annoying.
+.IP \(bu +2n
+Uses
+.B nrrdOneLine
+.SH OPTIONS
+.TP
+.RI < nin1 \ ...>
+input
+.IR nrrd (s)
+(1 or more
+.IR string s)
+.SH "SEE\ ALSO"
+.BR unu (1),
+.BR unu\-data (1),
+.BR head (1)
diff --git a/unu-heq.1 b/unu-heq.1
new file mode 100644
index 0000000..2d7a955
--- /dev/null
+++ b/unu-heq.1
@@ -0,0 +1,82 @@
+.TH UNU\-HEQ "1" "August 2021" "" "User Commands"
+.SH NAME
+unu\-heq \- perform histogram equalization
+.SH SYNOPSIS
+.B unu\ heq
+.BR \-b , \-\-bin
+.RI < bins >
+.RB [ \-s , \-\-smart
+.RI [< bins >]]
+.RB [ \-a , \-\-amount
+.RI < amount >]
+.RB [ \-m , \-\-map
+.RI < filename >]
+.RB [ \-i , \-\-input
+.RI < nin >]
+.RB [ \-o , \-\-output
+.RI < nout >]
+.SH DESCRIPTION
+Perform histogram equalization.
+If this seems to be doing nothing, try increasing the number of histograms bins
+by an order of magnitude or two (or more).
+Or, use
+.BR unu\-gamma (1)
+to warp the values in the direction you know they need to go.
+Either of these might work because extremely tall and narrow peaks in the
+equalization histogram will produce poor results.
+.IP \(bu +2n
+Uses
+.B nrrdHistoEq
+.SH OPTIONS
+.TP
+.B \-b\fR\ <\fIbins\fR>\ ,\ \fB\-\-bin\fR\ <\fIbins\fR>
+# bins to use in histogram that is created in order to calculate the mapping
+that achieves the equalization.
+.RI ( int )
+.TP
+.B \-s\fR\ [<\fIbins\fR>]\ ,\ \fB\-\-smart\fR\ [<\fIbins\fR>]
+# bins in value histogram to ignore in calculating the mapping.
+Bins are ignored when they get more hits than other bins, and when the values
+that fall in them are constant.
+This is an effective way to prevent large regions of background value from
+distorting the equalization mapping.
+(optional
+.IR int );
+default:
+.RB \(lq 0 \(rq
+.TP
+.B \-a\fR\ <\fIamount\fR>\ ,\ \fB\-\-amount\fR\ <\fIamount\fR>
+extent to which the histogram equalizing mapping should be applied;
+.BR 0.0 :
+no change,
+.BR 1.0 :
+full equalization
+.RI ( float );
+default:
+.B \-t\fR\ <\fItype\fR>\ ,\ \fB\-\-type\fR\ <\fItype\fR>
+output type; default:
+.RB \(lq 1.0 \(rq
+.TP
+.B \-m\fR\ <\fIfilename\fR>\ ,\ \fB\-\-map\fR\ <\fIfilename\fR>
+The value mapping used to achieve histogram equalization is represented by a
+univariate regular map.
+By giving a filename here, that map can be saved out and applied to other
+.IR nrrd s
+with
+.BR unu\-rmap (1)
+.RI ( string )
+.TP
+.B \-i\fR\ <\fInin\fR>\ ,\ \fB\-\-input\fR\ <\fInin\fR>
+input
+.I nrrd
+.TP
+.B \-o\fR\ <\fInout\fR>\ ,\ \fB\-\-output\fR\ <\fInout\fR>
+output
+.I nrrd
+.RI ( string );
+default:
+.RB \(lq \- \(rq
+.SH "SEE\ ALSO"
+.BR unu (1),
+.BR unu\-gamma (1),
+.BR unu\-rmap (1)
diff --git a/unu-histax.1 b/unu-histax.1
new file mode 100644
index 0000000..4c76380
--- /dev/null
+++ b/unu-histax.1
@@ -0,0 +1,94 @@
+.TH UNU\-HISTAX "1" "August 2021" "" "User Commands"
+.SH NAME
+unu\-histax \- replace each scanline along an axis with its histogram
+.SH SYNOPSIS
+.B unu\ histax
+.BR \-a , \-\-axis
+.RI < axis >
+.BR \-b , \-\-bin
+.RI < bins >
+.RB [ \-t , \-\-type
+.RI < type >]
+.RB [ \-min , \-\-minimum
+.RI < value >]
+.RB [ \-max , \-\-maximum
+.RI < value >]
+.RB [ \-blind8
+.RI < bool >]
+.RB [ \-i , \-\-input
+.RI < nin >]
+.RB [ \-o , \-\-output
+.RI < nout >]
+.SH DESCRIPTION
+Replace each scanline along an axis with its histogram.
+.IP \(bu +2n
+Uses
+.B nrrdHistoAxis
+.SH OPTIONS
+.TP
+.B \-a\fR\ <\fIaxis\fR>\ ,\ \fB\-\-axis\fR\ <\fIaxis\fR>
+axis to histogram along
+.RI ( unsigned\ int )
+.TP
+.B \-b\fR\ <\fIbins\fR>\ ,\ \fB\-\-bin\fR\ <\fIbins\fR>
+# of bins in histogram
+.RI ( unsigned\ int )
+.B \-t\fR\ <\fItype\fR>\ ,\ \fB\-\-type\fR\ <\fItype\fR>
+output type; default:
+.RB \(lq uint \(rq
+.TP
+.B \-min\fR\ <\fIvalue\fR>\ ,\ \fB\-\-minimum\fR\ <\fIvalue\fR>
+The value to map to zero, given explicitly as a regular number,
+.BR or ,
+if the number is given with a
+.RB \(lq % \(rq
+suffix, this minimum is specified in terms of the percentage of samples in
+input that are lower.
+.RB \(lq 0% \(rq
+means the lowest input value is used,
+.RB \(lq 1% \(rq
+means that the 1% of the lowest values are all mapped to zero.
+By default (not using this option), the lowest input value is used.
+.RI ( string );
+default:
+.RB \(lq nan \(rq
+.TP
+.B \-max\fR\ <\fIvalue\fR>\ ,\ \fB\-\-maximum\fR\ <\fIvalue\fR>
+The value to map to the highest unsigned integral value, given explicitly as a
+regular number,
+.BR or ,
+if the number is given with a
+.RB \(lq % \(rq
+suffix, this maximum is specified in terms of the percentage of samples in
+input that are higher.
+.RB \(lq 0% \(rq
+means the highest input value is used, which is also the default behavior (same
+as not using this option).
+.RI ( string );
+default:
+.RB \(lq nan \(rq
+.TP
+.B \-blind8\fR\ <\fIbool\fR>
+Whether to know the range of 8-bit data blindly
+.RI ( uchar
+is always
+.BR [0,255] ,
+.I signed\ char
+is
+.BR [-128,127] ).
+.RI ( bool );
+default:
+.RB \(lq true \(rq
+.TP
+.B \-i\fR\ <\fInin\fR>\ ,\ \fB\-\-input\fR\ <\fInin\fR>
+input
+.I nrrd
+.TP
+.B \-o\fR\ <\fInout\fR>\ ,\ \fB\-\-output\fR\ <\fInout\fR>
+output
+.I nrrd
+.RI ( string );
+default:
+.RB \(lq \- \(rq
+.SH "SEE\ ALSO"
+.BR unu (1)
diff --git a/unu-histo.1 b/unu-histo.1
new file mode 100644
index 0000000..e0b34b8
--- /dev/null
+++ b/unu-histo.1
@@ -0,0 +1,98 @@
+.TH UNU\-HISTO "1" "August 2021" "" "User Commands"
+.SH NAME
+unu\-histo \- create 1\-D histogram of values in a
+.I nrrd
+.SH SYNOPSIS
+.B unu\ histo
+.BR \-b , \-\-bins
+.RI < num >
+.RB [ \-w , \-\-weight
+.RI < nweight >]
+.RB [ \-min , \-\-minimum
+.RI < value >]
+.RB [ \-max , \-\-maximum
+.RI < value >]
+.RB [ \-blind8
+.RI < bool >]
+.RB [ \-t , \-\-type
+.RI < type >]
+.RB [ \-i , \-\-input
+.RI < nin >]
+.RB [ \-o , \-\-output
+.RI < nout >]
+.SH DESCRIPTION
+Create 1\-D histogram of values in a
+.IR nrrd .
+Can explicitly set bounds of histogram domain or can learn these from the data.
+.IP \(bu +2n
+Uses
+.B nrrdHisto
+.SH OPTIONS
+.TP
+.B \-b\fR\ <\fInum\fR>\ ,\ \fB\-\-bins\fR\ <\fInum\fR>
+# of bins in histogram
+.RI ( unsigned\ int )
+.TP
+.B \-w\fR\ <\fInweight\fR>\ ,\ \fB\-\-weight\fR\ <\fInweight\fR>
+how to weigh contributions to histogram.
+By default (not using this option), the increment is one bin count per sample,
+but by giving a
+.IR nrrd ,
+the value in the
+.I nrrd
+at the corresponding location will be the bin count increment
+.TP
+.B \-min\fR\ <\fIvalue\fR>\ ,\ \fB\-\-minimum\fR\ <\fIvalue\fR>
+Value at low end of histogram, given explicitly as a regular number,
+.BR or ,
+if the number is given with a
+.RB \(lq % \(rq
+suffix, this minimum is specified in terms of the percentage of samples in
+input that are lower.
+By default (not using this option), the lowest value found in input
+.IR nrrd .
+.RI ( string );
+default:
+.RB \(lq nan \(rq
+.TP
+.B \-max\fR\ <\fIvalue\fR>\ ,\ \fB\-\-maximum\fR\ <\fIvalue\fR>
+Value at high end of histogram, given explicitly as a regular number,
+.BR or ,
+if the number is given with a
+.RB \(lq % \(rq
+suffix, this maximum is specified in terms of the percentage of samples in
+input that are higher.
+Defaults to highest value found in input
+.IR nrrd .
+.RI ( string );
+default:
+.RB \(lq nan \(rq
+.TP
+.B \-blind8\fR\ <\fIbool\fR>
+Whether to know the range of 8-bit data blindly
+.RI ( uchar
+is always
+.BR [0,255] ,
+.I signed\ char
+is
+.BR [-128,127] ).
+.RI ( bool );
+default:
+.RB \(lq true \(rq
+.TP
+.B \-t\fR\ <\fItype\fR>\ ,\ \fB\-\-type\fR\ <\fItype\fR>
+type to use for bins in output histogram; default:
+.RB \(lq uint \(rq
+.TP
+.B \-i\fR\ <\fInin\fR>\ ,\ \fB\-\-input\fR\ <\fInin\fR>
+input
+.I nrrd
+.TP
+.B \-o\fR\ <\fInout\fR>\ ,\ \fB\-\-output\fR\ <\fInout\fR>
+output
+.I nrrd
+.RI ( string );
+default:
+.RB \(lq \- \(rq
+.SH "SEE\ ALSO"
+.BR unu (1)
diff --git a/unu-i2w.1 b/unu-i2w.1
new file mode 100644
index 0000000..3b29e31
--- /dev/null
+++ b/unu-i2w.1
@@ -0,0 +1,58 @@
+.TH UNU\-I2W "1" "April 2021" "" "User Commands"
+.SH NAME
+unu\-i2w \- converts from 1-D index to world position
+.SH SYNOPSIS
+.B unu\ i2w
+.RI < center >
+.RI < minPos >
+.RI < maxPos >
+.RI < num >
+.RI < index >
+.SH DESCRIPTION
+Converts from 1-D index to world position, given the centering of the data
+.RB ( cell
+vs.
+.BR node ),
+the range of positions, and the number of intervals into which position has
+been quantized.
+This is a demo/utility, which does not actually operate on any
+.IR nrrd s.
+Previously available as the stand-alone
+.B idx2pos
+binary.
+.P
+Uses
+.B NRRD_POS
+macro
+.SH OPTIONS
+.TP
+.RI < center >
+which centering applies to the quantized position.
+Possibilities are:
+.RS +7n
+.IP \(bu 2
+.RB \(lq cell \(rq
+for histogram bins, quantized values, and pixels-as-squares
+.IP \(bu 2
+.RB \(lq node \(rq
+for non-trivially interpolated sample points
+.RE
+.TP
+.RI < minPos >
+smallest position associated with index
+.B 0
+.RI ( double )
+.TP
+.RI < maxPos >
+highest position associated with highest index
+.RI ( double )
+.TP
+.RI < num >
+number of intervals into which position has been quantized
+.RI ( double )
+.TP
+.RI < index >
+the input index position, to be converted to world
+.RI ( double )
+.SH "SEE\ ALSO"
+.BR unu (1)
diff --git a/unu-imap.1 b/unu-imap.1
new file mode 100644
index 0000000..c79205f
--- /dev/null
+++ b/unu-imap.1
@@ -0,0 +1,118 @@
+.TH UNU\-IMAP "1" "August 2021" "" "User Commands"
+.SH NAME
+unu\-imap \- map
+.I nrrd
+through
+.B irregular
+univariate map (\(lqcolormap\(rq)
+.SH SYNOPSIS
+.B unu\ rmap
+.BR \-m , \-\-map
+.RI < map >
+.RB [ \-l , \-\-length
+.RI < aclLen >]
+.RB [ \-r , \-\-rescale ]
+.RB [ \-min , \-\-minimum
+.RI < value >]
+.RB [ \-max , \-\-maximum
+.RI < value >]
+.RB [ \-blind8
+.RI < bool >]
+.RB [ \-t , \-\-type
+.RI < type >]
+.RB [ \-i , \-\-input
+.RI < nin >]
+.RB [ \-o , \-\-output
+.RI < nout >]
+.SH DESCRIPTION
+Map
+.I nrrd
+through
+.B irregular
+univariate map (\(lqcolormap\(rq).
+A map is irregular if the control points are not evenly spaced along the
+domain, and hence their position must be explicitly represented in the map.
+As
+.IR nrrd s,
+these maps are necessarily 2D.
+Along axis
+.BR 0 ,
+the first value is the location of the control point, and the remaining values
+give are the range of the map for that control point.
+The output value(s) is the result of linearly interpolating between value(s)
+from the map.
+.IP \(bu +2n
+Uses
+.B nrrdApply1DIrregMap
+.SH OPTIONS
+.TP
+.B \-m\fR\ <\fImap\fR>\ ,\ \fB\-\-map\fR\ <\fImap\fR>
+irregular map to map input
+.I nrrd
+through
+.TP
+.B \-l\fR\ <\fIaclLen\fR>\ ,\ \fB\-\-length\fR\ <\fIaclLen\fR>
+length of accelerator array, used to try to speed-up task of finding between
+which pair of control points a given value lies.
+Not terribly useful for small maps (about 10 points or less).
+Use
+.B 0
+to turn accelorator off.
+.RI ( unsigned\ int );
+default:
+.RB \(lq 0 \(rq
+.TP
+.B \-r\fR ,\ \fB\-\-rescale
+rescale the input values from the input range to the map domain.
+.TP
+.B \-min\fR\ <\fIvalue\fR>\ ,\ \fB\-\-minimum\fR\ <\fIvalue\fR>
+Low end of input range.
+Defaults to lowest value found in input
+.IR nrrd .
+Explicitly setting this is useful only with rescaling
+.RB (\(lq \-r \(rq)
+.RI ( double )
+.TP
+.B \-max\fR\ <\fIvalue\fR>\ ,\ \fB\-\-maximum\fR\ <\fIvalue\fR>
+High end of input range.
+Defaults to highest value found in input
+.IR nrrd .
+Explicitly setting this is useful only with rescaling
+.RB (\(lq \-r \(rq)
+.RI ( double )
+.TP
+.B \-blind8\fR\ <\fIbool\fR>
+Whether to know the range of 8-bit data blindly
+.RI ( uchar
+is always
+.BR [0,255] ,
+.I signed\ char
+is
+.BR [-128,127] ).
+Explicitly setting this is useful only with rescaling
+.RB (\(lq \-r \(rq)
+.RI ( bool );
+default:
+.RB \(lq true \(rq
+.TP
+.B \-t\fR\ <\fItype\fR>\ ,\ \fB\-\-type\fR\ <\fItype\fR>
+specify the type
+.RB (\(lq int \(rq,
+.RB \(lq float \(rq,
+etc.) of the output
+.IR nrrd .
+By default (not using this option), the output type is the map\(cqs type.
+.TP
+.B \-i\fR\ <\fInin\fR>\ ,\ \fB\-\-input\fR\ <\fInin\fR>
+input
+.I nrrd
+.TP
+.B \-o\fR\ <\fInout\fR>\ ,\ \fB\-\-output\fR\ <\fInout\fR>
+output
+.I nrrd
+.RI ( string );
+default:
+.RB \(lq \- \(rq
+.SH "SEE\ ALSO"
+.BR unu (1),
+.BR unu\-rmap (1)
diff --git a/unu-inset.1 b/unu-inset.1
new file mode 100644
index 0000000..38059f7
--- /dev/null
+++ b/unu-inset.1
@@ -0,0 +1,61 @@
+.TH UNU\-INSET "1" "August 2021" "" "User Commands"
+.SH NAME
+unu\-inset \- replace a sub-region with a different
+.I nrrd
+.SH SYNOPSIS
+.B unu\ inset
+.BR \-min , \-\-minimum
+.RI < pos0 \ ...>
+.BR \-s , \-\-subset
+.RI < nsub >
+.RB [ \-i , \-\-input
+.RI < nin >]
+.RB [ \-o , \-\-output
+.RI < nout >]
+.SH DESCRIPTION
+Replace a sub-region with a different
+.IR nrrd .
+This is functionally the opposite of
+.RB \(lq crop \(rq.
+.IP \(bu +2n
+Uses
+.B nrrdInset
+.SH OPTIONS
+.TP
+.B \-min\fR\ [<\fIpos0\fR\ ...>]\ ,\ \fB\-\-minimum\fR\ [<\fIpos0\fR\ ...>]
+coordinates of where to locate sub-volume within input
+.IR nrrd .
+.RS +7n
+.IP \(bu 2
+.RI < int >
+gives 0\-based index
+.IP \(bu 2
+.BR M ,
+.BR M\+ <\fIint\fR>,
+.BR M\- <\fIint\fR>
+give index relative to the last sample on the axis
+.RB ( M== \fI#samples \-1 ).
+.RE
+.IP
+(1 or more
+.IR position s)
+.TP
+.B \-s\fR\ <\fInsub\fR>\ ,\ \fB\-\-subset\fR\ <\fInsub\fR>
+sub-region
+.IR nrrd .
+This is the data to be inset in
+.I nin
+.TP
+.B \-i\fR\ <\fInin\fR>\ ,\ \fB\-\-input\fR\ <\fInin\fR>
+input
+.I nrrd
+.TP
+.B \-o\fR\ <\fInout\fR>\ ,\ \fB\-\-output\fR\ <\fInout\fR>
+output
+.I nrrd
+.RI ( string );
+default:
+.RB \(lq \- \(rq
+.SH "SEE\ ALSO"
+.BR unu (1),
+.BR unu\-crop (1)
diff --git a/unu-jhisto.1 b/unu-jhisto.1
new file mode 100644
index 0000000..861e581
--- /dev/null
+++ b/unu-jhisto.1
@@ -0,0 +1,99 @@
+.TH UNU\-JHISTO "1" "August 2021" "" "User Commands"
+.SH NAME
+unu\-jhisto \- create joint histogram of two or more
+.IR nrrd s
+.SH SYNOPSIS
+.B unu\ jhisto
+.BR \-b , \-\-bin
+.RI < bins0 \ bins1 \ ...>
+.RB [ \-w , \-\-weight
+.RI < nweight >]
+.RB [ \-min , \-\-minimum
+.RI < min0 \ min1 \ ...>]
+.RB [ \-max , \-\-maximum
+.RI < max0 \ max1 \ ...>]
+.RB [ \-t , \-\-type
+.RI < type >]
+.RB [ \-i , \-\-input
+.RI < nin0 \ nin1 \ ...>]
+.RB [ \-a , \-\-axis
+.RI < axis >]
+.RB [ \-o , \-\-output
+.RI < nout >]
+.SH DESCRIPTION
+Create joint histogram of two or more
+.IR nrrd s.
+Each axis of the output corresponds to one of the input
+.IR nrrd s,
+and each bin in the output records the number of corresponding positions in the
+inputs with a combination of values represented by the coordinates of the bin.
+.IP \(bu +2n
+Uses
+.B nrrdHistoJoint
+.SH OPTIONS
+.TP
+.B \-b\fR\ <\fIbins0\fR\ \fIbins1\fR\ ...>\ ,\ \fB\-\-bin\fR\ <\fIbins0\fR\ \fIbins1\fR\ ...>
+.IR bins < i >
+is the number of bins to use along axis
+.I i
+(of joint histogram), which
+ represents the values of
+.IR nin < i >
+(2 or more
+.IR size_t s)
+.TP
+.B \-w\fR\ <\fInweight\fR>\ ,\ \fB\-\-weight\fR\ <\fInweight\fR>
+how to weigh contributions to joint histogram.
+By default (not using this option), the increment is one bin count per sample,
+but by giving a
+.IR nrrd ,
+the value in the
+.I nrrd
+at the corresponding location will be the bin count increment
+.TP
+.B \-min\fR\ <\fImin0\fR\ min1\fR\ ...>\ ,\ \fB\-\-minimum\fR\ <\fImin0\fR\ min1\fR\ ...>
+.IR min < i >
+is the low range of values to be quantized along axis
+.IR i ;
+use
+.RB \(lq nan \(rq
+to represent lowest value present
+(2 or more
+.IR double s)
+.TP
+.B \-max\fR\ <\fImax0\fR\ max1\fR\ ...>\ ,\ \fB\-\-maximum\fR\ <\fImax0\fR\ max1\fR\ ...>
+.IR max < i >
+is the high range of values to be quantized along axis
+.IR i ;
+use
+.RB \(lq nan \(rq
+to represent highest value present
+(2 or more
+.IR double s)
+.TP
+.B \-t\fR\ <\fItype\fR>\ ,\ \fB\-\-type\fR\ <\fItype\fR>
+type to use for output (the type used to store hit counts in the joint
+histogram).
+Clamping is done on hit counts so that they never overflow a fixed-point type;
+default:
+.RB \(lq uint \(rq
+.TP
+.B \-i\fR\ <\fInin0\fR\ [\fInin1\fR]\ ...>\ ,\ \fB\-\-input\fR\ <\fInin0\fR\ [\fInin1\fR]\ ...>
+list of
+.IR nrrd s
+(one for each axis of joint histogram), or, single
+.I nrrd
+that will be sliced along specified axis.
+(1 or more
+.IR nrrd s);
+default:
+.RB \(lq \- \(rq
+.TP
+.B \-o\fR\ <\fInout\fR>\ ,\ \fB\-\-output\fR\ <\fInout\fR>
+output
+.I nrrd
+.RI ( string );
+default:
+.RB \(lq \- \(rq
+.SH "SEE\ ALSO"
+.BR unu (1)
diff --git a/unu-join.1 b/unu-join.1
new file mode 100644
index 0000000..011a0ae
--- /dev/null
+++ b/unu-join.1
@@ -0,0 +1,89 @@
+.TH UNU\-JOIN "1" "August 2021" "" "User Commands"
+.SH NAME
+unu\-join \- connect slices and/or slabs into a bigger
+.I nrrd
+.SH SYNOPSIS
+.B unu\ join
+.RI [ @file \ ...]
+.BR \-i , \-\-input
+.RI < nin0 \ ...>
+.BR \-a , \-\-axis
+.RI < axis >
+.RB [ \-incr ]
+.RB [ \-l , \-\-label
+.RI < label >]
+.RB [ \-mm , \-\-minmax
+.RI < min \ max >]
+.RB [ \-sp , \-\-spacing
+.RI < spc >]
+.RB [ \-o , \-\-output
+.RI < nout >]
+.SH DESCRIPTION
+Connect slices and/or slabs into a bigger
+.IR nrrd .
+Can stich images into volumes, or tile images side by side, or attach images
+onto volumes.
+If
+there are many many files to name in the
+.RB \(lq \-i \(rq
+option, and using wildcards won\(cqt work, consider putting the list of
+filenames into a separate text file (e.g.
+.RB \(lq slices.txt \(rq),
+and then name this file as a response file (e.g.
+.RB \(lq \-i\ s(a)lices.txt \(rq).
+This command now allows you to set the same pieces of
+information that previously had to be set with
+.BR unu\-axinfo (1):
+label, spacing, and min/max.
+These can be use whether the join axis is new (because of
+.RB \(lq \-incr \(rq)
+or not.
+.IP \(bu +2n
+Uses
+.B nrrdJoin
+.SH OPTIONS
+.TP
+.IR @file \ ...
+response file(s) containing command-line arguments
+.TP
+.B \-i\fR\ <\fInin0\fR\ ...>\ ,\ \fB\-\-input\fR\ <\fInin0\fR\ ...>
+everything to be joined together (1 or more
+.IR nrrd s)
+.TP
+.B \-a\fR\ <\fIaxis\fR>\ ,\ \fB\-\-axis\fR\ <\fIaxis\fR>
+axis to join along
+.RI ( unsigned\ int )
+.TP
+.B \-incr
+in situations where the join axis is
+.B not
+among the existing axes of the input
+.IR nrrd s,
+then this flag signifies that the join axis should be
+.BR inserted ,
+and the output dimension should be one greater than input dimension.
+Without this flag, the
+.IR nrrd s
+are joined side-by-side, along an existing axis.
+.TP
+.B \-l\fR\ <\fIlabel\fR>\ ,\ \fB\-\-label\fR\ <\fIlabel\fR>
+label to associate with join axis
+.RI ( string )
+.TP
+.B \-mm\fR\ <\fImin\fR\ \fImax\fR>\ ,\ \fB\-\-minmax\fR\ <\fImin\fR\ \fImax\fR>
+min and max values along join axis (2
+.IR double s)
+.TP
+.B \-sp\fR\ <\fIspc\fR>\ ,\ \fB\-\-spacing\fR\ <\fIspc\fR>
+spacing between samples along join axis
+.RI ( double )
+.TP
+.B \-o\fR\ <\fInout\fR>\ ,\ \fB\-\-output\fR\ <\fInout\fR>
+output
+.I nrrd
+.RI ( string );
+default:
+.RB \(lq \- (rq
+.SH "SEE\ ALSO"
+.BR unu (1),
+.BR unu\-axinfo (1)
diff --git a/unu-lut.1 b/unu-lut.1
new file mode 100644
index 0000000..1812278
--- /dev/null
+++ b/unu-lut.1
@@ -0,0 +1,104 @@
+.TH UNU\-LUT "1" "August 2021" "" "User Commands"
+.SH NAME
+unu\-lut \- map
+.I nrrd
+through one univariate lookup table
+.SH SYNOPSIS
+.B unu\ lut
+.BR \-m , \-\-map
+.RI < lut >
+.RB [ \-r , \-\-rescale ]
+.RB [ \-min , \-\-minimum
+.RI < value >]
+.RB [ \-max , \-\-maximum
+.RI < value >]
+.RB [ \-blind8
+.RI < bool >]
+.RB [ \-t , \-\-type
+.RI < type >]
+.RB [ \-i , \-\-input
+.RI < nin >]
+.RB [ \-o , \-\-output
+.RI < nout >]
+.SH DESCRIPTION
+Map
+.I nrrd
+through one univariate lookup table (itself represented as a
+.IR nrrd ).
+The lookup table can be 1D, in which case the output has the same dimension as
+the input, or 2D, in which case the output has one more dimension than the
+input, and each value is mapped to a scanline (along axis
+.BR 0 )
+from the lookup table.
+.IP \(bu +2n
+Uses
+.B nrrdApply1DLut
+.SH OPTIONS
+.TP
+.B \-m\fR\ <\fIlut\fR>\ ,\ \fB\-\-map\fR\ <\fIlut\fR>
+lookup table to map input
+.I nrrd
+through
+.TP
+.B \-r\fR ,\ \fB\-\-rescale
+rescale the input values from the input range to the lut domain.
+The lut domain is either explicitly defined by the axis
+.IR min , max
+along axis
+.B 0
+or
+.BR 1 ,
+or, it is implicitly defined as zero to the length of that axis minus one.
+.TP
+.B \-min\fR\ <\fIvalue\fR>\ ,\ \fB\-\-minimum\fR\ <\fIvalue\fR>
+Low end of input range.
+Defaults to lowest value found in input
+.IR nrrd .
+Explicitly setting this is useful only with rescaling
+.RB (\(lq \-r \(rq)
+.RI ( double )
+.TP
+.B \-max\fR\ <\fIvalue\fR>\ ,\ \fB\-\-maximum\fR\ <\fIvalue\fR>
+High end of input range.
+Defaults to highest value found in input
+.IR nrrd .
+Explicitly setting this is useful only with rescaling
+.RB (\(lq \-r \(rq)
+.RI ( double )
+.TP
+.B \-blind8\fR\ <\fIbool\fR>
+Whether to know the range of 8-bit data blindly
+.RI ( uchar
+is always
+.BR [0,255] ,
+.I signed\ char
+is
+.BR [-128,127] ).
+Explicitly setting this is useful only with rescaling
+.RB (\(lq \-r \(rq)
+.RI ( bool );
+default:
+.RB \(lq true \(rq
+.TP
+.B \-t\fR\ <\fItype\fR>\ ,\ \fB\-\-type\fR\ <\fItype\fR>
+specify the type
+.RB (\(lq int \(rq,
+.RB \(lq float \(rq,
+etc.) of the output
+.IR nrrd .
+By default (not using this option), the output type is the lut\(cqs type.
+.TP
+.B \-i\fR\ <\fInin\fR>\ ,\ \fB\-\-input\fR\ <\fInin\fR>
+input
+.I nrrd
+.TP
+.B \-o\fR\ <\fInout\fR>\ ,\ \fB\-\-output\fR\ <\fInout\fR>
+output
+.I nrrd
+.RI ( string );
+default:
+.RB \(lq \- \(rq
+.SH "SEE\ ALSO"
+.BR unu (1),
+.BR unu\-mlut (1),
+.BR unu\-lut2 (1)
diff --git a/unu-lut2.1 b/unu-lut2.1
new file mode 100644
index 0000000..f6871c2
--- /dev/null
+++ b/unu-lut2.1
@@ -0,0 +1,114 @@
+.TH UNU\-LUT2 "1" "August 2021" "" "User Commands"
+.SH NAME
+unu\-lut2 \- map
+.I nrrd
+through a bivariate lookup table
+.SH SYNOPSIS
+.B unu\ lut2
+.BR \-m , \-\-map
+.RI < lut >
+.RB [ \-r , \-\-rescale
+.RI < bool \ bool >]
+.RB [ \-min , \-\-minimum
+.RI < value >]
+.RB [ \-max , \-\-maximum
+.RI < value >]
+.RB [ \-blind8
+.RI < bool >]
+.RB [ \-t , \-\-type
+.RI < type >]
+.RB [ \-i , \-\-input
+.RI < nin >]
+.RB [ \-o , \-\-output
+.RI < nout >]
+.SH DESCRIPTION
+Map
+.I nrrd
+through a bivariate lookup table (itself represented as a
+.IR nrrd ).
+The lookup table can be 2D, in which case the output has the same dimension as
+the input, or 3D, in which case the output has one more dimension than the
+input, and each value is mapped to a scanline (along axis
+.BR 0 )
+from the lookup table.
+In any case, axis
+.B 0
+of the input must have length two.
+.IP \(bu +2n
+Uses
+.B nrrdApply2DLut
+.SH OPTIONS
+.TP
+.B \-m\fR\ <\fIlut\fR>\ ,\ \fB\-\-map\fR\ <\fIlut\fR>
+lookup table to map input
+.I nrrd
+through
+.TP
+.B \-r\fR\ <\fIbool\fR\ \fIbool\fR>\ ,\ \fB\-\-rescale\fR\ <\fIbool\fR\ \fIbool\fR>
+rescale one or both of the input values from the input range to the lut domain.
+The lut domain is either explicitly defined by the axis
+.IR min , max
+along axis
+.B 0
+or
+.BR 1 ,
+or, it is implicitly defined as zero to the length of that axis minus one.
+(2
+.IR bool s);
+default:
+.RB \(lq false\ false \(rq
+.TP
+.B \-min\fR\ <\fImin0\fR\ \fImin1\fR>\ ,\ \fB\-\-minimum\fR\ <\fImin0\fR\ \fImin1\fR>
+Low end of input range.
+Defaults to lowest values found in input
+.IR nrrd .
+Explicitly setting this is useful only with rescaling
+.RB (\(lq \-r \(rq)
+(2
+.IR double s)
+.TP
+.B \-max\fR\ <\fImax0\fR\ \fImax1\fR>\ ,\ \fB\-\-maximum\fR\ <\fImax0\fR\ \fImax1\fR>
+High end of input range.
+Defaults to highest values found in input
+.IR nrrd .
+Explicitly setting this is useful only with rescaling
+.RB (\(lq \-r \(rq)
+(2
+.IR double s)
+.TP
+.B \-blind8\fR\ <\fIbool\fR>
+Whether to know the range of 8-bit data blindly
+.RI ( uchar
+is always
+.BR [0,255] ,
+.I signed\ char
+is
+.BR [-128,127] ).
+Explicitly setting this is useful only with rescaling
+.RB (\(lq \-r \(rq)
+.RI ( bool );
+default:
+.RB \(lq true \(rq
+.TP
+.B \-t\fR\ <\fItype\fR>\ ,\ \fB\-\-type\fR\ <\fItype\fR>
+specify the type
+.RB (\(lq int \(rq,
+.RB \(lq float \(rq,
+etc.) of the output
+.IR nrrd .
+By default (not using this option), the output type is the lut\(cqs type.
+.TP
+.B \-i\fR\ <\fInin\fR>\ ,\ \fB\-\-input\fR\ <\fInin\fR>
+input
+.I nrrd
+.TP
+.B \-o\fR\ <\fInout\fR>\ ,\ \fB\-\-output\fR\ <\fInout\fR>
+output
+.I nrrd
+.RI ( string );
+default:
+.RB \(lq \- \(rq
+.SH "SEE\ ALSO"
+.BR unu (1),
+.BR unu\-lut (1),
+.BR unu\-mlut (1)
diff --git a/unu-make.1 b/unu-make.1
new file mode 100644
index 0000000..d5a93b3
--- /dev/null
+++ b/unu-make.1
@@ -0,0 +1,383 @@
+.TH UNU\-MAKE "1" "May 2021" "" "User Commands"
+.SH NAME
+unu\-make \- create a \fInrrd\fR (or \fInrrd\fR header) from scratch
+.SH SYNOPSIS
+.B unu\ make
+.RI [ @file \ ...]
+.RB [ \-h ]
+.RB [ \-i , \-\-input
+.RI < file \ ...>]
+.BR \-t , \-\-type
+.RI < type >
+.BR \-s , \-\-size
+.RI < sz0 \ sz1 \ ...>
+.RB [ \-fd , \-\-filedim
+.RI < dim >]
+.RB [ \-sp , \-\-spacing
+.RI < sp0 \ sp1 \ ...>]
+.RB [ \-th , \-\-thickness
+.RI < th0 \ th1 \ ...>]
+.RB [ \-k , \-\-kind
+.RI < k0 \ k1 \ ...>]
+.RB [ \-cn , \-\-centering
+.RI < c0 \ c1 \ ...>]
+.RB [ \-l , \-\-label
+.RI < lb0 \ lb1 \ ...>]
+.RB [ \-u , \-\-unit
+.RI < un0 \ un1 \ ...>]
+.RB [ \-c , \-\-content
+.RI < content >]
+.RB [ \-ls , \-\-lineskip
+.RI < num >]
+.RB [ \-bs , \-\-byteskip
+.RI < num >]
+.RB [ \-e , \-\-encoding
+.RI < enc >]
+.RB [ \-en , \-\-endian
+.RI < end >]
+.RB [ \-kv , \-\-keyvalue
+.RI < key/val \ ...>]
+.RB [ \-spc , \-\-space
+.RI < space >]
+.RB [ \-orig , \-\-origin
+.RI < origin >]
+.RB [ \-dirs , \-\-directions
+.RI < v0 \ v1 \ ...>]
+.RB [ \-mf , \-\-measurementframe
+.RI < v0 \ v1 \ ...>]
+.RB [ \-spu , \-\-spaceunit
+.RI < su0 \ su1 \ ...>]
+.RB [ \-o , \-\-output
+.RI < nout >]
+.RB [ \-od , \-\-outputdata
+.RI < name >]
+.SH DESCRIPTION
+Create a
+.I nrrd
+(or
+.I nrrd
+header) from scratch.
+The data can be in one or more files, or coming from
+.BR stdin (3).
+This provides an easy way of specifying the information about some data as to
+wrap it in a NRRD file, either to pass on for further
+.BR unu (1)
+processing, or to save to disk.
+Note that with
+.RB \(lq \-h \(rq,
+this creates a detached
+.I nrrd
+header file, without ever reading or writing data files.
+.P
+When using multiple datafiles, the data from each is simply concatenated in
+memory (as opposed to interleaving along a faster axis).
+Keep in mind that all the options below refer to the finished data segment
+resulting from joining all the data pieces together, except for
+.RB \(lq \-ls \(rq,
+.RB \(lq \-bs \(rq,
+and
+.RB \(lq \-e \(rq,
+which apply (uniformly) to the individual data files.
+Use the
+.RB \(lq \-fd \(rq,
+option when the things being joined together are not slices of the final
+result, but slabs or scanlines.
+It may be easier to put multiple filenames in a response file; there can be one
+or more filenames per line of the response file. You can also use a
+.BR sprintf (3)-style
+format to identify a numbered range of files, so for example
+.RB \(lq \-i\ I.%03d\ 1\ 90\ 1 \(rq
+refers to
+.BR I.001 ,\ I.002 ,\ ...\ I.090 ,
+using the inclusive range from the first to the second integer (following the
+.BR sprintf (3)-style
+format), in steps of the third.
+Can optionally give a fourth integer to serve same role as
+.RB \(lq \-fd \(rq.
+.TP
+.B NOTE:
+For the
+.RB \(lq \-l \(rq
+(labels),
+.RB \(lq \-u \(rq
+(units), and
+.RB \(lq \-spu \(rq
+(space units) options below, you can use a single unquoted period
+.RB ( . )
+to signify an empty string.
+This creates a convenient way to convey something that the shell doesn\(cqt
+make it easy to convey.
+Shell expansion weirdness also requires the use of
+quotes
+around the arguments to
+.RB \(lq \-orig \(rq
+(space origin),
+.RB \(lq \-dirs \(rq
+(space directions), and
+.RB \(lq \-mf \(rq
+(measurement frame).
+.P
+Uses various components of file and data IO, but currently there is no library
+function that encapsulates the functionality here.
+.SH OPTIONS
+.TP
+.IR @file \ ...\:
+response file(s) containing command-line arguments
+.TP
+.B \-h
+Generate header
+.BR ONLY :
+don\(cqt write out the whole
+.IR nrrd ,
+don\(cqt even bother reading the input data, just output the detached
+.I nrrd
+header file (usually with a
+.RB \(lq .nhdr \(rq
+extension) determined by the options below.
+The single constraint is that detached headers are incompatible with using
+.BR stdin (3)
+as the data source.
+.TP
+.B \-i\fR\ <\fIfile\fR\ ...>\ ,\ \fB\-\-input\fR\ <\fIfile\fR\ ...>
+Filename(s) of data file(s); use
+.RB \(lq \- \(rq
+for
+.BR stdin (3).
+.BR OR ,
+can use
+.BR sprintf (3)-style
+format for identifying a range of numbered files, see above for details.
+(1 or more
+.IR string s);
+default:
+.RB \(lq \- \(rq
+.TP
+.B \-t\fR\ <\fItype\fR>\ ,\ \fB\-\-type\fR\ <\fItype\fR>
+type of data (e.g.\:
+.RB \(lq uchar \(rq,
+.RB \(lq int \(rq,
+.RB \(lq float \(rq,
+.RB \(lq double \(rq,
+etc.)
+.TP
+.B \-s\fR\ <\fIsz0\fR\ \fIsz1\fR\ ...>\ ,\ \fB\-\-size\fR\ <\fIsz0\fR\ \fIsz1\fR\ ...>
+number of samples along each axis (and implicit indicator of dimension of
+.IR nrrd )
+(1 or more
+.IR size_t s)
+.TP
+.B \-fd\fR\ <\fIdim\fR>\ ,\ \fB\-\-filedim\fR\ <\fIdim\fR>
+When using
+.B multiple
+input data files (to
+.RB \(lq \-i \(rq),
+what is the dimension of the array data in each individual file.
+By default (not using this option), this dimension is assumed to be one less
+than the whole data dimension.
+.RI ( unsigned\ int );
+default:
+.RB \(lq 0 \(rq)
+.TP
+.B \-sp\fR\ <\fIsp0\fR\ \fIsp1\fR\ ...>\ ,\ \fB\-\-spacing\fR\ <\fIsp0\fR\ \fIsp1\fR\ ...>
+spacing between samples on each axis.
+Use
+.RB \(lq nan \(rq
+for any non-spatial axes (e.g. spacing between red, green, and blue along axis
+0 of interleaved RGB image data) (1 or more
+.IR double s)
+.TP
+.B \-th\fR\ <\fIth0\fR\ \fIth1\fR\ ...>\ ,\ \fB\-\-thickness\fR\ <\fIth0\fR\ \fIth1\fR\ ...>
+thickness of region represented by one sample along each axis.
+As with spacing, use
+.RB \(lq nan \(rq
+for any non-spatial axes.
+(1 or more
+.IR double s)
+.TP
+.B \-k\fR\ <\fIk0\fR\ \fIk1\fR\ ...>\ ,\ \fB\-\-kind\fR\ <\fIk0\fR\ \fIk1\fR\ ...>
+what \(lqkind\(rq is each axis, from the
+.I nrrdKind
+.I airEnum
+(e.g.
+.BR space ,
+.BR time ,
+.BR 3-vector ,
+.BR 3D-masked-symmetric-matrix ,
+or
+.RB \(lq none \(rq
+to signify no kind) (1 or more
+.IR string s)
+.TP
+.B \-cn\fR\ <\fIc0\fR\ \fIc1\fR\ ...>\ ,\ \fB\-\-centering\fR\ <\fIc0\fR\ \fIc1\fR\ ...>
+kind of centering
+.RB ( node
+or
+.RB cell )
+for each axis, or
+.RB \(lq none \(rq
+to signify no centering) (1 or more
+.IR string s)
+.TP
+.B \-l\fR\ <\fIlb0\fR\ \fIlb1\fR\ ...>\ ,\ \fB\-\-label\fR\ <\fIlb0\fR\ \fIlb1\fR\ ...>
+short string labels for each of the axes (1 or more
+.IR string s)
+.TP
+.B \-u\fR\ <\fIun0\fR\ \fIun1\fR\ ...>\ ,\ \fB\-\-unit\fR\ <\fIun0\fR\ \fIun1\fR\ ...>
+short strings giving units for each of the axes (1 or more
+.TP
+.B \-c\fR\ <\fIcontent\fR>\ ,\ \fB\-\-content\fR\ <\fIcontent\fR>
+Specifies the content string of the
+.IR nrrd ,
+which is built upon by many
+.I nrrd
+functions to record a history of operations
+.RI ( string )
+.TP
+.B \-ls\fR\ <\fInum\fR>\ ,\ \fB\-\-lineskip\fR\ <\fInum\fR>
+number of ascii lines to skip before reading data
+.RI ( int )
+default:
+.RB \(lq 0 \(rq
+.TP
+.B \-bs\fR\ <\fInum\fR>\ ,\ \fB\-\-byteskip\fR\ <\fInum\fR>
+number of bytes to skip (after skipping ascii lines, if any) before reading
+data.
+Can use
+.RB \(lq \-bs\ \-1 \(rq
+to skip a binary header of unknown length in raw-encoded data
+.RI ( long int )
+default:
+.RB \(lq 0 \(rq
+.TP
+.B \-e\fR\ <\fIenc\fR>\ ,\ \fB\-\-encoding\fR\ <\fIenc\fR>
+encoding of input data.
+Possibilities include:
+.RS +7n
+.IP \(bu 2
+.RB \(lq raw \(rq
+raw encoding
+.IP \(bu 2
+.RB \(lq ascii \(rq
+ascii values, one scanline per line of text, values within line are delimited
+by space, tab, or comma
+.IP \(bu 2
+.RB \(lq hex \(rq
+two hex digits per byte
+.IP \(bu 2
+.RB \(lq gzip \(rq,\ \(lq gz \(rq
+gzip compressed raw data
+.IP \(bu 2
+.RB \(lq bzip2 \(rq,\ \(lq bz2 \(rq
+bzip2 compressed raw data
+.P
+default:
+.RB \(lq raw \(rq
+.RE
+.TP
+.B \-kv\fR\ <\fIkey/val\fR\ ...>\ ,\ \fB\-\-keyvalue\fR\ <\fIkey/val\fR\ ...>
+key/value string pairs to be stored in
+.IR nrrd .
+Each key/value pair must be a single string (put it in
+.BR \(dq\(dq s
+if the key or the value contain spaces).
+The format of each pair is
+.RI \(lq key\fB:=\fIvalue \(rq,
+with no spaces before or after
+.RB \(lq := \(rq.
+(1 or more
+.IR string s)
+.TP
+.B \-spc\fR\ <\fIspace\fR>\ ,\ \fB\-\-space\fR\ <\fIspace\fR>
+identify the space (e.g.\:
+.RB \(lq RAS \(rq,
+.RB \(lq LPS \(rq)
+in which the array conceptually lives, from the
+.I nrrdSpace
+.IR airEnum ,
+which in turn determines the dimension of the space.
+Or, use an
+.RB integer> 0
+to give the dimension of a space that
+.I nrrdSpace
+doesn\(cqt know about.
+By default (not using this option), the enclosing space is set as unknown.
+.RI ( string )
+.TP
+.B \-orig\fR\ <\fIorigin\fR>\ ,\ \fB\-\-origin\fR\ <\fIorigin\fR>
+.RB ( NOTE:
+must quote vector)
+the origin in space of the array: the location of the center of the first
+sample, of the form
+.RB \(lq (x,y,z) \(rq
+(or however many coefficients are needed for the chosen space).
+Quoting the vector is needed to stop interpretation from the shell
+.RI ( string )
+.TP
+.B \-dirs\fR\ <\fIv0\fR\ \fIv1\fR\ ...>\ ,\ \fB\-\-directions\fR\ <\fI\fIv0\fR\ \fIv1\fR\ ...>
+.RB ( NOTE:
+must quote whole vector list)
+The \(lqspace directions\(rq: the vectors in space spanned by incrementing (by
+one) each axis index (the column vectors of the index-to-world matrix
+transform),
+.BR OR ,
+.RB \(lq none \(rq
+for non-spatial axes.
+Give one vector per axis.
+(Quoting around whole vector list, not individually, is needed because of
+limitations in the parser)
+.RI ( string )
+.TP
+.B \-mf\fR\ <\fIv0\fR\ \fIv1\fR\ ...>\ ,\ \fB\-\-measurementframe\fR\ <\fI\fIv0\fR\ \fIv1\fR\ ...>
+.RB ( NOTE:
+must quote whole vector list)
+Each vector is a
+.I column
+vector of the matrix which transforms from coordinates in measurement frame (in
+which the coefficients of vectors and tensors are given) to coordinates of
+world space (given with
+.RB \(lq \-spc \(rq).
+This is not a per-axis field: the column vectors comprise a
+.IR D -by- D
+square matrix, where
+.I D
+is the dimension of world space.
+.RI ( string )
+.TP
+.B \-spu\fR\ <\fIsu0\fR\ \fIsu1\fR\ ...>\ ,\ \fB\-\-spaceunit\fR\ <\fI\fIsu0\fR\ \fIsu1\fR\ ...>
+short
+.IR string s
+giving units with which the coefficients of the space origin and direction
+vectors are measured.
+(1 or more
+.IR string s)
+.TP
+.B \-o\fR\ <\fInout\fR>\ ,\ \fB\-\-output\fR\ <\fInout\fR>
+output filename.
+If
+.RB \(lq \-h \(rq
+has been used, the output file is always a detached header.
+Otherwise, use extension
+.RB \(lq .nrrd \(rq
+to signal creation of self-contained
+.IR nrrd ,
+and
+.RB \(lq .nhdr \(rq
+to signal creating of a detached header with (single) data file.
+.RI ( string );
+default:
+.RB \(lq \- \(rq
+.TP
+.B \-od\fR\ <\fIname\fR>\ ,\ \fB\-\-outputdata\fR\ <\fIname\fR>
+when
+.I not
+using
+.RB \(lq \-h \(rq
+and saving to a
+.RB \(lq .nhdr \(rq
+file, using this option allows you to explicitly name the data file, instead of
+(by default, not using this option) having it be the same filename base as the
+header file.
+.RI ( string )
+.SH "SEE\ ALSO"
+.BR unu (1),
+.BR sprintf (3)
diff --git a/unu-minmax.1 b/unu-minmax.1
new file mode 100644
index 0000000..11f0533
--- /dev/null
+++ b/unu-minmax.1
@@ -0,0 +1,49 @@
+.TH UNU\-MINMAX "1" "May 2021" "" "User Commands"
+.SH NAME
+unu\-minmax \- print out min and max values in one or more
+.IR nrrd s
+.SH SYNOPSIS
+.B unu\ minmax
+.BR \-blind8
+.RI < bool >
+.RI < nin1 \ ...>
+.SH DESCRIPTION
+Print out min and max values in one or more
+.IR nrrd s.
+Unlike other commands, this doesn\(cqt produce a
+.IRnrrd .
+It only prints to standard out the min and max values found in the input
+.IR nrrd (s),
+and it also indicates if there are non-existent values.
+.IP \(bu +2n
+Uses
+.B nrrdRangeNewSet
+.SH OPTIONS
+.TP
+.B \-blind8\fR\ <\fIbool\fR>
+whether to blindly assert the range of 8-bit data, without actually going
+through the data values, i.e.
+.I uchar
+is always
+.BR [0,255] ,
+.I signed\ char
+is
+.BR [\-128,127] .
+Note that even if you do not use this option, the default
+.RB ( false )
+is potentialy over-riding the effect of environment variable
+.BR NRRD_STATE_BLIND_8_BIT_RANGE ;
+see
+.BR unu\-env (1)
+.RI ( bool );
+default:
+.RB \(lq false \(rq
+.TP
+.RI < nin1 \ ...>
+input
+.IR nrrd (s)
+(1 or more
+.IR string s)
+.SH "SEE\ ALSO"
+.BR unu (1),
+.BR unu\-env (1)
diff --git a/unu-mlut.1 b/unu-mlut.1
new file mode 100644
index 0000000..e4f0b8a
--- /dev/null
+++ b/unu-mlut.1
@@ -0,0 +1,115 @@
+.TH UNU\-MLUT "1" "August 2021" "" "User Commands"
+.SH NAME
+unu\-mlut \- map
+.I nrrd
+through whole
+.I nrrd
+of univariate lookup tables
+.SH SYNOPSIS
+.B unu\ mlut
+.BR \-m , \-\-map
+.RI < mlut \ ...>
+.RB [ \-r , \-\-rescale ]
+.RB [ \-min , \-\-minimum
+.RI < value >]
+.RB [ \-max , \-\-maximum
+.RI < value >]
+.RB [ \-blind8
+.RI < bool >]
+.RB [ \-t , \-\-type
+.RI < type >]
+.RB [ \-i , \-\-input
+.RI < nin >]
+.RB [ \-o , \-\-output
+.RI < nout >]
+.SH DESCRIPTION
+Map
+.I nrrd
+through whole
+.I nrrd
+of univariate lookup tables, with one lookup table per element of input
+.IR nrrd .
+The multiple tables are stored in a
+.I nrrd
+with a dimension which is either 1 or 2 more than the dimension of the input
+.IR nrrd ,
+resulting in an output which has either the same or one more dimension than the
+input, resptectively.
+.IP \(bu +2n
+Uses
+.B nrrdApplyMulti1DLut
+.SH OPTIONS
+.TP
+.B \-m\fR\ <\fIlut\fR>\ ,\ \fB\-\-map\fR\ <\fIlut\fR>
+one
+.I nrrd
+of lookup tables to map input
+.I nrrd
+through, or, list of
+.IR nrrd s
+which contain the individual entries of the lookup table at each voxel, which
+will be joined together.
+(1 or more
+.IR nrrd s)
+.TP
+.B \-r\fR ,\ \fB\-\-rescale
+rescale the input values from the input range to the lut domain.
+The lut domain is either explicitly defined by the axis
+.IR min , max
+along axis
+.B 0
+or
+.BR 1 ,
+or, it is implicitly defined as zero to the length of that axis minus one.
+.TP
+.B \-min\fR\ <\fIvalue\fR>\ ,\ \fB\-\-minimum\fR\ <\fIvalue\fR>
+Low end of input range.
+Defaults to lowest value found in input
+.IR nrrd .
+Explicitly setting this is useful only with rescaling
+.RB (\(lq \-r \(rq)
+.RI ( double )
+.TP
+.B \-max\fR\ <\fIvalue\fR>\ ,\ \fB\-\-maximum\fR\ <\fIvalue\fR>
+High end of input range.
+Defaults to highest value found in input
+.IR nrrd .
+Explicitly setting this is useful only with rescaling
+.RB (\(lq \-r \(rq)
+.RI ( double )
+.TP
+.B \-blind8\fR\ <\fIbool\fR>
+Whether to know the range of 8-bit data blindly
+.RI ( uchar
+is always
+.BR [0,255] ,
+.I signed\ char
+is
+.BR [-128,127] ).
+Explicitly setting this is useful only with rescaling
+.RB (\(lq \-r \(rq)
+.RI ( bool );
+default:
+.RB \(lq true \(rq
+.TP
+.B \-t\fR\ <\fItype\fR>\ ,\ \fB\-\-type\fR\ <\fItype\fR>
+specify the type
+.RB (\(lq int \(rq,
+.RB \(lq float \(rq,
+etc.) of the output
+.IR nrrd .
+By default (not using this option), the output type is the lut\(cqs type.
+.TP
+.B \-i\fR\ <\fInin\fR>\ ,\ \fB\-\-input\fR\ <\fInin\fR>
+input
+.I nrrd
+.TP
+.B \-o\fR\ <\fInout\fR>\ ,\ \fB\-\-output\fR\ <\fInout\fR>
+output
+.I nrrd
+.RI ( string );
+default:
+.RB \(lq \- \(rq
+.SH "SEE\ ALSO"
+.BR unu (1),
+.BR unu\-lut (1)
diff --git a/unu-mrmap.1 b/unu-mrmap.1
new file mode 100644
index 0000000..e5ddc68
--- /dev/null
+++ b/unu-mrmap.1
@@ -0,0 +1,123 @@
+.TH UNU\-MRMAP "1" "August 2021" "" "User Commands"
+.SH NAME
+unu\-mrmap \- map
+.I nrrd
+through whole
+.I nrrd
+of regular univariate maps
+.SH SYNOPSIS
+.B unu\ mrmap
+.BR \-m , \-\-map
+.RI < mmap \ ...>
+.RB [ \-r , \-\-rescale ]
+.RB [ \-min , \-\-minimum
+.RI < value >]
+.RB [ \-max , \-\-maximum
+.RI < value >]
+.RB [ \-blind8
+.RI < bool >]
+.RB [ \-t , \-\-type
+.RI < type >]
+.RB [ \-i , \-\-input
+.RI < nin >]
+.RB [ \-o , \-\-output
+.RI < nout >]
+.SH DESCRIPTION
+Map
+.I nrrd
+through whole
+.I nrrd
+of regular univariate maps, one map per sample in input.
+The
+.BI \(lq mmap \(rq
+.I nrrd
+has the same dimensional constraints as the
+.BI \(lq mlut \(rq
+.I nrrd
+for
+.BR unu\-mlut (1).
+This functionality is a generalization of
+.BR unu\-3op (1) \ lerp :
+it allows you to lerp through multiple
+.IR nrrd s,
+instead of just two.
+.IP \(bu +2n
+Uses
+.B nrrdApplyMulti1DRegMap
+.SH OPTIONS
+.TP
+.B \-m\fR\ <\fImmap\fR>\ ,\ \fB\-\-map\fR\ <\fImmap\fR>
+one
+.I nrrd
+of regular maps to map input
+.I nrrd
+through, or, list of
+.IR nrrd s
+which contain the individual entries of the map at each voxel, which will be
+joined together.
+(1 or more
+.IR nrrd s)
+.TP
+.B \-r\fR ,\ \fB\-\-rescale
+rescale the input values from the input range to the map domain.
+The map domain is either explicitly defined by the axis
+.IR min , max
+along axis
+.B 0
+or
+.BR 1 ,
+or, it is implicitly defined as zero to the length of that axis minus one.
+.TP
+.B \-min\fR\ <\fIvalue\fR>\ ,\ \fB\-\-minimum\fR\ <\fIvalue\fR>
+Low end of input range.
+Defaults to lowest value found in input
+.IR nrrd .
+Explicitly setting this is useful only with rescaling
+.RB (\(lq \-r \(rq)
+.RI ( double )
+.TP
+.B \-max\fR\ <\fIvalue\fR>\ ,\ \fB\-\-maximum\fR\ <\fIvalue\fR>
+High end of input range.
+Defaults to highest value found in input
+.IR nrrd .
+Explicitly setting this is useful only with rescaling
+.RB (\(lq \-r \(rq)
+.RI ( double )
+.TP
+.B \-blind8\fR\ <\fIbool\fR>
+Whether to know the range of 8-bit data blindly
+.RI ( uchar
+is always
+.BR [0,255] ,
+.I signed\ char
+is
+.BR [-128,127] ).
+Explicitly setting this is useful only with rescaling
+.RB (\(lq \-r \(rq)
+.RI ( bool );
+default:
+.RB \(lq true \(rq
+.TP
+.B \-t\fR\ <\fItype\fR>\ ,\ \fB\-\-type\fR\ <\fItype\fR>
+specify the type
+.RB (\(lq int \(rq,
+.RB \(lq float \(rq,
+etc.) of the output
+.IR nrrd .
+By default (not using this option), the output type is the map\(cqs type.
+.TP
+.B \-i\fR\ <\fInin\fR>\ ,\ \fB\-\-input\fR\ <\fInin\fR>
+input
+.I nrrd
+.TP
+.B \-o\fR\ <\fInout\fR>\ ,\ \fB\-\-output\fR\ <\fInout\fR>
+output
+.I nrrd
+.RI ( string );
+default:
+.RB \(lq \- \(rq
+.SH "SEE\ ALSO"
+.BR unu (1),
+.BR unu\-rmap (1),
+.BR unu\-mlut (1),
+.BR unu\-3op (1)
diff --git a/unu-pad.1 b/unu-pad.1
new file mode 100644
index 0000000..608c2fb
--- /dev/null
+++ b/unu-pad.1
@@ -0,0 +1,92 @@
+.TH UNU\-PAD "1" "August 2021" "" "User Commands"
+.SH NAME
+unu\-pad \- pad along each axis to make a bigger
+.I nrrd
+.SH SYNOPSIS
+.B unu\ pad
+.BR \-min , \-\-minimum
+.RI < pos0 \ ...>
+.BR \-max , \-\-maximum
+.RI < pos0 \ ...>
+.RB [ \-b , \-\-boundary
+.RI < behavior >]
+.RB [ \-v , \-\-value
+.RI < val >]
+.RB [ \-i , \-\-input
+.RI < nin >]
+.RB [ \-o , \-\-output
+.RI < nout >]
+.SH DESCRIPTION
+Pad along each axis to make a bigger
+.IR nrrd .
+.IP \(bu +2n
+Uses
+.B nrrdPad_nva
+.SH OPTIONS
+.TP
+.B \-min\fR\ <\fIpos0\fR\ ...>\ ,\ \fB\-\-minimum\fR\ <\fIpos0\fR\ ...>
+low corner of bounding box.
+.RS +7n
+.IP \(bu 2
+.RI < int >
+gives 0\-based index
+.IP \(bu 2
+.BR M ,
+.BR M\+ <\fIint\fR>,
+.BR M\- <\fIint\fR>
+give index relative to the last sample on the axis
+.RB ( M== \fI#samples \-1 ).
+.RE
+.IP
+(1 or more
+.IR position s)
+.TP
+.B \-max\fR\ <\fIpos0\fR\ ...>\ ,\ \fB\-\-maximum\fR\ <\fIpos0\fR\ ...>
+high corner of bounding box.
+.RS +7n
+.IP \(bu 2
+.RI < int >
+gives 0\-based index
+.IP \(bu 2
+.BR M ,
+.BR M\+ <\fIint\fR>,
+.BR M\- <\fIint\fR>
+give index relative to the last sample on the axis
+.RB ( M== \fI#samples \-1 ).
+.RE
+.IP
+(1 or more
+.IR position s)
+.TP
+.B \-b\fR\ <\fIbehavior\fR>\ ,\ \fB\-\-boundary\fR\ <\fIbehavior\fR>
+How to handle samples beyond the input bounds:
+.RS +7n
+.IP \(bu 2
+.RB \(lq pad \(rq:
+use some specified value
+.IP \(bu 2
+.RB \(lq bleed \(rq:
+extend border values outward
+.IP \(bu 2
+.RB \(lq mirror \(rq:
+repeated reflections
+.IP \(bu 2
+.RB \(lq wrap \(rq:
+wrap-around to other side
+.RE
+.IP
+default:
+.RB \(lq bleed \(rq
+.TP
+.B \-i\fR\ <\fInin\fR>\ ,\ \fB\-\-input\fR\ <\fInin\fR>
+input
+.I nrrd
+.TP
+.B \-o\fR\ <\fInout\fR>\ ,\ \fB\-\-output\fR\ <\fInout\fR>
+output
+.I nrrd
+.RI ( string );
+default:
+.RB \(lq \- \(rq
+.SH "SEE\ ALSO"
+.BR unu (1)
diff --git a/unu-permute.1 b/unu-permute.1
new file mode 100644
index 0000000..5a0eff6
--- /dev/null
+++ b/unu-permute.1
@@ -0,0 +1,38 @@
+.TH UNU\-PERMUTE "1" "August 2021" "" "User Commands"
+.SH NAME
+unu\-permute \- permute ordering of axes
+.SH SYNOPSIS
+.B unu\ permute
+.BR \-p , \-\-permute
+.RI < ax0 \ ax1 \ ...>
+.RB [ \-i , \-\-input
+.RI < nin >]
+.RB [ \-o , \-\-output
+.RI < nout >]
+.SH DESCRIPTION
+Permute ordering of axes.
+The permutation gives the new ordering of the old axes (in 0-based numbering).
+For example, the permutation 0->1, 1->2, 2->0 would be
+.RB \(lq 2\ 0\ 1 \(rq.
+.IP \(bu +2n
+Uses
+.B nrrdAxesPermute
+.SH OPTIONS
+.TP
+.B \-p\fR\ <\fIax0\fR \fIax1\fR\ ...>\ ,\ \fB\-\-permute\fR\ <\fIax0\fR \fIax1\fR\ ...>
+new axis ordering
+(1 or more
+.IR unsigned\ int s)
+.TP
+.B \-i\fR\ <\fInin\fR>\ ,\ \fB\-\-input\fR\ <\fInin\fR>
+input
+.I nrrd
+.TP
+.B \-o\fR\ <\fInout\fR>\ ,\ \fB\-\-output\fR\ <\fInout\fR>
+output
+.I nrrd
+.RI ( string );
+default:
+.RB \(lq \- \(rq
+.SH "SEE\ ALSO"
+.BR unu (1)
diff --git a/unu-project.1 b/unu-project.1
new file mode 100644
index 0000000..a246329
--- /dev/null
+++ b/unu-project.1
@@ -0,0 +1,103 @@
+.TH UNU\-PROJECT "1" "August 2021" "" "User Commands"
+.SH NAME
+unu\-project \- collapse scanlines to scalars along some axis
+.SH SYNOPSIS
+.B unu\ project
+.BR \-a , \-\-axis
+.RI < axis >
+.BR \-m , \-\-measure
+.RI < measr >
+.RB [ \-t , \-\-type
+.RI < type >]
+.RB [ \-i , \-\-input
+.RI < nin >]
+.RB [ \-o , \-\-output
+.RI < nout >]
+.SH DESCRIPTION
+Collapse scanlines to scalars along some axis.
+The scanline is reduced to a single scalar by \(lqmeasuring\(rq all the values
+in the scanline with some measure.
+The output
+.I nrrd
+has dimension one less than input (except when the input is itself 1\-D); the
+output type depends on the measure in a non-trivial way, or it can be set
+explicitly with the
+.RB \(lq \-t \(rq
+option.
+.IP \(bu +2n
+Uses
+.B nrrdProject
+.SH OPTIONS
+.TP
+.B \-a\fR\ <\fIaxis\fR>\ ,\ \fB\-\-axis\fR\ <\fIaxis\fR>
+axis to project along
+.RI ( unsigned\ int )
+.TP
+.B \-m\fR\ <\fImeasr\fR>\ ,\ \fB\-\-measure\fR\ <\fImeasr\fR>
+How to \(lqmeasure\(rq a scanline, by summarizing all its values with a single
+scalar.
+Possibilities include:
+.RS +7n
+.IP \(bu 2
+.RB \(lq min \(rq,
+.RB \(lq max \(rq,
+.RB \(lq mean \(rq,
+.RB \(lq median \(rq,
+.RB \(lq mode \(rq,
+.RB \(lq variance \(rq,
+.RB \(lq skew \(rq:
+(self-explanatory)
+.IP \(bu 2
+.RB \(lq intc \(rq,
+.RB \(lq slope \(rq,
+.RB \(lq error \(rq:
+intercept, slope, and error from line fitting
+.IP \(bu 2
+.RB \(lq stdv \(rq:
+standard deviation
+.IP \(bu 2
+.RB \(lq cov \(rq:
+coefficient of variation
+.IP \(bu 2
+.RB \(lq product \(rq,
+.RB \(lq sum \(rq:
+product or sum of all values
+.IP \(bu 2
+.RB \(lq L1 \(rq,
+.RB \(lq L2 \(rq,
+.RB \(lq NL2 \(rq,
+.RB \(lq RMS \(rq,
+.RB \(lq Linf \(rq:
+different norms
+.IP \(bu 2
+.RB \(lq histo-min \(rq,
+.RB \(lq histo-max \(rq,
+.RB \(lq histo-mean \(rq,
+.RB \(lq histo-median \(rq,
+.RB \(lq histo-mode \(rq,
+.RB \(lq histo-product \(rq,
+.RB \(lq histo-l2 \(rq,
+.RB \(lq histo-sum \(rq,
+.RB \(lq histo-variance \(rq,
+.RB \(lq histo-sd \(rq:
+same measures, but for situations where we\(cqre given not the original values,
+but a histogram of them.
+.RE
+.TP
+.B \-t\fR\ <\fItype\fR>\ ,\ \fB\-\-type\fR\ <\fItype\fR>
+type to use for output.
+By default (not using this option), the output type is determined
+auto-magically.
+.TP
+.B \-i\fR\ <\fInin\fR>\ ,\ \fB\-\-input\fR\ <\fInin\fR>
+input
+.I nrrd
+.TP
+.B \-o\fR\ <\fInout\fR>\ ,\ \fB\-\-output\fR\ <\fInout\fR>
+output
+.I nrrd
+.RI ( string );
+default:
+.RB \(lq \- \(rq
+.SH "SEE\ ALSO"
+.BR unu (1)
diff --git a/unu-quantize.1 b/unu-quantize.1
new file mode 100644
index 0000000..4686f3e
--- /dev/null
+++ b/unu-quantize.1
@@ -0,0 +1,135 @@
+.TH UNU\-QUANTIZE "1" "August 2021" "" "User Commands"
+.SH NAME
+unu\-quantize \- quantize values to 8, 16, or 32 bits
+.SH SYNOPSIS
+.B unu\ quantize
+.BR \-b , \-\-bits
+.RI < bits >
+.RB [ \-min , \-\-minimum
+.RI < value >]
+.RB [ \-max , \-\-maximum
+.RI < value >]
+.RB [ \-hb , \-\-bins
+.RI < bins >]
+.RB [ \-blind8
+.RI < bool >]
+.RB [ \-i , \-\-input
+.RI < nin >]
+.RB [ \-o , \-\-output
+.RI < nout >]
+.SH DESCRIPTION
+Quantize values to 8, 16, or 32 bits.
+Input values can be fixed point (e.g. quantizing ushorts down to uchars) or
+floating point.
+Values are clamped to the min and max before they are quantized, so there is no
+risk of getting
+.B 255
+where you expect
+.B 0
+(with
+.I unsigned char
+output, for example).
+The min and max can be specified explicitly (as a regular number), or in terms
+of percentiles (a number suffixed with
+.RB \(lq % \(rq,
+no space in between).
+This does only linear quantization.
+See also
+.BR unu\-convert (1),
+.BR unu\-2op (1) \ x ,
+and
+.BR unu\-3op (1) \ clamp .
+.IP \(bu +2n
+Uses
+.B nrrdQuantize
+.SH OPTIONS
+.TP
+.B \-b\fR\ <\fIbits\fR>\ ,\ \fB\-\-bits\fR\ <\fIbits\fR>
+Number of bits to quantize down to; determines the type of the output
+.IR nrrd :
+.RS +7n
+.IP \(bu 2
+.RB \(lq 8 \(rq:
+.I unsigned char
+.IP \(bu 2
+.RB \(lq 16 \(rq:
+.I unsigned short
+.IP \(bu 2
+.RB \(lq 32 \(rq:
+.I unsigned int
+.RE
+.TP
+.B \-min\fR\ <\fIvalue\fR>\ ,\ \fB\-\-minimum\fR\ <\fIvalue\fR>
+The value to map to zero, given explicitly as a regular number,
+.BR or ,
+if the number is given with a
+.RB \(lq % \(rq
+suffix, this minimum is specified in terms of the percentage of samples in
+input that are lower.
+.RB \(lq 0% \(rq
+means the lowest input value is used,
+.RB \(lq 1% \(rq
+means that the 1% of the lowest values are all mapped to zero.
+By default (not using this option), the lowest input value is used.
+.RI ( string );
+default:
+.RB \(lq nan \(rq
+.TP
+.B \-max\fR\ <\fIvalue\fR>\ ,\ \fB\-\-maximum\fR\ <\fIvalue\fR>
+The value to map to the highest unsigned integral value, given explicitly as a
+regular number,
+.BR or ,
+if the number is given with a
+.RB \(lq % \(rq
+suffix, this maximum is specified in terms of the percentage of samples in
+input that are higher.
+.RB \(lq 0% \(rq
+means the highest input value is used, which is also the default behavior (same
+as not using this option).
+.RI ( string );
+default:
+.RB \(lq nan \(rq
+.TP
+.B \-hb\fR\ <\fIbins\fR>\ ,\ \fB\-\-bins\fR\ <\fIbins\fR>
+number of bins in histogram of values, for determining min or max by
+percentiles.
+This has to be large enough so that any errant very high or very low values do
+not compress the interesting part of the histogram to an inscrutably small
+number of bins.
+.RI ( unsigned\ int );
+default:
+.RB \(lq 5000 \(rq
+.TP
+.B \-blind8\fR\ <\fIbins\fR>
+if not using
+.RB \(lq \-min \(rq
+or
+.RB \(lq \-max \(rq,
+whether to know the range of 8-bit data blindly
+.RI ( uchar
+is always
+.BR [0,255] ,
+.I signed char
+is
+.BR [-128,127] )
+.RI ( bool );
+default:
+.RB \(lq true \(rq,
+"true"
+.TP
+.B \-i\fR\ <\fInin\fR>\ ,\ \fB\-\-input\fR\ <\fInin\fR>
+input
+.I nrrd
+.TP
+.B \-o\fR\ <\fInout\fR>\ ,\ \fB\-\-output\fR\ <\fInout\fR>
+output
+.I nrrd
+.RI ( string );
+default:
+.RB \(lq \- \(rq
+.SH "SEE\ ALSO"
+.BR unu (1),
+.BR unu\-unquantize (1),
+.BR unu\-convert (1),
+.BR unu\-2op (1) \ x ,
+.BR unu\-3op (1) \ clamp
diff --git a/unu-resample.1 b/unu-resample.1
new file mode 100644
index 0000000..e08f625
--- /dev/null
+++ b/unu-resample.1
@@ -0,0 +1,344 @@
+.TH UNU\-RESAMPLE "1" "May 2021" "" "User Commands"
+.SH NAME
+unu\-resample \- filtering and {up,down}sampling with a separable kernel
+.SH SYNOPSIS
+.B unu\ resample
+.RB [ \-old ]
+.BR \-s , \-\-size
+.RI < sz0 \ ...>
+.RB [ \-off , \-\-offset
+.RI [< off0 \ ...>]]
+.RB [ \-min , \-\-minimum
+.RI [< min0 \ ...>]]
+.RB [ \-max , \-\-maximum
+.RI [< max0 \ ...>]]
+.RB [ \-k , \-\-kernel
+.RI < kern \ ...>]
+.RB [ \-nrn ]
+.RB [ \-ne , \-\-nonexistent
+.RI < behavior >]
+.RB [ \-b , \-\-boundary
+.RI < behavior >]
+.RB [ \-v , \-\-value
+.RI < value >]
+.RB [ \-t , \-\-type
+.RI < type >]
+.RB [ \-cheap ]
+.RB [ \-c , \-\-center
+.RI < center >]
+.RB [ \-co , \-\-center\-override ]
+.RB [ \-verbose
+.RI < v >]
+.RB [ \-i , \-\-input
+.RI < nin >]
+.RB [ \-o , \-\-output
+.RI < nout >]
+.SH DESCRIPTION
+Filtering and {up,down}sampling with a separable kernel.
+Simplifies access to the
+.B NrrdResampleContext
+functions by assuming (among other things) that the same kernel is used for
+resampling every axis that is being resampled.
+Only required option is
+.RB \(lq \-s \(rq
+to specify which axes to resample and how many output samples to generate.
+Resampling kernel
+.RB \(lq \-k \(rq
+defaults to an interpolating cubic, but many other choices are available.
+By default, resampling an axis resamples the full extent of its samples, but it
+is possible to offset this range via
+.RB \(lq \-off \(rq,
+or to crop and/or pad via
+.RB \(lq \-min \(rq
+and
+.RB \(lq \-max \(rq.
+The resampling respects the difference between cell- and node-centered data,
+but you can over-ride known centering with
+.RB \(lq \-co \(rq.
+.IP \(bu +2n
+Uses the many
+.B nrrdResample*
+functions operating on a
+.B nrrdResampleContext
+.SH OPTIONS
+.TP
+.B \-old
+instead of using the new
+.B nrrdResampleContext
+implementation, use the old
+.B nrrdSpatialResample
+implementation
+.TP
+.B \-s\fR\ <\fIsz0\fR\ ...>\ ,\ \fB\-\-size\fR\ <\fIsz0\fR\ ...>
+For each axis, information about how many samples in output:
+.RS +7n
+.IP \(bu 2
+.RB \(lq = \(rq:
+leave this axis completely untouched: no resampling whatsoever
+.IP \(bu 2
+.RB \(lq x\fIfloat \(rq:
+multiply the number of input samples by
+.IR float ,
+and round to the nearest integer, to get the number of output samples.
+Use
+.RB \(lq x1 \(rq
+to resample the axis but leave the number of samples unchanged
+.IP \(bu 2
+.RB \(lq /\fIfloat \(rq:
+divide number of input samples by
+.I float
+.IP \(bu 2
+.RB \(lq +=\fIuint \(rq,
+.RB \(lq \-=\fIuint \(rq:
+add
+.I uint
+to or subtract
+.I uint
+from number input samples to get number output samples
+.IP \(bu 2
+.RI \(lq uint \(rq:
+exact number of output samples
+.IP \(bu 2
+.RI \(lq a \(rq:
+resample this axis to whatever number of samples preserves the aspect ratio of
+other resampled axes.
+Currently needs to be used on all but one of the resampled axes, if at all.
+.RE
+.IP
+(1 or more sampling specifications)
+.TP
+.B \-off\fR\ [<\fIoff0\fR\ ...>]\ ,\ \fB\-\-offset\fR\ [<\fIoff0\fR\ ...>]
+For each axis, an offset or shift to the position (in index space) of the lower
+end of the sampling domain.
+Either
+.B \-off
+can be used, or
+.B \-min
+and
+.B \-max
+together, or none of these (so that, by default, the full domain of the axis is
+resampled).
+(0 or more
+.IR double s);
+default: \(lq\(rq
+.TP
+.B \-min\fR\ [<\fImin0\fR\ ...>]\ ,\ \fB\-\-minimum\fR\ [<\fImin0\fR\ ...>]
+For each axis, the lower end (in index space) of the domain of the resampling.
+Either
+.B \-off
+can be used, or
+.B \-min
+and
+.B \-max
+together, or none of these (so that, by default, the full domain of the axis is
+resampled).
+(0 or more
+.IR double s);
+default: \(lq\(rq
+.TP
+.B \-max\fR\ [<\fImax0\fR\ ...>]\ ,\ \fB\-\-maximum\fR\ [<\fImax0\fR\ ...>]
+For each axis, the upper end (in index space) of the domain of the resampling.
+Either
+.B \-off
+can be used, or
+.B \-max
+and
+.B \-max
+together, or none of these (so that, by default, the full domain of the axis is
+resampled).
+(0 or more
+.IR double s);
+default: \(lq\(rq
+.TP
+.B \-k\fR\ <\fIkern\fR>\ , \fB\-\-kernel\fR\ <\fIkern\fR>
+The kernel to use for resampling.
+Kernels logically live in the input index space for upsampling, and in the
+output index space for downsampling.
+Possibilities include:
+.RS +7n
+.IP \(bu 2
+.RB \(lq box \(rq:
+nearest neighbor interpolation on upsampling, and uniform averaging on
+downsampling
+.IP \(bu 2
+.RB \(lq cheap \(rq:
+nearest neighbor interpolation for upsampling, and non-blurring sub-sampling
+(pick subset of input samples) on downsampling
+.IP \(bu 2
+.RB \(lq tent \(rq:
+linear interpolation
+.IP \(bu 2
+.RB \(lq cubic:\fIB\fR,\fIC \(rq:
+Mitchell/Netravali BC-family of cubics:
+.IP
+.RB \(lq cubic:1,0 \(rq:
+B-spline; maximal blurring
+.IP
+.RB \(lq cubic:0,0.5 \(rq:
+Catmull-Rom; good interpolating kernel
+.IP \(bu 2
+.RB \(lq c4h \(rq:
+6-sample-support, C^4 continuous, accurate
+.IP \(bu 2
+.RB \(lq c4hai \(rq:
+discrete pre-filter to make
+.B c4h
+interpolate
+.IP \(bu 2
+.RB \(lq bspl3 \(rq,
+.RB \(lq bspl5 \(rq,
+.RB \(lq bspl7 \(rq:
+cubic (same as
+.BR cubic:1,0 ),
+quintic, and 7th order B-spline
+.IP \(bu 2
+.RB \(lq bspl3ai \(rq,
+.RB \(lq bspl5ai \(rq,
+.RB \(lq bspl7ai \(rq:
+discrete pre-filters to make
+.BR bspl3 ,
+.BR bspl5 ,
+.B bspl7
+interpolate
+.IP \(bu 2
+.RB \(lq hann:\fIR \(rq:
+Hann (cosine bell) windowed sinc, radius
+.I R
+.IP \(bu 2
+.RB \(lq black:\fIR \(rq:
+Blackman windowed sinc, radius
+.I R
+.IP \(bu 2
+.RB \(lq gauss:\fIS\fB,\fIC \(rq:
+Gaussian blurring, with standard deviation
+.I S
+and cut-off at
+.I C
+standard deviations
+.IP \(bu 2
+.RB \(lq dgauss:\fIS\fB,\fIC \(rq:
+Lindeberg\(cqs discrete Gaussian.
+.RE
+.IP
+default:
+.RB \(lq cubic:0,0.5 \(rq
+.TP
+.B \-nrn
+do NOT do per-pass kernel weight renormalization.
+Doing the renormalization is not a performance hit (hence is enabled by
+default), and the renormalization is sometimes needed to avoid \(lqgrating\(rq
+on non-integral down-sampling.
+Disabling the renormalization is needed for correct results with artificially
+narrow kernels.
+.TP
+.B \-ne\fR\ <\fIbehavior\fR>\ , \fB\-\-nonexistent\fR\ <\fIbehavior\fR>
+When resampling floating-point values, how to handle non-existent values within
+kernel support:
+.RS +7n
+.IP \(bu 2
+.RB \(lq noop \(rq:
+do nothing; let them pollute result
+.IP \(bu 2
+.RB \(lq renorm \(rq:
+ignore them and renormalize weights of existent values
+.IP \(bu 2
+.RB \(lq wght \(rq:
+ignore them and simply use weights of existent values
+.RE
+.IP
+default:
+.RB \(lq noop \(rq
+.TP
+.B \-b\fR\ <\fIbehavior\fR>\ , \fB\-\-boundary\fR\ <\fIbehavior\fR>
+How to handle samples beyond the input bounds:
+.RS +7n
+.IP \(bu 2
+.RB \(lq pad \(rq:
+use some specified value
+.IP \(bu 2
+.RB \(lq bleed \(rq:
+extend border values outward
+.IP \(bu 2
+.RB \(lq mirror \(rq:
+repeated reflections
+.IP \(bu 2
+.RB \(lq wrap \(rq:
+wrap-around to other side
+.RE
+.IP
+default:
+.RB \(lq noop \(rq
+.TP
+.B \-v\fR\ <\fIvalue\fR>\ , \fB\-\-value\fR\ <\fIvalue\fR>
+for
+.RB \(lq pad \(rq
+boundary behavior, pad with this value
+.RI ( double );
+default:
+.RB \(lq 0.0 \(rq
+.TP
+.B \-t\fR\ <\fItype\fR>\ , \fB\-\-type\fR\ <\fItype\fR>
+type to save output as.
+By default (not using this option), the output type is the same as the input
+type; default:
+.RB \(lq default \(rq
+.TP
+.B \-cheap
+.RI [ DEPRECATED :
+the
+.RB \(lq \-k\ cheap \(rq
+option is the new (and more reliable) way to access this functionality.
+.RB \(lq \-cheap \(rq
+is only here for legacy use in combination with
+.RB \(lq \-old \(rq.]
+.IP
+When downsampling (reducing number of samples), don\(cqt try to do correct
+filtering by scaling kernel to match new (stretched) index space; keep it in
+old index space.
+When used in conjunction with
+.RB \(lq \-k\ box \(rq,
+this can implement subsampling which chooses every Nth value.
+.TP
+.B \-c\fR\ <\fIcenter\fR>\ , \fB\-\-center\fR\ <\fIcenter\fR>
+(not available with
+.RB \(lq \-old \(rq)
+default centering of axes when input
+.I nrrd
+axes don\(cqt have a known centering:
+.RB \(lq cell \(rq
+or
+.RB \(lq node \(rq;
+default:
+.RB \(lq cell \(rq
+.TP
+.B \-co\fR\ , \fB\-\-center-override
+(not available with
+.RB \(lq \-old \(rq)
+centering info specified via
+.RB \(lq \-c \(rq
+should
+.B over-ride
+known centering, rather than simply be used when centering is unknown.
+.TP
+.B \-verbose\fR\ <\fIv\fR>
+(not available with
+.RB \(lq \-old \(rq)
+verbosity level
+.RI ( int );
+default:
+.RB \(lq 0 \(rq
+.TP
+.B \-i\fR\ <\fInin\fR>\ , \fB\-\-input\fR\ <\fInin\fR>
+input
+.IR nrrd ;
+default:
+.RB \(lq \- \(rq
+.TP
+.B \-o\fR\ <\fInout\fR>\ , \fB\-\-output\fR\ <\fInout\fR>
+output
+.I nrrd
+.RI ( string );
+default:
+.RB \(lq \- \(rq
+.SH "SEE\ ALSO"
+.BR unu (1)
diff --git a/unu-reshape.1 b/unu-reshape.1
new file mode 100644
index 0000000..f9660dd
--- /dev/null
+++ b/unu-reshape.1
@@ -0,0 +1,40 @@
+.TH UNU\-RESHAPE "1" "August 2021" "" "User Commands"
+.SH NAME
+unu\-reshape \- superficially change dimension and/or axes sizes
+.SH SYNOPSIS
+.B unu\ reshape
+.BR \-s , \-\-size
+.RI < sz0 \ sz1 \ ...>
+.RB [ \-i , \-\-input
+.RI < nin >]
+.RB [ \-o , \-\-output
+.RI < nout >]
+.SH DESCRIPTION
+Superficially change dimension and/or axes sizes.
+The underlying linear ordering of the samples is unchanged, but the reported
+dimension or axes sizes are changed.
+Identical in concept to Matlab\(cqs
+.RB \(lq reshape \(rq
+command.
+.IP \(bu +2n
+Uses
+.B nrrdReshape_nva
+.SH OPTIONS
+.TP
+.B \-s\fR\ <\fIsz0\fR \fIsz1\fR\ ...>\ ,\ \fB\-\-size\fR\ <\fI\fIsz0\fR \fIsz1\fR\ ...>
+new axes sizes
+(1 or more
+.IR size_t s)
+.TP
+.B \-i\fR\ <\fInin\fR>\ ,\ \fB\-\-input\fR\ <\fInin\fR>
+input
+.I nrrd
+.TP
+.B \-o\fR\ <\fInout\fR>\ ,\ \fB\-\-output\fR\ <\fInout\fR>
+output
+.I nrrd
+.RI ( string );
+default:
+.RB \(lq \- \(rq
+.SH "SEE\ ALSO"
+.BR unu (1)
diff --git a/unu-rmap.1 b/unu-rmap.1
new file mode 100644
index 0000000..ab2a69c
--- /dev/null
+++ b/unu-rmap.1
@@ -0,0 +1,119 @@
+.TH UNU\-RMAP "1" "August 2021" "" "User Commands"
+.SH NAME
+unu\-rmap \- map
+.I nrrd
+through one
+.B regular
+univariate map (\(lqcolormap\(rq)
+.SH SYNOPSIS
+.B unu\ rmap
+.BR \-m , \-\-map
+.RI < map >
+.RB [ \-r , \-\-rescale ]
+.RB [ \-min , \-\-minimum
+.RI < value >]
+.RB [ \-max , \-\-maximum
+.RI < value >]
+.RB [ \-blind8
+.RI < bool >]
+.RB [ \-t , \-\-type
+.RI < type >]
+.RB [ \-i , \-\-input
+.RI < nin >]
+.RB [ \-o , \-\-output
+.RI < nout >]
+.SH DESCRIPTION
+Map
+.I nrrd
+through one
+.B regular
+univariate map (\(lqcolormap\(rq).
+A map is regular if the control points are evenly spaced along the domain, and
+hence their position isn\(cqt explicitly represented in the map; the axis
+.IR min ,
+axis
+.IR max ,
+and number of points determine their location.
+The map can be a 1D
+.I nrrd
+(for \(lqgrayscale\(rq), in which case the output has the same dimension as the
+input, or a 2D
+.I nrrd
+(for \(lqcolor\(rq), in which case the output has one more dimension than the
+input.
+In either case, the output is the result of linearly interpolating between map
+points, either scalar values (\(lqgrayscale\(rq), or scanlines along axis
+.B 0
+(\(lqcolor\(rq).
+.IP \(bu +2n
+Uses
+.B nrrdApply1DRegMap
+.SH OPTIONS
+.TP
+.B \-m\fR\ <\fImap\fR>\ ,\ \fB\-\-map\fR\ <\fImap\fR>
+regular map to map input
+.I nrrd
+through
+.TP
+.B \-r\fR ,\ \fB\-\-rescale
+rescale the input values from the input range to the map domain.
+The map domain is either explicitly defined by the axis
+.IR min , max
+along axis
+.B 0
+or
+.BR 1 ,
+or, it is implicitly defined as zero to the length of that axis minus one.
+.TP
+.B \-min\fR\ <\fIvalue\fR>\ ,\ \fB\-\-minimum\fR\ <\fIvalue\fR>
+Low end of input range.
+Defaults to lowest value found in input
+.IR nrrd .
+Explicitly setting this is useful only with rescaling
+.RB (\(lq \-r \(rq)
+.RI ( double )
+.TP
+.B \-max\fR\ <\fIvalue\fR>\ ,\ \fB\-\-maximum\fR\ <\fIvalue\fR>
+High end of input range.
+Defaults to highest value found in input
+.IR nrrd .
+Explicitly setting this is useful only with rescaling
+.RB (\(lq \-r \(rq)
+.RI ( double )
+.TP
+.B \-blind8\fR\ <\fIbool\fR>
+Whether to know the range of 8-bit data blindly
+.RI ( uchar
+is always
+.BR [0,255] ,
+.I signed\ char
+is
+.BR [-128,127] ).
+Explicitly setting this is useful only with rescaling
+.RB (\(lq \-r \(rq)
+.RI ( bool );
+default:
+.RB \(lq true \(rq
+.TP
+.B \-t\fR\ <\fItype\fR>\ ,\ \fB\-\-type\fR\ <\fItype\fR>
+specify the type
+.RB (\(lq int \(rq,
+.RB \(lq float \(rq,
+etc.) of the output
+.IR nrrd .
+By default (not using this option), the output type is the map\(cqs type.
+.TP
+.B \-i\fR\ <\fInin\fR>\ ,\ \fB\-\-input\fR\ <\fInin\fR>
+input
+.I nrrd
+.TP
+.B \-o\fR\ <\fInout\fR>\ ,\ \fB\-\-output\fR\ <\fInout\fR>
+output
+.I nrrd
+.RI ( string );
+default:
+.RB \(lq \- \(rq
+.SH "SEE\ ALSO"
+.BR unu (1),
+.BR unu\-mrmap (1),
+.BR unu\-imap (1)
diff --git a/unu-save.1 b/unu-save.1
new file mode 100644
index 0000000..ac0d8f8
--- /dev/null
+++ b/unu-save.1
@@ -0,0 +1,150 @@
+.TH UNU\-SAVE
+.SH NAME
+unu\-save \- write
+.I nrrd
+with specific format, encoding, or endianness
+.SH SYNOPSIS
+.B unu\ save
+.BR \-f , \-\-format
+.RI < form >
+.RB [ \-e , \-\-encoding
+.RI < enc >]
+.RB [ \-en , \-\-endian
+.RI < end >]
+.RB [ \-i , \-\-input
+.RI < nin >]
+.RB [ \-o , \-\-output
+.RI < nout >]
+.RB [ \-od , \-\-ouputdata
+.RI < name >]
+.SH DESCRIPTION
+Write
+.I nrrd
+with specific format, encoding, or endianness.
+Use
+.in +4n
+.EX
+.B unu\ save\ \-f\ pnm\ |\ xv\ \-
+.EE
+.in
+to view PPM- or PGM-compatible
+.IR nrrd s on unix.
+EPS output is a EPSF\-3.0 file with
+.B BoundingBox
+and
+.B HiResBoundingBox
+DSC comments, and is suitable for inclusion into other PostScript documents.
+As a stand-alone file, the image is conveniently centered on an 8.5x11 inch
+page, with 0.5 inch margins.
+.IP \(bu +2n
+Uses various fields in the
+.B NrrdIOState
+passed to
+.B nrrdSave
+.SH OPTIONS
+.TP
+.B \-f\fR\ <\fIform\fR>\ ,\ \fB\-\-format\fR\ <\fIform\fR>
+output file format.
+Possibilities include:
+.RS +7n
+.IP \(bu +2n
+.RB \(lq nrrd \(rq:
+standard
+.I nrrd
+format
+.IP \(bu +2n
+.RB \(lq pnm \(rq:
+PNM image; PPM for color, PGM for grayscale
+.IP \(bu +2n
+.RB \(lq text \(rq:
+plain ASCII text for 1\-D and 2\-D data
+.IP \(bu +2n
+.RB \(lq vtk \(rq:
+VTK
+.RB \(lq STRUCTURED_POINTS \(rq
+dataset
+.IP \(bu +2n
+.RB \(lq png \(rq:
+PNG image
+.IP \(bu +2n
+.RB \(lq eps \(rq:
+EPS file
+.RE
+.TP
+.B \-e\fR\ <\fIenc\fR>\ ,\ \fB\-\-encoding\fR\ <\fIenc\fR>
+encoding of data in file.
+Not all encodings are supported in a given format.
+Possibilities include:
+.RS +7n
+.IP \(bu +2n
+.RB \(lq raw \(rq:
+raw encoding
+.IP \(bu +2n
+.RB \(lq ascii \(rq:
+print data in ascii
+.IP \(bu +2n
+.RB \(lq hex \(rq:
+two hex digits per byte
+.IP \(bu +2n
+.RB \(lq gzip \(rq,\ \(lq gz \(rq:
+gzip compressed raw data
+.IP \(bu +2n
+.RB \(lq bzip2 \(rq,\ \(lq bz2 \(rq:
+bzip2 compressed raw data
+.P
+The specifiers for compressions may be followed by a colon
+.RB \(lq : \(rq,
+followed by an optional digit giving compression \(lqlevel\(rq (for
+.BR gzip )
+or \(lqblock size\(rq (for
+.BR bzip2 ).
+For
+.BR gzip ,
+this can be followed by an optional character for a compression strategy:
+.IP \(bu +2n
+.RB \(lq d \(rq:
+default, Huffman with string match
+.IP \(bu +2n
+.RB \(lq h \(rq:
+Huffman alone
+.IP \(bu +2n
+.RB \(lq f \(rq:
+specialized for filtered data
+.P
+For example,
+.RB \(lq gz \(rq,
+.RB \(lq gz:9 \(rq,
+.RB \(lq gz:9f \(rq
+are all valid
+.RE
+.TP
+.B \-en\fR\ <\fIend\fR>\ ,\ \fB\-\-endian\fR\ <\fIend\fR>
+Endianness to save data out as;
+.RB \(lq little \(rq
+for Intel and friends;
+.RB \(lq big \(rq
+for everyone else.
+Defaults to endianness of this machine
+.TP
+.B \-i\fR\ <\fInin\fR>\ ,\ \fB\-\-input\fR\ <\fInin\fR>
+input
+.I nrrd
+.TP
+.B \-o\fR\ <\fInout\fR>\ ,\ \fB\-\-output\fR\ <\fInout\fR>
+output
+.I nrrd
+.RI ( string );
+default:
+.RB \(lq \- \(rq
+.TP
+.B \-od\fR\ <\fIname\fR>\ ,\ \fB\-\-ouputdata\fR\ <\fIname\fR>
+when saving to a
+.RB \(lq .nhdr \(rq
+file, this option allows you to explicitly name the data file, instead of (by
+default, not using this option) having it be the same filename base as the
+header file.
+.RI ( string )
+.SH "SEE\ ALSO"
+.BR unu (1)
+.P
+http://www.trilon.com/xv/
diff --git a/unu-shuffle.1 b/unu-shuffle.1
new file mode 100644
index 0000000..b2bd05e
--- /dev/null
+++ b/unu-shuffle.1
@@ -0,0 +1,57 @@
+.TH UNU\-SHUFFLE "1" "August 2021" "" "User Commands"
+.SH NAME
+unu\-shuffle \- permute slices along one axis
+.SH SYNOPSIS
+.B unu\ shuffle
+.RI [ @file \ ...]
+.BR \-p , \-\-permute
+.RI < slc0 \ slc1 \ ...>
+.RB [ \-inv , \-\-inverse ]
+.BR \-a , \-\-axis
+.RI < axis >
+.RB [ \-i , \-\-input
+.RI < nin >]
+.RB [ \-o , \-\-output
+.RI < nout >]
+.SH DESCRIPTION
+Permute slices along one axis.
+Slices along one axis are re-arranged as units according to the given
+permutation (or its inverse).
+The permutation tells which old slice to put at each new position.
+For example, the shuffle 0->1, 1->2, 2->0 would be
+.RB \(lq 2\ 0\ 1 \(rq.
+Obviously, if you have to rearrange the many slices of a large dataset, you
+should probably store the permutation in a plain text file and use it as a
+\(lqresponse file\rq).
+.IP \(bu +2n
+Uses
+.B nrrdShuffle
+.SH OPTIONS
+.TP
+.IR @file \ ...
+response file(s) containing command-line arguments
+.TP
+.B \-p\fR\ <\fIslc0\fR\ \fIslc1\fR\ ...>\ ,\ \fB\-\-permute\fR\ <\fIslc0\fR\ \fIslc1\fR\ ...>
+new slice ordering (1 or more
+.IR unsigned\ int s)
+.TP
+.B \-inv\fR\ ,\ \fB\-\-inverse
+use inverse of given permutation
+.TP
+.B \-a\fR\ <\fIaxis\fR>\ ,\ \fB\-\-axis\fR\ <\fIaxis\fR>
+axis to shuffle along
+.RI ( unsigned\ int )
+.TP
+.B \-i\fR\ <\fInin\fR>\ ,\ \fB\-\-input\fR\ <\fInin\fR>
+input
+.I nrrd
+.TP
+.B \-o\fR\ <\fInout\fR>\ ,\ \fB\-\-output\fR\ <\fInout\fR>
+output
+.I nrrd
+.RI ( string );
+default:
+.RB \(lq \- \(rq
+.SH "SEE\ ALSO"
+.BR unu (1),
+.BR unu\-flip (1)
diff --git a/unu-slice.1 b/unu-slice.1
new file mode 100644
index 0000000..fd6a2de
--- /dev/null
+++ b/unu-slice.1
@@ -0,0 +1,66 @@
+.TH UNU\-SLICE "1" "August 2021" "" "User Commands"
+.SH NAME
+unu\-slice \- slice along one or more axes at given positions
+.SH SYNOPSIS
+.B unu\ slice
+.BR \-a , \-\-axis
+.RI < axis \ ...>
+.BR \-p , \-\-position
+.RI < pos \ ...>
+.RB [ \-i , \-\-input
+.RI < nin >]
+.RB [ \-o , \-\-output
+.RI < nout >]
+.SH DESCRIPTION
+Slice along one or more axes at given positions.
+Output
+.I nrrd
+dimension is less than input
+.I nrrd
+dimension by the number of slice axes (except when the input is or gets down to
+1\-D).
+Can slice on all axes in order to sample a single value from the array.
+Per-axis information is preserved.
+.IP \(bu +2n
+Uses
+.B nrrdSlice
+(possibly called multiple times)
+.SH OPTIONS
+.TP
+.B \-a\fR\ <\fIaxis\fR\ ...>\ ,\ \fB\-\-axis\fR\ <\fIaxis\fR\ ...>
+single axis or multiple axes to slice along.
+Giving multiple axes here leads to doing multiple slices (at the corresponding
+positions given with
+.RB \(lq \-p \(rq.
+Multiple axes should be identified in terms of the axis numbering of the
+original
+.IR nrrd ;
+as the slices are done (in the given ordering) the actual slice axis will be
+different if previous slices were on lower-numbered (faster) axes.
+(1 or more
+.IR unsigned\ ints )
+.TP
+.B \-p\fR\ <\fIpos\fR>\ ,\ \fB\-\-position\fR\ <\fIpos\fR>
+position(s) to slice at:
+.RS +7n
+.IP \(bu 2
+.RI < int >
+gives 0-based index
+.IP \(bu 2
+.BR M\- <\fIint\fR>
+give index relative to the last sample on the axis
+.RB ( M== \fI#samples \-1 ).
+.RE
+.TP
+.B \-i\fR\ <\fInin\fR>\ ,\ \fB\-\-input\fR\ <\fInin\fR>
+input
+.I nrrd
+.TP
+.B \-o\fR\ <\fInout\fR>\ ,\ \fB\-\-output\fR\ <\fInout\fR>
+output
+.I nrrd
+.RI ( string );
+default:
+.RB \(lq \- \(rq
+.SH "SEE\ ALSO"
+.BR unu (1)
diff --git a/unu-splice.1 b/unu-splice.1
new file mode 100644
index 0000000..86d0ca5
--- /dev/null
+++ b/unu-splice.1
@@ -0,0 +1,56 @@
+.TH UNU\-SPLICE "1" "August 2021" "" "User Commands"
+.SH NAME
+unu\-splice \- replace a slice with a different \fInrrd
+.SH SYNOPSIS
+.B unu\ splice
+.BR \-a , \-\-axis
+.RI < axis >
+.BR \-p , \-\-position
+.RI < pos >
+.BR \-s , \-\-slice
+.RI < nslice >
+.RB [ \-i , \-\-input
+.RI < nin >]
+.RB [ \-o , \-\-output
+.RI < nout >]
+.SH DESCRIPTION
+Replace a slice with a different
+.IR nrrd .
+This is functionally the opposite of
+.RB \(lq slice \(rq.
+.IP \(bu +2n
+Uses
+.B nrrdSplice
+.SH OPTIONS
+.TP
+.B \-a\fR\ <\fIaxis\fR>\ ,\ \fB\-\-axis\fR\ <\fIaxis\fR>
+axis to splice along
+.RI ( unsigned\ int )
+.TP
+.B \-p\fR\ <\fIpos\fR>\ ,\ \fB\-\-position\fR\ <\fIpos\fR>
+position to splice at:
+.RS +7n
+.IP \(bu 2
+.RI < int >
+gives 0-based index
+.IP \(bu 2
+.BR M\- <\fIint\fR>
+give index relative to the last sample on the axis
+.RB ( M== \fI#samples \-1 ).
+.RE
+.TP
+.B \-i\fR\ <\fInin\fR>\ ,\ \fB\-\-input\fR\ <\fInin\fR>
+input
+.IR nrrd .
+This is the slice to insert into
+.I nin
+.TP
+.B \-o\fR\ <\fInout\fR>\ ,\ \fB\-\-output\fR\ <\fInout\fR>
+output
+.I nrrd
+.RI ( string );
+default:
+.RB \(lq \- (rq
+.SH "SEE\ ALSO"
+.BR unu (1),
+.BR unu\-slice (1)
diff --git a/unu-sselect.1 b/unu-sselect.1
new file mode 100644
index 0000000..84e21ff
--- /dev/null
+++ b/unu-sselect.1
@@ -0,0 +1,52 @@
+.TH UNU\-SSELECT "1" "August 2021" "" "User Commands"
+.SH NAME
+unu\-sselect \- select subset of slices along an axis
+.SH SYNOPSIS
+.B unu\ sselect
+.RB [ \-i , \-\-input
+.RI < nin >]
+.BR \-a , \-\-axis
+.RI < axis >
+.BR \-s , \-\-selector
+.RI < nline >
+.BR \-th
+.RI < thresh >
+.RB [ \-o , \-\-output
+.RI < above \ below >]
+.SH DESCRIPTION
+Select subset of slices along an axis.
+The choice to keep or nix a slice is determined by whether the values in a
+given 1\-D line of values is above or below a given threshold.
+.IP \(bu +2n
+Uses
+.B nrrdSliceSelect
+.SH OPTIONS
+.TP
+.B \-i\fR\ <\fInin\fR>\ ,\ \fB\-\-input\fR\ <\fInin\fR>
+input
+.I nrrd
+.TP
+.B \-a\fR\ <\fIaxis\fR>\ ,\ \fB\-\-axis\fR\ <\fIaxis\fR>
+axis to slice along
+.RI ( unsigned\ int )
+.TP
+.B \-s\fR\ <\fInline\fR>\ ,\ \fB\-\-selector\fR\ <\fInline\fR>
+the 1\-D
+.I nrrd
+of values to compare with threshold
+.TP
+.B \-th\fR\ <\fIthresh\fR>
+threshold on selector line
+.RI ( double )
+.TP
+.B \-o\fR\ <\fIabove\fR\ \fIbelow\fR>\ ,\ \fB\-\-output\fR\ <\fIabove\fR\ \fIbelow\fR>
+outputs for slices corresponding to values above (first) and below (second) given threshold.
+Use
+.RB \(lq x \(rq
+to say that no output is desired.
+(2
+.IR string s);
+default:
+.RB \(lq \-\ x \(rq
+.SH "SEE\ ALSO"
+.BR unu (1)
diff --git a/unu-subst.1 b/unu-subst.1
new file mode 100644
index 0000000..20e639a
--- /dev/null
+++ b/unu-subst.1
@@ -0,0 +1,55 @@
+.TH UNU\-SUBST "1" "August 2021" "" "User Commands"
+.SH NAME
+unu\-subst \- map
+.I nrrd
+through one univariate substitution table
+.SH SYNOPSIS
+.B unu\ subst
+.BR \-s , \-\-subst
+.RI < subst >
+.RB [ \-i , \-\-input
+.RI < nin >]
+.RB [ \-o , \-\-output
+.RI < nout >]
+.SH DESCRIPTION
+Map
+.I nrrd
+through one univariate substitution table (itself represented as a
+.IR nrrd ).
+The substitution table is a
+.RI 2\-by\- N
+table: a list of pairs giving the old value (to replace) and the the new value
+(to change to), in that order.
+A plain text file makes this easy.
+Unlike with
+.BR unu\-lut (1),
+.BR unu\-rmap (1),
+and
+.BR unu\-imap (1),
+the output type is the same as the input type, rather than the type of the lut
+or map.
+.IP \(bu +2n
+Uses
+.B nrrdApply1DSubstitution
+.SH OPTIONS
+.TP
+.B \-s\fR\ <\fIsubst\fR>\ ,\ \fB\-\-subst\fR\ <\fIsubst\fR>
+substitution table to map input
+.I nrrd
+through
+.TP
+.B \-i\fR\ <\fInin\fR>\ ,\ \fB\-\-input\fR\ <\fInin\fR>
+input
+.I nrrd
+.TP
+.B \-o\fR\ <\fInout\fR>\ ,\ \fB\-\-output\fR\ <\fInout\fR>
+output
+.I nrrd
+.RI ( string );
+default:
+.RB \(lq \- \(rq
+.SH "SEE\ ALSO"
+.BR unu (1),
+.BR unu\-lut (1),
+.BR unu\-rmap (1),
+.BR unu\-imap (1)
diff --git a/unu-swap.1 b/unu-swap.1
new file mode 100644
index 0000000..0189d7f
--- /dev/null
+++ b/unu-swap.1
@@ -0,0 +1,36 @@
+.TH UNU\-SWAP "1" "August 2021" "" "User Commands"
+.SH NAME
+unu\-swap \- interchange ordering of two axes
+.SH SYNOPSIS
+.B unu\ swap
+.BR \-a , \-\-axis
+.RI < axisA \ axisB >
+.RB [ \-i , \-\-input
+.RI < nin >]
+.RB [ \-o , \-\-output
+.RI < nout >]
+.SH DESCRIPTION
+Interchange ordering of two axes. Special case of
+.BR unu\-permute (1).
+.IP \(bu +2n
+Uses
+.B nrrdAxesSwap
+.SH OPTIONS
+.TP
+.B \-a\fR\ <\fIaxisA\fR\ \fIaxisB\fR>\ ,\ \fB\-\-axis\fR\ <\fIaxisA\fR\ \fIaxisB\fR>
+the two axes to switch (0-based numbering) (2
+.IR unsigned\ int s)
+.TP
+.B \-i\fR\ <\fInin\fR>\ ,\ \fB\-\-input\fR\ <\fInin\fR>
+input
+.I nrrd
+.TP
+.B \-o\fR\ <\fInout\fR>\ ,\ \fB\-\-output\fR\ <\fInout\fR>
+output
+.I nrrd
+.RI ( string );
+default:
+.RB \(lq \- \(rq
+.SH "SEE\ ALSO"
+.BR unu (1),
+.BR unu\-permute (1)
diff --git a/unu-tile.1 b/unu-tile.1
new file mode 100644
index 0000000..7346ecc
--- /dev/null
+++ b/unu-tile.1
@@ -0,0 +1,57 @@
+.TH UNU\-TILE "1" "August 2021" "" "User Commands"
+.SH NAME
+unu\-tile \- tile slices of one axis into two other axes
+.SH SYNOPSIS
+.B unu\ tile
+.BR \-a , \-\-axis
+.RI < axSplit \ ax0 \ ax1 >
+.BR \-s , \-\-size
+.RI < fast \ slow >
+.RB [ \-i , \-\-input
+.RI < nin >]
+.RB [ \-o , \-\-output
+.RI < nout >]
+.SH DESCRIPTION
+Tile slices of one axis into two other axes.
+Tiling an array means splitting one axis into fast and slow parts, and then
+interleaving those parts into other (existing) axes by doing two axis merges,
+which combine an existing axis with part of the split axis.
+This reduces the dimension by one.
+The three axis arguments all identify axes in the input array as is.
+This provides, for example, a simple way of viewing the 128 slices along the
+slow axis of a 3\-D volume as a 16x8 tiled array of 2\-D slices, as with
+.RB \(lq \-a\ 2\ 0\ 1\ \-s\ 16\ 8 \(rq.
+.IP \(bu +2n
+Uses
+.B nrrdTile2D
+.SH OPTIONS
+.TP
+.B \-a\fR\ <\fIaxSplit\fR\ \fIax0\fR\ \fIax1\fR>\ ,\ \fB\-\-axis\fR\ <\fIaxSplit\fR\ \fIax0\fR\ \fIax1\fR>
+.I axSplit
+is divided and merged with
+.I ax0
+and
+.I ax1
+(3
+.IR unsigned\ int s)
+.TP
+.B \-s\fR\ <\fIfast\fR\ \fIslow\fR>\ ,\ \fB\-\-size\fR\ <\fIfast\fR\ \fIslow\fR>
+fast and slow axis sizes to produce as result of splitting the
+.I axSplit
+ axis.
+(2
+.IR size_t s)
+.TP
+.B \-i\fR\ <\fInin\fR>\ ,\ \fB\-\-input\fR\ <\fInin\fR>
+input
+.I nrrd
+.TP
+.B \-o\fR\ <\fInout\fR>\ ,\ \fB\-\-output\fR\ <\fInout\fR>
+output
+.I nrrd
+.RI ( string );
+default:
+.RB \(lq \- \(rq
+.SH "SEE\ ALSO"
+.BR unu (1),
+.BR unu\-untile (1)
diff --git a/unu-unorient.1 b/unu-unorient.1
new file mode 100644
index 0000000..7a078db
--- /dev/null
+++ b/unu-unorient.1
@@ -0,0 +1,38 @@
+.TH UNU\-UNORIENT "1" "August 2021" "" "User Commands"
+.SH NAME
+unu\-unorient \- make image orientation be axis-aligned
+.SH SYNOPSIS
+.B unu\ unorient
+.BR \-i , \-\-input
+.RI < nin >
+.RB [ \-smfo ]
+.RB [ \-o , \-\-output
+.RI < nout >]
+.SH DESCRIPTION
+Make image orientation be axis-aligned.
+Does various tricks.
+.IP \(bu +2n
+Uses
+.B nrrdOrientationReduce
+.SH OPTIONS
+.TP
+.B \-i\fR\ <\fInin\fR>\ ,\ \fB\-\-input\fR\ <\fInin\fR>
+input
+.I nrrd
+(sorry, can’t use usual default of
+.RB \(lq \- \(rq
+for
+.I stdin
+because of hest quirk)
+.TP
+.B \-smfo
+set some axis mins based on space origin (hack)
+.TP
+.B \-o\fR\ <\fInout\fR>\ ,\ \fB\-\-output\fR\ <\fInout\fR>
+output
+.I nrrd
+.RI ( string );
+default:
+.RB \(lq \- \(rq
+.SH "SEE\ ALSO"
+.BR unu (1)
diff --git a/unu-unquantize.1 b/unu-unquantize.1
new file mode 100644
index 0000000..9c81558
--- /dev/null
+++ b/unu-unquantize.1
@@ -0,0 +1,83 @@
+.TH UNU\-UNQUANTIZE "1" "August 2021" "" "User Commands"
+.SH NAME
+unu\-unquantize \- recover floating point values from quantized data
+.SH SYNOPSIS
+.B unu\ unquantize
+.BR \-i , \-\-input
+.RI < nin >
+.RB [ \-min , \-\-minimum
+.RI < value >]
+.RB [ \-max , \-\-maximum
+.RI < value >]
+.RB [ \-double ]
+.RB [ \-o , \-\-output
+.RI < nout >]
+.SH DESCRIPTION
+Recover floating point values from quantized data.
+Uses the
+.B oldMin
+and
+.B oldMax
+fields in the
+.I nrrd
+of quantized values to regenerate approximate versions of the original
+unquantized values.
+Can also override these with
+.RB \(lq \-min \(rq
+and
+.RB \(lq \-max \(rq.
+.IP \(bu +2n
+Uses
+.B nrrdUnquantize
+.SH OPTIONS
+.TP
+.B \-i\fR\ <\fInin\fR>\ ,\ \fB\-\-input\fR\ <\fInin\fR>
+input
+.IR nrrd .
+That this argument is required instead of optional, as with most
+.BR unu (1)
+commands, is a quirk caused by the need to have
+.BR unu\-unquantize (1)
+generate usage info, combined with the fact that all the other arguments have
+sensible defaults.
+.TP
+.B \-min\fR\ <\fIvalue\fR>\ ,\ \fB\-\-minimum\fR\ <\fIvalue\fR>
+Defaults to
+.IB nin ->oldMin
+if it exists, otherwise
+.B 0.0
+.RI ( double )
+.TP
+.B \-max\fR\ <\fIvalue\fR>\ ,\ \fB\-\-maximum\fR\ <\fIvalue\fR>
+Defaults to
+.IB nin ->oldMax
+if it exists, otherwise
+.B 1.0
+.RI ( double )
+.TP
+.B \-double
+If not using
+.RB \(lq \-min \(rq
+or
+.RB \(lq \-max \(rq,
+whether to know the range of 8-bit data blindly
+.RI ( uchar
+is always
+.BR [0,255] ,
+.I signed char
+is
+.BR [-128,127] )
+.RI ( bool );
+default:
+.RB \(lq true \(rq,
+"true"
+.TP
+.B \-o\fR\ <\fInout\fR>\ ,\ \fB\-\-output\fR\ <\fInout\fR>
+output
+.I nrrd
+.RI ( string );
+default:
+.RB \(lq \- \(rq
+.SH "SEE\ ALSO"
+.BR unu (1),
+.BR unu\-quantize (1)
diff --git a/unu-untile.1 b/unu-untile.1
new file mode 100644
index 0000000..f1674af
--- /dev/null
+++ b/unu-untile.1
@@ -0,0 +1,90 @@
+.TH UNU\-UNTILE "1" "August 2021" "" "User Commands"
+.SH NAME
+unu\-untile \- undo
+.BR unu\-tile (1):
+merge slow parts of two axis splits
+.SH SYNOPSIS
+.B unu\ untile
+.BR \-a , \-\-axis
+.RI < axMerge \ ax0 \ ax1 >
+.BR \-s , \-\-size
+.RI < size0 \ size1 >
+.RB [ \-i , \-\-input
+.RI < nin >]
+.RB [ \-o , \-\-output
+.RI < nout >]
+.SH DESCRIPTION
+Undo
+.BR unu\-tile (1):
+merge slow parts of two axis splits.
+Untiling an array means spliting two axes, permuting the slow parts of those
+axes to be adjecent in the axis ordering, and then merging them.
+This increases the dimension by one.
+Undoing a
+.BR unu\-tile (1)
+uses the same
+.RB \(lq \-s \(rq
+argument, and
+sometimes a different
+.RB \(lq \-a \(rq
+argument, as demonstrated here for a 3\-D array:
+.IP
+.RB \(lq unu\ untile\ \-a\ 2\ 0\ 1 \(rq
+undoes
+.RB \(lq unu\ tile\ \-a\ 2\ 0\ 1 \(rq
+.IP
+.RB \(lq unu\ untile\ \-a\ 1\ 0\ 1 \(rq
+undoes
+.RB \(lq unu\ tile\ \-a\ 1\ 0\ 2 \(rq
+.IP
+.RB \(lq unu\ untile\ \-a\ 0\ 0\ 1 \(rq
+undoes
+.RB \(lq unu\ tile\ \-a\ 0\ 1\ 2 \(rq.
+.IP \(bu +2n
+Uses
+.B nrrdUntile2D
+.SH OPTIONS
+.TP
+.B \-a\fR\ <\fIaxMerge\fR\ \fIax0\fR\ \fIax1\fR>\ ,\ \fB\-\-axis\fR\ <\fIaxMerge\fR\ \fIax0\fR\ \fIax1\fR>
+the slow parts of axes
+.I ax0
+and
+.I ax1
+are merged into a (new) axis
+.IR axMerge ,
+with the axis
+.I ax0
+part being faster than
+.IR ax1 .
+(3
+.IR unsigned\ int s)
+.TP
+.B \-s\fR\ <\fIsize0\fR\ \fIsize1\fR>\ ,\ \fB\-\-size\fR\ <\fIsize0\fR\ \fIsize1\fR>
+the slow parts of axes
+.I ax0
+and
+.I ax1
+are taken to have size
+.I size0
+and
+.IR size1 ,
+respectively, and axis
+.I axMerge
+will have size
+.IB size0 * size1\fR.
+(2
+.IR size_t s)
+.TP
+.B \-i\fR\ <\fInin\fR>\ ,\ \fB\-\-input\fR\ <\fInin\fR>
+input
+.I nrrd
+.TP
+.B \-o\fR\ <\fInout\fR>\ ,\ \fB\-\-output\fR\ <\fInout\fR>
+output
+.I nrrd
+.RI ( string );
+default:
+.RB \(lq \- \(rq
+.SH "SEE\ ALSO"
+.BR unu (1),
+.BR unu\-tile (1)
diff --git a/unu-w2i.1 b/unu-w2i.1
new file mode 100644
index 0000000..17dd885
--- /dev/null
+++ b/unu-w2i.1
@@ -0,0 +1,58 @@
+.TH UNU\-W2I "1" "April 2021" "" "User Commands"
+.SH NAME
+unu\-w2i \- converts from 1-D world to index position
+.SH SYNOPSIS
+.B unu\ w2i
+.RI < center >
+.RI < minPos >
+.RI < maxPos >
+.RI < num >
+.RI < world >
+.SH DESCRIPTION
+Converts from 1-D world to index position, given the centering of the data
+.RB ( cell
+vs.
+.BR node ),
+the range of positions, and the number of intervals into which position has
+been quantized.
+This is a demo/utility, which does not actually operate on any
+.IR nrrd s.
+Previously available as the stand-alone
+.B pos2idx
+binary.
+.P
+Uses
+.B NRRD_IDX
+macro
+.SH OPTIONS
+.TP
+.RI < center >
+which centering applies to the quantized position.
+Possibilities are:
+.RS +7n
+.IP \(bu 2
+.RB \(lq cell \(rq
+for histogram bins, quantized values, and pixels-as-squares
+.IP \(bu 2
+.RB \(lq node \(rq
+for non-trivially interpolated sample points
+.RE
+.TP
+.RI < minPos >
+smallest position associated with index
+.B 0
+.RI ( double )
+.TP
+.RI < maxPos >
+highest position associated with highest index
+.RI ( double )
+.TP
+.RI < num >
+number of intervals into which position has been quantized
+.RI ( double )
+.TP
+.RI < world >
+the input world position, to be converted to index
+.RI ( double )
+.SH "SEE\ ALSO"
+.BR unu (1)
diff --git a/unu.1 b/unu.1
index 6f8b611..cb49b63 100644
--- a/unu.1
+++ b/unu.1
@@ -4,7 +4,7 @@ unu \- Utah Nrrd Utilities command-line interface
.SH SYNOPSIS
.B unu
.I COMMAND
-.RI [ OPTION ]...
+.RI [ OPTION ]...\:
.SH DESCRIPTION
.BR unu (1)
is a command-line interface to much of the functionality in \(lqnrrd\(rq, a C
@@ -116,11 +116,11 @@ Formats available:
Nrrd data encodings available:
.B raw ascii hex gz bz2
.SH COMMANDS
-\" .P
-\" Each of the following has a corresponding man page: for example,
-\" .BR unu\-about (1)
-\" for
-\" .RB \(lq unu\ about \(rq.
+.P
+Each of the following has a corresponding man page: for example,
+.BR unu\-about (1)
+for
+.RB \(lq unu\ about \(rq.
.SS unu about
Information about this program and its use
.SS unu env
@@ -303,6 +303,9 @@ Remap CC values down to lowest contiguous values
Write
.I nrrd
with specific format, encoding, or endianness
+.SH ENVIRONMENT
+See
+.BR unu\-env (1).
.SH "SEE\ ALSO"
.BR gprobe (1),
.BR ilk (1),
@@ -312,67 +315,66 @@ with specific format, encoding, or endianness
.BR overrgb (1),
.BR puller (1),
.BR tend (1),
-.BR vprobe (1)
-\" .BR vprobe (1),
-\" .BR unu-1op (1),
-\" .BR unu-2op (1),
-\" .BR unu-3op (1),
-\" .BR unu-about (1),
-\" .BR unu-acrop (1),
-\" .BR unu-affine (1),
-\" .BR unu-axdelete (1),
-\" .BR unu-axinfo (1),
-\" .BR unu-axinsert (1),
-\" .BR unu-axmerge (1),
-\" .BR unu-axsplit (1),
-\" .BR unu-ccadj (1),
-\" .BR unu-ccfind (1),
-\" .BR unu-ccmerge (1),
-\" .BR unu-ccsettle (1),
-\" .BR unu-cksum (1),
-\" .BR unu-cmedian (1),
-\" .BR unu-convert (1),
-\" .BR unu-crop (1),
-\" .BR unu-data (1),
-\" .BR unu-dhisto (1),
-\" .BR unu-dice (1),
-\" .BR unu-diff (1),
-\" .BR unu-dist (1),
-\" .BR unu-env (1),
-\" .BR unu-fft (1),
-\" .BR unu-flip (1),
-\" .BR unu-gamma (1),
-\" .BR unu-head (1),
-\" .BR unu-heq (1),
-\" .BR unu-histax (1),
-\" .BR unu-histo (1),
-\" .BR unu-i2w (1),
-\" .BR unu-imap (1),
-\" .BR unu-inset (1),
-\" .BR unu-jhisto (1),
-\" .BR unu-join (1),
-\" .BR unu-lut (1),
-\" .BR unu-lut2 (1),
-\" .BR unu-make (1),
-\" .BR unu-minmax (1),
-\" .BR unu-mlut (1),
-\" .BR unu-mrmap (1),
-\" .BR unu-pad (1),
-\" .BR unu-permute (1),
-\" .BR unu-project (1),
-\" .BR unu-quantize (1),
-\" .BR unu-resample (1),
-\" .BR unu-reshape (1),
-\" .BR unu-rmap (1),
-\" .BR unu-save (1),
-\" .BR unu-shuffle (1),
-\" .BR unu-slice (1),
-\" .BR unu-splice (1),
-\" .BR unu-sselect (1),
-\" .BR unu-subst (1),
-\" .BR unu-swap (1),
-\" .BR unu-tile (1),
-\" .BR unu-unorient (1),
-\" .BR unu-unquantize (1),
-\" .BR unu-untile (1),
-\" .BR unu-w2i (1)
+.BR vprobe (1),
+.BR unu-1op (1),
+.BR unu-2op (1),
+.BR unu-3op (1),
+.BR unu-about (1),
+.BR unu-acrop (1),
+.BR unu-affine (1),
+.BR unu-axdelete (1),
+.BR unu-axinfo (1),
+.BR unu-axinsert (1),
+.BR unu-axmerge (1),
+.BR unu-axsplit (1),
+.BR unu-ccadj (1),
+.BR unu-ccfind (1),
+.BR unu-ccmerge (1),
+.BR unu-ccsettle (1),
+.BR unu-cksum (1),
+.BR unu-cmedian (1),
+.BR unu-convert (1),
+.BR unu-crop (1),
+.BR unu-data (1),
+.BR unu-dhisto (1),
+.BR unu-dice (1),
+.BR unu-diff (1),
+.BR unu-dist (1),
+.BR unu-env (1),
+.BR unu-fft (1),
+.BR unu-flip (1),
+.BR unu-gamma (1),
+.BR unu-head (1),
+.BR unu-heq (1),
+.BR unu-histax (1),
+.BR unu-histo (1),
+.BR unu-i2w (1),
+.BR unu-imap (1),
+.BR unu-inset (1),
+.BR unu-jhisto (1),
+.BR unu-join (1),
+.BR unu-lut (1),
+.BR unu-lut2 (1),
+.BR unu-make (1),
+.BR unu-minmax (1),
+.BR unu-mlut (1),
+.BR unu-mrmap (1),
+.BR unu-pad (1),
+.BR unu-permute (1),
+.BR unu-project (1),
+.BR unu-quantize (1),
+.BR unu-resample (1),
+.BR unu-reshape (1),
+.BR unu-rmap (1),
+.BR unu-save (1),
+.BR unu-shuffle (1),
+.BR unu-slice (1),
+.BR unu-splice (1),
+.BR unu-sselect (1),
+.BR unu-subst (1),
+.BR unu-swap (1),
+.BR unu-tile (1),
+.BR unu-unorient (1),
+.BR unu-unquantize (1),
+.BR unu-untile (1),
+.BR unu-w2i (1)
commit 516324379066d7660be0b0eaa7e3ff0bd06b27c9
Author: Benjamin A. Beasley <code(a)musicinmybrain.net>
Date: Wed Jul 21 16:06:33 2021 -0400
Opt in to rpmautospec
diff --git a/changelog b/changelog
new file mode 100644
index 0000000..833cd80
--- /dev/null
+++ b/changelog
@@ -0,0 +1,60 @@
+* Tue Apr 13 2021 Benjamin A. Beasley <code(a)musicinmybrain.net> - 1.11.0-16
+- Finish man pages, except “unu” sub-pages. Start installing man pages.
+
+* Sat Apr 03 2021 Benjamin A. Beasley <code(a)musicinmybrain.net> - 1.11.0-15
+- Fix typo in lib_install_dir.patch
+- Update download URL from HTTP to HTTPS
+- Improved package summaries and descriptions
+- Adjust whitespace to personal preference
+- Fix stray line continuation backslash
+- Support EPEL7
+- Build with “levmar” support enabled
+- Use stricter file globs, including specifying the so-version
+- Add an -examples subpackage so the BSD and zlib licenses can be “contained”
+ to that subpackage; remove certain unused files with other licenses in %%prep
+- Require cmake-filesystem instead of all of CMake in -devel
+- More precisely skip overly-strict floating-point tests where required
+- Start work on adding a complete set of man pages
+
+* Wed Jan 27 2021 Fedora Release Engineering <releng(a)fedoraproject.org> - 1.11.0-14
+- Rebuilt for https://fedoraproject.org/wiki/Fedora_34_Mass_Rebuild
+
+* Fri Sep 04 2020 Ankur Sinha <ankursinha AT fedoraproject DOT org> - 1.11.0-13
+- Use cmake macros to fix build
+
+* Sat Aug 01 2020 Fedora Release Engineering <releng(a)fedoraproject.org> - 1.11.0-12
+- Second attempt - Rebuilt for
+ https://fedoraproject.org/wiki/Fedora_33_Mass_Rebuild
+
+* Wed Jul 29 2020 Fedora Release Engineering <releng(a)fedoraproject.org> - 1.11.0-11
+- Rebuilt for https://fedoraproject.org/wiki/Fedora_33_Mass_Rebuild
+
+* Fri Jan 31 2020 Fedora Release Engineering <releng(a)fedoraproject.org> - 1.11.0-10
+- Rebuilt for https://fedoraproject.org/wiki/Fedora_32_Mass_Rebuild
+
+* Sat Jul 27 2019 Fedora Release Engineering <releng(a)fedoraproject.org> - 1.11.0-9
+- Rebuilt for https://fedoraproject.org/wiki/Fedora_31_Mass_Rebuild
+
+* Sun Feb 03 2019 Fedora Release Engineering <releng(a)fedoraproject.org> - 1.11.0-8
+- Rebuilt for https://fedoraproject.org/wiki/Fedora_30_Mass_Rebuild
+
+* Sat Jul 14 2018 Fedora Release Engineering <releng(a)fedoraproject.org> - 1.11.0-7
+- Rebuilt for https://fedoraproject.org/wiki/Fedora_29_Mass_Rebuild
+
+* Fri Feb 09 2018 Fedora Release Engineering <releng(a)fedoraproject.org> - 1.11.0-6
+- Rebuilt for https://fedoraproject.org/wiki/Fedora_28_Mass_Rebuild
+
+* Thu Aug 03 2017 Fedora Release Engineering <releng(a)fedoraproject.org> - 1.11.0-5
+- Rebuilt for https://fedoraproject.org/wiki/Fedora_27_Binutils_Mass_Rebuild
+
+* Thu Jul 27 2017 Fedora Release Engineering <releng(a)fedoraproject.org> - 1.11.0-4
+- Rebuilt for https://fedoraproject.org/wiki/Fedora_27_Mass_Rebuild
+
+* Sat Feb 11 2017 Fedora Release Engineering <releng(a)fedoraproject.org> - 1.11.0-3
+- Rebuilt for https://fedoraproject.org/wiki/Fedora_26_Mass_Rebuild
+
+* Fri Feb 05 2016 Fedora Release Engineering <releng(a)fedoraproject.org> - 1.11.0-2
+- Rebuilt for https://fedoraproject.org/wiki/Fedora_24_Mass_Rebuild
+
+* Wed Nov 25 2015 Dmitry Mikhirev <mikhirev(a)gmail.com> 1.11.0-1
+- Initial package
diff --git a/teem.spec b/teem.spec
index 5c62687..aded044 100644
--- a/teem.spec
+++ b/teem.spec
@@ -1,7 +1,7 @@
Name: teem
Version: 1.11.0
%global so_version 1
-Release: 16%{?dist}
+Release: %autorelease -b 13
Summary: Libraries for processing and visualizing scientific raster data
# The entire source is LGPLv2+, except files noted at the License fields of certain subpackages.
@@ -351,63 +351,4 @@ install -t %{buildroot}%{_mandir}/man1 -p -m 0644 \
%changelog
-* Tue Apr 13 2021 Benjamin A. Beasley <code(a)musicinmybrain.net> - 1.11.0-16
-- Finish man pages, except “unu” sub-pages. Start installing man pages.
-
-* Sat Apr 03 2021 Benjamin A. Beasley <code(a)musicinmybrain.net> - 1.11.0-15
-- Fix typo in lib_install_dir.patch
-- Update download URL from HTTP to HTTPS
-- Improved package summaries and descriptions
-- Adjust whitespace to personal preference
-- Fix stray line continuation backslash
-- Support EPEL7
-- Build with “levmar” support enabled
-- Use stricter file globs, including specifying the so-version
-- Add an -examples subpackage so the BSD and zlib licenses can be “contained”
- to that subpackage; remove certain unused files with other licenses in %%prep
-- Require cmake-filesystem instead of all of CMake in -devel
-- More precisely skip overly-strict floating-point tests where required
-- Start work on adding a complete set of man pages
-
-* Wed Jan 27 2021 Fedora Release Engineering <releng(a)fedoraproject.org> - 1.11.0-14
-- Rebuilt for https://fedoraproject.org/wiki/Fedora_34_Mass_Rebuild
-
-* Fri Sep 04 2020 Ankur Sinha <ankursinha AT fedoraproject DOT org> - 1.11.0-13
-- Use cmake macros to fix build
-
-* Sat Aug 01 2020 Fedora Release Engineering <releng(a)fedoraproject.org> - 1.11.0-12
-- Second attempt - Rebuilt for
- https://fedoraproject.org/wiki/Fedora_33_Mass_Rebuild
-
-* Wed Jul 29 2020 Fedora Release Engineering <releng(a)fedoraproject.org> - 1.11.0-11
-- Rebuilt for https://fedoraproject.org/wiki/Fedora_33_Mass_Rebuild
-
-* Fri Jan 31 2020 Fedora Release Engineering <releng(a)fedoraproject.org> - 1.11.0-10
-- Rebuilt for https://fedoraproject.org/wiki/Fedora_32_Mass_Rebuild
-
-* Sat Jul 27 2019 Fedora Release Engineering <releng(a)fedoraproject.org> - 1.11.0-9
-- Rebuilt for https://fedoraproject.org/wiki/Fedora_31_Mass_Rebuild
-
-* Sun Feb 03 2019 Fedora Release Engineering <releng(a)fedoraproject.org> - 1.11.0-8
-- Rebuilt for https://fedoraproject.org/wiki/Fedora_30_Mass_Rebuild
-
-* Sat Jul 14 2018 Fedora Release Engineering <releng(a)fedoraproject.org> - 1.11.0-7
-- Rebuilt for https://fedoraproject.org/wiki/Fedora_29_Mass_Rebuild
-
-* Fri Feb 09 2018 Fedora Release Engineering <releng(a)fedoraproject.org> - 1.11.0-6
-- Rebuilt for https://fedoraproject.org/wiki/Fedora_28_Mass_Rebuild
-
-* Thu Aug 03 2017 Fedora Release Engineering <releng(a)fedoraproject.org> - 1.11.0-5
-- Rebuilt for https://fedoraproject.org/wiki/Fedora_27_Binutils_Mass_Rebuild
-
-* Thu Jul 27 2017 Fedora Release Engineering <releng(a)fedoraproject.org> - 1.11.0-4
-- Rebuilt for https://fedoraproject.org/wiki/Fedora_27_Mass_Rebuild
-
-* Sat Feb 11 2017 Fedora Release Engineering <releng(a)fedoraproject.org> - 1.11.0-3
-- Rebuilt for https://fedoraproject.org/wiki/Fedora_26_Mass_Rebuild
-
-* Fri Feb 05 2016 Fedora Release Engineering <releng(a)fedoraproject.org> - 1.11.0-2
-- Rebuilt for https://fedoraproject.org/wiki/Fedora_24_Mass_Rebuild
-
-* Wed Nov 25 2015 Dmitry Mikhirev <mikhirev(a)gmail.com> 1.11.0-1
-- Initial package
+%autochangelog
commit 7dd3eb9d51ce2c2984ed8f6c467d7483da181c65
Author: Benjamin A. Beasley <code(a)musicinmybrain.net>
Date: Tue Apr 13 12:04:10 2021 -0400
Finish man pages, except “unu” sub-pages. Start installing man pages.
diff --git a/ilk.1 b/ilk.1
index 51b2915..e88f365 100644
--- a/ilk.1
+++ b/ilk.1
@@ -33,7 +33,7 @@ Applies linear (homogenous coordinate) transforms to a given image, using the
given kernel for resampling.
.SH OPTIONS
.TP
-.BR @file \ ...
+.IR @file \ ...
response file(s) containing command-line arguments
.TP
.B \-i\fR <\fIimage\fR>
diff --git a/miter.1 b/miter.1
index 75d7ef4..254f8b8 100644
--- a/miter.1
+++ b/miter.1
@@ -79,7 +79,7 @@ miter \- a simple but effective little volume renderer
.RI < filename >]
.SH OPTIONS
.TP
-.BR @file \ ...
+.IR @file \ ...
response file(s) containing command-line arguments
.TP
.B \-i\fR <\fInsin\fR>
diff --git a/mrender.1 b/mrender.1
index f619ac2..c58da9e 100644
--- a/mrender.1
+++ b/mrender.1
@@ -69,7 +69,7 @@ in the output
image.
.SH OPTIONS
.TP
-.BR @file \ ...
+.IR @file \ ...
response file(s) containing command-line arguments
.TP
.B \-i\fR <\fInin\fR>
diff --git a/overrgb.1 b/overrgb.1
index 538bf57..a9e2a0c 100644
--- a/overrgb.1
+++ b/overrgb.1
@@ -31,9 +31,6 @@ have to be the same size as the input image; it will be resampled (with linear
interpolation) to fit.
.SH OPTIONS
.TP
-.BR @file \ ...
-response file(s) containing command-line arguments
-.TP
.B \-i\fR <\fInin\fR>
input nrrd to composite
.RI ( nrrd )
diff --git a/puller.1 b/puller.1
index fb03592..45b7635 100644
--- a/puller.1
+++ b/puller.1
@@ -3,7 +3,7 @@
puller \- command-line interface to the \(lqpull\(rq library
.SH SYNOPSIS
.B puller
-.RB [ @file \ ...]
+.RI [ @file \ ...]
.RB [ \-int
.RI < int >]
.RB [ \-enr
@@ -139,7 +139,7 @@ puller \- command-line interface to the \(lqpull\(rq library
.RI < base >]
.SH OPTIONS
.TP
-.BR @file \ ...
+.IR @file \ ...
response file(s) containing command-line arguments
.TP
.B \-int\fR <\fIint\fR>
diff --git a/teem.spec b/teem.spec
index 61764d0..5c62687 100644
--- a/teem.spec
+++ b/teem.spec
@@ -1,11 +1,7 @@
-# Writing man pages is a work in progress. We do not install them until we have
-# a complete set.
-%bcond_with man_pages
-
Name: teem
Version: 1.11.0
%global so_version 1
-Release: 15%{?dist}
+Release: 16%{?dist}
Summary: Libraries for processing and visualizing scientific raster data
# The entire source is LGPLv2+, except files noted at the License fields of certain subpackages.
@@ -45,36 +41,36 @@ Source807: tend-mconv.1
Source808: tend-msim.1
Source809: tend-make.1
Source810: tend-avg.1
-#Source811: tend-helix.1
-#Source812: tend-sten.1
-#Source813: tend-glyph.1
-#Source814: tend-ellipse.1
-#Source815: tend-anplot.1
-#Source816: tend-anvol.1
-#Source817: tend-anscale.1
-#Source818: tend-anhist.1
-#Source819: tend-triple.1
-#Source820: tend-tconv.1
-#Source821: tend-point.1
-#Source822: tend-slice.1
-#Source823: tend-norm.1
-#Source824: tend-fiber.1
-#Source825: tend-eval.1
-#Source826: tend-evalpow.1
-#Source827: tend-evalclamp.1
-#Source828: tend-evaladd.1
-#Source829: tend-evalmul.1
-#Source830: tend-log.1
-#Source831: tend-exp.1
-#Source832: tend-evec.1
-#Source833: tend-evecrgb.1
-#Source834: tend-evq.1
-#Source835: tend-unmf.1
-#Source836: tend-expand.1
-#Source837: tend-shrink.1
-#Source838: tend-bfit.1
-#Source839: tend-satin.1
-#
+Source811: tend-helix.1
+Source812: tend-sten.1
+Source813: tend-glyph.1
+Source814: tend-ellipse.1
+Source815: tend-anplot.1
+Source816: tend-anvol.1
+Source817: tend-anscale.1
+Source818: tend-anhist.1
+Source819: tend-triple.1
+Source820: tend-tconv.1
+Source821: tend-point.1
+Source822: tend-slice.1
+Source823: tend-norm.1
+Source824: tend-fiber.1
+Source825: tend-eval.1
+Source826: tend-evalpow.1
+Source827: tend-evalclamp.1
+Source828: tend-evaladd.1
+Source829: tend-evalmult.1
+Source830: tend-log.1
+Source831: tend-exp.1
+Source832: tend-evec.1
+Source833: tend-evecrgb.1
+Source834: tend-evq.1
+Source835: tend-unmf.1
+Source836: tend-expand.1
+Source837: tend-shrink.1
+Source838: tend-bfit.1
+Source839: tend-satin.1
+
#Source900: unu-about.1
#Source901: unu-env.1
#Source902: unu-i2w.1
@@ -273,20 +269,20 @@ install -t %{buildroot}%{_mandir}/man1 -p -m 0644 \
%{SOURCE820} %{SOURCE821} %{SOURCE822} %{SOURCE823} %{SOURCE824} \
%{SOURCE825} %{SOURCE826} %{SOURCE827} %{SOURCE828} %{SOURCE829} \
%{SOURCE830} %{SOURCE831} %{SOURCE832} %{SOURCE833} %{SOURCE834} \
- %{SOURCE835} %{SOURCE836} %{SOURCE837} %{SOURCE838} %{SOURCE839} \
- %{SOURCE900} %{SOURCE901} %{SOURCE902} %{SOURCE903} %{SOURCE904} \
- %{SOURCE905} %{SOURCE906} %{SOURCE907} %{SOURCE908} %{SOURCE909} \
- %{SOURCE910} %{SOURCE911} %{SOURCE912} %{SOURCE913} %{SOURCE914} \
- %{SOURCE915} %{SOURCE916} %{SOURCE917} %{SOURCE918} %{SOURCE919} \
- %{SOURCE920} %{SOURCE921} %{SOURCE922} %{SOURCE923} %{SOURCE924} \
- %{SOURCE925} %{SOURCE926} %{SOURCE927} %{SOURCE928} %{SOURCE929} \
- %{SOURCE930} %{SOURCE931} %{SOURCE932} %{SOURCE933} %{SOURCE934} \
- %{SOURCE935} %{SOURCE936} %{SOURCE937} %{SOURCE938} %{SOURCE939} \
- %{SOURCE940} %{SOURCE941} %{SOURCE942} %{SOURCE943} %{SOURCE944} \
- %{SOURCE945} %{SOURCE946} %{SOURCE947} %{SOURCE948} %{SOURCE949} \
- %{SOURCE950} %{SOURCE951} %{SOURCE952} %{SOURCE953} %{SOURCE954} \
- %{SOURCE955} %{SOURCE956} %{SOURCE957} %{SOURCE958} %{SOURCE959} \
- %{SOURCE960} %{SOURCE961}
+ %{SOURCE835} %{SOURCE836} %{SOURCE837} %{SOURCE838} %{SOURCE839}
+ #{SOURCE900} #{SOURCE901} #{SOURCE902} #{SOURCE903} #{SOURCE904} \
+ #{SOURCE905} #{SOURCE906} #{SOURCE907} #{SOURCE908} #{SOURCE909} \
+ #{SOURCE910} #{SOURCE911} #{SOURCE912} #{SOURCE913} #{SOURCE914} \
+ #{SOURCE915} #{SOURCE916} #{SOURCE917} #{SOURCE918} #{SOURCE919} \
+ #{SOURCE920} #{SOURCE921} #{SOURCE922} #{SOURCE923} #{SOURCE924} \
+ #{SOURCE925} #{SOURCE926} #{SOURCE927} #{SOURCE928} #{SOURCE929} \
+ #{SOURCE930} #{SOURCE931} #{SOURCE932} #{SOURCE933} #{SOURCE934} \
+ #{SOURCE935} #{SOURCE936} #{SOURCE937} #{SOURCE938} #{SOURCE939} \
+ #{SOURCE940} #{SOURCE941} #{SOURCE942} #{SOURCE943} #{SOURCE944} \
+ #{SOURCE945} #{SOURCE946} #{SOURCE947} #{SOURCE948} #{SOURCE949} \
+ #{SOURCE950} #{SOURCE951} #{SOURCE952} #{SOURCE953} #{SOURCE954} \
+ #{SOURCE955} #{SOURCE956} #{SOURCE957} #{SOURCE958} #{SOURCE959} \
+ #{SOURCE960} #{SOURCE961}
%endif
@@ -331,7 +327,7 @@ install -t %{buildroot}%{_mandir}/man1 -p -m 0644 \
%{_mandir}/man1/tend.1*
%{_mandir}/man1/tend-*.1*
%{_mandir}/man1/unu.1*
-%{_mandir}/man1/unu-*.1*
+#{_mandir}/man1/unu-*.1*
%{_mandir}/man1/vprobe.1*
%endif
@@ -355,6 +351,9 @@ install -t %{buildroot}%{_mandir}/man1 -p -m 0644 \
%changelog
+* Tue Apr 13 2021 Benjamin A. Beasley <code(a)musicinmybrain.net> - 1.11.0-16
+- Finish man pages, except “unu” sub-pages. Start installing man pages.
+
* Sat Apr 03 2021 Benjamin A. Beasley <code(a)musicinmybrain.net> - 1.11.0-15
- Fix typo in lib_install_dir.patch
- Update download URL from HTTP to HTTPS
diff --git a/tend-anhist.1 b/tend-anhist.1
new file mode 100644
index 0000000..a475647
--- /dev/null
+++ b/tend-anhist.1
@@ -0,0 +1,61 @@
+.TH TEND\-ANHIST "1" "April 2021" "" "User Commands"
+.SH NAME
+tend\-anhist \- generate barycentric histograms of anisotropy
+.SH SYNOPSIS
+.B tend\ anhist
+.RB [ \-v
+.RI < westin\ version >]
+.RB [ \-w
+.RI < nweight >]
+.B \-r
+.RI < res >
+.RB [ \-right ]
+.RB [ \-i
+.RI < nin >]
+.RB [ \-o
+.RI < nout >]
+.SH DESCRIPTION
+Generate barycentric histograms of anisotropy.
+The barycentric space used is either one of Westin\(cqs triple of spherical,
+linear, and planar anisotropy.
+The bin counts in the histogram are weighted by the confidence value.
+.SH OPTIONS
+.TP
+.B \-v\fR <\fIwestin\ version\fR>
+Which version of Westin\(cqs anisotropy metric triple to use, either
+.RB \(lq 1 \(rq
+or
+.RB \(lq 2 \(rq
+.RI ( int );
+default:
+.RB \(lq 1 \(rq
+.TP
+.B \-w\fR <\fInweight\fR>
+how to weigh contributions to histogram.
+By default (not using this option), the increment is one bin count per sample,
+but by giving a
+.IR nrrd ,
+the value in the
+.I nrrd
+at the corresponding location will be the bin count increment; default:
+\(lq\(rq
+.TP
+.B \-r\fR <\fIres\fR>
+resolution of anisotropy plot
+.RI ( int )
+.TP
+.B \-right
+sample a right-triangle-shaped region, instead of a roughly equilateral
+triangle.
+.TP
+.B \-i\fR <\fInin\fR>
+input diffusion tensor volume; default:
+.RB \(lq \- \(rq
+.TP
+.B \-o\fR <\fInout\fR>
+output image (floating point)
+.RI ( string )
+default:
+.RB \(lq \- \(rq
+.SH "SEE\ ALSO"
+.BR tend (1)
diff --git a/tend-anplot.1 b/tend-anplot.1
new file mode 100644
index 0000000..44024a6
--- /dev/null
+++ b/tend-anplot.1
@@ -0,0 +1,106 @@
+.TH TEND\-ANPLOT "1" "April 2021" "" "User Commands"
+.SH NAME
+tend\-anplot \- graph anisotropy metric in barycentric coords
+.SH SYNOPSIS
+.B tend\ anplot
+.RB [ \-r
+.RI < res >]
+.RB [ \-w ]
+.RB [ \-hflip ]
+.RB [ \-nan ]
+.B \-a
+.RI < aniso >
+.RB [ \-o
+.RI < nout >]
+.SH DESCRIPTION
+Graph anisotropy metric in barycentric coordinates.
+The metrics all vary from
+.B 0.0
+to
+.BR 1.0 ,
+and will be sampled in the lower right half of the image.
+The plane on which they are sampled is a surface of constant trace.
+You may want to use
+.in +4n
+.EX
+.B unu\ resample\ \-s\ =\ x0.57735\ \-k\ tent
+.in
+.EE
+to transform the triangle into a 30-60-90 triangle, and
+.in +4n
+.EX
+.B ilk\ \-t\ 1,\-0.5,0,0,0.866,0\ \-k\ tent\ \-0\ u:0,1\ \-b\ pad\ \-bg\ 0
+.EE
+.in
+(possibly followed by
+.BR teem/src/limntest/triimg )
+to transform the domain into an equilateral triangle.
+.SH OPTIONS
+.TP
+.B \-res\fR <\fIres\fR>
+resolution of anisotropy plot
+.RI ( int );
+default:
+.RB \(lq 256 \(rq
+.TP
+.B \-w
+sample the whole triangle of constant trace, instead of just the sixth of it in
+which the eigenvalues have the traditional sorted order.
+.TP
+.B \-hflip
+flip the two bottom corners (swapping the place of linear and planar)
+.TP
+.B \-nan
+set the pixel values outside the triangle to be
+.BR NaN ,
+instead of
+.B 0
+.TP
+.B \-a\fR <\fIaniso\fR>
+Which anisotropy metric to plot.
+All the Westin metrics come in two versions.
+Currently supported:
+.RS +7n
+.IP \(bu 2
+.RB \(lq cl1 \(rq,
+.RB \(lq cl2 \(rq:
+Westin\(cqs linear
+.IP \(bu 2
+.RB \(lq cp1 \(rq,
+.RB \(lq cp2 \(rq:
+Westin\(cqs planar
+.IP \(bu 2
+.RB \(lq ca1 \(rq,
+.RB \(lq ca2 \(rq:
+Westin\(cqs linear + planar
+.IP \(bu 2
+.RB \(lq cs1 \(rq,
+.RB \(lq cs2 \(rq:
+Westin\(cqs spherical
+.IP \(bu 2
+.RB \(lq ct1 \(rq,
+.RB \(lq ct2 \(rq:
+GK\(cqs anisotropy type (cp/ca)
+.IP \(bu 2
+.RB \(lq ra \(rq:
+Basser/Pierpaoli relative anisotropy/sqrt(2)
+.IP \(bu 2
+.RB \(lq fa \(rq:
+Basser/Pierpaoli fractional anisotropy
+.IP \(bu 2
+.RB \(lq vf \(rq:
+volume fraction = 1\-(Basser/Pierpaoli volume ratio)
+.IP \(bu 2
+.RB \(lq tr \(rq:
+trace
+.RE
+.TP
+.B \-o\fR <\fInout\fR>
+output image (floating point)
+.RI ( string )
+default:
+.RB \(lq \- \(rq
+.SH "SEE\ ALSO"
+.BR tend (1),
+.BR ilk (1),
+.BR unu\-resample (1)
diff --git a/tend-anscale.1 b/tend-anscale.1
new file mode 100644
index 0000000..740157d
--- /dev/null
+++ b/tend-anscale.1
@@ -0,0 +1,44 @@
+.TH TEND\-ANSCALE "1" "April 2021" "" "User Commands"
+.SH NAME
+tend\-anscale \- scale the anisotropic component of the tensors
+.SH SYNOPSIS
+.B tend\ anscale
+.B \-s
+.RI < scale >
+.RB [ \-fd ]
+.RB [ \-mp ]
+.RB [ \-i
+.RI < nin >]
+.RB [ \-o
+.RI < nout >]
+.SH DESCRIPTION
+Scale the anisotropic component of the tensors.
+This maintains the isotropic component of the tensor, and fixes either the
+trace or determinant, while scaling up (or down) the \(lqdeviatoric\(rq
+component of the tensor.
+Good for exaggerating the shape of nearly isotropic tensors.
+.SH OPTIONS
+.TP
+.B \-s\fR <\fIscale\fR>
+Amount by which to scale deviatoric component of tensor.
+.RI ( float )
+.TP
+.B \-fd
+instead of fixing the per-sample trace (the default), fix the determinant
+(ellipsoid volume)
+.TP
+.B \-mp
+after changing the eigenvalues of the tensor, enforce their non-negative-ness.
+By default, no such constraint is imposed.
+.TP
+.B \-i\fR <\fInin\fR>
+input diffusion tensor volume; default:
+.RB \(lq \- \(rq
+.TP
+.B \-o\fR <\fInout\fR>
+output image (floating point)
+.RI ( string )
+default:
+.RB \(lq \- \(rq
+.SH "SEE\ ALSO"
+.BR tend (1)
diff --git a/tend-anvol.1 b/tend-anvol.1
new file mode 100644
index 0000000..6c06441
--- /dev/null
+++ b/tend-anvol.1
@@ -0,0 +1,75 @@
+.TH TEND\-ANVOL "1" "April 2021" "" "User Commands"
+.SH NAME
+tend\-anvol \- apply an anisotropy metric to a DT volume
+.SH SYNOPSIS
+.B tend\ anvol
+.B \-a
+.RI < aniso >
+.RB [ \-t
+.RI < thresh >]
+.RB [ \-i
+.RI < nin >]
+.RB [ \-o
+.RI < nout >]
+.SH DESCRIPTION
+Apply an anisotropy metric to a DT volume.
+The anisotropy value will be zero in the locations which don\(cqt meet the
+given confidence threshold.
+.SH OPTIONS
+.TP
+.B \-a\fR <\fIaniso\fR>
+Which anisotropy metric to plot.
+All the Westin metrics come in two versions.
+Currently supported:
+.RS +7n
+.IP \(bu 2
+.RB \(lq cl1 \(rq,
+.RB \(lq cl2 \(rq:
+Westin\(cqs linear
+.IP \(bu 2
+.RB \(lq cp1 \(rq,
+.RB \(lq cp2 \(rq:
+Westin\(cqs planar
+.IP \(bu 2
+.RB \(lq ca1 \(rq,
+.RB \(lq ca2 \(rq:
+Westin\(cqs linear + planar
+.IP \(bu 2
+.RB \(lq cs1 \(rq,
+.RB \(lq cs2 \(rq:
+Westin\(cqs spherical
+.IP \(bu 2
+.RB \(lq ct1 \(rq,
+.RB \(lq ct2 \(rq:
+GK\(cqs anisotropy type (cp/ca)
+.IP \(bu 2
+.RB \(lq ra \(rq:
+Basser/Pierpaoli relative anisotropy/sqrt(2)
+.IP \(bu 2
+.RB \(lq fa \(rq:
+Basser/Pierpaoli fractional anisotropy
+.IP \(bu 2
+.RB \(lq vf \(rq:
+volume fraction = 1\-(Basser/Pierpaoli volume ratio)
+.IP \(bu 2
+.RB \(lq tr \(rq:
+trace
+.RE
+.TP
+.B \-t\fR <\fIthresh\fR>
+confidence threshold
+.RI ( float )
+default:
+.RB \(lq 0.5 \(rq
+.TP
+.B \-i\fR <\fInin\fR>
+input diffusion tensor volume; default:
+.RB \(lq \- \(rq
+.TP
+.B \-o\fR <\fInout\fR>
+output image (floating point)
+.RI ( string )
+default:
+.RB \(lq \- \(rq
+.SH "SEE\ ALSO"
+.BR tend (1)
diff --git a/tend-bfit.1 b/tend-bfit.1
new file mode 100644
index 0000000..9269f75
--- /dev/null
+++ b/tend-bfit.1
@@ -0,0 +1,72 @@
+.TH TEND\-BFIT "1" "April 2021" "" "User Commands"
+.SH NAME
+tend\-bfit \- non-linear least-squares fitting of \fIb\fR-value curves
+.SH SYNOPSIS
+.B tend\ bfit
+.RI [ @file \ ...]
+.RB [ \-i
+.RI < nin >]
+.B \-b
+.RI < b1 \ b2 \ ...>
+.RB [ \-w
+.RI < w1 \ w2 \ ...>]
+.RB [ \-imax
+.RI < #\ iter >]
+.RB [ \-eps
+.RI < epsilon >]
+.RB [ \-o
+.RI < nout >]
+.SH DESCRIPTION
+Non-linear least-squares fitting of \fIb\fR-value curves.
+Axis 0 is replaced by three values:
+.IR amp ,\ dec ,\ err ,
+based on a non-linear least-squares fit of
+.IB amp *exp(\- b * dec )
+to the range of DWI values along input axis 0, as a function of changing
+.I b
+values.
+.SH OPTIONS
+.TP
+.IR @file \ ...
+response file(s) containing command-line arguments
+.TP
+.B \-i\fR <\fInin\fR>
+Input
+.IR nrrd .
+List of DWIs from different
+.IR b -values
+must be along axis 0; default:
+.RB \(lq \- \(rq
+.TP
+.B \-b\fR <\fIb1\fR\ \fIb2\fR\ ...>
+.I b
+values across axis 0 of input
+.I nrrd
+(2 or more
+.IR double s)
+.TP
+.B \-w\fR <\fIw1\fR\ \fIw2\fR\ ...>
+weights for samples in non-linear fitting (2 or more
+.IR double s)
+.TP
+.B \-imax\fR <\fI#\ iter\fR>
+max number of iterations to use in non-linear fitting, or, use
+.B 0
+to do only initial linear fit
+.RI ( int );
+default:
+.RB \(lq 10 \(rq
+.TP
+.B \-eps\fR <\fIepsilon\fR>
+epsilon convergence threshold for non-linear fitting
+.RI ( double );
+default:
+.RB \(lq 1 \(rq
+.TP
+.B \-o\fR <\fInout\fR>
+output tensor volume
+.RI ( string );
+default:
+.RB \(lq \- \(rq
+.SH "SEE\ ALSO"
+.BR tend (1)
diff --git a/tend-ellipse.1 b/tend-ellipse.1
new file mode 100644
index 0000000..13f7913
--- /dev/null
+++ b/tend-ellipse.1
@@ -0,0 +1,99 @@
+.TH TEND\-ELLIPSE "1" "April 2021" "" "User Commands"
+.SH NAME
+tend\-ellipse \- generate postscript renderings of 2D glyphs
+.SH SYNOPSIS
+.B tend\ ellipse
+.RB [ \-ctr
+.RI < conf\ thresh >]
+.RB [ \-gsc
+.RI < scale >]
+.RB [ \-dot
+.RI < radius >]
+.RB [ \-wid
+.RI < width >]
+.RB [ \-inv ]
+.RB [ \-min
+.RI < minX \ minY >]
+.RB [ \-max
+.RI < maxX \ maxY >]
+.RB [ \-i
+.RI < nin >]
+.RB [ \-p
+.RI < pos\ array >]
+.RB [ \-s
+.RI < stn\ array >]
+.RB [ \-o
+.RI < nout >]
+.SH DESCRIPTION
+Generate postscript renderings of 2D glyphs.
+Not much to look at here.
+.SH OPTIONS
+.TP
+.B \-ctr\fR <\fIconf\ thresh\fR>
+Glyphs will be drawn only for tensors with confidence values greater than this
+threshold
+.RI ( float );
+default:
+.RB \(lq 0.5 \(rq
+.TP
+.B \-gsc\fR <\fIscale\fR>
+over-all glyph size
+.RI ( float );
+default:
+.RB \(lq 1 \(rq
+.TP
+.B \-dot\fR <\fIradius\fR>
+radius of little dot to put in middle of ellipse, or
+.RB \(lq 0 \(rq
+for no such dot
+.RI ( float );
+default:
+.RB \(lq 0.0 \(rq
+.TP
+.B \-wid\fR <\fIwidth\fR>
+width of lines for tractlets
+.RI ( float );
+default:
+.RB \(lq 0.0 \(rq
+.TP
+.B \-inv
+use white ellipses on black background, instead of reverse
+.TP
+.B \-min\fR <\fIminX\fR \fIminY\fR>
+when using
+.RB \(lq \-p \(rq,
+minimum corner (2
+.IR float s);
+default:
+.RB \(lq \-1\ \-1 \(rq
+.TP
+.B \-max\fR <\fImaxX\fR \fImaxY\fR>
+when using
+.RB \(lq \-p \(rq,
+maximum corner (2
+.IR float s);
+default:
+.RB \(lq 1\ 1 \(rq
+.TP
+.B \-i\fR <\fInin\fR>
+image of 2D tensors; default:
+.RB \(lq \- \(rq
+.TP
+.B \-p\fR <\fIpos\ array\fR>
+Instead of being on a grid, tensors are at arbitrary locations, as defined by
+this 2-by-N array of
+.IR float s);
+default: \(lq\(rq
+.TP
+.B \-s\fR <\fIstn\ array\fR>
+Locations given by
+.RB \(lq \-p \(rq
+have this connectivity; default: \(lq\(rq
+.TP
+.B \-o\fR <\fInout\fR>
+output PostScript file
+.RI ( string )
+default:
+.RB \(lq \- \(rq
+.SH "SEE\ ALSO"
+.BR tend (1)
diff --git a/tend-eval.1 b/tend-eval.1
new file mode 100644
index 0000000..de9dc27
--- /dev/null
+++ b/tend-eval.1
@@ -0,0 +1,47 @@
+.TH TEND\-EVAL "1" "April 2021" "" "User Commands"
+.SH NAME
+tend\-eval \- calculate one or more eigenvalues in a DT volume
+.SH SYNOPSIS
+.B tend\ eval
+.B \-c
+.RI < c0 \ ...>
+.RB [ \-t
+.RI < thresh >]
+.RB [ \-i
+.RI < nin >]
+.RB [ \-o
+.RI < nout >]
+.SH OPTIONS
+.TP
+.B \-c\fR <\fIc0\fR\ ...>
+which eigenvalues should be saved out.
+.RB \(lq 0 \(rq
+for the largest,
+.RB \(lq 1 \(rq
+for the middle,
+.RB \(lq 2 \(rq
+for the smallest,
+.RB \(lq 0\ 1 \(rq,
+.RB \(lq 1\ 2 \(rq,
+.RB \(lq 0\ 1\ 2 \(rq
+or similar for more than one (1..3
+.IR int s)
+.TP
+.B \-t\fR <\fIthresh\fR>
+confidence threshold
+.RI ( float );
+default:
+.RB \(lq 0.5 \(rq
+.TP
+.B \-i\fR <\fInin\fR>
+input diffusion tensor volume; default:
+.RB \(lq \- \(rq
+.TP
+.B \-o\fR <\fInout\fR>
+output image (floating point)
+.RI ( string )
+default:
+.RB \(lq \- \(rq
+.SH "SEE\ ALSO"
+.BR tend (1),
+.BR tend\-evec (1)
diff --git a/tend-evaladd.1 b/tend-evaladd.1
new file mode 100644
index 0000000..e8754f9
--- /dev/null
+++ b/tend-evaladd.1
@@ -0,0 +1,35 @@
+.TH TEND\-EVALADD "1" "April 2021" "" "User Commands"
+.SH NAME
+tend\-evaladd \- modify shape by adding a constant to all eigenvalues
+.SH SYNOPSIS
+.B tend\ evaladd
+.B \-v
+.RI < value >
+.RB [ \-i
+.RI < nin >]
+.RB [ \-o
+.RI < nout >]
+.SH DESCRIPTION
+Modify shape by adding a constant to all eigenvalues.
+The orientation of the tensor is unchanged.
+Note that unlike
+.BR tend\-anscale (1),
+this operation can completely change the shape of the tensor.
+.SH OPTIONS
+.TP
+.B \-v\fR <\fIvalue\fR>
+Value to add to all eigenvalues
+.RI ( float )
+.TP
+.B \-i\fR <\fInin\fR>
+input diffusion tensor volume; default:
+.RB \(lq \- \(rq
+.TP
+.B \-o\fR <\fInout\fR>
+output image (floating point)
+.RI ( string )
+default:
+.RB \(lq \- \(rq
+.SH "SEE\ ALSO"
+.BR tend (1),
+.BR tend\-anscale (1)
diff --git a/tend-evalclamp.1 b/tend-evalclamp.1
new file mode 100644
index 0000000..b88bd6e
--- /dev/null
+++ b/tend-evalclamp.1
@@ -0,0 +1,47 @@
+.TH TEND\-EVALCLAMP "1" "April 2021" "" "User Commands"
+.SH NAME
+tend\-evalclamp \- modify shape by clamping eigenvalues in some range
+.SH SYNOPSIS
+.B tend\ evalclamp
+.B \-min
+.RI < min >
+.RB [ \-max
+.RI < max >]
+.RB [ \-i
+.RI < nin >]
+.RB [ \-o
+.RI < nout >]
+.SH DESCRIPTION
+Modify shape by clamping eigenvalues in some range.
+The orientation of the tensor is unchanged.
+Note that unlike
+.BR tend\-anscale (1),
+this operation can completely change the shape of the tensor.
+.SH OPTIONS
+.TP
+.B \-min\fR <\fImin\fR>
+Eigenvalues are clamped from below by this (the minimum output eigenvalue).
+Use
+.RB \(lq nan \(rq
+to signify that no minimum clamping should be done.
+.RI ( float )
+.TP
+.B \-max\fR <\fImax\fR>
+Eigenvalues are clamped from above by this (the maximum output eigenvalue).
+Use
+.RB \(lq nan \(rq
+to signify that no maximum clamping should be done.
+.RI ( float )
+.TP
+.B \-i\fR <\fInin\fR>
+input diffusion tensor volume; default:
+.RB \(lq \- \(rq
+.TP
+.B \-o\fR <\fInout\fR>
+output image (floating point)
+.RI ( string )
+default:
+.RB \(lq \- \(rq
+.SH "SEE\ ALSO"
+.BR tend (1),
+.BR tend\-anscale (1)
diff --git a/tend-evalmult.1 b/tend-evalmult.1
new file mode 100644
index 0000000..3bf86b0
--- /dev/null
+++ b/tend-evalmult.1
@@ -0,0 +1,31 @@
+.TH TEND\-EVALMULT "1" "April 2021" "" "User Commands"
+.SH NAME
+tend\-evalmult \- modify shape by multiplying eigenvalues by a constant
+.SH SYNOPSIS
+.B tend\ evalmult
+.B \-v
+.RI < value >
+.RB [ \-i
+.RI < nin >]
+.RB [ \-o
+.RI < nout >]
+.SH DESCRIPTION
+Modify shape by multiplying eigenvalues by a constant.
+The orientation of the tensor is unchanged.
+.SH OPTIONS
+.TP
+.B \-v\fR <\fIvalue\fR>
+Value to multiply eigenvalues by
+.RI ( float )
+.TP
+.B \-i\fR <\fInin\fR>
+input diffusion tensor volume; default:
+.RB \(lq \- \(rq
+.TP
+.B \-o\fR <\fInout\fR>
+output image (floating point)
+.RI ( string )
+default:
+.RB \(lq \- \(rq
+.SH "SEE\ ALSO"
+.BR tend (1)
diff --git a/tend-evalpow.1 b/tend-evalpow.1
new file mode 100644
index 0000000..9f1adf5
--- /dev/null
+++ b/tend-evalpow.1
@@ -0,0 +1,31 @@
+.TH TEND\-EVALPOW "1" "April 2021" "" "User Commands"
+.SH NAME
+tend\-evalpow \- modify shape by raising eigenvalues to some power
+.SH SYNOPSIS
+.B tend\ evalpow
+.B \-p
+.RI < power >
+.RB [ \-i
+.RI < nin >]
+.RB [ \-o
+.RI < nout >]
+.SH DESCRIPTION
+Modify shape by raising eigenvalues to some power.
+The orientation of the tensor is unchanged.
+.SH OPTIONS
+.TP
+.B \-p\fR <\fIpower\fR>
+Power to which to raise all the eigenvalues.
+.RI ( float )
+.TP
+.B \-i\fR <\fInin\fR>
+input diffusion tensor volume; default:
+.RB \(lq \- \(rq
+.TP
+.B \-o\fR <\fInout\fR>
+output tensor volume
+.RI ( string )
+default:
+.RB \(lq \- \(rq
+.SH "SEE\ ALSO"
+.BR tend (1)
diff --git a/tend-evec.1 b/tend-evec.1
new file mode 100644
index 0000000..74e7988
--- /dev/null
+++ b/tend-evec.1
@@ -0,0 +1,47 @@
+.TH TEND\-EVEC "1" "April 2021" "" "User Commands"
+.SH NAME
+tend\-evec \- calculate one or more eigenvectors in a DT volume
+.SH SYNOPSIS
+.B tend\ evec
+.B \-c
+.RI < c0 \ ...>
+.RB [ \-t
+.RI < thresh >]
+.RB [ \-i
+.RI < nin >]
+.RB [ \-o
+.RI < nout >]
+.SH OPTIONS
+.TP
+.B \-c\fR <\fIc0\fR\ ...>
+which eigenvectors should be saved out.
+.RB \(lq 0 \(rq
+for the largest,
+.RB \(lq 1 \(rq
+for the middle,
+.RB \(lq 2 \(rq
+for the smallest,
+.RB \(lq 0\ 1 \(rq,
+.RB \(lq 1\ 2 \(rq,
+.RB \(lq 0\ 1\ 2 \(rq
+or similar for more than one (1..3
+.IR int s)
+.TP
+.B \-t\fR <\fIthresh\fR>
+confidence threshold
+.RI ( float );
+default:
+.RB \(lq 0.5 \(rq
+.TP
+.B \-i\fR <\fInin\fR>
+input diffusion tensor volume; default:
+.RB \(lq \- \(rq
+.TP
+.B \-o\fR <\fInout\fR>
+output image (floating point)
+.RI ( string )
+default:
+.RB \(lq \- \(rq
+.SH "SEE\ ALSO"
+.BR tend (1),
+.BR tend\-eval (1)
diff --git a/tend-evecrgb.1 b/tend-evecrgb.1
new file mode 100644
index 0000000..94e209c
--- /dev/null
+++ b/tend-evecrgb.1
@@ -0,0 +1,108 @@
+.TH TEND\-EVECRGB "1" "April 2021" "" "User Commands"
+.SH NAME
+tend\-evecrgb \- make an RGB volume from an eigenvector and an anisotropy
+.SH SYNOPSIS
+.B tend\ evecrgb
+.B \-c
+.RI < evec\ index >
+.B \-a
+.RI < aniso >
+.RB [ \-t
+.RI < thresh >]
+.RB [ \-bg
+.RI < background >]
+.RB [ \-gr
+.RI < gray >]
+.RB [ \-gam
+.RI < gamma >]
+.RB [ \-i
+.RI < nin >]
+.RB [ \-o
+.RI < nout >]
+.SH OPTIONS
+.TP
+.B \-c\fR <\fIevec\ index\fR>
+which eigenvector will be colored.
+.RB \(lq 0 \(rq
+for the principal,
+.RB \(lq 1 \(rq
+for the middle,
+.RB \(lq 2 \(rq
+for the minor
+.RI ( unsigned\ int )
+.TP
+.B \-a\fR <\fIaniso\fR>
+Which anisotropy to use for modulating the saturation of the colors.
+All the Westin metrics come in two versions.
+Currently supported:
+.RS +7n
+.IP \(bu 2
+.RB \(lq cl1 \(rq,
+.RB \(lq cl2 \(rq:
+Westin\(cqs linear
+.IP \(bu 2
+.RB \(lq cp1 \(rq,
+.RB \(lq cp2 \(rq:
+Westin\(cqs planar
+.IP \(bu 2
+.RB \(lq ca1 \(rq,
+.RB \(lq ca2 \(rq:
+Westin\(cqs linear + planar
+.IP \(bu 2
+.RB \(lq cs1 \(rq,
+.RB \(lq cs2 \(rq:
+Westin\(cqs spherical
+.IP \(bu 2
+.RB \(lq ct1 \(rq,
+.RB \(lq ct2 \(rq:
+GK\(cqs anisotropy type (cp/ca)
+.IP \(bu 2
+.RB \(lq ra \(rq:
+Basser/Pierpaoli relative anisotropy/sqrt(2)
+.IP \(bu 2
+.RB \(lq fa \(rq:
+Basser/Pierpaoli fractional anisotropy
+.IP \(bu 2
+.RB \(lq vf \(rq:
+volume fraction = 1\-(Basser/Pierpaoli volume ratio)
+.IP \(bu 2
+.RB \(lq tr \(rq:
+trace
+.RE
+.TP
+.B \-t\fR <\fIthresh\fR>
+confidence threshold
+.RI ( float )
+default:
+.RB \(lq 0.5 \(rq
+.TP
+.B \-bg\fR <\fIbackground\fR>
+gray level to use for voxels whose confidence is zero
+.RI ( double )
+default:
+.RB \(lq 0 \(rq
+.TP
+.B \-gr\fR <\fIgray\fR>
+the gray level to desaturate towards as anisotropy decreases (while confidence
+remains 1.0)
+.RI ( double )
+default:
+.RB \(lq 0 \(rq
+.TP
+.B \-gam\fR <\fIgamma\fR>
+gamma to use on color components
+.RI ( double )
+default:
+.RB \(lq 1 \(rq
+.TP
+.B \-i\fR <\fInin\fR>
+input diffusion tensor volume; default:
+.RB \(lq \- \(rq
+.TP
+.B \-o\fR <\fInout\fR>
+output image (floating point)
+.RI ( string )
+default:
+.RB \(lq \- \(rq
+.SH "SEE\ ALSO"
+.BR tend (1)
diff --git a/tend-evq.1 b/tend-evq.1
new file mode 100644
index 0000000..2266f75
--- /dev/null
+++ b/tend-evq.1
@@ -0,0 +1,93 @@
+.TH TEND\-EVQ "1" "April 2021" "" "User Commands"
+.SH NAME
+tend\-evq \- quantize directions of diffusion
+.SH SYNOPSIS
+.B tend\ evq
+.RB [ \-c
+.RI < evec\ index >]
+.B \-a
+.RI < aniso >
+.RB [ \-ns ]
+.RB [ \-i
+.RI < nin >]
+.RB [ \-o
+.RI < nout >]
+.SH DESCRIPTION
+Quantize directions of diffusion.
+Because VTK doesn\(cqt do multi-dimensional colormaps, we have to quantize
+directions of diffusion (usually just the principal eigenvector) in order to
+create the usual XYZ<->RGB coloring.
+Because eigenvector directions are poorly defined in regions of low anisotropy,
+the length of the vector (pre-quantization) is modulated by anisotropy,
+requiring the selection of some anisotropy metric.
+.SH OPTIONS
+.TP
+.B \-c\fR <\fIevec\ index\fR>
+Which eigenvector should be quantized:
+
+.RB \(lq 0 \(rq
+for the direction of fastest diffusion (eigenvector associated with largest
+eigenvalue),
+.RB \(lq 1 \(rq
+or
+.RB \(lq 2 \(rq
+for other two eigenvectors (associated with middle and smallest eigenvalue)
+.RI ( int );
+default:
+.RB \(lq 0 \(rq
+.TP
+.B \-a\fR <\fIaniso\fR>
+Which anisotropy metric to scale the eigenvector with.
+All the Westin metrics come in two versions.
+Currently supported:
+.RS +7n
+.IP \(bu 2
+.RB \(lq cl1 \(rq,
+.RB \(lq cl2 \(rq:
+Westin\(cqs linear
+.IP \(bu 2
+.RB \(lq cp1 \(rq,
+.RB \(lq cp2 \(rq:
+Westin\(cqs planar
+.IP \(bu 2
+.RB \(lq ca1 \(rq,
+.RB \(lq ca2 \(rq:
+Westin\(cqs linear + planar
+.IP \(bu 2
+.RB \(lq cs1 \(rq,
+.RB \(lq cs2 \(rq:
+Westin\(cqs spherical
+.IP \(bu 2
+.RB \(lq ct1 \(rq,
+.RB \(lq ct2 \(rq:
+GK\(cqs anisotropy type (cp/ca)
+.IP \(bu 2
+.RB \(lq ra \(rq:
+Basser/Pierpaoli relative anisotropy/sqrt(2)
+.IP \(bu 2
+.RB \(lq fa \(rq:
+Basser/Pierpaoli fractional anisotropy
+.IP \(bu 2
+.RB \(lq vf \(rq:
+volume fraction = 1\-(Basser/Pierpaoli volume ratio)
+.IP \(bu 2
+.RB \(lq tr \(rq:
+trace
+.RE
+.TP
+.B \-ns
+Don\(cqt attenuate the color by anisotropy.
+By default (not using this option), regions with low or no anisotropy are very
+dark colors or black
+.TP
+.B \-i\fR <\fInin\fR>
+input diffusion tensor volume; default:
+.RB \(lq \- \(rq
+.TP
+.B \-o\fR <\fInout\fR>
+output image (floating point)
+.RI ( string )
+default:
+.RB \(lq \- \(rq
+.SH "SEE\ ALSO"
+.BR tend (1)
diff --git a/tend-exp.1 b/tend-exp.1
new file mode 100644
index 0000000..e01f780
--- /dev/null
+++ b/tend-exp.1
@@ -0,0 +1,28 @@
+.TH TEND\-EXP "1" "April 2021" "" "User Commands"
+.SH NAME
+tend\-exp \- calculates \fBexp()\fR of the tensor
+.SH SYNOPSIS
+.B tend\ exp
+.RB [ \-i
+.RI < nin >]
+.B \-o
+.RI < nout >
+.SH DESCRIPTION
+Calculates
+.B exp()
+of the tensor, which is based on
+.B exp()
+of the eigenvalues.
+.SH OPTIONS
+.TP
+.B \-i\fR <\fInin\fR>
+input diffusion tensor volume; default:
+.RB \(lq \- \(rq
+.TP
+.B \-o\fR <\fInout\fR>
+output image
+.RI ( string )
+default:
+.RB \(lq \- \(rq
+.SH "SEE\ ALSO"
+.BR tend (1)
diff --git a/tend-expand.1 b/tend-expand.1
new file mode 100644
index 0000000..7025c54
--- /dev/null
+++ b/tend-expand.1
@@ -0,0 +1,72 @@
+.TH TEND\-EXPAND "1" "April 2021" "" "User Commands"
+.SH NAME
+tend\-expand \- converts masked non-redundant tensor images to redundant
+.SH SYNOPSIS
+.B tend\ expand
+.RB [ \-t
+.RI < thresh >]
+.RB [ \-s
+.RI < scale >]
+.B [ \-unmf ]
+.B [ \-ro ]
+.B [ \-roo ]
+.RB [ \-i
+.RI < nin >]
+.RB [ \-o
+.RI < nout >]
+.SH DESCRIPTION
+Converts masked non-redundant tensor images to redundant.
+For
+images of 3D tensors, this converts from a 7-value tensor starting with the
+confidence/mask value
+.BI ( conf ,\ Dxx ,\ Dxy ,\ Dxz ,\ Dyy ,\ Dyz ,\ Dzz )
+to a 9-value tensor
+with the full matrix
+.BI ( Dxx ,\ Dxy ,\ Dxz ,\ Dxy ,\ Dyy ,\ Dyz ,\ Dxz ,\ Dyz ,\ Dzz ).
+This is set to all zeros when the confidence is below the given threshold.
+For
+images
+of 2D tensors, the conversion is from
+.BI ( conf ,\ Dxx ,\ Dxy ,\ Dyy )
+to
+.BI ( Dxx ,\ Dxy ,\ Dxy ,\ Dyy ).
+.SH OPTIONS
+.TP
+.B \-t\fR <\fIthresh\fR>
+confidence level to threshold output tensors at.
+Should be between
+.B 0.0
+and
+.BR 1.0 .
+.RI ( float );
+default:
+.RB \(lq 0.5 \(rq
+.TP
+.B \-s\fR <\fIscale\fR>
+how to scale values before saving as 9-value tensor.
+Useful for visualization tools which assume certain characteristic ranges of
+eigenvalues
+.RI ( float );
+default:
+.RB \(lq 1.0 \(rq
+.TP
+.B \-unmf
+apply and remove the measurement frame, if it exists
+.TP
+.B \-ro
+reduce general image orientation to axis-aligned spacings
+.TP
+.B \-roo
+reduce general image orientation to axis-aligned spacings, while also making
+some effort to set axis mins from space origin
+.TP
+.B \-i\fR <\fInin\fR>
+input diffusion tensor volume, with 7 values per sample; default:
+.RB \(lq \- \(rq
+.TP
+.B \-o\fR <\fInout\fR>
+output tensor volume, with the 9 matrix components per sample
+.RI ( string )
+.SH "SEE\ ALSO"
+.BR tend (1),
+.BR tend\-shrink (1)
diff --git a/tend-fiber.1 b/tend-fiber.1
new file mode 100644
index 0000000..8733a8a
--- /dev/null
+++ b/tend-fiber.1
@@ -0,0 +1,163 @@
+.TH TEND\-FIBER "1" "April 2021" "" "User Commands"
+.SH NAME
+tend\-fiber \- fiber tractography, from one or more seeds
+.SH SYNOPSIS
+.B tend\ fiber
+.RB [ \-i
+.RI < nin >]
+.RB [ \-dwi ]
+.RB [ \-s
+.RI < seed\ point >]
+.RB [ \-ns
+.RI < seed\ nrrd >]
+.RB [ \-wsp ]
+.RB [ \-t
+.RI < type >]
+.RB [ \-n
+.RI < intg >]
+.RB [ \-k
+.RI < kernel >]
+.RB [ \-wp
+.RI < which >]
+.RB [ \-ap ]
+.RB [ \-wspo ]
+.RB [ \-step
+.RI < step\ size >]
+.B \-stop
+.RI < stop1 \ ...>
+.RB [ \-v
+.RI < verbose >]
+.RB [ \-nmat
+.RI < transform >]
+.RB [ \-o
+.RI < nout >]
+.SH DESCRIPTION
+Fiber tractography, from one or more seeds.
+A fairly complete command-line interface to the
+.B tenFiber
+API.
+.SH OPTIONS
+.TP
+.B \-i\fR <\fInin\fR>
+input volume; default:
+.RB \(lq \- \(rq
+.TP
+.B \-dwi
+input volume is a DWI volume, not a single tensor volume
+.TP
+.B \-s\fR <\fIseed\ point\fR>
+seed point for fiber; it will propogate in two opposite directions starting
+from here (3
+.IR double s);
+default:
+.RB \(lq 0\ 0\ 0 \(rq
+.TP
+.B \-ns\fR <\fIseed\ nrrd\fR>
+3-by-N
+.I nrrd
+of seedpoints; default: \(lq\(rq
+.TP
+.B \-wsp
+define seedpoint and output path in worldspace.
+Otherwise, (without using this option) everything is in index space
+.TP
+.B \-t\fR <\fItype\fR>
+fiber type; defaults to something
+.RI ( string )
+.TP
+.B \-n\fR <\fIintg\fR>
+integration method for fiber tracking; default:
+.RB \(lq rk4 \(rq
+.TP
+.B \-k\fR <\fIkernel\fR>
+kernel for reconstructing tensor field; default:
+.RB \(lq tent \(rq
+.TP
+.B \-wp\fR <\fIwhich\fR>
+when doing multi-tensor tracking, index of path to follow (made moot by
+.RB \(lq \-ap \(rq)
+.RI ( unsigned\ int );
+default:
+.RB \(lq 0 \(rq
+.TP
+.B \-ap
+follow all paths from (all) seedpoint(s), output will be
+.IR polydata ,
+rather than a single 3-by-N
+.IR nrrd ,
+even if only a single path is generated
+.TP
+.B \-wspo
+output should be in worldspace, even if input is not (this feature is unstable
+and/or confusing)
+.TP
+.B \-s\fR <\fIstep\ size\fR>
+stepsize along fiber, in world space
+.RI ( double );
+default:
+.RB \(lq 0.01 \(rq
+.TP
+.B \-stop\fR <\fIstop1\fR\ ...>
+the conditions that should signify the end of a fiber, or when to discard a
+fiber that is done propagating.
+Multiple stopping criteria are logically OR-ed and tested at every point along
+the fiber.
+Possibilities include:
+.RS +7n
+.IP \(bu 2
+.RB \(lq aniso:\fR<\fItype\fR>\fB,\fR<\fIthresh >\(rq:
+require anisotropy to be above the given threshold.
+Which anisotropy type is given as with
+.BR tend-anvol (1)
+.IP \(bu 2
+.RB \(lq len:\fR<\fIlength >\(rq:
+limits the length, in world space, of each fiber half
+.IP \(bu 2
+.RB \(lq steps:\fR<\fIN >\(rq:
+the number of steps in each fiber half is capped at
+.I N
+.IP \(bu 2
+.RB \(lq conf:\fR<\fIthresh >\(rq:
+requires the tensor confidence value to be above the given
+.I thresh
+.IP \(bu 2
+.RB \(lq radius:\fR<\fIthresh >\(rq:
+requires that the radius of curvature of the fiber stay above given
+.I thresh
+.IP \(bu 2
+.RB \(lq frac:\fR<\fIF >\(rq:
+in multi-tensor tracking, the fraction of the tracked component must stay above
+.I F
+.IP \(bu 2
+.RB \(lq minlen:\fR<\fIlen >\(rq:
+discard fiber if its final whole length is below
+.I len
+(not really a termination criterion)
+.IP \(bu 2
+.RB \(lq minsteps:\fR<\fIN >\(rq:
+discard fiber if its final number of steps is below
+.I N
+(not really a termination criterion)
+(1 or more fiber stops)
+.RE
+.TP
+.B \-v\fR <\fIverbose\fR>
+verbosity level
+.RI ( int )
+default:
+.RB \(lq 0 \(rq
+.TP
+.B \-nmat\fR <\fItransform\fR>
+a 4x4 homogenous transform matrix (as a
+.IR nrrd ,
+or just a text file) given with this option will be applied to the output
+tractography vertices just prior to output; default: \(lq\(rq
+.TP
+.B \-o\fR <\fInout\fR>
+output fiber(s)
+.RI ( string )
+default:
+.RB \(lq \- \(rq
+.SH "SEE\ ALSO"
+.BR tend (1),
+.BR tend\-anvol (1)
diff --git a/tend-glyph.1 b/tend-glyph.1
new file mode 100644
index 0000000..c0ee791
--- /dev/null
+++ b/tend-glyph.1
@@ -0,0 +1,462 @@
+.TH TEND\-GLYPH "1" "April 2021" "" "User Commands"
+.SH NAME
+tend\-glyph \- generate postscript or ray-traced renderings of 3D glyphs
+.SH SYNOPSIS
+.B tend\ glyph
+.RI [ @file \ ...]
+.RB [ \-rt ]
+.RB [ \-v
+.RI < level >]
+.RB [ \-ctr
+.RI < conf\ thresh >]
+.RB [ \-a
+.RI < aniso >]
+.RB [ \-atr
+.RI < aniso\ thresh >]
+.RB [ \-p
+.RI < pos\ array >]
+.RB [ \-m
+.RI < mask\ vol >]
+.RB [ \-mtr
+.RI < mask\ thresh >]
+.RB [ \-g
+.RI < glyph\ shape >]
+.RB [ \-sh
+.RI < sharpness >]
+.RB [ \-gsc
+.RI < scale >]
+.RB [ \-c
+.RI < evector\ # >]
+.RB [ \-sat
+.RI < saturation >]
+.RB [ \-ga
+.RI < aniso >]
+.RB [ \-am
+.RI < aniso\ mod >]
+.RB [ \-gg
+.RI < gray >]
+.RB [ \-gam
+.RI < gamma >]
+.RB [ \-emap
+.RI < env\ map >]
+.RB [ \-adsp
+.RI < phong >]
+.RB [ \-bg
+.RI < background >]
+.RB [ \-ec
+.RI < edge\ rgb >]
+.RB [ \-slc
+.RI < axis\ pos >]
+.RB [ \-si
+.RI < slice\ image >]
+.RB [ \-off
+.RI < slice\ offset >]
+.RB [ \-sg
+.RI < slice\ gamma >]
+.RB [ \-sb
+.RI < slice\ bias >]
+.B \-fr
+.RI < from\ point >
+.RB [ \-at
+.RI < at\ point >]
+.RB [ \-up
+.RI < up\ vector >]
+.RB [ \-rh ]
+.RB [ \-dn
+.RI < near\ clip >]
+.RB [ \-df
+.RI < far\ clip >]
+.RB [ \-or ]
+.RB [ \-ur
+.RI < uMin \ uMax >]
+.RB [ \-vr
+.RI < vMin \ vMax >]
+.RB [ \-fv
+.RI < fov >]
+.RB [ \-gr
+.RI < glyph\ res >]
+.RB [ \-wd
+.RI < 3\ widths >]
+.RB [ \-psc
+.RI < scale >]
+.RB [ \-ca
+.RI < angle >]
+.RB [ \-nobg ]
+.RB [ \-concave ]
+.RB [ \-is
+.RI < nx \ ny >]
+.RB [ \-ns
+.RI < #\ samp >]
+.RB [ \-nt
+.RI < #\ threads >]
+.RB [ \-al
+.RI < B \ U \ V \ N \ E >]
+.RB [ \-ao ]
+.RB [ \-shadow
+.RI < s >]
+.RB [ \-hack
+.RI < hack >]
+.RB [ \-i
+.RI < nin >]
+.RB [ \-o
+.RI < nout >]
+.SH DESCRIPTION
+Generate postscript or ray-traced renderings of 3D glyphs.
+Whether the output is postscript or a ray-traced image is controlled by the
+initial
+.RB \(lq \-rt \(rq
+flag (by default, the output is postscript).
+Because this is doing viz/graphics, many parameters need to be set.
+Use a response file to simplify giving the command-line options which aren\(cqt
+changing between invocations.
+The postscript output is an EPS file, suitable for including as a figure in
+LaTeX, or viewing with ghostview, or distilling into PDF.
+The
+ray-traced output is a 5 channel
+.RI ( R , G , B , A , T )
+.I float
+.IR nrrd ,
+suitable for
+.RB \(lq unu\ crop\ \-min\ 0\ 0\ 0\ \-max\ 2\ M\ M \(rq
+followed by
+.RB \(lq unu\ gamma \(rq
+and/or
+.RB \(lq unu\ quantize\ \-b\ 8 \(rq.
+.SH OPTIONS
+.TP
+.IR @file \ ...
+response file(s) containing command-line arguments
+.TP
+.B \-rt
+generate ray-traced output.
+By default (not using this option), postscript output is generated.
+.TP
+.B \-v\fR <\fIlevel\fR>
+verbosity level
+.RI ( int )
+default:
+.RB \(lq 0 \(rq
+.TP
+.B \-ctr\fR <\fIconf\ thresh\fR>
+Glyphs will be drawn only for tensors with confidence values greater than this
+threshold
+.RI ( float )
+default:
+.RB \(lq 0.5 \(rq
+.TP
+.B \-a\fR <\fIaniso\fR>
+Which anisotropy metric to use for thresholding the data points to be drawn;
+default:
+.RB \(lq fa \(rq
+.TP
+.B \-p\fR <\fIpos\ array\fR>
+Instead of being on a grid, tensors are at arbitrary locations, as defined by
+this 3-by-N array of floats.
+Doing this makes various other options moot; default: \(lq\(rq
+.TP
+.B \-m\fR <\fImask\ vol\fR>
+Scalar volume (if any) for masking region in which glyphs are drawn, in
+conjunction with
+.RB \(lq \-mtr \(rq
+flag; default: \(lq\(rq
+.TP
+.B \-mtr\fR <\fImask\ thresh\fR>
+Glyphs will be drawn only for tensors with mask value greater than this
+threshold
+.RI ( float )
+default:
+.RB \(lq 0.5 \(rq
+.TP
+.B \-g\fR <\fIglyph\ shape\fR>
+shape of glyph to use for display. Possibilities include
+.RB \(lq box \(rq,
+.RB \(lq sphere \(rq,
+.RB \(lq cylinder \(rq,
+and
+.RB \(lq superquad \(rq;
+default:
+.RB \(lq box \(rq
+.TP
+.B \-sh\fR <\fIsharpness\fR>
+for superquadric glyphs, how much to sharp edges form as a function of
+differences between eigenvalues.
+Higher values mean that edges form more easily
+.RI ( float )
+default:
+.RB \(lq 3.0 \(rq
+.TP
+.B \-gsc\fR <\fIscale\fR>
+over-all glyph size in world-space
+.RI ( float )
+default:
+.RB \(lq 0.01 \(rq
+.TP
+.B \-c\fR <\fIevector\ #\fR>
+which eigenvector should determine coloring. (formally
+.RB \(lq v \(rq)
+.RB \(lq 0 \(rq,
+.RB \(lq 1 \(rq,
+.RB \(lq 2 \(rq,
+are principal, medium, and minor
+.RI ( int )
+default:
+.RB \(lq 0 \(rq
+.TP
+.B \-sat\fR <\fIsaturation\fR>
+maximal saturation to use on glyph colors (use
+.B 0.0
+to create a black and white image)
+.RI ( float )
+default:
+.RB \(lq 1.0 \(rq
+.TP
+.B \-ga\fR <\fIaniso\fR>
+Which anisotropy metric to use for modulating the saturation of the glyph
+color; default:
+.RB \(lq fa \(rq
+.TP
+.B \-am\fR <\fIaniso\ mod\fR>
+How much to modulate glyph color saturation by anisotropy (as chosen by
+.RB \(lq \-ga \(rq.
+If
+.BR 1.0 ,
+then glyphs for zero anisotropy data points will have no hue.
+.RI ( float )
+default:
+.RB \(lq 0.0 \(rq
+.TP
+.B \-gg\fR <\fIgray\fR>
+desaturating glyph color due to low anisotropy tends towards this gray level
+.RI ( float )
+default:
+.RB \(lq 1.0 \(rq
+.TP
+.B \-gam\fR <\fIgamma\fR>
+gamma to use on color components (after saturation)
+.RI ( float )
+default:
+.RB \(lq 0.7 \(rq
+.TP
+.B \-emap\fR <\fIenv\ map\fR>
+environment map to use for shading glyphs.
+By default, there is no shading; default: \(lq\(rq
+.TP
+.B \-adsp\fR <\fIphong\fR>
+phong ambient, diffuse, specular components, and specular power (4
+.IR float s);
+default:
+.RB \(lq 0\ 1\ 0\ 30 \(rq
+.TP
+.B \-bg\fR <\fIbackground\fR>
+background RGB color; each component in range
+.B [0.0,1.0]
+(3
+.IR float s);
+default:
+.RB \(lq 1\ 1\ 1 \(rq
+.TP
+.B \-ec\fR <\fIedge\ rgb\fR>
+edge RGB color; each component in range
+.B [0.0,1.0]
+(3
+.IR float s);
+default:
+.RB \(lq 0\ 0\ 0 \(rq
+.TP
+.B \-slc\fR <\fIaxis\ pos\fR>
+For showing a gray-scale slice of anisotropy: the axis and position along which
+to slice.
+Use
+.RB \(lq \-1\ \-1 \(rq
+to signify that no slice should be shown (2
+.IR int s);
+default:
+.RB \(lq \-1\ \-1 \(rq
+.TP
+.B \-si\fR <\fIslice\ image\fR>
+Instead of showing a slice of the anisotropy used to cull glyphs, show
+something else; default: \(lq\(rq
+.TP
+.B \-off\fR <\fIslice\ offset\fR>
+Offset from slice position to render slice at (so that it doesn\(cqt occlude
+glyphs).
+.RI ( float )
+default:
+.RB \(lq 0.0 \(rq
+.TP
+.B \-sg\fR <\fIslice\ gamma\fR>
+Gamma to apply to values on slice.
+.RI ( float )
+default:
+.RB \(lq 1.7 \(rq
+.TP
+.B \-sb\fR <\fIslice\ bias\fR>
+amount by which to bump up slice gray values prior to gamma.
+.RI ( float )
+default:
+.RB \(lq 0.05 \(rq
+.TP
+.B \-fr\fR <\fIfrom\ point\fR>
+position of camera, used to determine view vector (3
+.IR double s)
+.TP
+.B \-at\fR <\fIat\ point\fR>
+camera look-at point, used to determine view vector (3
+.IR double s)
+.TP
+.B \-up\fR <\fIup\ vector\fR>
+camera pseudo-up vector, used to determine view coordinates (3
+.IR double s);
+default:
+.RB \(lq 0\ 0\ 1 \(rq
+.TP
+.B \-rh
+use a right-handed UVN frame (V points down)
+.TP
+.B \-dn\fR <\fInear\ clip\fR>
+position of near clipping plane, relative to look-at point
+.RI ( double );
+default:
+.RB \(lq \-2 \(rq
+.TP
+.B \-df\fR <\fIfar\ clip\fR>
+position of far clipping plane, relative to look-at point
+.RI ( double );
+default:
+.RB \(lq 2 \(rq
+.TP
+.B \-or
+use orthogonal projection
+.TP
+.B \-ur\fR <\fIuMin\fR\ \fIuMax\fR>
+range in U direction of image plane (2
+.IR double s);
+default:
+.RB \(lq \-1\ 1 \(rq
+.TP
+.B \-vr\fR <\fIvMin\fR\ \fIvMax\fR>
+range in V direction of image plane (2
+.IR double s);
+default:
+.RB \(lq \-1\ 1 \(rq
+.TP
+.B \-fv\fR <\fIfov\fR>
+if not
+.BR NaN ,
+vertical field-of-view, in degrees
+.RI ( double )
+.TP
+.B \-gr\fR <\fIglyph\ res\fR>
+.B postscript only
+.IP
+resolution of polygonalization of glyphs (all glyphs other than the default
+box)
+.RI ( int );
+default:
+.RB \(lq 10 \(rq
+.TP
+.B \-wd\fR <\fI3\ widths\fR>
+.B postscript only
+.IP
+width of edges drawn for three kinds of glyph edges: silohuette, crease,
+non-crease (3
+.IR float s);
+default:
+.RB \(lq 0.8\ 0.4\ 0.0 \(rq
+.TP
+.B \-psc\fR <\fIscale\fR>
+.B postscript only
+.IP
+scaling from screen space units to postscript units (in points)
+.RI ( float );
+default:
+.RB \(lq 300 \(rq
+.TP
+.B \-ca\fR <\fIangle\fR>
+.B postscript only
+.IP
+minimum crease angle
+.RI ( float );
+default:
+.RB \(lq 70 \(rq
+.TP
+.B \-nobg
+.B postscript only
+.IP
+don\(cqt initially fill with background color
+.TP
+.B \-concave
+use slightly buggy rendering method suitable for concave or self-occluding
+objects
+.TP
+.B \-is\fR <\fInx\fR\ \fIny\fR>
+.B ray-traced only
+.IP
+image size (resolution) to render (2
+.IR int s);
+default:
+.RB \(lq 256\ 256 \(rq
+.TP
+.B \-ns\fR <\fI#\ samp\fR>
+.B ray-traced only
+.IP
+number of samples per pixel (must be a square number)
+.RI ( int );
+default:
+.RB \(lq 4 \(rq
+.TP
+.B \-nt\fR <\fI#\ threads\fR>
+.B ray-traced only
+.IP
+number of threads to be used for rendering
+.RI ( int );
+default:
+.RB \(lq 1 \(rq
+.TP
+.B \-al\fR <\fIB\fR\ \fIU\fR\ \fIV\fR\ \fIN\fR\ \fIE\fR>
+.B ray-traced only
+.IP
+brightness
+.RI ( B ),
+view-space location
+.RI ( U \ V \ N ),
+and length of edge
+.RI ( E )
+of a square area light source, for getting soft shadows.
+Requires lots more samples
+.RB \(lq \-ns \(rq
+to converge.
+Use brightness
+.B 0
+(the default) to turn this off, and use environment map-based shading
+.RB (\(lq \-emap \(rq)
+instead.
+(5
+.IR float s);
+default:
+.RB \(lq 0\ \-1\ \-1\ \-4\ 0.7 \(rq
+.TP
+.B \-ao
+set up 6 area lights in a box to approximate ambient occlusion
+.TP
+.B \-shadow\fR <\fIs\fR>
+the extent to which shadowing occurs
+.RI ( float );
+default:
+.RB \(lq 1.0 \(rq
+.TP
+.B \-hack\fR <\fIhack\fR>
+don\(cqt mind me
+.RI ( string )
+.TP
+.B \-i\fR <\fInin\fR>
+input diffusion tensor volume; default:
+.RB \(lq \- \(rq
+.TP
+.B \-o\fR <\fInout\fR>
+output file
+.RI ( string )
+default:
+.RB \(lq \- \(rq
+.SH "SEE\ ALSO"
+.BR tend (1)
diff --git a/tend-helix.1 b/tend-helix.1
new file mode 100644
index 0000000..79326f5
--- /dev/null
+++ b/tend-helix.1
@@ -0,0 +1,133 @@
+.TH TEND\-HELIX "1" "April 2021" "" "User Commands"
+.SH NAME
+tend\-helix \- generate twisting helical tensor field
+.SH SYNOPSIS
+.B tend\ helix
+.RB [ \-s
+.RI < size >]
+.RB [ \-ip
+.RI < image\ orientation >]
+.RB [ \-mp
+.RI < measurement\ orientation >]
+.RB [ \-b
+.RI < boundary >]
+.RB [ \-r
+.RI < little\ radius >]
+.RB [ \-R
+.RI < big\ radius >]
+.RB [ \-S
+.RI < spacing >]
+.RB [ \-a
+.RI < angle >]
+.RB [ \-nit ]
+.RB [ \-ev
+.RI < eigenvalues >]
+.RB [ \-bg
+.RI < background >]
+.RB [ \-v
+.RI < verbose >]
+.RB [ \-o
+.RI < nout >]
+.SH DESCRIPTION
+Generate twisting helical tensor field.
+The main utility of such a field is to debug handling of coordinate systems in
+tensor field visualization.
+The \(lqspace directions\(rq and \(lqspace origin\(rq fields of the NRRD header
+determines the mapping from coordinates in the index space of the image to
+coordinates in the world space in which the image is sampled.
+The \(lqmeasurement frame\(rq field determines the mapping from the coordinates
+of the tensor itself, to coordinates of the world space.
+When these are correctly handled, the region of high anisotropy is a
+right-handed helix (same as DNA).
+Using differing axes sizes (via
+.RB \(lq \-s \(rq)
+helps make sure that the raster ordering of axes is correct.
+In addition, the tensors twist relative to the helix, which exposes handling of
+the measurement frame.
+If you trace paths guided by the principal eigenvector of the tensors, along
+the surface of the helical cylinder, you get another right-handed helix, as if
+the the tensor field is modeling the result of twisting a set of fibers into
+single-stranded helical bundle.
+.SH OPTIONS
+.TP
+.B \-s\fR <\fIsize\fR>
+sizes along fast, medium, and slow axes of the sampled volume, often called
+\(lqX\(rq, \(lqY\(rq, and \(lqZ\(rq.
+It is best to use slightly different sizes here, to expose errors in
+interpreting axis ordering (e.g. \(lq\fB\-s\ 39\ 40\ 41\fR\(rq) (3 \fIint\fRs)
+.TP
+.B \-ip\fR <\fIimage\ orientation\fR>
+quaternion quotient space orientation of image (3 \fIdouble\fRs); default:
+.RB \(lq 0\ 0\ 0 \(rq
+.TP
+.B \-mp\fR <\fImeasurement\ orientation\fR>
+quaternion quotient space orientation of measurement frame (3 \fIdouble\fRs);
+default:
+.RB \(lq 0\ 0\ 0 \(rq
+.TP
+.B \-b\fR <\fIboundary\fR>
+parameter governing how fuzzy the boundary between high and low anisotropy is.
+Use
+.RB \(lq \-b\ 0 \(rq
+for no fuzziness
+.RI ( double );
+default:
+.RB \(lq 10 \(rq
+.TP
+.B \-r\fR <\fIlittle\ radius\fR>
+(minor) radius of cylinder tracing helix
+.RI ( double );
+default:
+.RB \(lq 30 \(rq
+.TP
+.B \-R\fR <\fIbig\ radius\fR>
+(major) radius of helical turns
+.RI ( double );
+default:
+.RB \(lq 50 \(rq
+.TP
+.B \-S\fR <\fIspacing\fR>
+spacing between turns of helix (along its axis)
+.RI ( double );
+default:
+.RB \(lq 100 \(rq
+.TP
+.B \-a\fR <\fIangle\fR>
+maximal angle of twist of tensors along path.
+There is no twist at helical core of path, and twist increases linearly with
+radius around this path.
+Positive twist angle with positive spacing resulting in a right-handed twist
+around a right-handed helix.
+.RI ( double );
+default:
+.RB \(lq 60 \(rq
+.TP
+.B \-nit
+changes behavior of twist angle as function of distance from center of helical
+core: instead of increasing linearly as describe above, be at a constant angle
+.TP
+.B \-ev\fR <\fIeigenvalues\fR>
+eigenvalues of tensors (in order) along direction of coil, circumferential
+around coil, and radial around coil.
+(3 \fIdouble\fRs); default:
+.RB \(lq 0.006\ 0.002\ 0.001 \(rq
+.TP
+.B \-bg\fR <\fIbackground\fR>
+eigenvalue of isotropic background
+.RI ( double );
+default:
+.RB \(lq 0.5 \(rq
+.TP
+.B \-v\fR <\fIverbose\fR>
+verbose output
+.RI ( int );
+default:
+.RB \(lq 1 \(rq
+.TP
+.B \-o\fR <\fInout\fR>
+output file
+.RI ( string )
+default:
+.RB \(lq \- \(rq
+.SH "SEE\ ALSO"
+.BR tend (1)
diff --git a/tend-log.1 b/tend-log.1
new file mode 100644
index 0000000..07d328a
--- /dev/null
+++ b/tend-log.1
@@ -0,0 +1,25 @@
+.TH TEND\-LOG "1" "April 2021" "" "User Commands"
+.SH NAME
+tend\-log \- calculates logarithm of the tensor
+.SH SYNOPSIS
+.B tend\ log
+.RB [ \-i
+.RI < nin >]
+.B \-o
+.RI < nout >
+.SH DESCRIPTION
+Calculates logarithm of the tensor, which is based on finding the log of the
+eigenvalues.
+.SH OPTIONS
+.TP
+.B \-i\fR <\fInin\fR>
+input diffusion tensor volume; default:
+.RB \(lq \- \(rq
+.TP
+.B \-o\fR <\fInout\fR>
+output image
+.RI ( string )
+default:
+.RB \(lq \- \(rq
+.SH "SEE\ ALSO"
+.BR tend (1)
diff --git a/tend-msim.1 b/tend-msim.1
index 2d0b76a..3f37f0b 100644
--- a/tend-msim.1
+++ b/tend-msim.1
@@ -63,7 +63,8 @@ gradient list, one row per diffusion-weighted image
.B \-b0\fR <\fIb0\ image\fR>
reference non-diffusion-weighted
.RB (\(lq B0 \(rq)
-image, which may be needed if it isn't part of the given model parameter image
+image, which may be needed if it isn\(cqt part of the given model parameter
+image
.TP
.B \-m\fR <\fImodel\fR>
model with which to simulate DWIs, which must be specified if it is not
@@ -74,7 +75,7 @@ indicated by the first axis in input model image.
insert a non-DW
.B B0
image at the beginning of the experiment specification (useful if the given
-gradient list doesn't already have one) and hence also insert a
+gradient list doesn\(cqt already have one) and hence also insert a
.B B0
image at the beginning of the output simulated DWIs
.RB \(lq bool \(rq
diff --git a/tend-norm.1 b/tend-norm.1
new file mode 100644
index 0000000..9d74928
--- /dev/null
+++ b/tend-norm.1
@@ -0,0 +1,48 @@
+.TH TEND\-NORM "1" "April 2021" "" "User Commands"
+.SH NAME
+tend\-norm \- normalize tensor size
+.SH SYNOPSIS
+.B tend\ norm
+.B \-w
+.RI < w0 \ w1 \ w2 >
+.RB [ \-a
+.RI < amount >]
+.RB [ \-t
+.RI < target >]
+.RB [ \-i
+.RI < nin >]
+.RB [ \-o
+.RI < nout >]
+.SH OPTIONS
+.TP
+.B \-w\fR <\fIw0\fR\ \fIw1\fR\ \fIw2\fR>
+relative weights to put on major, medium, and minor eigenvalue when performing
+normalization (internally rescaled to have a 1.0 L1 norm).
+These weightings determine the tensors\(cqs \(lqsize\(rq.
+(3
+.IR double s)
+.TP
+.B \-a\fR <\fIamount\fR>
+how much of the normalization to perform
+.RI ( float )
+default:
+.RB \(lq 1.0 \(rq
+.TP
+.B \-t\fR <\fItarget\fR>
+target size, post normalization
+.RI ( float )
+default:
+.RB \(lq 1.0 \(rq
+.TP
+.TP
+.B \-i\fR <\fInin\fR>
+input diffusion tensor volume; default:
+.RB \(lq \- \(rq
+.TP
+.B \-o\fR <\fInout\fR>
+output image (floating point)
+.RI ( string )
+default:
+.RB \(lq \- \(rq
+.SH "SEE\ ALSO"
+.BR tend (1)
diff --git a/tend-point.1 b/tend-point.1
new file mode 100644
index 0000000..7e2e55e
--- /dev/null
+++ b/tend-point.1
@@ -0,0 +1,27 @@
+.TH TEND\-POINT "1" "April 2021" "" "User Commands"
+.SH NAME
+tend\-point \- describe everything about one sample in a DT volume
+.SH SYNOPSIS
+.B tend\ point
+.B \-p
+.RI < x \ y \ z >
+.RB [ \-i
+.RI < nin >]
+.SH OPTIONS
+.TP
+.TP
+.B \-p\fR <\fIx\fR\ \fIy\fR\ \fIz\fR>
+coordinates of sample to be described (3
+.IR int s)
+.TP
+.B \-i\fR <\fInin\fR>
+input diffusion tensor volume; default:
+.RB \(lq \- \(rq
+.TP
+.B \-o\fR <\fInout\fR>
+output array
+.RI ( string )
+default:
+.RB \(lq \- \(rq
+.SH "SEE\ ALSO"
+.BR tend (1)
diff --git a/tend-satin.1 b/tend-satin.1
new file mode 100644
index 0000000..5b6b85f
--- /dev/null
+++ b/tend-satin.1
@@ -0,0 +1,113 @@
+.TH TEND\-SATIN "1" "April 2021" "" "User Commands"
+.SH NAME
+tend\-satin \- generate a pretty synthetic DT volume
+.SH SYNOPSIS
+.B tend\ satin
+.RI [ \-t ]
+.B \-p
+.RI < aniso\ param >
+.RB [ \-max
+.RI < max\ cal >]
+.RB [ \-min
+.RI < min\ cal >]
+.RB [ \-b
+.RI < boundary >]
+.RB [ \-br
+.RI < ramp >]
+.RB [ \-th
+.RI < thickness >]
+.RB [ \-evsc
+.RI < eval\ scale >]
+.RB [ \-s
+.RI < size >]
+.RB [ \-o
+.RI < nout >]
+.SH DESCRIPTION
+Generate a pretty synthetic DT volume.
+The surface of a sphere or torus is covered with either linear or planar
+anisotropic tensors, or somewhere in between.
+.SH OPTIONS
+.TP
+.B \-t
+generate a torus dataset, instead of the default spherical
+.TP
+.B \-p\fR <\fIaniso\ param\fR>
+anisotropy parameter.
+.B 0.0
+for one direction of linear (along the equator for spheres, or along the larger
+circumference for toruses),
+.B 1.0
+for planar,
+.B 2.0
+for the other direction of linear (from pole to pole for spheres, or along the
+smaller circumference for toruses)
+.RI ( float )
+.TP
+.B \-max\fR <\fImax\ cal\fR>
+maximum anisotropy in dataset, according to the
+.RB \(lq ca1 \(rq
+anisotropy metric.
+.RB \(lq 1.0 \(rq
+means completely linear or completely planar anisotropy
+.RI ( float );
+default:
+.RB \(lq 1.0 \(rq
+.TP
+.B \-min\fR <\fImin\ cal\fR>
+minimum anisotropy in dataset
+.RI ( float );
+default:
+.RB \(lq 0.0 \(rq
+.TP
+.B \-b\fR <\fIboundary\fR>
+parameter governing how fuzzy the boundary between high and low anisotropy is.
+Use
+.RB \(lq \-b\ 0 \(rq
+for no fuzziness
+.RI ( float );
+default:
+.RB \(lq 0.05 \(rq
+.TP
+.B \-br\fR <\fIramp\fR>
+how much to ramp up effective
+.RI \(lq \-b \(rq
+along Y axis.
+Use
+.RB \(lq \-b\ 0 \(rq
+for no such ramping.
+.RI ( float );
+default:
+.RB \(lq 0.0 \(rq
+.TP
+.B \-th\fR <\fIthickness\fR>
+parameter governing how thick region of high anisotropy is
+.RI ( float );
+default:
+.RB \(lq 0.3 \(rq
+.TP
+.B \-evsc\fR <\fIeval\ scale\fR>
+scaling of eigenvalues
+.RI ( float );
+default:
+.RB \(lq 1.0 \(rq
+.TP
+.B \-s\fR <\fIsize\fR>
+dimensions of output volume.
+For size
+.IR N ,
+the output is
+.IB N \ x\ N \ x\ N
+for spheres, and
+.BI 2 N \ x\ 2 N \ x\ N
+for toruses
+.RI ( int );
+default:
+.RB \(lq 32 \(rq
+.TP
+.B \-o\fR <\fInout\fR>
+output filename
+.RI ( string );
+default:
+.RB \(lq \- \(rq
+.SH "SEE\ ALSO"
+.BR tend (1)
diff --git a/tend-shrink.1 b/tend-shrink.1
new file mode 100644
index 0000000..8c504e1
--- /dev/null
+++ b/tend-shrink.1
@@ -0,0 +1,32 @@
+.TH TEND\-SHRINK "1" "April 2021" "" "User Commands"
+.SH NAME
+tend\-shrink \- converts a 9-value DT volume to a 7-value DT volume
+.SH SYNOPSIS
+.B tend\ shrink
+.RB [ \-i
+.RI < nin >]
+.RB [ \-o
+.RI < nout >]
+.SH DESCRIPTION
+Converts a 9-value DT volume to a 7-value DT volume.
+The confidence value is set to
+.B 1.0
+everywhere.
+You can
+.BR unu\-splice (1)
+or
+.B nrrdSplice()
+something else in its place later.
+.SH OPTIONS
+.TP
+.B \-i\fR <\fInin\fR>
+input diffusion tensor volume, with 9 matrix components per sample; default:
+.RB \(lq \- \(rq
+.TP
+.B \-o\fR <\fInout\fR>
+output tensor volume, with the 7 values per sample
+.RI ( string )
+.SH "SEE\ ALSO"
+.BR tend (1),
+.BR tend\-expand (1),
+.BR unu\-splice (1)
diff --git a/tend-sim.1 b/tend-sim.1
index 3761398..c40a192 100644
--- a/tend-sim.1
+++ b/tend-sim.1
@@ -45,7 +45,7 @@ measurement frame.
.SH OPTIONS
.TP
.B \-old
-don't use the new
+don\(cqt use the new
.B tenEstimateContext
functionality
.TP
diff --git a/tend-slice.1 b/tend-slice.1
new file mode 100644
index 0000000..a42335a
--- /dev/null
+++ b/tend-slice.1
@@ -0,0 +1,45 @@
+.TH TEND\-SLICE "1" "April 2021" "" "User Commands"
+.SH NAME
+tend\-slice \- slice 3D tensors to get slab/image of 3D/2D tensors
+.SH SYNOPSIS
+.B tend\ slice
+.B \-a
+.RI < axis >
+.B \-p
+.RI < pos >
+.RB [ \-d
+.RI < dim >]
+.RB [ \-i
+.RI < nin >]
+.RB [ \-o
+.RI < nout >]
+.SH OPTIONS
+.TP
+.B \-a\fR <\fIaxis\fR>
+axis along which to slice
+.RI ( int )
+.TP
+.B \-p\fR <\fIpos\fR>
+position to slice at
+.RI ( int )
+.TP
+.B \-d\fR <\fIdim\fR>
+dimension of desired tensor output, can be either
+.B 2
+or
+.B 3
+.RI ( int );
+default:
+.RB \(lq 3 \(rq
+.TP
+.B \-i\fR <\fInin\fR>
+input diffusion tensor volume; default:
+.RB \(lq \- \(rq
+.TP
+.B \-o\fR <\fInout\fR>
+output tensor slice
+.RI ( string )
+default:
+.RB \(lq \- \(rq
+.SH "SEE\ ALSO"
+.BR tend (1)
diff --git a/tend-sten.1 b/tend-sten.1
new file mode 100644
index 0000000..e5a13a5
--- /dev/null
+++ b/tend-sten.1
@@ -0,0 +1,51 @@
+.TH TEND\-STEN "1" "April 2021" "" "User Commands"
+.SH NAME
+tend\-sten \- calculate structure tensors from a scalar field
+.SH SYNOPSIS
+.B tend\ sten
+.RB [ \-ds
+.RI < diff.\ scale >]
+.RB [ \-is
+.RI < int.\ scale >]
+.RB [ \-df
+.RI < downsample\ factor >]
+.RB [ \-i
+.RI < nin >]
+.RB [ \-o
+.RI < nout >]
+.SH DESCRIPTION
+Calculate structure tensors from a scalar field.
+Not a diffusion tensor, but it is symmetric and positive-definite.
+.SH OPTIONS
+.TP
+.B \-ds\fR <\fIdiff.\ scale\fR>
+differentiation scale, in pixels: the radius of the kernel used for
+differentation to compute gradient vectors
+.RI ( int );
+default:
+.RB \(lq 1 \(rq
+.TP
+.B \-is\fR <\fIint.\ scale\fR>
+integration scale, in pixels: the radius of the kernel used for blurring outer
+products of gradients in order compute structure tensors
+.RI ( int );
+default:
+.RB \(lq 2 \(rq
+.TP
+.B \-df\fR <\fIdownsample\ factor\fR>
+the factor by which to downsample when creating volume of structure tensors
+.RI ( int );
+default:
+.RB \(lq 1 \(rq
+.TP
+.B \-i\fR <\fInin\fR>
+input scalar volume; default:
+.RB \(lq \- \(rq
+.TP
+.B \-o\fR <\fInout\fR>
+output filename
+.RI ( string )
+default:
+.RB \(lq \- \(rq
+.SH "SEE\ ALSO"
+.BR tend (1)
diff --git a/tend-tconv.1 b/tend-tconv.1
new file mode 100644
index 0000000..7949634
--- /dev/null
+++ b/tend-tconv.1
@@ -0,0 +1,34 @@
+.TH TEND\-TCONV "1" "April 2021" "" "User Commands"
+.SH NAME
+tend\-tconv \- convert between different shape triples
+.SH SYNOPSIS
+.B tend\ tconv
+.B \-t
+.RI < inType \ outType >
+.RB [ \-i
+.RI < nin >]
+.RB [ \-o
+.RI < nout >]
+.SH DESCRIPTION
+Convert between different shape triples.
+The triple can be eigenvalues,
+invariants
+.BI ( J ,\ K ,\ R )\fR,
+and lots of other things.
+.SH OPTIONS
+.TP
+.B \-t\fR <\fIinType\fR\ \fIoutType\fR>
+given input and desired output type of triples (2
+.IR tenTripleType s)
+.TP
+.B \-i\fR <\fInin\fR>
+input array of triples; default:
+.RB \(lq \- \(rq
+.TP
+.B \-o\fR <\fInout\fR>
+output array
+.RI ( string )
+default:
+.RB \(lq \- \(rq
+.SH "SEE\ ALSO"
+.BR tend (1)
diff --git a/tend-triple.1 b/tend-triple.1
new file mode 100644
index 0000000..21fe16e
--- /dev/null
+++ b/tend-triple.1
@@ -0,0 +1,33 @@
+.TH TEND\-TRIPLE "1" "April 2021" "" "User Commands"
+.SH NAME
+tend\-triple \- compute volume of shape triples
+.SH SYNOPSIS
+.B tend\ triple
+.B \-t
+.RI < type >
+.RB [ \-i
+.RI < nin >]
+.RB [ \-o
+.RI < nout >]
+.SH DESCRIPTION
+Compute volume of shape triples.
+The triple can be eigenvalues,
+invariants
+.BI ( J ,\ K ,\ R )\fR,
+and lots of other things.
+.SH OPTIONS
+.TP
+.B \-t\fR <\fItype\fR>
+desired output triple type
+.TP
+.B \-i\fR <\fInin\fR>
+input diffusion tensor volume; default:
+.RB \(lq \- \(rq
+.TP
+.B \-o\fR <\fInout\fR>
+output triple volume
+.RI ( string )
+default:
+.RB \(lq \- \(rq
+.SH "SEE\ ALSO"
+.BR tend (1)
diff --git a/tend-unmf.1 b/tend-unmf.1
new file mode 100644
index 0000000..d97b5ce
--- /dev/null
+++ b/tend-unmf.1
@@ -0,0 +1,29 @@
+.TH TEND\-UNMF "1" "April 2021" "" "User Commands"
+.SH NAME
+tend\-unmf \- applies and removes the measurement frame
+.SH SYNOPSIS
+.B tend\ unmf
+.RB [ \-i
+.RI < nin >]
+.RB [ \-o
+.RI < nout >]
+.SH DESCRIPTION
+Applies and removes the measurement frame.
+When the given tensor volume has a measurement frame associated with it, this
+will apply the measurement frame transform to all tensors to convert them into
+world space, and remove the measurement frame from the
+.IR nrrd .
+.SH OPTIONS
+.TP
+.B \-i\fR <\fInin\fR>
+input diffusion tensor volume (sorry, can\(cqt use usual default of
+.RB \(lq \- \(rq
+for stdin because of hest quirk)
+.TP
+.B \-o\fR <\fInout\fR>
+output tensor volume
+.RI ( string )
+default:
+.RB \(lq \- \(rq
+.SH "SEE\ ALSO"
+.BR tend (1)
diff --git a/tend.1 b/tend.1
index e61040a..831950d 100644
--- a/tend.1
+++ b/tend.1
@@ -64,206 +64,92 @@ eigenvector, and then quantizes it to an 8-bit PNG:
.EE
.in
.SH OPTIONS
+.SH COMMANDS
+.P
+Each of the following has a corresponding man page: for example,
+.BR tend\-about (1)
+for
+.RB \(lq tend\ about \(rq.
.SS tend\ about
Information about this program and its use
-.P
-See
-.BR tend\-about (1).
.SS tend\ grads
Calculate balanced gradient directions for DWI acquisition
-.P
-See
-.BR tend\-grads (1).
.SS tend\ epireg
Register diffusion-weighted echo-planar images
-.P
-See
-.BR tend\-epireg (1).
.SS tend\ bmat
Calculate B-matrix given gradient directions
-.P
-See
-.BR tend\-bmat (1).
.SS tend\ estim
Estimate tensors from a set of DW images
-.P
-See
-.BR tend\-estim (1).
.SS tend\ sim
Simulate DW images from a tensor field
-.P
-See
-.BR tend\-sim (1).
.SS tend\ mfit
Estimate models from a set of DW images
-.P
-See
-.BR tend\-mfit (1).
.SS tend\ mconv
Convert from one model to another
-.P
-See
-.BR tend\-mconv (1).
.SS tend\ msim
Simulate DW images from an image of models
-.P
-See
-.BR tend\-msim (1).
.SS tend\ make
Create DT volume from confidence and eigensystem
-.P
-See
-.BR tend\-make (1).
.SS tend\ avg
Average across tensor volumes
-.P
-See
-.BR tend\-avg (1).
.SS tend\ helix
Generate twisting helical tensor field
-.P
-See
-.BR tend\-helix (1).
.SS tend\ sten
Calculate structure tensors from a scalar field
-.P
-See
-.BR tend\-sten (1).
.SS tend\ glyph
Generate postscript or ray-traced renderings of 3D glyphs
-.P
-See
-.BR tend\-glyph (1).
.SS tend\ ellipse
Generate postscript renderings of 2D glyphs
-.P
-See
-.BR tend\-ellipse (1).
.SS tend\ anplot
Graph anisotropy metric in barycentric coords
-.P
-See
-.BR tend\-anplot (1).
.SS tend\ anvol
Apply an anisotropy metric to a DT volume
-.P
-See
-.BR tend\-anvol (1).
.SS tend\ anscale
Scale the anisotropic component of the tensors
-.P
-See
-.BR tend\-anscale (1).
.SS tend\ anhist
Generate barycentric histograms of anisotropy
-.P
-See
-.BR tend\-anhist (1).
.SS tend\ triple
Compute volume of shape triples
-.P
-See
-.BR tend\-triple (1).
.SS tend\ tconv
Convert between different shape triples
-.P
-See
-.BR tend\-tconv (1).
.SS tend\ point
Describe everything about one sample in a DT volume
-.P
-See
-.BR tend\-point (1).
.SS tend\ slice
Slice 3D tensors to get slab/image of 3D/2D tensors
-.P
-See
-.BR tend\-slice (1).
.SS tend\ norm
Normalize tensor size
-.P
-See
-.BR tend\-norm (1).
.SS tend\ fiber
Fiber tractography, from one or more seeds
-.P
-See
-.BR tend\-fiber (1).
.SS tend\ eval
Calculate one or more eigenvalues in a DT volume
-.P
-See
-.BR tend\-eval (1).
.SS tend\ evalpow
Modify shape by raising eigenvalues to some power
-.P
-See
-.BR tend\-evalpow (1).
.SS tend\ evalclamp
Modify shape by clamping eigenvalues in some range
-.P
-See
-.BR tend\-evalclamp (1).
.SS tend\ evaladd
Modify shape by adding a constant to all eigenvalues
-.P
-See
-.BR tend\-evaladd (1).
-.SS tend\ evalmul
+.SS tend\ evalmult
Modify shape by multiplying eigenvalues by a constant
-.P
-See
-.BR tend\-evalmul (1).
.SS tend\ log
Calculates logarithm of the tensor
-.P
-See
-.BR tend\-log (1).
.SS tend\ exp
Calculates \fBexp()\fR of the tensor
-.P
-See
-.BR tend\-exp (1).
.SS tend\ evec
Calculate one or more eigenvectors in a DT volume
-.P
-See
-.BR tend\-evec (1).
.SS tend\ evecrgb
Make an RGB volume from an eigenvector and an anisotropy
-.P
-See
-.BR tend\-evecrgb (1).
.SS tend\ evq
Quantize directions of diffusion
-.P
-See
-.BR tend\-evq (1).
.SS tend\ unmf
Applies and removes the measurement frame
-.P
-See
-.BR tend\-unmf (1).
.SS tend\ expand
Converts masked non-redundant tensor images to redundant
-.P
-See
-.BR tend\-expand (1).
.SS tend\ shrink
Converts a 9-value DT volume to a 7-value DT volume
-.P
-See
-.BR tend\-shrink (1).
.SS tend\ bfit
Non-linear least-squares fitting of b-value curves
-.P
-See
-.BR tend\-bfit (1).
.SS tend\ satin
Generate a pretty synthetic DT volume
-.P
-See
-.BR tend\-satin (1).
.SH "SEE\ ALSO"
.BR gprobe (1),
.BR ilk (1),
@@ -288,7 +174,7 @@ See
.BR tend\-eval (1),
.BR tend\-evaladd (1),
.BR tend\-evalclamp (1),
-.BR tend\-evalmul (1),
+.BR tend\-evalmult (1),
.BR tend\-evalpow (1),
.BR tend\-evec (1),
.BR tend\-evecrgb (1),
diff --git a/unu.1 b/unu.1
index ef710be..6f8b611 100644
--- a/unu.1
+++ b/unu.1
@@ -115,6 +115,194 @@ Formats available:
.P
Nrrd data encodings available:
.B raw ascii hex gz bz2
+.SH COMMANDS
+\" .P
+\" Each of the following has a corresponding man page: for example,
+\" .BR unu\-about (1)
+\" for
+\" .RB \(lq unu\ about \(rq.
+.SS unu about
+Information about this program and its use
+.SS unu env
+List relevant environment variables and their values
+.SS unu i2w
+Convert from 1-D index to world position
+.SS unu w2i
+Convert from 1-D world to index position
+.SS unu make
+Create a
+.I nrrd
+(or
+.I nrrd
+header) from scratch
+.SS unu head
+Print header of one or more
+.I nrrd
+files
+.SS unu data
+Print data segment of a
+.I nrrd
+file
+.SS unu convert
+Convert to another type (as if by cast, w/ optional clamp)
+.SS unu resample
+Filtering and {up,down}sampling with a separable kernel
+.SS unu fft
+Fast Fourier Transform of selected axes
+.SS unu cmedian
+Cheap histogram-based median/mode filtering
+.SS unu dist
+Euclidean distance transform
+.SS unu minmax
+Print out min and max values in one or more
+.IR nrrd s
+.SS unu cksum
+Compute 32-bit CRC of
+.I nrrd
+data (same as via
+.BR cksum (1))
+.SS unu diff
+Sees if two
+.IR nrrd s
+are different in any way
+.SS unu quantize
+Quantize values to 8, 16, or 32 bits
+.SS unu unquantize
+Recover floating point values from quantized data
+.SS unu project
+Collapse scanlines to scalars along some axis
+.SS unu slice
+Slice along one or more axes at given positions
+.SS unu sselect
+Select subset of slices along an axis
+.SS unu dice
+Save all slices along one axis into separate files
+.SS unu splice
+Replace a slice with a different
+.I nrrd
+.SS unu join
+Connect slices and/or slabs into a bigger
+.I nrrd
+.SS unu crop
+Crop along each axis to make a smaller
+.I nrrd
+.SS unu acrop
+Automatically crop axes based on given measure
+.SS unu inset
+Replace a sub-region with a different
+.I nrrd
+.SS unu pad
+Pad along each axis to make a bigger
+.I nrrd
+.SS unu reshape
+Superficially change dimension and/or axes sizes
+.SS unu permute
+Permute ordering of axes
+.SS unu swap
+Interchange ordering of two axes
+.SS unu shuffle
+Permute slices along one axis
+.SS unu flip
+Reverse order of slices along one axis
+.SS unu unorient
+Make image orientation be axis-aligned
+.SS unu axinfo
+Modify attributes of one or more axes
+.SS unu axinsert
+Add a \(lqstub\(rq (length 1) axis to a
+.I nrrd
+.SS unu axsplit
+Split one axis into two axes
+.SS unu axdelete
+Remove one or more singleton axes from a
+.I nrrd
+.SS unu axmerge
+Merge two adjacent axes into one
+.SS unu tile
+Tile slices of one axis into two other axes
+.SS unu untile
+Undo
+.BR unu\-tile (1):
+merge slow parts of two axis splits
+.SS unu histo
+Create 1-D histogram of values in a
+.I nrrd
+.SS unu dhisto
+Create image of 1-D value histogram
+.SS unu jhisto
+Create joint histogram of two or more
+.IR nrrd s
+.SS unu histax
+Replace each scanline along an axis with its histogram
+.SS unu heq
+Perform histogram equalization
+.SS unu gamma
+Brighten or darken values with a gamma
+.SS unu 1op
+Unary operation on a
+.I nrrd
+.SS unu 2op
+Binary operation on two
+.IR nrrd s,
+or on a
+.I nrrd
+and a constant
+.SS unu 3op
+Ternary operation on three
+.IR nrrd s
+or constants
+.SS unu affine
+Affine (lerp) mapping on 5
+.IR nrrd s
+or constants
+.SS unu lut
+Map
+.I nrrd
+through one univariate lookup table
+.SS unu mlut
+Map
+.I nrrd
+through whole
+.I nrrd
+of univariate lookup tables
+.SS unu subst
+Map
+.I nrrd
+through a univariate substitution table
+.SS unu rmap
+Map
+.I nrrd
+through one
+.B regular
+univariate map (\(lqcolormap\(rq)
+.SS unu mrmap
+Map
+.I nrrd
+through a whole
+.I nrrd
+of regular univariate maps
+.SS unu imap
+Map
+.I nrrd
+through
+.B irregular
+univariate map (\(lqcolormap\(rq)
+.SS unu lut2
+Map
+.I nrrd
+through a bivariate lookup table
+.SS unu ccfind
+Find connected components (CCs)
+.SS unu ccadj
+Form adjecency matrix of connected components
+.SS unu ccmerge
+Merge CCs with their neighbors, under various constraints
+.SS unu ccsettle
+Remap CC values down to lowest contiguous values
+.SS unu save
+Write
+.I nrrd
+with specific format, encoding, or endianness
.SH "SEE\ ALSO"
.BR gprobe (1),
.BR ilk (1),
@@ -125,3 +313,66 @@ Nrrd data encodings available:
.BR puller (1),
.BR tend (1),
.BR vprobe (1)
+\" .BR vprobe (1),
+\" .BR unu-1op (1),
+\" .BR unu-2op (1),
+\" .BR unu-3op (1),
+\" .BR unu-about (1),
+\" .BR unu-acrop (1),
+\" .BR unu-affine (1),
+\" .BR unu-axdelete (1),
+\" .BR unu-axinfo (1),
+\" .BR unu-axinsert (1),
+\" .BR unu-axmerge (1),
+\" .BR unu-axsplit (1),
+\" .BR unu-ccadj (1),
+\" .BR unu-ccfind (1),
+\" .BR unu-ccmerge (1),
+\" .BR unu-ccsettle (1),
+\" .BR unu-cksum (1),
+\" .BR unu-cmedian (1),
+\" .BR unu-convert (1),
+\" .BR unu-crop (1),
+\" .BR unu-data (1),
+\" .BR unu-dhisto (1),
+\" .BR unu-dice (1),
+\" .BR unu-diff (1),
+\" .BR unu-dist (1),
+\" .BR unu-env (1),
+\" .BR unu-fft (1),
+\" .BR unu-flip (1),
+\" .BR unu-gamma (1),
+\" .BR unu-head (1),
+\" .BR unu-heq (1),
+\" .BR unu-histax (1),
+\" .BR unu-histo (1),
+\" .BR unu-i2w (1),
+\" .BR unu-imap (1),
+\" .BR unu-inset (1),
+\" .BR unu-jhisto (1),
+\" .BR unu-join (1),
+\" .BR unu-lut (1),
+\" .BR unu-lut2 (1),
+\" .BR unu-make (1),
+\" .BR unu-minmax (1),
+\" .BR unu-mlut (1),
+\" .BR unu-mrmap (1),
+\" .BR unu-pad (1),
+\" .BR unu-permute (1),
+\" .BR unu-project (1),
+\" .BR unu-quantize (1),
+\" .BR unu-resample (1),
+\" .BR unu-reshape (1),
+\" .BR unu-rmap (1),
+\" .BR unu-save (1),
+\" .BR unu-shuffle (1),
+\" .BR unu-slice (1),
+\" .BR unu-splice (1),
+\" .BR unu-sselect (1),
+\" .BR unu-subst (1),
+\" .BR unu-swap (1),
+\" .BR unu-tile (1),
+\" .BR unu-unorient (1),
+\" .BR unu-unquantize (1),
+\" .BR unu-untile (1),
+\" .BR unu-w2i (1)
commit 8d05a3a34a29ea41d5b2ca9712f366ef4f57a1b2
Author: Benjamin A. Beasley <code(a)musicinmybrain.net>
Date: Tue Apr 6 12:03:06 2021 -0400
Add an rpmlintrc file
diff --git a/teem.rpmlintrc b/teem.rpmlintrc
new file mode 100644
index 0000000..890462c
--- /dev/null
+++ b/teem.rpmlintrc
@@ -0,0 +1,5 @@
+# These are not real spelling errors...
+addFilter(r'spelling-error .*(\(en_US\)| en_US) (resample|datasets|'
+ r'tractography|unu) ')
+# Indeed, there is no dedicated documentation for some subpackages.
+addFilter(r' no-documentation$')
commit d09ed97c6dc65490fb502eeb6baaae9b3b304a14
Author: Benjamin A. Beasley <code(a)musicinmybrain.net>
Date: Tue Apr 6 11:59:36 2021 -0400
rewrap description
diff --git a/teem.spec b/teem.spec
index 563e3b5..61764d0 100644
--- a/teem.spec
+++ b/teem.spec
@@ -182,8 +182,8 @@ Strengths of Teem
Slicer.
• Teem is light-weight: The libraries are written with an eye towards
minimizing the annoyance of getting data in an out, by using the simplest
- possible constructs for the job, and by supporting combinations of operations
- that arise in common practice.
+ possible constructs for the job, and by supporting combinations of
+ operations that arise in common practice.
• Teem is coherent: There is a consistent logic to how information is
represented, and uniformity in the APIs across libraries.
• Teem is portable: All the code is written in plain ANSI C, so it compiles
commit b3cb082255b1984b8f5a864a4576729446f37aee
Author: Benjamin A. Beasley <code(a)musicinmybrain.net>
Date: Tue Apr 6 11:36:04 2021 -0400
Fix single-threading tests
diff --git a/teem.spec b/teem.spec
index b69288c..563e3b5 100644
--- a/teem.spec
+++ b/teem.spec
@@ -298,7 +298,8 @@ install -t %{buildroot}%{_mandir}/man1 -p -m 0644 \
%endif
# Tests are not parallel-safe
-%ctest3 %{?ctest_excludes} -j1
+%global _smp_mflags -j1
+%ctest3 %{?ctest_excludes}
%if 0%{?epel} == 7
commit ba847837bef28947baf576882c1463ada7d0ae7f
Author: Benjamin A. Beasley <code(a)musicinmybrain.net>
Date: Tue Apr 6 11:34:57 2021 -0400
Start work on adding a complete set of man pages
diff --git a/gprobe.1 b/gprobe.1
new file mode 100644
index 0000000..4f35b12
--- /dev/null
+++ b/gprobe.1
@@ -0,0 +1,264 @@
+.TH GPROBE "1" "April 2021" "" "User Commands"
+.SH NAME
+gprobe \- shows off the functionality of the gage library
+.SH SYNOPSIS
+.B gprobe
+.B \-i
+.RI < nin >
+.B \-k
+.RI < kind >
+.RB [ \-v
+.RI < verbosity >]
+.B \-q
+.RI < query >
+.RB [ \-gmc
+.RI < min\ gradmag >]
+.RB [ \-ofs ]
+.RB [ \-seed
+.RI < N >]
+.RB [ \-c
+.RI < bool >]
+.RB [ \-k00
+.RI < kern00 >]
+.RB [ \-k11
+.RI < kern11 >]
+.RB [ \-k22
+.RI < kern22 >]
+.RB [ \-rn ]
+.RB [ \-ssn
+.RI < SS\ # >]
+.RB [ \-ssr
+.RI < scale\ range >]
+.RB [ \-kssb
+.RI < kernel >]
+.RB [ \-kssr
+.RI < kernel >]
+.RB [ \-ssu ]
+.RB [ \-sso ]
+.RB [ \-ssnd ]
+.RB [ \-ssnb
+.RI < bias >]
+.RB [ \-ssf
+.RI < SS\ read/save\ format >]
+.RB [ \-s
+.RI < sclX \ xclY \ sxlZ >]
+.RB [ \-ssp
+.RI < pos >]
+.RB [ \-pg
+.RI < nrrd >]
+.RB [ \-pi
+.RI < nrrd >]
+.RB [ \-pp
+.RI < pos \ ...>]
+.RB [ \-eps
+.RI < epsilon >]
+.RB [ \-psi
+.RI < p >]
+.RB [ \-t
+.RI < type >]
+.RB [ \-o
+.RI < nout >]
+.SH DESCRIPTION
+.P
+Shows off the functionality of the gage library.
+Uses gageProbe() to query various kinds of volumes to learn various measured or
+derived quantities.
+.SH OPTIONS
+.TP
+.B \-i\fR\ <\fInin\fR>
+input volume
+.TP
+.B \-k\fR\ <\fIkind\fR>
+\(lqkind\(rq of volume
+.RB (\(lq scalar \(rq,
+.RB \(lq vector \(rq,
+.RB \(lq tensor \(rq,
+or
+.RB \(lq dwi \(rq)
+.TP
+.B \-v\fR\ <\fIverbosity\fR>
+verbosity level
+.RI ( int );
+default:
+.RB \(lq 1 \(rq
+.TP
+.B \-q\fR\ <\fIquery\fR>
+the quantity
+.RB ( scalar ,
+.BR vector ,
+or
+.BR matrix )
+to learn by probing
+.RI ( string )
+.TP
+.B \-gmc\fR\ <\fImin\ gradmag\fR>
+For curvature-based queries, use zero when gradient magnitude is below this
+.RI ( double );
+default:
+.RB \(lq 0.0 \(rq
+.TP
+.B \-ofs
+If only per-axis spacing is available, use that to contrive full orientation
+info
+.TP
+.B \-seed\fR\ <\fIN\fR>
+RNG seed; mostly for debugging
+.RI ( unsigned\ int );
+default:
+.RB \(lq 42 \(rq
+.TP
+.B \-c\fR\ <\fIbool\fR>
+clamp positions as part of probing
+.RI ( bool );
+default:
+.RB \(lq false \(rq
+.TP
+.B \-k00\fR\ <\fIkern00\fR>
+kernel for
+.BR gageKernel00 ;
+default:
+.RB \(lq tent \(rq
+.TP
+.B \-k11\fR\ <\fIkern11\fR>
+kernel for
+.BR gageKernel11 ;
+default:
+.RB \(lq cubicd:1,0 \(rq
+.TP
+.B \-k22\fR\ <\fIkern22\fR>
+kernel for
+.BR gageKernel22 ;
+default:
+.RB \(lq cubicdd:1,0 \(rq
+.TP
+.B \-rn
+renormalize kernel weights at each new sample location.
+\(lqAccurate\(rq kernels don\(cqt need this; doing it always makes things go
+slower
+.TP
+.B \-ssn\fR\ <\fISS\ #\fR>
+how many scale-space samples to evaluate, or,
+.B 0
+to turn-off all scale-space behavior
+.RI ( unsigned\ int );
+default:
+.RB \(lq 0 \(rq
+.TP
+.B \-ssr\fR\ <\fIscale\ range\fR>
+range of scales in scale-space (2
+.IR double s);
+default:
+.RB \(lq nan\ nan \(rq
+.TP
+.B \-kssb\fR\ <\fIkernel\fR>
+blurring kernel, to sample scale space; default:
+.RB \(lq dgauss:1,5 \(rq
+.TP
+.B \-kssr\fR\ <\fIkernel\fR>
+kernel for reconstructing from scale space samples
+.RB \(lq hermite \(rq
+.TP
+.B \-ssu
+do uniform samples along sigma, and not (by default) samples according to the
+effective diffusion scale
+.TP
+.B \-sso
+if not using \(lq\-ssu\(rq, use pre-computed optimal sigmas when possible
+.TP
+.B \-ssnd
+normalize derivatives by scale
+.TP
+.B \-ssnb\fR\ <\fIbias\fR>
+bias on scale-based derivative normalization
+.RI ( double );
+default:
+.RB \(lq 0.0 \(rq
+.TP
+.B \-ssf\fR\ <\fISS\ read/save\ format\fR>
+.BR printf (3)\-style
+format (including a
+.RB \(lq %u \(rq)
+for the filenames from which to read pre-blurred volumes computed for the
+stack, if they exist and match the stack parameters, and where to save them if
+they had to be re-computed.
+Leave this as empty string to disable this.
+.RI ( string );
+default: \(lq\(rq
+.TP
+.B \-s\fR\ <\fIsclX\fR\ \fIsclY\fR\ \fIsxlZ\fR>
+scaling factor for resampling on each axis
+.RB (> 1.0 :
+supersampling); use
+.RB \(lq \-ssp \(rq
+(and
+.RB \(lq \-psi \(rq)
+to specify scale position of sampling (3
+.IR double s);
+default:
+.RB \(lq 1\ 1\ 1 \(rq
+.TP
+.B \-ssp\fR\ <\fIpos\fR>
+when using scale-space, scale-position at which to probe
+.RI ( double );
+default:
+.RB \(lq 0 \(rq
+.TP
+.B \-pg\fR\ <\fInrrd\fR>
+overrides
+.RB \(lq \-s \(rq:
+2-D
+.I nrrd
+which specifies origin and direction vectors for sampling grid; default:
+\(lq\(rq
+.TP
+.B \-pi\fR\ <\fInrrd\fR>
+overrides
+.RB \(lq \-pv \(rq:
+probes at this list of 3-vec or 4-vec positions; default:
+\(lq\(rq
+.TP
+.B \-pp\fR\ <\fIpos\fR ...>
+overrides
+.RB \(lq \-pi \(rq:
+only sample at this specified point (3..4 \fIdouble\fRs); default:
+.RB \(lq nan\ nan \(rq
+.TP
+.B \-eps\fR\ <\fIepsilon\fR>
+if non-zero, and if query is a scalar, and if using
+.RB \(lq \-pp \(rq
+to query at a single point, then do
+.I epsilon
+offset probes to calculate discrete differences, to find the numerical gradient
+and hessian (for debugging)
+.RI ( double );
+default:
+.RB \(lq 0 \(rq
+.TP
+.B \-psi\fR\ <\fIp\fR>
+whether the probe location specification (by any of the four previous flags)
+are in index space
+.RI ( bool );
+default:
+.RB \(lq false \(rq
+.TP
+.B \-t\fR\ <\fItype\fR>
+type of output volume
+.RI ( type );
+default:
+.RB \(lq float \(rq
+.TP
+.B \-o\fR\ <\fInout\fR>
+output volume
+.RI ( string );
+default:
+.RB \(lq \- \(rq
+.SH "SEE\ ALSO"
+.BR ilk (1),
+.BR miter (1),
+.BR mrender (1),
+.BR nrrdSanity (1),
+.BR overrgb (1),
+.BR puller (1),
+.BR tend (1),
+.BR unu (1),
+.BR vprobe (1)
diff --git a/ilk.1 b/ilk.1
new file mode 100644
index 0000000..51b2915
--- /dev/null
+++ b/ilk.1
@@ -0,0 +1,180 @@
+.TH ILK "1" "April 2021" "" "User Commands"
+.SH NAME
+ilk \- (I)mage (L)inear Trans(X\-\->K)forms
+.SH SYNOPSIS
+.B ilk
+.RI [ @file \ ...]
+.RB [ \-i
+.RI < image >]
+.RB [ \-0
+.RI < origin >]
+.B \-t
+.RI < xform0 \ ...>
+.RB [ \-k
+.RI < kernel >]
+.RB [ \-min
+.RI < xMin \ yMin >]
+.RB [ \-max
+.RI < xMax \ yMax >]
+.RB [ \-b
+.RI < boundary >]
+.RB [ \-bg
+.RI < bg0 \ bg1 \ ...>]
+.RB [ \-s
+.RI < xSize \ ySize >]
+.RB [ \-a
+.RI < avg\ # >]
+.RB [ \-o
+.RI < filename >]
+.SH DESCRIPTION
+.P
+(I)mage (L)inear Trans(X\-\->K)forms.
+Applies linear (homogenous coordinate) transforms to a given image, using the
+given kernel for resampling.
+.SH OPTIONS
+.TP
+.BR @file \ ...
+response file(s) containing command-line arguments
+.TP
+.B \-i\fR <\fIimage\fR>
+input image; default:
+.RB \(lq \- \(rq
+.TP
+.B \-0\fR <\fIorigin\fR>
+where to locate
+.B (0,0)
+prior to applying transforms.
+.RS +7n
+.IP \(bu 2
+.RB \(lq u:<\fIfloat\fB>,<\fIfloat\fB> \(rq
+locate origin in a unit box
+.B [0,1]x[0,1]
+which covers the original image
+.IP \(bu 2
+.RB \(lq p:<\fIfloat\fB>,<\fIfloat\fB> \(rq
+locate origin at a particular pixel location, in the index space of the image
+.P
+default:
+.RB \(lq p:0,0 \(rq
+.RE
+.TP
+.B \-t\fR <\fIxform0\fR\ ...>
+transform(s) to apply to image.
+Transforms are applied in the order in which they appear.
+.RS +7n
+.IP \(bu 2
+.RB \(lq identity \(rq:
+no geometric transform, just resampling
+.IP \(bu 2
+.RB \(lq translate:\fIx\fB,\fIy\fB \(rq:
+shift image by vector
+.BR (\fIx\fB,\fIy\fB) ,
+as measured in pixels
+.IP \(bu 2
+.RB \(lq rotate:\fIang\fB \(rq:
+rotate CCW by
+.I ang
+degrees
+.IP \(bu 2
+.RB \(lq scale:\fIxs\fB,\fIys\fB \(rq:
+scale by
+.I xs
+in X, and
+.I ys
+in Y
+.IP \(bu 2
+.RB \(lq shear:\fIfix\fB,\fIamnt\fB \(rq:
+shear by
+.IR amnt ,
+keeping fixed the pixels along a direction
+.RI < fix >
+degrees from the X axis
+.IP \(bu 2
+.RB \(lq flip:\fIang\fB \(rq:
+flip along axis an angle
+.RI < ang >
+degrees from the X axis
+.IP \(bu 2
+.RB \(lq \fIa\fB,\fIb\fB,\fItx\fB,\fIc\fB,\fId\fB,\fIty\fB \(rq:
+specify the transform explicitly in row-major order (opposite of PostScript)
+(1 or more 2D transforms)
+.RE
+.TP
+.B \-k\fR <\fIkernel\fR>
+reconstruction kernel; default:
+.RB \(lq cubic:0,0.5 \(rq
+.TP
+.B \-min\fR <\fIxMin\fR\ \fIyMin\fR>
+lower bounding corner of output image.
+Default (by not using this option) is the lower corner of input image.
+(2 \fIdouble\fRs)
+.TP
+.B \-max\fR <\fIxMax\fR\ \fIyMax\fR>
+upper bounding corner of output image.
+Default (by not using this option) is the upper corner of input image.
+(2 \fIdouble\fRs)
+.TP
+.B \-b\fR <\fIboundary\fR>
+what to do when sampling outside original image.
+.RS +7n
+.IP \(bu 2
+.RB \(lq bleed \(rq:
+copy values at image border outward
+.IP \(bu 2
+.RB \(lq wrap \(rq:
+do wrap-around on image locations
+.IP \(bu 2
+.RB \(lq pad \(rq:
+use a given background value (via
+.RB \(lq \-bg \(rq)
+.P
+default:
+.RB \(lq bleed \(rq
+.RE
+.TP
+.B \-bg\fR <\fIbg0\fR\ \fIbg1\fR\ ...>
+background color to use with boundary behavior
+.RB \(lq pad \(rq.
+Defaults to all zeroes.
+(1 or more \fIfloat\fRs)
+.TP
+.B \-s\fR <\fIxSize\fR\ \fIySize\fR>
+For each axis, information about how many samples in output:
+.RS +7n
+.IP \(bu 2
+.RB \(lq x\fR<\fIfloat\fR> \(rq:
+number of output samples is some scaling of the number input of samples;
+multiplied by
+.RI < float >
+.IP \(bu 2
+.RB \(lq \fR<\fIint\fR> \(rq:
+specify exact number of samples
+(2 sampling specifications)
+.P
+default:
+.RB \(lq x1\ x1 \(rq
+.RE
+.TP
+.B \-a\fR <\fIavg\ #\fR>
+number of averages (if there there is only one rotation)
+.RI ( unsigned\ int );
+default:
+.RB \(lq 0 \(rq
+.TP
+.B \-o\fR <\fIfilename\fR>
+file to write output
+.I nrrd
+to
+.RI ( string );
+default:
+.RB \(lq \- \(rq
+.SH "SEE\ ALSO"
+.BR gprobe (1),
+.BR miter (1),
+.BR mrender (1),
+.BR nrrdSanity (1),
+.BR overrgb (1),
+.BR puller (1),
+.BR tend (1),
+.BR unu (1),
+.BR vprobe (1)
diff --git a/miter.1 b/miter.1
new file mode 100644
index 0000000..75d7ef4
--- /dev/null
+++ b/miter.1
@@ -0,0 +1,287 @@
+.TH MITER "1" "April 2021" "" "User Commands"
+.SH NAME
+miter \- a simple but effective little volume renderer
+.SH SYNOPSIS
+.B miter
+.RI [ @file \ ...]
+.RB [ \-i
+.RI < nsin >]
+.RB [ \-vi
+.RI < nvin >]
+.RB [ \-ti
+.RI < ntin >]
+.B \-txf
+.RI < nin \ ...>
+.B \-fr
+.RI < eye\ pos >
+.RB [ \-at
+.RI < at\ pos >]
+.RB [ \-up
+.RI < up\ dir >]
+.RB [ \-rh ]
+.RB [ \-or ]
+.B \-dn
+.RI < near >
+.B \-di
+.RI < image >
+.B \-df
+.RI < far >
+.RB [ \-ar ]
+.RB [ \-ur
+.RI < uMin\fR\ \fIuMax >]
+.RB [ \-vr
+.RI < vMin\fR\ \fIvMax >]
+.RB [ \-fv
+.RI < field\ of\ view >]
+.RB [ \-offfr ]
+.RB [ \-fr
+.RI < fake\ from >]
+.RB [ \-turn
+.RI < angle >]
+.RB [ \-am
+.RI < ambient >]
+.RB [ \-ld
+.RI < light\ pos >]
+.RB [ \-is
+.RI < image\ size >]
+.RB [ \-iss
+.RI < scale >]
+.RB [ \-ads
+.RI < ka\fR\ \fIkd\fR\ \fIks >]
+.RB [ \-sp
+.RI < spec\ pow >]
+.RB [ \-k00
+.RI < kernel >]
+.RB [ \-k11
+.RI < kernel >]
+.RB [ \-k22
+.RI < kernel >]
+.RB [ \-ss
+.RI < shading\ spec >]
+.RB [ \-ns
+.RI < normal\ spec >]
+.RB [ \-side
+.RI < normal\ side >]
+.RB [ \-rn ]
+.RB [ \-gmc
+.RI < min\ gradmag >]
+.RB [ \-step
+.RI < size >]
+.RB [ \-ref
+.RI < size >]
+.RB [ \-vp
+.RI < verbose pixel >]
+.RB [ \-n1
+.RI < near1 >]
+.RB [ \-nt
+.RI < #\ threads >]
+.RB [ \-o
+.RI < filename >]
+.SH OPTIONS
+.TP
+.BR @file \ ...
+response file(s) containing command-line arguments
+.TP
+.B \-i\fR <\fInsin\fR>
+input scalar volume to render
+.RI ( nrrd );
+default: \(lq\(rq
+.TP
+.B \-vi\fR <\fInvin\fR>
+input vector volume to render
+.RI ( nrrd );
+default: \(lq\(rq
+.TP
+.B \-ti\fR <\fIntin\fR>
+input tensor volume to render
+.RI ( nrrd );
+default: \(lq\(rq
+.TP
+.B \-txf\fR <\fInin\fR\ ...>
+one or more transfer functions (1 or more
+.IR nrrd s)
+.TP
+.B \-fr\fR <\fIeye\ pos\fR>
+camera eye point (3 \fIdouble\fRs)
+.TP
+.B \-at\fR <\fIat\ pos\fR>
+camera look-at point (3 \fIdouble\fRs); default:
+.RB \(lq 0\ 0\ 0 \(rq
+.TP
+.B \-up\fR <\fIup\ dir\fR>
+camera pseudo-up vector (3 \fIdouble\fRs); default:
+.RB \(lq 0\ 0\ 1 \(rq
+.TP
+.B \-rh
+use a right-handed UVN frame (V points down)
+.TP
+.B \-or
+orthogonal (not perspective) projection
+.TP
+.B \-dn\fR <\fInear\fR>
+distance to near clipping plane
+.RI ( double )
+.TP
+.B \-di\fR <\fIimage\fR>
+distance to image plane
+.RI ( double )
+.TP
+.B \-df\fR <\fIfar\fR>
+distance to far clipping plane
+.RI ( double )
+.TP
+.B \-ur\fR <\fIuMin\fR\ \fIuMax\fR>
+range in U direction of image plane (2
+.IR double s)
+.TP
+.B \-vr\fR <\fIvMin\fR\ \fIvMax\fR>
+range in V direction of image plane (2
+.IR double s)
+.TP
+.B \-fv\fR <\fIfield\ of\ view\fR>
+angle (in degrees) vertically subtended by view window
+.RI ( double );
+default:
+.RB \(lq 20 \(rq
+.TP
+.B \-offfr
+the given eye point
+.RB (\(lq \-fr \(rq)
+is to be interpreted as an offset from the at point.
+.TP
+.B \-ffr\fR <\fIfake\ from\fR>
+eye point to use for view-dependent transfer functions.
+By default (not using this option), the point used is the normally specified
+camera eye point. (3 \fIdouble\fRs)
+.TP
+.B \-turn\fR <\fIangle\fR>
+angle (degrees) by which to rotate the from point around true up, for making
+stereo pairs.
+Positive means move towards positive U (the right)
+.RI ( double );
+default:
+.RB \(lq 0.0 \(rq
+.TP
+.B \-am\fR <\fIambient\fR>
+ambient light color (3 \fIfloat\fRs); default:
+.RB \(lq 1\ 1\ 1 \(rq
+.TP
+.B \-ld\fR <\fIlight\ pos\fR>
+view space light position (extended to infinity) (3 \fIfloat\fRs); default:
+.RB \(lq 0\ 0\ \-1 \(rq
+.TP
+.B \-is\fR <\fIimage\ size\fR>
+image dimensions (2 \fIint\fRs); default:
+.RB \(lq 256\ 256 \(rq
+.TP
+.B \-iss\fR <\fIscale\fR>
+scaling of image size (from
+.RB \(lq \-is \(rq)
+.RI ( float );
+default:
+.RB \(lq 1.0 \(rq
+.TP
+.B \-ads\fR <\fIka\fR\ \fIkd\fR\ \fIks\fR>
+phong components (3 \fIfloat\fRs); default:
+.RB \(lq 0.1\ 0.6\ 0.3 \(rq
+.TP
+.B \-sp\fR <\fIka\fR\ \fIkd\fR\ \fIks\fR>
+phong specular power
+.RI ( double ); default:
+.RB \(lq 30 \(rq
+.TP
+.B \-k00\fR <\fIkernel\fR>
+value reconstruction kernel (\fIkernel specification\fR); default:
+.RB \(lq tent \(rq
+.TP
+.B \-k11\fR <\fIkernel\fR>
+first derivative kernel (\fIkernel specification\fR); default:
+.RB \(lq cubicd:1,0 \(rq
+.TP
+.B \-k22\fR <\fIkernel\fR>
+second derivative kernel (\fIkernel specification\fR); default:
+.RB \(lq cubicdd:1,0 \(rq
+.TP
+.B \-ss\fR <\fIshading\ spec\fR>
+how to do shading
+.RI ( string );
+default:
+.RB \(lq phong:gage(scalar:n) \(rq
+.TP
+.B \-ns\fR <\fInormal\ spec\fR>
+\(lqnormal\(rq to use for those miteVal\(cqs that need one
+.RI ( string );
+default: \(lq\(rq
+.TP
+.B \-side\fR <\fInormal\ side\fR>
+how to interpret gradients as normals:
+.RS +7n
+.IP \(bu 2
+.RB \(lq 1 \(rq:
+normal points to lower values (higher == more \(lqinside\(rq)
+.IP \(bu 2
+.RB \(lq 0 \(rq:
+\(lqtwo-sided\(rq: dot-products are \fBabs()\fR\(cqed
+.IP \(bu 2
+.RB \(lq \-1 \(rq:
+normal points to higher values (lower == more \(lqinside\(rq)
+.P
+default:
+.RB \(lq 1 \(rq
+.RE
+.TP
+.B \-rn
+renormalize kernel weights at each new sample location.
+\(lqAccurate\(rq kernels don\(cqt need this; doing it always makes things go
+slower
+.B \-gmc\fR\ <\fImin\ gradmag\fR>
+For curvature-based transfer functions, set curvature to zero when gradient
+magnitude is below this
+.RI ( double );
+default:
+.RB \(lq 0.0 \(rq
+.TP
+.B \-step\fR\ <\fIsize\fR>
+step size along ray in world space
+.RI ( double );
+default:
+.RB \(lq 0.01 \(rq
+.TP
+.B \-ref\fR\ <\fIsize\fR>
+\(lqreference\(rq step size (world space) for doing opacity correction in
+compositing
+.RI ( double );
+default:
+.RB \(lq 0.01 \(rq
+.TP
+.B \-vp\fR\ <\fIverbose\ pixel\fR>
+pixel for which to turn on verbose messages (2 \fIint\fRs); default:
+.RB \(lq \-1\ \-1 \(rq
+.TP
+.B \-n1\fR\ <\fInear1\fR>
+opacity close enough to 1.0 to terminate ray
+.RI ( double );
+default:
+.RB \(lq 0.99 \(rq
+.TP
+.B \-nt\fR\ <\fI#\ threads\fR>
+number of threads hoover should use
+.RI ( int );
+default:
+.RB \(lq 1 \(rq
+.TP
+.B \-o\fR <\fIfilename\fR>
+file to write output nrrd to
+.RI ( string );
+default:
+.RB \(lq \- \(rq
+.SH "SEE\ ALSO"
+.BR gprobe (1),
+.BR ilk (1),
+.BR mrender (1),
+.BR nrrdSanity (1),
+.BR overrgb (1),
+.BR puller (1),
+.BR tend (1),
+.BR unu (1),
+.BR vprobe (1)
diff --git a/mrender.1 b/mrender.1
new file mode 100644
index 0000000..f619ac2
--- /dev/null
+++ b/mrender.1
@@ -0,0 +1,294 @@
+.TH MRENDER "1" "April 2021" "" "User Commands"
+.SH NAME
+mrender \- a demonstration of hoover, gage, and nrrd measures
+.SH SYNOPSIS
+.B mrender
+.RI [ @file \ ...]
+.RB [ \-i
+.RI < nin >]
+.B \-k
+.RI < kind >
+.B \-fr
+.RI < eye\ pos >
+.RB [ \-at
+.RI < at\ pos >]
+.RB [ \-up
+.RI < up\ dir >]
+.RB [ \-rh ]
+.RB [ \-or ]
+.B \-dn
+.RI < near >
+.B \-di
+.RI < image >
+.B \-df
+.RI < far >
+.RB [ \-ar ]
+.RB [ \-ur
+.RI < uMin\fR\ \fIuMax >]
+.RB [ \-vr
+.RI < vMin\fR\ \fIvMax >]
+.RB [ \-fv
+.RI < field\ of\ view >]
+.RB [ \-offfr ]
+.RB [ \-turn
+.RI < angle >]
+.RB [ \-is
+.RI < image\ size >]
+.RB [ \-iss
+.RI < scale >]
+.RB [ \-k00
+.RI < kernel >]
+.RB [ \-k11
+.RI < kernel >]
+.RB [ \-k22
+.RI < kernel >]
+.RB [ \-rn ]
+.B \-q
+.RI < query >
+.B \-m
+.RI < measure >
+.RB [ \-gmc
+.RI < min\ gradmag >]
+.RB [ \-fn
+.RI < from\ nan >]
+.RB [ \-step
+.RB [ \-nt
+.RI < #\ threads >]
+.RB [ \-vp
+.RI < img\ coords >]
+.RB [ \-o
+.RI < filename >]
+.SH DESCRIPTION
+.P
+Uses hoover to cast rays through a volume (scalar, vector, or tensor), gage to
+measure one of various quantities along the rays, and a specified
+.I nrrd
+measure to reduce all the values along a ray down to one scalar, which is saved
+in the output
+.RI ( double )
+image.
+.SH OPTIONS
+.TP
+.BR @file \ ...
+response file(s) containing command-line arguments
+.TP
+.B \-i\fR <\fInin\fR>
+input
+.I nrrd
+to render
+.RI ( nrrd );
+default: \(lq\(rq
+.TP
+.B \-k\fR <\fIkind\fR>
+\(lqkind\(rq of volume
+.RB (\(lq scalar \(rq,
+.RB \(lq vector \(rq,
+or
+.RB \(lq tensor \(rq)
+.RI ( gageKind )
+.TP
+.B \-fr\fR <\fIeye\ pos\fR>
+camera eye point (3 \fIdouble\fRs)
+.TP
+.B \-at\fR <\fIat\ pos\fR>
+camera look-at point (3 \fIdouble\fRs); default:
+.RB \(lq 0\ 0\ 0 \(rq
+.TP
+.B \-up\fR <\fIup\ dir\fR>
+camera pseudo-up vector (3 \fIdouble\fRs); default:
+.RB \(lq 0\ 0\ 1 \(rq
+.TP
+.B \-rh
+use a right-handed UVN frame (V points down)
+.TP
+.B \-or
+orthogonal (not perspective) projection
+.TP
+.B \-dn\fR <\fInear\fR>
+distance to near clipping plane
+.RI ( double )
+.TP
+.B \-di\fR <\fIimage\fR>
+distance to image plane
+.RI ( double )
+.TP
+.B \-df\fR <\fIfar\fR>
+distance to far clipping plane
+.RI ( double )
+.TP
+.B \-ar
+near, image, and far plane distances are relative to the
+.B at
+point, instead of the eye point
+.TP
+.B \-ur\fR <\fIuMin\fR\ \fIuMax\fR>
+range in U direction of image plane (2
+.IR double s);
+default:
+.RB \(lq nan\ nan \(rq
+.TP
+.B \-vr\fR <\fIvMin\fR\ \fIvMax\fR>
+range in V direction of image plane (2
+.IR double s);
+default:
+.RB \(lq nan\ nan \(rq
+.TP
+.B \-fv\fR <\fIfield\ of\ view\fR>
+angle (in degrees) vertically subtended by view window
+.RI ( double );
+default:
+.RB \(lq 20 \(rq
+.TP
+.B \-offfr
+the given eye point
+.RB (\(lq \-fr \(rq)
+is to be interpreted as an offset from the at point.
+.TP
+.B \-turn\fR <\fIangle\fR>
+angle (degrees) by which to rotate the from point around true up, for making
+stereo pairs.
+Positive means move towards positive U (the right)
+.RI ( double );
+default:
+.RB \(lq 0.0 \(rq
+.TP
+.B \-is\fR <\fIimage\ size\fR>
+image dimensions (2 \fIint\fRs); default:
+.RB \(lq 256\ 256 \(rq
+.TP
+.B \-iss\fR <\fIscale\fR>
+scaling of image size (from
+.RB \(lq \-is \(rq)
+.RI ( float );
+default:
+.RB \(lq 1.0 \(rq
+.TP
+.B \-k00\fR <\fIkernel\fR>
+value reconstruction kernel (\fIkernel specification\fR); default:
+.RB \(lq tent \(rq
+.TP
+.B \-k11\fR <\fIkernel\fR>
+first derivative kernel (\fIkernel specification\fR); default:
+.RB \(lq cubicd:1,0 \(rq
+.TP
+.B \-k22\fR <\fIkernel\fR>
+second derivative kernel (\fIkernel specification\fR); default:
+.RB \(lq cubicdd:1,0 \(rq
+.TP
+.B \-rn
+renormalize kernel weights at each new sample location.
+\(lqAccurate\(rq kernels don\(cqt need this; doing it always makes things go
+slower
+.TP
+.B \-q\fR <\fIquery\fR>
+the quantity
+.RB ( scalar ,
+.BR vector ,
+or
+.BR matrix)
+to learn by probing
+.RI ( string )
+.TP
+.B \-m\fR <\fImeasure\fR>
+how to collapse list of ray samples into one scalar.
+Possibilities include:
+.RS +7n
+.IP \(bu 2
+.RB \(lq min \(rq,
+.RB \(lq max \(rq,
+.RB \(lq mean \(rq,
+.RB \(lq median \(rq,
+.RB \(lq mode \(rq,
+.RB \(lq variance \(rq,
+.RB \(lq skew \(rq:
+(self-explanatory)
+.IP \(bu 2
+.RB \(lq intc \(rq,
+.RB \(lq slope \(rq,
+.RB \(lq error \(rq:
+intercept, slope, and error from line fitting
+.IP \(bu 2
+.RB \(lq stdv \(rq:
+standard deviation
+.IP \(bu 2
+.RB \(lq cov \(rq:
+coefficient of variation
+.IP \(bu 2
+.RB \(lq product \(rq,
+.RB \(lq sum \(rq:
+product or sum of all values
+.IP \(bu 2
+.RB \(lq L1 \(rq,
+.RB \(lq L2 \(rq,
+.RB \(lq NL2 \(rq,
+.RB \(lq RMS \(rq,
+.RB \(lq Linf \(rq:
+different norms
+.IP \(bu 2
+.RB \(lq histo\-min \(rq,
+.RB \(lq histo\-max \(rq,
+.RB \(lq histo\-mean \(rq,
+.RB \(lq histo\-median \(rq,
+.RB \(lq histo\-mode \(rq,
+.RB \(lq histo\-product \(rq,
+.RB \(lq histo\-l2 \(rq,
+.RB \(lq histo\-sum \(rq,
+.RB \(lq histo\-variance \(rq,
+.RB \(lq histo\-sd \(rq:
+same measures, but for situations where we\(cqre given not the original values,
+but a histogram of them.
+.RI ( measure )
+.TP
+.B \-gmc\fR\ <\fImin\ gradmag\fR>
+For curvature-based queries, set answer to zero when gradient magnitude is
+below this
+.RI ( double );
+default:
+.RB \(lq 0.0 \(rq
+.TP
+.B \-fn\fR\ <\fIfrom\ nan\fR>
+When histo-based measures generate
+.B NaN
+answers, the value that should be substituted for
+.BR NaN .
+.RI ( double );
+default:
+.RB \(lq nan \(rq
+.TP
+.B \-step\fR\ <\fIsize\fR>
+step size along ray in world space
+.RI ( double );
+default:
+.RB \(lq 0.01 \(rq
+.TP
+.B \-nt\fR\ <\fI#\ threads\fR>
+number of threads hoover should use
+.RI ( int );
+default:
+.RB \(lq 1 \(rq
+.TP
+.B \-vp\fR\ <\fIimg\ coords\fR>
+pixel coordinates for which to turn on verbose debugging messages, or
+.RB \(lq \-1\ \-1 \(rq
+to disable this.
+(2 \fIint\fRs);
+default:
+.RB \(lq \-1\ \-1 \(rq
+.TP
+.B \-o\fR <\fIfilename\fR>
+file to write output
+.I nrrd
+to
+.RI ( string );
+default:
+.RB \(lq \- \(rq
+.SH "SEE\ ALSO"
+.BR gprobe (1),
+.BR ilk (1),
+.BR miter (1),
+.BR nrrdSanity (1),
+.BR overrgb (1),
+.BR puller (1),
+.BR tend (1),
+.BR unu (1),
+.BR vprobe (1)
diff --git a/nrrdSanity.1 b/nrrdSanity.1
new file mode 100644
index 0000000..70c577a
--- /dev/null
+++ b/nrrdSanity.1
@@ -0,0 +1,32 @@
+.TH NRRDSANITY "1" "April 2021" "" "User Commands"
+.SH NAME
+nrrdSanity \- nrrd sanity check
+.SH SYNOPSIS
+.B nrrdSanity
+.SH DESCRIPTION
+.P
+.BR nrrdSanity (1)
+calls the
+.B nrrdSanity()
+check to verify the correctness of all the information (set at compile-time)
+about the architecture, such as endianness, 32/64-bit, and the size of various
+types, as well as running sanity checks on the global default
+.RB ( nrrdDefault* )
+and state
+.RB ( nrrdState* )
+variables.
+.P
+As a convenience,
+.BR nrrdSanity (1)
+also lists the availability of the different formats and data encodings (for
+Nrrd files) supported by this build.
+.SH "SEE\ ALSO"
+.BR gprobe (1),
+.BR ilk (1),
+.BR miter (1),
+.BR mrender (1),
+.BR overrgb (1),
+.BR puller (1),
+.BR tend (1),
+.BR unu (1),
+.BR vprobe (1)
diff --git a/overrgb.1 b/overrgb.1
new file mode 100644
index 0000000..538bf57
--- /dev/null
+++ b/overrgb.1
@@ -0,0 +1,92 @@
+.TH OVERRGB "1" "April 2021" "" "User Commands"
+.SH NAME
+overrgb \- composites an RGBA nrrd over a background color (or image)
+.SH SYNOPSIS
+.B overrgb
+.B \-i
+.RI < nin >
+.RB [ \-c
+.RI < contrast >]
+.RB [ \-cfp
+.RI < fixed\ point >]
+.RB [ \-g
+.RI < gamma >]
+.RB [ \-b
+.RI < background >]
+.RB [ \-bi
+.RI < nbg >]
+.B \-o
+.RI < filename >
+.SH DESCRIPTION
+.P
+Composites an RGBA nrrd over a background color (or image), after doing gamma
+correction, then quantizes to an 8-bit image.
+Actually, the input nrrd can have more than 4 values per pixel, but only the
+first four are used.
+If the RGBA nrrd is floating point, the values are taken at face value; if it
+is fixed point, the values interpreted as having been quantized (so that 8-bit
+RGBA images will act as you expect).
+When compositing with a background image, the given background image does not
+have to be the same size as the input image; it will be resampled (with linear
+interpolation) to fit.
+.SH OPTIONS
+.TP
+.BR @file \ ...
+response file(s) containing command-line arguments
+.TP
+.B \-i\fR <\fInin\fR>
+input nrrd to composite
+.RI ( nrrd )
+.TP
+.B \-c\fR <\fIcontrast\fR>
+contrast to apply to RGB values, before gamma.
+.RB \(lq 0.0 \(rq
+means no change,
+.RB \(lq 1.0 \(rq
+means thresholding,
+.RB \(lq \-1.0 \(rq
+means a complete washout.
+.RI ( double );
+default:
+.RB \(lq 0.0 \(rq
+.TP
+.B \-cfp\fR <\fIfixed\ point\fR>
+component level that doesn\(cqt change with contrast
+.RI ( double );
+default:
+.RB \(lq 0.5 \(rq
+.TP
+.B \-g\fR <\fIgamma\fR>
+gamma to apply to image data, after contrast
+.RI ( double );
+default:
+.RB \(lq 1.0 \(rq
+.TP
+.B \-b\fR <\fIbackground\fR>
+background color to composite against; white is
+.BR 1\ 1\ 1 ,
+not
+.BR 255\ 255\ 255 .
+(3 \fIdouble\fRs); default:
+.RB \(lq 0\ 0\ 0 \(rq
+.TP
+.B \-bi\fR <\fInbg\fR>
+8-bit RGB background image to composite against
+.RI ( nrrd );
+default: \(lq\(rq
+.TP
+.B \-o\fR <\fIfilename\fR>
+file to write output PPM image to
+.RI ( string );
+default:
+.RB \(lq \- \(rq
+.SH "SEE\ ALSO"
+.BR gprobe (1),
+.BR ilk (1),
+.BR mrender (1),
+.BR miter (1),
+.BR nrrdSanity (1),
+.BR puller (1),
+.BR tend (1),
+.BR unu (1),
+.BR vprobe (1)
diff --git a/puller.1 b/puller.1
new file mode 100644
index 0000000..fb03592
--- /dev/null
+++ b/puller.1
@@ -0,0 +1,591 @@
+.TH PULLER "1" "April 2021" "" "User Commands"
+.SH NAME
+puller \- command-line interface to the \(lqpull\(rq library
+.SH SYNOPSIS
+.B puller
+.RB [ @file \ ...]
+.RB [ \-int
+.RI < int >]
+.RB [ \-enr
+.RI < spec >]
+.RB [ \-ens
+.RI < spec >]
+.RB [ \-enw
+.RI < spec >]
+.RB [ \-efs
+.RI < bool >]
+.RB [ \-nave
+.RI < bool >]
+.RB [ \-cbst
+.RI < bool >]
+.RB [ \-noadd ]
+.RB [ \-usa
+.RI < bool >]
+.RB [ \-pcet
+.RI < bool >]
+.RB [ \-nobin ]
+.RB [ \-lti
+.RI < bool >]
+.RB [ \-por
+.RI < bool >]
+.RB [ \-npcwza
+.RI < bool >]
+.RB [ \-ubfgl
+.RI < bool >]
+.RB [ \-ratb
+.RI < bool >]
+.RB [ \-svec
+.RI < vec >]
+.RB [ \-gssr
+.RI < rad >]
+.RB [ \-v
+.RI < verbosity >]
+.B \-vol
+.RI < vol0\ vol1 \ ...>
+.B \-info
+.RI < info0\ info1 \ ...>
+.RB [ \-k00
+.RI < kern00 >]
+.RB [ \-k11
+.RI < kern11 >]
+.RB [ \-k22
+.RI < kern22 >]
+.RB [ \-sscp
+.RI < path >]
+.RB [ \-kssb
+.RI < kernel >]
+.RB [ \-kssr
+.RI < kernel >]
+.RB [ \-nss
+.RI < #\ scl\ smpls >]
+.RB [ \-np
+.RI < #\ points >]
+.RB [ \-halton ]
+.RB [ \-ppv
+.RI < #\ pnts/vox >]
+.RB [ \-ppvzr
+.RI < z\ range >]
+.RB [ \-jit
+.RI < jitter >]
+.RB [ \-pi
+.RI < npos >]
+.RB [ \-step
+.RI < step >]
+.RB [ \-csm
+.RI < step >]
+.RB [ \-snap
+.RI < #\ iters >]
+.RB [ \-maxi
+.RI < #\ iters >]
+.RB [ \-stim
+.RI < #\ iters >]
+.RB [ \-maxci
+.RI < #\ iters >]
+.RB [ \-irad
+.RI < scale >]
+.RB [ \-srad
+.RI < scale >]
+.RB [ \-bws
+.RI < bin\ width >]
+.RB [ \-alpha
+.RI < alpha >]
+.RB [ \-beta
+.RI < beta >]
+.RB [ \-gamma
+.RI < gamma >]
+.RB [ \-theta
+.RI < theta >]
+.RB [ \-wall
+.RI < k >]
+.RB [ \-eip
+.RI < k >]
+.RB [ \-ess
+.RI < scl >]
+.RB [ \-oss
+.RI < scl >]
+.RB [ \-edmin
+.RI < frac >]
+.RB [ \-edpcmin
+.RI < frac >]
+.RB [ \-fnnm
+.RI < frac >]
+.RB [ \-pcp
+.RI < period >]
+.RB [ \-iad
+.RI < #\ iters >]
+.RB [ \-icb
+.RI < #\ iters >]
+.RB [ \-ac3c
+.RI < ac3c >]
+.RB [ \-sit
+.RI < sit >]
+.RB [ \-rng
+.RI < seed >]
+.RB [ \-pbm
+.RI < mod >]
+.RB [ \-eiphl
+.RI < hl >]
+.RB [ \-nt
+.RI < #\ threads >]
+.RB [ \-nprob
+.RI < prob >]
+.RB [ \-pprob
+.RI < prob >]
+.RB [ \-addlog
+.RI < fname >]
+.RB [ \-o
+.RI < nout >]
+.RB [ \-eob
+.RI < base >]
+.SH OPTIONS
+.TP
+.BR @file \ ...
+response file(s) containing command-line arguments
+.TP
+.B \-int\fR <\fIint\fR>
+inter-particle energy type
+.RI ( interaction\ type );
+default:
+.RB \(lq justr \(rq
+.TP
+.B \-enr\fR <\fIspec\fR>
+inter-particle energy, radial component
+.RI ( energy\ specification );
+default:
+.RB \(lq cotan \(rq
+.TP
+.B \-ens\fR <\fIspec\fR>
+inter-particle energy, scale component
+.RI ( energy\ specification );
+default:
+.RB \(lq zero \(rq
+.TP
+.B \-enw\fR <\fIspec\fR>
+windowing to create locality with additive scale-space interaction
+.RB (\(lq \-int\ add \(rq)
+.RI ( energy\ specification );
+default:
+.RB \(lq butter:16,0.8 \(rq
+.TP
+.B \-efs\fR <\fIbool\fR>
+whether or not strength contributes to particle-image energy
+.RI ( bool );
+default:
+.RB \(lq false \(rq
+.TP
+.B \-nave\fR <\fIbool\fR>
+whether or not to nix points at edge of volume, where gage had to invent values
+for kernel support
+.RI ( bool );
+default:
+.RB \(lq false \(rq
+.TP
+.B \-cbst\fR <\fIbool\fR>
+during initialization, try constraint satisfaction before testing
+.B seedThresh
+.RI ( bool );
+default:
+.RB \(lq false \(rq
+.TP
+.B \-noadd
+turn off adding during population control
+.TP
+.B \-usa\fR <\fIbool\fR>
+allow volumes to have different shapes
+.RB ( false
+is safe as different volume sizes are often accidental)
+.RI ( bool );
+default:
+.RB \(lq false \(rq
+.TP
+.B \-pcet\fR <\fIbool\fR>
+use neighbor-counting \(lqenough\(rq heuristic to bail out of population
+control
+.RI ( bool );
+default:
+.RB \(lq false \(rq
+.TP
+.B \-nobin
+turn off spatial binning (which prevents multi-threading from being useful),
+for debugging or speed-up measurement
+.TP
+.B \-lti\fR <\fIbool\fR>
+impose
+.B liveThresh
+on initialization
+.RI ( bool );
+default:
+.RB \(lq true \(rq
+.TP
+.B \-por\fR <\fIbool\fR>
+permute points during rebinning
+.RI ( bool );
+default:
+.RB \(lq true \(rq
+.TP
+.B \-npcwza\fR <\fIbool\fR>
+no population control with zero alpha
+.RI ( bool );
+default:
+.RB \(lq false \(rq
+.TP
+.B \-ubfgl\fR <\fIbool\fR>
+use beta for gamma learning
+.RI ( bool );
+default:
+.RB \(lq false \(rq
+.TP
+.B \-ratb\fR <\fIbool\fR>
+be choosy when adding points to bins to avoid overlap
+.RI ( bool );
+default:
+.RB \(lq true \(rq
+.TP
+.B \-svec\fR <\fIvec\fR>
+if non-zero (length), vector to use for displaying scale in 3-space (3
+\fIdouble\fRs); default:
+.RB \(lq 0\ 0\ 0 \(rq
+.TP
+.B \-gssr\fR <\fIrad\fR>
+if non-zero (length), scaling of scale to cylindrical tensors
+.RI ( double );
+default:
+.RB \(lq 0.0 \(rq
+.TP
+.B \-v\fR <\fIverbosity\fR>
+verbosity level
+.RI ( int );
+default:
+.RB \(lq 1 \(rq
+.TP
+.B \-vol\fR <\fIvol0\fR\ \fIvol1\fR\ ...>
+input volumes, in format
+<\fIfilename\fR>\fB:\fR<\fIkind\fR>\fB:\fR<\fIvolname\fR>
+(1 or more \fImeetPullVol\fRs)
+.TP
+.B \-info\fR <\fIinfo0\fR\ \fIinfo1\fR\ ...>
+info definitions, in format
+.RI < info >[\fB\-c\fR]\fB:\fR< volname >\fB:\fR< item >[\fB:\fR< zero >\fB:\fR< scale >]
+(1 or more \fImeetPullInfo\fRs)
+.TP
+.B \-k00\fR <\fIkern00\fR>
+kernel for
+.B gageKernel00
+.RI ( kernel\ specification );
+default:
+.RB \(lq cubic:1,0 \(rq
+.TP
+.B \-k11\fR <\fIkern11\fR>
+kernel for
+.B gageKernel11
+.RI ( kernel\ specification );
+default:
+.RB \(lq cubicd:1,0 \(rq
+.TP
+.B \-k22\fR <\fIkern22\fR>
+kernel for
+.B gageKernel22
+.RI ( kernel\ specification );
+default:
+.RB \(lq cubicdd:1,0 \(rq
+.TP
+.B \-sscp\fR <\fIpath\fR>
+path (without trailing
+.BR / )
+for where to read/write pre-blurred volumes for scale-space
+.RI ( string );
+default:
+.RB \(lq ./ \(rq
+.TP
+.B \-kssb\fR <\fIkernel\fR>
+blurring kernel, to sample scale space
+.RI ( kernel\ specification );
+default:
+.RB \(lq ds:1,5 \(rq
+.TP
+.B \-kssr\fR <\fIkernel\fR>
+kernel for reconstructing from scale space samples
+.RI ( kernel\ specification );
+default:
+.RB \(lq hermite \(rq
+.TP
+.B \-nssr\fR <\fI#\ scl\ smpls\fR>
+if using
+.RB \(lq \-ppv \(rq;
+number of samples along scale axis for each spatial position
+.RI ( unsigned\ int );
+default:
+.RB \(lq 1 \(rq
+.TP
+.B \-np\fR <\fI#\ points\fR>
+number of points to start in system
+.RI ( unsigned\ int );
+default:
+.RB \(lq 1000 \(rq
+.TP
+.B \-halton
+use Halton sequence initialization instead of uniform random
+.TP
+.B \-ppv\fR <\fI#\ p[nts/vox\fR>
+number of points per voxel to start in simulation (need to have a seed
+threshold volume, overrides
+.RB \(lq \-np \(rq)
+.RI ( int );
+default:
+.RB \(lq 0 \(rq
+.TP
+.B \-ppvzr\fR <\fIz\ range\fR>
+range of Z slices (1st num < 2nd num) to do
+.B ppv
+in, or,
+.RB \(lq 1\ 0 \(rq)
+for whole volume (2 \fIunsigned\ int\fRs); default:
+.RB \(lq 1\ 0 \(rq
+.TP
+.B \-jit\fR <\fIjitter\fR>
+amount of jittering to do with
+.B ppv
+.RI ( double );
+default:
+.RB \(lq 0 \(rq
+.TP
+.B \-pi\fR <\fInpos\fR>
+4-by-N array of positions to start at (overrides
+.RB \(lq \-np \(rq
+.RI ( nrrd );
+default: \(lq\(rq
+.TP
+.B \-step\fR <\fIstep\fR>
+initial step size for gradient descent
+.RI ( double );
+default:
+.RB \(lq 1 \(rq
+.TP
+.B \-csm\fR <\fIstep\fR>
+convergence criterion for constraint satisfaction
+.RI ( double );
+default:
+.RB \(lq 0.0001 \(rq
+.TP
+.B \-snap\fR <\fI#\ iters\fR>
+if non-zero, # iters between saved snapshots
+.RI ( unsigned\ int );
+default:
+.RB \(lq 0 \(rq
+.TP
+.B \-maxi\fR <\fI#\ iters\fR>
+if non-zero, max # iterations to run whole system
+.RI ( unsigned\ int );
+default:
+.RB \(lq 0 \(rq
+.TP
+.B \-stim\fR <\fI#\ iters\fR>
+if non-zero, max # iterations to allow a particle to be stuck before nixing
+.RI ( unsigned\ int );
+default:
+.RB \(lq 5 \(rq
+.TP
+.B \-maxci\fR <\fI#\ iters\fR>
+if non-zero, max # iterations for contraint enforcement
+.RI ( unsigned\ int );
+default:
+.RB \(lq 15 \(rq
+.TP
+.B \-irad\fR <\fIscale\fR>
+particle radius in spatial domain
+.RI ( double );
+default:
+.RB \(lq 1 \(rq
+.TP
+.B \-srad\fR <\fIscale\fR>
+particle radius in scale domain
+.RI ( double );
+default:
+.RB \(lq 1 \(rq
+.TP
+.B \-bws\fR <\fIbin\ width\fR>
+spatial bin width as multiple of spatial radius
+.RI ( double );
+default:
+.RB \(lq 1.001 \(rq
+.TP
+.B \-alpha\fR <\fIalpha\fR>
+blend between particle-image (\fIalpha\fR=\fB0\fR) and inter-particle
+(\fIalpha\fR=\fB1\fR) energies
+.RI ( double );
+default:
+.RB \(lq 0.5 \(rq
+.TP
+.B \-beta\fR <\fIbeta\fR>
+when using
+.B Phi2
+energy, blend between pure space repulsion (\fIbeta\fR=\fB0\fR) and scale
+attraction (\fIbeta\fR=\fB1\fR)
+.RI ( double );
+default:
+.RB \(lq 1.0 \(rq
+.TP
+.B \-gamma\fR <\fIgamma\fR>
+scaling factor on energy from strength
+.RI ( double );
+default:
+.RB \(lq 1.0 \(rq
+.TP
+.B \-theta\fR <\fItheta\fR>
+slope of increasing
+.B livethresh
+with respect to scale
+.RI ( double );
+default:
+.RB \(lq 0.0 \(rq
+.TP
+.B \-wall\fR <\fIk\fR>
+spring constant on walls
+.RI ( double );
+default:
+.RB \(lq 0.0 \(rq
+.TP
+.B \-eip\fR <\fIk\fR>
+amount by which it\(cqs okay for
+.B per-particle
+energy to increase during gradient descent process
+.RI ( double );
+default:
+.RB \(lq 0.0 \(rq
+.TP
+.B \-ess\fR <\fIscl\fR>
+when energy goes up instead of down, scale step size by this
+.RI ( double );
+default:
+.RB \(lq 0.5 \(rq
+.TP
+.B \-oss\fR <\fIscl\fR>
+opportunistic scaling (hopefully up, >1) of step size on every iteration
+.RI ( double );
+default:
+.RB \(lq 1.0 \(rq
+.TP
+.B \-edmin\fR <\fIfrac\fR>
+convergence threshold: stop when fractional improvement (decrease) in energy
+dips below this
+.RI ( double );
+default:
+.RB \(lq 0.0001 \(rq
+.TP
+.B \-edpcmin\fR <\fIfrac\fR>
+population control is triggered when energy improvement goes below this
+threshold
+.RI ( double );
+default:
+.RB \(lq 0.01 \(rq
+.TP
+.B \-fnnm\fR <\fIfrac\fR>
+don\(cqt nix if this fraction (or more) of neighbors have been nixed
+.RI ( double );
+default:
+.RB \(lq 0.25 \(rq
+.TP
+.B \-pcp\fR <\fIperiod\fR>
+# iters to wait between attempts at population control
+.RI ( unsigned\ int );
+default:
+.RB \(lq 20 \(rq
+.TP
+.B \-iad\fR <\fI#\ iters\fR>
+# iters to run descent on tentative new points during PC
+.RI ( unsigned\ int );
+default:
+.RB \(lq 10 \(rq
+.TP
+.B \-icb\fR <\fI#\ iters\fR>
+periodicity of calling rendering callback
+.RI ( unsigned\ int );
+default:
+.RB \(lq 1 \(rq
+.TP
+.B \-ac3c\fR <\fIac3c\fR>
+allow codimensions 3 constraints
+.RI ( bool );
+default:
+.RB \(lq false \(rq
+.TP
+.B \-sit\fR <\fIsit\fR>
+scale is tau
+.RI ( bool );
+default:
+.RB \(lq false \(rq
+.TP
+.B \-rng\fR <\fIseed\fR>
+base seed value for RNGs
+.RI ( unsigned\ int );
+default:
+.RB \(lq 42 \(rq
+.TP
+.B \-pbm\fR <\fImod\fR>
+progress bin mod
+.RI ( unsigned\ int );
+default:
+.RB \(lq 50 \(rq
+.TP
+.B \-eiphl\fR <\fIhl\fR>
+half-life of
+.BR energyIncreasePermute (\(lq \-eip \(rq)
+.RI ( unsigned\ int );
+default:
+.RB \(lq 0 \(rq
+.TP
+.B \-nt\fR <\fI#\ threads\fR>
+number of threads hoover should use
+.RI ( int );
+default:
+.RB \(lq 1 \(rq
+.TP
+.B \-nprob\fR <\fIprob\fR>
+do full neighbor discovery with this probability
+.RI ( double );
+default:
+.RB \(lq 1.0 \(rq
+.TP
+.B \-pprob\fR <\fIprob\fR>
+probe local image values with this probability
+.RI ( double );
+default:
+.RB \(lq 1.0 \(rq
+.TP
+.B \-addlog\fR <\fIfname\fR>
+name of file in which to log all particle additions
+.RI ( string );
+default: \(lq\(rq
+.TP
+.B \-o\fR <\fInout\fR>
+filename for saving computed positions
+.RI ( string );
+default:
+.RB \(lq \- \(rq
+.TP
+.B \-eob\fR <\fIbase\fR>
+save extra info (besides position), and use this string as the base of the
+filenames.
+Not using this eans the extra info is not saved.
+.RI ( string );
+default: \(lq\(rq
+.SH NOTES
+.P
+Published research using this tool or the \(lqpull\(rq library should cite the
+paper:
+.in +4n
+\fBGordon L. Kindlmann, Ra{\(rs\(aqu}l San Jos{\(rs\(aqe} Est{\(rs\(aqe}par,
+Stephen M. Smith, Carl-Fredrik Westin.
+Sampling and Visualizing Creases with Scale-Space Particles.
+IEEE Trans. on Visualization and Computer Graphics, 15(6):1415-1424 (2009).\fR
+.in
+.SH "SEE\ ALSO"
+.BR gprobe (1),
+.BR ilk (1),
+.BR mrender (1),
+.BR miter (1),
+.BR nrrdSanity (1),
+.BR overrgb (1),
+.BR tend (1),
+.BR unu (1),
+.BR vprobe (1)
diff --git a/teem.spec b/teem.spec
index 2ebe7a6..b69288c 100644
--- a/teem.spec
+++ b/teem.spec
@@ -1,3 +1,7 @@
+# Writing man pages is a work in progress. We do not install them until we have
+# a complete set.
+%bcond_with man_pages
+
Name: teem
Version: 1.11.0
%global so_version 1
@@ -16,8 +20,124 @@ License: LGPLv2+
# python/ctypes/pullDemo.py
# python/ctypes/teem-gen.py
# python/ctypes/teem.py
-URL: https://teem.sourceforge.net
+URL: http://teem.sourceforge.net
Source0: https://downloads.sourceforge.net/project/teem/teem/%{version}/%{name}-%{...
+
+Source1: gprobe.1
+Source2: ilk.1
+Source3: miter.1
+Source4: mrender.1
+Source5: nrrdSanity.1
+Source6: overrgb.1
+Source7: puller.1
+Source8: tend.1
+Source9: unu.1
+Source10: vprobe.1
+
+Source800: tend-about.1
+Source801: tend-grads.1
+Source802: tend-epireg.1
+Source803: tend-bmat.1
+Source804: tend-estim.1
+Source805: tend-sim.1
+Source806: tend-mfit.1
+Source807: tend-mconv.1
+Source808: tend-msim.1
+Source809: tend-make.1
+Source810: tend-avg.1
+#Source811: tend-helix.1
+#Source812: tend-sten.1
+#Source813: tend-glyph.1
+#Source814: tend-ellipse.1
+#Source815: tend-anplot.1
+#Source816: tend-anvol.1
+#Source817: tend-anscale.1
+#Source818: tend-anhist.1
+#Source819: tend-triple.1
+#Source820: tend-tconv.1
+#Source821: tend-point.1
+#Source822: tend-slice.1
+#Source823: tend-norm.1
+#Source824: tend-fiber.1
+#Source825: tend-eval.1
+#Source826: tend-evalpow.1
+#Source827: tend-evalclamp.1
+#Source828: tend-evaladd.1
+#Source829: tend-evalmul.1
+#Source830: tend-log.1
+#Source831: tend-exp.1
+#Source832: tend-evec.1
+#Source833: tend-evecrgb.1
+#Source834: tend-evq.1
+#Source835: tend-unmf.1
+#Source836: tend-expand.1
+#Source837: tend-shrink.1
+#Source838: tend-bfit.1
+#Source839: tend-satin.1
+#
+#Source900: unu-about.1
+#Source901: unu-env.1
+#Source902: unu-i2w.1
+#Source903: unu-w2i.1
+#Source904: unu-make.1
+#Source905: unu-head.1
+#Source906: unu-data.1
+#Source907: unu-convert.1
+#Source908: unu-resample.1
+#Source909: unu-fft.1
+#Source910: unu-cmedian.1
+#Source911: unu-dist.1
+#Source912: unu-minmax.1
+#Source913: unu-cksum.1
+#Source914: unu-diff.1
+#Source915: unu-quantize.1
+#Source916: unu-unquantize.1
+#Source917: unu-project.1
+#Source918: unu-slice.1
+#Source919: unu-sselect.1
+#Source920: unu-dice.1
+#Source921: unu-splice.1
+#Source922: unu-join.1
+#Source923: unu-crop.1
+#Source924: unu-acrop.1
+#Source925: unu-inset.1
+#Source926: unu-pad.1
+#Source927: unu-reshape.1
+#Source928: unu-permute.1
+#Source929: unu-swap.1
+#Source930: unu-shuffle.1
+#Source931: unu-flip.1
+#Source932: unu-unorient.1
+#Source933: unu-axinfo.1
+#Source934: unu-axinsert.1
+#Source935: unu-axsplit.1
+#Source936: unu-axdelete.1
+#Source937: unu-axmerge.1
+#Source938: unu-tile.1
+#Source939: unu-untile.1
+#Source940: unu-histo.1
+#Source941: unu-dhisto.1
+#Source942: unu-jhisto.1
+#Source943: unu-histax.1
+#Source944: unu-heq.1
+#Source945: unu-gamma.1
+#Source946: unu-1op.1
+#Source947: unu-2op.1
+#Source948: unu-3op.1
+#Source949: unu-affine.1
+#Source950: unu-lut.1
+#Source951: unu-mlut.1
+#Source952: unu-subst.1
+#Source953: unu-rmap.1
+#Source954: unu-mrmap.1
+#Source955: unu-imap.1
+#Source956: unu-lut2.1
+#Source957: unu-ccfind.1
+#Source958: unu-ccadj.1
+#Source959: unu-ccmerge.1
+#Source960: unu-ccsettle.1
+#Source961: unu-save.1
+
# Patch CMakeLists.txt to fix library install paths to match what Fedora
# expects. It is not immediately obvious if this patch is suitable for
# sending upstream or not.
@@ -141,6 +261,34 @@ rm -rvf python
%install
%cmake3_install
+%if %{with man_pages}
+install -d %{buildroot}%{_mandir}/man1
+install -t %{buildroot}%{_mandir}/man1 -p -m 0644 \
+ %{SOURCE1} %{SOURCE2} %{SOURCE3} %{SOURCE4} %{SOURCE5} %{SOURCE6} \
+ %{SOURCE7} %{SOURCE8} %{SOURCE9} %{SOURCE10} \
+ %{SOURCE800} %{SOURCE801} %{SOURCE802} %{SOURCE803} %{SOURCE804} \
+ %{SOURCE805} %{SOURCE806} %{SOURCE807} %{SOURCE808} %{SOURCE809} \
+ %{SOURCE810} %{SOURCE811} %{SOURCE812} %{SOURCE813} %{SOURCE814} \
+ %{SOURCE815} %{SOURCE816} %{SOURCE817} %{SOURCE818} %{SOURCE819} \
+ %{SOURCE820} %{SOURCE821} %{SOURCE822} %{SOURCE823} %{SOURCE824} \
+ %{SOURCE825} %{SOURCE826} %{SOURCE827} %{SOURCE828} %{SOURCE829} \
+ %{SOURCE830} %{SOURCE831} %{SOURCE832} %{SOURCE833} %{SOURCE834} \
+ %{SOURCE835} %{SOURCE836} %{SOURCE837} %{SOURCE838} %{SOURCE839} \
+ %{SOURCE900} %{SOURCE901} %{SOURCE902} %{SOURCE903} %{SOURCE904} \
+ %{SOURCE905} %{SOURCE906} %{SOURCE907} %{SOURCE908} %{SOURCE909} \
+ %{SOURCE910} %{SOURCE911} %{SOURCE912} %{SOURCE913} %{SOURCE914} \
+ %{SOURCE915} %{SOURCE916} %{SOURCE917} %{SOURCE918} %{SOURCE919} \
+ %{SOURCE920} %{SOURCE921} %{SOURCE922} %{SOURCE923} %{SOURCE924} \
+ %{SOURCE925} %{SOURCE926} %{SOURCE927} %{SOURCE928} %{SOURCE929} \
+ %{SOURCE930} %{SOURCE931} %{SOURCE932} %{SOURCE933} %{SOURCE934} \
+ %{SOURCE935} %{SOURCE936} %{SOURCE937} %{SOURCE938} %{SOURCE939} \
+ %{SOURCE940} %{SOURCE941} %{SOURCE942} %{SOURCE943} %{SOURCE944} \
+ %{SOURCE945} %{SOURCE946} %{SOURCE947} %{SOURCE948} %{SOURCE949} \
+ %{SOURCE950} %{SOURCE951} %{SOURCE952} %{SOURCE953} %{SOURCE954} \
+ %{SOURCE955} %{SOURCE956} %{SOURCE957} %{SOURCE958} %{SOURCE959} \
+ %{SOURCE960} %{SOURCE961}
+%endif
+
%check
# We must exclude probeSS_ctmr04 and probeSS_ctmr10 on certain architectures
@@ -171,6 +319,21 @@ rm -rvf python
%{_bindir}/unu
%{_bindir}/vprobe
+%if %{with man_pages}
+%{_mandir}/man1/gprobe.1*
+%{_mandir}/man1/ilk.1*
+%{_mandir}/man1/miter.1*
+%{_mandir}/man1/mrender.1*
+%{_mandir}/man1/nrrdSanity.1*
+%{_mandir}/man1/overrgb.1*
+%{_mandir}/man1/puller.1*
+%{_mandir}/man1/tend.1*
+%{_mandir}/man1/tend-*.1*
+%{_mandir}/man1/unu.1*
+%{_mandir}/man1/unu-*.1*
+%{_mandir}/man1/vprobe.1*
+%endif
+
%files libs
%license LICENSE.txt
@@ -193,7 +356,7 @@ rm -rvf python
%changelog
* Sat Apr 03 2021 Benjamin A. Beasley <code(a)musicinmybrain.net> - 1.11.0-15
- Fix typo in lib_install_dir.patch
-- Update URLs from HTTP to HTTPS
+- Update download URL from HTTP to HTTPS
- Improved package summaries and descriptions
- Adjust whitespace to personal preference
- Fix stray line continuation backslash
@@ -204,6 +367,7 @@ rm -rvf python
to that subpackage; remove certain unused files with other licenses in %%prep
- Require cmake-filesystem instead of all of CMake in -devel
- More precisely skip overly-strict floating-point tests where required
+- Start work on adding a complete set of man pages
* Wed Jan 27 2021 Fedora Release Engineering <releng(a)fedoraproject.org> - 1.11.0-14
- Rebuilt for https://fedoraproject.org/wiki/Fedora_34_Mass_Rebuild
diff --git a/tend-about.1 b/tend-about.1
new file mode 100644
index 0000000..21d8320
--- /dev/null
+++ b/tend-about.1
@@ -0,0 +1,9 @@
+.TH TEND\-ABOUT "1" "April 2021" "" "User Commands"
+.SH NAME
+tend\-about \- information about
+.BR tend (1)
+and its use
+.SH SYNOPSIS
+.B tend\ about
+.SH "SEE\ ALSO"
+.BR tend (1)
diff --git a/tend-avg.1 b/tend-avg.1
new file mode 100644
index 0000000..8b67389
--- /dev/null
+++ b/tend-avg.1
@@ -0,0 +1,32 @@
+.TH TEND\-AVG "1" "April 2021" "" "User Commands"
+.SH NAME
+tend\-avg \- average across tensor volumes
+.SH SYNOPSIS
+.B tend\ avg
+.B \-i
+.RI < nin1 \ nin2 \ ...>
+.RB [ \-t
+.RI < type >]
+.RB [ \-o
+.RI < nout >]
+.SH DESCRIPTION
+Average across tensor volumes.
+The output is the same size as the any one of the inputs.
+The individual tensors may be averaged in various ways.
+.SH OPTIONS
+.TP
+.B \-i\fR <\fInin1\fR\ \fInin2\fR\ ...>
+list of input diffusion tensor volumes (2 or more
+.IR nrrd s)
+.TP
+.B \-t\fR <\fItype\fR>
+averaging method; default:
+.RB \(lq linear \(rq
+.TP
+.B \-o\fR <\fInout\fR>
+output image (floating point)
+.RI ( string )
+default:
+.RB \(lq \- \(rq
+.SH "SEE\ ALSO"
+.BR tend (1)
diff --git a/tend-bmat.1 b/tend-bmat.1
new file mode 100644
index 0000000..e0a8c3b
--- /dev/null
+++ b/tend-bmat.1
@@ -0,0 +1,43 @@
+.TH TEND\-BMAT "1" "April 2021" "" "User Commands"
+.SH NAME
+tend\-bmat \- calculate B-matrix given gradient directions
+.SH SYNOPSIS
+.B tend\ bmat
+.B \-i
+.RI < grads >
+.RB [ \-o
+.RI < nout >]
+.SH DESCRIPTION
+.P
+Calculate B-matrix given gradient directions, assuming no diffusion weighting
+from the other imaging gradients.
+The input is a 3-by-N array of floats or doubles, each row being one of the
+gradient directions used for diffusion-weighted imaging.
+A plain text file with one gradient per line, no punctuation, is an easy way to
+specify this information.
+The gradient vector coefficients are used as is, without normalization (since
+different gradient strengths are sometimes desired).
+The output has one row of the
+B-matrix per line, with coefficient ordering
+.BR Bxx ,
+.BR Bxy ,
+.BR Bxz ,
+.BR Byy ,
+.BR Byz ,
+.BR Bzz ,
+and with the off-diagonal elements NOT pre-multiplied by 2.
+.SH OPTIONS
+.TP
+.B \-i\fR <\fIgrads\fR>
+array of gradient directions
+.TP
+.B \-o\fR <\fInout\fR>
+output B matrix
+.RI ( string )
+default:
+.RB \(lq \- \(rq
+.SH "SEE\ ALSO"
+.BR tend (1),
+.BR tend\-estim (1),
+.BR tend\-msim (1),
+.BR tend\-sim (1)
diff --git a/tend-epireg.1 b/tend-epireg.1
new file mode 100644
index 0000000..cb45cb6
--- /dev/null
+++ b/tend-epireg.1
@@ -0,0 +1,139 @@
+.TH TEND\-EPIREG "1" "April 2021" "" "User Commands"
+.SH NAME
+tend\-epireg \- register diffusion-weighted echo-planar images
+.SH SYNOPSIS
+.B tend\ epireg
+.B \-i
+.RI < dwi0\fR\ \fBdwi1 \ ...>
+.B \-g
+.RI < grads >
+.RB [ \-r
+.RI < reference >]
+.RB [ \-nv ]
+.RB [ \-p ]
+.RB [ \-bw
+.RI < x\fB,\fIy\fB\ blur >]
+.RB [ \-t
+.RI < DWI\ thresh >]
+.RB [ \-ncc ]
+.RB [ \-f
+.RI < fit\ frac >]
+.RB [ \-k
+.RI < kernel >]
+.RB [ \-s
+.RI < start\ # >]
+.RB [ \-o
+.RI < output/prefix >]
+.SH DESCRIPTION
+.P
+Register diffusion-weighted echo-planar images.
+This registration corrects the shear, scale, and translate along the phase
+encoding direction (assumed to be the Y (second) axis of the image) caused by
+eddy currents from the diffusion-encoding gradients with echo-planar imaging.
+The method is based on calculating moments of segmented images, where the
+segmentation is a simple procedure based on blurring (optional), thresholding
+and connected component analysis.
+The registered DWIs are resampled with the chosen kernel, with the separate
+DWIs stacked along axis 0.
+.SH OPTIONS
+.TP
+.B \-i\fR <\fIdwi0\fR\ \fIdwi1\fR\ ...>
+all the diffusion-weighted images (DWIs), as separate 3D
+.IR nrrd s,
+.BR OR :
+one 4D
+.I nrrd
+of all DWIs stacked along axis 0 (1 or more
+.IR nrrd s)
+.TP
+.B \-g\fR <\fIgrads\fR>
+array of gradient directions, in the same order as the associated DWIs were
+given to
+.RB \(lq \-i \(rq,
+.B OR
+.RB \(lq \-g\ kvp \(rq
+signifies that gradient directions should be read from the key/value pairs of
+the DWI
+.RI ( string )
+.TP
+.B \-r\fR <\fIreference\fR>
+which of the DW volumes (zero-based numbering) should be used as the standard,
+to which all other images are transformed.
+Using
+.B \-1
+(the default) means that 9 intrinsic parameters governing the relationship
+between the gradient direction and the resulting distortion are estimated and
+fitted, ensuring good registration with the non-diffusion-weighted T2 image
+(which is never explicitly used in registration).
+Otherwise, by picking a specific DWI, no distortion parameter estimation is
+done.
+.RI ( int );
+default:
+.RB \(lq \-1 \(rq
+.TP
+.B \-nv
+turn OFF verbose mode, and have no idea what stage processing is at.
+.TP
+.B \-p
+save out intermediate steps of processing
+.TP
+.B \-bw\fR <\fIx\fB,\fIy\ blur\fR>
+standard deviations in X and Y directions of gaussian filter used to blur the
+DWIs prior to doing segmentation.
+This blurring does not effect the final resampling of registered DWIs.
+Use
+.RB \(lq 0.0\ 0.0 \(rq
+to say \(lqno blurring\(rq (2
+.IR float s);
+default:
+.RB \(lq 1.0\ 2.0 \(rq
+.TP
+.B \-t\fR <\fIDWI\ thresh\fR>
+Threshold value to use on DWIs, to do initial separation of brain and
+non-brain.
+By default, the threshold is determined automatically by histogram analysis.
+.RI ( float )
+.TP
+.B \-ncc
+do
+.B NOT
+do connected component (CC) analysis, after thresholding and before moment
+calculation.
+Doing CC analysis usually gives better results because it converts the
+thresholding output into something much closer to a real segmentation
+.TP
+.B \-f\fR <\fIfit\ frac\fR>
+(only meaningful with
+.RB \(lq \-r\ \-1 \(rq)
+When doing linear fitting of the intrinsic distortion parameters, it is good to
+ignore the slices for which the segmentation was poor.
+A heuristic is used to rank the slices according to segmentation quality.
+This option controls how many of the (best) slices contribute to the fitting.
+Use
+.RB \(lq 0 \(rq
+to disable distortion parameter fitting.
+.RI ( float )
+default:
+.RB \(lq 0.70 \(rq
+.TP
+.B \-k\fR <\fIkernel\fR>
+kernel for resampling DWIs along the phase-encoding direction during final
+registration stage; default:
+.RB \(lq cubic:0,0.5 \(rq
+.TP
+.B \-s\fR <\fIstart\ #\fR>
+first number to use in numbered sequence of output files.
+.RI ( int );
+default:
+.RB \(lq 1 \(rq
+.TP
+.B \-o\fR <\fIoutput/prefix\ #\fR>
+For separate 3D DWI volume inputs: prefix for output filenames; will save out
+one (registered) DWI for each input DWI, using the same type as the input.
+.BR OR :
+For single 4D DWI input: output file name.
+.RI ( string );
+default:
+.RB \(lq \- \(rq
+.SH "SEE\ ALSO"
+.BR tend (1)
diff --git a/tend-estim.1 b/tend-estim.1
new file mode 100644
index 0000000..649a1e1
--- /dev/null
+++ b/tend-estim.1
@@ -0,0 +1,196 @@
+.TH TEND\-ESTIM "1" "April 2021" "" "User Commands"
+.SH NAME
+tend\-estim \- estimate tensors from a set of DW images
+.SH SYNOPSIS
+.B tend\ estim
+.RB [ \-old ]
+.RB [ \-sigma
+.RI < sigma >]
+.RB [ \-v
+.RI < verbose >]
+.RB [ \-est
+.RI < estimate\ method >]
+.RB [ \-wlsi
+.RI < WLS\ iters >]
+.RB [ \-fixneg ]
+.RB [ \-ee
+.RI < filename >]
+.RB [ \-eb
+.RI < filename >]
+.RB [ \-t
+.RI < thresh >]
+.RB [ \-soft
+.RI < soft >]
+.RB [ \-scale
+.RI < scale >]
+.RB [ \-mv
+.RI < min\ val >]
+.B \-B
+.RI < B\-list >
+.RB [ \-b
+.RI < b >]
+.B \-knownB0
+.RI < bool >
+.RB [ \-i
+.RI < dwi0\fR\ \fIdwi1\fR\ ...>]
+.RB [ \-o
+.RI < nout >]
+.SH DESCRIPTION
+.P
+Estimate tensors from a set of DW images.
+The tensor coefficient weightings associated with each of the DWIs, the
+B-matrix, is given either as a separate array, (see
+.BR tend\-bmat (1) for
+details), or by the key-value pairs in the DWI
+.I nrrd
+header.
+A \(lqconfidence\(rq value is computed with the tensor, based on a soft
+thresholding of the sum of all the DWIs, according to the threshold and
+softness parameters.
+.SH OPTIONS
+.TP
+.B \-old
+instead of the new
+.B tenEstimateContext
+code, use the old
+.B tenEstimateLinear
+code
+.TP
+.B \-sigma\fR <\fIsigma\fR>
+Rician noise parameter
+.RI ( float )
+.TP
+.B \-v\fR <\fIverbose\fR>
+verbosity level
+.RI ( int )
+default:
+.RB \(lq 0 \(rq
+.TP
+.B \-est\fR <\fIestimate\ method\fR>
+estimation method to use.
+.RB \(lq lls \(rq:
+linear-least squares; default:
+.RB \(lq lls \(rq
+.TP
+.B \-wlsi\fR <\fIWLS\ iters\fR>
+when using weighted-least-squares
+.RB (\(lq \-est\ wls \(rq),
+how many iterations to do after the initial weighted fit.
+.RI ( unsigned\ int )
+default:
+.RB \(lq 1 \(rq
+.TP
+.B \-fixneg
+after estimating the tensor, ensure that there are no negative eigenvalues by
+adding (to all eigenvalues) the amount by which the smallest is negative
+(corresponding to increasing the non-DWI image value).
+.TP
+.B \-ee\fR <\fIfilename\fR>
+Giving a filename here allows you to save out the tensor estimation error: a
+value which measures how much error there is between the tensor model and the
+given diffusion weighted measurements for each sample.
+By default, no such error calculation is saved.
+.RI ( string )
+.TP
+.B \-eb\fR <\fIfilename\fR>
+In those cases where there is no B=0 reference image given
+.RB (\(lq \-knownB0\ false \(rq),
+giving a filename here allows you to save out the
+.B B=0
+image which is estimated from the data.
+By default, this image value is estimated but not saved.
+.RI ( string )
+.TP
+.B \-t\fR <\fIthresh\fR>
+value at which to threshold the mean DWI value per pixel in order to generate
+the \(lqconfidence\(rq mask.
+By default, the threshold value is calculated automatically, based on histogram
+analysis.
+.RI ( double )
+.TP
+.B \-soft\fR <\fIsoft\fR>
+how fuzzy the confidence boundary should be.
+By default, confidence boundary is perfectly sharp
+.RI ( float )
+default:
+.RB \(lq 0 \(rq
+.TP
+.B \-scale\fR <\fIscale\fR>
+After estimating the tensor, scale all of its elements (but not the confidence
+value) by this amount.
+Can help with downstream numerical precision if values are very large or small.
+.RI ( float )
+default:
+.RB \(lq 1 \(rq
+.TP
+.B \-mv\fR <\fImin\ val\fR>
+minimum plausible value (especially important for linear least squares
+estimation)
+.RI ( double )
+default:
+.RB \(lq 1.0 \(rq
+.TP
+.B \-B\fR <\fIB\-list\fR>
+6-by-N list of B-matrices characterizing the diffusion weighting for each
+image.
+.BR tend\-bmat (1)
+is one source for such a matrix; see its usage info for specifics on how the
+coefficients of the B-matrix are ordered.
+An unadorned plain text file is a great way to specify the B-matrix.
+.IP
+.B OR
+.IP
+Can say just
+.RB \(lq \-B\ kvp \(rq
+to try to learn B matrices from key/value pair information in input images.
+.IP
+.RI ( string )
+.TP
+.B \-b\fR <\fIb\fR>
+\(lqb\(rq diffusion-weighting factor (units of sec/mm^2)
+.RI ( double )
+.TP
+.B \-knownB0\fR <\fIbool\fR>
+Indicates if the
+.B B=0
+non-diffusion-weighted reference image is known, or if it has to be estimated
+along with the tensor elements
+.RS +7n
+.IP \(bu 2
+if
+.RB \(lq true \(rq:
+in the given list of diffusion gradients or B-matrices, there are one or more
+with zero norm, which are simply averaged to find the
+.B B=0
+reference image value
+.IP \(bu 2
+if
+.RB \(lq false \(rq:
+there may or may not be diffusion-weighted images among the input; the
+.B B=0
+image value is going to be estimated along with the diffusion model
+.P
+.RI ( bool )
+.RE
+.TP
+.B \-i\fR <\fIdwi0\fR\ \fIdwi1\fR\ ...>
+all the diffusion-weighted images (DWIs), as separate 3D
+.IR nrrd s,
+.BR OR :
+One 4D
+.I nrrd
+of all DWIs stacked along axis 0 (1 or more
+.IR nrrd s);
+default:
+.RB \(lq \- \(rq
+.TP
+.B \-o\fR <\fInout\fR>
+output tensor volume
+.RI ( string )
+default:
+.RB \(lq \- \(rq
+.SH "SEE\ ALSO"
+.BR tend (1)
+.BR tend\-bmat (1),
+.BR tend\-msim (1),
+.BR tend\-sim (1)
diff --git a/tend-grads.1 b/tend-grads.1
new file mode 100644
index 0000000..e92bc8f
--- /dev/null
+++ b/tend-grads.1
@@ -0,0 +1,162 @@
+.TH TEND\-GRADS "1" "April 2021" "" "User Commands"
+.SH NAME
+tend\-grads \- calculate balanced gradient directions for DWI acquisition
+.SH SYNOPSIS
+.B tend\ grads
+.RB [ \-n
+.RI < #\ dir >]
+.RB [ \-i
+.RI < grads >]
+.RB [ \-seed
+.RI < value >]
+.RB [ \-step
+.RI < step >]
+.RB [ \-single ]
+.RB [ \-snap
+.RI < interval >]
+.RB [ \-jitter
+.RI < jitter >]
+.RB [ \-miniter
+.RI < #\ iters >]
+.RB [ \-maxiter
+.RI < #\ iters >]
+.RB [ \-minvelo
+.RI < vel >]
+.RB [ \-exp
+.RI < exponent >]
+.RB [ \-dp
+.RI < potential\ change >]
+.RB [ \-minimprov
+.RI < delta >]
+.RB [ \-minmean
+.RI < len >]
+.RB [ \-izv
+.RI < insert >]
+.RB [ \-o
+.RI < filename >]
+.SH DESCRIPTION
+.P
+Composites an RGBA
+.I nrrd
+over a background color (or image), after doing gamma correction, then
+quantizes to an 8-bit image.
+Actually, the input
+.I nrrd
+can have more than 4 values per pixel, but only the first four are used.
+If the RGBA
+.I nrrd
+s floating point, the values are taken at face value; if it is fixed point, the
+values interpreted as having been quantized (so that 8-bit RGBA images will act
+as you expect).
+When compositing with a background image, the given background image does not
+have to be the same size as the input image; it will be resampled (with linear
+interpolation) to fit.
+.SH OPTIONS
+.TP
+.B \-n\fR <\fI#\ dir\fR>
+desired number of diffusion gradient directions
+.RI ( int )
+default:
+.RB \(lq 6 \(rq
+.TP
+.B \-i\fR <\fIgrads\fR>
+initial gradient directions to start with, instead of default random initial
+directions (overrides
+.RB \(lq \-n \(rq);
+default: \(lq\(rq
+.TP
+.B \-seed\fR <\fIvalue\fR>
+seed value to used with
+.B airSrandMT()
+.RI ( unsigned\ int )
+default:
+.RB \(lq 42 \(rq
+.TP
+.B \-step\fR <\fIstep\fR>
+time increment in solver
+.RI ( double )
+default:
+.RB \(lq 1.0 \(rq
+.TP
+.B \-single
+instead of the default behavior of tracking a pair of antipodal points
+(appropriate for determining DWI gradients), use only single points
+(appropriate for who knows what).
+.TP
+.B \-snap\fR <\fIinterval\fR>
+specifies an interval between which snapshots of the point positions should be
+saved out.
+By default (not using this option), there is no such snapshot behavior
+.RI ( int )
+default:
+.RB \(lq 0 \(rq
+.TP
+.B \-jitter\fR <\fIjitter\fR>
+amount by which to perturb points when given an input
+.I nrrd
+.RI ( double )
+default:
+.RB \(lq 0.1 \(rq
+.TP
+.B \-miniter\fR <\fI#\ iters\fR>
+min number of iterations for which to run the simulation
+.RI ( int )
+default:
+.RB \(lq 0 \(rq
+.TP
+.B \-maxiter\fR <\fI#\ iters\fR>
+max number of iterations for which to run the simulation
+.RI ( int )
+default:
+.RB \(lq 1000000 \(rq
+.TP
+.B \-minvelo\fR <\fIvel\fR>
+low threshold on mean velocity of repelling points, at which point repulsion
+phase of algorithm terminates.
+.RI ( double )
+default:
+.RB \(lq 0.00001 \(rq
+.TP
+.B \-exp\fR <\fIexponent\fR>
+the exponent n that determines the potential energy
+.BR 1/r^n .
+.RI ( double )
+default:
+.RB \(lq 1 \(rq
+.TP
+.B \-dp\fR <\fIpotential\ change\fR>
+low threshold on fractional change of potential at which point repulsion phase
+of algorithm terminates.
+.RI ( double )
+default:
+.RB \(lq 0.000000001 \(rq
+.TP
+.B \-minimprov\fR <\fIdelta\fR>
+in the second phase of the algorithm, when stochastically balancing the sign of
+the gradients, the (small) improvement in length of mean gradient which
+triggers termination (as further improvements are unlikely.
+.RI ( double )
+default:
+.RB \(lq 0.00005 \(rq
+.TP
+.B \-minmean\fR <\fIlen\fR>
+if length of mean gradient falls below this, finish the balancing phase
+.RI ( double )
+default:
+.RB \(lq 0.0001 \(rq
+.TP
+.B \-izv\fR <\fIinsert\fR>
+adding zero vector at beginning of grads
+.RI ( bool )
+default:
+.RB \(lq false \(rq
+.TP
+.B \-o\fR <\fIfilename\fR>
+file to write output
+.I nrrd
+to
+.RI ( string );
+default:
+.RB \(lq \- \(rq
+.SH "SEE\ ALSO"
+.BR tend (1)
diff --git a/tend-make.1 b/tend-make.1
new file mode 100644
index 0000000..c4b7b08
--- /dev/null
+++ b/tend-make.1
@@ -0,0 +1,28 @@
+.TH TEND\-MAKE "1" "April 2021" "" "User Commands"
+.SH NAME
+tend\-make \- create DT volume from confidence and eigensystem
+.SH SYNOPSIS
+.B tend\ make
+.B \-i
+.RI < conf \ evals \ evecs >
+.RB [ \-o
+.RI < nout >]
+.SH DESCRIPTION
+Create DT volume from confidence and eigensystem.
+The input is in the form of three
+.IR nrrd s,
+one for confidence values (3D), one for eigenvalues (4D, three evals per
+voxel), and one for eigenvectors (4D, nine evec components per voxel).
+.SH OPTIONS
+.TP
+.B \-i\fR <\fIconf\fR\ \fIevals\fR\ \fIevecs\fR>
+input diffusion tensor volume (3
+.IR nrrd s)
+.TP
+.B \-o\fR <\fInout\fR>
+output image (floating point)
+.RI ( string )
+default:
+.RB \(lq \- \(rq
+.SH "SEE\ ALSO"
+.BR tend (1)
diff --git a/tend-mconv.1 b/tend-mconv.1
new file mode 100644
index 0000000..54a6f07
--- /dev/null
+++ b/tend-mconv.1
@@ -0,0 +1,39 @@
+.TH TEND\-MCONV "1" "April 2021" "" "User Commands"
+.SH NAME
+tend\-mconv \- convert from one model to another
+.SH SYNOPSIS
+.B tend\ mconv
+.B \-mo
+.RI < model >
+.RB [ \-mi
+.RI < model >]
+.RB [ \-i
+.RI < nin >]
+.RB [ \-o
+.RI < nout >]
+.SH OPTIONS
+.TP
+.B \-mo\fR <\fImodel\fR>
+which model to convert to
+.RI ( string )
+.TP
+.B \-mi\fR <\fImodel\fR>
+model converting from; if not set, will try to determine from input
+.I nrrd
+.RI ( string )
+.TP
+.B \-i\fR <\fInin\fR>
+input
+.I nrrd
+of model parms; default:
+.RB \(lq \- \(rq
+.TP
+.B \-o\fR <\fInout\fR>
+output
+.I nrrd
+of model parms
+.RI ( string );
+default:
+.RB \(lq \- \(rq
+.SH "SEE\ ALSO"
+.BR tend (1)
diff --git a/tend-mfit.1 b/tend-mfit.1
new file mode 100644
index 0000000..fc220b4
--- /dev/null
+++ b/tend-mfit.1
@@ -0,0 +1,129 @@
+.TH TEND\-MFIT "1" "April 2021" "" "User Commands"
+.SH NAME
+tend\-mfit \- estimate models from a set of DW images
+.SH SYNOPSIS
+.B tend\ mfit
+.RB [ \-v
+.RI < verbose >]
+.B \-m
+.RI < model >
+.RB [ \-ns
+.RI < #\ starts >]
+.RB [ \-ml ]
+.RB [ \-sigma
+.RI < sigma >]
+.RB [ \-eps
+.RI < eps >]
+.RB [ \-mini
+.RI < min\ iters >]
+.RB [ \-maxi
+.RI < max\ iters >]
+.B \-knownB0
+.RI < bool >
+.RB [ \-t
+.RI < type >]
+.RB [ \-i
+.RI < dwi >]
+.RB [ \-o
+.RI < nout >]
+.RB [ \-eo
+.RI < filename >]
+.RB [ \-co
+.RI < filename >]
+.RB [ \-io
+.RI < filename >]
+.SH OPTIONS
+.TP
+.B \-v\fR <\fIverbose\fR>
+verbosity level
+.RI ( int )
+default:
+.RB \(lq 0 \(rq
+.TP
+.B \-m\fR <\fImodel\fR>
+which model to fit.
+Use optional
+.RB \(lq b0+ \(rq
+prefix to indicate that the
+.B B0
+image should also be saved (independent of whether it was known or had to be
+estimated, according to
+.RB \(lq \-knownB0 \(rq).
+.RI ( string )
+.TP
+.B \-ns\fR <\fI#\ starts\fR>
+number of random starting points at which to initialize fitting
+.RI ( unsigned\ int )
+default:
+.RB \(lq 1 \(rq
+.TP
+.B \-ml
+do ML fitting, rather than least-squares, which also requires setting
+.RB \(lq \-sigma \(rq
+.TP
+.B \-sigma\fR <\fIsigma\fR>
+Gaussian/Rician noise parameter
+.RI ( double )
+.TP
+.B \-eps\fR <\fIeps\fR>
+convergence epsilon
+.RI ( double )
+default:
+.RB \(lq 0.01 \(rq
+.TP
+.B \-mini\fR <\fImin\ iters\fR>
+minimum required # iterations for fitting.
+.RI ( unsigned\ int )
+default:
+.RB \(lq 3 \(rq
+.TP
+.B \-maxi\fR <\fImax\ iters\fR>
+maximum allowable # iterations for fitting.
+.RI ( unsigned\ int )
+default:
+.RB \(lq 100 \(rq
+.TP
+.B \-knownB0\fR <\fIbool\fR>
+Indicates if the
+.B B=0
+non-diffusion-weighted reference image is known
+.RB (\(lq true \(rq)
+because it appears one or more times amongst the DWIs, or, if it has to be
+estimated along with the other model parameters
+.RB (\(lq false \(rq)
+.RI ( bool )
+.TP
+.B \-t\fR <\fItype\fR>
+output type of model parameters; default:
+.RB \(lq float \(rq
+.TP
+.B \-i\fR <\fIdwi\fR>
+all the diffusion-weighted images in one 4D
+.IR nrrd ;
+default:
+.RB \(lq \- \(rq
+.TP
+.B \-o\fR <\fInout\fR>
+output parameter vector image
+.RI ( string )
+default:
+.RB \(lq \- \(rq
+.TP
+.B \-eo\fR <\fIfilename\fR>
+Giving a filename here allows you to save out the per-sample fitting error.
+By default, no such error is saved.
+.RI ( string )
+.TP
+.B \-co\fR <\fIfilename\fR>
+Giving a filename here allows you to save out the per-sample convergence
+fraction.
+By default, no such error is saved.
+.RI ( string )
+.TP
+.B \-io\fR <\fIfilename\fR>
+Giving a filename here allows you to save out the per-sample number of
+iterations needed for fitting.
+By default, no such error is saved.
+.RI ( string )
+.SH "SEE\ ALSO"
+.BR tend (1)
diff --git a/tend-msim.1 b/tend-msim.1
new file mode 100644
index 0000000..2d0b76a
--- /dev/null
+++ b/tend-msim.1
@@ -0,0 +1,111 @@
+.TH TEND\-MSIM "1" "April 2021" "" "User Commands"
+.SH NAME
+tend\-msim \- simulate DW images from an image of models
+.SH SYNOPSIS
+.B tend\ msim
+.RB [ \-sigma
+.RI < sigma >]
+.RB [ \-seed
+.RI < seed >]
+.B \-g
+.RI < grad\ list >
+.RB [ \-b0
+.RI < b0\ image >]
+.RB [ \-i
+.RI < model\ image >]
+.B \-m
+.RI < model >
+.RB [ \-ib0
+.RI < bool >]
+.RB [ \-b
+.RI < b >]
+.RB [ \-kvp
+.RI < bool >]
+.RB [ \-t
+.RI < type >]
+.RB [ \-o
+.RI < nout >]
+.SH DESCRIPTION
+Simulate DW images from an image of models.
+The output will be in the same form as the input to
+.BR tend\-estim (1).
+The B-matrices
+.RB (\(lq \-B \(rq)
+can be the output from
+.BR tend\-bmat (1),
+or the gradients can be given directly
+.RB (\(lq \-g \(rq);
+one of these is required.
+Note that the input tensor image
+.RB (\(lq \-i \(rq)
+is the basis of the output per-axis fields and image orientation.
+.B NOTE:
+this includes the measurement frame used in the input tensor image, which
+implies that the given gradients or B-matrices are already expressed in that
+measurement frame.
+.SH OPTIONS
+.TP
+.B \-sigma\fR <\fIsigma\fR>
+Gaussian/Rician noise parameter
+.RI ( double )
+default:
+.RB \(lq 0.0 \(rq
+.TP
+.B \-seed\fR <\fIseed\fR>
+seed value for RNG which creates noise
+.RI ( int )
+default:
+.RB \(lq 42 \(rq
+.TP
+.B \-g\fR <\fIgrad\ list\fR>
+gradient list, one row per diffusion-weighted image
+.TP
+.B \-b0\fR <\fIb0\ image\fR>
+reference non-diffusion-weighted
+.RB (\(lq B0 \(rq)
+image, which may be needed if it isn't part of the given model parameter image
+.TP
+.B \-m\fR <\fImodel\fR>
+model with which to simulate DWIs, which must be specified if it is not
+indicated by the first axis in input model image.
+.RB \(lq string \(rq
+.TP
+.B \-ib0\fR <\fIbool\fR>
+insert a non-DW
+.B B0
+image at the beginning of the experiment specification (useful if the given
+gradient list doesn't already have one) and hence also insert a
+.B B0
+image at the beginning of the output simulated DWIs
+.RB \(lq bool \(rq
+default:
+.RB \(lq false \(rq
+.TP
+.B \-b\fR <\fIb\fR>
+.B b
+value for simulated scan
+.RB \(lq double \(rq
+default:
+.RB \(lq 1000 \(rq
+.TP
+.B \-kvp\fR <\fIbool\fR>
+generate key/value pairs in the NRRD header corresponding to the input
+.BR b -value
+and gradients.
+.RB \(lq bool \(rq
+default:
+.RB \(lq true \(rq
+.TP
+.B \-t\fR <\fItype\fR>
+output type of DWIs; default:
+.RB \(lq float \(rq
+.TP
+.B \-o\fR <\fInout\fR>
+output dwis
+.RI ( string )
+default:
+.RB \(lq \- \(rq
+.SH "SEE\ ALSO"
+.BR tend (1),
+.BR tend\-bmat (1),
+.BR tend\-estim (1)
diff --git a/tend-sim.1 b/tend-sim.1
new file mode 100644
index 0000000..3761398
--- /dev/null
+++ b/tend-sim.1
@@ -0,0 +1,101 @@
+.TH TEND\-SIM "1" "April 2021" "" "User Commands"
+.SH NAME
+tend\-sim \- simulate DW images from a tensor field
+.SH SYNOPSIS
+.B tend\ sim
+.RB [ \-old ]
+.RB [ \-sigma
+.RI < sigma >]
+.RB [ \-seed
+.RI < seed >]
+.RB [ \-g
+.RI < grad\ list >]
+.RB [ \-B
+.RI < B\ matrix >]
+.B \-r
+.RI < reference\ field >
+.RB [ \-i
+.RI < tensor\ field >]
+.RB [ \-b
+.RI < b >]
+.RB [ \-kvp ]
+.RB [ \-t
+.RI < type >]
+.RB [ \-o
+.RI < nout >]
+.SH DESCRIPTION
+.P
+Simulate DW images from a tensor field.
+The output will be in the same form as the input to
+.BR tend\-estim (1).
+The B-matrices
+.RB (\(lq \-B \(rq)
+can be the output from
+.BR tend\-bmat (1),
+or the gradients can be given directly
+.RB (\(lq \-g \(rq);
+one of these is required.
+Note that the input tensor field
+.RB (\(lq \-i \(rq)
+is the basis of the output per-axis fields and image orientation.
+.B NOTE:
+this includes the measurement frame used in the input tensor field, which
+implies that the given gradients or B-matrices are already expressed in that
+measurement frame.
+.SH OPTIONS
+.TP
+.B \-old
+don't use the new
+.B tenEstimateContext
+functionality
+.TP
+.B \-sigma\fR <\fIsigma\fR>
+Rician noise parameter
+.RI ( float )
+default:
+.RB \(lq 0.0 \(rq
+.TP
+.B \-seed\fR <\fIseed\fR>
+seed value for RNG which creates noise
+.RI ( int )
+default:
+.RB \(lq 42 \(rq
+.TP
+.B \-g\fR <\fIgrad\ list\fR>
+gradient list, one row per diffusion-weighted image
+.TP
+.B \-B\fR <\fIB\ matrix\fR>
+B matrix, one row per diffusion-weighted image.
+Using this overrides the gradient list input via
+.RB \(lq \-g \(rq
+.TP
+.B \-r\fR <\fIreference\ field\fR>
+reference anatomical scan, with no diffusion weighting
+.TP
+.B \-i\fR <\fItensor\ field\fR>
+input diffusion tensor field
+.TP
+.B \-b\fR <\fIb\fR>
+.B b
+value for simulated scan
+.RI ( float )
+default:
+.RB \(lq 1000 \(rq
+.TP
+.B \-kvp
+generate key/value pairs in the NRRD header corresponding to the input b-value
+and gradients or B-matrices.
+.TP
+.B \-t\fR <\fItype\fR>
+output type of DWIs; default:
+.RB \(lq float \(rq
+.TP
+.B \-o\fR <\fInout\fR>
+output image (floating point)
+.RI ( string )
+default:
+.RB \(lq \- \(rq
+.SH "SEE\ ALSO"
+.BR tend (1),
+.BR tend\-bmat (1),
+.BR tend\-estim (1)
diff --git a/tend.1 b/tend.1
new file mode 100644
index 0000000..e61040a
--- /dev/null
+++ b/tend.1
@@ -0,0 +1,316 @@
+.TH TEND "1" "April 2021" "" "User Commands"
+.SH NAME
+tend \- diffusion image processing and analysis
+.SH SYNOPSIS
+.B tend
+.I COMMAND
+.RI [ OPTION ]...
+.SH DESCRIPTION
+.BR tend (1)
+is a command-line interface to much of the functionality in \(lqten\(rq, a C
+library for diffusion image processing.
+Ten is one library in the \(lqTeem\(rq collection of libraries.
+More information about Teem is at
+.UR http://teem.sf.net
+http://teem.sf.net
+.UE .
+A checkout of Teem source is available via:
+.in +4n
+.EX
+.B svn co https://svn.code.sf.net/p/teem/code/teem/trunk teem
+.EE
+.in
+.P
+Long-term maintenance of this software depends on funding, and funding depends
+on being able to document who is using it for what.
+If
+.BR tend (1)
+or Ten has helped in your research, including for simple one-off experiments or
+mundane data hacking, the developers of Teem would love to know.
+There are multiple ways of communicating this.
+In your publications, consider adding a line such as this in the
+Acknowledgments:
+.in +4n
+Data processing performed with the tend tool, part of the Teem toolkit
+available at
+.UR http://teem.sf.net
+http://teem.sf.net
+.UE .
+.in
+Alternatively, please email
+.MT glk(a)uchicago.edu
+glk(a)uchicago.edu
+.ME
+and briefly describe how Teem software has helped in your work.
+Please also consider joining the
+.UR https://lists.sourceforge.net/lists/listinfo/teem-users
+teem\-users mailing list
+.UE .
+This is the primary forum for feedback, questions, and feature requests.
+.P
+Like
+.BR unu (1),
+another Teem command-line binary, it is often useful to chain together invocations of
+.BR tend (1)
+with pipes, as in the following, which estimates tensors from DWIs, takes a
+slice of the tensor volume, computes the standard RGB colormap of the principal
+eigenvector, and then quantizes it to an 8-bit PNG:
+.in +4n
+.EX
+\fBtend\ estim\ \-i\ dwi.nhdr\ \-B\ kvp\ \-knownB0\ true\ \(rs
+\ |\ tend\ slice\ \-a\ 2\ \-p\ 30\ \(rs
+\ |\ tend\ evecrgb\ \-c\ 0\ \-a\ cl2\ \-gam\ 1.2\ \(rs
+\ |\ unu\ quantize\ \-b\ 8\ \-min\ 0\ \-max\ 1\ \-o\ z30\-rgb.png
+.EE
+.in
+.SH OPTIONS
+.SS tend\ about
+Information about this program and its use
+.P
+See
+.BR tend\-about (1).
+.SS tend\ grads
+Calculate balanced gradient directions for DWI acquisition
+.P
+See
+.BR tend\-grads (1).
+.SS tend\ epireg
+Register diffusion-weighted echo-planar images
+.P
+See
+.BR tend\-epireg (1).
+.SS tend\ bmat
+Calculate B-matrix given gradient directions
+.P
+See
+.BR tend\-bmat (1).
+.SS tend\ estim
+Estimate tensors from a set of DW images
+.P
+See
+.BR tend\-estim (1).
+.SS tend\ sim
+Simulate DW images from a tensor field
+.P
+See
+.BR tend\-sim (1).
+.SS tend\ mfit
+Estimate models from a set of DW images
+.P
+See
+.BR tend\-mfit (1).
+.SS tend\ mconv
+Convert from one model to another
+.P
+See
+.BR tend\-mconv (1).
+.SS tend\ msim
+Simulate DW images from an image of models
+.P
+See
+.BR tend\-msim (1).
+.SS tend\ make
+Create DT volume from confidence and eigensystem
+.P
+See
+.BR tend\-make (1).
+.SS tend\ avg
+Average across tensor volumes
+.P
+See
+.BR tend\-avg (1).
+.SS tend\ helix
+Generate twisting helical tensor field
+.P
+See
+.BR tend\-helix (1).
+.SS tend\ sten
+Calculate structure tensors from a scalar field
+.P
+See
+.BR tend\-sten (1).
+.SS tend\ glyph
+Generate postscript or ray-traced renderings of 3D glyphs
+.P
+See
+.BR tend\-glyph (1).
+.SS tend\ ellipse
+Generate postscript renderings of 2D glyphs
+.P
+See
+.BR tend\-ellipse (1).
+.SS tend\ anplot
+Graph anisotropy metric in barycentric coords
+.P
+See
+.BR tend\-anplot (1).
+.SS tend\ anvol
+Apply an anisotropy metric to a DT volume
+.P
+See
+.BR tend\-anvol (1).
+.SS tend\ anscale
+Scale the anisotropic component of the tensors
+.P
+See
+.BR tend\-anscale (1).
+.SS tend\ anhist
+Generate barycentric histograms of anisotropy
+.P
+See
+.BR tend\-anhist (1).
+.SS tend\ triple
+Compute volume of shape triples
+.P
+See
+.BR tend\-triple (1).
+.SS tend\ tconv
+Convert between different shape triples
+.P
+See
+.BR tend\-tconv (1).
+.SS tend\ point
+Describe everything about one sample in a DT volume
+.P
+See
+.BR tend\-point (1).
+.SS tend\ slice
+Slice 3D tensors to get slab/image of 3D/2D tensors
+.P
+See
+.BR tend\-slice (1).
+.SS tend\ norm
+Normalize tensor size
+.P
+See
+.BR tend\-norm (1).
+.SS tend\ fiber
+Fiber tractography, from one or more seeds
+.P
+See
+.BR tend\-fiber (1).
+.SS tend\ eval
+Calculate one or more eigenvalues in a DT volume
+.P
+See
+.BR tend\-eval (1).
+.SS tend\ evalpow
+Modify shape by raising eigenvalues to some power
+.P
+See
+.BR tend\-evalpow (1).
+.SS tend\ evalclamp
+Modify shape by clamping eigenvalues in some range
+.P
+See
+.BR tend\-evalclamp (1).
+.SS tend\ evaladd
+Modify shape by adding a constant to all eigenvalues
+.P
+See
+.BR tend\-evaladd (1).
+.SS tend\ evalmul
+Modify shape by multiplying eigenvalues by a constant
+.P
+See
+.BR tend\-evalmul (1).
+.SS tend\ log
+Calculates logarithm of the tensor
+.P
+See
+.BR tend\-log (1).
+.SS tend\ exp
+Calculates \fBexp()\fR of the tensor
+.P
+See
+.BR tend\-exp (1).
+.SS tend\ evec
+Calculate one or more eigenvectors in a DT volume
+.P
+See
+.BR tend\-evec (1).
+.SS tend\ evecrgb
+Make an RGB volume from an eigenvector and an anisotropy
+.P
+See
+.BR tend\-evecrgb (1).
+.SS tend\ evq
+Quantize directions of diffusion
+.P
+See
+.BR tend\-evq (1).
+.SS tend\ unmf
+Applies and removes the measurement frame
+.P
+See
+.BR tend\-unmf (1).
+.SS tend\ expand
+Converts masked non-redundant tensor images to redundant
+.P
+See
+.BR tend\-expand (1).
+.SS tend\ shrink
+Converts a 9-value DT volume to a 7-value DT volume
+.P
+See
+.BR tend\-shrink (1).
+.SS tend\ bfit
+Non-linear least-squares fitting of b-value curves
+.P
+See
+.BR tend\-bfit (1).
+.SS tend\ satin
+Generate a pretty synthetic DT volume
+.P
+See
+.BR tend\-satin (1).
+.SH "SEE\ ALSO"
+.BR gprobe (1),
+.BR ilk (1),
+.BR mrender (1),
+.BR miter (1),
+.BR nrrdSanity (1),
+.BR overrgb (1),
+.BR puller (1),
+.BR unu (1),
+.BR vprobe (1),
+.BR tend\-about (1),
+.BR tend\-anhist (1),
+.BR tend\-anplot (1),
+.BR tend\-anscale (1),
+.BR tend\-anvol (1),
+.BR tend\-avg (1),
+.BR tend\-bfit (1),
+.BR tend\-bmat (1),
+.BR tend\-ellipse (1),
+.BR tend\-epireg (1),
+.BR tend\-estim (1),
+.BR tend\-eval (1),
+.BR tend\-evaladd (1),
+.BR tend\-evalclamp (1),
+.BR tend\-evalmul (1),
+.BR tend\-evalpow (1),
+.BR tend\-evec (1),
+.BR tend\-evecrgb (1),
+.BR tend\-evq (1),
+.BR tend\-exp (1),
+.BR tend\-expand (1),
+.BR tend\-fiber (1),
+.BR tend\-glyph (1),
+.BR tend\-grads (1),
+.BR tend\-helix (1),
+.BR tend\-log (1),
+.BR tend\-make (1),
+.BR tend\-mconv (1),
+.BR tend\-mfit (1),
+.BR tend\-msim (1),
+.BR tend\-norm (1),
+.BR tend\-point (1),
+.BR tend\-satin (1),
+.BR tend\-shrink (1),
+.BR tend\-sim (1),
+.BR tend\-slice (1),
+.BR tend\-sten (1),
+.BR tend\-tconv (1),
+.BR tend\-triple (1),
+.BR tend\-unmf (1)
diff --git a/unu.1 b/unu.1
new file mode 100644
index 0000000..ef710be
--- /dev/null
+++ b/unu.1
@@ -0,0 +1,127 @@
+.TH UNU "1" "April 2021" "" "User Commands"
+.SH NAME
+unu \- Utah Nrrd Utilities command-line interface
+.SH SYNOPSIS
+.B unu
+.I COMMAND
+.RI [ OPTION ]...
+.SH DESCRIPTION
+.BR unu (1)
+is a command-line interface to much of the functionality in \(lqnrrd\(rq, a C
+library for raster data processing.
+Nrrd is one library in the \(lqTeem\(rq collection of libraries.
+More information about Teem is at
+.UR http://teem.sf.net
+http://teem.sf.net
+.UE .
+A checkout of Teem source is available via:
+.in +4n
+.EX
+.B svn co https://svn.code.sf.net/p/teem/code/teem/trunk teem
+.EE
+.in
+.P
+Long-term maintenance of this software depends on funding, and funding depends
+on being able to document who is using it for what.
+If
+.BR unu (1)
+or Nrrd has helped in your research, including for simple one-off experiments
+or mundane data hacking, the developers of Teem would love to know.
+There are multiple ways of communicating this.
+In your publications, consider adding a line such as this in the
+Acknowledgments:
+.in +4n
+Data processing performed with the unu tool, part of the Teem toolkit available
+at
+.UR http://teem.sf.net
+http://teem.sf.net
+.UE .
+.in
+Alternatively, please email
+.MT glk(a)uchicago.edu
+glk(a)uchicago.edu
+.ME
+and briefly describe how Teem software has helped in your work.
+Please also consider joining the
+.UR https://lists.sourceforge.net/lists/listinfo/teem-users
+teem\-users mailing list
+.UE .
+This is the primary forum for feedback, questions, and feature requests.
+.P
+A summary list of unu commands is generated by running simply
+.RB \(lq unu \(rq.
+Running a
+.BR unu (1)
+command without additional arguments (e.g. \(lq\fBunu\ slice\fR\(rq)
+generates its description and usage information.
+This includes information (marked with
+.RB \(lq *\ Uses\ ... \(rq)
+about the underling Nrrd library calls (e.g. \fBnrrdSlice()\fR) that implement
+the basic functionality in that
+.BR unu (1)
+command.
+.P
+The utility of
+.BR unu (1)
+is mainly as a pre-processing tool for getting data into a type, encoding,
+format, or dimensions best suited for some visualization or rendering task.
+Also, slices and projections are effective ways to visually inspect the
+contents of a dataset.
+Especially useful
+ commands include
+.BR make ,
+.BR resample ,
+.BR crop ,
+.BR slice ,
+.BR project ,
+.BR histo ,
+.BR dhisto ,
+.BR quantize ,
+and
+.BR save .
+.BR unu (1)
+can process CT and MRI volume datasets, grayscale and color images,
+time-varying volumes of vector fields (5-D arrays), and more.
+Currently supported formats are plain text files (2-D float arrays), NRRD, VTK
+structured points, and PNG and PNM images.
+.RB \(lq unu\ make\ \-bs\ \-1 \(rq
+can read from most DICOM files.
+.RB \(lq unu\ save \(rq
+can generate EPS files.
+Supported encodings are raw, ascii, hex, gzip, and bzip2.
+.P
+Much of the functionality of
+.BR unu (1)
+derives from chaining multiple invocations together with pipes
+.RB (\(lq | \(rq),
+minimizing the need to save out intermediate files.
+For example, if
+.RB (\(lq data.raw.gz \(rq),
+is a gzip\(cqed 256\ x\ 256\ x\ 80 volume of raw floats written from a PC, then
+the following will save to
+.RB (\(lq zsum.png \(rq)
+a histogram equalized summation projection along the slowest axis:
+.in +4n
+.EX
+\fBunu\ make\ \-i\ data.raw.gz\ \-t\ float\ \-s\ 256\ 256\ 80\ \-e\ gzip\ \-en\ little\ \(rs
+\ |\ unu\ project\ \-a\ 2\ \-m\ sum\ \(rs
+\ |\ unu\ heq\ \-b\ 2000\ \-s\ 1\ \(rs
+\ |\ unu\ quantize\ \-b\ 8\ \-o\ zsum.png
+.EE
+.in
+.P
+Formats available:
+.B nrrd pnm png vtk text eps
+.P
+Nrrd data encodings available:
+.B raw ascii hex gz bz2
+.SH "SEE\ ALSO"
+.BR gprobe (1),
+.BR ilk (1),
+.BR mrender (1),
+.BR miter (1),
+.BR nrrdSanity (1),
+.BR overrgb (1),
+.BR puller (1),
+.BR tend (1),
+.BR vprobe (1)
diff --git a/vprobe.1 b/vprobe.1
new file mode 100644
index 0000000..bdee507
--- /dev/null
+++ b/vprobe.1
@@ -0,0 +1,214 @@
+.TH VPROBE "1" "April 2021" "" "User Commands"
+.SH NAME
+vprobe \- shows off the functionality of the gage library
+.SH SYNOPSIS
+.B vprobe
+.B \-i
+.RI < nin >
+.B \-k
+.RI < kind >
+.RB [ \-v
+.RI < verbosity >]
+.B \-q
+.RI < query >
+.RB [ \-s
+.RI < sclX \ xclY \ sxlZ >]
+.RB [ \-k00
+.RI < kern00 >]
+.RB [ \-k11
+.RI < kern11 >]
+.RB [ \-k22
+.RI < kern22 >]
+.RB [ \-seed
+.RI < N >]
+.RB [ \-ssn
+.RI < SS\ # >]
+.RB [ \-ssr
+.RI < scale\ range >]
+.RB [ \-ssrf
+.RI < SS\ read\ format >]
+.RB [ \-sssf
+.RI < SS\ save\ format >]
+.RB [ \-ssw
+.RI < SS\ pos >]
+.RB [ \-kssb
+.RI < kernel >]
+.RB [ \-kssr
+.RI < kernel >]
+.RB [ \-ssu ]
+.RB [ \-sso ]
+.RB [ \-ssnd ]
+.RB [ \-rn ]
+.RB [ \-gmc
+.RI < min\ gradmag >]
+.RB [ \-ofs ]
+.RB [ \-t
+.RI < type >]
+.RB [ \-o
+.RI < nout >]
+.SH DESCRIPTION
+.P
+Shows off the functionality of the gage library.
+Uses gageProbe() to query various kinds of volumes to learn various measured or
+derived quantities.
+Can set environment variable
+.B TEEM_VPROBE_HACK_ZI
+to limit probing to a single z slice.
+.SH OPTIONS
+.TP
+.B \-i\fR\ <\fInin\fR>
+input volume
+.TP
+.B \-k\fR\ <\fIkind\fR>
+\(lqkind\(rq of volume
+.RB (\(lq scalar \(rq,
+.RB \(lq vector \(rq,
+.RB \(lq tensor \(rq,
+or
+.RB \(lq dwi \(rq)
+.TP
+.B \-v\fR\ <\fIverbosity\fR>
+verbosity level
+.RI ( int );
+default:
+.RB \(lq 1 \(rq
+.TP
+.B \-q\fR\ <\fIquery\fR>
+the quantity
+.RB ( scalar ,
+.BR vector ,
+or
+.BR matrix )
+to learn by probing
+.RI ( string )
+.TP
+.B \-s\fR\ <\fIsclX\fR\ \fIsclY\fR\ \fIsxlZ\fR>
+scaling factor for resampling on each axis
+.RB (> 1.0 :
+supersampling); (3
+.IR double s);
+default:
+.RB \(lq 1.0\ 1.0\ 1.0 \(rq
+.TP
+.B \-k00\fR\ <\fIkern00\fR>
+kernel for
+.BR gageKernel00 ;
+default:
+.RB \(lq tent \(rq
+.TP
+.B \-k11\fR\ <\fIkern11\fR>
+kernel for
+.BR gageKernel11 ;
+default:
+.RB \(lq cubicd:1,0 \(rq
+.TP
+.B \-k22\fR\ <\fIkern22\fR>
+kernel for
+.BR gageKernel22 ;
+default:
+.RB \(lq cubicdd:1,0 \(rq
+.TP
+.B \-seed\fR\ <\fIN\fR>
+RNG seed; mostly for debugging
+.RI ( unsigned\ int );
+default:
+.RB \(lq 42 \(rq
+.TP
+.B \-ssn\fR\ <\fISS\ #\fR>
+how many scale-space samples to evaluate, or,
+.B 0
+to turn-off all scale-space behavior
+.RI ( unsigned\ int );
+default:
+.RB \(lq 0 \(rq
+.TP
+.B \-ssr\fR\ <\fIscale\ range\fR>
+range of scales in scale-space (2
+.IR double s);
+default:
+.RB \(lq nan\ nan \(rq
+.TP
+.B \-ssrf\fR\ <\fISS\ read\ format\fR>
+.BR printf (3)\-style
+format (including a
+.RB \(lq %u \(rq)
+for the filenames from which to
+.I read
+pre-blurred volumes computed for the stack
+.RI ( string );
+default: \(lq\(rq
+.TP
+.B \-sssf\fR\ <\fISS\ save\ format\fR>
+.BR printf (3)\-style
+format (including a
+.RB \(lq %u \(rq)
+for the filenames from which to
+.I save
+pre-blurred volumes computed for the stack
+.RI ( string );
+default: \(lq\(rq
+.TP
+.B \-ssw\fR\ <\fISS\ pos\fR>
+\(lqworld\(rq)-space position (true sigma) at which to sample in scale-space
+.RI ( double );
+default:
+.RB \(lq 0 \(rq
+.TP
+.B \-kssb\fR\ <\fIkernel\fR>
+blurring kernel, to sample scale space; default:
+.RB \(lq dgauss:1,5 \(rq
+.TP
+.B \-kssr\fR\ <\fIkernel\fR>
+kernel for reconstructing from scale space samples
+.RB \(lq hermite \(rq
+.TP
+.B \-ssu
+do uniform samples along sigma, and not (by default) samples according to the
+effective diffusion scale
+.TP
+.B \-sso
+if not using \(lq\-ssu\(rq, use pre-computed optimal sigmas when possible
+.TP
+.B \-ssnd
+normalize derivatives by scale
+.TP
+.B \-rn
+renormalize kernel weights at each new sample location.
+\(lqAccurate\(rq kernels don\(cqt need this; doing it always makes things go
+slower
+.TP
+.B \-gmc\fR\ <\fImin\ gradmag\fR>
+For curvature-based queries, use zero when gradient magnitude is below this
+.RI ( double );
+default:
+.RB \(lq 0.0 \(rq
+.TP
+.B \-ofs
+If only per-axis spacing is available, use that to contrive full orientation
+info
+.TP
+.B \-t\fR\ <\fItype\fR>
+type of output volume
+.RI ( type );
+default:
+.RB \(lq float \(rq
+.TP
+.B \-o\fR\ <\fInout\fR>
+output volume
+.RI ( string );
+default:
+.RB \(lq \- \(rq
+.SH ENVIRONMENT
+.TP
+.B TEEM_VPROBE_HACK_ZI
+If set, probing is limited to a single z slice.
+.SH "SEE\ ALSO"
+.BR gprobe (1),
+.BR ilk (1),
+.BR miter (1),
+.BR mrender (1),
+.BR nrrdSanity (1),
+.BR overrgb (1),
+.BR puller (1),
+.BR tend (1),
+.BR unu (1)
commit 593f4aa56a75b955f0a6b62a4a248954e702945d
Author: Benjamin A. Beasley <code(a)musicinmybrain.net>
Date: Sat Apr 3 12:41:10 2021 -0400
More precisely skip overly-strict floating-point tests where required
diff --git a/teem.spec b/teem.spec
index f97e08f..2ebe7a6 100644
--- a/teem.spec
+++ b/teem.spec
@@ -143,13 +143,15 @@ rm -rvf python
%check
-# tests fail on 32-bit arches
-# seems that computation accuracy is arch-dependant
-%global _smp_mflags -j1
-%ifarch x86_64
-%ctest3
+# We must exclude probeSS_ctmr04 and probeSS_ctmr10 on certain architectures
+# due to overly-strict rounding requirements.
+%ifarch %{arm64} ppc64le s390x
+%global ctest_excludes --exclude-regex '^(probeSS_ctmr(04|10))$'
%endif
+# Tests are not parallel-safe
+%ctest3 %{?ctest_excludes} -j1
+
%if 0%{?epel} == 7
%ldconfig_scriptlets libs
@@ -201,6 +203,7 @@ rm -rvf python
- Add an -examples subpackage so the BSD and zlib licenses can be “contained”
to that subpackage; remove certain unused files with other licenses in %%prep
- Require cmake-filesystem instead of all of CMake in -devel
+- More precisely skip overly-strict floating-point tests where required
* Wed Jan 27 2021 Fedora Release Engineering <releng(a)fedoraproject.org> - 1.11.0-14
- Rebuilt for https://fedoraproject.org/wiki/Fedora_34_Mass_Rebuild
commit 23e9f5a00538ba4ca386c4cf3cb6384c16caa08a
Author: Benjamin A. Beasley <code(a)musicinmybrain.net>
Date: Sat Apr 3 09:49:36 2021 -0400
Add explicit BR on make (implied by cmake/cmake3)
diff --git a/teem.spec b/teem.spec
index d1fa1e0..f97e08f 100644
--- a/teem.spec
+++ b/teem.spec
@@ -25,6 +25,7 @@ Patch0: lib_install_dir.patch
BuildRequires: gcc-c++
BuildRequires: cmake3
+BuildRequires: make
BuildRequires: zlib-devel
BuildRequires: libpng-devel
commit db87fd2490d946f17a6d9fc4b18e47d37bebb4b3
Author: Benjamin A. Beasley <code(a)musicinmybrain.net>
Date: Sat Apr 3 09:43:41 2021 -0400
Require cmake-filesystem instead of all of CMake in -devel
diff --git a/teem.spec b/teem.spec
index d4c3d79..d1fa1e0 100644
--- a/teem.spec
+++ b/teem.spec
@@ -92,7 +92,7 @@ applications that use Teem.
%package devel
Summary: Development files for %{name}
Requires: %{name}-libs%{?_isa} = %{version}-%{release}
-Requires: cmake%{?_isa}
+Requires: cmake-filesystem
%description devel %{common_description}
@@ -199,6 +199,7 @@ rm -rvf python
- Use stricter file globs, including specifying the so-version
- Add an -examples subpackage so the BSD and zlib licenses can be “contained”
to that subpackage; remove certain unused files with other licenses in %%prep
+- Require cmake-filesystem instead of all of CMake in -devel
* Wed Jan 27 2021 Fedora Release Engineering <releng(a)fedoraproject.org> - 1.11.0-14
- Rebuilt for https://fedoraproject.org/wiki/Fedora_34_Mass_Rebuild
commit e6982c7d45b51e73e424b3235560d9c3d6314908
Author: Benjamin A. Beasley <code(a)musicinmybrain.net>
Date: Sat Apr 3 09:42:16 2021 -0400
Add an -examples subpackage so the BSD and zlib licenses can be “contained” to that subpackage; remove certain unused files with other licenses in %%prep
diff --git a/teem.spec b/teem.spec
index b3f50d4..d4c3d79 100644
--- a/teem.spec
+++ b/teem.spec
@@ -4,7 +4,18 @@ Version: 1.11.0
Release: 15%{?dist}
Summary: Libraries for processing and visualizing scientific raster data
+# The entire source is LGPLv2+, except files noted at the License fields of certain subpackages.
License: LGPLv2+
+# Additionally, the following are removed in %%prep to assert that they do not
+# contribute to the built package:
+#
+# zlib:
+# arch/win32/include/zlib.h
+# MIT:
+# python/ctypes/Nrrd.py
+# python/ctypes/pullDemo.py
+# python/ctypes/teem-gen.py
+# python/ctypes/teem.py
URL: https://teem.sourceforge.net
Source0: https://downloads.sourceforge.net/project/teem/teem/%{version}/%{name}-%{...
# Patch CMakeLists.txt to fix library install paths to match what Fedora
@@ -89,9 +100,33 @@ The %{name}-devel package contains libraries and header files for developing
applications that use Teem.
+%package examples
+Summary: Examples for developing with for %{name}
+# The entire contents of this subpackage are LGPLv2+, except:
+#
+# zlib:
+# UseTeemCMakeDemo/sanity.c
+# BSD:
+# Examples/sanity/sanity.c
+License: LGPLv2+ and zlib and BSD
+BuildArch: noarch
+
+%description examples %{common_description}
+
+The %{name}-examples package contains examples for developing applications that
+use Teem.
+
+
%prep
%autosetup -n %{name}-%{version}-src -p1
+# Remove files that, while they have acceptable licenses, we want to assert do
+# not contribute to the built RPMs:
+# - Not applicable to this platform
+rm -rvf arch/win32
+# - Nothing here appears suitable for packaging:
+rm -rvf python
+
%build
%cmake3 \
@@ -141,12 +176,17 @@ applications that use Teem.
%files devel
-%doc Examples/sanity/
%{_includedir}/teem/
%{_libdir}/libteem.so
%{_libdir}/cmake/Teem-%{version}/
+%files examples
+%license LICENSE.txt
+%doc Examples
+%doc UseTeemCMakeDemo
+
+
%changelog
* Sat Apr 03 2021 Benjamin A. Beasley <code(a)musicinmybrain.net> - 1.11.0-15
- Fix typo in lib_install_dir.patch
@@ -156,6 +196,9 @@ applications that use Teem.
- Fix stray line continuation backslash
- Support EPEL7
- Build with “levmar” support enabled
+- Use stricter file globs, including specifying the so-version
+- Add an -examples subpackage so the BSD and zlib licenses can be “contained”
+ to that subpackage; remove certain unused files with other licenses in %%prep
* Wed Jan 27 2021 Fedora Release Engineering <releng(a)fedoraproject.org> - 1.11.0-14
- Rebuilt for https://fedoraproject.org/wiki/Fedora_34_Mass_Rebuild
commit f879b4ba5748261a4e94736d94a9aecf9e63a24b
Author: Benjamin A. Beasley <code(a)musicinmybrain.net>
Date: Sat Apr 3 09:22:50 2021 -0400
Use stricter file globs, including specifying the so-version
diff --git a/teem.spec b/teem.spec
index be5871f..b3f50d4 100644
--- a/teem.spec
+++ b/teem.spec
@@ -1,5 +1,6 @@
Name: teem
Version: 1.11.0
+%global so_version 1
Release: 15%{?dist}
Summary: Libraries for processing and visualizing scientific raster data
@@ -121,12 +122,22 @@ applications that use Teem.
%files
%doc README.txt
-%{_bindir}/*
+%{_bindir}/gprobe
+%{_bindir}/ilk
+%{_bindir}/miter
+%{_bindir}/mrender
+%{_bindir}/nrrdSanity
+%{_bindir}/overrgb
+%{_bindir}/puller
+%{_bindir}/tend
+%{_bindir}/unu
+%{_bindir}/vprobe
%files libs
%license LICENSE.txt
-%{_libdir}/libteem.so.*
+%{_libdir}/libteem.so.%{so_version}
+%{_libdir}/libteem.so.%{so_version}.*
%files devel
commit 8e9d83ec6f4083e2e472511cf71e6a4cb303f6f6
Author: Benjamin A. Beasley <code(a)musicinmybrain.net>
Date: Sat Apr 3 09:19:43 2021 -0400
Build with “levmar” support enabled
diff --git a/teem.spec b/teem.spec
index 5f11edb..be5871f 100644
--- a/teem.spec
+++ b/teem.spec
@@ -18,6 +18,7 @@ BuildRequires: zlib-devel
BuildRequires: libpng-devel
BuildRequires: bzip2-devel
BuildRequires: fftw-devel
+BuildRequires: levmar-devel
%global common_description %{expand:
What Is Teem?
@@ -95,7 +96,8 @@ applications that use Teem.
%cmake3 \
-DCMAKE_SKIP_INSTALL_RPATH=ON \
-DTeem_USE_LIB_INSTALL_SUBDIR=ON \
- -DTeem_FFTW3=ON
+ -DTeem_FFTW3=ON \
+ -DTeem_LEVMAR=ON
%cmake3_build
@@ -142,6 +144,7 @@ applications that use Teem.
- Adjust whitespace to personal preference
- Fix stray line continuation backslash
- Support EPEL7
+- Build with “levmar” support enabled
* Wed Jan 27 2021 Fedora Release Engineering <releng(a)fedoraproject.org> - 1.11.0-14
- Rebuilt for https://fedoraproject.org/wiki/Fedora_34_Mass_Rebuild
commit 3d52b904785970205228eea38b1f59eab1e5f7e8
Author: Benjamin A. Beasley <code(a)musicinmybrain.net>
Date: Sat Apr 3 09:18:27 2021 -0400
Rearrange BR’s, and drop gcc (implied by gcc-c++)
diff --git a/teem.spec b/teem.spec
index 31cd485..5f11edb 100644
--- a/teem.spec
+++ b/teem.spec
@@ -12,12 +12,12 @@ Source0: https://downloads.sourceforge.net/project/teem/teem/%{version}/%
Patch0: lib_install_dir.patch
BuildRequires: gcc-c++
-BuildRequires: gcc
BuildRequires: cmake3
+
+BuildRequires: zlib-devel
+BuildRequires: libpng-devel
BuildRequires: bzip2-devel
BuildRequires: fftw-devel
-BuildRequires: libpng-devel
-BuildRequires: zlib-devel
%global common_description %{expand:
What Is Teem?
commit b4222b2431c0b4975ed3ddbc8bf009cd7af182b3
Author: Benjamin A. Beasley <code(a)musicinmybrain.net>
Date: Sat Apr 3 09:15:50 2021 -0400
Support EPEL7
diff --git a/teem.spec b/teem.spec
index a6f7829..31cd485 100644
--- a/teem.spec
+++ b/teem.spec
@@ -13,7 +13,7 @@ Patch0: lib_install_dir.patch
BuildRequires: gcc-c++
BuildRequires: gcc
-BuildRequires: cmake
+BuildRequires: cmake3
BuildRequires: bzip2-devel
BuildRequires: fftw-devel
BuildRequires: libpng-devel
@@ -92,15 +92,15 @@ applications that use Teem.
%build
-%cmake \
+%cmake3 \
-DCMAKE_SKIP_INSTALL_RPATH=ON \
-DTeem_USE_LIB_INSTALL_SUBDIR=ON \
-DTeem_FFTW3=ON
-%cmake_build
+%cmake3_build
%install
-%cmake_install
+%cmake3_install
%check
@@ -108,11 +108,13 @@ applications that use Teem.
# seems that computation accuracy is arch-dependant
%global _smp_mflags -j1
%ifarch x86_64
-%ctest
+%ctest3
%endif
+%if 0%{?epel} == 7
%ldconfig_scriptlets libs
+%endif
%files
@@ -139,6 +141,7 @@ applications that use Teem.
- Improved package summaries and descriptions
- Adjust whitespace to personal preference
- Fix stray line continuation backslash
+- Support EPEL7
* Wed Jan 27 2021 Fedora Release Engineering <releng(a)fedoraproject.org> - 1.11.0-14
- Rebuilt for https://fedoraproject.org/wiki/Fedora_34_Mass_Rebuild
commit 59bc546adcb1c818b1406486ff585c265ec7d975
Author: Benjamin A. Beasley <code(a)musicinmybrain.net>
Date: Sat Apr 3 09:06:20 2021 -0400
Fix stray line continuation backslash
diff --git a/teem.spec b/teem.spec
index 96ea77c..a6f7829 100644
--- a/teem.spec
+++ b/teem.spec
@@ -95,7 +95,7 @@ applications that use Teem.
%cmake \
-DCMAKE_SKIP_INSTALL_RPATH=ON \
-DTeem_USE_LIB_INSTALL_SUBDIR=ON \
- -DTeem_FFTW3=ON \
+ -DTeem_FFTW3=ON
%cmake_build
@@ -138,6 +138,7 @@ applications that use Teem.
- Update URLs from HTTP to HTTPS
- Improved package summaries and descriptions
- Adjust whitespace to personal preference
+- Fix stray line continuation backslash
* Wed Jan 27 2021 Fedora Release Engineering <releng(a)fedoraproject.org> - 1.11.0-14
- Rebuilt for https://fedoraproject.org/wiki/Fedora_34_Mass_Rebuild
commit 1a749c45beb6a651404240b06296b5561d55e673
Author: Benjamin A. Beasley <code(a)musicinmybrain.net>
Date: Sat Apr 3 09:01:56 2021 -0400
Adjust whitespace to personal preference
diff --git a/teem.spec b/teem.spec
index 8f24e62..96ea77c 100644
--- a/teem.spec
+++ b/teem.spec
@@ -1,23 +1,23 @@
-Name: teem
-Version: 1.11.0
-Release: 15%{?dist}
-Summary: Libraries for processing and visualizing scientific raster data
-
-License: LGPLv2+
-URL: https://teem.sourceforge.net
-Source0: https://downloads.sourceforge.net/project/teem/teem/%{version}/%{name}-%{...
+Name: teem
+Version: 1.11.0
+Release: 15%{?dist}
+Summary: Libraries for processing and visualizing scientific raster data
+
+License: LGPLv2+
+URL: https://teem.sourceforge.net
+Source0: https://downloads.sourceforge.net/project/teem/teem/%{version}/%{name}-%{...
# Patch CMakeLists.txt to fix library install paths to match what Fedora
# expects. It is not immediately obvious if this patch is suitable for
# sending upstream or not.
-Patch0: lib_install_dir.patch
+Patch0: lib_install_dir.patch
BuildRequires: gcc-c++
-BuildRequires: gcc
-BuildRequires: cmake
-BuildRequires: bzip2-devel
-BuildRequires: fftw-devel
-BuildRequires: libpng-devel
-BuildRequires: zlib-devel
+BuildRequires: gcc
+BuildRequires: cmake
+BuildRequires: bzip2-devel
+BuildRequires: fftw-devel
+BuildRequires: libpng-devel
+BuildRequires: zlib-devel
%global common_description %{expand:
What Is Teem?
@@ -66,6 +66,7 @@ Strengths of Teem
The %{name} package contains the command-line tools included with Teem.
+
%package libs
Summary: Libraries files for %{name}
@@ -74,10 +75,11 @@ Summary: Libraries files for %{name}
The %{name}-libs package contains libraries that may be required at runtime by
applications that use Teem.
+
%package devel
Summary: Development files for %{name}
-Requires: %{name}-libs%{?_isa} = %{version}-%{release}
-Requires: cmake%{?_isa}
+Requires: %{name}-libs%{?_isa} = %{version}-%{release}
+Requires: cmake%{?_isa}
%description devel %{common_description}
@@ -100,6 +102,7 @@ applications that use Teem.
%install
%cmake_install
+
%check
# tests fail on 32-bit arches
# seems that computation accuracy is arch-dependant
@@ -108,16 +111,20 @@ applications that use Teem.
%ctest
%endif
+
%ldconfig_scriptlets libs
+
%files
%doc README.txt
%{_bindir}/*
+
%files libs
%license LICENSE.txt
%{_libdir}/libteem.so.*
+
%files devel
%doc Examples/sanity/
%{_includedir}/teem/
@@ -130,6 +137,7 @@ applications that use Teem.
- Fix typo in lib_install_dir.patch
- Update URLs from HTTP to HTTPS
- Improved package summaries and descriptions
+- Adjust whitespace to personal preference
* Wed Jan 27 2021 Fedora Release Engineering <releng(a)fedoraproject.org> - 1.11.0-14
- Rebuilt for https://fedoraproject.org/wiki/Fedora_34_Mass_Rebuild
commit 68faf7af85f2054b1476dc60655e208092e76cac
Author: Benjamin A. Beasley <code(a)musicinmybrain.net>
Date: Sat Apr 3 09:00:17 2021 -0400
Improved package summaries and descriptions
diff --git a/teem.spec b/teem.spec
index 04a9438..8f24e62 100644
--- a/teem.spec
+++ b/teem.spec
@@ -1,7 +1,7 @@
Name: teem
Version: 1.11.0
Release: 15%{?dist}
-Summary: Libraries for scientific raster data processing and visualizing
+Summary: Libraries for processing and visualizing scientific raster data
License: LGPLv2+
URL: https://teem.sourceforge.net
@@ -19,41 +19,70 @@ BuildRequires: fftw-devel
BuildRequires: libpng-devel
BuildRequires: zlib-devel
-%description
-Teem is a coordinated group of libraries for representing, processing,
-and visualizing scientific raster data. Teem includes command-line tools
-that permit the library functions to be quickly applied to files and streams,
-without having to write any code. The most important and useful libraries
-in Teem are:
-
-- Nrrd (and the unu command-line tool on top of it) supports a range
- of operations for transforming N-dimensional raster data (resample, crop,
- slice, project, histogram, etc.), as well as the NRRD file format
- for storing arrays and their meta-information.
-- Gage: fast convolution-based measurements at arbitrary point locations
- in volume datasets (scalar, vector, tensor, etc.)
-- Mite: a multi-threaded ray-casting volume render with transfer functions
- based on any quantity Gage can measure
-- Ten: for estimating, processing, and visualizing diffusion tensor fields,
- including fiber tractography methods.
-
+%global common_description %{expand:
+What Is Teem?
+
+Teem is a coordinated group of libraries for representing, processing, and
+visualizing scientific raster data. Teem includes command-line tools that
+permit the library functions to be quickly applied to files and streams,
+without having to write any code. The most important and useful libraries in
+Teem are:
+
+ • Nrrd (and the unu command-line tool on top of it) supports a range of
+ operations for transforming N-dimensional raster data (resample, crop,
+ slice, project, histogram, etc.), as well as the NRRD file format for
+ storing arrays and their meta-information.
+ • Gage: fast convolution-based measurements at arbitrary point locations in
+ volume datasets (scalar, vector, tensor, etc.)
+ • Mite: a multi-threaded ray-casting volume render with transfer functions
+ based on any quantity Gage can measure
+ • Ten: for estimating, processing, and visualizing diffusion tensor fields,
+ including fiber tractography methods.
+
+Strengths of Teem
+
+ • Teem works: Its purpose is to enable research in visualization and image
+ processing, and research is enabled when simple things are simple to do.
+ Teem’s functionality and its ease of use have allowed it to become a
+ component of larger research software projects, such as SCIRun and 3D
+ Slicer.
+ • Teem is light-weight: The libraries are written with an eye towards
+ minimizing the annoyance of getting data in an out, by using the simplest
+ possible constructs for the job, and by supporting combinations of operations
+ that arise in common practice.
+ • Teem is coherent: There is a consistent logic to how information is
+ represented, and uniformity in the APIs across libraries.
+ • Teem is portable: All the code is written in plain ANSI C, so it compiles
+ everywhere, including Windows, using either CMake or GNU Make. A Dashboard
+ is used to monitor compiler errors and warnings.
+ • Teem is growing: Some Teem libraries were created years ago and have
+ remained stable, but new libraries and new functionality are continually
+ being added.
+ • Teem is open source: Anyone can use it, and contributions are welcome. Teem
+ is licensed under the GNU Lesser General Public License, plus exceptions
+ which facilitate linking Teem into binary-only applications.}
+
+%description %{common_description}
+
+The %{name} package contains the command-line tools included with Teem.
%package libs
-Summary: Libraries for scientific raster data processing and visualizing
+Summary: Libraries files for %{name}
+
+%description libs %{common_description}
-%description libs
-Teem is a coordinated group of libraries for representing, processing,
-and visualizing scientific raster data.
+The %{name}-libs package contains libraries that may be required at runtime by
+applications that use Teem.
%package devel
-Summary: Libraries for scientific raster data processing and visualizing
+Summary: Development files for %{name}
Requires: %{name}-libs%{?_isa} = %{version}-%{release}
Requires: cmake%{?_isa}
-%description devel
-Teem is a coordinated group of libraries for representing, processing,
-and visualizing scientific raster data. This package contains development
-files.
+%description devel %{common_description}
+
+The %{name}-devel package contains libraries and header files for developing
+applications that use Teem.
%prep
@@ -100,6 +129,7 @@ files.
* Sat Apr 03 2021 Benjamin A. Beasley <code(a)musicinmybrain.net> - 1.11.0-15
- Fix typo in lib_install_dir.patch
- Update URLs from HTTP to HTTPS
+- Improved package summaries and descriptions
* Wed Jan 27 2021 Fedora Release Engineering <releng(a)fedoraproject.org> - 1.11.0-14
- Rebuilt for https://fedoraproject.org/wiki/Fedora_34_Mass_Rebuild
commit 45277db33360db4931556c3c678c8df5a42d11f9
Author: Benjamin A. Beasley <code(a)musicinmybrain.net>
Date: Sat Apr 3 08:50:07 2021 -0400
Update hash in sources file from MD5 to SHA-512
diff --git a/sources b/sources
index f09a911..4a5f2e5 100644
--- a/sources
+++ b/sources
@@ -1 +1 @@
-6b9737e8b7640e18eaf281e830fe59d1 teem-1.11.0-src.tar.gz
+SHA512 (teem-1.11.0-src.tar.gz) = 48b171a12db0f02dcfdaa87aa84464c651d661fa66201dc966b3cd5a8134c5bad1dad8987ffcc5d7c21c5d14c2eb617d48200410a1bda19008ef743c093ed575
commit ea786f8048639405a91a5ebb7ef9ed932b514674
Author: Benjamin A. Beasley <code(a)musicinmybrain.net>
Date: Sat Apr 3 08:48:37 2021 -0400
Update URLs from HTTP to HTTPS
diff --git a/teem.spec b/teem.spec
index 272c4b9..04a9438 100644
--- a/teem.spec
+++ b/teem.spec
@@ -4,8 +4,8 @@ Release: 15%{?dist}
Summary: Libraries for scientific raster data processing and visualizing
License: LGPLv2+
-URL: http://teem.sourceforge.net
-Source0: http://downloads.sourceforge.net/project/teem/teem/%{version}/%{name}-%{v...
+URL: https://teem.sourceforge.net
+Source0: https://downloads.sourceforge.net/project/teem/teem/%{version}/%{name}-%{...
# Patch CMakeLists.txt to fix library install paths to match what Fedora
# expects. It is not immediately obvious if this patch is suitable for
# sending upstream or not.
@@ -99,6 +99,7 @@ files.
%changelog
* Sat Apr 03 2021 Benjamin A. Beasley <code(a)musicinmybrain.net> - 1.11.0-15
- Fix typo in lib_install_dir.patch
+- Update URLs from HTTP to HTTPS
* Wed Jan 27 2021 Fedora Release Engineering <releng(a)fedoraproject.org> - 1.11.0-14
- Rebuilt for https://fedoraproject.org/wiki/Fedora_34_Mass_Rebuild
commit 5e008cd008b4422253f4aa2edc846e49efcb41cb
Author: Benjamin A. Beasley <code(a)musicinmybrain.net>
Date: Sat Apr 3 08:48:27 2021 -0400
Add a comment about lib_install_dir.patch
diff --git a/teem.spec b/teem.spec
index 6c490a4..272c4b9 100644
--- a/teem.spec
+++ b/teem.spec
@@ -6,6 +6,9 @@ Summary: Libraries for scientific raster data processing and visualizing
License: LGPLv2+
URL: http://teem.sourceforge.net
Source0: http://downloads.sourceforge.net/project/teem/teem/%{version}/%{name}-%{v...
+# Patch CMakeLists.txt to fix library install paths to match what Fedora
+# expects. It is not immediately obvious if this patch is suitable for
+# sending upstream or not.
Patch0: lib_install_dir.patch
BuildRequires: gcc-c++
commit 427091f9585922ee66a43470d65ea1a17726f334
Author: Benjamin A. Beasley <code(a)musicinmybrain.net>
Date: Sat Apr 3 08:35:58 2021 -0400
Fix typo in lib_install_dir.patch
diff --git a/lib_install_dir.patch b/lib_install_dir.patch
index ba4a5be..c39fed0 100644
--- a/lib_install_dir.patch
+++ b/lib_install_dir.patch
@@ -6,7 +6,7 @@ Index: teem-1.11.0-src/CMakeLists.txt
# RPATH stuff, and mac will ingore the INSTALL_RPATH. Make sure for
# macs to set BUILD_WITH_INSTALL_RPATH OFF and set INSTALL_NAME_DIR.
# I belive INSTALL_NAME_DIR will be ignored on linux.
-+SET(LIB_INSTALL_DIR ${CMAKE_INSTALL_PREFIX}/lib CACHE PATH "Lirary installation directory.")
++SET(LIB_INSTALL_DIR ${CMAKE_INSTALL_PREFIX}/lib CACHE PATH "Library installation directory.")
SET_TARGET_PROPERTIES(teem PROPERTIES
BUILD_WITH_INSTALL_RPATH OFF
- INSTALL_RPATH ${CMAKE_INSTALL_PREFIX}/lib
diff --git a/teem.spec b/teem.spec
index 77f8aa7..6c490a4 100644
--- a/teem.spec
+++ b/teem.spec
@@ -1,6 +1,6 @@
Name: teem
Version: 1.11.0
-Release: 14%{?dist}
+Release: 15%{?dist}
Summary: Libraries for scientific raster data processing and visualizing
License: LGPLv2+
@@ -94,6 +94,9 @@ files.
%changelog
+* Sat Apr 03 2021 Benjamin A. Beasley <code(a)musicinmybrain.net> - 1.11.0-15
+- Fix typo in lib_install_dir.patch
+
* Wed Jan 27 2021 Fedora Release Engineering <releng(a)fedoraproject.org> - 1.11.0-14
- Rebuilt for https://fedoraproject.org/wiki/Fedora_34_Mass_Rebuild
2 years, 9 months
[Report] Packages Restricting Arches
by root
List of packages currently excluding arches (2240)
===========================================
- 0ad
ExclusiveArch: %{ix86} x86_64 %{arm} aarch64 ppc64le
- 90-Second-Portraits
ExclusiveArch: %{arm} %{ix86} x86_64 aarch64 ppc64le
- GoldenCheetah
ExclusiveArch: %{qt5_qtwebengine_arches}
- GtkAda
ExclusiveArch: %{GPRbuild_arches}
- GtkAda3
ExclusiveArch: %{GPRbuild_arches}
- OpenImageIO
ExclusiveArch: x86_64 ppc64le
- OpenMolcas
ExclusiveArch: x86_64 aarch64 ppc64le s390x
- PragmARC
ExclusiveArch: %{GPRbuild_arches}
- R-V8
ExclusiveArch: %{nodejs_arches}
- RdRand
ExclusiveArch: %{ix86} x86_64
- RediSearch
ExclusiveArch: x86_64
- SLOF
ExclusiveArch: ppc64le
- YafaRay
ExclusiveArch: %{ix86} x86_64
- aboot
ExclusiveArch: alpha
- accel-config
ExclusiveArch: %{ix86} x86_64
- acpid
ExclusiveArch: ia64 x86_64 %{ix86} %{arm} aarch64
- ahven
ExclusiveArch: %{GPRbuild_arches}
- algobox
ExclusiveArch: %{qt5_qtwebengine_arches}
- alleyoop
ExclusiveArch: %{ix86} x86_64 ppc ppc64 ppc64le s390x %{arm} aarch64
- american-fuzzy-lop
ExclusiveArch: %{ix86} x86_64
- anet
ExclusiveArch: %{GPRbuild_arches}
- anki
ExclusiveArch: %{qt5_qtwebengine_arches} noarch
- apmd
ExclusiveArch: %{ix86}
- appstream-generator
ExclusiveArch: %{ldc_arches}
- arm-trusted-firmware
ExclusiveArch: aarch64
- atomic-queue
ExclusiveArch: x86_64 %{ix86} %{arm32} %{arm64}
- aunit
ExclusiveArch: %GPRbuild_arches
- avgtime
ExclusiveArch: %{ldc_arches}
- aws
ExclusiveArch: %GPRbuild_arches
- bcal
ExclusiveArch: x86_64 aarch64 ia64 ppc64 ppc64le s390x
- bcc
ExclusiveArch: x86_64 %{power64} aarch64 s390x armv7hl
- bcm283x-firmware
ExclusiveArch: %{arm} aarch64
- berusky2
ExclusiveArch: %{ix86} x86_64 %{arm} aarch64 %{mips}
- biosdevname
ExclusiveArch: %{ix86} x86_64
- blender
ExclusiveArch: x86_64 aarch64 ppc64le
- bless
ExclusiveArch: %mono_arches
- bpftrace
ExclusiveArch: x86_64 %{power64} aarch64 s390x
- calamares
ExclusiveArch: %{ix86} x86_64
- calibre
ExclusiveArch: %{qt5_qtwebengine_arches}
- ccdciel
ExclusiveArch: %{fpc_arches}
- cdcollect
ExclusiveArch: %{mono_arches}
- ceph
ExclusiveArch: x86_64 aarch64 ppc64le s390x
- chromium
ExclusiveArch: x86_64 i686
ExclusiveArch: x86_64 i686 aarch64
- cjdns
ExclusiveArch: %{nodejs_arches}
- clevis-pin-tpm2
ExclusiveArch: %{rust_arches}
- cmospwd
ExclusiveArch: %{ix86} x86_64
- cmrt
ExclusiveArch: %{ix86} x86_64 ia64
- coffee-script
ExclusiveArch: %{nodejs_arches} noarch
- colorful
ExclusiveArch: %{fpc_arches}
- cpu-x
ExclusiveArch: i686 x86_64
- cpufetch
ExclusiveArch: %{arm} aarch64 x86_64 ppc ppc64 ppc64le
- cpuid
ExclusiveArch: %{ix86} x86_64
- cqrlog
ExclusiveArch: %{fpc_arches}
- crash
ExclusiveArch: %{ix86} ia64 x86_64 ppc ppc64 s390 s390x %{arm} aarch64 ppc64le
- crash-gcore-command
ExclusiveArch: aarch64 ppc64le x86_64
- crash-trace-command
ExclusiveArch: aarch64 ppc64le s390x x86_64
- criu
ExclusiveArch: x86_64 %{arm} ppc64le aarch64 s390x
- cryptlib
ExclusiveArch: x86_64 %{ix86} aarch64 ppc64 ppc64le
- cryptobone
ExclusiveArch: x86_64 %{ix86} ppc64 ppc64le aarch64
- daq
ExclusiveArch: x86_64 aarch64
- darktable
ExclusiveArch: x86_64 aarch64
ExclusiveArch: x86_64 ppc64le
- dbus-parsec
ExclusiveArch: %{rust_arches}
- dbus-sharp
ExclusiveArch: %mono_arches
- dbus-sharp-glib
ExclusiveArch: %mono_arches
- dbxtool
ExclusiveArch: i386 x86_64 aarch64
- deepin-daemon
ExclusiveArch: %{?go_arches:%{go_arches}}%{!?go_arches:%{ix86} x86_64 aarch64 %{arm}}
- deepin-desktop-schemas
ExclusiveArch: %{go_arches}
- dlm
ExclusiveArch: i686 x86_64
- dmidecode
ExclusiveArch: %{ix86} x86_64 ia64 aarch64
- dmtcp
ExclusiveArch: x86_64 aarch64
- docker-distribution
ExclusiveArch: %{?go_arches:%{go_arches}}%{!?go_arches:%{ix86} x86_64 %{arm}}
- dolphin-emu
ExclusiveArch: x86_64 aarch64
- dotnet-build-reference-packages
ExclusiveArch: aarch64 x86_64
ExclusiveArch: x86_64
- dotnet3.1
ExclusiveArch: aarch64 x86_64
ExclusiveArch: x86_64
- dotnet5.0
ExclusiveArch: aarch64 x86_64
ExclusiveArch: x86_64
- dotnet5.0-build-reference-packages
ExclusiveArch: aarch64 x86_64
ExclusiveArch: x86_64
- doublecmd
ExclusiveArch: %{ix86} x86_64
- dpdk
ExclusiveArch: x86_64 i686 aarch64 ppc64le
- dyninst
ExclusiveArch: %{ix86} x86_64 ppc64le aarch64
- e3
ExclusiveArch: %{ix86} x86_64
- edk2
ExclusiveArch: %{ix86} x86_64 %{arm} aarch64
ExclusiveArch: x86_64 aarch64
- efibootmgr
ExclusiveArch: %{efi}
- efifs
ExclusiveArch: %{efi}
- efitools
ExclusiveArch: %{efi}
- efivar
ExclusiveArch: %{efi}
- elk
ExclusiveArch: x86_64 %{ix86}
ExclusiveArch: x86_64 %{ix86} aarch64 %{arm} %{power64}
- emacs-slime
ExclusiveArch: %{arm} %{ix86} x86_64 ppc sparcv9 aarch64
- enki
ExclusiveArch: %{qt5_qtwebengine_arches} noarch
- envytools
ExclusiveArch: %{ix86} x86_64 %{arm} aarch64
- eric
ExclusiveArch: %{qt5_qtwebengine_arches} noarch
- eth-tools
ExclusiveArch: x86_64
- extlinux-bootloader
ExclusiveArch: %{arm} aarch64
- fcitx-libpinyin
ExclusiveArch: %{qt5_qtwebengine_arches}
- fedora-dockerfiles
ExclusiveArch: %{go_arches}
- fes
ExclusiveArch: %{ix86} x86_64
- fkill-cli
ExclusiveArch: %{nodejs_arches} noarch
- florist
ExclusiveArch: %{GPRbuild_arches}
- fpc
ExclusiveArch: %{arm} aarch64 %{ix86} x86_64 ppc64le
- frescobaldi
ExclusiveArch: %{qt5_qtwebengine_arches}
- frysk
ExclusiveArch: %{ix86} x86_64 ppc64
- fwts
ExclusiveArch: x86_64 %{arm} aarch64 s390x riscv64 %{power64}
- fwupd-efi
ExclusiveArch: x86_64 aarch64
- fx
ExclusiveArch: %{nodejs_arches} noarch
- fx-completion
ExclusiveArch: %{nodejs_arches} noarch
- ga
ExclusiveArch: %{ix86} x86_64 %{arm} aarch64 ppc64le
- gbrainy
ExclusiveArch: %mono_arches
- gdata-sharp
ExclusiveArch: %mono_arches
- gdb-exploitable
ExclusiveArch: x86_64 i386
ExclusiveArch: x86_64 noarch
- gearhead1
ExclusiveArch: %{fpc_arches}
- gela-asis
ExclusiveArch: %GPRbuild_arches
- ghdl
ExclusiveArch: %{GNAT_arches}
- ghostwriter
ExclusiveArch: %{qt5_qtwebengine_arches}
- gio-sharp
ExclusiveArch: %mono_arches
- gir-to-d
ExclusiveArch: %{ldc_arches}
- git-octopus
ExclusiveArch: %{?go_arches:%{go_arches}}%{!?go_arches:%{ix86} x86_64 aarch64 %{arm}}
- gitqlient
ExclusiveArch: %{qt5_qtwebengine_arches}
- giver
ExclusiveArch: %{mono_arches}
- gkeyfile-sharp
ExclusiveArch: %mono_arches
- glibc32
ExclusiveArch: x86_64
- glibd
ExclusiveArch: %{ldc_arches}
- gmqcc
ExclusiveArch: %{ix86} x86_64 %{arm} aarch64
- gnatcoll
ExclusiveArch: %{GPRbuild_arches}
- gnatcoll-bindings
ExclusiveArch: %{GPRbuild_arches}
- gnatcoll-db
ExclusiveArch: %{GPRbuild_arches}
- gnome-boxes
ExclusiveArch: x86_64
- gnome-desktop-sharp
ExclusiveArch: %mono_arches
- gnome-do
ExclusiveArch: %mono_arches
- gnome-keyring-sharp
ExclusiveArch: %mono_arches
- gnome-rdp
ExclusiveArch: %{mono_arches}
- gnome-sharp
ExclusiveArch: %mono_arches
- gnome-subtitles
ExclusiveArch: %mono_arches
- gnu-efi
ExclusiveArch: %{efi}
- go-bindata
ExclusiveArch: %{?go_arches:%{go_arches}}%{!?go_arches:%{ix86} x86_64 %{arm}}
- go-compilers
ExclusiveArch: %{go_arches}
- go-rpm-macros
ExclusiveArch: %{golang_arches} %{gccgo_arches}
- godep
ExclusiveArch: %{?go_arches:%{go_arches}}%{!?go_arches:%{ix86} x86_64 %{arm}}
- golang
ExclusiveArch: %{golang_arches}
- gomtree
ExclusiveArch: %{?go_arches:%{go_arches}}%{!?go_arches:%{ix86} x86_64 %{arm}}
- gotun
ExclusiveArch: %{?go_arches:%{go_arches}}%{!?go_arches:%{ix86} x86_64 %{arm}}
ExclusiveArch: x86_64
- goverlay
ExclusiveArch: %{fpc_arches}
- gprbuild
ExclusiveArch: %{GPRbuild_arches} %{bootstrap_arch}
- gprolog
ExclusiveArch: x86_64 %{ix86} ppc alpha
- grafana
ExclusiveArch: %{grafana_arches}
- grafana-pcp
ExclusiveArch: %{grafanapcp_arches}
- gtk-sharp-beans
ExclusiveArch: %mono_arches
- gtk-sharp2
ExclusiveArch: %mono_arches
- gtk-sharp3
ExclusiveArch: %{mono_arches}
- gtkd
ExclusiveArch: %{ldc_arches}
- gudev-sharp
ExclusiveArch: %mono_arches
- guestfs-tools
ExclusiveArch: %{kernel_arches}
- hedgewars
ExclusiveArch: %{fpc_arches}
- hsakmt
ExclusiveArch: x86_64 aarch64
- hyena
ExclusiveArch: %{mono_arches}
- hyperscan
ExclusiveArch: x86_64
- hyperv-daemons
ExclusiveArch: i686 x86_64
- i3status-rs
ExclusiveArch: %{rust_arches}
- icaro
ExclusiveArch: %{ix86} x86_64 noarch
- imvirt
ExclusiveArch: %{ix86} x86_64 ia64
- indistarter
ExclusiveArch: %{fpc_arches}
- infinipath-psm
ExclusiveArch: x86_64
- intel-cmt-cat
ExclusiveArch: x86_64 i686 i586
ExclusiveArch: x86_64 i686 i586
- intel-gmmlib
ExclusiveArch: x86_64 i686
- intel-ipp-crypto-mb
ExclusiveArch: x86_64
ExclusiveArch: x86_64
- intel-ipsec-mb
ExclusiveArch: x86_64
ExclusiveArch: x86_64
- intel-mediasdk
ExclusiveArch: x86_64
- intel-undervolt
ExclusiveArch: i386 x86_64
- ioport
ExclusiveArch: %{ix86} x86_64
- ipmctl
ExclusiveArch: x86_64
- ipw2100-firmware
ExclusiveArch: noarch i386 x86_64
- ipw2200-firmware
ExclusiveArch: noarch i386 x86_64
- ispc
ExclusiveArch: x86_64 aarch64
- iucode-tool
ExclusiveArch: %{ix86} x86_64
- iyfct
ExclusiveArch: %{arm} %{ix86} x86_64 aarch64 ppc64le
- java-1.8.0-openjdk-aarch32
ExclusiveArch: %{arm}
- jigawatts
ExclusiveArch: x86_64 %{arm} ppc64le aarch64 s390x
- kchmviewer
ExclusiveArch: %{qt5_qtwebengine_arches}
- keepass
ExclusiveArch: %{mono_arches}
- kernel
ExclusiveArch: x86_64 s390x %{arm} aarch64 ppc64le
ExclusiveArch: noarch i386 i686 x86_64 s390x %{arm} aarch64 ppc64le
- keyring-ima-signer
ExclusiveArch: %{rust_arches}
- kf5-akonadi-search
ExclusiveArch: x86_64 ppc64le aarch64 %{arm}
- kf5-audiocd-kio
ExclusiveArch: x86_64 ppc64le aarch64 %{arm}
- kf5-kalarmcal
ExclusiveArch: x86_64 ppc64le aarch64 %{arm}
- kf5-kblog
ExclusiveArch: x86_64 ppc64le aarch64 %{arm}
- kf5-kcalendarcore
ExclusiveArch: x86_64 ppc64le aarch64 %{arm}
- kf5-kcalendarutils
ExclusiveArch: x86_64 ppc64le aarch64 %{arm}
- kf5-kitinerary
ExclusiveArch: x86_64 ppc64le aarch64 %{arm}
- kf5-ktnef
ExclusiveArch: x86_64 ppc64le aarch64 %{arm}
- kf5-libkdcraw
ExclusiveArch: x86_64 ppc64le %{arm}
- kicad
ExclusiveArch: %{ix86} x86_64 %{arm} aarch64 ppc64le
- kiwix-desktop
ExclusiveArch: %{qt5_qtwebengine_arches}
- knot-resolver
ExclusiveArch: %{ix86} x86_64
ExclusiveArch: %{arm} aarch64 %{ix86} x86_64
- knotes
ExclusiveArch: x86_64 %{arm}
- kompose
ExclusiveArch: %{ix86} x86_64 %{arm} aarch64 s390x
- kubernetes
ExclusiveArch: x86_64 aarch64 ppc64le s390x %{arm}
- lazarus
ExclusiveArch: %{fpc_arches}
- lazpaint
ExclusiveArch: %{fpc_arches}
- ldc
ExclusiveArch: %{ldc_arches}
- libbsr
ExclusiveArch: %{power64}
- libclc
ExclusiveArch: %{ix86} x86_64 %{arm} aarch64 %{power64} s390x
- libcxl
ExclusiveArch: %{power64}
- libdfp
ExclusiveArch: ppc ppc64 ppc64le s390 s390x x86_64
- libdispatch
ExclusiveArch: x86_64 aarch64
- libguestfs
ExclusiveArch: %{kernel_arches}
- libica
ExclusiveArch: s390 s390x
- libipt
ExclusiveArch: %{ix86} x86_64
ExclusiveArch: %{ix86} x86_64
- libnxz
ExclusiveArch: ppc64le
- libocxl
ExclusiveArch: ppc64le
- libpmemobj-cpp
ExclusiveArch: x86_64 ppc64le
- libpsm2
ExclusiveArch: x86_64
- libquentier
ExclusiveArch: %{qt5_qtwebengine_arches}
- libretro-desmume2015
ExclusiveArch: i686 x86_64
- librtas
ExclusiveArch: %{power64}
- libservicelog
ExclusiveArch: ppc %{power64}
- libsmbios
ExclusiveArch: x86_64 %{ix86}
- libunwind
ExclusiveArch: %{arm} aarch64 hppa ia64 mips ppc %{power64} s390x %{ix86} x86_64
- libva-intel-hybrid-driver
ExclusiveArch: %{ix86} x86_64 ia64
- libvma
ExclusiveArch: x86_64 ppc64le ppc64 aarch64
- libvmi
ExclusiveArch: x86_64
- libvpd
ExclusiveArch: %{power64}
- libxsmm
ExclusiveArch: x86_64
- libzfcphbaapi
ExclusiveArch: s390 s390x
- lightdm
ExclusiveArch: x86_64 ppc64le
- log4net
ExclusiveArch: %mono_arches
- lrmi
ExclusiveArch: %{ix86}
- lsvpd
ExclusiveArch: %{power64}
- luajit
ExclusiveArch: %{arm} %{ix86} x86_64 %{mips} aarch64 s390x ppc64le
- luxcorerender
ExclusiveArch: x86_64
- mactel-boot
ExclusiveArch: x86_64
- manifest-tool
ExclusiveArch: x86_64 aarch64 ppc64le s390x
- marked
ExclusiveArch: %{nodejs_arches} noarch
ExclusiveArch: %{ix86} x86_64 %{arm} noarch
- matreshka
ExclusiveArch: %GPRbuild_arches
- maxima
ExclusiveArch: %{arm} %{ix86} x86_64 aarch64 ppc sparcv9
ExclusiveArch: %{ix86} x86_64 ppc sparcv9
- mbpfan
ExclusiveArch: x86_64
- mcelog
ExclusiveArch: i686 x86_64
- mdevctl
ExclusiveArch: %{rust_arches}
- mediaconch
ExclusiveArch: %{qt5_qtwebengine_arches}
- mellowplayer
ExclusiveArch: %{qt5_qtwebengine_arches}
- memkind
ExclusiveArch: x86_64 ppc64 ppc64le s390x aarch64
- memtest86+
ExclusiveArch: %{ix86} x86_64
- mesos
ExclusiveArch: x86_64
- microcode_ctl
ExclusiveArch: %{ix86} x86_64
- micropython
ExclusiveArch: %{arm} %{ix86} x86_64
- mine_detector
ExclusiveArch: %{GPRbuild_arches}
- minetest
ExclusiveArch: %{ix86} x86_64
ExclusiveArch: %{arm} %{ix86} x86_64 %{mips} aarch64
- mingw-libidn2
ExclusiveArch: %{ix86} x86_64 %{arm}
- mingw-wine-gecko
ExclusiveArch: %{ix86} x86_64 %{arm} aarch64
- mirrorlist-server
ExclusiveArch: %{rust_arches}
- mkbootdisk
ExclusiveArch: %{ix86} sparc sparc64 x86_64
- mod_mono
ExclusiveArch: %mono_arches
- module-build-service
ExclusiveArch: %{ix86} x86_64 noarch
- mokutil
ExclusiveArch: %{ix86} x86_64 aarch64 %{arm}
- mono
ExclusiveArch: %mono_arches
- mono-addins
ExclusiveArch: %mono_arches
- mono-basic
ExclusiveArch: %{mono_arches}
- mono-bouncycastle
ExclusiveArch: %mono_arches
- mono-cecil
ExclusiveArch: %mono_arches
- mono-cecil-flowanalysis
ExclusiveArch: %mono_arches
- mono-reflection
ExclusiveArch: %mono_arches
- mono-tools
ExclusiveArch: %mono_arches
- mono-zeroconf
ExclusiveArch: %mono_arches
- monobristol
ExclusiveArch: %{mono_arches}
- monodevelop
ExclusiveArch: %mono_arches
- monodevelop-debugger-gdb
ExclusiveArch: %{mono_arches}
- monosim
ExclusiveArch: %mono_arches
- mrrescue
ExclusiveArch: %{arm} %{ix86} x86_64 aarch64 ppc64le
- msr-tools
ExclusiveArch: %{ix86} x86_64
- mustache-d
ExclusiveArch: %{ldc_arches}
- mysql-connector-net
ExclusiveArch: %{mono_arches}
- nant
ExclusiveArch: %mono_arches
- nativejit
ExclusiveArch: x86_64
- nbc
ExclusiveArch: %{fpc_arches}
- nbdkit
ExclusiveArch: x86_64
- ndesk-dbus
ExclusiveArch: %{mono_arches}
- ndesk-dbus-glib
ExclusiveArch: %{mono_arches}
- newsflash
ExclusiveArch: %{rust_arches}
- newtonsoft-json
ExclusiveArch: %{mono_arches}
- nispor
ExclusiveArch: %{rust_arches}
- nodejs
ExclusiveArch: %{nodejs_arches}
- nodejs-acorn-object-spread
ExclusiveArch: %{nodejs_arches} noarch
ExclusiveArch: %{ix86} x86_64 %{arm} noarch
- nodejs-backbone
ExclusiveArch: %{nodejs_arches} noarch
- nodejs-bash-language-server
ExclusiveArch: %{nodejs_arches} noarch
- nodejs-buble
ExclusiveArch: %{nodejs_arches} noarch
- nodejs-colors
ExclusiveArch: %{nodejs_arches} noarch
- nodejs-generic-pool
ExclusiveArch: %{nodejs_arches} noarch
- nodejs-linefix
ExclusiveArch: %{nodejs_arches} noarch
ExclusiveArch: %{ix86} x86_64 %{arm} noarch
- nodejs-nodemon
ExclusiveArch: %{nodejs_arches} noarch
- nodejs-packaging
ExclusiveArch: %{nodejs_arches} noarch
- nodejs-replace-require-self
ExclusiveArch: %{nodejs_arches} noarch
- nodejs-supervisor
ExclusiveArch: %{nodejs_arches} noarch
- nodejs-svgo
ExclusiveArch: %{nodejs_arches} noarch
- nodejs-tape
ExclusiveArch: %{nodejs_arches} noarch
- nodejs-typescript
ExclusiveArch: %{nodejs_arches} noarch
- nodejs-underscore
ExclusiveArch: %{nodejs_arches} noarch
ExclusiveArch: %{ix86} x86_64 %{arm} noarch
- notify-sharp
ExclusiveArch: %{mono_arches}
- notify-sharp3
ExclusiveArch: %{mono_arches}
- npm-name-cli
ExclusiveArch: %{nodejs_arches} noarch
- nuget
ExclusiveArch: %{mono_arches}
- numatop
ExclusiveArch: x86_64 ppc64le
- nunit
ExclusiveArch: %{mono_arches}
- nunit2
ExclusiveArch: %{mono_arches}
- nvml
ExclusiveArch: x86_64 ppc64le
- nwchem
ExclusiveArch: %{ix86} x86_64 %{arm} aarch64 ppc64le
- obs-service-rust2rpm
ExclusiveArch: %{rust_arches} noarch
- oci-kvm-hook
ExclusiveArch: %{?go_arches:%{go_arches}}%{!?go_arches:%{ix86} x86_64 aarch64 %{arm}}
- oci-seccomp-bpf-hook
ExclusiveArch: x86_64 %%{power64} aarch64 s390x armv7hl
- oci-umount
ExclusiveArch: %{ix86} x86_64 %{arm} aarch64 ppc64le s390x %{mips}
- oidn
ExclusiveArch: x86_64
- olpc-kbdshim
ExclusiveArch: %{ix86} %{arm}
- olpc-netutils
ExclusiveArch: %{ix86} %{arm}
- olpc-utils
ExclusiveArch: %{ix86} %{arm}
- onednn
ExclusiveArch: x86_64 aarch64 ppc64le s390x
- onedrive
ExclusiveArch: %{ldc_arches}
- opae
ExclusiveArch: x86_64
- opal-prd
ExclusiveArch: ppc64le
- open-vm-tools
ExclusiveArch: x86_64
ExclusiveArch: %{ix86} x86_64 aarch64
- openblas
ExclusiveArch: %{openblas_arches}
- openjfx
ExclusiveArch: x86_64
- openjfx8
ExclusiveArch: x86_64
- openlibm
ExclusiveArch: %{arm} %{ix86} x86_64 aarch64 %{power64}
- openni
ExclusiveArch: %{ix86} x86_64 %{arm}
- openni-primesense
ExclusiveArch: %{ix86} x86_64 %{arm}
- openshadinglanguage
ExclusiveArch: x86_64 aarch64 ppc64le s390x
- openssl-ibmca
ExclusiveArch: s390 s390x
- origin
ExclusiveArch: %{go_arches}
ExclusiveArch: x86_64 aarch64 ppc64le s390x
- orion
ExclusiveArch: %{qt5_qtwebengine_arches}
- orthorobot
ExclusiveArch: %{arm} %{ix86} x86_64 aarch64 ppc64le
- pacemaker
ExclusiveArch: aarch64 i686 ppc64le s390x x86_64 %{arm}
ExclusiveArch: aarch64 i686 ppc64le s390x x86_64
- paflib
ExclusiveArch: ppc %{power64}
- parsec
ExclusiveArch: %{rust_arches}
- parsec-tool
ExclusiveArch: %{rust_arches}
- pcc
ExclusiveArch: %{ix86} x86_64
- pcm
ExclusiveArch: %{ix86} x86_64
- pcmciautils
ExclusiveArch: %{ix86} x86_64 ia64 ppc ppc64 %{arm}
- pdbg
ExclusiveArch: ppc64le
- pdfmod
ExclusiveArch: %mono_arches
- perl-Dumbbench
ExclusiveArch: %{ix86} x86_64 noarch
- perl-Parse-DMIDecode
ExclusiveArch: %{ix86} x86_64 ia64 aarch64
- pesign
ExclusiveArch: %{ix86} x86_64 ia64 aarch64 %{arm}
- pesign-test-app
ExclusiveArch: x86_64
- pinta
ExclusiveArch: %mono_arches
- pioneer
ExclusiveArch: %{ix86} x86_64
- playonlinux
ExclusiveArch: %{arm} aarch64 %{ix86} x86_64
- pmdk-convert
ExclusiveArch: x86_64
- pmemkv
ExclusiveArch: x86_64
- poppler-sharp
ExclusiveArch: %mono_arches
- popub
ExclusiveArch: %{?go_arches:%{go_arches}}%{!?go_arches:%{ix86} x86_64 %{arm}}
- powerpc-utils
ExclusiveArch: ppc %{power64}
- ppc64-diag
ExclusiveArch: ppc %{power64}
- pveclib
ExclusiveArch: ppc %{power64}
- pvs-sbcl
ExclusiveArch: %{ix86} x86_64 ppc sparcv9
- pyqtwebengine
ExclusiveArch: %{qt5_qtwebengine_arches}
- python-cryptography
ExclusiveArch: %{rust_arches}
- python-etcd
ExclusiveArch: noarch %{ix86} x86_64 %{arm} aarch64 ppc64le s390x
- python-healpy
ExclusiveArch: aarch64 ppc64 ppc64le x86_64 s390x
- python-javabridge
ExclusiveArch: i686 x86_64
- python-openoffice
ExclusiveArch: noarch x86_64
- python-pymoc
ExclusiveArch: aarch64 ppc64 ppc64le x86_64 s390x
- python-rpi-gpio
ExclusiveArch: %{arm} aarch64
- python-setuptools-rust
ExclusiveArch: %{rust_arches}
- q4wine
ExclusiveArch: %{ix86} x86_64 %{arm} aarch64
- qatzip
ExclusiveArch: x86_64
- qcint
ExclusiveArch: x86_64
- qclib
ExclusiveArch: s390 s390x
- qemu-sanity-check
ExclusiveArch: %{kernel_arches}
- qevercloud
ExclusiveArch: %{qt5_qtwebengine_arches}
- qmapshack
ExclusiveArch: %{qt5_qtwebengine_arches}
- qt4pas
ExclusiveArch: %{fpc_arches}
- qt5-qtwebengine
ExclusiveArch: %{qt5_qtwebengine_arches}
- quantum-espresso
ExclusiveArch: x86_64 %{ix86} aarch64 %{power64}
- quentier
ExclusiveArch: %{qt5_qtwebengine_arches}
- rear
ExclusiveArch: %ix86 x86_64 ppc ppc64 ppc64le ia64
- renderdoc
ExclusiveArch: x86_64
- reptyr
ExclusiveArch: %{ix86} x86_64 %{arm}
- rescene
ExclusiveArch: %{mono_arches}
- restsharp
ExclusiveArch: %{mono_arches}
- rhythmbox-alternative-toolbar
ExclusiveArch: %{ix86} %{arm} x86_64 ppc64 ppc64le
- rocm-runtime
ExclusiveArch: x86_64 aarch64
- rocminfo
ExclusiveArch: x86_64 aarch64
- rpg-cli
ExclusiveArch: %{rust_arches}
- rpm-ostree
ExclusiveArch: %{rust_arches}
- rr
ExclusiveArch: %{ix86} x86_64
- rssguard
ExclusiveArch: %{qt5_qtwebengine_arches}
- rubygem-childprocess
ExclusiveArch: %{ix86} x86_64 noarch
- runc
ExclusiveArch: %{ix86} x86_64 %{arm} aarch64 ppc64le %{mips} s390x
- rust
ExclusiveArch: %{rust_arches}
- rust-Inflector
ExclusiveArch: %{rust_arches}
- rust-ab_glyph
ExclusiveArch: %{rust_arches}
- rust-ab_glyph_rasterizer
ExclusiveArch: %{rust_arches}
- rust-abomonation
ExclusiveArch: %{rust_arches}
- rust-actix
ExclusiveArch: %{rust_arches}
- rust-actix-codec
ExclusiveArch: %{rust_arches}
- rust-actix-connect
ExclusiveArch: %{rust_arches}
- rust-actix-files
ExclusiveArch: %{rust_arches}
- rust-actix-http
ExclusiveArch: %{rust_arches}
- rust-actix-macros
ExclusiveArch: %{rust_arches}
- rust-actix-macros0.1
ExclusiveArch: %{rust_arches}
- rust-actix-router
ExclusiveArch: %{rust_arches}
- rust-actix-rt
ExclusiveArch: %{rust_arches}
- rust-actix-rt1
ExclusiveArch: %{rust_arches}
- rust-actix-server
ExclusiveArch: %{rust_arches}
- rust-actix-server-config
ExclusiveArch: %{rust_arches}
- rust-actix-service
ExclusiveArch: %{rust_arches}
- rust-actix-testing
ExclusiveArch: %{rust_arches}
- rust-actix-threadpool
ExclusiveArch: %{rust_arches}
- rust-actix-tls
ExclusiveArch: %{rust_arches}
- rust-actix-utils
ExclusiveArch: %{rust_arches}
- rust-actix-web
ExclusiveArch: %{rust_arches}
- rust-actix-web-codegen
ExclusiveArch: %{rust_arches}
- rust-actix0.10
ExclusiveArch: %{rust_arches}
- rust-actix_derive
ExclusiveArch: %{rust_arches}
- rust-actix_derive0.5
ExclusiveArch: %{rust_arches}
- rust-addr2line
ExclusiveArch: %{rust_arches}
- rust-adler
ExclusiveArch: %{rust_arches}
- rust-adler32
ExclusiveArch: %{rust_arches}
- rust-aead
ExclusiveArch: %{rust_arches}
- rust-aes
ExclusiveArch: %{rust_arches}
- rust-aes-gcm
ExclusiveArch: %{rust_arches}
- rust-aes-soft
ExclusiveArch: %{rust_arches}
- rust-afterburn
ExclusiveArch: %{rust_arches}
- rust-ahash
ExclusiveArch: %{rust_arches}
- rust-ahash0.4
ExclusiveArch: %{rust_arches}
- rust-aho-corasick
ExclusiveArch: %{rust_arches}
- rust-alacritty
ExclusiveArch: %{rust_arches}
- rust-alacritty_config_derive
ExclusiveArch: %{rust_arches}
- rust-alacritty_terminal
ExclusiveArch: %{rust_arches}
- rust-alga
ExclusiveArch: %{rust_arches}
- rust-alga_derive
ExclusiveArch: %{rust_arches}
- rust-alloc-no-stdlib
ExclusiveArch: %{rust_arches}
- rust-alloc-stdlib
ExclusiveArch: %{rust_arches}
- rust-alphanumeric-sort
ExclusiveArch: %{rust_arches}
- rust-alsa
ExclusiveArch: %{rust_arches}
- rust-alsa-sys
ExclusiveArch: %{rust_arches}
- rust-ambient-authority
ExclusiveArch: %{rust_arches}
- rust-ammonia
ExclusiveArch: %{rust_arches}
- rust-andrew
ExclusiveArch: %{rust_arches}
- rust-ansi_colours
ExclusiveArch: %{rust_arches}
- rust-ansi_term
ExclusiveArch: %{rust_arches}
- rust-ansi_term0.11
ExclusiveArch: %{rust_arches}
- rust-antidote
ExclusiveArch: %{rust_arches}
- rust-anyhow
ExclusiveArch: %{rust_arches}
- rust-anymap
ExclusiveArch: %{rust_arches}
- rust-aom-sys
ExclusiveArch: %{rust_arches}
- rust-app_dirs
ExclusiveArch: %{rust_arches}
- rust-approx
ExclusiveArch: %{rust_arches}
- rust-ar
ExclusiveArch: %{rust_arches}
- rust-arbitrary
ExclusiveArch: %{rust_arches}
- rust-arbitrary0.4
ExclusiveArch: %{rust_arches}
- rust-arc-swap
ExclusiveArch: %{rust_arches}
- rust-arg_enum_proc_macro
ExclusiveArch: %{rust_arches}
- rust-argh
ExclusiveArch: %{rust_arches}
- rust-argh_derive
ExclusiveArch: %{rust_arches}
- rust-argh_shared
ExclusiveArch: %{rust_arches}
- rust-argparse
ExclusiveArch: %{rust_arches}
- rust-array-init
ExclusiveArch: %{rust_arches}
- rust-arraydeque
ExclusiveArch: %{rust_arches}
- rust-arrayref
ExclusiveArch: %{rust_arches}
- rust-arrayvec
ExclusiveArch: %{rust_arches}
- rust-article_scraper
ExclusiveArch: %{rust_arches}
- rust-ascii
ExclusiveArch: %{rust_arches}
- rust-ascii-canvas
ExclusiveArch: %{rust_arches}
- rust-askalono
ExclusiveArch: %{rust_arches}
- rust-askalono-cli
ExclusiveArch: %{rust_arches}
- rust-askama
ExclusiveArch: %{rust_arches}
- rust-askama_derive
ExclusiveArch: %{rust_arches}
- rust-askama_escape
ExclusiveArch: %{rust_arches}
- rust-askama_shared
ExclusiveArch: %{rust_arches}
- rust-asn1
ExclusiveArch: %{rust_arches}
- rust-asn1_derive
ExclusiveArch: %{rust_arches}
- rust-assert-impl
ExclusiveArch: %{rust_arches}
- rust-assert-json-diff
ExclusiveArch: %{rust_arches}
- rust-assert_approx_eq
ExclusiveArch: %{rust_arches}
- rust-assert_cli
ExclusiveArch: %{rust_arches}
- rust-assert_cmd
ExclusiveArch: %{rust_arches}
- rust-assert_fs
ExclusiveArch: %{rust_arches}
- rust-assert_matches
ExclusiveArch: %{rust_arches}
- rust-assign
ExclusiveArch: %{rust_arches}
- rust-async-attributes
ExclusiveArch: %{rust_arches}
- rust-async-channel
ExclusiveArch: %{rust_arches}
- rust-async-compression
ExclusiveArch: %{rust_arches}
- rust-async-executor
ExclusiveArch: %{rust_arches}
- rust-async-global-executor
ExclusiveArch: %{rust_arches}
- rust-async-io
ExclusiveArch: %{rust_arches}
- rust-async-lock
ExclusiveArch: %{rust_arches}
- rust-async-mutex
ExclusiveArch: %{rust_arches}
- rust-async-process
ExclusiveArch: %{rust_arches}
- rust-async-std
ExclusiveArch: %{rust_arches}
- rust-async-stream
ExclusiveArch: %{rust_arches}
- rust-async-stream-impl
ExclusiveArch: %{rust_arches}
- rust-async-task
ExclusiveArch: %{rust_arches}
- rust-async-trait
ExclusiveArch: %{rust_arches}
- rust-asyncgit
ExclusiveArch: %{rust_arches}
- rust-atk
ExclusiveArch: %{rust_arches}
- rust-atk-sys
ExclusiveArch: %{rust_arches}
- rust-atom
ExclusiveArch: %{rust_arches}
- rust-atomic-waker
ExclusiveArch: %{rust_arches}
- rust-atomicwrites
ExclusiveArch: %{rust_arches}
- rust-attohttpc
ExclusiveArch: %{rust_arches}
- rust-atty
ExclusiveArch: %{rust_arches}
- rust-autocfg
ExclusiveArch: %{rust_arches}
- rust-av-metrics
ExclusiveArch: %{rust_arches}
- rust-average
ExclusiveArch: %{rust_arches}
- rust-awc
ExclusiveArch: %{rust_arches}
- rust-az
ExclusiveArch: %{rust_arches}
- rust-backtrace
ExclusiveArch: %{rust_arches}
- rust-backtrace-sys
ExclusiveArch: %{rust_arches}
- rust-base100
ExclusiveArch: %{rust_arches}
- rust-base64
ExclusiveArch: %{rust_arches}
- rust-base64-0.10
ExclusiveArch: %{rust_arches}
- rust-base64-0.11
ExclusiveArch: %{rust_arches}
- rust-base64_0.12
ExclusiveArch: %{rust_arches}
- rust-bat
ExclusiveArch: %{rust_arches}
- rust-battery
ExclusiveArch: %{rust_arches}
- rust-beef
ExclusiveArch: %{rust_arches}
- rust-below
ExclusiveArch: %{rust_arches}
- rust-below-common
ExclusiveArch: %{rust_arches}
- rust-below-config
ExclusiveArch: %{rust_arches}
- rust-below-dump
ExclusiveArch: %{rust_arches}
- rust-below-model
ExclusiveArch: %{rust_arches}
- rust-below-render
ExclusiveArch: %{rust_arches}
- rust-below-store
ExclusiveArch: %{rust_arches}
- rust-below-view
ExclusiveArch: %{rust_arches}
- rust-below_derive
ExclusiveArch: %{rust_arches}
- rust-bencher
ExclusiveArch: %{rust_arches}
- rust-better-panic
ExclusiveArch: %{rust_arches}
- rust-bincode
ExclusiveArch: %{rust_arches}
- rust-bincode0.8
ExclusiveArch: %{rust_arches}
- rust-bindgen
ExclusiveArch: %{rust_arches}
- rust-binfarce
ExclusiveArch: %{rust_arches}
- rust-biscuit
ExclusiveArch: %{rust_arches}
- rust-bit-set
ExclusiveArch: %{rust_arches}
- rust-bit-vec
ExclusiveArch: %{rust_arches}
- rust-bitfield
ExclusiveArch: %{rust_arches}
- rust-bitflags
ExclusiveArch: %{rust_arches}
- rust-bitmaps
ExclusiveArch: %{rust_arches}
- rust-bitreader
ExclusiveArch: %{rust_arches}
- rust-bitstream-io
ExclusiveArch: %{rust_arches}
- rust-bitvec
ExclusiveArch: %{rust_arches}
- rust-blake2
ExclusiveArch: %{rust_arches}
- rust-blobby
ExclusiveArch: %{rust_arches}
- rust-blobby0.1
ExclusiveArch: %{rust_arches}
- rust-block-buffer
ExclusiveArch: %{rust_arches}
- rust-block-buffer0.7
ExclusiveArch: %{rust_arches}
- rust-block-modes
ExclusiveArch: %{rust_arches}
- rust-block-padding
ExclusiveArch: %{rust_arches}
- rust-blocking
ExclusiveArch: %{rust_arches}
- rust-blsctl
ExclusiveArch: %{rust_arches}
- rust-bodhi
ExclusiveArch: %{rust_arches}
- rust-bodhi-cli
ExclusiveArch: %{rust_arches}
- rust-bootupd
ExclusiveArch: x86_64 aarch64
- rust-box_drawing
ExclusiveArch: %{rust_arches}
- rust-boxfnonce
ExclusiveArch: %{rust_arches}
- rust-brev
ExclusiveArch: %{rust_arches}
- rust-brotli
ExclusiveArch: %{rust_arches}
- rust-brotli-decompressor
ExclusiveArch: %{rust_arches}
- rust-brotli-sys
ExclusiveArch: %{rust_arches}
- rust-brotli2
ExclusiveArch: %{rust_arches}
- rust-bstr
ExclusiveArch: %{rust_arches}
- rust-buf-min
ExclusiveArch: %{rust_arches}
- rust-buf_redux
ExclusiveArch: %{rust_arches}
- rust-buffered-reader
ExclusiveArch: %{rust_arches}
- rust-bufsize
ExclusiveArch: %{rust_arches}
- rust-bufstream
ExclusiveArch: %{rust_arches}
- rust-bugreport
ExclusiveArch: %{rust_arches}
- rust-build-env
ExclusiveArch: %{rust_arches}
- rust-build_const
ExclusiveArch: %{rust_arches}
- rust-bumpalo
ExclusiveArch: %{rust_arches}
- rust-byte-tools
ExclusiveArch: %{rust_arches}
- rust-byte-unit
ExclusiveArch: %{rust_arches}
- rust-byte-unit3
ExclusiveArch: %{rust_arches}
- rust-bytecheck
ExclusiveArch: %{rust_arches}
- rust-bytecheck_derive
ExclusiveArch: %{rust_arches}
- rust-bytecount
ExclusiveArch: %{rust_arches}
- rust-bytelines
ExclusiveArch: %{rust_arches}
- rust-bytemuck
ExclusiveArch: %{rust_arches}
- rust-bytemuck_derive
ExclusiveArch: %{rust_arches}
- rust-byteorder
ExclusiveArch: %{rust_arches}
- rust-bytes
ExclusiveArch: %{rust_arches}
- rust-bytes0.4
ExclusiveArch: %{rust_arches}
- rust-bytes0.5
ExclusiveArch: %{rust_arches}
- rust-bytes0.6
ExclusiveArch: %{rust_arches}
- rust-bytesize
ExclusiveArch: %{rust_arches}
- rust-bytestring
ExclusiveArch: %{rust_arches}
- rust-bzip2
ExclusiveArch: %{rust_arches}
- rust-bzip2-sys
ExclusiveArch: %{rust_arches}
- rust-c_vec
ExclusiveArch: %{rust_arches}
- rust-cache-padded
ExclusiveArch: %{rust_arches}
- rust-cairo-rs
ExclusiveArch: %{rust_arches}
- rust-cairo-sys-rs
ExclusiveArch: %{rust_arches}
- rust-calloop
ExclusiveArch: %{rust_arches}
- rust-calloop0.6
ExclusiveArch: %{rust_arches}
- rust-camino
ExclusiveArch: %{rust_arches}
- rust-cap-primitives
ExclusiveArch: %{rust_arches}
- rust-cap-rand
ExclusiveArch: %{rust_arches}
- rust-cap-std
ExclusiveArch: %{rust_arches}
- rust-capnp
ExclusiveArch: %{rust_arches}
- rust-capnp-futures
ExclusiveArch: %{rust_arches}
- rust-capnp-rpc
ExclusiveArch: %{rust_arches}
- rust-caps
ExclusiveArch: %{rust_arches}
- rust-cargo
ExclusiveArch: %{rust_arches}
- rust-cargo-bloat
ExclusiveArch: %{rust_arches}
- rust-cargo-c
ExclusiveArch: %{rust_arches}
- rust-cargo-husky
ExclusiveArch: %{rust_arches}
- rust-cargo-insta
ExclusiveArch: %{rust_arches}
- rust-cargo-platform
ExclusiveArch: %{rust_arches}
- rust-cargo-readme
ExclusiveArch: %{rust_arches}
- rust-cargo_metadata
ExclusiveArch: %{rust_arches}
- rust-case
ExclusiveArch: %{rust_arches}
- rust-cassowary
ExclusiveArch: %{rust_arches}
- rust-cast
ExclusiveArch: %{rust_arches}
- rust-cbindgen
ExclusiveArch: %{rust_arches}
- rust-cc
ExclusiveArch: %{rust_arches}
- rust-cedarwood
ExclusiveArch: %{rust_arches}
- rust-cexpr
ExclusiveArch: %{rust_arches}
- rust-cfg-expr
ExclusiveArch: %{rust_arches}
- rust-cfg-if
ExclusiveArch: %{rust_arches}
- rust-cfg-if0.1
ExclusiveArch: %{rust_arches}
- rust-cgmath
ExclusiveArch: %{rust_arches}
- rust-cgroupfs
ExclusiveArch: %{rust_arches}
- rust-chainerror
ExclusiveArch: %{rust_arches}
- rust-charset
ExclusiveArch: %{rust_arches}
- rust-chbs
ExclusiveArch: %{rust_arches}
- rust-checked_int_cast
ExclusiveArch: %{rust_arches}
- rust-chlorine
ExclusiveArch: %{rust_arches}
- rust-choosier
ExclusiveArch: %{rust_arches}
- rust-chrono
ExclusiveArch: %{rust_arches}
- rust-chrono-humanize
ExclusiveArch: %{rust_arches}
- rust-chrono-tz
ExclusiveArch: %{rust_arches}
- rust-chunked_transfer
ExclusiveArch: %{rust_arches}
- rust-cipher
ExclusiveArch: %{rust_arches}
- rust-clang-ast
ExclusiveArch: %{rust_arches}
- rust-clang-sys
ExclusiveArch: %{rust_arches}
- rust-clap
ExclusiveArch: %{rust_arches}
- rust-clap2
ExclusiveArch: %{rust_arches}
- rust-clap_derive
ExclusiveArch: %{rust_arches}
- rust-claxon
ExclusiveArch: %{rust_arches}
- rust-clicolors-control
ExclusiveArch: %{rust_arches}
- rust-clipboard
ExclusiveArch: %{rust_arches}
- rust-cloudflare-zlib
ExclusiveArch: x86_64 aarch64
- rust-cloudflare-zlib-sys
ExclusiveArch: x86_64 aarch64
- rust-cmake
ExclusiveArch: %{rust_arches}
- rust-codespan-reporting
ExclusiveArch: %{rust_arches}
- rust-codicon
ExclusiveArch: %{rust_arches}
- rust-color-backtrace
ExclusiveArch: %{rust_arches}
- rust-color_quant
ExclusiveArch: %{rust_arches}
- rust-colored
ExclusiveArch: %{rust_arches}
- rust-colored1
ExclusiveArch: %{rust_arches}
- rust-colored_json
ExclusiveArch: %{rust_arches}
- rust-colorful
ExclusiveArch: %{rust_arches}
- rust-compiletest_rs
ExclusiveArch: %{rust_arches}
- rust-comrak
ExclusiveArch: %{rust_arches}
- rust-concurrent-queue
ExclusiveArch: %{rust_arches}
- rust-config
ExclusiveArch: %{rust_arches}
- rust-configparser
ExclusiveArch: %{rust_arches}
- rust-confy
ExclusiveArch: %{rust_arches}
- rust-console
ExclusiveArch: %{rust_arches}
- rust-console0.11
ExclusiveArch: %{rust_arches}
- rust-console0.12
ExclusiveArch: %{rust_arches}
- rust-console0.13
ExclusiveArch: %{rust_arches}
- rust-console0.9
ExclusiveArch: %{rust_arches}
- rust-console_error_panic_hook
ExclusiveArch: %{rust_arches}
- rust-const-cstr
ExclusiveArch: %{rust_arches}
- rust-const-random
ExclusiveArch: %{rust_arches}
- rust-const-random-macro
ExclusiveArch: %{rust_arches}
- rust-const_fn
ExclusiveArch: %{rust_arches}
- rust-constant_time_eq
ExclusiveArch: %{rust_arches}
- rust-content_inspector
ExclusiveArch: %{rust_arches}
- rust-conv
ExclusiveArch: %{rust_arches}
- rust-convert_case
ExclusiveArch: %{rust_arches}
- rust-cookie
ExclusiveArch: %{rust_arches}
- rust-cookie_store
ExclusiveArch: %{rust_arches}
- rust-copydeps
ExclusiveArch: %{rust_arches}
- rust-copyless
ExclusiveArch: %{rust_arches}
- rust-copypasta
ExclusiveArch: %{rust_arches}
- rust-cordic
ExclusiveArch: %{rust_arches}
- rust-coreos-installer
ExclusiveArch: %{rust_arches}
- rust-counted-array
ExclusiveArch: %{rust_arches}
- rust-cpal
ExclusiveArch: %{rust_arches}
- rust-cpio
ExclusiveArch: %{rust_arches}
- rust-cpp_demangle
ExclusiveArch: %{rust_arches}
- rust-cpufeatures
ExclusiveArch: x86_64 aarch64
- rust-cpuid-bool
ExclusiveArch: %{rust_arches}
- rust-cpython
ExclusiveArch: %{rust_arches}
- rust-cranelift-bforest
ExclusiveArch: %{rust_arches}
- rust-cranelift-codegen
ExclusiveArch: %{rust_arches}
- rust-cranelift-codegen-meta
ExclusiveArch: %{rust_arches}
- rust-cranelift-codegen-shared
ExclusiveArch: %{rust_arches}
- rust-cranelift-entity
ExclusiveArch: %{rust_arches}
- rust-cranelift-frontend
ExclusiveArch: %{rust_arches}
- rust-cranelift-native
ExclusiveArch: %{rust_arches}
- rust-cranelift-wasm
ExclusiveArch: %{rust_arches}
- rust-crates-io
ExclusiveArch: %{rust_arches}
- rust-crc
ExclusiveArch: %{rust_arches}
- rust-crc-any
ExclusiveArch: %{rust_arches}
- rust-crc-core
ExclusiveArch: %{rust_arches}
- rust-crc32fast
ExclusiveArch: %{rust_arches}
- rust-criterion
ExclusiveArch: %{rust_arches}
- rust-criterion-plot
ExclusiveArch: %{rust_arches}
- rust-crossbeam
ExclusiveArch: %{rust_arches}
- rust-crossbeam-channel
ExclusiveArch: %{rust_arches}
- rust-crossbeam-channel0.4
ExclusiveArch: %{rust_arches}
- rust-crossbeam-deque
ExclusiveArch: %{rust_arches}
- rust-crossbeam-deque0.7
ExclusiveArch: %{rust_arches}
- rust-crossbeam-epoch
ExclusiveArch: %{rust_arches}
- rust-crossbeam-epoch0.8
ExclusiveArch: %{rust_arches}
- rust-crossbeam-queue
ExclusiveArch: %{rust_arches}
- rust-crossbeam-queue0.2
ExclusiveArch: %{rust_arches}
- rust-crossbeam-utils
ExclusiveArch: %{rust_arches}
- rust-crossbeam-utils0.7
ExclusiveArch: %{rust_arches}
- rust-crossbeam0.7
ExclusiveArch: %{rust_arches}
- rust-crossfont
ExclusiveArch: %{rust_arches}
- rust-crossterm
ExclusiveArch: %{rust_arches}
- rust-crossterm0.17
ExclusiveArch: %{rust_arches}
- rust-crosstermion
ExclusiveArch: %{rust_arches}
- rust-crunchy
ExclusiveArch: %{rust_arches}
- rust-crypto-hash
ExclusiveArch: %{rust_arches}
- rust-crypto-mac
ExclusiveArch: %{rust_arches}
- rust-crypto-mac0.8
ExclusiveArch: %{rust_arches}
- rust-cryptoki
ExclusiveArch: %{rust_arches}
- rust-cryptoki-sys
ExclusiveArch: %{rust_arches}
- rust-cryptovec
ExclusiveArch: %{rust_arches}
- rust-cssparser
ExclusiveArch: %{rust_arches}
- rust-cssparser-macros
ExclusiveArch: %{rust_arches}
- rust-cstr-argument
ExclusiveArch: %{rust_arches}
- rust-csv
ExclusiveArch: %{rust_arches}
- rust-csv-core
ExclusiveArch: %{rust_arches}
- rust-ct-logs
ExclusiveArch: %{rust_arches}
- rust-ctor
ExclusiveArch: %{rust_arches}
- rust-ctr
ExclusiveArch: %{rust_arches}
- rust-ctrlc
ExclusiveArch: %{rust_arches}
- rust-cty
ExclusiveArch: %{rust_arches}
- rust-curl
ExclusiveArch: %{rust_arches}
- rust-curl-sys
ExclusiveArch: %{rust_arches}
- rust-cursive
ExclusiveArch: %{rust_arches}
- rust-cursive_buffered_backend
ExclusiveArch: %{rust_arches}
- rust-cursive_core
ExclusiveArch: %{rust_arches}
- rust-custom_derive
ExclusiveArch: %{rust_arches}
- rust-cxx
ExclusiveArch: %{rust_arches}
- rust-cxx-build
ExclusiveArch: %{rust_arches}
- rust-cxx-gen
ExclusiveArch: %{rust_arches}
- rust-cxxbridge-flags
ExclusiveArch: %{rust_arches}
- rust-cxxbridge-macro
ExclusiveArch: %{rust_arches}
- rust-darling
ExclusiveArch: %{rust_arches}
- rust-darling_core
ExclusiveArch: %{rust_arches}
- rust-darling_macro
ExclusiveArch: %{rust_arches}
- rust-dashmap
ExclusiveArch: %{rust_arches}
- rust-data-encoding
ExclusiveArch: %{rust_arches}
- rust-data-url
ExclusiveArch: %{rust_arches}
- rust-datetime
ExclusiveArch: %{rust_arches}
- rust-dav1d-sys
ExclusiveArch: %{rust_arches}
- rust-dbus
ExclusiveArch: %{rust_arches}
- rust-dbus-codegen
ExclusiveArch: %{rust_arches}
- rust-dbus-crossroads
ExclusiveArch: %{rust_arches}
- rust-dbus-tokio
ExclusiveArch: %{rust_arches}
- rust-dbus-tree
ExclusiveArch: %{rust_arches}
- rust-dbus0.8
ExclusiveArch: %{rust_arches}
- rust-debug-helper
ExclusiveArch: %{rust_arches}
- rust-decimal
ExclusiveArch: %{rust_arches}
- rust-defer-drop
ExclusiveArch: %{rust_arches}
- rust-deflate
ExclusiveArch: %{rust_arches}
- rust-defmac
ExclusiveArch: %{rust_arches}
- rust-delta_e
ExclusiveArch: %{rust_arches}
- rust-derivative
ExclusiveArch: %{rust_arches}
- rust-derive-new
ExclusiveArch: %{rust_arches}
- rust-derive_arbitrary
ExclusiveArch: %{rust_arches}
- rust-derive_arbitrary0.4
ExclusiveArch: %{rust_arches}
- rust-derive_builder
ExclusiveArch: %{rust_arches}
- rust-derive_builder0.9
ExclusiveArch: %{rust_arches}
- rust-derive_builder_core
ExclusiveArch: %{rust_arches}
- rust-derive_builder_core0.9
ExclusiveArch: %{rust_arches}
- rust-derive_builder_macro
ExclusiveArch: %{rust_arches}
- rust-derive_more
ExclusiveArch: %{rust_arches}
- rust-des
ExclusiveArch: %{rust_arches}
- rust-desed
ExclusiveArch: %{rust_arches}
- rust-deunicode
ExclusiveArch: %{rust_arches}
- rust-devicemapper
ExclusiveArch: %{rust_arches}
- rust-dialoguer
ExclusiveArch: %{rust_arches}
- rust-diesel
ExclusiveArch: %{rust_arches}
- rust-diesel_derives
ExclusiveArch: %{rust_arches}
- rust-diesel_migrations
ExclusiveArch: %{rust_arches}
- rust-diff
ExclusiveArch: %{rust_arches}
- rust-difference
ExclusiveArch: %{rust_arches}
- rust-diffus
ExclusiveArch: %{rust_arches}
- rust-diffus-derive
ExclusiveArch: %{rust_arches}
- rust-digest
ExclusiveArch: %{rust_arches}
- rust-digest0.8
ExclusiveArch: %{rust_arches}
- rust-directories
ExclusiveArch: %{rust_arches}
- rust-directories-next
ExclusiveArch: %{rust_arches}
- rust-dirs
ExclusiveArch: %{rust_arches}
- rust-dirs-next
ExclusiveArch: %{rust_arches}
- rust-dirs-sys
ExclusiveArch: %{rust_arches}
- rust-dirs-sys-next
ExclusiveArch: %{rust_arches}
- rust-dirs2
ExclusiveArch: %{rust_arches}
- rust-diskonaut
ExclusiveArch: %{rust_arches}
- rust-dissimilar
ExclusiveArch: %{rust_arches}
- rust-dlib
ExclusiveArch: %{rust_arches}
- rust-dlib0.4
ExclusiveArch: %{rust_arches}
- rust-dlv-list
ExclusiveArch: %{rust_arches}
- rust-dns-lookup
ExclusiveArch: %{rust_arches}
- rust-dns-parser
ExclusiveArch: %{rust_arches}
- rust-doc-comment
ExclusiveArch: %{rust_arches}
- rust-docmatic
ExclusiveArch: %{rust_arches}
- rust-docopt
ExclusiveArch: %{rust_arches}
- rust-dotenv
ExclusiveArch: %{rust_arches}
- rust-downcast-rs
ExclusiveArch: %{rust_arches}
- rust-drg
ExclusiveArch: %{rust_arches}
- rust-dtoa
ExclusiveArch: %{rust_arches}
- rust-dtoa-short
ExclusiveArch: %{rust_arches}
- rust-dua-cli
ExclusiveArch: %{rust_arches}
- rust-duct
ExclusiveArch: %{rust_arches}
- rust-dummy
ExclusiveArch: %{rust_arches}
- rust-dunce
ExclusiveArch: %{rust_arches}
- rust-dutree
ExclusiveArch: %{rust_arches}
- rust-dyn-clone
ExclusiveArch: %{rust_arches}
- rust-easy-cast
ExclusiveArch: %{rust_arches}
- rust-easy-parallel
ExclusiveArch: %{rust_arches}
- rust-edit
ExclusiveArch: %{rust_arches}
- rust-edit-distance
ExclusiveArch: %{rust_arches}
- rust-either
ExclusiveArch: %{rust_arches}
- rust-elasticlunr-rs
ExclusiveArch: %{rust_arches}
- rust-elfcat
ExclusiveArch: %{rust_arches}
- rust-ena
ExclusiveArch: %{rust_arches}
- rust-encode_unicode
ExclusiveArch: %{rust_arches}
- rust-encoding
ExclusiveArch: %{rust_arches}
- rust-encoding-index-japanese
ExclusiveArch: %{rust_arches}
- rust-encoding-index-korean
ExclusiveArch: %{rust_arches}
- rust-encoding-index-simpchinese
ExclusiveArch: %{rust_arches}
- rust-encoding-index-singlebyte
ExclusiveArch: %{rust_arches}
- rust-encoding-index-tradchinese
ExclusiveArch: %{rust_arches}
- rust-encoding_index_tests
ExclusiveArch: %{rust_arches}
- rust-encoding_rs
ExclusiveArch: %{rust_arches}
- rust-encoding_rs_io
ExclusiveArch: %{rust_arches}
- rust-endian-type
ExclusiveArch: %{rust_arches}
- rust-entities
ExclusiveArch: %{rust_arches}
- rust-enum-as-inner
ExclusiveArch: %{rust_arches}
- rust-enum-iterator
ExclusiveArch: %{rust_arches}
- rust-enum-iterator-derive
ExclusiveArch: %{rust_arches}
- rust-enum-map
ExclusiveArch: %{rust_arches}
- rust-enum-map-derive
ExclusiveArch: %{rust_arches}
- rust-enum-repr
ExclusiveArch: %{rust_arches}
- rust-enum_primitive
ExclusiveArch: %{rust_arches}
- rust-enumflags2
ExclusiveArch: %{rust_arches}
- rust-enumflags2_derive
ExclusiveArch: %{rust_arches}
- rust-enumset
ExclusiveArch: %{rust_arches}
- rust-enumset_derive
ExclusiveArch: %{rust_arches}
- rust-env_logger
ExclusiveArch: %{rust_arches}
- rust-env_logger0.4
ExclusiveArch: %{rust_arches}
- rust-env_logger0.5
ExclusiveArch: %{rust_arches}
- rust-env_logger0.6
ExclusiveArch: %{rust_arches}
- rust-env_logger0.7
ExclusiveArch: %{rust_arches}
- rust-environment
ExclusiveArch: %{rust_arches}
- rust-envsubst
ExclusiveArch: %{rust_arches}
- rust-epoll
ExclusiveArch: %{rust_arches}
- rust-erased-serde
ExclusiveArch: %{rust_arches}
- rust-err-derive
ExclusiveArch: %{rust_arches}
- rust-errln
ExclusiveArch: %{rust_arches}
- rust-errno
ExclusiveArch: %{rust_arches}
- rust-error-chain
ExclusiveArch: %{rust_arches}
- rust-escaper
ExclusiveArch: %{rust_arches}
- rust-escargot
ExclusiveArch: %{rust_arches}
- rust-euclid
ExclusiveArch: %{rust_arches}
- rust-event-listener
ExclusiveArch: %{rust_arches}
- rust-exa
ExclusiveArch: %{rust_arches}
- rust-executable-path
ExclusiveArch: %{rust_arches}
- rust-expat-sys
ExclusiveArch: %{rust_arches}
- rust-extend
ExclusiveArch: %{rust_arches}
- rust-extprim
ExclusiveArch: %{rust_arches}
- rust-extprim_literals_macros
ExclusiveArch: %{rust_arches}
- rust-fail
ExclusiveArch: %{rust_arches}
- rust-failure
ExclusiveArch: %{rust_arches}
- rust-failure-tools
ExclusiveArch: %{rust_arches}
- rust-failure_derive
ExclusiveArch: %{rust_arches}
- rust-fake
ExclusiveArch: %{rust_arches}
- rust-fake-simd
ExclusiveArch: %{rust_arches}
- rust-fake_clock
ExclusiveArch: %{rust_arches}
- rust-fallible-iterator
ExclusiveArch: %{rust_arches}
- rust-fallible-streaming-iterator
ExclusiveArch: %{rust_arches}
- rust-fallible_collections
ExclusiveArch: %{rust_arches}
- rust-fancy-regex
ExclusiveArch: %{rust_arches}
- rust-fastrand
ExclusiveArch: %{rust_arches}
- rust-fb_procfs
ExclusiveArch: %{rust_arches}
- rust-fbthrift_codegen_includer_proc_macro
ExclusiveArch: %{rust_arches}
- rust-fd-find
ExclusiveArch: %{rust_arches}
- rust-fd-lock
ExclusiveArch: %{rust_arches}
- rust-fdlimit
ExclusiveArch: %{rust_arches}
- rust-fedora
ExclusiveArch: %{rust_arches}
- rust-fedora-coreos-pinger
ExclusiveArch: %{rust_arches}
- rust-fedora-update-feedback
ExclusiveArch: %{rust_arches}
- rust-feed-rs
ExclusiveArch: %{rust_arches}
- rust-feedbin_api
ExclusiveArch: %{rust_arches}
- rust-feedly_api
ExclusiveArch: %{rust_arches}
- rust-femme
ExclusiveArch: %{rust_arches}
- rust-fern
ExclusiveArch: %{rust_arches}
- rust-fever_api
ExclusiveArch: %{rust_arches}
- rust-ffsend
ExclusiveArch: %{rust_arches}
- rust-ffsend-api
ExclusiveArch: %{rust_arches}
- rust-field-offset
ExclusiveArch: %{rust_arches}
- rust-file-per-thread-logger
ExclusiveArch: %{rust_arches}
- rust-filedescriptor
ExclusiveArch: %{rust_arches}
- rust-filesize
ExclusiveArch: %{rust_arches}
- rust-filetime
ExclusiveArch: %{rust_arches}
- rust-filetreelist
ExclusiveArch: %{rust_arches}
- rust-findshlibs
ExclusiveArch: %{rust_arches}
- rust-fixed
ExclusiveArch: %{rust_arches}
- rust-fixedbitset
ExclusiveArch: %{rust_arches}
- rust-flame
ExclusiveArch: %{rust_arches}
- rust-flate2
ExclusiveArch: %{rust_arches}
- rust-float-cmp
ExclusiveArch: %{rust_arches}
- rust-float-cmp0.6
ExclusiveArch: %{rust_arches}
- rust-float-ord
ExclusiveArch: %{rust_arches}
- rust-flume
ExclusiveArch: %{rust_arches}
- rust-fn-error-context
ExclusiveArch: %{rust_arches}
- rust-fnv
ExclusiveArch: %{rust_arches}
- rust-font-kit
ExclusiveArch: %{rust_arches}
- rust-foreign-types
ExclusiveArch: %{rust_arches}
- rust-foreign-types-macros
ExclusiveArch: %{rust_arches}
- rust-foreign-types-shared
ExclusiveArch: %{rust_arches}
- rust-foreign-types-shared0.1
ExclusiveArch: %{rust_arches}
- rust-foreign-types0.3
ExclusiveArch: %{rust_arches}
- rust-form_urlencoded
ExclusiveArch: %{rust_arches}
- rust-fragile
ExclusiveArch: %{rust_arches}
- rust-freetype
ExclusiveArch: %{rust_arches}
- rust-freetype-rs
ExclusiveArch: %{rust_arches}
- rust-freetype-sys
ExclusiveArch: %{rust_arches}
- rust-fs-set-times
ExclusiveArch: %{rust_arches}
- rust-fs2
ExclusiveArch: %{rust_arches}
- rust-fs_extra
ExclusiveArch: %{rust_arches}
- rust-funty
ExclusiveArch: %{rust_arches}
- rust-fuse
ExclusiveArch: %{rust_arches}
- rust-futf
ExclusiveArch: %{rust_arches}
- rust-futures
ExclusiveArch: %{rust_arches}
- rust-futures-channel
ExclusiveArch: %{rust_arches}
- rust-futures-core
ExclusiveArch: %{rust_arches}
- rust-futures-cpupool
ExclusiveArch: %{rust_arches}
- rust-futures-executor
ExclusiveArch: %{rust_arches}
- rust-futures-io
ExclusiveArch: %{rust_arches}
- rust-futures-lite
ExclusiveArch: %{rust_arches}
- rust-futures-macro
ExclusiveArch: %{rust_arches}
- rust-futures-sink
ExclusiveArch: %{rust_arches}
- rust-futures-task
ExclusiveArch: %{rust_arches}
- rust-futures-test
ExclusiveArch: %{rust_arches}
- rust-futures-timer
ExclusiveArch: %{rust_arches}
- rust-futures-util
ExclusiveArch: %{rust_arches}
- rust-futures0.1
ExclusiveArch: %{rust_arches}
- rust-fuzzy-matcher
ExclusiveArch: %{rust_arches}
- rust-fxhash
ExclusiveArch: %{rust_arches}
- rust-gcsf
ExclusiveArch: %{rust_arches}
- rust-gdk
ExclusiveArch: %{rust_arches}
- rust-gdk-pixbuf
ExclusiveArch: %{rust_arches}
- rust-gdk-pixbuf-sys
ExclusiveArch: %{rust_arches}
- rust-gdk-sys
ExclusiveArch: %{rust_arches}
- rust-gdk4
ExclusiveArch: %{rust_arches}
- rust-gdk4-sys
ExclusiveArch: %{rust_arches}
- rust-generic-array
ExclusiveArch: %{rust_arches}
- rust-generic-array0.12
ExclusiveArch: %{rust_arches}
- rust-genmesh
ExclusiveArch: %{rust_arches}
- rust-getch
ExclusiveArch: %{rust_arches}
- rust-gethostname
ExclusiveArch: %{rust_arches}
- rust-getopts
ExclusiveArch: %{rust_arches}
- rust-getrandom
ExclusiveArch: %{rust_arches}
- rust-getrandom0.1
ExclusiveArch: %{rust_arches}
- rust-getset
ExclusiveArch: %{rust_arches}
- rust-gettext-rs
ExclusiveArch: %{rust_arches}
- rust-gettext-sys
ExclusiveArch: %{rust_arches}
- rust-ghash
ExclusiveArch: %{rust_arches}
- rust-ghost
ExclusiveArch: %{rust_arches}
- rust-gif
ExclusiveArch: %{rust_arches}
- rust-gimli
ExclusiveArch: %{rust_arches}
- rust-gio
ExclusiveArch: %{rust_arches}
- rust-gio-sys
ExclusiveArch: %{rust_arches}
- rust-gir-format-check
ExclusiveArch: %{rust_arches}
- rust-git-delta
ExclusiveArch: %{rust_arches}
- rust-git-version
ExclusiveArch: %{rust_arches}
- rust-git-version-macro
ExclusiveArch: %{rust_arches}
- rust-git2
ExclusiveArch: %{rust_arches}
- rust-git2-curl
ExclusiveArch: %{rust_arches}
- rust-gitui
ExclusiveArch: %{rust_arches}
- rust-gl_generator
ExclusiveArch: %{rust_arches}
- rust-glam
ExclusiveArch: %{rust_arches}
- rust-glib
ExclusiveArch: %{rust_arches}
- rust-glib-macros
ExclusiveArch: %{rust_arches}
- rust-glib-sys
ExclusiveArch: %{rust_arches}
- rust-glob
ExclusiveArch: %{rust_arches}
- rust-globset
ExclusiveArch: %{rust_arches}
- rust-globwalk
ExclusiveArch: %{rust_arches}
- rust-glutin
ExclusiveArch: %{rust_arches}
- rust-glutin_egl_sys
ExclusiveArch: %{rust_arches}
- rust-glutin_glx_sys
ExclusiveArch: %{rust_arches}
- rust-gobject-sys
ExclusiveArch: %{rust_arches}
- rust-goblin
ExclusiveArch: %{rust_arches}
- rust-google-drive3-fork
ExclusiveArch: %{rust_arches}
- rust-gptman
ExclusiveArch: %{rust_arches}
- rust-graphene-rs
ExclusiveArch: %{rust_arches}
- rust-graphene-sys
ExclusiveArch: %{rust_arches}
- rust-grep
ExclusiveArch: %{rust_arches}
- rust-grep-cli
ExclusiveArch: %{rust_arches}
- rust-grep-matcher
ExclusiveArch: %{rust_arches}
- rust-grep-pcre2
ExclusiveArch: %{rust_arches}
- rust-grep-printer
ExclusiveArch: %{rust_arches}
- rust-grep-regex
ExclusiveArch: %{rust_arches}
- rust-grep-searcher
ExclusiveArch: %{rust_arches}
- rust-groupable
ExclusiveArch: %{rust_arches}
- rust-gsk4
ExclusiveArch: %{rust_arches}
- rust-gsk4-sys
ExclusiveArch: %{rust_arches}
- rust-gstreamer
ExclusiveArch: %{rust_arches}
- rust-gstreamer-audio
ExclusiveArch: %{rust_arches}
- rust-gstreamer-audio-sys
ExclusiveArch: %{rust_arches}
- rust-gstreamer-base
ExclusiveArch: %{rust_arches}
- rust-gstreamer-base-sys
ExclusiveArch: %{rust_arches}
- rust-gstreamer-editing-services
ExclusiveArch: %{rust_arches}
- rust-gstreamer-editing-services-sys
ExclusiveArch: %{rust_arches}
- rust-gstreamer-pbutils
ExclusiveArch: %{rust_arches}
- rust-gstreamer-pbutils-sys
ExclusiveArch: %{rust_arches}
- rust-gstreamer-player
ExclusiveArch: %{rust_arches}
- rust-gstreamer-player-sys
ExclusiveArch: %{rust_arches}
- rust-gstreamer-sys
ExclusiveArch: %{rust_arches}
- rust-gstreamer-video
ExclusiveArch: %{rust_arches}
- rust-gstreamer-video-sys
ExclusiveArch: %{rust_arches}
- rust-gtk
ExclusiveArch: %{rust_arches}
- rust-gtk-macros
ExclusiveArch: %{rust_arches}
- rust-gtk-rs-lgpl-docs
ExclusiveArch: %{rust_arches}
- rust-gtk-sys
ExclusiveArch: %{rust_arches}
- rust-gtk3-macros
ExclusiveArch: %{rust_arches}
- rust-gtk4
ExclusiveArch: %{rust_arches}
- rust-gtk4-macros
ExclusiveArch: %{rust_arches}
- rust-gtk4-sys
ExclusiveArch: %{rust_arches}
- rust-gzip-header
ExclusiveArch: %{rust_arches}
- rust-h2
ExclusiveArch: %{rust_arches}
- rust-h2_0.2
ExclusiveArch: %{rust_arches}
- rust-half
ExclusiveArch: %{rust_arches}
- rust-hamcrest
ExclusiveArch: %{rust_arches}
- rust-hamcrest2
ExclusiveArch: %{rust_arches}
- rust-handlebars
ExclusiveArch: %{rust_arches}
- rust-hashbrown
ExclusiveArch: %{rust_arches}
- rust-hashlink
ExclusiveArch: %{rust_arches}
- rust-headers
ExclusiveArch: %{rust_arches}
- rust-headers-core
ExclusiveArch: %{rust_arches}
- rust-headers-derive
ExclusiveArch: %{rust_arches}
- rust-heapsize
ExclusiveArch: %{rust_arches}
- rust-heatseeker
ExclusiveArch: %{rust_arches}
- rust-heck
ExclusiveArch: %{rust_arches}
- rust-hex
ExclusiveArch: %{rust_arches}
- rust-hex-literal
ExclusiveArch: %{rust_arches}
- rust-hex-literal-impl
ExclusiveArch: %{rust_arches}
- rust-hex-literal0.2
ExclusiveArch: %{rust_arches}
- rust-hexyl
ExclusiveArch: %{rust_arches}
- rust-hkdf
ExclusiveArch: %{rust_arches}
- rust-hmac
ExclusiveArch: %{rust_arches}
- rust-home
ExclusiveArch: %{rust_arches}
- rust-horrorshow
ExclusiveArch: %{rust_arches}
- rust-hostname
ExclusiveArch: %{rust_arches}
- rust-hostname-validator
ExclusiveArch: %{rust_arches}
- rust-hound
ExclusiveArch: %{rust_arches}
- rust-html2pango
ExclusiveArch: %{rust_arches}
- rust-html5ever
ExclusiveArch: %{rust_arches}
- rust-http
ExclusiveArch: %{rust_arches}
- rust-http-body
ExclusiveArch: %{rust_arches}
- rust-http-body0.3
ExclusiveArch: %{rust_arches}
- rust-http0.1
ExclusiveArch: %{rust_arches}
- rust-httparse
ExclusiveArch: %{rust_arches}
- rust-httpdate
ExclusiveArch: %{rust_arches}
- rust-human-sort
ExclusiveArch: %{rust_arches}
- rust-humansize
ExclusiveArch: %{rust_arches}
- rust-humantime
ExclusiveArch: %{rust_arches}
- rust-humantime-serde
ExclusiveArch: %{rust_arches}
- rust-humantime1
ExclusiveArch: %{rust_arches}
- rust-hyper
ExclusiveArch: %{rust_arches}
- rust-hyper-native-tls
ExclusiveArch: %{rust_arches}
- rust-hyper-rustls
ExclusiveArch: %{rust_arches}
- rust-hyper-staticfile
ExclusiveArch: %{rust_arches}
- rust-hyper-tls
ExclusiveArch: %{rust_arches}
- rust-hyper-tls0.4
ExclusiveArch: %{rust_arches}
- rust-hyper0.10
ExclusiveArch: %{rust_arches}
- rust-hyper0.13
ExclusiveArch: %{rust_arches}
- rust-hyperfine
ExclusiveArch: %{rust_arches}
- rust-i3ipc
ExclusiveArch: %{rust_arches}
- rust-id_tree
ExclusiveArch: %{rust_arches}
- rust-ident_case
ExclusiveArch: %{rust_arches}
- rust-idna
ExclusiveArch: %{rust_arches}
- rust-idna0.1
ExclusiveArch: %{rust_arches}
- rust-ignore
ExclusiveArch: %{rust_arches}
- rust-im-rc
ExclusiveArch: %{rust_arches}
- rust-image
ExclusiveArch: %{rust_arches}
- rust-image-roll
ExclusiveArch: %{rust_arches}
- rust-imgref
ExclusiveArch: %{rust_arches}
- rust-imgui
ExclusiveArch: %{rust_arches}
- rust-imgui-sys
ExclusiveArch: %{rust_arches}
- rust-imgui-winit-support
ExclusiveArch: %{rust_arches}
- rust-indexmap
ExclusiveArch: %{rust_arches}
- rust-indicatif
ExclusiveArch: %{rust_arches}
- rust-indoc
ExclusiveArch: %{rust_arches}
- rust-inflate
ExclusiveArch: %{rust_arches}
- rust-inotify
ExclusiveArch: %{rust_arches}
- rust-inotify-sys
ExclusiveArch: %{rust_arches}
- rust-input_buffer
ExclusiveArch: %{rust_arches}
- rust-insta
ExclusiveArch: %{rust_arches}
- rust-instant
ExclusiveArch: %{rust_arches}
- rust-interpolate_name
ExclusiveArch: %{rust_arches}
- rust-intervaltree
ExclusiveArch: %{rust_arches}
- rust-invalidstring
ExclusiveArch: %{rust_arches}
- rust-inventory
ExclusiveArch: %{rust_arches}
- rust-inventory-impl
ExclusiveArch: %{rust_arches}
- rust-io-lifetimes
ExclusiveArch: %{rust_arches}
- rust-ioctl-rs
ExclusiveArch: %{rust_arches}
- rust-iocuddle
ExclusiveArch: %{rust_arches}
- rust-iovec
ExclusiveArch: %{rust_arches}
- rust-ipnet
ExclusiveArch: %{rust_arches}
- rust-ipnetwork
ExclusiveArch: %{rust_arches}
- rust-iron
ExclusiveArch: %{rust_arches}
- rust-isahc
ExclusiveArch: %{rust_arches}
- rust-iso8601
ExclusiveArch: %{rust_arches}
- rust-iter-read
ExclusiveArch: %{rust_arches}
- rust-itertools
ExclusiveArch: %{rust_arches}
- rust-itertools-num
ExclusiveArch: %{rust_arches}
- rust-itertools0.8
ExclusiveArch: %{rust_arches}
- rust-itertools0.9
ExclusiveArch: %{rust_arches}
- rust-itoa
ExclusiveArch: %{rust_arches}
- rust-ivf
ExclusiveArch: %{rust_arches}
- rust-javascriptcore-rs
ExclusiveArch: %{rust_arches}
- rust-javascriptcore-rs-sys
ExclusiveArch: %{rust_arches}
- rust-jetscii
ExclusiveArch: %{rust_arches}
- rust-jieba-rs
ExclusiveArch: %{rust_arches}
- rust-jobserver
ExclusiveArch: %{rust_arches}
- rust-jpeg-decoder
ExclusiveArch: %{rust_arches}
- rust-jql
ExclusiveArch: %{rust_arches}
- rust-js-sys
ExclusiveArch: %{rust_arches}
- rust-js_int
ExclusiveArch: %{rust_arches}
- rust-json
ExclusiveArch: %{rust_arches}
- rust-just
ExclusiveArch: %{rust_arches}
- rust-jwalk
ExclusiveArch: %{rust_arches}
- rust-k9
ExclusiveArch: %{rust_arches}
- rust-keccak
ExclusiveArch: %{rust_arches}
- rust-khronos_api
ExclusiveArch: %{rust_arches}
- rust-kstring
ExclusiveArch: %{rust_arches}
- rust-kv-log-macro
ExclusiveArch: %{rust_arches}
- rust-lab
ExclusiveArch: %{rust_arches}
- rust-lalrpop
ExclusiveArch: %{rust_arches}
- rust-lalrpop-util
ExclusiveArch: %{rust_arches}
- rust-language-tags
ExclusiveArch: %{rust_arches}
- rust-lazy-init
ExclusiveArch: %{rust_arches}
- rust-lazy_static
ExclusiveArch: %{rust_arches}
- rust-lazycell
ExclusiveArch: %{rust_arches}
- rust-leb128
ExclusiveArch: %{rust_arches}
- rust-lewton
ExclusiveArch: %{rust_arches}
- rust-lexical-core
ExclusiveArch: %{rust_arches}
- rust-lexiclean
ExclusiveArch: %{rust_arches}
- rust-libbpf-cargo
ExclusiveArch: %{rust_arches}
- rust-libbpf-rs
ExclusiveArch: %{rust_arches}
- rust-libbpf-sys
ExclusiveArch: %{rust_arches}
- rust-libc
ExclusiveArch: %{rust_arches}
- rust-libcryptsetup-rs
ExclusiveArch: %{rust_arches}
- rust-libcryptsetup-rs-sys
ExclusiveArch: %{rust_arches}
- rust-libdbus-sys
ExclusiveArch: %{rust_arches}
- rust-libdeflate-sys
ExclusiveArch: %{rust_arches}
- rust-libdeflater
ExclusiveArch: %{rust_arches}
- rust-libflate
ExclusiveArch: %{rust_arches}
- rust-libflate_lz77
ExclusiveArch: %{rust_arches}
- rust-libgit2-sys
ExclusiveArch: %{rust_arches}
- rust-libhandy
ExclusiveArch: %{rust_arches}
- rust-libhandy-sys
ExclusiveArch: %{rust_arches}
- rust-libloading
ExclusiveArch: %{rust_arches}
- rust-libloading0.6
ExclusiveArch: %{rust_arches}
- rust-libm
ExclusiveArch: %{rust_arches}
- rust-libmount
ExclusiveArch: %{rust_arches}
- rust-libnotcurses-sys
ExclusiveArch: %{rust_arches}
- rust-liboverdrop
ExclusiveArch: %{rust_arches}
- rust-libpulse-binding
ExclusiveArch: %{rust_arches}
- rust-libpulse-sys
ExclusiveArch: %{rust_arches}
- rust-libslirp
ExclusiveArch: %{rust_arches}
- rust-libslirp-sys
ExclusiveArch: %{rust_arches}
- rust-libsqlite3-sys
ExclusiveArch: %{rust_arches}
- rust-libssh2-sys
ExclusiveArch: %{rust_arches}
- rust-libsystemd
ExclusiveArch: %{rust_arches}
- rust-libsystemd-sys
ExclusiveArch: %{rust_arches}
- rust-libudev
ExclusiveArch: %{rust_arches}
- rust-libudev-sys
ExclusiveArch: %{rust_arches}
- rust-libwebp
ExclusiveArch: %{rust_arches}
- rust-libwebp-sys2
ExclusiveArch: %{rust_arches}
- rust-libxml
ExclusiveArch: %{rust_arches}
- rust-libz-sys
ExclusiveArch: %{rust_arches}
- rust-line-wrap
ExclusiveArch: %{rust_arches}
- rust-line_drawing
ExclusiveArch: %{rust_arches}
- rust-link-cplusplus
ExclusiveArch: %{rust_arches}
- rust-linked-hash-map
ExclusiveArch: %{rust_arches}
- rust-linkify
ExclusiveArch: %{rust_arches}
- rust-lipsum
ExclusiveArch: %{rust_arches}
- rust-liquid
ExclusiveArch: %{rust_arches}
- rust-liquid-core
ExclusiveArch: %{rust_arches}
- rust-liquid-derive
ExclusiveArch: %{rust_arches}
- rust-liquid-lib
ExclusiveArch: %{rust_arches}
- rust-listenfd
ExclusiveArch: %{rust_arches}
- rust-lmdb
ExclusiveArch: %{rust_arches}
- rust-lmdb-sys
ExclusiveArch: %{rust_arches}
- rust-locale
ExclusiveArch: %{rust_arches}
- rust-locale_config
ExclusiveArch: %{rust_arches}
- rust-lock_api
ExclusiveArch: %{rust_arches}
- rust-lockfree
ExclusiveArch: %{rust_arches}
- rust-log
ExclusiveArch: %{rust_arches}
- rust-log-mdc
ExclusiveArch: %{rust_arches}
- rust-log-panics
ExclusiveArch: %{rust_arches}
- rust-log0.3
ExclusiveArch: %{rust_arches}
- rust-log4rs
ExclusiveArch: %{rust_arches}
- rust-loggerv
ExclusiveArch: %{rust_arches}
- rust-loopdev
ExclusiveArch: %{rust_arches}
- rust-lru-cache
ExclusiveArch: %{rust_arches}
- rust-lru_time_cache
ExclusiveArch: %{rust_arches}
- rust-lscolors
ExclusiveArch: %{rust_arches}
- rust-lsd
ExclusiveArch: %{rust_arches}
- rust-lzma-sys
ExclusiveArch: %{rust_arches}
- rust-lzw
ExclusiveArch: %{rust_arches}
- rust-mac
ExclusiveArch: %{rust_arches}
- rust-macro-attr
ExclusiveArch: %{rust_arches}
- rust-macroquad_macro
ExclusiveArch: %{rust_arches}
- rust-magic-crypt
ExclusiveArch: %{rust_arches}
- rust-maildir
ExclusiveArch: %{rust_arches}
- rust-mailparse
ExclusiveArch: %{rust_arches}
- rust-man
ExclusiveArch: %{rust_arches}
- rust-maplit
ExclusiveArch: %{rust_arches}
- rust-markup5ever
ExclusiveArch: %{rust_arches}
- rust-markup5ever_rcdom
ExclusiveArch: %{rust_arches}
- rust-match_cfg
ExclusiveArch: %{rust_arches}
- rust-matches
ExclusiveArch: %{rust_arches}
- rust-matrixcompare
ExclusiveArch: %{rust_arches}
- rust-matrixcompare-core
ExclusiveArch: %{rust_arches}
- rust-matrixcompare-mock
ExclusiveArch: %{rust_arches}
- rust-matrixmultiply
ExclusiveArch: %{rust_arches}
- rust-maxminddb
ExclusiveArch: %{rust_arches}
- rust-maybe-owned
ExclusiveArch: %{rust_arches}
- rust-maybe-uninit
ExclusiveArch: %{rust_arches}
- rust-mbox
ExclusiveArch: %{rust_arches}
- rust-mbrman
ExclusiveArch: %{rust_arches}
- rust-md-5
ExclusiveArch: %{rust_arches}
- rust-md5
ExclusiveArch: %{rust_arches}
- rust-mdl
ExclusiveArch: %{rust_arches}
- rust-memchr
ExclusiveArch: %{rust_arches}
- rust-memmap
ExclusiveArch: %{rust_arches}
- rust-memmap2
ExclusiveArch: %{rust_arches}
- rust-memmap2_0.1
ExclusiveArch: %{rust_arches}
- rust-memoffset
ExclusiveArch: %{rust_arches}
- rust-memoffset0.5
ExclusiveArch: %{rust_arches}
- rust-memsec
ExclusiveArch: %{rust_arches}
- rust-metadeps
ExclusiveArch: %{rust_arches}
- rust-migrations_internals
ExclusiveArch: %{rust_arches}
- rust-migrations_macros
ExclusiveArch: %{rust_arches}
- rust-mime
ExclusiveArch: %{rust_arches}
- rust-mime-sniffer
ExclusiveArch: %{rust_arches}
- rust-mime0.2
ExclusiveArch: %{rust_arches}
- rust-mime_guess
ExclusiveArch: %{rust_arches}
- rust-mime_guess1
ExclusiveArch: %{rust_arches}
- rust-miniflux_api
ExclusiveArch: %{rust_arches}
- rust-minify-html
ExclusiveArch: %{rust_arches}
- rust-minimp3
ExclusiveArch: %{rust_arches}
- rust-minimp3-sys
ExclusiveArch: %{rust_arches}
- rust-miniz-sys
ExclusiveArch: %{rust_arches}
- rust-miniz_oxide
ExclusiveArch: %{rust_arches}
- rust-miniz_oxide0.3
ExclusiveArch: %{rust_arches}
- rust-miniz_oxide_c_api
ExclusiveArch: %{rust_arches}
- rust-mint
ExclusiveArch: %{rust_arches}
- rust-mio
ExclusiveArch: %{rust_arches}
- rust-mio-extras
ExclusiveArch: %{rust_arches}
- rust-mio-uds
ExclusiveArch: %{rust_arches}
- rust-mio0.6
ExclusiveArch: %{rust_arches}
- rust-mktemp
ExclusiveArch: %{rust_arches}
- rust-mnt
ExclusiveArch: %{rust_arches}
- rust-mockito
ExclusiveArch: %{rust_arches}
- rust-modifier
ExclusiveArch: %{rust_arches}
- rust-more-asserts
ExclusiveArch: %{rust_arches}
- rust-mp4parse
ExclusiveArch: %{rust_arches}
- rust-msgbox
ExclusiveArch: %{rust_arches}
- rust-muldiv
ExclusiveArch: %{rust_arches}
- rust-multimap
ExclusiveArch: %{rust_arches}
- rust-multipart
ExclusiveArch: %{rust_arches}
- rust-mustache
ExclusiveArch: %{rust_arches}
- rust-nalgebra
ExclusiveArch: %{rust_arches}
- rust-nalgebra-macros
ExclusiveArch: %{rust_arches}
- rust-nasm-rs
ExclusiveArch: %{rust_arches}
- rust-native-tls
ExclusiveArch: %{rust_arches}
- rust-natord
ExclusiveArch: %{rust_arches}
- rust-navi
ExclusiveArch: %{rust_arches}
- rust-nb-connect
ExclusiveArch: %{rust_arches}
- rust-ncurses
ExclusiveArch: %{rust_arches}
- rust-net2
ExclusiveArch: %{rust_arches}
- rust-netlink-packet-core
ExclusiveArch: %{rust_arches}
- rust-netlink-packet-route
ExclusiveArch: %{rust_arches}
- rust-netlink-packet-utils
ExclusiveArch: %{rust_arches}
- rust-netlink-proto
ExclusiveArch: %{rust_arches}
- rust-netlink-sys
ExclusiveArch: %{rust_arches}
- rust-netmap_sys
ExclusiveArch: %{rust_arches}
- rust-nettle
ExclusiveArch: %{rust_arches}
- rust-nettle-sys
ExclusiveArch: %{rust_arches}
- rust-new_debug_unreachable
ExclusiveArch: %{rust_arches}
- rust-news-flash
ExclusiveArch: %{rust_arches}
- rust-newsblur_api
ExclusiveArch: %{rust_arches}
- rust-newtype_derive
ExclusiveArch: %{rust_arches}
- rust-nibble_vec
ExclusiveArch: %{rust_arches}
- rust-nickel
ExclusiveArch: %{rust_arches}
- rust-nix
ExclusiveArch: %{rust_arches}
- rust-nix0.14
ExclusiveArch: %{rust_arches}
- rust-nix0.17
ExclusiveArch: %{rust_arches}
- rust-nix0.18
ExclusiveArch: %{rust_arches}
- rust-nix0.20
ExclusiveArch: %{rust_arches}
- rust-no-panic
ExclusiveArch: %{rust_arches}
- rust-nodrop
ExclusiveArch: %{rust_arches}
- rust-nom
ExclusiveArch: %{rust_arches}
- rust-nom4
ExclusiveArch: %{rust_arches}
- rust-nom5
ExclusiveArch: %{rust_arches}
- rust-noop_proc_macro
ExclusiveArch: %{rust_arches}
- rust-normalize-line-endings
ExclusiveArch: %{rust_arches}
- rust-notify
ExclusiveArch: %{rust_arches}
- rust-notify-rust
ExclusiveArch: %{rust_arches}
- rust-num
ExclusiveArch: %{rust_arches}
- rust-num-bigint
ExclusiveArch: %{rust_arches}
- rust-num-bigint-dig
ExclusiveArch: %{rust_arches}
- rust-num-bigint0.3
ExclusiveArch: %{rust_arches}
- rust-num-complex
ExclusiveArch: %{rust_arches}
- rust-num-complex0.3
ExclusiveArch: %{rust_arches}
- rust-num-derive
ExclusiveArch: %{rust_arches}
- rust-num-integer
ExclusiveArch: %{rust_arches}
- rust-num-iter
ExclusiveArch: %{rust_arches}
- rust-num-rational
ExclusiveArch: %{rust_arches}
- rust-num-rational0.3
ExclusiveArch: %{rust_arches}
- rust-num-traits
ExclusiveArch: %{rust_arches}
- rust-num-traits0.1
ExclusiveArch: %{rust_arches}
- rust-num0.3
ExclusiveArch: %{rust_arches}
- rust-num_cpus
ExclusiveArch: %{rust_arches}
- rust-num_enum
ExclusiveArch: %{rust_arches}
- rust-num_enum_derive
ExclusiveArch: %{rust_arches}
- rust-number_prefix
ExclusiveArch: %{rust_arches}
- rust-numtoa
ExclusiveArch: %{rust_arches}
- rust-oauth2
ExclusiveArch: %{rust_arches}
- rust-obfstr
ExclusiveArch: %{rust_arches}
- rust-obj
ExclusiveArch: %{rust_arches}
- rust-object
ExclusiveArch: %{rust_arches}
- rust-odds
ExclusiveArch: %{rust_arches}
- rust-ogg
ExclusiveArch: %{rust_arches}
- rust-oid
ExclusiveArch: %{rust_arches}
- rust-once_cell
ExclusiveArch: %{rust_arches}
- rust-onig
ExclusiveArch: %{rust_arches}
- rust-onig_sys
ExclusiveArch: %{rust_arches}
- rust-oorandom
ExclusiveArch: %{rust_arches}
- rust-opaque-debug
ExclusiveArch: %{rust_arches}
- rust-open
ExclusiveArch: %{rust_arches}
- rust-openat
ExclusiveArch: %{rust_arches}
- rust-openat-ext
ExclusiveArch: %{rust_arches}
- rust-opener
ExclusiveArch: %{rust_arches}
- rust-openssh-keys
ExclusiveArch: %{rust_arches}
- rust-openssl
ExclusiveArch: %{rust_arches}
- rust-openssl-probe
ExclusiveArch: %{rust_arches}
- rust-openssl-sys
ExclusiveArch: %{rust_arches}
- rust-opml
ExclusiveArch: %{rust_arches}
- rust-ord_subset
ExclusiveArch: %{rust_arches}
- rust-ordered-float
ExclusiveArch: %{rust_arches}
- rust-ordered-multimap
ExclusiveArch: %{rust_arches}
- rust-os_info
ExclusiveArch: %{rust_arches}
- rust-os_pipe
ExclusiveArch: %{rust_arches}
- rust-os_str_bytes
ExclusiveArch: %{rust_arches}
- rust-osmesa-sys
ExclusiveArch: %{rust_arches}
- rust-osstrtools
ExclusiveArch: %{rust_arches}
- rust-ostree
ExclusiveArch: %{rust_arches}
- rust-ostree-sys
ExclusiveArch: %{rust_arches}
- rust-ouroboros
ExclusiveArch: %{rust_arches}
- rust-ouroboros_macro
ExclusiveArch: %{rust_arches}
- rust-owned-alloc
ExclusiveArch: %{rust_arches}
- rust-owned_ttf_parser
ExclusiveArch: %{rust_arches}
- rust-owning_ref
ExclusiveArch: %{rust_arches}
- rust-oxipng
ExclusiveArch: %{rust_arches}
- rust-packaging
ExclusiveArch: %{rust_arches}
- rust-pad
ExclusiveArch: %{rust_arches}
- rust-page_size
ExclusiveArch: %{rust_arches}
- rust-pager
ExclusiveArch: %{rust_arches}
- rust-palette
ExclusiveArch: %{rust_arches}
- rust-palette_derive
ExclusiveArch: %{rust_arches}
- rust-pam
ExclusiveArch: %{rust_arches}
- rust-pam-client
ExclusiveArch: %{rust_arches}
- rust-pam-sys
ExclusiveArch: %{rust_arches}
- rust-pancurses
ExclusiveArch: %{rust_arches}
- rust-pango
ExclusiveArch: %{rust_arches}
- rust-pango-sys
ExclusiveArch: %{rust_arches}
- rust-pangocairo
ExclusiveArch: %{rust_arches}
- rust-pangocairo-sys
ExclusiveArch: %{rust_arches}
- rust-parity-wasm
ExclusiveArch: %{rust_arches}
- rust-parking
ExclusiveArch: %{rust_arches}
- rust-parking_lot
ExclusiveArch: %{rust_arches}
- rust-parking_lot_core
ExclusiveArch: %{rust_arches}
- rust-parse-zoneinfo
ExclusiveArch: %{rust_arches}
- rust-parse_cfg
ExclusiveArch: %{rust_arches}
- rust-parsec-client
ExclusiveArch: %{rust_arches}
- rust-parsec-interface
ExclusiveArch: %{rust_arches}
- rust-partial-io
ExclusiveArch: %{rust_arches}
- rust-partition-identity
ExclusiveArch: %{rust_arches}
- rust-paste
ExclusiveArch: %{rust_arches}
- rust-paste-impl
ExclusiveArch: %{rust_arches}
- rust-path-absolutize
ExclusiveArch: %{rust_arches}
- rust-path-dedot
ExclusiveArch: %{rust_arches}
- rust-path-slash
ExclusiveArch: %{rust_arches}
- rust-pathdiff
ExclusiveArch: %{rust_arches}
- rust-pathfinder_geometry
ExclusiveArch: %{rust_arches}
- rust-pathfinder_simd
ExclusiveArch: %{rust_arches}
- rust-pbr
ExclusiveArch: %{rust_arches}
- rust-pcap
ExclusiveArch: %{rust_arches}
- rust-pcre2
ExclusiveArch: %{rust_arches}
- rust-pcre2-sys
ExclusiveArch: %{rust_arches}
- rust-peeking_take_while
ExclusiveArch: %{rust_arches}
- rust-peg
ExclusiveArch: %{rust_arches}
- rust-peg-macros
ExclusiveArch: %{rust_arches}
- rust-peg-runtime
ExclusiveArch: %{rust_arches}
- rust-peg0.5
ExclusiveArch: %{rust_arches}
- rust-pem
ExclusiveArch: %{rust_arches}
- rust-percent-encoding
ExclusiveArch: %{rust_arches}
- rust-percent-encoding1
ExclusiveArch: %{rust_arches}
- rust-peresil
ExclusiveArch: %{rust_arches}
- rust-permutate
ExclusiveArch: %{rust_arches}
- rust-permutohedron
ExclusiveArch: %{rust_arches}
- rust-pest
ExclusiveArch: %{rust_arches}
- rust-pest_derive
ExclusiveArch: %{rust_arches}
- rust-pest_generator
ExclusiveArch: %{rust_arches}
- rust-pest_meta
ExclusiveArch: %{rust_arches}
- rust-petgraph
ExclusiveArch: %{rust_arches}
- rust-phf
ExclusiveArch: %{rust_arches}
- rust-phf0.7
ExclusiveArch: %{rust_arches}
- rust-phf_codegen
ExclusiveArch: %{rust_arches}
- rust-phf_codegen0.7
ExclusiveArch: %{rust_arches}
- rust-phf_generator
ExclusiveArch: %{rust_arches}
- rust-phf_generator0.7
ExclusiveArch: %{rust_arches}
- rust-phf_macros
ExclusiveArch: %{rust_arches}
- rust-phf_macros0.7
ExclusiveArch: %{rust_arches}
- rust-phf_shared
ExclusiveArch: %{rust_arches}
- rust-phf_shared0.7
ExclusiveArch: %{rust_arches}
- rust-picky-asn1
ExclusiveArch: %{rust_arches}
- rust-picky-asn1-der
ExclusiveArch: %{rust_arches}
- rust-picky-asn1-x509
ExclusiveArch: %{rust_arches}
- rust-pico-args
ExclusiveArch: %{rust_arches}
- rust-pin-project
ExclusiveArch: %{rust_arches}
- rust-pin-project-internal
ExclusiveArch: %{rust_arches}
- rust-pin-project-internal0.4
ExclusiveArch: %{rust_arches}
- rust-pin-project-lite
ExclusiveArch: %{rust_arches}
- rust-pin-project-lite0.1
ExclusiveArch: %{rust_arches}
- rust-pin-project0.4
ExclusiveArch: %{rust_arches}
- rust-pin-utils
ExclusiveArch: %{rust_arches}
- rust-pipe
ExclusiveArch: %{rust_arches}
- rust-piper
ExclusiveArch: %{rust_arches}
- rust-pkcs11
ExclusiveArch: %{rust_arches}
- rust-pkg-config
ExclusiveArch: %{rust_arches}
- rust-plain
ExclusiveArch: %{rust_arches}
- rust-platform-dirs
ExclusiveArch: %{rust_arches}
- rust-platforms
ExclusiveArch: %{rust_arches}
- rust-pleaser
ExclusiveArch: %{rust_arches}
- rust-plist
ExclusiveArch: %{rust_arches}
- rust-plotters
ExclusiveArch: %{rust_arches}
- rust-plotters-backend
ExclusiveArch: %{rust_arches}
- rust-plotters-bitmap
ExclusiveArch: %{rust_arches}
- rust-plotters-svg
ExclusiveArch: %{rust_arches}
- rust-plugin
ExclusiveArch: %{rust_arches}
- rust-pnet_base
ExclusiveArch: %{rust_arches}
- rust-pnet_datalink
ExclusiveArch: %{rust_arches}
- rust-pnet_sys
ExclusiveArch: %{rust_arches}
- rust-png
ExclusiveArch: %{rust_arches}
- rust-podio
ExclusiveArch: %{rust_arches}
- rust-polling
ExclusiveArch: %{rust_arches}
- rust-polyval
ExclusiveArch: %{rust_arches}
- rust-pom
ExclusiveArch: %{rust_arches}
- rust-pommes
ExclusiveArch: %{rust_arches}
- rust-posish
ExclusiveArch: %{rust_arches}
- rust-ppv-lite86
ExclusiveArch: %{rust_arches}
- rust-pq-sys
ExclusiveArch: %{rust_arches}
- rust-precomputed-hash
ExclusiveArch: %{rust_arches}
- rust-predicates
ExclusiveArch: %{rust_arches}
- rust-predicates-core
ExclusiveArch: %{rust_arches}
- rust-predicates-tree
ExclusiveArch: %{rust_arches}
- rust-pretty-git-prompt
ExclusiveArch: %{rust_arches}
- rust-pretty-hex
ExclusiveArch: %{rust_arches}
- rust-pretty_assertions
ExclusiveArch: %{rust_arches}
- rust-pretty_assertions0.6
ExclusiveArch: %{rust_arches}
- rust-pretty_env_logger
ExclusiveArch: %{rust_arches}
- rust-prettytable-rs
ExclusiveArch: %{rust_arches}
- rust-proc-macro-crate
ExclusiveArch: %{rust_arches}
- rust-proc-macro-crate0.1
ExclusiveArch: %{rust_arches}
- rust-proc-macro-error
ExclusiveArch: %{rust_arches}
- rust-proc-macro-error-attr
ExclusiveArch: %{rust_arches}
- rust-proc-macro-hack
ExclusiveArch: %{rust_arches}
- rust-proc-macro-nested
ExclusiveArch: %{rust_arches}
- rust-proc-macro2
ExclusiveArch: %{rust_arches}
- rust-proc-macro2-0.4
ExclusiveArch: %{rust_arches}
- rust-proc-mounts
ExclusiveArch: %{rust_arches}
- rust-proc-quote
ExclusiveArch: %{rust_arches}
- rust-proc-quote-impl
ExclusiveArch: %{rust_arches}
- rust-procedural-masquerade
ExclusiveArch: %{rust_arches}
- rust-process_control
ExclusiveArch: %{rust_arches}
- rust-process_path
ExclusiveArch: %{rust_arches}
- rust-procfs
ExclusiveArch: %{rust_arches}
- rust-procs
ExclusiveArch: %{rust_arches}
- rust-progress-streams
ExclusiveArch: %{rust_arches}
- rust-prometheus
ExclusiveArch: %{rust_arches}
- rust-proptest
ExclusiveArch: %{rust_arches}
- rust-proptest-derive
ExclusiveArch: %{rust_arches}
- rust-proptest0.10
ExclusiveArch: %{rust_arches}
- rust-prost
ExclusiveArch: %{rust_arches}
- rust-prost-build
ExclusiveArch: %{rust_arches}
- rust-prost-derive
ExclusiveArch: %{rust_arches}
- rust-prost-types
ExclusiveArch: %{rust_arches}
- rust-protobuf
ExclusiveArch: %{rust_arches}
- rust-protobuf-codegen
ExclusiveArch: %{rust_arches}
- rust-protobuf-codegen-pure
ExclusiveArch: %{rust_arches}
- rust-protoc
ExclusiveArch: %{rust_arches}
- rust-protoc-rust
ExclusiveArch: %{rust_arches}
- rust-psa-crypto
ExclusiveArch: %{rust_arches}
- rust-psa-crypto-sys
ExclusiveArch: %{rust_arches}
- rust-psd
ExclusiveArch: %{rust_arches}
- rust-psm
ExclusiveArch: %{rust_arches}
- rust-psutil
ExclusiveArch: %{rust_arches}
- rust-ptr_meta
ExclusiveArch: %{rust_arches}
- rust-ptr_meta_derive
ExclusiveArch: %{rust_arches}
- rust-publicsuffix
ExclusiveArch: %{rust_arches}
- rust-pulldown-cmark
ExclusiveArch: %{rust_arches}
- rust-pulse
ExclusiveArch: %{rust_arches}
- rust-pure-rust-locales
ExclusiveArch: %{rust_arches}
- rust-pyo3
ExclusiveArch: %{rust_arches}
- rust-pyo3-macros
ExclusiveArch: %{rust_arches}
- rust-pyo3-macros-backend
ExclusiveArch: %{rust_arches}
- rust-python3-sys
ExclusiveArch: %{rust_arches}
- rust-qr2term
ExclusiveArch: %{rust_arches}
- rust-qrcode
ExclusiveArch: %{rust_arches}
- rust-qstring
ExclusiveArch: %{rust_arches}
- rust-quad-rand
ExclusiveArch: %{rust_arches}
- rust-quick-error
ExclusiveArch: %{rust_arches}
- rust-quick-error1
ExclusiveArch: %{rust_arches}
- rust-quick-xml
ExclusiveArch: %{rust_arches}
- rust-quick-xml0.20
ExclusiveArch: %{rust_arches}
- rust-quickcheck
ExclusiveArch: %{rust_arches}
- rust-quickcheck0.6
ExclusiveArch: %{rust_arches}
- rust-quickcheck0.9
ExclusiveArch: %{rust_arches}
- rust-quickcheck_macros
ExclusiveArch: %{rust_arches}
- rust-quickersort
ExclusiveArch: %{rust_arches}
- rust-quote
ExclusiveArch: %{rust_arches}
- rust-quote0.3
ExclusiveArch: %{rust_arches}
- rust-quote0.6
ExclusiveArch: %{rust_arches}
- rust-quoted_printable
ExclusiveArch: %{rust_arches}
- rust-r2d2
ExclusiveArch: %{rust_arches}
- rust-radium
ExclusiveArch: %{rust_arches}
- rust-radix64
ExclusiveArch: %{rust_arches}
- rust-radix_trie
ExclusiveArch: %{rust_arches}
- rust-rand
ExclusiveArch: %{rust_arches}
- rust-rand0.4
ExclusiveArch: %{rust_arches}
- rust-rand0.5
ExclusiveArch: %{rust_arches}
- rust-rand0.6
ExclusiveArch: %{rust_arches}
- rust-rand0.7
ExclusiveArch: %{rust_arches}
- rust-rand_chacha
ExclusiveArch: %{rust_arches}
- rust-rand_chacha0.1
ExclusiveArch: %{rust_arches}
- rust-rand_chacha0.2
ExclusiveArch: %{rust_arches}
- rust-rand_core
ExclusiveArch: %{rust_arches}
- rust-rand_core0.3
ExclusiveArch: %{rust_arches}
- rust-rand_core0.4
ExclusiveArch: %{rust_arches}
- rust-rand_core0.5
ExclusiveArch: %{rust_arches}
- rust-rand_distr
ExclusiveArch: %{rust_arches}
- rust-rand_distr0.2
ExclusiveArch: %{rust_arches}
- rust-rand_hc
ExclusiveArch: %{rust_arches}
- rust-rand_hc0.1
ExclusiveArch: %{rust_arches}
- rust-rand_isaac
ExclusiveArch: %{rust_arches}
- rust-rand_isaac0.1
ExclusiveArch: %{rust_arches}
- rust-rand_jitter
ExclusiveArch: %{rust_arches}
- rust-rand_jitter0.1
ExclusiveArch: %{rust_arches}
- rust-rand_os
ExclusiveArch: %{rust_arches}
- rust-rand_os0.1
ExclusiveArch: %{rust_arches}
- rust-rand_pcg
ExclusiveArch: %{rust_arches}
- rust-rand_pcg0.1
ExclusiveArch: %{rust_arches}
- rust-rand_pcg0.2
ExclusiveArch: %{rust_arches}
- rust-rand_xorshift
ExclusiveArch: %{rust_arches}
- rust-rand_xorshift0.1
ExclusiveArch: %{rust_arches}
- rust-rand_xorshift0.2
ExclusiveArch: %{rust_arches}
- rust-rand_xoshiro
ExclusiveArch: %{rust_arches}
- rust-random-fast-rng
ExclusiveArch: %{rust_arches}
- rust-random-trait
ExclusiveArch: %{rust_arches}
- rust-randomize
ExclusiveArch: %{rust_arches}
- rust-rav1e
ExclusiveArch: %{rust_arches}
- rust-raw-window-handle
ExclusiveArch: %{rust_arches}
- rust-rawpointer
ExclusiveArch: %{rust_arches}
- rust-rawslice
ExclusiveArch: %{rust_arches}
- rust-rayon
ExclusiveArch: %{rust_arches}
- rust-rayon-core
ExclusiveArch: %{rust_arches}
- rust-read_input
ExclusiveArch: %{rust_arches}
- rust-readability-fork
ExclusiveArch: %{rust_arches}
- rust-readwrite
ExclusiveArch: %{rust_arches}
- rust-recycler
ExclusiveArch: %{rust_arches}
- rust-ref-cast
ExclusiveArch: %{rust_arches}
- rust-ref-cast-impl
ExclusiveArch: %{rust_arches}
- rust-regalloc
ExclusiveArch: %{rust_arches}
- rust-regex
ExclusiveArch: %{rust_arches}
- rust-regex-automata
ExclusiveArch: %{rust_arches}
- rust-regex-syntax
ExclusiveArch: %{rust_arches}
- rust-region
ExclusiveArch: %{rust_arches}
- rust-relative-path
ExclusiveArch: %{rust_arches}
- rust-relay
ExclusiveArch: %{rust_arches}
- rust-remove_dir_all
ExclusiveArch: %{rust_arches}
- rust-rend
ExclusiveArch: %{rust_arches}
- rust-rental
ExclusiveArch: %{rust_arches}
- rust-rental-impl
ExclusiveArch: %{rust_arches}
- rust-reqwest
ExclusiveArch: %{rust_arches}
- rust-reqwest0.10
ExclusiveArch: %{rust_arches}
- rust-resize
ExclusiveArch: %{rust_arches}
- rust-resolv-conf
ExclusiveArch: %{rust_arches}
- rust-restson
ExclusiveArch: %{rust_arches}
- rust-retry
ExclusiveArch: %{rust_arches}
- rust-rgb
ExclusiveArch: %{rust_arches}
- rust-ring
ExclusiveArch: %{rust_arches}
- rust-ripgrep
ExclusiveArch: %{rust_arches}
- rust-rkyv
ExclusiveArch: %{rust_arches}
- rust-rkyv_derive
ExclusiveArch: %{rust_arches}
- rust-rle-decode-fast
ExclusiveArch: %{rust_arches}
- rust-rmp
ExclusiveArch: %{rust_arches}
- rust-rmp-serde
ExclusiveArch: %{rust_arches}
- rust-rodio
ExclusiveArch: %{rust_arches}
- rust-roff
ExclusiveArch: %{rust_arches}
- rust-ron
ExclusiveArch: %{rust_arches}
- rust-roxmltree
ExclusiveArch: %{rust_arches}
- rust-rpassword
ExclusiveArch: %{rust_arches}
- rust-rpick
ExclusiveArch: %{rust_arches}
- rust-rsa
ExclusiveArch: %{rust_arches}
- rust-rspec
ExclusiveArch: %{rust_arches}
- rust-rtnetlink
ExclusiveArch: %{rust_arches}
- rust-rudo
ExclusiveArch: %{rust_arches}
- rust-ruma
ExclusiveArch: %{rust_arches}
- rust-ruma-api
ExclusiveArch: %{rust_arches}
- rust-ruma-api-macros
ExclusiveArch: %{rust_arches}
- rust-ruma-appservice-api
ExclusiveArch: %{rust_arches}
- rust-ruma-client-api
ExclusiveArch: %{rust_arches}
- rust-ruma-common
ExclusiveArch: %{rust_arches}
- rust-ruma-events
ExclusiveArch: %{rust_arches}
- rust-ruma-events-macros
ExclusiveArch: %{rust_arches}
- rust-ruma-federation-api
ExclusiveArch: %{rust_arches}
- rust-ruma-identifiers
ExclusiveArch: %{rust_arches}
- rust-ruma-identifiers-macros
ExclusiveArch: %{rust_arches}
- rust-ruma-identifiers-validation
ExclusiveArch: %{rust_arches}
- rust-ruma-identity-service-api
ExclusiveArch: %{rust_arches}
- rust-ruma-push-gateway-api
ExclusiveArch: %{rust_arches}
- rust-ruma-serde
ExclusiveArch: %{rust_arches}
- rust-ruma-serde-macros
ExclusiveArch: %{rust_arches}
- rust-ruma-signatures
ExclusiveArch: %{rust_arches}
- rust-rusqlite
ExclusiveArch: %{rust_arches}
- rust-rust-embed
ExclusiveArch: %{rust_arches}
- rust-rust-embed-impl
ExclusiveArch: %{rust_arches}
- rust-rust-embed-utils
ExclusiveArch: %{rust_arches}
- rust-rust-ini
ExclusiveArch: %{rust_arches}
- rust-rust-stemmers
ExclusiveArch: %{rust_arches}
- rust-rust_decimal
ExclusiveArch: %{rust_arches}
- rust-rust_hawktracer
ExclusiveArch: %{rust_arches}
- rust-rust_hawktracer_normal_macro
ExclusiveArch: %{rust_arches}
- rust-rust_hawktracer_proc_macro
ExclusiveArch: %{rust_arches}
- rust-rust_hawktracer_sys
ExclusiveArch: %{rust_arches}
- rust-rustbus
ExclusiveArch: %{rust_arches}
- rust-rustbus_derive
ExclusiveArch: %{rust_arches}
- rust-rustc-demangle
ExclusiveArch: %{rust_arches}
- rust-rustc-hash
ExclusiveArch: %{rust_arches}
- rust-rustc-serialize
ExclusiveArch: %{rust_arches}
- rust-rustc-test
ExclusiveArch: %{rust_arches}
- rust-rustc_tools_util
ExclusiveArch: %{rust_arches}
- rust-rustc_version
ExclusiveArch: %{rust_arches}
- rust-rustc_version0.3
ExclusiveArch: %{rust_arches}
- rust-rustdoc-stripper
ExclusiveArch: %{rust_arches}
- rust-rustfilt
ExclusiveArch: %{rust_arches}
- rust-rustfix
ExclusiveArch: %{rust_arches}
- rust-rustio
ExclusiveArch: %{rust_arches}
- rust-rustls
ExclusiveArch: %{rust_arches}
- rust-rustls-native-certs
ExclusiveArch: %{rust_arches}
- rust-rusttype
ExclusiveArch: %{rust_arches}
- rust-rustversion
ExclusiveArch: %{rust_arches}
- rust-rusty-fork
ExclusiveArch: %{rust_arches}
- rust-rustyline
ExclusiveArch: %{rust_arches}
- rust-rustyline-derive
ExclusiveArch: %{rust_arches}
- rust-ryu
ExclusiveArch: %{rust_arches}
- rust-safe-transmute
ExclusiveArch: %{rust_arches}
- rust-safe_arch
ExclusiveArch: %{rust_arches}
- rust-safemem
ExclusiveArch: %{rust_arches}
- rust-same-file
ExclusiveArch: %{rust_arches}
- rust-sapp-kms
ExclusiveArch: %{rust_arches}
- rust-sapp-linux
ExclusiveArch: %{rust_arches}
- rust-sass-rs
ExclusiveArch: %{rust_arches}
- rust-sass-sys
ExclusiveArch: %{rust_arches}
- rust-scan_fmt
ExclusiveArch: %{rust_arches}
- rust-scheduled-thread-pool
ExclusiveArch: %{rust_arches}
- rust-scoped-tls
ExclusiveArch: %{rust_arches}
- rust-scoped-tls-hkt
ExclusiveArch: %{rust_arches}
- rust-scoped_threadpool
ExclusiveArch: %{rust_arches}
- rust-scopeguard
ExclusiveArch: %{rust_arches}
- rust-scopetime
ExclusiveArch: %{rust_arches}
- rust-scratch
ExclusiveArch: %{rust_arches}
- rust-scroll
ExclusiveArch: %{rust_arches}
- rust-scroll_derive
ExclusiveArch: %{rust_arches}
- rust-sct
ExclusiveArch: %{rust_arches}
- rust-sd
ExclusiveArch: %{rust_arches}
- rust-sd-notify
ExclusiveArch: %{rust_arches}
- rust-seahash
ExclusiveArch: %{rust_arches}
- rust-seahash3
ExclusiveArch: %{rust_arches}
- rust-secrecy
ExclusiveArch: %{rust_arches}
- rust-secret-service
ExclusiveArch: %{rust_arches}
- rust-semver
ExclusiveArch: %{rust_arches}
- rust-semver-parser
ExclusiveArch: %{rust_arches}
- rust-semver-parser0.7
ExclusiveArch: %{rust_arches}
- rust-semver-parser0.9
ExclusiveArch: %{rust_arches}
- rust-semver0.10
ExclusiveArch: %{rust_arches}
- rust-semver0.11
ExclusiveArch: %{rust_arches}
- rust-sequoia-autocrypt
ExclusiveArch: %{rust_arches}
- rust-sequoia-ipc
ExclusiveArch: %{rust_arches}
- rust-sequoia-keyring-linter
ExclusiveArch: %{rust_arches}
- rust-sequoia-net
ExclusiveArch: %{rust_arches}
- rust-sequoia-octopus-librnp
ExclusiveArch: %{rust_arches}
- rust-sequoia-openpgp
ExclusiveArch: %{rust_arches}
- rust-sequoia-sop
ExclusiveArch: %{rust_arches}
- rust-sequoia-sq
ExclusiveArch: %{rust_arches}
- rust-sequoia-sqv
ExclusiveArch: %{rust_arches}
- rust-serde
ExclusiveArch: %{rust_arches}
- rust-serde-big-array
ExclusiveArch: %{rust_arches}
- rust-serde-pickle
ExclusiveArch: %{rust_arches}
- rust-serde-value
ExclusiveArch: %{rust_arches}
- rust-serde-xml-rs
ExclusiveArch: %{rust_arches}
- rust-serde_bser
ExclusiveArch: %{rust_arches}
- rust-serde_bytes
ExclusiveArch: %{rust_arches}
- rust-serde_cbor
ExclusiveArch: %{rust_arches}
- rust-serde_derive
ExclusiveArch: %{rust_arches}
- rust-serde_fmt
ExclusiveArch: %{rust_arches}
- rust-serde_ignored
ExclusiveArch: %{rust_arches}
- rust-serde_json
ExclusiveArch: %{rust_arches}
- rust-serde_repr
ExclusiveArch: %{rust_arches}
- rust-serde_stacker
ExclusiveArch: %{rust_arches}
- rust-serde_test
ExclusiveArch: %{rust_arches}
- rust-serde_url_params
ExclusiveArch: %{rust_arches}
- rust-serde_urlencoded
ExclusiveArch: %{rust_arches}
- rust-serde_urlencoded0.6
ExclusiveArch: %{rust_arches}
- rust-serde_with_macros
ExclusiveArch: %{rust_arches}
- rust-serde_yaml
ExclusiveArch: %{rust_arches}
- rust-serial-core
ExclusiveArch: %{rust_arches}
- rust-serial_test
ExclusiveArch: %{rust_arches}
- rust-serial_test_derive
ExclusiveArch: %{rust_arches}
- rust-servo-fontconfig
ExclusiveArch: %{rust_arches}
- rust-servo-fontconfig-sys
ExclusiveArch: %{rust_arches}
- rust-sev
ExclusiveArch: %{rust_arches}
- rust-sevctl
ExclusiveArch: %{rust_arches}
- rust-sha-1
ExclusiveArch: %{rust_arches}
- rust-sha1
ExclusiveArch: %{rust_arches}
- rust-sha1collisiondetection
ExclusiveArch: %{rust_arches}
- rust-sha2
ExclusiveArch: %{rust_arches}
- rust-sha3
ExclusiveArch: %{rust_arches}
- rust-shadow-rs
ExclusiveArch: %{rust_arches}
- rust-shared_child
ExclusiveArch: %{rust_arches}
- rust-shared_library
ExclusiveArch: %{rust_arches}
- rust-shell-escape
ExclusiveArch: %{rust_arches}
- rust-shell-words
ExclusiveArch: %{rust_arches}
- rust-shellexpand
ExclusiveArch: %{rust_arches}
- rust-shellwords
ExclusiveArch: %{rust_arches}
- rust-shlex
ExclusiveArch: %{rust_arches}
- rust-signal-hook
ExclusiveArch: %{rust_arches}
- rust-signal-hook-registry
ExclusiveArch: %{rust_arches}
- rust-signal-hook0.1
ExclusiveArch: %{rust_arches}
- rust-silver
ExclusiveArch: %{rust_arches}
- rust-simba
ExclusiveArch: %{rust_arches}
- rust-simd_helpers
ExclusiveArch: %{rust_arches}
- rust-simdutf8
ExclusiveArch: %{rust_arches}
- rust-simple_asn1
ExclusiveArch: %{rust_arches}
- rust-simple_logger
ExclusiveArch: %{rust_arches}
- rust-simplelog
ExclusiveArch: %{rust_arches}
- rust-siphasher
ExclusiveArch: %{rust_arches}
- rust-size
ExclusiveArch: %{rust_arches}
- rust-sized-chunks
ExclusiveArch: %{rust_arches}
- rust-skeptic
ExclusiveArch: %{rust_arches}
- rust-skim
ExclusiveArch: %{rust_arches}
- rust-slab
ExclusiveArch: %{rust_arches}
- rust-slash-formatter
ExclusiveArch: %{rust_arches}
- rust-slice-deque
ExclusiveArch: %{rust_arches}
- rust-slog
ExclusiveArch: %{rust_arches}
- rust-slog-async
ExclusiveArch: %{rust_arches}
- rust-slog-scope
ExclusiveArch: %{rust_arches}
- rust-slog-term
ExclusiveArch: %{rust_arches}
- rust-slotmap
ExclusiveArch: %{rust_arches}
- rust-slug
ExclusiveArch: %{rust_arches}
- rust-sluice
ExclusiveArch: %{rust_arches}
- rust-smallstr
ExclusiveArch: %{rust_arches}
- rust-smallvec
ExclusiveArch: %{rust_arches}
- rust-smart-default
ExclusiveArch: %{rust_arches}
- rust-smawk
ExclusiveArch: %{rust_arches}
- rust-smithay-client-toolkit
ExclusiveArch: %{rust_arches}
- rust-smithay-client-toolkit0.12
ExclusiveArch: %{rust_arches}
- rust-smithay-clipboard
ExclusiveArch: %{rust_arches}
- rust-snafu
ExclusiveArch: %{rust_arches}
- rust-snafu-derive
ExclusiveArch: %{rust_arches}
- rust-snake_case
ExclusiveArch: %{rust_arches}
- rust-snowflake
ExclusiveArch: %{rust_arches}
- rust-socket2
ExclusiveArch: %{rust_arches}
- rust-socket2_0.3
ExclusiveArch: %{rust_arches}
- rust-sop
ExclusiveArch: %{rust_arches}
- rust-soup-sys
ExclusiveArch: %{rust_arches}
- rust-spin
ExclusiveArch: %{rust_arches}
- rust-spin_on
ExclusiveArch: %{rust_arches}
- rust-spinning_top
ExclusiveArch: %{rust_arches}
- rust-spmc
ExclusiveArch: %{rust_arches}
- rust-ssh-key-dir
ExclusiveArch: %{rust_arches}
- rust-stable_deref_trait
ExclusiveArch: %{rust_arches}
- rust-stacker
ExclusiveArch: %{rust_arches}
- rust-standback
ExclusiveArch: %{rust_arches}
- rust-starship
ExclusiveArch: %{rust_arches}
- rust-starship_module_config_derive
ExclusiveArch: %{rust_arches}
- rust-static_assertions
ExclusiveArch: %{rust_arches}
- rust-statistical
ExclusiveArch: %{rust_arches}
- rust-stb_truetype
ExclusiveArch: %{rust_arches}
- rust-stderrlog
ExclusiveArch: %{rust_arches}
- rust-streaming-stats
ExclusiveArch: %{rust_arches}
- rust-string
ExclusiveArch: %{rust_arches}
- rust-string_cache
ExclusiveArch: %{rust_arches}
- rust-string_cache_codegen
ExclusiveArch: %{rust_arches}
- rust-string_cache_shared
ExclusiveArch: %{rust_arches}
- rust-strings
ExclusiveArch: %{rust_arches}
- rust-strip-ansi-escapes
ExclusiveArch: %{rust_arches}
- rust-strong-xml
ExclusiveArch: %{rust_arches}
- rust-strong-xml-derive
ExclusiveArch: %{rust_arches}
- rust-strsim
ExclusiveArch: %{rust_arches}
- rust-structopt
ExclusiveArch: %{rust_arches}
- rust-structopt-derive
ExclusiveArch: %{rust_arches}
- rust-structopt-derive0.2
ExclusiveArch: %{rust_arches}
- rust-structopt0.2
ExclusiveArch: %{rust_arches}
- rust-strum
ExclusiveArch: %{rust_arches}
- rust-strum_macros
ExclusiveArch: %{rust_arches}
- rust-subtle
ExclusiveArch: %{rust_arches}
- rust-sudo_plugin
ExclusiveArch: %{rust_arches}
- rust-sudo_plugin-sys
ExclusiveArch: %{rust_arches}
- rust-sval
ExclusiveArch: %{rust_arches}
- rust-sval_derive
ExclusiveArch: %{rust_arches}
- rust-sval_json
ExclusiveArch: %{rust_arches}
- rust-svg_metadata
ExclusiveArch: %{rust_arches}
- rust-svgfilters
ExclusiveArch: %{rust_arches}
- rust-svgtypes
ExclusiveArch: %{rust_arches}
- rust-sxd-document
ExclusiveArch: %{rust_arches}
- rust-syn
ExclusiveArch: %{rust_arches}
- rust-syn-mid
ExclusiveArch: %{rust_arches}
- rust-syn0.15
ExclusiveArch: %{rust_arches}
- rust-synom
ExclusiveArch: %{rust_arches}
- rust-synstructure
ExclusiveArch: %{rust_arches}
- rust-syntect
ExclusiveArch: %{rust_arches}
- rust-sys-info
ExclusiveArch: %{rust_arches}
- rust-sysctl
ExclusiveArch: %{rust_arches}
- rust-sysinfo
ExclusiveArch: %{rust_arches}
- rust-syslog
ExclusiveArch: %{rust_arches}
- rust-system-deps
ExclusiveArch: %{rust_arches}
- rust-systemd
ExclusiveArch: %{rust_arches}
- rust-tabular
ExclusiveArch: %{rust_arches}
- rust-tabwriter
ExclusiveArch: %{rust_arches}
- rust-take
ExclusiveArch: %{rust_arches}
- rust-take_mut
ExclusiveArch: %{rust_arches}
- rust-takeable
ExclusiveArch: %{rust_arches}
- rust-takeable-option
ExclusiveArch: %{rust_arches}
- rust-tap
ExclusiveArch: %{rust_arches}
- rust-tar
ExclusiveArch: %{rust_arches}
- rust-target
ExclusiveArch: %{rust_arches}
- rust-target-lexicon
ExclusiveArch: %{rust_arches}
- rust-tealdeer
ExclusiveArch: %{rust_arches}
- rust-teloxide
ExclusiveArch: %{rust_arches}
- rust-teloxide-macros
ExclusiveArch: %{rust_arches}
- rust-tempdir
ExclusiveArch: %{rust_arches}
- rust-tempfile
ExclusiveArch: %{rust_arches}
- rust-temptree
ExclusiveArch: %{rust_arches}
- rust-tendril
ExclusiveArch: %{rust_arches}
- rust-tera
ExclusiveArch: %{rust_arches}
- rust-term
ExclusiveArch: %{rust_arches}
- rust-term0.6
ExclusiveArch: %{rust_arches}
- rust-term_grid
ExclusiveArch: %{rust_arches}
- rust-term_size
ExclusiveArch: %{rust_arches}
- rust-termcolor
ExclusiveArch: %{rust_arches}
- rust-terminal_size
ExclusiveArch: %{rust_arches}
- rust-terminfo
ExclusiveArch: %{rust_arches}
- rust-termion
ExclusiveArch: %{rust_arches}
- rust-termios
ExclusiveArch: %{rust_arches}
- rust-test-assembler
ExclusiveArch: %{rust_arches}
- rust-tester
ExclusiveArch: %{rust_arches}
- rust-testing_logger
ExclusiveArch: %{rust_arches}
- rust-textwrap
ExclusiveArch: %{rust_arches}
- rust-textwrap0.11
ExclusiveArch: %{rust_arches}
- rust-textwrap0.12
ExclusiveArch: %{rust_arches}
- rust-thiserror
ExclusiveArch: %{rust_arches}
- rust-thiserror-impl
ExclusiveArch: %{rust_arches}
- rust-thread-id
ExclusiveArch: %{rust_arches}
- rust-thread-scoped
ExclusiveArch: %{rust_arches}
- rust-thread_local
ExclusiveArch: %{rust_arches}
- rust-threadpool
ExclusiveArch: %{rust_arches}
- rust-tiff
ExclusiveArch: %{rust_arches}
- rust-tiger-digest
ExclusiveArch: %{rust_arches}
- rust-time
ExclusiveArch: %{rust_arches}
- rust-time-macros
ExclusiveArch: %{rust_arches}
- rust-time-macros-impl
ExclusiveArch: %{rust_arches}
- rust-time0.1
ExclusiveArch: %{rust_arches}
- rust-timebomb
ExclusiveArch: %{rust_arches}
- rust-timer
ExclusiveArch: %{rust_arches}
- rust-timerfd
ExclusiveArch: %{rust_arches}
- rust-tiny-keccak
ExclusiveArch: %{rust_arches}
- rust-tiny_http
ExclusiveArch: %{rust_arches}
- rust-tinyfiledialogs
ExclusiveArch: %{rust_arches}
- rust-tinytemplate
ExclusiveArch: %{rust_arches}
- rust-tinyvec
ExclusiveArch: %{rust_arches}
- rust-tinyvec_macros
ExclusiveArch: %{rust_arches}
- rust-tokei
ExclusiveArch: %{rust_arches}
- rust-tokio
ExclusiveArch: %{rust_arches}
- rust-tokio-codec
ExclusiveArch: %{rust_arches}
- rust-tokio-core
ExclusiveArch: %{rust_arches}
- rust-tokio-current-thread
ExclusiveArch: %{rust_arches}
- rust-tokio-executor
ExclusiveArch: %{rust_arches}
- rust-tokio-fs
ExclusiveArch: %{rust_arches}
- rust-tokio-io
ExclusiveArch: %{rust_arches}
- rust-tokio-io-pool
ExclusiveArch: %{rust_arches}
- rust-tokio-macros
ExclusiveArch: %{rust_arches}
- rust-tokio-macros0.2
ExclusiveArch: %{rust_arches}
- rust-tokio-mock-task
ExclusiveArch: %{rust_arches}
- rust-tokio-native-tls
ExclusiveArch: %{rust_arches}
- rust-tokio-openssl
ExclusiveArch: %{rust_arches}
- rust-tokio-openssl0.4
ExclusiveArch: %{rust_arches}
- rust-tokio-reactor
ExclusiveArch: %{rust_arches}
- rust-tokio-rustls
ExclusiveArch: %{rust_arches}
- rust-tokio-socks
ExclusiveArch: %{rust_arches}
- rust-tokio-socks0.3
ExclusiveArch: %{rust_arches}
- rust-tokio-stream
ExclusiveArch: %{rust_arches}
- rust-tokio-sync
ExclusiveArch: %{rust_arches}
- rust-tokio-tcp
ExclusiveArch: %{rust_arches}
- rust-tokio-test
ExclusiveArch: %{rust_arches}
- rust-tokio-threadpool
ExclusiveArch: %{rust_arches}
- rust-tokio-timer
ExclusiveArch: %{rust_arches}
- rust-tokio-tls
ExclusiveArch: %{rust_arches}
- rust-tokio-tls0.2
ExclusiveArch: %{rust_arches}
- rust-tokio-tungstenite
ExclusiveArch: %{rust_arches}
- rust-tokio-udp
ExclusiveArch: %{rust_arches}
- rust-tokio-uds
ExclusiveArch: %{rust_arches}
- rust-tokio-util
ExclusiveArch: %{rust_arches}
- rust-tokio-util0.3
ExclusiveArch: %{rust_arches}
- rust-tokio0.1
ExclusiveArch: %{rust_arches}
- rust-tokio0.2
ExclusiveArch: %{rust_arches}
- rust-toml
ExclusiveArch: %{rust_arches}
- rust-toml0.4
ExclusiveArch: %{rust_arches}
- rust-tower-layer
ExclusiveArch: %{rust_arches}
- rust-tower-service
ExclusiveArch: %{rust_arches}
- rust-tower-test
ExclusiveArch: %{rust_arches}
- rust-tower-util
ExclusiveArch: %{rust_arches}
- rust-tpm2-policy
ExclusiveArch: %{rust_arches}
- rust-tracing
ExclusiveArch: %{rust_arches}
- rust-tracing-attributes
ExclusiveArch: %{rust_arches}
- rust-tracing-core
ExclusiveArch: %{rust_arches}
- rust-tracing-futures
ExclusiveArch: %{rust_arches}
- rust-traitobject
ExclusiveArch: %{rust_arches}
- rust-treebitmap
ExclusiveArch: %{rust_arches}
- rust-treeline
ExclusiveArch: %{rust_arches}
- rust-trust-dns-https
ExclusiveArch: %{rust_arches}
- rust-trust-dns-native-tls
ExclusiveArch: %{rust_arches}
- rust-trust-dns-native-tls0.19
ExclusiveArch: %{rust_arches}
- rust-trust-dns-openssl
ExclusiveArch: %{rust_arches}
- rust-trust-dns-openssl0.19
ExclusiveArch: %{rust_arches}
- rust-trust-dns-proto
ExclusiveArch: %{rust_arches}
- rust-trust-dns-proto0.19
ExclusiveArch: %{rust_arches}
- rust-trust-dns-resolver
ExclusiveArch: %{rust_arches}
- rust-trust-dns-resolver0.19
ExclusiveArch: %{rust_arches}
- rust-trust-dns-rustls
ExclusiveArch: %{rust_arches}
- rust-try-lock
ExclusiveArch: %{rust_arches}
- rust-try_from
ExclusiveArch: %{rust_arches}
- rust-try_or
ExclusiveArch: %{rust_arches}
- rust-trybuild
ExclusiveArch: %{rust_arches}
- rust-tss-esapi
ExclusiveArch: %{rust_arches}
- rust-tss-esapi-sys
ExclusiveArch: %{rust_arches}
- rust-ttf-parser
ExclusiveArch: %{rust_arches}
- rust-tui
ExclusiveArch: %{rust_arches}
- rust-tui-react
ExclusiveArch: %{rust_arches}
- rust-tui0.11
ExclusiveArch: %{rust_arches}
- rust-tui0.9
ExclusiveArch: %{rust_arches}
- rust-tuikit
ExclusiveArch: %{rust_arches}
- rust-tungstenite
ExclusiveArch: %{rust_arches}
- rust-twoway
ExclusiveArch: %{rust_arches}
- rust-typeable
ExclusiveArch: %{rust_arches}
- rust-typed-arena
ExclusiveArch: %{rust_arches}
- rust-typemap
ExclusiveArch: %{rust_arches}
- rust-typenum
ExclusiveArch: %{rust_arches}
- rust-typetag
ExclusiveArch: %{rust_arches}
- rust-typetag-impl
ExclusiveArch: %{rust_arches}
- rust-tzfile
ExclusiveArch: %{rust_arches}
- rust-ucd-parse
ExclusiveArch: %{rust_arches}
- rust-ucd-trie
ExclusiveArch: %{rust_arches}
- rust-ucd-util
ExclusiveArch: %{rust_arches}
- rust-unchecked-index
ExclusiveArch: %{rust_arches}
- rust-unescape
ExclusiveArch: %{rust_arches}
- rust-unic-char-property
ExclusiveArch: %{rust_arches}
- rust-unic-char-range
ExclusiveArch: %{rust_arches}
- rust-unic-common
ExclusiveArch: %{rust_arches}
- rust-unic-segment
ExclusiveArch: %{rust_arches}
- rust-unic-ucd-category
ExclusiveArch: %{rust_arches}
- rust-unic-ucd-common
ExclusiveArch: %{rust_arches}
- rust-unic-ucd-segment
ExclusiveArch: %{rust_arches}
- rust-unic-ucd-version
ExclusiveArch: %{rust_arches}
- rust-unicase
ExclusiveArch: %{rust_arches}
- rust-unicase1
ExclusiveArch: %{rust_arches}
- rust-unicode-bidi
ExclusiveArch: %{rust_arches}
- rust-unicode-linebreak
ExclusiveArch: %{rust_arches}
- rust-unicode-normalization
ExclusiveArch: %{rust_arches}
- rust-unicode-segmentation
ExclusiveArch: %{rust_arches}
- rust-unicode-truncate
ExclusiveArch: %{rust_arches}
- rust-unicode-width
ExclusiveArch: %{rust_arches}
- rust-unicode-xid
ExclusiveArch: %{rust_arches}
- rust-unicode-xid0.1
ExclusiveArch: %{rust_arches}
- rust-unicode_categories
ExclusiveArch: %{rust_arches}
- rust-unindent
ExclusiveArch: %{rust_arches}
- rust-universal-hash
ExclusiveArch: %{rust_arches}
- rust-unix_socket
ExclusiveArch: %{rust_arches}
- rust-unreachable
ExclusiveArch: %{rust_arches}
- rust-unsafe-any
ExclusiveArch: %{rust_arches}
- rust-unsafe-io
ExclusiveArch: %{rust_arches}
- rust-untrusted
ExclusiveArch: %{rust_arches}
- rust-uom
ExclusiveArch: %{rust_arches}
- rust-url
ExclusiveArch: %{rust_arches}
- rust-url1
ExclusiveArch: %{rust_arches}
- rust-url_serde
ExclusiveArch: %{rust_arches}
- rust-urlencoding
ExclusiveArch: %{rust_arches}
- rust-urlocator
ExclusiveArch: %{rust_arches}
- rust-urlshortener
ExclusiveArch: %{rust_arches}
- rust-users
ExclusiveArch: %{rust_arches}
- rust-users0.10
ExclusiveArch: %{rust_arches}
- rust-utf-8
ExclusiveArch: %{rust_arches}
- rust-utf8-cstr
ExclusiveArch: %{rust_arches}
- rust-utf8-ranges
ExclusiveArch: %{rust_arches}
- rust-utf8-width
ExclusiveArch: %{rust_arches}
- rust-utf8parse
ExclusiveArch: %{rust_arches}
- rust-uuid
ExclusiveArch: %{rust_arches}
- rust-uuid0.7
ExclusiveArch: %{rust_arches}
- rust-v_escape
ExclusiveArch: %{rust_arches}
- rust-v_escape_derive
ExclusiveArch: %{rust_arches}
- rust-v_frame
ExclusiveArch: %{rust_arches}
- rust-v_htmlescape
ExclusiveArch: %{rust_arches}
- rust-value-bag
ExclusiveArch: %{rust_arches}
- rust-varlink
ExclusiveArch: %{rust_arches}
- rust-varlink-cli
ExclusiveArch: %{rust_arches}
- rust-varlink_generator
ExclusiveArch: %{rust_arches}
- rust-varlink_parser
ExclusiveArch: %{rust_arches}
- rust-varlink_stdinterfaces
ExclusiveArch: %{rust_arches}
- rust-vcpkg
ExclusiveArch: %{rust_arches}
- rust-vec-arena
ExclusiveArch: %{rust_arches}
- rust-vec_map
ExclusiveArch: %{rust_arches}
- rust-vergen
ExclusiveArch: %{rust_arches}
- rust-vergen3
ExclusiveArch: %{rust_arches}
- rust-version
ExclusiveArch: %{rust_arches}
- rust-version-compare
ExclusiveArch: %{rust_arches}
- rust-version-sync
ExclusiveArch: %{rust_arches}
- rust-version-sync0.8
ExclusiveArch: %{rust_arches}
- rust-version_check
ExclusiveArch: %{rust_arches}
- rust-versions
ExclusiveArch: %{rust_arches}
- rust-virtio-bindings
ExclusiveArch: x86_64 aarch64 ppc64le
- rust-vm-memory
ExclusiveArch: x86_64 aarch64 ppc64le
- rust-vmm-sys-util
ExclusiveArch: x86_64 aarch64 ppc64le
- rust-vmw_backdoor
ExclusiveArch: %{rust_arches}
- rust-void
ExclusiveArch: %{rust_arches}
- rust-vsprintf
ExclusiveArch: %{rust_arches}
- rust-vte
ExclusiveArch: %{rust_arches}
- rust-vte0.3
ExclusiveArch: %{rust_arches}
- rust-vte_generate_state_changes
ExclusiveArch: %{rust_arches}
- rust-wait-timeout
ExclusiveArch: %{rust_arches}
- rust-waker-fn
ExclusiveArch: %{rust_arches}
- rust-walkdir
ExclusiveArch: %{rust_arches}
- rust-want
ExclusiveArch: %{rust_arches}
- rust-warp
ExclusiveArch: %{rust_arches}
- rust-warp0.2
ExclusiveArch: %{rust_arches}
- rust-wasm-bindgen
ExclusiveArch: %{rust_arches}
- rust-wasm-bindgen-backend
ExclusiveArch: %{rust_arches}
- rust-wasm-bindgen-futures
ExclusiveArch: %{rust_arches}
- rust-wasm-bindgen-macro
ExclusiveArch: %{rust_arches}
- rust-wasm-bindgen-macro-support
ExclusiveArch: %{rust_arches}
- rust-wasm-bindgen-shared
ExclusiveArch: %{rust_arches}
- rust-wasm-bindgen-test
ExclusiveArch: %{rust_arches}
- rust-wasm-bindgen-test-macro
ExclusiveArch: %{rust_arches}
- rust-wasmer_enumset
ExclusiveArch: %{rust_arches}
- rust-wasmer_enumset_derive
ExclusiveArch: %{rust_arches}
- rust-wasmparser
ExclusiveArch: %{rust_arches}
- rust-wasmtime-cache
ExclusiveArch: %{rust_arches}
- rust-wasmtime-environ
ExclusiveArch: %{rust_arches}
- rust-wasmtime-fiber
ExclusiveArch: %{rust_arches}
- rust-wast
ExclusiveArch: %{rust_arches}
- rust-wat
ExclusiveArch: %{rust_arches}
- rust-watchman_client
ExclusiveArch: %{rust_arches}
- rust-wayland-client
ExclusiveArch: %{rust_arches}
- rust-wayland-commons
ExclusiveArch: %{rust_arches}
- rust-wayland-cursor
ExclusiveArch: %{rust_arches}
- rust-wayland-egl
ExclusiveArch: %{rust_arches}
- rust-wayland-protocols
ExclusiveArch: %{rust_arches}
- rust-wayland-scanner
ExclusiveArch: %{rust_arches}
- rust-wayland-server
ExclusiveArch: %{rust_arches}
- rust-wayland-sys
ExclusiveArch: %{rust_arches}
- rust-web-sys
ExclusiveArch: %{rust_arches}
- rust-webbrowser
ExclusiveArch: %{rust_arches}
- rust-webkit2gtk
ExclusiveArch: %{rust_arches}
- rust-webkit2gtk-sys
ExclusiveArch: %{rust_arches}
- rust-webpki
ExclusiveArch: %{rust_arches}
- rust-webpki-roots
ExclusiveArch: %{rust_arches}
- rust-websocket
ExclusiveArch: %{rust_arches}
- rust-websocket-base
ExclusiveArch: %{rust_arches}
- rust-weezl
ExclusiveArch: %{rust_arches}
- rust-which
ExclusiveArch: %{rust_arches}
- rust-wide
ExclusiveArch: %{rust_arches}
- rust-wild
ExclusiveArch: %{rust_arches}
- rust-wildmatch
ExclusiveArch: %{rust_arches}
- rust-winit
ExclusiveArch: %{rust_arches}
- rust-witx
ExclusiveArch: %{rust_arches}
- rust-ws
ExclusiveArch: %{rust_arches}
- rust-wyz
ExclusiveArch: %{rust_arches}
- rust-x11
ExclusiveArch: %{rust_arches}
- rust-x11-clipboard
ExclusiveArch: %{rust_arches}
- rust-x11-dl
ExclusiveArch: %{rust_arches}
- rust-xattr
ExclusiveArch: %{rust_arches}
- rust-xcb
ExclusiveArch: %{rust_arches}
- rust-xcursor
ExclusiveArch: %{rust_arches}
- rust-xdg
ExclusiveArch: %{rust_arches}
- rust-xi-unicode
ExclusiveArch: %{rust_arches}
- rust-xkbcommon
ExclusiveArch: %{rust_arches}
- rust-xml-rs
ExclusiveArch: %{rust_arches}
- rust-xml5ever
ExclusiveArch: %{rust_arches}
- rust-xmlparser
ExclusiveArch: %{rust_arches}
- rust-xmltree
ExclusiveArch: %{rust_arches}
- rust-xmlwriter
ExclusiveArch: %{rust_arches}
- rust-xz2
ExclusiveArch: %{rust_arches}
- rust-y4m
ExclusiveArch: %{rust_arches}
- rust-yaml-rust
ExclusiveArch: %{rust_arches}
- rust-yaml-rust0.3
ExclusiveArch: %{rust_arches}
- rust-ybaas
ExclusiveArch: %{rust_arches}
- rust-ytop
ExclusiveArch: %{rust_arches}
- rust-yubibomb
ExclusiveArch: %{rust_arches}
- rust-yup-oauth2
ExclusiveArch: %{rust_arches}
- rust-zbase32
ExclusiveArch: %{rust_arches}
- rust-zbus
ExclusiveArch: %{rust_arches}
- rust-zbus_macros
ExclusiveArch: %{rust_arches}
- rust-zcomponents
ExclusiveArch: %{rust_arches}
- rust-zeroize
ExclusiveArch: %{rust_arches}
- rust-zeroize_derive
ExclusiveArch: %{rust_arches}
- rust-zincati
ExclusiveArch: %{rust_arches}
- rust-zip
ExclusiveArch: %{rust_arches}
- rust-zoneinfo_compiled
ExclusiveArch: %{rust_arches}
- rust-zopfli
ExclusiveArch: %{rust_arches}
- rust-zoxide
ExclusiveArch: %{rust_arches}
- rust-zram-generator
ExclusiveArch: %{rust_arches}
- rust-zstd
ExclusiveArch: %{rust_arches}
- rust-zstd-safe
ExclusiveArch: %{rust_arches}
- rust-zstd-sys
ExclusiveArch: %{rust_arches}
- rust-zvariant
ExclusiveArch: %{rust_arches}
- rust-zvariant_derive
ExclusiveArch: %{rust_arches}
- s390utils
ExclusiveArch: s390 s390x
- safetyblanket
ExclusiveArch: %{arm} %{ix86} x86_64 aarch64 ppc64le
- sagemath
ExclusiveArch: aarch64 %{ix86} x86_64 ppc sparcv9
- sbcl
ExclusiveArch: %{arm} %{ix86} x86_64 ppc sparcv9 aarch64
- sbd
ExclusiveArch: i686 x86_64 s390x aarch64 ppc64le
- sbsigntools
ExclusiveArch: x86_64 aarch64 %{arm} %{ix86}
- seabios
ExclusiveArch: x86_64
- seadrive-gui
ExclusiveArch: %{qt5_qtwebengine_arches}
- seafile-client
ExclusiveArch: %{qt5_qtwebengine_arches}
- seamonkey
ExclusiveArch: x86_64
- seqan3
ExclusiveArch: %{power64} x86_64 aarch64
- servicelog
ExclusiveArch: ppc %{power64}
- sgabios
ExclusiveArch: %{ix86} x86_64
- sharpfont
ExclusiveArch: %mono_arches
- sharpziplib
ExclusiveArch: %{mono_arches}
- shim
ExclusiveArch: %{efi}
- shim-unsigned-aarch64
ExclusiveArch: aarch64
- shim-unsigned-x64
ExclusiveArch: x86_64
- sigul
ExclusiveArch: x86_64
- skychart
ExclusiveArch: %{fpc_arches}
- snapd
ExclusiveArch: %{?go_arches:%{go_arches}}%{!?go_arches:%{ix86} x86_64 %{arm}}
ExclusiveArch: %{ix86} x86_64 %{arm} aarch64 ppc64le s390x
- soup-sharp
ExclusiveArch: %{mono_arches}
- sparkleshare
ExclusiveArch: %{mono_arches}
- spicctrl
ExclusiveArch: %{ix86} x86_64
- spice
ExclusiveArch: x86_64
ExclusiveArch: %{ix86} x86_64 %{arm} aarch64
- springlobby
ExclusiveArch: %{ix86} x86_64
- squeekboard
ExclusiveArch: %{rust_arches}
- startdde
ExclusiveArch: %{?go_arches:%{go_arches}}%{!?go_arches:%{ix86} x86_64 %{arm}}
- statsd
ExclusiveArch: %{nodejs_arches} noarch
- stratis-cli
ExclusiveArch: %{rust_arches} noarch
- stratisd
ExclusiveArch: %{rust_arches}
ExclusiveArch: ${rust_arches}
- stripesnoop
ExclusiveArch: %{ix86} x86_64
- supercollider
ExclusiveArch: %{qt5_qtwebengine_arches}
- supermin
ExclusiveArch: %{kernel_arches}
- svt-av1
ExclusiveArch: x86_64
- svt-vp9
ExclusiveArch: x86_64
- swift-lang
ExclusiveArch: x86_64 aarch64
- sysbench
ExclusiveArch: %{arm} %{ix86} x86_64 %{mips}
ExclusiveArch: %{arm} %{ix86} x86_64 %{mips} aarch64 ppc64le s390x
- syslinux
ExclusiveArch: %{ix86} x86_64
ExclusiveArch: %{ix86} x86_64
- taglib-sharp
ExclusiveArch: %{mono_arches}
- tarantool
ExclusiveArch: %{ix86} x86_64
- tboot
ExclusiveArch: %{ix86} x86_64
- tdlib
ExclusiveArch: x86_64
- templates_parser
ExclusiveArch: %GPRbuild_arches
- ternimal
ExclusiveArch: %{rust_arches}
- testcloud
ExclusiveArch: %{kernel_arches} noarch
- themonospot-base
ExclusiveArch: %mono_arches
- themonospot-console
ExclusiveArch: %mono_arches
- themonospot-gui-gtk
ExclusiveArch: %mono_arches
- themonospot-plugin-avi
ExclusiveArch: %mono_arches
- themonospot-plugin-mkv
ExclusiveArch: %mono_arches
- thermald
ExclusiveArch: %{ix86} x86_64
- tilix
ExclusiveArch: %{ldc_arches}
- tmux-top
ExclusiveArch: %{go_arches}
- tomboy
ExclusiveArch: %{mono_arches}
- topojson-client
ExclusiveArch: %{nodejs_arches} noarch
- topojson-server
ExclusiveArch: %{nodejs_arches} noarch
- topojson-simplify
ExclusiveArch: %{nodejs_arches} noarch
- torbrowser-launcher
ExclusiveArch: %{ix86} x86_64
- tuned-profiles-nfv-host-bin
ExclusiveArch: %{ix86} x86_64
- uClibc
ExclusiveArch: %{arm} %{ix86} x86_64 %{mips}
- ucx
ExclusiveArch: aarch64 ppc64le x86_64
- ugene
ExclusiveArch: %{ix86} x86_64
- uglify-js
ExclusiveArch: %{nodejs_arches} noarch
- uglify-js1
ExclusiveArch: %{nodejs_arches} noarch
ExclusiveArch: %{ix86} x86_64 %{arm} noarch
- unetbootin
ExclusiveArch: %{ix86} x86_64
- ursa-major
ExclusiveArch: noarch aarch64 ppc64le s390x x86_64
- usd
ExclusiveArch: x86_64
- v8-314
ExclusiveArch: %{ix86} x86_64 %{arm} mips mipsel ppc ppc64
- valgrind
ExclusiveArch: %{ix86} x86_64 ppc ppc64 ppc64le s390x armv7hl aarch64
- vboot-utils
ExclusiveArch: %{arm} aarch64 %{ix86} x86_64
- vim-go
ExclusiveArch: %{?golang_arches}%{!?golang_arches:%{ix86} x86_64 %{arm}}
- virt-p2v
ExclusiveArch: x86_64
- virt-v2v
ExclusiveArch: x86_64
- virtualbox-guest-additions
ExclusiveArch: i686 x86_64
- vkd3d
ExclusiveArch: %{ix86} x86_64 %{arm}
ExclusiveArch: %{ix86} x86_64 %{arm} aarch64
- vmaf
ExclusiveArch: x86_64
- vmem
ExclusiveArch: x86_64
- vmemcache
ExclusiveArch: x86_64 ppc64 ppc64le s390x aarch64
- vrq
ExclusiveArch: %{ix86} x86_64
- warsow
ExclusiveArch: %{ix86} x86_64 %{arm}
- warsow-data
ExclusiveArch: %{ix86} x86_64 %{arm} noarch
ExclusiveArch: %{ix86} x86_64 %{arm}
- webkit2-sharp
ExclusiveArch: %mono_arches
- wine
ExclusiveArch: %{ix86} x86_64 %{arm} aarch64
ExclusiveArch: %{ix86} %{arm}
- wine-dxvk
ExclusiveArch: %{ix86} x86_64
- winetricks
ExclusiveArch: %{ix86} x86_64 %{arm} aarch64
- wxMaxima
ExclusiveArch: %{arm} %{ix86} x86_64 aarch64 ppc sparcv9
- x2goclient
ExclusiveArch: x86_64
- xe-guest-utilities-latest
ExclusiveArch: %{ix86} x86_64
- xen
ExclusiveArch: %{ix86} x86_64 armv7hl aarch64
- xmlada
ExclusiveArch: %{GPRbuild_arches}
- xorg-x11-drv-armsoc
ExclusiveArch: %{arm} aarch64
- xorg-x11-drv-intel
ExclusiveArch: %{ix86} x86_64
- xorg-x11-drv-openchrome
ExclusiveArch: %{ix86} x86_64
- xorg-x11-drv-vesa
ExclusiveArch: %{ix86} x86_64
- xorg-x11-drv-vmware
ExclusiveArch: %{ix86} x86_64 ia64
- xsp
ExclusiveArch: %mono_arches
- yarnpkg
ExclusiveArch: %{nodejs_arches} noarch
- zeromq-ada
ExclusiveArch: %{GPRbuild_arches}
- zig
ExclusiveArch: %{zig_arches}
- zlib-ada
ExclusiveArch: %{GPRbuild_arches}
- zlib-ng
ExclusiveArch: aarch64 i686 ppc64le s390x x86_64
- zola
ExclusiveArch: %{rust_arches}
2 years, 9 months
Architecture specific change in rpms/libreoffice.git
by githook-noreply@fedoraproject.org
The package rpms/libreoffice.git has added or updated architecture specific content in its
spec file (ExclusiveArch/ExcludeArch or %ifarch/%ifnarch) in commit(s):
https://src.fedoraproject.org/cgit/rpms/libreoffice.git/commit/?id=d6ec43....
Change:
-%ifnarch s390x
Thanks.
Full change:
============
commit 60006c173f8ef1039af9495a62a3ed268418b3f6
Merge: 7c788fc 58cb95c
Author: Caolán McNamara <caolanm(a)redhat.com>
Date: Fri Aug 20 16:47:37 2021 +0100
Merge branch 'rawhide' into f35
commit 58cb95c86542a20f5855a17396cd1910c556db92
Author: Caolán McNamara <caolanm(a)redhat.com>
Date: Fri Aug 20 16:40:33 2021 +0100
try SMP_MFLAGS=2 instead of 3
diff --git a/libreoffice.spec b/libreoffice.spec
index e0a9811..a9cdf14 100644
--- a/libreoffice.spec
+++ b/libreoffice.spec
@@ -1068,7 +1068,7 @@ SMP_MFLAGS=%{?_smp_mflags}
SMP_MFLAGS=$[${SMP_MFLAGS/-j/}]
%ifarch armv7hl
-SMP_MFLAGS=3
+SMP_MFLAGS=2
%endif
%if 0%{?flatpak}
commit 41762c9f1e3e8e404b7cd33c0613b5b7a9026aa0
Author: Caolán McNamara <caolanm(a)redhat.com>
Date: Fri Aug 20 16:38:14 2021 +0100
s390x firebird is available again
diff --git a/libreoffice.spec b/libreoffice.spec
index cbbfc91..e0a9811 100644
--- a/libreoffice.spec
+++ b/libreoffice.spec
@@ -50,7 +50,7 @@ Summary: Free Software Productivity Suite
Name: libreoffice
Epoch: 1
Version: %{libo_version}.4
-Release: 1%{?libo_prerelease}%{?dist}
+Release: 2%{?libo_prerelease}%{?dist}
License: (MPLv1.1 or LGPLv3+) and LGPLv3 and LGPLv2+ and BSD and (MPLv1.1 or GPLv2 or LGPLv2 or Netscape) and Public Domain and ASL 2.0 and MPLv2.0 and CC0
URL: http://www.libreoffice.org/
@@ -2248,6 +2248,9 @@ gtk-update-icon-cache -q %{_datadir}/icons/hicolor &>/dev/null || :
%{_includedir}/LibreOfficeKit
%changelog
+* Fri Aug 20 2021 Caolán McNamara <caolanm(a)redhat.com> - 1:7.2.0.4-2
+- s390x firebird is available again
+
* Mon Aug 16 2021 Caolán McNamara <caolanm(a)redhat.com> - 1:7.2.0.4-1
- upgrade to 7.2.0
commit d6ec438d90bd22106aa791b5936b3dfd8a1448cf
Author: Caolán McNamara <caolanm(a)redhat.com>
Date: Fri Aug 20 11:21:08 2021 +0100
Related: rhbz#1969393 can now renable firebird support on s390x
diff --git a/libreoffice.spec b/libreoffice.spec
index d765b04..cbbfc91 100644
--- a/libreoffice.spec
+++ b/libreoffice.spec
@@ -126,10 +126,8 @@ BuildRequires: boost-devel
BuildRequires: cups-devel
BuildRequires: fontpackages-devel
%if 0%{?fedora}
-%ifnarch s390x
BuildRequires: firebird-devel
%endif
-%endif
BuildRequires: glm-devel
BuildRequires: hyphen-devel
BuildRequires: libjpeg-turbo-devel
@@ -333,10 +331,8 @@ to be written in python.
%package base
Summary: Database front-end for LibreOffice
%if 0%{?fedora}
-%ifnarch s390x
Requires: firebird
%endif
-%endif
Requires: pentaho-reporting-flow-engine
Requires: postgresql-jdbc
Requires: %{name}-core%{?_isa} = %{epoch}:%{version}-%{release}
@@ -1058,12 +1054,8 @@ export CXXFLAGS=$ARCH_FLAGS
%define distrooptions --disable-eot --disable-scripting-beanshell --disable-scripting-javascript --disable-firebird-sdbc
%else
# fedora
-%ifarch s390x
-%define distrooptions --enable-eot --enable-kf5 --disable-firebird-sdbc
-%else
%define distrooptions --enable-eot --enable-kf5
%endif
-%endif
%if %{with langpacks}
%define with_lang --with-lang='%{langpack_langs}'
@@ -1865,10 +1857,8 @@ rm -f %{buildroot}%{baseinstdir}/program/classes/smoketest.jar
%{baseinstdir}/program/libabplo.so
%{baseinstdir}/program/libdbplo.so
%if 0%{?fedora}
-%ifnarch s390x
%{baseinstdir}/program/libfirebird_sdbclo.so
%endif
-%endif
%{baseinstdir}/program/libhsqldb.so
%{baseinstdir}/program/librptlo.so
%{baseinstdir}/program/librptuilo.so
2 years, 9 months
Architecture specific change in rpms/pcl.git
by githook-noreply@fedoraproject.org
The package rpms/pcl.git has added or updated architecture specific content in its
spec file (ExclusiveArch/ExcludeArch or %ifarch/%ifnarch) in commit(s):
https://src.fedoraproject.org/cgit/rpms/pcl.git/commit/?id=bc37e95042b961....
Change:
-%ifarch %{arm}
Thanks.
Full change:
============
commit d115054c29fb244c1ad0d161c1fb3f6e60677a84
Author: Björn Esser <besser82(a)fedoraproject.org>
Date: Fri Aug 20 17:02:24 2021 +0200
Disable fat-lto-objects to reduce memory usage
Doing so will not hurt as we are not shipping any static libraries
diff --git a/pcl.spec b/pcl.spec
index a05375e..193093e 100644
--- a/pcl.spec
+++ b/pcl.spec
@@ -104,7 +104,7 @@ sed -i 's|@PCL_SOURCE_DIR@/build|@PCL_SOURCE_DIR@/%{_vpath_builddir}|' doc/doxyg
%build
# try to reduce memory usage of compile process (can cause OOM errors
# esp. on ARM builders)
-%global optflags %(echo %{optflags} | sed -e 's/-g /-g1 /' -e 's/-pipe //')
+%global optflags %(echo %{optflags} | sed -e 's/-g /-g1 /' -e 's/-pipe //' -e's/-ffat-lto-objects/-fno-fat-lto-objects/')
%cmake \
-DCMAKE_BUILD_TYPE=None \
@@ -191,7 +191,11 @@ mv $RPM_BUILD_ROOT%{_datadir}/%{name}-*/Modules $RPM_BUILD_ROOT%{_libdir}/cmake/
%doc %{_vpath_builddir}/doc/advanced/advanced
%changelog
-* Mon Aug 17 2021 Rich Mattes <richmattes(a)gmail.com> - 1.12.0-1
+* Fri Aug 20 2021 Björn Esser <besser82(a)fedoraproject.org> - 1.12.0-1
+- Disable fat-lto-objects to reduce memory usage
+ Doing so will not hurt as we are not shipping any static libraries
+
+* Tue Aug 17 2021 Rich Mattes <richmattes(a)gmail.com> - 1.12.0-1
- Drop "oom" patch to resolve multiple definition error and fix FTBFS (rhbz#1992893)
- Enable LTO and add library versions in files section
commit bc37e95042b961dbb194fb0e2bd9f187eea4e589
Author: Björn Esser <besser82(a)fedoraproject.org>
Date: Fri Aug 20 17:01:36 2021 +0200
Revert "Limit to 3 cpus on arm to try to prevent memory issues"
This reverts commit fcbd73e2a8805f79814cac3e72340a6edcead49b.
diff --git a/pcl.spec b/pcl.spec
index a95c5c5..a05375e 100644
--- a/pcl.spec
+++ b/pcl.spec
@@ -105,10 +105,6 @@ sed -i 's|@PCL_SOURCE_DIR@/build|@PCL_SOURCE_DIR@/%{_vpath_builddir}|' doc/doxyg
# try to reduce memory usage of compile process (can cause OOM errors
# esp. on ARM builders)
%global optflags %(echo %{optflags} | sed -e 's/-g /-g1 /' -e 's/-pipe //')
-%ifarch %{arm}
-# Likely running out of memory during build
-%global _smp_ncpus_max 3
-%endif
%cmake \
-DCMAKE_BUILD_TYPE=None \
@@ -195,10 +191,7 @@ mv $RPM_BUILD_ROOT%{_datadir}/%{name}-*/Modules $RPM_BUILD_ROOT%{_libdir}/cmake/
%doc %{_vpath_builddir}/doc/advanced/advanced
%changelog
-* Fri Aug 20 2021 Björn Esser <besser82(a)fedoraproject.org> - 1.12.0-1
-- Limit to 3 cpus on arm to try to prevent memory issues
-
-* Tue Aug 17 2021 Rich Mattes <richmattes(a)gmail.com> - 1.12.0-1
+* Mon Aug 17 2021 Rich Mattes <richmattes(a)gmail.com> - 1.12.0-1
- Drop "oom" patch to resolve multiple definition error and fix FTBFS (rhbz#1992893)
- Enable LTO and add library versions in files section
2 years, 9 months
Architecture specific change in rpms/pcl.git
by githook-noreply@fedoraproject.org
The package rpms/pcl.git has added or updated architecture specific content in its
spec file (ExclusiveArch/ExcludeArch or %ifarch/%ifnarch) in commit(s):
https://src.fedoraproject.org/cgit/rpms/pcl.git/commit/?id=fcbd73e2a8805f....
Change:
+%ifarch %{arm}
Thanks.
Full change:
============
commit fcbd73e2a8805f79814cac3e72340a6edcead49b
Author: Björn Esser <besser82(a)fedoraproject.org>
Date: Fri Aug 20 15:21:49 2021 +0200
Limit to 3 cpus on arm to try to prevent memory issues
diff --git a/pcl.spec b/pcl.spec
index a05375e..a95c5c5 100644
--- a/pcl.spec
+++ b/pcl.spec
@@ -105,6 +105,10 @@ sed -i 's|@PCL_SOURCE_DIR@/build|@PCL_SOURCE_DIR@/%{_vpath_builddir}|' doc/doxyg
# try to reduce memory usage of compile process (can cause OOM errors
# esp. on ARM builders)
%global optflags %(echo %{optflags} | sed -e 's/-g /-g1 /' -e 's/-pipe //')
+%ifarch %{arm}
+# Likely running out of memory during build
+%global _smp_ncpus_max 3
+%endif
%cmake \
-DCMAKE_BUILD_TYPE=None \
@@ -191,7 +195,10 @@ mv $RPM_BUILD_ROOT%{_datadir}/%{name}-*/Modules $RPM_BUILD_ROOT%{_libdir}/cmake/
%doc %{_vpath_builddir}/doc/advanced/advanced
%changelog
-* Mon Aug 17 2021 Rich Mattes <richmattes(a)gmail.com> - 1.12.0-1
+* Fri Aug 20 2021 Björn Esser <besser82(a)fedoraproject.org> - 1.12.0-1
+- Limit to 3 cpus on arm to try to prevent memory issues
+
+* Tue Aug 17 2021 Rich Mattes <richmattes(a)gmail.com> - 1.12.0-1
- Drop "oom" patch to resolve multiple definition error and fix FTBFS (rhbz#1992893)
- Enable LTO and add library versions in files section
2 years, 9 months
Architecture specific change in rpms/pacemaker.git
by githook-noreply@fedoraproject.org
The package rpms/pacemaker.git has added or updated architecture specific content in its
spec file (ExclusiveArch/ExcludeArch or %ifarch/%ifnarch) in commit(s):
https://src.fedoraproject.org/cgit/rpms/pacemaker.git/commit/?id=15ac1a3c....
Change:
+ExclusiveArch: aarch64 i686 ppc64le s390x x86_64
Thanks.
Full change:
============
commit eb015df7012a1ef2358394807b49a82021dd4c7f
Merge: e103b00 a74f8cf
Author: Klaus Wenninger <klaus.wenninger(a)aon.at>
Date: Fri Aug 20 14:16:02 2021 +0200
Merge branch 'rawhide' into f33
commit a74f8cfffa01425166cf6793858b614b09280026
Author: Klaus Wenninger <klaus.wenninger(a)aon.at>
Date: Fri Aug 20 01:22:00 2021 +0200
* Thu Aug 19 2021 Klaus Wenninger <kwenning(a)redhat.com> - 2.1.1-0.5.rc3
- Fix malformed xml in fence_watchdog metadata
diff --git a/0007-Fix-fence_watchdog-fix-malformed-xml-in-metadata.patch b/0007-Fix-fence_watchdog-fix-malformed-xml-in-metadata.patch
new file mode 100644
index 0000000..0d2c32e
--- /dev/null
+++ b/0007-Fix-fence_watchdog-fix-malformed-xml-in-metadata.patch
@@ -0,0 +1,25 @@
+From 46dd1118cae948649e000b2159e8e92623520ad9 Mon Sep 17 00:00:00 2001
+From: Klaus Wenninger <klaus.wenninger(a)aon.at>
+Date: Thu, 19 Aug 2021 09:28:54 +0200
+Subject: [PATCH] Fix: fence_watchdog: fix malformed xml in metadata
+
+---
+ daemons/fenced/fence_watchdog.in | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+diff --git a/daemons/fenced/fence_watchdog.in b/daemons/fenced/fence_watchdog.in
+index 700065e..eefa739 100755
+--- a/daemons/fenced/fence_watchdog.in
++++ b/daemons/fenced/fence_watchdog.in
+@@ -124,7 +124,7 @@ def metadata(avail_opt, options):
+ for option, dummy in sorted_options(avail_opt):
+ if "shortdesc" in ALL_OPT[option]:
+ print(' <parameter name="' + option +
+- 'required="' + ALL_OPT[option]["required"] + '">')
++ '" required="' + ALL_OPT[option]["required"] + '">')
+
+ default = ""
+ default_name_arg = "-" + ALL_OPT[option]["getopt"][:-1]
+--
+1.8.3.1
+
diff --git a/pacemaker.spec b/pacemaker.spec
index 2cd4aab..92eafb3 100644
--- a/pacemaker.spec
+++ b/pacemaker.spec
@@ -32,7 +32,7 @@
## can be incremented to build packages reliably considered "newer"
## than previously built packages with the same pcmkversion)
%global pcmkversion 2.1.1
-%global specversion 0.4.rc3
+%global specversion 0.5.rc3
## Upstream commit (full commit ID, abbreviated commit ID, or tag) to build
%global commit c6a4f6e6cdcedf08e0bfd81248b86812c2a571b2
@@ -221,6 +221,7 @@ Patch2: 0003-Fix-fence_watchdog-fix-version-output-needed-for-hel.patch
Patch3: 0004-Build-rpm-package-fence_watchdog-in-base-package.patch
Patch4: 0005-Fix-tools-make-exit-code-stdout-stderr-consistent-wi.patch
Patch5: 0006-Build-manpages-using-help2man-check-exit-code-of-too.patch
+Patch6: 0007-Fix-fence_watchdog-fix-malformed-xml-in-metadata.patch
# upstream commits
@@ -829,6 +830,9 @@ exit 0
%license %{nagios_name}-%{nagios_hash}/COPYING
%changelog
+* Thu Aug 19 2021 Klaus Wenninger <kwenning(a)redhat.com> - 2.1.1-0.5.rc3
+- Fix malformed xml in fence_watchdog metadata
+
* Tue Aug 17 2021 Klaus Wenninger <kwenning(a)redhat.com> - 2.1.1-0.4.rc3
- Update for new upstream tarball for release candidate: Pacemaker-2.1.1-rc3,
for full details, see included ChangeLog file or
commit b2569f82a6db1b7ddd4c28fc4e317f96fa3f5518
Author: Klaus Wenninger <klaus.wenninger(a)aon.at>
Date: Tue Aug 17 09:42:18 2021 +0200
* Tue Aug 17 2021 Klaus Wenninger <kwenning(a)redhat.com> - 2.1.1-0.4.rc3
- Update for new upstream tarball for release candidate: Pacemaker-2.1.1-rc3,
for full details, see included ChangeLog file or
https://github.com/ClusterLabs/pacemaker/releases/tag/Pacemaker-2.1.1-rc3
- Make tools exit-codes consistent for --version
- Check exit-codes of tools prior to manpage-generation
diff --git a/0005-Fix-controller-ensure-lost-node-s-transient-attribut.patch b/0005-Fix-controller-ensure-lost-node-s-transient-attribut.patch
deleted file mode 100644
index 3e64b25..0000000
--- a/0005-Fix-controller-ensure-lost-node-s-transient-attribut.patch
+++ /dev/null
@@ -1,122 +0,0 @@
-From ee7eba6a7a05bdf0a12d60ebabb334d8ee021101 Mon Sep 17 00:00:00 2001
-From: Ken Gaillot <kgaillot(a)redhat.com>
-Date: Mon, 9 Aug 2021 14:48:57 -0500
-Subject: [PATCH] Fix: controller: ensure lost node's transient attributes are
- cleared without DC
-
-Previously, peer_update_callback() cleared a lost node's transient attributes
-if either the local node is DC, or there is no DC.
-
-However, that left the possibility of the DC being lost at the same time as
-another node -- the local node would still have fsa_our_dc set while processing
-the leave notifications, so no node would clear the attributes for the non-DC
-node.
-
-Now, the controller has its own CPG configuration change callback, which sets a
-global boolean before calling the usual one, so that peer_update_callback() can
-know when the DC has been lost.
----
- daemons/controld/controld_callbacks.c | 4 ++-
- daemons/controld/controld_corosync.c | 57 ++++++++++++++++++++++++++++++++++-
- 2 files changed, 59 insertions(+), 2 deletions(-)
-
-diff --git a/daemons/controld/controld_callbacks.c b/daemons/controld/controld_callbacks.c
-index af24856..e564b3d 100644
---- a/daemons/controld/controld_callbacks.c
-+++ b/daemons/controld/controld_callbacks.c
-@@ -99,6 +99,8 @@ node_alive(const crm_node_t *node)
-
- #define state_text(state) ((state)? (const char *)(state) : "in unknown state")
-
-+bool controld_dc_left = false;
-+
- void
- peer_update_callback(enum crm_status_type type, crm_node_t * node, const void *data)
- {
-@@ -217,7 +219,7 @@ peer_update_callback(enum crm_status_type type, crm_node_t * node, const void *d
- cib_scope_local);
- }
-
-- } else if (AM_I_DC || (fsa_our_dc == NULL)) {
-+ } else if (AM_I_DC || controld_dc_left || (fsa_our_dc == NULL)) {
- /* This only needs to be done once, so normally the DC should do
- * it. However if there is no DC, every node must do it, since
- * there is no other way to ensure some one node does it.
-diff --git a/daemons/controld/controld_corosync.c b/daemons/controld/controld_corosync.c
-index db99630..c5ab658 100644
---- a/daemons/controld/controld_corosync.c
-+++ b/daemons/controld/controld_corosync.c
-@@ -87,6 +87,61 @@ crmd_cs_destroy(gpointer user_data)
- }
- }
-
-+extern bool controld_dc_left;
-+
-+/*!
-+ * \brief Handle a Corosync notification of a CPG configuration change
-+ *
-+ * \param[in] handle CPG connection
-+ * \param[in] cpg_name CPG group name
-+ * \param[in] member_list List of current CPG members
-+ * \param[in] member_list_entries Number of entries in \p member_list
-+ * \param[in] left_list List of CPG members that left
-+ * \param[in] left_list_entries Number of entries in \p left_list
-+ * \param[in] joined_list List of CPG members that joined
-+ * \param[in] joined_list_entries Number of entries in \p joined_list
-+ */
-+static void
-+cpg_membership_callback(cpg_handle_t handle, const struct cpg_name *cpg_name,
-+ const struct cpg_address *member_list,
-+ size_t member_list_entries,
-+ const struct cpg_address *left_list,
-+ size_t left_list_entries,
-+ const struct cpg_address *joined_list,
-+ size_t joined_list_entries)
-+{
-+ /* When nodes leave CPG, the DC clears their transient node attributes.
-+ *
-+ * However if there is no DC, or the DC is among the nodes that left, each
-+ * remaining node needs to do the clearing, to ensure it gets done.
-+ * Otherwise, the attributes would persist when the nodes rejoin, which
-+ * could have serious consequences for unfencing, agents that use attributes
-+ * for internal logic, etc.
-+ *
-+ * Here, we set a global boolean if the DC is among the nodes that left, for
-+ * use by the peer callback.
-+ */
-+ if (fsa_our_dc != NULL) {
-+ crm_node_t *peer = pcmk__search_cluster_node_cache(0, fsa_our_dc);
-+
-+ if (peer != NULL) {
-+ for (int i = 0; i < left_list_entries; ++i) {
-+ if (left_list[i].nodeid == peer->id) {
-+ controld_dc_left = true;
-+ break;
-+ }
-+ }
-+ }
-+ }
-+
-+ // Process the change normally, which will call the peer callback as needed
-+ pcmk_cpg_membership(handle, cpg_name, member_list, member_list_entries,
-+ left_list, left_list_entries,
-+ joined_list, joined_list_entries);
-+
-+ controld_dc_left = false;
-+}
-+
- extern gboolean crm_connect_corosync(crm_cluster_t * cluster);
-
- gboolean
-@@ -95,7 +150,7 @@ crm_connect_corosync(crm_cluster_t * cluster)
- if (is_corosync_cluster()) {
- crm_set_status_callback(&peer_update_callback);
- cluster->cpg.cpg_deliver_fn = crmd_cs_dispatch;
-- cluster->cpg.cpg_confchg_fn = pcmk_cpg_membership;
-+ cluster->cpg.cpg_confchg_fn = cpg_membership_callback;
- cluster->destroy = crmd_cs_destroy;
-
- if (crm_cluster_connect(cluster)) {
---
-1.8.3.1
-
diff --git a/0005-Fix-tools-make-exit-code-stdout-stderr-consistent-wi.patch b/0005-Fix-tools-make-exit-code-stdout-stderr-consistent-wi.patch
new file mode 100644
index 0000000..f72774f
--- /dev/null
+++ b/0005-Fix-tools-make-exit-code-stdout-stderr-consistent-wi.patch
@@ -0,0 +1,82 @@
+From b34570ecaeb4fb0106dd0aa5a8776a5417a07f90 Mon Sep 17 00:00:00 2001
+From: Klaus Wenninger <klaus.wenninger(a)aon.at>
+Date: Mon, 16 Aug 2021 16:41:18 +0200
+Subject: [PATCH] Fix: tools: make exit-code stdout/stderr consistent with
+ --version
+
+---
+ tools/crm_attribute.c | 2 +-
+ tools/crm_diff.c | 2 +-
+ tools/crm_error.c | 2 +-
+ tools/crm_node.c | 2 +-
+ tools/crm_rule.c | 2 +-
+ 5 files changed, 5 insertions(+), 5 deletions(-)
+
+diff --git a/tools/crm_attribute.c b/tools/crm_attribute.c
+index 6bd4e2a..faf4aae 100644
+--- a/tools/crm_attribute.c
++++ b/tools/crm_attribute.c
+@@ -326,7 +326,7 @@ main(int argc, char **argv)
+ g_strfreev(processed_args);
+ pcmk__free_arg_context(context);
+ /* FIXME: When crm_attribute is converted to use formatted output, this can go. */
+- pcmk__cli_help('v', CRM_EX_USAGE);
++ pcmk__cli_help('v', CRM_EX_OK);
+ }
+
+ if (options.promotion_score && options.attr_name == NULL) {
+diff --git a/tools/crm_diff.c b/tools/crm_diff.c
+index 9890c10..18f5d48 100644
+--- a/tools/crm_diff.c
++++ b/tools/crm_diff.c
+@@ -322,7 +322,7 @@ main(int argc, char **argv)
+ g_strfreev(processed_args);
+ pcmk__free_arg_context(context);
+ /* FIXME: When crm_diff is converted to use formatted output, this can go. */
+- pcmk__cli_help('v', CRM_EX_USAGE);
++ pcmk__cli_help('v', CRM_EX_OK);
+ }
+
+ if (options.apply && options.no_version) {
+diff --git a/tools/crm_error.c b/tools/crm_error.c
+index 923f393..f26efe7 100644
+--- a/tools/crm_error.c
++++ b/tools/crm_error.c
+@@ -93,7 +93,7 @@ main(int argc, char **argv)
+ g_strfreev(processed_args);
+ pcmk__free_arg_context(context);
+ /* FIXME: When crm_error is converted to use formatted output, this can go. */
+- pcmk__cli_help('v', CRM_EX_USAGE);
++ pcmk__cli_help('v', CRM_EX_OK);
+ }
+
+ if (options.do_list) {
+diff --git a/tools/crm_node.c b/tools/crm_node.c
+index 25da978..489a39a 100644
+--- a/tools/crm_node.c
++++ b/tools/crm_node.c
+@@ -549,7 +549,7 @@ main(int argc, char **argv)
+ g_strfreev(processed_args);
+ pcmk__free_arg_context(context);
+ /* FIXME: When crm_node is converted to use formatted output, this can go. */
+- pcmk__cli_help('v', CRM_EX_USAGE);
++ pcmk__cli_help('v', CRM_EX_OK);
+ }
+
+ if (options.command == 0) {
+diff --git a/tools/crm_rule.c b/tools/crm_rule.c
+index 30c5155..0880415 100644
+--- a/tools/crm_rule.c
++++ b/tools/crm_rule.c
+@@ -252,7 +252,7 @@ main(int argc, char **argv)
+ g_strfreev(processed_args);
+ pcmk__free_arg_context(context);
+ /* FIXME: When crm_rule is converted to use formatted output, this can go. */
+- pcmk__cli_help('v', CRM_EX_USAGE);
++ pcmk__cli_help('v', CRM_EX_OK);
+ }
+
+ /* Check command line arguments before opening a connection to
+--
+1.8.3.1
+
diff --git a/0006-Build-manpages-using-help2man-check-exit-code-of-too.patch b/0006-Build-manpages-using-help2man-check-exit-code-of-too.patch
new file mode 100644
index 0000000..7c408b8
--- /dev/null
+++ b/0006-Build-manpages-using-help2man-check-exit-code-of-too.patch
@@ -0,0 +1,29 @@
+From 2e0ac6390327e387e9b94e50080d13fd95f545d6 Mon Sep 17 00:00:00 2001
+From: Klaus Wenninger <klaus.wenninger(a)aon.at>
+Date: Mon, 16 Aug 2021 16:42:51 +0200
+Subject: [PATCH] Build: manpages: using help2man check exit-code of tools
+
+---
+ mk/common.mk | 6 ++++++
+ 1 file changed, 6 insertions(+)
+
+diff --git a/mk/common.mk b/mk/common.mk
+index aa59feb..c3d1993 100644
+--- a/mk/common.mk
++++ b/mk/common.mk
+@@ -68,6 +68,12 @@ HELP2MAN_ARGS = -N --section 8 --name "Part of the Pacemaker cluster resource ma
+ # and all wrappers to C code.
+ %.8: % $(MAN8DEPS)
+ $(AM_V_at)chmod a+x $(abs_builddir)/$<
++ PATH=$(abs_builddir):$$PATH $(abs_builddir)/$< --version >/dev/null
++ if [ -f $(abs_srcdir)/$@.inc ]; then \
++ PATH=$(abs_builddir):$$PATH $(abs_builddir)/$< --help-all >/dev/null; \
++ else \
++ PATH=$(abs_builddir):$$PATH $(abs_builddir)/$< --help >/dev/null; \
++ fi
+ $(AM_V_MAN)if [ -f $(abs_srcdir)/$@.inc ]; then \
+ PATH=$(abs_builddir):$$PATH $(HELP2MAN) $(HELP2MAN_ARGS) \
+ -h --help-all \
+--
+1.8.3.1
+
diff --git a/pacemaker.spec b/pacemaker.spec
index a138e31..2cd4aab 100644
--- a/pacemaker.spec
+++ b/pacemaker.spec
@@ -32,10 +32,10 @@
## can be incremented to build packages reliably considered "newer"
## than previously built packages with the same pcmkversion)
%global pcmkversion 2.1.1
-%global specversion 0.3.rc2
+%global specversion 0.4.rc3
## Upstream commit (full commit ID, abbreviated commit ID, or tag) to build
-%global commit a64ad221abe9cdd968ceacd35c23832ce0fcd189
+%global commit c6a4f6e6cdcedf08e0bfd81248b86812c2a571b2
## Since git v2.11, the extent of abbreviation is autoscaled by default
## (used to be constant of 7), so we need to convey it for non-tags, too.
@@ -219,7 +219,8 @@ Patch0: 0001-feature-watchdog-fencing-allow-restriction-to-certai.patch
Patch1: 0002-Fix-watchdog-fencing-Silence-warning-without-node-re.patch
Patch2: 0003-Fix-fence_watchdog-fix-version-output-needed-for-hel.patch
Patch3: 0004-Build-rpm-package-fence_watchdog-in-base-package.patch
-Patch4: 0005-Fix-controller-ensure-lost-node-s-transient-attribut.patch
+Patch4: 0005-Fix-tools-make-exit-code-stdout-stderr-consistent-wi.patch
+Patch5: 0006-Build-manpages-using-help2man-check-exit-code-of-too.patch
# upstream commits
@@ -828,6 +829,13 @@ exit 0
%license %{nagios_name}-%{nagios_hash}/COPYING
%changelog
+* Tue Aug 17 2021 Klaus Wenninger <kwenning(a)redhat.com> - 2.1.1-0.4.rc3
+- Update for new upstream tarball for release candidate: Pacemaker-2.1.1-rc3,
+ for full details, see included ChangeLog file or
+ https://github.com/ClusterLabs/pacemaker/releases/tag/Pacemaker-2.1.1-rc3
+- Make tools exit-codes consistent for --version
+- Check exit-codes of tools prior to manpage-generation
+
* Wed Aug 11 2021 Klaus Wenninger <kwenning(a)redhat.com> - 2.1.1-0.3.rc2
- package fence_watchdog in base-package instead if cli-subpackage
- fix version output of fence_watchdog as needed for help2man
diff --git a/sources b/sources
index 2ccc4d0..edc4941 100644
--- a/sources
+++ b/sources
@@ -1,2 +1,2 @@
SHA512 (nagios-agents-metadata-105ab8a7b2c16b9a29cf1c1596b80136eeef332b.tar.gz) = 11ddeb48a4929e7642b6dfa9c7962aa1d7a1af1c569830f55ed6cd6773abac13377317327bc1db8411c8077884f83f81cc54d746c834b63a99fa6dc219b5caad
-SHA512 (pacemaker-a64ad221a.tar.gz) = ee954bd8efd135965290f9958b65680cbf101e14e2fd22d326e53a250281a7e5b6b6ec979c7f76072514dbf4d29868e99931842dbcc99bab5e5ba5e4bbc4ef0a
+SHA512 (pacemaker-c6a4f6e6c.tar.gz) = 28a313c016add4f95ec2b546c468826c8234bafdc1a7a7d26b785733ee10ec209dc0dbf3f107df57604990b5f89b2d05831684b5d3adfd45479706d9c6d5654f
commit 7889c6669e78faaef3ce77ff149430c179bb6792
Author: Klaus Wenninger <klaus.wenninger(a)aon.at>
Date: Wed Aug 11 11:48:10 2021 +0200
* Wed Aug 11 2021 Klaus Wenninger <kwenning(a)redhat.com> - 2.1.1-0.3.rc2
- package fence_watchdog in base-package instead if cli-subpackage
- fix version output of fence_watchdog as needed for help2man
- ensure transient attributes of lost nodes are cleared reliably
diff --git a/0003-Fix-fence_watchdog-fix-version-output-needed-for-hel.patch b/0003-Fix-fence_watchdog-fix-version-output-needed-for-hel.patch
new file mode 100644
index 0000000..8245e4f
--- /dev/null
+++ b/0003-Fix-fence_watchdog-fix-version-output-needed-for-hel.patch
@@ -0,0 +1,24 @@
+From 88e75d5b98df197fa731e7642434951a24a67095 Mon Sep 17 00:00:00 2001
+From: Klaus Wenninger <klaus.wenninger(a)aon.at>
+Date: Tue, 10 Aug 2021 09:10:23 +0200
+Subject: [PATCH] Fix: fence_watchdog: fix version output needed for help2man
+
+---
+ daemons/fenced/fence_watchdog.in | 1 +
+ 1 file changed, 1 insertion(+)
+
+diff --git a/daemons/fenced/fence_watchdog.in b/daemons/fenced/fence_watchdog.in
+index c83304f..700065e 100755
+--- a/daemons/fenced/fence_watchdog.in
++++ b/daemons/fenced/fence_watchdog.in
+@@ -12,6 +12,7 @@ import sys
+ import atexit
+ import getopt
+
++AGENT_VERSION = "1.0.0"
+ SHORT_DESC = "Dummy watchdog fence agent"
+ LONG_DESC = """fence_watchdog just provides
+ meta-data - actual fencing is done by the pacemaker internal watchdog agent."""
+--
+1.8.3.1
+
diff --git a/0004-Build-rpm-package-fence_watchdog-in-base-package.patch b/0004-Build-rpm-package-fence_watchdog-in-base-package.patch
new file mode 100644
index 0000000..d112c40
--- /dev/null
+++ b/0004-Build-rpm-package-fence_watchdog-in-base-package.patch
@@ -0,0 +1,32 @@
+From 61eb9c240004d1dbd0b5973e2fecda3686bb4c53 Mon Sep 17 00:00:00 2001
+From: Klaus Wenninger <klaus.wenninger(a)aon.at>
+Date: Tue, 10 Aug 2021 09:06:55 +0200
+Subject: [PATCH] Build: rpm: package fence_watchdog in base-package
+
+---
+ rpm/pacemaker.spec.in | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+diff --git a/rpm/pacemaker.spec.in b/rpm/pacemaker.spec.in
+index f58357a..0c569b9 100644
+--- a/rpm/pacemaker.spec.in
++++ b/rpm/pacemaker.spec.in
+@@ -734,6 +734,7 @@ exit 0
+ %{_sbindir}/crm_attribute
+ %{_sbindir}/crm_master
+ %{_sbindir}/fence_legacy
++%{_sbindir}/fence_watchdog
+
+ %doc %{_mandir}/man7/pacemaker-controld.*
+ %doc %{_mandir}/man7/pacemaker-schedulerd.*
+@@ -797,7 +798,6 @@ exit 0
+ %{_sbindir}/crm_simulate
+ %{_sbindir}/crm_report
+ %{_sbindir}/crm_ticket
+-%{_sbindir}/fence_watchdog
+ %{_sbindir}/stonith_admin
+ # "dirname" is owned by -schemas, which is a prerequisite
+ %{_datadir}/pacemaker/report.collector
+--
+1.8.3.1
+
diff --git a/0005-Fix-controller-ensure-lost-node-s-transient-attribut.patch b/0005-Fix-controller-ensure-lost-node-s-transient-attribut.patch
new file mode 100644
index 0000000..3e64b25
--- /dev/null
+++ b/0005-Fix-controller-ensure-lost-node-s-transient-attribut.patch
@@ -0,0 +1,122 @@
+From ee7eba6a7a05bdf0a12d60ebabb334d8ee021101 Mon Sep 17 00:00:00 2001
+From: Ken Gaillot <kgaillot(a)redhat.com>
+Date: Mon, 9 Aug 2021 14:48:57 -0500
+Subject: [PATCH] Fix: controller: ensure lost node's transient attributes are
+ cleared without DC
+
+Previously, peer_update_callback() cleared a lost node's transient attributes
+if either the local node is DC, or there is no DC.
+
+However, that left the possibility of the DC being lost at the same time as
+another node -- the local node would still have fsa_our_dc set while processing
+the leave notifications, so no node would clear the attributes for the non-DC
+node.
+
+Now, the controller has its own CPG configuration change callback, which sets a
+global boolean before calling the usual one, so that peer_update_callback() can
+know when the DC has been lost.
+---
+ daemons/controld/controld_callbacks.c | 4 ++-
+ daemons/controld/controld_corosync.c | 57 ++++++++++++++++++++++++++++++++++-
+ 2 files changed, 59 insertions(+), 2 deletions(-)
+
+diff --git a/daemons/controld/controld_callbacks.c b/daemons/controld/controld_callbacks.c
+index af24856..e564b3d 100644
+--- a/daemons/controld/controld_callbacks.c
++++ b/daemons/controld/controld_callbacks.c
+@@ -99,6 +99,8 @@ node_alive(const crm_node_t *node)
+
+ #define state_text(state) ((state)? (const char *)(state) : "in unknown state")
+
++bool controld_dc_left = false;
++
+ void
+ peer_update_callback(enum crm_status_type type, crm_node_t * node, const void *data)
+ {
+@@ -217,7 +219,7 @@ peer_update_callback(enum crm_status_type type, crm_node_t * node, const void *d
+ cib_scope_local);
+ }
+
+- } else if (AM_I_DC || (fsa_our_dc == NULL)) {
++ } else if (AM_I_DC || controld_dc_left || (fsa_our_dc == NULL)) {
+ /* This only needs to be done once, so normally the DC should do
+ * it. However if there is no DC, every node must do it, since
+ * there is no other way to ensure some one node does it.
+diff --git a/daemons/controld/controld_corosync.c b/daemons/controld/controld_corosync.c
+index db99630..c5ab658 100644
+--- a/daemons/controld/controld_corosync.c
++++ b/daemons/controld/controld_corosync.c
+@@ -87,6 +87,61 @@ crmd_cs_destroy(gpointer user_data)
+ }
+ }
+
++extern bool controld_dc_left;
++
++/*!
++ * \brief Handle a Corosync notification of a CPG configuration change
++ *
++ * \param[in] handle CPG connection
++ * \param[in] cpg_name CPG group name
++ * \param[in] member_list List of current CPG members
++ * \param[in] member_list_entries Number of entries in \p member_list
++ * \param[in] left_list List of CPG members that left
++ * \param[in] left_list_entries Number of entries in \p left_list
++ * \param[in] joined_list List of CPG members that joined
++ * \param[in] joined_list_entries Number of entries in \p joined_list
++ */
++static void
++cpg_membership_callback(cpg_handle_t handle, const struct cpg_name *cpg_name,
++ const struct cpg_address *member_list,
++ size_t member_list_entries,
++ const struct cpg_address *left_list,
++ size_t left_list_entries,
++ const struct cpg_address *joined_list,
++ size_t joined_list_entries)
++{
++ /* When nodes leave CPG, the DC clears their transient node attributes.
++ *
++ * However if there is no DC, or the DC is among the nodes that left, each
++ * remaining node needs to do the clearing, to ensure it gets done.
++ * Otherwise, the attributes would persist when the nodes rejoin, which
++ * could have serious consequences for unfencing, agents that use attributes
++ * for internal logic, etc.
++ *
++ * Here, we set a global boolean if the DC is among the nodes that left, for
++ * use by the peer callback.
++ */
++ if (fsa_our_dc != NULL) {
++ crm_node_t *peer = pcmk__search_cluster_node_cache(0, fsa_our_dc);
++
++ if (peer != NULL) {
++ for (int i = 0; i < left_list_entries; ++i) {
++ if (left_list[i].nodeid == peer->id) {
++ controld_dc_left = true;
++ break;
++ }
++ }
++ }
++ }
++
++ // Process the change normally, which will call the peer callback as needed
++ pcmk_cpg_membership(handle, cpg_name, member_list, member_list_entries,
++ left_list, left_list_entries,
++ joined_list, joined_list_entries);
++
++ controld_dc_left = false;
++}
++
+ extern gboolean crm_connect_corosync(crm_cluster_t * cluster);
+
+ gboolean
+@@ -95,7 +150,7 @@ crm_connect_corosync(crm_cluster_t * cluster)
+ if (is_corosync_cluster()) {
+ crm_set_status_callback(&peer_update_callback);
+ cluster->cpg.cpg_deliver_fn = crmd_cs_dispatch;
+- cluster->cpg.cpg_confchg_fn = pcmk_cpg_membership;
++ cluster->cpg.cpg_confchg_fn = cpg_membership_callback;
+ cluster->destroy = crmd_cs_destroy;
+
+ if (crm_cluster_connect(cluster)) {
+--
+1.8.3.1
+
diff --git a/pacemaker.spec b/pacemaker.spec
index a8f1d51..a138e31 100644
--- a/pacemaker.spec
+++ b/pacemaker.spec
@@ -32,7 +32,7 @@
## can be incremented to build packages reliably considered "newer"
## than previously built packages with the same pcmkversion)
%global pcmkversion 2.1.1
-%global specversion 0.2.rc2
+%global specversion 0.3.rc2
## Upstream commit (full commit ID, abbreviated commit ID, or tag) to build
%global commit a64ad221abe9cdd968ceacd35c23832ce0fcd189
@@ -217,6 +217,9 @@ Source0: https://codeload.github.com/%{github_owner}/%{name}/tar.gz/%{arch
Source1: https://codeload.github.com/%{github_owner}/%{nagios_name}/tar.gz/%{nagio...
Patch0: 0001-feature-watchdog-fencing-allow-restriction-to-certai.patch
Patch1: 0002-Fix-watchdog-fencing-Silence-warning-without-node-re.patch
+Patch2: 0003-Fix-fence_watchdog-fix-version-output-needed-for-hel.patch
+Patch3: 0004-Build-rpm-package-fence_watchdog-in-base-package.patch
+Patch4: 0005-Fix-controller-ensure-lost-node-s-transient-attribut.patch
# upstream commits
@@ -660,6 +663,7 @@ exit 0
%{_sbindir}/crm_attribute
%{_sbindir}/crm_master
+%{_sbindir}/fence_watchdog
%doc %{_mandir}/man7/pacemaker-controld.*
%doc %{_mandir}/man7/pacemaker-schedulerd.*
@@ -709,7 +713,6 @@ exit 0
%{_sbindir}/crm_simulate
%{_sbindir}/crm_report
%{_sbindir}/crm_ticket
-%{_sbindir}/fence_watchdog
%{_sbindir}/stonith_admin
# "dirname" is owned by -schemas, which is a prerequisite
%{_datadir}/pacemaker/report.collector
@@ -825,6 +828,11 @@ exit 0
%license %{nagios_name}-%{nagios_hash}/COPYING
%changelog
+* Wed Aug 11 2021 Klaus Wenninger <kwenning(a)redhat.com> - 2.1.1-0.3.rc2
+- package fence_watchdog in base-package instead if cli-subpackage
+- fix version output of fence_watchdog as needed for help2man
+- ensure transient attributes of lost nodes are cleared reliably
+
* Mon Aug 9 2021 Klaus Wenninger <kwenning(a)redhat.com> - 2.1.1-0.2.rc2
- Update for new upstream tarball for release candidate: Pacemaker-2.1.1-rc2,
for full details, see included ChangeLog file or
commit e8f53e7d76dd1680d55e0436757380baf9abdc38
Author: Klaus Wenninger <klaus.wenninger(a)aon.at>
Date: Mon Aug 9 17:50:26 2021 +0200
* Mon Aug 9 2021 Klaus Wenninger <kwenning(a)redhat.com> - 2.1.1-0.2.rc2
- Update for new upstream tarball for release candidate: Pacemaker-2.1.1-rc2,
for full details, see included ChangeLog file or
https://github.com/ClusterLabs/pacemaker/releases/tag/Pacemaker-2.1.1-rc2
- Added feature patch that allows to explicitly specify nodes that
should do watchdog-fencing
diff --git a/0001-feature-watchdog-fencing-allow-restriction-to-certai.patch b/0001-feature-watchdog-fencing-allow-restriction-to-certai.patch
new file mode 100644
index 0000000..97e6071
--- /dev/null
+++ b/0001-feature-watchdog-fencing-allow-restriction-to-certai.patch
@@ -0,0 +1,1119 @@
+From 5dd1e4459335764e0adf5fa78d81c875ae2332e9 Mon Sep 17 00:00:00 2001
+From: Klaus Wenninger <klaus.wenninger(a)aon.at>
+Date: Fri, 30 Jul 2021 18:15:10 +0200
+Subject: [PATCH] feature: watchdog-fencing: allow restriction to certain nodes
+
+Bump CRM_FEATURE_SET to 3.11.0 to encourage cluster being
+fully upgraded to a version that supports the feature
+before explicitly adding a watchdog-fence-device.
+---
+ configure.ac | 1 +
+ daemons/controld/controld_control.c | 2 +-
+ daemons/controld/controld_fencing.c | 14 ++
+ daemons/controld/controld_fencing.h | 1 +
+ daemons/fenced/Makefile.am | 2 +-
+ daemons/fenced/fence_watchdog.in | 283 ++++++++++++++++++++++++++++++++++++
+ daemons/fenced/fenced_commands.c | 141 ++++++++++++++----
+ daemons/fenced/fenced_remote.c | 71 ++++++---
+ daemons/fenced/pacemaker-fenced.c | 131 ++++++++++++-----
+ daemons/fenced/pacemaker-fenced.h | 5 +-
+ include/crm/crm.h | 2 +-
+ include/crm/fencing/internal.h | 8 +-
+ lib/fencing/st_client.c | 61 ++++++++
+ lib/lrmd/lrmd_client.c | 6 +-
+ rpm/pacemaker.spec.in | 3 +
+ 16 files changed, 635 insertions(+), 97 deletions(-)
+ create mode 100755 daemons/fenced/fence_watchdog.in
+
+diff --git a/configure.ac b/configure.ac
+index 436100c..013562e 100644
+--- a/configure.ac
++++ b/configure.ac
+@@ -1972,6 +1972,7 @@ CONFIG_FILES_EXEC([cts/cts-cli],
+ [cts/support/fence_dummy],
+ [cts/support/pacemaker-cts-dummyd],
+ [daemons/fenced/fence_legacy],
++ [daemons/fenced/fence_watchdog],
+ [doc/abi-check],
+ [extra/resources/ClusterMon],
+ [extra/resources/HealthSMART],
+diff --git a/daemons/controld/controld_control.c b/daemons/controld/controld_control.c
+index 45a70bb..b5da6a4 100644
+--- a/daemons/controld/controld_control.c
++++ b/daemons/controld/controld_control.c
+@@ -615,7 +615,7 @@ static pcmk__cluster_option_t crmd_opts[] = {
+ },
+ {
+ "stonith-watchdog-timeout", NULL, "time", NULL,
+- "0", pcmk__valid_sbd_timeout,
++ "0", controld_verify_stonith_watchdog_timeout,
+ "How long to wait before we can assume nodes are safely down "
+ "when watchdog-based self-fencing via SBD is in use",
+ "If nonzero, along with `have-watchdog=true` automatically set by the "
+diff --git a/daemons/controld/controld_fencing.c b/daemons/controld/controld_fencing.c
+index 0fba661..6c2a6c5 100644
+--- a/daemons/controld/controld_fencing.c
++++ b/daemons/controld/controld_fencing.c
+@@ -11,6 +11,7 @@
+ #include <crm/crm.h>
+ #include <crm/msg_xml.h>
+ #include <crm/common/xml.h>
++#include <crm/stonith-ng.h>
+ #include <crm/fencing/internal.h>
+
+ #include <pacemaker-controld.h>
+@@ -886,6 +887,19 @@ te_fence_node(crm_graph_t *graph, crm_action_t *action)
+ return TRUE;
+ }
+
++bool
++controld_verify_stonith_watchdog_timeout(const char *value)
++{
++ gboolean rv = TRUE;
++
++ if (stonith_api && (stonith_api->state != stonith_disconnected) &&
++ stonith__watchdog_fencing_enabled_for_node_api(stonith_api,
++ fsa_our_uname)) {
++ rv = pcmk__valid_sbd_timeout(value);
++ }
++ return rv;
++}
++
+ /* end stonith API client functions */
+
+
+diff --git a/daemons/controld/controld_fencing.h b/daemons/controld/controld_fencing.h
+index d0ecc82..ef68a0c 100644
+--- a/daemons/controld/controld_fencing.h
++++ b/daemons/controld/controld_fencing.h
+@@ -24,6 +24,7 @@ void update_stonith_max_attempts(const char* value);
+ void controld_trigger_fencer_connect(void);
+ void controld_disconnect_fencer(bool destroy);
+ gboolean te_fence_node(crm_graph_t *graph, crm_action_t *action);
++bool controld_verify_stonith_watchdog_timeout(const char *value);
+
+ // stonith cleanup list
+ void add_stonith_cleanup(const char *target);
+diff --git a/daemons/fenced/Makefile.am b/daemons/fenced/Makefile.am
+index 43413e1..2923d7c 100644
+--- a/daemons/fenced/Makefile.am
++++ b/daemons/fenced/Makefile.am
+@@ -15,7 +15,7 @@ halibdir = $(CRM_DAEMON_DIR)
+
+ halib_PROGRAMS = pacemaker-fenced cts-fence-helper
+
+-sbin_SCRIPTS = fence_legacy
++sbin_SCRIPTS = fence_legacy fence_watchdog
+
+ noinst_HEADERS = pacemaker-fenced.h
+
+diff --git a/daemons/fenced/fence_watchdog.in b/daemons/fenced/fence_watchdog.in
+new file mode 100755
+index 0000000..c83304f
+--- /dev/null
++++ b/daemons/fenced/fence_watchdog.in
+@@ -0,0 +1,283 @@
++#!@PYTHON@
++"""Dummy watchdog fence agent for providing meta-data for the pacemaker internal agent
++"""
++
++__copyright__ = "Copyright 2012-2021 the Pacemaker project contributors"
++__license__ = "GNU General Public License version 2 or later (GPLv2+) WITHOUT ANY WARRANTY"
++
++import io
++import os
++import re
++import sys
++import atexit
++import getopt
++
++SHORT_DESC = "Dummy watchdog fence agent"
++LONG_DESC = """fence_watchdog just provides
++meta-data - actual fencing is done by the pacemaker internal watchdog agent."""
++
++ALL_OPT = {
++ "version" : {
++ "getopt" : "V",
++ "longopt" : "version",
++ "help" : "-V, --version Display version information and exit",
++ "required" : "0",
++ "shortdesc" : "Display version information and exit",
++ "order" : 53
++ },
++ "help" : {
++ "getopt" : "h",
++ "longopt" : "help",
++ "help" : "-h, --help Display this help and exit",
++ "required" : "0",
++ "shortdesc" : "Display help and exit",
++ "order" : 54
++ },
++ "action" : {
++ "getopt" : "o:",
++ "longopt" : "action",
++ "help" : "-o, --action=[action] Action: metadata",
++ "required" : "1",
++ "shortdesc" : "Fencing Action",
++ "default" : "metadata",
++ "order" : 1
++ },
++ "nodename" : {
++ "getopt" : "N:",
++ "longopt" : "nodename",
++ "help" : "-N, --nodename Node name of fence victim (ignored)",
++ "required" : "0",
++ "shortdesc" : "Ignored",
++ "order" : 2
++ },
++ "plug" : {
++ "getopt" : "n:",
++ "longopt" : "plug",
++ "help" : "-n, --plug=[id] Physical plug number on device (ignored)",
++ "required" : "1",
++ "shortdesc" : "Ignored",
++ "order" : 4
++ }
++}
++
++
++def agent():
++ """ Return name this file was run as. """
++
++ return os.path.basename(sys.argv[0])
++
++
++def fail_usage(message):
++ """ Print a usage message and exit. """
++
++ sys.exit("%s\nPlease use '-h' for usage" % message)
++
++
++def show_docs(options):
++ """ Handle informational options (display info and exit). """
++
++ device_opt = options["device_opt"]
++
++ if "-h" in options:
++ usage(device_opt)
++ sys.exit(0)
++
++ if "-o" in options and options["-o"].lower() == "metadata":
++ metadata(device_opt, options)
++ sys.exit(0)
++
++ if "-V" in options:
++ print(AGENT_VERSION)
++ sys.exit(0)
++
++
++def sorted_options(avail_opt):
++ """ Return a list of all options, in their internally specified order. """
++
++ sorted_list = [(key, ALL_OPT[key]) for key in avail_opt]
++ sorted_list.sort(key=lambda x: x[1]["order"])
++ return sorted_list
++
++
++def usage(avail_opt):
++ """ Print a usage message. """
++ print(LONG_DESC)
++ print()
++ print("Usage:")
++ print("\t" + agent() + " [options]")
++ print("Options:")
++
++ for dummy, value in sorted_options(avail_opt):
++ if len(value["help"]) != 0:
++ print(" " + value["help"])
++
++
++def metadata(avail_opt, options):
++ """ Print agent metadata. """
++
++ print("""<?xml version="1.0" ?>
++<resource-agent name="%s" shortdesc="%s">
++<longdesc>%s</longdesc>
++<parameters>""" % (agent(), SHORT_DESC, LONG_DESC))
++
++ for option, dummy in sorted_options(avail_opt):
++ if "shortdesc" in ALL_OPT[option]:
++ print(' <parameter name="' + option +
++ 'required="' + ALL_OPT[option]["required"] + '">')
++
++ default = ""
++ default_name_arg = "-" + ALL_OPT[option]["getopt"][:-1]
++ default_name_no_arg = "-" + ALL_OPT[option]["getopt"]
++
++ if "default" in ALL_OPT[option]:
++ default = 'default="%s"' % str(ALL_OPT[option]["default"])
++ elif default_name_arg in options:
++ if options[default_name_arg]:
++ try:
++ default = 'default="%s"' % options[default_name_arg]
++ except TypeError:
++ ## @todo/@note: Currently there is no clean way how to handle lists
++ ## we can create a string from it but we can't set it on command line
++ default = 'default="%s"' % str(options[default_name_arg])
++ elif default_name_no_arg in options:
++ default = 'default="true"'
++
++ mixed = ALL_OPT[option]["help"]
++ ## split it between option and help text
++ res = re.compile(r"^(.*--\S+)\s+", re.IGNORECASE | re.S).search(mixed)
++ if None != res:
++ mixed = res.group(1)
++ mixed = mixed.replace("<", "<").replace(">", ">")
++ print(' <getopt mixed="' + mixed + '" />')
++
++ if ALL_OPT[option]["getopt"].count(":") > 0:
++ print(' <content type="string" ' + default + ' />')
++ else:
++ print(' <content type="boolean" ' + default + ' />')
++
++ print(' <shortdesc lang="en">' + ALL_OPT[option]["shortdesc"] + '</shortdesc>')
++ print(' </parameter>')
++
++ print(' </parameters>\n <actions>')
++ print(' <action name="on" />')
++ print(' <action name="off" />')
++ print(' <action name="reboot" />')
++ print(' <action name="monitor" />')
++ print(' <action name="list" />')
++ print(' <action name="metadata" />')
++ print(' </actions>')
++ print('</resource-agent>')
++
++
++def option_longopt(option):
++ """ Return the getopt-compatible long-option name of the given option. """
++
++ if ALL_OPT[option]["getopt"].endswith(":"):
++ return ALL_OPT[option]["longopt"] + "="
++ else:
++ return ALL_OPT[option]["longopt"]
++
++
++def opts_from_command_line(argv, avail_opt):
++ """ Read options from command-line arguments. """
++
++ # Prepare list of options for getopt
++ getopt_string = ""
++ longopt_list = []
++ for k in avail_opt:
++ if k in ALL_OPT:
++ getopt_string += ALL_OPT[k]["getopt"]
++ else:
++ fail_usage("Parse error: unknown option '" + k + "'")
++
++ if k in ALL_OPT and "longopt" in ALL_OPT[k]:
++ longopt_list.append(option_longopt(k))
++
++ try:
++ opt, dummy = getopt.gnu_getopt(argv, getopt_string, longopt_list)
++ except getopt.GetoptError as error:
++ fail_usage("Parse error: " + error.msg)
++
++ # Transform longopt to short one which are used in fencing agents
++ old_opt = opt
++ opt = {}
++ for old_option in dict(old_opt).keys():
++ if old_option.startswith("--"):
++ for option in ALL_OPT.keys():
++ if "longopt" in ALL_OPT[option] and "--" + ALL_OPT[option]["longopt"] == old_option:
++ opt["-" + ALL_OPT[option]["getopt"].rstrip(":")] = dict(old_opt)[old_option]
++ else:
++ opt[old_option] = dict(old_opt)[old_option]
++
++ return opt
++
++
++def opts_from_stdin(avail_opt):
++ """ Read options from standard input. """
++
++ opt = {}
++ name = ""
++ for line in sys.stdin.readlines():
++ line = line.strip()
++ if line.startswith("#") or (len(line) == 0):
++ continue
++
++ (name, value) = (line + "=").split("=", 1)
++ value = value[:-1]
++
++ if name not in avail_opt:
++ print("Parse error: Ignoring unknown option '%s'" % line,
++ file=sys.stderr)
++ continue
++
++ if ALL_OPT[name]["getopt"].endswith(":"):
++ opt["-"+ALL_OPT[name]["getopt"].rstrip(":")] = value
++ elif value.lower() in ["1", "yes", "on", "true"]:
++ opt["-"+ALL_OPT[name]["getopt"]] = "1"
++
++ return opt
++
++
++def process_input(avail_opt):
++ """ Set standard environment variables, and parse all options. """
++
++ # Set standard environment
++ os.putenv("LANG", "C")
++ os.putenv("LC_ALL", "C")
++
++ # Read options from command line or standard input
++ if len(sys.argv) > 1:
++ return opts_from_command_line(sys.argv[1:], avail_opt)
++ else:
++ return opts_from_stdin(avail_opt)
++
++
++def atexit_handler():
++ """ Close stdout on exit. """
++
++ try:
++ sys.stdout.close()
++ os.close(1)
++ except IOError:
++ sys.exit("%s failed to close standard output" % agent())
++
++
++def main():
++ """ Make it so! """
++
++ device_opt = ALL_OPT.keys()
++
++ ## Defaults for fence agent
++ atexit.register(atexit_handler)
++ options = process_input(device_opt)
++ options["device_opt"] = device_opt
++ show_docs(options)
++
++ print("Watchdog fencing may be initiated only by the cluster, not this agent.",
++ file=sys.stderr)
++
++ sys.exit(1)
++
++
++if __name__ == "__main__":
++ main()
+diff --git a/daemons/fenced/fenced_commands.c b/daemons/fenced/fenced_commands.c
+index cd9968f..9470ea2 100644
+--- a/daemons/fenced/fenced_commands.c
++++ b/daemons/fenced/fenced_commands.c
+@@ -397,15 +397,13 @@ stonith_device_execute(stonith_device_t * device)
+ return TRUE;
+ }
+
+- if(pcmk__str_eq(device->agent, STONITH_WATCHDOG_AGENT, pcmk__str_casei)) {
+- if(pcmk__str_eq(cmd->action, "reboot", pcmk__str_casei)) {
+- pcmk__panic(__func__);
+- goto done;
+-
+- } else if(pcmk__str_eq(cmd->action, "off", pcmk__str_casei)) {
+- pcmk__panic(__func__);
+- goto done;
+-
++ if (pcmk__str_any_of(device->agent, STONITH_WATCHDOG_AGENT,
++ STONITH_WATCHDOG_AGENT_INTERNAL, NULL)) {
++ if (pcmk__strcase_any_of(cmd->action, "reboot", "off", NULL)) {
++ if (node_does_watchdog_fencing(stonith_our_uname)) {
++ pcmk__panic(__func__);
++ goto done;
++ }
+ } else {
+ crm_info("Faking success for %s watchdog operation", cmd->action);
+ cmd->done_cb(0, 0, NULL, cmd);
+@@ -716,7 +714,7 @@ get_agent_metadata(const char *agent, xmlNode ** metadata)
+ return EINVAL;
+ }
+ *metadata = NULL;
+- if (pcmk__str_eq(agent, STONITH_WATCHDOG_AGENT, pcmk__str_none)) {
++ if (pcmk__str_eq(agent, STONITH_WATCHDOG_AGENT_INTERNAL, pcmk__str_none)) {
+ return pcmk_rc_ok;
+ }
+ init_metadata_cache();
+@@ -1050,24 +1048,6 @@ schedule_internal_command(const char *origin,
+ schedule_stonith_command(cmd, device);
+ }
+
+-gboolean
+-string_in_list(GList *list, const char *item)
+-{
+- int lpc = 0;
+- int max = g_list_length(list);
+-
+- for (lpc = 0; lpc < max; lpc++) {
+- const char *value = g_list_nth_data(list, lpc);
+-
+- if (pcmk__str_eq(item, value, pcmk__str_casei)) {
+- return TRUE;
+- } else {
+- crm_trace("%d: '%s' != '%s'", lpc, item, value);
+- }
+- }
+- return FALSE;
+-}
+-
+ static void
+ status_search_cb(GPid pid, int rc, const char *output, gpointer user_data)
+ {
+@@ -1144,7 +1124,7 @@ dynamic_list_search_cb(GPid pid, int rc, const char *output, gpointer user_data)
+ if (!alias) {
+ alias = search->host;
+ }
+- if (string_in_list(dev->targets, alias)) {
++ if (pcmk__str_in_list(dev->targets, alias, pcmk__str_casei)) {
+ can_fence = TRUE;
+ }
+ }
+@@ -1215,9 +1195,62 @@ stonith_device_register(xmlNode * msg, const char **desc, gboolean from_cib)
+ stonith_device_t *dup = NULL;
+ stonith_device_t *device = build_device_from_xml(msg);
+ guint ndevices = 0;
++ int rv = pcmk_ok;
+
+ CRM_CHECK(device != NULL, return -ENOMEM);
+
++ /* do we have a watchdog-device? */
++ if (pcmk__str_eq(device->id, STONITH_WATCHDOG_ID, pcmk__str_none) ||
++ pcmk__str_any_of(device->agent, STONITH_WATCHDOG_AGENT,
++ STONITH_WATCHDOG_AGENT_INTERNAL, NULL)) do {
++ if (stonith_watchdog_timeout_ms <= 0) {
++ crm_err("Ignoring watchdog fence device without "
++ "stonith-watchdog-timeout set.");
++ rv = -ENODEV;
++ /* fall through to cleanup & return */
++ } else if (!pcmk__str_any_of(device->agent, STONITH_WATCHDOG_AGENT,
++ STONITH_WATCHDOG_AGENT_INTERNAL, NULL)) {
++ crm_err("Ignoring watchdog fence device with unknown "
++ "agent '%s' unequal '" STONITH_WATCHDOG_AGENT "'.",
++ device->agent?device->agent:"");
++ rv = -ENODEV;
++ /* fall through to cleanup & return */
++ } else if (!pcmk__str_eq(device->id, STONITH_WATCHDOG_ID,
++ pcmk__str_none)) {
++ crm_err("Ignoring watchdog fence device "
++ "named %s !='"STONITH_WATCHDOG_ID"'.",
++ device->id?device->id:"");
++ rv = -ENODEV;
++ /* fall through to cleanup & return */
++ } else {
++ if (pcmk__str_eq(device->agent, STONITH_WATCHDOG_AGENT,
++ pcmk__str_none)) {
++ /* this either has an empty list or the targets
++ configured for watchdog-fencing
++ */
++ g_list_free_full(stonith_watchdog_targets, free);
++ stonith_watchdog_targets = device->targets;
++ device->targets = NULL;
++ }
++ if (node_does_watchdog_fencing(stonith_our_uname)) {
++ g_list_free_full(device->targets, free);
++ device->targets = stonith__parse_targets(stonith_our_uname);
++ g_hash_table_replace(device->params,
++ strdup(PCMK_STONITH_HOST_LIST),
++ strdup(stonith_our_uname));
++ /* proceed as with any other stonith-device */
++ break;
++ }
++
++ crm_debug("Skip registration of watchdog fence device on node not in host-list.");
++ /* cleanup and fall through to more cleanup and return */
++ device->targets = NULL;
++ stonith_device_remove(device->id, from_cib);
++ }
++ free_device(device);
++ return rv;
++ } while (0);
++
+ dup = device_has_duplicate(device);
+ if (dup) {
+ ndevices = g_hash_table_size(device_list);
+@@ -1598,6 +1631,39 @@ stonith_level_remove(xmlNode *msg, char **desc)
+ * (CIB registration is not sufficient), because monitor should not be
+ * possible unless the device is "started" (API registered).
+ */
++
++static char *
++list_to_string(GList *list, const char *delim, gboolean terminate_with_delim)
++{
++ int max = g_list_length(list);
++ size_t delim_len = delim?strlen(delim):0;
++ size_t alloc_size = 1 + (max?((max-1+(terminate_with_delim?1:0))*delim_len):0);
++ char *rv;
++ GList *gIter;
++
++ for (gIter = list; gIter != NULL; gIter = gIter->next) {
++ const char *value = (const char *) gIter->data;
++
++ alloc_size += strlen(value);
++ }
++ rv = calloc(alloc_size, sizeof(char));
++ if (rv) {
++ char *pos = rv;
++ const char *lead_delim = "";
++
++ for (gIter = list; gIter != NULL; gIter = gIter->next) {
++ const char *value = (const char *) gIter->data;
++
++ pos = &pos[sprintf(pos, "%s%s", lead_delim, value)];
++ lead_delim = delim;
++ }
++ if (max && terminate_with_delim) {
++ sprintf(pos, "%s", delim);
++ }
++ }
++ return rv;
++}
++
+ static int
+ stonith_device_action(xmlNode * msg, char **output)
+ {
+@@ -1615,6 +1681,19 @@ stonith_device_action(xmlNode * msg, char **output)
+ return -EPROTO;
+ }
+
++ if (pcmk__str_eq(id, STONITH_WATCHDOG_ID, pcmk__str_none)) {
++ if (stonith_watchdog_timeout_ms <= 0) {
++ return -ENODEV;
++ } else {
++ if (pcmk__str_eq(action, "list", pcmk__str_casei)) {
++ *output = list_to_string(stonith_watchdog_targets, "\n", TRUE);
++ return pcmk_ok;
++ } else if (pcmk__str_eq(action, "monitor", pcmk__str_casei)) {
++ return pcmk_ok;
++ }
++ }
++ }
++
+ device = g_hash_table_lookup(device_list, id);
+ if ((device == NULL)
+ || (!device->api_registered && !strcmp(action, "monitor"))) {
+@@ -1742,7 +1821,7 @@ can_fence_host_with_device(stonith_device_t * dev, struct device_search_s *searc
+ * Only use if all hosts on which the device can be active can always fence all listed hosts
+ */
+
+- if (string_in_list(dev->targets, host)) {
++ if (pcmk__str_in_list(dev->targets, host, pcmk__str_casei)) {
+ can = TRUE;
+ } else if (g_hash_table_lookup(dev->params, PCMK_STONITH_HOST_MAP)
+ && g_hash_table_lookup(dev->aliases, host)) {
+@@ -1763,7 +1842,7 @@ can_fence_host_with_device(stonith_device_t * dev, struct device_search_s *searc
+ return;
+ }
+
+- if (string_in_list(dev->targets, alias)) {
++ if (pcmk__str_in_list(dev->targets, alias, pcmk__str_casei)) {
+ can = TRUE;
+ }
+
+diff --git a/daemons/fenced/fenced_remote.c b/daemons/fenced/fenced_remote.c
+index cf91aca..224f2ba 100644
+--- a/daemons/fenced/fenced_remote.c
++++ b/daemons/fenced/fenced_remote.c
+@@ -1522,6 +1522,25 @@ advance_topology_device_in_level(remote_fencing_op_t *op, const char *device,
+ }
+ }
+
++static gboolean
++check_watchdog_fencing_and_wait(remote_fencing_op_t * op)
++{
++ if (node_does_watchdog_fencing(op->target)) {
++
++ crm_notice("Waiting %lds for %s to self-fence (%s) for "
++ "client %s " CRM_XS " id=%.8s",
++ (stonith_watchdog_timeout_ms / 1000),
++ op->target, op->action, op->client_name, op->id);
++ op->op_timer_one = g_timeout_add(stonith_watchdog_timeout_ms,
++ remote_op_watchdog_done, op);
++ return TRUE;
++ } else {
++ crm_debug("Skipping fallback to watchdog-fencing as %s is "
++ "not in host-list", op->target);
++ }
++ return FALSE;
++}
++
+ void
+ call_remote_stonith(remote_fencing_op_t * op, st_query_result_t * peer, int rc)
+ {
+@@ -1592,26 +1611,33 @@ call_remote_stonith(remote_fencing_op_t * op, st_query_result_t * peer, int rc)
+ g_source_remove(op->op_timer_one);
+ }
+
+- if(stonith_watchdog_timeout_ms > 0 && device && pcmk__str_eq(device, "watchdog", pcmk__str_casei)) {
+- crm_notice("Waiting %lds for %s to self-fence (%s) for client %s "
+- CRM_XS " id=%.8s", (stonith_watchdog_timeout_ms / 1000),
+- op->target, op->action, op->client_name, op->id);
+- op->op_timer_one = g_timeout_add(stonith_watchdog_timeout_ms, remote_op_watchdog_done, op);
+-
+- /* TODO check devices to verify watchdog will be in use */
+- } else if(stonith_watchdog_timeout_ms > 0
+- && pcmk__str_eq(peer->host, op->target, pcmk__str_casei)
+- && !pcmk__str_eq(op->action, "on", pcmk__str_casei)) {
+- crm_notice("Waiting %lds for %s to self-fence (%s) for client %s "
+- CRM_XS " id=%.8s", (stonith_watchdog_timeout_ms / 1000),
+- op->target, op->action, op->client_name, op->id);
+- op->op_timer_one = g_timeout_add(stonith_watchdog_timeout_ms, remote_op_watchdog_done, op);
+-
+- } else {
++ if (!(stonith_watchdog_timeout_ms > 0 && (
++ (pcmk__str_eq(device, STONITH_WATCHDOG_ID,
++ pcmk__str_none)) ||
++ (pcmk__str_eq(peer->host, op->target, pcmk__str_casei)
++ && !pcmk__str_eq(op->action, "on", pcmk__str_casei))) &&
++ check_watchdog_fencing_and_wait(op))) {
++
++ /* Some thoughts about self-fencing cases reaching this point:
++ - Actually check in check_watchdog_fencing_and_wait
++ shouldn't fail if STONITH_WATCHDOG_ID is
++ chosen as fencing-device and it being present implies
++ watchdog-fencing is enabled anyway
++ - If watchdog-fencing is disabled either in general or for
++ a specific target - detected in check_watchdog_fencing_and_wait -
++ for some other kind of self-fencing we can't expect
++ a success answer but timeout is fine if the node doesn't
++ come back in between
++ - Delicate might be the case where we have watchdog-fencing
++ enabled for a node but the watchdog-fencing-device isn't
++ explicitly chosen for suicide. Local pe-execution in sbd
++ may detect the node as unclean and lead to timely suicide.
++ Otherwise the selection of stonith-watchdog-timeout at
++ least is questionable.
++ */
+ op->op_timer_one = g_timeout_add((1000 * timeout_one), remote_op_timeout_one, op);
+ }
+
+-
+ send_cluster_message(crm_get_peer(0, peer->host), crm_msg_stonith_ng, remote_op, FALSE);
+ peer->tried = TRUE;
+ free_xml(remote_op);
+@@ -1645,12 +1671,11 @@ call_remote_stonith(remote_fencing_op_t * op, st_query_result_t * peer, int rc)
+ * but we have all the expected replies, then no devices
+ * are available to execute the fencing operation. */
+
+- if(stonith_watchdog_timeout_ms && pcmk__str_eq(device, "watchdog", pcmk__str_null_matches | pcmk__str_casei)) {
+- crm_notice("Waiting %lds for %s to self-fence (%s) for client %s "
+- CRM_XS " id=%.8s", (stonith_watchdog_timeout_ms / 1000),
+- op->target, op->action, op->client_name, op->id);
+- op->op_timer_one = g_timeout_add(stonith_watchdog_timeout_ms, remote_op_watchdog_done, op);
+- return;
++ if(stonith_watchdog_timeout_ms > 0 && pcmk__str_eq(device,
++ STONITH_WATCHDOG_ID, pcmk__str_null_matches)) {
++ if (check_watchdog_fencing_and_wait(op)) {
++ return;
++ }
+ }
+
+ if (op->state == st_query) {
+diff --git a/daemons/fenced/pacemaker-fenced.c b/daemons/fenced/pacemaker-fenced.c
+index 39738d8..7f8b427 100644
+--- a/daemons/fenced/pacemaker-fenced.c
++++ b/daemons/fenced/pacemaker-fenced.c
+@@ -42,6 +42,7 @@
+
+ char *stonith_our_uname = NULL;
+ long stonith_watchdog_timeout_ms = 0;
++GList *stonith_watchdog_targets = NULL;
+
+ static GMainLoop *mainloop = NULL;
+
+@@ -578,7 +579,44 @@ our_node_allowed_for(pe_resource_t *rsc)
+ }
+
+ static void
+-watchdog_device_update(xmlNode *cib)
++watchdog_device_update(void)
++{
++ if (stonith_watchdog_timeout_ms > 0) {
++ if (!g_hash_table_lookup(device_list, STONITH_WATCHDOG_ID) &&
++ !stonith_watchdog_targets) {
++ /* getting here watchdog-fencing enabled, no device there yet
++ and reason isn't stonith_watchdog_targets preventing that
++ */
++ int rc;
++ xmlNode *xml;
++
++ xml = create_device_registration_xml(
++ STONITH_WATCHDOG_ID,
++ st_namespace_internal,
++ STONITH_WATCHDOG_AGENT,
++ NULL, /* stonith_device_register will add our
++ own name as PCMK_STONITH_HOST_LIST param
++ so we can skip that here
++ */
++ NULL);
++ rc = stonith_device_register(xml, NULL, TRUE);
++ free_xml(xml);
++ if (rc != pcmk_ok) {
++ crm_crit("Cannot register watchdog pseudo fence agent");
++ crm_exit(CRM_EX_FATAL);
++ }
++ }
++
++ } else {
++ /* be silent if no device - todo parameter to stonith_device_remove */
++ if (g_hash_table_lookup(device_list, STONITH_WATCHDOG_ID)) {
++ stonith_device_remove(STONITH_WATCHDOG_ID, TRUE);
++ }
++ }
++}
++
++static void
++update_stonith_watchdog_timeout_ms(xmlNode *cib)
+ {
+ xmlNode *stonith_enabled_xml = NULL;
+ const char *stonith_enabled_s = NULL;
+@@ -608,33 +646,7 @@ watchdog_device_update(xmlNode *cib)
+ }
+ }
+
+- if (timeout_ms != stonith_watchdog_timeout_ms) {
+- crm_notice("New watchdog timeout %lds (was %lds)", timeout_ms/1000, stonith_watchdog_timeout_ms/1000);
+- stonith_watchdog_timeout_ms = timeout_ms;
+-
+- if (stonith_watchdog_timeout_ms > 0) {
+- int rc;
+- xmlNode *xml;
+- stonith_key_value_t *params = NULL;
+-
+- params = stonith_key_value_add(params, PCMK_STONITH_HOST_LIST,
+- stonith_our_uname);
+-
+- xml = create_device_registration_xml("watchdog", st_namespace_internal,
+- STONITH_WATCHDOG_AGENT, params,
+- NULL);
+- stonith_key_value_freeall(params, 1, 1);
+- rc = stonith_device_register(xml, NULL, FALSE);
+- free_xml(xml);
+- if (rc != pcmk_ok) {
+- crm_crit("Cannot register watchdog pseudo fence agent");
+- crm_exit(CRM_EX_FATAL);
+- }
+-
+- } else {
+- stonith_device_remove("watchdog", FALSE);
+- }
+- }
++ stonith_watchdog_timeout_ms = timeout_ms;
+ }
+
+ /*!
+@@ -677,6 +689,16 @@ static void cib_device_update(pe_resource_t *rsc, pe_working_set_t *data_set)
+ return;
+ }
+
++ /* if watchdog-fencing is disabled handle any watchdog-fence
++ resource as if it was disabled
++ */
++ if ((stonith_watchdog_timeout_ms <= 0) &&
++ pcmk__str_eq(rsc->id, STONITH_WATCHDOG_ID, pcmk__str_none)) {
++ crm_info("Watchdog-fencing disabled thus handling "
++ "device %s as disabled", rsc->id);
++ return;
++ }
++
+ /* Check whether our node is allowed for this resource (and its parent if in a group) */
+ node = our_node_allowed_for(rsc);
+ if (rsc->parent && (rsc->parent->variant == pe_group)) {
+@@ -772,6 +794,12 @@ cib_devices_update(void)
+ }
+ }
+
++ /* have list repopulated if cib has a watchdog-fencing-resource
++ TODO: keep a cached list for queries happening while we are refreshing
++ */
++ g_list_free_full(stonith_watchdog_targets, free);
++ stonith_watchdog_targets = NULL;
++
+ for (gIter = fenced_data_set->resources; gIter != NULL; gIter = gIter->next) {
+ cib_device_update(gIter->data, fenced_data_set);
+ }
+@@ -825,6 +853,8 @@ update_cib_stonith_devices_v2(const char *event, xmlNode * msg)
+ if (search != NULL) {
+ *search = 0;
+ stonith_device_remove(rsc_id, TRUE);
++ /* watchdog_device_update called afterwards
++ to fall back to implicit definition if needed */
+ } else {
+ crm_warn("Ignoring malformed CIB update (resource deletion)");
+ }
+@@ -968,6 +998,24 @@ node_has_attr(const char *node, const char *name, const char *value)
+ return (match != NULL);
+ }
+
++/*!
++ * \internal
++ * \brief Check whether a node does watchdog-fencing
++ *
++ * \param[in] node Name of node to check
++ *
++ * \return TRUE if node found in stonith_watchdog_targets
++ * or stonith_watchdog_targets is empty indicating
++ * all nodes are doing watchdog-fencing
++ */
++gboolean
++node_does_watchdog_fencing(const char *node)
++{
++ return ((stonith_watchdog_targets == NULL) ||
++ pcmk__str_in_list(stonith_watchdog_targets, node, pcmk__str_casei));
++}
++
++
+ static void
+ update_fencing_topology(const char *event, xmlNode * msg)
+ {
+@@ -1073,6 +1121,8 @@ update_cib_cache_cb(const char *event, xmlNode * msg)
+ xmlNode *stonith_enabled_xml = NULL;
+ const char *stonith_enabled_s = NULL;
+ static gboolean stonith_enabled_saved = TRUE;
++ long timeout_ms_saved = stonith_watchdog_timeout_ms;
++ gboolean need_full_refresh = FALSE;
+
+ if(!have_cib_devices) {
+ crm_trace("Skipping updates until we get a full dump");
+@@ -1127,6 +1177,7 @@ update_cib_cache_cb(const char *event, xmlNode * msg)
+ }
+
+ pcmk__refresh_node_caches_from_cib(local_cib);
++ update_stonith_watchdog_timeout_ms(local_cib);
+
+ stonith_enabled_xml = get_xpath_object("//nvpair[@name='stonith-enabled']",
+ local_cib, LOG_NEVER);
+@@ -1134,23 +1185,30 @@ update_cib_cache_cb(const char *event, xmlNode * msg)
+ stonith_enabled_s = crm_element_value(stonith_enabled_xml, XML_NVPAIR_ATTR_VALUE);
+ }
+
+- watchdog_device_update(local_cib);
+-
+ if (stonith_enabled_s && crm_is_true(stonith_enabled_s) == FALSE) {
+ crm_trace("Ignoring CIB updates while fencing is disabled");
+ stonith_enabled_saved = FALSE;
+- return;
+
+ } else if (stonith_enabled_saved == FALSE) {
+ crm_info("Updating fencing device and topology lists "
+ "now that fencing is enabled");
+ stonith_enabled_saved = TRUE;
+- fencing_topology_init();
+- cib_devices_update();
++ need_full_refresh = TRUE;
+
+ } else {
+- update_fencing_topology(event, msg);
+- update_cib_stonith_devices(event, msg);
++ if (timeout_ms_saved != stonith_watchdog_timeout_ms) {
++ need_full_refresh = TRUE;
++ } else {
++ update_fencing_topology(event, msg);
++ update_cib_stonith_devices(event, msg);
++ watchdog_device_update();
++ }
++ }
++
++ if (need_full_refresh) {
++ fencing_topology_init();
++ cib_devices_update();
++ watchdog_device_update();
+ }
+ }
+
+@@ -1162,10 +1220,11 @@ init_cib_cache_cb(xmlNode * msg, int call_id, int rc, xmlNode * output, void *us
+ local_cib = copy_xml(output);
+
+ pcmk__refresh_node_caches_from_cib(local_cib);
++ update_stonith_watchdog_timeout_ms(local_cib);
+
+ fencing_topology_init();
+- watchdog_device_update(local_cib);
+ cib_devices_update();
++ watchdog_device_update();
+ }
+
+ static void
+diff --git a/daemons/fenced/pacemaker-fenced.h b/daemons/fenced/pacemaker-fenced.h
+index d330fda..14e085e 100644
+--- a/daemons/fenced/pacemaker-fenced.h
++++ b/daemons/fenced/pacemaker-fenced.h
+@@ -260,14 +260,15 @@ bool fencing_peer_active(crm_node_t *peer);
+
+ int stonith_manual_ack(xmlNode * msg, remote_fencing_op_t * op);
+
+-gboolean string_in_list(GList *list, const char *item);
+-
+ gboolean node_has_attr(const char *node, const char *name, const char *value);
+
++gboolean node_does_watchdog_fencing(const char *node);
++
+ extern char *stonith_our_uname;
+ extern gboolean stand_alone;
+ extern GHashTable *device_list;
+ extern GHashTable *topology;
+ extern long stonith_watchdog_timeout_ms;
++extern GList *stonith_watchdog_targets;
+
+ extern GHashTable *stonith_remote_op_list;
+diff --git a/include/crm/crm.h b/include/crm/crm.h
+index ee52c36..7861c16 100644
+--- a/include/crm/crm.h
++++ b/include/crm/crm.h
+@@ -66,7 +66,7 @@ extern "C" {
+ * >=3.0.13: Fail counts include operation name and interval
+ * >=3.2.0: DC supports PCMK_LRM_OP_INVALID and PCMK_LRM_OP_NOT_CONNECTED
+ */
+-# define CRM_FEATURE_SET "3.10.2"
++# define CRM_FEATURE_SET "3.11.0"
+
+ /* Pacemaker's CPG protocols use fixed-width binary fields for the sender and
+ * recipient of a CPG message. This imposes an arbitrary limit on cluster node
+diff --git a/include/crm/fencing/internal.h b/include/crm/fencing/internal.h
+index 8bcb544..f222edb 100644
+--- a/include/crm/fencing/internal.h
++++ b/include/crm/fencing/internal.h
+@@ -164,7 +164,10 @@ void stonith__device_parameter_flags(uint32_t *device_flags,
+ # define STONITH_OP_LEVEL_ADD "st_level_add"
+ # define STONITH_OP_LEVEL_DEL "st_level_remove"
+
+-# define STONITH_WATCHDOG_AGENT "#watchdog"
++# define STONITH_WATCHDOG_AGENT "fence_watchdog"
++/* Don't change 2 below as it would break rolling upgrade */
++# define STONITH_WATCHDOG_AGENT_INTERNAL "#watchdog"
++# define STONITH_WATCHDOG_ID "watchdog"
+
+ # ifdef HAVE_STONITH_STONITH_H
+ // utilities from st_lha.c
+@@ -211,4 +214,7 @@ stonith__op_state_pending(enum op_state state)
+ return state != st_failed && state != st_done;
+ }
+
++gboolean stonith__watchdog_fencing_enabled_for_node(const char *node);
++gboolean stonith__watchdog_fencing_enabled_for_node_api(stonith_t *st, const char *node);
++
+ #endif
+diff --git a/lib/fencing/st_client.c b/lib/fencing/st_client.c
+index e285f51..0ff9815 100644
+--- a/lib/fencing/st_client.c
++++ b/lib/fencing/st_client.c
+@@ -195,6 +195,67 @@ stonith_get_namespace(const char *agent, const char *namespace_s)
+ return st_namespace_invalid;
+ }
+
++gboolean
++stonith__watchdog_fencing_enabled_for_node_api(stonith_t *st, const char *node)
++{
++ gboolean rv = FALSE;
++ stonith_t *stonith_api = st?st:stonith_api_new();
++ char *list = NULL;
++
++ if(stonith_api) {
++ if (stonith_api->state == stonith_disconnected) {
++ int rc = stonith_api->cmds->connect(stonith_api, "stonith-api", NULL);
++
++ if (rc != pcmk_ok) {
++ crm_err("Failed connecting to Stonith-API for watchdog-fencing-query.");
++ }
++ }
++
++ if (stonith_api->state != stonith_disconnected) {
++ /* caveat!!!
++ * this might fail when when stonithd is just updating the device-list
++ * probably something we should fix as well for other api-calls */
++ int rc = stonith_api->cmds->list(stonith_api, st_opt_sync_call, STONITH_WATCHDOG_ID, &list, 0);
++ if ((rc != pcmk_ok) || (list == NULL)) {
++ /* due to the race described above it can happen that
++ * we drop in here - so as not to make remote nodes
++ * panic on that answer
++ */
++ crm_warn("watchdog-fencing-query failed");
++ } else if (list[0] == '\0') {
++ crm_warn("watchdog-fencing-query returned an empty list - any node");
++ rv = TRUE;
++ } else {
++ GList *targets = stonith__parse_targets(list);
++ rv = pcmk__str_in_list(targets, node, pcmk__str_casei);
++ g_list_free_full(targets, free);
++ }
++ free(list);
++ if (!st) {
++ /* if we're provided the api we still might have done the
++ * connection - but let's assume the caller won't bother
++ */
++ stonith_api->cmds->disconnect(stonith_api);
++ }
++ }
++
++ if (!st) {
++ stonith_api_delete(stonith_api);
++ }
++ } else {
++ crm_err("Stonith-API for watchdog-fencing-query couldn't be created.");
++ }
++ crm_trace("Pacemaker assumes node %s %sto do watchdog-fencing.",
++ node, rv?"":"not ");
++ return rv;
++}
++
++gboolean
++stonith__watchdog_fencing_enabled_for_node(const char *node)
++{
++ return stonith__watchdog_fencing_enabled_for_node_api(NULL, node);
++}
++
+ static void
+ log_action(stonith_action_t *action, pid_t pid)
+ {
+diff --git a/lib/lrmd/lrmd_client.c b/lib/lrmd/lrmd_client.c
+index 87d050e..bf4bceb 100644
+--- a/lib/lrmd/lrmd_client.c
++++ b/lib/lrmd/lrmd_client.c
+@@ -34,6 +34,7 @@
+ #include <crm/msg_xml.h>
+
+ #include <crm/stonith-ng.h>
++#include <crm/fencing/internal.h>
+
+ #ifdef HAVE_GNUTLS_GNUTLS_H
+ # undef KEYFILE
+@@ -934,7 +935,10 @@ lrmd__validate_remote_settings(lrmd_t *lrmd, GHashTable *hash)
+ crm_xml_add(data, F_LRMD_ORIGIN, __func__);
+
+ value = g_hash_table_lookup(hash, "stonith-watchdog-timeout");
+- crm_xml_add(data, F_LRMD_WATCHDOG, value);
++ if ((value) &&
++ (stonith__watchdog_fencing_enabled_for_node(native->remote_nodename))) {
++ crm_xml_add(data, F_LRMD_WATCHDOG, value);
++ }
+
+ rc = lrmd_send_command(lrmd, LRMD_OP_CHECK, data, NULL, 0, 0,
+ (native->type == pcmk__client_ipc));
+diff --git a/rpm/pacemaker.spec.in b/rpm/pacemaker.spec.in
+index 79e78ed..f58357a 100644
+--- a/rpm/pacemaker.spec.in
++++ b/rpm/pacemaker.spec.in
+@@ -744,6 +744,7 @@ exit 0
+ %doc %{_mandir}/man8/crm_attribute.*
+ %doc %{_mandir}/man8/crm_master.*
+ %doc %{_mandir}/man8/fence_legacy.*
++%doc %{_mandir}/man8/fence_watchdog.*
+ %doc %{_mandir}/man8/pacemakerd.*
+
+ %doc %{_datadir}/pacemaker/alerts
+@@ -796,6 +797,7 @@ exit 0
+ %{_sbindir}/crm_simulate
+ %{_sbindir}/crm_report
+ %{_sbindir}/crm_ticket
++%{_sbindir}/fence_watchdog
+ %{_sbindir}/stonith_admin
+ # "dirname" is owned by -schemas, which is a prerequisite
+ %{_datadir}/pacemaker/report.collector
+@@ -822,6 +824,7 @@ exit 0
+ %exclude %{_mandir}/man8/crm_attribute.*
+ %exclude %{_mandir}/man8/crm_master.*
+ %exclude %{_mandir}/man8/fence_legacy.*
++%exclude %{_mandir}/man8/fence_watchdog.*
+ %exclude %{_mandir}/man8/pacemakerd.*
+ %exclude %{_mandir}/man8/pacemaker-remoted.*
+
+--
+1.8.3.1
+
diff --git a/0002-Fix-watchdog-fencing-Silence-warning-without-node-re.patch b/0002-Fix-watchdog-fencing-Silence-warning-without-node-re.patch
new file mode 100644
index 0000000..ccfbccc
--- /dev/null
+++ b/0002-Fix-watchdog-fencing-Silence-warning-without-node-re.patch
@@ -0,0 +1,25 @@
+From 53dd360f096e5f005e3221e8d44d82d3654b5172 Mon Sep 17 00:00:00 2001
+From: Klaus Wenninger <klaus.wenninger(a)aon.at>
+Date: Wed, 4 Aug 2021 15:57:23 +0200
+Subject: [PATCH] Fix: watchdog-fencing: Silence warning without node
+ restriction
+
+---
+ lib/fencing/st_client.c | 1 -
+ 1 file changed, 1 deletion(-)
+
+diff --git a/lib/fencing/st_client.c b/lib/fencing/st_client.c
+index 0ff9815..14fa7b2 100644
+--- a/lib/fencing/st_client.c
++++ b/lib/fencing/st_client.c
+@@ -223,7 +223,6 @@ stonith__watchdog_fencing_enabled_for_node_api(stonith_t *st, const char *node)
+ */
+ crm_warn("watchdog-fencing-query failed");
+ } else if (list[0] == '\0') {
+- crm_warn("watchdog-fencing-query returned an empty list - any node");
+ rv = TRUE;
+ } else {
+ GList *targets = stonith__parse_targets(list);
+--
+1.8.3.1
+
diff --git a/pacemaker.spec b/pacemaker.spec
index b3f962b..a8f1d51 100644
--- a/pacemaker.spec
+++ b/pacemaker.spec
@@ -32,10 +32,10 @@
## can be incremented to build packages reliably considered "newer"
## than previously built packages with the same pcmkversion)
%global pcmkversion 2.1.1
-%global specversion 0.1.rc1
+%global specversion 0.2.rc2
## Upstream commit (full commit ID, abbreviated commit ID, or tag) to build
-%global commit 9e3a841a33c3125034922ec061f6516d307f20d1
+%global commit a64ad221abe9cdd968ceacd35c23832ce0fcd189
## Since git v2.11, the extent of abbreviation is autoscaled by default
## (used to be constant of 7), so we need to convey it for non-tags, too.
@@ -215,6 +215,8 @@ Url: https://www.clusterlabs.org/
# You can use "spectool -s 0 pacemaker.spec" (rpmdevtools) to show final URL.
Source0: https://codeload.github.com/%{github_owner}/%{name}/tar.gz/%{archive_gith...
Source1: https://codeload.github.com/%{github_owner}/%{nagios_name}/tar.gz/%{nagio...
+Patch0: 0001-feature-watchdog-fencing-allow-restriction-to-certai.patch
+Patch1: 0002-Fix-watchdog-fencing-Silence-warning-without-node-re.patch
# upstream commits
@@ -240,9 +242,19 @@ Requires: libqb >= 0.17.0
BuildRequires: libqb-devel >= 0.17.0
# Required basic build tools
-BuildRequires: coreutils findutils grep sed
-BuildRequires: autoconf automake gcc make pkgconfig
-BuildRequires: libtool %{?pkgname_libtool_devel}
+BuildRequires: autoconf
+BuildRequires: automake
+BuildRequires: coreutils
+BuildRequires: findutils
+BuildRequires: gcc
+BuildRequires: grep
+BuildRequires: libtool
+BuildRequires: make
+%if %{defined pkgname_libtool_devel}
+BuildRequires: %{?pkgname_libtool_devel}
+%endif
+BuildRequires: pkgconfig
+BuildRequires: sed
# Required for core functionality
BuildRequires: pkgconfig(glib-2.0) >= 2.42
@@ -250,8 +262,12 @@ BuildRequires: libxml2-devel libxslt-devel libuuid-devel
BuildRequires: %{pkgname_bzip2_devel}
# Enables optional functionality
-BuildRequires: ncurses-devel %{pkgname_docbook_xsl}
-BuildRequires: help2man %{pkgname_gnutls_devel} pam-devel pkgconfig(dbus-1)
+BuildRequires: pkgconfig(dbus-1)
+BuildRequires: %{pkgname_docbook_xsl}
+BuildRequires: %{pkgname_gnutls_devel}
+BuildRequires: help2man
+BuildRequires: ncurses-devel
+BuildRequires: pam-devel
BuildRequires: pkgconfig(systemd)
@@ -266,7 +282,9 @@ BuildRequires: %{pkgname_glue_libs}-devel
%endif
%if %{with doc}
-BuildRequires: asciidoc inkscape %{python_name}-sphinx
+BuildRequires: asciidoc
+BuildRequires: inkscape
+BuildRequires: %{python_name}-sphinx
%endif
Provides: pcmk-cluster-manager = %{version}-%{release}
@@ -368,11 +386,16 @@ License: GPLv2+ and LGPLv2+
Summary: Pacemaker development package
Requires: %{pkgname_pcmk_libs}%{?_isa} = %{version}-%{release}
Requires: %{name}-cluster-libs%{?_isa} = %{version}-%{release}
-Requires: %{?pkgname_libtool_devel_arch} libuuid-devel%{?_isa}
-Requires: libxml2-devel%{?_isa} libxslt-devel%{?_isa}
-Requires: %{pkgname_bzip2_devel}%{?_isa} glib2-devel%{?_isa}
+Requires: %{pkgname_bzip2_devel}%{?_isa}
+Requires: corosync-devel >= 2.0.0
+Requires: glib2-devel%{?_isa}
Requires: libqb-devel%{?_isa}
-Requires: corosync-devel%{?_isa} >= 2.0.0
+%if %{defined pkgname_libtool_devel_arch}
+Requires: %{?pkgname_libtool_devel_arch}
+%endif
+Requires: libuuid-devel%{?_isa}
+Requires: libxml2-devel%{?_isa}
+Requires: libxslt-devel%{?_isa}
%description -n %{pkgname_pcmk_libs}-devel
Pacemaker is an advanced, scalable High-Availability cluster resource
@@ -645,6 +668,7 @@ exit 0
%doc %{_mandir}/man7/ocf_pacemaker_remote.*
%doc %{_mandir}/man8/crm_attribute.*
%doc %{_mandir}/man8/crm_master.*
+%doc %{_mandir}/man8/fence_watchdog.*
%doc %{_mandir}/man8/pacemakerd.*
%doc %{_datadir}/pacemaker/alerts
@@ -685,6 +709,7 @@ exit 0
%{_sbindir}/crm_simulate
%{_sbindir}/crm_report
%{_sbindir}/crm_ticket
+%{_sbindir}/fence_watchdog
%{_sbindir}/stonith_admin
# "dirname" is owned by -schemas, which is a prerequisite
%{_datadir}/pacemaker/report.collector
@@ -711,6 +736,7 @@ exit 0
%exclude %{_mandir}/man8/crm_attribute.*
%exclude %{_mandir}/man8/crm_master.*
%exclude %{_mandir}/man8/fence_legacy.*
+%exclude %{_mandir}/man8/fence_watchdog.*
%exclude %{_mandir}/man8/pacemakerd.*
%exclude %{_mandir}/man8/pacemaker-remoted.*
@@ -799,6 +825,13 @@ exit 0
%license %{nagios_name}-%{nagios_hash}/COPYING
%changelog
+* Mon Aug 9 2021 Klaus Wenninger <kwenning(a)redhat.com> - 2.1.1-0.2.rc2
+- Update for new upstream tarball for release candidate: Pacemaker-2.1.1-rc2,
+ for full details, see included ChangeLog file or
+ https://github.com/ClusterLabs/pacemaker/releases/tag/Pacemaker-2.1.1-rc2
+- Added feature patch that allows to explicitly specify nodes that
+ should do watchdog-fencing
+
* Wed Jul 28 2021 Klaus Wenninger <kwenning(a)redhat.com> - 2.1.1-0.1.rc1
- Update for new upstream tarball for release candidate: Pacemaker-2.1.1-rc1,
for full details, see included ChangeLog file or
diff --git a/sources b/sources
index b933127..2ccc4d0 100644
--- a/sources
+++ b/sources
@@ -1,2 +1,2 @@
SHA512 (nagios-agents-metadata-105ab8a7b2c16b9a29cf1c1596b80136eeef332b.tar.gz) = 11ddeb48a4929e7642b6dfa9c7962aa1d7a1af1c569830f55ed6cd6773abac13377317327bc1db8411c8077884f83f81cc54d746c834b63a99fa6dc219b5caad
-SHA512 (pacemaker-9e3a841a3.tar.gz) = 26745ba0d9f6730de13ad106fe57beaff9d49c7df1ec528f0689c2faa872608408fb04401a4d49d73deec5422bd7665496a964b1fcbe69866a5983ac2306b6be
+SHA512 (pacemaker-a64ad221a.tar.gz) = ee954bd8efd135965290f9958b65680cbf101e14e2fd22d326e53a250281a7e5b6b6ec979c7f76072514dbf4d29868e99931842dbcc99bab5e5ba5e4bbc4ef0a
commit 6fcc0b8f5b6f278f33126c445552ad30336e5380
Author: Klaus Wenninger <klaus.wenninger(a)aon.at>
Date: Wed Jul 28 14:28:47 2021 +0200
* Wed Jul 28 2021 Klaus Wenninger <kwenning(a)redhat.com> - 2.1.1-0.1.rc1
- Update for new upstream tarball for release candidate: Pacemaker-2.1.1-rc1,
for full details, see included ChangeLog file or
https://github.com/ClusterLabs/pacemaker/releases/tag/Pacemaker-2.1.1-rc1
diff --git a/001-ping-agent.patch b/001-ping-agent.patch
deleted file mode 100644
index 89fe41a..0000000
--- a/001-ping-agent.patch
+++ /dev/null
@@ -1,225 +0,0 @@
-From c6ee0973522268ed7b3241cf0ec2e06398444114 Mon Sep 17 00:00:00 2001
-From: Grace Chin <gchin(a)redhat.com>
-Date: Tue, 4 May 2021 12:02:17 -0400
-Subject: [PATCH 1/4] Remove deprecated attrd_options
-
----
- extra/resources/ping | 11 +++--------
- 1 file changed, 3 insertions(+), 8 deletions(-)
-
-diff --git a/extra/resources/ping b/extra/resources/ping
-index 3cf8dfe..2e93f22 100755
---- a/extra/resources/ping
-+++ b/extra/resources/ping
-@@ -178,7 +178,7 @@ ping_stop() {
-
- rm -f "${OCF_RESKEY_pidfile}"
-
-- attrd_updater -D -n "$OCF_RESKEY_name" -d "$OCF_RESKEY_dampen" $attrd_options
-+ attrd_updater -D -n "$OCF_RESKEY_name" -d "$OCF_RESKEY_dampen"
-
- return $OCF_SUCCESS
- }
-@@ -302,9 +302,9 @@ ping_update() {
-
- score=$(expr $active \* $OCF_RESKEY_multiplier)
- if [ "$__OCF_ACTION" = "start" ] ; then
-- attrd_updater -n "$OCF_RESKEY_name" -B "$score" -d "$OCF_RESKEY_dampen" $attrd_options
-+ attrd_updater -n "$OCF_RESKEY_name" -B "$score" -d "$OCF_RESKEY_dampen"
- else
-- attrd_updater -n "$OCF_RESKEY_name" -v "$score" -d "$OCF_RESKEY_dampen" $attrd_options
-+ attrd_updater -n "$OCF_RESKEY_name" -v "$score" -d "$OCF_RESKEY_dampen"
- fi
- rc=$?
- case $rc in
-@@ -396,11 +396,6 @@ case "${OCF_RESKEY_debug}" in
- ;;
- esac
-
--attrd_options='-q'
--if [ "${OCF_RESKEY_debug}" = "true" ]; then
-- attrd_options=''
--fi
--
- case "$__OCF_ACTION" in
- meta-data) meta_data
- exit $OCF_SUCCESS
---
-1.8.3.1
-
-
-From 6d6c4691cf0970059689856c354daf9e098b4451 Mon Sep 17 00:00:00 2001
-From: Grace Chin <gchin(a)redhat.com>
-Date: Tue, 4 May 2021 14:50:37 -0400
-Subject: [PATCH 2/4] Replace debug values, true and false, with 0 and 1
-
----
- extra/resources/ping | 8 ++++----
- 1 file changed, 4 insertions(+), 4 deletions(-)
-
-diff --git a/extra/resources/ping b/extra/resources/ping
-index 2e93f22..fee019b 100755
---- a/extra/resources/ping
-+++ b/extra/resources/ping
-@@ -24,7 +24,7 @@
- : ${OCF_RESKEY_dampen:="5s"}
- : ${OCF_RESKEY_attempts:="3"}
- : ${OCF_RESKEY_multiplier:="1"}
--: ${OCF_RESKEY_debug:="false"}
-+: ${OCF_RESKEY_debug:="0"}
- : ${OCF_RESKEY_failure_score:="0"}
- : ${OCF_RESKEY_use_fping:="1"}
- : ${OCF_RESKEY_host_list:=""}
-@@ -152,7 +152,7 @@ END
-
- ping_conditional_log() {
- level="$1"; shift
-- if [ "${OCF_RESKEY_debug}" = "true" ]; then
-+ if [ $OCF_RESKEY_debug -gt 0 ]; then
- ocf_log "$level" "$*"
- fi
- }
-@@ -388,8 +388,8 @@ fi
-
- # Check the debug option
- case "${OCF_RESKEY_debug}" in
-- true|True|TRUE|1) OCF_RESKEY_debug=true;;
-- false|False|FALSE|0) OCF_RESKEY_debug=false;;
-+ true|True|TRUE|1) OCF_RESKEY_debug=0;;
-+ false|False|FALSE|0) OCF_RESKEY_debug=1;;
- *)
- ocf_log warn "Value for 'debug' is incorrect. Please specify 'true' or 'false' not: ${OCF_RESKEY_debug}"
- OCF_RESKEY_debug=false
---
-1.8.3.1
-
-
-From a886a31056b6aca764c6911f5432af2c5ebf51df Mon Sep 17 00:00:00 2001
-From: Grace Chin <gchin(a)redhat.com>
-Date: Tue, 11 May 2021 11:04:50 -0400
-Subject: [PATCH 3/4] Add verbose debug mode which logs ping and fping output
- when set
-
----
- extra/resources/ping | 19 ++++++++++++++-----
- 1 file changed, 14 insertions(+), 5 deletions(-)
-
-diff --git a/extra/resources/ping b/extra/resources/ping
-index fee019b..cc796af 100755
---- a/extra/resources/ping
-+++ b/extra/resources/ping
-@@ -249,10 +249,13 @@ fping_check() {
-
- case $rc in
- 0)
-+ if [ $OCF_RESKEY_debug -gt 1 ]; then
-+ ping_conditional_log info "$output"
-+ fi
- ;;
- 1)
- for h in $(echo "$output" | grep "is unreachable" | awk '{print $1}'); do
-- ping_conditional_log warn "$h is inactive"
-+ ping_conditional_log warn "$h is inactive: $output"
- done
- ;;
- *)
-@@ -282,7 +285,12 @@ ping_check() {
- p_out=$($p_exe $p_args $OCF_RESKEY_options $host 2>&1); rc=$?
-
- case $rc in
-- 0) active=$(expr $active + 1);;
-+ 0)
-+ active=$(expr $active + 1)
-+ if [ $OCF_RESKEY_debug -gt 1 ]; then
-+ ping_conditional_log info "$p_out"
-+ fi
-+ ;;
- 1) ping_conditional_log warn "$host is inactive: $p_out";;
- *) ocf_log err "Unexpected result for '$p_exe $p_args $OCF_RESKEY_options $host' $rc: $p_out";;
- esac
-@@ -388,10 +396,11 @@ fi
-
- # Check the debug option
- case "${OCF_RESKEY_debug}" in
-- true|True|TRUE|1) OCF_RESKEY_debug=0;;
-- false|False|FALSE|0) OCF_RESKEY_debug=1;;
-+ true|True|TRUE|1) OCF_RESKEY_debug=1;;
-+ false|False|FALSE|0) OCF_RESKEY_debug=0;;
-+ verbose|Verbose|VERBOSE|2) OCF_RESKEY_debug=2;;
- *)
-- ocf_log warn "Value for 'debug' is incorrect. Please specify 'true' or 'false' not: ${OCF_RESKEY_debug}"
-+ ocf_log warn "Value for 'debug' is incorrect. Please specify 'true', 'false', or 'verbose', not: ${OCF_RESKEY_debug}"
- OCF_RESKEY_debug=false
- ;;
- esac
---
-1.8.3.1
-
-
-From 460043f133ced80e923b1290af70502a72deb7f8 Mon Sep 17 00:00:00 2001
-From: Grace Chin <gchin(a)redhat.com>
-Date: Tue, 11 May 2021 11:07:05 -0400
-Subject: [PATCH 4/4] Improve variable names
-
----
- extra/resources/ping | 20 ++++++++++----------
- 1 file changed, 10 insertions(+), 10 deletions(-)
-
-diff --git a/extra/resources/ping b/extra/resources/ping
-index cc796af..9763b60 100755
---- a/extra/resources/ping
-+++ b/extra/resources/ping
-@@ -244,22 +244,22 @@ fping_check() {
- timeout=$(expr $OCF_RESKEY_timeout \* 1000 / $OCF_RESKEY_attempts)
-
- cmd="$p_exe -r $OCF_RESKEY_attempts -t $timeout -B 1.0 $OCF_RESKEY_options $OCF_RESKEY_host_list"
-- output=$($cmd 2>&1); rc=$?
-- active=$(echo "$output" | grep "is alive" | wc -l)
-+ fping_output=$($cmd 2>&1); rc=$?
-+ active=$(echo "$fping_output" | grep "is alive" | wc -l)
-
- case $rc in
- 0)
- if [ $OCF_RESKEY_debug -gt 1 ]; then
-- ping_conditional_log info "$output"
-+ ping_conditional_log info "$fping_output"
- fi
- ;;
- 1)
-- for h in $(echo "$output" | grep "is unreachable" | awk '{print $1}'); do
-- ping_conditional_log warn "$h is inactive: $output"
-+ for h in $(echo "$fping_output" | grep "is unreachable" | awk '{print $1}'); do
-+ ping_conditional_log warn "$h is inactive: $fping_output"
- done
- ;;
- *)
-- ocf_log err "Unexpected result for '$cmd' $rc: $(echo "$output" | tr '\n' ';')"
-+ ocf_log err "Unexpected result for '$cmd' $rc: $(echo "$fping_output" | tr '\n' ';')"
- ;;
- esac
-
-@@ -282,17 +282,17 @@ ping_check() {
- *:*) p_exe=ping6
- esac
-
-- p_out=$($p_exe $p_args $OCF_RESKEY_options $host 2>&1); rc=$?
-+ ping_output=$($p_exe $p_args $OCF_RESKEY_options $host 2>&1); rc=$?
-
- case $rc in
- 0)
- active=$(expr $active + 1)
- if [ $OCF_RESKEY_debug -gt 1 ]; then
-- ping_conditional_log info "$p_out"
-+ ping_conditional_log info "$ping_output"
- fi
- ;;
-- 1) ping_conditional_log warn "$host is inactive: $p_out";;
-- *) ocf_log err "Unexpected result for '$p_exe $p_args $OCF_RESKEY_options $host' $rc: $p_out";;
-+ 1) ping_conditional_log warn "$host is inactive: $ping_output";;
-+ *) ocf_log err "Unexpected result for '$p_exe $p_args $OCF_RESKEY_options $host' $rc: $ping_output";;
- esac
- done
- return $active
---
-1.8.3.1
-
diff --git a/002-pacemakerd-options.patch b/002-pacemakerd-options.patch
deleted file mode 100644
index 56941ec..0000000
--- a/002-pacemakerd-options.patch
+++ /dev/null
@@ -1,451 +0,0 @@
-From 0d40ebf10b1794ece2c5c9768ea7222d3834d3b3 Mon Sep 17 00:00:00 2001
-From: Chris Lumens <clumens(a)redhat.com>
-Date: Thu, 13 May 2021 11:42:18 -0400
-Subject: [PATCH 1/4] Build: Use a different variable to find man page
- includes.
-
-With other programs outside of the tools directory being converted to
-use glib for command line handling, their includes are not going to be
-in tools/. So we need to use a different autoconf variable to find
-them.
----
- mk/common.mk | 6 +++---
- 1 file changed, 3 insertions(+), 3 deletions(-)
-
-diff --git a/mk/common.mk b/mk/common.mk
-index b247670..aa59feb 100644
---- a/mk/common.mk
-+++ b/mk/common.mk
-@@ -1,5 +1,5 @@
- #
--# Copyright 2014-2020 the Pacemaker project contributors
-+# Copyright 2014-2021 the Pacemaker project contributors
- #
- # The version control history for this file may have further details.
- #
-@@ -68,11 +68,11 @@ HELP2MAN_ARGS = -N --section 8 --name "Part of the Pacemaker cluster resource ma
- # and all wrappers to C code.
- %.8: % $(MAN8DEPS)
- $(AM_V_at)chmod a+x $(abs_builddir)/$<
-- $(AM_V_MAN)if [ -f $(top_srcdir)/tools/$@.inc ]; then \
-+ $(AM_V_MAN)if [ -f $(abs_srcdir)/$@.inc ]; then \
- PATH=$(abs_builddir):$$PATH $(HELP2MAN) $(HELP2MAN_ARGS) \
- -h --help-all \
- --no-discard-stderr \
-- -i $(top_srcdir)/tools/$@.inc $(abs_builddir)/$< \
-+ -i $(abs_srcdir)/$@.inc $(abs_builddir)/$< \
- | sed -f $(top_srcdir)/tools/fix-manpages > $@ ; \
- else \
- PATH=$(abs_builddir):$$PATH $(HELP2MAN) $(HELP2MAN_ARGS) \
---
-1.8.3.1
-
-
-From c7ab1d901bcbbf0137277e783e072777ca2f82d9 Mon Sep 17 00:00:00 2001
-From: Chris Lumens <clumens(a)redhat.com>
-Date: Thu, 13 May 2021 11:44:16 -0400
-Subject: [PATCH 2/4] Refactor: daemons: Remove the pid_file variable from
- pacemakerd.
-
-It's never used anywhere.
----
- daemons/pacemakerd/pacemakerd.c | 3 ---
- 1 file changed, 3 deletions(-)
-
-diff --git a/daemons/pacemakerd/pacemakerd.c b/daemons/pacemakerd/pacemakerd.c
-index 8ec9708..03d688e 100644
---- a/daemons/pacemakerd/pacemakerd.c
-+++ b/daemons/pacemakerd/pacemakerd.c
-@@ -27,8 +27,7 @@
-
- static crm_trigger_t *shutdown_trigger = NULL;
- static crm_trigger_t *startup_trigger = NULL;
--static const char *pid_file = PCMK_RUN_DIR "/pacemaker.pid";
-
- /* state we report when asked via pacemakerd-api status-ping */
- static const char *pacemakerd_state = XML_PING_ATTR_PACEMAKERDSTATE_INIT;
- static gboolean running_with_sbd = FALSE; /* local copy */
-@@ -224,7 +222,6 @@ main(int argc, char **argv)
- /* Legacy */
- break;
- case 'p':
-- pid_file = optarg;
- break;
- case 's':
- pcmk__set_env_option("node_start_state", "standby");
---
-1.8.3.1
-
-
-From 98990eed9f6a5dbde7c8a5aa0783e93d5479295b Mon Sep 17 00:00:00 2001
-From: Chris Lumens <clumens(a)redhat.com>
-Date: Thu, 13 May 2021 13:14:38 -0400
-Subject: [PATCH 3/4] Refactor: daemons: Use glib for command line handling in
- pacemakerd.
-
----
- daemons/pacemakerd/Makefile.am | 2 +
- daemons/pacemakerd/pacemakerd.8.inc | 5 +
- daemons/pacemakerd/pacemakerd.c | 195 ++++++++++++++++++------------------
- 3 files changed, 102 insertions(+), 100 deletions(-)
- create mode 100644 daemons/pacemakerd/pacemakerd.8.inc
-
-diff --git a/daemons/pacemakerd/Makefile.am b/daemons/pacemakerd/Makefile.am
-index cc657f5..84517a3 100644
---- a/daemons/pacemakerd/Makefile.am
-+++ b/daemons/pacemakerd/Makefile.am
-@@ -15,6 +15,8 @@ if BUILD_SYSTEMD
- systemdsystemunit_DATA = pacemaker.service
- endif
-
-+EXTRA_DIST = pacemakerd.8.inc
-+
- ## SOURCES
-
- noinst_HEADERS = pacemakerd.h
-diff --git a/daemons/pacemakerd/pacemakerd.8.inc b/daemons/pacemakerd/pacemakerd.8.inc
-new file mode 100644
-index 0000000..902af4e
---- /dev/null
-+++ b/daemons/pacemakerd/pacemakerd.8.inc
-@@ -0,0 +1,5 @@
-+[synopsis]
-+pacemakerd [options]
-+
-+/subsidiary Pacemaker daemons/
-+.SH OPTIONS
-diff --git a/daemons/pacemakerd/pacemakerd.c b/daemons/pacemakerd/pacemakerd.c
-index 03d688e..ce194bf 100644
---- a/daemons/pacemakerd/pacemakerd.c
-+++ b/daemons/pacemakerd/pacemakerd.c
-@@ -23,12 +23,54 @@
- #include <crm/msg_xml.h>
- #include <crm/common/ipc_internal.h>
- #include <crm/common/mainloop.h>
-+#include <crm/common/cmdline_internal.h>
- #include <crm/cluster/internal.h>
- #include <crm/cluster.h>
-
- #include <dirent.h>
- #include <ctype.h>
-
-+#define SUMMARY "pacemakerd - primary Pacemaker daemon that launches and monitors all subsidiary Pacemaker daemons"
-+
-+struct {
-+ gboolean features;
-+ gboolean foreground;
-+ gboolean shutdown;
-+ gboolean standby;
-+} options;
-+
-+static gboolean
-+pid_cb(const gchar *option_name, const gchar *optarg, gpointer data, GError **err) {
-+ return TRUE;
-+}
-+
-+static gboolean
-+standby_cb(const gchar *option_name, const gchar *optarg, gpointer data, GError **err) {
-+ options.standby = TRUE;
-+ pcmk__set_env_option("node_start_state", "standby");
-+ return TRUE;
-+}
-+
-+static GOptionEntry entries[] = {
-+ { "features", 'F', 0, G_OPTION_ARG_NONE, &options.features,
-+ "Display full version and list of features Pacemaker was built with",
-+ NULL },
-+ { "foreground", 'f', 0, G_OPTION_ARG_NONE, &options.foreground,
-+ "(Ignored) Pacemaker always runs in the foreground",
-+ NULL },
-+ { "pid-file", 'p', 0, G_OPTION_ARG_CALLBACK, pid_cb,
-+ "(Ignored) Daemon pid file location",
-+ "FILE" },
-+ { "shutdown", 'S', 0, G_OPTION_ARG_NONE, &options.shutdown,
-+ "Instruct Pacemaker to shutdown on this machine",
-+ NULL },
-+ { "standby", 's', G_OPTION_FLAG_NO_ARG, G_OPTION_ARG_CALLBACK, standby_cb,
-+ "Start node in standby state",
-+ NULL },
-+
-+ { NULL }
-+};
-+
- static gboolean fatal_error = FALSE;
- static GMainLoop *mainloop = NULL;
- static bool global_keep_tracking = false;
-@@ -642,49 +685,6 @@ pcmk_sigquit(int nsig)
- .connection_destroyed = pcmk_ipc_destroy
- };
-
--static pcmk__cli_option_t long_options[] = {
-- // long option, argument type, storage, short option, description, flags
-- {
-- "help", no_argument, NULL, '?',
-- "\tThis text", pcmk__option_default
-- },
-- {
-- "version", no_argument, NULL, '$',
-- "\tVersion information", pcmk__option_default
-- },
-- {
-- "verbose", no_argument, NULL, 'V',
-- "\tIncrease debug output", pcmk__option_default
-- },
-- {
-- "shutdown", no_argument, NULL, 'S',
-- "\tInstruct Pacemaker to shutdown on this machine", pcmk__option_default
-- },
-- {
-- "features", no_argument, NULL, 'F',
-- "\tDisplay full version and list of features Pacemaker was built with",
-- pcmk__option_default
-- },
-- {
-- "-spacer-", no_argument, NULL, '-',
-- "\nAdditional Options:", pcmk__option_default
-- },
-- {
-- "foreground", no_argument, NULL, 'f',
-- "\t(Ignored) Pacemaker always runs in the foreground",
-- pcmk__option_default
-- },
-- {
-- "pid-file", required_argument, NULL, 'p',
-- "\t(Ignored) Daemon pid file location", pcmk__option_default
-- },
-- {
-- "standby", no_argument, NULL, 's',
-- "\tStart node in standby state", pcmk__option_default
-- },
-- { 0, 0, 0, 0 }
--};
--
- static void
- mcp_chown(const char *path, uid_t uid, gid_t gid)
- {
-@@ -1168,83 +1211,66 @@ request_shutdown(crm_ipc_t *ipc)
- return status;
- }
-
-+static GOptionContext *
-+build_arg_context(pcmk__common_args_t *args) {
-+ GOptionContext *context = NULL;
-+
-+ context = pcmk__build_arg_context(args, NULL, NULL, NULL);
-+ pcmk__add_main_args(context, entries);
-+ return context;
-+}
-+
- int
- main(int argc, char **argv)
- {
-- int flag;
-- int argerr = 0;
-+ crm_exit_t exit_code = CRM_EX_OK;
-+
-+ GError *error = NULL;
-+
-+ pcmk__common_args_t *args = pcmk__new_common_args(SUMMARY);
-+ gchar **processed_args = pcmk__cmdline_preproc(argv, "p");
-+ GOptionContext *context = build_arg_context(args);
-
-- int option_index = 0;
- bool old_instance_connected = false;
-- gboolean shutdown = FALSE;
-
- crm_ipc_t *old_instance = NULL;
- qb_ipcs_service_t *ipcs = NULL;
-
- crm_log_preinit(NULL, argc, argv);
-- pcmk__set_cli_options(NULL, "[options]", long_options,
-- "primary Pacemaker daemon that launches and "
-- "monitors all subsidiary Pacemaker daemons");
- mainloop_add_signal(SIGHUP, pcmk_ignore);
- mainloop_add_signal(SIGQUIT, pcmk_sigquit);
-
-- while (1) {
-- flag = pcmk__next_cli_option(argc, argv, &option_index, NULL);
-- if (flag == -1)
-- break;
--
-- switch (flag) {
-- case 'V':
-- crm_bump_log_level(argc, argv);
-- break;
-- case 'f':
-- /* Legacy */
-- break;
-- case 'p':
-- break;
-- case 's':
-- pcmk__set_env_option("node_start_state", "standby");
-- break;
-- case '$':
-- case '?':
-- pcmk__cli_help(flag, CRM_EX_OK);
-- break;
-- case 'S':
-- shutdown = TRUE;
-- break;
-- case 'F':
-- printf("Pacemaker %s (Build: %s)\n Supporting v%s: %s\n", PACEMAKER_VERSION, BUILD_VERSION,
-- CRM_FEATURE_SET, CRM_FEATURES);
-- crm_exit(CRM_EX_OK);
-- default:
-- printf("Argument code 0%o (%c) is not (?yet?) supported\n", flag, flag);
-- ++argerr;
-- break;
-- }
-+ if (!g_option_context_parse_strv(context, &processed_args, &error)) {
-+ exit_code = CRM_EX_USAGE;
-+ goto done;
- }
-
-- if (optind < argc) {
-- printf("non-option ARGV-elements: ");
-- while (optind < argc)
-- printf("%s ", argv[optind++]);
-- printf("\n");
-- }
-- if (argerr) {
-- pcmk__cli_help('?', CRM_EX_USAGE);
-+ if (options.features) {
-+ printf("Pacemaker %s (Build: %s)\n Supporting v%s: %s\n", PACEMAKER_VERSION, BUILD_VERSION,
-+ CRM_FEATURE_SET, CRM_FEATURES);
-+ exit_code = CRM_EX_OK;
-+ goto done;
- }
-
-+ if (args->version) {
-+ g_strfreev(processed_args);
-+ pcmk__free_arg_context(context);
-+ /* FIXME: When pacemakerd is converted to use formatted output, this can go. */
-+ pcmk__cli_help('v', CRM_EX_USAGE);
-+ }
-
- setenv("LC_ALL", "C", 1);
-
- pcmk__set_env_option("mcp", "true");
-
-+ pcmk__cli_init_logging("pacemakerd", args->verbosity);
- crm_log_init(NULL, LOG_INFO, TRUE, FALSE, argc, argv, FALSE);
-
- crm_debug("Checking for existing Pacemaker instance");
- old_instance = crm_ipc_new(CRM_SYSTEM_MCP, 0);
- old_instance_connected = crm_ipc_connect(old_instance);
-
-- if (shutdown) {
-+ if (options.shutdown) {
- if (old_instance_connected) {
- crm_exit(request_shutdown(old_instance));
- } else {
-@@ -1253,22 +1279,25 @@ main(int argc, char **argv)
- "Pacemaker instance: %s", strerror(errno));
- crm_ipc_close(old_instance);
- crm_ipc_destroy(old_instance);
-- crm_exit(CRM_EX_DISCONNECT);
-+ exit_code = CRM_EX_DISCONNECT;
-+ goto done;
- }
-
- } else if (old_instance_connected) {
- crm_ipc_close(old_instance);
- crm_ipc_destroy(old_instance);
- crm_err("Aborting start-up because active Pacemaker instance found");
-- crm_exit(CRM_EX_FATAL);
-+ exit_code = CRM_EX_FATAL;
-+ goto done;
- }
-
- crm_ipc_close(old_instance);
- crm_ipc_destroy(old_instance);
-
- #ifdef SUPPORT_COROSYNC
- if (mcp_read_config() == FALSE) {
-- crm_exit(CRM_EX_UNAVAILABLE);
-+ exit_code = CRM_EX_UNAVAILABLE;
-+ goto done;
- }
- #endif
-
-@@ -1292,7 +1321,8 @@ main(int argc, char **argv)
- #ifdef SUPPORT_COROSYNC
- /* Allows us to block shutdown */
- if (!cluster_connect_cfg()) {
-- crm_exit(CRM_EX_PROTOCOL);
-+ exit_code = CRM_EX_PROTOCOL;
-+ goto done;
- }
- #endif
-
-@@ -1307,9 +1337,11 @@ main(int argc, char **argv)
- case pcmk_rc_ok:
- break;
- case pcmk_rc_ipc_unauthorized:
-- crm_exit(CRM_EX_CANTCREAT);
-+ exit_code = CRM_EX_CANTCREAT;
-+ goto done;
- default:
-- crm_exit(CRM_EX_FATAL);
-+ exit_code = CRM_EX_FATAL;
-+ goto done;
- };
-
- mainloop_add_signal(SIGTERM, pcmk_shutdown);
-@@ -1342,5 +1374,11 @@ main(int argc, char **argv)
- #ifdef SUPPORT_COROSYNC
- cluster_disconnect_cfg();
- #endif
-- crm_exit(CRM_EX_OK);
-+
-+done:
-+ g_strfreev(processed_args);
-+ pcmk__free_arg_context(context);
-+
-+ pcmk__output_and_clear_error(error, NULL);
-+ crm_exit(exit_code);
- }
---
-1.8.3.1
-
-
-From 8f7924fbb2a012bedcad59335b7bebc5020b26e3 Mon Sep 17 00:00:00 2001
-From: Chris Lumens <clumens(a)redhat.com>
-Date: Thu, 13 May 2021 13:27:13 -0400
-Subject: [PATCH 4/4] Low: pacemaker.service: Don't start pacemakerd with -f.
-
-This option is completely ignored by pacemakerd.
----
- daemons/pacemakerd/pacemaker.service.in | 2 +-
- doc/sphinx/Clusters_from_Scratch/verification.rst | 2 +-
- 2 files changed, 2 insertions(+), 2 deletions(-)
-
-diff --git a/daemons/pacemakerd/pacemaker.service.in b/daemons/pacemakerd/pacemaker.service.in
-index b128ddc..0363a22 100644
---- a/daemons/pacemakerd/pacemaker.service.in
-+++ b/daemons/pacemakerd/pacemaker.service.in
-@@ -44,7 +44,7 @@ EnvironmentFile=-@CONFIGDIR@/pacemaker
- EnvironmentFile=-@CONFIGDIR@/sbd
- SuccessExitStatus=100
-
--ExecStart=@sbindir@/pacemakerd -f
-+ExecStart=@sbindir@/pacemakerd
-
- # Systemd v227 and above can limit the number of processes spawned by a
- # service. That is a bad idea for an HA cluster resource manager, so disable it
-diff --git a/doc/sphinx/Clusters_from_Scratch/verification.rst b/doc/sphinx/Clusters_from_Scratch/verification.rst
-index 9d647f8..b7fa20e 100644
---- a/doc/sphinx/Clusters_from_Scratch/verification.rst
-+++ b/doc/sphinx/Clusters_from_Scratch/verification.rst
-@@ -103,7 +103,7 @@ the necessary processes are running:
- 2 ? S 0:00 [kthreadd]
- ...lots of processes...
- 17121 ? SLsl 0:01 /usr/sbin/corosync -f
-- 17133 ? Ss 0:00 /usr/sbin/pacemakerd -f
-+ 17133 ? Ss 0:00 /usr/sbin/pacemakerd
- 17134 ? Ss 0:00 \_ /usr/libexec/pacemaker/pacemaker-based
- 17135 ? Ss 0:00 \_ /usr/libexec/pacemaker/pacemaker-fenced
- 17136 ? Ss 0:00 \_ /usr/libexec/pacemaker/pacemaker-execd
---
-1.8.3.1
-
diff --git a/003-pacemakerd-output.patch b/003-pacemakerd-output.patch
deleted file mode 100644
index 167e22b..0000000
--- a/003-pacemakerd-output.patch
+++ /dev/null
@@ -1,343 +0,0 @@
-From 7c35387a9896cb968cf4087b5cbed94af44e1ea5 Mon Sep 17 00:00:00 2001
-From: Chris Lumens <clumens(a)redhat.com>
-Date: Fri, 14 May 2021 12:03:46 -0400
-Subject: [PATCH 1/5] Feature: daemons: Convert pacemakerd to formatted output.
-
-The main purpose of this is to finish getting pacemakerd moved off the
-existing command line handling code (pcmk__cli_help in particular) so
-that code can eventually be deprecated or removed. pacemakerd itself
-does fairly little printing.
----
- daemons/pacemakerd/pacemakerd.c | 58 ++++++++++++++++++++++++++++++-----------
- 1 file changed, 43 insertions(+), 15 deletions(-)
-
-diff --git a/daemons/pacemakerd/pacemakerd.c b/daemons/pacemakerd/pacemakerd.c
-index ce194bf..bd59729 100644
---- a/daemons/pacemakerd/pacemakerd.c
-+++ b/daemons/pacemakerd/pacemakerd.c
-@@ -25,6 +25,7 @@
- #include <crm/common/ipc_internal.h>
- #include <crm/common/mainloop.h>
- #include <crm/common/cmdline_internal.h>
-+#include <crm/common/output_internal.h>
- #include <crm/cluster/internal.h>
- #include <crm/cluster.h>
-
-@@ -37,6 +38,14 @@ struct {
- gboolean standby;
- } options;
-
-+static pcmk__output_t *out = NULL;
-+
-+static pcmk__supported_format_t formats[] = {
-+ PCMK__SUPPORTED_FORMAT_NONE,
-+ PCMK__SUPPORTED_FORMAT_TEXT,
-+ { NULL, NULL, NULL }
-+};
-+
- static gboolean
- pid_cb(const gchar *option_name, const gchar *optarg, gpointer data, GError **err) {
- return TRUE;
-@@ -1167,10 +1176,10 @@ pacemakerd_event_cb(pcmk_ipc_api_t *pacemakerd_api,
- }
-
- static GOptionContext *
--build_arg_context(pcmk__common_args_t *args) {
-+build_arg_context(pcmk__common_args_t *args, GOptionGroup **group) {
- GOptionContext *context = NULL;
-
-- context = pcmk__build_arg_context(args, NULL, NULL, NULL);
-+ context = pcmk__build_arg_context(args, "text", group, NULL);
- pcmk__add_main_args(context, entries);
- return context;
- }
-@@ -1182,9 +1191,11 @@ main(int argc, char **argv)
-
- GError *error = NULL;
-
-+ int rc = pcmk_rc_ok;
-+ GOptionGroup *output_group = NULL;
- pcmk__common_args_t *args = pcmk__new_common_args(SUMMARY);
- gchar **processed_args = pcmk__cmdline_preproc(argv, "p");
-- GOptionContext *context = build_arg_context(args);
-+ GOptionContext *context = build_arg_context(args, &output_group);
-
- bool old_instance_connected = false;
-
-@@ -1195,23 +1205,30 @@ main(int argc, char **argv)
- mainloop_add_signal(SIGHUP, pcmk_ignore);
- mainloop_add_signal(SIGQUIT, pcmk_sigquit);
-
-+ pcmk__register_formats(output_group, formats);
- if (!g_option_context_parse_strv(context, &processed_args, &error)) {
- exit_code = CRM_EX_USAGE;
- goto done;
- }
-
-+ rc = pcmk__output_new(&out, args->output_ty, args->output_dest, argv);
-+ if (rc != pcmk_rc_ok) {
-+ exit_code = CRM_EX_ERROR;
-+ g_set_error(&error, PCMK__EXITC_ERROR, exit_code, "Error creating output format %s: %s",
-+ args->output_ty, pcmk_rc_str(rc));
-+ goto done;
-+ }
-+
- if (options.features) {
-- printf("Pacemaker %s (Build: %s)\n Supporting v%s: %s\n", PACEMAKER_VERSION, BUILD_VERSION,
-- CRM_FEATURE_SET, CRM_FEATURES);
-+ out->info(out, "Pacemaker %s (Build: %s)\n Supporting v%s: %s", PACEMAKER_VERSION,
-+ BUILD_VERSION, CRM_FEATURE_SET, CRM_FEATURES);
- exit_code = CRM_EX_OK;
- goto done;
- }
-
- if (args->version) {
-- g_strfreev(processed_args);
-- pcmk__free_arg_context(context);
-- /* FIXME: When pacemakerd is converted to use formatted output, this can go. */
-- pcmk__cli_help('v', CRM_EX_USAGE);
-+ out->version(out, false);
-+ goto done;
- }
-
- setenv("LC_ALL", "C", 1);
-@@ -1248,6 +1265,13 @@ main(int argc, char **argv)
- crm_ipc_close(old_instance);
- crm_ipc_destroy(old_instance);
-
-+ /* Don't allow any accidental output after this point. */
-+ if (out != NULL) {
-+ out->finish(out, exit_code, true, NULL);
-+ pcmk__output_free(out);
-+ out = NULL;
-+ }
-+
- #ifdef SUPPORT_COROSYNC
- if (mcp_read_config() == FALSE) {
- exit_code = CRM_EX_UNAVAILABLE;
-@@ -1333,6 +1357,11 @@ done:
- g_strfreev(processed_args);
- pcmk__free_arg_context(context);
-
-- pcmk__output_and_clear_error(error, NULL);
-+ pcmk__output_and_clear_error(error, out);
-+
-+ if (out != NULL) {
-+ out->finish(out, exit_code, true, NULL);
-+ pcmk__output_free(out);
-+ }
- crm_exit(exit_code);
- }
---
-1.8.3.1
-
-
-From 35e6da64381fcb092d81ce16835cc28670b077cb Mon Sep 17 00:00:00 2001
-From: Chris Lumens <clumens(a)redhat.com>
-Date: Mon, 17 May 2021 10:04:04 -0400
-Subject: [PATCH 2/5] Features: daemons: Output the pacemakerd feature list in
- XML.
-
----
- daemons/pacemakerd/pacemakerd.c | 45 ++++++++++++++++++++++++++++++++++++++---
- 1 file changed, 42 insertions(+), 3 deletions(-)
-
-diff --git a/daemons/pacemakerd/pacemakerd.c b/daemons/pacemakerd/pacemakerd.c
-index bd59729..93cf743 100644
---- a/daemons/pacemakerd/pacemakerd.c
-+++ b/daemons/pacemakerd/pacemakerd.c
-@@ -43,6 +43,42 @@ static pcmk__output_t *out = NULL;
- static pcmk__supported_format_t formats[] = {
- PCMK__SUPPORTED_FORMAT_NONE,
- PCMK__SUPPORTED_FORMAT_TEXT,
-+ PCMK__SUPPORTED_FORMAT_XML,
-+ { NULL, NULL, NULL }
-+};
-+
-+static int
-+pacemakerd_features(pcmk__output_t *out, va_list args) {
-+ out->info(out, "Pacemaker %s (Build: %s)\n Supporting v%s: %s", PACEMAKER_VERSION,
-+ BUILD_VERSION, CRM_FEATURE_SET, CRM_FEATURES);
-+ return pcmk_rc_ok;
-+}
-+
-+static int
-+pacemakerd_features_xml(pcmk__output_t *out, va_list args) {
-+ gchar **feature_list = g_strsplit(CRM_FEATURES, " ", 0);
-+
-+ pcmk__output_xml_create_parent(out, "pacemakerd",
-+ "version", PACEMAKER_VERSION,
-+ "build", BUILD_VERSION,
-+ "feature_set", CRM_FEATURE_SET,
-+ NULL);
-+ out->begin_list(out, NULL, NULL, "features");
-+
-+ for (char **s = feature_list; *s != NULL; s++) {
-+ pcmk__output_create_xml_text_node(out, "feature", *s);
-+ }
-+
-+ out->end_list(out);
-+
-+ g_strfreev(feature_list);
-+ return pcmk_rc_ok;
-+}
-+
-+static pcmk__message_entry_t fmt_functions[] = {
-+ { "features", "default", pacemakerd_features },
-+ { "features", "xml", pacemakerd_features_xml },
-+
- { NULL, NULL, NULL }
- };
-
-@@ -200,7 +236,7 @@ static GOptionContext *
- build_arg_context(pcmk__common_args_t *args, GOptionGroup **group) {
- GOptionContext *context = NULL;
-
-- context = pcmk__build_arg_context(args, "text", group, NULL);
-+ context = pcmk__build_arg_context(args, "text (default), xml", group, NULL);
- pcmk__add_main_args(context, entries);
- return context;
- }
-@@ -241,9 +277,12 @@ main(int argc, char **argv)
- goto done;
- }
-
-+ pcmk__force_args(context, &error, "%s --xml-simple-list", g_get_prgname());
-+
-+ pcmk__register_messages(out, fmt_functions);
-+
- if (options.features) {
-- out->info(out, "Pacemaker %s (Build: %s)\n Supporting v%s: %s", PACEMAKER_VERSION,
-- BUILD_VERSION, CRM_FEATURE_SET, CRM_FEATURES);
-+ out->message(out, "features");
- exit_code = CRM_EX_OK;
- goto done;
- }
---
-1.8.3.1
-
-
-From 5b7f5eb35b025b59805cf3c7c3dcb6a3cf4b71b3 Mon Sep 17 00:00:00 2001
-From: Chris Lumens <clumens(a)redhat.com>
-Date: Mon, 17 May 2021 11:09:53 -0400
-Subject: [PATCH 3/5] Low: daemons: Conditionally enable logging in pacemakerd.
-
-If we're doing an interactive command-line call, use
-pcmk__cli_init_logging. At the moment, all command line calls except
-for --shutdown do their work before logging would even come up, so we
-really only need to do this for --shutdown.
-
-If we're doing a daemon call, use crm_log_init.
----
- daemons/pacemakerd/pacemakerd.c | 7 +++++--
- 1 file changed, 5 insertions(+), 2 deletions(-)
-
-diff --git a/daemons/pacemakerd/pacemakerd.c b/daemons/pacemakerd/pacemakerd.c
-index 93cf743..c20bde7 100644
---- a/daemons/pacemakerd/pacemakerd.c
-+++ b/daemons/pacemakerd/pacemakerd.c
-@@ -296,8 +296,11 @@ main(int argc, char **argv)
-
- pcmk__set_env_option("mcp", "true");
-
-- pcmk__cli_init_logging("pacemakerd", args->verbosity);
-- crm_log_init(NULL, LOG_INFO, TRUE, FALSE, argc, argv, FALSE);
-+ if (options.shutdown) {
-+ pcmk__cli_init_logging("pacemakerd", args->verbosity);
-+ } else {
-+ crm_log_init(NULL, LOG_INFO, TRUE, FALSE, argc, argv, FALSE);
-+ }
-
- crm_debug("Checking for existing Pacemaker instance");
- old_instance = crm_ipc_new(CRM_SYSTEM_MCP, 0);
---
-1.8.3.1
-
-
-From 2393362bb7489e86d937ed46a1c5cfb93d9bf3ab Mon Sep 17 00:00:00 2001
-From: Chris Lumens <clumens(a)redhat.com>
-Date: Mon, 17 May 2021 11:58:06 -0400
-Subject: [PATCH 4/5] Fix: include: Bump CRM_FEATURE_SET for new pacemakerd
- args.
-
----
- include/crm/crm.h | 2 +-
- 1 file changed, 1 insertion(+), 1 deletion(-)
-
-diff --git a/include/crm/crm.h b/include/crm/crm.h
-index fdfc825..92a98fa 100644
---- a/include/crm/crm.h
-+++ b/include/crm/crm.h
-@@ -66,7 +66,7 @@ extern "C" {
- * >=3.0.13: Fail counts include operation name and interval
- * >=3.2.0: DC supports PCMK_LRM_OP_INVALID and PCMK_LRM_OP_NOT_CONNECTED
- */
--# define CRM_FEATURE_SET "3.10.0"
-+# define CRM_FEATURE_SET "3.10.1"
-
- /* Pacemaker's CPG protocols use fixed-width binary fields for the sender and
- * recipient of a CPG message. This imposes an arbitrary limit on cluster node
---
-1.8.3.1
-
-
-From 3ad8edbd91631b87ef5f53fa2d68f0c8bbb9ee2b Mon Sep 17 00:00:00 2001
-From: Chris Lumens <clumens(a)redhat.com>
-Date: Mon, 17 May 2021 11:57:09 -0400
-Subject: [PATCH 5/5] Feature: xml: Add schema for pacemakerd.
-
----
- xml/Makefile.am | 1 +
- xml/api/pacemakerd-2.10.rng | 28 ++++++++++++++++++++++++++++
- 2 files changed, 29 insertions(+)
- create mode 100644 xml/api/pacemakerd-2.10.rng
-
-diff --git a/xml/Makefile.am b/xml/Makefile.am
-index 12a51c5..b9448d4 100644
---- a/xml/Makefile.am
-+++ b/xml/Makefile.am
-@@ -56,6 +56,7 @@ API_request_base = command-output \
- crm_simulate \
- crmadmin \
- digests \
-+ pacemakerd \
- stonith_admin \
- version
-
-diff --git a/xml/api/pacemakerd-2.10.rng b/xml/api/pacemakerd-2.10.rng
-new file mode 100644
-index 0000000..41a11e7
---- /dev/null
-+++ b/xml/api/pacemakerd-2.10.rng
-@@ -0,0 +1,28 @@
-+<?xml version="1.0" encoding="UTF-8"?>
-+<grammar xmlns="http://relaxng.org/ns/structure/1.0"
-+ datatypeLibrary="http://www.w3.org/2001/XMLSchema-datatypes">
-+
-+ <start>
-+ <ref name="element-pacemakerd"/>
-+ </start>
-+
-+ <define name="element-pacemakerd">
-+ <element name="pacemakerd">
-+ <attribute name="version"> <text /> </attribute>
-+ <attribute name="build"> <text /> </attribute>
-+ <attribute name="feature_set"> <text /> </attribute>
-+
-+ <optional>
-+ <ref name="feature-list" />
-+ </optional>
-+ </element>
-+ </define>
-+
-+ <define name="feature-list">
-+ <element name="features">
-+ <oneOrMore>
-+ <element name="feature"> <text/> </element>
-+ </oneOrMore>
-+ </element>
-+ </define>
-+</grammar>
---
-1.8.3.1
-
diff --git a/004-check-level.patch b/004-check-level.patch
deleted file mode 100644
index f2abb5f..0000000
--- a/004-check-level.patch
+++ /dev/null
@@ -1,199 +0,0 @@
-From 3905e7eac11298fc20efd567a773666f948edf61 Mon Sep 17 00:00:00 2001
-From: Chris Lumens <clumens(a)redhat.com>
-Date: Mon, 3 May 2021 11:19:04 -0400
-Subject: [PATCH 1/2] Feature: tools: Add OCF_CHECK_LEVEL to crm_resource
- environment.
-
-If --validate= or --force-check= are given with a level, pass that along
-as OCF_CHECK_LEVEL. This argument is optional, and if no value is given
-then the environment variable will not be set and whatever's the default
-on the resource agent will be used.
-
-See: rhbz#1955792.
----
- tools/crm_resource.c | 29 +++++++++++++++++++++--------
- tools/crm_resource.h | 4 ++--
- tools/crm_resource_runtime.c | 13 ++++++++++---
- 3 files changed, 33 insertions(+), 13 deletions(-)
-
-diff --git a/tools/crm_resource.c b/tools/crm_resource.c
-index 45db2b2..6ca96f8 100644
---- a/tools/crm_resource.c
-+++ b/tools/crm_resource.c
-@@ -100,6 +100,7 @@ struct {
- int timeout_ms; // Parsed from --timeout value
- char *agent_spec; // Standard and/or provider and/or agent
- gchar *xml_file; // Value of (deprecated) --xml-file
-+ int check_level; // Optional value of --validate or --force-check
-
- // Resource configuration specified via command-line arguments
- gboolean cmdline_config; // Resource configuration was via arguments
-@@ -113,6 +114,7 @@ struct {
- GHashTable *override_params; // Resource parameter values that override config
- } options = {
- .attr_set_type = XML_TAG_ATTR_SETS,
-+ .check_level = -1,
- .cib_options = cib_sync_call,
- .require_cib = TRUE,
- .require_dataset = TRUE,
-@@ -402,14 +404,15 @@ static GOptionEntry query_entries[] = {
- };
-
- static GOptionEntry command_entries[] = {
-- { "validate", 0, G_OPTION_FLAG_NO_ARG, G_OPTION_ARG_CALLBACK,
-+ { "validate", 0, G_OPTION_FLAG_OPTIONAL_ARG, G_OPTION_ARG_CALLBACK,
- validate_or_force_cb,
- "Validate resource configuration by calling agent's validate-all\n"
- INDENT "action. The configuration may be specified either by giving an\n"
- INDENT "existing resource name with -r, or by specifying --class,\n"
- INDENT "--agent, and --provider arguments, along with any number of\n"
-- INDENT "--option arguments.",
-- NULL },
-+ INDENT "--option arguments. An optional LEVEL argument can be given\n"
-+ INDENT "to control the level of checking performed.",
-+ "LEVEL" },
- { "cleanup", 'C', G_OPTION_FLAG_NO_ARG, G_OPTION_ARG_CALLBACK, cleanup_refresh_cb,
- "If resource has any past failures, clear its history and fail\n"
- INDENT "count. Optionally filtered by --resource, --node, --operation\n"
-@@ -546,11 +549,12 @@ static GOptionEntry advanced_entries[] = {
- INDENT "the cluster believes the resource is a clone instance already\n"
- INDENT "running on the local node.",
- NULL },
-- { "force-check", 0, G_OPTION_FLAG_NO_ARG, G_OPTION_ARG_CALLBACK,
-+ { "force-check", 0, G_OPTION_FLAG_OPTIONAL_ARG, G_OPTION_ARG_CALLBACK,
- validate_or_force_cb,
- "(Advanced) Bypass the cluster and check the state of a resource on\n"
-- INDENT "the local node",
-- NULL },
-+ INDENT "the local node. An optional LEVEL argument can be given\n"
-+ INDENT "to control the level of checking performed.",
-+ "LEVEL" },
-
- { NULL }
- };
-@@ -910,6 +914,15 @@ validate_or_force_cb(const gchar *option_name, const gchar *optarg,
- if (options.override_params == NULL) {
- options.override_params = pcmk__strkey_table(free, free);
- }
-+
-+ if (optarg != NULL) {
-+ if (pcmk__scan_min_int(optarg, &options.check_level, 0) != pcmk_rc_ok) {
-+ g_set_error(error, G_OPTION_ERROR, CRM_EX_INVALID_PARAM,
-+ "Invalid check level setting: %s", optarg);
-+ return FALSE;
-+ }
-+ }
-+
- return TRUE;
- }
-
-@@ -1826,12 +1839,12 @@ main(int argc, char **argv)
- options.v_class, options.v_provider, options.v_agent,
- "validate-all", options.cmdline_params,
- options.override_params, options.timeout_ms,
-- args->verbosity, options.force);
-+ args->verbosity, options.force, options.check_level);
- } else {
- exit_code = cli_resource_execute(rsc, options.rsc_id,
- options.operation, options.override_params,
- options.timeout_ms, cib_conn, data_set,
-- args->verbosity, options.force);
-+ args->verbosity, options.force, options.check_level);
- }
- goto done;
-
-diff --git a/tools/crm_resource.h b/tools/crm_resource.h
-index 3560377..5ab10d6 100644
---- a/tools/crm_resource.h
-+++ b/tools/crm_resource.h
-@@ -88,11 +88,11 @@ crm_exit_t cli_resource_execute_from_params(pcmk__output_t *out, const char *rsc
- const char *rsc_type, const char *rsc_action,
- GHashTable *params, GHashTable *override_hash,
- int timeout_ms, int resource_verbose,
-- gboolean force);
-+ gboolean force, int check_level);
- crm_exit_t cli_resource_execute(pe_resource_t *rsc, const char *requested_name,
- const char *rsc_action, GHashTable *override_hash,
- int timeout_ms, cib_t *cib, pe_working_set_t *data_set,
-- int resource_verbose, gboolean force);
-+ int resource_verbose, gboolean force, int check_level);
-
- int cli_resource_update_attribute(pe_resource_t *rsc, const char *requested_name,
- const char *attr_set, const char *attr_set_type,
-diff --git a/tools/crm_resource_runtime.c b/tools/crm_resource_runtime.c
-index fe0ec98..bde83b6 100644
---- a/tools/crm_resource_runtime.c
-+++ b/tools/crm_resource_runtime.c
-@@ -1679,7 +1679,8 @@ cli_resource_execute_from_params(pcmk__output_t *out, const char *rsc_name,
- const char *rsc_class, const char *rsc_prov,
- const char *rsc_type, const char *action,
- GHashTable *params, GHashTable *override_hash,
-- int timeout_ms, int resource_verbose, gboolean force)
-+ int timeout_ms, int resource_verbose, gboolean force,
-+ int check_level)
- {
- GHashTable *params_copy = NULL;
- crm_exit_t exit_code = CRM_EX_OK;
-@@ -1703,6 +1704,12 @@ cli_resource_execute_from_params(pcmk__output_t *out, const char *rsc_name,
- /* add crm_feature_set env needed by some resource agents */
- g_hash_table_insert(params, strdup(XML_ATTR_CRM_VERSION), strdup(CRM_FEATURE_SET));
-
-+ if (check_level >= 0) {
-+ char *level = crm_strdup_printf("%d", check_level);
-+ setenv("OCF_CHECK_LEVEL", level, 1);
-+ free(level);
-+ }
-+
- /* resources_action_create frees the params hash table it's passed, but we
- * may need to reuse it in a second call to resources_action_create. Thus
- * we'll make a copy here so that gets freed and the original remains for
-@@ -1790,7 +1797,7 @@ crm_exit_t
- cli_resource_execute(pe_resource_t *rsc, const char *requested_name,
- const char *rsc_action, GHashTable *override_hash,
- int timeout_ms, cib_t * cib, pe_working_set_t *data_set,
-- int resource_verbose, gboolean force)
-+ int resource_verbose, gboolean force, int check_level)
- {
- pcmk__output_t *out = data_set->priv;
- crm_exit_t exit_code = CRM_EX_OK;
-@@ -1856,7 +1863,7 @@ cli_resource_execute(pe_resource_t *rsc, const char *requested_name,
-
- exit_code = cli_resource_execute_from_params(out, rid, rclass, rprov, rtype, action,
- params, override_hash, timeout_ms,
-- resource_verbose, force);
-+ resource_verbose, force, check_level);
- return exit_code;
- }
-
---
-1.8.3.1
-
-
-From d13ba4bd6defe0dd81fdf8ab39ae5b889513c0c0 Mon Sep 17 00:00:00 2001
-From: Chris Lumens <clumens(a)redhat.com>
-Date: Thu, 20 May 2021 10:59:23 -0400
-Subject: [PATCH 2/2] Fix: include: Bump feature set to 3.10.2.
-
-This is for the OCF_CHECK_LEVEL environment variable.
-
-See: rhbz#1955792.
----
- include/crm/crm.h | 2 +-
- 1 file changed, 1 insertion(+), 1 deletion(-)
-
-diff --git a/include/crm/crm.h b/include/crm/crm.h
-index 92a98fa..ee52c36 100644
---- a/include/crm/crm.h
-+++ b/include/crm/crm.h
-@@ -66,7 +66,7 @@ extern "C" {
- * >=3.0.13: Fail counts include operation name and interval
- * >=3.2.0: DC supports PCMK_LRM_OP_INVALID and PCMK_LRM_OP_NOT_CONNECTED
- */
--# define CRM_FEATURE_SET "3.10.1"
-+# define CRM_FEATURE_SET "3.10.2"
-
- /* Pacemaker's CPG protocols use fixed-width binary fields for the sender and
- * recipient of a CPG message. This imposes an arbitrary limit on cluster node
---
-1.8.3.1
-
diff --git a/005-crm_resource.patch b/005-crm_resource.patch
deleted file mode 100644
index 1683026..0000000
--- a/005-crm_resource.patch
+++ /dev/null
@@ -1,866 +0,0 @@
-From a5a507d4e1abf242903472719a19977811e6f164 Mon Sep 17 00:00:00 2001
-From: Chris Lumens <clumens(a)redhat.com>
-Date: Thu, 20 May 2021 11:59:36 -0400
-Subject: [PATCH 01/10] Feature: libcrmcommon: Add OCF_OUTPUT_FORMAT to
- crm_resource environment.
-
-See: rhbz#1644628
----
- lib/common/output.c | 2 ++
- 1 file changed, 2 insertions(+)
-
-diff --git a/lib/common/output.c b/lib/common/output.c
-index 6cb49b5..58872e0 100644
---- a/lib/common/output.c
-+++ b/lib/common/output.c
-@@ -71,6 +71,8 @@ pcmk__output_new(pcmk__output_t **out, const char *fmt_name, const char *filenam
- return ENOMEM;
- }
-
-+ setenv("OCF_OUTPUT_FORMAT", (*out)->fmt_name, 1);
-+
- return pcmk_rc_ok;
- }
-
---
-1.8.3.1
-
-
-From acc6ecdbfb797d69794e68f75a734d6252434e01 Mon Sep 17 00:00:00 2001
-From: Chris Lumens <clumens(a)redhat.com>
-Date: Fri, 21 May 2021 14:20:30 -0400
-Subject: [PATCH 02/10] Feature: schemas: Copy crm_resource schema in
- preparation for changes.
-
-See: rhbz#1644628
----
- xml/api/crm_resource-2.11.rng | 238 ++++++++++++++++++++++++++++++++++++++++++
- 1 file changed, 238 insertions(+)
- create mode 100644 xml/api/crm_resource-2.11.rng
-
-diff --git a/xml/api/crm_resource-2.11.rng b/xml/api/crm_resource-2.11.rng
-new file mode 100644
-index 0000000..8e386db
---- /dev/null
-+++ b/xml/api/crm_resource-2.11.rng
-@@ -0,0 +1,238 @@
-+<?xml version="1.0" encoding="UTF-8"?>
-+<grammar xmlns="http://relaxng.org/ns/structure/1.0"
-+ datatypeLibrary="http://www.w3.org/2001/XMLSchema-datatypes">
-+
-+ <start>
-+ <ref name="element-crm-resource"/>
-+ </start>
-+
-+ <define name="element-crm-resource">
-+ <choice>
-+ <ref name="agents-list" />
-+ <ref name="alternatives-list" />
-+ <ref name="constraints-list" />
-+ <externalRef href="generic-list-2.4.rng"/>
-+ <element name="metadata"> <text/> </element>
-+ <ref name="locate-list" />
-+ <ref name="operations-list" />
-+ <ref name="providers-list" />
-+ <ref name="reasons-list" />
-+ <ref name="resource-check" />
-+ <ref name="resource-config" />
-+ <ref name="resources-list" />
-+ </choice>
-+ </define>
-+
-+ <define name="agents-list">
-+ <element name="agents">
-+ <attribute name="standard"> <text/> </attribute>
-+ <optional>
-+ <attribute name="provider"> <text/> </attribute>
-+ </optional>
-+ <zeroOrMore>
-+ <element name="agent"> <text/> </element>
-+ </zeroOrMore>
-+ </element>
-+ </define>
-+
-+ <define name="alternatives-list">
-+ <element name="providers">
-+ <attribute name="for"> <text/> </attribute>
-+ <zeroOrMore>
-+ <element name="provider"> <text/> </element>
-+ </zeroOrMore>
-+ </element>
-+ </define>
-+
-+ <define name="constraints-list">
-+ <element name="constraints">
-+ <interleave>
-+ <zeroOrMore>
-+ <ref name="rsc-location" />
-+ </zeroOrMore>
-+ <zeroOrMore>
-+ <ref name="rsc-colocation" />
-+ </zeroOrMore>
-+ </interleave>
-+ </element>
-+ </define>
-+
-+ <define name="locate-list">
-+ <element name="nodes">
-+ <attribute name="resource"> <text/> </attribute>
-+ <zeroOrMore>
-+ <element name="node">
-+ <optional>
-+ <attribute name="state"><value>promoted</value></attribute>
-+ </optional>
-+ <text/>
-+ </element>
-+ </zeroOrMore>
-+ </element>
-+ </define>
-+
-+ <define name="rsc-location">
-+ <element name="rsc_location">
-+ <attribute name="node"> <text/> </attribute>
-+ <attribute name="rsc"> <text/> </attribute>
-+ <attribute name="id"> <text/> </attribute>
-+ <externalRef href="../score.rng"/>
-+ </element>
-+ </define>
-+
-+ <define name="operations-list">
-+ <element name="operations">
-+ <oneOrMore>
-+ <ref name="element-operation-list" />
-+ </oneOrMore>
-+ </element>
-+ </define>
-+
-+ <define name="providers-list">
-+ <element name="providers">
-+ <attribute name="standard"> <value>ocf</value> </attribute>
-+ <optional>
-+ <attribute name="agent"> <text/> </attribute>
-+ </optional>
-+ <zeroOrMore>
-+ <element name="provider"> <text/> </element>
-+ </zeroOrMore>
-+ </element>
-+ </define>
-+
-+ <define name="reasons-list">
-+ <choice>
-+ <ref name="no-resource-or-uname"/>
-+ <ref name="resource-and-uname"/>
-+ <ref name="no-resource-but-uname"/>
-+ <ref name="resource-but-no-uname"/>
-+ </choice>
-+ </define>
-+
-+ <define name="no-resource-or-uname">
-+ <element name="reason">
-+ <element name="resources">
-+ <zeroOrMore>
-+ <element name="resource">
-+ <attribute name="id"> <text/> </attribute>
-+ <attribute name="running"> <data type="boolean"/> </attribute>
-+ <ref name="resource-check"/>
-+ </element>
-+ </zeroOrMore>
-+ </element>
-+ </element>
-+ </define>
-+
-+ <define name="resource-and-uname">
-+ <element name="reason">
-+ <attribute name="running_on"> <text/> </attribute>
-+ <ref name="resource-check"/>
-+ </element>
-+ </define>
-+
-+ <define name="no-resource-but-uname">
-+ <element name="reason">
-+ <element name="resources">
-+ <zeroOrMore>
-+ <element name="resource">
-+ <attribute name="id"> <text/> </attribute>
-+ <attribute name="running"> <data type="boolean"/> </attribute>
-+ <attribute name="host"> <text/> </attribute>
-+ <ref name="resource-check"/>
-+ </element>
-+ </zeroOrMore>
-+ </element>
-+ </element>
-+ </define>
-+
-+ <define name="resource-but-no-uname">
-+ <element name="reason">
-+ <attribute name="running"> <data type="boolean"/> </attribute>
-+ <ref name="resource-check"/>
-+ </element>
-+ </define>
-+
-+ <define name="resource-config">
-+ <element name="resource_config">
-+ <externalRef href="resources-2.4.rng" />
-+ <element name="xml"> <text/> </element>
-+ </element>
-+ </define>
-+
-+ <define name="resource-check">
-+ <element name="check">
-+ <attribute name="id"> <text/> </attribute>
-+ <optional>
-+ <choice>
-+ <attribute name="remain_stopped"><value>true</value></attribute>
-+ <attribute name="promotable"><value>false</value></attribute>
-+ </choice>
-+ </optional>
-+ <optional>
-+ <attribute name="unmanaged"><value>true</value></attribute>
-+ </optional>
-+ <optional>
-+ <attribute name="locked-to"> <text/> </attribute>
-+ </optional>
-+ </element>
-+ </define>
-+
-+ <define name="resources-list">
-+ <element name="resources">
-+ <zeroOrMore>
-+ <externalRef href="resources-2.4.rng" />
-+ </zeroOrMore>
-+ </element>
-+ </define>
-+
-+ <define name="rsc-colocation">
-+ <element name="rsc_colocation">
-+ <attribute name="id"> <text/> </attribute>
-+ <attribute name="rsc"> <text/> </attribute>
-+ <attribute name="with-rsc"> <text/> </attribute>
-+ <externalRef href="../score.rng"/>
-+ <optional>
-+ <attribute name="node-attribute"> <text/> </attribute>
-+ </optional>
-+ <optional>
-+ <attribute name="rsc-role">
-+ <ref name="attribute-roles"/>
-+ </attribute>
-+ </optional>
-+ <optional>
-+ <attribute name="with-rsc-role">
-+ <ref name="attribute-roles"/>
-+ </attribute>
-+ </optional>
-+ </element>
-+ </define>
-+
-+ <define name="element-operation-list">
-+ <element name="operation">
-+ <optional>
-+ <group>
-+ <attribute name="rsc"> <text/> </attribute>
-+ <attribute name="agent"> <text/> </attribute>
-+ </group>
-+ </optional>
-+ <attribute name="op"> <text/> </attribute>
-+ <attribute name="node"> <text/> </attribute>
-+ <attribute name="call"> <data type="integer" /> </attribute>
-+ <attribute name="rc"> <data type="nonNegativeInteger" /> </attribute>
-+ <optional>
-+ <attribute name="last-rc-change"> <text/> </attribute>
-+ <attribute name="exec-time"> <data type="nonNegativeInteger" /> </attribute>
-+ </optional>
-+ <attribute name="status"> <text/> </attribute>
-+ </element>
-+ </define>
-+
-+ <define name="attribute-roles">
-+ <choice>
-+ <value>Stopped</value>
-+ <value>Started</value>
-+ <value>Master</value>
-+ <value>Slave</value>
-+ </choice>
-+ </define>
-+</grammar>
---
-1.8.3.1
-
-
-From 1bbdf2149a111e9e19c388834f82001e0d31c427 Mon Sep 17 00:00:00 2001
-From: Chris Lumens <clumens(a)redhat.com>
-Date: Mon, 24 May 2021 12:23:55 -0400
-Subject: [PATCH 03/10] Feature: xml: Update the crm_resource schema for XML
- output.
-
-See: rhbz#1644628
----
- xml/api/crm_resource-2.11.rng | 50 +++++++++++++++++++++++++++++++++++++++++++
- 1 file changed, 50 insertions(+)
-
-diff --git a/xml/api/crm_resource-2.11.rng b/xml/api/crm_resource-2.11.rng
-index 8e386db..aaa54d6 100644
---- a/xml/api/crm_resource-2.11.rng
-+++ b/xml/api/crm_resource-2.11.rng
-@@ -20,6 +20,7 @@
- <ref name="resource-check" />
- <ref name="resource-config" />
- <ref name="resources-list" />
-+ <ref name="resource-agent-action" />
- </choice>
- </define>
-
-@@ -227,6 +228,55 @@
- </element>
- </define>
-
-+ <define name="resource-agent-action">
-+ <element name="resource-agent-action">
-+ <attribute name="action"> <text/> </attribute>
-+ <optional>
-+ <attribute name="rsc"> <text/> </attribute>
-+ </optional>
-+ <attribute name="class"> <text/> </attribute>
-+ <attribute name="type"> <text/> </attribute>
-+ <optional>
-+ <attribute name="provider"> <text/> </attribute>
-+ </optional>
-+ <optional>
-+ <ref name="overrides-list"/>
-+ </optional>
-+ <ref name="agent-status"/>
-+ <optional>
-+ <choice>
-+ <element name="command">
-+ <text />
-+ </element>
-+ <externalRef href="command-output-1.0.rng"/>
-+ </choice>
-+ </optional>
-+ </element>
-+ </define>
-+
-+ <define name="overrides-list">
-+ <element name="overrides">
-+ <zeroOrMore>
-+ <element name="override">
-+ <optional>
-+ <attribute name="rsc"> <text/> </attribute>
-+ </optional>
-+ <attribute name="name"> <text/> </attribute>
-+ <attribute name="value"> <text/> </attribute>
-+ </element>
-+ </zeroOrMore>
-+ </element>
-+ </define>
-+
-+ <define name="agent-status">
-+ <element name="agent-status">
-+ <attribute name="code"> <data type="integer" /> </attribute>
-+ <optional>
-+ <attribute name="message"> <text/> </attribute>
-+ </optional>
-+ </element>
-+ </define>
-+
- <define name="attribute-roles">
- <choice>
- <value>Stopped</value>
---
-1.8.3.1
-
-
-From d89f5bc7fec856fdcd32fa14edbd0019507d5d15 Mon Sep 17 00:00:00 2001
-From: Chris Lumens <clumens(a)redhat.com>
-Date: Tue, 1 Jun 2021 15:26:58 -0400
-Subject: [PATCH 04/10] Low: libcrmcommon: Increase PCMK__API_VERSION for new
- crm_resource output.
-
-See: rhbz#1644628
----
- include/crm/common/output_internal.h | 2 +-
- 1 file changed, 1 insertion(+), 1 deletion(-)
-
-diff --git a/include/crm/common/output_internal.h b/include/crm/common/output_internal.h
-index 10b315b..0436cde 100644
---- a/include/crm/common/output_internal.h
-+++ b/include/crm/common/output_internal.h
-@@ -27,7 +27,7 @@ extern "C" {
- # include <glib.h>
- # include <crm/common/results.h>
-
--# define PCMK__API_VERSION "2.9"
-+# define PCMK__API_VERSION "2.11"
-
- #if defined(PCMK__WITH_ATTRIBUTE_OUTPUT_ARGS)
- # define PCMK__OUTPUT_ARGS(ARGS...) __attribute__((output_args(ARGS)))
---
-1.8.3.1
-
-
-From 30bd2ddf43ee2a911681e51f40ed9ba20ec250b0 Mon Sep 17 00:00:00 2001
-From: Chris Lumens <clumens(a)redhat.com>
-Date: Thu, 27 May 2021 13:57:12 -0400
-Subject: [PATCH 05/10] Low: tools: Pass NULL to
- cli_resource_execute_from_params...
-
-if no resource name is given. This happens if we are validating based
-on the --class/--agent/--provider command line options instead.
----
- tools/crm_resource.c | 2 +-
- tools/crm_resource_runtime.c | 8 ++++----
- 2 files changed, 5 insertions(+), 5 deletions(-)
-
-diff --git a/tools/crm_resource.c b/tools/crm_resource.c
-index 24f1121..37a0bb0 100644
---- a/tools/crm_resource.c
-+++ b/tools/crm_resource.c
-@@ -1840,7 +1840,7 @@ main(int argc, char **argv)
-
- case cmd_execute_agent:
- if (options.cmdline_config) {
-- exit_code = cli_resource_execute_from_params(out, "test",
-+ exit_code = cli_resource_execute_from_params(out, NULL,
- options.v_class, options.v_provider, options.v_agent,
- "validate-all", options.cmdline_params,
- options.override_params, options.timeout_ms,
-diff --git a/tools/crm_resource_runtime.c b/tools/crm_resource_runtime.c
-index 48a4b40..ebf48bb 100644
---- a/tools/crm_resource_runtime.c
-+++ b/tools/crm_resource_runtime.c
-@@ -1717,14 +1717,14 @@ cli_resource_execute_from_params(pcmk__output_t *out, const char *rsc_name,
- */
- params_copy = pcmk__str_table_dup(params);
-
-- op = resources_action_create(rsc_name, rsc_class, rsc_prov, rsc_type, action, 0,
-- timeout_ms, params_copy, 0);
-+ op = resources_action_create(rsc_name ? rsc_name : "test", rsc_class, rsc_prov,
-+ rsc_type, action, 0, timeout_ms, params_copy, 0);
- if (op == NULL) {
- /* Re-run with stderr enabled so we can display a sane error message */
- crm_enable_stderr(TRUE);
- params_copy = pcmk__str_table_dup(params);
-- op = resources_action_create(rsc_name, rsc_class, rsc_prov, rsc_type, action, 0,
-- timeout_ms, params_copy, 0);
-+ op = resources_action_create(rsc_name ? rsc_name : "test", rsc_class, rsc_prov,
-+ rsc_type, action, 0, timeout_ms, params_copy, 0);
-
- /* Callers of cli_resource_execute expect that the params hash table will
- * be freed. That function uses this one, so for that reason and for
---
-1.8.3.1
-
-
-From ee56efd53d14cfc4f902769540b72b3bb6096a73 Mon Sep 17 00:00:00 2001
-From: Chris Lumens <clumens(a)redhat.com>
-Date: Mon, 24 May 2021 12:08:52 -0400
-Subject: [PATCH 06/10] Feature: tools: Add an agent-status message for
- crm_resource.
-
-This moves what was previously only done in an out->info call to its own
-output message, which means it will appear in XML output as well. Also,
-note that if --class/--agent/--provider are given, the resource name
-will be set to "test". In that case, do not display the resource name
-in the output.
-
-This message will be used for --validate and the --force-* command line
-options to crm_resource.
-
-See: rhbz#1644628
----
- tools/crm_resource_print.c | 53 ++++++++++++++++++++++++++++++++++++++++++++++
- 1 file changed, 53 insertions(+)
-
-diff --git a/tools/crm_resource_print.c b/tools/crm_resource_print.c
-index 9d82cf8..88d5878 100644
---- a/tools/crm_resource_print.c
-+++ b/tools/crm_resource_print.c
-@@ -152,6 +152,57 @@ attribute_list_default(pcmk__output_t *out, va_list args) {
- return pcmk_rc_ok;
- }
-
-+PCMK__OUTPUT_ARGS("agent-status", "int", "const char *", "const char *", "const char *",
-+ "const char *", "const char *", "int")
-+static int
-+agent_status_default(pcmk__output_t *out, va_list args) {
-+ int status = va_arg(args, int);
-+ const char *action = va_arg(args, const char *);
-+ const char *name = va_arg(args, const char *);
-+ const char *class = va_arg(args, const char *);
-+ const char *provider = va_arg(args, const char *);
-+ const char *type = va_arg(args, const char *);
-+ int rc = va_arg(args, int);
-+
-+ if (status == PCMK_LRM_OP_DONE) {
-+ out->info(out, "Operation %s%s%s (%s%s%s:%s) returned: '%s' (%d)",
-+ action, name ? " for " : "", name ? name : "",
-+ class, provider ? ":" : "", provider ? provider : "", type,
-+ services_ocf_exitcode_str(rc), rc);
-+ } else {
-+ out->err(out, "Operation %s%s%s (%s%s%s:%s) failed: '%s' (%d)",
-+ action, name ? " for " : "", name ? name : "",
-+ class, provider ? ":" : "", provider ? provider : "", type,
-+ services_lrm_status_str(status), status);
-+ }
-+
-+ return pcmk_rc_ok;
-+}
-+
-+PCMK__OUTPUT_ARGS("agent-status", "int", "const char *", "const char *", "const char *",
-+ "const char *", "const char *", "int")
-+static int
-+agent_status_xml(pcmk__output_t *out, va_list args) {
-+ int status G_GNUC_UNUSED = va_arg(args, int);
-+ const char *action G_GNUC_UNUSED = va_arg(args, const char *);
-+ const char *name G_GNUC_UNUSED = va_arg(args, const char *);
-+ const char *class G_GNUC_UNUSED = va_arg(args, const char *);
-+ const char *provider G_GNUC_UNUSED = va_arg(args, const char *);
-+ const char *type G_GNUC_UNUSED = va_arg(args, const char *);
-+ int rc = va_arg(args, int);
-+
-+ char *status_str = pcmk__itoa(rc);
-+
-+ pcmk__output_create_xml_node(out, "agent-status",
-+ "code", status_str,
-+ "message", services_ocf_exitcode_str(rc),
-+ NULL);
-+
-+ free(status_str);
-+
-+ return pcmk_rc_ok;
-+}
-+
- PCMK__OUTPUT_ARGS("attribute-list", "pe_resource_t *", "char *", "GHashTable *")
- static int
- attribute_list_text(pcmk__output_t *out, va_list args) {
-@@ -562,6 +613,8 @@ resource_names(pcmk__output_t *out, va_list args) {
- }
-
- static pcmk__message_entry_t fmt_functions[] = {
-+ { "agent-status", "default", agent_status_default },
-+ { "agent-status", "xml", agent_status_xml },
- { "attribute-list", "default", attribute_list_default },
- { "attribute-list", "text", attribute_list_text },
- { "property-list", "default", property_list_default },
---
-1.8.3.1
-
-
-From 85cb6b6bff96b18c5174d11e4de4d49cbfb20bb7 Mon Sep 17 00:00:00 2001
-From: Chris Lumens <clumens(a)redhat.com>
-Date: Tue, 1 Jun 2021 14:47:30 -0400
-Subject: [PATCH 07/10] Feature: tools: Add an overridden params output
- message.
-
-This also replaces what was previously being done in an out->info call
-with an output message. This means it shows up in XML output as well.
-Also, note that if --class/--agent/--provider are given, the resource
-name will be set to "test". In that case, do not display the resource
-name in the output.
-
-See: rhbz#1644628
----
- tools/crm_resource_print.c | 39 +++++++++++++++++++++++++++++++++++++++
- 1 file changed, 39 insertions(+)
-
-diff --git a/tools/crm_resource_print.c b/tools/crm_resource_print.c
-index 88d5878..119d83f 100644
---- a/tools/crm_resource_print.c
-+++ b/tools/crm_resource_print.c
-@@ -224,6 +224,43 @@ attribute_list_text(pcmk__output_t *out, va_list args) {
- return pcmk_rc_ok;
- }
-
-+PCMK__OUTPUT_ARGS("override", "const char *", "const char *", "const char *")
-+static int
-+override_default(pcmk__output_t *out, va_list args) {
-+ const char *rsc_name = va_arg(args, const char *);
-+ const char *name = va_arg(args, const char *);
-+ const char *value = va_arg(args, const char *);
-+
-+ if (rsc_name == NULL) {
-+ out->list_item(out, NULL, "Overriding the cluster configuration with '%s' = '%s'",
-+ name, value);
-+ } else {
-+ out->list_item(out, NULL, "Overriding the cluster configuration for '%s' with '%s' = '%s'",
-+ rsc_name, name, value);
-+ }
-+
-+ return pcmk_rc_ok;
-+}
-+
-+PCMK__OUTPUT_ARGS("override", "const char *", "const char *", "const char *")
-+static int
-+override_xml(pcmk__output_t *out, va_list args) {
-+ const char *rsc_name = va_arg(args, const char *);
-+ const char *name = va_arg(args, const char *);
-+ const char *value = va_arg(args, const char *);
-+
-+ xmlNodePtr node = pcmk__output_create_xml_node(out, "override",
-+ "name", name,
-+ "value", value,
-+ NULL);
-+
-+ if (rsc_name != NULL) {
-+ crm_xml_add(node, "rsc", rsc_name);
-+ }
-+
-+ return pcmk_rc_ok;
-+}
-+
- PCMK__OUTPUT_ARGS("property-list", "pe_resource_t *", "char *")
- static int
- property_list_default(pcmk__output_t *out, va_list args) {
-@@ -617,6 +654,8 @@ static pcmk__message_entry_t fmt_functions[] = {
- { "agent-status", "xml", agent_status_xml },
- { "attribute-list", "default", attribute_list_default },
- { "attribute-list", "text", attribute_list_text },
-+ { "override", "default", override_default },
-+ { "override", "xml", override_xml },
- { "property-list", "default", property_list_default },
- { "property-list", "text", property_list_text },
- { "resource-check-list", "default", resource_check_list_default },
---
-1.8.3.1
-
-
-From e5e24592c7c3231c619fb5253e7925ffbc634a99 Mon Sep 17 00:00:00 2001
-From: Chris Lumens <clumens(a)redhat.com>
-Date: Fri, 4 Jun 2021 10:24:51 -0400
-Subject: [PATCH 08/10] Low: tools: Use simple XML lists for resource actions
- as well.
-
-See: rhbz#1644628
----
- tools/crm_resource.c | 1 +
- 1 file changed, 1 insertion(+)
-
-diff --git a/tools/crm_resource.c b/tools/crm_resource.c
-index 37a0bb0..e957011 100644
---- a/tools/crm_resource.c
-+++ b/tools/crm_resource.c
-@@ -1643,6 +1643,7 @@ main(int argc, char **argv)
- * saves from having to write custom messages to build the lists around all these things
- */
- switch (options.rsc_cmd) {
-+ case cmd_execute_agent:
- case cmd_list_resources:
- case cmd_query_xml:
- case cmd_query_raw_xml:
---
-1.8.3.1
-
-
-From 3e75174d0bc31b261adb1994214a5878b79da85b Mon Sep 17 00:00:00 2001
-From: Chris Lumens <clumens(a)redhat.com>
-Date: Fri, 4 Jun 2021 10:30:10 -0400
-Subject: [PATCH 09/10] Feature: tools: Add an output message for resource
- actions.
-
-This wraps up the override and agent-status messages into a single
-message, along with any stdout/stderr from the resource action. This
-message should be called after taking the action.
-
-This also implements handling XML output from resource actions. Check
-to see if the validate-all action returns XML. If so, output it as a
-CDATA block under a "command" element. If not, treat it as plain text
-and output it as stdout/stderr from a command.
-
-See: rhbz#1644628
----
- tools/crm_resource_print.c | 122 +++++++++++++++++++++++++++++++++++++++++++++
- 1 file changed, 122 insertions(+)
-
-diff --git a/tools/crm_resource_print.c b/tools/crm_resource_print.c
-index 119d83f..19a366d 100644
---- a/tools/crm_resource_print.c
-+++ b/tools/crm_resource_print.c
-@@ -293,6 +293,126 @@ property_list_text(pcmk__output_t *out, va_list args) {
- return pcmk_rc_ok;
- }
-
-+PCMK__OUTPUT_ARGS("resource-agent-action", "int", "const char *", "const char *",
-+ "const char *", "const char *", "const char *", "GHashTable *",
-+ "int", "int", "char *", "char *")
-+static int
-+resource_agent_action_default(pcmk__output_t *out, va_list args) {
-+ int verbose = va_arg(args, int);
-+
-+ const char *class = va_arg(args, const char *);
-+ const char *provider = va_arg(args, const char *);
-+ const char *type = va_arg(args, const char *);
-+ const char *rsc_name = va_arg(args, const char *);
-+ const char *action = va_arg(args, const char *);
-+ GHashTable *overrides = va_arg(args, GHashTable *);
-+ int rc = va_arg(args, int);
-+ int status = va_arg(args, int);
-+ char *stdout_data = va_arg(args, char *);
-+ char *stderr_data = va_arg(args, char *);
-+
-+ if (overrides) {
-+ GHashTableIter iter;
-+ char *name = NULL;
-+ char *value = NULL;
-+
-+ out->begin_list(out, NULL, NULL, "overrides");
-+
-+ g_hash_table_iter_init(&iter, overrides);
-+ while (g_hash_table_iter_next(&iter, (gpointer *) &name, (gpointer *) &value)) {
-+ out->message(out, "override", rsc_name, name, value);
-+ }
-+
-+ out->end_list(out);
-+ }
-+
-+ out->message(out, "agent-status", status, action, rsc_name, class, provider,
-+ type, rc);
-+
-+ /* hide output for validate-all if not in verbose */
-+ if (verbose == 0 && pcmk__str_eq(action, "validate-all", pcmk__str_casei)) {
-+ return pcmk_rc_ok;
-+ }
-+
-+ if (stdout_data || stderr_data) {
-+ xmlNodePtr doc = string2xml(stdout_data);
-+
-+ if (doc != NULL) {
-+ out->output_xml(out, "command", stdout_data);
-+ xmlFreeNode(doc);
-+ } else {
-+ out->subprocess_output(out, rc, stdout_data, stderr_data);
-+ }
-+ }
-+
-+ return pcmk_rc_ok;
-+}
-+
-+PCMK__OUTPUT_ARGS("resource-agent-action", "int", "const char *", "const char *",
-+ "const char *", "const char *", "const char *", "GHashTable *",
-+ "int", "int", "char *", "char *")
-+static int
-+resource_agent_action_xml(pcmk__output_t *out, va_list args) {
-+ int verbose G_GNUC_UNUSED = va_arg(args, int);
-+
-+ const char *class = va_arg(args, const char *);
-+ const char *provider = va_arg(args, const char *);
-+ const char *type = va_arg(args, const char *);
-+ const char *rsc_name = va_arg(args, const char *);
-+ const char *action = va_arg(args, const char *);
-+ GHashTable *overrides = va_arg(args, GHashTable *);
-+ int rc = va_arg(args, int);
-+ int status = va_arg(args, int);
-+ char *stdout_data = va_arg(args, char *);
-+ char *stderr_data = va_arg(args, char *);
-+
-+ xmlNodePtr node = pcmk__output_xml_create_parent(out, "resource-agent-action",
-+ "action", action,
-+ "class", class,
-+ "type", type,
-+ NULL);
-+
-+ if (rsc_name) {
-+ crm_xml_add(node, "rsc", rsc_name);
-+ }
-+
-+ if (provider) {
-+ crm_xml_add(node, "provider", provider);
-+ }
-+
-+ if (overrides) {
-+ GHashTableIter iter;
-+ char *name = NULL;
-+ char *value = NULL;
-+
-+ out->begin_list(out, NULL, NULL, "overrides");
-+
-+ g_hash_table_iter_init(&iter, overrides);
-+ while (g_hash_table_iter_next(&iter, (gpointer *) &name, (gpointer *) &value)) {
-+ out->message(out, "override", rsc_name, name, value);
-+ }
-+
-+ out->end_list(out);
-+ }
-+
-+ out->message(out, "agent-status", status, action, rsc_name, class, provider,
-+ type, rc);
-+
-+ if (stdout_data || stderr_data) {
-+ xmlNodePtr doc = string2xml(stdout_data);
-+
-+ if (doc != NULL) {
-+ out->output_xml(out, "command", stdout_data);
-+ xmlFreeNode(doc);
-+ } else {
-+ out->subprocess_output(out, rc, stdout_data, stderr_data);
-+ }
-+ }
-+
-+ pcmk__output_xml_pop_parent(out);
-+ return pcmk_rc_ok;
-+}
-+
- PCMK__OUTPUT_ARGS("resource-check-list", "resource_checks_t *")
- static int
- resource_check_list_default(pcmk__output_t *out, va_list args) {
-@@ -658,6 +778,8 @@ static pcmk__message_entry_t fmt_functions[] = {
- { "override", "xml", override_xml },
- { "property-list", "default", property_list_default },
- { "property-list", "text", property_list_text },
-+ { "resource-agent-action", "default", resource_agent_action_default },
-+ { "resource-agent-action", "xml", resource_agent_action_xml },
- { "resource-check-list", "default", resource_check_list_default },
- { "resource-check-list", "xml", resource_check_list_xml },
- { "resource-search-list", "default", resource_search_list_default },
---
-1.8.3.1
-
-
-From b50b2418e1e997b42f5370b4672a3f105d74634f Mon Sep 17 00:00:00 2001
-From: Chris Lumens <clumens(a)redhat.com>
-Date: Fri, 4 Jun 2021 10:40:16 -0400
-Subject: [PATCH 10/10] Feature: tools: Use the new resource-agent-action
- message.
-
-See: rhbz#1644628
----
- tools/crm_resource_runtime.c | 21 +++------------------
- 1 file changed, 3 insertions(+), 18 deletions(-)
-
-diff --git a/tools/crm_resource_runtime.c b/tools/crm_resource_runtime.c
-index ebf48bb..755be9f 100644
---- a/tools/crm_resource_runtime.c
-+++ b/tools/crm_resource_runtime.c
-@@ -1765,28 +1765,13 @@ cli_resource_execute_from_params(pcmk__output_t *out, const char *rsc_name,
- if (services_action_sync(op)) {
- exit_code = op->rc;
-
-- if (op->status == PCMK_LRM_OP_DONE) {
-- out->info(out, "Operation %s for %s (%s:%s:%s) returned: '%s' (%d)",
-- action, rsc_name, rsc_class, rsc_prov ? rsc_prov : "", rsc_type,
-- services_ocf_exitcode_str(op->rc), op->rc);
-- } else {
-- out->err(out, "Operation %s for %s (%s:%s:%s) failed: '%s' (%d)",
-- action, rsc_name, rsc_class, rsc_prov ? rsc_prov : "", rsc_type,
-- services_lrm_status_str(op->status), op->status);
-- }
--
-- /* hide output for validate-all if not in verbose */
-- if (resource_verbose == 0 && pcmk__str_eq(action, "validate-all", pcmk__str_casei))
-- goto done;
--
-- if (op->stdout_data || op->stderr_data) {
-- out->subprocess_output(out, op->rc, op->stdout_data, op->stderr_data);
-- }
-+ out->message(out, "resource-agent-action", resource_verbose, rsc_class,
-+ rsc_prov, rsc_type, rsc_name, action, override_hash, op->rc,
-+ op->status, op->stdout_data, op->stderr_data);
- } else {
- exit_code = op->rc == 0 ? CRM_EX_ERROR : op->rc;
- }
-
--done:
- services_action_free(op);
- /* See comment above about why we free params here. */
- g_hash_table_destroy(params);
---
-1.8.3.1
-
diff --git a/006-crm_simulate.patch b/006-crm_simulate.patch
deleted file mode 100644
index c8d4e3f..0000000
--- a/006-crm_simulate.patch
+++ /dev/null
@@ -1,896 +0,0 @@
-From 97571e6ccc9b7fa339a7e27d9b0b9ab782ff3003 Mon Sep 17 00:00:00 2001
-From: Chris Lumens <clumens(a)redhat.com>
-Date: Wed, 16 Jun 2021 13:54:10 -0400
-Subject: [PATCH 1/5] Low: schemas: Copy crm_mon.rng in preparation for
- changes.
-
----
- xml/api/crm_mon-2.12.rng | 243 +++++++++++++++++++++++++++++++++++++++++++++++
- 1 file changed, 243 insertions(+)
- create mode 100644 xml/api/crm_mon-2.12.rng
-
-diff --git a/xml/api/crm_mon-2.12.rng b/xml/api/crm_mon-2.12.rng
-new file mode 100644
-index 0000000..ffec923
---- /dev/null
-+++ b/xml/api/crm_mon-2.12.rng
-@@ -0,0 +1,243 @@
-+<?xml version="1.0" encoding="UTF-8"?>
-+<grammar xmlns="http://relaxng.org/ns/structure/1.0"
-+ datatypeLibrary="http://www.w3.org/2001/XMLSchema-datatypes">
-+
-+ <start>
-+ <ref name="element-crm-mon"/>
-+ </start>
-+
-+ <define name="element-crm-mon">
-+ <optional>
-+ <ref name="element-summary" />
-+ </optional>
-+ <optional>
-+ <ref name="nodes-list" />
-+ </optional>
-+ <optional>
-+ <ref name="resources-list" />
-+ </optional>
-+ <optional>
-+ <ref name="node-attributes-list" />
-+ </optional>
-+ <optional>
-+ <ref name="node-history-list" />
-+ </optional>
-+ <optional>
-+ <ref name="failures-list" />
-+ </optional>
-+ <optional>
-+ <ref name="fence-event-list" />
-+ </optional>
-+ <optional>
-+ <ref name="tickets-list" />
-+ </optional>
-+ <optional>
-+ <ref name="bans-list" />
-+ </optional>
-+ </define>
-+
-+ <define name="element-summary">
-+ <element name="summary">
-+ <optional>
-+ <element name="stack">
-+ <attribute name="type"> <text /> </attribute>
-+ </element>
-+ </optional>
-+ <optional>
-+ <element name="current_dc">
-+ <attribute name="present"> <data type="boolean" /> </attribute>
-+ <optional>
-+ <group>
-+ <attribute name="version"> <text /> </attribute>
-+ <attribute name="name"> <text /> </attribute>
-+ <attribute name="id"> <text /> </attribute>
-+ <attribute name="with_quorum"> <data type="boolean" /> </attribute>
-+ </group>
-+ </optional>
-+ </element>
-+ </optional>
-+ <optional>
-+ <element name="last_update">
-+ <attribute name="time"> <text /> </attribute>
-+ </element>
-+ <element name="last_change">
-+ <attribute name="time"> <text /> </attribute>
-+ <attribute name="user"> <text /> </attribute>
-+ <attribute name="client"> <text /> </attribute>
-+ <attribute name="origin"> <text /> </attribute>
-+ </element>
-+ </optional>
-+ <optional>
-+ <element name="nodes_configured">
-+ <attribute name="number"> <data type="nonNegativeInteger" /> </attribute>
-+ </element>
-+ <element name="resources_configured">
-+ <attribute name="number"> <data type="nonNegativeInteger" /> </attribute>
-+ <attribute name="disabled"> <data type="nonNegativeInteger" /> </attribute>
-+ <attribute name="blocked"> <data type="nonNegativeInteger" /> </attribute>
-+ </element>
-+ </optional>
-+ <optional>
-+ <element name="cluster_options">
-+ <attribute name="stonith-enabled"> <data type="boolean" /> </attribute>
-+ <attribute name="symmetric-cluster"> <data type="boolean" /> </attribute>
-+ <attribute name="no-quorum-policy"> <text /> </attribute>
-+ <attribute name="maintenance-mode"> <data type="boolean" /> </attribute>
-+ <attribute name="stop-all-resources"> <data type="boolean" /> </attribute>
-+ </element>
-+ </optional>
-+ </element>
-+ </define>
-+
-+ <define name="resources-list">
-+ <element name="resources">
-+ <zeroOrMore>
-+ <externalRef href="resources-2.4.rng" />
-+ </zeroOrMore>
-+ </element>
-+ </define>
-+
-+ <define name="nodes-list">
-+ <element name="nodes">
-+ <zeroOrMore>
-+ <externalRef href="nodes-2.8.rng" />
-+ </zeroOrMore>
-+ </element>
-+ </define>
-+
-+ <define name="node-attributes-list">
-+ <element name="node_attributes">
-+ <zeroOrMore>
-+ <externalRef href="node-attrs-2.8.rng" />
-+ </zeroOrMore>
-+ </element>
-+ </define>
-+
-+ <define name="node-history-list">
-+ <element name="node_history">
-+ <zeroOrMore>
-+ <ref name="element-node-history" />
-+ </zeroOrMore>
-+ </element>
-+ </define>
-+
-+ <define name="failures-list">
-+ <element name="failures">
-+ <zeroOrMore>
-+ <externalRef href="failure-2.8.rng" />
-+ </zeroOrMore>
-+ </element>
-+ </define>
-+
-+ <define name="fence-event-list">
-+ <element name="fence_history">
-+ <optional>
-+ <attribute name="status"> <data type="integer" /> </attribute>
-+ </optional>
-+ <zeroOrMore>
-+ <externalRef href="fence-event-2.0.rng" />
-+ </zeroOrMore>
-+ </element>
-+ </define>
-+
-+ <define name="tickets-list">
-+ <element name="tickets">
-+ <zeroOrMore>
-+ <ref name="element-ticket" />
-+ </zeroOrMore>
-+ </element>
-+ </define>
-+
-+ <define name="bans-list">
-+ <element name="bans">
-+ <zeroOrMore>
-+ <ref name="element-ban" />
-+ </zeroOrMore>
-+ </element>
-+ </define>
-+
-+ <define name="element-node-history">
-+ <element name="node">
-+ <attribute name="name"> <text /> </attribute>
-+ <zeroOrMore>
-+ <ref name="element-resource-history" />
-+ </zeroOrMore>
-+ </element>
-+ </define>
-+
-+ <define name="element-resource-history">
-+ <element name="resource_history">
-+ <attribute name="id"> <text /> </attribute>
-+ <attribute name="orphan"> <data type="boolean" /> </attribute>
-+ <optional>
-+ <group>
-+ <attribute name="migration-threshold"> <data type="nonNegativeInteger" /> </attribute>
-+ <optional>
-+ <attribute name="fail-count"> <text /> </attribute>
-+ </optional>
-+ <optional>
-+ <attribute name="last-failure"> <text /> </attribute>
-+ </optional>
-+ </group>
-+ </optional>
-+ <zeroOrMore>
-+ <ref name="element-operation-history" />
-+ </zeroOrMore>
-+ </element>
-+ </define>
-+
-+ <define name="element-operation-history">
-+ <element name="operation_history">
-+ <attribute name="call"> <text /> </attribute>
-+ <attribute name="task"> <text /> </attribute>
-+ <optional>
-+ <attribute name="interval"> <text /> </attribute>
-+ </optional>
-+ <optional>
-+ <attribute name="last-rc-change"> <text /> </attribute>
-+ </optional>
-+ <optional>
-+ <attribute name="last-run"> <text /> </attribute>
-+ </optional>
-+ <optional>
-+ <attribute name="exec-time"> <text /> </attribute>
-+ </optional>
-+ <optional>
-+ <attribute name="queue-time"> <text /> </attribute>
-+ </optional>
-+ <attribute name="rc"> <data type="integer" /> </attribute>
-+ <attribute name="rc_text"> <text /> </attribute>
-+ </element>
-+ </define>
-+
-+ <define name="element-ticket">
-+ <element name="ticket">
-+ <attribute name="id"> <text /> </attribute>
-+ <attribute name="status">
-+ <choice>
-+ <value>granted</value>
-+ <value>revoked</value>
-+ </choice>
-+ </attribute>
-+ <attribute name="standby"> <data type="boolean" /> </attribute>
-+ <optional>
-+ <attribute name="last-granted"> <text /> </attribute>
-+ </optional>
-+ </element>
-+ </define>
-+
-+ <define name="element-ban">
-+ <element name="ban">
-+ <attribute name="id"> <text /> </attribute>
-+ <attribute name="resource"> <text /> </attribute>
-+ <attribute name="node"> <text /> </attribute>
-+ <attribute name="weight"> <data type="integer" /> </attribute>
-+ <attribute name="promoted-only"> <data type="boolean" /> </attribute>
-+ <!-- DEPRECATED: master_only is a duplicate of promoted-only that is
-+ provided solely for API backward compatibility. It will be
-+ removed in a future release. Check promoted-only instead.
-+ -->
-+ <attribute name="master_only"> <data type="boolean" /> </attribute>
-+ </element>
-+ </define>
-+</grammar>
---
-1.8.3.1
-
-
-From da394983f106f974274ddd94675a04c85086010e Mon Sep 17 00:00:00 2001
-From: Chris Lumens <clumens(a)redhat.com>
-Date: Fri, 18 Jun 2021 15:06:34 -0400
-Subject: [PATCH 2/5] Refactor: Split node history out into its own XML schema.
-
-This allows for sharing it between crm_mon and crm_simulate.
----
- xml/Makefile.am | 2 +-
- xml/api/crm_mon-2.12.rng | 64 +--------------------------------------
- xml/api/node-history-2.12.rng | 70 +++++++++++++++++++++++++++++++++++++++++++
- 3 files changed, 72 insertions(+), 64 deletions(-)
- create mode 100644 xml/api/node-history-2.12.rng
-
-diff --git a/xml/Makefile.am b/xml/Makefile.am
-index b9448d4..8e7b6d3 100644
---- a/xml/Makefile.am
-+++ b/xml/Makefile.am
-@@ -64,7 +64,7 @@ API_request_base = command-output \
- CIB_cfg_base = options nodes resources constraints fencing acls tags alerts
-
- # Names of all schemas (including top level and those included by others)
--API_base = $(API_request_base) fence-event failure generic-list item node-attrs nodes resources status
-+API_base = $(API_request_base) fence-event failure generic-list item node-attrs node-history nodes resources status
- CIB_base = cib $(CIB_cfg_base) status score rule nvset
-
- # Static schema files and transforms (only CIB has transforms)
-diff --git a/xml/api/crm_mon-2.12.rng b/xml/api/crm_mon-2.12.rng
-index ffec923..be14412 100644
---- a/xml/api/crm_mon-2.12.rng
-+++ b/xml/api/crm_mon-2.12.rng
-@@ -20,7 +20,7 @@
- <ref name="node-attributes-list" />
- </optional>
- <optional>
-- <ref name="node-history-list" />
-+ <externalRef href="node-history-2.12.rng"/>
- </optional>
- <optional>
- <ref name="failures-list" />
-@@ -113,14 +113,6 @@
- </element>
- </define>
-
-- <define name="node-history-list">
-- <element name="node_history">
-- <zeroOrMore>
-- <ref name="element-node-history" />
-- </zeroOrMore>
-- </element>
-- </define>
--
- <define name="failures-list">
- <element name="failures">
- <zeroOrMore>
-@@ -156,60 +148,6 @@
- </element>
- </define>
-
-- <define name="element-node-history">
-- <element name="node">
-- <attribute name="name"> <text /> </attribute>
-- <zeroOrMore>
-- <ref name="element-resource-history" />
-- </zeroOrMore>
-- </element>
-- </define>
--
-- <define name="element-resource-history">
-- <element name="resource_history">
-- <attribute name="id"> <text /> </attribute>
-- <attribute name="orphan"> <data type="boolean" /> </attribute>
-- <optional>
-- <group>
-- <attribute name="migration-threshold"> <data type="nonNegativeInteger" /> </attribute>
-- <optional>
-- <attribute name="fail-count"> <text /> </attribute>
-- </optional>
-- <optional>
-- <attribute name="last-failure"> <text /> </attribute>
-- </optional>
-- </group>
-- </optional>
-- <zeroOrMore>
-- <ref name="element-operation-history" />
-- </zeroOrMore>
-- </element>
-- </define>
--
-- <define name="element-operation-history">
-- <element name="operation_history">
-- <attribute name="call"> <text /> </attribute>
-- <attribute name="task"> <text /> </attribute>
-- <optional>
-- <attribute name="interval"> <text /> </attribute>
-- </optional>
-- <optional>
-- <attribute name="last-rc-change"> <text /> </attribute>
-- </optional>
-- <optional>
-- <attribute name="last-run"> <text /> </attribute>
-- </optional>
-- <optional>
-- <attribute name="exec-time"> <text /> </attribute>
-- </optional>
-- <optional>
-- <attribute name="queue-time"> <text /> </attribute>
-- </optional>
-- <attribute name="rc"> <data type="integer" /> </attribute>
-- <attribute name="rc_text"> <text /> </attribute>
-- </element>
-- </define>
--
- <define name="element-ticket">
- <element name="ticket">
- <attribute name="id"> <text /> </attribute>
-diff --git a/xml/api/node-history-2.12.rng b/xml/api/node-history-2.12.rng
-new file mode 100644
-index 0000000..9628000
---- /dev/null
-+++ b/xml/api/node-history-2.12.rng
-@@ -0,0 +1,70 @@
-+<?xml version="1.0" encoding="UTF-8"?>
-+<grammar xmlns="http://relaxng.org/ns/structure/1.0"
-+ datatypeLibrary="http://www.w3.org/2001/XMLSchema-datatypes">
-+
-+ <start>
-+ <ref name="node-history-list" />
-+ </start>
-+
-+ <define name="node-history-list">
-+ <element name="node_history">
-+ <zeroOrMore>
-+ <ref name="element-node-history" />
-+ </zeroOrMore>
-+ </element>
-+ </define>
-+
-+ <define name="element-node-history">
-+ <element name="node">
-+ <attribute name="name"> <text /> </attribute>
-+ <zeroOrMore>
-+ <ref name="element-resource-history" />
-+ </zeroOrMore>
-+ </element>
-+ </define>
-+
-+ <define name="element-resource-history">
-+ <element name="resource_history">
-+ <attribute name="id"> <text /> </attribute>
-+ <attribute name="orphan"> <data type="boolean" /> </attribute>
-+ <optional>
-+ <group>
-+ <attribute name="migration-threshold"> <data type="nonNegativeInteger" /> </attribute>
-+ <optional>
-+ <attribute name="fail-count"> <text /> </attribute>
-+ </optional>
-+ <optional>
-+ <attribute name="last-failure"> <text /> </attribute>
-+ </optional>
-+ </group>
-+ </optional>
-+ <zeroOrMore>
-+ <ref name="element-operation-history" />
-+ </zeroOrMore>
-+ </element>
-+ </define>
-+
-+ <define name="element-operation-history">
-+ <element name="operation_history">
-+ <attribute name="call"> <text /> </attribute>
-+ <attribute name="task"> <text /> </attribute>
-+ <optional>
-+ <attribute name="interval"> <text /> </attribute>
-+ </optional>
-+ <optional>
-+ <attribute name="last-rc-change"> <text /> </attribute>
-+ </optional>
-+ <optional>
-+ <attribute name="last-run"> <text /> </attribute>
-+ </optional>
-+ <optional>
-+ <attribute name="exec-time"> <text /> </attribute>
-+ </optional>
-+ <optional>
-+ <attribute name="queue-time"> <text /> </attribute>
-+ </optional>
-+ <attribute name="rc"> <data type="integer" /> </attribute>
-+ <attribute name="rc_text"> <text /> </attribute>
-+ </element>
-+ </define>
-+</grammar>
---
-1.8.3.1
-
-
-From bf72b2615630eef7876e443d60b34d5a316de847 Mon Sep 17 00:00:00 2001
-From: Chris Lumens <clumens(a)redhat.com>
-Date: Wed, 16 Jun 2021 14:09:31 -0400
-Subject: [PATCH 3/5] Low: schemas: Copy crm_simulate.rng in preparation for
- changes.
-
----
- xml/api/crm_simulate-2.12.rng | 335 ++++++++++++++++++++++++++++++++++++++++++
- 1 file changed, 335 insertions(+)
- create mode 100644 xml/api/crm_simulate-2.12.rng
-
-diff --git a/xml/api/crm_simulate-2.12.rng b/xml/api/crm_simulate-2.12.rng
-new file mode 100644
-index 0000000..9a7612d
---- /dev/null
-+++ b/xml/api/crm_simulate-2.12.rng
-@@ -0,0 +1,335 @@
-+<?xml version="1.0" encoding="UTF-8"?>
-+<grammar xmlns="http://relaxng.org/ns/structure/1.0"
-+ datatypeLibrary="http://www.w3.org/2001/XMLSchema-datatypes">
-+
-+ <start>
-+ <ref name="element-crm-simulate"/>
-+ </start>
-+
-+ <define name="element-crm-simulate">
-+ <choice>
-+ <ref name="timings-list" />
-+ <group>
-+ <ref name="cluster-status" />
-+ <optional>
-+ <ref name="modifications-list" />
-+ </optional>
-+ <optional>
-+ <ref name="allocations-utilizations-list" />
-+ </optional>
-+ <optional>
-+ <ref name="action-list" />
-+ </optional>
-+ <optional>
-+ <ref name="cluster-injected-actions-list" />
-+ <ref name="revised-cluster-status" />
-+ </optional>
-+ </group>
-+ </choice>
-+ </define>
-+
-+ <define name="allocations-utilizations-list">
-+ <choice>
-+ <element name="allocations">
-+ <zeroOrMore>
-+ <choice>
-+ <ref name="element-allocation" />
-+ <ref name="element-promotion" />
-+ </choice>
-+ </zeroOrMore>
-+ </element>
-+ <element name="utilizations">
-+ <zeroOrMore>
-+ <choice>
-+ <ref name="element-capacity" />
-+ <ref name="element-utilization" />
-+ </choice>
-+ </zeroOrMore>
-+ </element>
-+ <element name="allocations_utilizations">
-+ <zeroOrMore>
-+ <choice>
-+ <ref name="element-allocation" />
-+ <ref name="element-promotion" />
-+ <ref name="element-capacity" />
-+ <ref name="element-utilization" />
-+ </choice>
-+ </zeroOrMore>
-+ </element>
-+ </choice>
-+ </define>
-+
-+ <define name="cluster-status">
-+ <element name="cluster_status">
-+ <ref name="nodes-list" />
-+ <ref name="resources-list" />
-+ <optional>
-+ <ref name="node-attributes-list" />
-+ </optional>
-+ <optional>
-+ <ref name="failures-list" />
-+ </optional>
-+ </element>
-+ </define>
-+
-+ <define name="modifications-list">
-+ <element name="modifications">
-+ <optional>
-+ <attribute name="quorum"> <text /> </attribute>
-+ </optional>
-+ <optional>
-+ <attribute name="watchdog"> <text /> </attribute>
-+ </optional>
-+ <zeroOrMore>
-+ <ref name="element-inject-modify-node" />
-+ </zeroOrMore>
-+ <zeroOrMore>
-+ <ref name="element-inject-modify-ticket" />
-+ </zeroOrMore>
-+ <zeroOrMore>
-+ <ref name="element-inject-spec" />
-+ </zeroOrMore>
-+ <zeroOrMore>
-+ <ref name="element-inject-attr" />
-+ </zeroOrMore>
-+ </element>
-+ </define>
-+
-+ <define name="revised-cluster-status">
-+ <element name="revised_cluster_status">
-+ <ref name="nodes-list" />
-+ <ref name="resources-list" />
-+ <optional>
-+ <ref name="node-attributes-list" />
-+ </optional>
-+ <optional>
-+ <ref name="failures-list" />
-+ </optional>
-+ </element>
-+ </define>
-+
-+ <define name="element-inject-attr">
-+ <element name="inject_attr">
-+ <attribute name="cib_node"> <text /> </attribute>
-+ <attribute name="name"> <text /> </attribute>
-+ <attribute name="node_path"> <text /> </attribute>
-+ <attribute name="value"> <text /> </attribute>
-+ </element>
-+ </define>
-+
-+ <define name="element-inject-modify-node">
-+ <element name="modify_node">
-+ <attribute name="action"> <text /> </attribute>
-+ <attribute name="node"> <text /> </attribute>
-+ </element>
-+ </define>
-+
-+ <define name="element-inject-spec">
-+ <element name="inject_spec">
-+ <attribute name="spec"> <text /> </attribute>
-+ </element>
-+ </define>
-+
-+ <define name="element-inject-modify-ticket">
-+ <element name="modify_ticket">
-+ <attribute name="action"> <text /> </attribute>
-+ <attribute name="ticket"> <text /> </attribute>
-+ </element>
-+ </define>
-+
-+ <define name="cluster-injected-actions-list">
-+ <element name="transition">
-+ <zeroOrMore>
-+ <ref name="element-injected-actions" />
-+ </zeroOrMore>
-+ </element>
-+ </define>
-+
-+ <define name="node-attributes-list">
-+ <element name="node_attributes">
-+ <zeroOrMore>
-+ <externalRef href="node-attrs-2.8.rng" />
-+ </zeroOrMore>
-+ </element>
-+ </define>
-+
-+ <define name="failures-list">
-+ <element name="failures">
-+ <zeroOrMore>
-+ <externalRef href="failure-2.8.rng" />
-+ </zeroOrMore>
-+ </element>
-+ </define>
-+
-+ <define name="nodes-list">
-+ <element name="nodes">
-+ <zeroOrMore>
-+ <externalRef href="nodes-2.8.rng" />
-+ </zeroOrMore>
-+ </element>
-+ </define>
-+
-+ <define name="resources-list">
-+ <element name="resources">
-+ <zeroOrMore>
-+ <externalRef href="resources-2.4.rng" />
-+ </zeroOrMore>
-+ </element>
-+ </define>
-+
-+ <define name="timings-list">
-+ <element name="timings">
-+ <zeroOrMore>
-+ <ref name="element-timing" />
-+ </zeroOrMore>
-+ </element>
-+ </define>
-+
-+ <define name="action-list">
-+ <element name="actions">
-+ <zeroOrMore>
-+ <ref name="element-node-action" />
-+ </zeroOrMore>
-+ <zeroOrMore>
-+ <ref name="element-rsc-action" />
-+ </zeroOrMore>
-+ </element>
-+ </define>
-+
-+ <define name="element-allocation">
-+ <element name="node_weight">
-+ <attribute name="function"> <text /> </attribute>
-+ <attribute name="node"> <text /> </attribute>
-+ <externalRef href="../score.rng" />
-+ <optional>
-+ <attribute name="id"> <text /> </attribute>
-+ </optional>
-+ </element>
-+ </define>
-+
-+ <define name="element-capacity">
-+ <element name="capacity">
-+ <attribute name="comment"> <text /> </attribute>
-+ <attribute name="node"> <text /> </attribute>
-+ <zeroOrMore>
-+ <element>
-+ <anyName />
-+ <text />
-+ </element>
-+ </zeroOrMore>
-+ </element>
-+ </define>
-+
-+ <define name="element-inject-cluster-action">
-+ <element name="cluster_action">
-+ <attribute name="node"> <text /> </attribute>
-+ <attribute name="task"> <text /> </attribute>
-+ <optional>
-+ <attribute name="id"> <text /> </attribute>
-+ </optional>
-+ </element>
-+ </define>
-+
-+ <define name="element-injected-actions">
-+ <choice>
-+ <ref name="element-inject-cluster-action" />
-+ <ref name="element-inject-fencing-action" />
-+ <ref name="element-inject-pseudo-action" />
-+ <ref name="element-inject-rsc-action" />
-+ </choice>
-+ </define>
-+
-+ <define name="element-inject-fencing-action">
-+ <element name="fencing_action">
-+ <attribute name="op"> <text /> </attribute>
-+ <attribute name="target"> <text /> </attribute>
-+ </element>
-+ </define>
-+
-+ <define name="element-node-action">
-+ <element name="node_action">
-+ <attribute name="node"> <text /> </attribute>
-+ <attribute name="reason"> <text /> </attribute>
-+ <attribute name="task"> <text /> </attribute>
-+ </element>
-+ </define>
-+
-+ <define name="element-promotion">
-+ <element name="promotion_score">
-+ <attribute name="id"> <text /> </attribute>
-+ <externalRef href="../score.rng" />
-+ <optional>
-+ <attribute name="node"> <text /> </attribute>
-+ </optional>
-+ </element>
-+ </define>
-+
-+ <define name="element-inject-pseudo-action">
-+ <element name="pseudo_action">
-+ <attribute name="task"> <text /> </attribute>
-+ <optional>
-+ <attribute name="node"> <text /> </attribute>
-+ </optional>
-+ </element>
-+ </define>
-+
-+ <define name="element-inject-rsc-action">
-+ <element name="rsc_action">
-+ <attribute name="node"> <text /> </attribute>
-+ <attribute name="op"> <text /> </attribute>
-+ <attribute name="resource"> <text /> </attribute>
-+ <optional>
-+ <attribute name="interval"> <data type="integer" /> </attribute>
-+ </optional>
-+ </element>
-+ </define>
-+
-+ <define name="element-timing">
-+ <element name="timing">
-+ <attribute name="file"> <text /> </attribute>
-+ <attribute name="duration"> <data type="double" /> </attribute>
-+ </element>
-+ </define>
-+
-+ <define name="element-rsc-action">
-+ <element name="rsc_action">
-+ <attribute name="action"> <text /> </attribute>
-+ <attribute name="resource"> <text /> </attribute>
-+ <optional>
-+ <attribute name="blocked"> <data type="boolean" /> </attribute>
-+ </optional>
-+ <optional>
-+ <attribute name="dest"> <text /> </attribute>
-+ </optional>
-+ <optional>
-+ <attribute name="next-role"> <text /> </attribute>
-+ </optional>
-+ <optional>
-+ <attribute name="node"> <text /> </attribute>
-+ </optional>
-+ <optional>
-+ <attribute name="reason"> <text /> </attribute>
-+ </optional>
-+ <optional>
-+ <attribute name="role"> <text /> </attribute>
-+ </optional>
-+ <optional>
-+ <attribute name="source"> <text /> </attribute>
-+ </optional>
-+ </element>
-+ </define>
-+
-+ <define name="element-utilization">
-+ <element name="utilization">
-+ <attribute name="function"> <text /> </attribute>
-+ <attribute name="node"> <text /> </attribute>
-+ <attribute name="resource"> <text /> </attribute>
-+ <zeroOrMore>
-+ <element>
-+ <anyName />
-+ <text />
-+ </element>
-+ </zeroOrMore>
-+ </element>
-+ </define>
-+</grammar>
---
-1.8.3.1
-
-
-From c46e07788788acf5669e3f89b9344190a91c7331 Mon Sep 17 00:00:00 2001
-From: Chris Lumens <clumens(a)redhat.com>
-Date: Fri, 18 Jun 2021 15:10:19 -0400
-Subject: [PATCH 4/5] Feature: tools: Add the node-summary to crm_simulate
- output.
-
-If --show-failcounts is given to crm_simulate, it should also display
-the node-summary message.
-
-See: rhbz#1686426
----
- tools/crm_simulate.c | 7 +++++--
- xml/api/crm_simulate-2.12.rng | 3 +++
- 2 files changed, 8 insertions(+), 2 deletions(-)
-
-diff --git a/tools/crm_simulate.c b/tools/crm_simulate.c
-index b4aa9d1..2ea292c 100644
---- a/tools/crm_simulate.c
-+++ b/tools/crm_simulate.c
-@@ -409,11 +409,14 @@ print_cluster_status(pe_working_set_t * data_set, unsigned int print_opts)
- FALSE, FALSE, all, all, FALSE);
-
- if (options.show_attrs) {
-- out->message(out, "node-attribute-list", data_set,
-- 0, rc == pcmk_rc_ok, FALSE, FALSE, FALSE, all, all);
-+ rc = out->message(out, "node-attribute-list", data_set,
-+ 0, rc == pcmk_rc_ok, FALSE, FALSE, FALSE, all, all);
- }
-
- if (options.show_failcounts) {
-+ rc = out->message(out, "node-summary", data_set, all, all,
-+ 0, print_opts, FALSE, FALSE, FALSE, FALSE, rc == pcmk_rc_ok);
-+
- out->message(out, "failed-action-list", data_set, all, all,
- rc == pcmk_rc_ok);
- }
-diff --git a/xml/api/crm_simulate-2.12.rng b/xml/api/crm_simulate-2.12.rng
-index 9a7612d..f90bd36 100644
---- a/xml/api/crm_simulate-2.12.rng
-+++ b/xml/api/crm_simulate-2.12.rng
-@@ -67,6 +67,9 @@
- <ref name="node-attributes-list" />
- </optional>
- <optional>
-+ <externalRef href="node-history-2.12.rng" />
-+ </optional>
-+ <optional>
- <ref name="failures-list" />
- </optional>
- </element>
---
-1.8.3.1
-
-
-From bac50336e0264604716e5997b87ee7e65311b982 Mon Sep 17 00:00:00 2001
-From: Chris Lumens <clumens(a)redhat.com>
-Date: Fri, 18 Jun 2021 15:21:52 -0400
-Subject: [PATCH 5/5] Low: libcrmcommon: Increase PCMK__API_VERSION for new
- crm_resource output.
-
-See: rhbz#1686426
----
- include/crm/common/output_internal.h | 2 +-
- 1 file changed, 1 insertion(+), 1 deletion(-)
-
-diff --git a/include/crm/common/output_internal.h b/include/crm/common/output_internal.h
-index 0436cde..ba9c423 100644
---- a/include/crm/common/output_internal.h
-+++ b/include/crm/common/output_internal.h
-@@ -27,7 +27,7 @@ extern "C" {
- # include <glib.h>
- # include <crm/common/results.h>
-
--# define PCMK__API_VERSION "2.11"
-+# define PCMK__API_VERSION "2.12"
-
- #if defined(PCMK__WITH_ATTRIBUTE_OUTPUT_ARGS)
- # define PCMK__OUTPUT_ARGS(ARGS...) __attribute__((output_args(ARGS)))
---
-1.8.3.1
-
diff --git a/007-unfencing-loop.patch b/007-unfencing-loop.patch
deleted file mode 100644
index d4950c8..0000000
--- a/007-unfencing-loop.patch
+++ /dev/null
@@ -1,733 +0,0 @@
-From 6dcd6b51d7d3993bc483588d6ed75077518ed600 Mon Sep 17 00:00:00 2001
-From: Ken Gaillot <kgaillot(a)redhat.com>
-Date: Fri, 4 Jun 2021 16:30:55 -0500
-Subject: [PATCH 01/11] Low: controller: check whether unfenced node was remote
- node
-
-... so the controller can indicate the node is remote (if known at that point,
-which is not guaranteed) when setting unfencing-related node attributes.
----
- daemons/controld/controld_fencing.c | 21 ++++++++++++++++++---
- 1 file changed, 18 insertions(+), 3 deletions(-)
-
-diff --git a/daemons/controld/controld_fencing.c b/daemons/controld/controld_fencing.c
-index 23dff28..0fba661 100644
---- a/daemons/controld/controld_fencing.c
-+++ b/daemons/controld/controld_fencing.c
-@@ -757,15 +757,30 @@ tengine_stonith_callback(stonith_t *stonith, stonith_callback_data_t *data)
- if (pcmk__str_eq("on", op, pcmk__str_casei)) {
- const char *value = NULL;
- char *now = pcmk__ttoa(time(NULL));
-+ gboolean is_remote_node = FALSE;
-+
-+ /* This check is not 100% reliable, since this node is not
-+ * guaranteed to have the remote node cached. However, it
-+ * doesn't have to be reliable, since the attribute manager can
-+ * learn a node's "remoteness" by other means sooner or later.
-+ * This allows it to learn more quickly if this node does have
-+ * the information.
-+ */
-+ if (g_hash_table_lookup(crm_remote_peer_cache, uuid) != NULL) {
-+ is_remote_node = TRUE;
-+ }
-
-- update_attrd(target, CRM_ATTR_UNFENCED, now, NULL, FALSE);
-+ update_attrd(target, CRM_ATTR_UNFENCED, now, NULL,
-+ is_remote_node);
- free(now);
-
- value = crm_meta_value(action->params, XML_OP_ATTR_DIGESTS_ALL);
-- update_attrd(target, CRM_ATTR_DIGESTS_ALL, value, NULL, FALSE);
-+ update_attrd(target, CRM_ATTR_DIGESTS_ALL, value, NULL,
-+ is_remote_node);
-
- value = crm_meta_value(action->params, XML_OP_ATTR_DIGESTS_SECURE);
-- update_attrd(target, CRM_ATTR_DIGESTS_SECURE, value, NULL, FALSE);
-+ update_attrd(target, CRM_ATTR_DIGESTS_SECURE, value, NULL,
-+ is_remote_node);
-
- } else if (action->sent_update == FALSE) {
- send_stonith_update(action, target, uuid);
---
-1.8.3.1
-
-
-From 3ef6d9403f68ab8559c45cc99f5a8da05ca6420b Mon Sep 17 00:00:00 2001
-From: Ken Gaillot <kgaillot(a)redhat.com>
-Date: Mon, 7 Jun 2021 10:50:36 -0500
-Subject: [PATCH 02/11] Refactor: pacemaker-attrd: functionize adding remote
- node to cache
-
-... for future reuse
----
- daemons/attrd/attrd_commands.c | 34 +++++++++++++++++++++++-----------
- 1 file changed, 23 insertions(+), 11 deletions(-)
-
-diff --git a/daemons/attrd/attrd_commands.c b/daemons/attrd/attrd_commands.c
-index 731c243..93a165b 100644
---- a/daemons/attrd/attrd_commands.c
-+++ b/daemons/attrd/attrd_commands.c
-@@ -102,6 +102,28 @@ free_attribute(gpointer data)
- }
- }
-
-+/*!
-+ * \internal
-+ * \brief Ensure a Pacemaker Remote node is in the correct peer cache
-+ *
-+ * \param[in]
-+ */
-+static void
-+cache_remote_node(const char *node_name)
-+{
-+ /* If we previously assumed this node was an unseen cluster node,
-+ * remove its entry from the cluster peer cache.
-+ */
-+ crm_node_t *dup = pcmk__search_cluster_node_cache(0, node_name);
-+
-+ if (dup && (dup->uuid == NULL)) {
-+ reap_crm_member(0, node_name);
-+ }
-+
-+ // Ensure node is in the remote peer cache
-+ CRM_ASSERT(crm_remote_peer_get(node_name) != NULL);
-+}
-+
- static xmlNode *
- build_attribute_xml(
- xmlNode *parent, const char *name, const char *set, const char *uuid, unsigned int timeout_ms, const char *user,
-@@ -709,17 +731,7 @@ attrd_lookup_or_create_value(GHashTable *values, const char *host, xmlNode *xml)
-
- crm_element_value_int(xml, PCMK__XA_ATTR_IS_REMOTE, &is_remote);
- if (is_remote) {
-- /* If we previously assumed this node was an unseen cluster node,
-- * remove its entry from the cluster peer cache.
-- */
-- crm_node_t *dup = pcmk__search_cluster_node_cache(0, host);
--
-- if (dup && (dup->uuid == NULL)) {
-- reap_crm_member(0, host);
-- }
--
-- /* Ensure this host is in the remote peer cache */
-- CRM_ASSERT(crm_remote_peer_get(host) != NULL);
-+ cache_remote_node(host);
- }
-
- if (v == NULL) {
---
-1.8.3.1
-
-
-From 6fac2c71bc2c56870ac828d7cd7b7c799279c47e Mon Sep 17 00:00:00 2001
-From: Ken Gaillot <kgaillot(a)redhat.com>
-Date: Mon, 7 Jun 2021 10:39:34 -0500
-Subject: [PATCH 03/11] Refactor: pacemaker-attrd: don't try to remove votes
- for remote nodes
-
-Remote nodes never vote.
-
-This has no effect in practice since the removal would simply do nothing,
-but we might as well not waste time trying.
----
- daemons/attrd/attrd_commands.c | 11 ++++++-----
- 1 file changed, 6 insertions(+), 5 deletions(-)
-
-diff --git a/daemons/attrd/attrd_commands.c b/daemons/attrd/attrd_commands.c
-index 93a165b..dbe777e 100644
---- a/daemons/attrd/attrd_commands.c
-+++ b/daemons/attrd/attrd_commands.c
-@@ -976,7 +976,8 @@ attrd_election_cb(gpointer user_data)
- void
- attrd_peer_change_cb(enum crm_status_type kind, crm_node_t *peer, const void *data)
- {
-- bool remove_voter = FALSE;
-+ bool gone = false;
-+ bool is_remote = pcmk_is_set(peer->flags, crm_remote_node);
-
- switch (kind) {
- case crm_status_uname:
-@@ -984,7 +985,7 @@ attrd_peer_change_cb(enum crm_status_type kind, crm_node_t *peer, const void *da
-
- case crm_status_processes:
- if (!pcmk_is_set(peer->processes, crm_get_cluster_proc())) {
-- remove_voter = TRUE;
-+ gone = true;
- }
- break;
-
-@@ -1000,13 +1001,13 @@ attrd_peer_change_cb(enum crm_status_type kind, crm_node_t *peer, const void *da
- } else {
- // Remove all attribute values associated with lost nodes
- attrd_peer_remove(peer->uname, FALSE, "loss");
-- remove_voter = TRUE;
-+ gone = true;
- }
- break;
- }
-
-- // In case an election is in progress, remove any vote by the node
-- if (remove_voter) {
-+ // Remove votes from cluster nodes that leave, in case election in progress
-+ if (gone && !is_remote) {
- attrd_remove_voter(peer);
- }
- }
---
-1.8.3.1
-
-
-From 54089fc663d6aaf10ca164c6c94b3b17237788de Mon Sep 17 00:00:00 2001
-From: Ken Gaillot <kgaillot(a)redhat.com>
-Date: Mon, 7 Jun 2021 10:40:06 -0500
-Subject: [PATCH 04/11] Low: pacemaker-attrd: check for remote nodes in peer
- update callback
-
-If a remote node was started before the local cluster node joined the cluster,
-the cluster node will assume its node attributes are for a cluster node until
-it learns otherwise. Check for remoteness in the peer update callback, to have
-another way we can learn it.
----
- daemons/attrd/attrd_commands.c | 4 ++++
- 1 file changed, 4 insertions(+)
-
-diff --git a/daemons/attrd/attrd_commands.c b/daemons/attrd/attrd_commands.c
-index dbe777e..5f6a754 100644
---- a/daemons/attrd/attrd_commands.c
-+++ b/daemons/attrd/attrd_commands.c
-@@ -1009,6 +1009,10 @@ attrd_peer_change_cb(enum crm_status_type kind, crm_node_t *peer, const void *da
- // Remove votes from cluster nodes that leave, in case election in progress
- if (gone && !is_remote) {
- attrd_remove_voter(peer);
-+
-+ // Ensure remote nodes that come up are in the remote node cache
-+ } else if (!gone && is_remote) {
-+ cache_remote_node(peer->uname);
- }
- }
-
---
-1.8.3.1
-
-
-From 8c048df0312d0d9c857d87b570a352429a710928 Mon Sep 17 00:00:00 2001
-From: Ken Gaillot <kgaillot(a)redhat.com>
-Date: Mon, 7 Jun 2021 11:29:12 -0500
-Subject: [PATCH 05/11] Log: pacemaker-attrd: log peer status changes
-
----
- daemons/attrd/attrd_commands.c | 9 +++++++++
- 1 file changed, 9 insertions(+)
-
-diff --git a/daemons/attrd/attrd_commands.c b/daemons/attrd/attrd_commands.c
-index 5f6a754..d6d179b 100644
---- a/daemons/attrd/attrd_commands.c
-+++ b/daemons/attrd/attrd_commands.c
-@@ -972,6 +972,7 @@ attrd_election_cb(gpointer user_data)
- return FALSE;
- }
-
-+#define state_text(state) ((state)? (const char *)(state) : "in unknown state")
-
- void
- attrd_peer_change_cb(enum crm_status_type kind, crm_node_t *peer, const void *data)
-@@ -981,15 +982,23 @@ attrd_peer_change_cb(enum crm_status_type kind, crm_node_t *peer, const void *da
-
- switch (kind) {
- case crm_status_uname:
-+ crm_debug("%s node %s is now %s",
-+ (is_remote? "Remote" : "Cluster"),
-+ peer->uname, state_text(peer->state));
- break;
-
- case crm_status_processes:
- if (!pcmk_is_set(peer->processes, crm_get_cluster_proc())) {
- gone = true;
- }
-+ crm_debug("Node %s is %s a peer",
-+ peer->uname, (gone? "no longer" : "now"));
- break;
-
- case crm_status_nstate:
-+ crm_debug("%s node %s is now %s (was %s)",
-+ (is_remote? "Remote" : "Cluster"),
-+ peer->uname, state_text(peer->state), state_text(data));
- if (pcmk__str_eq(peer->state, CRM_NODE_MEMBER, pcmk__str_casei)) {
- /* If we're the writer, send new peers a list of all attributes
- * (unless it's a remote node, which doesn't run its own attrd)
---
-1.8.3.1
-
-
-From 1dcc8dee4990cf0dbdec0e14db6d9a3ad67a41d5 Mon Sep 17 00:00:00 2001
-From: Ken Gaillot <kgaillot(a)redhat.com>
-Date: Mon, 7 Jun 2021 11:13:53 -0500
-Subject: [PATCH 06/11] Low: pacemaker-attrd: ensure node ID is only set for
- attributes when known
-
-In most cases, attribute updates contained the node ID, and the node ID was
-used by other code, only if known (i.e. positive). However a couple places did
-not check this, so add that.
-
-I am unsure whether the missing check caused problems in practice, but there
-appears to be the possibility that a remote node would wrongly be added to the
-cluster node cache.
----
- daemons/attrd/attrd_commands.c | 6 ++++--
- 1 file changed, 4 insertions(+), 2 deletions(-)
-
-diff --git a/daemons/attrd/attrd_commands.c b/daemons/attrd/attrd_commands.c
-index d6d179b..b3f441c 100644
---- a/daemons/attrd/attrd_commands.c
-+++ b/daemons/attrd/attrd_commands.c
-@@ -136,7 +136,9 @@ build_attribute_xml(
- crm_xml_add(xml, PCMK__XA_ATTR_UUID, uuid);
- crm_xml_add(xml, PCMK__XA_ATTR_USER, user);
- crm_xml_add(xml, PCMK__XA_ATTR_NODE_NAME, peer);
-- crm_xml_add_int(xml, PCMK__XA_ATTR_NODE_ID, peerid);
-+ if (peerid > 0) {
-+ crm_xml_add_int(xml, PCMK__XA_ATTR_NODE_ID, peerid);
-+ }
- crm_xml_add(xml, PCMK__XA_ATTR_VALUE, value);
- crm_xml_add_int(xml, PCMK__XA_ATTR_DAMPENING, timeout_ms/1000);
- crm_xml_add_int(xml, PCMK__XA_ATTR_IS_PRIVATE, is_private);
-@@ -937,7 +939,7 @@ attrd_peer_update(crm_node_t *peer, xmlNode *xml, const char *host, bool filter)
- /* If this is a cluster node whose node ID we are learning, remember it */
- if ((v->nodeid == 0) && (v->is_remote == FALSE)
- && (crm_element_value_int(xml, PCMK__XA_ATTR_NODE_ID,
-- (int*)&v->nodeid) == 0)) {
-+ (int*)&v->nodeid) == 0) && (v->nodeid > 0)) {
-
- crm_node_t *known_peer = crm_get_peer(v->nodeid, host);
-
---
-1.8.3.1
-
-
-From 8d12490e88b558d01db37a38f7d35175c6d2d69a Mon Sep 17 00:00:00 2001
-From: Ken Gaillot <kgaillot(a)redhat.com>
-Date: Thu, 10 Jun 2021 17:25:57 -0500
-Subject: [PATCH 07/11] Refactor: pacemaker-attrd: functionize processing a
- sync response
-
-... for code isolation, and because we need to add more to it
----
- daemons/attrd/attrd_commands.c | 59 ++++++++++++++++++++++++++++--------------
- 1 file changed, 39 insertions(+), 20 deletions(-)
-
-diff --git a/daemons/attrd/attrd_commands.c b/daemons/attrd/attrd_commands.c
-index b3f441c..d02d3e6 100644
---- a/daemons/attrd/attrd_commands.c
-+++ b/daemons/attrd/attrd_commands.c
-@@ -572,6 +572,43 @@ attrd_peer_clear_failure(crm_node_t *peer, xmlNode *xml)
- }
-
- /*!
-+ * \internal
-+ * \brief Load attributes from a peer sync response
-+ *
-+ * \param[in] peer Peer that sent clear request
-+ * \param[in] peer_won Whether peer is the attribute writer
-+ * \param[in] xml Request XML
-+ */
-+static void
-+process_peer_sync_response(crm_node_t *peer, bool peer_won, xmlNode *xml)
-+{
-+ crm_info("Processing " PCMK__ATTRD_CMD_SYNC_RESPONSE " from %s",
-+ peer->uname);
-+
-+ if (peer_won) {
-+ /* Initialize the "seen" flag for all attributes to cleared, so we can
-+ * detect attributes that local node has but the writer doesn't.
-+ */
-+ clear_attribute_value_seen();
-+ }
-+
-+ // Process each attribute update in the sync response
-+ for (xmlNode *child = pcmk__xml_first_child(xml); child != NULL;
-+ child = pcmk__xml_next(child)) {
-+ attrd_peer_update(peer, child,
-+ crm_element_value(child, PCMK__XA_ATTR_NODE_NAME),
-+ TRUE);
-+ }
-+
-+ if (peer_won) {
-+ /* If any attributes are still not marked as seen, the writer doesn't
-+ * know about them, so send all peers an update with them.
-+ */
-+ attrd_current_only_attribute_update(peer, xml);
-+ }
-+}
-+
-+/*!
- \internal
- \brief Broadcast private attribute for local node with protocol version
- */
-@@ -596,7 +633,7 @@ attrd_peer_message(crm_node_t *peer, xmlNode *xml)
- const char *op = crm_element_value(xml, PCMK__XA_TASK);
- const char *election_op = crm_element_value(xml, F_CRM_TASK);
- const char *host = crm_element_value(xml, PCMK__XA_ATTR_NODE_NAME);
-- bool peer_won = FALSE;
-+ bool peer_won = false;
-
- if (election_op) {
- attrd_handle_election_op(peer, xml);
-@@ -631,25 +668,7 @@ attrd_peer_message(crm_node_t *peer, xmlNode *xml)
-
- } else if (pcmk__str_eq(op, PCMK__ATTRD_CMD_SYNC_RESPONSE, pcmk__str_casei)
- && !pcmk__str_eq(peer->uname, attrd_cluster->uname, pcmk__str_casei)) {
-- xmlNode *child = NULL;
--
-- crm_info("Processing %s from %s", op, peer->uname);
--
-- /* Clear the seen flag for attribute processing held only in the own node. */
-- if (peer_won) {
-- clear_attribute_value_seen();
-- }
--
-- for (child = pcmk__xml_first_child(xml); child != NULL;
-- child = pcmk__xml_next(child)) {
-- host = crm_element_value(child, PCMK__XA_ATTR_NODE_NAME);
-- attrd_peer_update(peer, child, host, TRUE);
-- }
--
-- if (peer_won) {
-- /* Synchronize if there is an attribute held only by own node that Writer does not have. */
-- attrd_current_only_attribute_update(peer, xml);
-- }
-+ process_peer_sync_response(peer, peer_won, xml);
-
- } else if (pcmk__str_eq(op, PCMK__ATTRD_CMD_FLUSH, pcmk__str_casei)) {
- /* Ignore. The flush command was removed in 2.0.0 but may be
---
-1.8.3.1
-
-
-From a890a0e5bbbcabf907f51ed0460868035f72464d Mon Sep 17 00:00:00 2001
-From: Ken Gaillot <kgaillot(a)redhat.com>
-Date: Fri, 11 Jun 2021 14:40:39 -0500
-Subject: [PATCH 08/11] Refactor: pacemaker-attrd: functionize broadcasting
- local override
-
-... for code isolation
----
- daemons/attrd/attrd_commands.c | 42 +++++++++++++++++++++++++++++-------------
- 1 file changed, 29 insertions(+), 13 deletions(-)
-
-diff --git a/daemons/attrd/attrd_commands.c b/daemons/attrd/attrd_commands.c
-index d02d3e6..4783427 100644
---- a/daemons/attrd/attrd_commands.c
-+++ b/daemons/attrd/attrd_commands.c
-@@ -804,6 +804,34 @@ attrd_current_only_attribute_update(crm_node_t *peer, xmlNode *xml)
- free_xml(sync);
- }
-
-+/*!
-+ * \internal
-+ * \brief Override an attribute sync with a local value
-+ *
-+ * Broadcast the local node's value for an attribute that's different from the
-+ * value provided in a peer's attribute synchronization response. This ensures a
-+ * node's values for itself take precedence and all peers are kept in sync.
-+ *
-+ * \param[in] a Attribute entry to override
-+ *
-+ * \return Local instance of attribute value
-+ */
-+static attribute_value_t *
-+broadcast_local_value(attribute_t *a)
-+{
-+ attribute_value_t *v = g_hash_table_lookup(a->values, attrd_cluster->uname);
-+ xmlNode *sync = create_xml_node(NULL, __func__);
-+
-+ crm_xml_add(sync, PCMK__XA_TASK, PCMK__ATTRD_CMD_SYNC_RESPONSE);
-+ build_attribute_xml(sync, a->id, a->set, a->uuid, a->timeout_ms,
-+ a->user, a->is_private, v->nodename, v->nodeid,
-+ v->current, FALSE);
-+ attrd_xml_add_writer(sync);
-+ send_attrd_message(NULL, sync);
-+ free_xml(sync);
-+ return v;
-+}
-+
- void
- attrd_peer_update(crm_node_t *peer, xmlNode *xml, const char *host, bool filter)
- {
-@@ -899,21 +927,9 @@ attrd_peer_update(crm_node_t *peer, xmlNode *xml, const char *host, bool filter)
- if (filter && !pcmk__str_eq(v->current, value, pcmk__str_casei)
- && pcmk__str_eq(host, attrd_cluster->uname, pcmk__str_casei)) {
-
-- xmlNode *sync = create_xml_node(NULL, __func__);
--
- crm_notice("%s[%s]: local value '%s' takes priority over '%s' from %s",
- attr, host, v->current, value, peer->uname);
--
-- crm_xml_add(sync, PCMK__XA_TASK, PCMK__ATTRD_CMD_SYNC_RESPONSE);
-- v = g_hash_table_lookup(a->values, host);
-- build_attribute_xml(sync, attr, a->set, a->uuid, a->timeout_ms, a->user,
-- a->is_private, v->nodename, v->nodeid, v->current, FALSE);
--
-- attrd_xml_add_writer(sync);
--
-- /* Broadcast in case any other nodes had the inconsistent value */
-- send_attrd_message(NULL, sync);
-- free_xml(sync);
-+ v = broadcast_local_value(a);
-
- } else if (!pcmk__str_eq(v->current, value, pcmk__str_casei)) {
- crm_notice("Setting %s[%s]: %s -> %s " CRM_XS " from %s",
---
-1.8.3.1
-
-
-From f6f65e3dab070f1bbdf6d1383f4d6173a8840bc9 Mon Sep 17 00:00:00 2001
-From: Ken Gaillot <kgaillot(a)redhat.com>
-Date: Fri, 11 Jun 2021 14:50:29 -0500
-Subject: [PATCH 09/11] Log: pacemaker-attrd: improve messages when
- broadcasting local-only values
-
-The traces aren't necessary since build_attribute_xml() already logs the same
-info at debug. Also, rename function for clarity, and make static.
----
- daemons/attrd/attrd_commands.c | 35 ++++++++++++++++-------------------
- 1 file changed, 16 insertions(+), 19 deletions(-)
-
-diff --git a/daemons/attrd/attrd_commands.c b/daemons/attrd/attrd_commands.c
-index 4783427..356defb 100644
---- a/daemons/attrd/attrd_commands.c
-+++ b/daemons/attrd/attrd_commands.c
-@@ -51,11 +51,12 @@ GHashTable *attributes = NULL;
-
- void write_attribute(attribute_t *a, bool ignore_delay);
- void write_or_elect_attribute(attribute_t *a);
--void attrd_current_only_attribute_update(crm_node_t *peer, xmlNode *xml);
- void attrd_peer_update(crm_node_t *peer, xmlNode *xml, const char *host, bool filter);
- void attrd_peer_sync(crm_node_t *peer, xmlNode *xml);
- void attrd_peer_remove(const char *host, gboolean uncache, const char *source);
-
-+static void broadcast_unseen_local_values(crm_node_t *peer, xmlNode *xml);
-+
- static gboolean
- send_attrd_message(crm_node_t * node, xmlNode * data)
- {
-@@ -604,7 +605,7 @@ process_peer_sync_response(crm_node_t *peer, bool peer_won, xmlNode *xml)
- /* If any attributes are still not marked as seen, the writer doesn't
- * know about them, so send all peers an update with them.
- */
-- attrd_current_only_attribute_update(peer, xml);
-+ broadcast_unseen_local_values(peer, xml);
- }
- }
-
-@@ -768,40 +769,36 @@ attrd_lookup_or_create_value(GHashTable *values, const char *host, xmlNode *xml)
- return(v);
- }
-
--void
--attrd_current_only_attribute_update(crm_node_t *peer, xmlNode *xml)
-+void
-+broadcast_unseen_local_values(crm_node_t *peer, xmlNode *xml)
- {
- GHashTableIter aIter;
- GHashTableIter vIter;
-- attribute_t *a;
-+ attribute_t *a = NULL;
- attribute_value_t *v = NULL;
-- xmlNode *sync = create_xml_node(NULL, __func__);
-- gboolean build = FALSE;
--
-- crm_xml_add(sync, PCMK__XA_TASK, PCMK__ATTRD_CMD_SYNC_RESPONSE);
-+ xmlNode *sync = NULL;
-
- g_hash_table_iter_init(&aIter, attributes);
- while (g_hash_table_iter_next(&aIter, NULL, (gpointer *) & a)) {
- g_hash_table_iter_init(&vIter, a->values);
- while (g_hash_table_iter_next(&vIter, NULL, (gpointer *) & v)) {
-- if (pcmk__str_eq(v->nodename, attrd_cluster->uname, pcmk__str_casei) && v->seen == FALSE) {
-- crm_trace("Syncing %s[%s] = %s to everyone.(from local only attributes)", a->id, v->nodename, v->current);
--
-- build = TRUE;
-+ if (!(v->seen) && pcmk__str_eq(v->nodename, attrd_cluster->uname,
-+ pcmk__str_casei)) {
-+ if (sync == NULL) {
-+ sync = create_xml_node(NULL, __func__);
-+ crm_xml_add(sync, PCMK__XA_TASK, PCMK__ATTRD_CMD_SYNC_RESPONSE);
-+ }
- build_attribute_xml(sync, a->id, a->set, a->uuid, a->timeout_ms, a->user, a->is_private,
- v->nodename, v->nodeid, v->current, (a->timeout_ms && a->timer ? TRUE : FALSE));
-- } else {
-- crm_trace("Local attribute(%s[%s] = %s) was ignore.(another host) : [%s]", a->id, v->nodename, v->current, attrd_cluster->uname);
-- continue;
- }
- }
- }
-
-- if (build) {
-- crm_debug("Syncing values to everyone.(from local only attributes)");
-+ if (sync != NULL) {
-+ crm_debug("Broadcasting local-only values");
- send_attrd_message(NULL, sync);
-+ free_xml(sync);
- }
-- free_xml(sync);
- }
-
- /*!
---
-1.8.3.1
-
-
-From ab90ffb785ea018556f216b8f540f8c3429a3947 Mon Sep 17 00:00:00 2001
-From: Ken Gaillot <kgaillot(a)redhat.com>
-Date: Fri, 11 Jun 2021 15:04:20 -0500
-Subject: [PATCH 10/11] Refactor: pacemaker-attrd: simplify attribute XML
- creation function
-
-... and rename for clarity
----
- daemons/attrd/attrd_commands.c | 48 ++++++++++++++++++++++++------------------
- 1 file changed, 27 insertions(+), 21 deletions(-)
-
-diff --git a/daemons/attrd/attrd_commands.c b/daemons/attrd/attrd_commands.c
-index 356defb..5b32a77 100644
---- a/daemons/attrd/attrd_commands.c
-+++ b/daemons/attrd/attrd_commands.c
-@@ -125,25 +125,35 @@ cache_remote_node(const char *node_name)
- CRM_ASSERT(crm_remote_peer_get(node_name) != NULL);
- }
-
-+/*!
-+ * \internal
-+ * \brief Create an XML representation of an attribute for use in peer messages
-+ *
-+ * \param[in] parent Create attribute XML as child element of this element
-+ * \param[in] a Attribute to represent
-+ * \param[in] v Attribute value to represent
-+ * \param[in] force_write If true, value should be written even if unchanged
-+ *
-+ * \return XML representation of attribute
-+ */
- static xmlNode *
--build_attribute_xml(
-- xmlNode *parent, const char *name, const char *set, const char *uuid, unsigned int timeout_ms, const char *user,
-- gboolean is_private, const char *peer, uint32_t peerid, const char *value, gboolean is_force_write)
-+add_attribute_value_xml(xmlNode *parent, attribute_t *a, attribute_value_t *v,
-+ bool force_write)
- {
- xmlNode *xml = create_xml_node(parent, __func__);
-
-- crm_xml_add(xml, PCMK__XA_ATTR_NAME, name);
-- crm_xml_add(xml, PCMK__XA_ATTR_SET, set);
-- crm_xml_add(xml, PCMK__XA_ATTR_UUID, uuid);
-- crm_xml_add(xml, PCMK__XA_ATTR_USER, user);
-- crm_xml_add(xml, PCMK__XA_ATTR_NODE_NAME, peer);
-- if (peerid > 0) {
-- crm_xml_add_int(xml, PCMK__XA_ATTR_NODE_ID, peerid);
-+ crm_xml_add(xml, PCMK__XA_ATTR_NAME, a->id);
-+ crm_xml_add(xml, PCMK__XA_ATTR_SET, a->set);
-+ crm_xml_add(xml, PCMK__XA_ATTR_UUID, a->uuid);
-+ crm_xml_add(xml, PCMK__XA_ATTR_USER, a->user);
-+ crm_xml_add(xml, PCMK__XA_ATTR_NODE_NAME, v->nodename);
-+ if (v->nodeid > 0) {
-+ crm_xml_add_int(xml, PCMK__XA_ATTR_NODE_ID, v->nodeid);
- }
-- crm_xml_add(xml, PCMK__XA_ATTR_VALUE, value);
-- crm_xml_add_int(xml, PCMK__XA_ATTR_DAMPENING, timeout_ms/1000);
-- crm_xml_add_int(xml, PCMK__XA_ATTR_IS_PRIVATE, is_private);
-- crm_xml_add_int(xml, PCMK__XA_ATTR_FORCE, is_force_write);
-+ crm_xml_add(xml, PCMK__XA_ATTR_VALUE, v->current);
-+ crm_xml_add_int(xml, PCMK__XA_ATTR_DAMPENING, a->timeout_ms / 1000);
-+ crm_xml_add_int(xml, PCMK__XA_ATTR_IS_PRIVATE, a->is_private);
-+ crm_xml_add_int(xml, PCMK__XA_ATTR_FORCE, force_write);
-
- return xml;
- }
-@@ -695,8 +705,7 @@ attrd_peer_sync(crm_node_t *peer, xmlNode *xml)
- g_hash_table_iter_init(&vIter, a->values);
- while (g_hash_table_iter_next(&vIter, NULL, (gpointer *) & v)) {
- crm_debug("Syncing %s[%s] = %s to %s", a->id, v->nodename, v->current, peer?peer->uname:"everyone");
-- build_attribute_xml(sync, a->id, a->set, a->uuid, a->timeout_ms, a->user, a->is_private,
-- v->nodename, v->nodeid, v->current, FALSE);
-+ add_attribute_value_xml(sync, a, v, false);
- }
- }
-
-@@ -788,8 +797,7 @@ broadcast_unseen_local_values(crm_node_t *peer, xmlNode *xml)
- sync = create_xml_node(NULL, __func__);
- crm_xml_add(sync, PCMK__XA_TASK, PCMK__ATTRD_CMD_SYNC_RESPONSE);
- }
-- build_attribute_xml(sync, a->id, a->set, a->uuid, a->timeout_ms, a->user, a->is_private,
-- v->nodename, v->nodeid, v->current, (a->timeout_ms && a->timer ? TRUE : FALSE));
-+ add_attribute_value_xml(sync, a, v, a->timeout_ms && a->timer);
- }
- }
- }
-@@ -820,9 +828,7 @@ broadcast_local_value(attribute_t *a)
- xmlNode *sync = create_xml_node(NULL, __func__);
-
- crm_xml_add(sync, PCMK__XA_TASK, PCMK__ATTRD_CMD_SYNC_RESPONSE);
-- build_attribute_xml(sync, a->id, a->set, a->uuid, a->timeout_ms,
-- a->user, a->is_private, v->nodename, v->nodeid,
-- v->current, FALSE);
-+ add_attribute_value_xml(sync, a, v, false);
- attrd_xml_add_writer(sync);
- send_attrd_message(NULL, sync);
- free_xml(sync);
---
-1.8.3.1
-
-
-From 540d74130c5c8d9c626d6c50475e4dc4f64234e7 Mon Sep 17 00:00:00 2001
-From: Ken Gaillot <kgaillot(a)redhat.com>
-Date: Fri, 4 Jun 2021 16:34:26 -0500
-Subject: [PATCH 11/11] Fix: pacemaker-attrd: avoid repeated unfencing of
- remote nodes
-
-The attribute manager can't record a remote node's attributes to the CIB until
-it knows the node is remote. Normally, this is learned when the remote node
-starts, because the controller clears the CRM_OP_PROBED attribute and indicates
-that it is for a remote node.
-
-However, if a cluster node is down when a remote node starts, and later comes
-up, it learns the remote node's existing attributes as part of the attribute
-sync. Previously, this did not include whether each value is for a cluster or
-remote node, so the newly joined attribute manager couldn't write out remote
-nodes' attributes until it learned that via some other event -- which might not
-happen before the node becomes DC, in which case its scheduler will not see any
-unfencing-related node attributes and may wrongly schedule unfencing.
-
-The sync response handling already calls attrd_lookup_or_create_value(), which
-checks PCMK__XA_ATTR_IS_REMOTE, so all we need to do is add that to the sync
-response.
----
- daemons/attrd/attrd_commands.c | 6 +++++-
- 1 file changed, 5 insertions(+), 1 deletion(-)
-
-diff --git a/daemons/attrd/attrd_commands.c b/daemons/attrd/attrd_commands.c
-index 5b32a77..0142383 100644
---- a/daemons/attrd/attrd_commands.c
-+++ b/daemons/attrd/attrd_commands.c
-@@ -43,8 +43,9 @@
- * 1 1.1.15 PCMK__ATTRD_CMD_UPDATE_BOTH,
- * PCMK__ATTRD_CMD_UPDATE_DELAY
- * 2 1.1.17 PCMK__ATTRD_CMD_CLEAR_FAILURE
-+ * 3 2.1.1 PCMK__ATTRD_CMD_SYNC_RESPONSE indicates remote nodes
- */
--#define ATTRD_PROTOCOL_VERSION "2"
-+#define ATTRD_PROTOCOL_VERSION "3"
-
- int last_cib_op_done = 0;
- GHashTable *attributes = NULL;
-@@ -150,6 +151,9 @@ add_attribute_value_xml(xmlNode *parent, attribute_t *a, attribute_value_t *v,
- if (v->nodeid > 0) {
- crm_xml_add_int(xml, PCMK__XA_ATTR_NODE_ID, v->nodeid);
- }
-+ if (v->is_remote != 0) {
-+ crm_xml_add_int(xml, PCMK__XA_ATTR_IS_REMOTE, 1);
-+ }
- crm_xml_add(xml, PCMK__XA_ATTR_VALUE, v->current);
- crm_xml_add_int(xml, PCMK__XA_ATTR_DAMPENING, a->timeout_ms / 1000);
- crm_xml_add_int(xml, PCMK__XA_ATTR_IS_PRIVATE, a->is_private);
---
-1.8.3.1
-
diff --git a/pacemaker.spec b/pacemaker.spec
index a2a1dfb..b3f962b 100644
--- a/pacemaker.spec
+++ b/pacemaker.spec
@@ -31,11 +31,11 @@
## Upstream pacemaker version, and its package version (specversion
## can be incremented to build packages reliably considered "newer"
## than previously built packages with the same pcmkversion)
-%global pcmkversion 2.1.0
-%global specversion 6
+%global pcmkversion 2.1.1
+%global specversion 0.1.rc1
## Upstream commit (full commit ID, abbreviated commit ID, or tag) to build
-%global commit 7c3f660707a495a1331716ad32cd3ac9d9f8ff58
+%global commit 9e3a841a33c3125034922ec061f6516d307f20d1
## Since git v2.11, the extent of abbreviation is autoscaled by default
## (used to be constant of 7), so we need to convey it for non-tags, too.
@@ -80,7 +80,7 @@
## Add option to prefix package version with "0."
## (so later "official" packages will be considered updates)
-%bcond_with pre_release
+%bcond_without pre_release
## NOTE: skip --with upstart_job
@@ -201,7 +201,7 @@
Name: pacemaker
Summary: Scalable High-Availability cluster resource manager
Version: %{pcmkversion}
-Release: %{pcmk_release}%{?dist}.1
+Release: %{pcmk_release}%{?dist}
License: GPLv2+ and LGPLv2+
Url: https://www.clusterlabs.org/
@@ -217,13 +217,6 @@ Source0: https://codeload.github.com/%{github_owner}/%{name}/tar.gz/%{arch
Source1: https://codeload.github.com/%{github_owner}/%{nagios_name}/tar.gz/%{nagio...
# upstream commits
-Patch1: 001-ping-agent.patch
-Patch2: 002-pacemakerd-options.patch
-Patch3: 003-pacemakerd-output.patch
-Patch4: 004-check-level.patch
-Patch5: 005-crm_resource.patch
-Patch6: 006-crm_simulate.patch
-Patch7: 007-unfencing-loop.patch
Requires: resource-agents
Requires: %{pkgname_pcmk_libs}%{?_isa} = %{version}-%{release}
@@ -806,6 +799,11 @@ exit 0
%license %{nagios_name}-%{nagios_hash}/COPYING
%changelog
+* Wed Jul 28 2021 Klaus Wenninger <kwenning(a)redhat.com> - 2.1.1-0.1.rc1
+- Update for new upstream tarball for release candidate: Pacemaker-2.1.1-rc1,
+ for full details, see included ChangeLog file or
+ https://github.com/ClusterLabs/pacemaker/releases/tag/Pacemaker-2.1.1-rc1
+
* Thu Jul 22 2021 Fedora Release Engineering <releng(a)fedoraproject.org> - 2.1.0-6.1
- Rebuilt for https://fedoraproject.org/wiki/Fedora_35_Mass_Rebuild
diff --git a/sources b/sources
index 2ab7f32..b933127 100644
--- a/sources
+++ b/sources
@@ -1,2 +1,2 @@
SHA512 (nagios-agents-metadata-105ab8a7b2c16b9a29cf1c1596b80136eeef332b.tar.gz) = 11ddeb48a4929e7642b6dfa9c7962aa1d7a1af1c569830f55ed6cd6773abac13377317327bc1db8411c8077884f83f81cc54d746c834b63a99fa6dc219b5caad
-SHA512 (pacemaker-7c3f66070.tar.gz) = e1c53d019c965395affc1bd78358927617c9bc8572c405ee884737a8213455c5e85102e6f58bb5239ab23ebe5aa794f72c9cc0746900b7a6cc14fd3cf1879064
+SHA512 (pacemaker-9e3a841a3.tar.gz) = 26745ba0d9f6730de13ad106fe57beaff9d49c7df1ec528f0689c2faa872608408fb04401a4d49d73deec5422bd7665496a964b1fcbe69866a5983ac2306b6be
commit 084488a3ae7b0d9112cb80ef1f9b0f7064fbcd72
Author: Fedora Release Engineering <releng(a)fedoraproject.org>
Date: Thu Jul 22 17:33:56 2021 +0000
- Rebuilt for https://fedoraproject.org/wiki/Fedora_35_Mass_Rebuild
Signed-off-by: Fedora Release Engineering <releng(a)fedoraproject.org>
diff --git a/pacemaker.spec b/pacemaker.spec
index 48b6a44..a2a1dfb 100644
--- a/pacemaker.spec
+++ b/pacemaker.spec
@@ -201,7 +201,7 @@
Name: pacemaker
Summary: Scalable High-Availability cluster resource manager
Version: %{pcmkversion}
-Release: %{pcmk_release}%{?dist}
+Release: %{pcmk_release}%{?dist}.1
License: GPLv2+ and LGPLv2+
Url: https://www.clusterlabs.org/
@@ -806,6 +806,9 @@ exit 0
%license %{nagios_name}-%{nagios_hash}/COPYING
%changelog
+* Thu Jul 22 2021 Fedora Release Engineering <releng(a)fedoraproject.org> - 2.1.0-6.1
+- Rebuilt for https://fedoraproject.org/wiki/Fedora_35_Mass_Rebuild
+
* Mon Jul 5 2021 Klaus Wenninger <kwenning(a)redhat.com> - 2.1.0-6
- synced/merged with CS9 spec-file for current 2.1.0-release build
commit 15ac1a3c342218687bbddb1cdb2fc0edc968d5ac
Author: Klaus Wenninger <klaus.wenninger(a)aon.at>
Date: Mon Jul 5 18:38:43 2021 +0200
* Mon Jul 5 2021 Klaus Wenninger <kwenning(a)redhat.com> - 2.1.0-6
- synced/merged with CS9 spec-file for current 2.1.0-release build
* Tue Jun 8 2021 Klaus Wenninger <kwenning(a)redhat.com> - 2.1.0-0.5.rc3
- silence f33 s390x build complaining about possible format-trucation
diff --git a/001-ping-agent.patch b/001-ping-agent.patch
new file mode 100644
index 0000000..89fe41a
--- /dev/null
+++ b/001-ping-agent.patch
@@ -0,0 +1,225 @@
+From c6ee0973522268ed7b3241cf0ec2e06398444114 Mon Sep 17 00:00:00 2001
+From: Grace Chin <gchin(a)redhat.com>
+Date: Tue, 4 May 2021 12:02:17 -0400
+Subject: [PATCH 1/4] Remove deprecated attrd_options
+
+---
+ extra/resources/ping | 11 +++--------
+ 1 file changed, 3 insertions(+), 8 deletions(-)
+
+diff --git a/extra/resources/ping b/extra/resources/ping
+index 3cf8dfe..2e93f22 100755
+--- a/extra/resources/ping
++++ b/extra/resources/ping
+@@ -178,7 +178,7 @@ ping_stop() {
+
+ rm -f "${OCF_RESKEY_pidfile}"
+
+- attrd_updater -D -n "$OCF_RESKEY_name" -d "$OCF_RESKEY_dampen" $attrd_options
++ attrd_updater -D -n "$OCF_RESKEY_name" -d "$OCF_RESKEY_dampen"
+
+ return $OCF_SUCCESS
+ }
+@@ -302,9 +302,9 @@ ping_update() {
+
+ score=$(expr $active \* $OCF_RESKEY_multiplier)
+ if [ "$__OCF_ACTION" = "start" ] ; then
+- attrd_updater -n "$OCF_RESKEY_name" -B "$score" -d "$OCF_RESKEY_dampen" $attrd_options
++ attrd_updater -n "$OCF_RESKEY_name" -B "$score" -d "$OCF_RESKEY_dampen"
+ else
+- attrd_updater -n "$OCF_RESKEY_name" -v "$score" -d "$OCF_RESKEY_dampen" $attrd_options
++ attrd_updater -n "$OCF_RESKEY_name" -v "$score" -d "$OCF_RESKEY_dampen"
+ fi
+ rc=$?
+ case $rc in
+@@ -396,11 +396,6 @@ case "${OCF_RESKEY_debug}" in
+ ;;
+ esac
+
+-attrd_options='-q'
+-if [ "${OCF_RESKEY_debug}" = "true" ]; then
+- attrd_options=''
+-fi
+-
+ case "$__OCF_ACTION" in
+ meta-data) meta_data
+ exit $OCF_SUCCESS
+--
+1.8.3.1
+
+
+From 6d6c4691cf0970059689856c354daf9e098b4451 Mon Sep 17 00:00:00 2001
+From: Grace Chin <gchin(a)redhat.com>
+Date: Tue, 4 May 2021 14:50:37 -0400
+Subject: [PATCH 2/4] Replace debug values, true and false, with 0 and 1
+
+---
+ extra/resources/ping | 8 ++++----
+ 1 file changed, 4 insertions(+), 4 deletions(-)
+
+diff --git a/extra/resources/ping b/extra/resources/ping
+index 2e93f22..fee019b 100755
+--- a/extra/resources/ping
++++ b/extra/resources/ping
+@@ -24,7 +24,7 @@
+ : ${OCF_RESKEY_dampen:="5s"}
+ : ${OCF_RESKEY_attempts:="3"}
+ : ${OCF_RESKEY_multiplier:="1"}
+-: ${OCF_RESKEY_debug:="false"}
++: ${OCF_RESKEY_debug:="0"}
+ : ${OCF_RESKEY_failure_score:="0"}
+ : ${OCF_RESKEY_use_fping:="1"}
+ : ${OCF_RESKEY_host_list:=""}
+@@ -152,7 +152,7 @@ END
+
+ ping_conditional_log() {
+ level="$1"; shift
+- if [ "${OCF_RESKEY_debug}" = "true" ]; then
++ if [ $OCF_RESKEY_debug -gt 0 ]; then
+ ocf_log "$level" "$*"
+ fi
+ }
+@@ -388,8 +388,8 @@ fi
+
+ # Check the debug option
+ case "${OCF_RESKEY_debug}" in
+- true|True|TRUE|1) OCF_RESKEY_debug=true;;
+- false|False|FALSE|0) OCF_RESKEY_debug=false;;
++ true|True|TRUE|1) OCF_RESKEY_debug=0;;
++ false|False|FALSE|0) OCF_RESKEY_debug=1;;
+ *)
+ ocf_log warn "Value for 'debug' is incorrect. Please specify 'true' or 'false' not: ${OCF_RESKEY_debug}"
+ OCF_RESKEY_debug=false
+--
+1.8.3.1
+
+
+From a886a31056b6aca764c6911f5432af2c5ebf51df Mon Sep 17 00:00:00 2001
+From: Grace Chin <gchin(a)redhat.com>
+Date: Tue, 11 May 2021 11:04:50 -0400
+Subject: [PATCH 3/4] Add verbose debug mode which logs ping and fping output
+ when set
+
+---
+ extra/resources/ping | 19 ++++++++++++++-----
+ 1 file changed, 14 insertions(+), 5 deletions(-)
+
+diff --git a/extra/resources/ping b/extra/resources/ping
+index fee019b..cc796af 100755
+--- a/extra/resources/ping
++++ b/extra/resources/ping
+@@ -249,10 +249,13 @@ fping_check() {
+
+ case $rc in
+ 0)
++ if [ $OCF_RESKEY_debug -gt 1 ]; then
++ ping_conditional_log info "$output"
++ fi
+ ;;
+ 1)
+ for h in $(echo "$output" | grep "is unreachable" | awk '{print $1}'); do
+- ping_conditional_log warn "$h is inactive"
++ ping_conditional_log warn "$h is inactive: $output"
+ done
+ ;;
+ *)
+@@ -282,7 +285,12 @@ ping_check() {
+ p_out=$($p_exe $p_args $OCF_RESKEY_options $host 2>&1); rc=$?
+
+ case $rc in
+- 0) active=$(expr $active + 1);;
++ 0)
++ active=$(expr $active + 1)
++ if [ $OCF_RESKEY_debug -gt 1 ]; then
++ ping_conditional_log info "$p_out"
++ fi
++ ;;
+ 1) ping_conditional_log warn "$host is inactive: $p_out";;
+ *) ocf_log err "Unexpected result for '$p_exe $p_args $OCF_RESKEY_options $host' $rc: $p_out";;
+ esac
+@@ -388,10 +396,11 @@ fi
+
+ # Check the debug option
+ case "${OCF_RESKEY_debug}" in
+- true|True|TRUE|1) OCF_RESKEY_debug=0;;
+- false|False|FALSE|0) OCF_RESKEY_debug=1;;
++ true|True|TRUE|1) OCF_RESKEY_debug=1;;
++ false|False|FALSE|0) OCF_RESKEY_debug=0;;
++ verbose|Verbose|VERBOSE|2) OCF_RESKEY_debug=2;;
+ *)
+- ocf_log warn "Value for 'debug' is incorrect. Please specify 'true' or 'false' not: ${OCF_RESKEY_debug}"
++ ocf_log warn "Value for 'debug' is incorrect. Please specify 'true', 'false', or 'verbose', not: ${OCF_RESKEY_debug}"
+ OCF_RESKEY_debug=false
+ ;;
+ esac
+--
+1.8.3.1
+
+
+From 460043f133ced80e923b1290af70502a72deb7f8 Mon Sep 17 00:00:00 2001
+From: Grace Chin <gchin(a)redhat.com>
+Date: Tue, 11 May 2021 11:07:05 -0400
+Subject: [PATCH 4/4] Improve variable names
+
+---
+ extra/resources/ping | 20 ++++++++++----------
+ 1 file changed, 10 insertions(+), 10 deletions(-)
+
+diff --git a/extra/resources/ping b/extra/resources/ping
+index cc796af..9763b60 100755
+--- a/extra/resources/ping
++++ b/extra/resources/ping
+@@ -244,22 +244,22 @@ fping_check() {
+ timeout=$(expr $OCF_RESKEY_timeout \* 1000 / $OCF_RESKEY_attempts)
+
+ cmd="$p_exe -r $OCF_RESKEY_attempts -t $timeout -B 1.0 $OCF_RESKEY_options $OCF_RESKEY_host_list"
+- output=$($cmd 2>&1); rc=$?
+- active=$(echo "$output" | grep "is alive" | wc -l)
++ fping_output=$($cmd 2>&1); rc=$?
++ active=$(echo "$fping_output" | grep "is alive" | wc -l)
+
+ case $rc in
+ 0)
+ if [ $OCF_RESKEY_debug -gt 1 ]; then
+- ping_conditional_log info "$output"
++ ping_conditional_log info "$fping_output"
+ fi
+ ;;
+ 1)
+- for h in $(echo "$output" | grep "is unreachable" | awk '{print $1}'); do
+- ping_conditional_log warn "$h is inactive: $output"
++ for h in $(echo "$fping_output" | grep "is unreachable" | awk '{print $1}'); do
++ ping_conditional_log warn "$h is inactive: $fping_output"
+ done
+ ;;
+ *)
+- ocf_log err "Unexpected result for '$cmd' $rc: $(echo "$output" | tr '\n' ';')"
++ ocf_log err "Unexpected result for '$cmd' $rc: $(echo "$fping_output" | tr '\n' ';')"
+ ;;
+ esac
+
+@@ -282,17 +282,17 @@ ping_check() {
+ *:*) p_exe=ping6
+ esac
+
+- p_out=$($p_exe $p_args $OCF_RESKEY_options $host 2>&1); rc=$?
++ ping_output=$($p_exe $p_args $OCF_RESKEY_options $host 2>&1); rc=$?
+
+ case $rc in
+ 0)
+ active=$(expr $active + 1)
+ if [ $OCF_RESKEY_debug -gt 1 ]; then
+- ping_conditional_log info "$p_out"
++ ping_conditional_log info "$ping_output"
+ fi
+ ;;
+- 1) ping_conditional_log warn "$host is inactive: $p_out";;
+- *) ocf_log err "Unexpected result for '$p_exe $p_args $OCF_RESKEY_options $host' $rc: $p_out";;
++ 1) ping_conditional_log warn "$host is inactive: $ping_output";;
++ *) ocf_log err "Unexpected result for '$p_exe $p_args $OCF_RESKEY_options $host' $rc: $ping_output";;
+ esac
+ done
+ return $active
+--
+1.8.3.1
+
diff --git a/002-pacemakerd-options.patch b/002-pacemakerd-options.patch
new file mode 100644
index 0000000..56941ec
--- /dev/null
+++ b/002-pacemakerd-options.patch
@@ -0,0 +1,451 @@
+From 0d40ebf10b1794ece2c5c9768ea7222d3834d3b3 Mon Sep 17 00:00:00 2001
+From: Chris Lumens <clumens(a)redhat.com>
+Date: Thu, 13 May 2021 11:42:18 -0400
+Subject: [PATCH 1/4] Build: Use a different variable to find man page
+ includes.
+
+With other programs outside of the tools directory being converted to
+use glib for command line handling, their includes are not going to be
+in tools/. So we need to use a different autoconf variable to find
+them.
+---
+ mk/common.mk | 6 +++---
+ 1 file changed, 3 insertions(+), 3 deletions(-)
+
+diff --git a/mk/common.mk b/mk/common.mk
+index b247670..aa59feb 100644
+--- a/mk/common.mk
++++ b/mk/common.mk
+@@ -1,5 +1,5 @@
+ #
+-# Copyright 2014-2020 the Pacemaker project contributors
++# Copyright 2014-2021 the Pacemaker project contributors
+ #
+ # The version control history for this file may have further details.
+ #
+@@ -68,11 +68,11 @@ HELP2MAN_ARGS = -N --section 8 --name "Part of the Pacemaker cluster resource ma
+ # and all wrappers to C code.
+ %.8: % $(MAN8DEPS)
+ $(AM_V_at)chmod a+x $(abs_builddir)/$<
+- $(AM_V_MAN)if [ -f $(top_srcdir)/tools/$@.inc ]; then \
++ $(AM_V_MAN)if [ -f $(abs_srcdir)/$@.inc ]; then \
+ PATH=$(abs_builddir):$$PATH $(HELP2MAN) $(HELP2MAN_ARGS) \
+ -h --help-all \
+ --no-discard-stderr \
+- -i $(top_srcdir)/tools/$@.inc $(abs_builddir)/$< \
++ -i $(abs_srcdir)/$@.inc $(abs_builddir)/$< \
+ | sed -f $(top_srcdir)/tools/fix-manpages > $@ ; \
+ else \
+ PATH=$(abs_builddir):$$PATH $(HELP2MAN) $(HELP2MAN_ARGS) \
+--
+1.8.3.1
+
+
+From c7ab1d901bcbbf0137277e783e072777ca2f82d9 Mon Sep 17 00:00:00 2001
+From: Chris Lumens <clumens(a)redhat.com>
+Date: Thu, 13 May 2021 11:44:16 -0400
+Subject: [PATCH 2/4] Refactor: daemons: Remove the pid_file variable from
+ pacemakerd.
+
+It's never used anywhere.
+---
+ daemons/pacemakerd/pacemakerd.c | 3 ---
+ 1 file changed, 3 deletions(-)
+
+diff --git a/daemons/pacemakerd/pacemakerd.c b/daemons/pacemakerd/pacemakerd.c
+index 8ec9708..03d688e 100644
+--- a/daemons/pacemakerd/pacemakerd.c
++++ b/daemons/pacemakerd/pacemakerd.c
+@@ -27,8 +27,7 @@
+
+ static crm_trigger_t *shutdown_trigger = NULL;
+ static crm_trigger_t *startup_trigger = NULL;
+-static const char *pid_file = PCMK_RUN_DIR "/pacemaker.pid";
+
+ /* state we report when asked via pacemakerd-api status-ping */
+ static const char *pacemakerd_state = XML_PING_ATTR_PACEMAKERDSTATE_INIT;
+ static gboolean running_with_sbd = FALSE; /* local copy */
+@@ -224,7 +222,6 @@ main(int argc, char **argv)
+ /* Legacy */
+ break;
+ case 'p':
+- pid_file = optarg;
+ break;
+ case 's':
+ pcmk__set_env_option("node_start_state", "standby");
+--
+1.8.3.1
+
+
+From 98990eed9f6a5dbde7c8a5aa0783e93d5479295b Mon Sep 17 00:00:00 2001
+From: Chris Lumens <clumens(a)redhat.com>
+Date: Thu, 13 May 2021 13:14:38 -0400
+Subject: [PATCH 3/4] Refactor: daemons: Use glib for command line handling in
+ pacemakerd.
+
+---
+ daemons/pacemakerd/Makefile.am | 2 +
+ daemons/pacemakerd/pacemakerd.8.inc | 5 +
+ daemons/pacemakerd/pacemakerd.c | 195 ++++++++++++++++++------------------
+ 3 files changed, 102 insertions(+), 100 deletions(-)
+ create mode 100644 daemons/pacemakerd/pacemakerd.8.inc
+
+diff --git a/daemons/pacemakerd/Makefile.am b/daemons/pacemakerd/Makefile.am
+index cc657f5..84517a3 100644
+--- a/daemons/pacemakerd/Makefile.am
++++ b/daemons/pacemakerd/Makefile.am
+@@ -15,6 +15,8 @@ if BUILD_SYSTEMD
+ systemdsystemunit_DATA = pacemaker.service
+ endif
+
++EXTRA_DIST = pacemakerd.8.inc
++
+ ## SOURCES
+
+ noinst_HEADERS = pacemakerd.h
+diff --git a/daemons/pacemakerd/pacemakerd.8.inc b/daemons/pacemakerd/pacemakerd.8.inc
+new file mode 100644
+index 0000000..902af4e
+--- /dev/null
++++ b/daemons/pacemakerd/pacemakerd.8.inc
+@@ -0,0 +1,5 @@
++[synopsis]
++pacemakerd [options]
++
++/subsidiary Pacemaker daemons/
++.SH OPTIONS
+diff --git a/daemons/pacemakerd/pacemakerd.c b/daemons/pacemakerd/pacemakerd.c
+index 03d688e..ce194bf 100644
+--- a/daemons/pacemakerd/pacemakerd.c
++++ b/daemons/pacemakerd/pacemakerd.c
+@@ -23,12 +23,54 @@
+ #include <crm/msg_xml.h>
+ #include <crm/common/ipc_internal.h>
+ #include <crm/common/mainloop.h>
++#include <crm/common/cmdline_internal.h>
+ #include <crm/cluster/internal.h>
+ #include <crm/cluster.h>
+
+ #include <dirent.h>
+ #include <ctype.h>
+
++#define SUMMARY "pacemakerd - primary Pacemaker daemon that launches and monitors all subsidiary Pacemaker daemons"
++
++struct {
++ gboolean features;
++ gboolean foreground;
++ gboolean shutdown;
++ gboolean standby;
++} options;
++
++static gboolean
++pid_cb(const gchar *option_name, const gchar *optarg, gpointer data, GError **err) {
++ return TRUE;
++}
++
++static gboolean
++standby_cb(const gchar *option_name, const gchar *optarg, gpointer data, GError **err) {
++ options.standby = TRUE;
++ pcmk__set_env_option("node_start_state", "standby");
++ return TRUE;
++}
++
++static GOptionEntry entries[] = {
++ { "features", 'F', 0, G_OPTION_ARG_NONE, &options.features,
++ "Display full version and list of features Pacemaker was built with",
++ NULL },
++ { "foreground", 'f', 0, G_OPTION_ARG_NONE, &options.foreground,
++ "(Ignored) Pacemaker always runs in the foreground",
++ NULL },
++ { "pid-file", 'p', 0, G_OPTION_ARG_CALLBACK, pid_cb,
++ "(Ignored) Daemon pid file location",
++ "FILE" },
++ { "shutdown", 'S', 0, G_OPTION_ARG_NONE, &options.shutdown,
++ "Instruct Pacemaker to shutdown on this machine",
++ NULL },
++ { "standby", 's', G_OPTION_FLAG_NO_ARG, G_OPTION_ARG_CALLBACK, standby_cb,
++ "Start node in standby state",
++ NULL },
++
++ { NULL }
++};
++
+ static gboolean fatal_error = FALSE;
+ static GMainLoop *mainloop = NULL;
+ static bool global_keep_tracking = false;
+@@ -642,49 +685,6 @@ pcmk_sigquit(int nsig)
+ .connection_destroyed = pcmk_ipc_destroy
+ };
+
+-static pcmk__cli_option_t long_options[] = {
+- // long option, argument type, storage, short option, description, flags
+- {
+- "help", no_argument, NULL, '?',
+- "\tThis text", pcmk__option_default
+- },
+- {
+- "version", no_argument, NULL, '$',
+- "\tVersion information", pcmk__option_default
+- },
+- {
+- "verbose", no_argument, NULL, 'V',
+- "\tIncrease debug output", pcmk__option_default
+- },
+- {
+- "shutdown", no_argument, NULL, 'S',
+- "\tInstruct Pacemaker to shutdown on this machine", pcmk__option_default
+- },
+- {
+- "features", no_argument, NULL, 'F',
+- "\tDisplay full version and list of features Pacemaker was built with",
+- pcmk__option_default
+- },
+- {
+- "-spacer-", no_argument, NULL, '-',
+- "\nAdditional Options:", pcmk__option_default
+- },
+- {
+- "foreground", no_argument, NULL, 'f',
+- "\t(Ignored) Pacemaker always runs in the foreground",
+- pcmk__option_default
+- },
+- {
+- "pid-file", required_argument, NULL, 'p',
+- "\t(Ignored) Daemon pid file location", pcmk__option_default
+- },
+- {
+- "standby", no_argument, NULL, 's',
+- "\tStart node in standby state", pcmk__option_default
+- },
+- { 0, 0, 0, 0 }
+-};
+-
+ static void
+ mcp_chown(const char *path, uid_t uid, gid_t gid)
+ {
+@@ -1168,83 +1211,66 @@ request_shutdown(crm_ipc_t *ipc)
+ return status;
+ }
+
++static GOptionContext *
++build_arg_context(pcmk__common_args_t *args) {
++ GOptionContext *context = NULL;
++
++ context = pcmk__build_arg_context(args, NULL, NULL, NULL);
++ pcmk__add_main_args(context, entries);
++ return context;
++}
++
+ int
+ main(int argc, char **argv)
+ {
+- int flag;
+- int argerr = 0;
++ crm_exit_t exit_code = CRM_EX_OK;
++
++ GError *error = NULL;
++
++ pcmk__common_args_t *args = pcmk__new_common_args(SUMMARY);
++ gchar **processed_args = pcmk__cmdline_preproc(argv, "p");
++ GOptionContext *context = build_arg_context(args);
+
+- int option_index = 0;
+ bool old_instance_connected = false;
+- gboolean shutdown = FALSE;
+
+ crm_ipc_t *old_instance = NULL;
+ qb_ipcs_service_t *ipcs = NULL;
+
+ crm_log_preinit(NULL, argc, argv);
+- pcmk__set_cli_options(NULL, "[options]", long_options,
+- "primary Pacemaker daemon that launches and "
+- "monitors all subsidiary Pacemaker daemons");
+ mainloop_add_signal(SIGHUP, pcmk_ignore);
+ mainloop_add_signal(SIGQUIT, pcmk_sigquit);
+
+- while (1) {
+- flag = pcmk__next_cli_option(argc, argv, &option_index, NULL);
+- if (flag == -1)
+- break;
+-
+- switch (flag) {
+- case 'V':
+- crm_bump_log_level(argc, argv);
+- break;
+- case 'f':
+- /* Legacy */
+- break;
+- case 'p':
+- break;
+- case 's':
+- pcmk__set_env_option("node_start_state", "standby");
+- break;
+- case '$':
+- case '?':
+- pcmk__cli_help(flag, CRM_EX_OK);
+- break;
+- case 'S':
+- shutdown = TRUE;
+- break;
+- case 'F':
+- printf("Pacemaker %s (Build: %s)\n Supporting v%s: %s\n", PACEMAKER_VERSION, BUILD_VERSION,
+- CRM_FEATURE_SET, CRM_FEATURES);
+- crm_exit(CRM_EX_OK);
+- default:
+- printf("Argument code 0%o (%c) is not (?yet?) supported\n", flag, flag);
+- ++argerr;
+- break;
+- }
++ if (!g_option_context_parse_strv(context, &processed_args, &error)) {
++ exit_code = CRM_EX_USAGE;
++ goto done;
+ }
+
+- if (optind < argc) {
+- printf("non-option ARGV-elements: ");
+- while (optind < argc)
+- printf("%s ", argv[optind++]);
+- printf("\n");
+- }
+- if (argerr) {
+- pcmk__cli_help('?', CRM_EX_USAGE);
++ if (options.features) {
++ printf("Pacemaker %s (Build: %s)\n Supporting v%s: %s\n", PACEMAKER_VERSION, BUILD_VERSION,
++ CRM_FEATURE_SET, CRM_FEATURES);
++ exit_code = CRM_EX_OK;
++ goto done;
+ }
+
++ if (args->version) {
++ g_strfreev(processed_args);
++ pcmk__free_arg_context(context);
++ /* FIXME: When pacemakerd is converted to use formatted output, this can go. */
++ pcmk__cli_help('v', CRM_EX_USAGE);
++ }
+
+ setenv("LC_ALL", "C", 1);
+
+ pcmk__set_env_option("mcp", "true");
+
++ pcmk__cli_init_logging("pacemakerd", args->verbosity);
+ crm_log_init(NULL, LOG_INFO, TRUE, FALSE, argc, argv, FALSE);
+
+ crm_debug("Checking for existing Pacemaker instance");
+ old_instance = crm_ipc_new(CRM_SYSTEM_MCP, 0);
+ old_instance_connected = crm_ipc_connect(old_instance);
+
+- if (shutdown) {
++ if (options.shutdown) {
+ if (old_instance_connected) {
+ crm_exit(request_shutdown(old_instance));
+ } else {
+@@ -1253,22 +1279,25 @@ main(int argc, char **argv)
+ "Pacemaker instance: %s", strerror(errno));
+ crm_ipc_close(old_instance);
+ crm_ipc_destroy(old_instance);
+- crm_exit(CRM_EX_DISCONNECT);
++ exit_code = CRM_EX_DISCONNECT;
++ goto done;
+ }
+
+ } else if (old_instance_connected) {
+ crm_ipc_close(old_instance);
+ crm_ipc_destroy(old_instance);
+ crm_err("Aborting start-up because active Pacemaker instance found");
+- crm_exit(CRM_EX_FATAL);
++ exit_code = CRM_EX_FATAL;
++ goto done;
+ }
+
+ crm_ipc_close(old_instance);
+ crm_ipc_destroy(old_instance);
+
+ #ifdef SUPPORT_COROSYNC
+ if (mcp_read_config() == FALSE) {
+- crm_exit(CRM_EX_UNAVAILABLE);
++ exit_code = CRM_EX_UNAVAILABLE;
++ goto done;
+ }
+ #endif
+
+@@ -1292,7 +1321,8 @@ main(int argc, char **argv)
+ #ifdef SUPPORT_COROSYNC
+ /* Allows us to block shutdown */
+ if (!cluster_connect_cfg()) {
+- crm_exit(CRM_EX_PROTOCOL);
++ exit_code = CRM_EX_PROTOCOL;
++ goto done;
+ }
+ #endif
+
+@@ -1307,9 +1337,11 @@ main(int argc, char **argv)
+ case pcmk_rc_ok:
+ break;
+ case pcmk_rc_ipc_unauthorized:
+- crm_exit(CRM_EX_CANTCREAT);
++ exit_code = CRM_EX_CANTCREAT;
++ goto done;
+ default:
+- crm_exit(CRM_EX_FATAL);
++ exit_code = CRM_EX_FATAL;
++ goto done;
+ };
+
+ mainloop_add_signal(SIGTERM, pcmk_shutdown);
+@@ -1342,5 +1374,11 @@ main(int argc, char **argv)
+ #ifdef SUPPORT_COROSYNC
+ cluster_disconnect_cfg();
+ #endif
+- crm_exit(CRM_EX_OK);
++
++done:
++ g_strfreev(processed_args);
++ pcmk__free_arg_context(context);
++
++ pcmk__output_and_clear_error(error, NULL);
++ crm_exit(exit_code);
+ }
+--
+1.8.3.1
+
+
+From 8f7924fbb2a012bedcad59335b7bebc5020b26e3 Mon Sep 17 00:00:00 2001
+From: Chris Lumens <clumens(a)redhat.com>
+Date: Thu, 13 May 2021 13:27:13 -0400
+Subject: [PATCH 4/4] Low: pacemaker.service: Don't start pacemakerd with -f.
+
+This option is completely ignored by pacemakerd.
+---
+ daemons/pacemakerd/pacemaker.service.in | 2 +-
+ doc/sphinx/Clusters_from_Scratch/verification.rst | 2 +-
+ 2 files changed, 2 insertions(+), 2 deletions(-)
+
+diff --git a/daemons/pacemakerd/pacemaker.service.in b/daemons/pacemakerd/pacemaker.service.in
+index b128ddc..0363a22 100644
+--- a/daemons/pacemakerd/pacemaker.service.in
++++ b/daemons/pacemakerd/pacemaker.service.in
+@@ -44,7 +44,7 @@ EnvironmentFile=-@CONFIGDIR@/pacemaker
+ EnvironmentFile=-@CONFIGDIR@/sbd
+ SuccessExitStatus=100
+
+-ExecStart=@sbindir@/pacemakerd -f
++ExecStart=@sbindir@/pacemakerd
+
+ # Systemd v227 and above can limit the number of processes spawned by a
+ # service. That is a bad idea for an HA cluster resource manager, so disable it
+diff --git a/doc/sphinx/Clusters_from_Scratch/verification.rst b/doc/sphinx/Clusters_from_Scratch/verification.rst
+index 9d647f8..b7fa20e 100644
+--- a/doc/sphinx/Clusters_from_Scratch/verification.rst
++++ b/doc/sphinx/Clusters_from_Scratch/verification.rst
+@@ -103,7 +103,7 @@ the necessary processes are running:
+ 2 ? S 0:00 [kthreadd]
+ ...lots of processes...
+ 17121 ? SLsl 0:01 /usr/sbin/corosync -f
+- 17133 ? Ss 0:00 /usr/sbin/pacemakerd -f
++ 17133 ? Ss 0:00 /usr/sbin/pacemakerd
+ 17134 ? Ss 0:00 \_ /usr/libexec/pacemaker/pacemaker-based
+ 17135 ? Ss 0:00 \_ /usr/libexec/pacemaker/pacemaker-fenced
+ 17136 ? Ss 0:00 \_ /usr/libexec/pacemaker/pacemaker-execd
+--
+1.8.3.1
+
diff --git a/003-pacemakerd-output.patch b/003-pacemakerd-output.patch
new file mode 100644
index 0000000..167e22b
--- /dev/null
+++ b/003-pacemakerd-output.patch
@@ -0,0 +1,343 @@
+From 7c35387a9896cb968cf4087b5cbed94af44e1ea5 Mon Sep 17 00:00:00 2001
+From: Chris Lumens <clumens(a)redhat.com>
+Date: Fri, 14 May 2021 12:03:46 -0400
+Subject: [PATCH 1/5] Feature: daemons: Convert pacemakerd to formatted output.
+
+The main purpose of this is to finish getting pacemakerd moved off the
+existing command line handling code (pcmk__cli_help in particular) so
+that code can eventually be deprecated or removed. pacemakerd itself
+does fairly little printing.
+---
+ daemons/pacemakerd/pacemakerd.c | 58 ++++++++++++++++++++++++++++++-----------
+ 1 file changed, 43 insertions(+), 15 deletions(-)
+
+diff --git a/daemons/pacemakerd/pacemakerd.c b/daemons/pacemakerd/pacemakerd.c
+index ce194bf..bd59729 100644
+--- a/daemons/pacemakerd/pacemakerd.c
++++ b/daemons/pacemakerd/pacemakerd.c
+@@ -25,6 +25,7 @@
+ #include <crm/common/ipc_internal.h>
+ #include <crm/common/mainloop.h>
+ #include <crm/common/cmdline_internal.h>
++#include <crm/common/output_internal.h>
+ #include <crm/cluster/internal.h>
+ #include <crm/cluster.h>
+
+@@ -37,6 +38,14 @@ struct {
+ gboolean standby;
+ } options;
+
++static pcmk__output_t *out = NULL;
++
++static pcmk__supported_format_t formats[] = {
++ PCMK__SUPPORTED_FORMAT_NONE,
++ PCMK__SUPPORTED_FORMAT_TEXT,
++ { NULL, NULL, NULL }
++};
++
+ static gboolean
+ pid_cb(const gchar *option_name, const gchar *optarg, gpointer data, GError **err) {
+ return TRUE;
+@@ -1167,10 +1176,10 @@ pacemakerd_event_cb(pcmk_ipc_api_t *pacemakerd_api,
+ }
+
+ static GOptionContext *
+-build_arg_context(pcmk__common_args_t *args) {
++build_arg_context(pcmk__common_args_t *args, GOptionGroup **group) {
+ GOptionContext *context = NULL;
+
+- context = pcmk__build_arg_context(args, NULL, NULL, NULL);
++ context = pcmk__build_arg_context(args, "text", group, NULL);
+ pcmk__add_main_args(context, entries);
+ return context;
+ }
+@@ -1182,9 +1191,11 @@ main(int argc, char **argv)
+
+ GError *error = NULL;
+
++ int rc = pcmk_rc_ok;
++ GOptionGroup *output_group = NULL;
+ pcmk__common_args_t *args = pcmk__new_common_args(SUMMARY);
+ gchar **processed_args = pcmk__cmdline_preproc(argv, "p");
+- GOptionContext *context = build_arg_context(args);
++ GOptionContext *context = build_arg_context(args, &output_group);
+
+ bool old_instance_connected = false;
+
+@@ -1195,23 +1205,30 @@ main(int argc, char **argv)
+ mainloop_add_signal(SIGHUP, pcmk_ignore);
+ mainloop_add_signal(SIGQUIT, pcmk_sigquit);
+
++ pcmk__register_formats(output_group, formats);
+ if (!g_option_context_parse_strv(context, &processed_args, &error)) {
+ exit_code = CRM_EX_USAGE;
+ goto done;
+ }
+
++ rc = pcmk__output_new(&out, args->output_ty, args->output_dest, argv);
++ if (rc != pcmk_rc_ok) {
++ exit_code = CRM_EX_ERROR;
++ g_set_error(&error, PCMK__EXITC_ERROR, exit_code, "Error creating output format %s: %s",
++ args->output_ty, pcmk_rc_str(rc));
++ goto done;
++ }
++
+ if (options.features) {
+- printf("Pacemaker %s (Build: %s)\n Supporting v%s: %s\n", PACEMAKER_VERSION, BUILD_VERSION,
+- CRM_FEATURE_SET, CRM_FEATURES);
++ out->info(out, "Pacemaker %s (Build: %s)\n Supporting v%s: %s", PACEMAKER_VERSION,
++ BUILD_VERSION, CRM_FEATURE_SET, CRM_FEATURES);
+ exit_code = CRM_EX_OK;
+ goto done;
+ }
+
+ if (args->version) {
+- g_strfreev(processed_args);
+- pcmk__free_arg_context(context);
+- /* FIXME: When pacemakerd is converted to use formatted output, this can go. */
+- pcmk__cli_help('v', CRM_EX_USAGE);
++ out->version(out, false);
++ goto done;
+ }
+
+ setenv("LC_ALL", "C", 1);
+@@ -1248,6 +1265,13 @@ main(int argc, char **argv)
+ crm_ipc_close(old_instance);
+ crm_ipc_destroy(old_instance);
+
++ /* Don't allow any accidental output after this point. */
++ if (out != NULL) {
++ out->finish(out, exit_code, true, NULL);
++ pcmk__output_free(out);
++ out = NULL;
++ }
++
+ #ifdef SUPPORT_COROSYNC
+ if (mcp_read_config() == FALSE) {
+ exit_code = CRM_EX_UNAVAILABLE;
+@@ -1333,6 +1357,11 @@ done:
+ g_strfreev(processed_args);
+ pcmk__free_arg_context(context);
+
+- pcmk__output_and_clear_error(error, NULL);
++ pcmk__output_and_clear_error(error, out);
++
++ if (out != NULL) {
++ out->finish(out, exit_code, true, NULL);
++ pcmk__output_free(out);
++ }
+ crm_exit(exit_code);
+ }
+--
+1.8.3.1
+
+
+From 35e6da64381fcb092d81ce16835cc28670b077cb Mon Sep 17 00:00:00 2001
+From: Chris Lumens <clumens(a)redhat.com>
+Date: Mon, 17 May 2021 10:04:04 -0400
+Subject: [PATCH 2/5] Features: daemons: Output the pacemakerd feature list in
+ XML.
+
+---
+ daemons/pacemakerd/pacemakerd.c | 45 ++++++++++++++++++++++++++++++++++++++---
+ 1 file changed, 42 insertions(+), 3 deletions(-)
+
+diff --git a/daemons/pacemakerd/pacemakerd.c b/daemons/pacemakerd/pacemakerd.c
+index bd59729..93cf743 100644
+--- a/daemons/pacemakerd/pacemakerd.c
++++ b/daemons/pacemakerd/pacemakerd.c
+@@ -43,6 +43,42 @@ static pcmk__output_t *out = NULL;
+ static pcmk__supported_format_t formats[] = {
+ PCMK__SUPPORTED_FORMAT_NONE,
+ PCMK__SUPPORTED_FORMAT_TEXT,
++ PCMK__SUPPORTED_FORMAT_XML,
++ { NULL, NULL, NULL }
++};
++
++static int
++pacemakerd_features(pcmk__output_t *out, va_list args) {
++ out->info(out, "Pacemaker %s (Build: %s)\n Supporting v%s: %s", PACEMAKER_VERSION,
++ BUILD_VERSION, CRM_FEATURE_SET, CRM_FEATURES);
++ return pcmk_rc_ok;
++}
++
++static int
++pacemakerd_features_xml(pcmk__output_t *out, va_list args) {
++ gchar **feature_list = g_strsplit(CRM_FEATURES, " ", 0);
++
++ pcmk__output_xml_create_parent(out, "pacemakerd",
++ "version", PACEMAKER_VERSION,
++ "build", BUILD_VERSION,
++ "feature_set", CRM_FEATURE_SET,
++ NULL);
++ out->begin_list(out, NULL, NULL, "features");
++
++ for (char **s = feature_list; *s != NULL; s++) {
++ pcmk__output_create_xml_text_node(out, "feature", *s);
++ }
++
++ out->end_list(out);
++
++ g_strfreev(feature_list);
++ return pcmk_rc_ok;
++}
++
++static pcmk__message_entry_t fmt_functions[] = {
++ { "features", "default", pacemakerd_features },
++ { "features", "xml", pacemakerd_features_xml },
++
+ { NULL, NULL, NULL }
+ };
+
+@@ -200,7 +236,7 @@ static GOptionContext *
+ build_arg_context(pcmk__common_args_t *args, GOptionGroup **group) {
+ GOptionContext *context = NULL;
+
+- context = pcmk__build_arg_context(args, "text", group, NULL);
++ context = pcmk__build_arg_context(args, "text (default), xml", group, NULL);
+ pcmk__add_main_args(context, entries);
+ return context;
+ }
+@@ -241,9 +277,12 @@ main(int argc, char **argv)
+ goto done;
+ }
+
++ pcmk__force_args(context, &error, "%s --xml-simple-list", g_get_prgname());
++
++ pcmk__register_messages(out, fmt_functions);
++
+ if (options.features) {
+- out->info(out, "Pacemaker %s (Build: %s)\n Supporting v%s: %s", PACEMAKER_VERSION,
+- BUILD_VERSION, CRM_FEATURE_SET, CRM_FEATURES);
++ out->message(out, "features");
+ exit_code = CRM_EX_OK;
+ goto done;
+ }
+--
+1.8.3.1
+
+
+From 5b7f5eb35b025b59805cf3c7c3dcb6a3cf4b71b3 Mon Sep 17 00:00:00 2001
+From: Chris Lumens <clumens(a)redhat.com>
+Date: Mon, 17 May 2021 11:09:53 -0400
+Subject: [PATCH 3/5] Low: daemons: Conditionally enable logging in pacemakerd.
+
+If we're doing an interactive command-line call, use
+pcmk__cli_init_logging. At the moment, all command line calls except
+for --shutdown do their work before logging would even come up, so we
+really only need to do this for --shutdown.
+
+If we're doing a daemon call, use crm_log_init.
+---
+ daemons/pacemakerd/pacemakerd.c | 7 +++++--
+ 1 file changed, 5 insertions(+), 2 deletions(-)
+
+diff --git a/daemons/pacemakerd/pacemakerd.c b/daemons/pacemakerd/pacemakerd.c
+index 93cf743..c20bde7 100644
+--- a/daemons/pacemakerd/pacemakerd.c
++++ b/daemons/pacemakerd/pacemakerd.c
+@@ -296,8 +296,11 @@ main(int argc, char **argv)
+
+ pcmk__set_env_option("mcp", "true");
+
+- pcmk__cli_init_logging("pacemakerd", args->verbosity);
+- crm_log_init(NULL, LOG_INFO, TRUE, FALSE, argc, argv, FALSE);
++ if (options.shutdown) {
++ pcmk__cli_init_logging("pacemakerd", args->verbosity);
++ } else {
++ crm_log_init(NULL, LOG_INFO, TRUE, FALSE, argc, argv, FALSE);
++ }
+
+ crm_debug("Checking for existing Pacemaker instance");
+ old_instance = crm_ipc_new(CRM_SYSTEM_MCP, 0);
+--
+1.8.3.1
+
+
+From 2393362bb7489e86d937ed46a1c5cfb93d9bf3ab Mon Sep 17 00:00:00 2001
+From: Chris Lumens <clumens(a)redhat.com>
+Date: Mon, 17 May 2021 11:58:06 -0400
+Subject: [PATCH 4/5] Fix: include: Bump CRM_FEATURE_SET for new pacemakerd
+ args.
+
+---
+ include/crm/crm.h | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+diff --git a/include/crm/crm.h b/include/crm/crm.h
+index fdfc825..92a98fa 100644
+--- a/include/crm/crm.h
++++ b/include/crm/crm.h
+@@ -66,7 +66,7 @@ extern "C" {
+ * >=3.0.13: Fail counts include operation name and interval
+ * >=3.2.0: DC supports PCMK_LRM_OP_INVALID and PCMK_LRM_OP_NOT_CONNECTED
+ */
+-# define CRM_FEATURE_SET "3.10.0"
++# define CRM_FEATURE_SET "3.10.1"
+
+ /* Pacemaker's CPG protocols use fixed-width binary fields for the sender and
+ * recipient of a CPG message. This imposes an arbitrary limit on cluster node
+--
+1.8.3.1
+
+
+From 3ad8edbd91631b87ef5f53fa2d68f0c8bbb9ee2b Mon Sep 17 00:00:00 2001
+From: Chris Lumens <clumens(a)redhat.com>
+Date: Mon, 17 May 2021 11:57:09 -0400
+Subject: [PATCH 5/5] Feature: xml: Add schema for pacemakerd.
+
+---
+ xml/Makefile.am | 1 +
+ xml/api/pacemakerd-2.10.rng | 28 ++++++++++++++++++++++++++++
+ 2 files changed, 29 insertions(+)
+ create mode 100644 xml/api/pacemakerd-2.10.rng
+
+diff --git a/xml/Makefile.am b/xml/Makefile.am
+index 12a51c5..b9448d4 100644
+--- a/xml/Makefile.am
++++ b/xml/Makefile.am
+@@ -56,6 +56,7 @@ API_request_base = command-output \
+ crm_simulate \
+ crmadmin \
+ digests \
++ pacemakerd \
+ stonith_admin \
+ version
+
+diff --git a/xml/api/pacemakerd-2.10.rng b/xml/api/pacemakerd-2.10.rng
+new file mode 100644
+index 0000000..41a11e7
+--- /dev/null
++++ b/xml/api/pacemakerd-2.10.rng
+@@ -0,0 +1,28 @@
++<?xml version="1.0" encoding="UTF-8"?>
++<grammar xmlns="http://relaxng.org/ns/structure/1.0"
++ datatypeLibrary="http://www.w3.org/2001/XMLSchema-datatypes">
++
++ <start>
++ <ref name="element-pacemakerd"/>
++ </start>
++
++ <define name="element-pacemakerd">
++ <element name="pacemakerd">
++ <attribute name="version"> <text /> </attribute>
++ <attribute name="build"> <text /> </attribute>
++ <attribute name="feature_set"> <text /> </attribute>
++
++ <optional>
++ <ref name="feature-list" />
++ </optional>
++ </element>
++ </define>
++
++ <define name="feature-list">
++ <element name="features">
++ <oneOrMore>
++ <element name="feature"> <text/> </element>
++ </oneOrMore>
++ </element>
++ </define>
++</grammar>
+--
+1.8.3.1
+
diff --git a/004-check-level.patch b/004-check-level.patch
new file mode 100644
index 0000000..f2abb5f
--- /dev/null
+++ b/004-check-level.patch
@@ -0,0 +1,199 @@
+From 3905e7eac11298fc20efd567a773666f948edf61 Mon Sep 17 00:00:00 2001
+From: Chris Lumens <clumens(a)redhat.com>
+Date: Mon, 3 May 2021 11:19:04 -0400
+Subject: [PATCH 1/2] Feature: tools: Add OCF_CHECK_LEVEL to crm_resource
+ environment.
+
+If --validate= or --force-check= are given with a level, pass that along
+as OCF_CHECK_LEVEL. This argument is optional, and if no value is given
+then the environment variable will not be set and whatever's the default
+on the resource agent will be used.
+
+See: rhbz#1955792.
+---
+ tools/crm_resource.c | 29 +++++++++++++++++++++--------
+ tools/crm_resource.h | 4 ++--
+ tools/crm_resource_runtime.c | 13 ++++++++++---
+ 3 files changed, 33 insertions(+), 13 deletions(-)
+
+diff --git a/tools/crm_resource.c b/tools/crm_resource.c
+index 45db2b2..6ca96f8 100644
+--- a/tools/crm_resource.c
++++ b/tools/crm_resource.c
+@@ -100,6 +100,7 @@ struct {
+ int timeout_ms; // Parsed from --timeout value
+ char *agent_spec; // Standard and/or provider and/or agent
+ gchar *xml_file; // Value of (deprecated) --xml-file
++ int check_level; // Optional value of --validate or --force-check
+
+ // Resource configuration specified via command-line arguments
+ gboolean cmdline_config; // Resource configuration was via arguments
+@@ -113,6 +114,7 @@ struct {
+ GHashTable *override_params; // Resource parameter values that override config
+ } options = {
+ .attr_set_type = XML_TAG_ATTR_SETS,
++ .check_level = -1,
+ .cib_options = cib_sync_call,
+ .require_cib = TRUE,
+ .require_dataset = TRUE,
+@@ -402,14 +404,15 @@ static GOptionEntry query_entries[] = {
+ };
+
+ static GOptionEntry command_entries[] = {
+- { "validate", 0, G_OPTION_FLAG_NO_ARG, G_OPTION_ARG_CALLBACK,
++ { "validate", 0, G_OPTION_FLAG_OPTIONAL_ARG, G_OPTION_ARG_CALLBACK,
+ validate_or_force_cb,
+ "Validate resource configuration by calling agent's validate-all\n"
+ INDENT "action. The configuration may be specified either by giving an\n"
+ INDENT "existing resource name with -r, or by specifying --class,\n"
+ INDENT "--agent, and --provider arguments, along with any number of\n"
+- INDENT "--option arguments.",
+- NULL },
++ INDENT "--option arguments. An optional LEVEL argument can be given\n"
++ INDENT "to control the level of checking performed.",
++ "LEVEL" },
+ { "cleanup", 'C', G_OPTION_FLAG_NO_ARG, G_OPTION_ARG_CALLBACK, cleanup_refresh_cb,
+ "If resource has any past failures, clear its history and fail\n"
+ INDENT "count. Optionally filtered by --resource, --node, --operation\n"
+@@ -546,11 +549,12 @@ static GOptionEntry advanced_entries[] = {
+ INDENT "the cluster believes the resource is a clone instance already\n"
+ INDENT "running on the local node.",
+ NULL },
+- { "force-check", 0, G_OPTION_FLAG_NO_ARG, G_OPTION_ARG_CALLBACK,
++ { "force-check", 0, G_OPTION_FLAG_OPTIONAL_ARG, G_OPTION_ARG_CALLBACK,
+ validate_or_force_cb,
+ "(Advanced) Bypass the cluster and check the state of a resource on\n"
+- INDENT "the local node",
+- NULL },
++ INDENT "the local node. An optional LEVEL argument can be given\n"
++ INDENT "to control the level of checking performed.",
++ "LEVEL" },
+
+ { NULL }
+ };
+@@ -910,6 +914,15 @@ validate_or_force_cb(const gchar *option_name, const gchar *optarg,
+ if (options.override_params == NULL) {
+ options.override_params = pcmk__strkey_table(free, free);
+ }
++
++ if (optarg != NULL) {
++ if (pcmk__scan_min_int(optarg, &options.check_level, 0) != pcmk_rc_ok) {
++ g_set_error(error, G_OPTION_ERROR, CRM_EX_INVALID_PARAM,
++ "Invalid check level setting: %s", optarg);
++ return FALSE;
++ }
++ }
++
+ return TRUE;
+ }
+
+@@ -1826,12 +1839,12 @@ main(int argc, char **argv)
+ options.v_class, options.v_provider, options.v_agent,
+ "validate-all", options.cmdline_params,
+ options.override_params, options.timeout_ms,
+- args->verbosity, options.force);
++ args->verbosity, options.force, options.check_level);
+ } else {
+ exit_code = cli_resource_execute(rsc, options.rsc_id,
+ options.operation, options.override_params,
+ options.timeout_ms, cib_conn, data_set,
+- args->verbosity, options.force);
++ args->verbosity, options.force, options.check_level);
+ }
+ goto done;
+
+diff --git a/tools/crm_resource.h b/tools/crm_resource.h
+index 3560377..5ab10d6 100644
+--- a/tools/crm_resource.h
++++ b/tools/crm_resource.h
+@@ -88,11 +88,11 @@ crm_exit_t cli_resource_execute_from_params(pcmk__output_t *out, const char *rsc
+ const char *rsc_type, const char *rsc_action,
+ GHashTable *params, GHashTable *override_hash,
+ int timeout_ms, int resource_verbose,
+- gboolean force);
++ gboolean force, int check_level);
+ crm_exit_t cli_resource_execute(pe_resource_t *rsc, const char *requested_name,
+ const char *rsc_action, GHashTable *override_hash,
+ int timeout_ms, cib_t *cib, pe_working_set_t *data_set,
+- int resource_verbose, gboolean force);
++ int resource_verbose, gboolean force, int check_level);
+
+ int cli_resource_update_attribute(pe_resource_t *rsc, const char *requested_name,
+ const char *attr_set, const char *attr_set_type,
+diff --git a/tools/crm_resource_runtime.c b/tools/crm_resource_runtime.c
+index fe0ec98..bde83b6 100644
+--- a/tools/crm_resource_runtime.c
++++ b/tools/crm_resource_runtime.c
+@@ -1679,7 +1679,8 @@ cli_resource_execute_from_params(pcmk__output_t *out, const char *rsc_name,
+ const char *rsc_class, const char *rsc_prov,
+ const char *rsc_type, const char *action,
+ GHashTable *params, GHashTable *override_hash,
+- int timeout_ms, int resource_verbose, gboolean force)
++ int timeout_ms, int resource_verbose, gboolean force,
++ int check_level)
+ {
+ GHashTable *params_copy = NULL;
+ crm_exit_t exit_code = CRM_EX_OK;
+@@ -1703,6 +1704,12 @@ cli_resource_execute_from_params(pcmk__output_t *out, const char *rsc_name,
+ /* add crm_feature_set env needed by some resource agents */
+ g_hash_table_insert(params, strdup(XML_ATTR_CRM_VERSION), strdup(CRM_FEATURE_SET));
+
++ if (check_level >= 0) {
++ char *level = crm_strdup_printf("%d", check_level);
++ setenv("OCF_CHECK_LEVEL", level, 1);
++ free(level);
++ }
++
+ /* resources_action_create frees the params hash table it's passed, but we
+ * may need to reuse it in a second call to resources_action_create. Thus
+ * we'll make a copy here so that gets freed and the original remains for
+@@ -1790,7 +1797,7 @@ crm_exit_t
+ cli_resource_execute(pe_resource_t *rsc, const char *requested_name,
+ const char *rsc_action, GHashTable *override_hash,
+ int timeout_ms, cib_t * cib, pe_working_set_t *data_set,
+- int resource_verbose, gboolean force)
++ int resource_verbose, gboolean force, int check_level)
+ {
+ pcmk__output_t *out = data_set->priv;
+ crm_exit_t exit_code = CRM_EX_OK;
+@@ -1856,7 +1863,7 @@ cli_resource_execute(pe_resource_t *rsc, const char *requested_name,
+
+ exit_code = cli_resource_execute_from_params(out, rid, rclass, rprov, rtype, action,
+ params, override_hash, timeout_ms,
+- resource_verbose, force);
++ resource_verbose, force, check_level);
+ return exit_code;
+ }
+
+--
+1.8.3.1
+
+
+From d13ba4bd6defe0dd81fdf8ab39ae5b889513c0c0 Mon Sep 17 00:00:00 2001
+From: Chris Lumens <clumens(a)redhat.com>
+Date: Thu, 20 May 2021 10:59:23 -0400
+Subject: [PATCH 2/2] Fix: include: Bump feature set to 3.10.2.
+
+This is for the OCF_CHECK_LEVEL environment variable.
+
+See: rhbz#1955792.
+---
+ include/crm/crm.h | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+diff --git a/include/crm/crm.h b/include/crm/crm.h
+index 92a98fa..ee52c36 100644
+--- a/include/crm/crm.h
++++ b/include/crm/crm.h
+@@ -66,7 +66,7 @@ extern "C" {
+ * >=3.0.13: Fail counts include operation name and interval
+ * >=3.2.0: DC supports PCMK_LRM_OP_INVALID and PCMK_LRM_OP_NOT_CONNECTED
+ */
+-# define CRM_FEATURE_SET "3.10.1"
++# define CRM_FEATURE_SET "3.10.2"
+
+ /* Pacemaker's CPG protocols use fixed-width binary fields for the sender and
+ * recipient of a CPG message. This imposes an arbitrary limit on cluster node
+--
+1.8.3.1
+
diff --git a/005-crm_resource.patch b/005-crm_resource.patch
new file mode 100644
index 0000000..1683026
--- /dev/null
+++ b/005-crm_resource.patch
@@ -0,0 +1,866 @@
+From a5a507d4e1abf242903472719a19977811e6f164 Mon Sep 17 00:00:00 2001
+From: Chris Lumens <clumens(a)redhat.com>
+Date: Thu, 20 May 2021 11:59:36 -0400
+Subject: [PATCH 01/10] Feature: libcrmcommon: Add OCF_OUTPUT_FORMAT to
+ crm_resource environment.
+
+See: rhbz#1644628
+---
+ lib/common/output.c | 2 ++
+ 1 file changed, 2 insertions(+)
+
+diff --git a/lib/common/output.c b/lib/common/output.c
+index 6cb49b5..58872e0 100644
+--- a/lib/common/output.c
++++ b/lib/common/output.c
+@@ -71,6 +71,8 @@ pcmk__output_new(pcmk__output_t **out, const char *fmt_name, const char *filenam
+ return ENOMEM;
+ }
+
++ setenv("OCF_OUTPUT_FORMAT", (*out)->fmt_name, 1);
++
+ return pcmk_rc_ok;
+ }
+
+--
+1.8.3.1
+
+
+From acc6ecdbfb797d69794e68f75a734d6252434e01 Mon Sep 17 00:00:00 2001
+From: Chris Lumens <clumens(a)redhat.com>
+Date: Fri, 21 May 2021 14:20:30 -0400
+Subject: [PATCH 02/10] Feature: schemas: Copy crm_resource schema in
+ preparation for changes.
+
+See: rhbz#1644628
+---
+ xml/api/crm_resource-2.11.rng | 238 ++++++++++++++++++++++++++++++++++++++++++
+ 1 file changed, 238 insertions(+)
+ create mode 100644 xml/api/crm_resource-2.11.rng
+
+diff --git a/xml/api/crm_resource-2.11.rng b/xml/api/crm_resource-2.11.rng
+new file mode 100644
+index 0000000..8e386db
+--- /dev/null
++++ b/xml/api/crm_resource-2.11.rng
+@@ -0,0 +1,238 @@
++<?xml version="1.0" encoding="UTF-8"?>
++<grammar xmlns="http://relaxng.org/ns/structure/1.0"
++ datatypeLibrary="http://www.w3.org/2001/XMLSchema-datatypes">
++
++ <start>
++ <ref name="element-crm-resource"/>
++ </start>
++
++ <define name="element-crm-resource">
++ <choice>
++ <ref name="agents-list" />
++ <ref name="alternatives-list" />
++ <ref name="constraints-list" />
++ <externalRef href="generic-list-2.4.rng"/>
++ <element name="metadata"> <text/> </element>
++ <ref name="locate-list" />
++ <ref name="operations-list" />
++ <ref name="providers-list" />
++ <ref name="reasons-list" />
++ <ref name="resource-check" />
++ <ref name="resource-config" />
++ <ref name="resources-list" />
++ </choice>
++ </define>
++
++ <define name="agents-list">
++ <element name="agents">
++ <attribute name="standard"> <text/> </attribute>
++ <optional>
++ <attribute name="provider"> <text/> </attribute>
++ </optional>
++ <zeroOrMore>
++ <element name="agent"> <text/> </element>
++ </zeroOrMore>
++ </element>
++ </define>
++
++ <define name="alternatives-list">
++ <element name="providers">
++ <attribute name="for"> <text/> </attribute>
++ <zeroOrMore>
++ <element name="provider"> <text/> </element>
++ </zeroOrMore>
++ </element>
++ </define>
++
++ <define name="constraints-list">
++ <element name="constraints">
++ <interleave>
++ <zeroOrMore>
++ <ref name="rsc-location" />
++ </zeroOrMore>
++ <zeroOrMore>
++ <ref name="rsc-colocation" />
++ </zeroOrMore>
++ </interleave>
++ </element>
++ </define>
++
++ <define name="locate-list">
++ <element name="nodes">
++ <attribute name="resource"> <text/> </attribute>
++ <zeroOrMore>
++ <element name="node">
++ <optional>
++ <attribute name="state"><value>promoted</value></attribute>
++ </optional>
++ <text/>
++ </element>
++ </zeroOrMore>
++ </element>
++ </define>
++
++ <define name="rsc-location">
++ <element name="rsc_location">
++ <attribute name="node"> <text/> </attribute>
++ <attribute name="rsc"> <text/> </attribute>
++ <attribute name="id"> <text/> </attribute>
++ <externalRef href="../score.rng"/>
++ </element>
++ </define>
++
++ <define name="operations-list">
++ <element name="operations">
++ <oneOrMore>
++ <ref name="element-operation-list" />
++ </oneOrMore>
++ </element>
++ </define>
++
++ <define name="providers-list">
++ <element name="providers">
++ <attribute name="standard"> <value>ocf</value> </attribute>
++ <optional>
++ <attribute name="agent"> <text/> </attribute>
++ </optional>
++ <zeroOrMore>
++ <element name="provider"> <text/> </element>
++ </zeroOrMore>
++ </element>
++ </define>
++
++ <define name="reasons-list">
++ <choice>
++ <ref name="no-resource-or-uname"/>
++ <ref name="resource-and-uname"/>
++ <ref name="no-resource-but-uname"/>
++ <ref name="resource-but-no-uname"/>
++ </choice>
++ </define>
++
++ <define name="no-resource-or-uname">
++ <element name="reason">
++ <element name="resources">
++ <zeroOrMore>
++ <element name="resource">
++ <attribute name="id"> <text/> </attribute>
++ <attribute name="running"> <data type="boolean"/> </attribute>
++ <ref name="resource-check"/>
++ </element>
++ </zeroOrMore>
++ </element>
++ </element>
++ </define>
++
++ <define name="resource-and-uname">
++ <element name="reason">
++ <attribute name="running_on"> <text/> </attribute>
++ <ref name="resource-check"/>
++ </element>
++ </define>
++
++ <define name="no-resource-but-uname">
++ <element name="reason">
++ <element name="resources">
++ <zeroOrMore>
++ <element name="resource">
++ <attribute name="id"> <text/> </attribute>
++ <attribute name="running"> <data type="boolean"/> </attribute>
++ <attribute name="host"> <text/> </attribute>
++ <ref name="resource-check"/>
++ </element>
++ </zeroOrMore>
++ </element>
++ </element>
++ </define>
++
++ <define name="resource-but-no-uname">
++ <element name="reason">
++ <attribute name="running"> <data type="boolean"/> </attribute>
++ <ref name="resource-check"/>
++ </element>
++ </define>
++
++ <define name="resource-config">
++ <element name="resource_config">
++ <externalRef href="resources-2.4.rng" />
++ <element name="xml"> <text/> </element>
++ </element>
++ </define>
++
++ <define name="resource-check">
++ <element name="check">
++ <attribute name="id"> <text/> </attribute>
++ <optional>
++ <choice>
++ <attribute name="remain_stopped"><value>true</value></attribute>
++ <attribute name="promotable"><value>false</value></attribute>
++ </choice>
++ </optional>
++ <optional>
++ <attribute name="unmanaged"><value>true</value></attribute>
++ </optional>
++ <optional>
++ <attribute name="locked-to"> <text/> </attribute>
++ </optional>
++ </element>
++ </define>
++
++ <define name="resources-list">
++ <element name="resources">
++ <zeroOrMore>
++ <externalRef href="resources-2.4.rng" />
++ </zeroOrMore>
++ </element>
++ </define>
++
++ <define name="rsc-colocation">
++ <element name="rsc_colocation">
++ <attribute name="id"> <text/> </attribute>
++ <attribute name="rsc"> <text/> </attribute>
++ <attribute name="with-rsc"> <text/> </attribute>
++ <externalRef href="../score.rng"/>
++ <optional>
++ <attribute name="node-attribute"> <text/> </attribute>
++ </optional>
++ <optional>
++ <attribute name="rsc-role">
++ <ref name="attribute-roles"/>
++ </attribute>
++ </optional>
++ <optional>
++ <attribute name="with-rsc-role">
++ <ref name="attribute-roles"/>
++ </attribute>
++ </optional>
++ </element>
++ </define>
++
++ <define name="element-operation-list">
++ <element name="operation">
++ <optional>
++ <group>
++ <attribute name="rsc"> <text/> </attribute>
++ <attribute name="agent"> <text/> </attribute>
++ </group>
++ </optional>
++ <attribute name="op"> <text/> </attribute>
++ <attribute name="node"> <text/> </attribute>
++ <attribute name="call"> <data type="integer" /> </attribute>
++ <attribute name="rc"> <data type="nonNegativeInteger" /> </attribute>
++ <optional>
++ <attribute name="last-rc-change"> <text/> </attribute>
++ <attribute name="exec-time"> <data type="nonNegativeInteger" /> </attribute>
++ </optional>
++ <attribute name="status"> <text/> </attribute>
++ </element>
++ </define>
++
++ <define name="attribute-roles">
++ <choice>
++ <value>Stopped</value>
++ <value>Started</value>
++ <value>Master</value>
++ <value>Slave</value>
++ </choice>
++ </define>
++</grammar>
+--
+1.8.3.1
+
+
+From 1bbdf2149a111e9e19c388834f82001e0d31c427 Mon Sep 17 00:00:00 2001
+From: Chris Lumens <clumens(a)redhat.com>
+Date: Mon, 24 May 2021 12:23:55 -0400
+Subject: [PATCH 03/10] Feature: xml: Update the crm_resource schema for XML
+ output.
+
+See: rhbz#1644628
+---
+ xml/api/crm_resource-2.11.rng | 50 +++++++++++++++++++++++++++++++++++++++++++
+ 1 file changed, 50 insertions(+)
+
+diff --git a/xml/api/crm_resource-2.11.rng b/xml/api/crm_resource-2.11.rng
+index 8e386db..aaa54d6 100644
+--- a/xml/api/crm_resource-2.11.rng
++++ b/xml/api/crm_resource-2.11.rng
+@@ -20,6 +20,7 @@
+ <ref name="resource-check" />
+ <ref name="resource-config" />
+ <ref name="resources-list" />
++ <ref name="resource-agent-action" />
+ </choice>
+ </define>
+
+@@ -227,6 +228,55 @@
+ </element>
+ </define>
+
++ <define name="resource-agent-action">
++ <element name="resource-agent-action">
++ <attribute name="action"> <text/> </attribute>
++ <optional>
++ <attribute name="rsc"> <text/> </attribute>
++ </optional>
++ <attribute name="class"> <text/> </attribute>
++ <attribute name="type"> <text/> </attribute>
++ <optional>
++ <attribute name="provider"> <text/> </attribute>
++ </optional>
++ <optional>
++ <ref name="overrides-list"/>
++ </optional>
++ <ref name="agent-status"/>
++ <optional>
++ <choice>
++ <element name="command">
++ <text />
++ </element>
++ <externalRef href="command-output-1.0.rng"/>
++ </choice>
++ </optional>
++ </element>
++ </define>
++
++ <define name="overrides-list">
++ <element name="overrides">
++ <zeroOrMore>
++ <element name="override">
++ <optional>
++ <attribute name="rsc"> <text/> </attribute>
++ </optional>
++ <attribute name="name"> <text/> </attribute>
++ <attribute name="value"> <text/> </attribute>
++ </element>
++ </zeroOrMore>
++ </element>
++ </define>
++
++ <define name="agent-status">
++ <element name="agent-status">
++ <attribute name="code"> <data type="integer" /> </attribute>
++ <optional>
++ <attribute name="message"> <text/> </attribute>
++ </optional>
++ </element>
++ </define>
++
+ <define name="attribute-roles">
+ <choice>
+ <value>Stopped</value>
+--
+1.8.3.1
+
+
+From d89f5bc7fec856fdcd32fa14edbd0019507d5d15 Mon Sep 17 00:00:00 2001
+From: Chris Lumens <clumens(a)redhat.com>
+Date: Tue, 1 Jun 2021 15:26:58 -0400
+Subject: [PATCH 04/10] Low: libcrmcommon: Increase PCMK__API_VERSION for new
+ crm_resource output.
+
+See: rhbz#1644628
+---
+ include/crm/common/output_internal.h | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+diff --git a/include/crm/common/output_internal.h b/include/crm/common/output_internal.h
+index 10b315b..0436cde 100644
+--- a/include/crm/common/output_internal.h
++++ b/include/crm/common/output_internal.h
+@@ -27,7 +27,7 @@ extern "C" {
+ # include <glib.h>
+ # include <crm/common/results.h>
+
+-# define PCMK__API_VERSION "2.9"
++# define PCMK__API_VERSION "2.11"
+
+ #if defined(PCMK__WITH_ATTRIBUTE_OUTPUT_ARGS)
+ # define PCMK__OUTPUT_ARGS(ARGS...) __attribute__((output_args(ARGS)))
+--
+1.8.3.1
+
+
+From 30bd2ddf43ee2a911681e51f40ed9ba20ec250b0 Mon Sep 17 00:00:00 2001
+From: Chris Lumens <clumens(a)redhat.com>
+Date: Thu, 27 May 2021 13:57:12 -0400
+Subject: [PATCH 05/10] Low: tools: Pass NULL to
+ cli_resource_execute_from_params...
+
+if no resource name is given. This happens if we are validating based
+on the --class/--agent/--provider command line options instead.
+---
+ tools/crm_resource.c | 2 +-
+ tools/crm_resource_runtime.c | 8 ++++----
+ 2 files changed, 5 insertions(+), 5 deletions(-)
+
+diff --git a/tools/crm_resource.c b/tools/crm_resource.c
+index 24f1121..37a0bb0 100644
+--- a/tools/crm_resource.c
++++ b/tools/crm_resource.c
+@@ -1840,7 +1840,7 @@ main(int argc, char **argv)
+
+ case cmd_execute_agent:
+ if (options.cmdline_config) {
+- exit_code = cli_resource_execute_from_params(out, "test",
++ exit_code = cli_resource_execute_from_params(out, NULL,
+ options.v_class, options.v_provider, options.v_agent,
+ "validate-all", options.cmdline_params,
+ options.override_params, options.timeout_ms,
+diff --git a/tools/crm_resource_runtime.c b/tools/crm_resource_runtime.c
+index 48a4b40..ebf48bb 100644
+--- a/tools/crm_resource_runtime.c
++++ b/tools/crm_resource_runtime.c
+@@ -1717,14 +1717,14 @@ cli_resource_execute_from_params(pcmk__output_t *out, const char *rsc_name,
+ */
+ params_copy = pcmk__str_table_dup(params);
+
+- op = resources_action_create(rsc_name, rsc_class, rsc_prov, rsc_type, action, 0,
+- timeout_ms, params_copy, 0);
++ op = resources_action_create(rsc_name ? rsc_name : "test", rsc_class, rsc_prov,
++ rsc_type, action, 0, timeout_ms, params_copy, 0);
+ if (op == NULL) {
+ /* Re-run with stderr enabled so we can display a sane error message */
+ crm_enable_stderr(TRUE);
+ params_copy = pcmk__str_table_dup(params);
+- op = resources_action_create(rsc_name, rsc_class, rsc_prov, rsc_type, action, 0,
+- timeout_ms, params_copy, 0);
++ op = resources_action_create(rsc_name ? rsc_name : "test", rsc_class, rsc_prov,
++ rsc_type, action, 0, timeout_ms, params_copy, 0);
+
+ /* Callers of cli_resource_execute expect that the params hash table will
+ * be freed. That function uses this one, so for that reason and for
+--
+1.8.3.1
+
+
+From ee56efd53d14cfc4f902769540b72b3bb6096a73 Mon Sep 17 00:00:00 2001
+From: Chris Lumens <clumens(a)redhat.com>
+Date: Mon, 24 May 2021 12:08:52 -0400
+Subject: [PATCH 06/10] Feature: tools: Add an agent-status message for
+ crm_resource.
+
+This moves what was previously only done in an out->info call to its own
+output message, which means it will appear in XML output as well. Also,
+note that if --class/--agent/--provider are given, the resource name
+will be set to "test". In that case, do not display the resource name
+in the output.
+
+This message will be used for --validate and the --force-* command line
+options to crm_resource.
+
+See: rhbz#1644628
+---
+ tools/crm_resource_print.c | 53 ++++++++++++++++++++++++++++++++++++++++++++++
+ 1 file changed, 53 insertions(+)
+
+diff --git a/tools/crm_resource_print.c b/tools/crm_resource_print.c
+index 9d82cf8..88d5878 100644
+--- a/tools/crm_resource_print.c
++++ b/tools/crm_resource_print.c
+@@ -152,6 +152,57 @@ attribute_list_default(pcmk__output_t *out, va_list args) {
+ return pcmk_rc_ok;
+ }
+
++PCMK__OUTPUT_ARGS("agent-status", "int", "const char *", "const char *", "const char *",
++ "const char *", "const char *", "int")
++static int
++agent_status_default(pcmk__output_t *out, va_list args) {
++ int status = va_arg(args, int);
++ const char *action = va_arg(args, const char *);
++ const char *name = va_arg(args, const char *);
++ const char *class = va_arg(args, const char *);
++ const char *provider = va_arg(args, const char *);
++ const char *type = va_arg(args, const char *);
++ int rc = va_arg(args, int);
++
++ if (status == PCMK_LRM_OP_DONE) {
++ out->info(out, "Operation %s%s%s (%s%s%s:%s) returned: '%s' (%d)",
++ action, name ? " for " : "", name ? name : "",
++ class, provider ? ":" : "", provider ? provider : "", type,
++ services_ocf_exitcode_str(rc), rc);
++ } else {
++ out->err(out, "Operation %s%s%s (%s%s%s:%s) failed: '%s' (%d)",
++ action, name ? " for " : "", name ? name : "",
++ class, provider ? ":" : "", provider ? provider : "", type,
++ services_lrm_status_str(status), status);
++ }
++
++ return pcmk_rc_ok;
++}
++
++PCMK__OUTPUT_ARGS("agent-status", "int", "const char *", "const char *", "const char *",
++ "const char *", "const char *", "int")
++static int
++agent_status_xml(pcmk__output_t *out, va_list args) {
++ int status G_GNUC_UNUSED = va_arg(args, int);
++ const char *action G_GNUC_UNUSED = va_arg(args, const char *);
++ const char *name G_GNUC_UNUSED = va_arg(args, const char *);
++ const char *class G_GNUC_UNUSED = va_arg(args, const char *);
++ const char *provider G_GNUC_UNUSED = va_arg(args, const char *);
++ const char *type G_GNUC_UNUSED = va_arg(args, const char *);
++ int rc = va_arg(args, int);
++
++ char *status_str = pcmk__itoa(rc);
++
++ pcmk__output_create_xml_node(out, "agent-status",
++ "code", status_str,
++ "message", services_ocf_exitcode_str(rc),
++ NULL);
++
++ free(status_str);
++
++ return pcmk_rc_ok;
++}
++
+ PCMK__OUTPUT_ARGS("attribute-list", "pe_resource_t *", "char *", "GHashTable *")
+ static int
+ attribute_list_text(pcmk__output_t *out, va_list args) {
+@@ -562,6 +613,8 @@ resource_names(pcmk__output_t *out, va_list args) {
+ }
+
+ static pcmk__message_entry_t fmt_functions[] = {
++ { "agent-status", "default", agent_status_default },
++ { "agent-status", "xml", agent_status_xml },
+ { "attribute-list", "default", attribute_list_default },
+ { "attribute-list", "text", attribute_list_text },
+ { "property-list", "default", property_list_default },
+--
+1.8.3.1
+
+
+From 85cb6b6bff96b18c5174d11e4de4d49cbfb20bb7 Mon Sep 17 00:00:00 2001
+From: Chris Lumens <clumens(a)redhat.com>
+Date: Tue, 1 Jun 2021 14:47:30 -0400
+Subject: [PATCH 07/10] Feature: tools: Add an overridden params output
+ message.
+
+This also replaces what was previously being done in an out->info call
+with an output message. This means it shows up in XML output as well.
+Also, note that if --class/--agent/--provider are given, the resource
+name will be set to "test". In that case, do not display the resource
+name in the output.
+
+See: rhbz#1644628
+---
+ tools/crm_resource_print.c | 39 +++++++++++++++++++++++++++++++++++++++
+ 1 file changed, 39 insertions(+)
+
+diff --git a/tools/crm_resource_print.c b/tools/crm_resource_print.c
+index 88d5878..119d83f 100644
+--- a/tools/crm_resource_print.c
++++ b/tools/crm_resource_print.c
+@@ -224,6 +224,43 @@ attribute_list_text(pcmk__output_t *out, va_list args) {
+ return pcmk_rc_ok;
+ }
+
++PCMK__OUTPUT_ARGS("override", "const char *", "const char *", "const char *")
++static int
++override_default(pcmk__output_t *out, va_list args) {
++ const char *rsc_name = va_arg(args, const char *);
++ const char *name = va_arg(args, const char *);
++ const char *value = va_arg(args, const char *);
++
++ if (rsc_name == NULL) {
++ out->list_item(out, NULL, "Overriding the cluster configuration with '%s' = '%s'",
++ name, value);
++ } else {
++ out->list_item(out, NULL, "Overriding the cluster configuration for '%s' with '%s' = '%s'",
++ rsc_name, name, value);
++ }
++
++ return pcmk_rc_ok;
++}
++
++PCMK__OUTPUT_ARGS("override", "const char *", "const char *", "const char *")
++static int
++override_xml(pcmk__output_t *out, va_list args) {
++ const char *rsc_name = va_arg(args, const char *);
++ const char *name = va_arg(args, const char *);
++ const char *value = va_arg(args, const char *);
++
++ xmlNodePtr node = pcmk__output_create_xml_node(out, "override",
++ "name", name,
++ "value", value,
++ NULL);
++
++ if (rsc_name != NULL) {
++ crm_xml_add(node, "rsc", rsc_name);
++ }
++
++ return pcmk_rc_ok;
++}
++
+ PCMK__OUTPUT_ARGS("property-list", "pe_resource_t *", "char *")
+ static int
+ property_list_default(pcmk__output_t *out, va_list args) {
+@@ -617,6 +654,8 @@ static pcmk__message_entry_t fmt_functions[] = {
+ { "agent-status", "xml", agent_status_xml },
+ { "attribute-list", "default", attribute_list_default },
+ { "attribute-list", "text", attribute_list_text },
++ { "override", "default", override_default },
++ { "override", "xml", override_xml },
+ { "property-list", "default", property_list_default },
+ { "property-list", "text", property_list_text },
+ { "resource-check-list", "default", resource_check_list_default },
+--
+1.8.3.1
+
+
+From e5e24592c7c3231c619fb5253e7925ffbc634a99 Mon Sep 17 00:00:00 2001
+From: Chris Lumens <clumens(a)redhat.com>
+Date: Fri, 4 Jun 2021 10:24:51 -0400
+Subject: [PATCH 08/10] Low: tools: Use simple XML lists for resource actions
+ as well.
+
+See: rhbz#1644628
+---
+ tools/crm_resource.c | 1 +
+ 1 file changed, 1 insertion(+)
+
+diff --git a/tools/crm_resource.c b/tools/crm_resource.c
+index 37a0bb0..e957011 100644
+--- a/tools/crm_resource.c
++++ b/tools/crm_resource.c
+@@ -1643,6 +1643,7 @@ main(int argc, char **argv)
+ * saves from having to write custom messages to build the lists around all these things
+ */
+ switch (options.rsc_cmd) {
++ case cmd_execute_agent:
+ case cmd_list_resources:
+ case cmd_query_xml:
+ case cmd_query_raw_xml:
+--
+1.8.3.1
+
+
+From 3e75174d0bc31b261adb1994214a5878b79da85b Mon Sep 17 00:00:00 2001
+From: Chris Lumens <clumens(a)redhat.com>
+Date: Fri, 4 Jun 2021 10:30:10 -0400
+Subject: [PATCH 09/10] Feature: tools: Add an output message for resource
+ actions.
+
+This wraps up the override and agent-status messages into a single
+message, along with any stdout/stderr from the resource action. This
+message should be called after taking the action.
+
+This also implements handling XML output from resource actions. Check
+to see if the validate-all action returns XML. If so, output it as a
+CDATA block under a "command" element. If not, treat it as plain text
+and output it as stdout/stderr from a command.
+
+See: rhbz#1644628
+---
+ tools/crm_resource_print.c | 122 +++++++++++++++++++++++++++++++++++++++++++++
+ 1 file changed, 122 insertions(+)
+
+diff --git a/tools/crm_resource_print.c b/tools/crm_resource_print.c
+index 119d83f..19a366d 100644
+--- a/tools/crm_resource_print.c
++++ b/tools/crm_resource_print.c
+@@ -293,6 +293,126 @@ property_list_text(pcmk__output_t *out, va_list args) {
+ return pcmk_rc_ok;
+ }
+
++PCMK__OUTPUT_ARGS("resource-agent-action", "int", "const char *", "const char *",
++ "const char *", "const char *", "const char *", "GHashTable *",
++ "int", "int", "char *", "char *")
++static int
++resource_agent_action_default(pcmk__output_t *out, va_list args) {
++ int verbose = va_arg(args, int);
++
++ const char *class = va_arg(args, const char *);
++ const char *provider = va_arg(args, const char *);
++ const char *type = va_arg(args, const char *);
++ const char *rsc_name = va_arg(args, const char *);
++ const char *action = va_arg(args, const char *);
++ GHashTable *overrides = va_arg(args, GHashTable *);
++ int rc = va_arg(args, int);
++ int status = va_arg(args, int);
++ char *stdout_data = va_arg(args, char *);
++ char *stderr_data = va_arg(args, char *);
++
++ if (overrides) {
++ GHashTableIter iter;
++ char *name = NULL;
++ char *value = NULL;
++
++ out->begin_list(out, NULL, NULL, "overrides");
++
++ g_hash_table_iter_init(&iter, overrides);
++ while (g_hash_table_iter_next(&iter, (gpointer *) &name, (gpointer *) &value)) {
++ out->message(out, "override", rsc_name, name, value);
++ }
++
++ out->end_list(out);
++ }
++
++ out->message(out, "agent-status", status, action, rsc_name, class, provider,
++ type, rc);
++
++ /* hide output for validate-all if not in verbose */
++ if (verbose == 0 && pcmk__str_eq(action, "validate-all", pcmk__str_casei)) {
++ return pcmk_rc_ok;
++ }
++
++ if (stdout_data || stderr_data) {
++ xmlNodePtr doc = string2xml(stdout_data);
++
++ if (doc != NULL) {
++ out->output_xml(out, "command", stdout_data);
++ xmlFreeNode(doc);
++ } else {
++ out->subprocess_output(out, rc, stdout_data, stderr_data);
++ }
++ }
++
++ return pcmk_rc_ok;
++}
++
++PCMK__OUTPUT_ARGS("resource-agent-action", "int", "const char *", "const char *",
++ "const char *", "const char *", "const char *", "GHashTable *",
++ "int", "int", "char *", "char *")
++static int
++resource_agent_action_xml(pcmk__output_t *out, va_list args) {
++ int verbose G_GNUC_UNUSED = va_arg(args, int);
++
++ const char *class = va_arg(args, const char *);
++ const char *provider = va_arg(args, const char *);
++ const char *type = va_arg(args, const char *);
++ const char *rsc_name = va_arg(args, const char *);
++ const char *action = va_arg(args, const char *);
++ GHashTable *overrides = va_arg(args, GHashTable *);
++ int rc = va_arg(args, int);
++ int status = va_arg(args, int);
++ char *stdout_data = va_arg(args, char *);
++ char *stderr_data = va_arg(args, char *);
++
++ xmlNodePtr node = pcmk__output_xml_create_parent(out, "resource-agent-action",
++ "action", action,
++ "class", class,
++ "type", type,
++ NULL);
++
++ if (rsc_name) {
++ crm_xml_add(node, "rsc", rsc_name);
++ }
++
++ if (provider) {
++ crm_xml_add(node, "provider", provider);
++ }
++
++ if (overrides) {
++ GHashTableIter iter;
++ char *name = NULL;
++ char *value = NULL;
++
++ out->begin_list(out, NULL, NULL, "overrides");
++
++ g_hash_table_iter_init(&iter, overrides);
++ while (g_hash_table_iter_next(&iter, (gpointer *) &name, (gpointer *) &value)) {
++ out->message(out, "override", rsc_name, name, value);
++ }
++
++ out->end_list(out);
++ }
++
++ out->message(out, "agent-status", status, action, rsc_name, class, provider,
++ type, rc);
++
++ if (stdout_data || stderr_data) {
++ xmlNodePtr doc = string2xml(stdout_data);
++
++ if (doc != NULL) {
++ out->output_xml(out, "command", stdout_data);
++ xmlFreeNode(doc);
++ } else {
++ out->subprocess_output(out, rc, stdout_data, stderr_data);
++ }
++ }
++
++ pcmk__output_xml_pop_parent(out);
++ return pcmk_rc_ok;
++}
++
+ PCMK__OUTPUT_ARGS("resource-check-list", "resource_checks_t *")
+ static int
+ resource_check_list_default(pcmk__output_t *out, va_list args) {
+@@ -658,6 +778,8 @@ static pcmk__message_entry_t fmt_functions[] = {
+ { "override", "xml", override_xml },
+ { "property-list", "default", property_list_default },
+ { "property-list", "text", property_list_text },
++ { "resource-agent-action", "default", resource_agent_action_default },
++ { "resource-agent-action", "xml", resource_agent_action_xml },
+ { "resource-check-list", "default", resource_check_list_default },
+ { "resource-check-list", "xml", resource_check_list_xml },
+ { "resource-search-list", "default", resource_search_list_default },
+--
+1.8.3.1
+
+
+From b50b2418e1e997b42f5370b4672a3f105d74634f Mon Sep 17 00:00:00 2001
+From: Chris Lumens <clumens(a)redhat.com>
+Date: Fri, 4 Jun 2021 10:40:16 -0400
+Subject: [PATCH 10/10] Feature: tools: Use the new resource-agent-action
+ message.
+
+See: rhbz#1644628
+---
+ tools/crm_resource_runtime.c | 21 +++------------------
+ 1 file changed, 3 insertions(+), 18 deletions(-)
+
+diff --git a/tools/crm_resource_runtime.c b/tools/crm_resource_runtime.c
+index ebf48bb..755be9f 100644
+--- a/tools/crm_resource_runtime.c
++++ b/tools/crm_resource_runtime.c
+@@ -1765,28 +1765,13 @@ cli_resource_execute_from_params(pcmk__output_t *out, const char *rsc_name,
+ if (services_action_sync(op)) {
+ exit_code = op->rc;
+
+- if (op->status == PCMK_LRM_OP_DONE) {
+- out->info(out, "Operation %s for %s (%s:%s:%s) returned: '%s' (%d)",
+- action, rsc_name, rsc_class, rsc_prov ? rsc_prov : "", rsc_type,
+- services_ocf_exitcode_str(op->rc), op->rc);
+- } else {
+- out->err(out, "Operation %s for %s (%s:%s:%s) failed: '%s' (%d)",
+- action, rsc_name, rsc_class, rsc_prov ? rsc_prov : "", rsc_type,
+- services_lrm_status_str(op->status), op->status);
+- }
+-
+- /* hide output for validate-all if not in verbose */
+- if (resource_verbose == 0 && pcmk__str_eq(action, "validate-all", pcmk__str_casei))
+- goto done;
+-
+- if (op->stdout_data || op->stderr_data) {
+- out->subprocess_output(out, op->rc, op->stdout_data, op->stderr_data);
+- }
++ out->message(out, "resource-agent-action", resource_verbose, rsc_class,
++ rsc_prov, rsc_type, rsc_name, action, override_hash, op->rc,
++ op->status, op->stdout_data, op->stderr_data);
+ } else {
+ exit_code = op->rc == 0 ? CRM_EX_ERROR : op->rc;
+ }
+
+-done:
+ services_action_free(op);
+ /* See comment above about why we free params here. */
+ g_hash_table_destroy(params);
+--
+1.8.3.1
+
diff --git a/006-crm_simulate.patch b/006-crm_simulate.patch
new file mode 100644
index 0000000..c8d4e3f
--- /dev/null
+++ b/006-crm_simulate.patch
@@ -0,0 +1,896 @@
+From 97571e6ccc9b7fa339a7e27d9b0b9ab782ff3003 Mon Sep 17 00:00:00 2001
+From: Chris Lumens <clumens(a)redhat.com>
+Date: Wed, 16 Jun 2021 13:54:10 -0400
+Subject: [PATCH 1/5] Low: schemas: Copy crm_mon.rng in preparation for
+ changes.
+
+---
+ xml/api/crm_mon-2.12.rng | 243 +++++++++++++++++++++++++++++++++++++++++++++++
+ 1 file changed, 243 insertions(+)
+ create mode 100644 xml/api/crm_mon-2.12.rng
+
+diff --git a/xml/api/crm_mon-2.12.rng b/xml/api/crm_mon-2.12.rng
+new file mode 100644
+index 0000000..ffec923
+--- /dev/null
++++ b/xml/api/crm_mon-2.12.rng
+@@ -0,0 +1,243 @@
++<?xml version="1.0" encoding="UTF-8"?>
++<grammar xmlns="http://relaxng.org/ns/structure/1.0"
++ datatypeLibrary="http://www.w3.org/2001/XMLSchema-datatypes">
++
++ <start>
++ <ref name="element-crm-mon"/>
++ </start>
++
++ <define name="element-crm-mon">
++ <optional>
++ <ref name="element-summary" />
++ </optional>
++ <optional>
++ <ref name="nodes-list" />
++ </optional>
++ <optional>
++ <ref name="resources-list" />
++ </optional>
++ <optional>
++ <ref name="node-attributes-list" />
++ </optional>
++ <optional>
++ <ref name="node-history-list" />
++ </optional>
++ <optional>
++ <ref name="failures-list" />
++ </optional>
++ <optional>
++ <ref name="fence-event-list" />
++ </optional>
++ <optional>
++ <ref name="tickets-list" />
++ </optional>
++ <optional>
++ <ref name="bans-list" />
++ </optional>
++ </define>
++
++ <define name="element-summary">
++ <element name="summary">
++ <optional>
++ <element name="stack">
++ <attribute name="type"> <text /> </attribute>
++ </element>
++ </optional>
++ <optional>
++ <element name="current_dc">
++ <attribute name="present"> <data type="boolean" /> </attribute>
++ <optional>
++ <group>
++ <attribute name="version"> <text /> </attribute>
++ <attribute name="name"> <text /> </attribute>
++ <attribute name="id"> <text /> </attribute>
++ <attribute name="with_quorum"> <data type="boolean" /> </attribute>
++ </group>
++ </optional>
++ </element>
++ </optional>
++ <optional>
++ <element name="last_update">
++ <attribute name="time"> <text /> </attribute>
++ </element>
++ <element name="last_change">
++ <attribute name="time"> <text /> </attribute>
++ <attribute name="user"> <text /> </attribute>
++ <attribute name="client"> <text /> </attribute>
++ <attribute name="origin"> <text /> </attribute>
++ </element>
++ </optional>
++ <optional>
++ <element name="nodes_configured">
++ <attribute name="number"> <data type="nonNegativeInteger" /> </attribute>
++ </element>
++ <element name="resources_configured">
++ <attribute name="number"> <data type="nonNegativeInteger" /> </attribute>
++ <attribute name="disabled"> <data type="nonNegativeInteger" /> </attribute>
++ <attribute name="blocked"> <data type="nonNegativeInteger" /> </attribute>
++ </element>
++ </optional>
++ <optional>
++ <element name="cluster_options">
++ <attribute name="stonith-enabled"> <data type="boolean" /> </attribute>
++ <attribute name="symmetric-cluster"> <data type="boolean" /> </attribute>
++ <attribute name="no-quorum-policy"> <text /> </attribute>
++ <attribute name="maintenance-mode"> <data type="boolean" /> </attribute>
++ <attribute name="stop-all-resources"> <data type="boolean" /> </attribute>
++ </element>
++ </optional>
++ </element>
++ </define>
++
++ <define name="resources-list">
++ <element name="resources">
++ <zeroOrMore>
++ <externalRef href="resources-2.4.rng" />
++ </zeroOrMore>
++ </element>
++ </define>
++
++ <define name="nodes-list">
++ <element name="nodes">
++ <zeroOrMore>
++ <externalRef href="nodes-2.8.rng" />
++ </zeroOrMore>
++ </element>
++ </define>
++
++ <define name="node-attributes-list">
++ <element name="node_attributes">
++ <zeroOrMore>
++ <externalRef href="node-attrs-2.8.rng" />
++ </zeroOrMore>
++ </element>
++ </define>
++
++ <define name="node-history-list">
++ <element name="node_history">
++ <zeroOrMore>
++ <ref name="element-node-history" />
++ </zeroOrMore>
++ </element>
++ </define>
++
++ <define name="failures-list">
++ <element name="failures">
++ <zeroOrMore>
++ <externalRef href="failure-2.8.rng" />
++ </zeroOrMore>
++ </element>
++ </define>
++
++ <define name="fence-event-list">
++ <element name="fence_history">
++ <optional>
++ <attribute name="status"> <data type="integer" /> </attribute>
++ </optional>
++ <zeroOrMore>
++ <externalRef href="fence-event-2.0.rng" />
++ </zeroOrMore>
++ </element>
++ </define>
++
++ <define name="tickets-list">
++ <element name="tickets">
++ <zeroOrMore>
++ <ref name="element-ticket" />
++ </zeroOrMore>
++ </element>
++ </define>
++
++ <define name="bans-list">
++ <element name="bans">
++ <zeroOrMore>
++ <ref name="element-ban" />
++ </zeroOrMore>
++ </element>
++ </define>
++
++ <define name="element-node-history">
++ <element name="node">
++ <attribute name="name"> <text /> </attribute>
++ <zeroOrMore>
++ <ref name="element-resource-history" />
++ </zeroOrMore>
++ </element>
++ </define>
++
++ <define name="element-resource-history">
++ <element name="resource_history">
++ <attribute name="id"> <text /> </attribute>
++ <attribute name="orphan"> <data type="boolean" /> </attribute>
++ <optional>
++ <group>
++ <attribute name="migration-threshold"> <data type="nonNegativeInteger" /> </attribute>
++ <optional>
++ <attribute name="fail-count"> <text /> </attribute>
++ </optional>
++ <optional>
++ <attribute name="last-failure"> <text /> </attribute>
++ </optional>
++ </group>
++ </optional>
++ <zeroOrMore>
++ <ref name="element-operation-history" />
++ </zeroOrMore>
++ </element>
++ </define>
++
++ <define name="element-operation-history">
++ <element name="operation_history">
++ <attribute name="call"> <text /> </attribute>
++ <attribute name="task"> <text /> </attribute>
++ <optional>
++ <attribute name="interval"> <text /> </attribute>
++ </optional>
++ <optional>
++ <attribute name="last-rc-change"> <text /> </attribute>
++ </optional>
++ <optional>
++ <attribute name="last-run"> <text /> </attribute>
++ </optional>
++ <optional>
++ <attribute name="exec-time"> <text /> </attribute>
++ </optional>
++ <optional>
++ <attribute name="queue-time"> <text /> </attribute>
++ </optional>
++ <attribute name="rc"> <data type="integer" /> </attribute>
++ <attribute name="rc_text"> <text /> </attribute>
++ </element>
++ </define>
++
++ <define name="element-ticket">
++ <element name="ticket">
++ <attribute name="id"> <text /> </attribute>
++ <attribute name="status">
++ <choice>
++ <value>granted</value>
++ <value>revoked</value>
++ </choice>
++ </attribute>
++ <attribute name="standby"> <data type="boolean" /> </attribute>
++ <optional>
++ <attribute name="last-granted"> <text /> </attribute>
++ </optional>
++ </element>
++ </define>
++
++ <define name="element-ban">
++ <element name="ban">
++ <attribute name="id"> <text /> </attribute>
++ <attribute name="resource"> <text /> </attribute>
++ <attribute name="node"> <text /> </attribute>
++ <attribute name="weight"> <data type="integer" /> </attribute>
++ <attribute name="promoted-only"> <data type="boolean" /> </attribute>
++ <!-- DEPRECATED: master_only is a duplicate of promoted-only that is
++ provided solely for API backward compatibility. It will be
++ removed in a future release. Check promoted-only instead.
++ -->
++ <attribute name="master_only"> <data type="boolean" /> </attribute>
++ </element>
++ </define>
++</grammar>
+--
+1.8.3.1
+
+
+From da394983f106f974274ddd94675a04c85086010e Mon Sep 17 00:00:00 2001
+From: Chris Lumens <clumens(a)redhat.com>
+Date: Fri, 18 Jun 2021 15:06:34 -0400
+Subject: [PATCH 2/5] Refactor: Split node history out into its own XML schema.
+
+This allows for sharing it between crm_mon and crm_simulate.
+---
+ xml/Makefile.am | 2 +-
+ xml/api/crm_mon-2.12.rng | 64 +--------------------------------------
+ xml/api/node-history-2.12.rng | 70 +++++++++++++++++++++++++++++++++++++++++++
+ 3 files changed, 72 insertions(+), 64 deletions(-)
+ create mode 100644 xml/api/node-history-2.12.rng
+
+diff --git a/xml/Makefile.am b/xml/Makefile.am
+index b9448d4..8e7b6d3 100644
+--- a/xml/Makefile.am
++++ b/xml/Makefile.am
+@@ -64,7 +64,7 @@ API_request_base = command-output \
+ CIB_cfg_base = options nodes resources constraints fencing acls tags alerts
+
+ # Names of all schemas (including top level and those included by others)
+-API_base = $(API_request_base) fence-event failure generic-list item node-attrs nodes resources status
++API_base = $(API_request_base) fence-event failure generic-list item node-attrs node-history nodes resources status
+ CIB_base = cib $(CIB_cfg_base) status score rule nvset
+
+ # Static schema files and transforms (only CIB has transforms)
+diff --git a/xml/api/crm_mon-2.12.rng b/xml/api/crm_mon-2.12.rng
+index ffec923..be14412 100644
+--- a/xml/api/crm_mon-2.12.rng
++++ b/xml/api/crm_mon-2.12.rng
+@@ -20,7 +20,7 @@
+ <ref name="node-attributes-list" />
+ </optional>
+ <optional>
+- <ref name="node-history-list" />
++ <externalRef href="node-history-2.12.rng"/>
+ </optional>
+ <optional>
+ <ref name="failures-list" />
+@@ -113,14 +113,6 @@
+ </element>
+ </define>
+
+- <define name="node-history-list">
+- <element name="node_history">
+- <zeroOrMore>
+- <ref name="element-node-history" />
+- </zeroOrMore>
+- </element>
+- </define>
+-
+ <define name="failures-list">
+ <element name="failures">
+ <zeroOrMore>
+@@ -156,60 +148,6 @@
+ </element>
+ </define>
+
+- <define name="element-node-history">
+- <element name="node">
+- <attribute name="name"> <text /> </attribute>
+- <zeroOrMore>
+- <ref name="element-resource-history" />
+- </zeroOrMore>
+- </element>
+- </define>
+-
+- <define name="element-resource-history">
+- <element name="resource_history">
+- <attribute name="id"> <text /> </attribute>
+- <attribute name="orphan"> <data type="boolean" /> </attribute>
+- <optional>
+- <group>
+- <attribute name="migration-threshold"> <data type="nonNegativeInteger" /> </attribute>
+- <optional>
+- <attribute name="fail-count"> <text /> </attribute>
+- </optional>
+- <optional>
+- <attribute name="last-failure"> <text /> </attribute>
+- </optional>
+- </group>
+- </optional>
+- <zeroOrMore>
+- <ref name="element-operation-history" />
+- </zeroOrMore>
+- </element>
+- </define>
+-
+- <define name="element-operation-history">
+- <element name="operation_history">
+- <attribute name="call"> <text /> </attribute>
+- <attribute name="task"> <text /> </attribute>
+- <optional>
+- <attribute name="interval"> <text /> </attribute>
+- </optional>
+- <optional>
+- <attribute name="last-rc-change"> <text /> </attribute>
+- </optional>
+- <optional>
+- <attribute name="last-run"> <text /> </attribute>
+- </optional>
+- <optional>
+- <attribute name="exec-time"> <text /> </attribute>
+- </optional>
+- <optional>
+- <attribute name="queue-time"> <text /> </attribute>
+- </optional>
+- <attribute name="rc"> <data type="integer" /> </attribute>
+- <attribute name="rc_text"> <text /> </attribute>
+- </element>
+- </define>
+-
+ <define name="element-ticket">
+ <element name="ticket">
+ <attribute name="id"> <text /> </attribute>
+diff --git a/xml/api/node-history-2.12.rng b/xml/api/node-history-2.12.rng
+new file mode 100644
+index 0000000..9628000
+--- /dev/null
++++ b/xml/api/node-history-2.12.rng
+@@ -0,0 +1,70 @@
++<?xml version="1.0" encoding="UTF-8"?>
++<grammar xmlns="http://relaxng.org/ns/structure/1.0"
++ datatypeLibrary="http://www.w3.org/2001/XMLSchema-datatypes">
++
++ <start>
++ <ref name="node-history-list" />
++ </start>
++
++ <define name="node-history-list">
++ <element name="node_history">
++ <zeroOrMore>
++ <ref name="element-node-history" />
++ </zeroOrMore>
++ </element>
++ </define>
++
++ <define name="element-node-history">
++ <element name="node">
++ <attribute name="name"> <text /> </attribute>
++ <zeroOrMore>
++ <ref name="element-resource-history" />
++ </zeroOrMore>
++ </element>
++ </define>
++
++ <define name="element-resource-history">
++ <element name="resource_history">
++ <attribute name="id"> <text /> </attribute>
++ <attribute name="orphan"> <data type="boolean" /> </attribute>
++ <optional>
++ <group>
++ <attribute name="migration-threshold"> <data type="nonNegativeInteger" /> </attribute>
++ <optional>
++ <attribute name="fail-count"> <text /> </attribute>
++ </optional>
++ <optional>
++ <attribute name="last-failure"> <text /> </attribute>
++ </optional>
++ </group>
++ </optional>
++ <zeroOrMore>
++ <ref name="element-operation-history" />
++ </zeroOrMore>
++ </element>
++ </define>
++
++ <define name="element-operation-history">
++ <element name="operation_history">
++ <attribute name="call"> <text /> </attribute>
++ <attribute name="task"> <text /> </attribute>
++ <optional>
++ <attribute name="interval"> <text /> </attribute>
++ </optional>
++ <optional>
++ <attribute name="last-rc-change"> <text /> </attribute>
++ </optional>
++ <optional>
++ <attribute name="last-run"> <text /> </attribute>
++ </optional>
++ <optional>
++ <attribute name="exec-time"> <text /> </attribute>
++ </optional>
++ <optional>
++ <attribute name="queue-time"> <text /> </attribute>
++ </optional>
++ <attribute name="rc"> <data type="integer" /> </attribute>
++ <attribute name="rc_text"> <text /> </attribute>
++ </element>
++ </define>
++</grammar>
+--
+1.8.3.1
+
+
+From bf72b2615630eef7876e443d60b34d5a316de847 Mon Sep 17 00:00:00 2001
+From: Chris Lumens <clumens(a)redhat.com>
+Date: Wed, 16 Jun 2021 14:09:31 -0400
+Subject: [PATCH 3/5] Low: schemas: Copy crm_simulate.rng in preparation for
+ changes.
+
+---
+ xml/api/crm_simulate-2.12.rng | 335 ++++++++++++++++++++++++++++++++++++++++++
+ 1 file changed, 335 insertions(+)
+ create mode 100644 xml/api/crm_simulate-2.12.rng
+
+diff --git a/xml/api/crm_simulate-2.12.rng b/xml/api/crm_simulate-2.12.rng
+new file mode 100644
+index 0000000..9a7612d
+--- /dev/null
++++ b/xml/api/crm_simulate-2.12.rng
+@@ -0,0 +1,335 @@
++<?xml version="1.0" encoding="UTF-8"?>
++<grammar xmlns="http://relaxng.org/ns/structure/1.0"
++ datatypeLibrary="http://www.w3.org/2001/XMLSchema-datatypes">
++
++ <start>
++ <ref name="element-crm-simulate"/>
++ </start>
++
++ <define name="element-crm-simulate">
++ <choice>
++ <ref name="timings-list" />
++ <group>
++ <ref name="cluster-status" />
++ <optional>
++ <ref name="modifications-list" />
++ </optional>
++ <optional>
++ <ref name="allocations-utilizations-list" />
++ </optional>
++ <optional>
++ <ref name="action-list" />
++ </optional>
++ <optional>
++ <ref name="cluster-injected-actions-list" />
++ <ref name="revised-cluster-status" />
++ </optional>
++ </group>
++ </choice>
++ </define>
++
++ <define name="allocations-utilizations-list">
++ <choice>
++ <element name="allocations">
++ <zeroOrMore>
++ <choice>
++ <ref name="element-allocation" />
++ <ref name="element-promotion" />
++ </choice>
++ </zeroOrMore>
++ </element>
++ <element name="utilizations">
++ <zeroOrMore>
++ <choice>
++ <ref name="element-capacity" />
++ <ref name="element-utilization" />
++ </choice>
++ </zeroOrMore>
++ </element>
++ <element name="allocations_utilizations">
++ <zeroOrMore>
++ <choice>
++ <ref name="element-allocation" />
++ <ref name="element-promotion" />
++ <ref name="element-capacity" />
++ <ref name="element-utilization" />
++ </choice>
++ </zeroOrMore>
++ </element>
++ </choice>
++ </define>
++
++ <define name="cluster-status">
++ <element name="cluster_status">
++ <ref name="nodes-list" />
++ <ref name="resources-list" />
++ <optional>
++ <ref name="node-attributes-list" />
++ </optional>
++ <optional>
++ <ref name="failures-list" />
++ </optional>
++ </element>
++ </define>
++
++ <define name="modifications-list">
++ <element name="modifications">
++ <optional>
++ <attribute name="quorum"> <text /> </attribute>
++ </optional>
++ <optional>
++ <attribute name="watchdog"> <text /> </attribute>
++ </optional>
++ <zeroOrMore>
++ <ref name="element-inject-modify-node" />
++ </zeroOrMore>
++ <zeroOrMore>
++ <ref name="element-inject-modify-ticket" />
++ </zeroOrMore>
++ <zeroOrMore>
++ <ref name="element-inject-spec" />
++ </zeroOrMore>
++ <zeroOrMore>
++ <ref name="element-inject-attr" />
++ </zeroOrMore>
++ </element>
++ </define>
++
++ <define name="revised-cluster-status">
++ <element name="revised_cluster_status">
++ <ref name="nodes-list" />
++ <ref name="resources-list" />
++ <optional>
++ <ref name="node-attributes-list" />
++ </optional>
++ <optional>
++ <ref name="failures-list" />
++ </optional>
++ </element>
++ </define>
++
++ <define name="element-inject-attr">
++ <element name="inject_attr">
++ <attribute name="cib_node"> <text /> </attribute>
++ <attribute name="name"> <text /> </attribute>
++ <attribute name="node_path"> <text /> </attribute>
++ <attribute name="value"> <text /> </attribute>
++ </element>
++ </define>
++
++ <define name="element-inject-modify-node">
++ <element name="modify_node">
++ <attribute name="action"> <text /> </attribute>
++ <attribute name="node"> <text /> </attribute>
++ </element>
++ </define>
++
++ <define name="element-inject-spec">
++ <element name="inject_spec">
++ <attribute name="spec"> <text /> </attribute>
++ </element>
++ </define>
++
++ <define name="element-inject-modify-ticket">
++ <element name="modify_ticket">
++ <attribute name="action"> <text /> </attribute>
++ <attribute name="ticket"> <text /> </attribute>
++ </element>
++ </define>
++
++ <define name="cluster-injected-actions-list">
++ <element name="transition">
++ <zeroOrMore>
++ <ref name="element-injected-actions" />
++ </zeroOrMore>
++ </element>
++ </define>
++
++ <define name="node-attributes-list">
++ <element name="node_attributes">
++ <zeroOrMore>
++ <externalRef href="node-attrs-2.8.rng" />
++ </zeroOrMore>
++ </element>
++ </define>
++
++ <define name="failures-list">
++ <element name="failures">
++ <zeroOrMore>
++ <externalRef href="failure-2.8.rng" />
++ </zeroOrMore>
++ </element>
++ </define>
++
++ <define name="nodes-list">
++ <element name="nodes">
++ <zeroOrMore>
++ <externalRef href="nodes-2.8.rng" />
++ </zeroOrMore>
++ </element>
++ </define>
++
++ <define name="resources-list">
++ <element name="resources">
++ <zeroOrMore>
++ <externalRef href="resources-2.4.rng" />
++ </zeroOrMore>
++ </element>
++ </define>
++
++ <define name="timings-list">
++ <element name="timings">
++ <zeroOrMore>
++ <ref name="element-timing" />
++ </zeroOrMore>
++ </element>
++ </define>
++
++ <define name="action-list">
++ <element name="actions">
++ <zeroOrMore>
++ <ref name="element-node-action" />
++ </zeroOrMore>
++ <zeroOrMore>
++ <ref name="element-rsc-action" />
++ </zeroOrMore>
++ </element>
++ </define>
++
++ <define name="element-allocation">
++ <element name="node_weight">
++ <attribute name="function"> <text /> </attribute>
++ <attribute name="node"> <text /> </attribute>
++ <externalRef href="../score.rng" />
++ <optional>
++ <attribute name="id"> <text /> </attribute>
++ </optional>
++ </element>
++ </define>
++
++ <define name="element-capacity">
++ <element name="capacity">
++ <attribute name="comment"> <text /> </attribute>
++ <attribute name="node"> <text /> </attribute>
++ <zeroOrMore>
++ <element>
++ <anyName />
++ <text />
++ </element>
++ </zeroOrMore>
++ </element>
++ </define>
++
++ <define name="element-inject-cluster-action">
++ <element name="cluster_action">
++ <attribute name="node"> <text /> </attribute>
++ <attribute name="task"> <text /> </attribute>
++ <optional>
++ <attribute name="id"> <text /> </attribute>
++ </optional>
++ </element>
++ </define>
++
++ <define name="element-injected-actions">
++ <choice>
++ <ref name="element-inject-cluster-action" />
++ <ref name="element-inject-fencing-action" />
++ <ref name="element-inject-pseudo-action" />
++ <ref name="element-inject-rsc-action" />
++ </choice>
++ </define>
++
++ <define name="element-inject-fencing-action">
++ <element name="fencing_action">
++ <attribute name="op"> <text /> </attribute>
++ <attribute name="target"> <text /> </attribute>
++ </element>
++ </define>
++
++ <define name="element-node-action">
++ <element name="node_action">
++ <attribute name="node"> <text /> </attribute>
++ <attribute name="reason"> <text /> </attribute>
++ <attribute name="task"> <text /> </attribute>
++ </element>
++ </define>
++
++ <define name="element-promotion">
++ <element name="promotion_score">
++ <attribute name="id"> <text /> </attribute>
++ <externalRef href="../score.rng" />
++ <optional>
++ <attribute name="node"> <text /> </attribute>
++ </optional>
++ </element>
++ </define>
++
++ <define name="element-inject-pseudo-action">
++ <element name="pseudo_action">
++ <attribute name="task"> <text /> </attribute>
++ <optional>
++ <attribute name="node"> <text /> </attribute>
++ </optional>
++ </element>
++ </define>
++
++ <define name="element-inject-rsc-action">
++ <element name="rsc_action">
++ <attribute name="node"> <text /> </attribute>
++ <attribute name="op"> <text /> </attribute>
++ <attribute name="resource"> <text /> </attribute>
++ <optional>
++ <attribute name="interval"> <data type="integer" /> </attribute>
++ </optional>
++ </element>
++ </define>
++
++ <define name="element-timing">
++ <element name="timing">
++ <attribute name="file"> <text /> </attribute>
++ <attribute name="duration"> <data type="double" /> </attribute>
++ </element>
++ </define>
++
++ <define name="element-rsc-action">
++ <element name="rsc_action">
++ <attribute name="action"> <text /> </attribute>
++ <attribute name="resource"> <text /> </attribute>
++ <optional>
++ <attribute name="blocked"> <data type="boolean" /> </attribute>
++ </optional>
++ <optional>
++ <attribute name="dest"> <text /> </attribute>
++ </optional>
++ <optional>
++ <attribute name="next-role"> <text /> </attribute>
++ </optional>
++ <optional>
++ <attribute name="node"> <text /> </attribute>
++ </optional>
++ <optional>
++ <attribute name="reason"> <text /> </attribute>
++ </optional>
++ <optional>
++ <attribute name="role"> <text /> </attribute>
++ </optional>
++ <optional>
++ <attribute name="source"> <text /> </attribute>
++ </optional>
++ </element>
++ </define>
++
++ <define name="element-utilization">
++ <element name="utilization">
++ <attribute name="function"> <text /> </attribute>
++ <attribute name="node"> <text /> </attribute>
++ <attribute name="resource"> <text /> </attribute>
++ <zeroOrMore>
++ <element>
++ <anyName />
++ <text />
++ </element>
++ </zeroOrMore>
++ </element>
++ </define>
++</grammar>
+--
+1.8.3.1
+
+
+From c46e07788788acf5669e3f89b9344190a91c7331 Mon Sep 17 00:00:00 2001
+From: Chris Lumens <clumens(a)redhat.com>
+Date: Fri, 18 Jun 2021 15:10:19 -0400
+Subject: [PATCH 4/5] Feature: tools: Add the node-summary to crm_simulate
+ output.
+
+If --show-failcounts is given to crm_simulate, it should also display
+the node-summary message.
+
+See: rhbz#1686426
+---
+ tools/crm_simulate.c | 7 +++++--
+ xml/api/crm_simulate-2.12.rng | 3 +++
+ 2 files changed, 8 insertions(+), 2 deletions(-)
+
+diff --git a/tools/crm_simulate.c b/tools/crm_simulate.c
+index b4aa9d1..2ea292c 100644
+--- a/tools/crm_simulate.c
++++ b/tools/crm_simulate.c
+@@ -409,11 +409,14 @@ print_cluster_status(pe_working_set_t * data_set, unsigned int print_opts)
+ FALSE, FALSE, all, all, FALSE);
+
+ if (options.show_attrs) {
+- out->message(out, "node-attribute-list", data_set,
+- 0, rc == pcmk_rc_ok, FALSE, FALSE, FALSE, all, all);
++ rc = out->message(out, "node-attribute-list", data_set,
++ 0, rc == pcmk_rc_ok, FALSE, FALSE, FALSE, all, all);
+ }
+
+ if (options.show_failcounts) {
++ rc = out->message(out, "node-summary", data_set, all, all,
++ 0, print_opts, FALSE, FALSE, FALSE, FALSE, rc == pcmk_rc_ok);
++
+ out->message(out, "failed-action-list", data_set, all, all,
+ rc == pcmk_rc_ok);
+ }
+diff --git a/xml/api/crm_simulate-2.12.rng b/xml/api/crm_simulate-2.12.rng
+index 9a7612d..f90bd36 100644
+--- a/xml/api/crm_simulate-2.12.rng
++++ b/xml/api/crm_simulate-2.12.rng
+@@ -67,6 +67,9 @@
+ <ref name="node-attributes-list" />
+ </optional>
+ <optional>
++ <externalRef href="node-history-2.12.rng" />
++ </optional>
++ <optional>
+ <ref name="failures-list" />
+ </optional>
+ </element>
+--
+1.8.3.1
+
+
+From bac50336e0264604716e5997b87ee7e65311b982 Mon Sep 17 00:00:00 2001
+From: Chris Lumens <clumens(a)redhat.com>
+Date: Fri, 18 Jun 2021 15:21:52 -0400
+Subject: [PATCH 5/5] Low: libcrmcommon: Increase PCMK__API_VERSION for new
+ crm_resource output.
+
+See: rhbz#1686426
+---
+ include/crm/common/output_internal.h | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+diff --git a/include/crm/common/output_internal.h b/include/crm/common/output_internal.h
+index 0436cde..ba9c423 100644
+--- a/include/crm/common/output_internal.h
++++ b/include/crm/common/output_internal.h
+@@ -27,7 +27,7 @@ extern "C" {
+ # include <glib.h>
+ # include <crm/common/results.h>
+
+-# define PCMK__API_VERSION "2.11"
++# define PCMK__API_VERSION "2.12"
+
+ #if defined(PCMK__WITH_ATTRIBUTE_OUTPUT_ARGS)
+ # define PCMK__OUTPUT_ARGS(ARGS...) __attribute__((output_args(ARGS)))
+--
+1.8.3.1
+
diff --git a/007-unfencing-loop.patch b/007-unfencing-loop.patch
new file mode 100644
index 0000000..d4950c8
--- /dev/null
+++ b/007-unfencing-loop.patch
@@ -0,0 +1,733 @@
+From 6dcd6b51d7d3993bc483588d6ed75077518ed600 Mon Sep 17 00:00:00 2001
+From: Ken Gaillot <kgaillot(a)redhat.com>
+Date: Fri, 4 Jun 2021 16:30:55 -0500
+Subject: [PATCH 01/11] Low: controller: check whether unfenced node was remote
+ node
+
+... so the controller can indicate the node is remote (if known at that point,
+which is not guaranteed) when setting unfencing-related node attributes.
+---
+ daemons/controld/controld_fencing.c | 21 ++++++++++++++++++---
+ 1 file changed, 18 insertions(+), 3 deletions(-)
+
+diff --git a/daemons/controld/controld_fencing.c b/daemons/controld/controld_fencing.c
+index 23dff28..0fba661 100644
+--- a/daemons/controld/controld_fencing.c
++++ b/daemons/controld/controld_fencing.c
+@@ -757,15 +757,30 @@ tengine_stonith_callback(stonith_t *stonith, stonith_callback_data_t *data)
+ if (pcmk__str_eq("on", op, pcmk__str_casei)) {
+ const char *value = NULL;
+ char *now = pcmk__ttoa(time(NULL));
++ gboolean is_remote_node = FALSE;
++
++ /* This check is not 100% reliable, since this node is not
++ * guaranteed to have the remote node cached. However, it
++ * doesn't have to be reliable, since the attribute manager can
++ * learn a node's "remoteness" by other means sooner or later.
++ * This allows it to learn more quickly if this node does have
++ * the information.
++ */
++ if (g_hash_table_lookup(crm_remote_peer_cache, uuid) != NULL) {
++ is_remote_node = TRUE;
++ }
+
+- update_attrd(target, CRM_ATTR_UNFENCED, now, NULL, FALSE);
++ update_attrd(target, CRM_ATTR_UNFENCED, now, NULL,
++ is_remote_node);
+ free(now);
+
+ value = crm_meta_value(action->params, XML_OP_ATTR_DIGESTS_ALL);
+- update_attrd(target, CRM_ATTR_DIGESTS_ALL, value, NULL, FALSE);
++ update_attrd(target, CRM_ATTR_DIGESTS_ALL, value, NULL,
++ is_remote_node);
+
+ value = crm_meta_value(action->params, XML_OP_ATTR_DIGESTS_SECURE);
+- update_attrd(target, CRM_ATTR_DIGESTS_SECURE, value, NULL, FALSE);
++ update_attrd(target, CRM_ATTR_DIGESTS_SECURE, value, NULL,
++ is_remote_node);
+
+ } else if (action->sent_update == FALSE) {
+ send_stonith_update(action, target, uuid);
+--
+1.8.3.1
+
+
+From 3ef6d9403f68ab8559c45cc99f5a8da05ca6420b Mon Sep 17 00:00:00 2001
+From: Ken Gaillot <kgaillot(a)redhat.com>
+Date: Mon, 7 Jun 2021 10:50:36 -0500
+Subject: [PATCH 02/11] Refactor: pacemaker-attrd: functionize adding remote
+ node to cache
+
+... for future reuse
+---
+ daemons/attrd/attrd_commands.c | 34 +++++++++++++++++++++++-----------
+ 1 file changed, 23 insertions(+), 11 deletions(-)
+
+diff --git a/daemons/attrd/attrd_commands.c b/daemons/attrd/attrd_commands.c
+index 731c243..93a165b 100644
+--- a/daemons/attrd/attrd_commands.c
++++ b/daemons/attrd/attrd_commands.c
+@@ -102,6 +102,28 @@ free_attribute(gpointer data)
+ }
+ }
+
++/*!
++ * \internal
++ * \brief Ensure a Pacemaker Remote node is in the correct peer cache
++ *
++ * \param[in]
++ */
++static void
++cache_remote_node(const char *node_name)
++{
++ /* If we previously assumed this node was an unseen cluster node,
++ * remove its entry from the cluster peer cache.
++ */
++ crm_node_t *dup = pcmk__search_cluster_node_cache(0, node_name);
++
++ if (dup && (dup->uuid == NULL)) {
++ reap_crm_member(0, node_name);
++ }
++
++ // Ensure node is in the remote peer cache
++ CRM_ASSERT(crm_remote_peer_get(node_name) != NULL);
++}
++
+ static xmlNode *
+ build_attribute_xml(
+ xmlNode *parent, const char *name, const char *set, const char *uuid, unsigned int timeout_ms, const char *user,
+@@ -709,17 +731,7 @@ attrd_lookup_or_create_value(GHashTable *values, const char *host, xmlNode *xml)
+
+ crm_element_value_int(xml, PCMK__XA_ATTR_IS_REMOTE, &is_remote);
+ if (is_remote) {
+- /* If we previously assumed this node was an unseen cluster node,
+- * remove its entry from the cluster peer cache.
+- */
+- crm_node_t *dup = pcmk__search_cluster_node_cache(0, host);
+-
+- if (dup && (dup->uuid == NULL)) {
+- reap_crm_member(0, host);
+- }
+-
+- /* Ensure this host is in the remote peer cache */
+- CRM_ASSERT(crm_remote_peer_get(host) != NULL);
++ cache_remote_node(host);
+ }
+
+ if (v == NULL) {
+--
+1.8.3.1
+
+
+From 6fac2c71bc2c56870ac828d7cd7b7c799279c47e Mon Sep 17 00:00:00 2001
+From: Ken Gaillot <kgaillot(a)redhat.com>
+Date: Mon, 7 Jun 2021 10:39:34 -0500
+Subject: [PATCH 03/11] Refactor: pacemaker-attrd: don't try to remove votes
+ for remote nodes
+
+Remote nodes never vote.
+
+This has no effect in practice since the removal would simply do nothing,
+but we might as well not waste time trying.
+---
+ daemons/attrd/attrd_commands.c | 11 ++++++-----
+ 1 file changed, 6 insertions(+), 5 deletions(-)
+
+diff --git a/daemons/attrd/attrd_commands.c b/daemons/attrd/attrd_commands.c
+index 93a165b..dbe777e 100644
+--- a/daemons/attrd/attrd_commands.c
++++ b/daemons/attrd/attrd_commands.c
+@@ -976,7 +976,8 @@ attrd_election_cb(gpointer user_data)
+ void
+ attrd_peer_change_cb(enum crm_status_type kind, crm_node_t *peer, const void *data)
+ {
+- bool remove_voter = FALSE;
++ bool gone = false;
++ bool is_remote = pcmk_is_set(peer->flags, crm_remote_node);
+
+ switch (kind) {
+ case crm_status_uname:
+@@ -984,7 +985,7 @@ attrd_peer_change_cb(enum crm_status_type kind, crm_node_t *peer, const void *da
+
+ case crm_status_processes:
+ if (!pcmk_is_set(peer->processes, crm_get_cluster_proc())) {
+- remove_voter = TRUE;
++ gone = true;
+ }
+ break;
+
+@@ -1000,13 +1001,13 @@ attrd_peer_change_cb(enum crm_status_type kind, crm_node_t *peer, const void *da
+ } else {
+ // Remove all attribute values associated with lost nodes
+ attrd_peer_remove(peer->uname, FALSE, "loss");
+- remove_voter = TRUE;
++ gone = true;
+ }
+ break;
+ }
+
+- // In case an election is in progress, remove any vote by the node
+- if (remove_voter) {
++ // Remove votes from cluster nodes that leave, in case election in progress
++ if (gone && !is_remote) {
+ attrd_remove_voter(peer);
+ }
+ }
+--
+1.8.3.1
+
+
+From 54089fc663d6aaf10ca164c6c94b3b17237788de Mon Sep 17 00:00:00 2001
+From: Ken Gaillot <kgaillot(a)redhat.com>
+Date: Mon, 7 Jun 2021 10:40:06 -0500
+Subject: [PATCH 04/11] Low: pacemaker-attrd: check for remote nodes in peer
+ update callback
+
+If a remote node was started before the local cluster node joined the cluster,
+the cluster node will assume its node attributes are for a cluster node until
+it learns otherwise. Check for remoteness in the peer update callback, to have
+another way we can learn it.
+---
+ daemons/attrd/attrd_commands.c | 4 ++++
+ 1 file changed, 4 insertions(+)
+
+diff --git a/daemons/attrd/attrd_commands.c b/daemons/attrd/attrd_commands.c
+index dbe777e..5f6a754 100644
+--- a/daemons/attrd/attrd_commands.c
++++ b/daemons/attrd/attrd_commands.c
+@@ -1009,6 +1009,10 @@ attrd_peer_change_cb(enum crm_status_type kind, crm_node_t *peer, const void *da
+ // Remove votes from cluster nodes that leave, in case election in progress
+ if (gone && !is_remote) {
+ attrd_remove_voter(peer);
++
++ // Ensure remote nodes that come up are in the remote node cache
++ } else if (!gone && is_remote) {
++ cache_remote_node(peer->uname);
+ }
+ }
+
+--
+1.8.3.1
+
+
+From 8c048df0312d0d9c857d87b570a352429a710928 Mon Sep 17 00:00:00 2001
+From: Ken Gaillot <kgaillot(a)redhat.com>
+Date: Mon, 7 Jun 2021 11:29:12 -0500
+Subject: [PATCH 05/11] Log: pacemaker-attrd: log peer status changes
+
+---
+ daemons/attrd/attrd_commands.c | 9 +++++++++
+ 1 file changed, 9 insertions(+)
+
+diff --git a/daemons/attrd/attrd_commands.c b/daemons/attrd/attrd_commands.c
+index 5f6a754..d6d179b 100644
+--- a/daemons/attrd/attrd_commands.c
++++ b/daemons/attrd/attrd_commands.c
+@@ -972,6 +972,7 @@ attrd_election_cb(gpointer user_data)
+ return FALSE;
+ }
+
++#define state_text(state) ((state)? (const char *)(state) : "in unknown state")
+
+ void
+ attrd_peer_change_cb(enum crm_status_type kind, crm_node_t *peer, const void *data)
+@@ -981,15 +982,23 @@ attrd_peer_change_cb(enum crm_status_type kind, crm_node_t *peer, const void *da
+
+ switch (kind) {
+ case crm_status_uname:
++ crm_debug("%s node %s is now %s",
++ (is_remote? "Remote" : "Cluster"),
++ peer->uname, state_text(peer->state));
+ break;
+
+ case crm_status_processes:
+ if (!pcmk_is_set(peer->processes, crm_get_cluster_proc())) {
+ gone = true;
+ }
++ crm_debug("Node %s is %s a peer",
++ peer->uname, (gone? "no longer" : "now"));
+ break;
+
+ case crm_status_nstate:
++ crm_debug("%s node %s is now %s (was %s)",
++ (is_remote? "Remote" : "Cluster"),
++ peer->uname, state_text(peer->state), state_text(data));
+ if (pcmk__str_eq(peer->state, CRM_NODE_MEMBER, pcmk__str_casei)) {
+ /* If we're the writer, send new peers a list of all attributes
+ * (unless it's a remote node, which doesn't run its own attrd)
+--
+1.8.3.1
+
+
+From 1dcc8dee4990cf0dbdec0e14db6d9a3ad67a41d5 Mon Sep 17 00:00:00 2001
+From: Ken Gaillot <kgaillot(a)redhat.com>
+Date: Mon, 7 Jun 2021 11:13:53 -0500
+Subject: [PATCH 06/11] Low: pacemaker-attrd: ensure node ID is only set for
+ attributes when known
+
+In most cases, attribute updates contained the node ID, and the node ID was
+used by other code, only if known (i.e. positive). However a couple places did
+not check this, so add that.
+
+I am unsure whether the missing check caused problems in practice, but there
+appears to be the possibility that a remote node would wrongly be added to the
+cluster node cache.
+---
+ daemons/attrd/attrd_commands.c | 6 ++++--
+ 1 file changed, 4 insertions(+), 2 deletions(-)
+
+diff --git a/daemons/attrd/attrd_commands.c b/daemons/attrd/attrd_commands.c
+index d6d179b..b3f441c 100644
+--- a/daemons/attrd/attrd_commands.c
++++ b/daemons/attrd/attrd_commands.c
+@@ -136,7 +136,9 @@ build_attribute_xml(
+ crm_xml_add(xml, PCMK__XA_ATTR_UUID, uuid);
+ crm_xml_add(xml, PCMK__XA_ATTR_USER, user);
+ crm_xml_add(xml, PCMK__XA_ATTR_NODE_NAME, peer);
+- crm_xml_add_int(xml, PCMK__XA_ATTR_NODE_ID, peerid);
++ if (peerid > 0) {
++ crm_xml_add_int(xml, PCMK__XA_ATTR_NODE_ID, peerid);
++ }
+ crm_xml_add(xml, PCMK__XA_ATTR_VALUE, value);
+ crm_xml_add_int(xml, PCMK__XA_ATTR_DAMPENING, timeout_ms/1000);
+ crm_xml_add_int(xml, PCMK__XA_ATTR_IS_PRIVATE, is_private);
+@@ -937,7 +939,7 @@ attrd_peer_update(crm_node_t *peer, xmlNode *xml, const char *host, bool filter)
+ /* If this is a cluster node whose node ID we are learning, remember it */
+ if ((v->nodeid == 0) && (v->is_remote == FALSE)
+ && (crm_element_value_int(xml, PCMK__XA_ATTR_NODE_ID,
+- (int*)&v->nodeid) == 0)) {
++ (int*)&v->nodeid) == 0) && (v->nodeid > 0)) {
+
+ crm_node_t *known_peer = crm_get_peer(v->nodeid, host);
+
+--
+1.8.3.1
+
+
+From 8d12490e88b558d01db37a38f7d35175c6d2d69a Mon Sep 17 00:00:00 2001
+From: Ken Gaillot <kgaillot(a)redhat.com>
+Date: Thu, 10 Jun 2021 17:25:57 -0500
+Subject: [PATCH 07/11] Refactor: pacemaker-attrd: functionize processing a
+ sync response
+
+... for code isolation, and because we need to add more to it
+---
+ daemons/attrd/attrd_commands.c | 59 ++++++++++++++++++++++++++++--------------
+ 1 file changed, 39 insertions(+), 20 deletions(-)
+
+diff --git a/daemons/attrd/attrd_commands.c b/daemons/attrd/attrd_commands.c
+index b3f441c..d02d3e6 100644
+--- a/daemons/attrd/attrd_commands.c
++++ b/daemons/attrd/attrd_commands.c
+@@ -572,6 +572,43 @@ attrd_peer_clear_failure(crm_node_t *peer, xmlNode *xml)
+ }
+
+ /*!
++ * \internal
++ * \brief Load attributes from a peer sync response
++ *
++ * \param[in] peer Peer that sent clear request
++ * \param[in] peer_won Whether peer is the attribute writer
++ * \param[in] xml Request XML
++ */
++static void
++process_peer_sync_response(crm_node_t *peer, bool peer_won, xmlNode *xml)
++{
++ crm_info("Processing " PCMK__ATTRD_CMD_SYNC_RESPONSE " from %s",
++ peer->uname);
++
++ if (peer_won) {
++ /* Initialize the "seen" flag for all attributes to cleared, so we can
++ * detect attributes that local node has but the writer doesn't.
++ */
++ clear_attribute_value_seen();
++ }
++
++ // Process each attribute update in the sync response
++ for (xmlNode *child = pcmk__xml_first_child(xml); child != NULL;
++ child = pcmk__xml_next(child)) {
++ attrd_peer_update(peer, child,
++ crm_element_value(child, PCMK__XA_ATTR_NODE_NAME),
++ TRUE);
++ }
++
++ if (peer_won) {
++ /* If any attributes are still not marked as seen, the writer doesn't
++ * know about them, so send all peers an update with them.
++ */
++ attrd_current_only_attribute_update(peer, xml);
++ }
++}
++
++/*!
+ \internal
+ \brief Broadcast private attribute for local node with protocol version
+ */
+@@ -596,7 +633,7 @@ attrd_peer_message(crm_node_t *peer, xmlNode *xml)
+ const char *op = crm_element_value(xml, PCMK__XA_TASK);
+ const char *election_op = crm_element_value(xml, F_CRM_TASK);
+ const char *host = crm_element_value(xml, PCMK__XA_ATTR_NODE_NAME);
+- bool peer_won = FALSE;
++ bool peer_won = false;
+
+ if (election_op) {
+ attrd_handle_election_op(peer, xml);
+@@ -631,25 +668,7 @@ attrd_peer_message(crm_node_t *peer, xmlNode *xml)
+
+ } else if (pcmk__str_eq(op, PCMK__ATTRD_CMD_SYNC_RESPONSE, pcmk__str_casei)
+ && !pcmk__str_eq(peer->uname, attrd_cluster->uname, pcmk__str_casei)) {
+- xmlNode *child = NULL;
+-
+- crm_info("Processing %s from %s", op, peer->uname);
+-
+- /* Clear the seen flag for attribute processing held only in the own node. */
+- if (peer_won) {
+- clear_attribute_value_seen();
+- }
+-
+- for (child = pcmk__xml_first_child(xml); child != NULL;
+- child = pcmk__xml_next(child)) {
+- host = crm_element_value(child, PCMK__XA_ATTR_NODE_NAME);
+- attrd_peer_update(peer, child, host, TRUE);
+- }
+-
+- if (peer_won) {
+- /* Synchronize if there is an attribute held only by own node that Writer does not have. */
+- attrd_current_only_attribute_update(peer, xml);
+- }
++ process_peer_sync_response(peer, peer_won, xml);
+
+ } else if (pcmk__str_eq(op, PCMK__ATTRD_CMD_FLUSH, pcmk__str_casei)) {
+ /* Ignore. The flush command was removed in 2.0.0 but may be
+--
+1.8.3.1
+
+
+From a890a0e5bbbcabf907f51ed0460868035f72464d Mon Sep 17 00:00:00 2001
+From: Ken Gaillot <kgaillot(a)redhat.com>
+Date: Fri, 11 Jun 2021 14:40:39 -0500
+Subject: [PATCH 08/11] Refactor: pacemaker-attrd: functionize broadcasting
+ local override
+
+... for code isolation
+---
+ daemons/attrd/attrd_commands.c | 42 +++++++++++++++++++++++++++++-------------
+ 1 file changed, 29 insertions(+), 13 deletions(-)
+
+diff --git a/daemons/attrd/attrd_commands.c b/daemons/attrd/attrd_commands.c
+index d02d3e6..4783427 100644
+--- a/daemons/attrd/attrd_commands.c
++++ b/daemons/attrd/attrd_commands.c
+@@ -804,6 +804,34 @@ attrd_current_only_attribute_update(crm_node_t *peer, xmlNode *xml)
+ free_xml(sync);
+ }
+
++/*!
++ * \internal
++ * \brief Override an attribute sync with a local value
++ *
++ * Broadcast the local node's value for an attribute that's different from the
++ * value provided in a peer's attribute synchronization response. This ensures a
++ * node's values for itself take precedence and all peers are kept in sync.
++ *
++ * \param[in] a Attribute entry to override
++ *
++ * \return Local instance of attribute value
++ */
++static attribute_value_t *
++broadcast_local_value(attribute_t *a)
++{
++ attribute_value_t *v = g_hash_table_lookup(a->values, attrd_cluster->uname);
++ xmlNode *sync = create_xml_node(NULL, __func__);
++
++ crm_xml_add(sync, PCMK__XA_TASK, PCMK__ATTRD_CMD_SYNC_RESPONSE);
++ build_attribute_xml(sync, a->id, a->set, a->uuid, a->timeout_ms,
++ a->user, a->is_private, v->nodename, v->nodeid,
++ v->current, FALSE);
++ attrd_xml_add_writer(sync);
++ send_attrd_message(NULL, sync);
++ free_xml(sync);
++ return v;
++}
++
+ void
+ attrd_peer_update(crm_node_t *peer, xmlNode *xml, const char *host, bool filter)
+ {
+@@ -899,21 +927,9 @@ attrd_peer_update(crm_node_t *peer, xmlNode *xml, const char *host, bool filter)
+ if (filter && !pcmk__str_eq(v->current, value, pcmk__str_casei)
+ && pcmk__str_eq(host, attrd_cluster->uname, pcmk__str_casei)) {
+
+- xmlNode *sync = create_xml_node(NULL, __func__);
+-
+ crm_notice("%s[%s]: local value '%s' takes priority over '%s' from %s",
+ attr, host, v->current, value, peer->uname);
+-
+- crm_xml_add(sync, PCMK__XA_TASK, PCMK__ATTRD_CMD_SYNC_RESPONSE);
+- v = g_hash_table_lookup(a->values, host);
+- build_attribute_xml(sync, attr, a->set, a->uuid, a->timeout_ms, a->user,
+- a->is_private, v->nodename, v->nodeid, v->current, FALSE);
+-
+- attrd_xml_add_writer(sync);
+-
+- /* Broadcast in case any other nodes had the inconsistent value */
+- send_attrd_message(NULL, sync);
+- free_xml(sync);
++ v = broadcast_local_value(a);
+
+ } else if (!pcmk__str_eq(v->current, value, pcmk__str_casei)) {
+ crm_notice("Setting %s[%s]: %s -> %s " CRM_XS " from %s",
+--
+1.8.3.1
+
+
+From f6f65e3dab070f1bbdf6d1383f4d6173a8840bc9 Mon Sep 17 00:00:00 2001
+From: Ken Gaillot <kgaillot(a)redhat.com>
+Date: Fri, 11 Jun 2021 14:50:29 -0500
+Subject: [PATCH 09/11] Log: pacemaker-attrd: improve messages when
+ broadcasting local-only values
+
+The traces aren't necessary since build_attribute_xml() already logs the same
+info at debug. Also, rename function for clarity, and make static.
+---
+ daemons/attrd/attrd_commands.c | 35 ++++++++++++++++-------------------
+ 1 file changed, 16 insertions(+), 19 deletions(-)
+
+diff --git a/daemons/attrd/attrd_commands.c b/daemons/attrd/attrd_commands.c
+index 4783427..356defb 100644
+--- a/daemons/attrd/attrd_commands.c
++++ b/daemons/attrd/attrd_commands.c
+@@ -51,11 +51,12 @@ GHashTable *attributes = NULL;
+
+ void write_attribute(attribute_t *a, bool ignore_delay);
+ void write_or_elect_attribute(attribute_t *a);
+-void attrd_current_only_attribute_update(crm_node_t *peer, xmlNode *xml);
+ void attrd_peer_update(crm_node_t *peer, xmlNode *xml, const char *host, bool filter);
+ void attrd_peer_sync(crm_node_t *peer, xmlNode *xml);
+ void attrd_peer_remove(const char *host, gboolean uncache, const char *source);
+
++static void broadcast_unseen_local_values(crm_node_t *peer, xmlNode *xml);
++
+ static gboolean
+ send_attrd_message(crm_node_t * node, xmlNode * data)
+ {
+@@ -604,7 +605,7 @@ process_peer_sync_response(crm_node_t *peer, bool peer_won, xmlNode *xml)
+ /* If any attributes are still not marked as seen, the writer doesn't
+ * know about them, so send all peers an update with them.
+ */
+- attrd_current_only_attribute_update(peer, xml);
++ broadcast_unseen_local_values(peer, xml);
+ }
+ }
+
+@@ -768,40 +769,36 @@ attrd_lookup_or_create_value(GHashTable *values, const char *host, xmlNode *xml)
+ return(v);
+ }
+
+-void
+-attrd_current_only_attribute_update(crm_node_t *peer, xmlNode *xml)
++void
++broadcast_unseen_local_values(crm_node_t *peer, xmlNode *xml)
+ {
+ GHashTableIter aIter;
+ GHashTableIter vIter;
+- attribute_t *a;
++ attribute_t *a = NULL;
+ attribute_value_t *v = NULL;
+- xmlNode *sync = create_xml_node(NULL, __func__);
+- gboolean build = FALSE;
+-
+- crm_xml_add(sync, PCMK__XA_TASK, PCMK__ATTRD_CMD_SYNC_RESPONSE);
++ xmlNode *sync = NULL;
+
+ g_hash_table_iter_init(&aIter, attributes);
+ while (g_hash_table_iter_next(&aIter, NULL, (gpointer *) & a)) {
+ g_hash_table_iter_init(&vIter, a->values);
+ while (g_hash_table_iter_next(&vIter, NULL, (gpointer *) & v)) {
+- if (pcmk__str_eq(v->nodename, attrd_cluster->uname, pcmk__str_casei) && v->seen == FALSE) {
+- crm_trace("Syncing %s[%s] = %s to everyone.(from local only attributes)", a->id, v->nodename, v->current);
+-
+- build = TRUE;
++ if (!(v->seen) && pcmk__str_eq(v->nodename, attrd_cluster->uname,
++ pcmk__str_casei)) {
++ if (sync == NULL) {
++ sync = create_xml_node(NULL, __func__);
++ crm_xml_add(sync, PCMK__XA_TASK, PCMK__ATTRD_CMD_SYNC_RESPONSE);
++ }
+ build_attribute_xml(sync, a->id, a->set, a->uuid, a->timeout_ms, a->user, a->is_private,
+ v->nodename, v->nodeid, v->current, (a->timeout_ms && a->timer ? TRUE : FALSE));
+- } else {
+- crm_trace("Local attribute(%s[%s] = %s) was ignore.(another host) : [%s]", a->id, v->nodename, v->current, attrd_cluster->uname);
+- continue;
+ }
+ }
+ }
+
+- if (build) {
+- crm_debug("Syncing values to everyone.(from local only attributes)");
++ if (sync != NULL) {
++ crm_debug("Broadcasting local-only values");
+ send_attrd_message(NULL, sync);
++ free_xml(sync);
+ }
+- free_xml(sync);
+ }
+
+ /*!
+--
+1.8.3.1
+
+
+From ab90ffb785ea018556f216b8f540f8c3429a3947 Mon Sep 17 00:00:00 2001
+From: Ken Gaillot <kgaillot(a)redhat.com>
+Date: Fri, 11 Jun 2021 15:04:20 -0500
+Subject: [PATCH 10/11] Refactor: pacemaker-attrd: simplify attribute XML
+ creation function
+
+... and rename for clarity
+---
+ daemons/attrd/attrd_commands.c | 48 ++++++++++++++++++++++++------------------
+ 1 file changed, 27 insertions(+), 21 deletions(-)
+
+diff --git a/daemons/attrd/attrd_commands.c b/daemons/attrd/attrd_commands.c
+index 356defb..5b32a77 100644
+--- a/daemons/attrd/attrd_commands.c
++++ b/daemons/attrd/attrd_commands.c
+@@ -125,25 +125,35 @@ cache_remote_node(const char *node_name)
+ CRM_ASSERT(crm_remote_peer_get(node_name) != NULL);
+ }
+
++/*!
++ * \internal
++ * \brief Create an XML representation of an attribute for use in peer messages
++ *
++ * \param[in] parent Create attribute XML as child element of this element
++ * \param[in] a Attribute to represent
++ * \param[in] v Attribute value to represent
++ * \param[in] force_write If true, value should be written even if unchanged
++ *
++ * \return XML representation of attribute
++ */
+ static xmlNode *
+-build_attribute_xml(
+- xmlNode *parent, const char *name, const char *set, const char *uuid, unsigned int timeout_ms, const char *user,
+- gboolean is_private, const char *peer, uint32_t peerid, const char *value, gboolean is_force_write)
++add_attribute_value_xml(xmlNode *parent, attribute_t *a, attribute_value_t *v,
++ bool force_write)
+ {
+ xmlNode *xml = create_xml_node(parent, __func__);
+
+- crm_xml_add(xml, PCMK__XA_ATTR_NAME, name);
+- crm_xml_add(xml, PCMK__XA_ATTR_SET, set);
+- crm_xml_add(xml, PCMK__XA_ATTR_UUID, uuid);
+- crm_xml_add(xml, PCMK__XA_ATTR_USER, user);
+- crm_xml_add(xml, PCMK__XA_ATTR_NODE_NAME, peer);
+- if (peerid > 0) {
+- crm_xml_add_int(xml, PCMK__XA_ATTR_NODE_ID, peerid);
++ crm_xml_add(xml, PCMK__XA_ATTR_NAME, a->id);
++ crm_xml_add(xml, PCMK__XA_ATTR_SET, a->set);
++ crm_xml_add(xml, PCMK__XA_ATTR_UUID, a->uuid);
++ crm_xml_add(xml, PCMK__XA_ATTR_USER, a->user);
++ crm_xml_add(xml, PCMK__XA_ATTR_NODE_NAME, v->nodename);
++ if (v->nodeid > 0) {
++ crm_xml_add_int(xml, PCMK__XA_ATTR_NODE_ID, v->nodeid);
+ }
+- crm_xml_add(xml, PCMK__XA_ATTR_VALUE, value);
+- crm_xml_add_int(xml, PCMK__XA_ATTR_DAMPENING, timeout_ms/1000);
+- crm_xml_add_int(xml, PCMK__XA_ATTR_IS_PRIVATE, is_private);
+- crm_xml_add_int(xml, PCMK__XA_ATTR_FORCE, is_force_write);
++ crm_xml_add(xml, PCMK__XA_ATTR_VALUE, v->current);
++ crm_xml_add_int(xml, PCMK__XA_ATTR_DAMPENING, a->timeout_ms / 1000);
++ crm_xml_add_int(xml, PCMK__XA_ATTR_IS_PRIVATE, a->is_private);
++ crm_xml_add_int(xml, PCMK__XA_ATTR_FORCE, force_write);
+
+ return xml;
+ }
+@@ -695,8 +705,7 @@ attrd_peer_sync(crm_node_t *peer, xmlNode *xml)
+ g_hash_table_iter_init(&vIter, a->values);
+ while (g_hash_table_iter_next(&vIter, NULL, (gpointer *) & v)) {
+ crm_debug("Syncing %s[%s] = %s to %s", a->id, v->nodename, v->current, peer?peer->uname:"everyone");
+- build_attribute_xml(sync, a->id, a->set, a->uuid, a->timeout_ms, a->user, a->is_private,
+- v->nodename, v->nodeid, v->current, FALSE);
++ add_attribute_value_xml(sync, a, v, false);
+ }
+ }
+
+@@ -788,8 +797,7 @@ broadcast_unseen_local_values(crm_node_t *peer, xmlNode *xml)
+ sync = create_xml_node(NULL, __func__);
+ crm_xml_add(sync, PCMK__XA_TASK, PCMK__ATTRD_CMD_SYNC_RESPONSE);
+ }
+- build_attribute_xml(sync, a->id, a->set, a->uuid, a->timeout_ms, a->user, a->is_private,
+- v->nodename, v->nodeid, v->current, (a->timeout_ms && a->timer ? TRUE : FALSE));
++ add_attribute_value_xml(sync, a, v, a->timeout_ms && a->timer);
+ }
+ }
+ }
+@@ -820,9 +828,7 @@ broadcast_local_value(attribute_t *a)
+ xmlNode *sync = create_xml_node(NULL, __func__);
+
+ crm_xml_add(sync, PCMK__XA_TASK, PCMK__ATTRD_CMD_SYNC_RESPONSE);
+- build_attribute_xml(sync, a->id, a->set, a->uuid, a->timeout_ms,
+- a->user, a->is_private, v->nodename, v->nodeid,
+- v->current, FALSE);
++ add_attribute_value_xml(sync, a, v, false);
+ attrd_xml_add_writer(sync);
+ send_attrd_message(NULL, sync);
+ free_xml(sync);
+--
+1.8.3.1
+
+
+From 540d74130c5c8d9c626d6c50475e4dc4f64234e7 Mon Sep 17 00:00:00 2001
+From: Ken Gaillot <kgaillot(a)redhat.com>
+Date: Fri, 4 Jun 2021 16:34:26 -0500
+Subject: [PATCH 11/11] Fix: pacemaker-attrd: avoid repeated unfencing of
+ remote nodes
+
+The attribute manager can't record a remote node's attributes to the CIB until
+it knows the node is remote. Normally, this is learned when the remote node
+starts, because the controller clears the CRM_OP_PROBED attribute and indicates
+that it is for a remote node.
+
+However, if a cluster node is down when a remote node starts, and later comes
+up, it learns the remote node's existing attributes as part of the attribute
+sync. Previously, this did not include whether each value is for a cluster or
+remote node, so the newly joined attribute manager couldn't write out remote
+nodes' attributes until it learned that via some other event -- which might not
+happen before the node becomes DC, in which case its scheduler will not see any
+unfencing-related node attributes and may wrongly schedule unfencing.
+
+The sync response handling already calls attrd_lookup_or_create_value(), which
+checks PCMK__XA_ATTR_IS_REMOTE, so all we need to do is add that to the sync
+response.
+---
+ daemons/attrd/attrd_commands.c | 6 +++++-
+ 1 file changed, 5 insertions(+), 1 deletion(-)
+
+diff --git a/daemons/attrd/attrd_commands.c b/daemons/attrd/attrd_commands.c
+index 5b32a77..0142383 100644
+--- a/daemons/attrd/attrd_commands.c
++++ b/daemons/attrd/attrd_commands.c
+@@ -43,8 +43,9 @@
+ * 1 1.1.15 PCMK__ATTRD_CMD_UPDATE_BOTH,
+ * PCMK__ATTRD_CMD_UPDATE_DELAY
+ * 2 1.1.17 PCMK__ATTRD_CMD_CLEAR_FAILURE
++ * 3 2.1.1 PCMK__ATTRD_CMD_SYNC_RESPONSE indicates remote nodes
+ */
+-#define ATTRD_PROTOCOL_VERSION "2"
++#define ATTRD_PROTOCOL_VERSION "3"
+
+ int last_cib_op_done = 0;
+ GHashTable *attributes = NULL;
+@@ -150,6 +151,9 @@ add_attribute_value_xml(xmlNode *parent, attribute_t *a, attribute_value_t *v,
+ if (v->nodeid > 0) {
+ crm_xml_add_int(xml, PCMK__XA_ATTR_NODE_ID, v->nodeid);
+ }
++ if (v->is_remote != 0) {
++ crm_xml_add_int(xml, PCMK__XA_ATTR_IS_REMOTE, 1);
++ }
+ crm_xml_add(xml, PCMK__XA_ATTR_VALUE, v->current);
+ crm_xml_add_int(xml, PCMK__XA_ATTR_DAMPENING, a->timeout_ms / 1000);
+ crm_xml_add_int(xml, PCMK__XA_ATTR_IS_PRIVATE, a->is_private);
+--
+1.8.3.1
+
diff --git a/gating.yaml b/gating.yaml
index 9e15ced..e8aadb9 100644
--- a/gating.yaml
+++ b/gating.yaml
@@ -5,6 +5,7 @@ decision_context: bodhi_update_push_testing
subject_type: koji_build
rules:
- !PassingTestCaseRule {test_case_name: fedora-ci.koji-build.tier0.functional}
+
--- !Policy
product_versions:
- fedora-*
@@ -13,3 +14,9 @@ subject_type: koji_build
rules:
- !PassingTestCaseRule {test_case_name: fedora-ci.koji-build.tier0.functional}
+--- !Policy
+product_versions:
+ - rhel-*
+decision_context: osci_compose_gate
+rules:
+ - !PassingTestCaseRule {test_case_name: osci.brew-build.tier0.functional}
diff --git a/pacemaker.spec b/pacemaker.spec
index 1f9dce2..48b6a44 100644
--- a/pacemaker.spec
+++ b/pacemaker.spec
@@ -1,9 +1,9 @@
-# Globals and defines to control package behavior (configure these as desired)
+# User-configurable globals and defines to control package behavior
+# (these should not test {with X} values, which are declared later)
## User and group to use for nonprivileged services
%global uname hacluster
%global gname haclient
-%global hacluster_id 189
## Where to install Pacemaker documentation
%if 0%{?rhel}
@@ -17,6 +17,13 @@
## Where bug reports should be submitted
## Leave bug_url undefined to use ClusterLabs default, others define it here
+%if 0%{?rhel}
+%global bug_url https://bugzilla.redhat.com/
+%else
+%if 0%{?fedora}
+%global bug_url https://bugz.fedoraproject.org/%{name}
+%endif
+%endif
## What to use as the OCF resource agent root directory
%global ocf_root %{_prefix}/lib/ocf
@@ -25,19 +32,21 @@
## can be incremented to build packages reliably considered "newer"
## than previously built packages with the same pcmkversion)
%global pcmkversion 2.1.0
-%global specversion 4
+%global specversion 6
+
+## Upstream commit (full commit ID, abbreviated commit ID, or tag) to build
+%global commit 7c3f660707a495a1331716ad32cd3ac9d9f8ff58
-## Upstream commit (or git tag, such as "Pacemaker-" plus the
-## {pcmkversion} macro for an official release) to use for this package
-%global commit Pacemaker-2.1.0-rc3
## Since git v2.11, the extent of abbreviation is autoscaled by default
## (used to be constant of 7), so we need to convey it for non-tags, too.
%global commit_abbrev 9
+
# Define conditionals so that "rpmbuild --with <feature>" and
# "rpmbuild --without <feature>" can enable and disable specific features
-## NOTE: skip --with stonithd
+## Add option to enable support for stonith/external fencing agents
+%bcond_with stonithd
## Add option for whether to support storing sensitive information outside CIB
%if (0%{?fedora} && 0%{?fedora} <= 33) || (0%{?rhel} && 0%{?rhel} <= 8)
@@ -52,8 +61,13 @@
## Add option to create binaries with coverage analysis
%bcond_with coverage
-## Add option to skip/enable generating documentation
+## Add option to skip (or enable, on RHEL) generating documentation
+## (the build tools aren't available everywhere)
+%if 0%{?rhel}
+%bcond_with doc
+%else
%bcond_without doc
+%endif
## Add option to default to start-up synchronization with SBD.
##
@@ -66,16 +80,19 @@
## Add option to prefix package version with "0."
## (so later "official" packages will be considered updates)
-%bcond_without pre_release
+%bcond_with pre_release
## NOTE: skip --with upstart_job
## Add option to turn off hardening of libraries and daemon executables
%bcond_without hardening
-## Add option to enable links for legacy daemon names
+## Add option to enable (or disable, on RHEL 8) links for legacy daemon names
+%if 0%{?rhel} && 0%{?rhel} <= 8
+%bcond_without legacy_links
+%else
%bcond_with legacy_links
-
+%endif
## Nagios source control identifiers
%global nagios_name nagios-agents-metadata
@@ -94,29 +111,36 @@
## Portion of export/dist tarball name after "pacemaker-", and release version
%if 0%{tag_release}
-%define archive_version %{commit}
+%define archive_version %(c=%{commit}; echo ${c:10})
%define archive_github_url %{commit}#/%{name}-%{archive_version}.tar.gz
-%define pcmk_release %(c=%{commit}; case $c in *-rc[[:digit:]]*%{rparen}
- echo 0.%{specversion}.${c: -3} ;;
- *%{rparen} echo %{specversion} ;; esac)
%else
%define archive_version %(c=%{commit}; echo ${c:0:%{commit_abbrev}})
%define archive_github_url %{archive_version}#/%{name}-%{archive_version}.tar.gz
-%if %{with pre_release}
-%define pcmk_release 0.%{specversion}.%{archive_version}.git
-%else
-%define pcmk_release %{specversion}.%{archive_version}.git
-%endif
%endif
+### Always use a simple release number
+%define pcmk_release %{specversion}
## Base GnuTLS cipher priorities (presumably only the initial, required keyword)
## overridable with "rpmbuild --define 'pcmk_gnutls_priorities PRIORITY-SPEC'"
%define gnutls_priorities %{?pcmk_gnutls_priorities}%{!?pcmk_gnutls_priorities:@SYSTEM}
+## Different distros name certain packages differently
+## (note: corosync libraries also differ, but all provide corosync-devel)
+%global pkgname_libtool_devel libtool-ltdl-devel
+%global pkgname_libtool_devel_arch libtool-ltdl-devel%{?_isa}
+%global pkgname_bzip2_devel bzip2-devel
+%global pkgname_docbook_xsl docbook-style-xsl
+%global pkgname_gnutls_devel gnutls-devel
+%global pkgname_shadow_utils shadow-utils
+%global pkgname_procps procps-ng
+%global pkgname_glue_libs cluster-glue-libs
+%global pkgname_pcmk_libs %{name}-libs
+%global hacluster_id 189
+
## Distro-specific configuration choices
### Use 2.0-style output when other distro packages don't support current output
-%if 0%{?fedora}
+%if 0%{?fedora} || ( 0%{?rhel} && 0%{?rhel} <= 8 )
%global compat20 --enable-compat-2.0
%endif
@@ -130,6 +154,7 @@
%global resource_stickiness --with-resource-stickiness-default=1
%endif
+
# Python-related definitions
## Turn off auto-compilation of Python files outside Python specific paths,
@@ -145,13 +170,24 @@
sed -e 's!/usr/lib[^[:space:]]*/brp-python-bytecompile[[:space:]].*$!!g'; })
%endif
-## Values that differ by Python major version
+## Prefer Python 3 definitions explicitly, in case 2 is also available
+%if %{defined __python3}
%global python_name python3
-%global python_path %{?__python3}%{!?__python3:/usr/bin/python%{?python3_pkgversion}%{!?python3_pkgversion:3}}
+%global python_path %{__python3}
%define python_site %{?python3_sitelib}%{!?python3_sitelib:%(
%{python_path} -c 'from distutils.sysconfig import get_python_lib as gpl; print(gpl(1))' 2>/dev/null)}
-%global python_min 3.2
-# python_min still required?
+%else
+%if %{defined python_version}
+%global python_name python%(echo %{python_version} | cut -d'.' -f1)
+%define python_path %{?__python}%{!?__python:/usr/bin/%{python_name}}
+%else
+%global python_name python
+%global python_path %{?__python}%{!?__python:/usr/bin/python%{?python_pkgversion}}
+%endif
+%define python_site %{?python_sitelib}%{!?python_sitelib:%(
+ %{python_name} -c 'from distutils.sysconfig import get_python_lib as gpl; print(gpl(1))' 2>/dev/null)}
+%endif
+
# Keep sane profiling data if requested
%if %{with profiling}
@@ -165,25 +201,46 @@
Name: pacemaker
Summary: Scalable High-Availability cluster resource manager
Version: %{pcmkversion}
-Release: %{pcmk_release}%{?dist}.1
+Release: %{pcmk_release}%{?dist}
License: GPLv2+ and LGPLv2+
-Url: https://www.clusterlabs.org
-
-# Hint: use "spectool -s 0 pacemaker.spec" (rpmdevtools) to check the final URL
+Url: https://www.clusterlabs.org/
+
+# Example: https://codeload.github.com/ClusterLabs/pacemaker/tar.gz/e91769e
+# will download pacemaker-e91769e.tar.gz
+#
+# The ending part starting with '#' is ignored by github but necessary for
+# rpmbuild to know what the tar archive name is. (The downloaded file will be
+# named correctly only for commit IDs, not tagged releases.)
+#
+# You can use "spectool -s 0 pacemaker.spec" (rpmdevtools) to show final URL.
Source0: https://codeload.github.com/%{github_owner}/%{name}/tar.gz/%{archive_gith...
Source1: https://codeload.github.com/%{github_owner}/%{nagios_name}/tar.gz/%{nagio...
-# ---
+
+# upstream commits
+Patch1: 001-ping-agent.patch
+Patch2: 002-pacemakerd-options.patch
+Patch3: 003-pacemakerd-output.patch
+Patch4: 004-check-level.patch
+Patch5: 005-crm_resource.patch
+Patch6: 006-crm_simulate.patch
+Patch7: 007-unfencing-loop.patch
Requires: resource-agents
-Requires: %{name}-libs%{?_isa} = %{version}-%{release}
+Requires: %{pkgname_pcmk_libs}%{?_isa} = %{version}-%{release}
Requires: %{name}-cluster-libs%{?_isa} = %{version}-%{release}
Requires: %{name}-cli = %{version}-%{release}
%{?systemd_requires}
-# Pacemaker requires a minimum Python functionality
-Requires: %{python_name} >= %{python_min}
-BuildRequires: make
-BuildRequires: %{python_name}-devel >= %{python_min}
+%if %{defined centos}
+ExclusiveArch: aarch64 i686 ppc64le s390x x86_64 %{arm}
+%else
+%if 0%{?rhel}
+ExclusiveArch: aarch64 i686 ppc64le s390x x86_64
+%endif
+%endif
+
+Requires: %{python_path}
+BuildRequires: %{python_name}-devel
# Pacemaker requires a minimum libqb functionality
Requires: libqb >= 0.17.0
@@ -192,37 +249,41 @@ BuildRequires: libqb-devel >= 0.17.0
# Required basic build tools
BuildRequires: coreutils findutils grep sed
BuildRequires: autoconf automake gcc make pkgconfig
-BuildRequires: libtool libtool-ltdl-devel
+BuildRequires: libtool %{?pkgname_libtool_devel}
# Required for core functionality
BuildRequires: pkgconfig(glib-2.0) >= 2.42
BuildRequires: libxml2-devel libxslt-devel libuuid-devel
-BuildRequires: bzip2-devel
+BuildRequires: %{pkgname_bzip2_devel}
# Enables optional functionality
-BuildRequires: ncurses-devel docbook-style-xsl
-BuildRequires: help2man gnutls-devel pam-devel pkgconfig(dbus-1)
+BuildRequires: ncurses-devel %{pkgname_docbook_xsl}
+BuildRequires: help2man %{pkgname_gnutls_devel} pam-devel pkgconfig(dbus-1)
BuildRequires: pkgconfig(systemd)
+# RH patches are created by git, so we need git to apply them
+BuildRequires: git
+
Requires: corosync >= 2.0.0
BuildRequires: corosync-devel >= 2.0.0
-#XXX
-#BuildRequires: pkgconfig(libcpg)
-#BuildRequires: pkgconfig(libcfg)
+
+%if %{with stonithd}
+BuildRequires: %{pkgname_glue_libs}-devel
+%endif
%if %{with doc}
BuildRequires: asciidoc inkscape %{python_name}-sphinx
%endif
-# git-style patch application
-#BuildRequires: git
-
Provides: pcmk-cluster-manager = %{version}-%{release}
Provides: pcmk-cluster-manager%{?_isa} = %{version}-%{release}
-# Pacemaker uses the crypto/md5 module from gnulib
+# Bundled bits
+## Pacemaker uses the crypto/md5-buffer module from gnulib
+%if 0%{?fedora} || 0%{?rhel}
Provides: bundled(gnulib)
+%endif
%description
Pacemaker is an advanced, scalable High-Availability cluster resource
@@ -236,18 +297,18 @@ when related resources fail and can be configured to periodically check
resource health.
Available rpmbuild rebuild options:
- --with(out) : cibsecrets coverage doc hardening pre_release profiling
+ --with(out) : cibsecrets coverage doc hardening pre_release profiling stonithd
%package cli
License: GPLv2+ and LGPLv2+
Summary: Command line tools for controlling Pacemaker clusters
-Requires: %{name}-libs%{?_isa} = %{version}-%{release}
+Requires: %{pkgname_pcmk_libs}%{?_isa} = %{version}-%{release}
Recommends: pcmk-cluster-manager = %{version}-%{release}
# For crm_report
Recommends: tar
Recommends: bzip2
Requires: perl-TimeDate
-Requires: procps-ng
+Requires: %{pkgname_procps}
Requires: psmisc
Requires(post):coreutils
@@ -259,28 +320,29 @@ The %{name}-cli package contains command line tools that can be used
to query and control the cluster from machines that may, or may not,
be part of the cluster.
-%package libs
+%package -n %{pkgname_pcmk_libs}
License: GPLv2+ and LGPLv2+
Summary: Core Pacemaker libraries
-Requires(pre): shadow-utils
+Requires(pre): %{pkgname_shadow_utils}
Requires: %{name}-schemas = %{version}-%{release}
# sbd 1.4.0+ supports the libpe_status API for pe_working_set_t
# sbd 1.4.2+ supports startup/shutdown handshake via pacemakerd-api
# and handshake defaults to enabled for rhel builds
-# sbd 1.4.3+ handshake defaults to enabled for fedora builds
-Conflicts: sbd < 1.4.3
+# sbd 1.5.0+ handshake defaults to enabled with upstream sbd-release
+# implicitly supports handshake defaults to enabled in this spec
+Conflicts: sbd < 1.5.0
-%description libs
+%description -n %{pkgname_pcmk_libs}
Pacemaker is an advanced, scalable High-Availability cluster resource
manager.
-The %{name}-libs package contains shared libraries needed for cluster
+The %{pkgname_pcmk_libs} package contains shared libraries needed for cluster
nodes and those just running the CLI tools.
%package cluster-libs
License: GPLv2+ and LGPLv2+
Summary: Cluster Libraries used by Pacemaker
-Requires: %{name}-libs%{?_isa} = %{version}-%{release}
+Requires: %{pkgname_pcmk_libs}%{?_isa} = %{version}-%{release}
%description cluster-libs
Pacemaker is an advanced, scalable High-Availability cluster resource
@@ -292,7 +354,7 @@ libraries needed for nodes that will form part of the cluster nodes.
%package remote
License: GPLv2+ and LGPLv2+
Summary: Pacemaker remote executor daemon for non-cluster nodes
-Requires: %{name}-libs%{?_isa} = %{version}-%{release}
+Requires: %{pkgname_pcmk_libs}%{?_isa} = %{version}-%{release}
Requires: %{name}-cli = %{version}-%{release}
Requires: resource-agents
# -remote can be fully independent of systemd
@@ -308,35 +370,40 @@ The %{name}-remote package contains the Pacemaker Remote daemon
which is capable of extending pacemaker functionality to remote
nodes not running the full corosync/cluster stack.
-%package libs-devel
+%package -n %{pkgname_pcmk_libs}-devel
License: GPLv2+ and LGPLv2+
Summary: Pacemaker development package
-Requires: %{name}-libs%{?_isa} = %{version}-%{release}
+Requires: %{pkgname_pcmk_libs}%{?_isa} = %{version}-%{release}
Requires: %{name}-cluster-libs%{?_isa} = %{version}-%{release}
-Requires: libtool-ltdl-devel%{?_isa} libuuid-devel%{?_isa}
+Requires: %{?pkgname_libtool_devel_arch} libuuid-devel%{?_isa}
Requires: libxml2-devel%{?_isa} libxslt-devel%{?_isa}
-Requires: bzip2-devel%{?_isa} glib2-devel%{?_isa}
+Requires: %{pkgname_bzip2_devel}%{?_isa} glib2-devel%{?_isa}
Requires: libqb-devel%{?_isa}
Requires: corosync-devel%{?_isa} >= 2.0.0
-%description libs-devel
+%description -n %{pkgname_pcmk_libs}-devel
Pacemaker is an advanced, scalable High-Availability cluster resource
manager.
-The %{name}-libs-devel package contains headers and shared libraries
+The %{pkgname_pcmk_libs}-devel package contains headers and shared libraries
for developing tools for Pacemaker.
%package cts
License: GPLv2+ and LGPLv2+
Summary: Test framework for cluster-related technologies like Pacemaker
Requires: %{python_path}
-Requires: %{python_name} >= %{python_min}
-Requires: %{name}-libs = %{version}-%{release}
-Requires: procps-ng
+Requires: %{pkgname_pcmk_libs} = %{version}-%{release}
+Requires: %{name}-cli = %{version}-%{release}
+Requires: %{pkgname_procps}
Requires: psmisc
BuildArch: noarch
+# systemd Python bindings are a separate package in some distros
+%if %{defined systemd_requires}
+%if %{defined fedora} || %{defined rhel}
Requires: %{python_name}-systemd
+%endif
+%endif
%description cts
Test framework for cluster-related technologies like Pacemaker
@@ -382,7 +449,10 @@ The metadata files required for Pacemaker to execute the nagios plugin
monitor resources.
%prep
-%setup -q -a 1 -n %{name}-%{archive_version}
+%autosetup -a 1 -n %{name}-%{archive_version} -S git_am -p 1
+# in f33 s390x complains but shouldn't hurt globally
+# as configure.ac is checking for support
+sed -i configure.ac -e "s/-Wall/-Wall -Wno-format-truncation/"
%build
@@ -420,11 +490,10 @@ export LDFLAGS_HARDENED_LIB="%{?_hardening_ldflags}"
--with-initdir=%{_initrddir} \
--with-runstatedir=%{_rundir} \
--localstatedir=%{_var} \
- --with-version=%{version}-%{release} \
- --with-bug-url=https://bugz.fedoraproject.org/%{name} \
- --with-nagios \
- --with-nagios-metadata-dir=%{_datadir}/pacemaker/nagios/plugins-metadata/ \
- --with-nagios-plugin-dir=%{_libdir}/nagios/plugins/
+ --with-nagios \
+ --with-nagios-metadata-dir=%{_datadir}/pacemaker/nagios/plugins-metadata/ \
+ --with-nagios-plugin-dir=%{_libdir}/nagios/plugins/ \
+ --with-version=%{version}-%{release}
make %{_smp_mflags} V=1
@@ -453,11 +522,7 @@ done
mkdir -p ${RPM_BUILD_ROOT}%{_localstatedir}/lib/rpm-state/%{name}
-# These are not actually scripts
-find %{buildroot} -name '*.xml' -type f -print0 | xargs -0 chmod a-x
-
-# Don't package static libs
-find %{buildroot} -name '*.a' -type f -print0 | xargs -0 rm -f
+# Don't package libtool archives
find %{buildroot} -name '*.la' -type f -print0 | xargs -0 rm -f
# Do not package these either
@@ -475,7 +540,7 @@ rm -f %{buildroot}/%{_initrddir}/pacemaker
rm -f %{buildroot}/%{_initrddir}/pacemaker_remote
# Byte-compile Python sources where suitable and the distro procedures known
-%if %{defined py_byte_compile} && %{defined python_path}
+%if %{defined py_byte_compile}
%{py_byte_compile %{python_path} %{buildroot}%{_datadir}/pacemaker/tests}
%if !%{defined _python_bytecompile_extra}
%{py_byte_compile %{python_path} %{buildroot}%{python_site}/cts}
@@ -552,15 +617,14 @@ fi
%postun cli
%systemd_postun_with_restart crm_mon.service
-%pre libs
-# XXX keep an eye on https://fedoraproject.org/wiki/Changes/SystemdSysusers
-# reopened recently:
-# https://lists.fedoraproject.org/archives/list/devel@lists.fedoraproject.o...
+%pre -n %{pkgname_pcmk_libs}
+# @TODO Use sysusers.d:
+# https://fedoraproject.org/wiki/Changes/Adopting_sysusers.d_format
getent group %{gname} >/dev/null || groupadd -r %{gname} -g %{hacluster_id}
getent passwd %{uname} >/dev/null || useradd -r -g %{gname} -u %{hacluster_id} -s /sbin/nologin -c "cluster user" %{uname}
exit 0
-%ldconfig_scriptlets libs
+%ldconfig_scriptlets -n %{pkgname_pcmk_libs}
%ldconfig_scriptlets cluster-libs
%files
@@ -667,7 +731,7 @@ exit 0
%dir %attr (770, %{uname}, %{gname}) %{_var}/log/pacemaker
%dir %attr (770, %{uname}, %{gname}) %{_var}/log/pacemaker/bundles
-%files libs
+%files -n %{pkgname_pcmk_libs}
%{_libdir}/libcib.so.*
%{_libdir}/liblrmd.so.*
%{_libdir}/libcrmservice.so.*
@@ -716,7 +780,7 @@ exit 0
%doc COPYING
%doc ChangeLog
-%files libs-devel
+%files -n %{pkgname_pcmk_libs}-devel
%{_includedir}/pacemaker
%{_libdir}/*.so
%if %{with coverage}
@@ -742,6 +806,12 @@ exit 0
%license %{nagios_name}-%{nagios_hash}/COPYING
%changelog
+* Mon Jul 5 2021 Klaus Wenninger <kwenning(a)redhat.com> - 2.1.0-6
+- synced/merged with CS9 spec-file for current 2.1.0-release build
+
+* Tue Jun 8 2021 Klaus Wenninger <kwenning(a)redhat.com> - 2.1.0-0.5.rc3
+- silence f33 s390x build complaining about possible format-trucation
+
* Mon Jun 07 2021 Python Maint <python-maint(a)redhat.com> - 2.1.0-0.4.rc3.1
- Rebuilt for Python 3.10
@@ -753,7 +823,7 @@ exit 0
* Fri Jun 04 2021 Python Maint <python-maint(a)redhat.com> - 2.1.0-0.3.rc2.1
- Rebuilt for Python 3.10
-* Mon Jun 1 2021 Klaus Wenninger <kwenning(a)redhat.com> - 2.1.0-0.3.rc2
+* Mon May 31 2021 Klaus Wenninger <kwenning(a)redhat.com> - 2.1.0-0.3.rc2
- Update for new upstream tarball for release candidate: Pacemaker-2.1.0-rc2,
for full details, see included ChangeLog file or
https://github.com/ClusterLabs/pacemaker/releases/tag/Pacemaker-2.1.0-rc2
diff --git a/sources b/sources
index af5cd77..2ab7f32 100644
--- a/sources
+++ b/sources
@@ -1,2 +1,2 @@
SHA512 (nagios-agents-metadata-105ab8a7b2c16b9a29cf1c1596b80136eeef332b.tar.gz) = 11ddeb48a4929e7642b6dfa9c7962aa1d7a1af1c569830f55ed6cd6773abac13377317327bc1db8411c8077884f83f81cc54d746c834b63a99fa6dc219b5caad
-SHA512 (pacemaker-Pacemaker-2.1.0-rc3.tar.gz) = eb17566e079d313394dab509b055e326b642508844d974a21cde62509a3a526e44977a57c65f80d5094f085fec8ad92eda0deed4f4dfd32ecf0c3b80722e5504
+SHA512 (pacemaker-7c3f66070.tar.gz) = e1c53d019c965395affc1bd78358927617c9bc8572c405ee884737a8213455c5e85102e6f58bb5239ab23ebe5aa794f72c9cc0746900b7a6cc14fd3cf1879064
commit 5dc83e38659be95da4423eed082e2fb0029e2848
Author: Python Maint <python-maint(a)redhat.com>
Date: Mon Jun 7 19:14:33 2021 +0200
Rebuilt for Python 3.10
diff --git a/pacemaker.spec b/pacemaker.spec
index bf413b5..1f9dce2 100644
--- a/pacemaker.spec
+++ b/pacemaker.spec
@@ -165,7 +165,7 @@
Name: pacemaker
Summary: Scalable High-Availability cluster resource manager
Version: %{pcmkversion}
-Release: %{pcmk_release}%{?dist}
+Release: %{pcmk_release}%{?dist}.1
License: GPLv2+ and LGPLv2+
Url: https://www.clusterlabs.org
@@ -742,6 +742,9 @@ exit 0
%license %{nagios_name}-%{nagios_hash}/COPYING
%changelog
+* Mon Jun 07 2021 Python Maint <python-maint(a)redhat.com> - 2.1.0-0.4.rc3.1
+- Rebuilt for Python 3.10
+
* Mon Jun 07 2021 Klaus Wenninger <kwenning(a)redhat.com> - 2.1.0-0.4.rc3
- Update for new upstream tarball for release candidate: Pacemaker-2.1.0-rc3,
for full details, see included ChangeLog file or
commit e52de4390a10fcc15efd749a705386ea8b46e0b4
Author: Klaus Wenninger <klaus.wenninger(a)aon.at>
Date: Mon Jun 7 15:55:07 2021 +0200
* Mon Jun 07 2021 Klaus Wenninger <kwenning(a)redhat.com> - 2.1.0-0.4.rc3
- Update for new upstream tarball for release candidate: Pacemaker-2.1.0-rc3,
for full details, see included ChangeLog file or
https://github.com/ClusterLabs/pacemaker/releases/tag/Pacemaker-2.1.0-rc3
diff --git a/pacemaker.spec b/pacemaker.spec
index a6800fc..bf413b5 100644
--- a/pacemaker.spec
+++ b/pacemaker.spec
@@ -25,11 +25,11 @@
## can be incremented to build packages reliably considered "newer"
## than previously built packages with the same pcmkversion)
%global pcmkversion 2.1.0
-%global specversion 3
+%global specversion 4
## Upstream commit (or git tag, such as "Pacemaker-" plus the
## {pcmkversion} macro for an official release) to use for this package
-%global commit Pacemaker-2.1.0-rc2
+%global commit Pacemaker-2.1.0-rc3
## Since git v2.11, the extent of abbreviation is autoscaled by default
## (used to be constant of 7), so we need to convey it for non-tags, too.
%global commit_abbrev 9
@@ -165,7 +165,7 @@
Name: pacemaker
Summary: Scalable High-Availability cluster resource manager
Version: %{pcmkversion}
-Release: %{pcmk_release}%{?dist}.1
+Release: %{pcmk_release}%{?dist}
License: GPLv2+ and LGPLv2+
Url: https://www.clusterlabs.org
@@ -742,6 +742,11 @@ exit 0
%license %{nagios_name}-%{nagios_hash}/COPYING
%changelog
+* Mon Jun 07 2021 Klaus Wenninger <kwenning(a)redhat.com> - 2.1.0-0.4.rc3
+- Update for new upstream tarball for release candidate: Pacemaker-2.1.0-rc3,
+ for full details, see included ChangeLog file or
+ https://github.com/ClusterLabs/pacemaker/releases/tag/Pacemaker-2.1.0-rc3
+
* Fri Jun 04 2021 Python Maint <python-maint(a)redhat.com> - 2.1.0-0.3.rc2.1
- Rebuilt for Python 3.10
diff --git a/sources b/sources
index 76e3c9a..af5cd77 100644
--- a/sources
+++ b/sources
@@ -1,2 +1,2 @@
SHA512 (nagios-agents-metadata-105ab8a7b2c16b9a29cf1c1596b80136eeef332b.tar.gz) = 11ddeb48a4929e7642b6dfa9c7962aa1d7a1af1c569830f55ed6cd6773abac13377317327bc1db8411c8077884f83f81cc54d746c834b63a99fa6dc219b5caad
-SHA512 (pacemaker-Pacemaker-2.1.0-rc2.tar.gz) = 01396cd23b0cb49cab40b870e227b132f1e0c9b94cf6ac29d58e61bd3ff4ee4ea95fa6509c9a4a09179284b8fdbdba8319ceb46e8f94ce9cea92cf13832f9f4d
+SHA512 (pacemaker-Pacemaker-2.1.0-rc3.tar.gz) = eb17566e079d313394dab509b055e326b642508844d974a21cde62509a3a526e44977a57c65f80d5094f085fec8ad92eda0deed4f4dfd32ecf0c3b80722e5504
commit 172718bb48a11ed751025770698e5452ae20a815
Author: Python Maint <python-maint(a)redhat.com>
Date: Fri Jun 4 20:14:25 2021 +0200
Rebuilt for Python 3.10
diff --git a/pacemaker.spec b/pacemaker.spec
index 979a0e0..a6800fc 100644
--- a/pacemaker.spec
+++ b/pacemaker.spec
@@ -165,7 +165,7 @@
Name: pacemaker
Summary: Scalable High-Availability cluster resource manager
Version: %{pcmkversion}
-Release: %{pcmk_release}%{?dist}
+Release: %{pcmk_release}%{?dist}.1
License: GPLv2+ and LGPLv2+
Url: https://www.clusterlabs.org
@@ -742,6 +742,9 @@ exit 0
%license %{nagios_name}-%{nagios_hash}/COPYING
%changelog
+* Fri Jun 04 2021 Python Maint <python-maint(a)redhat.com> - 2.1.0-0.3.rc2.1
+- Rebuilt for Python 3.10
+
* Mon Jun 1 2021 Klaus Wenninger <kwenning(a)redhat.com> - 2.1.0-0.3.rc2
- Update for new upstream tarball for release candidate: Pacemaker-2.1.0-rc2,
for full details, see included ChangeLog file or
commit 595ee6bf2a7d18022e3d17a40b07aafea1960ecf
Author: Klaus Wenninger <klaus.wenninger(a)aon.at>
Date: Tue Jun 1 13:50:20 2021 +0200
* Mon Jun 1 2021 Klaus Wenninger <kwenning(a)redhat.com> - 2.1.0-0.3.rc2
- Update for new upstream tarball for release candidate: Pacemaker-2.1.0-rc2,
for full details, see included ChangeLog file or
https://github.com/ClusterLabs/pacemaker/releases/tag/Pacemaker-2.1.0-rc2
- merge changes in upstream specfile sind Pacemaker-2.1.0-rc1
add configurable ocf_root
enable cibsecrets for > f33
require libqb >= 0.17.0 instead of 0.13.0
default to syncing with sbd
conflict all sbd < 1.4.3 to enforce sbd-build has the same default
diff --git a/pacemaker.spec b/pacemaker.spec
index 885a70e..979a0e0 100644
--- a/pacemaker.spec
+++ b/pacemaker.spec
@@ -15,15 +15,21 @@
## GitHub entity that distributes source (for ease of using a fork)
%global github_owner ClusterLabs
+## Where bug reports should be submitted
+## Leave bug_url undefined to use ClusterLabs default, others define it here
+
+## What to use as the OCF resource agent root directory
+%global ocf_root %{_prefix}/lib/ocf
+
## Upstream pacemaker version, and its package version (specversion
## can be incremented to build packages reliably considered "newer"
## than previously built packages with the same pcmkversion)
%global pcmkversion 2.1.0
-%global specversion 2
+%global specversion 3
## Upstream commit (or git tag, such as "Pacemaker-" plus the
## {pcmkversion} macro for an official release) to use for this package
-%global commit Pacemaker-2.1.0-rc1
+%global commit Pacemaker-2.1.0-rc2
## Since git v2.11, the extent of abbreviation is autoscaled by default
## (used to be constant of 7), so we need to convey it for non-tags, too.
%global commit_abbrev 9
@@ -33,8 +39,12 @@
## NOTE: skip --with stonithd
-## Add option to enable support for storing sensitive information outside CIB
+## Add option for whether to support storing sensitive information outside CIB
+%if (0%{?fedora} && 0%{?fedora} <= 33) || (0%{?rhel} && 0%{?rhel} <= 8)
%bcond_with cibsecrets
+%else
+%bcond_without cibsecrets
+%endif
## Add option to create binaries suitable for use with profiling tools
%bcond_with profiling
@@ -45,6 +55,15 @@
## Add option to skip/enable generating documentation
%bcond_without doc
+## Add option to default to start-up synchronization with SBD.
+##
+## If enabled, SBD *MUST* be built to default similarly, otherwise data
+## corruption could occur. Building both Pacemaker and SBD to default
+## to synchronization improves safety, without requiring higher-level tools
+## to be aware of the setting or requiring users to modify configurations
+## after upgrading to versions that support synchronization.
+%bcond_without sbd_sync
+
## Add option to prefix package version with "0."
## (so later "official" packages will be considered updates)
%bcond_without pre_release
@@ -167,14 +186,15 @@ BuildRequires: make
BuildRequires: %{python_name}-devel >= %{python_min}
# Pacemaker requires a minimum libqb functionality
-Requires: libqb >= 0.13.0
-BuildRequires: libqb-devel >= 0.13.0
+Requires: libqb >= 0.17.0
+BuildRequires: libqb-devel >= 0.17.0
-# Basics required for the build (even if usually satisfied through other BRs)
+# Required basic build tools
BuildRequires: coreutils findutils grep sed
+BuildRequires: autoconf automake gcc make pkgconfig
+BuildRequires: libtool libtool-ltdl-devel
# Required for core functionality
-BuildRequires: automake autoconf gcc libtool pkgconfig libtool-ltdl-devel
BuildRequires: pkgconfig(glib-2.0) >= 2.42
BuildRequires: libxml2-devel libxslt-devel libuuid-devel
BuildRequires: bzip2-devel
@@ -245,7 +265,10 @@ Summary: Core Pacemaker libraries
Requires(pre): shadow-utils
Requires: %{name}-schemas = %{version}-%{release}
# sbd 1.4.0+ supports the libpe_status API for pe_working_set_t
-Conflicts: sbd < 1.4.0
+# sbd 1.4.2+ supports startup/shutdown handshake via pacemakerd-api
+# and handshake defaults to enabled for rhel builds
+# sbd 1.4.3+ handshake defaults to enabled for fedora builds
+Conflicts: sbd < 1.4.3
%description libs
Pacemaker is an advanced, scalable High-Availability cluster resource
@@ -268,7 +291,7 @@ libraries needed for nodes that will form part of the cluster nodes.
%package remote
License: GPLv2+ and LGPLv2+
-Summary: Pacemaker remote daemon for non-cluster nodes
+Summary: Pacemaker remote executor daemon for non-cluster nodes
Requires: %{name}-libs%{?_isa} = %{version}-%{release}
Requires: %{name}-cli = %{version}-%{release}
Requires: resource-agents
@@ -386,7 +409,10 @@ export LDFLAGS_HARDENED_LIB="%{?_hardening_ldflags}"
%{?with_profiling: --with-profiling} \
%{?with_coverage: --with-coverage} \
%{?with_cibsecrets: --with-cibsecrets} \
+ %{?with_sbd_sync: --with-sbd-sync-default="true"} \
%{?gnutls_priorities: --with-gnutls-priorities="%{gnutls_priorities}"} \
+ %{?bug_url: --with-bug-url=%{bug_url}} \
+ %{?ocf_root: --with-ocfdir=%{ocf_root}} \
%{?concurrent_fencing} \
%{?resource_stickiness} \
%{?compat20} \
@@ -572,8 +598,8 @@ exit 0
%dir %attr (750, %{uname}, %{gname}) %{_var}/lib/pacemaker/cib
%dir %attr (750, %{uname}, %{gname}) %{_var}/lib/pacemaker/pengine
-/usr/lib/ocf/resource.d/pacemaker/controld
-/usr/lib/ocf/resource.d/pacemaker/remote
+%{ocf_root}/resource.d/pacemaker/controld
+%{ocf_root}/resource.d/pacemaker/remote
%files cli
%dir %attr (750, root, %{gname}) %{_sysconfdir}/pacemaker
@@ -609,13 +635,13 @@ exit 0
# XXX "dirname" is not owned by any prerequisite
%{_datadir}/snmp/mibs/PCMK-MIB.txt
-%exclude /usr/lib/ocf/resource.d/pacemaker/controld
-%exclude /usr/lib/ocf/resource.d/pacemaker/o2cb
-%exclude /usr/lib/ocf/resource.d/pacemaker/remote
+%exclude %{ocf_root}/resource.d/pacemaker/controld
+%exclude %{ocf_root}/resource.d/pacemaker/o2cb
+%exclude %{ocf_root}/resource.d/pacemaker/remote
-%dir /usr/lib/ocf
-%dir /usr/lib/ocf/resource.d
-/usr/lib/ocf/resource.d/pacemaker
+%dir %{ocf_root}
+%dir %{ocf_root}/resource.d
+%{ocf_root}/resource.d/pacemaker
%doc %{_mandir}/man7/*
%exclude %{_mandir}/man7/pacemaker-controld.*
@@ -716,6 +742,17 @@ exit 0
%license %{nagios_name}-%{nagios_hash}/COPYING
%changelog
+* Mon Jun 1 2021 Klaus Wenninger <kwenning(a)redhat.com> - 2.1.0-0.3.rc2
+- Update for new upstream tarball for release candidate: Pacemaker-2.1.0-rc2,
+ for full details, see included ChangeLog file or
+ https://github.com/ClusterLabs/pacemaker/releases/tag/Pacemaker-2.1.0-rc2
+- merge changes in upstream specfile sind Pacemaker-2.1.0-rc1
+ add configurable ocf_root
+ enable cibsecrets for > f33
+ require libqb >= 0.17.0 instead of 0.13.0
+ default to syncing with sbd
+ conflict all sbd < 1.4.3 to enforce sbd-build has the same default
+
* Wed May 5 2021 Klaus Wenninger <kwenning(a)redhat.com> - 2.1.0-0.2.rc1
- remove PCMK_TIME_EMERGENCY_CGT not present in pacemaker codebase anymore
- enable Pacemaker-2.0-compatibility for a fedora versions
diff --git a/sources b/sources
index a976443..76e3c9a 100644
--- a/sources
+++ b/sources
@@ -1,2 +1,2 @@
SHA512 (nagios-agents-metadata-105ab8a7b2c16b9a29cf1c1596b80136eeef332b.tar.gz) = 11ddeb48a4929e7642b6dfa9c7962aa1d7a1af1c569830f55ed6cd6773abac13377317327bc1db8411c8077884f83f81cc54d746c834b63a99fa6dc219b5caad
-SHA512 (pacemaker-Pacemaker-2.1.0-rc1.tar.gz) = 64fef270ac0f0df63a2ac974c4467dfb6a646cbe2089c6b523f14311bf236653caa75cd9e2851af7882057d63651ecd3c9d40647349a76619c4522c938063acf
+SHA512 (pacemaker-Pacemaker-2.1.0-rc2.tar.gz) = 01396cd23b0cb49cab40b870e227b132f1e0c9b94cf6ac29d58e61bd3ff4ee4ea95fa6509c9a4a09179284b8fdbdba8319ceb46e8f94ce9cea92cf13832f9f4d
commit e7aab8014f11ec96139f581de2cfe66c85975ed0
Author: Klaus Wenninger <klaus.wenninger(a)aon.at>
Date: Fri May 7 11:11:12 2021 +0200
* Wed May 5 2021 Klaus Wenninger <kwenning(a)redhat.com> - 2.1.0-0.2.rc1
- remove PCMK_TIME_EMERGENCY_CGT not present in pacemaker codebase anymore
- enable Pacemaker-2.0-compatibility for a fedora versions
diff --git a/pacemaker.spec b/pacemaker.spec
index 676075c..885a70e 100644
--- a/pacemaker.spec
+++ b/pacemaker.spec
@@ -19,7 +19,7 @@
## can be incremented to build packages reliably considered "newer"
## than previously built packages with the same pcmkversion)
%global pcmkversion 2.1.0
-%global specversion 1
+%global specversion 2
## Upstream commit (or git tag, such as "Pacemaker-" plus the
## {pcmkversion} macro for an official release) to use for this package
@@ -97,7 +97,7 @@
## Distro-specific configuration choices
### Use 2.0-style output when other distro packages don't support current output
-%if 0%{?fedora} && 0%{?fedora} <= 34
+%if 0%{?fedora}
%global compat20 --enable-compat-2.0
%endif
@@ -379,9 +379,6 @@ export LDFLAGS_HARDENED_LIB="%{?_hardening_ldflags}"
./autogen.sh
-# Rawhide glibc doesn't like ftime at all
-export CPPFLAGS="-UPCMK_TIME_EMERGENCY_CGT $CPPFLAGS"
-
%{configure} \
PYTHON=%{python_path} \
%{!?with_hardening: --disable-hardening} \
@@ -719,6 +716,10 @@ exit 0
%license %{nagios_name}-%{nagios_hash}/COPYING
%changelog
+* Wed May 5 2021 Klaus Wenninger <kwenning(a)redhat.com> - 2.1.0-0.2.rc1
+- remove PCMK_TIME_EMERGENCY_CGT not present in pacemaker codebase anymore
+- enable Pacemaker-2.0-compatibility for a fedora versions
+
* Tue May 4 2021 Klaus Wenninger <kwenning(a)redhat.com> - 2.1.0-0.1.rc1
- Update for new upstream tarball for release candidate: Pacemaker-2.1.0-rc1,
for full details, see included ChangeLog file or
commit c8cc15254d66b4d69d4d3eb547808fcfd172662a
Author: Klaus Wenninger <klaus.wenninger(a)aon.at>
Date: Tue May 4 22:24:44 2021 +0200
* Tue May 4 2021 Klaus Wenninger <kwenning(a)redhat.com> - 2.1.0-0.1.rc1
- Update for new upstream tarball for release candidate: Pacemaker-2.1.0-rc1,
for full details, see included ChangeLog file or
https://github.com/ClusterLabs/pacemaker/releases/tag/Pacemaker-2.1.0-rc1
- merge changes in upstream spec-file since Pacemaker-2.0.5
- disable legacy-links
diff --git a/pacemaker.spec b/pacemaker.spec
index b786f9c..676075c 100644
--- a/pacemaker.spec
+++ b/pacemaker.spec
@@ -18,12 +18,12 @@
## Upstream pacemaker version, and its package version (specversion
## can be incremented to build packages reliably considered "newer"
## than previously built packages with the same pcmkversion)
-%global pcmkversion 2.0.5
-%global specversion 10
+%global pcmkversion 2.1.0
+%global specversion 1
## Upstream commit (or git tag, such as "Pacemaker-" plus the
## {pcmkversion} macro for an official release) to use for this package
-%global commit Pacemaker-2.0.5
+%global commit Pacemaker-2.1.0-rc1
## Since git v2.11, the extent of abbreviation is autoscaled by default
## (used to be constant of 7), so we need to convey it for non-tags, too.
%global commit_abbrev 9
@@ -43,24 +43,19 @@
%bcond_with coverage
## Add option to skip/enable generating documentation
-## (the build tools aren't available everywhere)
-%if 0%{?rhel}
- %bcond_with doc
-%else
- %bcond_without doc
-%endif
+%bcond_without doc
## Add option to prefix package version with "0."
## (so later "official" packages will be considered updates)
-%bcond_with pre_release
+%bcond_without pre_release
## NOTE: skip --with upstart_job
## Add option to turn off hardening of libraries and daemon executables
%bcond_without hardening
-## Add option to disable links for legacy daemon names
-%bcond_without legacy_links
+## Add option to enable links for legacy daemon names
+%bcond_with legacy_links
## Nagios source control identifiers
@@ -95,14 +90,27 @@
%endif
%endif
-## Heuristic used to infer bleeding-edge deployments that are
-## less likely to have working versions of the documentation tools
-%define bleeding %(test ! -e /etc/yum.repos.d/fedora-rawhide.repo; echo $?)
-
## Base GnuTLS cipher priorities (presumably only the initial, required keyword)
## overridable with "rpmbuild --define 'pcmk_gnutls_priorities PRIORITY-SPEC'"
%define gnutls_priorities %{?pcmk_gnutls_priorities}%{!?pcmk_gnutls_priorities:@SYSTEM}
+## Distro-specific configuration choices
+
+### Use 2.0-style output when other distro packages don't support current output
+%if 0%{?fedora} && 0%{?fedora} <= 34
+%global compat20 --enable-compat-2.0
+%endif
+
+### Default concurrent-fencing to true when distro prefers that
+%if 0%{?rhel}
+%global concurrent_fencing --with-concurrent-fencing-default=true
+%endif
+
+### Default resource-stickiness to 1 when distro prefers that
+%if 0%{?fedora} >= 35 || 0%{?rhel} >= 9
+%global resource_stickiness --with-resource-stickiness-default=1
+%endif
+
# Python-related definitions
## Turn off auto-compilation of Python files outside Python specific paths,
@@ -138,7 +146,7 @@
Name: pacemaker
Summary: Scalable High-Availability cluster resource manager
Version: %{pcmkversion}
-Release: %{pcmk_release}%{?dist}.2
+Release: %{pcmk_release}%{?dist}
License: GPLv2+ and LGPLv2+
Url: https://www.clusterlabs.org
@@ -167,7 +175,7 @@ BuildRequires: coreutils findutils grep sed
# Required for core functionality
BuildRequires: automake autoconf gcc libtool pkgconfig libtool-ltdl-devel
-BuildRequires: pkgconfig(glib-2.0) >= 2.16
+BuildRequires: pkgconfig(glib-2.0) >= 2.42
BuildRequires: libxml2-devel libxslt-devel libuuid-devel
BuildRequires: bzip2-devel
@@ -183,11 +191,8 @@ BuildRequires: corosync-devel >= 2.0.0
#BuildRequires: pkgconfig(libcpg)
#BuildRequires: pkgconfig(libcfg)
-## (note no avoiding effect when building through non-customized mock)
-%if !%{bleeding}
%if %{with doc}
-BuildRequires: asciidoc inkscape publican
-%endif
+BuildRequires: asciidoc inkscape %{python_name}-sphinx
%endif
# git-style patch application
@@ -380,12 +385,14 @@ export CPPFLAGS="-UPCMK_TIME_EMERGENCY_CGT $CPPFLAGS"
%{configure} \
PYTHON=%{python_path} \
%{!?with_hardening: --disable-hardening} \
- %{!?with_legacy_links: --disable-legacy-links} \
+ %{?with_legacy_links: --enable-legacy-links} \
%{?with_profiling: --with-profiling} \
%{?with_coverage: --with-coverage} \
%{?with_cibsecrets: --with-cibsecrets} \
- %{!?with_doc: --with-brand=} \
%{?gnutls_priorities: --with-gnutls-priorities="%{gnutls_priorities}"} \
+ %{?concurrent_fencing} \
+ %{?resource_stickiness} \
+ %{?compat20} \
--disable-static \
--with-initdir=%{_initrddir} \
--with-runstatedir=%{_rundir} \
@@ -415,10 +422,6 @@ make install \
DESTDIR=%{buildroot} V=1 docdir=%{pcmk_docdir} \
%{?_python_bytecompile_extra:%{?py_byte_compile:am__py_compile=true}}
-mkdir -p ${RPM_BUILD_ROOT}%{_sysconfdir}/sysconfig
-install -m 644 daemons/pacemakerd/pacemaker.sysconfig ${RPM_BUILD_ROOT}%{_sysconfdir}/sysconfig/pacemaker
-install -m 644 tools/crm_mon.sysconfig ${RPM_BUILD_ROOT}%{_sysconfdir}/sysconfig/crm_mon
-
mkdir -p %{buildroot}%{_datadir}/pacemaker/nagios/plugins-metadata
for file in $(find %{nagios_name}-%{nagios_hash}/metadata -type f); do
install -m 644 $file %{buildroot}%{_datadir}/pacemaker/nagios/plugins-metadata
@@ -549,9 +552,7 @@ exit 0
%exclude %{_libexecdir}/pacemaker/cts-log-watcher
%exclude %{_libexecdir}/pacemaker/cts-support
%exclude %{_sbindir}/pacemaker-remoted
-%if %{with legacy_links}
%exclude %{_sbindir}/pacemaker_remoted
-%endif
%{_libexecdir}/pacemaker/*
%{_sbindir}/crm_attribute
@@ -671,9 +672,7 @@ exit 0
%{_unitdir}/pacemaker_remote.service
%{_sbindir}/pacemaker-remoted
-%if %{with legacy_links}
%{_sbindir}/pacemaker_remoted
-%endif
%{_mandir}/man8/pacemaker-remoted.*
%license licenses/GPLv2
%doc COPYING
@@ -720,6 +719,13 @@ exit 0
%license %{nagios_name}-%{nagios_hash}/COPYING
%changelog
+* Tue May 4 2021 Klaus Wenninger <kwenning(a)redhat.com> - 2.1.0-0.1.rc1
+- Update for new upstream tarball for release candidate: Pacemaker-2.1.0-rc1,
+ for full details, see included ChangeLog file or
+ https://github.com/ClusterLabs/pacemaker/releases/tag/Pacemaker-2.1.0-rc1
+- merge changes in upstream spec-file since Pacemaker-2.0.5
+- disable legacy-links
+
* Tue Mar 02 2021 Zbigniew Jędrzejewski-Szmek <zbyszek(a)in.waw.pl> - 2.0.5-10.2
- Rebuilt for updated systemd-rpm-macros
See https://pagure.io/fesco/issue/2583.
diff --git a/sources b/sources
index b19a593..a976443 100644
--- a/sources
+++ b/sources
@@ -1,2 +1,2 @@
SHA512 (nagios-agents-metadata-105ab8a7b2c16b9a29cf1c1596b80136eeef332b.tar.gz) = 11ddeb48a4929e7642b6dfa9c7962aa1d7a1af1c569830f55ed6cd6773abac13377317327bc1db8411c8077884f83f81cc54d746c834b63a99fa6dc219b5caad
-SHA512 (pacemaker-Pacemaker-2.0.5.tar.gz) = 5fd3614f0284297babb66ea2dc567583315052fcf77f49107c52161e8e8c164ae3169d98528fdc1316d8eabaacc98ed24e9c6e6c90b3286f2f012a4ee874bbba
+SHA512 (pacemaker-Pacemaker-2.1.0-rc1.tar.gz) = 64fef270ac0f0df63a2ac974c4467dfb6a646cbe2089c6b523f14311bf236653caa75cd9e2851af7882057d63651ecd3c9d40647349a76619c4522c938063acf
commit 3978725c064d9bf7d504f7c8118f3c9d34bfb715
Author: Zbigniew Jędrzejewski-Szmek <zbyszek(a)in.waw.pl>
Date: Tue Mar 2 16:13:07 2021 +0100
Rebuilt for updated systemd-rpm-macros
See https://pagure.io/fesco/issue/2583.
diff --git a/pacemaker.spec b/pacemaker.spec
index 8f15ea0..b786f9c 100644
--- a/pacemaker.spec
+++ b/pacemaker.spec
@@ -138,7 +138,7 @@
Name: pacemaker
Summary: Scalable High-Availability cluster resource manager
Version: %{pcmkversion}
-Release: %{pcmk_release}%{?dist}.1
+Release: %{pcmk_release}%{?dist}.2
License: GPLv2+ and LGPLv2+
Url: https://www.clusterlabs.org
@@ -720,6 +720,10 @@ exit 0
%license %{nagios_name}-%{nagios_hash}/COPYING
%changelog
+* Tue Mar 02 2021 Zbigniew Jędrzejewski-Szmek <zbyszek(a)in.waw.pl> - 2.0.5-10.2
+- Rebuilt for updated systemd-rpm-macros
+ See https://pagure.io/fesco/issue/2583.
+
* Tue Jan 26 2021 Fedora Release Engineering <releng(a)fedoraproject.org> - 2.0.5-10.1
- Rebuilt for https://fedoraproject.org/wiki/Fedora_34_Mass_Rebuild
commit 5069bd8f40edfdf1691a34044350bec9164be344
Author: Fedora Release Engineering <releng(a)fedoraproject.org>
Date: Tue Jan 26 22:48:40 2021 +0000
- Rebuilt for https://fedoraproject.org/wiki/Fedora_34_Mass_Rebuild
Signed-off-by: Fedora Release Engineering <releng(a)fedoraproject.org>
diff --git a/pacemaker.spec b/pacemaker.spec
index 1f2badf..8f15ea0 100644
--- a/pacemaker.spec
+++ b/pacemaker.spec
@@ -138,7 +138,7 @@
Name: pacemaker
Summary: Scalable High-Availability cluster resource manager
Version: %{pcmkversion}
-Release: %{pcmk_release}%{?dist}
+Release: %{pcmk_release}%{?dist}.1
License: GPLv2+ and LGPLv2+
Url: https://www.clusterlabs.org
@@ -720,6 +720,9 @@ exit 0
%license %{nagios_name}-%{nagios_hash}/COPYING
%changelog
+* Tue Jan 26 2021 Fedora Release Engineering <releng(a)fedoraproject.org> - 2.0.5-10.1
+- Rebuilt for https://fedoraproject.org/wiki/Fedora_34_Mass_Rebuild
+
* Mon Dec 7 2020 Klaus Wenninger <kwenning(a)redhat.com> - 2.0.5-10
- Conflicts of doc package introduced to fix upgrade/downgrade
issues needs to be independent from arch
commit 190f2c862460b6c1e5db34b8a607fa22375ae019
Author: Tom Stellard <tstellar(a)redhat.com>
Date: Thu Jan 7 06:50:48 2021 +0000
Add BuildRequires: make
https://fedoraproject.org/wiki/Changes/Remove_make_from_BuildRoot
diff --git a/pacemaker.spec b/pacemaker.spec
index 54c9f68..1f2badf 100644
--- a/pacemaker.spec
+++ b/pacemaker.spec
@@ -155,6 +155,7 @@ Requires: %{name}-cli = %{version}-%{release}
# Pacemaker requires a minimum Python functionality
Requires: %{python_name} >= %{python_min}
+BuildRequires: make
BuildRequires: %{python_name}-devel >= %{python_min}
# Pacemaker requires a minimum libqb functionality
commit 83ce149390888dd8f2ed65b5c7e43b2451c464e0
Author: Klaus Wenninger <klaus.wenninger(a)aon.at>
Date: Mon Dec 7 17:55:17 2020 +0100
* Mon Dec 7 2020 Klaus Wenninger <kwenning(a)redhat.com> - 2.0.5-10
- Conflicts of doc package introduced to fix upgrade/downgrade
issues needs to be independent from arch
diff --git a/pacemaker.spec b/pacemaker.spec
index bbdd023..54c9f68 100644
--- a/pacemaker.spec
+++ b/pacemaker.spec
@@ -19,7 +19,7 @@
## can be incremented to build packages reliably considered "newer"
## than previously built packages with the same pcmkversion)
%global pcmkversion 2.0.5
-%global specversion 9
+%global specversion 10
## Upstream commit (or git tag, such as "Pacemaker-" plus the
## {pcmkversion} macro for an official release) to use for this package
@@ -316,8 +316,8 @@ Test framework for cluster-related technologies like Pacemaker
License: CC-BY-SA-4.0
Summary: Documentation for Pacemaker
BuildArch: noarch
-Conflicts: %{name}-libs%{?_isa} > %{version}-%{release}
-Conflicts: %{name}-libs%{?_isa} < %{version}-%{release}
+Conflicts: %{name}-libs > %{version}-%{release}
+Conflicts: %{name}-libs < %{version}-%{release}
%description doc
Documentation for Pacemaker.
@@ -719,6 +719,10 @@ exit 0
%license %{nagios_name}-%{nagios_hash}/COPYING
%changelog
+* Mon Dec 7 2020 Klaus Wenninger <kwenning(a)redhat.com> - 2.0.5-10
+- Conflicts of doc package introduced to fix upgrade/downgrade
+ issues needs to be independent from arch
+
* Fri Dec 4 2020 Klaus Wenninger <kwenning(a)redhat.com> - 2.0.5-9
- Make doc-package conflict with wrong version of libs
to fix upgrade/downgrade issues
commit e2224005a94e0ee24cac2a955bb1741c6b40a1cb
Author: Klaus Wenninger <klaus.wenninger(a)aon.at>
Date: Mon Dec 7 17:23:55 2020 +0100
* Fri Dec 4 2020 Klaus Wenninger <kwenning(a)redhat.com> - 2.0.5-9
- Make doc-package conflict with wrong version of libs
to fix upgrade/downgrade issues
diff --git a/pacemaker.spec b/pacemaker.spec
index 056493c..bbdd023 100644
--- a/pacemaker.spec
+++ b/pacemaker.spec
@@ -19,7 +19,7 @@
## can be incremented to build packages reliably considered "newer"
## than previously built packages with the same pcmkversion)
%global pcmkversion 2.0.5
-%global specversion 8
+%global specversion 9
## Upstream commit (or git tag, such as "Pacemaker-" plus the
## {pcmkversion} macro for an official release) to use for this package
@@ -316,6 +316,8 @@ Test framework for cluster-related technologies like Pacemaker
License: CC-BY-SA-4.0
Summary: Documentation for Pacemaker
BuildArch: noarch
+Conflicts: %{name}-libs%{?_isa} > %{version}-%{release}
+Conflicts: %{name}-libs%{?_isa} < %{version}-%{release}
%description doc
Documentation for Pacemaker.
@@ -717,6 +719,10 @@ exit 0
%license %{nagios_name}-%{nagios_hash}/COPYING
%changelog
+* Fri Dec 4 2020 Klaus Wenninger <kwenning(a)redhat.com> - 2.0.5-9
+- Make doc-package conflict with wrong version of libs
+ to fix upgrade/downgrade issues
+
* Fri Dec 4 2020 Klaus Wenninger <kwenning(a)redhat.com> - 2.0.5-8
- Update for new upstream release tarball: Pacemaker-2.0.5
for full details, see included ChangeLog file or
commit a8619d3eb126acef158f32277e3ee7c917446a57
Author: Klaus Wenninger <klaus.wenninger(a)aon.at>
Date: Fri Dec 4 17:11:33 2020 +0100
* Fri Dec 4 2020 Klaus Wenninger <kwenning(a)redhat.com> - 2.0.5-8
- Update for new upstream release tarball: Pacemaker-2.0.5
for full details, see included ChangeLog file or
https://github.com/ClusterLabs/pacemaker/releases/tag/Pacemaker-2.0.5
diff --git a/pacemaker.spec b/pacemaker.spec
index a8d6773..056493c 100644
--- a/pacemaker.spec
+++ b/pacemaker.spec
@@ -19,11 +19,11 @@
## can be incremented to build packages reliably considered "newer"
## than previously built packages with the same pcmkversion)
%global pcmkversion 2.0.5
-%global specversion 7
+%global specversion 8
## Upstream commit (or git tag, such as "Pacemaker-" plus the
## {pcmkversion} macro for an official release) to use for this package
-%global commit Pacemaker-2.0.5-rc3
+%global commit Pacemaker-2.0.5
## Since git v2.11, the extent of abbreviation is autoscaled by default
## (used to be constant of 7), so we need to convey it for non-tags, too.
%global commit_abbrev 9
@@ -52,7 +52,7 @@
## Add option to prefix package version with "0."
## (so later "official" packages will be considered updates)
-%bcond_without pre_release
+%bcond_with pre_release
## NOTE: skip --with upstart_job
@@ -717,6 +717,11 @@ exit 0
%license %{nagios_name}-%{nagios_hash}/COPYING
%changelog
+* Fri Dec 4 2020 Klaus Wenninger <kwenning(a)redhat.com> - 2.0.5-8
+- Update for new upstream release tarball: Pacemaker-2.0.5
+ for full details, see included ChangeLog file or
+ https://github.com/ClusterLabs/pacemaker/releases/tag/Pacemaker-2.0.5
+
* Wed Nov 18 2020 Klaus Wenninger <kwenning(a)redhat.com> - 2.0.5-0.7.rc3
- a little more syncing with upstream spec-file
diff --git a/sources b/sources
index f863b6a..b19a593 100644
--- a/sources
+++ b/sources
@@ -1,2 +1,2 @@
SHA512 (nagios-agents-metadata-105ab8a7b2c16b9a29cf1c1596b80136eeef332b.tar.gz) = 11ddeb48a4929e7642b6dfa9c7962aa1d7a1af1c569830f55ed6cd6773abac13377317327bc1db8411c8077884f83f81cc54d746c834b63a99fa6dc219b5caad
-SHA512 (pacemaker-Pacemaker-2.0.5-rc3.tar.gz) = adf558d80d93ee7944b8e9eb5d07304e374ee61a4d81159daa9c6a9108ee77294e109766c21ed6f6eef2847ebc67d98925b8c63ff49bba958058bb7ef68d0f07
+SHA512 (pacemaker-Pacemaker-2.0.5.tar.gz) = 5fd3614f0284297babb66ea2dc567583315052fcf77f49107c52161e8e8c164ae3169d98528fdc1316d8eabaacc98ed24e9c6e6c90b3286f2f012a4ee874bbba
commit fecb07e0d0e09f1a5056fd1cef3962745cc18b1d
Author: Klaus Wenninger <klaus.wenninger(a)aon.at>
Date: Wed Nov 18 11:49:19 2020 +0100
* Wed Nov 18 2020 Klaus Wenninger <kwenning(a)redhat.com> - 2.0.5-0.7.rc3
- a little more syncing with upstream spec-file
diff --git a/pacemaker.spec b/pacemaker.spec
index bcab6ca..a8d6773 100644
--- a/pacemaker.spec
+++ b/pacemaker.spec
@@ -3,6 +3,7 @@
## User and group to use for nonprivileged services
%global uname hacluster
%global gname haclient
+%global hacluster_id 189
## Where to install Pacemaker documentation
%if 0%{?rhel}
@@ -18,7 +19,7 @@
## can be incremented to build packages reliably considered "newer"
## than previously built packages with the same pcmkversion)
%global pcmkversion 2.0.5
-%global specversion 6
+%global specversion 7
## Upstream commit (or git tag, such as "Pacemaker-" plus the
## {pcmkversion} macro for an official release) to use for this package
@@ -508,7 +509,7 @@ fi
%post cli
%systemd_post crm_mon.service
-if [ "$1" = 2 ]; then
+if [ "$1" -eq 2 ]; then
# Package upgrade, not initial install:
# Move any pre-2.0 logs to new location to ensure they get rotated
{ mv -fbS.rpmsave %{_var}/log/pacemaker.log* %{_var}/log/pacemaker \
@@ -526,8 +527,8 @@ fi
# XXX keep an eye on https://fedoraproject.org/wiki/Changes/SystemdSysusers
# reopened recently:
# https://lists.fedoraproject.org/archives/list/devel@lists.fedoraproject.o...
-getent group %{gname} >/dev/null || groupadd -r %{gname} -g 189
-getent passwd %{uname} >/dev/null || useradd -r -g %{gname} -u 189 -s /sbin/nologin -c "cluster user" %{uname}
+getent group %{gname} >/dev/null || groupadd -r %{gname} -g %{hacluster_id}
+getent passwd %{uname} >/dev/null || useradd -r -g %{gname} -u %{hacluster_id} -s /sbin/nologin -c "cluster user" %{uname}
exit 0
%ldconfig_scriptlets libs
@@ -716,6 +717,9 @@ exit 0
%license %{nagios_name}-%{nagios_hash}/COPYING
%changelog
+* Wed Nov 18 2020 Klaus Wenninger <kwenning(a)redhat.com> - 2.0.5-0.7.rc3
+- a little more syncing with upstream spec-file
+
* Tue Nov 17 2020 Klaus Wenninger <kwenning(a)redhat.com> - 2.0.5-0.6.rc3
- Update for new upstream tarball for release candidate: Pacemaker-2.0.5-rc3
for full details, see included ChangeLog file or
commit b8f03333b11afe4a833b4c91ac0d881d9a956958
Author: Klaus Wenninger <klaus.wenninger(a)aon.at>
Date: Tue Nov 17 16:15:13 2020 +0100
* Tue Nov 17 2020 Klaus Wenninger <kwenning(a)redhat.com> - 2.0.5-0.6.rc3
- Update for new upstream tarball for release candidate: Pacemaker-2.0.5-rc3
for full details, see included ChangeLog file or
https://github.com/ClusterLabs/pacemaker/releases/tag/Pacemaker-2.0.5-rc3
- Corosync in Fedora now provides corosync-devel as well in isa-flavor
diff --git a/pacemaker.spec b/pacemaker.spec
index 816df62..bcab6ca 100644
--- a/pacemaker.spec
+++ b/pacemaker.spec
@@ -18,11 +18,11 @@
## can be incremented to build packages reliably considered "newer"
## than previously built packages with the same pcmkversion)
%global pcmkversion 2.0.5
-%global specversion 5
+%global specversion 6
## Upstream commit (or git tag, such as "Pacemaker-" plus the
## {pcmkversion} macro for an official release) to use for this package
-%global commit Pacemaker-2.0.5-rc2
+%global commit Pacemaker-2.0.5-rc3
## Since git v2.11, the extent of abbreviation is autoscaled by default
## (used to be constant of 7), so we need to convey it for non-tags, too.
%global commit_abbrev 9
@@ -176,7 +176,7 @@ BuildRequires: help2man gnutls-devel pam-devel pkgconfig(dbus-1)
BuildRequires: pkgconfig(systemd)
Requires: corosync >= 2.0.0
-BuildRequires: corosynclib-devel >= 2.0.0
+BuildRequires: corosync-devel >= 2.0.0
#XXX
#BuildRequires: pkgconfig(libcpg)
#BuildRequires: pkgconfig(libcfg)
@@ -287,7 +287,7 @@ Requires: libtool-ltdl-devel%{?_isa} libuuid-devel%{?_isa}
Requires: libxml2-devel%{?_isa} libxslt-devel%{?_isa}
Requires: bzip2-devel%{?_isa} glib2-devel%{?_isa}
Requires: libqb-devel%{?_isa}
-Requires: corosynclib-devel%{?_isa} >= 2.0.0
+Requires: corosync-devel%{?_isa} >= 2.0.0
%description libs-devel
Pacemaker is an advanced, scalable High-Availability cluster resource
@@ -716,6 +716,11 @@ exit 0
%license %{nagios_name}-%{nagios_hash}/COPYING
%changelog
+* Tue Nov 17 2020 Klaus Wenninger <kwenning(a)redhat.com> - 2.0.5-0.6.rc3
+- Update for new upstream tarball for release candidate: Pacemaker-2.0.5-rc3
+ for full details, see included ChangeLog file or
+ https://github.com/ClusterLabs/pacemaker/releases/tag/Pacemaker-2.0.5-rc3
+- Corosync in Fedora now provides corosync-devel as well in isa-flavor
* Sun Nov 1 2020 Klaus Wenninger <kwenning(a)redhat.com> - 2.0.5-0.5.rc2
- remove no more working dist.rpmdeplint from gating
diff --git a/sources b/sources
index 19d2b5b..f863b6a 100644
--- a/sources
+++ b/sources
@@ -1,2 +1,2 @@
SHA512 (nagios-agents-metadata-105ab8a7b2c16b9a29cf1c1596b80136eeef332b.tar.gz) = 11ddeb48a4929e7642b6dfa9c7962aa1d7a1af1c569830f55ed6cd6773abac13377317327bc1db8411c8077884f83f81cc54d746c834b63a99fa6dc219b5caad
-SHA512 (pacemaker-Pacemaker-2.0.5-rc2.tar.gz) = 67fe1ca539f4a690c8b9860c9b82679d77a77294ee9278c384b68099f788ba4db4d2c35bf34d1340daa6d3bac7aae550ef06527046da601a7bde30b78ee110a7
+SHA512 (pacemaker-Pacemaker-2.0.5-rc3.tar.gz) = adf558d80d93ee7944b8e9eb5d07304e374ee61a4d81159daa9c6a9108ee77294e109766c21ed6f6eef2847ebc67d98925b8c63ff49bba958058bb7ef68d0f07
commit be156c4421004a24b5bb62b4c638b06befc9ad72
Author: Klaus Wenninger <klaus.wenninger(a)aon.at>
Date: Sun Nov 1 23:13:39 2020 +0100
* Sun Nov 1 2020 Klaus Wenninger <kwenning(a)redhat.com> - 2.0.5-0.5.rc2
- remove no more working dist.rpmdeplint from gating
diff --git a/pacemaker.spec b/pacemaker.spec
index 5261cae..816df62 100644
--- a/pacemaker.spec
+++ b/pacemaker.spec
@@ -18,7 +18,7 @@
## can be incremented to build packages reliably considered "newer"
## than previously built packages with the same pcmkversion)
%global pcmkversion 2.0.5
-%global specversion 6
+%global specversion 5
## Upstream commit (or git tag, such as "Pacemaker-" plus the
## {pcmkversion} macro for an official release) to use for this package
@@ -717,7 +717,7 @@ exit 0
%changelog
-* Sun Nov 1 2020 Klaus Wenninger <kwenning(a)redhat.com> - 2.0.5-0.6.rc2
+* Sun Nov 1 2020 Klaus Wenninger <kwenning(a)redhat.com> - 2.0.5-0.5.rc2
- remove no more working dist.rpmdeplint from gating
* Fri Oct 30 2020 Klaus Wenninger <kwenning(a)redhat.com> - 2.0.5-0.4.rc2
commit 6d62f98baff974d549d51c25427647a9dfa8dfc4
Author: Klaus Wenninger <klaus.wenninger(a)aon.at>
Date: Sun Nov 1 23:11:04 2020 +0100
* Sun Nov 1 2020 Klaus Wenninger <kwenning(a)redhat.com> - 2.0.5-0.6.rc2
- remove no more working dist.rpmdeplint from gating
diff --git a/gating.yaml b/gating.yaml
index b829170..9e15ced 100644
--- a/gating.yaml
+++ b/gating.yaml
@@ -4,7 +4,6 @@ product_versions:
decision_context: bodhi_update_push_testing
subject_type: koji_build
rules:
- - !PassingTestCaseRule {test_case_name: dist.rpmdeplint}
- !PassingTestCaseRule {test_case_name: fedora-ci.koji-build.tier0.functional}
--- !Policy
product_versions:
@@ -12,6 +11,5 @@ product_versions:
decision_context: bodhi_update_push_stable
subject_type: koji_build
rules:
- - !PassingTestCaseRule {test_case_name: dist.rpmdeplint}
- !PassingTestCaseRule {test_case_name: fedora-ci.koji-build.tier0.functional}
diff --git a/pacemaker.spec b/pacemaker.spec
index fe13aed..5261cae 100644
--- a/pacemaker.spec
+++ b/pacemaker.spec
@@ -18,7 +18,7 @@
## can be incremented to build packages reliably considered "newer"
## than previously built packages with the same pcmkversion)
%global pcmkversion 2.0.5
-%global specversion 5
+%global specversion 6
## Upstream commit (or git tag, such as "Pacemaker-" plus the
## {pcmkversion} macro for an official release) to use for this package
@@ -716,7 +716,8 @@ exit 0
%license %{nagios_name}-%{nagios_hash}/COPYING
%changelog
-* Sun Nov 1 2020 Klaus Wenninger <kwenning(a)redhat.com> - 2.0.5-0.5.rc2
+
+* Sun Nov 1 2020 Klaus Wenninger <kwenning(a)redhat.com> - 2.0.5-0.6.rc2
- remove no more working dist.rpmdeplint from gating
* Fri Oct 30 2020 Klaus Wenninger <kwenning(a)redhat.com> - 2.0.5-0.4.rc2
commit 5ed78e234f212f871e38af6e50ec94ad5efa8711
Author: Klaus Wenninger <klaus.wenninger(a)aon.at>
Date: Sun Nov 1 22:52:35 2020 +0100
* Sun Nov 1 2020 Klaus Wenninger <kwenning(a)redhat.com> - 2.0.5-0.5.rc2
- remove no more working dist.rpmdeplint from gating
diff --git a/pacemaker.spec b/pacemaker.spec
index 56510b2..fe13aed 100644
--- a/pacemaker.spec
+++ b/pacemaker.spec
@@ -18,7 +18,7 @@
## can be incremented to build packages reliably considered "newer"
## than previously built packages with the same pcmkversion)
%global pcmkversion 2.0.5
-%global specversion 4
+%global specversion 5
## Upstream commit (or git tag, such as "Pacemaker-" plus the
## {pcmkversion} macro for an official release) to use for this package
@@ -716,6 +716,9 @@ exit 0
%license %{nagios_name}-%{nagios_hash}/COPYING
%changelog
+* Sun Nov 1 2020 Klaus Wenninger <kwenning(a)redhat.com> - 2.0.5-0.5.rc2
+- remove no more working dist.rpmdeplint from gating
+
* Fri Oct 30 2020 Klaus Wenninger <kwenning(a)redhat.com> - 2.0.5-0.4.rc2
- never use spec-variables in changelog
- replace dist.depcheck by dist.rpmdeplint
commit 20ba4de547c39a877978f48b670bbc3fb105125e
Author: Klaus Wenninger <klaus.wenninger(a)aon.at>
Date: Fri Oct 30 12:26:27 2020 +0100
* Fri Oct 30 2020 Klaus Wenninger <kwenning(a)redhat.com> - 2.0.5-0.4.rc2
- never use spec-variables in changelog
- replace dist.depcheck by dist.rpmdeplint
- do gate stable as well to be effective on rawhide
diff --git a/pacemaker.spec b/pacemaker.spec
index 7bfd66f..56510b2 100644
--- a/pacemaker.spec
+++ b/pacemaker.spec
@@ -18,7 +18,7 @@
## can be incremented to build packages reliably considered "newer"
## than previously built packages with the same pcmkversion)
%global pcmkversion 2.0.5
-%global specversion 3
+%global specversion 4
## Upstream commit (or git tag, such as "Pacemaker-" plus the
## {pcmkversion} macro for an official release) to use for this package
commit 1576d8e7818e16c7f86d01f0d3d89ac3d45eab31
Author: Klaus Wenninger <klaus.wenninger(a)aon.at>
Date: Fri Oct 30 12:24:05 2020 +0100
* Fri Oct 30 2020 Klaus Wenninger <kwenning(a)redhat.com> - 2.0.5-0.4.rc2
- never use spec-variables in changelog
- replace dist.depcheck by dist.rpmdeplint
- do gate stable as well to be effective on rawhide
diff --git a/gating.yaml b/gating.yaml
index 1af3453..b829170 100644
--- a/gating.yaml
+++ b/gating.yaml
@@ -4,6 +4,14 @@ product_versions:
decision_context: bodhi_update_push_testing
subject_type: koji_build
rules:
- - !PassingTestCaseRule {test_case_name: dist.depcheck}
+ - !PassingTestCaseRule {test_case_name: dist.rpmdeplint}
+ - !PassingTestCaseRule {test_case_name: fedora-ci.koji-build.tier0.functional}
+--- !Policy
+product_versions:
+ - fedora-*
+decision_context: bodhi_update_push_stable
+subject_type: koji_build
+rules:
+ - !PassingTestCaseRule {test_case_name: dist.rpmdeplint}
- !PassingTestCaseRule {test_case_name: fedora-ci.koji-build.tier0.functional}
diff --git a/pacemaker.spec b/pacemaker.spec
index 4d14ad5..7bfd66f 100644
--- a/pacemaker.spec
+++ b/pacemaker.spec
@@ -716,9 +716,14 @@ exit 0
%license %{nagios_name}-%{nagios_hash}/COPYING
%changelog
+* Fri Oct 30 2020 Klaus Wenninger <kwenning(a)redhat.com> - 2.0.5-0.4.rc2
+- never use spec-variables in changelog
+- replace dist.depcheck by dist.rpmdeplint
+- do gate stable as well to be effective on rawhide
+
* Fri Oct 30 2020 Klaus Wenninger <kwenning(a)redhat.com> - 2.0.5-0.3.rc2
- revert dependency corosync-devel back to corosynclib-devel as long
- as corosynclib-devel-package doesn't provide corosync-devel%{?_isa}
+ as corosynclib-devel-package doesn't provide corosync-devel(isa)
we would need for pacemaker-libs-devel to require
- enable some basic gating-tests
- re-add building documentation using publican to everything but ELN
commit af37d53a69a8deabad0a23cf7dca1f2009c145b2
Author: Klaus Wenninger <klaus.wenninger(a)aon.at>
Date: Fri Oct 30 08:24:58 2020 +0100
* Fri Oct 30 2020 Klaus Wenninger <kwenning(a)redhat.com> - 2.0.5-0.3.rc2
- revert dependency corosync-devel back to corosynclib-devel as long
as corosynclib-devel-package doesn't provide corosync-devel%{?_isa}
we would need for pacemaker-libs-devel to require
- enable some basic gating-tests
- re-add building documentation using publican to everything but ELN
- rename doc-dir for ELN
diff --git a/gating.yaml b/gating.yaml
new file mode 100644
index 0000000..1af3453
--- /dev/null
+++ b/gating.yaml
@@ -0,0 +1,9 @@
+--- !Policy
+product_versions:
+ - fedora-*
+decision_context: bodhi_update_push_testing
+subject_type: koji_build
+rules:
+ - !PassingTestCaseRule {test_case_name: dist.depcheck}
+ - !PassingTestCaseRule {test_case_name: fedora-ci.koji-build.tier0.functional}
+
diff --git a/pacemaker.spec b/pacemaker.spec
index 256e4c7..4d14ad5 100644
--- a/pacemaker.spec
+++ b/pacemaker.spec
@@ -5,7 +5,11 @@
%global gname haclient
## Where to install Pacemaker documentation
+%if 0%{?rhel}
+%global pcmk_docdir %{_docdir}/%{name}-doc
+%else
%global pcmk_docdir %{_docdir}/%{name}
+%endif
## GitHub entity that distributes source (for ease of using a fork)
%global github_owner ClusterLabs
@@ -14,7 +18,7 @@
## can be incremented to build packages reliably considered "newer"
## than previously built packages with the same pcmkversion)
%global pcmkversion 2.0.5
-%global specversion 2
+%global specversion 3
## Upstream commit (or git tag, such as "Pacemaker-" plus the
## {pcmkversion} macro for an official release) to use for this package
@@ -37,9 +41,13 @@
## Add option to create binaries with coverage analysis
%bcond_with coverage
-## Add option to skip generating documentation
+## Add option to skip/enable generating documentation
## (the build tools aren't available everywhere)
-%bcond_with doc
+%if 0%{?rhel}
+ %bcond_with doc
+%else
+ %bcond_without doc
+%endif
## Add option to prefix package version with "0."
## (so later "official" packages will be considered updates)
@@ -168,7 +176,7 @@ BuildRequires: help2man gnutls-devel pam-devel pkgconfig(dbus-1)
BuildRequires: pkgconfig(systemd)
Requires: corosync >= 2.0.0
-BuildRequires: corosync-devel >= 2.0.0
+BuildRequires: corosynclib-devel >= 2.0.0
#XXX
#BuildRequires: pkgconfig(libcpg)
#BuildRequires: pkgconfig(libcfg)
@@ -279,7 +287,7 @@ Requires: libtool-ltdl-devel%{?_isa} libuuid-devel%{?_isa}
Requires: libxml2-devel%{?_isa} libxslt-devel%{?_isa}
Requires: bzip2-devel%{?_isa} glib2-devel%{?_isa}
Requires: libqb-devel%{?_isa}
-Requires: corosync-devel%{?_isa} >= 2.0.0
+Requires: corosynclib-devel%{?_isa} >= 2.0.0
%description libs-devel
Pacemaker is an advanced, scalable High-Availability cluster resource
@@ -708,6 +716,14 @@ exit 0
%license %{nagios_name}-%{nagios_hash}/COPYING
%changelog
+* Fri Oct 30 2020 Klaus Wenninger <kwenning(a)redhat.com> - 2.0.5-0.3.rc2
+- revert dependency corosync-devel back to corosynclib-devel as long
+ as corosynclib-devel-package doesn't provide corosync-devel%{?_isa}
+ we would need for pacemaker-libs-devel to require
+- enable some basic gating-tests
+- re-add building documentation using publican to everything but ELN
+- rename doc-dir for ELN
+
* Wed Oct 28 2020 Klaus Wenninger <kwenning(a)redhat.com> - 2.0.5-0.2.rc2
- Update for new upstream tarball for release candidate: Pacemaker-2.0.5-rc2,
includes fix for CVE-2020-25654
diff --git a/tests/inventory b/tests/inventory
new file mode 100755
index 0000000..52687db
--- /dev/null
+++ b/tests/inventory
@@ -0,0 +1,4 @@
+#!/bin/bash
+export TEST_DOCKER_EXTRA_ARGS="--network host"
+exec merge-standard-inventory "$@"
+
diff --git a/tests/tests.yml b/tests/tests.yml
new file mode 100644
index 0000000..71d4612
--- /dev/null
+++ b/tests/tests.yml
@@ -0,0 +1,14 @@
+---
+- hosts: localhost
+ roles:
+ - role: standard-test-basic
+ tags:
+ - classic
+ - container
+ tests:
+ - cts-regression:
+ dir: .
+ run: /usr/share/pacemaker/tests/cts-regression cli scheduler
+ required_packages:
+ - pacemaker
+ - pacemaker-cts
commit 739244b77b91a37a1c52828cfbac4d6930f95cd9
Author: Klaus Wenninger <klaus.wenninger(a)aon.at>
Date: Wed Oct 28 11:31:25 2020 +0100
* Wed Oct 28 2020 Klaus Wenninger <kwenning(a)redhat.com> - 2.0.5-0.2.rc2
- Update for new upstream tarball for release candidate: Pacemaker-2.0.5-rc2,
includes fix for CVE-2020-25654
for full details, see included ChangeLog file or
https://github.com/ClusterLabs/pacemaker/releases/tag/Pacemaker-2.0.5-rc2
diff --git a/pacemaker.spec b/pacemaker.spec
index d1076e0..256e4c7 100644
--- a/pacemaker.spec
+++ b/pacemaker.spec
@@ -14,11 +14,11 @@
## can be incremented to build packages reliably considered "newer"
## than previously built packages with the same pcmkversion)
%global pcmkversion 2.0.5
-%global specversion 1
+%global specversion 2
## Upstream commit (or git tag, such as "Pacemaker-" plus the
## {pcmkversion} macro for an official release) to use for this package
-%global commit Pacemaker-2.0.5-rc1
+%global commit Pacemaker-2.0.5-rc2
## Since git v2.11, the extent of abbreviation is autoscaled by default
## (used to be constant of 7), so we need to convey it for non-tags, too.
%global commit_abbrev 9
@@ -71,7 +71,7 @@
## Portion of export/dist tarball name after "pacemaker-", and release version
%if 0%{tag_release}
-%define archive_version %(c=%{commit}; echo ${c:10})
+%define archive_version %{commit}
%define archive_github_url %{commit}#/%{name}-%{archive_version}.tar.gz
%define pcmk_release %(c=%{commit}; case $c in *-rc[[:digit:]]*%{rparen}
echo 0.%{specversion}.${c: -3} ;;
@@ -342,7 +342,7 @@ The metadata files required for Pacemaker to execute the nagios plugin
monitor resources.
%prep
-%setup -q -a 1 -n %{name}-%{commit}
+%setup -q -a 1 -n %{name}-%{archive_version}
%build
@@ -708,6 +708,12 @@ exit 0
%license %{nagios_name}-%{nagios_hash}/COPYING
%changelog
+* Wed Oct 28 2020 Klaus Wenninger <kwenning(a)redhat.com> - 2.0.5-0.2.rc2
+- Update for new upstream tarball for release candidate: Pacemaker-2.0.5-rc2,
+ includes fix for CVE-2020-25654
+ for full details, see included ChangeLog file or
+ https://github.com/ClusterLabs/pacemaker/releases/tag/Pacemaker-2.0.5-rc2
+
* Thu Oct 22 2020 Klaus Wenninger <kwenning(a)redhat.com> - 2.0.5-0.1.rc1
- Update for new upstream tarball for release candidate: Pacemaker-2.0.5-rc1,
for full details, see included ChangeLog file or
diff --git a/sources b/sources
index fa48479..19d2b5b 100644
--- a/sources
+++ b/sources
@@ -1,2 +1,2 @@
SHA512 (nagios-agents-metadata-105ab8a7b2c16b9a29cf1c1596b80136eeef332b.tar.gz) = 11ddeb48a4929e7642b6dfa9c7962aa1d7a1af1c569830f55ed6cd6773abac13377317327bc1db8411c8077884f83f81cc54d746c834b63a99fa6dc219b5caad
-SHA512 (pacemaker-2.0.5-rc1.tar.gz) = 90e2994df8ee60f525dd919a5edad2c48bbe60fabd42318527c02bc47900bff24901f73eb6498d3fdf75996539e647460b540375008d263baf7890f271fae76c
+SHA512 (pacemaker-Pacemaker-2.0.5-rc2.tar.gz) = 67fe1ca539f4a690c8b9860c9b82679d77a77294ee9278c384b68099f788ba4db4d2c35bf34d1340daa6d3bac7aae550ef06527046da601a7bde30b78ee110a7
commit c7391e41b80340cf9b0cbac6c88c21a7d45f3b8f
Author: Klaus Wenninger <klaus.wenninger(a)aon.at>
Date: Mon Oct 26 14:09:03 2020 +0100
* Thu Oct 22 2020 Klaus Wenninger <kwenning(a)redhat.com> - 2.0.5-0.1.rc1
- Update for new upstream tarball for release candidate: Pacemaker-2.0.5-rc1,
for full details, see included ChangeLog file or
https://github.com/ClusterLabs/pacemaker/releases/tag/Pacemaker-2.0.5-rc1
- Disable building of documentation - as not to pull in publican
- Remove dependencies to nagios-plugins from metadata-package
- some sync with structure of upstream spec-file
- removed some legacy conditionals
- added with-cibsecrets
diff --git a/pacemaker.spec b/pacemaker.spec
index 7b56863..d1076e0 100644
--- a/pacemaker.spec
+++ b/pacemaker.spec
@@ -13,20 +13,51 @@
## Upstream pacemaker version, and its package version (specversion
## can be incremented to build packages reliably considered "newer"
## than previously built packages with the same pcmkversion)
-%global pcmkversion 2.0.4
+%global pcmkversion 2.0.5
%global specversion 1
## Upstream commit (or git tag, such as "Pacemaker-" plus the
## {pcmkversion} macro for an official release) to use for this package
-%global commit Pacemaker-2.0.4
+%global commit Pacemaker-2.0.5-rc1
## Since git v2.11, the extent of abbreviation is autoscaled by default
## (used to be constant of 7), so we need to convey it for non-tags, too.
%global commit_abbrev 9
+# Define conditionals so that "rpmbuild --with <feature>" and
+# "rpmbuild --without <feature>" can enable and disable specific features
+
+## NOTE: skip --with stonithd
+
+## Add option to enable support for storing sensitive information outside CIB
+%bcond_with cibsecrets
+
+## Add option to create binaries suitable for use with profiling tools
+%bcond_with profiling
+
+## Add option to create binaries with coverage analysis
+%bcond_with coverage
+
+## Add option to skip generating documentation
+## (the build tools aren't available everywhere)
+%bcond_with doc
+
+## Add option to prefix package version with "0."
+## (so later "official" packages will be considered updates)
+%bcond_without pre_release
+
+## NOTE: skip --with upstart_job
+
+## Add option to turn off hardening of libraries and daemon executables
+%bcond_without hardening
+
+## Add option to disable links for legacy daemon names
+%bcond_without legacy_links
+
+
## Nagios source control identifiers
%global nagios_name nagios-agents-metadata
%global nagios_hash 105ab8a7b2c16b9a29cf1c1596b80136eeef332b
-
+%global nagios_archive_github_url %{nagios_hash}#/%{nagios_name}-%{nagios_hash}.tar.gz
# Define globals for convenient use later
@@ -34,18 +65,26 @@
%global lparen (
%global rparen )
-## Short version of git commit
-%define shortcommit %(c=%{commit}; case ${c} in
- Pacemaker-*%{rparen} echo ${c:10};;
- *%{rparen} echo ${c:0:%{commit_abbrev}};; esac)
+## Whether this is a tagged release (final or release candidate)
+%define tag_release %(c=%{commit}; case ${c} in Pacemaker-*%{rparen} echo 1 ;;
+ *%{rparen} echo 0 ;; esac)
-## Whether this is a tagged release
-%define tag_release %([ %{commit} != Pacemaker-%{shortcommit} ]; echo $?)
-
-## Whether this is a release candidate (in case of a tagged release)
-%define pre_release %([ "%{tag_release}" -eq 0 ] || {
- case "%{shortcommit}" in *-rc[[:digit:]]*%{rparen} false;;
- esac; }; echo $?)
+## Portion of export/dist tarball name after "pacemaker-", and release version
+%if 0%{tag_release}
+%define archive_version %(c=%{commit}; echo ${c:10})
+%define archive_github_url %{commit}#/%{name}-%{archive_version}.tar.gz
+%define pcmk_release %(c=%{commit}; case $c in *-rc[[:digit:]]*%{rparen}
+ echo 0.%{specversion}.${c: -3} ;;
+ *%{rparen} echo %{specversion} ;; esac)
+%else
+%define archive_version %(c=%{commit}; echo ${c:0:%{commit_abbrev}})
+%define archive_github_url %{archive_version}#/%{name}-%{archive_version}.tar.gz
+%if %{with pre_release}
+%define pcmk_release 0.%{specversion}.%{archive_version}.git
+%else
+%define pcmk_release %{specversion}.%{archive_version}.git
+%endif
+%endif
## Heuristic used to infer bleeding-edge deployments that are
## less likely to have working versions of the documentation tools
@@ -71,40 +110,12 @@
%endif
## Values that differ by Python major version
-%global python_path /usr/bin/python%{?python3_pkgversion}%{!?python3_pkgversion:3}
-%global python_pkg python3
+%global python_name python3
+%global python_path %{?__python3}%{!?__python3:/usr/bin/python%{?python3_pkgversion}%{!?python3_pkgversion:3}}
+%define python_site %{?python3_sitelib}%{!?python3_sitelib:%(
+ %{python_path} -c 'from distutils.sysconfig import get_python_lib as gpl; print(gpl(1))' 2>/dev/null)}
%global python_min 3.2
-%define py_site %{?python3_sitelib}%{!?python3_sitelib:%(
- python3 -c 'from distutils.sysconfig import get_python_lib as gpl; print(gpl(1))' 2>/dev/null)}
-
-
-# Define conditionals so that "rpmbuild --with <feature>" and
-# "rpmbuild --without <feature>" can enable and disable specific features
-
-## NOTE: skip --with stonith
-
-## Add option to create binaries suitable for use with profiling tools
-%bcond_with profiling
-
-## Add option to create binaries with coverage analysis
-%bcond_with coverage
-
-## Add option to skip generating documentation
-## (the build tools aren't available everywhere)
-%bcond_without doc
-
-## Add option to prefix package version with "0."
-## (so later "official" packages will be considered updates)
-%bcond_with pre_release
-
-## NOTE: skip --with upstart_job
-
-## Add option to turn off hardening of libraries and daemon executables
-%bcond_without hardening
-
-## Add option to disable links for legacy daemon names
-%bcond_without legacy_links
-
+# python_min still required?
# Keep sane profiling data if requested
%if %{with profiling}
@@ -115,33 +126,16 @@
%endif
-# Define the release version
-# (do not look at externally enforced pre-release flag for tagged releases
-# as only -rc tags, captured with the second condition, implies that then)
-%if (!%{tag_release} && %{with pre_release}) || 0%{pre_release}
-%if 0%{pre_release}
-%define pcmk_release 0.%{specversion}.%(s=%{shortcommit}; echo ${s: -3})
-%else
-%define pcmk_release 0.%{specversion}.%{shortcommit}.git
-%endif
-%else
-%if 0%{tag_release}
-%define pcmk_release %{specversion}
-%else
-%define pcmk_release %{specversion}.%{shortcommit}.git
-%endif
-%endif
-
Name: pacemaker
Summary: Scalable High-Availability cluster resource manager
Version: %{pcmkversion}
-Release: %{pcmk_release}%{?dist}.1
+Release: %{pcmk_release}%{?dist}
License: GPLv2+ and LGPLv2+
-Url: http://www.clusterlabs.org
+Url: https://www.clusterlabs.org
# Hint: use "spectool -s 0 pacemaker.spec" (rpmdevtools) to check the final URL
-Source0: https://github.com/%{github_owner}/%{name}/archive/%{commit}/%{name}-%{sh...
-Source1: https://github.com/%{github_owner}/%{nagios_name}/archive/%{nagios_hash}/...
+Source0: https://codeload.github.com/%{github_owner}/%{name}/tar.gz/%{archive_gith...
+Source1: https://codeload.github.com/%{github_owner}/%{nagios_name}/tar.gz/%{nagio...
# ---
Requires: resource-agents
@@ -151,8 +145,8 @@ Requires: %{name}-cli = %{version}-%{release}
%{?systemd_requires}
# Pacemaker requires a minimum Python functionality
-Requires: %{python_pkg} >= %{python_min}
-BuildRequires: %{python_pkg}-devel >= %{python_min}
+Requires: %{python_name} >= %{python_min}
+BuildRequires: %{python_name}-devel >= %{python_min}
# Pacemaker requires a minimum libqb functionality
Requires: libqb >= 0.13.0
@@ -174,7 +168,7 @@ BuildRequires: help2man gnutls-devel pam-devel pkgconfig(dbus-1)
BuildRequires: pkgconfig(systemd)
Requires: corosync >= 2.0.0
-BuildRequires: corosynclib-devel >= 2.0.0
+BuildRequires: corosync-devel >= 2.0.0
#XXX
#BuildRequires: pkgconfig(libcpg)
#BuildRequires: pkgconfig(libcfg)
@@ -207,15 +201,16 @@ when related resources fail and can be configured to periodically check
resource health.
Available rpmbuild rebuild options:
- --with(out) : coverage doc hardening pre_release profiling
+ --with(out) : cibsecrets coverage doc hardening pre_release profiling
%package cli
License: GPLv2+ and LGPLv2+
Summary: Command line tools for controlling Pacemaker clusters
Requires: %{name}-libs%{?_isa} = %{version}-%{release}
-%if 0%{?fedora} > 22 || 0%{?rhel} > 7
Recommends: pcmk-cluster-manager = %{version}-%{release}
-%endif
+# For crm_report
+Recommends: tar
+Recommends: bzip2
Requires: perl-TimeDate
Requires: procps-ng
Requires: psmisc
@@ -284,7 +279,7 @@ Requires: libtool-ltdl-devel%{?_isa} libuuid-devel%{?_isa}
Requires: libxml2-devel%{?_isa} libxslt-devel%{?_isa}
Requires: bzip2-devel%{?_isa} glib2-devel%{?_isa}
Requires: libqb-devel%{?_isa}
-Requires: corosynclib-devel%{?_isa} >= 2.0.0
+Requires: corosync-devel%{?_isa} >= 2.0.0
%description libs-devel
Pacemaker is an advanced, scalable High-Availability cluster resource
@@ -296,19 +291,20 @@ for developing tools for Pacemaker.
%package cts
License: GPLv2+ and LGPLv2+
Summary: Test framework for cluster-related technologies like Pacemaker
-Requires: %{python_pkg} >= %{python_min}
+Requires: %{python_path}
+Requires: %{python_name} >= %{python_min}
Requires: %{name}-libs = %{version}-%{release}
Requires: procps-ng
Requires: psmisc
BuildArch: noarch
-Requires: %{python_pkg}-systemd
+Requires: %{python_name}-systemd
%description cts
Test framework for cluster-related technologies like Pacemaker
%package doc
-License: CC-BY-SA
+License: CC-BY-SA-4.0
Summary: Documentation for Pacemaker
BuildArch: noarch
@@ -334,11 +330,11 @@ License: GPLv3
Summary: Pacemaker Nagios Metadata
BuildArch: noarch
# NOTE below are the plugins this metadata uses.
-Requires: nagios-plugins-http
-Requires: nagios-plugins-ldap
-Requires: nagios-plugins-mysql
-Requires: nagios-plugins-pgsql
-Requires: nagios-plugins-tcp
+# Requires: nagios-plugins-http
+# Requires: nagios-plugins-ldap
+# Requires: nagios-plugins-mysql
+# Requires: nagios-plugins-pgsql
+# Requires: nagios-plugins-tcp
Requires: pcmk-cluster-manager
%description nagios-plugins-metadata
@@ -347,15 +343,9 @@ monitor resources.
%prep
%setup -q -a 1 -n %{name}-%{commit}
-#global __scm git_am
-#__scm_setup_git
-#autopatch -p1
%build
-# Early versions of autotools (e.g. RHEL <= 5) do not support --docdir
-export docdir=%{pcmk_docdir}
-
export systemdsystemunitdir=%{?_unitdir}%{!?_unitdir:no}
%if %{with hardening}
@@ -381,6 +371,7 @@ export CPPFLAGS="-UPCMK_TIME_EMERGENCY_CGT $CPPFLAGS"
%{!?with_legacy_links: --disable-legacy-links} \
%{?with_profiling: --with-profiling} \
%{?with_coverage: --with-coverage} \
+ %{?with_cibsecrets: --with-cibsecrets} \
%{!?with_doc: --with-brand=} \
%{?gnutls_priorities: --with-gnutls-priorities="%{gnutls_priorities}"} \
--disable-static \
@@ -396,6 +387,7 @@ export CPPFLAGS="-UPCMK_TIME_EMERGENCY_CGT $CPPFLAGS"
make %{_smp_mflags} V=1
%check
+make %{_smp_mflags} check
{ cts/cts-scheduler --run load-stopped-loop \
&& cts/cts-cli \
&& touch .CHECKED
@@ -448,7 +440,7 @@ rm -f %{buildroot}/%{_initrddir}/pacemaker_remote
%if %{defined py_byte_compile} && %{defined python_path}
%{py_byte_compile %{python_path} %{buildroot}%{_datadir}/pacemaker/tests}
%if !%{defined _python_bytecompile_extra}
-%{py_byte_compile %{python_path} %{buildroot}%{py_site}/cts}
+%{py_byte_compile %{python_path} %{buildroot}%{python_site}/cts}
%endif
%endif
@@ -582,6 +574,9 @@ exit 0
%{_sbindir}/attrd_updater
%{_sbindir}/cibadmin
+%if %{with cibsecrets}
+%{_sbindir}/cibsecret
+%endif
%{_sbindir}/crm_diff
%{_sbindir}/crm_error
%{_sbindir}/crm_failcount
@@ -677,7 +672,7 @@ exit 0
%license licenses/CC-BY-SA-4.0
%files cts
-%{py_site}/cts
+%{python_site}/cts
%{_datadir}/pacemaker/tests
%{_libexecdir}/pacemaker/cts-log-watcher
@@ -713,6 +708,16 @@ exit 0
%license %{nagios_name}-%{nagios_hash}/COPYING
%changelog
+* Thu Oct 22 2020 Klaus Wenninger <kwenning(a)redhat.com> - 2.0.5-0.1.rc1
+- Update for new upstream tarball for release candidate: Pacemaker-2.0.5-rc1,
+ for full details, see included ChangeLog file or
+ https://github.com/ClusterLabs/pacemaker/releases/tag/Pacemaker-2.0.5-rc1
+- Disable building of documentation - as not to pull in publican
+- Remove dependencies to nagios-plugins from metadata-package
+- some sync with structure of upstream spec-file
+- removed some legacy conditionals
+- added with-cibsecrets
+
* Tue Jul 28 2020 Fedora Release Engineering <releng(a)fedoraproject.org> - 2.0.4-1.1
- Rebuilt for https://fedoraproject.org/wiki/Fedora_33_Mass_Rebuild
diff --git a/sources b/sources
index f0f399b..fa48479 100644
--- a/sources
+++ b/sources
@@ -1,2 +1,2 @@
SHA512 (nagios-agents-metadata-105ab8a7b2c16b9a29cf1c1596b80136eeef332b.tar.gz) = 11ddeb48a4929e7642b6dfa9c7962aa1d7a1af1c569830f55ed6cd6773abac13377317327bc1db8411c8077884f83f81cc54d746c834b63a99fa6dc219b5caad
-SHA512 (pacemaker-2.0.4.tar.gz) = fbff3b5ab05ad52d469304d5bc7fca518df2f491a1176a24735cd1a9b46f7ff90dc5aeae65e5e29feb86e8a210e69e528261138847d6f8773933fb557e71133d
+SHA512 (pacemaker-2.0.5-rc1.tar.gz) = 90e2994df8ee60f525dd919a5edad2c48bbe60fabd42318527c02bc47900bff24901f73eb6498d3fdf75996539e647460b540375008d263baf7890f271fae76c
2 years, 9 months
Architecture specific change in rpms/pacemaker.git
by githook-noreply@fedoraproject.org
The package rpms/pacemaker.git has added or updated architecture specific content in its
spec file (ExclusiveArch/ExcludeArch or %ifarch/%ifnarch) in commit(s):
https://src.fedoraproject.org/cgit/rpms/pacemaker.git/commit/?id=15ac1a3c....
Change:
+ExclusiveArch: aarch64 i686 ppc64le s390x x86_64
Thanks.
Full change:
============
commit a9ddf9be665d4fda694ee27bf35dfe51e2c5b614
Merge: 28e8e45 a74f8cf
Author: Klaus Wenninger <klaus.wenninger(a)aon.at>
Date: Fri Aug 20 13:12:54 2021 +0200
Merge branch 'rawhide' into f34
commit a74f8cfffa01425166cf6793858b614b09280026
Author: Klaus Wenninger <klaus.wenninger(a)aon.at>
Date: Fri Aug 20 01:22:00 2021 +0200
* Thu Aug 19 2021 Klaus Wenninger <kwenning(a)redhat.com> - 2.1.1-0.5.rc3
- Fix malformed xml in fence_watchdog metadata
diff --git a/0007-Fix-fence_watchdog-fix-malformed-xml-in-metadata.patch b/0007-Fix-fence_watchdog-fix-malformed-xml-in-metadata.patch
new file mode 100644
index 0000000..0d2c32e
--- /dev/null
+++ b/0007-Fix-fence_watchdog-fix-malformed-xml-in-metadata.patch
@@ -0,0 +1,25 @@
+From 46dd1118cae948649e000b2159e8e92623520ad9 Mon Sep 17 00:00:00 2001
+From: Klaus Wenninger <klaus.wenninger(a)aon.at>
+Date: Thu, 19 Aug 2021 09:28:54 +0200
+Subject: [PATCH] Fix: fence_watchdog: fix malformed xml in metadata
+
+---
+ daemons/fenced/fence_watchdog.in | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+diff --git a/daemons/fenced/fence_watchdog.in b/daemons/fenced/fence_watchdog.in
+index 700065e..eefa739 100755
+--- a/daemons/fenced/fence_watchdog.in
++++ b/daemons/fenced/fence_watchdog.in
+@@ -124,7 +124,7 @@ def metadata(avail_opt, options):
+ for option, dummy in sorted_options(avail_opt):
+ if "shortdesc" in ALL_OPT[option]:
+ print(' <parameter name="' + option +
+- 'required="' + ALL_OPT[option]["required"] + '">')
++ '" required="' + ALL_OPT[option]["required"] + '">')
+
+ default = ""
+ default_name_arg = "-" + ALL_OPT[option]["getopt"][:-1]
+--
+1.8.3.1
+
diff --git a/pacemaker.spec b/pacemaker.spec
index 2cd4aab..92eafb3 100644
--- a/pacemaker.spec
+++ b/pacemaker.spec
@@ -32,7 +32,7 @@
## can be incremented to build packages reliably considered "newer"
## than previously built packages with the same pcmkversion)
%global pcmkversion 2.1.1
-%global specversion 0.4.rc3
+%global specversion 0.5.rc3
## Upstream commit (full commit ID, abbreviated commit ID, or tag) to build
%global commit c6a4f6e6cdcedf08e0bfd81248b86812c2a571b2
@@ -221,6 +221,7 @@ Patch2: 0003-Fix-fence_watchdog-fix-version-output-needed-for-hel.patch
Patch3: 0004-Build-rpm-package-fence_watchdog-in-base-package.patch
Patch4: 0005-Fix-tools-make-exit-code-stdout-stderr-consistent-wi.patch
Patch5: 0006-Build-manpages-using-help2man-check-exit-code-of-too.patch
+Patch6: 0007-Fix-fence_watchdog-fix-malformed-xml-in-metadata.patch
# upstream commits
@@ -829,6 +830,9 @@ exit 0
%license %{nagios_name}-%{nagios_hash}/COPYING
%changelog
+* Thu Aug 19 2021 Klaus Wenninger <kwenning(a)redhat.com> - 2.1.1-0.5.rc3
+- Fix malformed xml in fence_watchdog metadata
+
* Tue Aug 17 2021 Klaus Wenninger <kwenning(a)redhat.com> - 2.1.1-0.4.rc3
- Update for new upstream tarball for release candidate: Pacemaker-2.1.1-rc3,
for full details, see included ChangeLog file or
commit b2569f82a6db1b7ddd4c28fc4e317f96fa3f5518
Author: Klaus Wenninger <klaus.wenninger(a)aon.at>
Date: Tue Aug 17 09:42:18 2021 +0200
* Tue Aug 17 2021 Klaus Wenninger <kwenning(a)redhat.com> - 2.1.1-0.4.rc3
- Update for new upstream tarball for release candidate: Pacemaker-2.1.1-rc3,
for full details, see included ChangeLog file or
https://github.com/ClusterLabs/pacemaker/releases/tag/Pacemaker-2.1.1-rc3
- Make tools exit-codes consistent for --version
- Check exit-codes of tools prior to manpage-generation
diff --git a/0005-Fix-controller-ensure-lost-node-s-transient-attribut.patch b/0005-Fix-controller-ensure-lost-node-s-transient-attribut.patch
deleted file mode 100644
index 3e64b25..0000000
--- a/0005-Fix-controller-ensure-lost-node-s-transient-attribut.patch
+++ /dev/null
@@ -1,122 +0,0 @@
-From ee7eba6a7a05bdf0a12d60ebabb334d8ee021101 Mon Sep 17 00:00:00 2001
-From: Ken Gaillot <kgaillot(a)redhat.com>
-Date: Mon, 9 Aug 2021 14:48:57 -0500
-Subject: [PATCH] Fix: controller: ensure lost node's transient attributes are
- cleared without DC
-
-Previously, peer_update_callback() cleared a lost node's transient attributes
-if either the local node is DC, or there is no DC.
-
-However, that left the possibility of the DC being lost at the same time as
-another node -- the local node would still have fsa_our_dc set while processing
-the leave notifications, so no node would clear the attributes for the non-DC
-node.
-
-Now, the controller has its own CPG configuration change callback, which sets a
-global boolean before calling the usual one, so that peer_update_callback() can
-know when the DC has been lost.
----
- daemons/controld/controld_callbacks.c | 4 ++-
- daemons/controld/controld_corosync.c | 57 ++++++++++++++++++++++++++++++++++-
- 2 files changed, 59 insertions(+), 2 deletions(-)
-
-diff --git a/daemons/controld/controld_callbacks.c b/daemons/controld/controld_callbacks.c
-index af24856..e564b3d 100644
---- a/daemons/controld/controld_callbacks.c
-+++ b/daemons/controld/controld_callbacks.c
-@@ -99,6 +99,8 @@ node_alive(const crm_node_t *node)
-
- #define state_text(state) ((state)? (const char *)(state) : "in unknown state")
-
-+bool controld_dc_left = false;
-+
- void
- peer_update_callback(enum crm_status_type type, crm_node_t * node, const void *data)
- {
-@@ -217,7 +219,7 @@ peer_update_callback(enum crm_status_type type, crm_node_t * node, const void *d
- cib_scope_local);
- }
-
-- } else if (AM_I_DC || (fsa_our_dc == NULL)) {
-+ } else if (AM_I_DC || controld_dc_left || (fsa_our_dc == NULL)) {
- /* This only needs to be done once, so normally the DC should do
- * it. However if there is no DC, every node must do it, since
- * there is no other way to ensure some one node does it.
-diff --git a/daemons/controld/controld_corosync.c b/daemons/controld/controld_corosync.c
-index db99630..c5ab658 100644
---- a/daemons/controld/controld_corosync.c
-+++ b/daemons/controld/controld_corosync.c
-@@ -87,6 +87,61 @@ crmd_cs_destroy(gpointer user_data)
- }
- }
-
-+extern bool controld_dc_left;
-+
-+/*!
-+ * \brief Handle a Corosync notification of a CPG configuration change
-+ *
-+ * \param[in] handle CPG connection
-+ * \param[in] cpg_name CPG group name
-+ * \param[in] member_list List of current CPG members
-+ * \param[in] member_list_entries Number of entries in \p member_list
-+ * \param[in] left_list List of CPG members that left
-+ * \param[in] left_list_entries Number of entries in \p left_list
-+ * \param[in] joined_list List of CPG members that joined
-+ * \param[in] joined_list_entries Number of entries in \p joined_list
-+ */
-+static void
-+cpg_membership_callback(cpg_handle_t handle, const struct cpg_name *cpg_name,
-+ const struct cpg_address *member_list,
-+ size_t member_list_entries,
-+ const struct cpg_address *left_list,
-+ size_t left_list_entries,
-+ const struct cpg_address *joined_list,
-+ size_t joined_list_entries)
-+{
-+ /* When nodes leave CPG, the DC clears their transient node attributes.
-+ *
-+ * However if there is no DC, or the DC is among the nodes that left, each
-+ * remaining node needs to do the clearing, to ensure it gets done.
-+ * Otherwise, the attributes would persist when the nodes rejoin, which
-+ * could have serious consequences for unfencing, agents that use attributes
-+ * for internal logic, etc.
-+ *
-+ * Here, we set a global boolean if the DC is among the nodes that left, for
-+ * use by the peer callback.
-+ */
-+ if (fsa_our_dc != NULL) {
-+ crm_node_t *peer = pcmk__search_cluster_node_cache(0, fsa_our_dc);
-+
-+ if (peer != NULL) {
-+ for (int i = 0; i < left_list_entries; ++i) {
-+ if (left_list[i].nodeid == peer->id) {
-+ controld_dc_left = true;
-+ break;
-+ }
-+ }
-+ }
-+ }
-+
-+ // Process the change normally, which will call the peer callback as needed
-+ pcmk_cpg_membership(handle, cpg_name, member_list, member_list_entries,
-+ left_list, left_list_entries,
-+ joined_list, joined_list_entries);
-+
-+ controld_dc_left = false;
-+}
-+
- extern gboolean crm_connect_corosync(crm_cluster_t * cluster);
-
- gboolean
-@@ -95,7 +150,7 @@ crm_connect_corosync(crm_cluster_t * cluster)
- if (is_corosync_cluster()) {
- crm_set_status_callback(&peer_update_callback);
- cluster->cpg.cpg_deliver_fn = crmd_cs_dispatch;
-- cluster->cpg.cpg_confchg_fn = pcmk_cpg_membership;
-+ cluster->cpg.cpg_confchg_fn = cpg_membership_callback;
- cluster->destroy = crmd_cs_destroy;
-
- if (crm_cluster_connect(cluster)) {
---
-1.8.3.1
-
diff --git a/0005-Fix-tools-make-exit-code-stdout-stderr-consistent-wi.patch b/0005-Fix-tools-make-exit-code-stdout-stderr-consistent-wi.patch
new file mode 100644
index 0000000..f72774f
--- /dev/null
+++ b/0005-Fix-tools-make-exit-code-stdout-stderr-consistent-wi.patch
@@ -0,0 +1,82 @@
+From b34570ecaeb4fb0106dd0aa5a8776a5417a07f90 Mon Sep 17 00:00:00 2001
+From: Klaus Wenninger <klaus.wenninger(a)aon.at>
+Date: Mon, 16 Aug 2021 16:41:18 +0200
+Subject: [PATCH] Fix: tools: make exit-code stdout/stderr consistent with
+ --version
+
+---
+ tools/crm_attribute.c | 2 +-
+ tools/crm_diff.c | 2 +-
+ tools/crm_error.c | 2 +-
+ tools/crm_node.c | 2 +-
+ tools/crm_rule.c | 2 +-
+ 5 files changed, 5 insertions(+), 5 deletions(-)
+
+diff --git a/tools/crm_attribute.c b/tools/crm_attribute.c
+index 6bd4e2a..faf4aae 100644
+--- a/tools/crm_attribute.c
++++ b/tools/crm_attribute.c
+@@ -326,7 +326,7 @@ main(int argc, char **argv)
+ g_strfreev(processed_args);
+ pcmk__free_arg_context(context);
+ /* FIXME: When crm_attribute is converted to use formatted output, this can go. */
+- pcmk__cli_help('v', CRM_EX_USAGE);
++ pcmk__cli_help('v', CRM_EX_OK);
+ }
+
+ if (options.promotion_score && options.attr_name == NULL) {
+diff --git a/tools/crm_diff.c b/tools/crm_diff.c
+index 9890c10..18f5d48 100644
+--- a/tools/crm_diff.c
++++ b/tools/crm_diff.c
+@@ -322,7 +322,7 @@ main(int argc, char **argv)
+ g_strfreev(processed_args);
+ pcmk__free_arg_context(context);
+ /* FIXME: When crm_diff is converted to use formatted output, this can go. */
+- pcmk__cli_help('v', CRM_EX_USAGE);
++ pcmk__cli_help('v', CRM_EX_OK);
+ }
+
+ if (options.apply && options.no_version) {
+diff --git a/tools/crm_error.c b/tools/crm_error.c
+index 923f393..f26efe7 100644
+--- a/tools/crm_error.c
++++ b/tools/crm_error.c
+@@ -93,7 +93,7 @@ main(int argc, char **argv)
+ g_strfreev(processed_args);
+ pcmk__free_arg_context(context);
+ /* FIXME: When crm_error is converted to use formatted output, this can go. */
+- pcmk__cli_help('v', CRM_EX_USAGE);
++ pcmk__cli_help('v', CRM_EX_OK);
+ }
+
+ if (options.do_list) {
+diff --git a/tools/crm_node.c b/tools/crm_node.c
+index 25da978..489a39a 100644
+--- a/tools/crm_node.c
++++ b/tools/crm_node.c
+@@ -549,7 +549,7 @@ main(int argc, char **argv)
+ g_strfreev(processed_args);
+ pcmk__free_arg_context(context);
+ /* FIXME: When crm_node is converted to use formatted output, this can go. */
+- pcmk__cli_help('v', CRM_EX_USAGE);
++ pcmk__cli_help('v', CRM_EX_OK);
+ }
+
+ if (options.command == 0) {
+diff --git a/tools/crm_rule.c b/tools/crm_rule.c
+index 30c5155..0880415 100644
+--- a/tools/crm_rule.c
++++ b/tools/crm_rule.c
+@@ -252,7 +252,7 @@ main(int argc, char **argv)
+ g_strfreev(processed_args);
+ pcmk__free_arg_context(context);
+ /* FIXME: When crm_rule is converted to use formatted output, this can go. */
+- pcmk__cli_help('v', CRM_EX_USAGE);
++ pcmk__cli_help('v', CRM_EX_OK);
+ }
+
+ /* Check command line arguments before opening a connection to
+--
+1.8.3.1
+
diff --git a/0006-Build-manpages-using-help2man-check-exit-code-of-too.patch b/0006-Build-manpages-using-help2man-check-exit-code-of-too.patch
new file mode 100644
index 0000000..7c408b8
--- /dev/null
+++ b/0006-Build-manpages-using-help2man-check-exit-code-of-too.patch
@@ -0,0 +1,29 @@
+From 2e0ac6390327e387e9b94e50080d13fd95f545d6 Mon Sep 17 00:00:00 2001
+From: Klaus Wenninger <klaus.wenninger(a)aon.at>
+Date: Mon, 16 Aug 2021 16:42:51 +0200
+Subject: [PATCH] Build: manpages: using help2man check exit-code of tools
+
+---
+ mk/common.mk | 6 ++++++
+ 1 file changed, 6 insertions(+)
+
+diff --git a/mk/common.mk b/mk/common.mk
+index aa59feb..c3d1993 100644
+--- a/mk/common.mk
++++ b/mk/common.mk
+@@ -68,6 +68,12 @@ HELP2MAN_ARGS = -N --section 8 --name "Part of the Pacemaker cluster resource ma
+ # and all wrappers to C code.
+ %.8: % $(MAN8DEPS)
+ $(AM_V_at)chmod a+x $(abs_builddir)/$<
++ PATH=$(abs_builddir):$$PATH $(abs_builddir)/$< --version >/dev/null
++ if [ -f $(abs_srcdir)/$@.inc ]; then \
++ PATH=$(abs_builddir):$$PATH $(abs_builddir)/$< --help-all >/dev/null; \
++ else \
++ PATH=$(abs_builddir):$$PATH $(abs_builddir)/$< --help >/dev/null; \
++ fi
+ $(AM_V_MAN)if [ -f $(abs_srcdir)/$@.inc ]; then \
+ PATH=$(abs_builddir):$$PATH $(HELP2MAN) $(HELP2MAN_ARGS) \
+ -h --help-all \
+--
+1.8.3.1
+
diff --git a/pacemaker.spec b/pacemaker.spec
index a138e31..2cd4aab 100644
--- a/pacemaker.spec
+++ b/pacemaker.spec
@@ -32,10 +32,10 @@
## can be incremented to build packages reliably considered "newer"
## than previously built packages with the same pcmkversion)
%global pcmkversion 2.1.1
-%global specversion 0.3.rc2
+%global specversion 0.4.rc3
## Upstream commit (full commit ID, abbreviated commit ID, or tag) to build
-%global commit a64ad221abe9cdd968ceacd35c23832ce0fcd189
+%global commit c6a4f6e6cdcedf08e0bfd81248b86812c2a571b2
## Since git v2.11, the extent of abbreviation is autoscaled by default
## (used to be constant of 7), so we need to convey it for non-tags, too.
@@ -219,7 +219,8 @@ Patch0: 0001-feature-watchdog-fencing-allow-restriction-to-certai.patch
Patch1: 0002-Fix-watchdog-fencing-Silence-warning-without-node-re.patch
Patch2: 0003-Fix-fence_watchdog-fix-version-output-needed-for-hel.patch
Patch3: 0004-Build-rpm-package-fence_watchdog-in-base-package.patch
-Patch4: 0005-Fix-controller-ensure-lost-node-s-transient-attribut.patch
+Patch4: 0005-Fix-tools-make-exit-code-stdout-stderr-consistent-wi.patch
+Patch5: 0006-Build-manpages-using-help2man-check-exit-code-of-too.patch
# upstream commits
@@ -828,6 +829,13 @@ exit 0
%license %{nagios_name}-%{nagios_hash}/COPYING
%changelog
+* Tue Aug 17 2021 Klaus Wenninger <kwenning(a)redhat.com> - 2.1.1-0.4.rc3
+- Update for new upstream tarball for release candidate: Pacemaker-2.1.1-rc3,
+ for full details, see included ChangeLog file or
+ https://github.com/ClusterLabs/pacemaker/releases/tag/Pacemaker-2.1.1-rc3
+- Make tools exit-codes consistent for --version
+- Check exit-codes of tools prior to manpage-generation
+
* Wed Aug 11 2021 Klaus Wenninger <kwenning(a)redhat.com> - 2.1.1-0.3.rc2
- package fence_watchdog in base-package instead if cli-subpackage
- fix version output of fence_watchdog as needed for help2man
diff --git a/sources b/sources
index 2ccc4d0..edc4941 100644
--- a/sources
+++ b/sources
@@ -1,2 +1,2 @@
SHA512 (nagios-agents-metadata-105ab8a7b2c16b9a29cf1c1596b80136eeef332b.tar.gz) = 11ddeb48a4929e7642b6dfa9c7962aa1d7a1af1c569830f55ed6cd6773abac13377317327bc1db8411c8077884f83f81cc54d746c834b63a99fa6dc219b5caad
-SHA512 (pacemaker-a64ad221a.tar.gz) = ee954bd8efd135965290f9958b65680cbf101e14e2fd22d326e53a250281a7e5b6b6ec979c7f76072514dbf4d29868e99931842dbcc99bab5e5ba5e4bbc4ef0a
+SHA512 (pacemaker-c6a4f6e6c.tar.gz) = 28a313c016add4f95ec2b546c468826c8234bafdc1a7a7d26b785733ee10ec209dc0dbf3f107df57604990b5f89b2d05831684b5d3adfd45479706d9c6d5654f
commit 7889c6669e78faaef3ce77ff149430c179bb6792
Author: Klaus Wenninger <klaus.wenninger(a)aon.at>
Date: Wed Aug 11 11:48:10 2021 +0200
* Wed Aug 11 2021 Klaus Wenninger <kwenning(a)redhat.com> - 2.1.1-0.3.rc2
- package fence_watchdog in base-package instead if cli-subpackage
- fix version output of fence_watchdog as needed for help2man
- ensure transient attributes of lost nodes are cleared reliably
diff --git a/0003-Fix-fence_watchdog-fix-version-output-needed-for-hel.patch b/0003-Fix-fence_watchdog-fix-version-output-needed-for-hel.patch
new file mode 100644
index 0000000..8245e4f
--- /dev/null
+++ b/0003-Fix-fence_watchdog-fix-version-output-needed-for-hel.patch
@@ -0,0 +1,24 @@
+From 88e75d5b98df197fa731e7642434951a24a67095 Mon Sep 17 00:00:00 2001
+From: Klaus Wenninger <klaus.wenninger(a)aon.at>
+Date: Tue, 10 Aug 2021 09:10:23 +0200
+Subject: [PATCH] Fix: fence_watchdog: fix version output needed for help2man
+
+---
+ daemons/fenced/fence_watchdog.in | 1 +
+ 1 file changed, 1 insertion(+)
+
+diff --git a/daemons/fenced/fence_watchdog.in b/daemons/fenced/fence_watchdog.in
+index c83304f..700065e 100755
+--- a/daemons/fenced/fence_watchdog.in
++++ b/daemons/fenced/fence_watchdog.in
+@@ -12,6 +12,7 @@ import sys
+ import atexit
+ import getopt
+
++AGENT_VERSION = "1.0.0"
+ SHORT_DESC = "Dummy watchdog fence agent"
+ LONG_DESC = """fence_watchdog just provides
+ meta-data - actual fencing is done by the pacemaker internal watchdog agent."""
+--
+1.8.3.1
+
diff --git a/0004-Build-rpm-package-fence_watchdog-in-base-package.patch b/0004-Build-rpm-package-fence_watchdog-in-base-package.patch
new file mode 100644
index 0000000..d112c40
--- /dev/null
+++ b/0004-Build-rpm-package-fence_watchdog-in-base-package.patch
@@ -0,0 +1,32 @@
+From 61eb9c240004d1dbd0b5973e2fecda3686bb4c53 Mon Sep 17 00:00:00 2001
+From: Klaus Wenninger <klaus.wenninger(a)aon.at>
+Date: Tue, 10 Aug 2021 09:06:55 +0200
+Subject: [PATCH] Build: rpm: package fence_watchdog in base-package
+
+---
+ rpm/pacemaker.spec.in | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+diff --git a/rpm/pacemaker.spec.in b/rpm/pacemaker.spec.in
+index f58357a..0c569b9 100644
+--- a/rpm/pacemaker.spec.in
++++ b/rpm/pacemaker.spec.in
+@@ -734,6 +734,7 @@ exit 0
+ %{_sbindir}/crm_attribute
+ %{_sbindir}/crm_master
+ %{_sbindir}/fence_legacy
++%{_sbindir}/fence_watchdog
+
+ %doc %{_mandir}/man7/pacemaker-controld.*
+ %doc %{_mandir}/man7/pacemaker-schedulerd.*
+@@ -797,7 +798,6 @@ exit 0
+ %{_sbindir}/crm_simulate
+ %{_sbindir}/crm_report
+ %{_sbindir}/crm_ticket
+-%{_sbindir}/fence_watchdog
+ %{_sbindir}/stonith_admin
+ # "dirname" is owned by -schemas, which is a prerequisite
+ %{_datadir}/pacemaker/report.collector
+--
+1.8.3.1
+
diff --git a/0005-Fix-controller-ensure-lost-node-s-transient-attribut.patch b/0005-Fix-controller-ensure-lost-node-s-transient-attribut.patch
new file mode 100644
index 0000000..3e64b25
--- /dev/null
+++ b/0005-Fix-controller-ensure-lost-node-s-transient-attribut.patch
@@ -0,0 +1,122 @@
+From ee7eba6a7a05bdf0a12d60ebabb334d8ee021101 Mon Sep 17 00:00:00 2001
+From: Ken Gaillot <kgaillot(a)redhat.com>
+Date: Mon, 9 Aug 2021 14:48:57 -0500
+Subject: [PATCH] Fix: controller: ensure lost node's transient attributes are
+ cleared without DC
+
+Previously, peer_update_callback() cleared a lost node's transient attributes
+if either the local node is DC, or there is no DC.
+
+However, that left the possibility of the DC being lost at the same time as
+another node -- the local node would still have fsa_our_dc set while processing
+the leave notifications, so no node would clear the attributes for the non-DC
+node.
+
+Now, the controller has its own CPG configuration change callback, which sets a
+global boolean before calling the usual one, so that peer_update_callback() can
+know when the DC has been lost.
+---
+ daemons/controld/controld_callbacks.c | 4 ++-
+ daemons/controld/controld_corosync.c | 57 ++++++++++++++++++++++++++++++++++-
+ 2 files changed, 59 insertions(+), 2 deletions(-)
+
+diff --git a/daemons/controld/controld_callbacks.c b/daemons/controld/controld_callbacks.c
+index af24856..e564b3d 100644
+--- a/daemons/controld/controld_callbacks.c
++++ b/daemons/controld/controld_callbacks.c
+@@ -99,6 +99,8 @@ node_alive(const crm_node_t *node)
+
+ #define state_text(state) ((state)? (const char *)(state) : "in unknown state")
+
++bool controld_dc_left = false;
++
+ void
+ peer_update_callback(enum crm_status_type type, crm_node_t * node, const void *data)
+ {
+@@ -217,7 +219,7 @@ peer_update_callback(enum crm_status_type type, crm_node_t * node, const void *d
+ cib_scope_local);
+ }
+
+- } else if (AM_I_DC || (fsa_our_dc == NULL)) {
++ } else if (AM_I_DC || controld_dc_left || (fsa_our_dc == NULL)) {
+ /* This only needs to be done once, so normally the DC should do
+ * it. However if there is no DC, every node must do it, since
+ * there is no other way to ensure some one node does it.
+diff --git a/daemons/controld/controld_corosync.c b/daemons/controld/controld_corosync.c
+index db99630..c5ab658 100644
+--- a/daemons/controld/controld_corosync.c
++++ b/daemons/controld/controld_corosync.c
+@@ -87,6 +87,61 @@ crmd_cs_destroy(gpointer user_data)
+ }
+ }
+
++extern bool controld_dc_left;
++
++/*!
++ * \brief Handle a Corosync notification of a CPG configuration change
++ *
++ * \param[in] handle CPG connection
++ * \param[in] cpg_name CPG group name
++ * \param[in] member_list List of current CPG members
++ * \param[in] member_list_entries Number of entries in \p member_list
++ * \param[in] left_list List of CPG members that left
++ * \param[in] left_list_entries Number of entries in \p left_list
++ * \param[in] joined_list List of CPG members that joined
++ * \param[in] joined_list_entries Number of entries in \p joined_list
++ */
++static void
++cpg_membership_callback(cpg_handle_t handle, const struct cpg_name *cpg_name,
++ const struct cpg_address *member_list,
++ size_t member_list_entries,
++ const struct cpg_address *left_list,
++ size_t left_list_entries,
++ const struct cpg_address *joined_list,
++ size_t joined_list_entries)
++{
++ /* When nodes leave CPG, the DC clears their transient node attributes.
++ *
++ * However if there is no DC, or the DC is among the nodes that left, each
++ * remaining node needs to do the clearing, to ensure it gets done.
++ * Otherwise, the attributes would persist when the nodes rejoin, which
++ * could have serious consequences for unfencing, agents that use attributes
++ * for internal logic, etc.
++ *
++ * Here, we set a global boolean if the DC is among the nodes that left, for
++ * use by the peer callback.
++ */
++ if (fsa_our_dc != NULL) {
++ crm_node_t *peer = pcmk__search_cluster_node_cache(0, fsa_our_dc);
++
++ if (peer != NULL) {
++ for (int i = 0; i < left_list_entries; ++i) {
++ if (left_list[i].nodeid == peer->id) {
++ controld_dc_left = true;
++ break;
++ }
++ }
++ }
++ }
++
++ // Process the change normally, which will call the peer callback as needed
++ pcmk_cpg_membership(handle, cpg_name, member_list, member_list_entries,
++ left_list, left_list_entries,
++ joined_list, joined_list_entries);
++
++ controld_dc_left = false;
++}
++
+ extern gboolean crm_connect_corosync(crm_cluster_t * cluster);
+
+ gboolean
+@@ -95,7 +150,7 @@ crm_connect_corosync(crm_cluster_t * cluster)
+ if (is_corosync_cluster()) {
+ crm_set_status_callback(&peer_update_callback);
+ cluster->cpg.cpg_deliver_fn = crmd_cs_dispatch;
+- cluster->cpg.cpg_confchg_fn = pcmk_cpg_membership;
++ cluster->cpg.cpg_confchg_fn = cpg_membership_callback;
+ cluster->destroy = crmd_cs_destroy;
+
+ if (crm_cluster_connect(cluster)) {
+--
+1.8.3.1
+
diff --git a/pacemaker.spec b/pacemaker.spec
index a8f1d51..a138e31 100644
--- a/pacemaker.spec
+++ b/pacemaker.spec
@@ -32,7 +32,7 @@
## can be incremented to build packages reliably considered "newer"
## than previously built packages with the same pcmkversion)
%global pcmkversion 2.1.1
-%global specversion 0.2.rc2
+%global specversion 0.3.rc2
## Upstream commit (full commit ID, abbreviated commit ID, or tag) to build
%global commit a64ad221abe9cdd968ceacd35c23832ce0fcd189
@@ -217,6 +217,9 @@ Source0: https://codeload.github.com/%{github_owner}/%{name}/tar.gz/%{arch
Source1: https://codeload.github.com/%{github_owner}/%{nagios_name}/tar.gz/%{nagio...
Patch0: 0001-feature-watchdog-fencing-allow-restriction-to-certai.patch
Patch1: 0002-Fix-watchdog-fencing-Silence-warning-without-node-re.patch
+Patch2: 0003-Fix-fence_watchdog-fix-version-output-needed-for-hel.patch
+Patch3: 0004-Build-rpm-package-fence_watchdog-in-base-package.patch
+Patch4: 0005-Fix-controller-ensure-lost-node-s-transient-attribut.patch
# upstream commits
@@ -660,6 +663,7 @@ exit 0
%{_sbindir}/crm_attribute
%{_sbindir}/crm_master
+%{_sbindir}/fence_watchdog
%doc %{_mandir}/man7/pacemaker-controld.*
%doc %{_mandir}/man7/pacemaker-schedulerd.*
@@ -709,7 +713,6 @@ exit 0
%{_sbindir}/crm_simulate
%{_sbindir}/crm_report
%{_sbindir}/crm_ticket
-%{_sbindir}/fence_watchdog
%{_sbindir}/stonith_admin
# "dirname" is owned by -schemas, which is a prerequisite
%{_datadir}/pacemaker/report.collector
@@ -825,6 +828,11 @@ exit 0
%license %{nagios_name}-%{nagios_hash}/COPYING
%changelog
+* Wed Aug 11 2021 Klaus Wenninger <kwenning(a)redhat.com> - 2.1.1-0.3.rc2
+- package fence_watchdog in base-package instead if cli-subpackage
+- fix version output of fence_watchdog as needed for help2man
+- ensure transient attributes of lost nodes are cleared reliably
+
* Mon Aug 9 2021 Klaus Wenninger <kwenning(a)redhat.com> - 2.1.1-0.2.rc2
- Update for new upstream tarball for release candidate: Pacemaker-2.1.1-rc2,
for full details, see included ChangeLog file or
commit e8f53e7d76dd1680d55e0436757380baf9abdc38
Author: Klaus Wenninger <klaus.wenninger(a)aon.at>
Date: Mon Aug 9 17:50:26 2021 +0200
* Mon Aug 9 2021 Klaus Wenninger <kwenning(a)redhat.com> - 2.1.1-0.2.rc2
- Update for new upstream tarball for release candidate: Pacemaker-2.1.1-rc2,
for full details, see included ChangeLog file or
https://github.com/ClusterLabs/pacemaker/releases/tag/Pacemaker-2.1.1-rc2
- Added feature patch that allows to explicitly specify nodes that
should do watchdog-fencing
diff --git a/0001-feature-watchdog-fencing-allow-restriction-to-certai.patch b/0001-feature-watchdog-fencing-allow-restriction-to-certai.patch
new file mode 100644
index 0000000..97e6071
--- /dev/null
+++ b/0001-feature-watchdog-fencing-allow-restriction-to-certai.patch
@@ -0,0 +1,1119 @@
+From 5dd1e4459335764e0adf5fa78d81c875ae2332e9 Mon Sep 17 00:00:00 2001
+From: Klaus Wenninger <klaus.wenninger(a)aon.at>
+Date: Fri, 30 Jul 2021 18:15:10 +0200
+Subject: [PATCH] feature: watchdog-fencing: allow restriction to certain nodes
+
+Bump CRM_FEATURE_SET to 3.11.0 to encourage cluster being
+fully upgraded to a version that supports the feature
+before explicitly adding a watchdog-fence-device.
+---
+ configure.ac | 1 +
+ daemons/controld/controld_control.c | 2 +-
+ daemons/controld/controld_fencing.c | 14 ++
+ daemons/controld/controld_fencing.h | 1 +
+ daemons/fenced/Makefile.am | 2 +-
+ daemons/fenced/fence_watchdog.in | 283 ++++++++++++++++++++++++++++++++++++
+ daemons/fenced/fenced_commands.c | 141 ++++++++++++++----
+ daemons/fenced/fenced_remote.c | 71 ++++++---
+ daemons/fenced/pacemaker-fenced.c | 131 ++++++++++++-----
+ daemons/fenced/pacemaker-fenced.h | 5 +-
+ include/crm/crm.h | 2 +-
+ include/crm/fencing/internal.h | 8 +-
+ lib/fencing/st_client.c | 61 ++++++++
+ lib/lrmd/lrmd_client.c | 6 +-
+ rpm/pacemaker.spec.in | 3 +
+ 16 files changed, 635 insertions(+), 97 deletions(-)
+ create mode 100755 daemons/fenced/fence_watchdog.in
+
+diff --git a/configure.ac b/configure.ac
+index 436100c..013562e 100644
+--- a/configure.ac
++++ b/configure.ac
+@@ -1972,6 +1972,7 @@ CONFIG_FILES_EXEC([cts/cts-cli],
+ [cts/support/fence_dummy],
+ [cts/support/pacemaker-cts-dummyd],
+ [daemons/fenced/fence_legacy],
++ [daemons/fenced/fence_watchdog],
+ [doc/abi-check],
+ [extra/resources/ClusterMon],
+ [extra/resources/HealthSMART],
+diff --git a/daemons/controld/controld_control.c b/daemons/controld/controld_control.c
+index 45a70bb..b5da6a4 100644
+--- a/daemons/controld/controld_control.c
++++ b/daemons/controld/controld_control.c
+@@ -615,7 +615,7 @@ static pcmk__cluster_option_t crmd_opts[] = {
+ },
+ {
+ "stonith-watchdog-timeout", NULL, "time", NULL,
+- "0", pcmk__valid_sbd_timeout,
++ "0", controld_verify_stonith_watchdog_timeout,
+ "How long to wait before we can assume nodes are safely down "
+ "when watchdog-based self-fencing via SBD is in use",
+ "If nonzero, along with `have-watchdog=true` automatically set by the "
+diff --git a/daemons/controld/controld_fencing.c b/daemons/controld/controld_fencing.c
+index 0fba661..6c2a6c5 100644
+--- a/daemons/controld/controld_fencing.c
++++ b/daemons/controld/controld_fencing.c
+@@ -11,6 +11,7 @@
+ #include <crm/crm.h>
+ #include <crm/msg_xml.h>
+ #include <crm/common/xml.h>
++#include <crm/stonith-ng.h>
+ #include <crm/fencing/internal.h>
+
+ #include <pacemaker-controld.h>
+@@ -886,6 +887,19 @@ te_fence_node(crm_graph_t *graph, crm_action_t *action)
+ return TRUE;
+ }
+
++bool
++controld_verify_stonith_watchdog_timeout(const char *value)
++{
++ gboolean rv = TRUE;
++
++ if (stonith_api && (stonith_api->state != stonith_disconnected) &&
++ stonith__watchdog_fencing_enabled_for_node_api(stonith_api,
++ fsa_our_uname)) {
++ rv = pcmk__valid_sbd_timeout(value);
++ }
++ return rv;
++}
++
+ /* end stonith API client functions */
+
+
+diff --git a/daemons/controld/controld_fencing.h b/daemons/controld/controld_fencing.h
+index d0ecc82..ef68a0c 100644
+--- a/daemons/controld/controld_fencing.h
++++ b/daemons/controld/controld_fencing.h
+@@ -24,6 +24,7 @@ void update_stonith_max_attempts(const char* value);
+ void controld_trigger_fencer_connect(void);
+ void controld_disconnect_fencer(bool destroy);
+ gboolean te_fence_node(crm_graph_t *graph, crm_action_t *action);
++bool controld_verify_stonith_watchdog_timeout(const char *value);
+
+ // stonith cleanup list
+ void add_stonith_cleanup(const char *target);
+diff --git a/daemons/fenced/Makefile.am b/daemons/fenced/Makefile.am
+index 43413e1..2923d7c 100644
+--- a/daemons/fenced/Makefile.am
++++ b/daemons/fenced/Makefile.am
+@@ -15,7 +15,7 @@ halibdir = $(CRM_DAEMON_DIR)
+
+ halib_PROGRAMS = pacemaker-fenced cts-fence-helper
+
+-sbin_SCRIPTS = fence_legacy
++sbin_SCRIPTS = fence_legacy fence_watchdog
+
+ noinst_HEADERS = pacemaker-fenced.h
+
+diff --git a/daemons/fenced/fence_watchdog.in b/daemons/fenced/fence_watchdog.in
+new file mode 100755
+index 0000000..c83304f
+--- /dev/null
++++ b/daemons/fenced/fence_watchdog.in
+@@ -0,0 +1,283 @@
++#!@PYTHON@
++"""Dummy watchdog fence agent for providing meta-data for the pacemaker internal agent
++"""
++
++__copyright__ = "Copyright 2012-2021 the Pacemaker project contributors"
++__license__ = "GNU General Public License version 2 or later (GPLv2+) WITHOUT ANY WARRANTY"
++
++import io
++import os
++import re
++import sys
++import atexit
++import getopt
++
++SHORT_DESC = "Dummy watchdog fence agent"
++LONG_DESC = """fence_watchdog just provides
++meta-data - actual fencing is done by the pacemaker internal watchdog agent."""
++
++ALL_OPT = {
++ "version" : {
++ "getopt" : "V",
++ "longopt" : "version",
++ "help" : "-V, --version Display version information and exit",
++ "required" : "0",
++ "shortdesc" : "Display version information and exit",
++ "order" : 53
++ },
++ "help" : {
++ "getopt" : "h",
++ "longopt" : "help",
++ "help" : "-h, --help Display this help and exit",
++ "required" : "0",
++ "shortdesc" : "Display help and exit",
++ "order" : 54
++ },
++ "action" : {
++ "getopt" : "o:",
++ "longopt" : "action",
++ "help" : "-o, --action=[action] Action: metadata",
++ "required" : "1",
++ "shortdesc" : "Fencing Action",
++ "default" : "metadata",
++ "order" : 1
++ },
++ "nodename" : {
++ "getopt" : "N:",
++ "longopt" : "nodename",
++ "help" : "-N, --nodename Node name of fence victim (ignored)",
++ "required" : "0",
++ "shortdesc" : "Ignored",
++ "order" : 2
++ },
++ "plug" : {
++ "getopt" : "n:",
++ "longopt" : "plug",
++ "help" : "-n, --plug=[id] Physical plug number on device (ignored)",
++ "required" : "1",
++ "shortdesc" : "Ignored",
++ "order" : 4
++ }
++}
++
++
++def agent():
++ """ Return name this file was run as. """
++
++ return os.path.basename(sys.argv[0])
++
++
++def fail_usage(message):
++ """ Print a usage message and exit. """
++
++ sys.exit("%s\nPlease use '-h' for usage" % message)
++
++
++def show_docs(options):
++ """ Handle informational options (display info and exit). """
++
++ device_opt = options["device_opt"]
++
++ if "-h" in options:
++ usage(device_opt)
++ sys.exit(0)
++
++ if "-o" in options and options["-o"].lower() == "metadata":
++ metadata(device_opt, options)
++ sys.exit(0)
++
++ if "-V" in options:
++ print(AGENT_VERSION)
++ sys.exit(0)
++
++
++def sorted_options(avail_opt):
++ """ Return a list of all options, in their internally specified order. """
++
++ sorted_list = [(key, ALL_OPT[key]) for key in avail_opt]
++ sorted_list.sort(key=lambda x: x[1]["order"])
++ return sorted_list
++
++
++def usage(avail_opt):
++ """ Print a usage message. """
++ print(LONG_DESC)
++ print()
++ print("Usage:")
++ print("\t" + agent() + " [options]")
++ print("Options:")
++
++ for dummy, value in sorted_options(avail_opt):
++ if len(value["help"]) != 0:
++ print(" " + value["help"])
++
++
++def metadata(avail_opt, options):
++ """ Print agent metadata. """
++
++ print("""<?xml version="1.0" ?>
++<resource-agent name="%s" shortdesc="%s">
++<longdesc>%s</longdesc>
++<parameters>""" % (agent(), SHORT_DESC, LONG_DESC))
++
++ for option, dummy in sorted_options(avail_opt):
++ if "shortdesc" in ALL_OPT[option]:
++ print(' <parameter name="' + option +
++ 'required="' + ALL_OPT[option]["required"] + '">')
++
++ default = ""
++ default_name_arg = "-" + ALL_OPT[option]["getopt"][:-1]
++ default_name_no_arg = "-" + ALL_OPT[option]["getopt"]
++
++ if "default" in ALL_OPT[option]:
++ default = 'default="%s"' % str(ALL_OPT[option]["default"])
++ elif default_name_arg in options:
++ if options[default_name_arg]:
++ try:
++ default = 'default="%s"' % options[default_name_arg]
++ except TypeError:
++ ## @todo/@note: Currently there is no clean way how to handle lists
++ ## we can create a string from it but we can't set it on command line
++ default = 'default="%s"' % str(options[default_name_arg])
++ elif default_name_no_arg in options:
++ default = 'default="true"'
++
++ mixed = ALL_OPT[option]["help"]
++ ## split it between option and help text
++ res = re.compile(r"^(.*--\S+)\s+", re.IGNORECASE | re.S).search(mixed)
++ if None != res:
++ mixed = res.group(1)
++ mixed = mixed.replace("<", "<").replace(">", ">")
++ print(' <getopt mixed="' + mixed + '" />')
++
++ if ALL_OPT[option]["getopt"].count(":") > 0:
++ print(' <content type="string" ' + default + ' />')
++ else:
++ print(' <content type="boolean" ' + default + ' />')
++
++ print(' <shortdesc lang="en">' + ALL_OPT[option]["shortdesc"] + '</shortdesc>')
++ print(' </parameter>')
++
++ print(' </parameters>\n <actions>')
++ print(' <action name="on" />')
++ print(' <action name="off" />')
++ print(' <action name="reboot" />')
++ print(' <action name="monitor" />')
++ print(' <action name="list" />')
++ print(' <action name="metadata" />')
++ print(' </actions>')
++ print('</resource-agent>')
++
++
++def option_longopt(option):
++ """ Return the getopt-compatible long-option name of the given option. """
++
++ if ALL_OPT[option]["getopt"].endswith(":"):
++ return ALL_OPT[option]["longopt"] + "="
++ else:
++ return ALL_OPT[option]["longopt"]
++
++
++def opts_from_command_line(argv, avail_opt):
++ """ Read options from command-line arguments. """
++
++ # Prepare list of options for getopt
++ getopt_string = ""
++ longopt_list = []
++ for k in avail_opt:
++ if k in ALL_OPT:
++ getopt_string += ALL_OPT[k]["getopt"]
++ else:
++ fail_usage("Parse error: unknown option '" + k + "'")
++
++ if k in ALL_OPT and "longopt" in ALL_OPT[k]:
++ longopt_list.append(option_longopt(k))
++
++ try:
++ opt, dummy = getopt.gnu_getopt(argv, getopt_string, longopt_list)
++ except getopt.GetoptError as error:
++ fail_usage("Parse error: " + error.msg)
++
++ # Transform longopt to short one which are used in fencing agents
++ old_opt = opt
++ opt = {}
++ for old_option in dict(old_opt).keys():
++ if old_option.startswith("--"):
++ for option in ALL_OPT.keys():
++ if "longopt" in ALL_OPT[option] and "--" + ALL_OPT[option]["longopt"] == old_option:
++ opt["-" + ALL_OPT[option]["getopt"].rstrip(":")] = dict(old_opt)[old_option]
++ else:
++ opt[old_option] = dict(old_opt)[old_option]
++
++ return opt
++
++
++def opts_from_stdin(avail_opt):
++ """ Read options from standard input. """
++
++ opt = {}
++ name = ""
++ for line in sys.stdin.readlines():
++ line = line.strip()
++ if line.startswith("#") or (len(line) == 0):
++ continue
++
++ (name, value) = (line + "=").split("=", 1)
++ value = value[:-1]
++
++ if name not in avail_opt:
++ print("Parse error: Ignoring unknown option '%s'" % line,
++ file=sys.stderr)
++ continue
++
++ if ALL_OPT[name]["getopt"].endswith(":"):
++ opt["-"+ALL_OPT[name]["getopt"].rstrip(":")] = value
++ elif value.lower() in ["1", "yes", "on", "true"]:
++ opt["-"+ALL_OPT[name]["getopt"]] = "1"
++
++ return opt
++
++
++def process_input(avail_opt):
++ """ Set standard environment variables, and parse all options. """
++
++ # Set standard environment
++ os.putenv("LANG", "C")
++ os.putenv("LC_ALL", "C")
++
++ # Read options from command line or standard input
++ if len(sys.argv) > 1:
++ return opts_from_command_line(sys.argv[1:], avail_opt)
++ else:
++ return opts_from_stdin(avail_opt)
++
++
++def atexit_handler():
++ """ Close stdout on exit. """
++
++ try:
++ sys.stdout.close()
++ os.close(1)
++ except IOError:
++ sys.exit("%s failed to close standard output" % agent())
++
++
++def main():
++ """ Make it so! """
++
++ device_opt = ALL_OPT.keys()
++
++ ## Defaults for fence agent
++ atexit.register(atexit_handler)
++ options = process_input(device_opt)
++ options["device_opt"] = device_opt
++ show_docs(options)
++
++ print("Watchdog fencing may be initiated only by the cluster, not this agent.",
++ file=sys.stderr)
++
++ sys.exit(1)
++
++
++if __name__ == "__main__":
++ main()
+diff --git a/daemons/fenced/fenced_commands.c b/daemons/fenced/fenced_commands.c
+index cd9968f..9470ea2 100644
+--- a/daemons/fenced/fenced_commands.c
++++ b/daemons/fenced/fenced_commands.c
+@@ -397,15 +397,13 @@ stonith_device_execute(stonith_device_t * device)
+ return TRUE;
+ }
+
+- if(pcmk__str_eq(device->agent, STONITH_WATCHDOG_AGENT, pcmk__str_casei)) {
+- if(pcmk__str_eq(cmd->action, "reboot", pcmk__str_casei)) {
+- pcmk__panic(__func__);
+- goto done;
+-
+- } else if(pcmk__str_eq(cmd->action, "off", pcmk__str_casei)) {
+- pcmk__panic(__func__);
+- goto done;
+-
++ if (pcmk__str_any_of(device->agent, STONITH_WATCHDOG_AGENT,
++ STONITH_WATCHDOG_AGENT_INTERNAL, NULL)) {
++ if (pcmk__strcase_any_of(cmd->action, "reboot", "off", NULL)) {
++ if (node_does_watchdog_fencing(stonith_our_uname)) {
++ pcmk__panic(__func__);
++ goto done;
++ }
+ } else {
+ crm_info("Faking success for %s watchdog operation", cmd->action);
+ cmd->done_cb(0, 0, NULL, cmd);
+@@ -716,7 +714,7 @@ get_agent_metadata(const char *agent, xmlNode ** metadata)
+ return EINVAL;
+ }
+ *metadata = NULL;
+- if (pcmk__str_eq(agent, STONITH_WATCHDOG_AGENT, pcmk__str_none)) {
++ if (pcmk__str_eq(agent, STONITH_WATCHDOG_AGENT_INTERNAL, pcmk__str_none)) {
+ return pcmk_rc_ok;
+ }
+ init_metadata_cache();
+@@ -1050,24 +1048,6 @@ schedule_internal_command(const char *origin,
+ schedule_stonith_command(cmd, device);
+ }
+
+-gboolean
+-string_in_list(GList *list, const char *item)
+-{
+- int lpc = 0;
+- int max = g_list_length(list);
+-
+- for (lpc = 0; lpc < max; lpc++) {
+- const char *value = g_list_nth_data(list, lpc);
+-
+- if (pcmk__str_eq(item, value, pcmk__str_casei)) {
+- return TRUE;
+- } else {
+- crm_trace("%d: '%s' != '%s'", lpc, item, value);
+- }
+- }
+- return FALSE;
+-}
+-
+ static void
+ status_search_cb(GPid pid, int rc, const char *output, gpointer user_data)
+ {
+@@ -1144,7 +1124,7 @@ dynamic_list_search_cb(GPid pid, int rc, const char *output, gpointer user_data)
+ if (!alias) {
+ alias = search->host;
+ }
+- if (string_in_list(dev->targets, alias)) {
++ if (pcmk__str_in_list(dev->targets, alias, pcmk__str_casei)) {
+ can_fence = TRUE;
+ }
+ }
+@@ -1215,9 +1195,62 @@ stonith_device_register(xmlNode * msg, const char **desc, gboolean from_cib)
+ stonith_device_t *dup = NULL;
+ stonith_device_t *device = build_device_from_xml(msg);
+ guint ndevices = 0;
++ int rv = pcmk_ok;
+
+ CRM_CHECK(device != NULL, return -ENOMEM);
+
++ /* do we have a watchdog-device? */
++ if (pcmk__str_eq(device->id, STONITH_WATCHDOG_ID, pcmk__str_none) ||
++ pcmk__str_any_of(device->agent, STONITH_WATCHDOG_AGENT,
++ STONITH_WATCHDOG_AGENT_INTERNAL, NULL)) do {
++ if (stonith_watchdog_timeout_ms <= 0) {
++ crm_err("Ignoring watchdog fence device without "
++ "stonith-watchdog-timeout set.");
++ rv = -ENODEV;
++ /* fall through to cleanup & return */
++ } else if (!pcmk__str_any_of(device->agent, STONITH_WATCHDOG_AGENT,
++ STONITH_WATCHDOG_AGENT_INTERNAL, NULL)) {
++ crm_err("Ignoring watchdog fence device with unknown "
++ "agent '%s' unequal '" STONITH_WATCHDOG_AGENT "'.",
++ device->agent?device->agent:"");
++ rv = -ENODEV;
++ /* fall through to cleanup & return */
++ } else if (!pcmk__str_eq(device->id, STONITH_WATCHDOG_ID,
++ pcmk__str_none)) {
++ crm_err("Ignoring watchdog fence device "
++ "named %s !='"STONITH_WATCHDOG_ID"'.",
++ device->id?device->id:"");
++ rv = -ENODEV;
++ /* fall through to cleanup & return */
++ } else {
++ if (pcmk__str_eq(device->agent, STONITH_WATCHDOG_AGENT,
++ pcmk__str_none)) {
++ /* this either has an empty list or the targets
++ configured for watchdog-fencing
++ */
++ g_list_free_full(stonith_watchdog_targets, free);
++ stonith_watchdog_targets = device->targets;
++ device->targets = NULL;
++ }
++ if (node_does_watchdog_fencing(stonith_our_uname)) {
++ g_list_free_full(device->targets, free);
++ device->targets = stonith__parse_targets(stonith_our_uname);
++ g_hash_table_replace(device->params,
++ strdup(PCMK_STONITH_HOST_LIST),
++ strdup(stonith_our_uname));
++ /* proceed as with any other stonith-device */
++ break;
++ }
++
++ crm_debug("Skip registration of watchdog fence device on node not in host-list.");
++ /* cleanup and fall through to more cleanup and return */
++ device->targets = NULL;
++ stonith_device_remove(device->id, from_cib);
++ }
++ free_device(device);
++ return rv;
++ } while (0);
++
+ dup = device_has_duplicate(device);
+ if (dup) {
+ ndevices = g_hash_table_size(device_list);
+@@ -1598,6 +1631,39 @@ stonith_level_remove(xmlNode *msg, char **desc)
+ * (CIB registration is not sufficient), because monitor should not be
+ * possible unless the device is "started" (API registered).
+ */
++
++static char *
++list_to_string(GList *list, const char *delim, gboolean terminate_with_delim)
++{
++ int max = g_list_length(list);
++ size_t delim_len = delim?strlen(delim):0;
++ size_t alloc_size = 1 + (max?((max-1+(terminate_with_delim?1:0))*delim_len):0);
++ char *rv;
++ GList *gIter;
++
++ for (gIter = list; gIter != NULL; gIter = gIter->next) {
++ const char *value = (const char *) gIter->data;
++
++ alloc_size += strlen(value);
++ }
++ rv = calloc(alloc_size, sizeof(char));
++ if (rv) {
++ char *pos = rv;
++ const char *lead_delim = "";
++
++ for (gIter = list; gIter != NULL; gIter = gIter->next) {
++ const char *value = (const char *) gIter->data;
++
++ pos = &pos[sprintf(pos, "%s%s", lead_delim, value)];
++ lead_delim = delim;
++ }
++ if (max && terminate_with_delim) {
++ sprintf(pos, "%s", delim);
++ }
++ }
++ return rv;
++}
++
+ static int
+ stonith_device_action(xmlNode * msg, char **output)
+ {
+@@ -1615,6 +1681,19 @@ stonith_device_action(xmlNode * msg, char **output)
+ return -EPROTO;
+ }
+
++ if (pcmk__str_eq(id, STONITH_WATCHDOG_ID, pcmk__str_none)) {
++ if (stonith_watchdog_timeout_ms <= 0) {
++ return -ENODEV;
++ } else {
++ if (pcmk__str_eq(action, "list", pcmk__str_casei)) {
++ *output = list_to_string(stonith_watchdog_targets, "\n", TRUE);
++ return pcmk_ok;
++ } else if (pcmk__str_eq(action, "monitor", pcmk__str_casei)) {
++ return pcmk_ok;
++ }
++ }
++ }
++
+ device = g_hash_table_lookup(device_list, id);
+ if ((device == NULL)
+ || (!device->api_registered && !strcmp(action, "monitor"))) {
+@@ -1742,7 +1821,7 @@ can_fence_host_with_device(stonith_device_t * dev, struct device_search_s *searc
+ * Only use if all hosts on which the device can be active can always fence all listed hosts
+ */
+
+- if (string_in_list(dev->targets, host)) {
++ if (pcmk__str_in_list(dev->targets, host, pcmk__str_casei)) {
+ can = TRUE;
+ } else if (g_hash_table_lookup(dev->params, PCMK_STONITH_HOST_MAP)
+ && g_hash_table_lookup(dev->aliases, host)) {
+@@ -1763,7 +1842,7 @@ can_fence_host_with_device(stonith_device_t * dev, struct device_search_s *searc
+ return;
+ }
+
+- if (string_in_list(dev->targets, alias)) {
++ if (pcmk__str_in_list(dev->targets, alias, pcmk__str_casei)) {
+ can = TRUE;
+ }
+
+diff --git a/daemons/fenced/fenced_remote.c b/daemons/fenced/fenced_remote.c
+index cf91aca..224f2ba 100644
+--- a/daemons/fenced/fenced_remote.c
++++ b/daemons/fenced/fenced_remote.c
+@@ -1522,6 +1522,25 @@ advance_topology_device_in_level(remote_fencing_op_t *op, const char *device,
+ }
+ }
+
++static gboolean
++check_watchdog_fencing_and_wait(remote_fencing_op_t * op)
++{
++ if (node_does_watchdog_fencing(op->target)) {
++
++ crm_notice("Waiting %lds for %s to self-fence (%s) for "
++ "client %s " CRM_XS " id=%.8s",
++ (stonith_watchdog_timeout_ms / 1000),
++ op->target, op->action, op->client_name, op->id);
++ op->op_timer_one = g_timeout_add(stonith_watchdog_timeout_ms,
++ remote_op_watchdog_done, op);
++ return TRUE;
++ } else {
++ crm_debug("Skipping fallback to watchdog-fencing as %s is "
++ "not in host-list", op->target);
++ }
++ return FALSE;
++}
++
+ void
+ call_remote_stonith(remote_fencing_op_t * op, st_query_result_t * peer, int rc)
+ {
+@@ -1592,26 +1611,33 @@ call_remote_stonith(remote_fencing_op_t * op, st_query_result_t * peer, int rc)
+ g_source_remove(op->op_timer_one);
+ }
+
+- if(stonith_watchdog_timeout_ms > 0 && device && pcmk__str_eq(device, "watchdog", pcmk__str_casei)) {
+- crm_notice("Waiting %lds for %s to self-fence (%s) for client %s "
+- CRM_XS " id=%.8s", (stonith_watchdog_timeout_ms / 1000),
+- op->target, op->action, op->client_name, op->id);
+- op->op_timer_one = g_timeout_add(stonith_watchdog_timeout_ms, remote_op_watchdog_done, op);
+-
+- /* TODO check devices to verify watchdog will be in use */
+- } else if(stonith_watchdog_timeout_ms > 0
+- && pcmk__str_eq(peer->host, op->target, pcmk__str_casei)
+- && !pcmk__str_eq(op->action, "on", pcmk__str_casei)) {
+- crm_notice("Waiting %lds for %s to self-fence (%s) for client %s "
+- CRM_XS " id=%.8s", (stonith_watchdog_timeout_ms / 1000),
+- op->target, op->action, op->client_name, op->id);
+- op->op_timer_one = g_timeout_add(stonith_watchdog_timeout_ms, remote_op_watchdog_done, op);
+-
+- } else {
++ if (!(stonith_watchdog_timeout_ms > 0 && (
++ (pcmk__str_eq(device, STONITH_WATCHDOG_ID,
++ pcmk__str_none)) ||
++ (pcmk__str_eq(peer->host, op->target, pcmk__str_casei)
++ && !pcmk__str_eq(op->action, "on", pcmk__str_casei))) &&
++ check_watchdog_fencing_and_wait(op))) {
++
++ /* Some thoughts about self-fencing cases reaching this point:
++ - Actually check in check_watchdog_fencing_and_wait
++ shouldn't fail if STONITH_WATCHDOG_ID is
++ chosen as fencing-device and it being present implies
++ watchdog-fencing is enabled anyway
++ - If watchdog-fencing is disabled either in general or for
++ a specific target - detected in check_watchdog_fencing_and_wait -
++ for some other kind of self-fencing we can't expect
++ a success answer but timeout is fine if the node doesn't
++ come back in between
++ - Delicate might be the case where we have watchdog-fencing
++ enabled for a node but the watchdog-fencing-device isn't
++ explicitly chosen for suicide. Local pe-execution in sbd
++ may detect the node as unclean and lead to timely suicide.
++ Otherwise the selection of stonith-watchdog-timeout at
++ least is questionable.
++ */
+ op->op_timer_one = g_timeout_add((1000 * timeout_one), remote_op_timeout_one, op);
+ }
+
+-
+ send_cluster_message(crm_get_peer(0, peer->host), crm_msg_stonith_ng, remote_op, FALSE);
+ peer->tried = TRUE;
+ free_xml(remote_op);
+@@ -1645,12 +1671,11 @@ call_remote_stonith(remote_fencing_op_t * op, st_query_result_t * peer, int rc)
+ * but we have all the expected replies, then no devices
+ * are available to execute the fencing operation. */
+
+- if(stonith_watchdog_timeout_ms && pcmk__str_eq(device, "watchdog", pcmk__str_null_matches | pcmk__str_casei)) {
+- crm_notice("Waiting %lds for %s to self-fence (%s) for client %s "
+- CRM_XS " id=%.8s", (stonith_watchdog_timeout_ms / 1000),
+- op->target, op->action, op->client_name, op->id);
+- op->op_timer_one = g_timeout_add(stonith_watchdog_timeout_ms, remote_op_watchdog_done, op);
+- return;
++ if(stonith_watchdog_timeout_ms > 0 && pcmk__str_eq(device,
++ STONITH_WATCHDOG_ID, pcmk__str_null_matches)) {
++ if (check_watchdog_fencing_and_wait(op)) {
++ return;
++ }
+ }
+
+ if (op->state == st_query) {
+diff --git a/daemons/fenced/pacemaker-fenced.c b/daemons/fenced/pacemaker-fenced.c
+index 39738d8..7f8b427 100644
+--- a/daemons/fenced/pacemaker-fenced.c
++++ b/daemons/fenced/pacemaker-fenced.c
+@@ -42,6 +42,7 @@
+
+ char *stonith_our_uname = NULL;
+ long stonith_watchdog_timeout_ms = 0;
++GList *stonith_watchdog_targets = NULL;
+
+ static GMainLoop *mainloop = NULL;
+
+@@ -578,7 +579,44 @@ our_node_allowed_for(pe_resource_t *rsc)
+ }
+
+ static void
+-watchdog_device_update(xmlNode *cib)
++watchdog_device_update(void)
++{
++ if (stonith_watchdog_timeout_ms > 0) {
++ if (!g_hash_table_lookup(device_list, STONITH_WATCHDOG_ID) &&
++ !stonith_watchdog_targets) {
++ /* getting here watchdog-fencing enabled, no device there yet
++ and reason isn't stonith_watchdog_targets preventing that
++ */
++ int rc;
++ xmlNode *xml;
++
++ xml = create_device_registration_xml(
++ STONITH_WATCHDOG_ID,
++ st_namespace_internal,
++ STONITH_WATCHDOG_AGENT,
++ NULL, /* stonith_device_register will add our
++ own name as PCMK_STONITH_HOST_LIST param
++ so we can skip that here
++ */
++ NULL);
++ rc = stonith_device_register(xml, NULL, TRUE);
++ free_xml(xml);
++ if (rc != pcmk_ok) {
++ crm_crit("Cannot register watchdog pseudo fence agent");
++ crm_exit(CRM_EX_FATAL);
++ }
++ }
++
++ } else {
++ /* be silent if no device - todo parameter to stonith_device_remove */
++ if (g_hash_table_lookup(device_list, STONITH_WATCHDOG_ID)) {
++ stonith_device_remove(STONITH_WATCHDOG_ID, TRUE);
++ }
++ }
++}
++
++static void
++update_stonith_watchdog_timeout_ms(xmlNode *cib)
+ {
+ xmlNode *stonith_enabled_xml = NULL;
+ const char *stonith_enabled_s = NULL;
+@@ -608,33 +646,7 @@ watchdog_device_update(xmlNode *cib)
+ }
+ }
+
+- if (timeout_ms != stonith_watchdog_timeout_ms) {
+- crm_notice("New watchdog timeout %lds (was %lds)", timeout_ms/1000, stonith_watchdog_timeout_ms/1000);
+- stonith_watchdog_timeout_ms = timeout_ms;
+-
+- if (stonith_watchdog_timeout_ms > 0) {
+- int rc;
+- xmlNode *xml;
+- stonith_key_value_t *params = NULL;
+-
+- params = stonith_key_value_add(params, PCMK_STONITH_HOST_LIST,
+- stonith_our_uname);
+-
+- xml = create_device_registration_xml("watchdog", st_namespace_internal,
+- STONITH_WATCHDOG_AGENT, params,
+- NULL);
+- stonith_key_value_freeall(params, 1, 1);
+- rc = stonith_device_register(xml, NULL, FALSE);
+- free_xml(xml);
+- if (rc != pcmk_ok) {
+- crm_crit("Cannot register watchdog pseudo fence agent");
+- crm_exit(CRM_EX_FATAL);
+- }
+-
+- } else {
+- stonith_device_remove("watchdog", FALSE);
+- }
+- }
++ stonith_watchdog_timeout_ms = timeout_ms;
+ }
+
+ /*!
+@@ -677,6 +689,16 @@ static void cib_device_update(pe_resource_t *rsc, pe_working_set_t *data_set)
+ return;
+ }
+
++ /* if watchdog-fencing is disabled handle any watchdog-fence
++ resource as if it was disabled
++ */
++ if ((stonith_watchdog_timeout_ms <= 0) &&
++ pcmk__str_eq(rsc->id, STONITH_WATCHDOG_ID, pcmk__str_none)) {
++ crm_info("Watchdog-fencing disabled thus handling "
++ "device %s as disabled", rsc->id);
++ return;
++ }
++
+ /* Check whether our node is allowed for this resource (and its parent if in a group) */
+ node = our_node_allowed_for(rsc);
+ if (rsc->parent && (rsc->parent->variant == pe_group)) {
+@@ -772,6 +794,12 @@ cib_devices_update(void)
+ }
+ }
+
++ /* have list repopulated if cib has a watchdog-fencing-resource
++ TODO: keep a cached list for queries happening while we are refreshing
++ */
++ g_list_free_full(stonith_watchdog_targets, free);
++ stonith_watchdog_targets = NULL;
++
+ for (gIter = fenced_data_set->resources; gIter != NULL; gIter = gIter->next) {
+ cib_device_update(gIter->data, fenced_data_set);
+ }
+@@ -825,6 +853,8 @@ update_cib_stonith_devices_v2(const char *event, xmlNode * msg)
+ if (search != NULL) {
+ *search = 0;
+ stonith_device_remove(rsc_id, TRUE);
++ /* watchdog_device_update called afterwards
++ to fall back to implicit definition if needed */
+ } else {
+ crm_warn("Ignoring malformed CIB update (resource deletion)");
+ }
+@@ -968,6 +998,24 @@ node_has_attr(const char *node, const char *name, const char *value)
+ return (match != NULL);
+ }
+
++/*!
++ * \internal
++ * \brief Check whether a node does watchdog-fencing
++ *
++ * \param[in] node Name of node to check
++ *
++ * \return TRUE if node found in stonith_watchdog_targets
++ * or stonith_watchdog_targets is empty indicating
++ * all nodes are doing watchdog-fencing
++ */
++gboolean
++node_does_watchdog_fencing(const char *node)
++{
++ return ((stonith_watchdog_targets == NULL) ||
++ pcmk__str_in_list(stonith_watchdog_targets, node, pcmk__str_casei));
++}
++
++
+ static void
+ update_fencing_topology(const char *event, xmlNode * msg)
+ {
+@@ -1073,6 +1121,8 @@ update_cib_cache_cb(const char *event, xmlNode * msg)
+ xmlNode *stonith_enabled_xml = NULL;
+ const char *stonith_enabled_s = NULL;
+ static gboolean stonith_enabled_saved = TRUE;
++ long timeout_ms_saved = stonith_watchdog_timeout_ms;
++ gboolean need_full_refresh = FALSE;
+
+ if(!have_cib_devices) {
+ crm_trace("Skipping updates until we get a full dump");
+@@ -1127,6 +1177,7 @@ update_cib_cache_cb(const char *event, xmlNode * msg)
+ }
+
+ pcmk__refresh_node_caches_from_cib(local_cib);
++ update_stonith_watchdog_timeout_ms(local_cib);
+
+ stonith_enabled_xml = get_xpath_object("//nvpair[@name='stonith-enabled']",
+ local_cib, LOG_NEVER);
+@@ -1134,23 +1185,30 @@ update_cib_cache_cb(const char *event, xmlNode * msg)
+ stonith_enabled_s = crm_element_value(stonith_enabled_xml, XML_NVPAIR_ATTR_VALUE);
+ }
+
+- watchdog_device_update(local_cib);
+-
+ if (stonith_enabled_s && crm_is_true(stonith_enabled_s) == FALSE) {
+ crm_trace("Ignoring CIB updates while fencing is disabled");
+ stonith_enabled_saved = FALSE;
+- return;
+
+ } else if (stonith_enabled_saved == FALSE) {
+ crm_info("Updating fencing device and topology lists "
+ "now that fencing is enabled");
+ stonith_enabled_saved = TRUE;
+- fencing_topology_init();
+- cib_devices_update();
++ need_full_refresh = TRUE;
+
+ } else {
+- update_fencing_topology(event, msg);
+- update_cib_stonith_devices(event, msg);
++ if (timeout_ms_saved != stonith_watchdog_timeout_ms) {
++ need_full_refresh = TRUE;
++ } else {
++ update_fencing_topology(event, msg);
++ update_cib_stonith_devices(event, msg);
++ watchdog_device_update();
++ }
++ }
++
++ if (need_full_refresh) {
++ fencing_topology_init();
++ cib_devices_update();
++ watchdog_device_update();
+ }
+ }
+
+@@ -1162,10 +1220,11 @@ init_cib_cache_cb(xmlNode * msg, int call_id, int rc, xmlNode * output, void *us
+ local_cib = copy_xml(output);
+
+ pcmk__refresh_node_caches_from_cib(local_cib);
++ update_stonith_watchdog_timeout_ms(local_cib);
+
+ fencing_topology_init();
+- watchdog_device_update(local_cib);
+ cib_devices_update();
++ watchdog_device_update();
+ }
+
+ static void
+diff --git a/daemons/fenced/pacemaker-fenced.h b/daemons/fenced/pacemaker-fenced.h
+index d330fda..14e085e 100644
+--- a/daemons/fenced/pacemaker-fenced.h
++++ b/daemons/fenced/pacemaker-fenced.h
+@@ -260,14 +260,15 @@ bool fencing_peer_active(crm_node_t *peer);
+
+ int stonith_manual_ack(xmlNode * msg, remote_fencing_op_t * op);
+
+-gboolean string_in_list(GList *list, const char *item);
+-
+ gboolean node_has_attr(const char *node, const char *name, const char *value);
+
++gboolean node_does_watchdog_fencing(const char *node);
++
+ extern char *stonith_our_uname;
+ extern gboolean stand_alone;
+ extern GHashTable *device_list;
+ extern GHashTable *topology;
+ extern long stonith_watchdog_timeout_ms;
++extern GList *stonith_watchdog_targets;
+
+ extern GHashTable *stonith_remote_op_list;
+diff --git a/include/crm/crm.h b/include/crm/crm.h
+index ee52c36..7861c16 100644
+--- a/include/crm/crm.h
++++ b/include/crm/crm.h
+@@ -66,7 +66,7 @@ extern "C" {
+ * >=3.0.13: Fail counts include operation name and interval
+ * >=3.2.0: DC supports PCMK_LRM_OP_INVALID and PCMK_LRM_OP_NOT_CONNECTED
+ */
+-# define CRM_FEATURE_SET "3.10.2"
++# define CRM_FEATURE_SET "3.11.0"
+
+ /* Pacemaker's CPG protocols use fixed-width binary fields for the sender and
+ * recipient of a CPG message. This imposes an arbitrary limit on cluster node
+diff --git a/include/crm/fencing/internal.h b/include/crm/fencing/internal.h
+index 8bcb544..f222edb 100644
+--- a/include/crm/fencing/internal.h
++++ b/include/crm/fencing/internal.h
+@@ -164,7 +164,10 @@ void stonith__device_parameter_flags(uint32_t *device_flags,
+ # define STONITH_OP_LEVEL_ADD "st_level_add"
+ # define STONITH_OP_LEVEL_DEL "st_level_remove"
+
+-# define STONITH_WATCHDOG_AGENT "#watchdog"
++# define STONITH_WATCHDOG_AGENT "fence_watchdog"
++/* Don't change 2 below as it would break rolling upgrade */
++# define STONITH_WATCHDOG_AGENT_INTERNAL "#watchdog"
++# define STONITH_WATCHDOG_ID "watchdog"
+
+ # ifdef HAVE_STONITH_STONITH_H
+ // utilities from st_lha.c
+@@ -211,4 +214,7 @@ stonith__op_state_pending(enum op_state state)
+ return state != st_failed && state != st_done;
+ }
+
++gboolean stonith__watchdog_fencing_enabled_for_node(const char *node);
++gboolean stonith__watchdog_fencing_enabled_for_node_api(stonith_t *st, const char *node);
++
+ #endif
+diff --git a/lib/fencing/st_client.c b/lib/fencing/st_client.c
+index e285f51..0ff9815 100644
+--- a/lib/fencing/st_client.c
++++ b/lib/fencing/st_client.c
+@@ -195,6 +195,67 @@ stonith_get_namespace(const char *agent, const char *namespace_s)
+ return st_namespace_invalid;
+ }
+
++gboolean
++stonith__watchdog_fencing_enabled_for_node_api(stonith_t *st, const char *node)
++{
++ gboolean rv = FALSE;
++ stonith_t *stonith_api = st?st:stonith_api_new();
++ char *list = NULL;
++
++ if(stonith_api) {
++ if (stonith_api->state == stonith_disconnected) {
++ int rc = stonith_api->cmds->connect(stonith_api, "stonith-api", NULL);
++
++ if (rc != pcmk_ok) {
++ crm_err("Failed connecting to Stonith-API for watchdog-fencing-query.");
++ }
++ }
++
++ if (stonith_api->state != stonith_disconnected) {
++ /* caveat!!!
++ * this might fail when when stonithd is just updating the device-list
++ * probably something we should fix as well for other api-calls */
++ int rc = stonith_api->cmds->list(stonith_api, st_opt_sync_call, STONITH_WATCHDOG_ID, &list, 0);
++ if ((rc != pcmk_ok) || (list == NULL)) {
++ /* due to the race described above it can happen that
++ * we drop in here - so as not to make remote nodes
++ * panic on that answer
++ */
++ crm_warn("watchdog-fencing-query failed");
++ } else if (list[0] == '\0') {
++ crm_warn("watchdog-fencing-query returned an empty list - any node");
++ rv = TRUE;
++ } else {
++ GList *targets = stonith__parse_targets(list);
++ rv = pcmk__str_in_list(targets, node, pcmk__str_casei);
++ g_list_free_full(targets, free);
++ }
++ free(list);
++ if (!st) {
++ /* if we're provided the api we still might have done the
++ * connection - but let's assume the caller won't bother
++ */
++ stonith_api->cmds->disconnect(stonith_api);
++ }
++ }
++
++ if (!st) {
++ stonith_api_delete(stonith_api);
++ }
++ } else {
++ crm_err("Stonith-API for watchdog-fencing-query couldn't be created.");
++ }
++ crm_trace("Pacemaker assumes node %s %sto do watchdog-fencing.",
++ node, rv?"":"not ");
++ return rv;
++}
++
++gboolean
++stonith__watchdog_fencing_enabled_for_node(const char *node)
++{
++ return stonith__watchdog_fencing_enabled_for_node_api(NULL, node);
++}
++
+ static void
+ log_action(stonith_action_t *action, pid_t pid)
+ {
+diff --git a/lib/lrmd/lrmd_client.c b/lib/lrmd/lrmd_client.c
+index 87d050e..bf4bceb 100644
+--- a/lib/lrmd/lrmd_client.c
++++ b/lib/lrmd/lrmd_client.c
+@@ -34,6 +34,7 @@
+ #include <crm/msg_xml.h>
+
+ #include <crm/stonith-ng.h>
++#include <crm/fencing/internal.h>
+
+ #ifdef HAVE_GNUTLS_GNUTLS_H
+ # undef KEYFILE
+@@ -934,7 +935,10 @@ lrmd__validate_remote_settings(lrmd_t *lrmd, GHashTable *hash)
+ crm_xml_add(data, F_LRMD_ORIGIN, __func__);
+
+ value = g_hash_table_lookup(hash, "stonith-watchdog-timeout");
+- crm_xml_add(data, F_LRMD_WATCHDOG, value);
++ if ((value) &&
++ (stonith__watchdog_fencing_enabled_for_node(native->remote_nodename))) {
++ crm_xml_add(data, F_LRMD_WATCHDOG, value);
++ }
+
+ rc = lrmd_send_command(lrmd, LRMD_OP_CHECK, data, NULL, 0, 0,
+ (native->type == pcmk__client_ipc));
+diff --git a/rpm/pacemaker.spec.in b/rpm/pacemaker.spec.in
+index 79e78ed..f58357a 100644
+--- a/rpm/pacemaker.spec.in
++++ b/rpm/pacemaker.spec.in
+@@ -744,6 +744,7 @@ exit 0
+ %doc %{_mandir}/man8/crm_attribute.*
+ %doc %{_mandir}/man8/crm_master.*
+ %doc %{_mandir}/man8/fence_legacy.*
++%doc %{_mandir}/man8/fence_watchdog.*
+ %doc %{_mandir}/man8/pacemakerd.*
+
+ %doc %{_datadir}/pacemaker/alerts
+@@ -796,6 +797,7 @@ exit 0
+ %{_sbindir}/crm_simulate
+ %{_sbindir}/crm_report
+ %{_sbindir}/crm_ticket
++%{_sbindir}/fence_watchdog
+ %{_sbindir}/stonith_admin
+ # "dirname" is owned by -schemas, which is a prerequisite
+ %{_datadir}/pacemaker/report.collector
+@@ -822,6 +824,7 @@ exit 0
+ %exclude %{_mandir}/man8/crm_attribute.*
+ %exclude %{_mandir}/man8/crm_master.*
+ %exclude %{_mandir}/man8/fence_legacy.*
++%exclude %{_mandir}/man8/fence_watchdog.*
+ %exclude %{_mandir}/man8/pacemakerd.*
+ %exclude %{_mandir}/man8/pacemaker-remoted.*
+
+--
+1.8.3.1
+
diff --git a/0002-Fix-watchdog-fencing-Silence-warning-without-node-re.patch b/0002-Fix-watchdog-fencing-Silence-warning-without-node-re.patch
new file mode 100644
index 0000000..ccfbccc
--- /dev/null
+++ b/0002-Fix-watchdog-fencing-Silence-warning-without-node-re.patch
@@ -0,0 +1,25 @@
+From 53dd360f096e5f005e3221e8d44d82d3654b5172 Mon Sep 17 00:00:00 2001
+From: Klaus Wenninger <klaus.wenninger(a)aon.at>
+Date: Wed, 4 Aug 2021 15:57:23 +0200
+Subject: [PATCH] Fix: watchdog-fencing: Silence warning without node
+ restriction
+
+---
+ lib/fencing/st_client.c | 1 -
+ 1 file changed, 1 deletion(-)
+
+diff --git a/lib/fencing/st_client.c b/lib/fencing/st_client.c
+index 0ff9815..14fa7b2 100644
+--- a/lib/fencing/st_client.c
++++ b/lib/fencing/st_client.c
+@@ -223,7 +223,6 @@ stonith__watchdog_fencing_enabled_for_node_api(stonith_t *st, const char *node)
+ */
+ crm_warn("watchdog-fencing-query failed");
+ } else if (list[0] == '\0') {
+- crm_warn("watchdog-fencing-query returned an empty list - any node");
+ rv = TRUE;
+ } else {
+ GList *targets = stonith__parse_targets(list);
+--
+1.8.3.1
+
diff --git a/pacemaker.spec b/pacemaker.spec
index b3f962b..a8f1d51 100644
--- a/pacemaker.spec
+++ b/pacemaker.spec
@@ -32,10 +32,10 @@
## can be incremented to build packages reliably considered "newer"
## than previously built packages with the same pcmkversion)
%global pcmkversion 2.1.1
-%global specversion 0.1.rc1
+%global specversion 0.2.rc2
## Upstream commit (full commit ID, abbreviated commit ID, or tag) to build
-%global commit 9e3a841a33c3125034922ec061f6516d307f20d1
+%global commit a64ad221abe9cdd968ceacd35c23832ce0fcd189
## Since git v2.11, the extent of abbreviation is autoscaled by default
## (used to be constant of 7), so we need to convey it for non-tags, too.
@@ -215,6 +215,8 @@ Url: https://www.clusterlabs.org/
# You can use "spectool -s 0 pacemaker.spec" (rpmdevtools) to show final URL.
Source0: https://codeload.github.com/%{github_owner}/%{name}/tar.gz/%{archive_gith...
Source1: https://codeload.github.com/%{github_owner}/%{nagios_name}/tar.gz/%{nagio...
+Patch0: 0001-feature-watchdog-fencing-allow-restriction-to-certai.patch
+Patch1: 0002-Fix-watchdog-fencing-Silence-warning-without-node-re.patch
# upstream commits
@@ -240,9 +242,19 @@ Requires: libqb >= 0.17.0
BuildRequires: libqb-devel >= 0.17.0
# Required basic build tools
-BuildRequires: coreutils findutils grep sed
-BuildRequires: autoconf automake gcc make pkgconfig
-BuildRequires: libtool %{?pkgname_libtool_devel}
+BuildRequires: autoconf
+BuildRequires: automake
+BuildRequires: coreutils
+BuildRequires: findutils
+BuildRequires: gcc
+BuildRequires: grep
+BuildRequires: libtool
+BuildRequires: make
+%if %{defined pkgname_libtool_devel}
+BuildRequires: %{?pkgname_libtool_devel}
+%endif
+BuildRequires: pkgconfig
+BuildRequires: sed
# Required for core functionality
BuildRequires: pkgconfig(glib-2.0) >= 2.42
@@ -250,8 +262,12 @@ BuildRequires: libxml2-devel libxslt-devel libuuid-devel
BuildRequires: %{pkgname_bzip2_devel}
# Enables optional functionality
-BuildRequires: ncurses-devel %{pkgname_docbook_xsl}
-BuildRequires: help2man %{pkgname_gnutls_devel} pam-devel pkgconfig(dbus-1)
+BuildRequires: pkgconfig(dbus-1)
+BuildRequires: %{pkgname_docbook_xsl}
+BuildRequires: %{pkgname_gnutls_devel}
+BuildRequires: help2man
+BuildRequires: ncurses-devel
+BuildRequires: pam-devel
BuildRequires: pkgconfig(systemd)
@@ -266,7 +282,9 @@ BuildRequires: %{pkgname_glue_libs}-devel
%endif
%if %{with doc}
-BuildRequires: asciidoc inkscape %{python_name}-sphinx
+BuildRequires: asciidoc
+BuildRequires: inkscape
+BuildRequires: %{python_name}-sphinx
%endif
Provides: pcmk-cluster-manager = %{version}-%{release}
@@ -368,11 +386,16 @@ License: GPLv2+ and LGPLv2+
Summary: Pacemaker development package
Requires: %{pkgname_pcmk_libs}%{?_isa} = %{version}-%{release}
Requires: %{name}-cluster-libs%{?_isa} = %{version}-%{release}
-Requires: %{?pkgname_libtool_devel_arch} libuuid-devel%{?_isa}
-Requires: libxml2-devel%{?_isa} libxslt-devel%{?_isa}
-Requires: %{pkgname_bzip2_devel}%{?_isa} glib2-devel%{?_isa}
+Requires: %{pkgname_bzip2_devel}%{?_isa}
+Requires: corosync-devel >= 2.0.0
+Requires: glib2-devel%{?_isa}
Requires: libqb-devel%{?_isa}
-Requires: corosync-devel%{?_isa} >= 2.0.0
+%if %{defined pkgname_libtool_devel_arch}
+Requires: %{?pkgname_libtool_devel_arch}
+%endif
+Requires: libuuid-devel%{?_isa}
+Requires: libxml2-devel%{?_isa}
+Requires: libxslt-devel%{?_isa}
%description -n %{pkgname_pcmk_libs}-devel
Pacemaker is an advanced, scalable High-Availability cluster resource
@@ -645,6 +668,7 @@ exit 0
%doc %{_mandir}/man7/ocf_pacemaker_remote.*
%doc %{_mandir}/man8/crm_attribute.*
%doc %{_mandir}/man8/crm_master.*
+%doc %{_mandir}/man8/fence_watchdog.*
%doc %{_mandir}/man8/pacemakerd.*
%doc %{_datadir}/pacemaker/alerts
@@ -685,6 +709,7 @@ exit 0
%{_sbindir}/crm_simulate
%{_sbindir}/crm_report
%{_sbindir}/crm_ticket
+%{_sbindir}/fence_watchdog
%{_sbindir}/stonith_admin
# "dirname" is owned by -schemas, which is a prerequisite
%{_datadir}/pacemaker/report.collector
@@ -711,6 +736,7 @@ exit 0
%exclude %{_mandir}/man8/crm_attribute.*
%exclude %{_mandir}/man8/crm_master.*
%exclude %{_mandir}/man8/fence_legacy.*
+%exclude %{_mandir}/man8/fence_watchdog.*
%exclude %{_mandir}/man8/pacemakerd.*
%exclude %{_mandir}/man8/pacemaker-remoted.*
@@ -799,6 +825,13 @@ exit 0
%license %{nagios_name}-%{nagios_hash}/COPYING
%changelog
+* Mon Aug 9 2021 Klaus Wenninger <kwenning(a)redhat.com> - 2.1.1-0.2.rc2
+- Update for new upstream tarball for release candidate: Pacemaker-2.1.1-rc2,
+ for full details, see included ChangeLog file or
+ https://github.com/ClusterLabs/pacemaker/releases/tag/Pacemaker-2.1.1-rc2
+- Added feature patch that allows to explicitly specify nodes that
+ should do watchdog-fencing
+
* Wed Jul 28 2021 Klaus Wenninger <kwenning(a)redhat.com> - 2.1.1-0.1.rc1
- Update for new upstream tarball for release candidate: Pacemaker-2.1.1-rc1,
for full details, see included ChangeLog file or
diff --git a/sources b/sources
index b933127..2ccc4d0 100644
--- a/sources
+++ b/sources
@@ -1,2 +1,2 @@
SHA512 (nagios-agents-metadata-105ab8a7b2c16b9a29cf1c1596b80136eeef332b.tar.gz) = 11ddeb48a4929e7642b6dfa9c7962aa1d7a1af1c569830f55ed6cd6773abac13377317327bc1db8411c8077884f83f81cc54d746c834b63a99fa6dc219b5caad
-SHA512 (pacemaker-9e3a841a3.tar.gz) = 26745ba0d9f6730de13ad106fe57beaff9d49c7df1ec528f0689c2faa872608408fb04401a4d49d73deec5422bd7665496a964b1fcbe69866a5983ac2306b6be
+SHA512 (pacemaker-a64ad221a.tar.gz) = ee954bd8efd135965290f9958b65680cbf101e14e2fd22d326e53a250281a7e5b6b6ec979c7f76072514dbf4d29868e99931842dbcc99bab5e5ba5e4bbc4ef0a
commit 6fcc0b8f5b6f278f33126c445552ad30336e5380
Author: Klaus Wenninger <klaus.wenninger(a)aon.at>
Date: Wed Jul 28 14:28:47 2021 +0200
* Wed Jul 28 2021 Klaus Wenninger <kwenning(a)redhat.com> - 2.1.1-0.1.rc1
- Update for new upstream tarball for release candidate: Pacemaker-2.1.1-rc1,
for full details, see included ChangeLog file or
https://github.com/ClusterLabs/pacemaker/releases/tag/Pacemaker-2.1.1-rc1
diff --git a/001-ping-agent.patch b/001-ping-agent.patch
deleted file mode 100644
index 89fe41a..0000000
--- a/001-ping-agent.patch
+++ /dev/null
@@ -1,225 +0,0 @@
-From c6ee0973522268ed7b3241cf0ec2e06398444114 Mon Sep 17 00:00:00 2001
-From: Grace Chin <gchin(a)redhat.com>
-Date: Tue, 4 May 2021 12:02:17 -0400
-Subject: [PATCH 1/4] Remove deprecated attrd_options
-
----
- extra/resources/ping | 11 +++--------
- 1 file changed, 3 insertions(+), 8 deletions(-)
-
-diff --git a/extra/resources/ping b/extra/resources/ping
-index 3cf8dfe..2e93f22 100755
---- a/extra/resources/ping
-+++ b/extra/resources/ping
-@@ -178,7 +178,7 @@ ping_stop() {
-
- rm -f "${OCF_RESKEY_pidfile}"
-
-- attrd_updater -D -n "$OCF_RESKEY_name" -d "$OCF_RESKEY_dampen" $attrd_options
-+ attrd_updater -D -n "$OCF_RESKEY_name" -d "$OCF_RESKEY_dampen"
-
- return $OCF_SUCCESS
- }
-@@ -302,9 +302,9 @@ ping_update() {
-
- score=$(expr $active \* $OCF_RESKEY_multiplier)
- if [ "$__OCF_ACTION" = "start" ] ; then
-- attrd_updater -n "$OCF_RESKEY_name" -B "$score" -d "$OCF_RESKEY_dampen" $attrd_options
-+ attrd_updater -n "$OCF_RESKEY_name" -B "$score" -d "$OCF_RESKEY_dampen"
- else
-- attrd_updater -n "$OCF_RESKEY_name" -v "$score" -d "$OCF_RESKEY_dampen" $attrd_options
-+ attrd_updater -n "$OCF_RESKEY_name" -v "$score" -d "$OCF_RESKEY_dampen"
- fi
- rc=$?
- case $rc in
-@@ -396,11 +396,6 @@ case "${OCF_RESKEY_debug}" in
- ;;
- esac
-
--attrd_options='-q'
--if [ "${OCF_RESKEY_debug}" = "true" ]; then
-- attrd_options=''
--fi
--
- case "$__OCF_ACTION" in
- meta-data) meta_data
- exit $OCF_SUCCESS
---
-1.8.3.1
-
-
-From 6d6c4691cf0970059689856c354daf9e098b4451 Mon Sep 17 00:00:00 2001
-From: Grace Chin <gchin(a)redhat.com>
-Date: Tue, 4 May 2021 14:50:37 -0400
-Subject: [PATCH 2/4] Replace debug values, true and false, with 0 and 1
-
----
- extra/resources/ping | 8 ++++----
- 1 file changed, 4 insertions(+), 4 deletions(-)
-
-diff --git a/extra/resources/ping b/extra/resources/ping
-index 2e93f22..fee019b 100755
---- a/extra/resources/ping
-+++ b/extra/resources/ping
-@@ -24,7 +24,7 @@
- : ${OCF_RESKEY_dampen:="5s"}
- : ${OCF_RESKEY_attempts:="3"}
- : ${OCF_RESKEY_multiplier:="1"}
--: ${OCF_RESKEY_debug:="false"}
-+: ${OCF_RESKEY_debug:="0"}
- : ${OCF_RESKEY_failure_score:="0"}
- : ${OCF_RESKEY_use_fping:="1"}
- : ${OCF_RESKEY_host_list:=""}
-@@ -152,7 +152,7 @@ END
-
- ping_conditional_log() {
- level="$1"; shift
-- if [ "${OCF_RESKEY_debug}" = "true" ]; then
-+ if [ $OCF_RESKEY_debug -gt 0 ]; then
- ocf_log "$level" "$*"
- fi
- }
-@@ -388,8 +388,8 @@ fi
-
- # Check the debug option
- case "${OCF_RESKEY_debug}" in
-- true|True|TRUE|1) OCF_RESKEY_debug=true;;
-- false|False|FALSE|0) OCF_RESKEY_debug=false;;
-+ true|True|TRUE|1) OCF_RESKEY_debug=0;;
-+ false|False|FALSE|0) OCF_RESKEY_debug=1;;
- *)
- ocf_log warn "Value for 'debug' is incorrect. Please specify 'true' or 'false' not: ${OCF_RESKEY_debug}"
- OCF_RESKEY_debug=false
---
-1.8.3.1
-
-
-From a886a31056b6aca764c6911f5432af2c5ebf51df Mon Sep 17 00:00:00 2001
-From: Grace Chin <gchin(a)redhat.com>
-Date: Tue, 11 May 2021 11:04:50 -0400
-Subject: [PATCH 3/4] Add verbose debug mode which logs ping and fping output
- when set
-
----
- extra/resources/ping | 19 ++++++++++++++-----
- 1 file changed, 14 insertions(+), 5 deletions(-)
-
-diff --git a/extra/resources/ping b/extra/resources/ping
-index fee019b..cc796af 100755
---- a/extra/resources/ping
-+++ b/extra/resources/ping
-@@ -249,10 +249,13 @@ fping_check() {
-
- case $rc in
- 0)
-+ if [ $OCF_RESKEY_debug -gt 1 ]; then
-+ ping_conditional_log info "$output"
-+ fi
- ;;
- 1)
- for h in $(echo "$output" | grep "is unreachable" | awk '{print $1}'); do
-- ping_conditional_log warn "$h is inactive"
-+ ping_conditional_log warn "$h is inactive: $output"
- done
- ;;
- *)
-@@ -282,7 +285,12 @@ ping_check() {
- p_out=$($p_exe $p_args $OCF_RESKEY_options $host 2>&1); rc=$?
-
- case $rc in
-- 0) active=$(expr $active + 1);;
-+ 0)
-+ active=$(expr $active + 1)
-+ if [ $OCF_RESKEY_debug -gt 1 ]; then
-+ ping_conditional_log info "$p_out"
-+ fi
-+ ;;
- 1) ping_conditional_log warn "$host is inactive: $p_out";;
- *) ocf_log err "Unexpected result for '$p_exe $p_args $OCF_RESKEY_options $host' $rc: $p_out";;
- esac
-@@ -388,10 +396,11 @@ fi
-
- # Check the debug option
- case "${OCF_RESKEY_debug}" in
-- true|True|TRUE|1) OCF_RESKEY_debug=0;;
-- false|False|FALSE|0) OCF_RESKEY_debug=1;;
-+ true|True|TRUE|1) OCF_RESKEY_debug=1;;
-+ false|False|FALSE|0) OCF_RESKEY_debug=0;;
-+ verbose|Verbose|VERBOSE|2) OCF_RESKEY_debug=2;;
- *)
-- ocf_log warn "Value for 'debug' is incorrect. Please specify 'true' or 'false' not: ${OCF_RESKEY_debug}"
-+ ocf_log warn "Value for 'debug' is incorrect. Please specify 'true', 'false', or 'verbose', not: ${OCF_RESKEY_debug}"
- OCF_RESKEY_debug=false
- ;;
- esac
---
-1.8.3.1
-
-
-From 460043f133ced80e923b1290af70502a72deb7f8 Mon Sep 17 00:00:00 2001
-From: Grace Chin <gchin(a)redhat.com>
-Date: Tue, 11 May 2021 11:07:05 -0400
-Subject: [PATCH 4/4] Improve variable names
-
----
- extra/resources/ping | 20 ++++++++++----------
- 1 file changed, 10 insertions(+), 10 deletions(-)
-
-diff --git a/extra/resources/ping b/extra/resources/ping
-index cc796af..9763b60 100755
---- a/extra/resources/ping
-+++ b/extra/resources/ping
-@@ -244,22 +244,22 @@ fping_check() {
- timeout=$(expr $OCF_RESKEY_timeout \* 1000 / $OCF_RESKEY_attempts)
-
- cmd="$p_exe -r $OCF_RESKEY_attempts -t $timeout -B 1.0 $OCF_RESKEY_options $OCF_RESKEY_host_list"
-- output=$($cmd 2>&1); rc=$?
-- active=$(echo "$output" | grep "is alive" | wc -l)
-+ fping_output=$($cmd 2>&1); rc=$?
-+ active=$(echo "$fping_output" | grep "is alive" | wc -l)
-
- case $rc in
- 0)
- if [ $OCF_RESKEY_debug -gt 1 ]; then
-- ping_conditional_log info "$output"
-+ ping_conditional_log info "$fping_output"
- fi
- ;;
- 1)
-- for h in $(echo "$output" | grep "is unreachable" | awk '{print $1}'); do
-- ping_conditional_log warn "$h is inactive: $output"
-+ for h in $(echo "$fping_output" | grep "is unreachable" | awk '{print $1}'); do
-+ ping_conditional_log warn "$h is inactive: $fping_output"
- done
- ;;
- *)
-- ocf_log err "Unexpected result for '$cmd' $rc: $(echo "$output" | tr '\n' ';')"
-+ ocf_log err "Unexpected result for '$cmd' $rc: $(echo "$fping_output" | tr '\n' ';')"
- ;;
- esac
-
-@@ -282,17 +282,17 @@ ping_check() {
- *:*) p_exe=ping6
- esac
-
-- p_out=$($p_exe $p_args $OCF_RESKEY_options $host 2>&1); rc=$?
-+ ping_output=$($p_exe $p_args $OCF_RESKEY_options $host 2>&1); rc=$?
-
- case $rc in
- 0)
- active=$(expr $active + 1)
- if [ $OCF_RESKEY_debug -gt 1 ]; then
-- ping_conditional_log info "$p_out"
-+ ping_conditional_log info "$ping_output"
- fi
- ;;
-- 1) ping_conditional_log warn "$host is inactive: $p_out";;
-- *) ocf_log err "Unexpected result for '$p_exe $p_args $OCF_RESKEY_options $host' $rc: $p_out";;
-+ 1) ping_conditional_log warn "$host is inactive: $ping_output";;
-+ *) ocf_log err "Unexpected result for '$p_exe $p_args $OCF_RESKEY_options $host' $rc: $ping_output";;
- esac
- done
- return $active
---
-1.8.3.1
-
diff --git a/002-pacemakerd-options.patch b/002-pacemakerd-options.patch
deleted file mode 100644
index 56941ec..0000000
--- a/002-pacemakerd-options.patch
+++ /dev/null
@@ -1,451 +0,0 @@
-From 0d40ebf10b1794ece2c5c9768ea7222d3834d3b3 Mon Sep 17 00:00:00 2001
-From: Chris Lumens <clumens(a)redhat.com>
-Date: Thu, 13 May 2021 11:42:18 -0400
-Subject: [PATCH 1/4] Build: Use a different variable to find man page
- includes.
-
-With other programs outside of the tools directory being converted to
-use glib for command line handling, their includes are not going to be
-in tools/. So we need to use a different autoconf variable to find
-them.
----
- mk/common.mk | 6 +++---
- 1 file changed, 3 insertions(+), 3 deletions(-)
-
-diff --git a/mk/common.mk b/mk/common.mk
-index b247670..aa59feb 100644
---- a/mk/common.mk
-+++ b/mk/common.mk
-@@ -1,5 +1,5 @@
- #
--# Copyright 2014-2020 the Pacemaker project contributors
-+# Copyright 2014-2021 the Pacemaker project contributors
- #
- # The version control history for this file may have further details.
- #
-@@ -68,11 +68,11 @@ HELP2MAN_ARGS = -N --section 8 --name "Part of the Pacemaker cluster resource ma
- # and all wrappers to C code.
- %.8: % $(MAN8DEPS)
- $(AM_V_at)chmod a+x $(abs_builddir)/$<
-- $(AM_V_MAN)if [ -f $(top_srcdir)/tools/$@.inc ]; then \
-+ $(AM_V_MAN)if [ -f $(abs_srcdir)/$@.inc ]; then \
- PATH=$(abs_builddir):$$PATH $(HELP2MAN) $(HELP2MAN_ARGS) \
- -h --help-all \
- --no-discard-stderr \
-- -i $(top_srcdir)/tools/$@.inc $(abs_builddir)/$< \
-+ -i $(abs_srcdir)/$@.inc $(abs_builddir)/$< \
- | sed -f $(top_srcdir)/tools/fix-manpages > $@ ; \
- else \
- PATH=$(abs_builddir):$$PATH $(HELP2MAN) $(HELP2MAN_ARGS) \
---
-1.8.3.1
-
-
-From c7ab1d901bcbbf0137277e783e072777ca2f82d9 Mon Sep 17 00:00:00 2001
-From: Chris Lumens <clumens(a)redhat.com>
-Date: Thu, 13 May 2021 11:44:16 -0400
-Subject: [PATCH 2/4] Refactor: daemons: Remove the pid_file variable from
- pacemakerd.
-
-It's never used anywhere.
----
- daemons/pacemakerd/pacemakerd.c | 3 ---
- 1 file changed, 3 deletions(-)
-
-diff --git a/daemons/pacemakerd/pacemakerd.c b/daemons/pacemakerd/pacemakerd.c
-index 8ec9708..03d688e 100644
---- a/daemons/pacemakerd/pacemakerd.c
-+++ b/daemons/pacemakerd/pacemakerd.c
-@@ -27,8 +27,7 @@
-
- static crm_trigger_t *shutdown_trigger = NULL;
- static crm_trigger_t *startup_trigger = NULL;
--static const char *pid_file = PCMK_RUN_DIR "/pacemaker.pid";
-
- /* state we report when asked via pacemakerd-api status-ping */
- static const char *pacemakerd_state = XML_PING_ATTR_PACEMAKERDSTATE_INIT;
- static gboolean running_with_sbd = FALSE; /* local copy */
-@@ -224,7 +222,6 @@ main(int argc, char **argv)
- /* Legacy */
- break;
- case 'p':
-- pid_file = optarg;
- break;
- case 's':
- pcmk__set_env_option("node_start_state", "standby");
---
-1.8.3.1
-
-
-From 98990eed9f6a5dbde7c8a5aa0783e93d5479295b Mon Sep 17 00:00:00 2001
-From: Chris Lumens <clumens(a)redhat.com>
-Date: Thu, 13 May 2021 13:14:38 -0400
-Subject: [PATCH 3/4] Refactor: daemons: Use glib for command line handling in
- pacemakerd.
-
----
- daemons/pacemakerd/Makefile.am | 2 +
- daemons/pacemakerd/pacemakerd.8.inc | 5 +
- daemons/pacemakerd/pacemakerd.c | 195 ++++++++++++++++++------------------
- 3 files changed, 102 insertions(+), 100 deletions(-)
- create mode 100644 daemons/pacemakerd/pacemakerd.8.inc
-
-diff --git a/daemons/pacemakerd/Makefile.am b/daemons/pacemakerd/Makefile.am
-index cc657f5..84517a3 100644
---- a/daemons/pacemakerd/Makefile.am
-+++ b/daemons/pacemakerd/Makefile.am
-@@ -15,6 +15,8 @@ if BUILD_SYSTEMD
- systemdsystemunit_DATA = pacemaker.service
- endif
-
-+EXTRA_DIST = pacemakerd.8.inc
-+
- ## SOURCES
-
- noinst_HEADERS = pacemakerd.h
-diff --git a/daemons/pacemakerd/pacemakerd.8.inc b/daemons/pacemakerd/pacemakerd.8.inc
-new file mode 100644
-index 0000000..902af4e
---- /dev/null
-+++ b/daemons/pacemakerd/pacemakerd.8.inc
-@@ -0,0 +1,5 @@
-+[synopsis]
-+pacemakerd [options]
-+
-+/subsidiary Pacemaker daemons/
-+.SH OPTIONS
-diff --git a/daemons/pacemakerd/pacemakerd.c b/daemons/pacemakerd/pacemakerd.c
-index 03d688e..ce194bf 100644
---- a/daemons/pacemakerd/pacemakerd.c
-+++ b/daemons/pacemakerd/pacemakerd.c
-@@ -23,12 +23,54 @@
- #include <crm/msg_xml.h>
- #include <crm/common/ipc_internal.h>
- #include <crm/common/mainloop.h>
-+#include <crm/common/cmdline_internal.h>
- #include <crm/cluster/internal.h>
- #include <crm/cluster.h>
-
- #include <dirent.h>
- #include <ctype.h>
-
-+#define SUMMARY "pacemakerd - primary Pacemaker daemon that launches and monitors all subsidiary Pacemaker daemons"
-+
-+struct {
-+ gboolean features;
-+ gboolean foreground;
-+ gboolean shutdown;
-+ gboolean standby;
-+} options;
-+
-+static gboolean
-+pid_cb(const gchar *option_name, const gchar *optarg, gpointer data, GError **err) {
-+ return TRUE;
-+}
-+
-+static gboolean
-+standby_cb(const gchar *option_name, const gchar *optarg, gpointer data, GError **err) {
-+ options.standby = TRUE;
-+ pcmk__set_env_option("node_start_state", "standby");
-+ return TRUE;
-+}
-+
-+static GOptionEntry entries[] = {
-+ { "features", 'F', 0, G_OPTION_ARG_NONE, &options.features,
-+ "Display full version and list of features Pacemaker was built with",
-+ NULL },
-+ { "foreground", 'f', 0, G_OPTION_ARG_NONE, &options.foreground,
-+ "(Ignored) Pacemaker always runs in the foreground",
-+ NULL },
-+ { "pid-file", 'p', 0, G_OPTION_ARG_CALLBACK, pid_cb,
-+ "(Ignored) Daemon pid file location",
-+ "FILE" },
-+ { "shutdown", 'S', 0, G_OPTION_ARG_NONE, &options.shutdown,
-+ "Instruct Pacemaker to shutdown on this machine",
-+ NULL },
-+ { "standby", 's', G_OPTION_FLAG_NO_ARG, G_OPTION_ARG_CALLBACK, standby_cb,
-+ "Start node in standby state",
-+ NULL },
-+
-+ { NULL }
-+};
-+
- static gboolean fatal_error = FALSE;
- static GMainLoop *mainloop = NULL;
- static bool global_keep_tracking = false;
-@@ -642,49 +685,6 @@ pcmk_sigquit(int nsig)
- .connection_destroyed = pcmk_ipc_destroy
- };
-
--static pcmk__cli_option_t long_options[] = {
-- // long option, argument type, storage, short option, description, flags
-- {
-- "help", no_argument, NULL, '?',
-- "\tThis text", pcmk__option_default
-- },
-- {
-- "version", no_argument, NULL, '$',
-- "\tVersion information", pcmk__option_default
-- },
-- {
-- "verbose", no_argument, NULL, 'V',
-- "\tIncrease debug output", pcmk__option_default
-- },
-- {
-- "shutdown", no_argument, NULL, 'S',
-- "\tInstruct Pacemaker to shutdown on this machine", pcmk__option_default
-- },
-- {
-- "features", no_argument, NULL, 'F',
-- "\tDisplay full version and list of features Pacemaker was built with",
-- pcmk__option_default
-- },
-- {
-- "-spacer-", no_argument, NULL, '-',
-- "\nAdditional Options:", pcmk__option_default
-- },
-- {
-- "foreground", no_argument, NULL, 'f',
-- "\t(Ignored) Pacemaker always runs in the foreground",
-- pcmk__option_default
-- },
-- {
-- "pid-file", required_argument, NULL, 'p',
-- "\t(Ignored) Daemon pid file location", pcmk__option_default
-- },
-- {
-- "standby", no_argument, NULL, 's',
-- "\tStart node in standby state", pcmk__option_default
-- },
-- { 0, 0, 0, 0 }
--};
--
- static void
- mcp_chown(const char *path, uid_t uid, gid_t gid)
- {
-@@ -1168,83 +1211,66 @@ request_shutdown(crm_ipc_t *ipc)
- return status;
- }
-
-+static GOptionContext *
-+build_arg_context(pcmk__common_args_t *args) {
-+ GOptionContext *context = NULL;
-+
-+ context = pcmk__build_arg_context(args, NULL, NULL, NULL);
-+ pcmk__add_main_args(context, entries);
-+ return context;
-+}
-+
- int
- main(int argc, char **argv)
- {
-- int flag;
-- int argerr = 0;
-+ crm_exit_t exit_code = CRM_EX_OK;
-+
-+ GError *error = NULL;
-+
-+ pcmk__common_args_t *args = pcmk__new_common_args(SUMMARY);
-+ gchar **processed_args = pcmk__cmdline_preproc(argv, "p");
-+ GOptionContext *context = build_arg_context(args);
-
-- int option_index = 0;
- bool old_instance_connected = false;
-- gboolean shutdown = FALSE;
-
- crm_ipc_t *old_instance = NULL;
- qb_ipcs_service_t *ipcs = NULL;
-
- crm_log_preinit(NULL, argc, argv);
-- pcmk__set_cli_options(NULL, "[options]", long_options,
-- "primary Pacemaker daemon that launches and "
-- "monitors all subsidiary Pacemaker daemons");
- mainloop_add_signal(SIGHUP, pcmk_ignore);
- mainloop_add_signal(SIGQUIT, pcmk_sigquit);
-
-- while (1) {
-- flag = pcmk__next_cli_option(argc, argv, &option_index, NULL);
-- if (flag == -1)
-- break;
--
-- switch (flag) {
-- case 'V':
-- crm_bump_log_level(argc, argv);
-- break;
-- case 'f':
-- /* Legacy */
-- break;
-- case 'p':
-- break;
-- case 's':
-- pcmk__set_env_option("node_start_state", "standby");
-- break;
-- case '$':
-- case '?':
-- pcmk__cli_help(flag, CRM_EX_OK);
-- break;
-- case 'S':
-- shutdown = TRUE;
-- break;
-- case 'F':
-- printf("Pacemaker %s (Build: %s)\n Supporting v%s: %s\n", PACEMAKER_VERSION, BUILD_VERSION,
-- CRM_FEATURE_SET, CRM_FEATURES);
-- crm_exit(CRM_EX_OK);
-- default:
-- printf("Argument code 0%o (%c) is not (?yet?) supported\n", flag, flag);
-- ++argerr;
-- break;
-- }
-+ if (!g_option_context_parse_strv(context, &processed_args, &error)) {
-+ exit_code = CRM_EX_USAGE;
-+ goto done;
- }
-
-- if (optind < argc) {
-- printf("non-option ARGV-elements: ");
-- while (optind < argc)
-- printf("%s ", argv[optind++]);
-- printf("\n");
-- }
-- if (argerr) {
-- pcmk__cli_help('?', CRM_EX_USAGE);
-+ if (options.features) {
-+ printf("Pacemaker %s (Build: %s)\n Supporting v%s: %s\n", PACEMAKER_VERSION, BUILD_VERSION,
-+ CRM_FEATURE_SET, CRM_FEATURES);
-+ exit_code = CRM_EX_OK;
-+ goto done;
- }
-
-+ if (args->version) {
-+ g_strfreev(processed_args);
-+ pcmk__free_arg_context(context);
-+ /* FIXME: When pacemakerd is converted to use formatted output, this can go. */
-+ pcmk__cli_help('v', CRM_EX_USAGE);
-+ }
-
- setenv("LC_ALL", "C", 1);
-
- pcmk__set_env_option("mcp", "true");
-
-+ pcmk__cli_init_logging("pacemakerd", args->verbosity);
- crm_log_init(NULL, LOG_INFO, TRUE, FALSE, argc, argv, FALSE);
-
- crm_debug("Checking for existing Pacemaker instance");
- old_instance = crm_ipc_new(CRM_SYSTEM_MCP, 0);
- old_instance_connected = crm_ipc_connect(old_instance);
-
-- if (shutdown) {
-+ if (options.shutdown) {
- if (old_instance_connected) {
- crm_exit(request_shutdown(old_instance));
- } else {
-@@ -1253,22 +1279,25 @@ main(int argc, char **argv)
- "Pacemaker instance: %s", strerror(errno));
- crm_ipc_close(old_instance);
- crm_ipc_destroy(old_instance);
-- crm_exit(CRM_EX_DISCONNECT);
-+ exit_code = CRM_EX_DISCONNECT;
-+ goto done;
- }
-
- } else if (old_instance_connected) {
- crm_ipc_close(old_instance);
- crm_ipc_destroy(old_instance);
- crm_err("Aborting start-up because active Pacemaker instance found");
-- crm_exit(CRM_EX_FATAL);
-+ exit_code = CRM_EX_FATAL;
-+ goto done;
- }
-
- crm_ipc_close(old_instance);
- crm_ipc_destroy(old_instance);
-
- #ifdef SUPPORT_COROSYNC
- if (mcp_read_config() == FALSE) {
-- crm_exit(CRM_EX_UNAVAILABLE);
-+ exit_code = CRM_EX_UNAVAILABLE;
-+ goto done;
- }
- #endif
-
-@@ -1292,7 +1321,8 @@ main(int argc, char **argv)
- #ifdef SUPPORT_COROSYNC
- /* Allows us to block shutdown */
- if (!cluster_connect_cfg()) {
-- crm_exit(CRM_EX_PROTOCOL);
-+ exit_code = CRM_EX_PROTOCOL;
-+ goto done;
- }
- #endif
-
-@@ -1307,9 +1337,11 @@ main(int argc, char **argv)
- case pcmk_rc_ok:
- break;
- case pcmk_rc_ipc_unauthorized:
-- crm_exit(CRM_EX_CANTCREAT);
-+ exit_code = CRM_EX_CANTCREAT;
-+ goto done;
- default:
-- crm_exit(CRM_EX_FATAL);
-+ exit_code = CRM_EX_FATAL;
-+ goto done;
- };
-
- mainloop_add_signal(SIGTERM, pcmk_shutdown);
-@@ -1342,5 +1374,11 @@ main(int argc, char **argv)
- #ifdef SUPPORT_COROSYNC
- cluster_disconnect_cfg();
- #endif
-- crm_exit(CRM_EX_OK);
-+
-+done:
-+ g_strfreev(processed_args);
-+ pcmk__free_arg_context(context);
-+
-+ pcmk__output_and_clear_error(error, NULL);
-+ crm_exit(exit_code);
- }
---
-1.8.3.1
-
-
-From 8f7924fbb2a012bedcad59335b7bebc5020b26e3 Mon Sep 17 00:00:00 2001
-From: Chris Lumens <clumens(a)redhat.com>
-Date: Thu, 13 May 2021 13:27:13 -0400
-Subject: [PATCH 4/4] Low: pacemaker.service: Don't start pacemakerd with -f.
-
-This option is completely ignored by pacemakerd.
----
- daemons/pacemakerd/pacemaker.service.in | 2 +-
- doc/sphinx/Clusters_from_Scratch/verification.rst | 2 +-
- 2 files changed, 2 insertions(+), 2 deletions(-)
-
-diff --git a/daemons/pacemakerd/pacemaker.service.in b/daemons/pacemakerd/pacemaker.service.in
-index b128ddc..0363a22 100644
---- a/daemons/pacemakerd/pacemaker.service.in
-+++ b/daemons/pacemakerd/pacemaker.service.in
-@@ -44,7 +44,7 @@ EnvironmentFile=-@CONFIGDIR@/pacemaker
- EnvironmentFile=-@CONFIGDIR@/sbd
- SuccessExitStatus=100
-
--ExecStart=@sbindir@/pacemakerd -f
-+ExecStart=@sbindir@/pacemakerd
-
- # Systemd v227 and above can limit the number of processes spawned by a
- # service. That is a bad idea for an HA cluster resource manager, so disable it
-diff --git a/doc/sphinx/Clusters_from_Scratch/verification.rst b/doc/sphinx/Clusters_from_Scratch/verification.rst
-index 9d647f8..b7fa20e 100644
---- a/doc/sphinx/Clusters_from_Scratch/verification.rst
-+++ b/doc/sphinx/Clusters_from_Scratch/verification.rst
-@@ -103,7 +103,7 @@ the necessary processes are running:
- 2 ? S 0:00 [kthreadd]
- ...lots of processes...
- 17121 ? SLsl 0:01 /usr/sbin/corosync -f
-- 17133 ? Ss 0:00 /usr/sbin/pacemakerd -f
-+ 17133 ? Ss 0:00 /usr/sbin/pacemakerd
- 17134 ? Ss 0:00 \_ /usr/libexec/pacemaker/pacemaker-based
- 17135 ? Ss 0:00 \_ /usr/libexec/pacemaker/pacemaker-fenced
- 17136 ? Ss 0:00 \_ /usr/libexec/pacemaker/pacemaker-execd
---
-1.8.3.1
-
diff --git a/003-pacemakerd-output.patch b/003-pacemakerd-output.patch
deleted file mode 100644
index 167e22b..0000000
--- a/003-pacemakerd-output.patch
+++ /dev/null
@@ -1,343 +0,0 @@
-From 7c35387a9896cb968cf4087b5cbed94af44e1ea5 Mon Sep 17 00:00:00 2001
-From: Chris Lumens <clumens(a)redhat.com>
-Date: Fri, 14 May 2021 12:03:46 -0400
-Subject: [PATCH 1/5] Feature: daemons: Convert pacemakerd to formatted output.
-
-The main purpose of this is to finish getting pacemakerd moved off the
-existing command line handling code (pcmk__cli_help in particular) so
-that code can eventually be deprecated or removed. pacemakerd itself
-does fairly little printing.
----
- daemons/pacemakerd/pacemakerd.c | 58 ++++++++++++++++++++++++++++++-----------
- 1 file changed, 43 insertions(+), 15 deletions(-)
-
-diff --git a/daemons/pacemakerd/pacemakerd.c b/daemons/pacemakerd/pacemakerd.c
-index ce194bf..bd59729 100644
---- a/daemons/pacemakerd/pacemakerd.c
-+++ b/daemons/pacemakerd/pacemakerd.c
-@@ -25,6 +25,7 @@
- #include <crm/common/ipc_internal.h>
- #include <crm/common/mainloop.h>
- #include <crm/common/cmdline_internal.h>
-+#include <crm/common/output_internal.h>
- #include <crm/cluster/internal.h>
- #include <crm/cluster.h>
-
-@@ -37,6 +38,14 @@ struct {
- gboolean standby;
- } options;
-
-+static pcmk__output_t *out = NULL;
-+
-+static pcmk__supported_format_t formats[] = {
-+ PCMK__SUPPORTED_FORMAT_NONE,
-+ PCMK__SUPPORTED_FORMAT_TEXT,
-+ { NULL, NULL, NULL }
-+};
-+
- static gboolean
- pid_cb(const gchar *option_name, const gchar *optarg, gpointer data, GError **err) {
- return TRUE;
-@@ -1167,10 +1176,10 @@ pacemakerd_event_cb(pcmk_ipc_api_t *pacemakerd_api,
- }
-
- static GOptionContext *
--build_arg_context(pcmk__common_args_t *args) {
-+build_arg_context(pcmk__common_args_t *args, GOptionGroup **group) {
- GOptionContext *context = NULL;
-
-- context = pcmk__build_arg_context(args, NULL, NULL, NULL);
-+ context = pcmk__build_arg_context(args, "text", group, NULL);
- pcmk__add_main_args(context, entries);
- return context;
- }
-@@ -1182,9 +1191,11 @@ main(int argc, char **argv)
-
- GError *error = NULL;
-
-+ int rc = pcmk_rc_ok;
-+ GOptionGroup *output_group = NULL;
- pcmk__common_args_t *args = pcmk__new_common_args(SUMMARY);
- gchar **processed_args = pcmk__cmdline_preproc(argv, "p");
-- GOptionContext *context = build_arg_context(args);
-+ GOptionContext *context = build_arg_context(args, &output_group);
-
- bool old_instance_connected = false;
-
-@@ -1195,23 +1205,30 @@ main(int argc, char **argv)
- mainloop_add_signal(SIGHUP, pcmk_ignore);
- mainloop_add_signal(SIGQUIT, pcmk_sigquit);
-
-+ pcmk__register_formats(output_group, formats);
- if (!g_option_context_parse_strv(context, &processed_args, &error)) {
- exit_code = CRM_EX_USAGE;
- goto done;
- }
-
-+ rc = pcmk__output_new(&out, args->output_ty, args->output_dest, argv);
-+ if (rc != pcmk_rc_ok) {
-+ exit_code = CRM_EX_ERROR;
-+ g_set_error(&error, PCMK__EXITC_ERROR, exit_code, "Error creating output format %s: %s",
-+ args->output_ty, pcmk_rc_str(rc));
-+ goto done;
-+ }
-+
- if (options.features) {
-- printf("Pacemaker %s (Build: %s)\n Supporting v%s: %s\n", PACEMAKER_VERSION, BUILD_VERSION,
-- CRM_FEATURE_SET, CRM_FEATURES);
-+ out->info(out, "Pacemaker %s (Build: %s)\n Supporting v%s: %s", PACEMAKER_VERSION,
-+ BUILD_VERSION, CRM_FEATURE_SET, CRM_FEATURES);
- exit_code = CRM_EX_OK;
- goto done;
- }
-
- if (args->version) {
-- g_strfreev(processed_args);
-- pcmk__free_arg_context(context);
-- /* FIXME: When pacemakerd is converted to use formatted output, this can go. */
-- pcmk__cli_help('v', CRM_EX_USAGE);
-+ out->version(out, false);
-+ goto done;
- }
-
- setenv("LC_ALL", "C", 1);
-@@ -1248,6 +1265,13 @@ main(int argc, char **argv)
- crm_ipc_close(old_instance);
- crm_ipc_destroy(old_instance);
-
-+ /* Don't allow any accidental output after this point. */
-+ if (out != NULL) {
-+ out->finish(out, exit_code, true, NULL);
-+ pcmk__output_free(out);
-+ out = NULL;
-+ }
-+
- #ifdef SUPPORT_COROSYNC
- if (mcp_read_config() == FALSE) {
- exit_code = CRM_EX_UNAVAILABLE;
-@@ -1333,6 +1357,11 @@ done:
- g_strfreev(processed_args);
- pcmk__free_arg_context(context);
-
-- pcmk__output_and_clear_error(error, NULL);
-+ pcmk__output_and_clear_error(error, out);
-+
-+ if (out != NULL) {
-+ out->finish(out, exit_code, true, NULL);
-+ pcmk__output_free(out);
-+ }
- crm_exit(exit_code);
- }
---
-1.8.3.1
-
-
-From 35e6da64381fcb092d81ce16835cc28670b077cb Mon Sep 17 00:00:00 2001
-From: Chris Lumens <clumens(a)redhat.com>
-Date: Mon, 17 May 2021 10:04:04 -0400
-Subject: [PATCH 2/5] Features: daemons: Output the pacemakerd feature list in
- XML.
-
----
- daemons/pacemakerd/pacemakerd.c | 45 ++++++++++++++++++++++++++++++++++++++---
- 1 file changed, 42 insertions(+), 3 deletions(-)
-
-diff --git a/daemons/pacemakerd/pacemakerd.c b/daemons/pacemakerd/pacemakerd.c
-index bd59729..93cf743 100644
---- a/daemons/pacemakerd/pacemakerd.c
-+++ b/daemons/pacemakerd/pacemakerd.c
-@@ -43,6 +43,42 @@ static pcmk__output_t *out = NULL;
- static pcmk__supported_format_t formats[] = {
- PCMK__SUPPORTED_FORMAT_NONE,
- PCMK__SUPPORTED_FORMAT_TEXT,
-+ PCMK__SUPPORTED_FORMAT_XML,
-+ { NULL, NULL, NULL }
-+};
-+
-+static int
-+pacemakerd_features(pcmk__output_t *out, va_list args) {
-+ out->info(out, "Pacemaker %s (Build: %s)\n Supporting v%s: %s", PACEMAKER_VERSION,
-+ BUILD_VERSION, CRM_FEATURE_SET, CRM_FEATURES);
-+ return pcmk_rc_ok;
-+}
-+
-+static int
-+pacemakerd_features_xml(pcmk__output_t *out, va_list args) {
-+ gchar **feature_list = g_strsplit(CRM_FEATURES, " ", 0);
-+
-+ pcmk__output_xml_create_parent(out, "pacemakerd",
-+ "version", PACEMAKER_VERSION,
-+ "build", BUILD_VERSION,
-+ "feature_set", CRM_FEATURE_SET,
-+ NULL);
-+ out->begin_list(out, NULL, NULL, "features");
-+
-+ for (char **s = feature_list; *s != NULL; s++) {
-+ pcmk__output_create_xml_text_node(out, "feature", *s);
-+ }
-+
-+ out->end_list(out);
-+
-+ g_strfreev(feature_list);
-+ return pcmk_rc_ok;
-+}
-+
-+static pcmk__message_entry_t fmt_functions[] = {
-+ { "features", "default", pacemakerd_features },
-+ { "features", "xml", pacemakerd_features_xml },
-+
- { NULL, NULL, NULL }
- };
-
-@@ -200,7 +236,7 @@ static GOptionContext *
- build_arg_context(pcmk__common_args_t *args, GOptionGroup **group) {
- GOptionContext *context = NULL;
-
-- context = pcmk__build_arg_context(args, "text", group, NULL);
-+ context = pcmk__build_arg_context(args, "text (default), xml", group, NULL);
- pcmk__add_main_args(context, entries);
- return context;
- }
-@@ -241,9 +277,12 @@ main(int argc, char **argv)
- goto done;
- }
-
-+ pcmk__force_args(context, &error, "%s --xml-simple-list", g_get_prgname());
-+
-+ pcmk__register_messages(out, fmt_functions);
-+
- if (options.features) {
-- out->info(out, "Pacemaker %s (Build: %s)\n Supporting v%s: %s", PACEMAKER_VERSION,
-- BUILD_VERSION, CRM_FEATURE_SET, CRM_FEATURES);
-+ out->message(out, "features");
- exit_code = CRM_EX_OK;
- goto done;
- }
---
-1.8.3.1
-
-
-From 5b7f5eb35b025b59805cf3c7c3dcb6a3cf4b71b3 Mon Sep 17 00:00:00 2001
-From: Chris Lumens <clumens(a)redhat.com>
-Date: Mon, 17 May 2021 11:09:53 -0400
-Subject: [PATCH 3/5] Low: daemons: Conditionally enable logging in pacemakerd.
-
-If we're doing an interactive command-line call, use
-pcmk__cli_init_logging. At the moment, all command line calls except
-for --shutdown do their work before logging would even come up, so we
-really only need to do this for --shutdown.
-
-If we're doing a daemon call, use crm_log_init.
----
- daemons/pacemakerd/pacemakerd.c | 7 +++++--
- 1 file changed, 5 insertions(+), 2 deletions(-)
-
-diff --git a/daemons/pacemakerd/pacemakerd.c b/daemons/pacemakerd/pacemakerd.c
-index 93cf743..c20bde7 100644
---- a/daemons/pacemakerd/pacemakerd.c
-+++ b/daemons/pacemakerd/pacemakerd.c
-@@ -296,8 +296,11 @@ main(int argc, char **argv)
-
- pcmk__set_env_option("mcp", "true");
-
-- pcmk__cli_init_logging("pacemakerd", args->verbosity);
-- crm_log_init(NULL, LOG_INFO, TRUE, FALSE, argc, argv, FALSE);
-+ if (options.shutdown) {
-+ pcmk__cli_init_logging("pacemakerd", args->verbosity);
-+ } else {
-+ crm_log_init(NULL, LOG_INFO, TRUE, FALSE, argc, argv, FALSE);
-+ }
-
- crm_debug("Checking for existing Pacemaker instance");
- old_instance = crm_ipc_new(CRM_SYSTEM_MCP, 0);
---
-1.8.3.1
-
-
-From 2393362bb7489e86d937ed46a1c5cfb93d9bf3ab Mon Sep 17 00:00:00 2001
-From: Chris Lumens <clumens(a)redhat.com>
-Date: Mon, 17 May 2021 11:58:06 -0400
-Subject: [PATCH 4/5] Fix: include: Bump CRM_FEATURE_SET for new pacemakerd
- args.
-
----
- include/crm/crm.h | 2 +-
- 1 file changed, 1 insertion(+), 1 deletion(-)
-
-diff --git a/include/crm/crm.h b/include/crm/crm.h
-index fdfc825..92a98fa 100644
---- a/include/crm/crm.h
-+++ b/include/crm/crm.h
-@@ -66,7 +66,7 @@ extern "C" {
- * >=3.0.13: Fail counts include operation name and interval
- * >=3.2.0: DC supports PCMK_LRM_OP_INVALID and PCMK_LRM_OP_NOT_CONNECTED
- */
--# define CRM_FEATURE_SET "3.10.0"
-+# define CRM_FEATURE_SET "3.10.1"
-
- /* Pacemaker's CPG protocols use fixed-width binary fields for the sender and
- * recipient of a CPG message. This imposes an arbitrary limit on cluster node
---
-1.8.3.1
-
-
-From 3ad8edbd91631b87ef5f53fa2d68f0c8bbb9ee2b Mon Sep 17 00:00:00 2001
-From: Chris Lumens <clumens(a)redhat.com>
-Date: Mon, 17 May 2021 11:57:09 -0400
-Subject: [PATCH 5/5] Feature: xml: Add schema for pacemakerd.
-
----
- xml/Makefile.am | 1 +
- xml/api/pacemakerd-2.10.rng | 28 ++++++++++++++++++++++++++++
- 2 files changed, 29 insertions(+)
- create mode 100644 xml/api/pacemakerd-2.10.rng
-
-diff --git a/xml/Makefile.am b/xml/Makefile.am
-index 12a51c5..b9448d4 100644
---- a/xml/Makefile.am
-+++ b/xml/Makefile.am
-@@ -56,6 +56,7 @@ API_request_base = command-output \
- crm_simulate \
- crmadmin \
- digests \
-+ pacemakerd \
- stonith_admin \
- version
-
-diff --git a/xml/api/pacemakerd-2.10.rng b/xml/api/pacemakerd-2.10.rng
-new file mode 100644
-index 0000000..41a11e7
---- /dev/null
-+++ b/xml/api/pacemakerd-2.10.rng
-@@ -0,0 +1,28 @@
-+<?xml version="1.0" encoding="UTF-8"?>
-+<grammar xmlns="http://relaxng.org/ns/structure/1.0"
-+ datatypeLibrary="http://www.w3.org/2001/XMLSchema-datatypes">
-+
-+ <start>
-+ <ref name="element-pacemakerd"/>
-+ </start>
-+
-+ <define name="element-pacemakerd">
-+ <element name="pacemakerd">
-+ <attribute name="version"> <text /> </attribute>
-+ <attribute name="build"> <text /> </attribute>
-+ <attribute name="feature_set"> <text /> </attribute>
-+
-+ <optional>
-+ <ref name="feature-list" />
-+ </optional>
-+ </element>
-+ </define>
-+
-+ <define name="feature-list">
-+ <element name="features">
-+ <oneOrMore>
-+ <element name="feature"> <text/> </element>
-+ </oneOrMore>
-+ </element>
-+ </define>
-+</grammar>
---
-1.8.3.1
-
diff --git a/004-check-level.patch b/004-check-level.patch
deleted file mode 100644
index f2abb5f..0000000
--- a/004-check-level.patch
+++ /dev/null
@@ -1,199 +0,0 @@
-From 3905e7eac11298fc20efd567a773666f948edf61 Mon Sep 17 00:00:00 2001
-From: Chris Lumens <clumens(a)redhat.com>
-Date: Mon, 3 May 2021 11:19:04 -0400
-Subject: [PATCH 1/2] Feature: tools: Add OCF_CHECK_LEVEL to crm_resource
- environment.
-
-If --validate= or --force-check= are given with a level, pass that along
-as OCF_CHECK_LEVEL. This argument is optional, and if no value is given
-then the environment variable will not be set and whatever's the default
-on the resource agent will be used.
-
-See: rhbz#1955792.
----
- tools/crm_resource.c | 29 +++++++++++++++++++++--------
- tools/crm_resource.h | 4 ++--
- tools/crm_resource_runtime.c | 13 ++++++++++---
- 3 files changed, 33 insertions(+), 13 deletions(-)
-
-diff --git a/tools/crm_resource.c b/tools/crm_resource.c
-index 45db2b2..6ca96f8 100644
---- a/tools/crm_resource.c
-+++ b/tools/crm_resource.c
-@@ -100,6 +100,7 @@ struct {
- int timeout_ms; // Parsed from --timeout value
- char *agent_spec; // Standard and/or provider and/or agent
- gchar *xml_file; // Value of (deprecated) --xml-file
-+ int check_level; // Optional value of --validate or --force-check
-
- // Resource configuration specified via command-line arguments
- gboolean cmdline_config; // Resource configuration was via arguments
-@@ -113,6 +114,7 @@ struct {
- GHashTable *override_params; // Resource parameter values that override config
- } options = {
- .attr_set_type = XML_TAG_ATTR_SETS,
-+ .check_level = -1,
- .cib_options = cib_sync_call,
- .require_cib = TRUE,
- .require_dataset = TRUE,
-@@ -402,14 +404,15 @@ static GOptionEntry query_entries[] = {
- };
-
- static GOptionEntry command_entries[] = {
-- { "validate", 0, G_OPTION_FLAG_NO_ARG, G_OPTION_ARG_CALLBACK,
-+ { "validate", 0, G_OPTION_FLAG_OPTIONAL_ARG, G_OPTION_ARG_CALLBACK,
- validate_or_force_cb,
- "Validate resource configuration by calling agent's validate-all\n"
- INDENT "action. The configuration may be specified either by giving an\n"
- INDENT "existing resource name with -r, or by specifying --class,\n"
- INDENT "--agent, and --provider arguments, along with any number of\n"
-- INDENT "--option arguments.",
-- NULL },
-+ INDENT "--option arguments. An optional LEVEL argument can be given\n"
-+ INDENT "to control the level of checking performed.",
-+ "LEVEL" },
- { "cleanup", 'C', G_OPTION_FLAG_NO_ARG, G_OPTION_ARG_CALLBACK, cleanup_refresh_cb,
- "If resource has any past failures, clear its history and fail\n"
- INDENT "count. Optionally filtered by --resource, --node, --operation\n"
-@@ -546,11 +549,12 @@ static GOptionEntry advanced_entries[] = {
- INDENT "the cluster believes the resource is a clone instance already\n"
- INDENT "running on the local node.",
- NULL },
-- { "force-check", 0, G_OPTION_FLAG_NO_ARG, G_OPTION_ARG_CALLBACK,
-+ { "force-check", 0, G_OPTION_FLAG_OPTIONAL_ARG, G_OPTION_ARG_CALLBACK,
- validate_or_force_cb,
- "(Advanced) Bypass the cluster and check the state of a resource on\n"
-- INDENT "the local node",
-- NULL },
-+ INDENT "the local node. An optional LEVEL argument can be given\n"
-+ INDENT "to control the level of checking performed.",
-+ "LEVEL" },
-
- { NULL }
- };
-@@ -910,6 +914,15 @@ validate_or_force_cb(const gchar *option_name, const gchar *optarg,
- if (options.override_params == NULL) {
- options.override_params = pcmk__strkey_table(free, free);
- }
-+
-+ if (optarg != NULL) {
-+ if (pcmk__scan_min_int(optarg, &options.check_level, 0) != pcmk_rc_ok) {
-+ g_set_error(error, G_OPTION_ERROR, CRM_EX_INVALID_PARAM,
-+ "Invalid check level setting: %s", optarg);
-+ return FALSE;
-+ }
-+ }
-+
- return TRUE;
- }
-
-@@ -1826,12 +1839,12 @@ main(int argc, char **argv)
- options.v_class, options.v_provider, options.v_agent,
- "validate-all", options.cmdline_params,
- options.override_params, options.timeout_ms,
-- args->verbosity, options.force);
-+ args->verbosity, options.force, options.check_level);
- } else {
- exit_code = cli_resource_execute(rsc, options.rsc_id,
- options.operation, options.override_params,
- options.timeout_ms, cib_conn, data_set,
-- args->verbosity, options.force);
-+ args->verbosity, options.force, options.check_level);
- }
- goto done;
-
-diff --git a/tools/crm_resource.h b/tools/crm_resource.h
-index 3560377..5ab10d6 100644
---- a/tools/crm_resource.h
-+++ b/tools/crm_resource.h
-@@ -88,11 +88,11 @@ crm_exit_t cli_resource_execute_from_params(pcmk__output_t *out, const char *rsc
- const char *rsc_type, const char *rsc_action,
- GHashTable *params, GHashTable *override_hash,
- int timeout_ms, int resource_verbose,
-- gboolean force);
-+ gboolean force, int check_level);
- crm_exit_t cli_resource_execute(pe_resource_t *rsc, const char *requested_name,
- const char *rsc_action, GHashTable *override_hash,
- int timeout_ms, cib_t *cib, pe_working_set_t *data_set,
-- int resource_verbose, gboolean force);
-+ int resource_verbose, gboolean force, int check_level);
-
- int cli_resource_update_attribute(pe_resource_t *rsc, const char *requested_name,
- const char *attr_set, const char *attr_set_type,
-diff --git a/tools/crm_resource_runtime.c b/tools/crm_resource_runtime.c
-index fe0ec98..bde83b6 100644
---- a/tools/crm_resource_runtime.c
-+++ b/tools/crm_resource_runtime.c
-@@ -1679,7 +1679,8 @@ cli_resource_execute_from_params(pcmk__output_t *out, const char *rsc_name,
- const char *rsc_class, const char *rsc_prov,
- const char *rsc_type, const char *action,
- GHashTable *params, GHashTable *override_hash,
-- int timeout_ms, int resource_verbose, gboolean force)
-+ int timeout_ms, int resource_verbose, gboolean force,
-+ int check_level)
- {
- GHashTable *params_copy = NULL;
- crm_exit_t exit_code = CRM_EX_OK;
-@@ -1703,6 +1704,12 @@ cli_resource_execute_from_params(pcmk__output_t *out, const char *rsc_name,
- /* add crm_feature_set env needed by some resource agents */
- g_hash_table_insert(params, strdup(XML_ATTR_CRM_VERSION), strdup(CRM_FEATURE_SET));
-
-+ if (check_level >= 0) {
-+ char *level = crm_strdup_printf("%d", check_level);
-+ setenv("OCF_CHECK_LEVEL", level, 1);
-+ free(level);
-+ }
-+
- /* resources_action_create frees the params hash table it's passed, but we
- * may need to reuse it in a second call to resources_action_create. Thus
- * we'll make a copy here so that gets freed and the original remains for
-@@ -1790,7 +1797,7 @@ crm_exit_t
- cli_resource_execute(pe_resource_t *rsc, const char *requested_name,
- const char *rsc_action, GHashTable *override_hash,
- int timeout_ms, cib_t * cib, pe_working_set_t *data_set,
-- int resource_verbose, gboolean force)
-+ int resource_verbose, gboolean force, int check_level)
- {
- pcmk__output_t *out = data_set->priv;
- crm_exit_t exit_code = CRM_EX_OK;
-@@ -1856,7 +1863,7 @@ cli_resource_execute(pe_resource_t *rsc, const char *requested_name,
-
- exit_code = cli_resource_execute_from_params(out, rid, rclass, rprov, rtype, action,
- params, override_hash, timeout_ms,
-- resource_verbose, force);
-+ resource_verbose, force, check_level);
- return exit_code;
- }
-
---
-1.8.3.1
-
-
-From d13ba4bd6defe0dd81fdf8ab39ae5b889513c0c0 Mon Sep 17 00:00:00 2001
-From: Chris Lumens <clumens(a)redhat.com>
-Date: Thu, 20 May 2021 10:59:23 -0400
-Subject: [PATCH 2/2] Fix: include: Bump feature set to 3.10.2.
-
-This is for the OCF_CHECK_LEVEL environment variable.
-
-See: rhbz#1955792.
----
- include/crm/crm.h | 2 +-
- 1 file changed, 1 insertion(+), 1 deletion(-)
-
-diff --git a/include/crm/crm.h b/include/crm/crm.h
-index 92a98fa..ee52c36 100644
---- a/include/crm/crm.h
-+++ b/include/crm/crm.h
-@@ -66,7 +66,7 @@ extern "C" {
- * >=3.0.13: Fail counts include operation name and interval
- * >=3.2.0: DC supports PCMK_LRM_OP_INVALID and PCMK_LRM_OP_NOT_CONNECTED
- */
--# define CRM_FEATURE_SET "3.10.1"
-+# define CRM_FEATURE_SET "3.10.2"
-
- /* Pacemaker's CPG protocols use fixed-width binary fields for the sender and
- * recipient of a CPG message. This imposes an arbitrary limit on cluster node
---
-1.8.3.1
-
diff --git a/005-crm_resource.patch b/005-crm_resource.patch
deleted file mode 100644
index 1683026..0000000
--- a/005-crm_resource.patch
+++ /dev/null
@@ -1,866 +0,0 @@
-From a5a507d4e1abf242903472719a19977811e6f164 Mon Sep 17 00:00:00 2001
-From: Chris Lumens <clumens(a)redhat.com>
-Date: Thu, 20 May 2021 11:59:36 -0400
-Subject: [PATCH 01/10] Feature: libcrmcommon: Add OCF_OUTPUT_FORMAT to
- crm_resource environment.
-
-See: rhbz#1644628
----
- lib/common/output.c | 2 ++
- 1 file changed, 2 insertions(+)
-
-diff --git a/lib/common/output.c b/lib/common/output.c
-index 6cb49b5..58872e0 100644
---- a/lib/common/output.c
-+++ b/lib/common/output.c
-@@ -71,6 +71,8 @@ pcmk__output_new(pcmk__output_t **out, const char *fmt_name, const char *filenam
- return ENOMEM;
- }
-
-+ setenv("OCF_OUTPUT_FORMAT", (*out)->fmt_name, 1);
-+
- return pcmk_rc_ok;
- }
-
---
-1.8.3.1
-
-
-From acc6ecdbfb797d69794e68f75a734d6252434e01 Mon Sep 17 00:00:00 2001
-From: Chris Lumens <clumens(a)redhat.com>
-Date: Fri, 21 May 2021 14:20:30 -0400
-Subject: [PATCH 02/10] Feature: schemas: Copy crm_resource schema in
- preparation for changes.
-
-See: rhbz#1644628
----
- xml/api/crm_resource-2.11.rng | 238 ++++++++++++++++++++++++++++++++++++++++++
- 1 file changed, 238 insertions(+)
- create mode 100644 xml/api/crm_resource-2.11.rng
-
-diff --git a/xml/api/crm_resource-2.11.rng b/xml/api/crm_resource-2.11.rng
-new file mode 100644
-index 0000000..8e386db
---- /dev/null
-+++ b/xml/api/crm_resource-2.11.rng
-@@ -0,0 +1,238 @@
-+<?xml version="1.0" encoding="UTF-8"?>
-+<grammar xmlns="http://relaxng.org/ns/structure/1.0"
-+ datatypeLibrary="http://www.w3.org/2001/XMLSchema-datatypes">
-+
-+ <start>
-+ <ref name="element-crm-resource"/>
-+ </start>
-+
-+ <define name="element-crm-resource">
-+ <choice>
-+ <ref name="agents-list" />
-+ <ref name="alternatives-list" />
-+ <ref name="constraints-list" />
-+ <externalRef href="generic-list-2.4.rng"/>
-+ <element name="metadata"> <text/> </element>
-+ <ref name="locate-list" />
-+ <ref name="operations-list" />
-+ <ref name="providers-list" />
-+ <ref name="reasons-list" />
-+ <ref name="resource-check" />
-+ <ref name="resource-config" />
-+ <ref name="resources-list" />
-+ </choice>
-+ </define>
-+
-+ <define name="agents-list">
-+ <element name="agents">
-+ <attribute name="standard"> <text/> </attribute>
-+ <optional>
-+ <attribute name="provider"> <text/> </attribute>
-+ </optional>
-+ <zeroOrMore>
-+ <element name="agent"> <text/> </element>
-+ </zeroOrMore>
-+ </element>
-+ </define>
-+
-+ <define name="alternatives-list">
-+ <element name="providers">
-+ <attribute name="for"> <text/> </attribute>
-+ <zeroOrMore>
-+ <element name="provider"> <text/> </element>
-+ </zeroOrMore>
-+ </element>
-+ </define>
-+
-+ <define name="constraints-list">
-+ <element name="constraints">
-+ <interleave>
-+ <zeroOrMore>
-+ <ref name="rsc-location" />
-+ </zeroOrMore>
-+ <zeroOrMore>
-+ <ref name="rsc-colocation" />
-+ </zeroOrMore>
-+ </interleave>
-+ </element>
-+ </define>
-+
-+ <define name="locate-list">
-+ <element name="nodes">
-+ <attribute name="resource"> <text/> </attribute>
-+ <zeroOrMore>
-+ <element name="node">
-+ <optional>
-+ <attribute name="state"><value>promoted</value></attribute>
-+ </optional>
-+ <text/>
-+ </element>
-+ </zeroOrMore>
-+ </element>
-+ </define>
-+
-+ <define name="rsc-location">
-+ <element name="rsc_location">
-+ <attribute name="node"> <text/> </attribute>
-+ <attribute name="rsc"> <text/> </attribute>
-+ <attribute name="id"> <text/> </attribute>
-+ <externalRef href="../score.rng"/>
-+ </element>
-+ </define>
-+
-+ <define name="operations-list">
-+ <element name="operations">
-+ <oneOrMore>
-+ <ref name="element-operation-list" />
-+ </oneOrMore>
-+ </element>
-+ </define>
-+
-+ <define name="providers-list">
-+ <element name="providers">
-+ <attribute name="standard"> <value>ocf</value> </attribute>
-+ <optional>
-+ <attribute name="agent"> <text/> </attribute>
-+ </optional>
-+ <zeroOrMore>
-+ <element name="provider"> <text/> </element>
-+ </zeroOrMore>
-+ </element>
-+ </define>
-+
-+ <define name="reasons-list">
-+ <choice>
-+ <ref name="no-resource-or-uname"/>
-+ <ref name="resource-and-uname"/>
-+ <ref name="no-resource-but-uname"/>
-+ <ref name="resource-but-no-uname"/>
-+ </choice>
-+ </define>
-+
-+ <define name="no-resource-or-uname">
-+ <element name="reason">
-+ <element name="resources">
-+ <zeroOrMore>
-+ <element name="resource">
-+ <attribute name="id"> <text/> </attribute>
-+ <attribute name="running"> <data type="boolean"/> </attribute>
-+ <ref name="resource-check"/>
-+ </element>
-+ </zeroOrMore>
-+ </element>
-+ </element>
-+ </define>
-+
-+ <define name="resource-and-uname">
-+ <element name="reason">
-+ <attribute name="running_on"> <text/> </attribute>
-+ <ref name="resource-check"/>
-+ </element>
-+ </define>
-+
-+ <define name="no-resource-but-uname">
-+ <element name="reason">
-+ <element name="resources">
-+ <zeroOrMore>
-+ <element name="resource">
-+ <attribute name="id"> <text/> </attribute>
-+ <attribute name="running"> <data type="boolean"/> </attribute>
-+ <attribute name="host"> <text/> </attribute>
-+ <ref name="resource-check"/>
-+ </element>
-+ </zeroOrMore>
-+ </element>
-+ </element>
-+ </define>
-+
-+ <define name="resource-but-no-uname">
-+ <element name="reason">
-+ <attribute name="running"> <data type="boolean"/> </attribute>
-+ <ref name="resource-check"/>
-+ </element>
-+ </define>
-+
-+ <define name="resource-config">
-+ <element name="resource_config">
-+ <externalRef href="resources-2.4.rng" />
-+ <element name="xml"> <text/> </element>
-+ </element>
-+ </define>
-+
-+ <define name="resource-check">
-+ <element name="check">
-+ <attribute name="id"> <text/> </attribute>
-+ <optional>
-+ <choice>
-+ <attribute name="remain_stopped"><value>true</value></attribute>
-+ <attribute name="promotable"><value>false</value></attribute>
-+ </choice>
-+ </optional>
-+ <optional>
-+ <attribute name="unmanaged"><value>true</value></attribute>
-+ </optional>
-+ <optional>
-+ <attribute name="locked-to"> <text/> </attribute>
-+ </optional>
-+ </element>
-+ </define>
-+
-+ <define name="resources-list">
-+ <element name="resources">
-+ <zeroOrMore>
-+ <externalRef href="resources-2.4.rng" />
-+ </zeroOrMore>
-+ </element>
-+ </define>
-+
-+ <define name="rsc-colocation">
-+ <element name="rsc_colocation">
-+ <attribute name="id"> <text/> </attribute>
-+ <attribute name="rsc"> <text/> </attribute>
-+ <attribute name="with-rsc"> <text/> </attribute>
-+ <externalRef href="../score.rng"/>
-+ <optional>
-+ <attribute name="node-attribute"> <text/> </attribute>
-+ </optional>
-+ <optional>
-+ <attribute name="rsc-role">
-+ <ref name="attribute-roles"/>
-+ </attribute>
-+ </optional>
-+ <optional>
-+ <attribute name="with-rsc-role">
-+ <ref name="attribute-roles"/>
-+ </attribute>
-+ </optional>
-+ </element>
-+ </define>
-+
-+ <define name="element-operation-list">
-+ <element name="operation">
-+ <optional>
-+ <group>
-+ <attribute name="rsc"> <text/> </attribute>
-+ <attribute name="agent"> <text/> </attribute>
-+ </group>
-+ </optional>
-+ <attribute name="op"> <text/> </attribute>
-+ <attribute name="node"> <text/> </attribute>
-+ <attribute name="call"> <data type="integer" /> </attribute>
-+ <attribute name="rc"> <data type="nonNegativeInteger" /> </attribute>
-+ <optional>
-+ <attribute name="last-rc-change"> <text/> </attribute>
-+ <attribute name="exec-time"> <data type="nonNegativeInteger" /> </attribute>
-+ </optional>
-+ <attribute name="status"> <text/> </attribute>
-+ </element>
-+ </define>
-+
-+ <define name="attribute-roles">
-+ <choice>
-+ <value>Stopped</value>
-+ <value>Started</value>
-+ <value>Master</value>
-+ <value>Slave</value>
-+ </choice>
-+ </define>
-+</grammar>
---
-1.8.3.1
-
-
-From 1bbdf2149a111e9e19c388834f82001e0d31c427 Mon Sep 17 00:00:00 2001
-From: Chris Lumens <clumens(a)redhat.com>
-Date: Mon, 24 May 2021 12:23:55 -0400
-Subject: [PATCH 03/10] Feature: xml: Update the crm_resource schema for XML
- output.
-
-See: rhbz#1644628
----
- xml/api/crm_resource-2.11.rng | 50 +++++++++++++++++++++++++++++++++++++++++++
- 1 file changed, 50 insertions(+)
-
-diff --git a/xml/api/crm_resource-2.11.rng b/xml/api/crm_resource-2.11.rng
-index 8e386db..aaa54d6 100644
---- a/xml/api/crm_resource-2.11.rng
-+++ b/xml/api/crm_resource-2.11.rng
-@@ -20,6 +20,7 @@
- <ref name="resource-check" />
- <ref name="resource-config" />
- <ref name="resources-list" />
-+ <ref name="resource-agent-action" />
- </choice>
- </define>
-
-@@ -227,6 +228,55 @@
- </element>
- </define>
-
-+ <define name="resource-agent-action">
-+ <element name="resource-agent-action">
-+ <attribute name="action"> <text/> </attribute>
-+ <optional>
-+ <attribute name="rsc"> <text/> </attribute>
-+ </optional>
-+ <attribute name="class"> <text/> </attribute>
-+ <attribute name="type"> <text/> </attribute>
-+ <optional>
-+ <attribute name="provider"> <text/> </attribute>
-+ </optional>
-+ <optional>
-+ <ref name="overrides-list"/>
-+ </optional>
-+ <ref name="agent-status"/>
-+ <optional>
-+ <choice>
-+ <element name="command">
-+ <text />
-+ </element>
-+ <externalRef href="command-output-1.0.rng"/>
-+ </choice>
-+ </optional>
-+ </element>
-+ </define>
-+
-+ <define name="overrides-list">
-+ <element name="overrides">
-+ <zeroOrMore>
-+ <element name="override">
-+ <optional>
-+ <attribute name="rsc"> <text/> </attribute>
-+ </optional>
-+ <attribute name="name"> <text/> </attribute>
-+ <attribute name="value"> <text/> </attribute>
-+ </element>
-+ </zeroOrMore>
-+ </element>
-+ </define>
-+
-+ <define name="agent-status">
-+ <element name="agent-status">
-+ <attribute name="code"> <data type="integer" /> </attribute>
-+ <optional>
-+ <attribute name="message"> <text/> </attribute>
-+ </optional>
-+ </element>
-+ </define>
-+
- <define name="attribute-roles">
- <choice>
- <value>Stopped</value>
---
-1.8.3.1
-
-
-From d89f5bc7fec856fdcd32fa14edbd0019507d5d15 Mon Sep 17 00:00:00 2001
-From: Chris Lumens <clumens(a)redhat.com>
-Date: Tue, 1 Jun 2021 15:26:58 -0400
-Subject: [PATCH 04/10] Low: libcrmcommon: Increase PCMK__API_VERSION for new
- crm_resource output.
-
-See: rhbz#1644628
----
- include/crm/common/output_internal.h | 2 +-
- 1 file changed, 1 insertion(+), 1 deletion(-)
-
-diff --git a/include/crm/common/output_internal.h b/include/crm/common/output_internal.h
-index 10b315b..0436cde 100644
---- a/include/crm/common/output_internal.h
-+++ b/include/crm/common/output_internal.h
-@@ -27,7 +27,7 @@ extern "C" {
- # include <glib.h>
- # include <crm/common/results.h>
-
--# define PCMK__API_VERSION "2.9"
-+# define PCMK__API_VERSION "2.11"
-
- #if defined(PCMK__WITH_ATTRIBUTE_OUTPUT_ARGS)
- # define PCMK__OUTPUT_ARGS(ARGS...) __attribute__((output_args(ARGS)))
---
-1.8.3.1
-
-
-From 30bd2ddf43ee2a911681e51f40ed9ba20ec250b0 Mon Sep 17 00:00:00 2001
-From: Chris Lumens <clumens(a)redhat.com>
-Date: Thu, 27 May 2021 13:57:12 -0400
-Subject: [PATCH 05/10] Low: tools: Pass NULL to
- cli_resource_execute_from_params...
-
-if no resource name is given. This happens if we are validating based
-on the --class/--agent/--provider command line options instead.
----
- tools/crm_resource.c | 2 +-
- tools/crm_resource_runtime.c | 8 ++++----
- 2 files changed, 5 insertions(+), 5 deletions(-)
-
-diff --git a/tools/crm_resource.c b/tools/crm_resource.c
-index 24f1121..37a0bb0 100644
---- a/tools/crm_resource.c
-+++ b/tools/crm_resource.c
-@@ -1840,7 +1840,7 @@ main(int argc, char **argv)
-
- case cmd_execute_agent:
- if (options.cmdline_config) {
-- exit_code = cli_resource_execute_from_params(out, "test",
-+ exit_code = cli_resource_execute_from_params(out, NULL,
- options.v_class, options.v_provider, options.v_agent,
- "validate-all", options.cmdline_params,
- options.override_params, options.timeout_ms,
-diff --git a/tools/crm_resource_runtime.c b/tools/crm_resource_runtime.c
-index 48a4b40..ebf48bb 100644
---- a/tools/crm_resource_runtime.c
-+++ b/tools/crm_resource_runtime.c
-@@ -1717,14 +1717,14 @@ cli_resource_execute_from_params(pcmk__output_t *out, const char *rsc_name,
- */
- params_copy = pcmk__str_table_dup(params);
-
-- op = resources_action_create(rsc_name, rsc_class, rsc_prov, rsc_type, action, 0,
-- timeout_ms, params_copy, 0);
-+ op = resources_action_create(rsc_name ? rsc_name : "test", rsc_class, rsc_prov,
-+ rsc_type, action, 0, timeout_ms, params_copy, 0);
- if (op == NULL) {
- /* Re-run with stderr enabled so we can display a sane error message */
- crm_enable_stderr(TRUE);
- params_copy = pcmk__str_table_dup(params);
-- op = resources_action_create(rsc_name, rsc_class, rsc_prov, rsc_type, action, 0,
-- timeout_ms, params_copy, 0);
-+ op = resources_action_create(rsc_name ? rsc_name : "test", rsc_class, rsc_prov,
-+ rsc_type, action, 0, timeout_ms, params_copy, 0);
-
- /* Callers of cli_resource_execute expect that the params hash table will
- * be freed. That function uses this one, so for that reason and for
---
-1.8.3.1
-
-
-From ee56efd53d14cfc4f902769540b72b3bb6096a73 Mon Sep 17 00:00:00 2001
-From: Chris Lumens <clumens(a)redhat.com>
-Date: Mon, 24 May 2021 12:08:52 -0400
-Subject: [PATCH 06/10] Feature: tools: Add an agent-status message for
- crm_resource.
-
-This moves what was previously only done in an out->info call to its own
-output message, which means it will appear in XML output as well. Also,
-note that if --class/--agent/--provider are given, the resource name
-will be set to "test". In that case, do not display the resource name
-in the output.
-
-This message will be used for --validate and the --force-* command line
-options to crm_resource.
-
-See: rhbz#1644628
----
- tools/crm_resource_print.c | 53 ++++++++++++++++++++++++++++++++++++++++++++++
- 1 file changed, 53 insertions(+)
-
-diff --git a/tools/crm_resource_print.c b/tools/crm_resource_print.c
-index 9d82cf8..88d5878 100644
---- a/tools/crm_resource_print.c
-+++ b/tools/crm_resource_print.c
-@@ -152,6 +152,57 @@ attribute_list_default(pcmk__output_t *out, va_list args) {
- return pcmk_rc_ok;
- }
-
-+PCMK__OUTPUT_ARGS("agent-status", "int", "const char *", "const char *", "const char *",
-+ "const char *", "const char *", "int")
-+static int
-+agent_status_default(pcmk__output_t *out, va_list args) {
-+ int status = va_arg(args, int);
-+ const char *action = va_arg(args, const char *);
-+ const char *name = va_arg(args, const char *);
-+ const char *class = va_arg(args, const char *);
-+ const char *provider = va_arg(args, const char *);
-+ const char *type = va_arg(args, const char *);
-+ int rc = va_arg(args, int);
-+
-+ if (status == PCMK_LRM_OP_DONE) {
-+ out->info(out, "Operation %s%s%s (%s%s%s:%s) returned: '%s' (%d)",
-+ action, name ? " for " : "", name ? name : "",
-+ class, provider ? ":" : "", provider ? provider : "", type,
-+ services_ocf_exitcode_str(rc), rc);
-+ } else {
-+ out->err(out, "Operation %s%s%s (%s%s%s:%s) failed: '%s' (%d)",
-+ action, name ? " for " : "", name ? name : "",
-+ class, provider ? ":" : "", provider ? provider : "", type,
-+ services_lrm_status_str(status), status);
-+ }
-+
-+ return pcmk_rc_ok;
-+}
-+
-+PCMK__OUTPUT_ARGS("agent-status", "int", "const char *", "const char *", "const char *",
-+ "const char *", "const char *", "int")
-+static int
-+agent_status_xml(pcmk__output_t *out, va_list args) {
-+ int status G_GNUC_UNUSED = va_arg(args, int);
-+ const char *action G_GNUC_UNUSED = va_arg(args, const char *);
-+ const char *name G_GNUC_UNUSED = va_arg(args, const char *);
-+ const char *class G_GNUC_UNUSED = va_arg(args, const char *);
-+ const char *provider G_GNUC_UNUSED = va_arg(args, const char *);
-+ const char *type G_GNUC_UNUSED = va_arg(args, const char *);
-+ int rc = va_arg(args, int);
-+
-+ char *status_str = pcmk__itoa(rc);
-+
-+ pcmk__output_create_xml_node(out, "agent-status",
-+ "code", status_str,
-+ "message", services_ocf_exitcode_str(rc),
-+ NULL);
-+
-+ free(status_str);
-+
-+ return pcmk_rc_ok;
-+}
-+
- PCMK__OUTPUT_ARGS("attribute-list", "pe_resource_t *", "char *", "GHashTable *")
- static int
- attribute_list_text(pcmk__output_t *out, va_list args) {
-@@ -562,6 +613,8 @@ resource_names(pcmk__output_t *out, va_list args) {
- }
-
- static pcmk__message_entry_t fmt_functions[] = {
-+ { "agent-status", "default", agent_status_default },
-+ { "agent-status", "xml", agent_status_xml },
- { "attribute-list", "default", attribute_list_default },
- { "attribute-list", "text", attribute_list_text },
- { "property-list", "default", property_list_default },
---
-1.8.3.1
-
-
-From 85cb6b6bff96b18c5174d11e4de4d49cbfb20bb7 Mon Sep 17 00:00:00 2001
-From: Chris Lumens <clumens(a)redhat.com>
-Date: Tue, 1 Jun 2021 14:47:30 -0400
-Subject: [PATCH 07/10] Feature: tools: Add an overridden params output
- message.
-
-This also replaces what was previously being done in an out->info call
-with an output message. This means it shows up in XML output as well.
-Also, note that if --class/--agent/--provider are given, the resource
-name will be set to "test". In that case, do not display the resource
-name in the output.
-
-See: rhbz#1644628
----
- tools/crm_resource_print.c | 39 +++++++++++++++++++++++++++++++++++++++
- 1 file changed, 39 insertions(+)
-
-diff --git a/tools/crm_resource_print.c b/tools/crm_resource_print.c
-index 88d5878..119d83f 100644
---- a/tools/crm_resource_print.c
-+++ b/tools/crm_resource_print.c
-@@ -224,6 +224,43 @@ attribute_list_text(pcmk__output_t *out, va_list args) {
- return pcmk_rc_ok;
- }
-
-+PCMK__OUTPUT_ARGS("override", "const char *", "const char *", "const char *")
-+static int
-+override_default(pcmk__output_t *out, va_list args) {
-+ const char *rsc_name = va_arg(args, const char *);
-+ const char *name = va_arg(args, const char *);
-+ const char *value = va_arg(args, const char *);
-+
-+ if (rsc_name == NULL) {
-+ out->list_item(out, NULL, "Overriding the cluster configuration with '%s' = '%s'",
-+ name, value);
-+ } else {
-+ out->list_item(out, NULL, "Overriding the cluster configuration for '%s' with '%s' = '%s'",
-+ rsc_name, name, value);
-+ }
-+
-+ return pcmk_rc_ok;
-+}
-+
-+PCMK__OUTPUT_ARGS("override", "const char *", "const char *", "const char *")
-+static int
-+override_xml(pcmk__output_t *out, va_list args) {
-+ const char *rsc_name = va_arg(args, const char *);
-+ const char *name = va_arg(args, const char *);
-+ const char *value = va_arg(args, const char *);
-+
-+ xmlNodePtr node = pcmk__output_create_xml_node(out, "override",
-+ "name", name,
-+ "value", value,
-+ NULL);
-+
-+ if (rsc_name != NULL) {
-+ crm_xml_add(node, "rsc", rsc_name);
-+ }
-+
-+ return pcmk_rc_ok;
-+}
-+
- PCMK__OUTPUT_ARGS("property-list", "pe_resource_t *", "char *")
- static int
- property_list_default(pcmk__output_t *out, va_list args) {
-@@ -617,6 +654,8 @@ static pcmk__message_entry_t fmt_functions[] = {
- { "agent-status", "xml", agent_status_xml },
- { "attribute-list", "default", attribute_list_default },
- { "attribute-list", "text", attribute_list_text },
-+ { "override", "default", override_default },
-+ { "override", "xml", override_xml },
- { "property-list", "default", property_list_default },
- { "property-list", "text", property_list_text },
- { "resource-check-list", "default", resource_check_list_default },
---
-1.8.3.1
-
-
-From e5e24592c7c3231c619fb5253e7925ffbc634a99 Mon Sep 17 00:00:00 2001
-From: Chris Lumens <clumens(a)redhat.com>
-Date: Fri, 4 Jun 2021 10:24:51 -0400
-Subject: [PATCH 08/10] Low: tools: Use simple XML lists for resource actions
- as well.
-
-See: rhbz#1644628
----
- tools/crm_resource.c | 1 +
- 1 file changed, 1 insertion(+)
-
-diff --git a/tools/crm_resource.c b/tools/crm_resource.c
-index 37a0bb0..e957011 100644
---- a/tools/crm_resource.c
-+++ b/tools/crm_resource.c
-@@ -1643,6 +1643,7 @@ main(int argc, char **argv)
- * saves from having to write custom messages to build the lists around all these things
- */
- switch (options.rsc_cmd) {
-+ case cmd_execute_agent:
- case cmd_list_resources:
- case cmd_query_xml:
- case cmd_query_raw_xml:
---
-1.8.3.1
-
-
-From 3e75174d0bc31b261adb1994214a5878b79da85b Mon Sep 17 00:00:00 2001
-From: Chris Lumens <clumens(a)redhat.com>
-Date: Fri, 4 Jun 2021 10:30:10 -0400
-Subject: [PATCH 09/10] Feature: tools: Add an output message for resource
- actions.
-
-This wraps up the override and agent-status messages into a single
-message, along with any stdout/stderr from the resource action. This
-message should be called after taking the action.
-
-This also implements handling XML output from resource actions. Check
-to see if the validate-all action returns XML. If so, output it as a
-CDATA block under a "command" element. If not, treat it as plain text
-and output it as stdout/stderr from a command.
-
-See: rhbz#1644628
----
- tools/crm_resource_print.c | 122 +++++++++++++++++++++++++++++++++++++++++++++
- 1 file changed, 122 insertions(+)
-
-diff --git a/tools/crm_resource_print.c b/tools/crm_resource_print.c
-index 119d83f..19a366d 100644
---- a/tools/crm_resource_print.c
-+++ b/tools/crm_resource_print.c
-@@ -293,6 +293,126 @@ property_list_text(pcmk__output_t *out, va_list args) {
- return pcmk_rc_ok;
- }
-
-+PCMK__OUTPUT_ARGS("resource-agent-action", "int", "const char *", "const char *",
-+ "const char *", "const char *", "const char *", "GHashTable *",
-+ "int", "int", "char *", "char *")
-+static int
-+resource_agent_action_default(pcmk__output_t *out, va_list args) {
-+ int verbose = va_arg(args, int);
-+
-+ const char *class = va_arg(args, const char *);
-+ const char *provider = va_arg(args, const char *);
-+ const char *type = va_arg(args, const char *);
-+ const char *rsc_name = va_arg(args, const char *);
-+ const char *action = va_arg(args, const char *);
-+ GHashTable *overrides = va_arg(args, GHashTable *);
-+ int rc = va_arg(args, int);
-+ int status = va_arg(args, int);
-+ char *stdout_data = va_arg(args, char *);
-+ char *stderr_data = va_arg(args, char *);
-+
-+ if (overrides) {
-+ GHashTableIter iter;
-+ char *name = NULL;
-+ char *value = NULL;
-+
-+ out->begin_list(out, NULL, NULL, "overrides");
-+
-+ g_hash_table_iter_init(&iter, overrides);
-+ while (g_hash_table_iter_next(&iter, (gpointer *) &name, (gpointer *) &value)) {
-+ out->message(out, "override", rsc_name, name, value);
-+ }
-+
-+ out->end_list(out);
-+ }
-+
-+ out->message(out, "agent-status", status, action, rsc_name, class, provider,
-+ type, rc);
-+
-+ /* hide output for validate-all if not in verbose */
-+ if (verbose == 0 && pcmk__str_eq(action, "validate-all", pcmk__str_casei)) {
-+ return pcmk_rc_ok;
-+ }
-+
-+ if (stdout_data || stderr_data) {
-+ xmlNodePtr doc = string2xml(stdout_data);
-+
-+ if (doc != NULL) {
-+ out->output_xml(out, "command", stdout_data);
-+ xmlFreeNode(doc);
-+ } else {
-+ out->subprocess_output(out, rc, stdout_data, stderr_data);
-+ }
-+ }
-+
-+ return pcmk_rc_ok;
-+}
-+
-+PCMK__OUTPUT_ARGS("resource-agent-action", "int", "const char *", "const char *",
-+ "const char *", "const char *", "const char *", "GHashTable *",
-+ "int", "int", "char *", "char *")
-+static int
-+resource_agent_action_xml(pcmk__output_t *out, va_list args) {
-+ int verbose G_GNUC_UNUSED = va_arg(args, int);
-+
-+ const char *class = va_arg(args, const char *);
-+ const char *provider = va_arg(args, const char *);
-+ const char *type = va_arg(args, const char *);
-+ const char *rsc_name = va_arg(args, const char *);
-+ const char *action = va_arg(args, const char *);
-+ GHashTable *overrides = va_arg(args, GHashTable *);
-+ int rc = va_arg(args, int);
-+ int status = va_arg(args, int);
-+ char *stdout_data = va_arg(args, char *);
-+ char *stderr_data = va_arg(args, char *);
-+
-+ xmlNodePtr node = pcmk__output_xml_create_parent(out, "resource-agent-action",
-+ "action", action,
-+ "class", class,
-+ "type", type,
-+ NULL);
-+
-+ if (rsc_name) {
-+ crm_xml_add(node, "rsc", rsc_name);
-+ }
-+
-+ if (provider) {
-+ crm_xml_add(node, "provider", provider);
-+ }
-+
-+ if (overrides) {
-+ GHashTableIter iter;
-+ char *name = NULL;
-+ char *value = NULL;
-+
-+ out->begin_list(out, NULL, NULL, "overrides");
-+
-+ g_hash_table_iter_init(&iter, overrides);
-+ while (g_hash_table_iter_next(&iter, (gpointer *) &name, (gpointer *) &value)) {
-+ out->message(out, "override", rsc_name, name, value);
-+ }
-+
-+ out->end_list(out);
-+ }
-+
-+ out->message(out, "agent-status", status, action, rsc_name, class, provider,
-+ type, rc);
-+
-+ if (stdout_data || stderr_data) {
-+ xmlNodePtr doc = string2xml(stdout_data);
-+
-+ if (doc != NULL) {
-+ out->output_xml(out, "command", stdout_data);
-+ xmlFreeNode(doc);
-+ } else {
-+ out->subprocess_output(out, rc, stdout_data, stderr_data);
-+ }
-+ }
-+
-+ pcmk__output_xml_pop_parent(out);
-+ return pcmk_rc_ok;
-+}
-+
- PCMK__OUTPUT_ARGS("resource-check-list", "resource_checks_t *")
- static int
- resource_check_list_default(pcmk__output_t *out, va_list args) {
-@@ -658,6 +778,8 @@ static pcmk__message_entry_t fmt_functions[] = {
- { "override", "xml", override_xml },
- { "property-list", "default", property_list_default },
- { "property-list", "text", property_list_text },
-+ { "resource-agent-action", "default", resource_agent_action_default },
-+ { "resource-agent-action", "xml", resource_agent_action_xml },
- { "resource-check-list", "default", resource_check_list_default },
- { "resource-check-list", "xml", resource_check_list_xml },
- { "resource-search-list", "default", resource_search_list_default },
---
-1.8.3.1
-
-
-From b50b2418e1e997b42f5370b4672a3f105d74634f Mon Sep 17 00:00:00 2001
-From: Chris Lumens <clumens(a)redhat.com>
-Date: Fri, 4 Jun 2021 10:40:16 -0400
-Subject: [PATCH 10/10] Feature: tools: Use the new resource-agent-action
- message.
-
-See: rhbz#1644628
----
- tools/crm_resource_runtime.c | 21 +++------------------
- 1 file changed, 3 insertions(+), 18 deletions(-)
-
-diff --git a/tools/crm_resource_runtime.c b/tools/crm_resource_runtime.c
-index ebf48bb..755be9f 100644
---- a/tools/crm_resource_runtime.c
-+++ b/tools/crm_resource_runtime.c
-@@ -1765,28 +1765,13 @@ cli_resource_execute_from_params(pcmk__output_t *out, const char *rsc_name,
- if (services_action_sync(op)) {
- exit_code = op->rc;
-
-- if (op->status == PCMK_LRM_OP_DONE) {
-- out->info(out, "Operation %s for %s (%s:%s:%s) returned: '%s' (%d)",
-- action, rsc_name, rsc_class, rsc_prov ? rsc_prov : "", rsc_type,
-- services_ocf_exitcode_str(op->rc), op->rc);
-- } else {
-- out->err(out, "Operation %s for %s (%s:%s:%s) failed: '%s' (%d)",
-- action, rsc_name, rsc_class, rsc_prov ? rsc_prov : "", rsc_type,
-- services_lrm_status_str(op->status), op->status);
-- }
--
-- /* hide output for validate-all if not in verbose */
-- if (resource_verbose == 0 && pcmk__str_eq(action, "validate-all", pcmk__str_casei))
-- goto done;
--
-- if (op->stdout_data || op->stderr_data) {
-- out->subprocess_output(out, op->rc, op->stdout_data, op->stderr_data);
-- }
-+ out->message(out, "resource-agent-action", resource_verbose, rsc_class,
-+ rsc_prov, rsc_type, rsc_name, action, override_hash, op->rc,
-+ op->status, op->stdout_data, op->stderr_data);
- } else {
- exit_code = op->rc == 0 ? CRM_EX_ERROR : op->rc;
- }
-
--done:
- services_action_free(op);
- /* See comment above about why we free params here. */
- g_hash_table_destroy(params);
---
-1.8.3.1
-
diff --git a/006-crm_simulate.patch b/006-crm_simulate.patch
deleted file mode 100644
index c8d4e3f..0000000
--- a/006-crm_simulate.patch
+++ /dev/null
@@ -1,896 +0,0 @@
-From 97571e6ccc9b7fa339a7e27d9b0b9ab782ff3003 Mon Sep 17 00:00:00 2001
-From: Chris Lumens <clumens(a)redhat.com>
-Date: Wed, 16 Jun 2021 13:54:10 -0400
-Subject: [PATCH 1/5] Low: schemas: Copy crm_mon.rng in preparation for
- changes.
-
----
- xml/api/crm_mon-2.12.rng | 243 +++++++++++++++++++++++++++++++++++++++++++++++
- 1 file changed, 243 insertions(+)
- create mode 100644 xml/api/crm_mon-2.12.rng
-
-diff --git a/xml/api/crm_mon-2.12.rng b/xml/api/crm_mon-2.12.rng
-new file mode 100644
-index 0000000..ffec923
---- /dev/null
-+++ b/xml/api/crm_mon-2.12.rng
-@@ -0,0 +1,243 @@
-+<?xml version="1.0" encoding="UTF-8"?>
-+<grammar xmlns="http://relaxng.org/ns/structure/1.0"
-+ datatypeLibrary="http://www.w3.org/2001/XMLSchema-datatypes">
-+
-+ <start>
-+ <ref name="element-crm-mon"/>
-+ </start>
-+
-+ <define name="element-crm-mon">
-+ <optional>
-+ <ref name="element-summary" />
-+ </optional>
-+ <optional>
-+ <ref name="nodes-list" />
-+ </optional>
-+ <optional>
-+ <ref name="resources-list" />
-+ </optional>
-+ <optional>
-+ <ref name="node-attributes-list" />
-+ </optional>
-+ <optional>
-+ <ref name="node-history-list" />
-+ </optional>
-+ <optional>
-+ <ref name="failures-list" />
-+ </optional>
-+ <optional>
-+ <ref name="fence-event-list" />
-+ </optional>
-+ <optional>
-+ <ref name="tickets-list" />
-+ </optional>
-+ <optional>
-+ <ref name="bans-list" />
-+ </optional>
-+ </define>
-+
-+ <define name="element-summary">
-+ <element name="summary">
-+ <optional>
-+ <element name="stack">
-+ <attribute name="type"> <text /> </attribute>
-+ </element>
-+ </optional>
-+ <optional>
-+ <element name="current_dc">
-+ <attribute name="present"> <data type="boolean" /> </attribute>
-+ <optional>
-+ <group>
-+ <attribute name="version"> <text /> </attribute>
-+ <attribute name="name"> <text /> </attribute>
-+ <attribute name="id"> <text /> </attribute>
-+ <attribute name="with_quorum"> <data type="boolean" /> </attribute>
-+ </group>
-+ </optional>
-+ </element>
-+ </optional>
-+ <optional>
-+ <element name="last_update">
-+ <attribute name="time"> <text /> </attribute>
-+ </element>
-+ <element name="last_change">
-+ <attribute name="time"> <text /> </attribute>
-+ <attribute name="user"> <text /> </attribute>
-+ <attribute name="client"> <text /> </attribute>
-+ <attribute name="origin"> <text /> </attribute>
-+ </element>
-+ </optional>
-+ <optional>
-+ <element name="nodes_configured">
-+ <attribute name="number"> <data type="nonNegativeInteger" /> </attribute>
-+ </element>
-+ <element name="resources_configured">
-+ <attribute name="number"> <data type="nonNegativeInteger" /> </attribute>
-+ <attribute name="disabled"> <data type="nonNegativeInteger" /> </attribute>
-+ <attribute name="blocked"> <data type="nonNegativeInteger" /> </attribute>
-+ </element>
-+ </optional>
-+ <optional>
-+ <element name="cluster_options">
-+ <attribute name="stonith-enabled"> <data type="boolean" /> </attribute>
-+ <attribute name="symmetric-cluster"> <data type="boolean" /> </attribute>
-+ <attribute name="no-quorum-policy"> <text /> </attribute>
-+ <attribute name="maintenance-mode"> <data type="boolean" /> </attribute>
-+ <attribute name="stop-all-resources"> <data type="boolean" /> </attribute>
-+ </element>
-+ </optional>
-+ </element>
-+ </define>
-+
-+ <define name="resources-list">
-+ <element name="resources">
-+ <zeroOrMore>
-+ <externalRef href="resources-2.4.rng" />
-+ </zeroOrMore>
-+ </element>
-+ </define>
-+
-+ <define name="nodes-list">
-+ <element name="nodes">
-+ <zeroOrMore>
-+ <externalRef href="nodes-2.8.rng" />
-+ </zeroOrMore>
-+ </element>
-+ </define>
-+
-+ <define name="node-attributes-list">
-+ <element name="node_attributes">
-+ <zeroOrMore>
-+ <externalRef href="node-attrs-2.8.rng" />
-+ </zeroOrMore>
-+ </element>
-+ </define>
-+
-+ <define name="node-history-list">
-+ <element name="node_history">
-+ <zeroOrMore>
-+ <ref name="element-node-history" />
-+ </zeroOrMore>
-+ </element>
-+ </define>
-+
-+ <define name="failures-list">
-+ <element name="failures">
-+ <zeroOrMore>
-+ <externalRef href="failure-2.8.rng" />
-+ </zeroOrMore>
-+ </element>
-+ </define>
-+
-+ <define name="fence-event-list">
-+ <element name="fence_history">
-+ <optional>
-+ <attribute name="status"> <data type="integer" /> </attribute>
-+ </optional>
-+ <zeroOrMore>
-+ <externalRef href="fence-event-2.0.rng" />
-+ </zeroOrMore>
-+ </element>
-+ </define>
-+
-+ <define name="tickets-list">
-+ <element name="tickets">
-+ <zeroOrMore>
-+ <ref name="element-ticket" />
-+ </zeroOrMore>
-+ </element>
-+ </define>
-+
-+ <define name="bans-list">
-+ <element name="bans">
-+ <zeroOrMore>
-+ <ref name="element-ban" />
-+ </zeroOrMore>
-+ </element>
-+ </define>
-+
-+ <define name="element-node-history">
-+ <element name="node">
-+ <attribute name="name"> <text /> </attribute>
-+ <zeroOrMore>
-+ <ref name="element-resource-history" />
-+ </zeroOrMore>
-+ </element>
-+ </define>
-+
-+ <define name="element-resource-history">
-+ <element name="resource_history">
-+ <attribute name="id"> <text /> </attribute>
-+ <attribute name="orphan"> <data type="boolean" /> </attribute>
-+ <optional>
-+ <group>
-+ <attribute name="migration-threshold"> <data type="nonNegativeInteger" /> </attribute>
-+ <optional>
-+ <attribute name="fail-count"> <text /> </attribute>
-+ </optional>
-+ <optional>
-+ <attribute name="last-failure"> <text /> </attribute>
-+ </optional>
-+ </group>
-+ </optional>
-+ <zeroOrMore>
-+ <ref name="element-operation-history" />
-+ </zeroOrMore>
-+ </element>
-+ </define>
-+
-+ <define name="element-operation-history">
-+ <element name="operation_history">
-+ <attribute name="call"> <text /> </attribute>
-+ <attribute name="task"> <text /> </attribute>
-+ <optional>
-+ <attribute name="interval"> <text /> </attribute>
-+ </optional>
-+ <optional>
-+ <attribute name="last-rc-change"> <text /> </attribute>
-+ </optional>
-+ <optional>
-+ <attribute name="last-run"> <text /> </attribute>
-+ </optional>
-+ <optional>
-+ <attribute name="exec-time"> <text /> </attribute>
-+ </optional>
-+ <optional>
-+ <attribute name="queue-time"> <text /> </attribute>
-+ </optional>
-+ <attribute name="rc"> <data type="integer" /> </attribute>
-+ <attribute name="rc_text"> <text /> </attribute>
-+ </element>
-+ </define>
-+
-+ <define name="element-ticket">
-+ <element name="ticket">
-+ <attribute name="id"> <text /> </attribute>
-+ <attribute name="status">
-+ <choice>
-+ <value>granted</value>
-+ <value>revoked</value>
-+ </choice>
-+ </attribute>
-+ <attribute name="standby"> <data type="boolean" /> </attribute>
-+ <optional>
-+ <attribute name="last-granted"> <text /> </attribute>
-+ </optional>
-+ </element>
-+ </define>
-+
-+ <define name="element-ban">
-+ <element name="ban">
-+ <attribute name="id"> <text /> </attribute>
-+ <attribute name="resource"> <text /> </attribute>
-+ <attribute name="node"> <text /> </attribute>
-+ <attribute name="weight"> <data type="integer" /> </attribute>
-+ <attribute name="promoted-only"> <data type="boolean" /> </attribute>
-+ <!-- DEPRECATED: master_only is a duplicate of promoted-only that is
-+ provided solely for API backward compatibility. It will be
-+ removed in a future release. Check promoted-only instead.
-+ -->
-+ <attribute name="master_only"> <data type="boolean" /> </attribute>
-+ </element>
-+ </define>
-+</grammar>
---
-1.8.3.1
-
-
-From da394983f106f974274ddd94675a04c85086010e Mon Sep 17 00:00:00 2001
-From: Chris Lumens <clumens(a)redhat.com>
-Date: Fri, 18 Jun 2021 15:06:34 -0400
-Subject: [PATCH 2/5] Refactor: Split node history out into its own XML schema.
-
-This allows for sharing it between crm_mon and crm_simulate.
----
- xml/Makefile.am | 2 +-
- xml/api/crm_mon-2.12.rng | 64 +--------------------------------------
- xml/api/node-history-2.12.rng | 70 +++++++++++++++++++++++++++++++++++++++++++
- 3 files changed, 72 insertions(+), 64 deletions(-)
- create mode 100644 xml/api/node-history-2.12.rng
-
-diff --git a/xml/Makefile.am b/xml/Makefile.am
-index b9448d4..8e7b6d3 100644
---- a/xml/Makefile.am
-+++ b/xml/Makefile.am
-@@ -64,7 +64,7 @@ API_request_base = command-output \
- CIB_cfg_base = options nodes resources constraints fencing acls tags alerts
-
- # Names of all schemas (including top level and those included by others)
--API_base = $(API_request_base) fence-event failure generic-list item node-attrs nodes resources status
-+API_base = $(API_request_base) fence-event failure generic-list item node-attrs node-history nodes resources status
- CIB_base = cib $(CIB_cfg_base) status score rule nvset
-
- # Static schema files and transforms (only CIB has transforms)
-diff --git a/xml/api/crm_mon-2.12.rng b/xml/api/crm_mon-2.12.rng
-index ffec923..be14412 100644
---- a/xml/api/crm_mon-2.12.rng
-+++ b/xml/api/crm_mon-2.12.rng
-@@ -20,7 +20,7 @@
- <ref name="node-attributes-list" />
- </optional>
- <optional>
-- <ref name="node-history-list" />
-+ <externalRef href="node-history-2.12.rng"/>
- </optional>
- <optional>
- <ref name="failures-list" />
-@@ -113,14 +113,6 @@
- </element>
- </define>
-
-- <define name="node-history-list">
-- <element name="node_history">
-- <zeroOrMore>
-- <ref name="element-node-history" />
-- </zeroOrMore>
-- </element>
-- </define>
--
- <define name="failures-list">
- <element name="failures">
- <zeroOrMore>
-@@ -156,60 +148,6 @@
- </element>
- </define>
-
-- <define name="element-node-history">
-- <element name="node">
-- <attribute name="name"> <text /> </attribute>
-- <zeroOrMore>
-- <ref name="element-resource-history" />
-- </zeroOrMore>
-- </element>
-- </define>
--
-- <define name="element-resource-history">
-- <element name="resource_history">
-- <attribute name="id"> <text /> </attribute>
-- <attribute name="orphan"> <data type="boolean" /> </attribute>
-- <optional>
-- <group>
-- <attribute name="migration-threshold"> <data type="nonNegativeInteger" /> </attribute>
-- <optional>
-- <attribute name="fail-count"> <text /> </attribute>
-- </optional>
-- <optional>
-- <attribute name="last-failure"> <text /> </attribute>
-- </optional>
-- </group>
-- </optional>
-- <zeroOrMore>
-- <ref name="element-operation-history" />
-- </zeroOrMore>
-- </element>
-- </define>
--
-- <define name="element-operation-history">
-- <element name="operation_history">
-- <attribute name="call"> <text /> </attribute>
-- <attribute name="task"> <text /> </attribute>
-- <optional>
-- <attribute name="interval"> <text /> </attribute>
-- </optional>
-- <optional>
-- <attribute name="last-rc-change"> <text /> </attribute>
-- </optional>
-- <optional>
-- <attribute name="last-run"> <text /> </attribute>
-- </optional>
-- <optional>
-- <attribute name="exec-time"> <text /> </attribute>
-- </optional>
-- <optional>
-- <attribute name="queue-time"> <text /> </attribute>
-- </optional>
-- <attribute name="rc"> <data type="integer" /> </attribute>
-- <attribute name="rc_text"> <text /> </attribute>
-- </element>
-- </define>
--
- <define name="element-ticket">
- <element name="ticket">
- <attribute name="id"> <text /> </attribute>
-diff --git a/xml/api/node-history-2.12.rng b/xml/api/node-history-2.12.rng
-new file mode 100644
-index 0000000..9628000
---- /dev/null
-+++ b/xml/api/node-history-2.12.rng
-@@ -0,0 +1,70 @@
-+<?xml version="1.0" encoding="UTF-8"?>
-+<grammar xmlns="http://relaxng.org/ns/structure/1.0"
-+ datatypeLibrary="http://www.w3.org/2001/XMLSchema-datatypes">
-+
-+ <start>
-+ <ref name="node-history-list" />
-+ </start>
-+
-+ <define name="node-history-list">
-+ <element name="node_history">
-+ <zeroOrMore>
-+ <ref name="element-node-history" />
-+ </zeroOrMore>
-+ </element>
-+ </define>
-+
-+ <define name="element-node-history">
-+ <element name="node">
-+ <attribute name="name"> <text /> </attribute>
-+ <zeroOrMore>
-+ <ref name="element-resource-history" />
-+ </zeroOrMore>
-+ </element>
-+ </define>
-+
-+ <define name="element-resource-history">
-+ <element name="resource_history">
-+ <attribute name="id"> <text /> </attribute>
-+ <attribute name="orphan"> <data type="boolean" /> </attribute>
-+ <optional>
-+ <group>
-+ <attribute name="migration-threshold"> <data type="nonNegativeInteger" /> </attribute>
-+ <optional>
-+ <attribute name="fail-count"> <text /> </attribute>
-+ </optional>
-+ <optional>
-+ <attribute name="last-failure"> <text /> </attribute>
-+ </optional>
-+ </group>
-+ </optional>
-+ <zeroOrMore>
-+ <ref name="element-operation-history" />
-+ </zeroOrMore>
-+ </element>
-+ </define>
-+
-+ <define name="element-operation-history">
-+ <element name="operation_history">
-+ <attribute name="call"> <text /> </attribute>
-+ <attribute name="task"> <text /> </attribute>
-+ <optional>
-+ <attribute name="interval"> <text /> </attribute>
-+ </optional>
-+ <optional>
-+ <attribute name="last-rc-change"> <text /> </attribute>
-+ </optional>
-+ <optional>
-+ <attribute name="last-run"> <text /> </attribute>
-+ </optional>
-+ <optional>
-+ <attribute name="exec-time"> <text /> </attribute>
-+ </optional>
-+ <optional>
-+ <attribute name="queue-time"> <text /> </attribute>
-+ </optional>
-+ <attribute name="rc"> <data type="integer" /> </attribute>
-+ <attribute name="rc_text"> <text /> </attribute>
-+ </element>
-+ </define>
-+</grammar>
---
-1.8.3.1
-
-
-From bf72b2615630eef7876e443d60b34d5a316de847 Mon Sep 17 00:00:00 2001
-From: Chris Lumens <clumens(a)redhat.com>
-Date: Wed, 16 Jun 2021 14:09:31 -0400
-Subject: [PATCH 3/5] Low: schemas: Copy crm_simulate.rng in preparation for
- changes.
-
----
- xml/api/crm_simulate-2.12.rng | 335 ++++++++++++++++++++++++++++++++++++++++++
- 1 file changed, 335 insertions(+)
- create mode 100644 xml/api/crm_simulate-2.12.rng
-
-diff --git a/xml/api/crm_simulate-2.12.rng b/xml/api/crm_simulate-2.12.rng
-new file mode 100644
-index 0000000..9a7612d
---- /dev/null
-+++ b/xml/api/crm_simulate-2.12.rng
-@@ -0,0 +1,335 @@
-+<?xml version="1.0" encoding="UTF-8"?>
-+<grammar xmlns="http://relaxng.org/ns/structure/1.0"
-+ datatypeLibrary="http://www.w3.org/2001/XMLSchema-datatypes">
-+
-+ <start>
-+ <ref name="element-crm-simulate"/>
-+ </start>
-+
-+ <define name="element-crm-simulate">
-+ <choice>
-+ <ref name="timings-list" />
-+ <group>
-+ <ref name="cluster-status" />
-+ <optional>
-+ <ref name="modifications-list" />
-+ </optional>
-+ <optional>
-+ <ref name="allocations-utilizations-list" />
-+ </optional>
-+ <optional>
-+ <ref name="action-list" />
-+ </optional>
-+ <optional>
-+ <ref name="cluster-injected-actions-list" />
-+ <ref name="revised-cluster-status" />
-+ </optional>
-+ </group>
-+ </choice>
-+ </define>
-+
-+ <define name="allocations-utilizations-list">
-+ <choice>
-+ <element name="allocations">
-+ <zeroOrMore>
-+ <choice>
-+ <ref name="element-allocation" />
-+ <ref name="element-promotion" />
-+ </choice>
-+ </zeroOrMore>
-+ </element>
-+ <element name="utilizations">
-+ <zeroOrMore>
-+ <choice>
-+ <ref name="element-capacity" />
-+ <ref name="element-utilization" />
-+ </choice>
-+ </zeroOrMore>
-+ </element>
-+ <element name="allocations_utilizations">
-+ <zeroOrMore>
-+ <choice>
-+ <ref name="element-allocation" />
-+ <ref name="element-promotion" />
-+ <ref name="element-capacity" />
-+ <ref name="element-utilization" />
-+ </choice>
-+ </zeroOrMore>
-+ </element>
-+ </choice>
-+ </define>
-+
-+ <define name="cluster-status">
-+ <element name="cluster_status">
-+ <ref name="nodes-list" />
-+ <ref name="resources-list" />
-+ <optional>
-+ <ref name="node-attributes-list" />
-+ </optional>
-+ <optional>
-+ <ref name="failures-list" />
-+ </optional>
-+ </element>
-+ </define>
-+
-+ <define name="modifications-list">
-+ <element name="modifications">
-+ <optional>
-+ <attribute name="quorum"> <text /> </attribute>
-+ </optional>
-+ <optional>
-+ <attribute name="watchdog"> <text /> </attribute>
-+ </optional>
-+ <zeroOrMore>
-+ <ref name="element-inject-modify-node" />
-+ </zeroOrMore>
-+ <zeroOrMore>
-+ <ref name="element-inject-modify-ticket" />
-+ </zeroOrMore>
-+ <zeroOrMore>
-+ <ref name="element-inject-spec" />
-+ </zeroOrMore>
-+ <zeroOrMore>
-+ <ref name="element-inject-attr" />
-+ </zeroOrMore>
-+ </element>
-+ </define>
-+
-+ <define name="revised-cluster-status">
-+ <element name="revised_cluster_status">
-+ <ref name="nodes-list" />
-+ <ref name="resources-list" />
-+ <optional>
-+ <ref name="node-attributes-list" />
-+ </optional>
-+ <optional>
-+ <ref name="failures-list" />
-+ </optional>
-+ </element>
-+ </define>
-+
-+ <define name="element-inject-attr">
-+ <element name="inject_attr">
-+ <attribute name="cib_node"> <text /> </attribute>
-+ <attribute name="name"> <text /> </attribute>
-+ <attribute name="node_path"> <text /> </attribute>
-+ <attribute name="value"> <text /> </attribute>
-+ </element>
-+ </define>
-+
-+ <define name="element-inject-modify-node">
-+ <element name="modify_node">
-+ <attribute name="action"> <text /> </attribute>
-+ <attribute name="node"> <text /> </attribute>
-+ </element>
-+ </define>
-+
-+ <define name="element-inject-spec">
-+ <element name="inject_spec">
-+ <attribute name="spec"> <text /> </attribute>
-+ </element>
-+ </define>
-+
-+ <define name="element-inject-modify-ticket">
-+ <element name="modify_ticket">
-+ <attribute name="action"> <text /> </attribute>
-+ <attribute name="ticket"> <text /> </attribute>
-+ </element>
-+ </define>
-+
-+ <define name="cluster-injected-actions-list">
-+ <element name="transition">
-+ <zeroOrMore>
-+ <ref name="element-injected-actions" />
-+ </zeroOrMore>
-+ </element>
-+ </define>
-+
-+ <define name="node-attributes-list">
-+ <element name="node_attributes">
-+ <zeroOrMore>
-+ <externalRef href="node-attrs-2.8.rng" />
-+ </zeroOrMore>
-+ </element>
-+ </define>
-+
-+ <define name="failures-list">
-+ <element name="failures">
-+ <zeroOrMore>
-+ <externalRef href="failure-2.8.rng" />
-+ </zeroOrMore>
-+ </element>
-+ </define>
-+
-+ <define name="nodes-list">
-+ <element name="nodes">
-+ <zeroOrMore>
-+ <externalRef href="nodes-2.8.rng" />
-+ </zeroOrMore>
-+ </element>
-+ </define>
-+
-+ <define name="resources-list">
-+ <element name="resources">
-+ <zeroOrMore>
-+ <externalRef href="resources-2.4.rng" />
-+ </zeroOrMore>
-+ </element>
-+ </define>
-+
-+ <define name="timings-list">
-+ <element name="timings">
-+ <zeroOrMore>
-+ <ref name="element-timing" />
-+ </zeroOrMore>
-+ </element>
-+ </define>
-+
-+ <define name="action-list">
-+ <element name="actions">
-+ <zeroOrMore>
-+ <ref name="element-node-action" />
-+ </zeroOrMore>
-+ <zeroOrMore>
-+ <ref name="element-rsc-action" />
-+ </zeroOrMore>
-+ </element>
-+ </define>
-+
-+ <define name="element-allocation">
-+ <element name="node_weight">
-+ <attribute name="function"> <text /> </attribute>
-+ <attribute name="node"> <text /> </attribute>
-+ <externalRef href="../score.rng" />
-+ <optional>
-+ <attribute name="id"> <text /> </attribute>
-+ </optional>
-+ </element>
-+ </define>
-+
-+ <define name="element-capacity">
-+ <element name="capacity">
-+ <attribute name="comment"> <text /> </attribute>
-+ <attribute name="node"> <text /> </attribute>
-+ <zeroOrMore>
-+ <element>
-+ <anyName />
-+ <text />
-+ </element>
-+ </zeroOrMore>
-+ </element>
-+ </define>
-+
-+ <define name="element-inject-cluster-action">
-+ <element name="cluster_action">
-+ <attribute name="node"> <text /> </attribute>
-+ <attribute name="task"> <text /> </attribute>
-+ <optional>
-+ <attribute name="id"> <text /> </attribute>
-+ </optional>
-+ </element>
-+ </define>
-+
-+ <define name="element-injected-actions">
-+ <choice>
-+ <ref name="element-inject-cluster-action" />
-+ <ref name="element-inject-fencing-action" />
-+ <ref name="element-inject-pseudo-action" />
-+ <ref name="element-inject-rsc-action" />
-+ </choice>
-+ </define>
-+
-+ <define name="element-inject-fencing-action">
-+ <element name="fencing_action">
-+ <attribute name="op"> <text /> </attribute>
-+ <attribute name="target"> <text /> </attribute>
-+ </element>
-+ </define>
-+
-+ <define name="element-node-action">
-+ <element name="node_action">
-+ <attribute name="node"> <text /> </attribute>
-+ <attribute name="reason"> <text /> </attribute>
-+ <attribute name="task"> <text /> </attribute>
-+ </element>
-+ </define>
-+
-+ <define name="element-promotion">
-+ <element name="promotion_score">
-+ <attribute name="id"> <text /> </attribute>
-+ <externalRef href="../score.rng" />
-+ <optional>
-+ <attribute name="node"> <text /> </attribute>
-+ </optional>
-+ </element>
-+ </define>
-+
-+ <define name="element-inject-pseudo-action">
-+ <element name="pseudo_action">
-+ <attribute name="task"> <text /> </attribute>
-+ <optional>
-+ <attribute name="node"> <text /> </attribute>
-+ </optional>
-+ </element>
-+ </define>
-+
-+ <define name="element-inject-rsc-action">
-+ <element name="rsc_action">
-+ <attribute name="node"> <text /> </attribute>
-+ <attribute name="op"> <text /> </attribute>
-+ <attribute name="resource"> <text /> </attribute>
-+ <optional>
-+ <attribute name="interval"> <data type="integer" /> </attribute>
-+ </optional>
-+ </element>
-+ </define>
-+
-+ <define name="element-timing">
-+ <element name="timing">
-+ <attribute name="file"> <text /> </attribute>
-+ <attribute name="duration"> <data type="double" /> </attribute>
-+ </element>
-+ </define>
-+
-+ <define name="element-rsc-action">
-+ <element name="rsc_action">
-+ <attribute name="action"> <text /> </attribute>
-+ <attribute name="resource"> <text /> </attribute>
-+ <optional>
-+ <attribute name="blocked"> <data type="boolean" /> </attribute>
-+ </optional>
-+ <optional>
-+ <attribute name="dest"> <text /> </attribute>
-+ </optional>
-+ <optional>
-+ <attribute name="next-role"> <text /> </attribute>
-+ </optional>
-+ <optional>
-+ <attribute name="node"> <text /> </attribute>
-+ </optional>
-+ <optional>
-+ <attribute name="reason"> <text /> </attribute>
-+ </optional>
-+ <optional>
-+ <attribute name="role"> <text /> </attribute>
-+ </optional>
-+ <optional>
-+ <attribute name="source"> <text /> </attribute>
-+ </optional>
-+ </element>
-+ </define>
-+
-+ <define name="element-utilization">
-+ <element name="utilization">
-+ <attribute name="function"> <text /> </attribute>
-+ <attribute name="node"> <text /> </attribute>
-+ <attribute name="resource"> <text /> </attribute>
-+ <zeroOrMore>
-+ <element>
-+ <anyName />
-+ <text />
-+ </element>
-+ </zeroOrMore>
-+ </element>
-+ </define>
-+</grammar>
---
-1.8.3.1
-
-
-From c46e07788788acf5669e3f89b9344190a91c7331 Mon Sep 17 00:00:00 2001
-From: Chris Lumens <clumens(a)redhat.com>
-Date: Fri, 18 Jun 2021 15:10:19 -0400
-Subject: [PATCH 4/5] Feature: tools: Add the node-summary to crm_simulate
- output.
-
-If --show-failcounts is given to crm_simulate, it should also display
-the node-summary message.
-
-See: rhbz#1686426
----
- tools/crm_simulate.c | 7 +++++--
- xml/api/crm_simulate-2.12.rng | 3 +++
- 2 files changed, 8 insertions(+), 2 deletions(-)
-
-diff --git a/tools/crm_simulate.c b/tools/crm_simulate.c
-index b4aa9d1..2ea292c 100644
---- a/tools/crm_simulate.c
-+++ b/tools/crm_simulate.c
-@@ -409,11 +409,14 @@ print_cluster_status(pe_working_set_t * data_set, unsigned int print_opts)
- FALSE, FALSE, all, all, FALSE);
-
- if (options.show_attrs) {
-- out->message(out, "node-attribute-list", data_set,
-- 0, rc == pcmk_rc_ok, FALSE, FALSE, FALSE, all, all);
-+ rc = out->message(out, "node-attribute-list", data_set,
-+ 0, rc == pcmk_rc_ok, FALSE, FALSE, FALSE, all, all);
- }
-
- if (options.show_failcounts) {
-+ rc = out->message(out, "node-summary", data_set, all, all,
-+ 0, print_opts, FALSE, FALSE, FALSE, FALSE, rc == pcmk_rc_ok);
-+
- out->message(out, "failed-action-list", data_set, all, all,
- rc == pcmk_rc_ok);
- }
-diff --git a/xml/api/crm_simulate-2.12.rng b/xml/api/crm_simulate-2.12.rng
-index 9a7612d..f90bd36 100644
---- a/xml/api/crm_simulate-2.12.rng
-+++ b/xml/api/crm_simulate-2.12.rng
-@@ -67,6 +67,9 @@
- <ref name="node-attributes-list" />
- </optional>
- <optional>
-+ <externalRef href="node-history-2.12.rng" />
-+ </optional>
-+ <optional>
- <ref name="failures-list" />
- </optional>
- </element>
---
-1.8.3.1
-
-
-From bac50336e0264604716e5997b87ee7e65311b982 Mon Sep 17 00:00:00 2001
-From: Chris Lumens <clumens(a)redhat.com>
-Date: Fri, 18 Jun 2021 15:21:52 -0400
-Subject: [PATCH 5/5] Low: libcrmcommon: Increase PCMK__API_VERSION for new
- crm_resource output.
-
-See: rhbz#1686426
----
- include/crm/common/output_internal.h | 2 +-
- 1 file changed, 1 insertion(+), 1 deletion(-)
-
-diff --git a/include/crm/common/output_internal.h b/include/crm/common/output_internal.h
-index 0436cde..ba9c423 100644
---- a/include/crm/common/output_internal.h
-+++ b/include/crm/common/output_internal.h
-@@ -27,7 +27,7 @@ extern "C" {
- # include <glib.h>
- # include <crm/common/results.h>
-
--# define PCMK__API_VERSION "2.11"
-+# define PCMK__API_VERSION "2.12"
-
- #if defined(PCMK__WITH_ATTRIBUTE_OUTPUT_ARGS)
- # define PCMK__OUTPUT_ARGS(ARGS...) __attribute__((output_args(ARGS)))
---
-1.8.3.1
-
diff --git a/007-unfencing-loop.patch b/007-unfencing-loop.patch
deleted file mode 100644
index d4950c8..0000000
--- a/007-unfencing-loop.patch
+++ /dev/null
@@ -1,733 +0,0 @@
-From 6dcd6b51d7d3993bc483588d6ed75077518ed600 Mon Sep 17 00:00:00 2001
-From: Ken Gaillot <kgaillot(a)redhat.com>
-Date: Fri, 4 Jun 2021 16:30:55 -0500
-Subject: [PATCH 01/11] Low: controller: check whether unfenced node was remote
- node
-
-... so the controller can indicate the node is remote (if known at that point,
-which is not guaranteed) when setting unfencing-related node attributes.
----
- daemons/controld/controld_fencing.c | 21 ++++++++++++++++++---
- 1 file changed, 18 insertions(+), 3 deletions(-)
-
-diff --git a/daemons/controld/controld_fencing.c b/daemons/controld/controld_fencing.c
-index 23dff28..0fba661 100644
---- a/daemons/controld/controld_fencing.c
-+++ b/daemons/controld/controld_fencing.c
-@@ -757,15 +757,30 @@ tengine_stonith_callback(stonith_t *stonith, stonith_callback_data_t *data)
- if (pcmk__str_eq("on", op, pcmk__str_casei)) {
- const char *value = NULL;
- char *now = pcmk__ttoa(time(NULL));
-+ gboolean is_remote_node = FALSE;
-+
-+ /* This check is not 100% reliable, since this node is not
-+ * guaranteed to have the remote node cached. However, it
-+ * doesn't have to be reliable, since the attribute manager can
-+ * learn a node's "remoteness" by other means sooner or later.
-+ * This allows it to learn more quickly if this node does have
-+ * the information.
-+ */
-+ if (g_hash_table_lookup(crm_remote_peer_cache, uuid) != NULL) {
-+ is_remote_node = TRUE;
-+ }
-
-- update_attrd(target, CRM_ATTR_UNFENCED, now, NULL, FALSE);
-+ update_attrd(target, CRM_ATTR_UNFENCED, now, NULL,
-+ is_remote_node);
- free(now);
-
- value = crm_meta_value(action->params, XML_OP_ATTR_DIGESTS_ALL);
-- update_attrd(target, CRM_ATTR_DIGESTS_ALL, value, NULL, FALSE);
-+ update_attrd(target, CRM_ATTR_DIGESTS_ALL, value, NULL,
-+ is_remote_node);
-
- value = crm_meta_value(action->params, XML_OP_ATTR_DIGESTS_SECURE);
-- update_attrd(target, CRM_ATTR_DIGESTS_SECURE, value, NULL, FALSE);
-+ update_attrd(target, CRM_ATTR_DIGESTS_SECURE, value, NULL,
-+ is_remote_node);
-
- } else if (action->sent_update == FALSE) {
- send_stonith_update(action, target, uuid);
---
-1.8.3.1
-
-
-From 3ef6d9403f68ab8559c45cc99f5a8da05ca6420b Mon Sep 17 00:00:00 2001
-From: Ken Gaillot <kgaillot(a)redhat.com>
-Date: Mon, 7 Jun 2021 10:50:36 -0500
-Subject: [PATCH 02/11] Refactor: pacemaker-attrd: functionize adding remote
- node to cache
-
-... for future reuse
----
- daemons/attrd/attrd_commands.c | 34 +++++++++++++++++++++++-----------
- 1 file changed, 23 insertions(+), 11 deletions(-)
-
-diff --git a/daemons/attrd/attrd_commands.c b/daemons/attrd/attrd_commands.c
-index 731c243..93a165b 100644
---- a/daemons/attrd/attrd_commands.c
-+++ b/daemons/attrd/attrd_commands.c
-@@ -102,6 +102,28 @@ free_attribute(gpointer data)
- }
- }
-
-+/*!
-+ * \internal
-+ * \brief Ensure a Pacemaker Remote node is in the correct peer cache
-+ *
-+ * \param[in]
-+ */
-+static void
-+cache_remote_node(const char *node_name)
-+{
-+ /* If we previously assumed this node was an unseen cluster node,
-+ * remove its entry from the cluster peer cache.
-+ */
-+ crm_node_t *dup = pcmk__search_cluster_node_cache(0, node_name);
-+
-+ if (dup && (dup->uuid == NULL)) {
-+ reap_crm_member(0, node_name);
-+ }
-+
-+ // Ensure node is in the remote peer cache
-+ CRM_ASSERT(crm_remote_peer_get(node_name) != NULL);
-+}
-+
- static xmlNode *
- build_attribute_xml(
- xmlNode *parent, const char *name, const char *set, const char *uuid, unsigned int timeout_ms, const char *user,
-@@ -709,17 +731,7 @@ attrd_lookup_or_create_value(GHashTable *values, const char *host, xmlNode *xml)
-
- crm_element_value_int(xml, PCMK__XA_ATTR_IS_REMOTE, &is_remote);
- if (is_remote) {
-- /* If we previously assumed this node was an unseen cluster node,
-- * remove its entry from the cluster peer cache.
-- */
-- crm_node_t *dup = pcmk__search_cluster_node_cache(0, host);
--
-- if (dup && (dup->uuid == NULL)) {
-- reap_crm_member(0, host);
-- }
--
-- /* Ensure this host is in the remote peer cache */
-- CRM_ASSERT(crm_remote_peer_get(host) != NULL);
-+ cache_remote_node(host);
- }
-
- if (v == NULL) {
---
-1.8.3.1
-
-
-From 6fac2c71bc2c56870ac828d7cd7b7c799279c47e Mon Sep 17 00:00:00 2001
-From: Ken Gaillot <kgaillot(a)redhat.com>
-Date: Mon, 7 Jun 2021 10:39:34 -0500
-Subject: [PATCH 03/11] Refactor: pacemaker-attrd: don't try to remove votes
- for remote nodes
-
-Remote nodes never vote.
-
-This has no effect in practice since the removal would simply do nothing,
-but we might as well not waste time trying.
----
- daemons/attrd/attrd_commands.c | 11 ++++++-----
- 1 file changed, 6 insertions(+), 5 deletions(-)
-
-diff --git a/daemons/attrd/attrd_commands.c b/daemons/attrd/attrd_commands.c
-index 93a165b..dbe777e 100644
---- a/daemons/attrd/attrd_commands.c
-+++ b/daemons/attrd/attrd_commands.c
-@@ -976,7 +976,8 @@ attrd_election_cb(gpointer user_data)
- void
- attrd_peer_change_cb(enum crm_status_type kind, crm_node_t *peer, const void *data)
- {
-- bool remove_voter = FALSE;
-+ bool gone = false;
-+ bool is_remote = pcmk_is_set(peer->flags, crm_remote_node);
-
- switch (kind) {
- case crm_status_uname:
-@@ -984,7 +985,7 @@ attrd_peer_change_cb(enum crm_status_type kind, crm_node_t *peer, const void *da
-
- case crm_status_processes:
- if (!pcmk_is_set(peer->processes, crm_get_cluster_proc())) {
-- remove_voter = TRUE;
-+ gone = true;
- }
- break;
-
-@@ -1000,13 +1001,13 @@ attrd_peer_change_cb(enum crm_status_type kind, crm_node_t *peer, const void *da
- } else {
- // Remove all attribute values associated with lost nodes
- attrd_peer_remove(peer->uname, FALSE, "loss");
-- remove_voter = TRUE;
-+ gone = true;
- }
- break;
- }
-
-- // In case an election is in progress, remove any vote by the node
-- if (remove_voter) {
-+ // Remove votes from cluster nodes that leave, in case election in progress
-+ if (gone && !is_remote) {
- attrd_remove_voter(peer);
- }
- }
---
-1.8.3.1
-
-
-From 54089fc663d6aaf10ca164c6c94b3b17237788de Mon Sep 17 00:00:00 2001
-From: Ken Gaillot <kgaillot(a)redhat.com>
-Date: Mon, 7 Jun 2021 10:40:06 -0500
-Subject: [PATCH 04/11] Low: pacemaker-attrd: check for remote nodes in peer
- update callback
-
-If a remote node was started before the local cluster node joined the cluster,
-the cluster node will assume its node attributes are for a cluster node until
-it learns otherwise. Check for remoteness in the peer update callback, to have
-another way we can learn it.
----
- daemons/attrd/attrd_commands.c | 4 ++++
- 1 file changed, 4 insertions(+)
-
-diff --git a/daemons/attrd/attrd_commands.c b/daemons/attrd/attrd_commands.c
-index dbe777e..5f6a754 100644
---- a/daemons/attrd/attrd_commands.c
-+++ b/daemons/attrd/attrd_commands.c
-@@ -1009,6 +1009,10 @@ attrd_peer_change_cb(enum crm_status_type kind, crm_node_t *peer, const void *da
- // Remove votes from cluster nodes that leave, in case election in progress
- if (gone && !is_remote) {
- attrd_remove_voter(peer);
-+
-+ // Ensure remote nodes that come up are in the remote node cache
-+ } else if (!gone && is_remote) {
-+ cache_remote_node(peer->uname);
- }
- }
-
---
-1.8.3.1
-
-
-From 8c048df0312d0d9c857d87b570a352429a710928 Mon Sep 17 00:00:00 2001
-From: Ken Gaillot <kgaillot(a)redhat.com>
-Date: Mon, 7 Jun 2021 11:29:12 -0500
-Subject: [PATCH 05/11] Log: pacemaker-attrd: log peer status changes
-
----
- daemons/attrd/attrd_commands.c | 9 +++++++++
- 1 file changed, 9 insertions(+)
-
-diff --git a/daemons/attrd/attrd_commands.c b/daemons/attrd/attrd_commands.c
-index 5f6a754..d6d179b 100644
---- a/daemons/attrd/attrd_commands.c
-+++ b/daemons/attrd/attrd_commands.c
-@@ -972,6 +972,7 @@ attrd_election_cb(gpointer user_data)
- return FALSE;
- }
-
-+#define state_text(state) ((state)? (const char *)(state) : "in unknown state")
-
- void
- attrd_peer_change_cb(enum crm_status_type kind, crm_node_t *peer, const void *data)
-@@ -981,15 +982,23 @@ attrd_peer_change_cb(enum crm_status_type kind, crm_node_t *peer, const void *da
-
- switch (kind) {
- case crm_status_uname:
-+ crm_debug("%s node %s is now %s",
-+ (is_remote? "Remote" : "Cluster"),
-+ peer->uname, state_text(peer->state));
- break;
-
- case crm_status_processes:
- if (!pcmk_is_set(peer->processes, crm_get_cluster_proc())) {
- gone = true;
- }
-+ crm_debug("Node %s is %s a peer",
-+ peer->uname, (gone? "no longer" : "now"));
- break;
-
- case crm_status_nstate:
-+ crm_debug("%s node %s is now %s (was %s)",
-+ (is_remote? "Remote" : "Cluster"),
-+ peer->uname, state_text(peer->state), state_text(data));
- if (pcmk__str_eq(peer->state, CRM_NODE_MEMBER, pcmk__str_casei)) {
- /* If we're the writer, send new peers a list of all attributes
- * (unless it's a remote node, which doesn't run its own attrd)
---
-1.8.3.1
-
-
-From 1dcc8dee4990cf0dbdec0e14db6d9a3ad67a41d5 Mon Sep 17 00:00:00 2001
-From: Ken Gaillot <kgaillot(a)redhat.com>
-Date: Mon, 7 Jun 2021 11:13:53 -0500
-Subject: [PATCH 06/11] Low: pacemaker-attrd: ensure node ID is only set for
- attributes when known
-
-In most cases, attribute updates contained the node ID, and the node ID was
-used by other code, only if known (i.e. positive). However a couple places did
-not check this, so add that.
-
-I am unsure whether the missing check caused problems in practice, but there
-appears to be the possibility that a remote node would wrongly be added to the
-cluster node cache.
----
- daemons/attrd/attrd_commands.c | 6 ++++--
- 1 file changed, 4 insertions(+), 2 deletions(-)
-
-diff --git a/daemons/attrd/attrd_commands.c b/daemons/attrd/attrd_commands.c
-index d6d179b..b3f441c 100644
---- a/daemons/attrd/attrd_commands.c
-+++ b/daemons/attrd/attrd_commands.c
-@@ -136,7 +136,9 @@ build_attribute_xml(
- crm_xml_add(xml, PCMK__XA_ATTR_UUID, uuid);
- crm_xml_add(xml, PCMK__XA_ATTR_USER, user);
- crm_xml_add(xml, PCMK__XA_ATTR_NODE_NAME, peer);
-- crm_xml_add_int(xml, PCMK__XA_ATTR_NODE_ID, peerid);
-+ if (peerid > 0) {
-+ crm_xml_add_int(xml, PCMK__XA_ATTR_NODE_ID, peerid);
-+ }
- crm_xml_add(xml, PCMK__XA_ATTR_VALUE, value);
- crm_xml_add_int(xml, PCMK__XA_ATTR_DAMPENING, timeout_ms/1000);
- crm_xml_add_int(xml, PCMK__XA_ATTR_IS_PRIVATE, is_private);
-@@ -937,7 +939,7 @@ attrd_peer_update(crm_node_t *peer, xmlNode *xml, const char *host, bool filter)
- /* If this is a cluster node whose node ID we are learning, remember it */
- if ((v->nodeid == 0) && (v->is_remote == FALSE)
- && (crm_element_value_int(xml, PCMK__XA_ATTR_NODE_ID,
-- (int*)&v->nodeid) == 0)) {
-+ (int*)&v->nodeid) == 0) && (v->nodeid > 0)) {
-
- crm_node_t *known_peer = crm_get_peer(v->nodeid, host);
-
---
-1.8.3.1
-
-
-From 8d12490e88b558d01db37a38f7d35175c6d2d69a Mon Sep 17 00:00:00 2001
-From: Ken Gaillot <kgaillot(a)redhat.com>
-Date: Thu, 10 Jun 2021 17:25:57 -0500
-Subject: [PATCH 07/11] Refactor: pacemaker-attrd: functionize processing a
- sync response
-
-... for code isolation, and because we need to add more to it
----
- daemons/attrd/attrd_commands.c | 59 ++++++++++++++++++++++++++++--------------
- 1 file changed, 39 insertions(+), 20 deletions(-)
-
-diff --git a/daemons/attrd/attrd_commands.c b/daemons/attrd/attrd_commands.c
-index b3f441c..d02d3e6 100644
---- a/daemons/attrd/attrd_commands.c
-+++ b/daemons/attrd/attrd_commands.c
-@@ -572,6 +572,43 @@ attrd_peer_clear_failure(crm_node_t *peer, xmlNode *xml)
- }
-
- /*!
-+ * \internal
-+ * \brief Load attributes from a peer sync response
-+ *
-+ * \param[in] peer Peer that sent clear request
-+ * \param[in] peer_won Whether peer is the attribute writer
-+ * \param[in] xml Request XML
-+ */
-+static void
-+process_peer_sync_response(crm_node_t *peer, bool peer_won, xmlNode *xml)
-+{
-+ crm_info("Processing " PCMK__ATTRD_CMD_SYNC_RESPONSE " from %s",
-+ peer->uname);
-+
-+ if (peer_won) {
-+ /* Initialize the "seen" flag for all attributes to cleared, so we can
-+ * detect attributes that local node has but the writer doesn't.
-+ */
-+ clear_attribute_value_seen();
-+ }
-+
-+ // Process each attribute update in the sync response
-+ for (xmlNode *child = pcmk__xml_first_child(xml); child != NULL;
-+ child = pcmk__xml_next(child)) {
-+ attrd_peer_update(peer, child,
-+ crm_element_value(child, PCMK__XA_ATTR_NODE_NAME),
-+ TRUE);
-+ }
-+
-+ if (peer_won) {
-+ /* If any attributes are still not marked as seen, the writer doesn't
-+ * know about them, so send all peers an update with them.
-+ */
-+ attrd_current_only_attribute_update(peer, xml);
-+ }
-+}
-+
-+/*!
- \internal
- \brief Broadcast private attribute for local node with protocol version
- */
-@@ -596,7 +633,7 @@ attrd_peer_message(crm_node_t *peer, xmlNode *xml)
- const char *op = crm_element_value(xml, PCMK__XA_TASK);
- const char *election_op = crm_element_value(xml, F_CRM_TASK);
- const char *host = crm_element_value(xml, PCMK__XA_ATTR_NODE_NAME);
-- bool peer_won = FALSE;
-+ bool peer_won = false;
-
- if (election_op) {
- attrd_handle_election_op(peer, xml);
-@@ -631,25 +668,7 @@ attrd_peer_message(crm_node_t *peer, xmlNode *xml)
-
- } else if (pcmk__str_eq(op, PCMK__ATTRD_CMD_SYNC_RESPONSE, pcmk__str_casei)
- && !pcmk__str_eq(peer->uname, attrd_cluster->uname, pcmk__str_casei)) {
-- xmlNode *child = NULL;
--
-- crm_info("Processing %s from %s", op, peer->uname);
--
-- /* Clear the seen flag for attribute processing held only in the own node. */
-- if (peer_won) {
-- clear_attribute_value_seen();
-- }
--
-- for (child = pcmk__xml_first_child(xml); child != NULL;
-- child = pcmk__xml_next(child)) {
-- host = crm_element_value(child, PCMK__XA_ATTR_NODE_NAME);
-- attrd_peer_update(peer, child, host, TRUE);
-- }
--
-- if (peer_won) {
-- /* Synchronize if there is an attribute held only by own node that Writer does not have. */
-- attrd_current_only_attribute_update(peer, xml);
-- }
-+ process_peer_sync_response(peer, peer_won, xml);
-
- } else if (pcmk__str_eq(op, PCMK__ATTRD_CMD_FLUSH, pcmk__str_casei)) {
- /* Ignore. The flush command was removed in 2.0.0 but may be
---
-1.8.3.1
-
-
-From a890a0e5bbbcabf907f51ed0460868035f72464d Mon Sep 17 00:00:00 2001
-From: Ken Gaillot <kgaillot(a)redhat.com>
-Date: Fri, 11 Jun 2021 14:40:39 -0500
-Subject: [PATCH 08/11] Refactor: pacemaker-attrd: functionize broadcasting
- local override
-
-... for code isolation
----
- daemons/attrd/attrd_commands.c | 42 +++++++++++++++++++++++++++++-------------
- 1 file changed, 29 insertions(+), 13 deletions(-)
-
-diff --git a/daemons/attrd/attrd_commands.c b/daemons/attrd/attrd_commands.c
-index d02d3e6..4783427 100644
---- a/daemons/attrd/attrd_commands.c
-+++ b/daemons/attrd/attrd_commands.c
-@@ -804,6 +804,34 @@ attrd_current_only_attribute_update(crm_node_t *peer, xmlNode *xml)
- free_xml(sync);
- }
-
-+/*!
-+ * \internal
-+ * \brief Override an attribute sync with a local value
-+ *
-+ * Broadcast the local node's value for an attribute that's different from the
-+ * value provided in a peer's attribute synchronization response. This ensures a
-+ * node's values for itself take precedence and all peers are kept in sync.
-+ *
-+ * \param[in] a Attribute entry to override
-+ *
-+ * \return Local instance of attribute value
-+ */
-+static attribute_value_t *
-+broadcast_local_value(attribute_t *a)
-+{
-+ attribute_value_t *v = g_hash_table_lookup(a->values, attrd_cluster->uname);
-+ xmlNode *sync = create_xml_node(NULL, __func__);
-+
-+ crm_xml_add(sync, PCMK__XA_TASK, PCMK__ATTRD_CMD_SYNC_RESPONSE);
-+ build_attribute_xml(sync, a->id, a->set, a->uuid, a->timeout_ms,
-+ a->user, a->is_private, v->nodename, v->nodeid,
-+ v->current, FALSE);
-+ attrd_xml_add_writer(sync);
-+ send_attrd_message(NULL, sync);
-+ free_xml(sync);
-+ return v;
-+}
-+
- void
- attrd_peer_update(crm_node_t *peer, xmlNode *xml, const char *host, bool filter)
- {
-@@ -899,21 +927,9 @@ attrd_peer_update(crm_node_t *peer, xmlNode *xml, const char *host, bool filter)
- if (filter && !pcmk__str_eq(v->current, value, pcmk__str_casei)
- && pcmk__str_eq(host, attrd_cluster->uname, pcmk__str_casei)) {
-
-- xmlNode *sync = create_xml_node(NULL, __func__);
--
- crm_notice("%s[%s]: local value '%s' takes priority over '%s' from %s",
- attr, host, v->current, value, peer->uname);
--
-- crm_xml_add(sync, PCMK__XA_TASK, PCMK__ATTRD_CMD_SYNC_RESPONSE);
-- v = g_hash_table_lookup(a->values, host);
-- build_attribute_xml(sync, attr, a->set, a->uuid, a->timeout_ms, a->user,
-- a->is_private, v->nodename, v->nodeid, v->current, FALSE);
--
-- attrd_xml_add_writer(sync);
--
-- /* Broadcast in case any other nodes had the inconsistent value */
-- send_attrd_message(NULL, sync);
-- free_xml(sync);
-+ v = broadcast_local_value(a);
-
- } else if (!pcmk__str_eq(v->current, value, pcmk__str_casei)) {
- crm_notice("Setting %s[%s]: %s -> %s " CRM_XS " from %s",
---
-1.8.3.1
-
-
-From f6f65e3dab070f1bbdf6d1383f4d6173a8840bc9 Mon Sep 17 00:00:00 2001
-From: Ken Gaillot <kgaillot(a)redhat.com>
-Date: Fri, 11 Jun 2021 14:50:29 -0500
-Subject: [PATCH 09/11] Log: pacemaker-attrd: improve messages when
- broadcasting local-only values
-
-The traces aren't necessary since build_attribute_xml() already logs the same
-info at debug. Also, rename function for clarity, and make static.
----
- daemons/attrd/attrd_commands.c | 35 ++++++++++++++++-------------------
- 1 file changed, 16 insertions(+), 19 deletions(-)
-
-diff --git a/daemons/attrd/attrd_commands.c b/daemons/attrd/attrd_commands.c
-index 4783427..356defb 100644
---- a/daemons/attrd/attrd_commands.c
-+++ b/daemons/attrd/attrd_commands.c
-@@ -51,11 +51,12 @@ GHashTable *attributes = NULL;
-
- void write_attribute(attribute_t *a, bool ignore_delay);
- void write_or_elect_attribute(attribute_t *a);
--void attrd_current_only_attribute_update(crm_node_t *peer, xmlNode *xml);
- void attrd_peer_update(crm_node_t *peer, xmlNode *xml, const char *host, bool filter);
- void attrd_peer_sync(crm_node_t *peer, xmlNode *xml);
- void attrd_peer_remove(const char *host, gboolean uncache, const char *source);
-
-+static void broadcast_unseen_local_values(crm_node_t *peer, xmlNode *xml);
-+
- static gboolean
- send_attrd_message(crm_node_t * node, xmlNode * data)
- {
-@@ -604,7 +605,7 @@ process_peer_sync_response(crm_node_t *peer, bool peer_won, xmlNode *xml)
- /* If any attributes are still not marked as seen, the writer doesn't
- * know about them, so send all peers an update with them.
- */
-- attrd_current_only_attribute_update(peer, xml);
-+ broadcast_unseen_local_values(peer, xml);
- }
- }
-
-@@ -768,40 +769,36 @@ attrd_lookup_or_create_value(GHashTable *values, const char *host, xmlNode *xml)
- return(v);
- }
-
--void
--attrd_current_only_attribute_update(crm_node_t *peer, xmlNode *xml)
-+void
-+broadcast_unseen_local_values(crm_node_t *peer, xmlNode *xml)
- {
- GHashTableIter aIter;
- GHashTableIter vIter;
-- attribute_t *a;
-+ attribute_t *a = NULL;
- attribute_value_t *v = NULL;
-- xmlNode *sync = create_xml_node(NULL, __func__);
-- gboolean build = FALSE;
--
-- crm_xml_add(sync, PCMK__XA_TASK, PCMK__ATTRD_CMD_SYNC_RESPONSE);
-+ xmlNode *sync = NULL;
-
- g_hash_table_iter_init(&aIter, attributes);
- while (g_hash_table_iter_next(&aIter, NULL, (gpointer *) & a)) {
- g_hash_table_iter_init(&vIter, a->values);
- while (g_hash_table_iter_next(&vIter, NULL, (gpointer *) & v)) {
-- if (pcmk__str_eq(v->nodename, attrd_cluster->uname, pcmk__str_casei) && v->seen == FALSE) {
-- crm_trace("Syncing %s[%s] = %s to everyone.(from local only attributes)", a->id, v->nodename, v->current);
--
-- build = TRUE;
-+ if (!(v->seen) && pcmk__str_eq(v->nodename, attrd_cluster->uname,
-+ pcmk__str_casei)) {
-+ if (sync == NULL) {
-+ sync = create_xml_node(NULL, __func__);
-+ crm_xml_add(sync, PCMK__XA_TASK, PCMK__ATTRD_CMD_SYNC_RESPONSE);
-+ }
- build_attribute_xml(sync, a->id, a->set, a->uuid, a->timeout_ms, a->user, a->is_private,
- v->nodename, v->nodeid, v->current, (a->timeout_ms && a->timer ? TRUE : FALSE));
-- } else {
-- crm_trace("Local attribute(%s[%s] = %s) was ignore.(another host) : [%s]", a->id, v->nodename, v->current, attrd_cluster->uname);
-- continue;
- }
- }
- }
-
-- if (build) {
-- crm_debug("Syncing values to everyone.(from local only attributes)");
-+ if (sync != NULL) {
-+ crm_debug("Broadcasting local-only values");
- send_attrd_message(NULL, sync);
-+ free_xml(sync);
- }
-- free_xml(sync);
- }
-
- /*!
---
-1.8.3.1
-
-
-From ab90ffb785ea018556f216b8f540f8c3429a3947 Mon Sep 17 00:00:00 2001
-From: Ken Gaillot <kgaillot(a)redhat.com>
-Date: Fri, 11 Jun 2021 15:04:20 -0500
-Subject: [PATCH 10/11] Refactor: pacemaker-attrd: simplify attribute XML
- creation function
-
-... and rename for clarity
----
- daemons/attrd/attrd_commands.c | 48 ++++++++++++++++++++++++------------------
- 1 file changed, 27 insertions(+), 21 deletions(-)
-
-diff --git a/daemons/attrd/attrd_commands.c b/daemons/attrd/attrd_commands.c
-index 356defb..5b32a77 100644
---- a/daemons/attrd/attrd_commands.c
-+++ b/daemons/attrd/attrd_commands.c
-@@ -125,25 +125,35 @@ cache_remote_node(const char *node_name)
- CRM_ASSERT(crm_remote_peer_get(node_name) != NULL);
- }
-
-+/*!
-+ * \internal
-+ * \brief Create an XML representation of an attribute for use in peer messages
-+ *
-+ * \param[in] parent Create attribute XML as child element of this element
-+ * \param[in] a Attribute to represent
-+ * \param[in] v Attribute value to represent
-+ * \param[in] force_write If true, value should be written even if unchanged
-+ *
-+ * \return XML representation of attribute
-+ */
- static xmlNode *
--build_attribute_xml(
-- xmlNode *parent, const char *name, const char *set, const char *uuid, unsigned int timeout_ms, const char *user,
-- gboolean is_private, const char *peer, uint32_t peerid, const char *value, gboolean is_force_write)
-+add_attribute_value_xml(xmlNode *parent, attribute_t *a, attribute_value_t *v,
-+ bool force_write)
- {
- xmlNode *xml = create_xml_node(parent, __func__);
-
-- crm_xml_add(xml, PCMK__XA_ATTR_NAME, name);
-- crm_xml_add(xml, PCMK__XA_ATTR_SET, set);
-- crm_xml_add(xml, PCMK__XA_ATTR_UUID, uuid);
-- crm_xml_add(xml, PCMK__XA_ATTR_USER, user);
-- crm_xml_add(xml, PCMK__XA_ATTR_NODE_NAME, peer);
-- if (peerid > 0) {
-- crm_xml_add_int(xml, PCMK__XA_ATTR_NODE_ID, peerid);
-+ crm_xml_add(xml, PCMK__XA_ATTR_NAME, a->id);
-+ crm_xml_add(xml, PCMK__XA_ATTR_SET, a->set);
-+ crm_xml_add(xml, PCMK__XA_ATTR_UUID, a->uuid);
-+ crm_xml_add(xml, PCMK__XA_ATTR_USER, a->user);
-+ crm_xml_add(xml, PCMK__XA_ATTR_NODE_NAME, v->nodename);
-+ if (v->nodeid > 0) {
-+ crm_xml_add_int(xml, PCMK__XA_ATTR_NODE_ID, v->nodeid);
- }
-- crm_xml_add(xml, PCMK__XA_ATTR_VALUE, value);
-- crm_xml_add_int(xml, PCMK__XA_ATTR_DAMPENING, timeout_ms/1000);
-- crm_xml_add_int(xml, PCMK__XA_ATTR_IS_PRIVATE, is_private);
-- crm_xml_add_int(xml, PCMK__XA_ATTR_FORCE, is_force_write);
-+ crm_xml_add(xml, PCMK__XA_ATTR_VALUE, v->current);
-+ crm_xml_add_int(xml, PCMK__XA_ATTR_DAMPENING, a->timeout_ms / 1000);
-+ crm_xml_add_int(xml, PCMK__XA_ATTR_IS_PRIVATE, a->is_private);
-+ crm_xml_add_int(xml, PCMK__XA_ATTR_FORCE, force_write);
-
- return xml;
- }
-@@ -695,8 +705,7 @@ attrd_peer_sync(crm_node_t *peer, xmlNode *xml)
- g_hash_table_iter_init(&vIter, a->values);
- while (g_hash_table_iter_next(&vIter, NULL, (gpointer *) & v)) {
- crm_debug("Syncing %s[%s] = %s to %s", a->id, v->nodename, v->current, peer?peer->uname:"everyone");
-- build_attribute_xml(sync, a->id, a->set, a->uuid, a->timeout_ms, a->user, a->is_private,
-- v->nodename, v->nodeid, v->current, FALSE);
-+ add_attribute_value_xml(sync, a, v, false);
- }
- }
-
-@@ -788,8 +797,7 @@ broadcast_unseen_local_values(crm_node_t *peer, xmlNode *xml)
- sync = create_xml_node(NULL, __func__);
- crm_xml_add(sync, PCMK__XA_TASK, PCMK__ATTRD_CMD_SYNC_RESPONSE);
- }
-- build_attribute_xml(sync, a->id, a->set, a->uuid, a->timeout_ms, a->user, a->is_private,
-- v->nodename, v->nodeid, v->current, (a->timeout_ms && a->timer ? TRUE : FALSE));
-+ add_attribute_value_xml(sync, a, v, a->timeout_ms && a->timer);
- }
- }
- }
-@@ -820,9 +828,7 @@ broadcast_local_value(attribute_t *a)
- xmlNode *sync = create_xml_node(NULL, __func__);
-
- crm_xml_add(sync, PCMK__XA_TASK, PCMK__ATTRD_CMD_SYNC_RESPONSE);
-- build_attribute_xml(sync, a->id, a->set, a->uuid, a->timeout_ms,
-- a->user, a->is_private, v->nodename, v->nodeid,
-- v->current, FALSE);
-+ add_attribute_value_xml(sync, a, v, false);
- attrd_xml_add_writer(sync);
- send_attrd_message(NULL, sync);
- free_xml(sync);
---
-1.8.3.1
-
-
-From 540d74130c5c8d9c626d6c50475e4dc4f64234e7 Mon Sep 17 00:00:00 2001
-From: Ken Gaillot <kgaillot(a)redhat.com>
-Date: Fri, 4 Jun 2021 16:34:26 -0500
-Subject: [PATCH 11/11] Fix: pacemaker-attrd: avoid repeated unfencing of
- remote nodes
-
-The attribute manager can't record a remote node's attributes to the CIB until
-it knows the node is remote. Normally, this is learned when the remote node
-starts, because the controller clears the CRM_OP_PROBED attribute and indicates
-that it is for a remote node.
-
-However, if a cluster node is down when a remote node starts, and later comes
-up, it learns the remote node's existing attributes as part of the attribute
-sync. Previously, this did not include whether each value is for a cluster or
-remote node, so the newly joined attribute manager couldn't write out remote
-nodes' attributes until it learned that via some other event -- which might not
-happen before the node becomes DC, in which case its scheduler will not see any
-unfencing-related node attributes and may wrongly schedule unfencing.
-
-The sync response handling already calls attrd_lookup_or_create_value(), which
-checks PCMK__XA_ATTR_IS_REMOTE, so all we need to do is add that to the sync
-response.
----
- daemons/attrd/attrd_commands.c | 6 +++++-
- 1 file changed, 5 insertions(+), 1 deletion(-)
-
-diff --git a/daemons/attrd/attrd_commands.c b/daemons/attrd/attrd_commands.c
-index 5b32a77..0142383 100644
---- a/daemons/attrd/attrd_commands.c
-+++ b/daemons/attrd/attrd_commands.c
-@@ -43,8 +43,9 @@
- * 1 1.1.15 PCMK__ATTRD_CMD_UPDATE_BOTH,
- * PCMK__ATTRD_CMD_UPDATE_DELAY
- * 2 1.1.17 PCMK__ATTRD_CMD_CLEAR_FAILURE
-+ * 3 2.1.1 PCMK__ATTRD_CMD_SYNC_RESPONSE indicates remote nodes
- */
--#define ATTRD_PROTOCOL_VERSION "2"
-+#define ATTRD_PROTOCOL_VERSION "3"
-
- int last_cib_op_done = 0;
- GHashTable *attributes = NULL;
-@@ -150,6 +151,9 @@ add_attribute_value_xml(xmlNode *parent, attribute_t *a, attribute_value_t *v,
- if (v->nodeid > 0) {
- crm_xml_add_int(xml, PCMK__XA_ATTR_NODE_ID, v->nodeid);
- }
-+ if (v->is_remote != 0) {
-+ crm_xml_add_int(xml, PCMK__XA_ATTR_IS_REMOTE, 1);
-+ }
- crm_xml_add(xml, PCMK__XA_ATTR_VALUE, v->current);
- crm_xml_add_int(xml, PCMK__XA_ATTR_DAMPENING, a->timeout_ms / 1000);
- crm_xml_add_int(xml, PCMK__XA_ATTR_IS_PRIVATE, a->is_private);
---
-1.8.3.1
-
diff --git a/pacemaker.spec b/pacemaker.spec
index a2a1dfb..b3f962b 100644
--- a/pacemaker.spec
+++ b/pacemaker.spec
@@ -31,11 +31,11 @@
## Upstream pacemaker version, and its package version (specversion
## can be incremented to build packages reliably considered "newer"
## than previously built packages with the same pcmkversion)
-%global pcmkversion 2.1.0
-%global specversion 6
+%global pcmkversion 2.1.1
+%global specversion 0.1.rc1
## Upstream commit (full commit ID, abbreviated commit ID, or tag) to build
-%global commit 7c3f660707a495a1331716ad32cd3ac9d9f8ff58
+%global commit 9e3a841a33c3125034922ec061f6516d307f20d1
## Since git v2.11, the extent of abbreviation is autoscaled by default
## (used to be constant of 7), so we need to convey it for non-tags, too.
@@ -80,7 +80,7 @@
## Add option to prefix package version with "0."
## (so later "official" packages will be considered updates)
-%bcond_with pre_release
+%bcond_without pre_release
## NOTE: skip --with upstart_job
@@ -201,7 +201,7 @@
Name: pacemaker
Summary: Scalable High-Availability cluster resource manager
Version: %{pcmkversion}
-Release: %{pcmk_release}%{?dist}.1
+Release: %{pcmk_release}%{?dist}
License: GPLv2+ and LGPLv2+
Url: https://www.clusterlabs.org/
@@ -217,13 +217,6 @@ Source0: https://codeload.github.com/%{github_owner}/%{name}/tar.gz/%{arch
Source1: https://codeload.github.com/%{github_owner}/%{nagios_name}/tar.gz/%{nagio...
# upstream commits
-Patch1: 001-ping-agent.patch
-Patch2: 002-pacemakerd-options.patch
-Patch3: 003-pacemakerd-output.patch
-Patch4: 004-check-level.patch
-Patch5: 005-crm_resource.patch
-Patch6: 006-crm_simulate.patch
-Patch7: 007-unfencing-loop.patch
Requires: resource-agents
Requires: %{pkgname_pcmk_libs}%{?_isa} = %{version}-%{release}
@@ -806,6 +799,11 @@ exit 0
%license %{nagios_name}-%{nagios_hash}/COPYING
%changelog
+* Wed Jul 28 2021 Klaus Wenninger <kwenning(a)redhat.com> - 2.1.1-0.1.rc1
+- Update for new upstream tarball for release candidate: Pacemaker-2.1.1-rc1,
+ for full details, see included ChangeLog file or
+ https://github.com/ClusterLabs/pacemaker/releases/tag/Pacemaker-2.1.1-rc1
+
* Thu Jul 22 2021 Fedora Release Engineering <releng(a)fedoraproject.org> - 2.1.0-6.1
- Rebuilt for https://fedoraproject.org/wiki/Fedora_35_Mass_Rebuild
diff --git a/sources b/sources
index 2ab7f32..b933127 100644
--- a/sources
+++ b/sources
@@ -1,2 +1,2 @@
SHA512 (nagios-agents-metadata-105ab8a7b2c16b9a29cf1c1596b80136eeef332b.tar.gz) = 11ddeb48a4929e7642b6dfa9c7962aa1d7a1af1c569830f55ed6cd6773abac13377317327bc1db8411c8077884f83f81cc54d746c834b63a99fa6dc219b5caad
-SHA512 (pacemaker-7c3f66070.tar.gz) = e1c53d019c965395affc1bd78358927617c9bc8572c405ee884737a8213455c5e85102e6f58bb5239ab23ebe5aa794f72c9cc0746900b7a6cc14fd3cf1879064
+SHA512 (pacemaker-9e3a841a3.tar.gz) = 26745ba0d9f6730de13ad106fe57beaff9d49c7df1ec528f0689c2faa872608408fb04401a4d49d73deec5422bd7665496a964b1fcbe69866a5983ac2306b6be
commit 084488a3ae7b0d9112cb80ef1f9b0f7064fbcd72
Author: Fedora Release Engineering <releng(a)fedoraproject.org>
Date: Thu Jul 22 17:33:56 2021 +0000
- Rebuilt for https://fedoraproject.org/wiki/Fedora_35_Mass_Rebuild
Signed-off-by: Fedora Release Engineering <releng(a)fedoraproject.org>
diff --git a/pacemaker.spec b/pacemaker.spec
index 48b6a44..a2a1dfb 100644
--- a/pacemaker.spec
+++ b/pacemaker.spec
@@ -201,7 +201,7 @@
Name: pacemaker
Summary: Scalable High-Availability cluster resource manager
Version: %{pcmkversion}
-Release: %{pcmk_release}%{?dist}
+Release: %{pcmk_release}%{?dist}.1
License: GPLv2+ and LGPLv2+
Url: https://www.clusterlabs.org/
@@ -806,6 +806,9 @@ exit 0
%license %{nagios_name}-%{nagios_hash}/COPYING
%changelog
+* Thu Jul 22 2021 Fedora Release Engineering <releng(a)fedoraproject.org> - 2.1.0-6.1
+- Rebuilt for https://fedoraproject.org/wiki/Fedora_35_Mass_Rebuild
+
* Mon Jul 5 2021 Klaus Wenninger <kwenning(a)redhat.com> - 2.1.0-6
- synced/merged with CS9 spec-file for current 2.1.0-release build
commit 15ac1a3c342218687bbddb1cdb2fc0edc968d5ac
Author: Klaus Wenninger <klaus.wenninger(a)aon.at>
Date: Mon Jul 5 18:38:43 2021 +0200
* Mon Jul 5 2021 Klaus Wenninger <kwenning(a)redhat.com> - 2.1.0-6
- synced/merged with CS9 spec-file for current 2.1.0-release build
* Tue Jun 8 2021 Klaus Wenninger <kwenning(a)redhat.com> - 2.1.0-0.5.rc3
- silence f33 s390x build complaining about possible format-trucation
diff --git a/001-ping-agent.patch b/001-ping-agent.patch
new file mode 100644
index 0000000..89fe41a
--- /dev/null
+++ b/001-ping-agent.patch
@@ -0,0 +1,225 @@
+From c6ee0973522268ed7b3241cf0ec2e06398444114 Mon Sep 17 00:00:00 2001
+From: Grace Chin <gchin(a)redhat.com>
+Date: Tue, 4 May 2021 12:02:17 -0400
+Subject: [PATCH 1/4] Remove deprecated attrd_options
+
+---
+ extra/resources/ping | 11 +++--------
+ 1 file changed, 3 insertions(+), 8 deletions(-)
+
+diff --git a/extra/resources/ping b/extra/resources/ping
+index 3cf8dfe..2e93f22 100755
+--- a/extra/resources/ping
++++ b/extra/resources/ping
+@@ -178,7 +178,7 @@ ping_stop() {
+
+ rm -f "${OCF_RESKEY_pidfile}"
+
+- attrd_updater -D -n "$OCF_RESKEY_name" -d "$OCF_RESKEY_dampen" $attrd_options
++ attrd_updater -D -n "$OCF_RESKEY_name" -d "$OCF_RESKEY_dampen"
+
+ return $OCF_SUCCESS
+ }
+@@ -302,9 +302,9 @@ ping_update() {
+
+ score=$(expr $active \* $OCF_RESKEY_multiplier)
+ if [ "$__OCF_ACTION" = "start" ] ; then
+- attrd_updater -n "$OCF_RESKEY_name" -B "$score" -d "$OCF_RESKEY_dampen" $attrd_options
++ attrd_updater -n "$OCF_RESKEY_name" -B "$score" -d "$OCF_RESKEY_dampen"
+ else
+- attrd_updater -n "$OCF_RESKEY_name" -v "$score" -d "$OCF_RESKEY_dampen" $attrd_options
++ attrd_updater -n "$OCF_RESKEY_name" -v "$score" -d "$OCF_RESKEY_dampen"
+ fi
+ rc=$?
+ case $rc in
+@@ -396,11 +396,6 @@ case "${OCF_RESKEY_debug}" in
+ ;;
+ esac
+
+-attrd_options='-q'
+-if [ "${OCF_RESKEY_debug}" = "true" ]; then
+- attrd_options=''
+-fi
+-
+ case "$__OCF_ACTION" in
+ meta-data) meta_data
+ exit $OCF_SUCCESS
+--
+1.8.3.1
+
+
+From 6d6c4691cf0970059689856c354daf9e098b4451 Mon Sep 17 00:00:00 2001
+From: Grace Chin <gchin(a)redhat.com>
+Date: Tue, 4 May 2021 14:50:37 -0400
+Subject: [PATCH 2/4] Replace debug values, true and false, with 0 and 1
+
+---
+ extra/resources/ping | 8 ++++----
+ 1 file changed, 4 insertions(+), 4 deletions(-)
+
+diff --git a/extra/resources/ping b/extra/resources/ping
+index 2e93f22..fee019b 100755
+--- a/extra/resources/ping
++++ b/extra/resources/ping
+@@ -24,7 +24,7 @@
+ : ${OCF_RESKEY_dampen:="5s"}
+ : ${OCF_RESKEY_attempts:="3"}
+ : ${OCF_RESKEY_multiplier:="1"}
+-: ${OCF_RESKEY_debug:="false"}
++: ${OCF_RESKEY_debug:="0"}
+ : ${OCF_RESKEY_failure_score:="0"}
+ : ${OCF_RESKEY_use_fping:="1"}
+ : ${OCF_RESKEY_host_list:=""}
+@@ -152,7 +152,7 @@ END
+
+ ping_conditional_log() {
+ level="$1"; shift
+- if [ "${OCF_RESKEY_debug}" = "true" ]; then
++ if [ $OCF_RESKEY_debug -gt 0 ]; then
+ ocf_log "$level" "$*"
+ fi
+ }
+@@ -388,8 +388,8 @@ fi
+
+ # Check the debug option
+ case "${OCF_RESKEY_debug}" in
+- true|True|TRUE|1) OCF_RESKEY_debug=true;;
+- false|False|FALSE|0) OCF_RESKEY_debug=false;;
++ true|True|TRUE|1) OCF_RESKEY_debug=0;;
++ false|False|FALSE|0) OCF_RESKEY_debug=1;;
+ *)
+ ocf_log warn "Value for 'debug' is incorrect. Please specify 'true' or 'false' not: ${OCF_RESKEY_debug}"
+ OCF_RESKEY_debug=false
+--
+1.8.3.1
+
+
+From a886a31056b6aca764c6911f5432af2c5ebf51df Mon Sep 17 00:00:00 2001
+From: Grace Chin <gchin(a)redhat.com>
+Date: Tue, 11 May 2021 11:04:50 -0400
+Subject: [PATCH 3/4] Add verbose debug mode which logs ping and fping output
+ when set
+
+---
+ extra/resources/ping | 19 ++++++++++++++-----
+ 1 file changed, 14 insertions(+), 5 deletions(-)
+
+diff --git a/extra/resources/ping b/extra/resources/ping
+index fee019b..cc796af 100755
+--- a/extra/resources/ping
++++ b/extra/resources/ping
+@@ -249,10 +249,13 @@ fping_check() {
+
+ case $rc in
+ 0)
++ if [ $OCF_RESKEY_debug -gt 1 ]; then
++ ping_conditional_log info "$output"
++ fi
+ ;;
+ 1)
+ for h in $(echo "$output" | grep "is unreachable" | awk '{print $1}'); do
+- ping_conditional_log warn "$h is inactive"
++ ping_conditional_log warn "$h is inactive: $output"
+ done
+ ;;
+ *)
+@@ -282,7 +285,12 @@ ping_check() {
+ p_out=$($p_exe $p_args $OCF_RESKEY_options $host 2>&1); rc=$?
+
+ case $rc in
+- 0) active=$(expr $active + 1);;
++ 0)
++ active=$(expr $active + 1)
++ if [ $OCF_RESKEY_debug -gt 1 ]; then
++ ping_conditional_log info "$p_out"
++ fi
++ ;;
+ 1) ping_conditional_log warn "$host is inactive: $p_out";;
+ *) ocf_log err "Unexpected result for '$p_exe $p_args $OCF_RESKEY_options $host' $rc: $p_out";;
+ esac
+@@ -388,10 +396,11 @@ fi
+
+ # Check the debug option
+ case "${OCF_RESKEY_debug}" in
+- true|True|TRUE|1) OCF_RESKEY_debug=0;;
+- false|False|FALSE|0) OCF_RESKEY_debug=1;;
++ true|True|TRUE|1) OCF_RESKEY_debug=1;;
++ false|False|FALSE|0) OCF_RESKEY_debug=0;;
++ verbose|Verbose|VERBOSE|2) OCF_RESKEY_debug=2;;
+ *)
+- ocf_log warn "Value for 'debug' is incorrect. Please specify 'true' or 'false' not: ${OCF_RESKEY_debug}"
++ ocf_log warn "Value for 'debug' is incorrect. Please specify 'true', 'false', or 'verbose', not: ${OCF_RESKEY_debug}"
+ OCF_RESKEY_debug=false
+ ;;
+ esac
+--
+1.8.3.1
+
+
+From 460043f133ced80e923b1290af70502a72deb7f8 Mon Sep 17 00:00:00 2001
+From: Grace Chin <gchin(a)redhat.com>
+Date: Tue, 11 May 2021 11:07:05 -0400
+Subject: [PATCH 4/4] Improve variable names
+
+---
+ extra/resources/ping | 20 ++++++++++----------
+ 1 file changed, 10 insertions(+), 10 deletions(-)
+
+diff --git a/extra/resources/ping b/extra/resources/ping
+index cc796af..9763b60 100755
+--- a/extra/resources/ping
++++ b/extra/resources/ping
+@@ -244,22 +244,22 @@ fping_check() {
+ timeout=$(expr $OCF_RESKEY_timeout \* 1000 / $OCF_RESKEY_attempts)
+
+ cmd="$p_exe -r $OCF_RESKEY_attempts -t $timeout -B 1.0 $OCF_RESKEY_options $OCF_RESKEY_host_list"
+- output=$($cmd 2>&1); rc=$?
+- active=$(echo "$output" | grep "is alive" | wc -l)
++ fping_output=$($cmd 2>&1); rc=$?
++ active=$(echo "$fping_output" | grep "is alive" | wc -l)
+
+ case $rc in
+ 0)
+ if [ $OCF_RESKEY_debug -gt 1 ]; then
+- ping_conditional_log info "$output"
++ ping_conditional_log info "$fping_output"
+ fi
+ ;;
+ 1)
+- for h in $(echo "$output" | grep "is unreachable" | awk '{print $1}'); do
+- ping_conditional_log warn "$h is inactive: $output"
++ for h in $(echo "$fping_output" | grep "is unreachable" | awk '{print $1}'); do
++ ping_conditional_log warn "$h is inactive: $fping_output"
+ done
+ ;;
+ *)
+- ocf_log err "Unexpected result for '$cmd' $rc: $(echo "$output" | tr '\n' ';')"
++ ocf_log err "Unexpected result for '$cmd' $rc: $(echo "$fping_output" | tr '\n' ';')"
+ ;;
+ esac
+
+@@ -282,17 +282,17 @@ ping_check() {
+ *:*) p_exe=ping6
+ esac
+
+- p_out=$($p_exe $p_args $OCF_RESKEY_options $host 2>&1); rc=$?
++ ping_output=$($p_exe $p_args $OCF_RESKEY_options $host 2>&1); rc=$?
+
+ case $rc in
+ 0)
+ active=$(expr $active + 1)
+ if [ $OCF_RESKEY_debug -gt 1 ]; then
+- ping_conditional_log info "$p_out"
++ ping_conditional_log info "$ping_output"
+ fi
+ ;;
+- 1) ping_conditional_log warn "$host is inactive: $p_out";;
+- *) ocf_log err "Unexpected result for '$p_exe $p_args $OCF_RESKEY_options $host' $rc: $p_out";;
++ 1) ping_conditional_log warn "$host is inactive: $ping_output";;
++ *) ocf_log err "Unexpected result for '$p_exe $p_args $OCF_RESKEY_options $host' $rc: $ping_output";;
+ esac
+ done
+ return $active
+--
+1.8.3.1
+
diff --git a/002-pacemakerd-options.patch b/002-pacemakerd-options.patch
new file mode 100644
index 0000000..56941ec
--- /dev/null
+++ b/002-pacemakerd-options.patch
@@ -0,0 +1,451 @@
+From 0d40ebf10b1794ece2c5c9768ea7222d3834d3b3 Mon Sep 17 00:00:00 2001
+From: Chris Lumens <clumens(a)redhat.com>
+Date: Thu, 13 May 2021 11:42:18 -0400
+Subject: [PATCH 1/4] Build: Use a different variable to find man page
+ includes.
+
+With other programs outside of the tools directory being converted to
+use glib for command line handling, their includes are not going to be
+in tools/. So we need to use a different autoconf variable to find
+them.
+---
+ mk/common.mk | 6 +++---
+ 1 file changed, 3 insertions(+), 3 deletions(-)
+
+diff --git a/mk/common.mk b/mk/common.mk
+index b247670..aa59feb 100644
+--- a/mk/common.mk
++++ b/mk/common.mk
+@@ -1,5 +1,5 @@
+ #
+-# Copyright 2014-2020 the Pacemaker project contributors
++# Copyright 2014-2021 the Pacemaker project contributors
+ #
+ # The version control history for this file may have further details.
+ #
+@@ -68,11 +68,11 @@ HELP2MAN_ARGS = -N --section 8 --name "Part of the Pacemaker cluster resource ma
+ # and all wrappers to C code.
+ %.8: % $(MAN8DEPS)
+ $(AM_V_at)chmod a+x $(abs_builddir)/$<
+- $(AM_V_MAN)if [ -f $(top_srcdir)/tools/$@.inc ]; then \
++ $(AM_V_MAN)if [ -f $(abs_srcdir)/$@.inc ]; then \
+ PATH=$(abs_builddir):$$PATH $(HELP2MAN) $(HELP2MAN_ARGS) \
+ -h --help-all \
+ --no-discard-stderr \
+- -i $(top_srcdir)/tools/$@.inc $(abs_builddir)/$< \
++ -i $(abs_srcdir)/$@.inc $(abs_builddir)/$< \
+ | sed -f $(top_srcdir)/tools/fix-manpages > $@ ; \
+ else \
+ PATH=$(abs_builddir):$$PATH $(HELP2MAN) $(HELP2MAN_ARGS) \
+--
+1.8.3.1
+
+
+From c7ab1d901bcbbf0137277e783e072777ca2f82d9 Mon Sep 17 00:00:00 2001
+From: Chris Lumens <clumens(a)redhat.com>
+Date: Thu, 13 May 2021 11:44:16 -0400
+Subject: [PATCH 2/4] Refactor: daemons: Remove the pid_file variable from
+ pacemakerd.
+
+It's never used anywhere.
+---
+ daemons/pacemakerd/pacemakerd.c | 3 ---
+ 1 file changed, 3 deletions(-)
+
+diff --git a/daemons/pacemakerd/pacemakerd.c b/daemons/pacemakerd/pacemakerd.c
+index 8ec9708..03d688e 100644
+--- a/daemons/pacemakerd/pacemakerd.c
++++ b/daemons/pacemakerd/pacemakerd.c
+@@ -27,8 +27,7 @@
+
+ static crm_trigger_t *shutdown_trigger = NULL;
+ static crm_trigger_t *startup_trigger = NULL;
+-static const char *pid_file = PCMK_RUN_DIR "/pacemaker.pid";
+
+ /* state we report when asked via pacemakerd-api status-ping */
+ static const char *pacemakerd_state = XML_PING_ATTR_PACEMAKERDSTATE_INIT;
+ static gboolean running_with_sbd = FALSE; /* local copy */
+@@ -224,7 +222,6 @@ main(int argc, char **argv)
+ /* Legacy */
+ break;
+ case 'p':
+- pid_file = optarg;
+ break;
+ case 's':
+ pcmk__set_env_option("node_start_state", "standby");
+--
+1.8.3.1
+
+
+From 98990eed9f6a5dbde7c8a5aa0783e93d5479295b Mon Sep 17 00:00:00 2001
+From: Chris Lumens <clumens(a)redhat.com>
+Date: Thu, 13 May 2021 13:14:38 -0400
+Subject: [PATCH 3/4] Refactor: daemons: Use glib for command line handling in
+ pacemakerd.
+
+---
+ daemons/pacemakerd/Makefile.am | 2 +
+ daemons/pacemakerd/pacemakerd.8.inc | 5 +
+ daemons/pacemakerd/pacemakerd.c | 195 ++++++++++++++++++------------------
+ 3 files changed, 102 insertions(+), 100 deletions(-)
+ create mode 100644 daemons/pacemakerd/pacemakerd.8.inc
+
+diff --git a/daemons/pacemakerd/Makefile.am b/daemons/pacemakerd/Makefile.am
+index cc657f5..84517a3 100644
+--- a/daemons/pacemakerd/Makefile.am
++++ b/daemons/pacemakerd/Makefile.am
+@@ -15,6 +15,8 @@ if BUILD_SYSTEMD
+ systemdsystemunit_DATA = pacemaker.service
+ endif
+
++EXTRA_DIST = pacemakerd.8.inc
++
+ ## SOURCES
+
+ noinst_HEADERS = pacemakerd.h
+diff --git a/daemons/pacemakerd/pacemakerd.8.inc b/daemons/pacemakerd/pacemakerd.8.inc
+new file mode 100644
+index 0000000..902af4e
+--- /dev/null
++++ b/daemons/pacemakerd/pacemakerd.8.inc
+@@ -0,0 +1,5 @@
++[synopsis]
++pacemakerd [options]
++
++/subsidiary Pacemaker daemons/
++.SH OPTIONS
+diff --git a/daemons/pacemakerd/pacemakerd.c b/daemons/pacemakerd/pacemakerd.c
+index 03d688e..ce194bf 100644
+--- a/daemons/pacemakerd/pacemakerd.c
++++ b/daemons/pacemakerd/pacemakerd.c
+@@ -23,12 +23,54 @@
+ #include <crm/msg_xml.h>
+ #include <crm/common/ipc_internal.h>
+ #include <crm/common/mainloop.h>
++#include <crm/common/cmdline_internal.h>
+ #include <crm/cluster/internal.h>
+ #include <crm/cluster.h>
+
+ #include <dirent.h>
+ #include <ctype.h>
+
++#define SUMMARY "pacemakerd - primary Pacemaker daemon that launches and monitors all subsidiary Pacemaker daemons"
++
++struct {
++ gboolean features;
++ gboolean foreground;
++ gboolean shutdown;
++ gboolean standby;
++} options;
++
++static gboolean
++pid_cb(const gchar *option_name, const gchar *optarg, gpointer data, GError **err) {
++ return TRUE;
++}
++
++static gboolean
++standby_cb(const gchar *option_name, const gchar *optarg, gpointer data, GError **err) {
++ options.standby = TRUE;
++ pcmk__set_env_option("node_start_state", "standby");
++ return TRUE;
++}
++
++static GOptionEntry entries[] = {
++ { "features", 'F', 0, G_OPTION_ARG_NONE, &options.features,
++ "Display full version and list of features Pacemaker was built with",
++ NULL },
++ { "foreground", 'f', 0, G_OPTION_ARG_NONE, &options.foreground,
++ "(Ignored) Pacemaker always runs in the foreground",
++ NULL },
++ { "pid-file", 'p', 0, G_OPTION_ARG_CALLBACK, pid_cb,
++ "(Ignored) Daemon pid file location",
++ "FILE" },
++ { "shutdown", 'S', 0, G_OPTION_ARG_NONE, &options.shutdown,
++ "Instruct Pacemaker to shutdown on this machine",
++ NULL },
++ { "standby", 's', G_OPTION_FLAG_NO_ARG, G_OPTION_ARG_CALLBACK, standby_cb,
++ "Start node in standby state",
++ NULL },
++
++ { NULL }
++};
++
+ static gboolean fatal_error = FALSE;
+ static GMainLoop *mainloop = NULL;
+ static bool global_keep_tracking = false;
+@@ -642,49 +685,6 @@ pcmk_sigquit(int nsig)
+ .connection_destroyed = pcmk_ipc_destroy
+ };
+
+-static pcmk__cli_option_t long_options[] = {
+- // long option, argument type, storage, short option, description, flags
+- {
+- "help", no_argument, NULL, '?',
+- "\tThis text", pcmk__option_default
+- },
+- {
+- "version", no_argument, NULL, '$',
+- "\tVersion information", pcmk__option_default
+- },
+- {
+- "verbose", no_argument, NULL, 'V',
+- "\tIncrease debug output", pcmk__option_default
+- },
+- {
+- "shutdown", no_argument, NULL, 'S',
+- "\tInstruct Pacemaker to shutdown on this machine", pcmk__option_default
+- },
+- {
+- "features", no_argument, NULL, 'F',
+- "\tDisplay full version and list of features Pacemaker was built with",
+- pcmk__option_default
+- },
+- {
+- "-spacer-", no_argument, NULL, '-',
+- "\nAdditional Options:", pcmk__option_default
+- },
+- {
+- "foreground", no_argument, NULL, 'f',
+- "\t(Ignored) Pacemaker always runs in the foreground",
+- pcmk__option_default
+- },
+- {
+- "pid-file", required_argument, NULL, 'p',
+- "\t(Ignored) Daemon pid file location", pcmk__option_default
+- },
+- {
+- "standby", no_argument, NULL, 's',
+- "\tStart node in standby state", pcmk__option_default
+- },
+- { 0, 0, 0, 0 }
+-};
+-
+ static void
+ mcp_chown(const char *path, uid_t uid, gid_t gid)
+ {
+@@ -1168,83 +1211,66 @@ request_shutdown(crm_ipc_t *ipc)
+ return status;
+ }
+
++static GOptionContext *
++build_arg_context(pcmk__common_args_t *args) {
++ GOptionContext *context = NULL;
++
++ context = pcmk__build_arg_context(args, NULL, NULL, NULL);
++ pcmk__add_main_args(context, entries);
++ return context;
++}
++
+ int
+ main(int argc, char **argv)
+ {
+- int flag;
+- int argerr = 0;
++ crm_exit_t exit_code = CRM_EX_OK;
++
++ GError *error = NULL;
++
++ pcmk__common_args_t *args = pcmk__new_common_args(SUMMARY);
++ gchar **processed_args = pcmk__cmdline_preproc(argv, "p");
++ GOptionContext *context = build_arg_context(args);
+
+- int option_index = 0;
+ bool old_instance_connected = false;
+- gboolean shutdown = FALSE;
+
+ crm_ipc_t *old_instance = NULL;
+ qb_ipcs_service_t *ipcs = NULL;
+
+ crm_log_preinit(NULL, argc, argv);
+- pcmk__set_cli_options(NULL, "[options]", long_options,
+- "primary Pacemaker daemon that launches and "
+- "monitors all subsidiary Pacemaker daemons");
+ mainloop_add_signal(SIGHUP, pcmk_ignore);
+ mainloop_add_signal(SIGQUIT, pcmk_sigquit);
+
+- while (1) {
+- flag = pcmk__next_cli_option(argc, argv, &option_index, NULL);
+- if (flag == -1)
+- break;
+-
+- switch (flag) {
+- case 'V':
+- crm_bump_log_level(argc, argv);
+- break;
+- case 'f':
+- /* Legacy */
+- break;
+- case 'p':
+- break;
+- case 's':
+- pcmk__set_env_option("node_start_state", "standby");
+- break;
+- case '$':
+- case '?':
+- pcmk__cli_help(flag, CRM_EX_OK);
+- break;
+- case 'S':
+- shutdown = TRUE;
+- break;
+- case 'F':
+- printf("Pacemaker %s (Build: %s)\n Supporting v%s: %s\n", PACEMAKER_VERSION, BUILD_VERSION,
+- CRM_FEATURE_SET, CRM_FEATURES);
+- crm_exit(CRM_EX_OK);
+- default:
+- printf("Argument code 0%o (%c) is not (?yet?) supported\n", flag, flag);
+- ++argerr;
+- break;
+- }
++ if (!g_option_context_parse_strv(context, &processed_args, &error)) {
++ exit_code = CRM_EX_USAGE;
++ goto done;
+ }
+
+- if (optind < argc) {
+- printf("non-option ARGV-elements: ");
+- while (optind < argc)
+- printf("%s ", argv[optind++]);
+- printf("\n");
+- }
+- if (argerr) {
+- pcmk__cli_help('?', CRM_EX_USAGE);
++ if (options.features) {
++ printf("Pacemaker %s (Build: %s)\n Supporting v%s: %s\n", PACEMAKER_VERSION, BUILD_VERSION,
++ CRM_FEATURE_SET, CRM_FEATURES);
++ exit_code = CRM_EX_OK;
++ goto done;
+ }
+
++ if (args->version) {
++ g_strfreev(processed_args);
++ pcmk__free_arg_context(context);
++ /* FIXME: When pacemakerd is converted to use formatted output, this can go. */
++ pcmk__cli_help('v', CRM_EX_USAGE);
++ }
+
+ setenv("LC_ALL", "C", 1);
+
+ pcmk__set_env_option("mcp", "true");
+
++ pcmk__cli_init_logging("pacemakerd", args->verbosity);
+ crm_log_init(NULL, LOG_INFO, TRUE, FALSE, argc, argv, FALSE);
+
+ crm_debug("Checking for existing Pacemaker instance");
+ old_instance = crm_ipc_new(CRM_SYSTEM_MCP, 0);
+ old_instance_connected = crm_ipc_connect(old_instance);
+
+- if (shutdown) {
++ if (options.shutdown) {
+ if (old_instance_connected) {
+ crm_exit(request_shutdown(old_instance));
+ } else {
+@@ -1253,22 +1279,25 @@ main(int argc, char **argv)
+ "Pacemaker instance: %s", strerror(errno));
+ crm_ipc_close(old_instance);
+ crm_ipc_destroy(old_instance);
+- crm_exit(CRM_EX_DISCONNECT);
++ exit_code = CRM_EX_DISCONNECT;
++ goto done;
+ }
+
+ } else if (old_instance_connected) {
+ crm_ipc_close(old_instance);
+ crm_ipc_destroy(old_instance);
+ crm_err("Aborting start-up because active Pacemaker instance found");
+- crm_exit(CRM_EX_FATAL);
++ exit_code = CRM_EX_FATAL;
++ goto done;
+ }
+
+ crm_ipc_close(old_instance);
+ crm_ipc_destroy(old_instance);
+
+ #ifdef SUPPORT_COROSYNC
+ if (mcp_read_config() == FALSE) {
+- crm_exit(CRM_EX_UNAVAILABLE);
++ exit_code = CRM_EX_UNAVAILABLE;
++ goto done;
+ }
+ #endif
+
+@@ -1292,7 +1321,8 @@ main(int argc, char **argv)
+ #ifdef SUPPORT_COROSYNC
+ /* Allows us to block shutdown */
+ if (!cluster_connect_cfg()) {
+- crm_exit(CRM_EX_PROTOCOL);
++ exit_code = CRM_EX_PROTOCOL;
++ goto done;
+ }
+ #endif
+
+@@ -1307,9 +1337,11 @@ main(int argc, char **argv)
+ case pcmk_rc_ok:
+ break;
+ case pcmk_rc_ipc_unauthorized:
+- crm_exit(CRM_EX_CANTCREAT);
++ exit_code = CRM_EX_CANTCREAT;
++ goto done;
+ default:
+- crm_exit(CRM_EX_FATAL);
++ exit_code = CRM_EX_FATAL;
++ goto done;
+ };
+
+ mainloop_add_signal(SIGTERM, pcmk_shutdown);
+@@ -1342,5 +1374,11 @@ main(int argc, char **argv)
+ #ifdef SUPPORT_COROSYNC
+ cluster_disconnect_cfg();
+ #endif
+- crm_exit(CRM_EX_OK);
++
++done:
++ g_strfreev(processed_args);
++ pcmk__free_arg_context(context);
++
++ pcmk__output_and_clear_error(error, NULL);
++ crm_exit(exit_code);
+ }
+--
+1.8.3.1
+
+
+From 8f7924fbb2a012bedcad59335b7bebc5020b26e3 Mon Sep 17 00:00:00 2001
+From: Chris Lumens <clumens(a)redhat.com>
+Date: Thu, 13 May 2021 13:27:13 -0400
+Subject: [PATCH 4/4] Low: pacemaker.service: Don't start pacemakerd with -f.
+
+This option is completely ignored by pacemakerd.
+---
+ daemons/pacemakerd/pacemaker.service.in | 2 +-
+ doc/sphinx/Clusters_from_Scratch/verification.rst | 2 +-
+ 2 files changed, 2 insertions(+), 2 deletions(-)
+
+diff --git a/daemons/pacemakerd/pacemaker.service.in b/daemons/pacemakerd/pacemaker.service.in
+index b128ddc..0363a22 100644
+--- a/daemons/pacemakerd/pacemaker.service.in
++++ b/daemons/pacemakerd/pacemaker.service.in
+@@ -44,7 +44,7 @@ EnvironmentFile=-@CONFIGDIR@/pacemaker
+ EnvironmentFile=-@CONFIGDIR@/sbd
+ SuccessExitStatus=100
+
+-ExecStart=@sbindir@/pacemakerd -f
++ExecStart=@sbindir@/pacemakerd
+
+ # Systemd v227 and above can limit the number of processes spawned by a
+ # service. That is a bad idea for an HA cluster resource manager, so disable it
+diff --git a/doc/sphinx/Clusters_from_Scratch/verification.rst b/doc/sphinx/Clusters_from_Scratch/verification.rst
+index 9d647f8..b7fa20e 100644
+--- a/doc/sphinx/Clusters_from_Scratch/verification.rst
++++ b/doc/sphinx/Clusters_from_Scratch/verification.rst
+@@ -103,7 +103,7 @@ the necessary processes are running:
+ 2 ? S 0:00 [kthreadd]
+ ...lots of processes...
+ 17121 ? SLsl 0:01 /usr/sbin/corosync -f
+- 17133 ? Ss 0:00 /usr/sbin/pacemakerd -f
++ 17133 ? Ss 0:00 /usr/sbin/pacemakerd
+ 17134 ? Ss 0:00 \_ /usr/libexec/pacemaker/pacemaker-based
+ 17135 ? Ss 0:00 \_ /usr/libexec/pacemaker/pacemaker-fenced
+ 17136 ? Ss 0:00 \_ /usr/libexec/pacemaker/pacemaker-execd
+--
+1.8.3.1
+
diff --git a/003-pacemakerd-output.patch b/003-pacemakerd-output.patch
new file mode 100644
index 0000000..167e22b
--- /dev/null
+++ b/003-pacemakerd-output.patch
@@ -0,0 +1,343 @@
+From 7c35387a9896cb968cf4087b5cbed94af44e1ea5 Mon Sep 17 00:00:00 2001
+From: Chris Lumens <clumens(a)redhat.com>
+Date: Fri, 14 May 2021 12:03:46 -0400
+Subject: [PATCH 1/5] Feature: daemons: Convert pacemakerd to formatted output.
+
+The main purpose of this is to finish getting pacemakerd moved off the
+existing command line handling code (pcmk__cli_help in particular) so
+that code can eventually be deprecated or removed. pacemakerd itself
+does fairly little printing.
+---
+ daemons/pacemakerd/pacemakerd.c | 58 ++++++++++++++++++++++++++++++-----------
+ 1 file changed, 43 insertions(+), 15 deletions(-)
+
+diff --git a/daemons/pacemakerd/pacemakerd.c b/daemons/pacemakerd/pacemakerd.c
+index ce194bf..bd59729 100644
+--- a/daemons/pacemakerd/pacemakerd.c
++++ b/daemons/pacemakerd/pacemakerd.c
+@@ -25,6 +25,7 @@
+ #include <crm/common/ipc_internal.h>
+ #include <crm/common/mainloop.h>
+ #include <crm/common/cmdline_internal.h>
++#include <crm/common/output_internal.h>
+ #include <crm/cluster/internal.h>
+ #include <crm/cluster.h>
+
+@@ -37,6 +38,14 @@ struct {
+ gboolean standby;
+ } options;
+
++static pcmk__output_t *out = NULL;
++
++static pcmk__supported_format_t formats[] = {
++ PCMK__SUPPORTED_FORMAT_NONE,
++ PCMK__SUPPORTED_FORMAT_TEXT,
++ { NULL, NULL, NULL }
++};
++
+ static gboolean
+ pid_cb(const gchar *option_name, const gchar *optarg, gpointer data, GError **err) {
+ return TRUE;
+@@ -1167,10 +1176,10 @@ pacemakerd_event_cb(pcmk_ipc_api_t *pacemakerd_api,
+ }
+
+ static GOptionContext *
+-build_arg_context(pcmk__common_args_t *args) {
++build_arg_context(pcmk__common_args_t *args, GOptionGroup **group) {
+ GOptionContext *context = NULL;
+
+- context = pcmk__build_arg_context(args, NULL, NULL, NULL);
++ context = pcmk__build_arg_context(args, "text", group, NULL);
+ pcmk__add_main_args(context, entries);
+ return context;
+ }
+@@ -1182,9 +1191,11 @@ main(int argc, char **argv)
+
+ GError *error = NULL;
+
++ int rc = pcmk_rc_ok;
++ GOptionGroup *output_group = NULL;
+ pcmk__common_args_t *args = pcmk__new_common_args(SUMMARY);
+ gchar **processed_args = pcmk__cmdline_preproc(argv, "p");
+- GOptionContext *context = build_arg_context(args);
++ GOptionContext *context = build_arg_context(args, &output_group);
+
+ bool old_instance_connected = false;
+
+@@ -1195,23 +1205,30 @@ main(int argc, char **argv)
+ mainloop_add_signal(SIGHUP, pcmk_ignore);
+ mainloop_add_signal(SIGQUIT, pcmk_sigquit);
+
++ pcmk__register_formats(output_group, formats);
+ if (!g_option_context_parse_strv(context, &processed_args, &error)) {
+ exit_code = CRM_EX_USAGE;
+ goto done;
+ }
+
++ rc = pcmk__output_new(&out, args->output_ty, args->output_dest, argv);
++ if (rc != pcmk_rc_ok) {
++ exit_code = CRM_EX_ERROR;
++ g_set_error(&error, PCMK__EXITC_ERROR, exit_code, "Error creating output format %s: %s",
++ args->output_ty, pcmk_rc_str(rc));
++ goto done;
++ }
++
+ if (options.features) {
+- printf("Pacemaker %s (Build: %s)\n Supporting v%s: %s\n", PACEMAKER_VERSION, BUILD_VERSION,
+- CRM_FEATURE_SET, CRM_FEATURES);
++ out->info(out, "Pacemaker %s (Build: %s)\n Supporting v%s: %s", PACEMAKER_VERSION,
++ BUILD_VERSION, CRM_FEATURE_SET, CRM_FEATURES);
+ exit_code = CRM_EX_OK;
+ goto done;
+ }
+
+ if (args->version) {
+- g_strfreev(processed_args);
+- pcmk__free_arg_context(context);
+- /* FIXME: When pacemakerd is converted to use formatted output, this can go. */
+- pcmk__cli_help('v', CRM_EX_USAGE);
++ out->version(out, false);
++ goto done;
+ }
+
+ setenv("LC_ALL", "C", 1);
+@@ -1248,6 +1265,13 @@ main(int argc, char **argv)
+ crm_ipc_close(old_instance);
+ crm_ipc_destroy(old_instance);
+
++ /* Don't allow any accidental output after this point. */
++ if (out != NULL) {
++ out->finish(out, exit_code, true, NULL);
++ pcmk__output_free(out);
++ out = NULL;
++ }
++
+ #ifdef SUPPORT_COROSYNC
+ if (mcp_read_config() == FALSE) {
+ exit_code = CRM_EX_UNAVAILABLE;
+@@ -1333,6 +1357,11 @@ done:
+ g_strfreev(processed_args);
+ pcmk__free_arg_context(context);
+
+- pcmk__output_and_clear_error(error, NULL);
++ pcmk__output_and_clear_error(error, out);
++
++ if (out != NULL) {
++ out->finish(out, exit_code, true, NULL);
++ pcmk__output_free(out);
++ }
+ crm_exit(exit_code);
+ }
+--
+1.8.3.1
+
+
+From 35e6da64381fcb092d81ce16835cc28670b077cb Mon Sep 17 00:00:00 2001
+From: Chris Lumens <clumens(a)redhat.com>
+Date: Mon, 17 May 2021 10:04:04 -0400
+Subject: [PATCH 2/5] Features: daemons: Output the pacemakerd feature list in
+ XML.
+
+---
+ daemons/pacemakerd/pacemakerd.c | 45 ++++++++++++++++++++++++++++++++++++++---
+ 1 file changed, 42 insertions(+), 3 deletions(-)
+
+diff --git a/daemons/pacemakerd/pacemakerd.c b/daemons/pacemakerd/pacemakerd.c
+index bd59729..93cf743 100644
+--- a/daemons/pacemakerd/pacemakerd.c
++++ b/daemons/pacemakerd/pacemakerd.c
+@@ -43,6 +43,42 @@ static pcmk__output_t *out = NULL;
+ static pcmk__supported_format_t formats[] = {
+ PCMK__SUPPORTED_FORMAT_NONE,
+ PCMK__SUPPORTED_FORMAT_TEXT,
++ PCMK__SUPPORTED_FORMAT_XML,
++ { NULL, NULL, NULL }
++};
++
++static int
++pacemakerd_features(pcmk__output_t *out, va_list args) {
++ out->info(out, "Pacemaker %s (Build: %s)\n Supporting v%s: %s", PACEMAKER_VERSION,
++ BUILD_VERSION, CRM_FEATURE_SET, CRM_FEATURES);
++ return pcmk_rc_ok;
++}
++
++static int
++pacemakerd_features_xml(pcmk__output_t *out, va_list args) {
++ gchar **feature_list = g_strsplit(CRM_FEATURES, " ", 0);
++
++ pcmk__output_xml_create_parent(out, "pacemakerd",
++ "version", PACEMAKER_VERSION,
++ "build", BUILD_VERSION,
++ "feature_set", CRM_FEATURE_SET,
++ NULL);
++ out->begin_list(out, NULL, NULL, "features");
++
++ for (char **s = feature_list; *s != NULL; s++) {
++ pcmk__output_create_xml_text_node(out, "feature", *s);
++ }
++
++ out->end_list(out);
++
++ g_strfreev(feature_list);
++ return pcmk_rc_ok;
++}
++
++static pcmk__message_entry_t fmt_functions[] = {
++ { "features", "default", pacemakerd_features },
++ { "features", "xml", pacemakerd_features_xml },
++
+ { NULL, NULL, NULL }
+ };
+
+@@ -200,7 +236,7 @@ static GOptionContext *
+ build_arg_context(pcmk__common_args_t *args, GOptionGroup **group) {
+ GOptionContext *context = NULL;
+
+- context = pcmk__build_arg_context(args, "text", group, NULL);
++ context = pcmk__build_arg_context(args, "text (default), xml", group, NULL);
+ pcmk__add_main_args(context, entries);
+ return context;
+ }
+@@ -241,9 +277,12 @@ main(int argc, char **argv)
+ goto done;
+ }
+
++ pcmk__force_args(context, &error, "%s --xml-simple-list", g_get_prgname());
++
++ pcmk__register_messages(out, fmt_functions);
++
+ if (options.features) {
+- out->info(out, "Pacemaker %s (Build: %s)\n Supporting v%s: %s", PACEMAKER_VERSION,
+- BUILD_VERSION, CRM_FEATURE_SET, CRM_FEATURES);
++ out->message(out, "features");
+ exit_code = CRM_EX_OK;
+ goto done;
+ }
+--
+1.8.3.1
+
+
+From 5b7f5eb35b025b59805cf3c7c3dcb6a3cf4b71b3 Mon Sep 17 00:00:00 2001
+From: Chris Lumens <clumens(a)redhat.com>
+Date: Mon, 17 May 2021 11:09:53 -0400
+Subject: [PATCH 3/5] Low: daemons: Conditionally enable logging in pacemakerd.
+
+If we're doing an interactive command-line call, use
+pcmk__cli_init_logging. At the moment, all command line calls except
+for --shutdown do their work before logging would even come up, so we
+really only need to do this for --shutdown.
+
+If we're doing a daemon call, use crm_log_init.
+---
+ daemons/pacemakerd/pacemakerd.c | 7 +++++--
+ 1 file changed, 5 insertions(+), 2 deletions(-)
+
+diff --git a/daemons/pacemakerd/pacemakerd.c b/daemons/pacemakerd/pacemakerd.c
+index 93cf743..c20bde7 100644
+--- a/daemons/pacemakerd/pacemakerd.c
++++ b/daemons/pacemakerd/pacemakerd.c
+@@ -296,8 +296,11 @@ main(int argc, char **argv)
+
+ pcmk__set_env_option("mcp", "true");
+
+- pcmk__cli_init_logging("pacemakerd", args->verbosity);
+- crm_log_init(NULL, LOG_INFO, TRUE, FALSE, argc, argv, FALSE);
++ if (options.shutdown) {
++ pcmk__cli_init_logging("pacemakerd", args->verbosity);
++ } else {
++ crm_log_init(NULL, LOG_INFO, TRUE, FALSE, argc, argv, FALSE);
++ }
+
+ crm_debug("Checking for existing Pacemaker instance");
+ old_instance = crm_ipc_new(CRM_SYSTEM_MCP, 0);
+--
+1.8.3.1
+
+
+From 2393362bb7489e86d937ed46a1c5cfb93d9bf3ab Mon Sep 17 00:00:00 2001
+From: Chris Lumens <clumens(a)redhat.com>
+Date: Mon, 17 May 2021 11:58:06 -0400
+Subject: [PATCH 4/5] Fix: include: Bump CRM_FEATURE_SET for new pacemakerd
+ args.
+
+---
+ include/crm/crm.h | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+diff --git a/include/crm/crm.h b/include/crm/crm.h
+index fdfc825..92a98fa 100644
+--- a/include/crm/crm.h
++++ b/include/crm/crm.h
+@@ -66,7 +66,7 @@ extern "C" {
+ * >=3.0.13: Fail counts include operation name and interval
+ * >=3.2.0: DC supports PCMK_LRM_OP_INVALID and PCMK_LRM_OP_NOT_CONNECTED
+ */
+-# define CRM_FEATURE_SET "3.10.0"
++# define CRM_FEATURE_SET "3.10.1"
+
+ /* Pacemaker's CPG protocols use fixed-width binary fields for the sender and
+ * recipient of a CPG message. This imposes an arbitrary limit on cluster node
+--
+1.8.3.1
+
+
+From 3ad8edbd91631b87ef5f53fa2d68f0c8bbb9ee2b Mon Sep 17 00:00:00 2001
+From: Chris Lumens <clumens(a)redhat.com>
+Date: Mon, 17 May 2021 11:57:09 -0400
+Subject: [PATCH 5/5] Feature: xml: Add schema for pacemakerd.
+
+---
+ xml/Makefile.am | 1 +
+ xml/api/pacemakerd-2.10.rng | 28 ++++++++++++++++++++++++++++
+ 2 files changed, 29 insertions(+)
+ create mode 100644 xml/api/pacemakerd-2.10.rng
+
+diff --git a/xml/Makefile.am b/xml/Makefile.am
+index 12a51c5..b9448d4 100644
+--- a/xml/Makefile.am
++++ b/xml/Makefile.am
+@@ -56,6 +56,7 @@ API_request_base = command-output \
+ crm_simulate \
+ crmadmin \
+ digests \
++ pacemakerd \
+ stonith_admin \
+ version
+
+diff --git a/xml/api/pacemakerd-2.10.rng b/xml/api/pacemakerd-2.10.rng
+new file mode 100644
+index 0000000..41a11e7
+--- /dev/null
++++ b/xml/api/pacemakerd-2.10.rng
+@@ -0,0 +1,28 @@
++<?xml version="1.0" encoding="UTF-8"?>
++<grammar xmlns="http://relaxng.org/ns/structure/1.0"
++ datatypeLibrary="http://www.w3.org/2001/XMLSchema-datatypes">
++
++ <start>
++ <ref name="element-pacemakerd"/>
++ </start>
++
++ <define name="element-pacemakerd">
++ <element name="pacemakerd">
++ <attribute name="version"> <text /> </attribute>
++ <attribute name="build"> <text /> </attribute>
++ <attribute name="feature_set"> <text /> </attribute>
++
++ <optional>
++ <ref name="feature-list" />
++ </optional>
++ </element>
++ </define>
++
++ <define name="feature-list">
++ <element name="features">
++ <oneOrMore>
++ <element name="feature"> <text/> </element>
++ </oneOrMore>
++ </element>
++ </define>
++</grammar>
+--
+1.8.3.1
+
diff --git a/004-check-level.patch b/004-check-level.patch
new file mode 100644
index 0000000..f2abb5f
--- /dev/null
+++ b/004-check-level.patch
@@ -0,0 +1,199 @@
+From 3905e7eac11298fc20efd567a773666f948edf61 Mon Sep 17 00:00:00 2001
+From: Chris Lumens <clumens(a)redhat.com>
+Date: Mon, 3 May 2021 11:19:04 -0400
+Subject: [PATCH 1/2] Feature: tools: Add OCF_CHECK_LEVEL to crm_resource
+ environment.
+
+If --validate= or --force-check= are given with a level, pass that along
+as OCF_CHECK_LEVEL. This argument is optional, and if no value is given
+then the environment variable will not be set and whatever's the default
+on the resource agent will be used.
+
+See: rhbz#1955792.
+---
+ tools/crm_resource.c | 29 +++++++++++++++++++++--------
+ tools/crm_resource.h | 4 ++--
+ tools/crm_resource_runtime.c | 13 ++++++++++---
+ 3 files changed, 33 insertions(+), 13 deletions(-)
+
+diff --git a/tools/crm_resource.c b/tools/crm_resource.c
+index 45db2b2..6ca96f8 100644
+--- a/tools/crm_resource.c
++++ b/tools/crm_resource.c
+@@ -100,6 +100,7 @@ struct {
+ int timeout_ms; // Parsed from --timeout value
+ char *agent_spec; // Standard and/or provider and/or agent
+ gchar *xml_file; // Value of (deprecated) --xml-file
++ int check_level; // Optional value of --validate or --force-check
+
+ // Resource configuration specified via command-line arguments
+ gboolean cmdline_config; // Resource configuration was via arguments
+@@ -113,6 +114,7 @@ struct {
+ GHashTable *override_params; // Resource parameter values that override config
+ } options = {
+ .attr_set_type = XML_TAG_ATTR_SETS,
++ .check_level = -1,
+ .cib_options = cib_sync_call,
+ .require_cib = TRUE,
+ .require_dataset = TRUE,
+@@ -402,14 +404,15 @@ static GOptionEntry query_entries[] = {
+ };
+
+ static GOptionEntry command_entries[] = {
+- { "validate", 0, G_OPTION_FLAG_NO_ARG, G_OPTION_ARG_CALLBACK,
++ { "validate", 0, G_OPTION_FLAG_OPTIONAL_ARG, G_OPTION_ARG_CALLBACK,
+ validate_or_force_cb,
+ "Validate resource configuration by calling agent's validate-all\n"
+ INDENT "action. The configuration may be specified either by giving an\n"
+ INDENT "existing resource name with -r, or by specifying --class,\n"
+ INDENT "--agent, and --provider arguments, along with any number of\n"
+- INDENT "--option arguments.",
+- NULL },
++ INDENT "--option arguments. An optional LEVEL argument can be given\n"
++ INDENT "to control the level of checking performed.",
++ "LEVEL" },
+ { "cleanup", 'C', G_OPTION_FLAG_NO_ARG, G_OPTION_ARG_CALLBACK, cleanup_refresh_cb,
+ "If resource has any past failures, clear its history and fail\n"
+ INDENT "count. Optionally filtered by --resource, --node, --operation\n"
+@@ -546,11 +549,12 @@ static GOptionEntry advanced_entries[] = {
+ INDENT "the cluster believes the resource is a clone instance already\n"
+ INDENT "running on the local node.",
+ NULL },
+- { "force-check", 0, G_OPTION_FLAG_NO_ARG, G_OPTION_ARG_CALLBACK,
++ { "force-check", 0, G_OPTION_FLAG_OPTIONAL_ARG, G_OPTION_ARG_CALLBACK,
+ validate_or_force_cb,
+ "(Advanced) Bypass the cluster and check the state of a resource on\n"
+- INDENT "the local node",
+- NULL },
++ INDENT "the local node. An optional LEVEL argument can be given\n"
++ INDENT "to control the level of checking performed.",
++ "LEVEL" },
+
+ { NULL }
+ };
+@@ -910,6 +914,15 @@ validate_or_force_cb(const gchar *option_name, const gchar *optarg,
+ if (options.override_params == NULL) {
+ options.override_params = pcmk__strkey_table(free, free);
+ }
++
++ if (optarg != NULL) {
++ if (pcmk__scan_min_int(optarg, &options.check_level, 0) != pcmk_rc_ok) {
++ g_set_error(error, G_OPTION_ERROR, CRM_EX_INVALID_PARAM,
++ "Invalid check level setting: %s", optarg);
++ return FALSE;
++ }
++ }
++
+ return TRUE;
+ }
+
+@@ -1826,12 +1839,12 @@ main(int argc, char **argv)
+ options.v_class, options.v_provider, options.v_agent,
+ "validate-all", options.cmdline_params,
+ options.override_params, options.timeout_ms,
+- args->verbosity, options.force);
++ args->verbosity, options.force, options.check_level);
+ } else {
+ exit_code = cli_resource_execute(rsc, options.rsc_id,
+ options.operation, options.override_params,
+ options.timeout_ms, cib_conn, data_set,
+- args->verbosity, options.force);
++ args->verbosity, options.force, options.check_level);
+ }
+ goto done;
+
+diff --git a/tools/crm_resource.h b/tools/crm_resource.h
+index 3560377..5ab10d6 100644
+--- a/tools/crm_resource.h
++++ b/tools/crm_resource.h
+@@ -88,11 +88,11 @@ crm_exit_t cli_resource_execute_from_params(pcmk__output_t *out, const char *rsc
+ const char *rsc_type, const char *rsc_action,
+ GHashTable *params, GHashTable *override_hash,
+ int timeout_ms, int resource_verbose,
+- gboolean force);
++ gboolean force, int check_level);
+ crm_exit_t cli_resource_execute(pe_resource_t *rsc, const char *requested_name,
+ const char *rsc_action, GHashTable *override_hash,
+ int timeout_ms, cib_t *cib, pe_working_set_t *data_set,
+- int resource_verbose, gboolean force);
++ int resource_verbose, gboolean force, int check_level);
+
+ int cli_resource_update_attribute(pe_resource_t *rsc, const char *requested_name,
+ const char *attr_set, const char *attr_set_type,
+diff --git a/tools/crm_resource_runtime.c b/tools/crm_resource_runtime.c
+index fe0ec98..bde83b6 100644
+--- a/tools/crm_resource_runtime.c
++++ b/tools/crm_resource_runtime.c
+@@ -1679,7 +1679,8 @@ cli_resource_execute_from_params(pcmk__output_t *out, const char *rsc_name,
+ const char *rsc_class, const char *rsc_prov,
+ const char *rsc_type, const char *action,
+ GHashTable *params, GHashTable *override_hash,
+- int timeout_ms, int resource_verbose, gboolean force)
++ int timeout_ms, int resource_verbose, gboolean force,
++ int check_level)
+ {
+ GHashTable *params_copy = NULL;
+ crm_exit_t exit_code = CRM_EX_OK;
+@@ -1703,6 +1704,12 @@ cli_resource_execute_from_params(pcmk__output_t *out, const char *rsc_name,
+ /* add crm_feature_set env needed by some resource agents */
+ g_hash_table_insert(params, strdup(XML_ATTR_CRM_VERSION), strdup(CRM_FEATURE_SET));
+
++ if (check_level >= 0) {
++ char *level = crm_strdup_printf("%d", check_level);
++ setenv("OCF_CHECK_LEVEL", level, 1);
++ free(level);
++ }
++
+ /* resources_action_create frees the params hash table it's passed, but we
+ * may need to reuse it in a second call to resources_action_create. Thus
+ * we'll make a copy here so that gets freed and the original remains for
+@@ -1790,7 +1797,7 @@ crm_exit_t
+ cli_resource_execute(pe_resource_t *rsc, const char *requested_name,
+ const char *rsc_action, GHashTable *override_hash,
+ int timeout_ms, cib_t * cib, pe_working_set_t *data_set,
+- int resource_verbose, gboolean force)
++ int resource_verbose, gboolean force, int check_level)
+ {
+ pcmk__output_t *out = data_set->priv;
+ crm_exit_t exit_code = CRM_EX_OK;
+@@ -1856,7 +1863,7 @@ cli_resource_execute(pe_resource_t *rsc, const char *requested_name,
+
+ exit_code = cli_resource_execute_from_params(out, rid, rclass, rprov, rtype, action,
+ params, override_hash, timeout_ms,
+- resource_verbose, force);
++ resource_verbose, force, check_level);
+ return exit_code;
+ }
+
+--
+1.8.3.1
+
+
+From d13ba4bd6defe0dd81fdf8ab39ae5b889513c0c0 Mon Sep 17 00:00:00 2001
+From: Chris Lumens <clumens(a)redhat.com>
+Date: Thu, 20 May 2021 10:59:23 -0400
+Subject: [PATCH 2/2] Fix: include: Bump feature set to 3.10.2.
+
+This is for the OCF_CHECK_LEVEL environment variable.
+
+See: rhbz#1955792.
+---
+ include/crm/crm.h | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+diff --git a/include/crm/crm.h b/include/crm/crm.h
+index 92a98fa..ee52c36 100644
+--- a/include/crm/crm.h
++++ b/include/crm/crm.h
+@@ -66,7 +66,7 @@ extern "C" {
+ * >=3.0.13: Fail counts include operation name and interval
+ * >=3.2.0: DC supports PCMK_LRM_OP_INVALID and PCMK_LRM_OP_NOT_CONNECTED
+ */
+-# define CRM_FEATURE_SET "3.10.1"
++# define CRM_FEATURE_SET "3.10.2"
+
+ /* Pacemaker's CPG protocols use fixed-width binary fields for the sender and
+ * recipient of a CPG message. This imposes an arbitrary limit on cluster node
+--
+1.8.3.1
+
diff --git a/005-crm_resource.patch b/005-crm_resource.patch
new file mode 100644
index 0000000..1683026
--- /dev/null
+++ b/005-crm_resource.patch
@@ -0,0 +1,866 @@
+From a5a507d4e1abf242903472719a19977811e6f164 Mon Sep 17 00:00:00 2001
+From: Chris Lumens <clumens(a)redhat.com>
+Date: Thu, 20 May 2021 11:59:36 -0400
+Subject: [PATCH 01/10] Feature: libcrmcommon: Add OCF_OUTPUT_FORMAT to
+ crm_resource environment.
+
+See: rhbz#1644628
+---
+ lib/common/output.c | 2 ++
+ 1 file changed, 2 insertions(+)
+
+diff --git a/lib/common/output.c b/lib/common/output.c
+index 6cb49b5..58872e0 100644
+--- a/lib/common/output.c
++++ b/lib/common/output.c
+@@ -71,6 +71,8 @@ pcmk__output_new(pcmk__output_t **out, const char *fmt_name, const char *filenam
+ return ENOMEM;
+ }
+
++ setenv("OCF_OUTPUT_FORMAT", (*out)->fmt_name, 1);
++
+ return pcmk_rc_ok;
+ }
+
+--
+1.8.3.1
+
+
+From acc6ecdbfb797d69794e68f75a734d6252434e01 Mon Sep 17 00:00:00 2001
+From: Chris Lumens <clumens(a)redhat.com>
+Date: Fri, 21 May 2021 14:20:30 -0400
+Subject: [PATCH 02/10] Feature: schemas: Copy crm_resource schema in
+ preparation for changes.
+
+See: rhbz#1644628
+---
+ xml/api/crm_resource-2.11.rng | 238 ++++++++++++++++++++++++++++++++++++++++++
+ 1 file changed, 238 insertions(+)
+ create mode 100644 xml/api/crm_resource-2.11.rng
+
+diff --git a/xml/api/crm_resource-2.11.rng b/xml/api/crm_resource-2.11.rng
+new file mode 100644
+index 0000000..8e386db
+--- /dev/null
++++ b/xml/api/crm_resource-2.11.rng
+@@ -0,0 +1,238 @@
++<?xml version="1.0" encoding="UTF-8"?>
++<grammar xmlns="http://relaxng.org/ns/structure/1.0"
++ datatypeLibrary="http://www.w3.org/2001/XMLSchema-datatypes">
++
++ <start>
++ <ref name="element-crm-resource"/>
++ </start>
++
++ <define name="element-crm-resource">
++ <choice>
++ <ref name="agents-list" />
++ <ref name="alternatives-list" />
++ <ref name="constraints-list" />
++ <externalRef href="generic-list-2.4.rng"/>
++ <element name="metadata"> <text/> </element>
++ <ref name="locate-list" />
++ <ref name="operations-list" />
++ <ref name="providers-list" />
++ <ref name="reasons-list" />
++ <ref name="resource-check" />
++ <ref name="resource-config" />
++ <ref name="resources-list" />
++ </choice>
++ </define>
++
++ <define name="agents-list">
++ <element name="agents">
++ <attribute name="standard"> <text/> </attribute>
++ <optional>
++ <attribute name="provider"> <text/> </attribute>
++ </optional>
++ <zeroOrMore>
++ <element name="agent"> <text/> </element>
++ </zeroOrMore>
++ </element>
++ </define>
++
++ <define name="alternatives-list">
++ <element name="providers">
++ <attribute name="for"> <text/> </attribute>
++ <zeroOrMore>
++ <element name="provider"> <text/> </element>
++ </zeroOrMore>
++ </element>
++ </define>
++
++ <define name="constraints-list">
++ <element name="constraints">
++ <interleave>
++ <zeroOrMore>
++ <ref name="rsc-location" />
++ </zeroOrMore>
++ <zeroOrMore>
++ <ref name="rsc-colocation" />
++ </zeroOrMore>
++ </interleave>
++ </element>
++ </define>
++
++ <define name="locate-list">
++ <element name="nodes">
++ <attribute name="resource"> <text/> </attribute>
++ <zeroOrMore>
++ <element name="node">
++ <optional>
++ <attribute name="state"><value>promoted</value></attribute>
++ </optional>
++ <text/>
++ </element>
++ </zeroOrMore>
++ </element>
++ </define>
++
++ <define name="rsc-location">
++ <element name="rsc_location">
++ <attribute name="node"> <text/> </attribute>
++ <attribute name="rsc"> <text/> </attribute>
++ <attribute name="id"> <text/> </attribute>
++ <externalRef href="../score.rng"/>
++ </element>
++ </define>
++
++ <define name="operations-list">
++ <element name="operations">
++ <oneOrMore>
++ <ref name="element-operation-list" />
++ </oneOrMore>
++ </element>
++ </define>
++
++ <define name="providers-list">
++ <element name="providers">
++ <attribute name="standard"> <value>ocf</value> </attribute>
++ <optional>
++ <attribute name="agent"> <text/> </attribute>
++ </optional>
++ <zeroOrMore>
++ <element name="provider"> <text/> </element>
++ </zeroOrMore>
++ </element>
++ </define>
++
++ <define name="reasons-list">
++ <choice>
++ <ref name="no-resource-or-uname"/>
++ <ref name="resource-and-uname"/>
++ <ref name="no-resource-but-uname"/>
++ <ref name="resource-but-no-uname"/>
++ </choice>
++ </define>
++
++ <define name="no-resource-or-uname">
++ <element name="reason">
++ <element name="resources">
++ <zeroOrMore>
++ <element name="resource">
++ <attribute name="id"> <text/> </attribute>
++ <attribute name="running"> <data type="boolean"/> </attribute>
++ <ref name="resource-check"/>
++ </element>
++ </zeroOrMore>
++ </element>
++ </element>
++ </define>
++
++ <define name="resource-and-uname">
++ <element name="reason">
++ <attribute name="running_on"> <text/> </attribute>
++ <ref name="resource-check"/>
++ </element>
++ </define>
++
++ <define name="no-resource-but-uname">
++ <element name="reason">
++ <element name="resources">
++ <zeroOrMore>
++ <element name="resource">
++ <attribute name="id"> <text/> </attribute>
++ <attribute name="running"> <data type="boolean"/> </attribute>
++ <attribute name="host"> <text/> </attribute>
++ <ref name="resource-check"/>
++ </element>
++ </zeroOrMore>
++ </element>
++ </element>
++ </define>
++
++ <define name="resource-but-no-uname">
++ <element name="reason">
++ <attribute name="running"> <data type="boolean"/> </attribute>
++ <ref name="resource-check"/>
++ </element>
++ </define>
++
++ <define name="resource-config">
++ <element name="resource_config">
++ <externalRef href="resources-2.4.rng" />
++ <element name="xml"> <text/> </element>
++ </element>
++ </define>
++
++ <define name="resource-check">
++ <element name="check">
++ <attribute name="id"> <text/> </attribute>
++ <optional>
++ <choice>
++ <attribute name="remain_stopped"><value>true</value></attribute>
++ <attribute name="promotable"><value>false</value></attribute>
++ </choice>
++ </optional>
++ <optional>
++ <attribute name="unmanaged"><value>true</value></attribute>
++ </optional>
++ <optional>
++ <attribute name="locked-to"> <text/> </attribute>
++ </optional>
++ </element>
++ </define>
++
++ <define name="resources-list">
++ <element name="resources">
++ <zeroOrMore>
++ <externalRef href="resources-2.4.rng" />
++ </zeroOrMore>
++ </element>
++ </define>
++
++ <define name="rsc-colocation">
++ <element name="rsc_colocation">
++ <attribute name="id"> <text/> </attribute>
++ <attribute name="rsc"> <text/> </attribute>
++ <attribute name="with-rsc"> <text/> </attribute>
++ <externalRef href="../score.rng"/>
++ <optional>
++ <attribute name="node-attribute"> <text/> </attribute>
++ </optional>
++ <optional>
++ <attribute name="rsc-role">
++ <ref name="attribute-roles"/>
++ </attribute>
++ </optional>
++ <optional>
++ <attribute name="with-rsc-role">
++ <ref name="attribute-roles"/>
++ </attribute>
++ </optional>
++ </element>
++ </define>
++
++ <define name="element-operation-list">
++ <element name="operation">
++ <optional>
++ <group>
++ <attribute name="rsc"> <text/> </attribute>
++ <attribute name="agent"> <text/> </attribute>
++ </group>
++ </optional>
++ <attribute name="op"> <text/> </attribute>
++ <attribute name="node"> <text/> </attribute>
++ <attribute name="call"> <data type="integer" /> </attribute>
++ <attribute name="rc"> <data type="nonNegativeInteger" /> </attribute>
++ <optional>
++ <attribute name="last-rc-change"> <text/> </attribute>
++ <attribute name="exec-time"> <data type="nonNegativeInteger" /> </attribute>
++ </optional>
++ <attribute name="status"> <text/> </attribute>
++ </element>
++ </define>
++
++ <define name="attribute-roles">
++ <choice>
++ <value>Stopped</value>
++ <value>Started</value>
++ <value>Master</value>
++ <value>Slave</value>
++ </choice>
++ </define>
++</grammar>
+--
+1.8.3.1
+
+
+From 1bbdf2149a111e9e19c388834f82001e0d31c427 Mon Sep 17 00:00:00 2001
+From: Chris Lumens <clumens(a)redhat.com>
+Date: Mon, 24 May 2021 12:23:55 -0400
+Subject: [PATCH 03/10] Feature: xml: Update the crm_resource schema for XML
+ output.
+
+See: rhbz#1644628
+---
+ xml/api/crm_resource-2.11.rng | 50 +++++++++++++++++++++++++++++++++++++++++++
+ 1 file changed, 50 insertions(+)
+
+diff --git a/xml/api/crm_resource-2.11.rng b/xml/api/crm_resource-2.11.rng
+index 8e386db..aaa54d6 100644
+--- a/xml/api/crm_resource-2.11.rng
++++ b/xml/api/crm_resource-2.11.rng
+@@ -20,6 +20,7 @@
+ <ref name="resource-check" />
+ <ref name="resource-config" />
+ <ref name="resources-list" />
++ <ref name="resource-agent-action" />
+ </choice>
+ </define>
+
+@@ -227,6 +228,55 @@
+ </element>
+ </define>
+
++ <define name="resource-agent-action">
++ <element name="resource-agent-action">
++ <attribute name="action"> <text/> </attribute>
++ <optional>
++ <attribute name="rsc"> <text/> </attribute>
++ </optional>
++ <attribute name="class"> <text/> </attribute>
++ <attribute name="type"> <text/> </attribute>
++ <optional>
++ <attribute name="provider"> <text/> </attribute>
++ </optional>
++ <optional>
++ <ref name="overrides-list"/>
++ </optional>
++ <ref name="agent-status"/>
++ <optional>
++ <choice>
++ <element name="command">
++ <text />
++ </element>
++ <externalRef href="command-output-1.0.rng"/>
++ </choice>
++ </optional>
++ </element>
++ </define>
++
++ <define name="overrides-list">
++ <element name="overrides">
++ <zeroOrMore>
++ <element name="override">
++ <optional>
++ <attribute name="rsc"> <text/> </attribute>
++ </optional>
++ <attribute name="name"> <text/> </attribute>
++ <attribute name="value"> <text/> </attribute>
++ </element>
++ </zeroOrMore>
++ </element>
++ </define>
++
++ <define name="agent-status">
++ <element name="agent-status">
++ <attribute name="code"> <data type="integer" /> </attribute>
++ <optional>
++ <attribute name="message"> <text/> </attribute>
++ </optional>
++ </element>
++ </define>
++
+ <define name="attribute-roles">
+ <choice>
+ <value>Stopped</value>
+--
+1.8.3.1
+
+
+From d89f5bc7fec856fdcd32fa14edbd0019507d5d15 Mon Sep 17 00:00:00 2001
+From: Chris Lumens <clumens(a)redhat.com>
+Date: Tue, 1 Jun 2021 15:26:58 -0400
+Subject: [PATCH 04/10] Low: libcrmcommon: Increase PCMK__API_VERSION for new
+ crm_resource output.
+
+See: rhbz#1644628
+---
+ include/crm/common/output_internal.h | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+diff --git a/include/crm/common/output_internal.h b/include/crm/common/output_internal.h
+index 10b315b..0436cde 100644
+--- a/include/crm/common/output_internal.h
++++ b/include/crm/common/output_internal.h
+@@ -27,7 +27,7 @@ extern "C" {
+ # include <glib.h>
+ # include <crm/common/results.h>
+
+-# define PCMK__API_VERSION "2.9"
++# define PCMK__API_VERSION "2.11"
+
+ #if defined(PCMK__WITH_ATTRIBUTE_OUTPUT_ARGS)
+ # define PCMK__OUTPUT_ARGS(ARGS...) __attribute__((output_args(ARGS)))
+--
+1.8.3.1
+
+
+From 30bd2ddf43ee2a911681e51f40ed9ba20ec250b0 Mon Sep 17 00:00:00 2001
+From: Chris Lumens <clumens(a)redhat.com>
+Date: Thu, 27 May 2021 13:57:12 -0400
+Subject: [PATCH 05/10] Low: tools: Pass NULL to
+ cli_resource_execute_from_params...
+
+if no resource name is given. This happens if we are validating based
+on the --class/--agent/--provider command line options instead.
+---
+ tools/crm_resource.c | 2 +-
+ tools/crm_resource_runtime.c | 8 ++++----
+ 2 files changed, 5 insertions(+), 5 deletions(-)
+
+diff --git a/tools/crm_resource.c b/tools/crm_resource.c
+index 24f1121..37a0bb0 100644
+--- a/tools/crm_resource.c
++++ b/tools/crm_resource.c
+@@ -1840,7 +1840,7 @@ main(int argc, char **argv)
+
+ case cmd_execute_agent:
+ if (options.cmdline_config) {
+- exit_code = cli_resource_execute_from_params(out, "test",
++ exit_code = cli_resource_execute_from_params(out, NULL,
+ options.v_class, options.v_provider, options.v_agent,
+ "validate-all", options.cmdline_params,
+ options.override_params, options.timeout_ms,
+diff --git a/tools/crm_resource_runtime.c b/tools/crm_resource_runtime.c
+index 48a4b40..ebf48bb 100644
+--- a/tools/crm_resource_runtime.c
++++ b/tools/crm_resource_runtime.c
+@@ -1717,14 +1717,14 @@ cli_resource_execute_from_params(pcmk__output_t *out, const char *rsc_name,
+ */
+ params_copy = pcmk__str_table_dup(params);
+
+- op = resources_action_create(rsc_name, rsc_class, rsc_prov, rsc_type, action, 0,
+- timeout_ms, params_copy, 0);
++ op = resources_action_create(rsc_name ? rsc_name : "test", rsc_class, rsc_prov,
++ rsc_type, action, 0, timeout_ms, params_copy, 0);
+ if (op == NULL) {
+ /* Re-run with stderr enabled so we can display a sane error message */
+ crm_enable_stderr(TRUE);
+ params_copy = pcmk__str_table_dup(params);
+- op = resources_action_create(rsc_name, rsc_class, rsc_prov, rsc_type, action, 0,
+- timeout_ms, params_copy, 0);
++ op = resources_action_create(rsc_name ? rsc_name : "test", rsc_class, rsc_prov,
++ rsc_type, action, 0, timeout_ms, params_copy, 0);
+
+ /* Callers of cli_resource_execute expect that the params hash table will
+ * be freed. That function uses this one, so for that reason and for
+--
+1.8.3.1
+
+
+From ee56efd53d14cfc4f902769540b72b3bb6096a73 Mon Sep 17 00:00:00 2001
+From: Chris Lumens <clumens(a)redhat.com>
+Date: Mon, 24 May 2021 12:08:52 -0400
+Subject: [PATCH 06/10] Feature: tools: Add an agent-status message for
+ crm_resource.
+
+This moves what was previously only done in an out->info call to its own
+output message, which means it will appear in XML output as well. Also,
+note that if --class/--agent/--provider are given, the resource name
+will be set to "test". In that case, do not display the resource name
+in the output.
+
+This message will be used for --validate and the --force-* command line
+options to crm_resource.
+
+See: rhbz#1644628
+---
+ tools/crm_resource_print.c | 53 ++++++++++++++++++++++++++++++++++++++++++++++
+ 1 file changed, 53 insertions(+)
+
+diff --git a/tools/crm_resource_print.c b/tools/crm_resource_print.c
+index 9d82cf8..88d5878 100644
+--- a/tools/crm_resource_print.c
++++ b/tools/crm_resource_print.c
+@@ -152,6 +152,57 @@ attribute_list_default(pcmk__output_t *out, va_list args) {
+ return pcmk_rc_ok;
+ }
+
++PCMK__OUTPUT_ARGS("agent-status", "int", "const char *", "const char *", "const char *",
++ "const char *", "const char *", "int")
++static int
++agent_status_default(pcmk__output_t *out, va_list args) {
++ int status = va_arg(args, int);
++ const char *action = va_arg(args, const char *);
++ const char *name = va_arg(args, const char *);
++ const char *class = va_arg(args, const char *);
++ const char *provider = va_arg(args, const char *);
++ const char *type = va_arg(args, const char *);
++ int rc = va_arg(args, int);
++
++ if (status == PCMK_LRM_OP_DONE) {
++ out->info(out, "Operation %s%s%s (%s%s%s:%s) returned: '%s' (%d)",
++ action, name ? " for " : "", name ? name : "",
++ class, provider ? ":" : "", provider ? provider : "", type,
++ services_ocf_exitcode_str(rc), rc);
++ } else {
++ out->err(out, "Operation %s%s%s (%s%s%s:%s) failed: '%s' (%d)",
++ action, name ? " for " : "", name ? name : "",
++ class, provider ? ":" : "", provider ? provider : "", type,
++ services_lrm_status_str(status), status);
++ }
++
++ return pcmk_rc_ok;
++}
++
++PCMK__OUTPUT_ARGS("agent-status", "int", "const char *", "const char *", "const char *",
++ "const char *", "const char *", "int")
++static int
++agent_status_xml(pcmk__output_t *out, va_list args) {
++ int status G_GNUC_UNUSED = va_arg(args, int);
++ const char *action G_GNUC_UNUSED = va_arg(args, const char *);
++ const char *name G_GNUC_UNUSED = va_arg(args, const char *);
++ const char *class G_GNUC_UNUSED = va_arg(args, const char *);
++ const char *provider G_GNUC_UNUSED = va_arg(args, const char *);
++ const char *type G_GNUC_UNUSED = va_arg(args, const char *);
++ int rc = va_arg(args, int);
++
++ char *status_str = pcmk__itoa(rc);
++
++ pcmk__output_create_xml_node(out, "agent-status",
++ "code", status_str,
++ "message", services_ocf_exitcode_str(rc),
++ NULL);
++
++ free(status_str);
++
++ return pcmk_rc_ok;
++}
++
+ PCMK__OUTPUT_ARGS("attribute-list", "pe_resource_t *", "char *", "GHashTable *")
+ static int
+ attribute_list_text(pcmk__output_t *out, va_list args) {
+@@ -562,6 +613,8 @@ resource_names(pcmk__output_t *out, va_list args) {
+ }
+
+ static pcmk__message_entry_t fmt_functions[] = {
++ { "agent-status", "default", agent_status_default },
++ { "agent-status", "xml", agent_status_xml },
+ { "attribute-list", "default", attribute_list_default },
+ { "attribute-list", "text", attribute_list_text },
+ { "property-list", "default", property_list_default },
+--
+1.8.3.1
+
+
+From 85cb6b6bff96b18c5174d11e4de4d49cbfb20bb7 Mon Sep 17 00:00:00 2001
+From: Chris Lumens <clumens(a)redhat.com>
+Date: Tue, 1 Jun 2021 14:47:30 -0400
+Subject: [PATCH 07/10] Feature: tools: Add an overridden params output
+ message.
+
+This also replaces what was previously being done in an out->info call
+with an output message. This means it shows up in XML output as well.
+Also, note that if --class/--agent/--provider are given, the resource
+name will be set to "test". In that case, do not display the resource
+name in the output.
+
+See: rhbz#1644628
+---
+ tools/crm_resource_print.c | 39 +++++++++++++++++++++++++++++++++++++++
+ 1 file changed, 39 insertions(+)
+
+diff --git a/tools/crm_resource_print.c b/tools/crm_resource_print.c
+index 88d5878..119d83f 100644
+--- a/tools/crm_resource_print.c
++++ b/tools/crm_resource_print.c
+@@ -224,6 +224,43 @@ attribute_list_text(pcmk__output_t *out, va_list args) {
+ return pcmk_rc_ok;
+ }
+
++PCMK__OUTPUT_ARGS("override", "const char *", "const char *", "const char *")
++static int
++override_default(pcmk__output_t *out, va_list args) {
++ const char *rsc_name = va_arg(args, const char *);
++ const char *name = va_arg(args, const char *);
++ const char *value = va_arg(args, const char *);
++
++ if (rsc_name == NULL) {
++ out->list_item(out, NULL, "Overriding the cluster configuration with '%s' = '%s'",
++ name, value);
++ } else {
++ out->list_item(out, NULL, "Overriding the cluster configuration for '%s' with '%s' = '%s'",
++ rsc_name, name, value);
++ }
++
++ return pcmk_rc_ok;
++}
++
++PCMK__OUTPUT_ARGS("override", "const char *", "const char *", "const char *")
++static int
++override_xml(pcmk__output_t *out, va_list args) {
++ const char *rsc_name = va_arg(args, const char *);
++ const char *name = va_arg(args, const char *);
++ const char *value = va_arg(args, const char *);
++
++ xmlNodePtr node = pcmk__output_create_xml_node(out, "override",
++ "name", name,
++ "value", value,
++ NULL);
++
++ if (rsc_name != NULL) {
++ crm_xml_add(node, "rsc", rsc_name);
++ }
++
++ return pcmk_rc_ok;
++}
++
+ PCMK__OUTPUT_ARGS("property-list", "pe_resource_t *", "char *")
+ static int
+ property_list_default(pcmk__output_t *out, va_list args) {
+@@ -617,6 +654,8 @@ static pcmk__message_entry_t fmt_functions[] = {
+ { "agent-status", "xml", agent_status_xml },
+ { "attribute-list", "default", attribute_list_default },
+ { "attribute-list", "text", attribute_list_text },
++ { "override", "default", override_default },
++ { "override", "xml", override_xml },
+ { "property-list", "default", property_list_default },
+ { "property-list", "text", property_list_text },
+ { "resource-check-list", "default", resource_check_list_default },
+--
+1.8.3.1
+
+
+From e5e24592c7c3231c619fb5253e7925ffbc634a99 Mon Sep 17 00:00:00 2001
+From: Chris Lumens <clumens(a)redhat.com>
+Date: Fri, 4 Jun 2021 10:24:51 -0400
+Subject: [PATCH 08/10] Low: tools: Use simple XML lists for resource actions
+ as well.
+
+See: rhbz#1644628
+---
+ tools/crm_resource.c | 1 +
+ 1 file changed, 1 insertion(+)
+
+diff --git a/tools/crm_resource.c b/tools/crm_resource.c
+index 37a0bb0..e957011 100644
+--- a/tools/crm_resource.c
++++ b/tools/crm_resource.c
+@@ -1643,6 +1643,7 @@ main(int argc, char **argv)
+ * saves from having to write custom messages to build the lists around all these things
+ */
+ switch (options.rsc_cmd) {
++ case cmd_execute_agent:
+ case cmd_list_resources:
+ case cmd_query_xml:
+ case cmd_query_raw_xml:
+--
+1.8.3.1
+
+
+From 3e75174d0bc31b261adb1994214a5878b79da85b Mon Sep 17 00:00:00 2001
+From: Chris Lumens <clumens(a)redhat.com>
+Date: Fri, 4 Jun 2021 10:30:10 -0400
+Subject: [PATCH 09/10] Feature: tools: Add an output message for resource
+ actions.
+
+This wraps up the override and agent-status messages into a single
+message, along with any stdout/stderr from the resource action. This
+message should be called after taking the action.
+
+This also implements handling XML output from resource actions. Check
+to see if the validate-all action returns XML. If so, output it as a
+CDATA block under a "command" element. If not, treat it as plain text
+and output it as stdout/stderr from a command.
+
+See: rhbz#1644628
+---
+ tools/crm_resource_print.c | 122 +++++++++++++++++++++++++++++++++++++++++++++
+ 1 file changed, 122 insertions(+)
+
+diff --git a/tools/crm_resource_print.c b/tools/crm_resource_print.c
+index 119d83f..19a366d 100644
+--- a/tools/crm_resource_print.c
++++ b/tools/crm_resource_print.c
+@@ -293,6 +293,126 @@ property_list_text(pcmk__output_t *out, va_list args) {
+ return pcmk_rc_ok;
+ }
+
++PCMK__OUTPUT_ARGS("resource-agent-action", "int", "const char *", "const char *",
++ "const char *", "const char *", "const char *", "GHashTable *",
++ "int", "int", "char *", "char *")
++static int
++resource_agent_action_default(pcmk__output_t *out, va_list args) {
++ int verbose = va_arg(args, int);
++
++ const char *class = va_arg(args, const char *);
++ const char *provider = va_arg(args, const char *);
++ const char *type = va_arg(args, const char *);
++ const char *rsc_name = va_arg(args, const char *);
++ const char *action = va_arg(args, const char *);
++ GHashTable *overrides = va_arg(args, GHashTable *);
++ int rc = va_arg(args, int);
++ int status = va_arg(args, int);
++ char *stdout_data = va_arg(args, char *);
++ char *stderr_data = va_arg(args, char *);
++
++ if (overrides) {
++ GHashTableIter iter;
++ char *name = NULL;
++ char *value = NULL;
++
++ out->begin_list(out, NULL, NULL, "overrides");
++
++ g_hash_table_iter_init(&iter, overrides);
++ while (g_hash_table_iter_next(&iter, (gpointer *) &name, (gpointer *) &value)) {
++ out->message(out, "override", rsc_name, name, value);
++ }
++
++ out->end_list(out);
++ }
++
++ out->message(out, "agent-status", status, action, rsc_name, class, provider,
++ type, rc);
++
++ /* hide output for validate-all if not in verbose */
++ if (verbose == 0 && pcmk__str_eq(action, "validate-all", pcmk__str_casei)) {
++ return pcmk_rc_ok;
++ }
++
++ if (stdout_data || stderr_data) {
++ xmlNodePtr doc = string2xml(stdout_data);
++
++ if (doc != NULL) {
++ out->output_xml(out, "command", stdout_data);
++ xmlFreeNode(doc);
++ } else {
++ out->subprocess_output(out, rc, stdout_data, stderr_data);
++ }
++ }
++
++ return pcmk_rc_ok;
++}
++
++PCMK__OUTPUT_ARGS("resource-agent-action", "int", "const char *", "const char *",
++ "const char *", "const char *", "const char *", "GHashTable *",
++ "int", "int", "char *", "char *")
++static int
++resource_agent_action_xml(pcmk__output_t *out, va_list args) {
++ int verbose G_GNUC_UNUSED = va_arg(args, int);
++
++ const char *class = va_arg(args, const char *);
++ const char *provider = va_arg(args, const char *);
++ const char *type = va_arg(args, const char *);
++ const char *rsc_name = va_arg(args, const char *);
++ const char *action = va_arg(args, const char *);
++ GHashTable *overrides = va_arg(args, GHashTable *);
++ int rc = va_arg(args, int);
++ int status = va_arg(args, int);
++ char *stdout_data = va_arg(args, char *);
++ char *stderr_data = va_arg(args, char *);
++
++ xmlNodePtr node = pcmk__output_xml_create_parent(out, "resource-agent-action",
++ "action", action,
++ "class", class,
++ "type", type,
++ NULL);
++
++ if (rsc_name) {
++ crm_xml_add(node, "rsc", rsc_name);
++ }
++
++ if (provider) {
++ crm_xml_add(node, "provider", provider);
++ }
++
++ if (overrides) {
++ GHashTableIter iter;
++ char *name = NULL;
++ char *value = NULL;
++
++ out->begin_list(out, NULL, NULL, "overrides");
++
++ g_hash_table_iter_init(&iter, overrides);
++ while (g_hash_table_iter_next(&iter, (gpointer *) &name, (gpointer *) &value)) {
++ out->message(out, "override", rsc_name, name, value);
++ }
++
++ out->end_list(out);
++ }
++
++ out->message(out, "agent-status", status, action, rsc_name, class, provider,
++ type, rc);
++
++ if (stdout_data || stderr_data) {
++ xmlNodePtr doc = string2xml(stdout_data);
++
++ if (doc != NULL) {
++ out->output_xml(out, "command", stdout_data);
++ xmlFreeNode(doc);
++ } else {
++ out->subprocess_output(out, rc, stdout_data, stderr_data);
++ }
++ }
++
++ pcmk__output_xml_pop_parent(out);
++ return pcmk_rc_ok;
++}
++
+ PCMK__OUTPUT_ARGS("resource-check-list", "resource_checks_t *")
+ static int
+ resource_check_list_default(pcmk__output_t *out, va_list args) {
+@@ -658,6 +778,8 @@ static pcmk__message_entry_t fmt_functions[] = {
+ { "override", "xml", override_xml },
+ { "property-list", "default", property_list_default },
+ { "property-list", "text", property_list_text },
++ { "resource-agent-action", "default", resource_agent_action_default },
++ { "resource-agent-action", "xml", resource_agent_action_xml },
+ { "resource-check-list", "default", resource_check_list_default },
+ { "resource-check-list", "xml", resource_check_list_xml },
+ { "resource-search-list", "default", resource_search_list_default },
+--
+1.8.3.1
+
+
+From b50b2418e1e997b42f5370b4672a3f105d74634f Mon Sep 17 00:00:00 2001
+From: Chris Lumens <clumens(a)redhat.com>
+Date: Fri, 4 Jun 2021 10:40:16 -0400
+Subject: [PATCH 10/10] Feature: tools: Use the new resource-agent-action
+ message.
+
+See: rhbz#1644628
+---
+ tools/crm_resource_runtime.c | 21 +++------------------
+ 1 file changed, 3 insertions(+), 18 deletions(-)
+
+diff --git a/tools/crm_resource_runtime.c b/tools/crm_resource_runtime.c
+index ebf48bb..755be9f 100644
+--- a/tools/crm_resource_runtime.c
++++ b/tools/crm_resource_runtime.c
+@@ -1765,28 +1765,13 @@ cli_resource_execute_from_params(pcmk__output_t *out, const char *rsc_name,
+ if (services_action_sync(op)) {
+ exit_code = op->rc;
+
+- if (op->status == PCMK_LRM_OP_DONE) {
+- out->info(out, "Operation %s for %s (%s:%s:%s) returned: '%s' (%d)",
+- action, rsc_name, rsc_class, rsc_prov ? rsc_prov : "", rsc_type,
+- services_ocf_exitcode_str(op->rc), op->rc);
+- } else {
+- out->err(out, "Operation %s for %s (%s:%s:%s) failed: '%s' (%d)",
+- action, rsc_name, rsc_class, rsc_prov ? rsc_prov : "", rsc_type,
+- services_lrm_status_str(op->status), op->status);
+- }
+-
+- /* hide output for validate-all if not in verbose */
+- if (resource_verbose == 0 && pcmk__str_eq(action, "validate-all", pcmk__str_casei))
+- goto done;
+-
+- if (op->stdout_data || op->stderr_data) {
+- out->subprocess_output(out, op->rc, op->stdout_data, op->stderr_data);
+- }
++ out->message(out, "resource-agent-action", resource_verbose, rsc_class,
++ rsc_prov, rsc_type, rsc_name, action, override_hash, op->rc,
++ op->status, op->stdout_data, op->stderr_data);
+ } else {
+ exit_code = op->rc == 0 ? CRM_EX_ERROR : op->rc;
+ }
+
+-done:
+ services_action_free(op);
+ /* See comment above about why we free params here. */
+ g_hash_table_destroy(params);
+--
+1.8.3.1
+
diff --git a/006-crm_simulate.patch b/006-crm_simulate.patch
new file mode 100644
index 0000000..c8d4e3f
--- /dev/null
+++ b/006-crm_simulate.patch
@@ -0,0 +1,896 @@
+From 97571e6ccc9b7fa339a7e27d9b0b9ab782ff3003 Mon Sep 17 00:00:00 2001
+From: Chris Lumens <clumens(a)redhat.com>
+Date: Wed, 16 Jun 2021 13:54:10 -0400
+Subject: [PATCH 1/5] Low: schemas: Copy crm_mon.rng in preparation for
+ changes.
+
+---
+ xml/api/crm_mon-2.12.rng | 243 +++++++++++++++++++++++++++++++++++++++++++++++
+ 1 file changed, 243 insertions(+)
+ create mode 100644 xml/api/crm_mon-2.12.rng
+
+diff --git a/xml/api/crm_mon-2.12.rng b/xml/api/crm_mon-2.12.rng
+new file mode 100644
+index 0000000..ffec923
+--- /dev/null
++++ b/xml/api/crm_mon-2.12.rng
+@@ -0,0 +1,243 @@
++<?xml version="1.0" encoding="UTF-8"?>
++<grammar xmlns="http://relaxng.org/ns/structure/1.0"
++ datatypeLibrary="http://www.w3.org/2001/XMLSchema-datatypes">
++
++ <start>
++ <ref name="element-crm-mon"/>
++ </start>
++
++ <define name="element-crm-mon">
++ <optional>
++ <ref name="element-summary" />
++ </optional>
++ <optional>
++ <ref name="nodes-list" />
++ </optional>
++ <optional>
++ <ref name="resources-list" />
++ </optional>
++ <optional>
++ <ref name="node-attributes-list" />
++ </optional>
++ <optional>
++ <ref name="node-history-list" />
++ </optional>
++ <optional>
++ <ref name="failures-list" />
++ </optional>
++ <optional>
++ <ref name="fence-event-list" />
++ </optional>
++ <optional>
++ <ref name="tickets-list" />
++ </optional>
++ <optional>
++ <ref name="bans-list" />
++ </optional>
++ </define>
++
++ <define name="element-summary">
++ <element name="summary">
++ <optional>
++ <element name="stack">
++ <attribute name="type"> <text /> </attribute>
++ </element>
++ </optional>
++ <optional>
++ <element name="current_dc">
++ <attribute name="present"> <data type="boolean" /> </attribute>
++ <optional>
++ <group>
++ <attribute name="version"> <text /> </attribute>
++ <attribute name="name"> <text /> </attribute>
++ <attribute name="id"> <text /> </attribute>
++ <attribute name="with_quorum"> <data type="boolean" /> </attribute>
++ </group>
++ </optional>
++ </element>
++ </optional>
++ <optional>
++ <element name="last_update">
++ <attribute name="time"> <text /> </attribute>
++ </element>
++ <element name="last_change">
++ <attribute name="time"> <text /> </attribute>
++ <attribute name="user"> <text /> </attribute>
++ <attribute name="client"> <text /> </attribute>
++ <attribute name="origin"> <text /> </attribute>
++ </element>
++ </optional>
++ <optional>
++ <element name="nodes_configured">
++ <attribute name="number"> <data type="nonNegativeInteger" /> </attribute>
++ </element>
++ <element name="resources_configured">
++ <attribute name="number"> <data type="nonNegativeInteger" /> </attribute>
++ <attribute name="disabled"> <data type="nonNegativeInteger" /> </attribute>
++ <attribute name="blocked"> <data type="nonNegativeInteger" /> </attribute>
++ </element>
++ </optional>
++ <optional>
++ <element name="cluster_options">
++ <attribute name="stonith-enabled"> <data type="boolean" /> </attribute>
++ <attribute name="symmetric-cluster"> <data type="boolean" /> </attribute>
++ <attribute name="no-quorum-policy"> <text /> </attribute>
++ <attribute name="maintenance-mode"> <data type="boolean" /> </attribute>
++ <attribute name="stop-all-resources"> <data type="boolean" /> </attribute>
++ </element>
++ </optional>
++ </element>
++ </define>
++
++ <define name="resources-list">
++ <element name="resources">
++ <zeroOrMore>
++ <externalRef href="resources-2.4.rng" />
++ </zeroOrMore>
++ </element>
++ </define>
++
++ <define name="nodes-list">
++ <element name="nodes">
++ <zeroOrMore>
++ <externalRef href="nodes-2.8.rng" />
++ </zeroOrMore>
++ </element>
++ </define>
++
++ <define name="node-attributes-list">
++ <element name="node_attributes">
++ <zeroOrMore>
++ <externalRef href="node-attrs-2.8.rng" />
++ </zeroOrMore>
++ </element>
++ </define>
++
++ <define name="node-history-list">
++ <element name="node_history">
++ <zeroOrMore>
++ <ref name="element-node-history" />
++ </zeroOrMore>
++ </element>
++ </define>
++
++ <define name="failures-list">
++ <element name="failures">
++ <zeroOrMore>
++ <externalRef href="failure-2.8.rng" />
++ </zeroOrMore>
++ </element>
++ </define>
++
++ <define name="fence-event-list">
++ <element name="fence_history">
++ <optional>
++ <attribute name="status"> <data type="integer" /> </attribute>
++ </optional>
++ <zeroOrMore>
++ <externalRef href="fence-event-2.0.rng" />
++ </zeroOrMore>
++ </element>
++ </define>
++
++ <define name="tickets-list">
++ <element name="tickets">
++ <zeroOrMore>
++ <ref name="element-ticket" />
++ </zeroOrMore>
++ </element>
++ </define>
++
++ <define name="bans-list">
++ <element name="bans">
++ <zeroOrMore>
++ <ref name="element-ban" />
++ </zeroOrMore>
++ </element>
++ </define>
++
++ <define name="element-node-history">
++ <element name="node">
++ <attribute name="name"> <text /> </attribute>
++ <zeroOrMore>
++ <ref name="element-resource-history" />
++ </zeroOrMore>
++ </element>
++ </define>
++
++ <define name="element-resource-history">
++ <element name="resource_history">
++ <attribute name="id"> <text /> </attribute>
++ <attribute name="orphan"> <data type="boolean" /> </attribute>
++ <optional>
++ <group>
++ <attribute name="migration-threshold"> <data type="nonNegativeInteger" /> </attribute>
++ <optional>
++ <attribute name="fail-count"> <text /> </attribute>
++ </optional>
++ <optional>
++ <attribute name="last-failure"> <text /> </attribute>
++ </optional>
++ </group>
++ </optional>
++ <zeroOrMore>
++ <ref name="element-operation-history" />
++ </zeroOrMore>
++ </element>
++ </define>
++
++ <define name="element-operation-history">
++ <element name="operation_history">
++ <attribute name="call"> <text /> </attribute>
++ <attribute name="task"> <text /> </attribute>
++ <optional>
++ <attribute name="interval"> <text /> </attribute>
++ </optional>
++ <optional>
++ <attribute name="last-rc-change"> <text /> </attribute>
++ </optional>
++ <optional>
++ <attribute name="last-run"> <text /> </attribute>
++ </optional>
++ <optional>
++ <attribute name="exec-time"> <text /> </attribute>
++ </optional>
++ <optional>
++ <attribute name="queue-time"> <text /> </attribute>
++ </optional>
++ <attribute name="rc"> <data type="integer" /> </attribute>
++ <attribute name="rc_text"> <text /> </attribute>
++ </element>
++ </define>
++
++ <define name="element-ticket">
++ <element name="ticket">
++ <attribute name="id"> <text /> </attribute>
++ <attribute name="status">
++ <choice>
++ <value>granted</value>
++ <value>revoked</value>
++ </choice>
++ </attribute>
++ <attribute name="standby"> <data type="boolean" /> </attribute>
++ <optional>
++ <attribute name="last-granted"> <text /> </attribute>
++ </optional>
++ </element>
++ </define>
++
++ <define name="element-ban">
++ <element name="ban">
++ <attribute name="id"> <text /> </attribute>
++ <attribute name="resource"> <text /> </attribute>
++ <attribute name="node"> <text /> </attribute>
++ <attribute name="weight"> <data type="integer" /> </attribute>
++ <attribute name="promoted-only"> <data type="boolean" /> </attribute>
++ <!-- DEPRECATED: master_only is a duplicate of promoted-only that is
++ provided solely for API backward compatibility. It will be
++ removed in a future release. Check promoted-only instead.
++ -->
++ <attribute name="master_only"> <data type="boolean" /> </attribute>
++ </element>
++ </define>
++</grammar>
+--
+1.8.3.1
+
+
+From da394983f106f974274ddd94675a04c85086010e Mon Sep 17 00:00:00 2001
+From: Chris Lumens <clumens(a)redhat.com>
+Date: Fri, 18 Jun 2021 15:06:34 -0400
+Subject: [PATCH 2/5] Refactor: Split node history out into its own XML schema.
+
+This allows for sharing it between crm_mon and crm_simulate.
+---
+ xml/Makefile.am | 2 +-
+ xml/api/crm_mon-2.12.rng | 64 +--------------------------------------
+ xml/api/node-history-2.12.rng | 70 +++++++++++++++++++++++++++++++++++++++++++
+ 3 files changed, 72 insertions(+), 64 deletions(-)
+ create mode 100644 xml/api/node-history-2.12.rng
+
+diff --git a/xml/Makefile.am b/xml/Makefile.am
+index b9448d4..8e7b6d3 100644
+--- a/xml/Makefile.am
++++ b/xml/Makefile.am
+@@ -64,7 +64,7 @@ API_request_base = command-output \
+ CIB_cfg_base = options nodes resources constraints fencing acls tags alerts
+
+ # Names of all schemas (including top level and those included by others)
+-API_base = $(API_request_base) fence-event failure generic-list item node-attrs nodes resources status
++API_base = $(API_request_base) fence-event failure generic-list item node-attrs node-history nodes resources status
+ CIB_base = cib $(CIB_cfg_base) status score rule nvset
+
+ # Static schema files and transforms (only CIB has transforms)
+diff --git a/xml/api/crm_mon-2.12.rng b/xml/api/crm_mon-2.12.rng
+index ffec923..be14412 100644
+--- a/xml/api/crm_mon-2.12.rng
++++ b/xml/api/crm_mon-2.12.rng
+@@ -20,7 +20,7 @@
+ <ref name="node-attributes-list" />
+ </optional>
+ <optional>
+- <ref name="node-history-list" />
++ <externalRef href="node-history-2.12.rng"/>
+ </optional>
+ <optional>
+ <ref name="failures-list" />
+@@ -113,14 +113,6 @@
+ </element>
+ </define>
+
+- <define name="node-history-list">
+- <element name="node_history">
+- <zeroOrMore>
+- <ref name="element-node-history" />
+- </zeroOrMore>
+- </element>
+- </define>
+-
+ <define name="failures-list">
+ <element name="failures">
+ <zeroOrMore>
+@@ -156,60 +148,6 @@
+ </element>
+ </define>
+
+- <define name="element-node-history">
+- <element name="node">
+- <attribute name="name"> <text /> </attribute>
+- <zeroOrMore>
+- <ref name="element-resource-history" />
+- </zeroOrMore>
+- </element>
+- </define>
+-
+- <define name="element-resource-history">
+- <element name="resource_history">
+- <attribute name="id"> <text /> </attribute>
+- <attribute name="orphan"> <data type="boolean" /> </attribute>
+- <optional>
+- <group>
+- <attribute name="migration-threshold"> <data type="nonNegativeInteger" /> </attribute>
+- <optional>
+- <attribute name="fail-count"> <text /> </attribute>
+- </optional>
+- <optional>
+- <attribute name="last-failure"> <text /> </attribute>
+- </optional>
+- </group>
+- </optional>
+- <zeroOrMore>
+- <ref name="element-operation-history" />
+- </zeroOrMore>
+- </element>
+- </define>
+-
+- <define name="element-operation-history">
+- <element name="operation_history">
+- <attribute name="call"> <text /> </attribute>
+- <attribute name="task"> <text /> </attribute>
+- <optional>
+- <attribute name="interval"> <text /> </attribute>
+- </optional>
+- <optional>
+- <attribute name="last-rc-change"> <text /> </attribute>
+- </optional>
+- <optional>
+- <attribute name="last-run"> <text /> </attribute>
+- </optional>
+- <optional>
+- <attribute name="exec-time"> <text /> </attribute>
+- </optional>
+- <optional>
+- <attribute name="queue-time"> <text /> </attribute>
+- </optional>
+- <attribute name="rc"> <data type="integer" /> </attribute>
+- <attribute name="rc_text"> <text /> </attribute>
+- </element>
+- </define>
+-
+ <define name="element-ticket">
+ <element name="ticket">
+ <attribute name="id"> <text /> </attribute>
+diff --git a/xml/api/node-history-2.12.rng b/xml/api/node-history-2.12.rng
+new file mode 100644
+index 0000000..9628000
+--- /dev/null
++++ b/xml/api/node-history-2.12.rng
+@@ -0,0 +1,70 @@
++<?xml version="1.0" encoding="UTF-8"?>
++<grammar xmlns="http://relaxng.org/ns/structure/1.0"
++ datatypeLibrary="http://www.w3.org/2001/XMLSchema-datatypes">
++
++ <start>
++ <ref name="node-history-list" />
++ </start>
++
++ <define name="node-history-list">
++ <element name="node_history">
++ <zeroOrMore>
++ <ref name="element-node-history" />
++ </zeroOrMore>
++ </element>
++ </define>
++
++ <define name="element-node-history">
++ <element name="node">
++ <attribute name="name"> <text /> </attribute>
++ <zeroOrMore>
++ <ref name="element-resource-history" />
++ </zeroOrMore>
++ </element>
++ </define>
++
++ <define name="element-resource-history">
++ <element name="resource_history">
++ <attribute name="id"> <text /> </attribute>
++ <attribute name="orphan"> <data type="boolean" /> </attribute>
++ <optional>
++ <group>
++ <attribute name="migration-threshold"> <data type="nonNegativeInteger" /> </attribute>
++ <optional>
++ <attribute name="fail-count"> <text /> </attribute>
++ </optional>
++ <optional>
++ <attribute name="last-failure"> <text /> </attribute>
++ </optional>
++ </group>
++ </optional>
++ <zeroOrMore>
++ <ref name="element-operation-history" />
++ </zeroOrMore>
++ </element>
++ </define>
++
++ <define name="element-operation-history">
++ <element name="operation_history">
++ <attribute name="call"> <text /> </attribute>
++ <attribute name="task"> <text /> </attribute>
++ <optional>
++ <attribute name="interval"> <text /> </attribute>
++ </optional>
++ <optional>
++ <attribute name="last-rc-change"> <text /> </attribute>
++ </optional>
++ <optional>
++ <attribute name="last-run"> <text /> </attribute>
++ </optional>
++ <optional>
++ <attribute name="exec-time"> <text /> </attribute>
++ </optional>
++ <optional>
++ <attribute name="queue-time"> <text /> </attribute>
++ </optional>
++ <attribute name="rc"> <data type="integer" /> </attribute>
++ <attribute name="rc_text"> <text /> </attribute>
++ </element>
++ </define>
++</grammar>
+--
+1.8.3.1
+
+
+From bf72b2615630eef7876e443d60b34d5a316de847 Mon Sep 17 00:00:00 2001
+From: Chris Lumens <clumens(a)redhat.com>
+Date: Wed, 16 Jun 2021 14:09:31 -0400
+Subject: [PATCH 3/5] Low: schemas: Copy crm_simulate.rng in preparation for
+ changes.
+
+---
+ xml/api/crm_simulate-2.12.rng | 335 ++++++++++++++++++++++++++++++++++++++++++
+ 1 file changed, 335 insertions(+)
+ create mode 100644 xml/api/crm_simulate-2.12.rng
+
+diff --git a/xml/api/crm_simulate-2.12.rng b/xml/api/crm_simulate-2.12.rng
+new file mode 100644
+index 0000000..9a7612d
+--- /dev/null
++++ b/xml/api/crm_simulate-2.12.rng
+@@ -0,0 +1,335 @@
++<?xml version="1.0" encoding="UTF-8"?>
++<grammar xmlns="http://relaxng.org/ns/structure/1.0"
++ datatypeLibrary="http://www.w3.org/2001/XMLSchema-datatypes">
++
++ <start>
++ <ref name="element-crm-simulate"/>
++ </start>
++
++ <define name="element-crm-simulate">
++ <choice>
++ <ref name="timings-list" />
++ <group>
++ <ref name="cluster-status" />
++ <optional>
++ <ref name="modifications-list" />
++ </optional>
++ <optional>
++ <ref name="allocations-utilizations-list" />
++ </optional>
++ <optional>
++ <ref name="action-list" />
++ </optional>
++ <optional>
++ <ref name="cluster-injected-actions-list" />
++ <ref name="revised-cluster-status" />
++ </optional>
++ </group>
++ </choice>
++ </define>
++
++ <define name="allocations-utilizations-list">
++ <choice>
++ <element name="allocations">
++ <zeroOrMore>
++ <choice>
++ <ref name="element-allocation" />
++ <ref name="element-promotion" />
++ </choice>
++ </zeroOrMore>
++ </element>
++ <element name="utilizations">
++ <zeroOrMore>
++ <choice>
++ <ref name="element-capacity" />
++ <ref name="element-utilization" />
++ </choice>
++ </zeroOrMore>
++ </element>
++ <element name="allocations_utilizations">
++ <zeroOrMore>
++ <choice>
++ <ref name="element-allocation" />
++ <ref name="element-promotion" />
++ <ref name="element-capacity" />
++ <ref name="element-utilization" />
++ </choice>
++ </zeroOrMore>
++ </element>
++ </choice>
++ </define>
++
++ <define name="cluster-status">
++ <element name="cluster_status">
++ <ref name="nodes-list" />
++ <ref name="resources-list" />
++ <optional>
++ <ref name="node-attributes-list" />
++ </optional>
++ <optional>
++ <ref name="failures-list" />
++ </optional>
++ </element>
++ </define>
++
++ <define name="modifications-list">
++ <element name="modifications">
++ <optional>
++ <attribute name="quorum"> <text /> </attribute>
++ </optional>
++ <optional>
++ <attribute name="watchdog"> <text /> </attribute>
++ </optional>
++ <zeroOrMore>
++ <ref name="element-inject-modify-node" />
++ </zeroOrMore>
++ <zeroOrMore>
++ <ref name="element-inject-modify-ticket" />
++ </zeroOrMore>
++ <zeroOrMore>
++ <ref name="element-inject-spec" />
++ </zeroOrMore>
++ <zeroOrMore>
++ <ref name="element-inject-attr" />
++ </zeroOrMore>
++ </element>
++ </define>
++
++ <define name="revised-cluster-status">
++ <element name="revised_cluster_status">
++ <ref name="nodes-list" />
++ <ref name="resources-list" />
++ <optional>
++ <ref name="node-attributes-list" />
++ </optional>
++ <optional>
++ <ref name="failures-list" />
++ </optional>
++ </element>
++ </define>
++
++ <define name="element-inject-attr">
++ <element name="inject_attr">
++ <attribute name="cib_node"> <text /> </attribute>
++ <attribute name="name"> <text /> </attribute>
++ <attribute name="node_path"> <text /> </attribute>
++ <attribute name="value"> <text /> </attribute>
++ </element>
++ </define>
++
++ <define name="element-inject-modify-node">
++ <element name="modify_node">
++ <attribute name="action"> <text /> </attribute>
++ <attribute name="node"> <text /> </attribute>
++ </element>
++ </define>
++
++ <define name="element-inject-spec">
++ <element name="inject_spec">
++ <attribute name="spec"> <text /> </attribute>
++ </element>
++ </define>
++
++ <define name="element-inject-modify-ticket">
++ <element name="modify_ticket">
++ <attribute name="action"> <text /> </attribute>
++ <attribute name="ticket"> <text /> </attribute>
++ </element>
++ </define>
++
++ <define name="cluster-injected-actions-list">
++ <element name="transition">
++ <zeroOrMore>
++ <ref name="element-injected-actions" />
++ </zeroOrMore>
++ </element>
++ </define>
++
++ <define name="node-attributes-list">
++ <element name="node_attributes">
++ <zeroOrMore>
++ <externalRef href="node-attrs-2.8.rng" />
++ </zeroOrMore>
++ </element>
++ </define>
++
++ <define name="failures-list">
++ <element name="failures">
++ <zeroOrMore>
++ <externalRef href="failure-2.8.rng" />
++ </zeroOrMore>
++ </element>
++ </define>
++
++ <define name="nodes-list">
++ <element name="nodes">
++ <zeroOrMore>
++ <externalRef href="nodes-2.8.rng" />
++ </zeroOrMore>
++ </element>
++ </define>
++
++ <define name="resources-list">
++ <element name="resources">
++ <zeroOrMore>
++ <externalRef href="resources-2.4.rng" />
++ </zeroOrMore>
++ </element>
++ </define>
++
++ <define name="timings-list">
++ <element name="timings">
++ <zeroOrMore>
++ <ref name="element-timing" />
++ </zeroOrMore>
++ </element>
++ </define>
++
++ <define name="action-list">
++ <element name="actions">
++ <zeroOrMore>
++ <ref name="element-node-action" />
++ </zeroOrMore>
++ <zeroOrMore>
++ <ref name="element-rsc-action" />
++ </zeroOrMore>
++ </element>
++ </define>
++
++ <define name="element-allocation">
++ <element name="node_weight">
++ <attribute name="function"> <text /> </attribute>
++ <attribute name="node"> <text /> </attribute>
++ <externalRef href="../score.rng" />
++ <optional>
++ <attribute name="id"> <text /> </attribute>
++ </optional>
++ </element>
++ </define>
++
++ <define name="element-capacity">
++ <element name="capacity">
++ <attribute name="comment"> <text /> </attribute>
++ <attribute name="node"> <text /> </attribute>
++ <zeroOrMore>
++ <element>
++ <anyName />
++ <text />
++ </element>
++ </zeroOrMore>
++ </element>
++ </define>
++
++ <define name="element-inject-cluster-action">
++ <element name="cluster_action">
++ <attribute name="node"> <text /> </attribute>
++ <attribute name="task"> <text /> </attribute>
++ <optional>
++ <attribute name="id"> <text /> </attribute>
++ </optional>
++ </element>
++ </define>
++
++ <define name="element-injected-actions">
++ <choice>
++ <ref name="element-inject-cluster-action" />
++ <ref name="element-inject-fencing-action" />
++ <ref name="element-inject-pseudo-action" />
++ <ref name="element-inject-rsc-action" />
++ </choice>
++ </define>
++
++ <define name="element-inject-fencing-action">
++ <element name="fencing_action">
++ <attribute name="op"> <text /> </attribute>
++ <attribute name="target"> <text /> </attribute>
++ </element>
++ </define>
++
++ <define name="element-node-action">
++ <element name="node_action">
++ <attribute name="node"> <text /> </attribute>
++ <attribute name="reason"> <text /> </attribute>
++ <attribute name="task"> <text /> </attribute>
++ </element>
++ </define>
++
++ <define name="element-promotion">
++ <element name="promotion_score">
++ <attribute name="id"> <text /> </attribute>
++ <externalRef href="../score.rng" />
++ <optional>
++ <attribute name="node"> <text /> </attribute>
++ </optional>
++ </element>
++ </define>
++
++ <define name="element-inject-pseudo-action">
++ <element name="pseudo_action">
++ <attribute name="task"> <text /> </attribute>
++ <optional>
++ <attribute name="node"> <text /> </attribute>
++ </optional>
++ </element>
++ </define>
++
++ <define name="element-inject-rsc-action">
++ <element name="rsc_action">
++ <attribute name="node"> <text /> </attribute>
++ <attribute name="op"> <text /> </attribute>
++ <attribute name="resource"> <text /> </attribute>
++ <optional>
++ <attribute name="interval"> <data type="integer" /> </attribute>
++ </optional>
++ </element>
++ </define>
++
++ <define name="element-timing">
++ <element name="timing">
++ <attribute name="file"> <text /> </attribute>
++ <attribute name="duration"> <data type="double" /> </attribute>
++ </element>
++ </define>
++
++ <define name="element-rsc-action">
++ <element name="rsc_action">
++ <attribute name="action"> <text /> </attribute>
++ <attribute name="resource"> <text /> </attribute>
++ <optional>
++ <attribute name="blocked"> <data type="boolean" /> </attribute>
++ </optional>
++ <optional>
++ <attribute name="dest"> <text /> </attribute>
++ </optional>
++ <optional>
++ <attribute name="next-role"> <text /> </attribute>
++ </optional>
++ <optional>
++ <attribute name="node"> <text /> </attribute>
++ </optional>
++ <optional>
++ <attribute name="reason"> <text /> </attribute>
++ </optional>
++ <optional>
++ <attribute name="role"> <text /> </attribute>
++ </optional>
++ <optional>
++ <attribute name="source"> <text /> </attribute>
++ </optional>
++ </element>
++ </define>
++
++ <define name="element-utilization">
++ <element name="utilization">
++ <attribute name="function"> <text /> </attribute>
++ <attribute name="node"> <text /> </attribute>
++ <attribute name="resource"> <text /> </attribute>
++ <zeroOrMore>
++ <element>
++ <anyName />
++ <text />
++ </element>
++ </zeroOrMore>
++ </element>
++ </define>
++</grammar>
+--
+1.8.3.1
+
+
+From c46e07788788acf5669e3f89b9344190a91c7331 Mon Sep 17 00:00:00 2001
+From: Chris Lumens <clumens(a)redhat.com>
+Date: Fri, 18 Jun 2021 15:10:19 -0400
+Subject: [PATCH 4/5] Feature: tools: Add the node-summary to crm_simulate
+ output.
+
+If --show-failcounts is given to crm_simulate, it should also display
+the node-summary message.
+
+See: rhbz#1686426
+---
+ tools/crm_simulate.c | 7 +++++--
+ xml/api/crm_simulate-2.12.rng | 3 +++
+ 2 files changed, 8 insertions(+), 2 deletions(-)
+
+diff --git a/tools/crm_simulate.c b/tools/crm_simulate.c
+index b4aa9d1..2ea292c 100644
+--- a/tools/crm_simulate.c
++++ b/tools/crm_simulate.c
+@@ -409,11 +409,14 @@ print_cluster_status(pe_working_set_t * data_set, unsigned int print_opts)
+ FALSE, FALSE, all, all, FALSE);
+
+ if (options.show_attrs) {
+- out->message(out, "node-attribute-list", data_set,
+- 0, rc == pcmk_rc_ok, FALSE, FALSE, FALSE, all, all);
++ rc = out->message(out, "node-attribute-list", data_set,
++ 0, rc == pcmk_rc_ok, FALSE, FALSE, FALSE, all, all);
+ }
+
+ if (options.show_failcounts) {
++ rc = out->message(out, "node-summary", data_set, all, all,
++ 0, print_opts, FALSE, FALSE, FALSE, FALSE, rc == pcmk_rc_ok);
++
+ out->message(out, "failed-action-list", data_set, all, all,
+ rc == pcmk_rc_ok);
+ }
+diff --git a/xml/api/crm_simulate-2.12.rng b/xml/api/crm_simulate-2.12.rng
+index 9a7612d..f90bd36 100644
+--- a/xml/api/crm_simulate-2.12.rng
++++ b/xml/api/crm_simulate-2.12.rng
+@@ -67,6 +67,9 @@
+ <ref name="node-attributes-list" />
+ </optional>
+ <optional>
++ <externalRef href="node-history-2.12.rng" />
++ </optional>
++ <optional>
+ <ref name="failures-list" />
+ </optional>
+ </element>
+--
+1.8.3.1
+
+
+From bac50336e0264604716e5997b87ee7e65311b982 Mon Sep 17 00:00:00 2001
+From: Chris Lumens <clumens(a)redhat.com>
+Date: Fri, 18 Jun 2021 15:21:52 -0400
+Subject: [PATCH 5/5] Low: libcrmcommon: Increase PCMK__API_VERSION for new
+ crm_resource output.
+
+See: rhbz#1686426
+---
+ include/crm/common/output_internal.h | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+diff --git a/include/crm/common/output_internal.h b/include/crm/common/output_internal.h
+index 0436cde..ba9c423 100644
+--- a/include/crm/common/output_internal.h
++++ b/include/crm/common/output_internal.h
+@@ -27,7 +27,7 @@ extern "C" {
+ # include <glib.h>
+ # include <crm/common/results.h>
+
+-# define PCMK__API_VERSION "2.11"
++# define PCMK__API_VERSION "2.12"
+
+ #if defined(PCMK__WITH_ATTRIBUTE_OUTPUT_ARGS)
+ # define PCMK__OUTPUT_ARGS(ARGS...) __attribute__((output_args(ARGS)))
+--
+1.8.3.1
+
diff --git a/007-unfencing-loop.patch b/007-unfencing-loop.patch
new file mode 100644
index 0000000..d4950c8
--- /dev/null
+++ b/007-unfencing-loop.patch
@@ -0,0 +1,733 @@
+From 6dcd6b51d7d3993bc483588d6ed75077518ed600 Mon Sep 17 00:00:00 2001
+From: Ken Gaillot <kgaillot(a)redhat.com>
+Date: Fri, 4 Jun 2021 16:30:55 -0500
+Subject: [PATCH 01/11] Low: controller: check whether unfenced node was remote
+ node
+
+... so the controller can indicate the node is remote (if known at that point,
+which is not guaranteed) when setting unfencing-related node attributes.
+---
+ daemons/controld/controld_fencing.c | 21 ++++++++++++++++++---
+ 1 file changed, 18 insertions(+), 3 deletions(-)
+
+diff --git a/daemons/controld/controld_fencing.c b/daemons/controld/controld_fencing.c
+index 23dff28..0fba661 100644
+--- a/daemons/controld/controld_fencing.c
++++ b/daemons/controld/controld_fencing.c
+@@ -757,15 +757,30 @@ tengine_stonith_callback(stonith_t *stonith, stonith_callback_data_t *data)
+ if (pcmk__str_eq("on", op, pcmk__str_casei)) {
+ const char *value = NULL;
+ char *now = pcmk__ttoa(time(NULL));
++ gboolean is_remote_node = FALSE;
++
++ /* This check is not 100% reliable, since this node is not
++ * guaranteed to have the remote node cached. However, it
++ * doesn't have to be reliable, since the attribute manager can
++ * learn a node's "remoteness" by other means sooner or later.
++ * This allows it to learn more quickly if this node does have
++ * the information.
++ */
++ if (g_hash_table_lookup(crm_remote_peer_cache, uuid) != NULL) {
++ is_remote_node = TRUE;
++ }
+
+- update_attrd(target, CRM_ATTR_UNFENCED, now, NULL, FALSE);
++ update_attrd(target, CRM_ATTR_UNFENCED, now, NULL,
++ is_remote_node);
+ free(now);
+
+ value = crm_meta_value(action->params, XML_OP_ATTR_DIGESTS_ALL);
+- update_attrd(target, CRM_ATTR_DIGESTS_ALL, value, NULL, FALSE);
++ update_attrd(target, CRM_ATTR_DIGESTS_ALL, value, NULL,
++ is_remote_node);
+
+ value = crm_meta_value(action->params, XML_OP_ATTR_DIGESTS_SECURE);
+- update_attrd(target, CRM_ATTR_DIGESTS_SECURE, value, NULL, FALSE);
++ update_attrd(target, CRM_ATTR_DIGESTS_SECURE, value, NULL,
++ is_remote_node);
+
+ } else if (action->sent_update == FALSE) {
+ send_stonith_update(action, target, uuid);
+--
+1.8.3.1
+
+
+From 3ef6d9403f68ab8559c45cc99f5a8da05ca6420b Mon Sep 17 00:00:00 2001
+From: Ken Gaillot <kgaillot(a)redhat.com>
+Date: Mon, 7 Jun 2021 10:50:36 -0500
+Subject: [PATCH 02/11] Refactor: pacemaker-attrd: functionize adding remote
+ node to cache
+
+... for future reuse
+---
+ daemons/attrd/attrd_commands.c | 34 +++++++++++++++++++++++-----------
+ 1 file changed, 23 insertions(+), 11 deletions(-)
+
+diff --git a/daemons/attrd/attrd_commands.c b/daemons/attrd/attrd_commands.c
+index 731c243..93a165b 100644
+--- a/daemons/attrd/attrd_commands.c
++++ b/daemons/attrd/attrd_commands.c
+@@ -102,6 +102,28 @@ free_attribute(gpointer data)
+ }
+ }
+
++/*!
++ * \internal
++ * \brief Ensure a Pacemaker Remote node is in the correct peer cache
++ *
++ * \param[in]
++ */
++static void
++cache_remote_node(const char *node_name)
++{
++ /* If we previously assumed this node was an unseen cluster node,
++ * remove its entry from the cluster peer cache.
++ */
++ crm_node_t *dup = pcmk__search_cluster_node_cache(0, node_name);
++
++ if (dup && (dup->uuid == NULL)) {
++ reap_crm_member(0, node_name);
++ }
++
++ // Ensure node is in the remote peer cache
++ CRM_ASSERT(crm_remote_peer_get(node_name) != NULL);
++}
++
+ static xmlNode *
+ build_attribute_xml(
+ xmlNode *parent, const char *name, const char *set, const char *uuid, unsigned int timeout_ms, const char *user,
+@@ -709,17 +731,7 @@ attrd_lookup_or_create_value(GHashTable *values, const char *host, xmlNode *xml)
+
+ crm_element_value_int(xml, PCMK__XA_ATTR_IS_REMOTE, &is_remote);
+ if (is_remote) {
+- /* If we previously assumed this node was an unseen cluster node,
+- * remove its entry from the cluster peer cache.
+- */
+- crm_node_t *dup = pcmk__search_cluster_node_cache(0, host);
+-
+- if (dup && (dup->uuid == NULL)) {
+- reap_crm_member(0, host);
+- }
+-
+- /* Ensure this host is in the remote peer cache */
+- CRM_ASSERT(crm_remote_peer_get(host) != NULL);
++ cache_remote_node(host);
+ }
+
+ if (v == NULL) {
+--
+1.8.3.1
+
+
+From 6fac2c71bc2c56870ac828d7cd7b7c799279c47e Mon Sep 17 00:00:00 2001
+From: Ken Gaillot <kgaillot(a)redhat.com>
+Date: Mon, 7 Jun 2021 10:39:34 -0500
+Subject: [PATCH 03/11] Refactor: pacemaker-attrd: don't try to remove votes
+ for remote nodes
+
+Remote nodes never vote.
+
+This has no effect in practice since the removal would simply do nothing,
+but we might as well not waste time trying.
+---
+ daemons/attrd/attrd_commands.c | 11 ++++++-----
+ 1 file changed, 6 insertions(+), 5 deletions(-)
+
+diff --git a/daemons/attrd/attrd_commands.c b/daemons/attrd/attrd_commands.c
+index 93a165b..dbe777e 100644
+--- a/daemons/attrd/attrd_commands.c
++++ b/daemons/attrd/attrd_commands.c
+@@ -976,7 +976,8 @@ attrd_election_cb(gpointer user_data)
+ void
+ attrd_peer_change_cb(enum crm_status_type kind, crm_node_t *peer, const void *data)
+ {
+- bool remove_voter = FALSE;
++ bool gone = false;
++ bool is_remote = pcmk_is_set(peer->flags, crm_remote_node);
+
+ switch (kind) {
+ case crm_status_uname:
+@@ -984,7 +985,7 @@ attrd_peer_change_cb(enum crm_status_type kind, crm_node_t *peer, const void *da
+
+ case crm_status_processes:
+ if (!pcmk_is_set(peer->processes, crm_get_cluster_proc())) {
+- remove_voter = TRUE;
++ gone = true;
+ }
+ break;
+
+@@ -1000,13 +1001,13 @@ attrd_peer_change_cb(enum crm_status_type kind, crm_node_t *peer, const void *da
+ } else {
+ // Remove all attribute values associated with lost nodes
+ attrd_peer_remove(peer->uname, FALSE, "loss");
+- remove_voter = TRUE;
++ gone = true;
+ }
+ break;
+ }
+
+- // In case an election is in progress, remove any vote by the node
+- if (remove_voter) {
++ // Remove votes from cluster nodes that leave, in case election in progress
++ if (gone && !is_remote) {
+ attrd_remove_voter(peer);
+ }
+ }
+--
+1.8.3.1
+
+
+From 54089fc663d6aaf10ca164c6c94b3b17237788de Mon Sep 17 00:00:00 2001
+From: Ken Gaillot <kgaillot(a)redhat.com>
+Date: Mon, 7 Jun 2021 10:40:06 -0500
+Subject: [PATCH 04/11] Low: pacemaker-attrd: check for remote nodes in peer
+ update callback
+
+If a remote node was started before the local cluster node joined the cluster,
+the cluster node will assume its node attributes are for a cluster node until
+it learns otherwise. Check for remoteness in the peer update callback, to have
+another way we can learn it.
+---
+ daemons/attrd/attrd_commands.c | 4 ++++
+ 1 file changed, 4 insertions(+)
+
+diff --git a/daemons/attrd/attrd_commands.c b/daemons/attrd/attrd_commands.c
+index dbe777e..5f6a754 100644
+--- a/daemons/attrd/attrd_commands.c
++++ b/daemons/attrd/attrd_commands.c
+@@ -1009,6 +1009,10 @@ attrd_peer_change_cb(enum crm_status_type kind, crm_node_t *peer, const void *da
+ // Remove votes from cluster nodes that leave, in case election in progress
+ if (gone && !is_remote) {
+ attrd_remove_voter(peer);
++
++ // Ensure remote nodes that come up are in the remote node cache
++ } else if (!gone && is_remote) {
++ cache_remote_node(peer->uname);
+ }
+ }
+
+--
+1.8.3.1
+
+
+From 8c048df0312d0d9c857d87b570a352429a710928 Mon Sep 17 00:00:00 2001
+From: Ken Gaillot <kgaillot(a)redhat.com>
+Date: Mon, 7 Jun 2021 11:29:12 -0500
+Subject: [PATCH 05/11] Log: pacemaker-attrd: log peer status changes
+
+---
+ daemons/attrd/attrd_commands.c | 9 +++++++++
+ 1 file changed, 9 insertions(+)
+
+diff --git a/daemons/attrd/attrd_commands.c b/daemons/attrd/attrd_commands.c
+index 5f6a754..d6d179b 100644
+--- a/daemons/attrd/attrd_commands.c
++++ b/daemons/attrd/attrd_commands.c
+@@ -972,6 +972,7 @@ attrd_election_cb(gpointer user_data)
+ return FALSE;
+ }
+
++#define state_text(state) ((state)? (const char *)(state) : "in unknown state")
+
+ void
+ attrd_peer_change_cb(enum crm_status_type kind, crm_node_t *peer, const void *data)
+@@ -981,15 +982,23 @@ attrd_peer_change_cb(enum crm_status_type kind, crm_node_t *peer, const void *da
+
+ switch (kind) {
+ case crm_status_uname:
++ crm_debug("%s node %s is now %s",
++ (is_remote? "Remote" : "Cluster"),
++ peer->uname, state_text(peer->state));
+ break;
+
+ case crm_status_processes:
+ if (!pcmk_is_set(peer->processes, crm_get_cluster_proc())) {
+ gone = true;
+ }
++ crm_debug("Node %s is %s a peer",
++ peer->uname, (gone? "no longer" : "now"));
+ break;
+
+ case crm_status_nstate:
++ crm_debug("%s node %s is now %s (was %s)",
++ (is_remote? "Remote" : "Cluster"),
++ peer->uname, state_text(peer->state), state_text(data));
+ if (pcmk__str_eq(peer->state, CRM_NODE_MEMBER, pcmk__str_casei)) {
+ /* If we're the writer, send new peers a list of all attributes
+ * (unless it's a remote node, which doesn't run its own attrd)
+--
+1.8.3.1
+
+
+From 1dcc8dee4990cf0dbdec0e14db6d9a3ad67a41d5 Mon Sep 17 00:00:00 2001
+From: Ken Gaillot <kgaillot(a)redhat.com>
+Date: Mon, 7 Jun 2021 11:13:53 -0500
+Subject: [PATCH 06/11] Low: pacemaker-attrd: ensure node ID is only set for
+ attributes when known
+
+In most cases, attribute updates contained the node ID, and the node ID was
+used by other code, only if known (i.e. positive). However a couple places did
+not check this, so add that.
+
+I am unsure whether the missing check caused problems in practice, but there
+appears to be the possibility that a remote node would wrongly be added to the
+cluster node cache.
+---
+ daemons/attrd/attrd_commands.c | 6 ++++--
+ 1 file changed, 4 insertions(+), 2 deletions(-)
+
+diff --git a/daemons/attrd/attrd_commands.c b/daemons/attrd/attrd_commands.c
+index d6d179b..b3f441c 100644
+--- a/daemons/attrd/attrd_commands.c
++++ b/daemons/attrd/attrd_commands.c
+@@ -136,7 +136,9 @@ build_attribute_xml(
+ crm_xml_add(xml, PCMK__XA_ATTR_UUID, uuid);
+ crm_xml_add(xml, PCMK__XA_ATTR_USER, user);
+ crm_xml_add(xml, PCMK__XA_ATTR_NODE_NAME, peer);
+- crm_xml_add_int(xml, PCMK__XA_ATTR_NODE_ID, peerid);
++ if (peerid > 0) {
++ crm_xml_add_int(xml, PCMK__XA_ATTR_NODE_ID, peerid);
++ }
+ crm_xml_add(xml, PCMK__XA_ATTR_VALUE, value);
+ crm_xml_add_int(xml, PCMK__XA_ATTR_DAMPENING, timeout_ms/1000);
+ crm_xml_add_int(xml, PCMK__XA_ATTR_IS_PRIVATE, is_private);
+@@ -937,7 +939,7 @@ attrd_peer_update(crm_node_t *peer, xmlNode *xml, const char *host, bool filter)
+ /* If this is a cluster node whose node ID we are learning, remember it */
+ if ((v->nodeid == 0) && (v->is_remote == FALSE)
+ && (crm_element_value_int(xml, PCMK__XA_ATTR_NODE_ID,
+- (int*)&v->nodeid) == 0)) {
++ (int*)&v->nodeid) == 0) && (v->nodeid > 0)) {
+
+ crm_node_t *known_peer = crm_get_peer(v->nodeid, host);
+
+--
+1.8.3.1
+
+
+From 8d12490e88b558d01db37a38f7d35175c6d2d69a Mon Sep 17 00:00:00 2001
+From: Ken Gaillot <kgaillot(a)redhat.com>
+Date: Thu, 10 Jun 2021 17:25:57 -0500
+Subject: [PATCH 07/11] Refactor: pacemaker-attrd: functionize processing a
+ sync response
+
+... for code isolation, and because we need to add more to it
+---
+ daemons/attrd/attrd_commands.c | 59 ++++++++++++++++++++++++++++--------------
+ 1 file changed, 39 insertions(+), 20 deletions(-)
+
+diff --git a/daemons/attrd/attrd_commands.c b/daemons/attrd/attrd_commands.c
+index b3f441c..d02d3e6 100644
+--- a/daemons/attrd/attrd_commands.c
++++ b/daemons/attrd/attrd_commands.c
+@@ -572,6 +572,43 @@ attrd_peer_clear_failure(crm_node_t *peer, xmlNode *xml)
+ }
+
+ /*!
++ * \internal
++ * \brief Load attributes from a peer sync response
++ *
++ * \param[in] peer Peer that sent clear request
++ * \param[in] peer_won Whether peer is the attribute writer
++ * \param[in] xml Request XML
++ */
++static void
++process_peer_sync_response(crm_node_t *peer, bool peer_won, xmlNode *xml)
++{
++ crm_info("Processing " PCMK__ATTRD_CMD_SYNC_RESPONSE " from %s",
++ peer->uname);
++
++ if (peer_won) {
++ /* Initialize the "seen" flag for all attributes to cleared, so we can
++ * detect attributes that local node has but the writer doesn't.
++ */
++ clear_attribute_value_seen();
++ }
++
++ // Process each attribute update in the sync response
++ for (xmlNode *child = pcmk__xml_first_child(xml); child != NULL;
++ child = pcmk__xml_next(child)) {
++ attrd_peer_update(peer, child,
++ crm_element_value(child, PCMK__XA_ATTR_NODE_NAME),
++ TRUE);
++ }
++
++ if (peer_won) {
++ /* If any attributes are still not marked as seen, the writer doesn't
++ * know about them, so send all peers an update with them.
++ */
++ attrd_current_only_attribute_update(peer, xml);
++ }
++}
++
++/*!
+ \internal
+ \brief Broadcast private attribute for local node with protocol version
+ */
+@@ -596,7 +633,7 @@ attrd_peer_message(crm_node_t *peer, xmlNode *xml)
+ const char *op = crm_element_value(xml, PCMK__XA_TASK);
+ const char *election_op = crm_element_value(xml, F_CRM_TASK);
+ const char *host = crm_element_value(xml, PCMK__XA_ATTR_NODE_NAME);
+- bool peer_won = FALSE;
++ bool peer_won = false;
+
+ if (election_op) {
+ attrd_handle_election_op(peer, xml);
+@@ -631,25 +668,7 @@ attrd_peer_message(crm_node_t *peer, xmlNode *xml)
+
+ } else if (pcmk__str_eq(op, PCMK__ATTRD_CMD_SYNC_RESPONSE, pcmk__str_casei)
+ && !pcmk__str_eq(peer->uname, attrd_cluster->uname, pcmk__str_casei)) {
+- xmlNode *child = NULL;
+-
+- crm_info("Processing %s from %s", op, peer->uname);
+-
+- /* Clear the seen flag for attribute processing held only in the own node. */
+- if (peer_won) {
+- clear_attribute_value_seen();
+- }
+-
+- for (child = pcmk__xml_first_child(xml); child != NULL;
+- child = pcmk__xml_next(child)) {
+- host = crm_element_value(child, PCMK__XA_ATTR_NODE_NAME);
+- attrd_peer_update(peer, child, host, TRUE);
+- }
+-
+- if (peer_won) {
+- /* Synchronize if there is an attribute held only by own node that Writer does not have. */
+- attrd_current_only_attribute_update(peer, xml);
+- }
++ process_peer_sync_response(peer, peer_won, xml);
+
+ } else if (pcmk__str_eq(op, PCMK__ATTRD_CMD_FLUSH, pcmk__str_casei)) {
+ /* Ignore. The flush command was removed in 2.0.0 but may be
+--
+1.8.3.1
+
+
+From a890a0e5bbbcabf907f51ed0460868035f72464d Mon Sep 17 00:00:00 2001
+From: Ken Gaillot <kgaillot(a)redhat.com>
+Date: Fri, 11 Jun 2021 14:40:39 -0500
+Subject: [PATCH 08/11] Refactor: pacemaker-attrd: functionize broadcasting
+ local override
+
+... for code isolation
+---
+ daemons/attrd/attrd_commands.c | 42 +++++++++++++++++++++++++++++-------------
+ 1 file changed, 29 insertions(+), 13 deletions(-)
+
+diff --git a/daemons/attrd/attrd_commands.c b/daemons/attrd/attrd_commands.c
+index d02d3e6..4783427 100644
+--- a/daemons/attrd/attrd_commands.c
++++ b/daemons/attrd/attrd_commands.c
+@@ -804,6 +804,34 @@ attrd_current_only_attribute_update(crm_node_t *peer, xmlNode *xml)
+ free_xml(sync);
+ }
+
++/*!
++ * \internal
++ * \brief Override an attribute sync with a local value
++ *
++ * Broadcast the local node's value for an attribute that's different from the
++ * value provided in a peer's attribute synchronization response. This ensures a
++ * node's values for itself take precedence and all peers are kept in sync.
++ *
++ * \param[in] a Attribute entry to override
++ *
++ * \return Local instance of attribute value
++ */
++static attribute_value_t *
++broadcast_local_value(attribute_t *a)
++{
++ attribute_value_t *v = g_hash_table_lookup(a->values, attrd_cluster->uname);
++ xmlNode *sync = create_xml_node(NULL, __func__);
++
++ crm_xml_add(sync, PCMK__XA_TASK, PCMK__ATTRD_CMD_SYNC_RESPONSE);
++ build_attribute_xml(sync, a->id, a->set, a->uuid, a->timeout_ms,
++ a->user, a->is_private, v->nodename, v->nodeid,
++ v->current, FALSE);
++ attrd_xml_add_writer(sync);
++ send_attrd_message(NULL, sync);
++ free_xml(sync);
++ return v;
++}
++
+ void
+ attrd_peer_update(crm_node_t *peer, xmlNode *xml, const char *host, bool filter)
+ {
+@@ -899,21 +927,9 @@ attrd_peer_update(crm_node_t *peer, xmlNode *xml, const char *host, bool filter)
+ if (filter && !pcmk__str_eq(v->current, value, pcmk__str_casei)
+ && pcmk__str_eq(host, attrd_cluster->uname, pcmk__str_casei)) {
+
+- xmlNode *sync = create_xml_node(NULL, __func__);
+-
+ crm_notice("%s[%s]: local value '%s' takes priority over '%s' from %s",
+ attr, host, v->current, value, peer->uname);
+-
+- crm_xml_add(sync, PCMK__XA_TASK, PCMK__ATTRD_CMD_SYNC_RESPONSE);
+- v = g_hash_table_lookup(a->values, host);
+- build_attribute_xml(sync, attr, a->set, a->uuid, a->timeout_ms, a->user,
+- a->is_private, v->nodename, v->nodeid, v->current, FALSE);
+-
+- attrd_xml_add_writer(sync);
+-
+- /* Broadcast in case any other nodes had the inconsistent value */
+- send_attrd_message(NULL, sync);
+- free_xml(sync);
++ v = broadcast_local_value(a);
+
+ } else if (!pcmk__str_eq(v->current, value, pcmk__str_casei)) {
+ crm_notice("Setting %s[%s]: %s -> %s " CRM_XS " from %s",
+--
+1.8.3.1
+
+
+From f6f65e3dab070f1bbdf6d1383f4d6173a8840bc9 Mon Sep 17 00:00:00 2001
+From: Ken Gaillot <kgaillot(a)redhat.com>
+Date: Fri, 11 Jun 2021 14:50:29 -0500
+Subject: [PATCH 09/11] Log: pacemaker-attrd: improve messages when
+ broadcasting local-only values
+
+The traces aren't necessary since build_attribute_xml() already logs the same
+info at debug. Also, rename function for clarity, and make static.
+---
+ daemons/attrd/attrd_commands.c | 35 ++++++++++++++++-------------------
+ 1 file changed, 16 insertions(+), 19 deletions(-)
+
+diff --git a/daemons/attrd/attrd_commands.c b/daemons/attrd/attrd_commands.c
+index 4783427..356defb 100644
+--- a/daemons/attrd/attrd_commands.c
++++ b/daemons/attrd/attrd_commands.c
+@@ -51,11 +51,12 @@ GHashTable *attributes = NULL;
+
+ void write_attribute(attribute_t *a, bool ignore_delay);
+ void write_or_elect_attribute(attribute_t *a);
+-void attrd_current_only_attribute_update(crm_node_t *peer, xmlNode *xml);
+ void attrd_peer_update(crm_node_t *peer, xmlNode *xml, const char *host, bool filter);
+ void attrd_peer_sync(crm_node_t *peer, xmlNode *xml);
+ void attrd_peer_remove(const char *host, gboolean uncache, const char *source);
+
++static void broadcast_unseen_local_values(crm_node_t *peer, xmlNode *xml);
++
+ static gboolean
+ send_attrd_message(crm_node_t * node, xmlNode * data)
+ {
+@@ -604,7 +605,7 @@ process_peer_sync_response(crm_node_t *peer, bool peer_won, xmlNode *xml)
+ /* If any attributes are still not marked as seen, the writer doesn't
+ * know about them, so send all peers an update with them.
+ */
+- attrd_current_only_attribute_update(peer, xml);
++ broadcast_unseen_local_values(peer, xml);
+ }
+ }
+
+@@ -768,40 +769,36 @@ attrd_lookup_or_create_value(GHashTable *values, const char *host, xmlNode *xml)
+ return(v);
+ }
+
+-void
+-attrd_current_only_attribute_update(crm_node_t *peer, xmlNode *xml)
++void
++broadcast_unseen_local_values(crm_node_t *peer, xmlNode *xml)
+ {
+ GHashTableIter aIter;
+ GHashTableIter vIter;
+- attribute_t *a;
++ attribute_t *a = NULL;
+ attribute_value_t *v = NULL;
+- xmlNode *sync = create_xml_node(NULL, __func__);
+- gboolean build = FALSE;
+-
+- crm_xml_add(sync, PCMK__XA_TASK, PCMK__ATTRD_CMD_SYNC_RESPONSE);
++ xmlNode *sync = NULL;
+
+ g_hash_table_iter_init(&aIter, attributes);
+ while (g_hash_table_iter_next(&aIter, NULL, (gpointer *) & a)) {
+ g_hash_table_iter_init(&vIter, a->values);
+ while (g_hash_table_iter_next(&vIter, NULL, (gpointer *) & v)) {
+- if (pcmk__str_eq(v->nodename, attrd_cluster->uname, pcmk__str_casei) && v->seen == FALSE) {
+- crm_trace("Syncing %s[%s] = %s to everyone.(from local only attributes)", a->id, v->nodename, v->current);
+-
+- build = TRUE;
++ if (!(v->seen) && pcmk__str_eq(v->nodename, attrd_cluster->uname,
++ pcmk__str_casei)) {
++ if (sync == NULL) {
++ sync = create_xml_node(NULL, __func__);
++ crm_xml_add(sync, PCMK__XA_TASK, PCMK__ATTRD_CMD_SYNC_RESPONSE);
++ }
+ build_attribute_xml(sync, a->id, a->set, a->uuid, a->timeout_ms, a->user, a->is_private,
+ v->nodename, v->nodeid, v->current, (a->timeout_ms && a->timer ? TRUE : FALSE));
+- } else {
+- crm_trace("Local attribute(%s[%s] = %s) was ignore.(another host) : [%s]", a->id, v->nodename, v->current, attrd_cluster->uname);
+- continue;
+ }
+ }
+ }
+
+- if (build) {
+- crm_debug("Syncing values to everyone.(from local only attributes)");
++ if (sync != NULL) {
++ crm_debug("Broadcasting local-only values");
+ send_attrd_message(NULL, sync);
++ free_xml(sync);
+ }
+- free_xml(sync);
+ }
+
+ /*!
+--
+1.8.3.1
+
+
+From ab90ffb785ea018556f216b8f540f8c3429a3947 Mon Sep 17 00:00:00 2001
+From: Ken Gaillot <kgaillot(a)redhat.com>
+Date: Fri, 11 Jun 2021 15:04:20 -0500
+Subject: [PATCH 10/11] Refactor: pacemaker-attrd: simplify attribute XML
+ creation function
+
+... and rename for clarity
+---
+ daemons/attrd/attrd_commands.c | 48 ++++++++++++++++++++++++------------------
+ 1 file changed, 27 insertions(+), 21 deletions(-)
+
+diff --git a/daemons/attrd/attrd_commands.c b/daemons/attrd/attrd_commands.c
+index 356defb..5b32a77 100644
+--- a/daemons/attrd/attrd_commands.c
++++ b/daemons/attrd/attrd_commands.c
+@@ -125,25 +125,35 @@ cache_remote_node(const char *node_name)
+ CRM_ASSERT(crm_remote_peer_get(node_name) != NULL);
+ }
+
++/*!
++ * \internal
++ * \brief Create an XML representation of an attribute for use in peer messages
++ *
++ * \param[in] parent Create attribute XML as child element of this element
++ * \param[in] a Attribute to represent
++ * \param[in] v Attribute value to represent
++ * \param[in] force_write If true, value should be written even if unchanged
++ *
++ * \return XML representation of attribute
++ */
+ static xmlNode *
+-build_attribute_xml(
+- xmlNode *parent, const char *name, const char *set, const char *uuid, unsigned int timeout_ms, const char *user,
+- gboolean is_private, const char *peer, uint32_t peerid, const char *value, gboolean is_force_write)
++add_attribute_value_xml(xmlNode *parent, attribute_t *a, attribute_value_t *v,
++ bool force_write)
+ {
+ xmlNode *xml = create_xml_node(parent, __func__);
+
+- crm_xml_add(xml, PCMK__XA_ATTR_NAME, name);
+- crm_xml_add(xml, PCMK__XA_ATTR_SET, set);
+- crm_xml_add(xml, PCMK__XA_ATTR_UUID, uuid);
+- crm_xml_add(xml, PCMK__XA_ATTR_USER, user);
+- crm_xml_add(xml, PCMK__XA_ATTR_NODE_NAME, peer);
+- if (peerid > 0) {
+- crm_xml_add_int(xml, PCMK__XA_ATTR_NODE_ID, peerid);
++ crm_xml_add(xml, PCMK__XA_ATTR_NAME, a->id);
++ crm_xml_add(xml, PCMK__XA_ATTR_SET, a->set);
++ crm_xml_add(xml, PCMK__XA_ATTR_UUID, a->uuid);
++ crm_xml_add(xml, PCMK__XA_ATTR_USER, a->user);
++ crm_xml_add(xml, PCMK__XA_ATTR_NODE_NAME, v->nodename);
++ if (v->nodeid > 0) {
++ crm_xml_add_int(xml, PCMK__XA_ATTR_NODE_ID, v->nodeid);
+ }
+- crm_xml_add(xml, PCMK__XA_ATTR_VALUE, value);
+- crm_xml_add_int(xml, PCMK__XA_ATTR_DAMPENING, timeout_ms/1000);
+- crm_xml_add_int(xml, PCMK__XA_ATTR_IS_PRIVATE, is_private);
+- crm_xml_add_int(xml, PCMK__XA_ATTR_FORCE, is_force_write);
++ crm_xml_add(xml, PCMK__XA_ATTR_VALUE, v->current);
++ crm_xml_add_int(xml, PCMK__XA_ATTR_DAMPENING, a->timeout_ms / 1000);
++ crm_xml_add_int(xml, PCMK__XA_ATTR_IS_PRIVATE, a->is_private);
++ crm_xml_add_int(xml, PCMK__XA_ATTR_FORCE, force_write);
+
+ return xml;
+ }
+@@ -695,8 +705,7 @@ attrd_peer_sync(crm_node_t *peer, xmlNode *xml)
+ g_hash_table_iter_init(&vIter, a->values);
+ while (g_hash_table_iter_next(&vIter, NULL, (gpointer *) & v)) {
+ crm_debug("Syncing %s[%s] = %s to %s", a->id, v->nodename, v->current, peer?peer->uname:"everyone");
+- build_attribute_xml(sync, a->id, a->set, a->uuid, a->timeout_ms, a->user, a->is_private,
+- v->nodename, v->nodeid, v->current, FALSE);
++ add_attribute_value_xml(sync, a, v, false);
+ }
+ }
+
+@@ -788,8 +797,7 @@ broadcast_unseen_local_values(crm_node_t *peer, xmlNode *xml)
+ sync = create_xml_node(NULL, __func__);
+ crm_xml_add(sync, PCMK__XA_TASK, PCMK__ATTRD_CMD_SYNC_RESPONSE);
+ }
+- build_attribute_xml(sync, a->id, a->set, a->uuid, a->timeout_ms, a->user, a->is_private,
+- v->nodename, v->nodeid, v->current, (a->timeout_ms && a->timer ? TRUE : FALSE));
++ add_attribute_value_xml(sync, a, v, a->timeout_ms && a->timer);
+ }
+ }
+ }
+@@ -820,9 +828,7 @@ broadcast_local_value(attribute_t *a)
+ xmlNode *sync = create_xml_node(NULL, __func__);
+
+ crm_xml_add(sync, PCMK__XA_TASK, PCMK__ATTRD_CMD_SYNC_RESPONSE);
+- build_attribute_xml(sync, a->id, a->set, a->uuid, a->timeout_ms,
+- a->user, a->is_private, v->nodename, v->nodeid,
+- v->current, FALSE);
++ add_attribute_value_xml(sync, a, v, false);
+ attrd_xml_add_writer(sync);
+ send_attrd_message(NULL, sync);
+ free_xml(sync);
+--
+1.8.3.1
+
+
+From 540d74130c5c8d9c626d6c50475e4dc4f64234e7 Mon Sep 17 00:00:00 2001
+From: Ken Gaillot <kgaillot(a)redhat.com>
+Date: Fri, 4 Jun 2021 16:34:26 -0500
+Subject: [PATCH 11/11] Fix: pacemaker-attrd: avoid repeated unfencing of
+ remote nodes
+
+The attribute manager can't record a remote node's attributes to the CIB until
+it knows the node is remote. Normally, this is learned when the remote node
+starts, because the controller clears the CRM_OP_PROBED attribute and indicates
+that it is for a remote node.
+
+However, if a cluster node is down when a remote node starts, and later comes
+up, it learns the remote node's existing attributes as part of the attribute
+sync. Previously, this did not include whether each value is for a cluster or
+remote node, so the newly joined attribute manager couldn't write out remote
+nodes' attributes until it learned that via some other event -- which might not
+happen before the node becomes DC, in which case its scheduler will not see any
+unfencing-related node attributes and may wrongly schedule unfencing.
+
+The sync response handling already calls attrd_lookup_or_create_value(), which
+checks PCMK__XA_ATTR_IS_REMOTE, so all we need to do is add that to the sync
+response.
+---
+ daemons/attrd/attrd_commands.c | 6 +++++-
+ 1 file changed, 5 insertions(+), 1 deletion(-)
+
+diff --git a/daemons/attrd/attrd_commands.c b/daemons/attrd/attrd_commands.c
+index 5b32a77..0142383 100644
+--- a/daemons/attrd/attrd_commands.c
++++ b/daemons/attrd/attrd_commands.c
+@@ -43,8 +43,9 @@
+ * 1 1.1.15 PCMK__ATTRD_CMD_UPDATE_BOTH,
+ * PCMK__ATTRD_CMD_UPDATE_DELAY
+ * 2 1.1.17 PCMK__ATTRD_CMD_CLEAR_FAILURE
++ * 3 2.1.1 PCMK__ATTRD_CMD_SYNC_RESPONSE indicates remote nodes
+ */
+-#define ATTRD_PROTOCOL_VERSION "2"
++#define ATTRD_PROTOCOL_VERSION "3"
+
+ int last_cib_op_done = 0;
+ GHashTable *attributes = NULL;
+@@ -150,6 +151,9 @@ add_attribute_value_xml(xmlNode *parent, attribute_t *a, attribute_value_t *v,
+ if (v->nodeid > 0) {
+ crm_xml_add_int(xml, PCMK__XA_ATTR_NODE_ID, v->nodeid);
+ }
++ if (v->is_remote != 0) {
++ crm_xml_add_int(xml, PCMK__XA_ATTR_IS_REMOTE, 1);
++ }
+ crm_xml_add(xml, PCMK__XA_ATTR_VALUE, v->current);
+ crm_xml_add_int(xml, PCMK__XA_ATTR_DAMPENING, a->timeout_ms / 1000);
+ crm_xml_add_int(xml, PCMK__XA_ATTR_IS_PRIVATE, a->is_private);
+--
+1.8.3.1
+
diff --git a/gating.yaml b/gating.yaml
index 9e15ced..e8aadb9 100644
--- a/gating.yaml
+++ b/gating.yaml
@@ -5,6 +5,7 @@ decision_context: bodhi_update_push_testing
subject_type: koji_build
rules:
- !PassingTestCaseRule {test_case_name: fedora-ci.koji-build.tier0.functional}
+
--- !Policy
product_versions:
- fedora-*
@@ -13,3 +14,9 @@ subject_type: koji_build
rules:
- !PassingTestCaseRule {test_case_name: fedora-ci.koji-build.tier0.functional}
+--- !Policy
+product_versions:
+ - rhel-*
+decision_context: osci_compose_gate
+rules:
+ - !PassingTestCaseRule {test_case_name: osci.brew-build.tier0.functional}
diff --git a/pacemaker.spec b/pacemaker.spec
index 1f9dce2..48b6a44 100644
--- a/pacemaker.spec
+++ b/pacemaker.spec
@@ -1,9 +1,9 @@
-# Globals and defines to control package behavior (configure these as desired)
+# User-configurable globals and defines to control package behavior
+# (these should not test {with X} values, which are declared later)
## User and group to use for nonprivileged services
%global uname hacluster
%global gname haclient
-%global hacluster_id 189
## Where to install Pacemaker documentation
%if 0%{?rhel}
@@ -17,6 +17,13 @@
## Where bug reports should be submitted
## Leave bug_url undefined to use ClusterLabs default, others define it here
+%if 0%{?rhel}
+%global bug_url https://bugzilla.redhat.com/
+%else
+%if 0%{?fedora}
+%global bug_url https://bugz.fedoraproject.org/%{name}
+%endif
+%endif
## What to use as the OCF resource agent root directory
%global ocf_root %{_prefix}/lib/ocf
@@ -25,19 +32,21 @@
## can be incremented to build packages reliably considered "newer"
## than previously built packages with the same pcmkversion)
%global pcmkversion 2.1.0
-%global specversion 4
+%global specversion 6
+
+## Upstream commit (full commit ID, abbreviated commit ID, or tag) to build
+%global commit 7c3f660707a495a1331716ad32cd3ac9d9f8ff58
-## Upstream commit (or git tag, such as "Pacemaker-" plus the
-## {pcmkversion} macro for an official release) to use for this package
-%global commit Pacemaker-2.1.0-rc3
## Since git v2.11, the extent of abbreviation is autoscaled by default
## (used to be constant of 7), so we need to convey it for non-tags, too.
%global commit_abbrev 9
+
# Define conditionals so that "rpmbuild --with <feature>" and
# "rpmbuild --without <feature>" can enable and disable specific features
-## NOTE: skip --with stonithd
+## Add option to enable support for stonith/external fencing agents
+%bcond_with stonithd
## Add option for whether to support storing sensitive information outside CIB
%if (0%{?fedora} && 0%{?fedora} <= 33) || (0%{?rhel} && 0%{?rhel} <= 8)
@@ -52,8 +61,13 @@
## Add option to create binaries with coverage analysis
%bcond_with coverage
-## Add option to skip/enable generating documentation
+## Add option to skip (or enable, on RHEL) generating documentation
+## (the build tools aren't available everywhere)
+%if 0%{?rhel}
+%bcond_with doc
+%else
%bcond_without doc
+%endif
## Add option to default to start-up synchronization with SBD.
##
@@ -66,16 +80,19 @@
## Add option to prefix package version with "0."
## (so later "official" packages will be considered updates)
-%bcond_without pre_release
+%bcond_with pre_release
## NOTE: skip --with upstart_job
## Add option to turn off hardening of libraries and daemon executables
%bcond_without hardening
-## Add option to enable links for legacy daemon names
+## Add option to enable (or disable, on RHEL 8) links for legacy daemon names
+%if 0%{?rhel} && 0%{?rhel} <= 8
+%bcond_without legacy_links
+%else
%bcond_with legacy_links
-
+%endif
## Nagios source control identifiers
%global nagios_name nagios-agents-metadata
@@ -94,29 +111,36 @@
## Portion of export/dist tarball name after "pacemaker-", and release version
%if 0%{tag_release}
-%define archive_version %{commit}
+%define archive_version %(c=%{commit}; echo ${c:10})
%define archive_github_url %{commit}#/%{name}-%{archive_version}.tar.gz
-%define pcmk_release %(c=%{commit}; case $c in *-rc[[:digit:]]*%{rparen}
- echo 0.%{specversion}.${c: -3} ;;
- *%{rparen} echo %{specversion} ;; esac)
%else
%define archive_version %(c=%{commit}; echo ${c:0:%{commit_abbrev}})
%define archive_github_url %{archive_version}#/%{name}-%{archive_version}.tar.gz
-%if %{with pre_release}
-%define pcmk_release 0.%{specversion}.%{archive_version}.git
-%else
-%define pcmk_release %{specversion}.%{archive_version}.git
-%endif
%endif
+### Always use a simple release number
+%define pcmk_release %{specversion}
## Base GnuTLS cipher priorities (presumably only the initial, required keyword)
## overridable with "rpmbuild --define 'pcmk_gnutls_priorities PRIORITY-SPEC'"
%define gnutls_priorities %{?pcmk_gnutls_priorities}%{!?pcmk_gnutls_priorities:@SYSTEM}
+## Different distros name certain packages differently
+## (note: corosync libraries also differ, but all provide corosync-devel)
+%global pkgname_libtool_devel libtool-ltdl-devel
+%global pkgname_libtool_devel_arch libtool-ltdl-devel%{?_isa}
+%global pkgname_bzip2_devel bzip2-devel
+%global pkgname_docbook_xsl docbook-style-xsl
+%global pkgname_gnutls_devel gnutls-devel
+%global pkgname_shadow_utils shadow-utils
+%global pkgname_procps procps-ng
+%global pkgname_glue_libs cluster-glue-libs
+%global pkgname_pcmk_libs %{name}-libs
+%global hacluster_id 189
+
## Distro-specific configuration choices
### Use 2.0-style output when other distro packages don't support current output
-%if 0%{?fedora}
+%if 0%{?fedora} || ( 0%{?rhel} && 0%{?rhel} <= 8 )
%global compat20 --enable-compat-2.0
%endif
@@ -130,6 +154,7 @@
%global resource_stickiness --with-resource-stickiness-default=1
%endif
+
# Python-related definitions
## Turn off auto-compilation of Python files outside Python specific paths,
@@ -145,13 +170,24 @@
sed -e 's!/usr/lib[^[:space:]]*/brp-python-bytecompile[[:space:]].*$!!g'; })
%endif
-## Values that differ by Python major version
+## Prefer Python 3 definitions explicitly, in case 2 is also available
+%if %{defined __python3}
%global python_name python3
-%global python_path %{?__python3}%{!?__python3:/usr/bin/python%{?python3_pkgversion}%{!?python3_pkgversion:3}}
+%global python_path %{__python3}
%define python_site %{?python3_sitelib}%{!?python3_sitelib:%(
%{python_path} -c 'from distutils.sysconfig import get_python_lib as gpl; print(gpl(1))' 2>/dev/null)}
-%global python_min 3.2
-# python_min still required?
+%else
+%if %{defined python_version}
+%global python_name python%(echo %{python_version} | cut -d'.' -f1)
+%define python_path %{?__python}%{!?__python:/usr/bin/%{python_name}}
+%else
+%global python_name python
+%global python_path %{?__python}%{!?__python:/usr/bin/python%{?python_pkgversion}}
+%endif
+%define python_site %{?python_sitelib}%{!?python_sitelib:%(
+ %{python_name} -c 'from distutils.sysconfig import get_python_lib as gpl; print(gpl(1))' 2>/dev/null)}
+%endif
+
# Keep sane profiling data if requested
%if %{with profiling}
@@ -165,25 +201,46 @@
Name: pacemaker
Summary: Scalable High-Availability cluster resource manager
Version: %{pcmkversion}
-Release: %{pcmk_release}%{?dist}.1
+Release: %{pcmk_release}%{?dist}
License: GPLv2+ and LGPLv2+
-Url: https://www.clusterlabs.org
-
-# Hint: use "spectool -s 0 pacemaker.spec" (rpmdevtools) to check the final URL
+Url: https://www.clusterlabs.org/
+
+# Example: https://codeload.github.com/ClusterLabs/pacemaker/tar.gz/e91769e
+# will download pacemaker-e91769e.tar.gz
+#
+# The ending part starting with '#' is ignored by github but necessary for
+# rpmbuild to know what the tar archive name is. (The downloaded file will be
+# named correctly only for commit IDs, not tagged releases.)
+#
+# You can use "spectool -s 0 pacemaker.spec" (rpmdevtools) to show final URL.
Source0: https://codeload.github.com/%{github_owner}/%{name}/tar.gz/%{archive_gith...
Source1: https://codeload.github.com/%{github_owner}/%{nagios_name}/tar.gz/%{nagio...
-# ---
+
+# upstream commits
+Patch1: 001-ping-agent.patch
+Patch2: 002-pacemakerd-options.patch
+Patch3: 003-pacemakerd-output.patch
+Patch4: 004-check-level.patch
+Patch5: 005-crm_resource.patch
+Patch6: 006-crm_simulate.patch
+Patch7: 007-unfencing-loop.patch
Requires: resource-agents
-Requires: %{name}-libs%{?_isa} = %{version}-%{release}
+Requires: %{pkgname_pcmk_libs}%{?_isa} = %{version}-%{release}
Requires: %{name}-cluster-libs%{?_isa} = %{version}-%{release}
Requires: %{name}-cli = %{version}-%{release}
%{?systemd_requires}
-# Pacemaker requires a minimum Python functionality
-Requires: %{python_name} >= %{python_min}
-BuildRequires: make
-BuildRequires: %{python_name}-devel >= %{python_min}
+%if %{defined centos}
+ExclusiveArch: aarch64 i686 ppc64le s390x x86_64 %{arm}
+%else
+%if 0%{?rhel}
+ExclusiveArch: aarch64 i686 ppc64le s390x x86_64
+%endif
+%endif
+
+Requires: %{python_path}
+BuildRequires: %{python_name}-devel
# Pacemaker requires a minimum libqb functionality
Requires: libqb >= 0.17.0
@@ -192,37 +249,41 @@ BuildRequires: libqb-devel >= 0.17.0
# Required basic build tools
BuildRequires: coreutils findutils grep sed
BuildRequires: autoconf automake gcc make pkgconfig
-BuildRequires: libtool libtool-ltdl-devel
+BuildRequires: libtool %{?pkgname_libtool_devel}
# Required for core functionality
BuildRequires: pkgconfig(glib-2.0) >= 2.42
BuildRequires: libxml2-devel libxslt-devel libuuid-devel
-BuildRequires: bzip2-devel
+BuildRequires: %{pkgname_bzip2_devel}
# Enables optional functionality
-BuildRequires: ncurses-devel docbook-style-xsl
-BuildRequires: help2man gnutls-devel pam-devel pkgconfig(dbus-1)
+BuildRequires: ncurses-devel %{pkgname_docbook_xsl}
+BuildRequires: help2man %{pkgname_gnutls_devel} pam-devel pkgconfig(dbus-1)
BuildRequires: pkgconfig(systemd)
+# RH patches are created by git, so we need git to apply them
+BuildRequires: git
+
Requires: corosync >= 2.0.0
BuildRequires: corosync-devel >= 2.0.0
-#XXX
-#BuildRequires: pkgconfig(libcpg)
-#BuildRequires: pkgconfig(libcfg)
+
+%if %{with stonithd}
+BuildRequires: %{pkgname_glue_libs}-devel
+%endif
%if %{with doc}
BuildRequires: asciidoc inkscape %{python_name}-sphinx
%endif
-# git-style patch application
-#BuildRequires: git
-
Provides: pcmk-cluster-manager = %{version}-%{release}
Provides: pcmk-cluster-manager%{?_isa} = %{version}-%{release}
-# Pacemaker uses the crypto/md5 module from gnulib
+# Bundled bits
+## Pacemaker uses the crypto/md5-buffer module from gnulib
+%if 0%{?fedora} || 0%{?rhel}
Provides: bundled(gnulib)
+%endif
%description
Pacemaker is an advanced, scalable High-Availability cluster resource
@@ -236,18 +297,18 @@ when related resources fail and can be configured to periodically check
resource health.
Available rpmbuild rebuild options:
- --with(out) : cibsecrets coverage doc hardening pre_release profiling
+ --with(out) : cibsecrets coverage doc hardening pre_release profiling stonithd
%package cli
License: GPLv2+ and LGPLv2+
Summary: Command line tools for controlling Pacemaker clusters
-Requires: %{name}-libs%{?_isa} = %{version}-%{release}
+Requires: %{pkgname_pcmk_libs}%{?_isa} = %{version}-%{release}
Recommends: pcmk-cluster-manager = %{version}-%{release}
# For crm_report
Recommends: tar
Recommends: bzip2
Requires: perl-TimeDate
-Requires: procps-ng
+Requires: %{pkgname_procps}
Requires: psmisc
Requires(post):coreutils
@@ -259,28 +320,29 @@ The %{name}-cli package contains command line tools that can be used
to query and control the cluster from machines that may, or may not,
be part of the cluster.
-%package libs
+%package -n %{pkgname_pcmk_libs}
License: GPLv2+ and LGPLv2+
Summary: Core Pacemaker libraries
-Requires(pre): shadow-utils
+Requires(pre): %{pkgname_shadow_utils}
Requires: %{name}-schemas = %{version}-%{release}
# sbd 1.4.0+ supports the libpe_status API for pe_working_set_t
# sbd 1.4.2+ supports startup/shutdown handshake via pacemakerd-api
# and handshake defaults to enabled for rhel builds
-# sbd 1.4.3+ handshake defaults to enabled for fedora builds
-Conflicts: sbd < 1.4.3
+# sbd 1.5.0+ handshake defaults to enabled with upstream sbd-release
+# implicitly supports handshake defaults to enabled in this spec
+Conflicts: sbd < 1.5.0
-%description libs
+%description -n %{pkgname_pcmk_libs}
Pacemaker is an advanced, scalable High-Availability cluster resource
manager.
-The %{name}-libs package contains shared libraries needed for cluster
+The %{pkgname_pcmk_libs} package contains shared libraries needed for cluster
nodes and those just running the CLI tools.
%package cluster-libs
License: GPLv2+ and LGPLv2+
Summary: Cluster Libraries used by Pacemaker
-Requires: %{name}-libs%{?_isa} = %{version}-%{release}
+Requires: %{pkgname_pcmk_libs}%{?_isa} = %{version}-%{release}
%description cluster-libs
Pacemaker is an advanced, scalable High-Availability cluster resource
@@ -292,7 +354,7 @@ libraries needed for nodes that will form part of the cluster nodes.
%package remote
License: GPLv2+ and LGPLv2+
Summary: Pacemaker remote executor daemon for non-cluster nodes
-Requires: %{name}-libs%{?_isa} = %{version}-%{release}
+Requires: %{pkgname_pcmk_libs}%{?_isa} = %{version}-%{release}
Requires: %{name}-cli = %{version}-%{release}
Requires: resource-agents
# -remote can be fully independent of systemd
@@ -308,35 +370,40 @@ The %{name}-remote package contains the Pacemaker Remote daemon
which is capable of extending pacemaker functionality to remote
nodes not running the full corosync/cluster stack.
-%package libs-devel
+%package -n %{pkgname_pcmk_libs}-devel
License: GPLv2+ and LGPLv2+
Summary: Pacemaker development package
-Requires: %{name}-libs%{?_isa} = %{version}-%{release}
+Requires: %{pkgname_pcmk_libs}%{?_isa} = %{version}-%{release}
Requires: %{name}-cluster-libs%{?_isa} = %{version}-%{release}
-Requires: libtool-ltdl-devel%{?_isa} libuuid-devel%{?_isa}
+Requires: %{?pkgname_libtool_devel_arch} libuuid-devel%{?_isa}
Requires: libxml2-devel%{?_isa} libxslt-devel%{?_isa}
-Requires: bzip2-devel%{?_isa} glib2-devel%{?_isa}
+Requires: %{pkgname_bzip2_devel}%{?_isa} glib2-devel%{?_isa}
Requires: libqb-devel%{?_isa}
Requires: corosync-devel%{?_isa} >= 2.0.0
-%description libs-devel
+%description -n %{pkgname_pcmk_libs}-devel
Pacemaker is an advanced, scalable High-Availability cluster resource
manager.
-The %{name}-libs-devel package contains headers and shared libraries
+The %{pkgname_pcmk_libs}-devel package contains headers and shared libraries
for developing tools for Pacemaker.
%package cts
License: GPLv2+ and LGPLv2+
Summary: Test framework for cluster-related technologies like Pacemaker
Requires: %{python_path}
-Requires: %{python_name} >= %{python_min}
-Requires: %{name}-libs = %{version}-%{release}
-Requires: procps-ng
+Requires: %{pkgname_pcmk_libs} = %{version}-%{release}
+Requires: %{name}-cli = %{version}-%{release}
+Requires: %{pkgname_procps}
Requires: psmisc
BuildArch: noarch
+# systemd Python bindings are a separate package in some distros
+%if %{defined systemd_requires}
+%if %{defined fedora} || %{defined rhel}
Requires: %{python_name}-systemd
+%endif
+%endif
%description cts
Test framework for cluster-related technologies like Pacemaker
@@ -382,7 +449,10 @@ The metadata files required for Pacemaker to execute the nagios plugin
monitor resources.
%prep
-%setup -q -a 1 -n %{name}-%{archive_version}
+%autosetup -a 1 -n %{name}-%{archive_version} -S git_am -p 1
+# in f33 s390x complains but shouldn't hurt globally
+# as configure.ac is checking for support
+sed -i configure.ac -e "s/-Wall/-Wall -Wno-format-truncation/"
%build
@@ -420,11 +490,10 @@ export LDFLAGS_HARDENED_LIB="%{?_hardening_ldflags}"
--with-initdir=%{_initrddir} \
--with-runstatedir=%{_rundir} \
--localstatedir=%{_var} \
- --with-version=%{version}-%{release} \
- --with-bug-url=https://bugz.fedoraproject.org/%{name} \
- --with-nagios \
- --with-nagios-metadata-dir=%{_datadir}/pacemaker/nagios/plugins-metadata/ \
- --with-nagios-plugin-dir=%{_libdir}/nagios/plugins/
+ --with-nagios \
+ --with-nagios-metadata-dir=%{_datadir}/pacemaker/nagios/plugins-metadata/ \
+ --with-nagios-plugin-dir=%{_libdir}/nagios/plugins/ \
+ --with-version=%{version}-%{release}
make %{_smp_mflags} V=1
@@ -453,11 +522,7 @@ done
mkdir -p ${RPM_BUILD_ROOT}%{_localstatedir}/lib/rpm-state/%{name}
-# These are not actually scripts
-find %{buildroot} -name '*.xml' -type f -print0 | xargs -0 chmod a-x
-
-# Don't package static libs
-find %{buildroot} -name '*.a' -type f -print0 | xargs -0 rm -f
+# Don't package libtool archives
find %{buildroot} -name '*.la' -type f -print0 | xargs -0 rm -f
# Do not package these either
@@ -475,7 +540,7 @@ rm -f %{buildroot}/%{_initrddir}/pacemaker
rm -f %{buildroot}/%{_initrddir}/pacemaker_remote
# Byte-compile Python sources where suitable and the distro procedures known
-%if %{defined py_byte_compile} && %{defined python_path}
+%if %{defined py_byte_compile}
%{py_byte_compile %{python_path} %{buildroot}%{_datadir}/pacemaker/tests}
%if !%{defined _python_bytecompile_extra}
%{py_byte_compile %{python_path} %{buildroot}%{python_site}/cts}
@@ -552,15 +617,14 @@ fi
%postun cli
%systemd_postun_with_restart crm_mon.service
-%pre libs
-# XXX keep an eye on https://fedoraproject.org/wiki/Changes/SystemdSysusers
-# reopened recently:
-# https://lists.fedoraproject.org/archives/list/devel@lists.fedoraproject.o...
+%pre -n %{pkgname_pcmk_libs}
+# @TODO Use sysusers.d:
+# https://fedoraproject.org/wiki/Changes/Adopting_sysusers.d_format
getent group %{gname} >/dev/null || groupadd -r %{gname} -g %{hacluster_id}
getent passwd %{uname} >/dev/null || useradd -r -g %{gname} -u %{hacluster_id} -s /sbin/nologin -c "cluster user" %{uname}
exit 0
-%ldconfig_scriptlets libs
+%ldconfig_scriptlets -n %{pkgname_pcmk_libs}
%ldconfig_scriptlets cluster-libs
%files
@@ -667,7 +731,7 @@ exit 0
%dir %attr (770, %{uname}, %{gname}) %{_var}/log/pacemaker
%dir %attr (770, %{uname}, %{gname}) %{_var}/log/pacemaker/bundles
-%files libs
+%files -n %{pkgname_pcmk_libs}
%{_libdir}/libcib.so.*
%{_libdir}/liblrmd.so.*
%{_libdir}/libcrmservice.so.*
@@ -716,7 +780,7 @@ exit 0
%doc COPYING
%doc ChangeLog
-%files libs-devel
+%files -n %{pkgname_pcmk_libs}-devel
%{_includedir}/pacemaker
%{_libdir}/*.so
%if %{with coverage}
@@ -742,6 +806,12 @@ exit 0
%license %{nagios_name}-%{nagios_hash}/COPYING
%changelog
+* Mon Jul 5 2021 Klaus Wenninger <kwenning(a)redhat.com> - 2.1.0-6
+- synced/merged with CS9 spec-file for current 2.1.0-release build
+
+* Tue Jun 8 2021 Klaus Wenninger <kwenning(a)redhat.com> - 2.1.0-0.5.rc3
+- silence f33 s390x build complaining about possible format-trucation
+
* Mon Jun 07 2021 Python Maint <python-maint(a)redhat.com> - 2.1.0-0.4.rc3.1
- Rebuilt for Python 3.10
@@ -753,7 +823,7 @@ exit 0
* Fri Jun 04 2021 Python Maint <python-maint(a)redhat.com> - 2.1.0-0.3.rc2.1
- Rebuilt for Python 3.10
-* Mon Jun 1 2021 Klaus Wenninger <kwenning(a)redhat.com> - 2.1.0-0.3.rc2
+* Mon May 31 2021 Klaus Wenninger <kwenning(a)redhat.com> - 2.1.0-0.3.rc2
- Update for new upstream tarball for release candidate: Pacemaker-2.1.0-rc2,
for full details, see included ChangeLog file or
https://github.com/ClusterLabs/pacemaker/releases/tag/Pacemaker-2.1.0-rc2
diff --git a/sources b/sources
index af5cd77..2ab7f32 100644
--- a/sources
+++ b/sources
@@ -1,2 +1,2 @@
SHA512 (nagios-agents-metadata-105ab8a7b2c16b9a29cf1c1596b80136eeef332b.tar.gz) = 11ddeb48a4929e7642b6dfa9c7962aa1d7a1af1c569830f55ed6cd6773abac13377317327bc1db8411c8077884f83f81cc54d746c834b63a99fa6dc219b5caad
-SHA512 (pacemaker-Pacemaker-2.1.0-rc3.tar.gz) = eb17566e079d313394dab509b055e326b642508844d974a21cde62509a3a526e44977a57c65f80d5094f085fec8ad92eda0deed4f4dfd32ecf0c3b80722e5504
+SHA512 (pacemaker-7c3f66070.tar.gz) = e1c53d019c965395affc1bd78358927617c9bc8572c405ee884737a8213455c5e85102e6f58bb5239ab23ebe5aa794f72c9cc0746900b7a6cc14fd3cf1879064
commit 5dc83e38659be95da4423eed082e2fb0029e2848
Author: Python Maint <python-maint(a)redhat.com>
Date: Mon Jun 7 19:14:33 2021 +0200
Rebuilt for Python 3.10
diff --git a/pacemaker.spec b/pacemaker.spec
index bf413b5..1f9dce2 100644
--- a/pacemaker.spec
+++ b/pacemaker.spec
@@ -165,7 +165,7 @@
Name: pacemaker
Summary: Scalable High-Availability cluster resource manager
Version: %{pcmkversion}
-Release: %{pcmk_release}%{?dist}
+Release: %{pcmk_release}%{?dist}.1
License: GPLv2+ and LGPLv2+
Url: https://www.clusterlabs.org
@@ -742,6 +742,9 @@ exit 0
%license %{nagios_name}-%{nagios_hash}/COPYING
%changelog
+* Mon Jun 07 2021 Python Maint <python-maint(a)redhat.com> - 2.1.0-0.4.rc3.1
+- Rebuilt for Python 3.10
+
* Mon Jun 07 2021 Klaus Wenninger <kwenning(a)redhat.com> - 2.1.0-0.4.rc3
- Update for new upstream tarball for release candidate: Pacemaker-2.1.0-rc3,
for full details, see included ChangeLog file or
commit e52de4390a10fcc15efd749a705386ea8b46e0b4
Author: Klaus Wenninger <klaus.wenninger(a)aon.at>
Date: Mon Jun 7 15:55:07 2021 +0200
* Mon Jun 07 2021 Klaus Wenninger <kwenning(a)redhat.com> - 2.1.0-0.4.rc3
- Update for new upstream tarball for release candidate: Pacemaker-2.1.0-rc3,
for full details, see included ChangeLog file or
https://github.com/ClusterLabs/pacemaker/releases/tag/Pacemaker-2.1.0-rc3
diff --git a/pacemaker.spec b/pacemaker.spec
index a6800fc..bf413b5 100644
--- a/pacemaker.spec
+++ b/pacemaker.spec
@@ -25,11 +25,11 @@
## can be incremented to build packages reliably considered "newer"
## than previously built packages with the same pcmkversion)
%global pcmkversion 2.1.0
-%global specversion 3
+%global specversion 4
## Upstream commit (or git tag, such as "Pacemaker-" plus the
## {pcmkversion} macro for an official release) to use for this package
-%global commit Pacemaker-2.1.0-rc2
+%global commit Pacemaker-2.1.0-rc3
## Since git v2.11, the extent of abbreviation is autoscaled by default
## (used to be constant of 7), so we need to convey it for non-tags, too.
%global commit_abbrev 9
@@ -165,7 +165,7 @@
Name: pacemaker
Summary: Scalable High-Availability cluster resource manager
Version: %{pcmkversion}
-Release: %{pcmk_release}%{?dist}.1
+Release: %{pcmk_release}%{?dist}
License: GPLv2+ and LGPLv2+
Url: https://www.clusterlabs.org
@@ -742,6 +742,11 @@ exit 0
%license %{nagios_name}-%{nagios_hash}/COPYING
%changelog
+* Mon Jun 07 2021 Klaus Wenninger <kwenning(a)redhat.com> - 2.1.0-0.4.rc3
+- Update for new upstream tarball for release candidate: Pacemaker-2.1.0-rc3,
+ for full details, see included ChangeLog file or
+ https://github.com/ClusterLabs/pacemaker/releases/tag/Pacemaker-2.1.0-rc3
+
* Fri Jun 04 2021 Python Maint <python-maint(a)redhat.com> - 2.1.0-0.3.rc2.1
- Rebuilt for Python 3.10
diff --git a/sources b/sources
index 76e3c9a..af5cd77 100644
--- a/sources
+++ b/sources
@@ -1,2 +1,2 @@
SHA512 (nagios-agents-metadata-105ab8a7b2c16b9a29cf1c1596b80136eeef332b.tar.gz) = 11ddeb48a4929e7642b6dfa9c7962aa1d7a1af1c569830f55ed6cd6773abac13377317327bc1db8411c8077884f83f81cc54d746c834b63a99fa6dc219b5caad
-SHA512 (pacemaker-Pacemaker-2.1.0-rc2.tar.gz) = 01396cd23b0cb49cab40b870e227b132f1e0c9b94cf6ac29d58e61bd3ff4ee4ea95fa6509c9a4a09179284b8fdbdba8319ceb46e8f94ce9cea92cf13832f9f4d
+SHA512 (pacemaker-Pacemaker-2.1.0-rc3.tar.gz) = eb17566e079d313394dab509b055e326b642508844d974a21cde62509a3a526e44977a57c65f80d5094f085fec8ad92eda0deed4f4dfd32ecf0c3b80722e5504
commit 172718bb48a11ed751025770698e5452ae20a815
Author: Python Maint <python-maint(a)redhat.com>
Date: Fri Jun 4 20:14:25 2021 +0200
Rebuilt for Python 3.10
diff --git a/pacemaker.spec b/pacemaker.spec
index 979a0e0..a6800fc 100644
--- a/pacemaker.spec
+++ b/pacemaker.spec
@@ -165,7 +165,7 @@
Name: pacemaker
Summary: Scalable High-Availability cluster resource manager
Version: %{pcmkversion}
-Release: %{pcmk_release}%{?dist}
+Release: %{pcmk_release}%{?dist}.1
License: GPLv2+ and LGPLv2+
Url: https://www.clusterlabs.org
@@ -742,6 +742,9 @@ exit 0
%license %{nagios_name}-%{nagios_hash}/COPYING
%changelog
+* Fri Jun 04 2021 Python Maint <python-maint(a)redhat.com> - 2.1.0-0.3.rc2.1
+- Rebuilt for Python 3.10
+
* Mon Jun 1 2021 Klaus Wenninger <kwenning(a)redhat.com> - 2.1.0-0.3.rc2
- Update for new upstream tarball for release candidate: Pacemaker-2.1.0-rc2,
for full details, see included ChangeLog file or
commit 595ee6bf2a7d18022e3d17a40b07aafea1960ecf
Author: Klaus Wenninger <klaus.wenninger(a)aon.at>
Date: Tue Jun 1 13:50:20 2021 +0200
* Mon Jun 1 2021 Klaus Wenninger <kwenning(a)redhat.com> - 2.1.0-0.3.rc2
- Update for new upstream tarball for release candidate: Pacemaker-2.1.0-rc2,
for full details, see included ChangeLog file or
https://github.com/ClusterLabs/pacemaker/releases/tag/Pacemaker-2.1.0-rc2
- merge changes in upstream specfile sind Pacemaker-2.1.0-rc1
add configurable ocf_root
enable cibsecrets for > f33
require libqb >= 0.17.0 instead of 0.13.0
default to syncing with sbd
conflict all sbd < 1.4.3 to enforce sbd-build has the same default
diff --git a/pacemaker.spec b/pacemaker.spec
index 885a70e..979a0e0 100644
--- a/pacemaker.spec
+++ b/pacemaker.spec
@@ -15,15 +15,21 @@
## GitHub entity that distributes source (for ease of using a fork)
%global github_owner ClusterLabs
+## Where bug reports should be submitted
+## Leave bug_url undefined to use ClusterLabs default, others define it here
+
+## What to use as the OCF resource agent root directory
+%global ocf_root %{_prefix}/lib/ocf
+
## Upstream pacemaker version, and its package version (specversion
## can be incremented to build packages reliably considered "newer"
## than previously built packages with the same pcmkversion)
%global pcmkversion 2.1.0
-%global specversion 2
+%global specversion 3
## Upstream commit (or git tag, such as "Pacemaker-" plus the
## {pcmkversion} macro for an official release) to use for this package
-%global commit Pacemaker-2.1.0-rc1
+%global commit Pacemaker-2.1.0-rc2
## Since git v2.11, the extent of abbreviation is autoscaled by default
## (used to be constant of 7), so we need to convey it for non-tags, too.
%global commit_abbrev 9
@@ -33,8 +39,12 @@
## NOTE: skip --with stonithd
-## Add option to enable support for storing sensitive information outside CIB
+## Add option for whether to support storing sensitive information outside CIB
+%if (0%{?fedora} && 0%{?fedora} <= 33) || (0%{?rhel} && 0%{?rhel} <= 8)
%bcond_with cibsecrets
+%else
+%bcond_without cibsecrets
+%endif
## Add option to create binaries suitable for use with profiling tools
%bcond_with profiling
@@ -45,6 +55,15 @@
## Add option to skip/enable generating documentation
%bcond_without doc
+## Add option to default to start-up synchronization with SBD.
+##
+## If enabled, SBD *MUST* be built to default similarly, otherwise data
+## corruption could occur. Building both Pacemaker and SBD to default
+## to synchronization improves safety, without requiring higher-level tools
+## to be aware of the setting or requiring users to modify configurations
+## after upgrading to versions that support synchronization.
+%bcond_without sbd_sync
+
## Add option to prefix package version with "0."
## (so later "official" packages will be considered updates)
%bcond_without pre_release
@@ -167,14 +186,15 @@ BuildRequires: make
BuildRequires: %{python_name}-devel >= %{python_min}
# Pacemaker requires a minimum libqb functionality
-Requires: libqb >= 0.13.0
-BuildRequires: libqb-devel >= 0.13.0
+Requires: libqb >= 0.17.0
+BuildRequires: libqb-devel >= 0.17.0
-# Basics required for the build (even if usually satisfied through other BRs)
+# Required basic build tools
BuildRequires: coreutils findutils grep sed
+BuildRequires: autoconf automake gcc make pkgconfig
+BuildRequires: libtool libtool-ltdl-devel
# Required for core functionality
-BuildRequires: automake autoconf gcc libtool pkgconfig libtool-ltdl-devel
BuildRequires: pkgconfig(glib-2.0) >= 2.42
BuildRequires: libxml2-devel libxslt-devel libuuid-devel
BuildRequires: bzip2-devel
@@ -245,7 +265,10 @@ Summary: Core Pacemaker libraries
Requires(pre): shadow-utils
Requires: %{name}-schemas = %{version}-%{release}
# sbd 1.4.0+ supports the libpe_status API for pe_working_set_t
-Conflicts: sbd < 1.4.0
+# sbd 1.4.2+ supports startup/shutdown handshake via pacemakerd-api
+# and handshake defaults to enabled for rhel builds
+# sbd 1.4.3+ handshake defaults to enabled for fedora builds
+Conflicts: sbd < 1.4.3
%description libs
Pacemaker is an advanced, scalable High-Availability cluster resource
@@ -268,7 +291,7 @@ libraries needed for nodes that will form part of the cluster nodes.
%package remote
License: GPLv2+ and LGPLv2+
-Summary: Pacemaker remote daemon for non-cluster nodes
+Summary: Pacemaker remote executor daemon for non-cluster nodes
Requires: %{name}-libs%{?_isa} = %{version}-%{release}
Requires: %{name}-cli = %{version}-%{release}
Requires: resource-agents
@@ -386,7 +409,10 @@ export LDFLAGS_HARDENED_LIB="%{?_hardening_ldflags}"
%{?with_profiling: --with-profiling} \
%{?with_coverage: --with-coverage} \
%{?with_cibsecrets: --with-cibsecrets} \
+ %{?with_sbd_sync: --with-sbd-sync-default="true"} \
%{?gnutls_priorities: --with-gnutls-priorities="%{gnutls_priorities}"} \
+ %{?bug_url: --with-bug-url=%{bug_url}} \
+ %{?ocf_root: --with-ocfdir=%{ocf_root}} \
%{?concurrent_fencing} \
%{?resource_stickiness} \
%{?compat20} \
@@ -572,8 +598,8 @@ exit 0
%dir %attr (750, %{uname}, %{gname}) %{_var}/lib/pacemaker/cib
%dir %attr (750, %{uname}, %{gname}) %{_var}/lib/pacemaker/pengine
-/usr/lib/ocf/resource.d/pacemaker/controld
-/usr/lib/ocf/resource.d/pacemaker/remote
+%{ocf_root}/resource.d/pacemaker/controld
+%{ocf_root}/resource.d/pacemaker/remote
%files cli
%dir %attr (750, root, %{gname}) %{_sysconfdir}/pacemaker
@@ -609,13 +635,13 @@ exit 0
# XXX "dirname" is not owned by any prerequisite
%{_datadir}/snmp/mibs/PCMK-MIB.txt
-%exclude /usr/lib/ocf/resource.d/pacemaker/controld
-%exclude /usr/lib/ocf/resource.d/pacemaker/o2cb
-%exclude /usr/lib/ocf/resource.d/pacemaker/remote
+%exclude %{ocf_root}/resource.d/pacemaker/controld
+%exclude %{ocf_root}/resource.d/pacemaker/o2cb
+%exclude %{ocf_root}/resource.d/pacemaker/remote
-%dir /usr/lib/ocf
-%dir /usr/lib/ocf/resource.d
-/usr/lib/ocf/resource.d/pacemaker
+%dir %{ocf_root}
+%dir %{ocf_root}/resource.d
+%{ocf_root}/resource.d/pacemaker
%doc %{_mandir}/man7/*
%exclude %{_mandir}/man7/pacemaker-controld.*
@@ -716,6 +742,17 @@ exit 0
%license %{nagios_name}-%{nagios_hash}/COPYING
%changelog
+* Mon Jun 1 2021 Klaus Wenninger <kwenning(a)redhat.com> - 2.1.0-0.3.rc2
+- Update for new upstream tarball for release candidate: Pacemaker-2.1.0-rc2,
+ for full details, see included ChangeLog file or
+ https://github.com/ClusterLabs/pacemaker/releases/tag/Pacemaker-2.1.0-rc2
+- merge changes in upstream specfile sind Pacemaker-2.1.0-rc1
+ add configurable ocf_root
+ enable cibsecrets for > f33
+ require libqb >= 0.17.0 instead of 0.13.0
+ default to syncing with sbd
+ conflict all sbd < 1.4.3 to enforce sbd-build has the same default
+
* Wed May 5 2021 Klaus Wenninger <kwenning(a)redhat.com> - 2.1.0-0.2.rc1
- remove PCMK_TIME_EMERGENCY_CGT not present in pacemaker codebase anymore
- enable Pacemaker-2.0-compatibility for a fedora versions
diff --git a/sources b/sources
index a976443..76e3c9a 100644
--- a/sources
+++ b/sources
@@ -1,2 +1,2 @@
SHA512 (nagios-agents-metadata-105ab8a7b2c16b9a29cf1c1596b80136eeef332b.tar.gz) = 11ddeb48a4929e7642b6dfa9c7962aa1d7a1af1c569830f55ed6cd6773abac13377317327bc1db8411c8077884f83f81cc54d746c834b63a99fa6dc219b5caad
-SHA512 (pacemaker-Pacemaker-2.1.0-rc1.tar.gz) = 64fef270ac0f0df63a2ac974c4467dfb6a646cbe2089c6b523f14311bf236653caa75cd9e2851af7882057d63651ecd3c9d40647349a76619c4522c938063acf
+SHA512 (pacemaker-Pacemaker-2.1.0-rc2.tar.gz) = 01396cd23b0cb49cab40b870e227b132f1e0c9b94cf6ac29d58e61bd3ff4ee4ea95fa6509c9a4a09179284b8fdbdba8319ceb46e8f94ce9cea92cf13832f9f4d
commit e7aab8014f11ec96139f581de2cfe66c85975ed0
Author: Klaus Wenninger <klaus.wenninger(a)aon.at>
Date: Fri May 7 11:11:12 2021 +0200
* Wed May 5 2021 Klaus Wenninger <kwenning(a)redhat.com> - 2.1.0-0.2.rc1
- remove PCMK_TIME_EMERGENCY_CGT not present in pacemaker codebase anymore
- enable Pacemaker-2.0-compatibility for a fedora versions
diff --git a/pacemaker.spec b/pacemaker.spec
index 676075c..885a70e 100644
--- a/pacemaker.spec
+++ b/pacemaker.spec
@@ -19,7 +19,7 @@
## can be incremented to build packages reliably considered "newer"
## than previously built packages with the same pcmkversion)
%global pcmkversion 2.1.0
-%global specversion 1
+%global specversion 2
## Upstream commit (or git tag, such as "Pacemaker-" plus the
## {pcmkversion} macro for an official release) to use for this package
@@ -97,7 +97,7 @@
## Distro-specific configuration choices
### Use 2.0-style output when other distro packages don't support current output
-%if 0%{?fedora} && 0%{?fedora} <= 34
+%if 0%{?fedora}
%global compat20 --enable-compat-2.0
%endif
@@ -379,9 +379,6 @@ export LDFLAGS_HARDENED_LIB="%{?_hardening_ldflags}"
./autogen.sh
-# Rawhide glibc doesn't like ftime at all
-export CPPFLAGS="-UPCMK_TIME_EMERGENCY_CGT $CPPFLAGS"
-
%{configure} \
PYTHON=%{python_path} \
%{!?with_hardening: --disable-hardening} \
@@ -719,6 +716,10 @@ exit 0
%license %{nagios_name}-%{nagios_hash}/COPYING
%changelog
+* Wed May 5 2021 Klaus Wenninger <kwenning(a)redhat.com> - 2.1.0-0.2.rc1
+- remove PCMK_TIME_EMERGENCY_CGT not present in pacemaker codebase anymore
+- enable Pacemaker-2.0-compatibility for a fedora versions
+
* Tue May 4 2021 Klaus Wenninger <kwenning(a)redhat.com> - 2.1.0-0.1.rc1
- Update for new upstream tarball for release candidate: Pacemaker-2.1.0-rc1,
for full details, see included ChangeLog file or
commit c8cc15254d66b4d69d4d3eb547808fcfd172662a
Author: Klaus Wenninger <klaus.wenninger(a)aon.at>
Date: Tue May 4 22:24:44 2021 +0200
* Tue May 4 2021 Klaus Wenninger <kwenning(a)redhat.com> - 2.1.0-0.1.rc1
- Update for new upstream tarball for release candidate: Pacemaker-2.1.0-rc1,
for full details, see included ChangeLog file or
https://github.com/ClusterLabs/pacemaker/releases/tag/Pacemaker-2.1.0-rc1
- merge changes in upstream spec-file since Pacemaker-2.0.5
- disable legacy-links
diff --git a/pacemaker.spec b/pacemaker.spec
index b786f9c..676075c 100644
--- a/pacemaker.spec
+++ b/pacemaker.spec
@@ -18,12 +18,12 @@
## Upstream pacemaker version, and its package version (specversion
## can be incremented to build packages reliably considered "newer"
## than previously built packages with the same pcmkversion)
-%global pcmkversion 2.0.5
-%global specversion 10
+%global pcmkversion 2.1.0
+%global specversion 1
## Upstream commit (or git tag, such as "Pacemaker-" plus the
## {pcmkversion} macro for an official release) to use for this package
-%global commit Pacemaker-2.0.5
+%global commit Pacemaker-2.1.0-rc1
## Since git v2.11, the extent of abbreviation is autoscaled by default
## (used to be constant of 7), so we need to convey it for non-tags, too.
%global commit_abbrev 9
@@ -43,24 +43,19 @@
%bcond_with coverage
## Add option to skip/enable generating documentation
-## (the build tools aren't available everywhere)
-%if 0%{?rhel}
- %bcond_with doc
-%else
- %bcond_without doc
-%endif
+%bcond_without doc
## Add option to prefix package version with "0."
## (so later "official" packages will be considered updates)
-%bcond_with pre_release
+%bcond_without pre_release
## NOTE: skip --with upstart_job
## Add option to turn off hardening of libraries and daemon executables
%bcond_without hardening
-## Add option to disable links for legacy daemon names
-%bcond_without legacy_links
+## Add option to enable links for legacy daemon names
+%bcond_with legacy_links
## Nagios source control identifiers
@@ -95,14 +90,27 @@
%endif
%endif
-## Heuristic used to infer bleeding-edge deployments that are
-## less likely to have working versions of the documentation tools
-%define bleeding %(test ! -e /etc/yum.repos.d/fedora-rawhide.repo; echo $?)
-
## Base GnuTLS cipher priorities (presumably only the initial, required keyword)
## overridable with "rpmbuild --define 'pcmk_gnutls_priorities PRIORITY-SPEC'"
%define gnutls_priorities %{?pcmk_gnutls_priorities}%{!?pcmk_gnutls_priorities:@SYSTEM}
+## Distro-specific configuration choices
+
+### Use 2.0-style output when other distro packages don't support current output
+%if 0%{?fedora} && 0%{?fedora} <= 34
+%global compat20 --enable-compat-2.0
+%endif
+
+### Default concurrent-fencing to true when distro prefers that
+%if 0%{?rhel}
+%global concurrent_fencing --with-concurrent-fencing-default=true
+%endif
+
+### Default resource-stickiness to 1 when distro prefers that
+%if 0%{?fedora} >= 35 || 0%{?rhel} >= 9
+%global resource_stickiness --with-resource-stickiness-default=1
+%endif
+
# Python-related definitions
## Turn off auto-compilation of Python files outside Python specific paths,
@@ -138,7 +146,7 @@
Name: pacemaker
Summary: Scalable High-Availability cluster resource manager
Version: %{pcmkversion}
-Release: %{pcmk_release}%{?dist}.2
+Release: %{pcmk_release}%{?dist}
License: GPLv2+ and LGPLv2+
Url: https://www.clusterlabs.org
@@ -167,7 +175,7 @@ BuildRequires: coreutils findutils grep sed
# Required for core functionality
BuildRequires: automake autoconf gcc libtool pkgconfig libtool-ltdl-devel
-BuildRequires: pkgconfig(glib-2.0) >= 2.16
+BuildRequires: pkgconfig(glib-2.0) >= 2.42
BuildRequires: libxml2-devel libxslt-devel libuuid-devel
BuildRequires: bzip2-devel
@@ -183,11 +191,8 @@ BuildRequires: corosync-devel >= 2.0.0
#BuildRequires: pkgconfig(libcpg)
#BuildRequires: pkgconfig(libcfg)
-## (note no avoiding effect when building through non-customized mock)
-%if !%{bleeding}
%if %{with doc}
-BuildRequires: asciidoc inkscape publican
-%endif
+BuildRequires: asciidoc inkscape %{python_name}-sphinx
%endif
# git-style patch application
@@ -380,12 +385,14 @@ export CPPFLAGS="-UPCMK_TIME_EMERGENCY_CGT $CPPFLAGS"
%{configure} \
PYTHON=%{python_path} \
%{!?with_hardening: --disable-hardening} \
- %{!?with_legacy_links: --disable-legacy-links} \
+ %{?with_legacy_links: --enable-legacy-links} \
%{?with_profiling: --with-profiling} \
%{?with_coverage: --with-coverage} \
%{?with_cibsecrets: --with-cibsecrets} \
- %{!?with_doc: --with-brand=} \
%{?gnutls_priorities: --with-gnutls-priorities="%{gnutls_priorities}"} \
+ %{?concurrent_fencing} \
+ %{?resource_stickiness} \
+ %{?compat20} \
--disable-static \
--with-initdir=%{_initrddir} \
--with-runstatedir=%{_rundir} \
@@ -415,10 +422,6 @@ make install \
DESTDIR=%{buildroot} V=1 docdir=%{pcmk_docdir} \
%{?_python_bytecompile_extra:%{?py_byte_compile:am__py_compile=true}}
-mkdir -p ${RPM_BUILD_ROOT}%{_sysconfdir}/sysconfig
-install -m 644 daemons/pacemakerd/pacemaker.sysconfig ${RPM_BUILD_ROOT}%{_sysconfdir}/sysconfig/pacemaker
-install -m 644 tools/crm_mon.sysconfig ${RPM_BUILD_ROOT}%{_sysconfdir}/sysconfig/crm_mon
-
mkdir -p %{buildroot}%{_datadir}/pacemaker/nagios/plugins-metadata
for file in $(find %{nagios_name}-%{nagios_hash}/metadata -type f); do
install -m 644 $file %{buildroot}%{_datadir}/pacemaker/nagios/plugins-metadata
@@ -549,9 +552,7 @@ exit 0
%exclude %{_libexecdir}/pacemaker/cts-log-watcher
%exclude %{_libexecdir}/pacemaker/cts-support
%exclude %{_sbindir}/pacemaker-remoted
-%if %{with legacy_links}
%exclude %{_sbindir}/pacemaker_remoted
-%endif
%{_libexecdir}/pacemaker/*
%{_sbindir}/crm_attribute
@@ -671,9 +672,7 @@ exit 0
%{_unitdir}/pacemaker_remote.service
%{_sbindir}/pacemaker-remoted
-%if %{with legacy_links}
%{_sbindir}/pacemaker_remoted
-%endif
%{_mandir}/man8/pacemaker-remoted.*
%license licenses/GPLv2
%doc COPYING
@@ -720,6 +719,13 @@ exit 0
%license %{nagios_name}-%{nagios_hash}/COPYING
%changelog
+* Tue May 4 2021 Klaus Wenninger <kwenning(a)redhat.com> - 2.1.0-0.1.rc1
+- Update for new upstream tarball for release candidate: Pacemaker-2.1.0-rc1,
+ for full details, see included ChangeLog file or
+ https://github.com/ClusterLabs/pacemaker/releases/tag/Pacemaker-2.1.0-rc1
+- merge changes in upstream spec-file since Pacemaker-2.0.5
+- disable legacy-links
+
* Tue Mar 02 2021 Zbigniew Jędrzejewski-Szmek <zbyszek(a)in.waw.pl> - 2.0.5-10.2
- Rebuilt for updated systemd-rpm-macros
See https://pagure.io/fesco/issue/2583.
diff --git a/sources b/sources
index b19a593..a976443 100644
--- a/sources
+++ b/sources
@@ -1,2 +1,2 @@
SHA512 (nagios-agents-metadata-105ab8a7b2c16b9a29cf1c1596b80136eeef332b.tar.gz) = 11ddeb48a4929e7642b6dfa9c7962aa1d7a1af1c569830f55ed6cd6773abac13377317327bc1db8411c8077884f83f81cc54d746c834b63a99fa6dc219b5caad
-SHA512 (pacemaker-Pacemaker-2.0.5.tar.gz) = 5fd3614f0284297babb66ea2dc567583315052fcf77f49107c52161e8e8c164ae3169d98528fdc1316d8eabaacc98ed24e9c6e6c90b3286f2f012a4ee874bbba
+SHA512 (pacemaker-Pacemaker-2.1.0-rc1.tar.gz) = 64fef270ac0f0df63a2ac974c4467dfb6a646cbe2089c6b523f14311bf236653caa75cd9e2851af7882057d63651ecd3c9d40647349a76619c4522c938063acf
commit 3978725c064d9bf7d504f7c8118f3c9d34bfb715
Author: Zbigniew Jędrzejewski-Szmek <zbyszek(a)in.waw.pl>
Date: Tue Mar 2 16:13:07 2021 +0100
Rebuilt for updated systemd-rpm-macros
See https://pagure.io/fesco/issue/2583.
diff --git a/pacemaker.spec b/pacemaker.spec
index 8f15ea0..b786f9c 100644
--- a/pacemaker.spec
+++ b/pacemaker.spec
@@ -138,7 +138,7 @@
Name: pacemaker
Summary: Scalable High-Availability cluster resource manager
Version: %{pcmkversion}
-Release: %{pcmk_release}%{?dist}.1
+Release: %{pcmk_release}%{?dist}.2
License: GPLv2+ and LGPLv2+
Url: https://www.clusterlabs.org
@@ -720,6 +720,10 @@ exit 0
%license %{nagios_name}-%{nagios_hash}/COPYING
%changelog
+* Tue Mar 02 2021 Zbigniew Jędrzejewski-Szmek <zbyszek(a)in.waw.pl> - 2.0.5-10.2
+- Rebuilt for updated systemd-rpm-macros
+ See https://pagure.io/fesco/issue/2583.
+
* Tue Jan 26 2021 Fedora Release Engineering <releng(a)fedoraproject.org> - 2.0.5-10.1
- Rebuilt for https://fedoraproject.org/wiki/Fedora_34_Mass_Rebuild
2 years, 9 months
Architecture specific change in rpms/wabt.git
by githook-noreply@fedoraproject.org
The package rpms/wabt.git has added or updated architecture specific content in its
spec file (ExclusiveArch/ExcludeArch or %ifarch/%ifnarch) in commit(s):
https://src.fedoraproject.org/cgit/rpms/wabt.git/commit/?id=a5b8120bc45b3....
Change:
-%ifarch s390x
Thanks.
Full change:
============
commit 9ac7663c7233185f239259091aa5c7616602dfdf
Merge: 584973c a5b8120
Author: Dominik 'Rathann' Mierzejewski <dominik(a)greysector.net>
Date: Fri Aug 20 11:09:21 2021 +0200
Merge branch 'f34' into f35
diff --cc wabt.spec
index a5831d6,2610d9a..ebc8d6f
--- a/wabt.spec
+++ b/wabt.spec
@@@ -125,9 -114,10 +114,13 @@@ test/run-tests.py -v --bindir %{_vpath_
%{_mandir}/man1/wat2wasm.1*
%changelog
+ * Fri Aug 20 2021 Dominik Mierzejewski <rpm(a)greysector.net> 1.0.24-1
+ - update to 1.0.24 (#1993479)
+ - enable skipped tests on s390x (fixed upstream)
+
+* Fri Jul 23 2021 Fedora Release Engineering <releng(a)fedoraproject.org> - 1.0.23-2
+- Rebuilt for https://fedoraproject.org/wiki/Fedora_35_Mass_Rebuild
+
* Fri May 14 2021 Dominik Mierzejewski <rpm(a)greysector.net> 1.0.23-1
- update to 1.0.23 (#1934466)
- drop obsolete patch
commit a5b8120bc45b3ab5979a70b73af3d8a011a25856
Author: Dominik 'Rathann' Mierzejewski <dominik(a)greysector.net>
Date: Fri Aug 20 11:08:36 2021 +0200
update to 1.0.24 (#1993479)
enable skipped tests on s390x (fixed upstream)
diff --git a/.gitignore b/.gitignore
index 746c0e9..797a8f7 100644
--- a/.gitignore
+++ b/.gitignore
@@ -18,3 +18,5 @@
/wabt-09ac53e41d959ce8e14e703e627327971e67f6d3.tar.gz
/wabt-1.0.23.tar.gz
/wabt-testsuite-9994915.tar.gz
+/wabt-1.0.24.tar.gz
+/wabt-testsuite-01efde8.tar.gz
diff --git a/sources b/sources
index 17e65a7..7becfb5 100644
--- a/sources
+++ b/sources
@@ -1,3 +1,3 @@
-SHA512 (wabt-1.0.23.tar.gz) = caa11433d850d6d3117a6fd6b16fe8c41a8813052dd79f99b7b2e5f97720e806cf41c3aed109e25683bb1da1fa94ff9f8d8248bcd0e293b1bac9854c90338595
-SHA512 (wabt-testsuite-9994915.tar.gz) = c8b9a6cfbfc438932b294eb71f1c2813a8607afd5c7762a3f2f73b33a68ea1f7ec17083b8ef97e9219797ba6d2cf01b12c62692472820ee350f902437444f468
+SHA512 (wabt-1.0.24.tar.gz) = 6257702f06baace7eded7fd4890cbf23e533f20ee6c40de82a7a4cd32fe931a49efd3cbfc1355c4da49e6fbd3b62b1cf05edf1699313840d4825a2165a3928a8
+SHA512 (wabt-testsuite-01efde8.tar.gz) = 549c2e13ebbd7d87013426d8cdeaa0a6177928307b688e1b90026e79683f7964f09dfb47294b34ba9190d0f3acb279f6d0a924e1f1a9112fc9d89637b8cb9efd
SHA512 (wabt-wasm-c-api-d9a8009.tar.gz) = b6ee7d2d83a3ca7b3cb3ee110282f7d32eada1d9020cadd3fdec58f40478879fd9ac9fada526021d8f6e8a26e1df7353df313d4149be2ffe4aea3a28f856fd89
diff --git a/wabt.spec b/wabt.spec
index a1db44a..2610d9a 100644
--- a/wabt.spec
+++ b/wabt.spec
@@ -1,15 +1,15 @@
%undefine __cmake_in_source_build
%bcond_without check
-%global commit aa0515b3c808da880942db8658abeaa969534667
+%global commit 21279a861fa3dbac9af9d2bab16c741df17a86af
%global shortcommit %(c=%{commit}; echo ${c:0:7})
-%global ts_commit 9994915e0cca8b42a16c577e4c85491822367dde
+%global ts_commit 01efde81028c5b0d099eb836645a2dc5e7755449
%global ts_shortcommit %(c=%{ts_commit}; echo ${c:0:7})
%global wc_commit d9a80099d496b5cdba6f3fe8fc77586e0e505ddc
%global wc_shortcommit %(c=%{wc_commit}; echo ${c:0:7})
Summary: The WebAssembly Binary Toolkit
Name: wabt
-Version: 1.0.23
+Version: 1.0.24
Release: 1%{?dist}
URL: https://github.com/WebAssembly/wabt
Source0: https://github.com/WebAssembly/wabt/archive/%{version}/%{name}-%{version}...
@@ -67,17 +67,6 @@ rm spec/conversions.txt
rm spec/simd/simd_conversions.txt
rm wasm2c/spec/conversions.txt
%endif
-# https://github.com/WebAssembly/wabt/issues/1670
-%ifarch s390x
-rm spec/simd/simd_load16_lane.txt
-rm spec/simd/simd_load32_lane.txt
-rm spec/simd/simd_load64_lane.txt
-rm spec/simd/simd_load8_lane.txt
-rm spec/simd/simd_store16_lane.txt
-rm spec/simd/simd_store32_lane.txt
-rm spec/simd/simd_store64_lane.txt
-rm spec/simd/simd_store8_lane.txt
-%endif
popd
%endif
@@ -125,6 +114,10 @@ test/run-tests.py -v --bindir %{_vpath_builddir} --timeout=80 %{?_smp_mflags}
%{_mandir}/man1/wat2wasm.1*
%changelog
+* Fri Aug 20 2021 Dominik Mierzejewski <rpm(a)greysector.net> 1.0.24-1
+- update to 1.0.24 (#1993479)
+- enable skipped tests on s390x (fixed upstream)
+
* Fri May 14 2021 Dominik Mierzejewski <rpm(a)greysector.net> 1.0.23-1
- update to 1.0.23 (#1934466)
- drop obsolete patch
2 years, 9 months
Architecture specific change in rpms/wabt.git
by githook-noreply@fedoraproject.org
The package rpms/wabt.git has added or updated architecture specific content in its
spec file (ExclusiveArch/ExcludeArch or %ifarch/%ifnarch) in commit(s):
https://src.fedoraproject.org/cgit/rpms/wabt.git/commit/?id=a5b8120bc45b3....
Change:
-%ifarch s390x
Thanks.
Full change:
============
commit a5b8120bc45b3ab5979a70b73af3d8a011a25856
Author: Dominik 'Rathann' Mierzejewski <dominik(a)greysector.net>
Date: Fri Aug 20 11:08:36 2021 +0200
update to 1.0.24 (#1993479)
enable skipped tests on s390x (fixed upstream)
diff --git a/.gitignore b/.gitignore
index 746c0e9..797a8f7 100644
--- a/.gitignore
+++ b/.gitignore
@@ -18,3 +18,5 @@
/wabt-09ac53e41d959ce8e14e703e627327971e67f6d3.tar.gz
/wabt-1.0.23.tar.gz
/wabt-testsuite-9994915.tar.gz
+/wabt-1.0.24.tar.gz
+/wabt-testsuite-01efde8.tar.gz
diff --git a/sources b/sources
index 17e65a7..7becfb5 100644
--- a/sources
+++ b/sources
@@ -1,3 +1,3 @@
-SHA512 (wabt-1.0.23.tar.gz) = caa11433d850d6d3117a6fd6b16fe8c41a8813052dd79f99b7b2e5f97720e806cf41c3aed109e25683bb1da1fa94ff9f8d8248bcd0e293b1bac9854c90338595
-SHA512 (wabt-testsuite-9994915.tar.gz) = c8b9a6cfbfc438932b294eb71f1c2813a8607afd5c7762a3f2f73b33a68ea1f7ec17083b8ef97e9219797ba6d2cf01b12c62692472820ee350f902437444f468
+SHA512 (wabt-1.0.24.tar.gz) = 6257702f06baace7eded7fd4890cbf23e533f20ee6c40de82a7a4cd32fe931a49efd3cbfc1355c4da49e6fbd3b62b1cf05edf1699313840d4825a2165a3928a8
+SHA512 (wabt-testsuite-01efde8.tar.gz) = 549c2e13ebbd7d87013426d8cdeaa0a6177928307b688e1b90026e79683f7964f09dfb47294b34ba9190d0f3acb279f6d0a924e1f1a9112fc9d89637b8cb9efd
SHA512 (wabt-wasm-c-api-d9a8009.tar.gz) = b6ee7d2d83a3ca7b3cb3ee110282f7d32eada1d9020cadd3fdec58f40478879fd9ac9fada526021d8f6e8a26e1df7353df313d4149be2ffe4aea3a28f856fd89
diff --git a/wabt.spec b/wabt.spec
index a1db44a..2610d9a 100644
--- a/wabt.spec
+++ b/wabt.spec
@@ -1,15 +1,15 @@
%undefine __cmake_in_source_build
%bcond_without check
-%global commit aa0515b3c808da880942db8658abeaa969534667
+%global commit 21279a861fa3dbac9af9d2bab16c741df17a86af
%global shortcommit %(c=%{commit}; echo ${c:0:7})
-%global ts_commit 9994915e0cca8b42a16c577e4c85491822367dde
+%global ts_commit 01efde81028c5b0d099eb836645a2dc5e7755449
%global ts_shortcommit %(c=%{ts_commit}; echo ${c:0:7})
%global wc_commit d9a80099d496b5cdba6f3fe8fc77586e0e505ddc
%global wc_shortcommit %(c=%{wc_commit}; echo ${c:0:7})
Summary: The WebAssembly Binary Toolkit
Name: wabt
-Version: 1.0.23
+Version: 1.0.24
Release: 1%{?dist}
URL: https://github.com/WebAssembly/wabt
Source0: https://github.com/WebAssembly/wabt/archive/%{version}/%{name}-%{version}...
@@ -67,17 +67,6 @@ rm spec/conversions.txt
rm spec/simd/simd_conversions.txt
rm wasm2c/spec/conversions.txt
%endif
-# https://github.com/WebAssembly/wabt/issues/1670
-%ifarch s390x
-rm spec/simd/simd_load16_lane.txt
-rm spec/simd/simd_load32_lane.txt
-rm spec/simd/simd_load64_lane.txt
-rm spec/simd/simd_load8_lane.txt
-rm spec/simd/simd_store16_lane.txt
-rm spec/simd/simd_store32_lane.txt
-rm spec/simd/simd_store64_lane.txt
-rm spec/simd/simd_store8_lane.txt
-%endif
popd
%endif
@@ -125,6 +114,10 @@ test/run-tests.py -v --bindir %{_vpath_builddir} --timeout=80 %{?_smp_mflags}
%{_mandir}/man1/wat2wasm.1*
%changelog
+* Fri Aug 20 2021 Dominik Mierzejewski <rpm(a)greysector.net> 1.0.24-1
+- update to 1.0.24 (#1993479)
+- enable skipped tests on s390x (fixed upstream)
+
* Fri May 14 2021 Dominik Mierzejewski <rpm(a)greysector.net> 1.0.23-1
- update to 1.0.23 (#1934466)
- drop obsolete patch
2 years, 9 months