From 8dfa3c843a3706474dcd41c217dfe5cb51103bb0 Mon Sep 17 00:00:00 2001 From: Reyk Floeter Date: Sat, 1 Jun 2019 23:41:32 +0200 Subject: [PATCH] Add "-r rootdisk" growdisk support --- agent/Makefile | 1 + agent/cloud-agent.8 | 10 ++- agent/growdisk.c | 149 ++++++++++++++++++++++++++++++++++++++++++++ agent/main.c | 50 +++++++++------ agent/main.h | 4 ++ 5 files changed, 193 insertions(+), 21 deletions(-) create mode 100644 agent/growdisk.c diff --git a/agent/Makefile b/agent/Makefile index 8d57acb..0e2925c 100644 --- a/agent/Makefile +++ b/agent/Makefile @@ -1,6 +1,7 @@ PROG= cloud-agent SRCS= http.c json.c jsmn.c log.c main.c xml.c SRCS+= azure.c cloudinit.c opennebula.c openstack.c +SRCS+= growdisk.c BINDIR= /usr/local/libexec MANDIR= /usr/local/man/man diff --git a/agent/cloud-agent.8 b/agent/cloud-agent.8 index 2c6e2c4..0cec012 100644 --- a/agent/cloud-agent.8 +++ b/agent/cloud-agent.8 @@ -23,6 +23,7 @@ .Sh SYNOPSIS .Nm cloud-agent .Op Fl nuv +.Op Fl r Ar rootdisk .Op Fl t Ar timeout .Op Fl U Ar username .Ar interface @@ -53,6 +54,10 @@ configuration. Using .Dq root is supported, but not recommended. +.It Fl r Ar rootdisk +Automatically grow the last +.Ox +FFS partition of the root disk to use all the available space. .It Fl u Deprovision and unconfigure the system. This deletes keys, passwords, and logs files without asking for permission. @@ -64,11 +69,12 @@ Enable .Nm in the .Xr hostname.if 5 -of the VM's primary networking interface: +of the VM's primary networking interface and automatically the last +partition of the root disk: .Bd -literal -offset indent # cat /etc/hostname.hvn0 dhcp -!/usr/local/libexec/cloud-agent "\e$if" +!/usr/local/libexec/cloud-agent -r sd0 "\e$if" .Ed .Sh FILES .Bl -tag -width "/usr/local/libexec/cloud-agentX" -compact diff --git a/agent/growdisk.c b/agent/growdisk.c new file mode 100644 index 0000000..dd920bb --- /dev/null +++ b/agent/growdisk.c @@ -0,0 +1,149 @@ +/* + * Copyright (c) 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 /* DEV_BSIZE */ +#include +#include +#include +#define DKTYPENAMES +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "main.h" + +static uint16_t dkcksum(struct disklabel *); + +static uint16_t +dkcksum(struct disklabel *lp) +{ + uint16_t *start, *end, sum; + start = (uint16_t *)lp; + end = (uint16_t *)&lp->d_partitions[lp->d_npartitions]; + for (sum = 0; start < end;) + sum ^= *start++; + return (sum); +} + +int +growdisk(struct system_config *sc) +{ + char c, last_part = 0, *out, *path = NULL; + int ret = -1, i, errfd, outfd; + struct partition *pp, *p = NULL; + struct disklabel lp; + int fd; + uint16_t cksum; + + /* + * Grow the OpenBSD MBR partition + */ + + /* XXX this is a bit ugly but easier to do */ + if (shellout("e 3\n\n\n\n*\nw\nq\n", &out, + "fdisk", "-e", sc->sc_rootdisk, NULL) != 0) { + log_warnx("failed to grow OpenBSD partition"); + return (-1); + } + free(out); + + /* + * Grow the last partition in the disklabel + */ + + if ((fd = opendev(sc->sc_rootdisk, + O_RDWR, OPENDEV_PART, NULL)) == -1) { + log_warn("failed to open %s", sc->sc_rootdisk); + return (-1); + } + + if (ioctl(fd, DIOCGDINFO, &lp) == -1) { + log_warn("failed to get disklabel"); + goto done; + } + + if (lp.d_magic != DISKMAGIC || lp.d_magic2 != DISKMAGIC) { + log_warnx("invalid disklabel magic bytes"); + goto done; + } + cksum = lp.d_checksum; + lp.d_checksum = 0; + + if (dkcksum(&lp) != cksum) { + log_warnx("invalid disklabel checksum"); + goto done; + } + + pp = lp.d_partitions; + for (i = 0, pp = lp.d_partitions; i < lp.d_npartitions; i++, pp++) { + if (!DL_GETPSIZE(pp)) + continue; + c = 'a' + i; + if (pp->p_fstype == FS_BSDFFS) { + last_part = c; + p = pp; + } + } + + if (last_part == 0) { + log_warnx("last BSD partition not found"); + goto done; + } + + /* Update OpenBSD boundaries */ + DL_SETBEND(&lp, DL_GETDSIZE(&lp) - DL_GETBSTART(&lp)); + + /* Update the size of the last partition */ + DL_SETPSIZE(p, DL_GETBEND(&lp) - DL_GETPOFFSET(p)); + + lp.d_checksum = dkcksum(&lp); + + if (ioctl(fd, DIOCWDINFO, &lp) == -1) { + log_warn("failed to write disklabel"); + goto done; + } + + /* + * Grow the filesystem + */ + + if (asprintf(&path, "/dev/%s%c", sc->sc_rootdisk, last_part) == -1) + goto done; + + errfd = disable_output(sc, STDERR_FILENO); + outfd = disable_output(sc, STDOUT_FILENO); + + (void)shell("umount", "-f", path, NULL); + (void)shell("growfs", "-yq", path, NULL); + if ((ret = shell("fsck", "-y", path, NULL)) != 0) + ret = -1; + + enable_output(sc, STDERR_FILENO, errfd); + enable_output(sc, STDOUT_FILENO, outfd); + + ret = 0; + done: + free(path); + close(fd); + return (ret); +} diff --git a/agent/main.c b/agent/main.c index 4ec29df..4632c97 100644 --- a/agent/main.c +++ b/agent/main.c @@ -42,17 +42,18 @@ #include "main.h" #include "xml.h" -__dead void usage(void); -static struct system_config *agent_init(const char *, int, int); -static int agent_configure(struct system_config *); -static int agent_network(struct system_config *); -static void agent_free(struct system_config *); -static int agent_pf(struct system_config *, int); -static int agent_userdata(const unsigned char *, size_t); -static void agent_unconfigure(void); -static char *metadata_parse(char *, size_t, enum strtype); +__dead void usage(void); +static struct system_config + *agent_init(const char *, const char *, int, int); +static int agent_configure(struct system_config *); +static int agent_network(struct system_config *); +static void agent_free(struct system_config *); +static int agent_pf(struct system_config *, int); +static int agent_userdata(const unsigned char *, size_t); +static void agent_unconfigure(void); +static char *metadata_parse(char *, size_t, enum strtype); -static int agent_timeout; +static int agent_timeout; int shell(const char *arg, ...) @@ -306,7 +307,7 @@ get_word(const unsigned char *ptr, size_t len) } static struct system_config * -agent_init(const char *ifname, int dryrun, int timeout) +agent_init(const char *ifname, const char *rootdisk, int dryrun, int timeout) { struct system_config *sc; int fd, ret; @@ -318,6 +319,7 @@ agent_init(const char *ifname, int dryrun, int timeout) sc->sc_cdrom = "/dev/cd0c"; sc->sc_dryrun = dryrun ? 1 : 0; sc->sc_timeout = agent_timeout = timeout < 1 ? -1 : timeout * 1000; + sc->sc_rootdisk = rootdisk; TAILQ_INIT(&sc->sc_pubkeys); TAILQ_INIT(&sc->sc_netaddrs); @@ -1089,8 +1091,8 @@ usage(void) { extern char *__progname; - fprintf(stderr, "usage: %s [-nuv] [-t 3] [-U puffy] interface\n", - __progname); + fprintf(stderr, "usage: %s [-nuv] [-r rootdisk] [-t 3] [-U puffy] " + "interface\n", __progname); exit(1); } @@ -1130,19 +1132,19 @@ main(int argc, char *const *argv) struct system_config *sc; int verbose = 0, dryrun = 0, unconfigure = 0; int ch, ret, timeout = CONNECT_TIMEOUT; - const char *error = NULL; + const char *error = NULL, *rootdisk = NULL; char *args, *username = NULL; if ((args = get_args(argc, argv)) == NULL) fatalx("failed to save args"); - while ((ch = getopt(argc, argv, "nvt:U:u")) != -1) { + while ((ch = getopt(argc, argv, "nr:t:U:uv")) != -1) { switch (ch) { case 'n': dryrun = 1; break; - case 'v': - verbose += 2; + case 'r': + rootdisk = optarg; break; case 't': timeout = strtonum(optarg, -1, 86400, &error); @@ -1156,6 +1158,9 @@ main(int argc, char *const *argv) case 'u': unconfigure = 1; break; + case 'v': + verbose += 2; + break; default: usage(); } @@ -1176,11 +1181,18 @@ main(int argc, char *const *argv) if (argc != 1) usage(); - if (pledge("stdio cpath rpath wpath exec proc dns inet", NULL) == -1) + if (pledge(rootdisk == NULL ? + "stdio cpath rpath wpath exec proc dns inet" : + "stdio cpath rpath wpath exec proc dns inet disklabel", + NULL) == -1) fatal("pledge"); - if ((sc = agent_init(argv[0], dryrun, timeout)) == NULL) + if ((sc = agent_init(argv[0], rootdisk, dryrun, timeout)) == NULL) fatalx("agent"); + + if (rootdisk != NULL && growdisk(sc) == -1) + fatalx("failed to grow %s", rootdisk); + sc->sc_args = args; if (username != NULL) { free(sc->sc_username); diff --git a/agent/main.h b/agent/main.h index 17fd724..29a59ec 100644 --- a/agent/main.h +++ b/agent/main.h @@ -82,6 +82,7 @@ struct system_config { const char *sc_ovfenv; const char *sc_interface; const char *sc_cdrom; + const char *sc_rootdisk; int sc_mount; struct source sc_addr; @@ -129,6 +130,9 @@ int opennebula(struct system_config *); /* openstack.c */ int openstack(struct system_config *); +/* growdisk.c */ +int growdisk(struct system_config *); + /* main.c */ int shell(const char *, ...); int shellout(const char *, char **, const char *, ...);