Changeset 226

Show
Ignore:
Timestamp:
04/08/05 03:37:29 (4 years ago)
Author:
athomas
Message:

Added -l argument which lists available commands.

Also added a help="<help>" option which defines the help string displayed
by -l.

Cleaned up the code a bit, adding some basic dynamic array functions instead of
replicating the code across multiple areas.

Closes #4

Files:

Legend:

Unmodified
Added
Removed
Modified
Copied
Moved
  • op/trunk/ChangeLog

    r221 r226  
    130130incorrect permissions.  
    131131 
    132 07/04/05 - op 1.27 
     13207/04/05 - op 1.27/1.28 
    133133================== 
    134134Added ''nolog'' option which suppresses informational logs. Useful for 
     
    138138variables in configuration files to be used deterministically. Commands 
    139139can also be overridden in this fashion. 
     140 
     14108/04/05 - op 1.29 
     142================== 
     143Added -l argument which lists available commands. 
     144 
     145Also added a {{{help="<help>"}}} option which defines the help string displayed 
     146by -l. 
     147 
     148Cleaned up the code a bit, adding some basic dynamic array functions instead of 
     149replicating the code across multiple areas. 
     150 
     151Closes #4 
  • op/trunk/defs.h

    r224 r226  
    2525} var_t; 
    2626 
     27typedef struct array_s { 
     28        void **data; 
     29        int size, capacity; 
     30} array_t; 
     31 
     32/* functions to manage a dynamically extensible array of pointers */ 
     33#define ARRAY_CHUNK     32 
     34array_t *array_alloc(); 
     35void array_free(array_t *array); 
     36array_t *array_free_contents(array_t *array); 
     37void *array_push(array_t *array, void *object); 
     38void *array_pop(array_t *array); 
     39int array_extend(array_t *array, int capacity); 
     40 
    2741extern cmd_t    *First, *Build(); 
    2842extern var_t    *Variables; 
     
    4054#define OP_ACCESS       "/etc/op.conf" 
    4155#define OP_ACCESS_DIR   "/etc/op.d" 
    42 #define VERSION     "1.28
     56#define VERSION     "1.29
    4357 
    4458#define VAR_EXPAND_LEN  8192 
  • op/trunk/lex.l

    r216 r226  
    5151^{WS}                   BEGIN ARGS; 
    5252<ARGS>";"               state++; 
    53 <ARGS>'(\\.|[^'])*'   addquotedarg(state, cmd, yytext); 
    54 <ARGS>\"(\\.|[^\"])*\"        addquotedarg(state, cmd, yytext); 
     53<ARGS>[^ \n\t;=]+='(\\.|[^'])*'       addquotedarg(state, cmd, yytext); 
     54<ARGS>[^ \n\t;=]+=\"(\\.|[^\"])*\"    addquotedarg(state, cmd, yytext); 
    5555<ARGS>{NWS}             addarg(state, cmd, yytext); 
    5656<ARGS>{WS}              ; 
     
    176176static void addquotedarg(int state, cmd_t *cmd, const char *instr) { 
    177177char buffer[MAXSTRLEN]; 
    178 int i, o
     178int i, o, q
    179179 
    180180        if (strlen(instr) + 2 > MAXSTRLEN) { 
     
    182182                exit(1); 
    183183        } 
    184         for (o = 0, i = 1; instr[i] != instr[0]; ++i, ++o) { 
     184        for (o = 0; !strchr("'\"", instr[o]); ++o) 
     185                buffer[o] = instr[o]; 
     186        q = o; 
     187 
     188        for (i = o + 1; instr[i] && instr[i] != instr[q]; ++i, ++o) { 
    185189                if (instr[i] == '\\') { 
    186190                int c = instr[++i]; 
  • op/trunk/main.c

    r223 r226  
    7575cmd_t   *Find(); 
    7676int Verify(cmd_t *cmd, int num, int argc, char **argv); 
     77int VerifyPermissions(cmd_t *cmd); 
    7778cmd_t   *Find(char *name); 
     79char    *FindOpt(cmd_t *cmd, char *str); 
     80void    ListCommands(); 
    7881int Go(cmd_t *cmd, int num, int argc, char **argv); 
    7982cmd_t   *First = NULL; 
     
    8689void Usage() 
    8790{ 
    88         fatal(0, "Usage: %s mnemonic [args]\n       %s -V -H [-u username] mnemonic", 
    89                         gargv[0], gargv[0]); 
    90 
    91  
    92 int file_compare(const void *a, const void *b) 
     91        fatal(0,        "Usage: %s mnemonic [args]\n" 
     92                                "       %s -l     List available commands\n" 
     93                                "       %s -V     Show op version", 
     94                        gargv[0], gargv[0], gargv[0]); 
     95
     96 
     97int FileCompare(const void *a, const void *b) 
    9398{ 
    9499        return strcmp(*(char**)a, *(char**)b); 
     100} 
     101 
     102int SortCommandList(const void *a, const void *b) 
     103{ 
     104        return strcmp((*(cmd_t**)a)->name, (*(cmd_t**)b)->name); 
     105} 
     106 
     107void ListCommands() 
     108{ 
     109cmd_t *cmd; 
     110array_t *cmds = array_alloc(); 
     111int length = 0, i; 
     112 
     113        /*      first pass, get maximum command length and number of commands we have 
     114                permission to use */ 
     115        for (cmd = First; cmd != NULL; cmd = cmd->next) { 
     116                if (strcmp(cmd->name, "DEFAULT") && VerifyPermissions(cmd) >= 0) { 
     117                int l = strlen(cmd->name); 
     118 
     119                        for (i = 0; i < cmds->size; ++i) 
     120                                if (!strcmp(((cmd_t*)cmds->data[i])->name, cmd->name)) 
     121                                        break; 
     122                        if (i == cmds->size) { 
     123                                if (l > length) length = l; 
     124                                array_push(cmds, cmd); 
     125                        } 
     126                } 
     127        } 
     128 
     129        qsort(cmds->data, cmds->size, sizeof(void*), SortCommandList); 
     130 
     131        /* second pass, display */ 
     132        for (i = 0; i < cmds->size; ++i) { 
     133                cmd = cmds->data[i]; 
     134 
     135                if (!strcmp(cmd->name, "DEFAULT")) continue; 
     136 
     137                if (VerifyPermissions(cmd) >= 0) { 
     138                char *help = FindOpt(cmd, "help"); 
     139 
     140                        if (!help) help = "(no help available)"; 
     141                        printf("%-*s", length + 2, cmd->name); 
     142                        while (*help) { 
     143                        int j; 
     144 
     145                                printf("%-*.*s\n", 77 - length, 77 - length, help); 
     146                                for (j = 0; j < 77 - length && *help; ++j, ++help) ; 
     147                                if (j == 77 - length) 
     148                                        printf("%-*s", length + 2, ""); 
     149                        } 
     150                } 
     151        } 
     152        array_free(cmds); 
    95153} 
    96154 
     
    102160        { 
    103161        struct dirent *f; 
    104         int i, successes = 0, dir_entries = 0, dir_capacity = 32; 
    105         char **dir_list; 
    106  
    107                 if (!(dir_list = malloc(sizeof(char*) * dir_capacity))) 
    108                         fatal(1, "failed to malloc space for directory entries"); 
     162        int i, successes = 0; 
     163        array_t *dir_list = array_alloc(); 
    109164 
    110165                while ((f = readdir(d))) 
     
    112167                        if (f->d_name[0] == '.' || (strlen(f->d_name) > 5 && strcmp(f->d_name + strlen(f->d_name) - 5, ".conf"))) 
    113168                                continue; 
    114                         if (dir_entries >= dir_capacity) { 
    115                                 dir_capacity += 32; 
    116                                 if (!(dir_list = realloc(dir_list, sizeof(char*) * dir_capacity))) 
    117                                         fatal(1, "reallocation of directory entry list failed"); 
    118                         } 
    119                         if (!(dir_list[dir_entries] = strdup(f->d_name))) 
     169                        if (!array_push(dir_list, savestr(f->d_name))) 
    120170                                fatal(1, "failed to malloc space for directory entry"); 
    121                         ++dir_entries; 
    122171                } 
    123172                closedir(d); 
    124                 qsort(dir_list, dir_entries, sizeof(char*), file_compare); 
    125                 for (i = 0; i < dir_entries; ++i) { 
     173                qsort(dir_list->data, dir_list->size, sizeof(void*), FileCompare); 
     174                for (i = 0; i < dir_list->size; ++i) { 
    126175                char full_path[PATH_MAX]; 
    127176#ifdef HAVE_SNPRINTF 
    128                         snprintf(full_path, PATH_MAX, "%s/%s", OP_ACCESS_DIR, dir_list[i]); 
     177                        snprintf(full_path, PATH_MAX, "%s/%s", OP_ACCESS_DIR, (char*)dir_list->data[i]); 
    129178#else 
    130                         sprintf(full_path, "%s/%s", OP_ACCESS_DIR, dir_list[i]); 
     179                        sprintf(full_path, "%s/%s", OP_ACCESS_DIR, (char*)dir_list->data[i]); 
    131180#endif 
    132181                        if (ReadFile(full_path)) successes++; 
     
    145194        cmd_t           *cmd, *def, *new; 
    146195        struct passwd   *pw; 
    147         int             hflag = 0, read_conf = 0, read_conf_dir = 0; 
     196        int             lflag = 0, hflag = 0, read_conf = 0, read_conf_dir = 0; 
    148197        char            *uptr = NULL; 
    149198        char            cmd_s[MAXSTRLEN]; 
     
    169218                        printf("%s\n", VERSION); 
    170219                        return 0; 
     220                } else if (strcmp("-l", argv[argStart]) == 0) { 
     221                        lflag++; 
     222                        argStart++; 
    171223                } else if (strcmp("-H", argv[argStart]) == 0) { 
    172224                        hflag++; 
     
    215267                fatal(1, "Could not open %s or any configuration files in %s", OP_ACCESS, OP_ACCESS_DIR); 
    216268 
     269        if ((pw = getpwuid(getuid())) == NULL)  
     270                exit(1); 
     271        realuser = getpwuid(getuid()); 
     272        strncpy(user, pw->pw_name, MAXSTRLEN); 
     273 
     274        if (lflag) { 
     275                ListCommands(); 
     276                return 0; 
     277        } 
     278 
    217279        if (hflag) { 
    218280                if (uptr != NULL) { 
     
    246308                num = -num; 
    247309 
    248         if ((pw = getpwuid(getuid())) == NULL)  
    249                 exit(1); 
    250         realuser = getpwuid(getuid()); 
    251         strncpy(user, pw->pw_name, MAXSTRLEN); 
    252310        pcmd_s = format_cmd(argc, argv, cmd_s, MAXSTRLEN); 
    253311        if (Verify(new, num, argc, argv) < 0) 
     
    263321 
    264322        for (cmd = First; cmd != NULL; cmd = cmd ->next) { 
    265                 if (strcmp(cmd->name, name) == 0
     323                if (strcmp(cmd->name, name) == 0 && VerifyPermissions(cmd) >= 0
    266324                        break; 
    267325        } 
     
    353411                                        return PAM_CONV_ERR; 
    354412                                } 
    355                                 pr->resp = strdup(pass); 
     413                                pr->resp = savestr(pass); 
    356414                        break; 
    357415                        case PAM_TEXT_INFO : 
     
    384442#endif 
    385443 
     444int VerifyPermissions(cmd_t *cmd) 
     445{ 
     446int             gr_fail = 1, uid_fail = 1, netgr_fail = 1; 
     447int             i; 
     448char            *cp, str[MAXSTRLEN], hostname[HOST_NAME_MAX]; 
     449regexp          *reg1 = NULL; 
     450struct passwd   *pw; 
     451#ifdef USE_PAM 
     452struct pam_conv pamconv = { pam_conversation, NULL }; 
     453pam_handle_t *pam; 
     454#endif 
     455struct group    *gr; 
     456 
     457        /* root always has access - it is pointless refusing */ 
     458        if (getuid() == 0)  
     459                return 0; 
     460 
     461        if (gethostname(hostname, HOST_NAME_MAX) == -1) 
     462                return logger(LOG_ERR, "Could not get hostname"); 
     463 
     464        if ((pw = getpwuid(getuid())) == NULL)  
     465                return logger(LOG_ERR, "Could not get uid of current effective uid"); 
     466 
     467        if ((cp = FindOpt(cmd, "groups")) != NULL) { 
     468        char grouphost[MAXSTRLEN + HOST_NAME_MAX], 
     469                regstr[MAXSTRLEN]; 
     470 
     471                for (cp = GetField(cp, str, MAXSTRLEN - 5); cp != NULL; cp = GetField(cp, str, MAXSTRLEN - 5)) { 
     472                        strcpy(regstr, "^("); 
     473                        strcat(regstr, str); 
     474                        strcat(regstr, ")$"); 
     475 
     476                        if ((reg1 = regcomp(regstr)) == NULL) 
     477                                return logger(LOG_ERR, "Invalid regex '%s'", regstr); 
     478 
     479                        if ((gr = getgrgid(pw->pw_gid)) != NULL) { 
     480                                strcpy(grouphost, gr->gr_name); 
     481                                strcat(grouphost, "@"); 
     482                                strcat(grouphost, hostname); 
     483 
     484                                if (regexec(reg1,gr->gr_name) == 1 || regexec(reg1, grouphost)) { 
     485                                        gr_fail = 0; 
     486                                        break; 
     487                                } 
     488                        } 
     489 
     490                        setgrent(); 
     491                        while ((gr = getgrent()) != NULL) { 
     492                                i = 0; 
     493                                while (gr->gr_mem[i] != NULL) { 
     494                                        if (strcmp(gr->gr_mem[i], pw->pw_name)==0) break; 
     495                                        i++; 
     496                                } 
     497 
     498                                if (gr->gr_mem[i] != NULL) { 
     499                                        strcpy(grouphost, gr->gr_name); 
     500                                        strcat(grouphost, "@"); 
     501                                        strcat(grouphost, hostname); 
     502                                        if (regexec(reg1, gr->gr_name) == 1 || regexec(reg1, grouphost)) { 
     503                                                gr_fail = 0; 
     504                                                break; 
     505                                        } 
     506                                } 
     507                        } 
     508                } 
     509        } 
     510        if(reg1 != NULL){ 
     511                free(reg1); 
     512                reg1=NULL; 
     513        } 
     514 
     515        if (gr_fail && ((cp = FindOpt(cmd, "users")) != NULL)) { 
     516        char currenttime[13], userhost[MAXSTRLEN + HOST_NAME_MAX], 
     517                regstr[MAXSTRLEN]; 
     518        time_t now = time(NULL); 
     519                 
     520                strftime(currenttime, 13, "%Y%m%d%H%M", localtime(&now)); 
     521 
     522                for (cp=GetField(cp, str, MAXSTRLEN - 5); cp!=NULL; cp=GetField(cp, str, MAXSTRLEN - 5)) { 
     523                char expiretime[13], *expirestart = strchr(str, '/'); 
     524 
     525                        if (expirestart) *expirestart = 0; 
     526 
     527                        strcpy(regstr, "^("); 
     528                        strcat(regstr, str); 
     529                        strcat(regstr, ")$"); 
     530 
     531                        strcpy(userhost, pw->pw_name); 
     532                        strcat(userhost, "@"); 
     533                        strcat(userhost, hostname); 
     534 
     535                        if ((reg1=regcomp(regstr)) == NULL) 
     536                                return logger(LOG_ERR, "Invalid regex '%s'", regstr); 
     537 
     538                        if (regexec(reg1,pw->pw_name) == 1 || regexec(reg1, userhost) == 1) { 
     539                                /* valid user, check expiry (if any) */ 
     540                                if (expirestart) { 
     541                                int i; 
     542 
     543                                        ++expirestart; 
     544 
     545                                        /* ensure at least some sanity in the expiry time */ 
     546                                        for (i = 0; expirestart[i]; ++i) { 
     547                                                if (i > 11) 
     548                                                        return logger(LOG_ERR, "Expiry value (%s) has too many digits", expirestart); 
     549                                                if (!isdigit(expirestart[i])) 
     550                                                        return logger(LOG_ERR, "Expiry value (%s) has non-numeric characters", expirestart); 
     551                                        } 
     552 
     553                                        strcpy(expiretime, "000000000000"); /* YYYYMMDD[HHmm] */ 
     554                                        strncpy(expiretime, expirestart, strlen(expirestart)); 
     555 
     556                                        if (strcmp(currenttime, expiretime) >= 0) 
     557                                                return logger(LOG_ERR, "Access expired at %s", expiretime); 
     558                                } 
     559 
     560                                uid_fail = 0; 
     561                                break; 
     562                        } 
     563                } 
     564        } 
     565        if(reg1 != NULL){ 
     566                free(reg1); 
     567                reg1=NULL; 
     568        } 
     569 
     570        if (uid_fail && (cp = FindOpt(cmd, "netgroups")) != NULL) { 
     571                for (cp = GetField(cp, str, MAXSTRLEN - 5); cp != NULL && netgr_fail; cp = GetField(cp, str, MAXSTRLEN - 5)) { 
     572                        if (innetgr(str, hostname, pw->pw_name, NULL)) { 
     573                                netgr_fail = 0; 
     574                                break; 
     575                        } 
     576                } 
     577        } 
     578 
     579        if (gr_fail && uid_fail && netgr_fail) 
     580                return -1; 
     581        return 0; 
     582} 
     583 
    386584int Verify(cmd, num, argc, argv) 
    387585cmd_t   *cmd; 
     
    390588char    **argv; 
    391589{ 
    392 int             gr_fail = 1, uid_fail = 1, netgr_fail = 1; 
    393590int             i, j, val; 
    394 char            *np, *cp, str[MAXSTRLEN], buf[MAXSTRLEN], hostname[HOST_NAME_MAX]
     591char            *np, *cp, str[MAXSTRLEN], buf[MAXSTRLEN]
    395592regexp          *reg1 = NULL; 
    396593regexp          *reg2 = NULL; 
     
    403600pam_handle_t *pam; 
    404601#endif 
    405 struct group    *gr; 
    406602#ifdef SECURID 
    407603struct          SD_CLIENT sd_dat, *sd; 
     
    471667        } 
    472668 
    473         if (gethostname(hostname, HOST_NAME_MAX) == -1) 
    474                 return logger(LOG_ERR, "Could not get hostname"); 
    475  
    476         if ((pw = getpwuid(getuid())) == NULL)  
    477                 return logger(LOG_ERR, "Could not get uid of current effective uid"); 
    478  
    479         if ((cp = FindOpt(cmd, "groups")) != NULL) { 
    480         char grouphost[MAXSTRLEN + HOST_NAME_MAX], 
    481                 regstr[MAXSTRLEN]; 
    482  
    483                 for (cp = GetField(cp, str, MAXSTRLEN - 5); cp != NULL; cp = GetField(cp, str, MAXSTRLEN - 5)) { 
    484                         strcpy(regstr, "^("); 
    485                         strcat(regstr, str); 
    486                         strcat(regstr, ")$"); 
    487  
    488                         if ((reg1 = regcomp(regstr)) == NULL) 
    489                                 return logger(LOG_ERR, "Invalid regex '%s'", regstr); 
    490  
    491                         if ((gr = getgrgid(pw->pw_gid)) != NULL) { 
    492                                 strcpy(grouphost, gr->gr_name); 
    493                                 strcat(grouphost, "@"); 
    494                                 strcat(grouphost, hostname); 
    495  
    496                                 if (regexec(reg1,gr->gr_name) == 1 || regexec(reg1, grouphost)) { 
    497                                         gr_fail = 0; 
    498                                         break; 
    499                                 } 
    500                         } 
    501  
    502                         setgrent(); 
    503                         while ((gr = getgrent()) != NULL) { 
    504                                 i = 0; 
    505                                 while (gr->gr_mem[i] != NULL) { 
    506                                         if (strcmp(gr->gr_mem[i], pw->pw_name)==0) break; 
    507                                         i++; 
    508                                 } 
    509  
    510                                 if (gr->gr_mem[i] != NULL) { 
    511                                         strcpy(grouphost, gr->gr_name); 
    512                                         strcat(grouphost, "@"); 
    513                                         strcat(grouphost, hostname); 
    514                                         if (regexec(reg1, gr->gr_name) == 1 || regexec(reg1, grouphost)) { 
    515                                                 gr_fail = 0; 
    516                                                 break; 
    517                                         } 
    518                                 } 
    519                         } 
    520                 } 
    521         } 
    522         if(reg1 != NULL){ 
    523                 free(reg1); 
    524                 reg1=NULL; 
    525         } 
    526  
    527         if (gr_fail && ((cp = FindOpt(cmd, "users")) != NULL)) { 
    528         char currenttime[13], userhost[MAXSTRLEN + HOST_NAME_MAX], 
    529                 regstr[MAXSTRLEN]; 
    530         time_t now = time(NULL); 
    531                  
    532                 strftime(currenttime, 13, "%Y%m%d%H%M", localtime(&now)); 
    533  
    534                 for (cp=GetField(cp, str, MAXSTRLEN - 5); cp!=NULL; cp=GetField(cp, str, MAXSTRLEN - 5)) { 
    535                 char expiretime[13], *expirestart = strchr(str, '/'); 
    536  
    537                         if (expirestart) *expirestart = 0; 
    538  
    539                         strcpy(regstr, "^("); 
    540                         strcat(regstr, str); 
    541                         strcat(regstr, ")$"); 
    542  
    543                         strcpy(userhost, pw->pw_name); 
    544                         strcat(userhost, "@"); 
    545                         strcat(userhost, hostname); 
    546  
    547                         if ((reg1=regcomp(regstr)) == NULL) 
    548                                 return logger(LOG_ERR, "Invalid regex '%s'", regstr); 
    549  
    550                         if (regexec(reg1,pw->pw_name) == 1 || regexec(reg1, userhost) == 1) { 
    551                                 /* valid user, check expiry (if any) */ 
    552                                 if (expirestart) { 
    553                                 int i; 
    554  
    555                                         ++expirestart; 
    556  
    557                                         /* ensure at least some sanity in the expiry time */ 
    558                                         for (i = 0; expirestart[i]; ++i) { 
    559                                                 if (i > 11) 
    560                                                         return logger(LOG_ERR, "Expiry value (%s) has too many digits", expirestart); 
    561                                                 if (!isdigit(expirestart[i])) 
    562                                                         return logger(LOG_ERR, "Expiry value (%s) has non-numeric characters", expirestart); 
    563                                         } 
    564  
    565                                         strcpy(expiretime, "000000000000"); /* YYYYMMDD[HHmm] */ 
    566                                         strncpy(expiretime, expirestart, strlen(expirestart)); 
    567  
    568                                         if (strcmp(currenttime, expiretime) >= 0) 
    569                                                 return logger(LOG_ERR, "Access expired at %s", expiretime); 
    570                                 } 
    571  
    572                                 uid_fail = 0; 
    573                                 break; 
    574                         } 
    575                 } 
    576         } 
    577         if(reg1 != NULL){ 
    578                 free(reg1); 
    579                 reg1=NULL; 
    580         } 
    581  
    582         if (uid_fail && (cp = FindOpt(cmd, "netgroups")) != NULL) { 
    583                 for (cp = GetField(cp, str, MAXSTRLEN - 5); cp != NULL && netgr_fail; cp = GetField(cp, str, MAXSTRLEN - 5)) { 
    584                         if (innetgr(str, hostname, pw->pw_name, NULL)) { 
    585                                 netgr_fail = 0; 
    586                                 break; 
    587                         } 
    588                 } 
    589         } 
    590  
    591         if (gr_fail && uid_fail && netgr_fail) 
     669        if ((i = VerifyPermissions(cmd) < 0)) 
    592670                return logger(LOG_ERR, "Both user, group and netgroup authentication failed"); 
    593671 
  • op/trunk/Makefile

    r217 r226  
    8585CFLAGS= $(OPTS) $(INC) $(GLOBALOPTS) $(SECURID) 
    8686REG = regexp.o 
    87 OBJ = lex.o main.o atov.o $(REG) 
     87OBJ = lex.o util.o main.o atov.o $(REG) 
    8888op: $(OBJ) op.list 
    8989        $(CC) $(LDFLAGS) $(CFLAGS) -o $@ $(LDFLAGS) $(SECURIDLIBDIR) $(OBJ) $(SECURIDLIB) $(LIBS) 
  • op/trunk/op.1

    r220 r226  
    1616security-related aspects of environment of each 
    1717operation can be carefully controlled. 
     18.SH OPTIONS 
     19.TP 
     20.B -V 
     21Show version number. 
     22.TP 
     23.B -l 
     24List available commands. Note that this will only display commands you are 
     25permitted to run. 
     26.SH CONFIGURATION 
    1827.PP 
    1928Configuration entries are read from 
     
    163172Disables the destruction of the users environment. 
    164173.TP 
     174.B help 
     175Define help for this mnemonic. 
     176.I op -l 
     177will display this help when it lists the available commands. eg. 
     178.I help="This is some help" 
     179.TP 
    165180.B nolog 
    166181Disables informational logging per command. Useful for cron jobs to avoid