--- old/usr/src/Targetdirs	Mon Feb 11 08:17:17 2019
+++ new/usr/src/Targetdirs	Mon Feb 11 08:17:17 2019
@@ -26,7 +26,7 @@
 # Copyright 2012 OmniTI Computer Consulting, Inc.  All rights reserved.
 # Copyright 2014 Garrett D'Amore <garrett@damore.org>
 # Copyright 2016 Hans Rosenfeld <rosenfeld@grumpf.hope-2000.org>
-# Copyright 2016 Nexenta Systems, Inc.
+# Copyright 2017 Nexenta Systems, Inc.
 # Copyright 2017 RackTop Systems.
 # Copyright 2019 OmniOS Community Edition (OmniOSce) Association.
 #
@@ -178,6 +178,7 @@
 	/etc/svc/profile \
 	/etc/svc/profile/site \
 	/etc/svc/volatile \
+	/etc/system.d \
 	/etc/tm  \
 	/etc/usb   \
 	/etc/user_attr.d \
--- old/usr/src/cmd/boot/bootadm/bootadm.c	Mon Feb 11 08:17:17 2019
+++ new/usr/src/cmd/boot/bootadm/bootadm.c	Mon Feb 11 08:17:17 2019
@@ -24,7 +24,7 @@
  * Copyright 2012 Milan Jurik. All rights reserved.
  * Copyright (c) 2015 by Delphix. All rights reserved.
  * Copyright 2016 Toomas Soome <tsoome@me.com>
- * Copyright 2016 Nexenta Systems, Inc.
+ * Copyright 2017 Nexenta Systems, Inc.
  * Copyright 2018 OmniOS Community Edition (OmniOSce) Association.
  */
 
@@ -160,6 +160,9 @@
 #define	STAGE1			"/boot/grub/stage1"
 #define	STAGE2			"/boot/grub/stage2"
 
+#define	ETC_SYSTEM_DIR		"etc/system.d"
+#define	SELF_ASSEMBLY		"etc/system.d/.self-assembly"
+
 /*
  * Default file attributes
  */
@@ -245,6 +248,7 @@
 static int bam_zfs;
 static int bam_mbr;
 char rootbuf[PATH_MAX] = "/";
+static char self_assembly[PATH_MAX];
 static int bam_update_all;
 static int bam_alt_platform;
 static char *bam_platform;
@@ -283,6 +287,7 @@
 static error_t set_option(menu_t *, char *, char *);
 static error_t set_kernel(menu_t *, menu_cmd_t, char *, char *, size_t);
 static error_t get_kernel(menu_t *, menu_cmd_t, char *, size_t);
+static error_t build_etc_system_dir(char *);
 static char *expand_path(const char *);
 
 static long s_strtol(char *);
@@ -2459,6 +2464,19 @@
 			}
 		}
 
+		/*
+		 * Update self-assembly file if there are changes in
+		 * /etc/system.d directory
+		 */
+		if (strstr(file, ETC_SYSTEM_DIR)) {
+			ret = update_dircache(self_assembly, flags);
+			if (ret == BAM_ERROR) {
+				bam_error(_("directory cache update failed "
+				    "for %s\n"), file);
+				return (-1);
+			}
+		}
+
 		if (bam_verbose) {
 			if (bam_smf_check)
 				bam_print("    %s\n", file);
@@ -3791,7 +3809,132 @@
 	return (BAM_ERROR);
 }
 
+static int
+assemble_systemfile(char *infilename, char *outfilename)
+{
+	char buf[BUFSIZ];
+	FILE *infile, *outfile;
+	size_t n;
+
+	if ((infile = fopen(infilename, "r")) == NULL) {
+		bam_error(_("failed to open file: %s: %s\n"), infilename,
+		    strerror(errno));
+		return (BAM_ERROR);
+	}
+
+	if ((outfile = fopen(outfilename, "a")) == NULL) {
+		bam_error(_("failed to open file: %s: %s\n"), outfilename,
+		    strerror(errno));
+		(void) fclose(infile);
+		return (BAM_ERROR);
+	}
+
+	while ((n = fread(buf, 1, sizeof (buf), infile)) > 0) {
+		if (fwrite(buf, 1, n, outfile) != n) {
+			bam_error(_("failed to write file: %s: %s\n"),
+			    outfilename, strerror(errno));
+			(void) fclose(infile);
+			(void) fclose(outfile);
+			return (BAM_ERROR);
+		}
+	}
+
+	(void) fclose(infile);
+	(void) fclose(outfile);
+
+	return (BAM_SUCCESS);
+}
+
+/*
+ * Concatenate all files (except those starting with a dot)
+ * from /etc/system.d directory into a single /etc/system.d/.self-assembly
+ * file. The kernel reads it before /etc/system file.
+ */
 static error_t
+build_etc_system_dir(char *root)
+{
+	struct dirent **filelist;
+	char path[PATH_MAX], tmpfile[PATH_MAX];
+	int i, files, sysfiles = 0;
+	int ret = BAM_SUCCESS;
+	struct stat st;
+	timespec_t times[2];
+
+	(void) snprintf(path, sizeof (path), "%s/%s", root, ETC_SYSTEM_DIR);
+	(void) snprintf(self_assembly, sizeof (self_assembly),
+	    "%s%s", root, SELF_ASSEMBLY);
+	(void) snprintf(tmpfile, sizeof (tmpfile), "%s.%ld",
+	    self_assembly, (long)getpid());
+
+	if (stat(self_assembly, &st) >= 0 && (st.st_mode & S_IFMT) == S_IFREG) {
+		times[0] = times[1] = st.st_mtim;
+	} else {
+		times[1].tv_nsec = 0;
+	}
+
+	if ((files = scandir(path, &filelist, NULL, alphasort)) < 0) {
+		/* Don't fail the update if <ROOT>/etc/system.d doesn't exist */
+		if (errno == ENOENT)
+			return (BAM_SUCCESS);
+		bam_error(_("can't read %s: %s\n"), path, strerror(errno));
+		return (BAM_ERROR);
+	}
+
+	(void) unlink(tmpfile);
+
+	for (i = 0; i < files; i++) {
+		char	filepath[PATH_MAX];
+		char	*fname;
+
+		fname = filelist[i]->d_name;
+
+		/* skip anything that starts with a dot */
+		if (strncmp(fname, ".", 1) == 0) {
+			free(filelist[i]);
+			continue;
+		}
+
+		if (bam_verbose)
+			bam_print(_("/etc/system.d adding %s/%s\n"),
+			    path, fname);
+
+		(void) snprintf(filepath, sizeof (filepath), "%s/%s",
+		    path, fname);
+
+		if ((assemble_systemfile(filepath, tmpfile)) < 0) {
+			bam_error(_("failed to append file: %s: %s\n"),
+			    filepath, strerror(errno));
+			ret = BAM_ERROR;
+			break;
+		}
+		sysfiles++;
+	}
+
+	if (sysfiles > 0) {
+		if (rename(tmpfile, self_assembly) < 0) {
+			bam_error(_("failed to rename file: %s: %s\n"), tmpfile,
+			    strerror(errno));
+			return (BAM_ERROR);
+		}
+
+		/*
+		 * Use previous attribute times to avoid
+		 * boot archive recreation.
+		 */
+		if (times[1].tv_nsec != 0 &&
+		    utimensat(AT_FDCWD, self_assembly, times, 0) != 0) {
+			bam_error(_("failed to change times: %s\n"),
+			    strerror(errno));
+			return (BAM_ERROR);
+		}
+	} else {
+		(void) unlink(tmpfile);
+		(void) unlink(self_assembly);
+	}
+	return (ret);
+}
+
+static error_t
 create_ramdisk(char *root)
 {
 	char *cmdline, path[PATH_MAX];
@@ -4155,6 +4298,12 @@
 	}
 
 	/*
+	 * Process the /etc/system.d/.self-assembly file.
+	 */
+	if (build_etc_system_dir(bam_root) == BAM_ERROR)
+		return (BAM_ERROR);
+
+	/*
 	 * Now check if an update is really needed.
 	 */
 	ret = update_required(root);
--- old/usr/src/cmd/boot/filelist/i386/filelist.ramdisk	Mon Feb 11 08:17:17 2019
+++ new/usr/src/cmd/boot/filelist/i386/filelist.ramdisk	Mon Feb 11 08:17:17 2019
@@ -16,6 +16,7 @@
 etc/path_to_inst
 etc/rtc_config
 etc/system
+etc/system.d
 etc/hostid
 kernel
 platform/i86hvm/kernel
--- old/usr/src/cmd/nsadmin/system	Mon Feb 11 08:17:17 2019
+++ new/usr/src/cmd/nsadmin/system	Mon Feb 11 08:17:17 2019
@@ -1,4 +1,3 @@
-*ident	"%Z%%M%	%I%	%E% SMI" /* SVR4 1.5 */
 *
 * CDDL HEADER START
 *
@@ -24,6 +23,18 @@
 * SYSTEM SPECIFICATION FILE
 *
 
+* !!!!!!!!!!!!!!!!!!!!!!!!!!!!!! NOTE !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
+*
+* It is not recommended to edit this file directly but rather
+* to deliver configuration fragments into files under /etc/system.d;
+* files in /etc/system.d are combined in alphabetical order and read by
+* the kernel before this file (/etc/system) is processed.
+*
+* Refer to the system(4) manual page for more information and
+* recommendations on naming fragment files.
+*
+* !!!!!!!!!!!!!!!!!!!!!!!!!!!!!! NOTE !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
+
 * moddir:
 *
 *	Set the search path for modules.  This has a format similar to the
@@ -72,7 +83,7 @@
 *
 *	Cause these modules to be loaded at boot time, (just before mounting
 *	the root filesystem) rather than at first reference. Note that
-* 	forceload expects a filename which includes the directory. Also
+*	forceload expects a filename which includes the directory. Also
 *	note that loading a module does not necessarily imply that it will
 *	be installed.
 *
--- old/usr/src/man/man4/system.4	Mon Feb 11 08:17:18 2019
+++ new/usr/src/man/man4/system.4	Mon Feb 11 08:17:18 2019
@@ -1,11 +1,12 @@
 '\" te
 .\" Copyright (c) 2003 Sun Microsystems, Inc.  All Rights Reserved.
 .\" Copyright 2016 Hans Rosenfeld <rosenfeld@grumpf.hope-2000.org>
+.\" Copyright 2019 OmniOS Community Edition (OmniOSce) Association.
 .\" Copyright 1989 AT&T
 .\" The contents of this file are subject to the terms of the Common Development and Distribution License (the "License").  You may not use this file except in compliance with the License.
 .\" You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE or http://www.opensolaris.org/os/licensing.  See the License for the specific language governing permissions and limitations under the License.
 .\" When distributing Covered Code, include this CDDL HEADER in each file and include the License file at usr/src/OPENSOLARIS.LICENSE.  If applicable, add the following below this CDDL HEADER, with the fields enclosed by brackets "[]" replaced with your own identifying information: Portions Copyright [yyyy] [name of copyright owner]
-.TH SYSTEM 4 "Jan 25, 2016"
+.TH SYSTEM 4 "Jan 29, 2019"
 .SH NAME
 system \- system configuration information file
 .SH DESCRIPTION
@@ -15,6 +16,23 @@
 \fBsystem\fR file before modifying it.
 .sp
 .LP
+It is not recommended to edit the \fB/etc/system\fR file directly but rather
+to deliver configuration fragments into files under \fB/etc/system.d\fR;
+files in this directory are combined in alphabetical order and read by the
+kernel before \fB/etc/system\fR is processed. Directives in \fB/etc/system\fR
+therefore take precedence over any settings delivered in fragment files.
+.sp
+.LP
+The recommended naming schema for the fragment files is to use the name of
+the package which is delivering the file with '\fB/\fR' characters replaced
+by '\fB:\fR'; file names that start with a dot (\fB.\fR) will be ignored.
+.sp
+.LP
+If \fB/etc/system.d/\fR exists and contains any fragment files,
+then the directory must also be writable or it will not be possible to
+create or update the system boot archive.
+.sp
+.LP
 The \fBsystem\fR file contains commands which are read by the kernel during
 initialization and used to customize the operation of your system. These
 commands are useful for modifying the system's treatment of its loadable kernel
@@ -286,9 +304,9 @@
 .sp
 .in +2
 .nf
-\en 	(newline)
-\et 	(tab)
-\eb 	(backspace)
+\en	(newline)
+\et	(tab)
+\eb	(backspace)
 .fi
 .in -2
 .sp
@@ -360,4 +378,4 @@
 settings.
 .SH NOTES
 .LP
-The \fB/etc/system\fR file is read only once, at boot time.
+The \fBsystem\fR files are read only once, at boot time.
--- old/usr/src/pkg/manifests/SUNWcs.mf	Mon Feb 11 08:17:18 2019
+++ new/usr/src/pkg/manifests/SUNWcs.mf	Mon Feb 11 08:17:18 2019
@@ -23,7 +23,7 @@
 # Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved.
 # Copyright (c) 2013 Gary Mills
 # Copyright 2014 Garrett D'Amore <garrett@damore.org>
-# Copyright 2015 Nexenta Systems, Inc. All rights reserved.
+# Copyright 2017 Nexenta Systems, Inc.
 # Copyright 2017 Toomas Soome <tsoome@me.com>
 # Copyright 2019 Peter Tribble.
 #
@@ -90,6 +90,7 @@
 dir path=etc/svc/volatile group=sys
 dir path=etc/sysevent group=sys
 dir path=etc/sysevent/config group=sys
+dir path=etc/system.d group=sys
 dir path=etc/tm group=sys
 dir path=etc/user_attr.d group=sys
 dir path=export group=sys
--- old/usr/src/uts/common/os/modctl.c	Mon Feb 11 08:17:18 2019
+++ new/usr/src/uts/common/os/modctl.c	Mon Feb 11 08:17:18 2019
@@ -1234,7 +1234,7 @@
 /*ARGSUSED*/
 static int
 modctl_devid2paths(ddi_devid_t udevid, char *uminor_name, uint_t flag,
-	size_t *ulensp, char *upaths)
+    size_t *ulensp, char *upaths)
 {
 	ddi_devid_t	devid = NULL;
 	int		devid_len;
@@ -3351,7 +3351,7 @@
 	if ((fd = kobj_open(systemfile)) != -1L)
 		kobj_close(fd);
 	else
-		systemfile = NULL;
+		systemfile = self_assembly = NULL;
 
 	/*CONSTANTCONDITION*/
 	while (1) {
@@ -3363,12 +3363,13 @@
 		if (s0[0] == '\0')
 			break;
 		else if (strcmp(s0, "/dev/null") == 0) {
-			systemfile = NULL;
+			systemfile = self_assembly = NULL;
 			break;
 		} else {
 			if ((fd = kobj_open(s0)) != -1L) {
 				kobj_close(fd);
 				systemfile = s0;
+				self_assembly = NULL;
 				break;
 			}
 		}
--- old/usr/src/uts/common/os/modsysfile.c	Mon Feb 11 08:17:18 2019
+++ new/usr/src/uts/common/os/modsysfile.c	Mon Feb 11 08:17:18 2019
@@ -22,7 +22,7 @@
 /*
  * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
  * Use is subject to license terms.
- * Copyright 2016 Nexenta Systems, Inc.
+ * Copyright 2017 Nexenta Systems, Inc.
  */
 
 #include <sys/types.h>
@@ -64,6 +64,7 @@
 static char dafile[] = DAFILE;
 static char dacffile[] = DACFFILE;
 
+char *self_assembly = "/etc/system.d/.self-assembly";
 char *systemfile = "/etc/system";	/* name of ascii system file */
 
 static struct sysparam *sysparam_hd;	/* head of parameters list */
@@ -751,8 +752,8 @@
 	return (NULL);
 }
 
-void
-mod_read_system_file(int ask)
+static void
+read_system_file(char *name)
 {
 	register struct sysparam *sp;
 	register struct _buf *file;
@@ -759,61 +760,74 @@
 	register token_t token, last_tok;
 	char tokval[MAXLINESIZE];
 
-	mod_sysfile_arena = vmem_create("mod_sysfile", NULL, 0, 8,
-	    segkmem_alloc, segkmem_free, heap_arena, 0, VM_SLEEP);
-
-	if (ask)
-		mod_askparams();
-
-	if (systemfile != NULL) {
-
-		if ((file = kobj_open_file(systemfile)) ==
-		    (struct _buf *)-1) {
+	if ((file = kobj_open_file(name)) ==
+	    (struct _buf *)-1) {
+		if (strcmp(name, systemfile) == 0)
 			cmn_err(CE_WARN, "cannot open system file: %s",
-			    systemfile);
-		} else {
+			    name);
+	} else {
+		if (sysparam_tl == NULL)
 			sysparam_tl = (struct sysparam *)&sysparam_hd;
 
-			last_tok = NEWLINE;
-			while ((token = kobj_lex(file, tokval,
-			    sizeof (tokval))) != EOF) {
-				switch (token) {
-				case STAR:
-				case POUND:
-					/*
-					 * Skip comments.
-					 */
+		last_tok = NEWLINE;
+		while ((token = kobj_lex(file, tokval,
+		    sizeof (tokval))) != EOF) {
+			switch (token) {
+			case STAR:
+			case POUND:
+				/*
+				 * Skip comments.
+				 */
+				kobj_find_eol(file);
+				break;
+			case NEWLINE:
+				kobj_newline(file);
+				last_tok = NEWLINE;
+				break;
+			case NAME:
+				if (last_tok != NEWLINE) {
+					kobj_file_err(CE_WARN, file,
+					    extra_err, tokval);
 					kobj_find_eol(file);
-					break;
-				case NEWLINE:
-					kobj_newline(file);
-					last_tok = NEWLINE;
-					break;
-				case NAME:
-					if (last_tok != NEWLINE) {
-						kobj_file_err(CE_WARN, file,
-						    extra_err, tokval);
-						kobj_find_eol(file);
-					} else if ((sp = do_sysfile_cmd(file,
-					    tokval)) != NULL) {
-						sp->sys_next = NULL;
-						sysparam_tl->sys_next = sp;
-						sysparam_tl = sp;
-					}
-					last_tok = NAME;
-					break;
-				default:
-					kobj_file_err(CE_WARN,
-					    file, tok_err, tokval);
-					kobj_find_eol(file);
-					break;
+				} else if ((sp = do_sysfile_cmd(file,
+				    tokval)) != NULL) {
+					sp->sys_next = NULL;
+					sysparam_tl->sys_next = sp;
+					sysparam_tl = sp;
 				}
+				last_tok = NAME;
+				break;
+			default:
+				kobj_file_err(CE_WARN,
+				    file, tok_err, tokval);
+				kobj_find_eol(file);
+				break;
 			}
-			kobj_close_file(file);
 		}
+		kobj_close_file(file);
 	}
+}
 
+void
+mod_read_system_file(int ask)
+{
+	mod_sysfile_arena = vmem_create("mod_sysfile", NULL, 0, 8,
+	    segkmem_alloc, segkmem_free, heap_arena, 0, VM_SLEEP);
+
+	if (ask)
+		mod_askparams();
+
 	/*
+	 * Read the user self-assembly file first
+	 * to preserve existing system settings.
+	 */
+	if (self_assembly != NULL)
+		read_system_file(self_assembly);
+
+	if (systemfile != NULL)
+		read_system_file(systemfile);
+
+	/*
 	 * Sanity check of /etc/system.
 	 */
 	check_system_file();
@@ -2384,16 +2398,16 @@
 
 /*
  * read_dacf_binding_file()
- * 	Read the /etc/dacf.conf file and build the dacf_rule_t database from it.
+ *	Read the /etc/dacf.conf file and build the dacf_rule_t database from it.
  *
  * The syntax of a line in the dacf.conf file is:
- *   dev-spec 	[module:]op-set	operation options 	[config-args];
+ *   dev-spec	[module:]op-set	operation options	[config-args];
  *
  * Where:
- *   	1. dev-spec is of the format: name="data"
- *   	2. operation is the operation that this rule matches. (i.e. pre-detach)
- *   	3. options is a comma delimited list of options (i.e. debug,foobar)
- *   	4. config-data is a whitespace delimited list of the format: name="data"
+ *	1. dev-spec is of the format: name="data"
+ *	2. operation is the operation that this rule matches. (i.e. pre-detach)
+ *	3. options is a comma delimited list of options (i.e. debug,foobar)
+ *	4. config-data is a whitespace delimited list of the format: name="data"
  */
 int
 read_dacf_binding_file(char *filename)
--- old/usr/src/uts/common/sys/modctl.h	Mon Feb 11 08:17:18 2019
+++ new/usr/src/uts/common/sys/modctl.h	Mon Feb 11 08:17:19 2019
@@ -546,6 +546,7 @@
 extern kmutex_t mod_lock;
 
 extern char *systemfile;
+extern char *self_assembly;
 extern char **syscallnames;
 extern int moddebug;
 
