On the central machine (the "piktmaster"), there are eight controlling config files:
Every config file is a sequence of stanzas, usually though not necessarily separated by blank lines. A stanza consists of an identifier, in the first column, followed by additional text, either on the same line, or in multiple lines. Follow-up lines must be indented, using either spaces, tabs, or the #indent preprocessor directive.
Note that a single space or tab is sufficient for establishing indentation. In programs.cfg and files.cfg, PIKT considers any whitespace following the first whitespace on a line to be part of your program or file formatting. (In all other config files, the extent of indentation doesn't matter.)
For example, in programs.cfg, you should do this
foobar.pl
#!=perl
^
[single tab char]
and not this
foobar.pl
#!=perl
^^^^^^^^
[eight space chars]
since in the second example, this would be the processed result
#!/usr/bin/perl
^^^^^^^
[seven space chars]
which would cause an error.
Stanza identifiers must begin with a letter, followed by an unlimited number (practically speaking) of letters, digits, hyphens (-), underscores (_), and/or periods (.). Forward slashes (/) are allowed in programs.cfg and files.cfg when specifying program identifiers. So, the following identifiers must conform to the aforementioned naming rule:
-
operating systems, host aliases, and host groups, in systems.cfg
-
defines, in defines.cfg
-
macros, in macros.cfg
-
alerts, in alerts.cfg
-
alarms, in alarms.cfg
-
object sets, in objects.cfg
-
program names, in programs.cfg
-
system file names, in files.cfg.
If you duplicate an identifier within a config file, this will generate an error. You may repeat the same identifier in different config domains, however. (For example, you might have a machine named "test" in systems.cfg, a define named "test" in defines.cfg, a macro named "test" in macros.cfg, and an object named "test" in an objects include file.)
If you have two (or more) different files (or programs) with the same name, you must use two (or more) different identifiers, and specify file paths either in the identifer name, else using the "path" keyword. For example, you should do this
/etc/rc.d/init.d/sshd
...
/etc/pam.d/sshd
...
or this
sshd_rc path "/etc/rc.d/init.d/sshd" ...
...
sshd_pam path "/etc/pam.d/sshd" ...
...
The second is the preferred method, especially for the following reason: You specify program and file identifiers without their directory at the piktc command line. In other words, this is okay
piktc -iv +P sshd_rc sshd_pam +H ...
while this is ambiguous
piktc -iv +P sshd +H ...
(You could still install both sshd's using 'piktc -iv +P all ...', however.)
The last line of every config file must end with a linefeed. Without the linefeed, obscure parser errors (for example, the parser silently failing without explanation) might result.
Almost without exception, and aside from the above simple rules and the Pikt language syntax rules, Pikt is indifferent to script and config file layout. In other words, spacing and line breaks really don't matter, and you may lay out your config files in any style that pleases you.
Pikt employs comments in the C++ style. The // double-slash character sequence, which can appear anywhere in the line, comments out the remainder of the line. The /* <text> */ sequence comments out everything between the opening /* and closing */. /* */ comments may not be nested. Unless inside a /* */ comment, /* and */ after a // on the same line are ignored.
This config file specifies host systems, host aliases, and host groups. First, all hosts systems should be registered, usually by operating system. Host registration stanzas are signaled by the "hosts" keyword:
<osname>
hosts <host> <host> <host> ...
Next, as an option, come host aliases, signaled by the "aliases" keyword:
<host>
aliases <alias> <alias> <alias> ...
The <host> in an aliases stanza must be registered in a previous hosts stanza. Note that these hosts aliases may be, but don't have to be, DNS aliases (specified in CNAME records). PIKT host aliases may be anything you want.
Finally, also as an option, come host groups, signaled by the "members" keyword:
<group>
members <host|alias|group|osname> <host|alias|group|osname> ...
Here, too, member hosts, aliases, groups, and osnames must be pre-registered.
The usual way of registering hosts is by operating system, then subcategorizing by OS version in host groups as necessary. It is possible, however, to do it the opposite way--registering by OS version subcategories, then aggregating in a host group stanza. For example, this is one way to register hosts:
solaris
hosts newdelhi bangalore amritsar raipur agra bhopal
bombay madras srinagar puri calcutta hyderabad
...
solaris27
members newdelhi bangalore amritsar raipur agra bhopal
solaris26
members bombay madras srinagar puri
solaris25
members calcutta hyderabad
And here is another way you can do it:
solaris27
hosts newdelhi bangalore amritsar raipur agra bhopal
solaris26
hosts bombay madras srinagar puri
solaris25
hosts calcutta hyderabad
...
solaris
members solaris27 solaris26 solaris25
The second way involves less typing and helps ensure against your forgetting to subcategorize one or more hosts.
There is an automatic, implicit definition of so-called "non" groups (set complements) for every explicit OS and hostgroup defined in systems.cfg. For example, if you have defined the hostgroup "linux", the "nonlinux" hostgroup--consisting of all PIKT systems not in the "linux" hostgroup--is automatically defined. The implicitly defined "nonnfsserver" group would include all systems not in the "nfsserver" group.
An explicit definition of a "non" group in systems.cfg overrides any implicit definition.
The chief use of the "non" hostgroups is to provide a finer degree of control when specifying 'piktc ... +H ... -H ...'.
A standard PIKT hostgroup is downsys, as in
downsys // set '-H downsys' for these
members
#include <systems/downsys_systems.cfg>
But what if you have no down systems, i.e., the downsys_systems.cfg file is empty? That's no problem, because PIKT also supports empty hosts, aliases, and members stanzas. So, you may do things like
aix
hosts // empty
...
testsys
members
...
Although you are advised to list all of your hosts stanzas first, aliases stanzas second, and members stanzas third, you may in fact freely intermix stanzas, and in just about any order.
Therefore, you can prepare #include files, for example linux_systems.cfg, putting all of your linux hosts, aliases, and groups together in one file.
Or you might organize your systems information by organizational unit, for example:
#include <systems/engineering_systems.cfg> // hosts, aliases & groups
#include <systems/production_systems.cfg> // ditto
#include <systems/marketing_systems.cfg> // ditto
#include <systems/finance_systems.cfg> // ditto
You must still be careful, however, to register hosts and aliases before mention in any host groups. If you do this, for example,
printserver
members ... mickey roger yogi whitey ...
#include <systems/linux_systems.cfg>
where the mickey, roger, yogi, and whitey systems are first registered in linux_systems.cfg after the printserver specification--this would be an error.
Also, you must register a hostname in a hosts stanza before registering any aliases for it.
Host and alias (and group) names may include .'s (dots), allowing your setup to span more than one network domain. For every system listed in a host (operating system) stanza as a fully qualified domain name, you will probably want to give those hosts aliases, for easier referencing in the config files and at the command line. (So, for example, by giving the host "foobar.widgets.acme.com" the alias "foobar", you can use the second shorter form subsequently in all your config files and piktc commands.)
Refer to the sample systems.cfg for examples.
The define config file specifies a set of logical defines. A define stanza takes the form
<define> TRUE|true|YES|yes|ON|on|1|FALSE|false|NO|no|OFF|off|0|
[<proc>]
where the TRUE, FALSE, etc. can follow on the same line or on a subsequent indented line.
You may also set a define according to the exit status (TRUE or FALSE) of any process, for example
dst // TRUE if Daylight Savings Time now applies, FALSE otherwise
// at our site, `date +%Z` returns "CDT" if DST is in effect,
// "CST" otherwise; use your own time zone string as needed
[test `date +%Z` = "CDT"]
Observe that you can set/unset defines on a per-machine basis in the defines.cfg file, for example
#if dbserver
paranoid TRUE
#else
paranoid FALSE
#endif
Or, alternatively and equivalently,
paranoid
#if dbserver
TRUE
#else
FALSE
#endif
You may use #ifdef, #ifndef, and #setdef in defines.cfg just so long as the referenced define is specified earlier in defines.cfg. For example, you may do this:
attentive TRUE // the lowest security level
cautious // the second security level
# if misscritsys | cssys
TRUE
# else
FALSE
# endif
worried // the third security level
#if misscritsys
TRUE
#else
FALSE
#endif
paranoid FALSE // the fourth, and highest, security level
#ifdef cautious
# setdef attentive = TRUE // or: #define attentive
#endifdef
#ifdef worried
# setdef attentive = TRUE
# setdef cautious = TRUE
#endifdef
#ifdef paranoid
# setdef attentive = TRUE
# setdef cautious = TRUE
# setdef worried = TRUE
#endifdef
This code ensures that if you are in a certain security state, all lower security levels are also set to TRUE. (If you are paranoid, you are guaranteed also worried, cautious, and attentive.)
Refer to the sample defines.cfg for examples.
The macro config file specifies string substitutions. A macro stanza may take the form
<macro> <macro definition text>
where the definition text can follow on the same line and/or subsequent indented lines. In config files, a macro is signified by an equal sign-identifier combination, =<macro>. Macro definitions, but not macro names, may include embedded macros. In other words, this is legal:
<macro1> <macrodef 1>
...
<macro2> <... =macro1 ...>
A macro definition may only reference other macros defined before it.
Macros of the form
<macro> <macro definition text>
are simple string substitutions. Macro stanzas of the form
<macro>(<arg1>, <arg2>, ...) <text> (<arg1>) <text> (<arg2>) ...
allow you to specify arguments in your macro substitutions.
For example, if you define a macro in macros.cfg like so:
remind(Y,M,D,MSG) if #now() > #datevalue((Y),(M),(D))
output mail (MSG)
endif
and reference it in alarms.cfg like so:
=remind(2000, 2, 15, "Lift the pwherman account restriction.")
this will appear in the .alt file:
if #now() > #datevalue(2000, 2, 15)
output mail "Lift the pwherman account restriction."
endif
Note that in the macro definition, macro parameters must be enclosed within parentheses. In the macro reference, however, enclosing parentheses are not necessary. Macro parameters must begin with a letter, followed by any number of letters, digits, or underscores. (Making macro parameters all caps, as in the examples above, is not a requirement, but it makes it easier to see them in the macro definition if you do so.)
As with simple macros, macros-with-arguments may reference other macros-with-arguments, so all manner of macro nesting is allowed. For example, these macros.cfg macro definitions are all perfectly legal:
foo1 blah
foo2(A) blah (A) =foo1
foo3(A, B) =foo2("=foo1") (B) blahblah =foo2((A)) =foo1=foo1
The macro reference (in a subsequent config file)
=foo3("yadda, yadda", =foo1)
would preprocess to
blah "blah" blah blah blahblah blah "yadda, yadda" blah blahblah
A self-referencing macro definition or macro reference is an error, for example:
foo(A) blah =foo((A))
or
=foo(=foo("yadda, yadda"))
For some real-life macro examples, refer to the sample macros.cfg and alarms.cfg.
You may employ macros within piktc command lines, for the +C (command) item (when specifying program paths, for example), or when specifying +|-A (alert), +|-P (program), +|-F (file), and/or +|-O (object) items. See the piktc section of the Reference for details.
Although macro definitions, including the specification of macro parameters, may span multiple lines in macros.cfg, a macro reference is restricted to a single line. In other words (and unless and until we can code otherwise), spanning a macro reference across multiple lines is illegal, for example:
=remind(
#if perf
2005,
#else
2000,
#endif
2, 15, "Lift the temporary pwherman account restriction.")
We hope to add this flexibility, but for various reasons it won't be easy.
There are a limited number of built-in, pre-defined macros:
-
piktdir: resolves to the PIKT home directory
-
pikthostname: translates to the name of the machine that piktc is currently processing
-
piktmaster: the name of the master control machine
-
pikthosts: a list of all hosts defined in systems.cfg
-
piktopsystems: a list of all operating systems defined in systems.cfg
-
piktnever: resolves to a special timing string that signals piktd to bypass the current alert
The following built-in macros are intended for PIKT.conf management:
-
piktuid: resolves to the uid (as specified in keys.conf) for the current host
-
piktgid: resolves to the gid (as specified in keys.conf) for the current host
-
piktprivate_key: resolves to the private_key (as specified in keys.conf) for the current host
See the keys.conf section of this Reference for more detail.
The following built-in macros are useful for document formatting (and are especially important when defining other macros):
-
piktnullchar: resolves to the null character (also: =_)
-
piktspacechar: resolves to the space character
-
pikttabchar: resolves to the tab character
-
piktnewlinechar: resolves to the newline character
If you wanted to define a 'spc' macro in defines.cfg, this would not work:
spc
for then you would have no stanza body. And neither would this work:
spc " "
for then " " would be inserted, including the quotation marks, into your text. This is the correct way to define the 'spc' macro:
spc =piktspacechar
The special '=_' pre-defined macro is a kind of null macro, used to separate a macro component from the following text. For example, if you have defined '=dash' in macros.cfg as
dash --
the following would not work as intended
=dashIn the first case ...
because piktc would complain about not finding the '=dashIn' macro. You would solve this problem by redoing the line this way:
=dash=_In the first case ...
for then the =_ provides the needed separation between '=dash' and 'In'. Just to be clear, in the example, the after-preprocessing result would be
--In the first case ...
Refer to the sample macros.cfg for examples.
The alerts.cfg file groups together alarm scripts. The format is:
<alert>
timing <min> <hr> <dom> <moy> <dow> [<drift>]
drift <minutes> [optional]
priority|nice <nice level> [optional]
[or:]
nicecmd "<nicecmd>" [optional]
mailcmd "<mailcmd>" [optional]
lpcmd "<lpcmd>" [optional]
execcmd "<execcmd>" [optional]
alarms|scripts <alarm> [optional]
<alarm>
<alarm>
...
The alert name usually indicates its timing or purpose.
The timing parameters follow the usual cron conventions (see the crontab man page) and then some.
One not so usual timing spec is x-y/z, which says to range from x through y at intervals of z.
Another novel timing spec is random timings. You may specify timing values like so:
timing 20% * * * *
This says to run the alert randomly on average every five minutes. (That is, there is a 20% chance of running the alert any given minute.)
Here is another example:
timing 30 25% * * *
This says to run the alert randomly on average every four hours (at half past the hour). (That is, there is a 25% chance of running the alert any given hour.)
To be more precise, in the first example, the '20%' is like a '*', that is, match every single minute, then apply a 20% probability modifier to the entire timing. Probability modifiers are cumulative. So, in this example,
timing 20% 25% * * *
in effect, every minute there is a 20% times 25% chance, or just 5% chance overall, of running the alert (on average three times every hour, because there is a one-in-twenty chance of running each minute, and there are sixty minutes in an hour). To run an alert at 3:30 in the morning on average once every four days, you could do this
timing 30 3 25% * *
or this
timing 30 3 * 25% *
or this
timing 30 3 * * 25%
or even this
timing 30 3 50% * 50%
The last four examples would all have the same effect.
The random timing spec is especially useful in security situations, where you want some unpredictability in your monitoring schedules.
Still another novel timing spec is "drift". "drift" is how many minutes an alert launch may randomly occur before or after a specified time.
For example,
timing 0,30 * * * * 5
or equivalently,
timing 0,30 * * * *
drift 5
says to run an alert twice an hour, at the top of the hour also the bottom of the hour, give or take a random 5 minutes. In other words, the alert might run randomly anywhere from, say, 14:55 to 15:05, and from 15:25 to 15:35 (and likewise for every other hour during the day).
Timing drift may be as large as you want, so long as there is no possibility of alert launches overlapping. So, it is even possible to schedule alerts with drift extending to days or even weeks.
Timing drift is especially useful when you want alerts to run around a certain time (e.g., midnight), but you don't want them all to go off at precisely that time on all systems (to avoid "bunching up").
The multiple alert timing option allows you to set, say, different alert schedules for normal in-week business hours, in-week night-time hours, and weekend hours. For example
Critical
timing 15,45 8-16 * * 1-5 5 // run every 1/2 hour
// (with 5-minute drift)
// from 8 AM to before 5 PM
// on Monday thru Friday
15 0-7,17-23 * * 1-5 5 // run once hourly
// (with 5-minute drift)
// before 8 AM and after 5 PM
// on Monday thru Friday
15 0-18/6 * * 0,6 5 // run only every six hours
// (with 5-minute drift)
// on Saturday and Sunday
With these settings, the following lines would appear in piktd.conf (with line wrap shown here for display purposes):
15,45 8-16 * * 1-5 5 /pikt/bin/pikt +M "/usr/bin/mailx -s
'PIKT Alert on vienna: Critical' brahms\@hamburg" +A Critical
15 0-7,17-23 * * 1-5 5 /pikt/bin/pikt +M "/usr/bin/mailx -s
'PIKT Alert on vienna: Critical' brahms\@hamburg" +A Critical
15 0-18/6 * * 0,6 5 /pikt/bin/pikt +M "/usr/bin/mailx -s
'PIKT Alert on vienna: Critical' brahms\@hamburg" +A Critical
The idea here is to cut down on the volume of mail when nobody is usually around to respond to it. (Of course, this might cut down on the frequency of non-mail actions--such as auto-fixing what's broken--during evenings and weekends. So you must use this feature carefully.)
If you specify multiple timings, they must follow this format:
timing <mins> <hrs> <dom> <moy> <dow> <drift>
<mins> <hrs> <dom> <moy> <dow> <drift>
<mins> <hrs> <dom> <moy> <dow> <drift>
...
up to a maximum of sixteen timing specs. If you use multiple timing specs, all six fields are required; omitting <drift> (or using a separate line 'drift <drift>', permitted with single timing specs) is not an option. With the appropriate #if and #ifdef directives, you can customize these multiple timings on a per-machine and per-define basis to your heart's content. (For example, go into less-frequent-alert mode on weekends on your non-mission-critical systems only. The mission-critical systems alert as usual, with no diminishment of frequency, overnight and on weekends.)
For scripts meant to be executed via 'piktc -x' and not run via piktd, you should specify the timing using the built-in macro =piktnever.
You can specify the alert's "nice" level using either the "priority" or "nice" keyword. The priority level must range from -20 (highest priority) to 19 (lowest priority). If no priority is specified, the nice level defaults to 0.
Alternatively, you can use the "nicecmd" keyword, and specify the full nice command following (e.g., "/usr/bin/nice -10"; you also have the option of inserting here something other than the default Unix nice command).
As alarm scripts are run, their output is queued. At the end of the alert run (an alert is a set of alarms), the queued output may be sent as a single e-mail message, to one or more systems administrators, or printed out. The mailcmd/lpcmd lines indicate what mail/print commands to use, and in the case of mailcmd who the e-mail gets sent to. (It's possible to dispense with any reporting, hence both mailcmd and lpcmd are optional.)
What usually follows is a list of alarms or Pikt scripts. Every item in the alarms or scripts list must have a corresponding alarm or script definition in alarms.cfg.
Alarms are usually grouped together by function, by level of criticality, or by timing.
With execcmd, you may instead register simple one-liner, crontab-like commands directly in piktd.conf, for example:
#if mailserver
BakMail // do nightly backup of /var/mail
timing 40 23 * * *
priority 10
execcmd "=prgdir/bakmail.pl -R 1"
#endif
The command
# piktc -ierv +A BakMail +H mailserver
installs the following alarm script on all mailserver machines
BakMailScript
exec "/pikt/lib/programs/bakmail.pl -R 1"
(note: registration of this script in alarms.cfg is unnecessary; it is implicit when using execcmd) and adds the following line to those systems' piktd.conf (and restarts the piktd):
40 23 * * * 0 /usr/bin/nice -10 /pikt/bin/pikt +A BakMail
Note that you may not use execcmd together with an alarms or scripts listing. You either specify one execcmd, or you specify an alarms or scripts list.
Refer to the sample alerts.cfg for examples.
The alarms.cfg file is a series of one or more Pikt alarm scripts. For a more detailed discussion of Pikt scripts, see the Pikt Script Language. Refer to the sample alarms.cfg for examples.
In objects.cfg, you specify system objects to be monitored. The form of an objects stanza is
<objects> <object>
<object>
...
Object listings can also encompass data parameters:
<objects> <object> <parameter1> <parameter2> ...
<object> <parameter1> <parameter2> ...
...
Refer to the sample objects.cfg for examples.
The file programs.cfg contains programs written in other languages, with each program in its own stanza. The form of a program stanza is:
<program> [path "<filepath>"]
[mode <octal filemode> uid <number> gid <number>]
<program statement>
<program statement>
...
Since programs may contain blank lines, what separates one program from another are program identifiers, listed in the first column. The actual programs follow, on succeeding indented lines. (See the discussion in Format about how to properly indent program lines.)
Specifying a program path is optional. With no directory specified, the program will be installed in the =piktdir/lib/programs directory. To override this, you can either prepend the program's stanza identifier with the directory, else include a path specification.
For example, this will install foobar.pl in =piktdir/lib/programs
foobar.pl mode 750 uid 0 gid 0
...
this will install it in /usr/local/bin
/usr/local/bin/foobar.pl mode 750 uid 0 gid 0
...
and this, too, will install it in /usr/local/bin
foobar.pl path "/usr/local/bin/foobar.pl" mode 750 uid 0 gid 0
...
This latter method, using the "path" keyword, is preferred over the previous method, using the entire filepath as the stanza identifier. (See also the discussion in Format on how to handle identically named programs.)
Specifying a program file's mode (e.g., 755), uid (e.g., 0), and gid (e.g., 1) is optional. If they are absent, the defaults are 750, 0 & 0.
Since other programs may contain their own comments as well as special character sequences, Pikt comment stripping is handled in a special way. See piktc.
Refer to the sample programs.cfg for examples.
In files.cfg, you can centrally manage system configuration files (such as /etc/inetd.conf, /etc/motd, ...), and indeed any text file. The form of a file stanza is:
<file> [path "<filepath>"]
[mode <octal filemode> uid <number> gid <number>]
<text line>
<text line>
...
files.cfg is much like programs.cfg, except that it can and should contain non-program files and/or programs external to the PIKT setup. Most of what is said about programs.cfg above applies also to files.cfg.
Specifying a file's mode (e.g., 644), uid (e.g., 0), and gid (e.g., 1) is optional. If they are absent, the defaults are 640, 0 & 0.
In files.cfg, unlike in programs.cfg, there is no default path: All file stanzas in files.cfg must include the "path" specification (or the stanza identifiers must be full pathnames.) (In programs.cfg, path-less programs get installed in the =piktdir/lib/programs directory.)
Refer to the sample files.cfg for examples.
In a complete PIKT setup, you have all eight basic configuration files: systems.cfg, defines.cfg, macros.cfg, alerts.cfg, alarms.cfg, objects.cfg, programs.cfg, and files.cfg. You might, in addition, have #include file spinoffs from those basic eight.
It is possible to deploy PIKT in a partial configuration, with subsets of the eight basic config file types. systems.cfg is always required, but all the rest are optional.
For example, here are a few (among many) of the possible PIKT setups:
-
piktc as rsh/ssh replacement (no macros or defines): systems.cfg only.
-
piktc as rsh/ssh replacement (with macros and possibly defines): systems.cfg, macros.cfg; and optionally defines.cfg.
-
piktc as rdist replacement: systems.cfg, files.cfg; and optionally programs.cfg, macros.cfg, defines.cfg.
-
a centrally managed cron replacement: systems.cfg, alerts.cfg, alarms.cfg; and optionally macros.cfg, defines.cfg.
-
system/network monitor (but without system files management): all config files except files.cfg.
-
system/network monitor; rsh/ssh, rdist, cron replacements: all config files.
Other combinations are possible, but these are the most common.
So, you may utilize all that PIKT has to offer, or just pick and choose among its many functionalities.
|