Print this page
9696 add /etc/system.d support
Reviewed by: Dan Fields <dan.fields@nexenta.com>
Reviewed by: Roman Strashkin <roman.strashkin@nexenta.com>
Reviewed by: Cynthia Eastham <cynthia.eastham@nexenta.com>
Reviewed by: Toomas Soome <tsoome@me.com>
Reviewed by: Peter Tribble <peter.tribble@gmail.com>
Reviewed by: Andy Fiddaman <andy@omniosce.org>

@@ -22,11 +22,11 @@
 /*
  * Copyright (c) 2005, 2010, Oracle and/or its affiliates. All rights reserved.
  * 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.
  */
 
 /*
  * bootadm(1M) is a new utility for managing bootability of

@@ -158,10 +158,13 @@
 
 #define INSTALLGRUB             "/sbin/installgrub"
 #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
  */
 #define DEFAULT_DEV_MODE        0644    /* default permissions */
 #define DEFAULT_DEV_UID         0       /* user root */

@@ -243,10 +246,11 @@
 static int bam_smf_check;
 static int bam_lock_fd = -1;
 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;
 static char *bam_home_env = NULL;
 

@@ -281,10 +285,11 @@
 static error_t update_all(char *, char *);
 static error_t read_list(char *, filelist_t *);
 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 *);
 static int s_fputs(char *, FILE *);
 

@@ -2457,10 +2462,23 @@
                                     "for %s\n"), file);
                                 return (-1);
                         }
                 }
 
+                /*
+                 * 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);
                         else
                                 bam_print(_("    changed %s\n"), file);

@@ -3789,11 +3807,134 @@
         bam_error(_("unable to create path on mountpoint %s, path too long\n"),
             root);
         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);
+        }
+
+        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];
         size_t len;
         struct stat sb;

@@ -4153,10 +4294,16 @@
                 set_flag(RDONLY_FSCHK);
                 bam_check = 1;
         }
 
         /*
+         * 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);
 
         /*