Hello all,

Not sure if this is something anyone would like to see included in newsyslog(8) but I've been maintaining a patch for it which adds support for glob(3) patterns to newsyslog/newsyslog.conf and now I rewrote parts of it to make it more presentable and wanted to share in case anyone is interested.

Apart from the functionality change, I've moved from the linked list to using SLIST but apart from that everything should work the same way as before. No flags/arguments necessary in order to enable the glob support. The only known change in behavior is that newsyslog no longer prints warning messages like:

newsyslog: /etc/newsyslog.conf: /var/log/foo not found

mostly because I couldn't find a clever way to do this without a lot of extra overhead but that can be fixed if there's any interest in this.

Anyway, if anyone thinks this is worth the effort, I'd be willing to work to make it suitable for submission as long as someone gives me some help/pointers or maybe someone more experienced can handle this.

Let me know if you have any questions.

Cheers,
Seraphim
Index: newsyslog.c
===================================================================
RCS file: /cvs/src/usr.bin/newsyslog/newsyslog.c,v
retrieving revision 1.92
diff -u -p -r1.92 newsyslog.c
--- newsyslog.c	26 Nov 2013 13:19:07 -0000	1.92
+++ newsyslog.c	17 Dec 2014 13:32:53 -0000
@@ -91,6 +91,7 @@
 #endif
 
 #include <sys/param.h>
+#include <sys/queue.h>
 #include <sys/stat.h>
 #include <sys/time.h>
 #include <sys/wait.h>
@@ -99,6 +100,7 @@
 #include <err.h>
 #include <errno.h>
 #include <fcntl.h>
+#include <glob.h>
 #include <grp.h>
 #include <limits.h>
 #include <pwd.h>
@@ -138,8 +140,10 @@ struct conf_entry {
 	char	*whom;		/* Whom to notify if logfile changes */
 	char	*pidfile;	/* Path to file containing pid to signal */
 	char	*runcmd;	/* Command to run instead of sending a signal */
-	struct conf_entry *next; /* Linked list pointer */
+	SLIST_ENTRY(conf_entry) entries; /* SLIST struct */
 };
+SLIST_HEAD(conflist, conf_entry);
+struct conflist all_confs = SLIST_HEAD_INITIALIZER(all_confs);
 
 struct pidinfo {
 	char	*file;
@@ -170,8 +174,9 @@ int	movefile(char *, char *, uid_t, gid_
 int	stat_suffix(char *, size_t, char *, struct stat *,
 	    int (*)(const char *, struct stat *));
 off_t	sizefile(struct stat *);
-struct conf_entry *
-	parse_file(int *);
+void 	parse_file(int *);
+struct  conf_entry *
+	parse_line(char *, char *, char *, int);
 time_t	parse8601(char *);
 time_t	parseDWM(char *);
 void	child_killer(int);
@@ -187,11 +192,12 @@ void	usage(void);
 int
 main(int argc, char **argv)
 {
-	struct conf_entry *p, *q, *x, *y;
+	struct conf_entry *q, *t;
 	struct pidinfo *pidlist, *pl;
+	struct conflist *p = &all_confs;
 	int status, listlen;
 	char **av;
-	
+
 	parse_args(argc, argv);
 	argc -= optind;
 	argv += optind;
@@ -199,30 +205,26 @@ main(int argc, char **argv)
 	if (needroot && getuid() && geteuid())
 		errx(1, "You must be root.");
 
-	p = parse_file(&listlen);
+	parse_file(&listlen);
+
 	if (argc > 0) {
 		/* Only rotate specified files. */
-		x = y = NULL;
-		listlen = 0;
-		for (av = argv; *av; av++) {
-			for (q = p; q; q = q->next)
-				if (strcmp(*av, q->log) == 0) {
-					if (x == NULL)
-						x = y = q;
-					else {
-						y->next = q;
-						y = q;
-					}
-					listlen++;
-					break;
-				}
-			if (q == NULL)
-				warnx("%s: %s not found", conf, *av);
+		SLIST_FOREACH_SAFE(q, p, entries, t) {
+			for (av = argv; *av; av++) {
+				if (strcmp(*av, q->log) == 0)
+					goto next;
+			}
+			/* FIXME: Find a clever way to print the warning */
+			//warnx("%s: %s not found", conf, *av);
+			SLIST_REMOVE(p, q, conf_entry, entries);
+			free(q);
+			listlen--;
+next:
+			;
 		}
-		if (x == NULL)
+
+		if (SLIST_EMPTY(p))
 			errx(1, "%s: no specified log files", conf);
-		y->next = NULL;
-		p = x;
 	}
 
 	pidlist = (struct pidinfo *)calloc(listlen + 1, sizeof(struct pidinfo));
@@ -232,11 +234,12 @@ main(int argc, char **argv)
 	signal(SIGCHLD, child_killer);
 
 	/* Step 1, rotate all log files */
-	for (q = p; q; q = q->next)
+	SLIST_FOREACH(q, p, entries)
 		do_entry(q);
 
 	/* Step 2, make a list of unique pid files */
-	for (q = p, pl = pidlist; q; ) {
+	SLIST_FOREACH(q, p, entries) {
+		pl = pidlist;
 		if (q->flags & CE_ROTATED) {
 			struct pidinfo *pltmp;
 
@@ -259,7 +262,6 @@ main(int argc, char **argv)
 				pl++;
 			}
 		}
-		q = q->next;
 	}
 
 	/* Step 3, send a signal or run a command */
@@ -275,12 +277,12 @@ main(int argc, char **argv)
 		sleep(5);
 
 	/* Step 4, compress the log.0 file if configured to do so and free */
-	while (p) {
-		if ((p->flags & CE_COMPACT) && (p->flags & CE_ROTATED) &&
-		    p->numlogs > 0)
-			compress_log(p);
-		q = p;
-		p = p->next;
+	while (!SLIST_EMPTY(p)) {
+		q = SLIST_FIRST(p);
+		if ((q->flags & CE_COMPACT) && (q->flags & CE_ROTATED) &&
+		    q->numlogs > 0)
+			compress_log(q);
+		SLIST_REMOVE_HEAD(p, entries);
 		free(q);
 	}
 
@@ -473,17 +475,14 @@ usage(void)
  * Parse a configuration file and return a linked list of all the logs
  * to process
  */
-struct conf_entry *
+void
 parse_file(int *nentries)
 {
-	char line[BUFSIZ], *parse, *q, *errline, *group, *tmp, *ep;
-	struct conf_entry *working = NULL, *first = NULL;
-	struct passwd *pwd;
-	struct group *grp;
-	struct stat sb;
-	int lineno;
+	char line[BUFSIZ], *parse, *errline, *tmp, *q;
+	struct conf_entry *working = NULL;
+	int lineno, i;
 	FILE *f;
-	long l;
+	glob_t g;
 
 	if (strcmp(conf, "-") == 0)
 		f = stdin;
@@ -498,241 +497,254 @@ parse_file(int *nentries)
 		errline = strdup(tmp);
 		if (errline == NULL)
 			err(1, "strdup");
-		(*nentries)++;
-		if (!first) {
-			working = malloc(sizeof(struct conf_entry));
-			if (working == NULL)
-				err(1, "malloc");
-			first = working;
-		} else {
-			working->next = malloc(sizeof(struct conf_entry));
-			if (working->next == NULL)
-				err(1, "malloc");
-			working = working->next;
+
+                q = parse = missing_field(sob(line), errline, lineno);
+                *(parse = son(line)) = '\0';
+                parse++;
+                if(glob(q, GLOB_NOCHECK, NULL, &g) != 0)
+                        err(1, "glob");
+
+		for (i=0; i < g.gl_matchc ; i++) {
+			(*nentries)++;
+			working = parse_line(g.gl_pathv[i], parse, errline, lineno);
+			SLIST_INSERT_HEAD(&all_confs, working, entries);
 		}
+		free(errline);
+	}
+	(void)fclose(f);
+}
 
-		q = parse = missing_field(sob(line), errline, lineno);
-		*(parse = son(line)) = '\0';
-		working->log = strdup(q);
-		if (working->log == NULL)
-			err(1, "strdup");
+struct conf_entry *
+parse_line(char *logfile, char *rest, char *errline, int lineno)
+{
+	char *line, *parse, *q, *group, *tmp, *ep;
+	struct conf_entry *working = NULL;
+	struct passwd *pwd;
+	struct group *grp;
+	struct stat sb;
+	long l;
 
-		if ((working->logbase = strrchr(working->log, '/')) != NULL)
-			working->logbase++;
+	working = malloc(sizeof(struct conf_entry));
+	if (working == NULL)
+		err(1, "malloc");
 
-		q = parse = missing_field(sob(++parse), errline, lineno);
-		*(parse = son(parse)) = '\0';
-		if ((group = strchr(q, ':')) != NULL ||
-		    (group = strrchr(q, '.')) != NULL)  {
-			*group++ = '\0';
-			if (*q) {
-				if (!(isnumberstr(q))) {
-					if ((pwd = getpwnam(q)) == NULL)
-						errx(1, "%s:%d: unknown user: %s",
-						    conf, lineno, q);
-					working->uid = pwd->pw_uid;
-				} else
-					working->uid = atoi(q);
-			} else
-				working->uid = (uid_t)-1;
-			
-			q = group;
-			if (*q) {
-				if (!(isnumberstr(q))) {
-					if ((grp = getgrnam(q)) == NULL)
-						errx(1, "%s:%d: unknown group: %s",
-						    conf, lineno, q);
-					working->gid = grp->gr_gid;
-				} else
-					working->gid = atoi(q);
+	working->log = strdup(logfile);
+	if (working->log == NULL)
+		err(1, "strdup");
+
+	if ((working->logbase = strrchr(working->log, '/')) != NULL)
+		working->logbase++;
+
+	line = strdup(rest);
+	q = parse = missing_field(sob(line), errline, lineno);
+	*(parse = son(parse)) = '\0';
+	if ((group = strchr(q, ':')) != NULL ||
+	    (group = strrchr(q, '.')) != NULL)  {
+		*group++ = '\0';
+		if (*q) {
+			if (!(isnumberstr(q))) {
+				if ((pwd = getpwnam(q)) == NULL)
+					errx(1, "%s:%d: unknown user: %s",
+					    conf, lineno, q);
+				working->uid = pwd->pw_uid;
 			} else
-				working->gid = (gid_t)-1;
-			
-			q = parse = missing_field(sob(++parse), errline, lineno);
-			*(parse = son(parse)) = '\0';
-		} else {
+				working->uid = atoi(q);
+		} else
 			working->uid = (uid_t)-1;
-			working->gid = (gid_t)-1;
-		}
 
-		l = strtol(q, &ep, 8);
-		if (*ep != '\0' || l < 0 || l > ALLPERMS)
-			errx(1, "%s:%d: bad permissions: %s", conf, lineno, q);
-		working->permissions = (mode_t)l;
+		q = group;
+		if (*q) {
+			if (!(isnumberstr(q))) {
+				if ((grp = getgrnam(q)) == NULL)
+					errx(1, "%s:%d: unknown group: %s",
+					    conf, lineno, q);
+				working->gid = grp->gr_gid;
+			} else
+				working->gid = atoi(q);
+		} else
+			working->gid = (gid_t)-1;
 
 		q = parse = missing_field(sob(++parse), errline, lineno);
 		*(parse = son(parse)) = '\0';
-		l = strtol(q, &ep, 10);
-		if (*ep != '\0' || l < 0 || l >= INT_MAX)
-			errx(1, "%s:%d: bad number: %s", conf, lineno, q);
-		working->numlogs = (int)l;
+	} else {
+		working->uid = (uid_t)-1;
+		working->gid = (gid_t)-1;
+	}
 
-		q = parse = missing_field(sob(++parse), errline, lineno);
-		*(parse = son(parse)) = '\0';
-		if (isdigit((unsigned char)*q))
-			working->size = atoi(q) * 1024;
-		else
-			working->size = -1;
-		
-		working->flags = 0;
-		q = parse = missing_field(sob(++parse), errline, lineno);
-		*(parse = son(parse)) = '\0';
-		l = strtol(q, &ep, 10);
-		if (l < 0 || l >= INT_MAX)
-			errx(1, "%s:%d: interval out of range: %s", conf,
-			    lineno, q);
-		working->hours = (int)l;
-		switch (*ep) {
-		case '\0':
-			break;
-		case '@':
-			working->trim_at = parse8601(ep + 1);
-			if (working->trim_at == (time_t) - 1)
-				errx(1, "%s:%d: bad time: %s", conf, lineno, q);
-			working->flags |= CE_TRIMAT;
-			break;
-		case '$':
-			working->trim_at = parseDWM(ep + 1);
-			if (working->trim_at == (time_t) - 1)
-				errx(1, "%s:%d: bad time: %s", conf, lineno, q);
-			working->flags |= CE_TRIMAT;
+	l = strtol(q, &ep, 8);
+	if (*ep != '\0' || l < 0 || l > ALLPERMS)
+		errx(1, "%s:%d: bad permissions: %s", conf, lineno, q);
+	working->permissions = (mode_t)l;
+
+	q = parse = missing_field(sob(++parse), errline, lineno);
+	*(parse = son(parse)) = '\0';
+	l = strtol(q, &ep, 10);
+	if (*ep != '\0' || l < 0 || l >= INT_MAX)
+		errx(1, "%s:%d: bad number: %s", conf, lineno, q);
+	working->numlogs = (int)l;
+
+	q = parse = missing_field(sob(++parse), errline, lineno);
+	*(parse = son(parse)) = '\0';
+	if (isdigit((unsigned char)*q))
+		working->size = atoi(q) * 1024;
+	else
+		working->size = -1;
+
+	working->flags = 0;
+	q = parse = missing_field(sob(++parse), errline, lineno);
+	*(parse = son(parse)) = '\0';
+	l = strtol(q, &ep, 10);
+	if (l < 0 || l >= INT_MAX)
+		errx(1, "%s:%d: interval out of range: %s", conf,
+		    lineno, q);
+	working->hours = (int)l;
+	switch (*ep) {
+	case '\0':
+		break;
+	case '@':
+		working->trim_at = parse8601(ep + 1);
+		if (working->trim_at == (time_t) - 1)
+			errx(1, "%s:%d: bad time: %s", conf, lineno, q);
+		working->flags |= CE_TRIMAT;
+		break;
+	case '$':
+		working->trim_at = parseDWM(ep + 1);
+		if (working->trim_at == (time_t) - 1)
+			errx(1, "%s:%d: bad time: %s", conf, lineno, q);
+		working->flags |= CE_TRIMAT;
+		break;
+	case '*':
+		if (q == ep)
 			break;
-		case '*':
-			if (q == ep)
+		/* FALLTHROUGH */
+	default:
+		errx(1, "%s:%d: bad interval/at: %s", conf, lineno, q);
+		break;
+	}
+
+	q = sob(++parse);	/* Optional field */
+	if (*q == 'Z' || *q == 'z' || *q == 'B' || *q == 'b' ||
+	    *q == 'M' || *q == 'm') {
+		*(parse = son(q)) = '\0';
+		while (*q) {
+			switch (*q) {
+			case 'Z':
+			case 'z':
+				working->flags |= CE_COMPACT;
 				break;
-			/* FALLTHROUGH */
-		default:
-			errx(1, "%s:%d: bad interval/at: %s", conf, lineno, q);
-			break;
+			case 'B':
+			case 'b':
+				working->flags |= CE_BINARY;
+				break;
+			case 'M':
+			case 'm':
+				working->flags |= CE_MONITOR;
+				break;
+			case 'F':
+			case 'f':
+				working->flags |= CE_FOLLOW;
+				break;
+			default:
+				errx(1, "%s:%d: illegal flag: `%c'",
+				    conf, lineno, *q);
+				break;
+			}
+			q++;
 		}
+	} else
+		parse--;	/* no flags so undo */
 
-		q = sob(++parse);	/* Optional field */
-		if (*q == 'Z' || *q == 'z' || *q == 'B' || *q == 'b' ||
-		    *q == 'M' || *q == 'm') {
-			*(parse = son(q)) = '\0';
-			while (*q) {
-				switch (*q) {
-				case 'Z':
-				case 'z':
-					working->flags |= CE_COMPACT;
-					break;
-				case 'B':
-				case 'b':
-					working->flags |= CE_BINARY;
-					break;
-				case 'M':
-				case 'm':
-					working->flags |= CE_MONITOR;
-					break;
-				case 'F':
-				case 'f':
-					working->flags |= CE_FOLLOW;
-					break;
-				default:
-					errx(1, "%s:%d: illegal flag: `%c'",
-					    conf, lineno, *q);
-					break;
-				}
-				q++;
+	working->pidfile = PIDFILE;
+	working->signal = SIGHUP;
+	working->runcmd = NULL;
+	working->whom = NULL;
+	for (;;) {
+		q = parse = sob(++parse);	/* Optional field */
+		if (q == NULL || *q == '\0')
+			break;
+		if (*q == '/') {
+			*(parse = son(parse)) = '\0';
+			if (strlen(q) >= MAXPATHLEN)
+				errx(1, "%s:%d: pathname too long: %s",
+				    conf, lineno, q);
+			working->pidfile = strdup(q);
+			if (working->pidfile == NULL)
+				err(1, "strdup");
+		} else if (*q == '"' && (tmp = strchr(q + 1, '"'))) {
+			*(parse = tmp) = '\0';
+			if (*++q != '\0') {
+				working->runcmd = strdup(q);
+				if (working->runcmd == NULL)
+					err(1, "strdup");
 			}
-		} else
-			parse--;	/* no flags so undo */
+			working->pidfile = NULL;
+			working->signal = -1;
+		} else if (strncmp(q, "SIG", 3) == 0) {
+			int i;
 
-		working->pidfile = PIDFILE;
-		working->signal = SIGHUP;
-		working->runcmd = NULL;
-		working->whom = NULL;
-		for (;;) {
-			q = parse = sob(++parse);	/* Optional field */
-			if (q == NULL || *q == '\0')
-				break;
-			if (*q == '/') {
-				*(parse = son(parse)) = '\0';
-				if (strlen(q) >= MAXPATHLEN)
-					errx(1, "%s:%d: pathname too long: %s",
-					    conf, lineno, q);
-				working->pidfile = strdup(q);
-				if (working->pidfile == NULL)
-					err(1, "strdup");
-			} else if (*q == '"' && (tmp = strchr(q + 1, '"'))) {
-				*(parse = tmp) = '\0';
-				if (*++q != '\0') {
-					working->runcmd = strdup(q);
-					if (working->runcmd == NULL)
-						err(1, "strdup");
-				}
-				working->pidfile = NULL;
-				working->signal = -1;
-			} else if (strncmp(q, "SIG", 3) == 0) {
-				int i;
-
-				*(parse = son(parse)) = '\0';
-				for (i = 1; i < NSIG; i++) {
-					if (!strcmp(sys_signame[i], q + 3)) {
-						working->signal = i;
-						break;
-					}
+			*(parse = son(parse)) = '\0';
+			for (i = 1; i < NSIG; i++) {
+				if (!strcmp(sys_signame[i], q + 3)) {
+					working->signal = i;
+					break;
 				}
-				if (i == NSIG)
-					errx(1, "%s:%d: unknown signal: %s",
-					    conf, lineno, q);
-			} else if (working->flags & CE_MONITOR) {
-				*(parse = son(parse)) = '\0';
-				working->whom = strdup(q);
-				if (working->whom == NULL)
-					err(1, "strdup");
-			} else
-				errx(1, "%s:%d: unrecognized field: %s",
-				    conf, lineno, q);
-		}
-		free(errline);
-
-		if ((working->flags & CE_MONITOR) && working->whom == NULL)
-			errx(1, "%s:%d: missing monitor notification field",
-			    conf, lineno);
-
-		/* 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, "malloc");
-				*(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;
 			}
+			if (i == NSIG)
+				errx(1, "%s:%d: unknown signal: %s",
+				    conf, lineno, q);
+		} else if (working->flags & CE_MONITOR) {
+			*(parse = son(parse)) = '\0';
+			working->whom = strdup(q);
+			if (working->whom == NULL)
+				err(1, "strdup");
 		} else
-			working->backdir = NULL;
+			errx(1, "%s:%d: unrecognized field: %s",
+			    conf, lineno, q);
+	}
 
-		/* Make sure we can't oflow MAXPATHLEN */
-		if (working->backdir != NULL) {
-			if (snprintf(line, sizeof(line), "%s/%s.%d%s",
-			    working->backdir, working->logbase,
-			    working->numlogs, COMPRESS_POSTFIX) >= MAXPATHLEN)
-				errx(1, "%s:%d: pathname too long: %s",
-				    conf, lineno, q);
+	if ((working->flags & CE_MONITOR) && working->whom == NULL)
+		errx(1, "%s:%d: missing monitor notification field",
+		    conf, lineno);
+
+	/* If there is an arcdir, set working->backdir. */
+	if (arcdir != NULL && working->logbase != NULL) {
+		if (*arcdir == '/') {
+			/* Fully qualified arcdir */
+			working->backdir = arcdir;
 		} else {
-			if (snprintf(line, sizeof(line), "%s.%d%s",
-			    working->log, working->numlogs, COMPRESS_POSTFIX)
-			    >= MAXPATHLEN)
-				errx(1, "%s:%d: pathname too long: %s",
-				    conf, lineno, working->log);
+			/* arcdir is relative to log's parent dir */
+			*(working->logbase - 1) = '\0';
+			if ((asprintf(&working->backdir, "%s/%s",
+			    working->log, arcdir)) == -1)
+				err(1, "malloc");
+			*(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 MAXPATHLEN */
+	if (working->backdir != NULL) {
+		if (snprintf(line, sizeof(line), "%s/%s.%d%s",
+		    working->backdir, working->logbase,
+		    working->numlogs, COMPRESS_POSTFIX) >= MAXPATHLEN)
+			errx(1, "%s:%d: pathname too long: %s",
+			    conf, lineno, q);
+	} else {
+		if (snprintf(line, sizeof(line), "%s.%d%s",
+		    working->log, working->numlogs, COMPRESS_POSTFIX)
+		    >= MAXPATHLEN)
+			errx(1, "%s:%d: pathname too long: %s",
+			    conf, lineno, working->log);
 	}
-	if (working)
-		working->next = NULL;
-	(void)fclose(f);
-	return (first);
+
+	return working;
 }
 
 char *
@@ -1036,7 +1048,7 @@ domonitor(struct conf_entry *ent)
 			fclose(fp);
 			goto cleanup;
 		}
-		
+
 		/* Send message. */
 		fclose(fp);
 
@@ -1069,8 +1081,7 @@ update:
 
 cleanup:
 	free(flog);
-	if (rb != NULL)
-		free(rb);
+	free(rb);
 	return (1);
 }
 

Reply via email to