root/daemons/execd/remoted_schemas.c

/* [previous][next][first][last][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. rm_files
  2. clean_up_extra_schema_files
  3. write_extra_schema_file
  4. get_schema_files
  5. get_schema_files_complete
  6. remoted_request_cib_schema_files

   1 /*
   2  * Copyright 2023-2024 the Pacemaker project contributors
   3  *
   4  * The version control history for this file may have further details.
   5  *
   6  * This source code is licensed under the GNU Lesser General Public License
   7  * version 2.1 or later (LGPLv2.1+) WITHOUT ANY WARRANTY.
   8  */
   9 
  10 #include <crm_internal.h>
  11 
  12 #include <ftw.h>
  13 #include <unistd.h>
  14 #include <sys/stat.h>
  15 
  16 #include <crm/cib.h>
  17 #include <crm/cib/cib_types.h>
  18 #include <crm/cib/internal.h>
  19 #include <crm/crm.h>
  20 #include <crm/common/mainloop.h>
  21 #include <crm/common/xml.h>
  22 
  23 #include "pacemaker-execd.h"
  24 
  25 static pid_t schema_fetch_pid = 0;
  26 
  27 static int
  28 rm_files(const char *pathname, const struct stat *sbuf, int type, struct FTW *ftwb)
     /* [previous][next][first][last][top][bottom][index][help] */
  29 {
  30     /* Don't delete PCMK__REMOTE_SCHEMA_DIR . */
  31     if (ftwb->level == 0) {
  32         return 0;
  33     }
  34 
  35     if (remove(pathname) != 0) {
  36         int rc = errno;
  37         crm_err("Could not remove %s: %s", pathname, pcmk_rc_str(rc));
  38         return -1;
  39     }
  40 
  41     return 0;
  42 }
  43 
  44 static void
  45 clean_up_extra_schema_files(void)
     /* [previous][next][first][last][top][bottom][index][help] */
  46 {
  47     const char *remote_schema_dir = pcmk__remote_schema_dir();
  48     struct stat sb;
  49     int rc;
  50 
  51     rc = stat(remote_schema_dir, &sb);
  52 
  53     if (rc == -1) {
  54         if (errno == ENOENT) {
  55             /* If the directory doesn't exist, try to make it first. */
  56             if (mkdir(remote_schema_dir, 0755) != 0) {
  57                 rc = errno;
  58                 crm_err("Could not create directory for schemas: %s",
  59                         pcmk_rc_str(rc));
  60             }
  61 
  62         } else {
  63             rc = errno;
  64             crm_err("Could not create directory for schemas: %s",
  65                     pcmk_rc_str(rc));
  66         }
  67 
  68     } else if (!S_ISDIR(sb.st_mode)) {
  69         /* If something exists with the same name that's not a directory, that's
  70          * an error.
  71          */
  72         crm_err("%s already exists but is not a directory", remote_schema_dir);
  73 
  74     } else {
  75         /* It's a directory - clear it out so we can download potentially new
  76          * schema files.
  77          */
  78         rc = nftw(remote_schema_dir, rm_files, 10, FTW_DEPTH|FTW_MOUNT|FTW_PHYS);
  79 
  80         if (rc != 0) {
  81             crm_err("Could not remove %s: %s", remote_schema_dir, pcmk_rc_str(rc));
  82         }
  83     }
  84 }
  85 
  86 static void
  87 write_extra_schema_file(xmlNode *xml, void *user_data)
     /* [previous][next][first][last][top][bottom][index][help] */
  88 {
  89     const char *remote_schema_dir = pcmk__remote_schema_dir();
  90     const char *file = NULL;
  91     char *path = NULL;
  92     int rc;
  93 
  94     file = crm_element_value(xml, PCMK_XA_PATH);
  95     if (file == NULL) {
  96         crm_warn("No destination path given in schema request");
  97         return;
  98     }
  99 
 100     path = crm_strdup_printf("%s/%s", remote_schema_dir, file);
 101 
 102     /* The schema is a CDATA node, which is a child of the <file> node.  Traverse
 103      * all children and look for the first CDATA child.  There can't be more than
 104      * one because we only have one file attribute on the parent.
 105      */
 106     for (xmlNode *child = xml->children; child != NULL; child = child->next) {
 107         FILE *stream = NULL;
 108 
 109         if (child->type != XML_CDATA_SECTION_NODE) {
 110             continue;
 111         }
 112 
 113         stream = fopen(path, "w+");
 114         if (stream == NULL) {
 115             crm_warn("Could not write schema file %s: %s", path, strerror(errno));
 116         } else {
 117             rc = fprintf(stream, "%s", child->content);
 118 
 119             if (rc < 0) {
 120                 crm_warn("Could not write schema file %s: %s", path, strerror(errno));
 121             }
 122 
 123             fclose(stream);
 124         }
 125 
 126         break;
 127     }
 128 
 129     free(path);
 130 }
 131 
 132 static void
 133 get_schema_files(void)
     /* [previous][next][first][last][top][bottom][index][help] */
 134 {
 135     int rc = pcmk_rc_ok;
 136     cib_t *cib = NULL;
 137     xmlNode *reply;
 138 
 139     cib = cib_new();
 140     if (cib == NULL) {
 141         pcmk_common_cleanup();
 142         _exit(CRM_EX_OSERR);
 143     }
 144 
 145     rc = cib->cmds->signon(cib, crm_system_name, cib_query);
 146     rc = pcmk_legacy2rc(rc);
 147     if (rc != pcmk_rc_ok) {
 148         crm_err("Could not connect to the CIB manager: %s", pcmk_rc_str(rc));
 149         pcmk_common_cleanup();
 150         _exit(pcmk_rc2exitc(rc));
 151     }
 152 
 153     rc = cib->cmds->fetch_schemas(cib, &reply, pcmk__highest_schema_name(),
 154                                   cib_sync_call);
 155     if (rc != pcmk_ok) {
 156         crm_err("Could not get schema files: %s", pcmk_strerror(rc));
 157         rc = pcmk_legacy2rc(rc);
 158 
 159     } else if (reply->children != NULL) {
 160         /* The returned document looks something like this:
 161          * <cib_command>
 162          *   <cib_calldata>
 163          *     <schemas>
 164          *       <schema version="pacemaker-1.1">
 165          *         <file path="foo-1.1">
 166          *         </file>
 167          *         <file path="bar-1.1">
 168          *         </file>
 169          *         ...
 170          *       </schema>
 171          *       <schema version="pacemaker-1.2">
 172          *       </schema>
 173          *       ...
 174          *     </schemas>
 175          *   </cib_calldata>
 176          * </cib_command>
 177          *
 178          * All the <schemas> and <schema> tags are really just there for organizing
 179          * the XML a little better.  What we really care about are the <file> nodes,
 180          * and specifically the path attributes and the CDATA children (not shown)
 181          * of each.  We can use an xpath query to reach down and get all the <file>
 182          * nodes at once.
 183          *
 184          * If we already have the latest schema version, or we asked for one later
 185          * than what the cluster supports, we'll get back an empty <schemas> node,
 186          * so all this will continue to work.  It just won't do anything.
 187          */
 188         crm_foreach_xpath_result(reply, "//" PCMK_XA_FILE,
 189                                  write_extra_schema_file, NULL);
 190     }
 191 
 192     pcmk__xml_free(reply);
 193     cib__clean_up_connection(&cib);
 194     pcmk_common_cleanup();
 195     _exit(pcmk_rc2exitc(rc));
 196 }
 197 
 198 /* Load any additional schema files when the child is finished fetching and
 199  * saving them to disk.
 200  */
 201 static void
 202 get_schema_files_complete(mainloop_child_t *p, pid_t pid, int core, int signo, int exitcode)
     /* [previous][next][first][last][top][bottom][index][help] */
 203 {
 204     const char *errmsg = "Could not load additional schema files";
 205 
 206     if ((signo == 0) && (exitcode == 0)) {
 207         const char *remote_schema_dir = pcmk__remote_schema_dir();
 208 
 209         /* Don't just call pcmk__schema_init() here because that will load the
 210          * base schemas again too. Instead just load the things we fetched.
 211          */
 212         pcmk__load_schemas_from_dir(remote_schema_dir);
 213         pcmk__sort_schemas();
 214         crm_info("Fetching extra schema files completed successfully");
 215 
 216     } else {
 217         if (signo == 0) {
 218             crm_err("%s: process %d exited %d", errmsg, (int) pid, exitcode);
 219 
 220         } else {
 221             crm_err("%s: process %d terminated with signal %d (%s)%s",
 222                     errmsg, (int) pid, signo, strsignal(signo),
 223                     (core? " and dumped core" : ""));
 224         }
 225 
 226         /* Clean up any incomplete schema data we might have been downloading
 227          * when the process timed out or crashed. We don't need to do any extra
 228          * cleanup because we never loaded the extra schemas, and we don't need
 229          * to call pcmk__schema_init() because that was called in
 230          * remoted_request_cib_schema_files() before this function.
 231          */
 232         clean_up_extra_schema_files();
 233     }
 234 }
 235 
 236 void
 237 remoted_request_cib_schema_files(void)
     /* [previous][next][first][last][top][bottom][index][help] */
 238 {
 239     pid_t pid;
 240     int rc;
 241 
 242     /* If a previous schema-fetch process is still running when we're called
 243      * again, it's hung.  Attempt to kill it before cleaning up the extra
 244      * directory.
 245      */
 246     if (schema_fetch_pid != 0) {
 247         if (mainloop_child_kill(schema_fetch_pid) == FALSE) {
 248             crm_warn("Unable to kill pre-existing schema-fetch process");
 249             return;
 250         }
 251 
 252         schema_fetch_pid = 0;
 253     }
 254 
 255     /* Clean up any extra schema files we downloaded from a previous cluster
 256      * connection.  After the files are gone, we need to wipe them from
 257      * known_schemas, but there's no opposite operation for add_schema().
 258      *
 259      * Instead, unload all the schemas.  This means we'll also forget about all
 260      * installed schemas as well, which means that pcmk__highest_schema_name()
 261      * would fail. So we need to load the base schemas right now.
 262      */
 263     clean_up_extra_schema_files();
 264     pcmk__schema_cleanup();
 265     pcmk__schema_init();
 266 
 267     crm_info("Fetching extra schema files from cluster");
 268     pid = fork();
 269 
 270     switch (pid) {
 271         case -1: {
 272             rc = errno;
 273             crm_warn("Could not spawn process to get schema files: %s", pcmk_rc_str(rc));
 274             break;
 275         }
 276 
 277         case 0:
 278             /* child */
 279             get_schema_files();
 280             break;
 281 
 282         default:
 283             /* parent */
 284             schema_fetch_pid = pid;
 285             mainloop_child_add_with_flags(pid, 5 * 60 * 1000, "schema-fetch", NULL,
 286                                           mainloop_leave_pid_group,
 287                                           get_schema_files_complete);
 288             break;
 289     }
 290 }

/* [previous][next][first][last][top][bottom][index][help] */