Allow to use configured/running VMs as templates for other VM instances.
authorreyk <reyk@openbsd.org>
Thu, 12 Jul 2018 12:04:49 +0000 (12:04 +0000)
committerreyk <reyk@openbsd.org>
Thu, 12 Jul 2018 12:04:49 +0000 (12:04 +0000)
This introduces new grammar and the -t optional in vmctl start.

(For now, only root can create VM instances; but it is planned to allow
users to create their own VMs based on permissions and quota.)

OK ccardenas@ mlarkin@ jmc@

usr.sbin/vmctl/main.c
usr.sbin/vmctl/vmctl.8
usr.sbin/vmctl/vmctl.c
usr.sbin/vmctl/vmctl.h
usr.sbin/vmd/parse.y
usr.sbin/vmd/vm.conf.5
usr.sbin/vmd/vmd.c
usr.sbin/vmd/vmd.h
usr.sbin/vmd/vmm.c

index 7b288d5..2ab74c8 100644 (file)
@@ -1,4 +1,4 @@
-/*     $OpenBSD: main.c,v 1.37 2018/07/11 13:19:47 reyk Exp $  */
+/*     $OpenBSD: main.c,v 1.38 2018/07/12 12:04:49 reyk Exp $  */
 
 /*
  * Copyright (c) 2015 Reyk Floeter <reyk@openbsd.org>
@@ -71,7 +71,7 @@ struct ctl_command ctl_commands[] = {
        { "show",       CMD_STATUS,     ctl_status,     "[id]" },
        { "start",      CMD_START,      ctl_start,      "\"name\""
            " [-Lc] [-b image] [-r image] [-m size]\n"
-           "\t\t[-n switch] [-i count] [-d disk]*" },
+           "\t\t[-n switch] [-i count] [-d disk]* [-I name]" },
        { "status",     CMD_STATUS,     ctl_status,     "[id]" },
        { "stop",       CMD_STOP,       ctl_stop,       "id [-fw]" },
        { "pause",      CMD_PAUSE,      ctl_pause,      "id" },
@@ -206,7 +206,7 @@ vmmaction(struct parse_result *res)
        case CMD_START:
                ret = vm_start(res->id, res->name, res->size, res->nifs,
                    res->nets, res->ndisks, res->disks, res->path,
-                   res->isopath);
+                   res->isopath, res->instance);
                if (ret) {
                        errno = ret;
                        err(1, "start VM operation failed");
@@ -330,6 +330,7 @@ parse_free(struct parse_result *res)
        free(res->name);
        free(res->path);
        free(res->isopath);
+       free(res->instance);
        for (i = 0; i < res->ndisks; i++)
                free(res->disks[i]);
        free(res->disks);
@@ -451,6 +452,20 @@ parse_vmid(struct parse_result *res, char *word, int needname)
        return (0);
 }
 
+int
+parse_instance(struct parse_result *res, char *word)
+{
+       if (strlen(word) >= VMM_MAX_NAME_LEN) {
+               warnx("instance vm name too long");
+               return (-1);
+       }
+       res->id = 0;
+       if ((res->instance = strdup(word)) == NULL)
+               errx(1, "strdup");
+
+       return (0);
+}
+
 int
 ctl_create(struct parse_result *res, int argc, char *argv[])
 {
@@ -577,7 +592,7 @@ ctl_start(struct parse_result *res, int argc, char *argv[])
        argc--;
        argv++;
 
-       while ((ch = getopt(argc, argv, "b:r:cLm:n:d:i:")) != -1) {
+       while ((ch = getopt(argc, argv, "b:r:cLm:n:d:I:i:")) != -1) {
                switch (ch) {
                case 'b':
                        if (res->path)
@@ -618,6 +633,10 @@ ctl_start(struct parse_result *res, int argc, char *argv[])
                        if (parse_disk(res, path) != 0)
                                errx(1, "invalid disk: %s", optarg);
                        break;
+               case 'I':
+                       if (parse_instance(res, optarg) == -1)
+                               errx(1, "invalid name: %s", optarg);
+                       break;
                case 'i':
                        if (res->nifs != -1)
                                errx(1, "interfaces specified multiple times");
index 8c1eb11..81b54c1 100644 (file)
@@ -1,4 +1,4 @@
-.\"    $OpenBSD: vmctl.8,v 1.42 2018/07/11 17:21:57 jmc Exp $
+.\"    $OpenBSD: vmctl.8,v 1.43 2018/07/12 12:04:49 reyk Exp $
 .\"
 .\" Copyright (c) 2015 Mike Larkin <mlarkin@openbsd.org>
 .\"
@@ -14,7 +14,7 @@
 .\" ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
 .\" OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 .\"
-.Dd $Mdocdate: July 11 2018 $
+.Dd $Mdocdate: July 12 2018 $
 .Dt VMCTL 8
 .Os
 .Sh NAME
@@ -94,10 +94,11 @@ command.
 .Op Fl n Ar switch
 .Bk -words
 .Op Fl r Ar path
+.Op Fl t Ar name
 .Ek
 .Xc
 Starts a VM defined by the specified name and parameters:
-.Bl -tag -width "-i count"
+.Bl -tag -width "-I parent"
 .It Fl b Ar path
 Boot the VM with the specified kernel or BIOS image.
 If not specified, the default is to boot using the BIOS image in
@@ -139,6 +140,13 @@ This image file will be available in the
 selected VM as a SCSI CD-ROM device attached to a virtio SCSI adapter
 (e.g.\&
 .Xr vioscsi 4 ) .
+.It Fl t Ar name
+Use an existing VM with the specified
+.Ar name
+as a template to create a new VM instance.
+The instance will inherit settings from the parent VM,
+except for exclusive options such as disk, interface lladdr, or
+interface names.
 .El
 .Pp
 Note that the VM name supplied to the 'start' command can only consist of
@@ -287,6 +295,12 @@ Create a new VM with 1GB memory, one network interface, one disk image
 # vmctl start "myvm" -m 1G -i 1 -b /bsd -d disk.img
 .Ed
 .Pp
+Start a new VM instance with the name 'myvm' from a pre-configured
+VM 'openbsd.4G':
+.Bd -literal -offset indent
+# vmctl start "myvm" -t "openbsd.4G" -d mydisk.img
+.Ed
+.Pp
 .Xr vmd 8
 will create a new
 .Xr tap 4
index c6dec18..7ab9a9b 100644 (file)
@@ -1,4 +1,4 @@
-/*     $OpenBSD: vmctl.c,v 1.53 2018/07/11 21:29:05 reyk Exp $ */
+/*     $OpenBSD: vmctl.c,v 1.54 2018/07/12 12:04:49 reyk Exp $ */
 
 /*
  * Copyright (c) 2014 Mike Larkin <mlarkin@openbsd.org>
@@ -62,6 +62,7 @@ int info_console;
  *  disks: disk image file names
  *  kernel: kernel image to load
  *  iso: iso image file
+ *  instance: create instance from vm
  *
  * Return:
  *  0 if the request to start the VM was sent successfully.
@@ -69,7 +70,8 @@ int info_console;
  */
 int
 vm_start(uint32_t start_id, const char *name, int memsize, int nnics,
-    char **nics, int ndisks, char **disks, char *kernel, char *iso)
+    char **nics, int ndisks, char **disks, char *kernel, char *iso,
+    char *instance)
 {
        struct vmop_create_params *vmc;
        struct vm_create_params *vcp;
@@ -87,7 +89,9 @@ vm_start(uint32_t start_id, const char *name, int memsize, int nnics,
                flags |= VMOP_CREATE_KERNEL;
        if (iso)
                flags |= VMOP_CREATE_CDROM;
-       if (flags != 0) {
+       if (instance)
+               flags |= VMOP_CREATE_INSTANCE;
+       else if (flags != 0) {
                if (memsize < 1)
                        memsize = VM_DEFAULT_MEMORY;
                if (ndisks > VMM_MAX_DISKS_PER_VM)
@@ -172,6 +176,10 @@ vm_start(uint32_t start_id, const char *name, int memsize, int nnics,
                if (strlcpy(vcp->vcp_cdrom, iso,
                    sizeof(vcp->vcp_cdrom)) >= sizeof(vcp->vcp_cdrom))
                        errx(1, "cdrom name too long");
+       if (instance != NULL)
+               if (strlcpy(vmc->vmc_instance, instance,
+                   sizeof(vmc->vmc_instance)) >= sizeof(vmc->vmc_instance))
+                       errx(1, "instance vm name too long");
 
        imsg_compose(ibuf, IMSG_VMDOP_START_VM_REQUEST, 0, 0, -1,
            vmc, sizeof(struct vmop_create_params));
@@ -219,7 +227,7 @@ vm_start_complete(struct imsg *imsg, int *ret, int autoconnect)
                                *ret = ENOENT;
                                break;
                        case VMD_DISK_MISSING:
-                               warnx("could not open specified disk image(s)");
+                               warnx("could not open disk image(s)");
                                *ret = ENOENT;
                                break;
                        case VMD_DISK_INVALID:
index 7fc8af8..91ade10 100644 (file)
@@ -1,4 +1,4 @@
-/*     $OpenBSD: vmctl.h,v 1.20 2018/07/11 13:19:47 reyk Exp $ */
+/*     $OpenBSD: vmctl.h,v 1.21 2018/07/12 12:04:49 reyk Exp $ */
 
 /*
  * Copyright (c) 2015 Reyk Floeter <reyk@openbsd.org>
@@ -53,6 +53,7 @@ struct parse_result {
        size_t                   ndisks;
        char                    **disks;
        int                      verbose;
+       char                    *instance;
        unsigned int             flags;
        unsigned int             mode;
        struct ctl_command      *ctl;
@@ -75,6 +76,7 @@ int    parse_network(struct parse_result *, char *);
 int     parse_size(struct parse_result *, char *, long long);
 int     parse_disk(struct parse_result *, char *);
 int     parse_vmid(struct parse_result *, char *, int);
+int     parse_instance(struct parse_result *, char *);
 void    parse_free(struct parse_result *);
 int     parse(int, char *[]);
 __dead void
@@ -83,7 +85,7 @@ __dead void
 /* vmctl.c */
 int     create_imagefile(const char *, long);
 int     vm_start(uint32_t, const char *, int, int, char **, int,
-           char **, char *, char *);
+           char **, char *, char *, char *);
 int     vm_start_complete(struct imsg *, int *, int);
 void    terminate_vm(uint32_t, const char *, unsigned int);
 int     terminate_vm_complete(struct imsg *, int *, unsigned int);
index 305a603..a17079c 100644 (file)
@@ -1,4 +1,4 @@
-/*     $OpenBSD: parse.y,v 1.40 2018/07/11 16:43:24 reyk Exp $ */
+/*     $OpenBSD: parse.y,v 1.41 2018/07/12 12:04:49 reyk Exp $ */
 
 /*
  * Copyright (c) 2007-2016 Reyk Floeter <reyk@openbsd.org>
@@ -93,7 +93,6 @@ int            parse_disk(char *);
 static struct vmop_create_params vmc;
 static struct vm_create_params *vcp;
 static struct vmd_switch       *vsw;
-static struct vmd_vm           *vm;
 static char                     vsw_type[IF_NAMESIZE];
 static int                      vcp_disable;
 static size_t                   vcp_nnics;
@@ -118,7 +117,7 @@ typedef struct {
 
 
 %token INCLUDE ERROR
-%token ADD BOOT CDROM DISABLE DISK DOWN ENABLE GROUP INTERFACE LLADDR
+%token ADD BOOT CDROM DISABLE DISK DOWN ENABLE GROUP INSTANCE INTERFACE LLADDR
 %token LOCAL LOCKED MEMORY NIFS OWNER PATH PREFIX RDOMAIN SIZE SOCKET SWITCH
 %token UP VM VMID
 %token <v.number>      NUMBER
@@ -131,6 +130,7 @@ typedef struct {
 %type  <v.owner>       owner_id
 %type  <v.string>      optstring
 %type  <v.string>      string
+%type  <v.string>      vm_instance
 
 %%
 
@@ -279,22 +279,42 @@ switch_opts       : disable                       {
                }
                ;
 
-vm             : VM string                     {
+vm             : VM string vm_instance         {
                        unsigned int     i;
+                       char            *name;
 
                        memset(&vmc, 0, sizeof(vmc));
                        vcp = &vmc.vmc_params;
                        vcp_disable = 0;
                        vcp_nnics = 0;
 
+                       if ($3 != NULL) {
+                               /* This is an instance of a pre-configured VM */
+                               if (strlcpy(vmc.vmc_instance, $2,
+                                   sizeof(vmc.vmc_instance)) >=
+                                   sizeof(vmc.vmc_instance)) {
+                                       yyerror("vm %s name too long", $2);
+                                       free($2);
+                                       free($3);
+                                       YYERROR;
+                               }
+                               
+                               free($2);
+                               name = $3;
+                               vmc.vmc_flags |= VMOP_CREATE_INSTANCE;
+                       } else
+                               name = $2;
+
                        for (i = 0; i < VMM_MAX_NICS_PER_VM; i++) {
                                /* Set the interface to UP by default */
                                vmc.vmc_ifflags[i] |= IFF_UP;
                        }
 
-                       if (strlcpy(vcp->vcp_name, $2, sizeof(vcp->vcp_name)) >=
-                           sizeof(vcp->vcp_name)) {
+                       if (strlcpy(vcp->vcp_name, name,
+                           sizeof(vcp->vcp_name)) >= sizeof(vcp->vcp_name)) {
                                yyerror("vm name too long");
+                               free($2);
+                               free($3);
                                YYERROR;
                        }
 
@@ -302,7 +322,8 @@ vm          : VM string                     {
                        vmc.vmc_uid = 0;
                        vmc.vmc_gid = -1;
                } '{' optnl vm_opts_l '}'       {
-                       int ret;
+                       struct vmd_vm   *vm;
+                       int              ret;
 
                        /* configured interfaces vs. number of interfaces */
                        if (vcp_nnics > vcp->vcp_nnics)
@@ -318,9 +339,8 @@ vm          : VM string                     {
                                            vcp->vcp_name, vm->vm_running ?
                                            "running" : "already exists");
                                } else if (ret == -1) {
-                                       log_warn("%s:%d: vm \"%s\" failed",
-                                           file->name, yylval.lineno,
-                                           vcp->vcp_name);
+                                       yyerror("vm \"%s\" failed: %s",
+                                           vcp->vcp_name, strerror(errno));
                                        YYERROR;
                                } else {
                                        if (vcp_disable)
@@ -337,6 +357,10 @@ vm         : VM string                     {
                }
                ;
 
+vm_instance    : /* empty */                   { $$ = NULL; }
+               | INSTANCE string               { $$ = $2; }
+               ;
+
 vm_opts_l      : vm_opts_l vm_opts nl
                | vm_opts optnl
                ;
@@ -673,6 +697,7 @@ lookup(char *s)
                { "group",              GROUP },
                { "id",                 VMID },
                { "include",            INCLUDE },
+               { "instance",           INSTANCE },
                { "interface",          INTERFACE },
                { "interfaces",         NIFS },
                { "lladdr",             LLADDR },
@@ -1163,12 +1188,19 @@ parse_size(char *word, int64_t val)
 int
 parse_disk(char *word)
 {
+       char    path[PATH_MAX];
+
        if (vcp->vcp_ndisks >= VMM_MAX_DISKS_PER_VM) {
                log_warnx("too many disks");
                return (-1);
        }
 
-       if (strlcpy(vcp->vcp_disks[vcp->vcp_ndisks], word,
+       if (realpath(word, path) == NULL) {
+               log_warn("disk %s", word);
+               return (-1);
+       }
+
+       if (strlcpy(vcp->vcp_disks[vcp->vcp_ndisks], path,
            VMM_MAX_PATH_DISK) >= VMM_MAX_PATH_DISK) {
                log_warnx("disk path too long");
                return (-1);
index a73a561..b16bda1 100644 (file)
@@ -1,4 +1,4 @@
-.\" $OpenBSD: vm.conf.5,v 1.31 2018/06/26 11:34:25 jmc Exp $
+.\" $OpenBSD: vm.conf.5,v 1.32 2018/07/12 12:04:49 reyk Exp $
 .\"
 .\" Copyright (c) 2015 Mike Larkin <mlarkin@openbsd.org>
 .\" Copyright (c) 2015 Reyk Floeter <reyk@openbsd.org>
@@ -15,7 +15,7 @@
 .\" ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
 .\" OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 .\"
-.Dd $Mdocdate: June 26 2018 $
+.Dd $Mdocdate: July 12 2018 $
 .Dt VM.CONF 5
 .Os
 .Sh NAME
@@ -121,6 +121,16 @@ section starts with a declaration of the virtual machine
 The name can be any alphanumeric string along with '.', '-', and '_' characters.
 However, it cannot start with '.', '-', or '_'.
 Typically the name is a hostname.
+.It Ic vm Ar parent Ic instance Ar name Brq ...
+A virtual machine can be created as an instance of any other
+configured VM.
+The new instance will inherit settings from the VM
+.Ar parent ,
+except for exclusive options such as
+.Ic disk ,
+.Ic interface lladdr ,
+or
+.Ic interface name .
 .El
 .Pp
 Followed by a block of parameters that is enclosed in curly brackets:
@@ -317,6 +327,14 @@ vm "vm2.example.com" {
 }
 .Ed
 .Pp
+Create a new VM as an instance from
+.Sq vm2.example.com :
+.Bd -literal -offset indent
+vm "vm2.example.com" instance "vm3.example.com" {
+       disk "/home/joe/vm3-disk.img"
+}
+.Ed
+.Pp
 Create the switch "uplink" with an additional physical network interface:
 .Bd -literal -offset indent
 switch "uplink" {
index db1d05e..7fcef0d 100644 (file)
@@ -1,4 +1,4 @@
-/*     $OpenBSD: vmd.c,v 1.94 2018/07/11 16:37:31 reyk Exp $   */
+/*     $OpenBSD: vmd.c,v 1.95 2018/07/12 12:04:49 reyk Exp $   */
 
 /*
  * Copyright (c) 2015 Reyk Floeter <reyk@openbsd.org>
@@ -58,6 +58,9 @@ int    vmd_dispatch_control(int, struct privsep_proc *, struct imsg *);
 int     vmd_dispatch_vmm(int, struct privsep_proc *, struct imsg *);
 int     vmd_check_vmh(struct vm_dump_header *);
 
+int     vm_instance(struct privsep *, struct vmd_vm **,
+           struct vmop_create_params *, uid_t);
+
 struct vmd     *env;
 
 static struct privsep_proc procs[] = {
@@ -70,6 +73,7 @@ static struct privsep_proc procs[] = {
 /* For the privileged process */
 static struct privsep_proc *proc_priv = &procs[0];
 static struct passwd proc_privpw;
+static const uint8_t zero_mac[ETHER_ADDR_LEN];
 
 int
 vmd_dispatch_control(int fd, struct privsep_proc *p, struct imsg *imsg)
@@ -1140,14 +1144,17 @@ int
 vm_register(struct privsep *ps, struct vmop_create_params *vmc,
     struct vmd_vm **ret_vm, uint32_t id, uid_t uid)
 {
-       struct vmd_vm           *vm = NULL;
+       struct vmd_vm           *vm = NULL, *vm_parent = NULL;
        struct vm_create_params *vcp = &vmc->vmc_params;
-       static const uint8_t     zero_mac[ETHER_ADDR_LEN];
        uint32_t                 rng;
        unsigned int             i;
        struct vmd_switch       *sw;
        char                    *s;
 
+       /* Check if this is an instance of another VM */
+       if (vm_instance(ps, &vm_parent, vmc, uid) == -1)
+               return (-1);
+
        errno = 0;
        *ret_vm = NULL;
 
@@ -1162,16 +1169,14 @@ vm_register(struct privsep *ps, struct vmop_create_params *vmc,
                goto fail;
        }
 
-       /*
-        * non-root users can only start existing VMs
-        * XXX there could be a mechanism to allow overriding some options
-        */
-       if (vm_checkperm(NULL, uid) != 0) {
+       /* non-root users can only start existing VMs or instances */
+       if (vm_checkperm(vm_parent, uid) != 0) {
                errno = EPERM;
                goto fail;
        }
        if (vmc->vmc_flags == 0) {
-               errno = ENOENT;
+               log_warnx("invalid configuration, no devices");
+               errno = VMD_DISK_MISSING;
                goto fail;
        }
        if (vcp->vcp_ncpus == 0)
@@ -1267,6 +1272,153 @@ vm_register(struct privsep *ps, struct vmop_create_params *vmc,
        return (-1);
 }
 
+int
+vm_instance(struct privsep *ps, struct vmd_vm **vm_parent,
+    struct vmop_create_params *vmc, uid_t uid)
+{
+       char                    *name;
+       struct vm_create_params *vcp = &vmc->vmc_params;
+       struct vmop_create_params *vmcp;
+       struct vm_create_params *vcpp;
+       struct vmd_vm           *vm = NULL;
+       unsigned int             i, j;
+       uint32_t                 id;
+
+       /* return without error if the parent is NULL (nothing to inherit) */
+       if ((vmc->vmc_flags & VMOP_CREATE_INSTANCE) == 0 ||
+           (*vm_parent = vm_getbyname(vmc->vmc_instance)) == NULL)
+               return (0);
+
+       errno = 0;
+       vmcp = &(*vm_parent)->vm_params;
+       vcpp = &vmcp->vmc_params;
+
+       /*
+        * Are we allowed to create an instance from this VM?
+        *
+        * XXX This is currently allowed for root but will check *vm_parent
+        * XXX once we defined instance permissions and quota.
+        */
+       if (vm_checkperm(NULL, uid) != 0) {
+               log_warnx("vm \"%s\" no permission to create vm instance",
+                   vcpp->vcp_name);
+               errno = ENAMETOOLONG;
+               return (-1);
+       }
+
+       id = vcp->vcp_id;
+       name = vcp->vcp_name;
+
+       if ((vm = vm_getbyname(vcp->vcp_name)) != NULL ||
+           (vm = vm_getbyvmid(vcp->vcp_id)) != NULL) {
+               errno = EPROCLIM;
+               return (-1);
+       }
+
+       /* machine options */
+       if (vcp->vcp_ncpus == 0)
+               vcp->vcp_ncpus = vcpp->vcp_ncpus;
+       if (vcp->vcp_memranges[0].vmr_size == 0)
+               vcp->vcp_memranges[0].vmr_size =
+                   vcpp->vcp_memranges[0].vmr_size;
+
+       /* disks cannot be inherited */
+       for (i = 0; i < vcp->vcp_ndisks; i++) {
+               /* Check if this disk is already used in the parent */
+               for (j = 0; j < vcpp->vcp_ndisks; j++) {
+                       if (strcmp(vcp->vcp_disks[i],
+                           vcpp->vcp_disks[j]) == 0) {
+                               log_warnx("vm \"%s\" disk %s cannot be reused",
+                                   name, vcp->vcp_disks[i]);
+                               errno = EBUSY;
+                               return (-1);
+                       }
+               }
+       }
+
+       /* interfaces */
+       for (i = 0; i < vcpp->vcp_nnics; i++) {
+               /* Interface got overwritten */
+               if (i < vcp->vcp_nnics)
+                       continue;
+
+               /* Copy interface from parent */
+               vmc->vmc_ifflags[i] = vmcp->vmc_ifflags[i];
+               (void)strlcpy(vmc->vmc_ifnames[i], vmcp->vmc_ifnames[i],
+                   sizeof(vmc->vmc_ifnames[i]));
+               (void)strlcpy(vmc->vmc_ifswitch[i], vmcp->vmc_ifswitch[i],
+                   sizeof(vmc->vmc_ifswitch[i]));
+               (void)strlcpy(vmc->vmc_ifgroup[i], vmcp->vmc_ifgroup[i],
+                   sizeof(vmc->vmc_ifgroup[i]));
+               memcpy(vcp->vcp_macs[i], vcpp->vcp_macs[i],
+                   sizeof(vcp->vcp_macs[i]));
+               vmc->vmc_ifrdomain[i] = vmcp->vmc_ifrdomain[i];
+               vcp->vcp_nnics++;
+       }
+       for (i = 0; i < vcp->vcp_nnics; i++) {
+               for (j = 0; j < vcpp->vcp_nnics; j++) {
+                       if (memcmp(zero_mac, vcp->vcp_macs[i],
+                           sizeof(vcp->vcp_macs[i])) != 0 &&
+                           memcmp(vcpp->vcp_macs[i], vcp->vcp_macs[i],
+                           sizeof(vcp->vcp_macs[i])) != 0) {
+                               log_warnx("vm \"%s\" lladdr cannot be reused",
+                                   name);
+                               errno = EBUSY;
+                               return (-1);
+                       }
+                       if (strlen(vmc->vmc_ifnames[i]) &&
+                           strcmp(vmc->vmc_ifnames[i],
+                           vmcp->vmc_ifnames[j]) == 0) {
+                               log_warnx("vm \"%s\" %s cannot be reused",
+                                   vmc->vmc_ifnames[i], name);
+                               errno = EBUSY;
+                               return (-1);
+                       }
+               }
+       }
+
+       /* kernel */
+       if (strlen(vcp->vcp_kernel) == 0 &&
+           strlcpy(vcp->vcp_kernel, vcpp->vcp_kernel,
+           sizeof(vcp->vcp_kernel)) >= sizeof(vcp->vcp_kernel)) {
+               log_warnx("vm \"%s\" kernel name too long", name);
+               errno = EINVAL;
+               return (-1);
+       }
+
+       /* cdrom */
+       if (strlen(vcp->vcp_cdrom) == 0 &&
+           strlcpy(vcp->vcp_cdrom, vcpp->vcp_cdrom,
+           sizeof(vcp->vcp_cdrom)) >= sizeof(vcp->vcp_cdrom)) {
+               log_warnx("vm \"%s\" cdrom name too long", name);
+               errno = EINVAL;
+               return (-1);
+       }
+
+       /* user */
+       if (vmc->vmc_uid == 0)
+               vmc->vmc_uid = vmcp->vmc_uid;
+       else if (vmc->vmc_uid != vmcp->vmc_uid) {
+               log_warnx("vm \"%s\" user mismatch", name);
+               errno = EPERM;
+               return (-1);
+       }
+
+       /* group */
+       if (vmc->vmc_gid == 0)
+               vmc->vmc_gid = vmcp->vmc_gid;
+       else if (vmc->vmc_gid != vmcp->vmc_gid) {
+               log_warnx("vm \"%s\" group mismatch", name);
+               errno = EPERM;
+               return (-1);
+       }
+
+       /* finished, remove instance flags */
+       vmc->vmc_flags &= ~VMOP_CREATE_INSTANCE;
+
+       return (0);
+}
+
 /*
  * vm_checkperm
  *
index c25f964..0365574 100644 (file)
@@ -1,4 +1,4 @@
-/*     $OpenBSD: vmd.h,v 1.74 2018/07/11 13:19:47 reyk Exp $   */
+/*     $OpenBSD: vmd.h,v 1.75 2018/07/12 12:04:49 reyk Exp $   */
 
 /*
  * Copyright (c) 2015 Mike Larkin <mlarkin@openbsd.org>
@@ -141,6 +141,7 @@ struct vmop_create_params {
 #define VMOP_CREATE_NETWORK    0x04
 #define VMOP_CREATE_DISK       0x08
 #define VMOP_CREATE_CDROM      0x10
+#define VMOP_CREATE_INSTANCE   0x20
 
        /* userland-only part of the create params */
        unsigned int             vmc_ifflags[VMM_MAX_NICS_PER_VM];
@@ -153,6 +154,7 @@ struct vmop_create_params {
        char                     vmc_ifswitch[VMM_MAX_NICS_PER_VM][VM_NAME_MAX];
        char                     vmc_ifgroup[VMM_MAX_NICS_PER_VM][IF_NAMESIZE];
        unsigned int             vmc_ifrdomain[VMM_MAX_NICS_PER_VM];
+       char                     vmc_instance[VMM_MAX_NAME_LEN];
        uid_t                    vmc_uid;
        int64_t                  vmc_gid;
 };
index 9beed72..12ce8cc 100644 (file)
@@ -1,4 +1,4 @@
-/*     $OpenBSD: vmm.c,v 1.86 2018/07/11 13:19:47 reyk Exp $   */
+/*     $OpenBSD: vmm.c,v 1.87 2018/07/12 12:04:49 reyk Exp $   */
 
 /*
  * Copyright (c) 2015 Mike Larkin <mlarkin@openbsd.org>
@@ -284,7 +284,8 @@ vmm_dispatch_parent(int fd, struct privsep_proc *p, struct imsg *imsg)
        case IMSG_VMDOP_RECEIVE_VM_REQUEST:
                IMSG_SIZE_CHECK(imsg, &vmc);
                memcpy(&vmc, imsg->data, sizeof(vmc));
-               ret = vm_register(ps, &vmc, &vm, imsg->hdr.peerid, vmc.vmc_uid);
+               ret = vm_register(ps, &vmc, &vm,
+                   imsg->hdr.peerid, vmc.vmc_uid);
                vm->vm_tty = imsg->fd;
                vm->vm_received = 1;
                break;