Index | Thread | Search

From:
Alvar Penning <post@0x21.biz>
Subject:
Re: newsyslog: support glob(3) through new G flag
To:
"Todd C. Miller" <Todd.Miller@sudo.ws>
Cc:
tech@openbsd.org
Date:
Fri, 26 Apr 2024 15:35:24 +0200

Download raw body.

Thread
On 2024-04-24 19:12, "Todd C. Miller" <Todd.Miller@sudo.ws> wrote:
> Do we really need yet another flag bit for this?  If we want to
> support globbing it should be enough to check for glob characters
> in logfile_name and call glob(3) if they are present.

Thanks for your comment. This seems fair enough. I have attached an
updated diff with this change.

Best,
Alvar


diff --git usr.bin/newsyslog/newsyslog.8 usr.bin/newsyslog/newsyslog.8
index 9e3f84a0b26..4dd95cb8e12 100644
--- usr.bin/newsyslog/newsyslog.8
+++ usr.bin/newsyslog/newsyslog.8
@@ -170,7 +170,7 @@ By default, this configuration file is
 Each line of the file contains information about a particular log file
 that should be handled by
 .Nm newsyslog .
-Each line has five mandatory fields and up to three optional fields, with
+Each line has five mandatory fields and up to four optional fields, with
 whitespace separating each field.
 Blank lines or lines beginning with a hash mark
 .Pq Ql #
@@ -180,6 +180,9 @@ follows:
 .Bl -tag -width XXXXXXXXXXXXXXXX
 .It Ar logfile_name
 The full pathname of the system log file to be archived.
+This path might be a
+.Xr glob 7
+pattern, which will be expanded accordingly.
 .It Ar owner:group
 This optional field specifies the owner and group for the archive file.
 The
@@ -359,8 +362,13 @@ rotate on every 5th day of the month at 6:00 hr
 .It Ar flags
 The optional
 .Ar flags
-field specifies if the archives should have any special processing
-done to the archived log files.
+field specifies any special processing for each entry.
+Multiple
+.Ar flags
+can be concatenated together.
+.Pp
+.Bl -tag -width Ds -compact -offset indent
+.It Li Z
 The
 .Sq Z
 flag will make the archive
@@ -368,7 +376,9 @@ files compressed to save space using
 .Xr gzip 1
 or
 .Xr compress 1 ,
-depending on compilation options.
+depending on compilation options, defaulting to
+.Xr gzip 1 .
+.It Li B
 The
 .Sq B
 flag means that the file is a
@@ -376,13 +386,16 @@ binary file, and so the ASCII message which
 .Nm
 inserts to indicate the fact that the logs have been turned over
 should not be included.
+.It Li M
 The
 .Sq M
 flag marks this entry as a monitored
 log file.
+.It Li F
 The
 .Sq F
 flag specifies that symbolic links should be followed.
+.El
 .It Ar monitor
 Specify the username (or email address) that should receive notification
 messages if this is a monitored log file.
@@ -434,6 +447,7 @@ default configuration file
 .Sh SEE ALSO
 .Xr compress 1 ,
 .Xr gzip 1 ,
+.Xr glob 7 ,
 .Xr syslog 3 ,
 .Xr syslogd 8
 .Sh AUTHORS
diff --git usr.bin/newsyslog/newsyslog.c usr.bin/newsyslog/newsyslog.c
index 852f1dfac87..3fbf230d5a4 100644
--- usr.bin/newsyslog/newsyslog.c
+++ usr.bin/newsyslog/newsyslog.c
@@ -88,6 +88,7 @@
 #include <err.h>
 #include <errno.h>
 #include <fcntl.h>
+#include <glob.h>
 #include <grp.h>
 #include <limits.h>
 #include <pwd.h>
@@ -160,6 +161,7 @@ int	stat_suffix(char *, size_t, char *, struct stat *,
 	    int (*)(const char *, struct stat *));
 off_t	sizefile(struct stat *);
 int	parse_file(struct entrylist *, int *);
+int parse_entry(struct entrylist *, int *, int, struct conf_entry *);
 time_t	parse8601(char *);
 time_t	parseDWM(char *);
 void	child_killer(int);
@@ -471,12 +473,13 @@ int
 parse_file(struct entrylist *list, int *nentries)
 {
 	char line[BUFSIZ], *parse, *q, *errline, *group, *tmp, *ep;
-	struct conf_entry *working;
-	struct stat sb;
+	struct conf_entry *working, *working_glob;
+	size_t i;
 	int lineno = 0;
 	int ret = 0;
 	FILE *f;
 	long l;
+	glob_t g;
 
 	if (strcmp(conf, "-") == 0)
 		f = stdin;
@@ -504,9 +507,6 @@ nextline:
 		if (working->log == NULL)
 			err(1, NULL);
 
-		if ((working->logbase = strrchr(working->log, '/')) != NULL)
-			working->logbase++;
-
 		q = parse = missing_field(sob(++parse), errline, lineno);
 		*(parse = son(parse)) = '\0';
 		if ((group = strchr(q, ':')) != NULL ||
@@ -717,57 +717,111 @@ nextline:
 			goto nextline;
 		}
 
-		/* If there is an arcdir, set working->backdir. */
-		if (arcdir != NULL && working->logbase != NULL) {
-			if (*arcdir == '/') {
-				/* Fully qualified arcdir */
-				working->backdir = arcdir;
-			} else {
-				/* arcdir is relative to log's parent dir */
-				*(working->logbase - 1) = '\0';
-				if ((asprintf(&working->backdir, "%s/%s",
-				    working->log, arcdir)) == -1)
-					err(1, NULL);
-				*(working->logbase - 1) = '/';
-			}
-			/* Ignore arcdir if it doesn't exist. */
-			if (stat(working->backdir, &sb) != 0 ||
-			    !S_ISDIR(sb.st_mode)) {
-				if (working->backdir != arcdir)
-					free(working->backdir);
-				working->backdir = NULL;
-			}
-		} else
-			working->backdir = NULL;
-
-		/* Make sure we can't oflow PATH_MAX */
-		if (working->backdir != NULL) {
-			if (snprintf(line, sizeof(line), "%s/%s.%d%s",
-			    working->backdir, working->logbase,
-			    working->numlogs, COMPRESS_POSTFIX) >= PATH_MAX) {
-				warnx("%s:%d: pathname too long: %s"
-				    " --> skipping", conf, lineno, q);
+		if (strpbrk(working->log, "*?[")) {
+			/*
+			 * This case also matches for escaped glob characters. However, the
+			 * glob(3) function itself honors escape characters, thus resulting
+			 * to a globed file list with only the desired logfile.
+			 */
+			if (glob(working->log, GLOB_NOSORT, NULL, &g)) {
+				warnx("%s:%d: cannot glob(3), %s --> skipping",
+					conf, lineno, strerror(errno));
 				ret = 1;
 				goto nextline;
 			}
-		} else {
-			if (snprintf(line, sizeof(line), "%s.%d%s",
-			    working->log, working->numlogs, COMPRESS_POSTFIX)
-			    >= PATH_MAX) {
-				warnx("%s:%d: pathname too long: %s"
-				    " --> skipping", conf, lineno,
-				    working->log);
+			DPRINTF(("%s:%d: expanded glob %s to %zu files\n",
+			    conf, lineno, working->log, g.gl_pathc));
+
+			for (i = 0; i < g.gl_pathc; i++) {
+				working_glob = malloc(sizeof(*working));
+				if (working_glob == NULL)
+					err(1, NULL);
+
+				memcpy(working_glob, working, sizeof(*working));
+
+				if ((working_glob->log = strdup(g.gl_pathv[i])) == NULL)
+					err(1, NULL);
+
+				if (parse_entry(list, nentries, lineno, working_glob)) {
+					ret = 1;
+					goto nextline;
+				}
+			}
+
+			/*
+			 * It is not necessary to duplicate and free the other three char
+			 * pointers in conf_entry as those values are identical for each
+			 * globed file. Even more important, later on they will only be
+			 * referenced in a reading fashion.
+			 */
+			free(working->log);
+			free(working);
+
+			globfree(&g);
+		} else
+			if (parse_entry(list, nentries, lineno, working)) {
 				ret = 1;
 				goto nextline;
 			}
-		}
-		TAILQ_INSERT_TAIL(list, working, next);
-		(*nentries)++;
 	}
 	(void)fclose(f);
 	return (ret);
 }
 
+int
+parse_entry(
+	struct entrylist *list, int *nentries,
+	int lineno, struct conf_entry *working
+)
+{
+	char line[BUFSIZ];
+	int linelen;
+	struct stat sb;
+
+	if ((working->logbase = strrchr(working->log, '/')) != NULL)
+		working->logbase++;
+
+	/* If there is an arcdir, set working->backdir. */
+	if (arcdir != NULL && working->logbase != NULL) {
+		if (*arcdir == '/') {
+			/* Fully qualified arcdir */
+			working->backdir = arcdir;
+		} else {
+			/* arcdir is relative to log's parent dir */
+			*(working->logbase - 1) = '\0';
+			if ((asprintf(&working->backdir, "%s/%s",
+			    working->log, arcdir)) == -1)
+				err(1, NULL);
+			*(working->logbase - 1) = '/';
+		}
+		/* Ignore arcdir if it doesn't exist. */
+		if (stat(working->backdir, &sb) != 0 ||
+		    !S_ISDIR(sb.st_mode)) {
+			if (working->backdir != arcdir)
+				free(working->backdir);
+			working->backdir = NULL;
+		}
+	} else
+		working->backdir = NULL;
+
+	/* Make sure we can't oflow PATH_MAX */
+	if (working->backdir != NULL)
+		linelen = snprintf(line, sizeof(line), "%s/%s.%d%s",
+		    working->backdir, working->logbase,
+		    working->numlogs, COMPRESS_POSTFIX);
+	else
+		linelen = snprintf(line, sizeof(line), "%s.%d%s",
+		    working->log, working->numlogs, COMPRESS_POSTFIX);
+	if (linelen >= PATH_MAX) {
+		warnx("%s:%d: pathname too long: %s --> skipping", conf, lineno, line);
+		return 1;
+	}
+
+	TAILQ_INSERT_TAIL(list, working, next);
+	(*nentries)++;
+	return 0;
+}
+
 char *
 missing_field(char *p, char *errline, int lineno)
 {