On Tue, Jun 12, 2018 at 09:13:05PM +0200, Job Snijders wrote:
> On Tue, Jun 12, 2018 at 09:54:47AM -0600, Theo de Raadt wrote:
> > I would prefer if the -q and -n descriptions were in a table.  I dislike
> > the ancient style of describing such things inline (harder to spot).
> > And it really falls down when there are multiple ones.  How do you
> > feel about that jmc?
> 
> Agreed, I changed it a bit to improve readability.
> 

morning.

i agree a small list for the options is probably clearer. but not in
the way your diff does it. the format of the page is very odd anyway.
barring a rewrite, i'd just keep it as it is, but add a standard text
along the lines of:

        Commands may be modified as follows:
        .Bl -tag width Ds
        .It Li %
        ...
        .It Fl n Ar command
        ...

something like that?

i don;t want to see it split up into these small mini sections, but i
understand the need. the page might benefit from some sort of reworking,
but i'd save that for another diff.

jmc

> > Also, do -qn and -nq work?  How about -nnn.  Not saying those make a
> > lot of sense, but once getopt syntax is borrowed it should probably be
> > honoured.
> 
> I redid this piece a little bit, and opted to go a bit stricter to leave
> as much freedom as possible for future extensions.
> 
> OK:
>     -n command
>     -n -q command
>     -q -n command
>     -q command
>     command
> 
> Not OK:
>     -nn command
>     -qn command
>     -q -q command
>     -n -n -q command
> 
> My thinking is by being strict now, we make it possible to add arguments
> to options in the future. If we allow "-nn" or "-nq" now, we won't be
> able to allow "[email protected]" in the future. Or maybe we'll want
> "-v" to mean something different than "-vv". I don't know, so prefer to
> be less forgiving.
> 
> Kind regards,
> 
> Job
> 
> 
> diff --git usr.sbin/cron/crontab.5 usr.sbin/cron/crontab.5
> index 9c2e651980a..d9330698fd3 100644
> --- usr.sbin/cron/crontab.5
> +++ usr.sbin/cron/crontab.5
> @@ -193,15 +193,29 @@ will be changed into newline characters, and all data
>  after the first
>  .Ql %
>  will be sent to the command as standard input.
> -If the
> +.Ss Options
> +The
>  .Ar command
> -field begins with
> -.Ql -q ,
> -execution will not be logged.
> +field can begin with one or more options.
> +.Bl -tag -width Ds
> +.It Fl n
> +No mail is send after a successful run.
> +The execution output will only be mailed if the command exits with a non-zero
> +exit code.
> +The
> +.Ql -n
> +option is an attempt to cure potentially copious volumes of mail coming from
> +.Xr cron 8 .
> +.It Fl q
> +Execution will not be logged.
> +.El
> +.Pp
>  Use whitespace to separate
> -.Ql -q
> -from the command.
> +.Ql -n ,
> +.Ql -q ,
> +and the command.
>  .Pp
> +.Ss Execution
>  Commands are executed by
>  .Xr cron 8
>  when the
> @@ -329,6 +343,9 @@ Ranges may include
>  .It
>  Months or days of the week can be specified by name.
>  .It
> +Mailing after a successful run can be suppressed with
> +.Ql -n .
> +.It
>  Logging can be suppressed with
>  .Ql -q .
>  .It
> diff --git usr.sbin/cron/do_command.c usr.sbin/cron/do_command.c
> index 6a4022fcc9a..4fbca61d170 100644
> --- usr.sbin/cron/do_command.c
> +++ usr.sbin/cron/do_command.c
> @@ -3,6 +3,7 @@
>  /* Copyright 1988,1990,1993,1994 by Paul Vixie
>   * Copyright (c) 2004 by Internet Systems Consortium, Inc. ("ISC")
>   * Copyright (c) 1997,2000 by Internet Software Consortium, Inc.
> + * Copyright (c) 2018 Job Snijders <[email protected]>
>   *
>   * Permission to use, copy, modify, and distribute this software for any
>   * purpose with or without fee is hereby granted, provided that the above
> @@ -80,7 +81,6 @@ child_process(entry *e, user *u)
>       char **p, *input_data, *usernm;
>       auth_session_t *as;
>       login_cap_t *lc;
> -     int children = 0;
>       extern char **environ;
>  
>       /* mark ourselves as different to PS command watchers */
> @@ -146,7 +146,9 @@ child_process(entry *e, user *u)
>  
>       /* fork again, this time so we can exec the user's command.
>        */
> -     switch (fork()) {
> +
> +     pid_t   jobpid;
> +     switch (jobpid = fork()) {
>       case -1:
>               syslog(LOG_ERR, "(CRON) CAN'T FORK (%m)");
>               _exit(EXIT_FAILURE);
> @@ -260,8 +262,6 @@ child_process(entry *e, user *u)
>               break;
>       }
>  
> -     children++;
> -
>       /* middle process, child of original cron, parent of process running
>        * the user's command.
>        */
> @@ -283,7 +283,8 @@ child_process(entry *e, user *u)
>        * we would block here.  thus we must fork again.
>        */
>  
> -     if (*input_data && fork() == 0) {
> +     pid_t   stdinjob;
> +     if (*input_data && (stdinjob = fork()) == 0) {
>               FILE *out = fdopen(stdin_pipe[WRITE_PIPE], "w");
>               int need_newline = FALSE;
>               int escaped = FALSE;
> @@ -331,8 +332,6 @@ child_process(entry *e, user *u)
>        */
>       close(stdin_pipe[WRITE_PIPE]);
>  
> -     children++;
> -
>       /*
>        * read output from the grandchild.  it's stderr has been redirected to
>        * it's stdout, which has been redirected to our pipe.  if there is any
> @@ -342,15 +341,17 @@ child_process(entry *e, user *u)
>  
>       (void) signal(SIGPIPE, SIG_IGN);
>       in = fdopen(stdout_pipe[READ_PIPE], "r");
> +
> +     char    *mailto;
> +     FILE    *mail = NULL;
> +     int     status = 0;
> +     pid_t   mailpid;
> +     size_t  bytes = 1;
> +
>       if (in != NULL) {
>               int     ch = getc(in);
>  
>               if (ch != EOF) {
> -                     FILE    *mail = NULL;
> -                     char    *mailto;
> -                     size_t  bytes = 1;
> -                     int     status = 0;
> -                     pid_t   mailpid;
>  
>                       /* get name of recipient.  this is MAILTO if set to a
>                        * valid local username; USER otherwise.
> @@ -415,30 +416,6 @@ child_process(entry *e, user *u)
>                                       fputc(ch, mail);
>                       }
>  
> -                     /* only close pipe if we opened it -- i.e., we're
> -                      * mailing...
> -                      */
> -
> -                     if (mail) {
> -                             /* Note: the pclose will probably see
> -                              * the termination of the grandchild
> -                              * in addition to the mail process, since
> -                              * it (the grandchild) is likely to exit
> -                              * after closing its stdout.
> -                              */
> -                             status = cron_pclose(mail, mailpid);
> -                     }
> -
> -                     /* if there was output and we could not mail it,
> -                      * log the facts so the poor user can figure out
> -                      * what's going on.
> -                      */
> -                     if (mail && status) {
> -                             syslog(LOG_NOTICE, "(%s) MAIL (mailed %zu byte"
> -                             "%s of output but got status 0x%04x)", usernm,
> -                             bytes, (bytes == 1) ? "" : "s", status);
> -                     }
> -
>               } /*if data from grandchild*/
>  
>               fclose(in);     /* also closes stdout_pipe[READ_PIPE] */
> @@ -446,20 +423,50 @@ child_process(entry *e, user *u)
>  
>       /* wait for children to die.
>        */
> -     for (; children > 0; children--) {
> -             int waiter;
> -             pid_t pid;
> -
> -             while ((pid = wait(&waiter)) < 0 && errno == EINTR)
> +     int waiter;
> +     if (jobpid > 0) {
> +                             ;
> +             while (waitpid(jobpid, &waiter, 0) < 0 && errno == EINTR)
>                       ;
> -             if (pid < 0) {
> -                     break;
> +
> +             /* If everything went well, and -n was set, _and_ we have mail,
> +              * we won't be mailing... so shoot the messenger!
> +              */
> +             if (WIFEXITED(waiter) && WEXITSTATUS(waiter) == 0
> +                 && (e->flags & MAIL_WHEN_ERR) == MAIL_WHEN_ERR
> +                 && mail) {
> +                     kill(mailpid, SIGKILL);
> +                     (void)fclose(mail);
> +                     mail = NULL;
>               }
> -             /*
> -             if (WIFSIGNALED(waiter) && WCOREDUMP(waiter))
> -                     Debug(DPROC, (", dumped core"))
> +
> +             /* only close pipe if we opened it -- i.e., we're
> +             * mailing...
>               */
> +             if (mail) {
> +                     /*
> +                      * Note: the pclose will probably see the termination
> +                      * of the grandchild in addition to the mail process,
> +                      * since it (the grandchild) is likely to exit after
> +                      * closing its stdout.
> +                      */
> +                     status = cron_pclose(mail, mailpid);
> +             }
> +
> +             /* if there was output and we could not mail it,
> +              * log the facts so the poor user can figure out
> +              * what's going on.
> +              */
> +             if (mail && status) {
> +                     syslog(LOG_NOTICE, "(%s) MAIL (mailed %zu byte"
> +                         "%s of output but got status 0x%04x)", usernm,
> +                         bytes, (bytes == 1) ? "" : "s", status);
> +             }
>       }
> +
> +     if (stdinjob > 0)
> +             while (waitpid(stdinjob, &waiter, 0) < 0 && errno == EINTR)
> +                     ;
>  }
>  
>  int
> diff --git usr.sbin/cron/entry.c usr.sbin/cron/entry.c
> index 761f01d3d6f..cba6e805dc0 100644
> --- usr.sbin/cron/entry.c
> +++ usr.sbin/cron/entry.c
> @@ -338,14 +338,32 @@ load_entry(FILE *file, void (*error_func)(const char 
> *), struct passwd *pw,
>       ch = get_char(file);
>       while (ch == '-') {
>               switch (ch = get_char(file)) {
> +             case 'n':
> +                     /* only allow the user to set this option once */
> +                     if ((e->flags & MAIL_WHEN_ERR) == MAIL_WHEN_ERR) {
> +                             ecode = e_option;
> +                             goto eof;
> +                     }
> +                     e->flags |= MAIL_WHEN_ERR;
> +                     break;
>               case 'q':
> +                     /* only allow the user to set this option once */
> +                     if ((e->flags & DONT_LOG) == DONT_LOG) {
> +                             ecode = e_option;
> +                             goto eof;
> +                     }
>                       e->flags |= DONT_LOG;
> -                     Skip_Nonblanks(ch, file)
>                       break;
>               default:
>                       ecode = e_option;
>                       goto eof;
>               }
> +             ch = get_char(file);
> +             if (ch!='\t' && ch!=' ') {
> +                     ecode = e_option;
> +                     goto eof;
> +             }
> +
>               Skip_Blanks(ch, file)
>               if (ch == EOF || ch == '\n') {
>                       ecode = e_cmd;
> diff --git usr.sbin/cron/structs.h usr.sbin/cron/structs.h
> index af0d75c3b55..597f65d6614 100644
> --- usr.sbin/cron/structs.h
> +++ usr.sbin/cron/structs.h
> @@ -38,6 +38,7 @@ typedef     struct _entry {
>  #define      DOW_STAR        0x08
>  #define      WHEN_REBOOT     0x10
>  #define      DONT_LOG        0x20
> +#define      MAIL_WHEN_ERR   0x40
>  } entry;
>  
>                       /* the crontab database will be a list of the
> 

Reply via email to