/* * Copyright (c) 2018, 2019 Reyk Floeter * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include #include #include #include #include #include #include #include #include #include #include #include "main.h" int opennebula(struct system_config *sc) { FILE *fp; const char *delim = "\\\\\0", *errstr; char *line = NULL, *k, *v, *p, q; char *value = NULL, *next = NULL, *last; char *hname = NULL, *uname = NULL; size_t len, lineno = 0, i; int ret = -1; unsigned short unit; if (sc->sc_state == STATE_INIT) { sc->sc_state = STATE_169; return (-1); } /* Return silently without error */ if ((fp = fopen("/mnt/context.sh", "r")) == NULL) goto done; while ((line = fparseln(fp, &len, &lineno, delim, FPARSELN_UNESCALL)) != NULL) { /* key */ k = line + strspn(line, " \t\r"); /* a context always starts with this header */ if (lineno == 1) { ret = strcmp(k, "# Context variables generated by OpenNebula"); if (ret != 0) { log_debug("%s: unsupported context", __func__); goto done; } free(line); continue; } /* Strip comments that do not occur within a value */ if (*k == '#') { free(line); continue; } /* value */ if ((v = strchr(line, '=')) == NULL || *(v + 1) == '\0') { free(line); continue; } *v++ = '\0'; /* value is quoted */ q = *v; if (strspn(v, "\"'") == 0) { free(line); continue; } *v++ = '\0'; /* quoted value can be continued on multiple lines */ if ((value = strdup("")) == NULL) { log_debug("%s: strdup", __func__); goto done; } next = v; do { if ((p = strrchr(next, q)) != NULL) *p++ = '\0'; if (*next) { last = value; if (asprintf(&value, "%s%s\n", last, next) == -1) { log_debug("%s: asprintf", __func__); if (next != v) free(next); goto done; } free(last); } if (next != v) free(next); } while (p == NULL && (next = fparseln(fp, &len, &lineno, delim, FPARSELN_UNESCALL)) != NULL); next = NULL; v = value; /* strip trailing newline */ if ((p = strrchr(v, '\n')) != NULL) *p = '\0'; /* continue if value is empty */ if (*v == '\0') { free(line); free(value); value = NULL; continue; } /* print key/value unless it is a multi-line value */ if (strcasecmp("SSH_PUBLIC_KEY", k) != 0 && strcasecmp("START_SCRIPT", k) != 0 && strcasecmp("START_SCRIPT_BASE64", k) != 0) log_debug("%s: %s = %s", __func__, k, v); if (strcasecmp("NETWORK", k) == 0) { if (strcasecmp("YES", v) == 0) sc->sc_network = 1; else if (strcasecmp("YES", v) == 0) sc->sc_network = 0; } else if (fnmatch("ETH*_*", k, 0) != FNM_NOMATCH) { /* Extract interface unit */ if ((p = strdup(k + 3)) == NULL) { log_debug("%s: %s", __func__, k); goto done; } p[strcspn(p, "_")] = '\0'; unit = strtonum(p, 0, UINT16_MAX, &errstr); free(p); if (errstr != NULL) { log_debug("%s: %s", __func__, k); goto done; } /* Get subkey */ k += strcspn(k, "_") + 1; if (strcasecmp("DNS", k) == 0) { /* We don't support per-interface DNS */ for (p = v; *p != '\0'; v = p) { p = v + strcspn(v, " \t"); *p++ = '\0'; if ((ret = agent_addnetaddr(sc, 0, v, AF_UNSPEC, NET_DNS)) != 0) break; } } else if (strcasecmp("SEARCH_DOMAIN", k) == 0) { for (p = v; *p != '\0'; v = p) { p = v + strcspn(v, " \t"); *p++ = '\0'; if ((ret = agent_addnetaddr(sc, 0, v, AF_UNSPEC, NET_DNS_DOMAIN)) != 0) break; } } else if (strcasecmp("IP", k) == 0) { ret = agent_addnetaddr(sc, unit, v, AF_INET, NET_IP); } else if (strcasecmp("MASK", k) == 0) { ret = agent_addnetaddr(sc, unit, v, AF_INET, NET_MASK); } else if (strcasecmp("GATEWAY", k) == 0) { ret = agent_addnetaddr(sc, unit, v, AF_INET, NET_GATEWAY); } else if (strcasecmp("IP6", k) == 0) { ret = agent_addnetaddr(sc, unit, v, AF_INET6, NET_IP); } else if (strcasecmp("GATEWAY6", k) == 0) { ret = agent_addnetaddr(sc, unit, v, AF_INET6, NET_GATEWAY); } else if (strcasecmp("PREFIX_LENGTH", k) == 0) { ret = agent_addnetaddr(sc, unit, v, AF_INET6, NET_PREFIX); } else if (strcasecmp("MAC", k) == 0) { if (unit == 0 && hname == NULL) { /* Fake a hostname using the mac */ if ((hname = p = calloc(1, strlen(v) + 3)) == NULL) { log_debug("%s: calloc", __func__); goto done; } *p++ = 'v'; *p++ = 'm'; for (i = 0; i < strlen(v); i++) { if (!isalnum(v[i])) continue; *p++ = v[i]; } } ret = agent_addnetaddr(sc, unit, v, AF_UNSPEC, NET_MAC); } else if (strcasecmp("MTU", k) == 0) { ret = agent_addnetaddr(sc, unit, v, AF_UNSPEC, NET_MTU); } else ret = 0; if (ret != 0) { log_debug("%s: failed to parse %s", __func__, k); goto done; } } else if (strcasecmp("HOSTNAME", k) == 0) { if ((hname = strdup(v)) == NULL) log_warnx("failed to set hostname"); } else if (strcasecmp("SSH_PUBLIC_KEY", k) == 0) { do { p = v + strcspn(v, "\n"); *p++ = '\0'; if (*v) log_debug("%s: %s = %s", __func__, k, v); if (*v && agent_addpubkey(sc, v, NULL) != 0) log_warnx("failed to set ssh pubkey"); v = p + strspn(p, "\n"); } while (*v != '\0'); } else if (strcasecmp("START_SCRIPT", k) == 0 || strcasecmp("START_SCRIPT_BASE64", k) == 0) { log_debug("%s: %s = ...", __func__, k); /* We will detect and decode base64 later */ if ((sc->sc_userdata = strdup(v)) == NULL) log_warnx("failed to set userdata"); } else if (strcasecmp("USERNAME", k) == 0) { if ((uname = strdup(v)) == NULL) log_warnx("failed to set username"); else { free(sc->sc_username); sc->sc_username = uname; } } free(line); free(value); value = NULL; } fclose(fp); fp = NULL; /* * OpenNebula doesn't provide an instance id so we * calculate one using the hash of the context file. * This might break if the context is not consistent. */ if ((sc->sc_instance = calloc(1, SHA256_DIGEST_STRING_LENGTH)) == NULL || SHA256File("/mnt/context.sh", sc->sc_instance) == NULL) { log_debug("%s: failed to calculate instance hash", __func__); goto done; } log_debug("%s: context instance %s", __func__, sc->sc_instance); /* Even the hostname is optional */ if (hname != NULL) { free(sc->sc_hostname); sc->sc_hostname = hname; log_debug("%s: hostname %s", __func__, hname); } line = NULL; ret = 0; done: if (fp != NULL) fclose(fp); free(line); free(value); return (ret); }