Reporting/Fixing a Problem
(NOTE: Some of the techniques shown or described on this page--marked in purple--require new features in the latest official PIKT 1.19.0 release (pikt-current.tar.gz) that are unavailable in any previous version.)
Oftentimes in a Pikt script, you will both report and fix problems.
For example, ClearTmpNotice is a typical Pikt script to routinely clear tmp directories--automatically dispose of old files & directories and garbage files & directories in the /tmp directory and other temporary directories, such as /var/tmp, etc.
ClearTmp init status active level notice task "Clear the tmp directories of garbage files & directories" input proc "=find =tmp -exec =lld {} \\; 2>/dev/null" filter "=egrep '^(-|d)'" // filter out all but files & dirs dat $usr 3 dat $nam $ rule set #fa = =fileage($nam) #ifdef debug rule output "$text(#fa) $usr $nam" #endifdef rule // skip these files/directories; add to list as needed if $nam =~ "^/tmp/(lost\\+found|\\.X11-unix|\\.X0-lock|\\.ICE-unix)" // =execwait "=touch $nam" next endif rule // remove clearly marked junk files/directories // 7 or more days old; add to list as needed if $nam =~ "^/tmp/(junk[0-9]*)" if (#fa >= 7) =execwait "=rm -rf $nam" endif next endif rule // warn users about stuff 7 or more days old // but less than 14 days old if $usr ne "root" && ((#fa >= 7) && (#fa < 14)) =output_other_mail(CLEARTMP, 'PIKT ClearTmp', $usr, "orphaned?: $inlin") next endif rule // remove all non-root stuff 14 or more days old, // or all stuff in, for example, /var/tmp if #fa >= 14 && (($usr ne "root") || ($nam !~ "^/tmp/")) =execwait "=rm -rf $nam" next fi #if ! ( sheffield | berlin ) rule // report all root stuff 30 or more days old if #fa >= 30 && $usr eq "root" if #fa % 7 == 0 // report only every 7 days output mail "orphaned?: $inlin" endif endif #endifIn the 'input proc' statement, =find is a macro defined in macros.cfg (or one of its #include files, for example, unixcmds_suse_linux_macros.cfg) as
find /usr/bin/find=tmp is another macro defined as
tmp /tmp /var/tmpWe could skip the macro and just do this
input proc "=find /tmp /var/tmp -exec =lld {} \\; 2>/dev/null"but we might use the =tmp macro elsewhere in other scripts and PIKT contexts. If we hard-code the tmp directories here and elsewhere, later on, if we want to add to the list, we will have to hunt down and change the tmp list in possibly many different places. By using the =tmp macro, on the other hand, we would only have to make our changes in one place, at the =tmp macro definition (in macros.cfg). Wherever possible, use macros in this way, as it will make your PIKT operations much more efficient (not to mention less error-prone).
The =lld macro, defined as
lld =ls -ld(where =ls is yet another macro, which resolves to '/bin/ls') gives a so-called long listing.
Before processing the input, we pass it through the filter "=egrep '^(-|d)'" to filter out all but files and directories. Note that we could have merged the filter with the proc (and eliminated the separate filter statement) by means of a Unix pipe:
input proc "=find =tmp -exec =lld {} \\; 2>/dev/null | =egrep '^(-|d)'"(There is, however, good reason sometimes to have a separate filter statement, for example, when using "input file
The proc-filter combo yields input like so:
drwxrwxrwt 77 root root 8192 May 8 02:56 /tmp ... -rw-r--r-- 1 root root 0 Feb 13 2006 /tmp/.clst_portage -r--r--r-- 1 root root 11 Feb 14 06:07 /tmp/.X0-lock drwxrwxrwt 2 root root 4096 Feb 14 06:07 /tmp/.X11-unix -rw------- 1 brahms users 202 Jan 30 00:17 /tmp/.XauthW29qXz -rw-r----- 1 root root 86 May 4 06:40 /tmp/junk1 drwxr-x--- 12 root root 4096 Jan 30 07:30 /tmp/junk10 drwx------ 2 root root 4096 Jan 30 01:08 /tmp/kde-root -rw-r--r-- 1 root root 928 Dec 5 03:21 /tmp/nightly-updates.out drwx------ 2 brahms games 4096 Sep 29 2006 /tmp/kde-brahms -rw-rw-rw- 1 root root 0 Jan 12 03:53 /tmp/foo -rw-r----- 1 root root 3999 Nov 9 06:15 /tmp/junk23/alarms.cfg ...We map some data variables to the input data by means of the dat statements:
dat $usr 3 dat $nam $That is, assign the third input data field (for example, "root") to $usr, and the last data field (for example, "/tmp/.X0-lock") to $nam. These assignments would be redone anew with each succeeding input line.
As we enter the input loop, in the first rule we set a variable, #fa, to the file age (in days) of the current file or directory. Although there is a standard, built-in #fileage() function for determining a file or directory age, in this instance we prefer to use the macro pseudo-function =fileage(), as in:
fileage(F) #trunc((#now()-#filemtime((F)))/=secs_in_day)where =secs_in_day is another macro defined (again in macros.cfg) as:
secs_in_day (60*60*24)PIKT comes equipped with a broad lineup of built-in numerical and string functions. Where lacking, you can define your own macro pseudo-functions in the manner shown.
You may have noticed by now that in the Pikt script language, variables--and functions!--are strongly data typed. For example, #fa, #trunc(), #now() & #filemtime() are all numerical types, while $usr, $nam & $text() are string types. Compared to other languages (such as Perl or the shell), this can be a bit jarring at first, but one gets used to it.
If we have set the debug define to TRUE (see below), then the second rule would be included in the installed ClearTmp script. Debugging output might look something like:
0 root /tmp ... 448 root /tmp/.clst_portage 82 root /tmp/.X0-lock 82 root /tmp/.X11-unix 98 brahms /tmp/.XauthW29qXz 3 root /tmp/junk1 97 root /tmp/junk10 98 root /tmp/kde-root 153 root /tmp/nightly-updates.out 39 brahms /tmp/kde-brahms 115 root /tmp/foo 179 root /tmp/junk23/alarms.cfg ...In the third rule, we bypass some standard files and directories. (Note that we have commented out an exec command to "touch" the file or directory, thereby updating its date stamp.)
The fourth rule tests whether to delete obvious garbage files and directories seven or more days old. (We usually name our temporary files and directories as junk<#>.) First, we test if the current file or directory name pattern matches the regular expression "^/tmp/(junk[0-9]*)". If it does, we then test to see if the file or directory is at least seven days old. If it is, we do an rm of that file or directory--maybe.
Why maybe? The action statement is
=execwait "=rm -rf $nam"=execwait is a macro defined (in macros.cfg) as:
#ifdef doexec execwait exec wait #elsedef execwait output mail #endifdefdoexec, like debug, is a so-called PIKT "define", a logical switch we use to include or exclude sections of scripts, programs, and object files. Defines are set either in the defines.cfg file as
doexec TRUEor elsewhere in the config files by means of '#define doexec' or '#setdef doexec = TRUE' or maybe at the piktc command line using 'piktc -iv +D doexec ...'. In this case, we might be testing this cleanup script and have therefore set doexec to FALSE (perhaps because we installed the script using 'piktc -iv -D doexec ...'). In this case, =execwait is effectively defined as
execwait output mailthe action statement resolves to
output mail "/bin/rm -rf $nam"on the slave system, and PIKT would send us a message of intent only. When we are finished with our testing, we could set doexec to TRUE. After reinstalling the script on the slave, the action statement would then resolve to
exec wait "/bin/rm -rf $nam"By this macro trick, we avoid having to use the longer construct
#ifdef doexec exec wait "/bin/rm -rf $nam" #elsedef output mail "/bin/rm -rf $nam" #endifdefagain and again in our code.
It bears repeating that these macro resolutions, because they involve preprocessor directives, are answered at the time of script installation. =execwait would resolve one way or another depending on whether doexec is set to TRUE or FALSE--at the instant we install the script. The end-result, post-preprocessing script on the slave systems--the script as actually run--would look one way or another depending on how we have specified our PIKT defines (also depending on the current system, when considering #if statements) when we install the script.
Combined with the #ifdef-#elsedef-#endif preprocessor directives, logical defines (for example, doexec, debug, paranoid, and so on) have widespread and varied uses. Together with the #if directive (see below), they help give you a finely grained control over your code and make possible all sorts of sophistication and cleverness.
There is another way we can safely conduct tests with newly written Pikt script code. If we set the script status like so
status testingthen script 'exec' and 'exec wait' statements become effectively 'output mail' (or just plain 'output' if we run the script at the command line), and command strings are reported and/or logged rather than executed, for example,
May 8 09:51:48 antwerp pikt[8953]: [ID 1 INFO] in ClearTmp, exec wait "/bin/rm -rf /tmp/.XauthW29qXz"So, with 'status testing', there is no need to use =execwait, #ifdef doexec, etc.. We could safely use the straightforward 'exec wait' instead and, while in testing status, let PIKT automatically interpret that as 'output mail' for us. (Using the '-T' option with piktc and/or pikt is yet another way that we could effectively achieve testing mode ala 'status testing'.)
Returning to the script, note the nested if-endif's in the fourth rule. For maneuvering around the input and rule sets, Pikt has a complete lineup of flow-control keywords, including: if-elif-else-endif, while, for, repeat-until, switch, break, continue, again, leave, redo, next, skip, last, quit, and die.
Concluding the fourth rule, whether or not the file or directory is at least seven days old, we are done with it and continue on to the next input line.
In the fifth rule, we warn users about possibly orphaned files and directories seven days old or older but less than fourteen days old. (The sixth rule will take care of files fourteen or more days old.)
In the alerts.cfg file, we have included the ClearTmp script in the Notice alerts group. In that group, we have specified who receives warning messages in the mailcmd
mailcmd "=mailx -s 'PIKT Alert on =pikthostname: Notice' =pikt-notice"where the recipient =pikt-notice is a macro resolving to the sysadmins.
Here, in ClearTmp's fifth rule, we send warning e-mail to recipients other than the alert group default, =pikt-notice. We instead send the warning e-mail to the file or directory owner, that is, the $usr, by means of the statement
=output_other_mail(CLEARTMP, 'PIKT ClearTmp', $usr, "orphaned?: $inlin")=output_other_mail() is a pseudo-function defined in the macros.cfg file as
/////////////////////////////////////////////////////////////////////////////// output_other_mail(P, S, R, L) // output conditional mail to addressee(s) // beyond those specified in the alert // mailcmd; we don't #pclose() the (P) // proc handle at the end, instead letting // pikt do it, enabling us to make this a // a one-liner macro // (P) is the proc handle name (e.g., MAIL) // (S) is the subject (e.g., 'check this out') // (R) is the recipient (e.g., brahms\) // (L) is the line (e.g., $inlin) if ! #defined(#isopen(P)) set #isopen(P) = #false() fi if ! #isopen(P) if #popen((P), "=mailx -s (S) (R)", "w") != #err() set #isopen(P) = #true() else output mail "\#popen() failure for: =mailx -s (S) (R)" quit fi fi do #write((P), (L)) ///////////////////////////////////////////////////////////////////////////////"(P, S, R, L)" are macro arguments. After piktc preprocessing, the end-result ClearTmp script as installed on the PIKT slave system(s) would look something like this:
... if ! #defined(#isopenCLEARTMP) set #isopenCLEARTMP = #false() fi if ! #isopenCLEARTMP if #popen(CLEARTMP, "/usr/bin/mailx -s 'PIKT ClearTmp' $usr", "w") != #err() set #isopenCLEARTMP = #true() else output mail "\#popen() failure for: /usr/bin/mailx -s 'PIKT ClearTmp' $usr" quit fi fi do #write(CLEARTMP, "orphaned?: $inlin") ...
The sixth rule deletes all non-root files and directories, and all files and directories in /var/tmp (and any other directories other than /tmp that we define in the =tmp macro), fourteen or more days old. PIKT logs (in this example to Notice.log, because we have added ClearTmp to the Notice alerts) any file or directory deletions (indeed, any actions that might impact on the external environment).
The last rule considers all other files and directories: root-owned files and directories in the =tmp directories. If any of these are at least 30 days old, we send e-mail indicating that (in this case to the alert =pikt-notice recipient), for example:
PIKT ALERT Thu May 8 09:51:50 2007 antwerp NOTICE: ClearTmp Clear the tmp directories of garbage files & directories ... orphaned?: -rw-r--r-- 1 root root 0 Feb 13 2006 /tmp/.clst_portage orphaned?: -rw-r--r-- 1 root root 928 Dec 5 03:21 /tmp/nightly-updates.out orphaned?: drwx------ 2 root root 4096 Jan 30 01:08 /tmp/kde-root orphaned?: -rw-rw-rw- 1 root root 0 Jan 12 03:53 /tmp/foo ...Note how, by using the "#fa % 7" test, we restrict this warning e-mail to just once every seven days. We don't want to be buried in a blizzard of unimportant reminder alerts, so using tricks like these are common.
Note, also, that by means of the #if directive, we filter out the last rule from the target ClearTmp script on two machines, where long-standing /tmp files and directories are commonplace. The last rule would appear in the preprocessed target on every machine other than the two listed.
Similar to #ifdef, the #if-#else-#endif preprocessor directives have PIKT include or exclude script (and other) statements at the per-machine or per-operating system layer. #if directives are governed by the hosts and operating systems you specify in the piktmaster's system.cfg file.
You might have noticed something odd about the nested if-endifs in the fourth rule above. In the inner if-endif, the condition is enclosed in parentheses, while the condition for the outer if-endif is not. In Pikt, parentheses around if-endif conditions, as well as parentheses in while, repeat-until, for and switch statements are optional. No pesky semicolons to worry about either!
And that's not all. Did you catch the "fi" in the sixth rule (also in the =output_other_mail() definition)? For many keywords, you may choose among several synonymous alternatives. If you program using many different languages like we do, how many times have you used "elif" instead of "elsif" or "elseif," and your program failed for that (aggravatingly stupid) reason? In Pikt, elif, elsif, and elseif are all legal, and all have the same effect!
prev page | 1st page | next page |