1#!/bin/sh
2
3#################################################################################
4#
5#   Lynis
6# ------------------
7#
8# Copyright 2007-2013, Michael Boelen
9# Copyright 2007-2021, CISOfy
10#
11# Website  : https://cisofy.com
12# Blog     : http://linux-audit.com
13# GitHub   : https://github.com/CISOfy/lynis
14#
15# Lynis comes with ABSOLUTELY NO WARRANTY. This is free software, and you are
16# welcome to redistribute it under the terms of the GNU General Public License.
17# See LICENSE file for usage of this software.
18#
19######################################################################
20#
21# Helper program to share details
22#
23######################################################################
24#
25# How to use:
26# ------------
27# Run: lynis show <option>
28#
29######################################################################
30
31COMMANDS="audit configure generate show update upload-only"
32HELPERS="audit configure show update"
33OPTIONS="--auditor\n--cronjob (--cron)\n--debug\n--developer\n--devops\n--forensics\n--help (-h)\n--log-file\n--manpage (--man)\n--no-colors\n--no-log\n--no-plugins\n--pentest\n--profile\n--plugin-dir\n--quick (-Q)\n--quiet (-q)\n--report-file\n--reverse-colors\n--tests\n--tests-from-category\n--tests-from-group\n--usecwd\n--upload\n--verbose\n--version (-V)\n--wait\n--warnings-only"
34
35SHOW_ARGS="categories changelog commands dbdir details environment eol groups help hostids includedir language license logfile man options os pidfile plugindir profiles release releasedate report settings tests version workdir"
36SHOW_HELP="lynis show ${BROWN}categories${NORMAL}              (display test categories)
37lynis show ${BROWN}changelog${NORMAL} ${GRAY}[version]${NORMAL}     (release details)
38lynis show ${BROWN}commands${NORMAL}                (all available commands)
39lynis show ${BROWN}dbdir${NORMAL}                   (database directory)
40lynis show ${BROWN}details${NORMAL}                 (display test details from log file)
41lynis show ${BROWN}environment${NORMAL}             (hardware, virtual machine, or container type)
42lynis show ${BROWN}eol${NORMAL}                     (OS end-of-life status)
43lynis show ${BROWN}groups${NORMAL}                  (test groups)
44lynis show ${BROWN}help${NORMAL}                    (detailed information about arguments)
45lynis show ${BROWN}hostids${NORMAL}                 (unique IDs for this system)
46lynis show ${BROWN}includedir${NORMAL}              (include directory for tests and functions)
47lynis show ${BROWN}language${NORMAL}                (configured or detected language)
48lynis show ${BROWN}license${NORMAL}                 (license details)
49lynis show ${BROWN}logfile${NORMAL}                 (location of logfile)
50lynis show ${BROWN}man${NORMAL}                     (show help)
51lynis show ${BROWN}options${NORMAL}                 (available flags and options)
52lynis show ${BROWN}os${NORMAL}                      (operating system and version)
53lynis show ${BROWN}pidfile${NORMAL}                 (active file to stored process ID)
54lynis show ${BROWN}plugindir${NORMAL}               (directory with plugins)
55lynis show ${BROWN}profiles${NORMAL}                (discovered profiles)
56lynis show ${BROWN}release${NORMAL}                 (version)
57lynis show ${BROWN}releasedate${NORMAL}             (date of release)
58lynis show ${BROWN}report${NORMAL}                  (location of report data)
59lynis show ${BROWN}settings${NORMAL}                (display configured settings, ${WHITE}options:${NORMAL} ${CYAN}--brief --nocolors${NORMAL})
60lynis show ${BROWN}tests${NORMAL} ${GRAY}[test]${NORMAL}            (display information about one or more tests)
61lynis show ${BROWN}tests skipped${NORMAL}           (which tests to skip according profile)
62lynis show ${BROWN}version${NORMAL}                 (${PROGRAM_NAME} version)
63lynis show ${BROWN}workdir${NORMAL}                 (work directory)"
64
65AUDIT_ARGS="( dockerfile | system )"
66AUDIT_HELP="
67  ${WHITE}lynis audit <target>${NORMAL}
68
69  ${CYAN}audit dockerfile ${BROWN}<file>${NORMAL}
70
71  Perform security audit on a Docker build file
72  ${GRAY}Example:${NORMAL}
73    lynis audit dockerfile Dockerfile
74
75
76  ${CYAN}audit system ${GRAY}[options]${NORMAL}
77
78  Perform security system audit
79
80  ${GRAY}Examples:${NORMAL}
81    lynis audit system
82    lynis audit system --cronjob
83    lynis audit system --profile developer.prf
84    lynis audit system --quick
85
86
87  ${CYAN}audit system remote ${BROWN}<target> ${GRAY}[options]${NORMAL}
88
89  Perform security system audit on a remote target
90
91  ${GRAY}Examples:${NORMAL}
92    lynis audit system remote 192.168.1.100
93    lynis audit system remote 192.168.1.100 --no-colors
94
95"
96
97GENERATE_ARGS="( --save )"
98GENERATE_HELP="
99  Generate random value for hostid and hostid2
100  ${WHITE}lynis generate hostids${NORMAL}
101
102  Generate and save values
103  ${WHITE}lynis generate hostids --save${NORMAL}
104
105  Generate systemd units to run Lynis on a schedule (e.g. daily)
106  ${WHITE}lynis generate systemd-units${NORMAL}
107
108"
109
110
111UPDATE_ARGS="check | info"
112UPDATE_HELP="
113  ${CYAN}update info${NORMAL}
114
115  Check and show version information
116
117  ${CYAN}update check${NORMAL}
118
119  Just check if version is up-to-date
120
121"
122
123UPLOAD_ONLY_HELP="
124  ${CYAN}update-only${NORMAL}
125
126  Perform just a data upload
127
128"
129
130SHOW_SETTINGS_ARGS="--brief --configured-only --nocolors"
131SHOW_TESTS_ARGS="skipped"
132
133COMMANDS_AUDIT_SYSTEM_USAGE="Usage: lynis audit system"
134COMMANDS_AUDIT_SYSTEM_FUNCTION="Function: performs a security audit of the system"
135
136if [ $# -gt 0 ]; then
137    case $1 in
138        "categories")
139            ViewCategories
140            ;;
141        "changelog")
142            # Allow providing a version
143            if [ $# -gt 1 ]; then
144                shift; SEARCH_VERSION="$1"
145            fi
146            PROGRAM_NAME_LOWER=$( echo ${PROGRAM_NAME} | tr '[:upper:]' '[:lower:]')
147            CHANGELOG_PATHS="/usr/share/doc/${PROGRAM_NAME} /usr/share/doc/${PROGRAM_NAME}-${PROGRAM_VERSION} /usr/share/doc/${PROGRAM_NAME_LOWER} ."
148            CHANGELOG=""
149            if [ -z "${SEARCH_VERSION}" ]; then SEARCH_VERSION="${PROGRAM_VERSION}"; fi
150            STARTED=0
151            for FILEPATH in ${CHANGELOG_PATHS}; do
152                if [ -f ${FILEPATH}/CHANGELOG.md ]; then
153                    CHANGELOG="${FILEPATH}/CHANGELOG.md"
154                # Check also for gzipped changelog
155                elif [ -f ${FILEPATH}/changelog.gz ]; then
156                    ZCAT=$(which zcat 2> /dev/null | grep -v "no [^ ]* in ")
157                    if [ -n "${ZCAT}" ]; then
158                        CreateTempFile
159                        CHANGELOG="${TEMP_FILE}"
160                        LogText "Result: found gzipped changelog in ${FILEPATH}"
161                        LogText "Action: Creating temporary file to store text"
162                        ${ZCAT} ${FILEPATH}/changelog.gz > ${CHANGELOG}
163                    else
164                        DisplayError "Could not find zcat utility to use on gzipped changelog"
165                    fi
166                fi
167                if [ -n "${CHANGELOG}" ]; then LogText "Result: found changelog file: ${CHANGELOG}"; break; fi
168            done
169            if [ -n "${CHANGELOG}" ]; then
170                SEARCH=$(sed 's/^## //' ${CHANGELOG} | grep -E "^${PROGRAM_NAME} ${SEARCH_VERSION}")
171                if [ $? -eq 0 ]; then
172                    while read -r LINE; do
173                        if [ ${STARTED} -eq 0 ]; then
174                            SEARCH=$(echo ${LINE} | sed 's/^## //' | grep -E "^${PROGRAM_NAME} ${SEARCH_VERSION}")
175                            if [ $? -eq 0 ]; then STARTED=1; ${ECHOCMD} "${BOLD}${LINE}${NORMAL}"; fi
176                        else
177                            # Stop if we find the next Lynis version
178                            SEARCH=$(echo ${LINE} | sed 's/^## //' | grep -E "^${PROGRAM_NAME} [0-9]\.[0-9]\.[0-9]")
179                            if [ $? -eq 0 ]; then
180                                break
181                            else
182                                ${ECHOCMD} "${LINE}"
183                            fi
184                        fi
185                    done < ${CHANGELOG}
186                else
187                    DisplayError "Could not find this version in the changelog"
188                    ${ECHOCMD} ""
189                    ${ECHOCMD} "${HEADER}Usage:${NORMAL}"
190                    ${ECHOCMD} "$0 lynis show changelog [version]"
191                    ${ECHOCMD} ""
192                    ${ECHOCMD} "${HEADER}${PROGRAM_NAME} versions:${NORMAL}"
193                    SEARCH=$(sed 's/^## //' ${CHANGELOG} | grep -E "^Lynis [0-9]\.[0-9]\.[0-9] " | awk '{print $2}' | sort -n)
194                    ${ECHOCMD} ${SEARCH}
195                    ExitFatal
196                fi
197            else
198                DisplayError "Could not find the changelog file (searched in ${CHANGELOG_PATHS})"
199                ExitFatal
200            fi
201            ;;
202        "commands")
203            if [ $# -eq 1 ]; then
204                ${ECHOCMD} "\n${WHITE}Commands:${NORMAL}"
205                for ITEM in ${COMMANDS}; do
206                    ${ECHOCMD} "lynis ${CYAN}${ITEM}${NORMAL}"
207                done
208                ${ECHOCMD} ""
209            else
210                shift
211                if [ $# -eq 1 ]; then
212                    case $1 in
213                        "audit") ${ECHOCMD} "${AUDIT_HELP}" ;;
214                        "configure") ${ECHOCMD} "No help available yet" ;;
215                        "generate") ${ECHOCMD} "${GENERATE_HELP}" ;;
216                        "show") ${ECHOCMD} "${SHOW_HELP}" ;;
217                        "update")  ${ECHOCMD} "${UPDATE_HELP}" ;;
218                        "upload-only") ${ECHOCMD} "${UPLOAD_ONLY_HELP}" ;;
219                        *) DisplayError "Unknown argument for 'commands'"
220                    esac
221                else
222                    shift
223                    case $1 in
224                        "dockerfile")
225                            ${ECHOCMD} "Usage: lynis audit dockerfile <file>"
226                        ;;
227                        "system")
228                            ${ECHOCMD} "${COMMANDS_AUDIT_SYSTEM_USAGE}\n${COMMANDS_AUDIT_SYSTEM_FUNCTION}\n"
229                        ;;
230                        *)
231                            DisplayError "Unknown argument '$1' for commands"
232                        ;;
233                    esac
234                fi
235            fi
236            ;;
237        "dbdir")
238            ${ECHOCMD} "${DBDIR}"
239            ;;
240        "details")
241            if [ -z "${LOGFILE}" ]; then DisplayError "Could not find log file to parse"; fi
242            if [ $# -eq 1 ]; then
243                DisplayError "This command needs a test ID (e.g. CORE-1000) to search for. This command is used after a scan (lynis audit system). Run 'lynis show tests' to see all available tests."
244            else
245                shift
246                if [ $# -eq 1 ]; then
247                    TESTID="$1"
248                    awk -v search="Performing test ID $TESTID" '$0 ~ search {++f;p=1}p&&f==1;/====/{p=0}' ${LOGFILE}
249                fi
250            fi
251            ;;
252        "environment")
253            if [ -z "${CONTAINER_TYPE}" ]; then
254                ${ECHOCMD} "container=0"
255            else
256                ${ECHOCMD} "container=1"
257                ${ECHOCMD} "container-type=${CONTAINER_TYPE}"
258            fi
259            if [ ${ISVIRTUALMACHINE} -eq 1 ]; then
260                ${ECHOCMD} "virtual-machine=1"
261                ${ECHOCMD} "virtual-machine-type=${VMTYPE}"
262            else
263                ${ECHOCMD} "hardware=1"
264                ${ECHOCMD} "virtual-machine=0"
265            fi
266            ;;
267        "eol")
268            ${ECHOCMD} "Operating-system=${OS_FULLNAME}"
269            if [ ${EOL} -eq 0 ]; then
270                ${ECHOCMD} "End-of-life=No"
271            elif [ ${EOL} -eq 1 ]; then
272                ${ECHOCMD} "End-of-life=Yes"
273            elif [ ${EOL} -eq 255 ]; then
274                ${ECHOCMD} "End-of-life=Not tested"
275            else
276                ${ECHOCMD} "End-of-life=Unknown"
277            fi
278            ;;
279        "groups")
280            ViewGroups
281            ;;
282        "help" | "--help" | "-h")
283            if [ $# -eq 1 ]; then
284                ${ECHOCMD} "${PROGRAM_NAME} ${PROGRAM_VERSION} - Help"
285                ${ECHOCMD} "=========================="
286                ${ECHOCMD} ""
287                ${ECHOCMD} "${WHITE}Commands${NORMAL}:"
288                for ITEM in ${COMMANDS}; do
289                    ${ECHOCMD} "${CYAN}${ITEM}${NORMAL}"
290                done
291                ${ECHOCMD} ""
292                ${ECHOCMD} "Use 'lynis show help ${CYAN}<command>${NORMAL}' to see details"
293                ${ECHOCMD} ""; ${ECHOCMD} ""
294                ${ECHOCMD} "${WHITE}Options${NORMAL}:\n${GRAY}${OPTIONS}${NORMAL}"
295            else
296                shift
297                case $1 in
298                   "audit") ${ECHOCMD} "${AUDIT_HELP}" ;;
299                   "configure") ${ECHOCMD} "No help available yet" ;;
300                   "generate") ${ECHOCMD} "${GENERATE_HELP}" ;;
301                   "show") ${ECHOCMD} "${SHOW_HELP}" ;;
302                   "update") ${ECHOCMD} "${UPDATE_HELP}" ;;
303                   "upload-only") ${ECHOCMD} "${UPLOAD_ONLY_HELP}" ;;
304                   "?") ${ECHOCMD} "${SHOW_ARGS}" ;;
305                   *) ${ECHOCMD} "Unknown argument provided for lynis show help" ;;
306                esac
307            fi
308            ;;
309        "helpers")              for ITEM in ${HELPERS}; do ${ECHOCMD} ${ITEM}; done ;;
310        "hostids" | "hostid")
311            ${ECHOCMD} "hostid=${HOSTID}"
312            ${ECHOCMD} "hostid2=${HOSTID2}"
313            ${ECHOCMD} "machineid=${MACHINEID}"
314            ;;
315        "includedir")
316            ${ECHOCMD} "${INCLUDEDIR}"
317            ;;
318        "language")             ${ECHOCMD} "${LANGUAGE}" ;;
319        "license")              ${ECHOCMD} "${PROGRAM_LICENSE}" ;;
320        "logfile")              ${ECHOCMD} "${LOGFILE}" ;;
321        "man")                  ${ECHOCMD} "Use ./lynis --man or man lynis" ;;
322        "options")              ${ECHOCMD} "${OPTIONS}" ;;
323        "os")
324                                ${ECHOCMD} "OS=${OS}"
325                                ${ECHOCMD} "OS_NAME=${OS_NAME}"
326                                ${ECHOCMD} "OS_FULLNAME=${OS_FULLNAME}"
327                                ${ECHOCMD} "OS_VERSION=${OS_VERSION}"
328        ;;
329        "pidfile")              ${ECHOCMD} "${PIDFILE}" ;;
330        "profile" | "profiles") for ITEM in ${PROFILES}; do ${ECHOCMD} ${ITEM}; done ;;
331        "profiledir")           ${ECHOCMD} "${PROFILEDIR}" ;;
332        "plugindir")            ${ECHOCMD} "${PLUGINDIR}" ;;
333        "release")              ${ECHOCMD} "${PROGRAM_VERSION}-${PROGRAM_RELEASE_TYPE}" ;;
334        "releasedate")          ${ECHOCMD} "${PROGRAM_RELEASE_DATE}" ;;
335        "report")               ${ECHOCMD} "${REPORTFILE}" ;;
336        "settings")
337            BRIEF_OUTPUT=0
338            COLORED_OUTPUT=1
339            CONFIGURED_ONLY_OUTPUT=0
340            while [ $# -gt 1 ]; do
341                shift
342                case $1 in
343                   "--brief" | "--br") BRIEF_OUTPUT=1 ;;
344                   "--configured-only" | "--co") CONFIGURED_ONLY_OUTPUT=1 ;;
345                   "--nocolors" | "--no-colors" | "--nc") COLORED_OUTPUT=0; COLORS=0 ;;
346                   *)
347                       ${ECHOCMD} "${RED}Error${NORMAL}: Invalid argument provided to 'lynis show settings'\n\n"
348                       ${ECHOCMD} "Suggestions:"
349                       for ITEM in ${SHOW_SETTINGS_ARGS}; do ${ECHOCMD} "lynis show settings ${ITEM}"; done
350                       ExitFatal
351                   ;;
352                esac
353            done
354            if [ ${COLORED_OUTPUT} -eq 0 ]; then RemoveColors; fi
355            # Sort all settings and display them
356            SETTINGS=$(sort ${SETTINGS_FILE} | sed 's/ /:space:/g')
357            for LINE in ${SETTINGS}; do
358                SETTING=$(echo ${LINE} | awk -F';' '{print $1}')
359                VALUE=$(echo ${LINE} | awk -F';' '{print $2}')
360                DESCRIPTION=$(echo ${LINE} | awk -F';' '{print $3}' | sed 's/:space:/ /g')
361                if [ -z "${VALUE}" -a ${CONFIGURED_ONLY_OUTPUT} -eq 0 ]; then VALUE="${GRAY}[not configured]${NORMAL}"; fi
362                if [ -n "${VALUE}" ]; then
363                    if [ ${BRIEF_OUTPUT} -eq 0 ]; then ${ECHOCMD} "${GRAY}# ${DESCRIPTION}${NORMAL}"; fi
364                    ${ECHOCMD} "${WHITE}${SETTING}${NORMAL}=${CYAN}${VALUE}${NORMAL}"
365                    if [ ${BRIEF_OUTPUT} -eq 0 ]; then ${ECHOCMD} ""; fi
366                fi
367            done
368            if [ ${BRIEF_OUTPUT} -eq 0 -a ${CONFIGURED_ONLY_OUTPUT} -eq 0 -a ${COLORED_OUTPUT} -eq 1 ]; then
369                if [ ${COLORS} -eq 1 ]; then
370                    ${ECHOCMD} "# Add --brief to hide descriptions, --configured-only to show configured items only, or --nocolors to remove colors"
371                else
372                    ${ECHOCMD} "# Add --brief to hide descriptions, --configured-only to show configured items only"
373                fi
374            fi
375
376            ;;
377        "tests")
378            if [ $# -gt 1 ]; then
379                shift
380                case $1 in
381                    "skipped")
382                        if [ -z "${SKIP_TESTS}" ]; then
383                            ${ECHOCMD} "# ${CYAN}No tests are skipped (according profile)${NORMAL}"
384                        else
385                            ${ECHOCMD} "# Skipped tests (according profile)"
386                            ${ECHOCMD} "${SKIP_TESTS}"
387                        fi
388                    ;;
389                    *)
390                        if [ -f ${DBDIR}/tests.db ]; then
391                            SEARCH="$1"
392                            FIND=$(grep "^${SEARCH}" ${DBDIR}/tests.db | sed "s/ /:space:/g")
393                            if [ -z "${FIND}" ]; then
394                                ${ECHOCMD} "${WARNING}Error${NORMAL}: ${BOLD}Could not find this test in the database${NORMAL}\n\n"
395                                ExitFatal
396                            else
397                                for ITEM in ${FIND}; do
398                                    TEST_DESCRIPTION=$(echo ${ITEM} | sed "s/:space:/ /g" | awk -F: '{print $6}')
399                                    TEST=$(echo ${ITEM} | awk -F: '{print $1}')
400                                    TEST_TYPE=$(echo ${ITEM} | awk -F: '{print $2}')
401                                    TEST_CATEGORY=$(echo ${ITEM} | awk -F: '{print $3}')
402                                    TEST_GROUP=$(echo ${ITEM} | awk -F: '{print $4}')
403                                    TEST_OS=$(echo ${ITEM} | awk -F: '{print $5}')
404                                    TEST_SKIPPED=0
405                                    ${ECHOCMD} "${CYAN}${TEST}${NORMAL}"
406                                    ${ECHOCMD} "==================================="
407                                    ${ECHOCMD} ""
408                                    ${ECHOCMD} "${WHITE}Type:${NORMAL} ${TEST_TYPE}"
409                                    ${ECHOCMD} ""
410                                    ${ECHOCMD} "${WHITE}Description:${NORMAL}"
411                                    ${ECHOCMD} "${TEST_DESCRIPTION}"
412                                    ${ECHOCMD} ""
413                                    ${ECHOCMD} "${WHITE}Category:${NORMAL} ${TEST_CATEGORY}, ${WHITE}Group:${NORMAL} ${TEST_GROUP}"
414                                    ${ECHOCMD} ""
415                                    ${ECHOCMD} "${WHITE}Test Execution:${NORMAL}"
416                                    if [ "${TEST_OS}" = "" ]; then
417                                        ${ECHOCMD} "  Operating System:    ${GREEN}Yes${NORMAL} (all systems)"
418                                    elif [ "${TEST_OS}" = "${OS}" ]; then
419                                        ${ECHOCMD} "  Operating System:    ${GREEN}Yes${NORMAL} (${TEST_OS} only)"
420                                    else
421                                        ${ECHOCMD} "  Operating System:    ${RED}No${NORMAL} (${TEST_OS} only)"
422                                        TEST_SKIPPED=1
423                                    fi
424                                    if [ -z "${SKIP_TESTS}" ]; then
425                                        ${ECHOCMD} "  Profile:             ${GREEN}Yes${NORMAL} (not configured)"
426                                    else
427                                        FIND=$(echo ${SKIP_TESTS} | grep -E "${TEST}")
428                                        if [ -z "${FIND}" ]; then
429                                            ${ECHOCMD} "  Profile:             ${GREEN}Yes${NORMAL} (test not marked to be skipped)"
430                                        else
431                                            ${ECHOCMD} "  Profile:             ${RED}No${NORMAL} (marked test as to be skipped)"
432                                            TEST_SKIPPED=1
433                                        fi
434                                    fi
435                                    if [ ${TEST_SKIPPED} -eq 1 ]; then ${ECHOCMD} ""; ${ECHOCMD} "  This test will NOT be performed on this system"; fi
436
437                                    ${ECHOCMD} ""
438                                    ${ECHOCMD} ""
439
440                                done
441                            fi
442                        else
443                            ShowError "Can not find tests database"
444                            ${ECHOCMD} "The changelog might not be installed on your system. Details can be found at ${PROGRAM_SOURCE}."
445                            ExitFatal
446                        fi
447                    ;;
448                esac
449            else
450                if [ -f ${DBDIR}/tests.db ]; then
451                    ${ECHOCMD} "# Test       OS         Description"
452                    ${ECHOCMD} "# ======================================================================================"
453                    awk -F: '{ if ($1 !~ /^#/) printf("%-10s %-10s %s (%s)\n",$1,$5,$6,$3)}' ${DBDIR}/tests.db
454                else
455                    ShowError "Can not find tests database"
456                    ${ECHOCMD} "The changelog might not be installed on your system. Details can be found at ${PROGRAM_SOURCE}."
457                    ExitFatal
458                fi
459            fi
460            ;;
461        "version")              ${ECHOCMD} "${PROGRAM_VERSION}" ;;
462        "workdir")              ${ECHOCMD} "${WORKDIR}" ;;
463        "?")                    ${ECHOCMD} "${SHOW_ARGS}" ;;
464        *)                      ${ECHOCMD} "Unknown argument '${RED}$1${NORMAL}' for lynis show" ;;
465    esac
466else
467    ${ECHOCMD} "\n  ${WHITE}Provide an additional argument${NORMAL}\n\n"
468    for ITEM in ${SHOW_ARGS}; do
469        ${ECHOCMD} "    lynis show ${BROWN}${ITEM}${NORMAL}"
470    done
471    ${ECHOCMD} "\n"
472    ${ECHOCMD} ""
473    ${ECHOCMD} "Extended help about the show command can be provided with: $0 show commands show"
474fi
475
476
477ExitClean
478
479# More additions:
480# - categories
481# - workdir
482
483# The End
484