Mirror of https://github.com/reyk/cloud-agent
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
284 lines
7.2 KiB
284 lines
7.2 KiB
/* |
|
* Copyright (c) 2018, 2019 Reyk Floeter <reyk@openbsd.org> |
|
* |
|
* 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 <stdio.h> |
|
#include <stdlib.h> |
|
#include <stdint.h> |
|
#include <string.h> |
|
#include <unistd.h> |
|
#include <fnmatch.h> |
|
#include <fcntl.h> |
|
#include <ctype.h> |
|
#include <sha2.h> |
|
#include <err.h> |
|
#include <util.h> |
|
|
|
#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; |
|
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"); |
|
} |
|
|
|
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); |
|
}
|
|
|