summaryrefslogtreecommitdiff
path: root/usr.sbin/vmd
diff options
context:
space:
mode:
authorReyk Floeter <reyk@cvs.openbsd.org>2018-07-15 14:36:55 +0000
committerReyk Floeter <reyk@cvs.openbsd.org>2018-07-15 14:36:55 +0000
commit8cdeb6e4ffa590d62cbbf399393a880b02aa1391 (patch)
tree6c9c3be26c2fd9724f333668c202b922a8f7036f /usr.sbin/vmd
parent46a838c59155c1cb5be7092a1d73a53b3bb51c2d (diff)
Track resources and enforce cpu/memory/interface limits for non-root users.
The limits are currently hard-coded and undocumented (4 CPUs/VMs, 2G memory, 8 interfaces) but will be configurable in an upcoming diff. These limits are tracked in total usage; for example, a user will be able to run up to 4 VMs with 512M of memory or a single VM with 2G. OK ccardenas@ mlarkin@
Diffstat (limited to 'usr.sbin/vmd')
-rw-r--r--usr.sbin/vmd/config.c19
-rw-r--r--usr.sbin/vmd/proc.h3
-rw-r--r--usr.sbin/vmd/vioscsi.c56
-rw-r--r--usr.sbin/vmd/vmd.c114
-rw-r--r--usr.sbin/vmd/vmd.h24
5 files changed, 183 insertions, 33 deletions
diff --git a/usr.sbin/vmd/config.c b/usr.sbin/vmd/config.c
index 5f6db1afba8..ac5f1808a67 100644
--- a/usr.sbin/vmd/config.c
+++ b/usr.sbin/vmd/config.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: config.c,v 1.47 2018/07/13 10:26:57 reyk Exp $ */
+/* $OpenBSD: config.c,v 1.48 2018/07/15 14:36:54 reyk Exp $ */
/*
* Copyright (c) 2015 Reyk Floeter <reyk@openbsd.org>
@@ -68,6 +68,12 @@ config_init(struct vmd *env)
return (-1);
TAILQ_INIT(env->vmd_switches);
}
+ if (what & CONFIG_USERS) {
+ if ((env->vmd_users = calloc(1,
+ sizeof(*env->vmd_users))) == NULL)
+ return (-1);
+ TAILQ_INIT(env->vmd_users);
+ }
return (0);
}
@@ -187,7 +193,16 @@ config_setvm(struct privsep *ps, struct vmd_vm *vm, uint32_t peerid, uid_t uid)
if (vm->vm_running) {
log_warnx("%s: vm is already running", __func__);
errno = EALREADY;
- goto fail;
+ return (-1);
+ }
+
+ /* increase the user reference counter and check user limits */
+ if (vm->vm_user != NULL && user_get(vm->vm_user->usr_id.uid) != NULL) {
+ user_inc(vcp, vm->vm_user, 1);
+ if (user_checklimit(vm->vm_user, vcp) == -1) {
+ errno = EPERM;
+ goto fail;
+ }
}
diskfds = reallocarray(NULL, vcp->vcp_ndisks, sizeof(*diskfds));
diff --git a/usr.sbin/vmd/proc.h b/usr.sbin/vmd/proc.h
index 323b57acbc8..f0e4704aefb 100644
--- a/usr.sbin/vmd/proc.h
+++ b/usr.sbin/vmd/proc.h
@@ -1,4 +1,4 @@
-/* $OpenBSD: proc.h,v 1.13 2018/06/26 10:00:08 reyk Exp $ */
+/* $OpenBSD: proc.h,v 1.14 2018/07/15 14:36:54 reyk Exp $ */
/*
* Copyright (c) 2010-2015 Reyk Floeter <reyk@openbsd.org>
@@ -98,6 +98,7 @@ enum privsep_procid {
#define CONFIG_RELOAD 0x00
#define CONFIG_VMS 0x01
#define CONFIG_SWITCHES 0x02
+#define CONFIG_USERS 0x04
#define CONFIG_ALL 0xff
struct privsep_pipes {
diff --git a/usr.sbin/vmd/vioscsi.c b/usr.sbin/vmd/vioscsi.c
index f8b44a72a85..091136df597 100644
--- a/usr.sbin/vmd/vioscsi.c
+++ b/usr.sbin/vmd/vioscsi.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: vioscsi.c,v 1.7 2018/07/10 20:43:15 reyk Exp $ */
+/* $OpenBSD: vioscsi.c,v 1.8 2018/07/15 14:36:54 reyk Exp $ */
/*
* Copyright (c) 2017 Carlos Cardenas <ccardenas@openbsd.org>
@@ -261,7 +261,7 @@ vioscsi_handle_inquiry(struct vioscsi_dev *dev,
inq = (struct scsi_inquiry *)(req->cdb);
inq_len = (uint16_t)_2btol(inq->length);
- log_debug("%s: INQ - EVPD %d PAGE_CODE 0x%08x LEN %d", __func__,
+ DPRINTF("%s: INQ - EVPD %d PAGE_CODE 0x%08x LEN %d", __func__,
inq->flags & SI_EVPD, inq->pagecode, inq_len);
vioscsi_prepare_resp(&resp,
@@ -341,7 +341,7 @@ vioscsi_handle_mode_sense(struct vioscsi_dev *dev,
mode_page_ctl = mode_sense->page & SMS_PAGE_CTRL;
mode_page_code = mode_sense->page & SMS_PAGE_CODE;
- log_debug("%s: M_SENSE - DBD %d Page Ctrl 0x%x Code 0x%x Len %u",
+ DPRINTF("%s: M_SENSE - DBD %d Page Ctrl 0x%x Code 0x%x Len %u",
__func__, mode_sense->byte2 & SMS_DBD, mode_page_ctl,
mode_page_code, mode_sense->length);
@@ -484,7 +484,7 @@ vioscsi_handle_mode_sense_big(struct vioscsi_dev *dev,
mode_page_code = mode_sense_10->page & SMS_PAGE_CODE;
mode_sense_len = (uint16_t)_2btol(mode_sense_10->length);
- log_debug("%s: M_SENSE_10 - DBD %d Page Ctrl 0x%x Code 0x%x Len %u",
+ DPRINTF("%s: M_SENSE_10 - DBD %d Page Ctrl 0x%x Code 0x%x Len %u",
__func__, mode_sense_10->byte2 & SMS_DBD, mode_page_ctl,
mode_page_code, mode_sense_len);
@@ -620,7 +620,7 @@ vioscsi_handle_read_capacity(struct vioscsi_dev *dev,
memset(&resp, 0, sizeof(resp));
r_cap = (struct scsi_read_capacity *)(req->cdb);
r_cap_addr = _4btol(r_cap->addr);
- log_debug("%s: %s - Addr 0x%08x", __func__,
+ DPRINTF("%s: %s - Addr 0x%08x", __func__,
vioscsi_op_names(r_cap->opcode), r_cap_addr);
vioscsi_prepare_resp(&resp,
@@ -633,7 +633,7 @@ vioscsi_handle_read_capacity(struct vioscsi_dev *dev,
goto read_capacity_out;
}
- log_debug("%s: ISO has %lld bytes and %lld blocks",
+ DPRINTF("%s: ISO has %lld bytes and %lld blocks",
__func__, dev->sz, dev->n_blocks);
/*
@@ -709,7 +709,7 @@ vioscsi_handle_read_capacity_16(struct vioscsi_dev *dev,
memset(&resp, 0, sizeof(resp));
r_cap_16 = (struct scsi_read_capacity_16 *)(req->cdb);
r_cap_addr_16 = _8btol(r_cap_16->addr);
- log_debug("%s: %s - Addr 0x%016llx", __func__,
+ DPRINTF("%s: %s - Addr 0x%016llx", __func__,
vioscsi_op_names(r_cap_16->opcode), r_cap_addr_16);
vioscsi_prepare_resp(&resp, VIRTIO_SCSI_S_OK, SCSI_OK, 0, 0, 0);
@@ -722,7 +722,7 @@ vioscsi_handle_read_capacity_16(struct vioscsi_dev *dev,
goto read_capacity_16_out;
}
- log_debug("%s: ISO has %lld bytes and %lld blocks", __func__,
+ DPRINTF("%s: ISO has %lld bytes and %lld blocks", __func__,
dev->sz, dev->n_blocks);
_lto8b(dev->n_blocks - 1, r_cap_data_16->addr);
@@ -785,11 +785,11 @@ vioscsi_handle_report_luns(struct vioscsi_dev *dev,
rpl = (struct scsi_report_luns *)(req->cdb);
rpl_length = _4btol(rpl->length);
- log_debug("%s: REPORT_LUNS Report 0x%x Length %d", __func__,
+ DPRINTF("%s: REPORT_LUNS Report 0x%x Length %d", __func__,
rpl->selectreport, rpl_length);
if (rpl_length < RPL_MIN_SIZE) {
- log_debug("%s: RPL_Length %d < %d (RPL_MIN_SIZE)", __func__,
+ DPRINTF("%s: RPL_Length %d < %d (RPL_MIN_SIZE)", __func__,
rpl_length, RPL_MIN_SIZE);
vioscsi_prepare_resp(&resp,
@@ -886,12 +886,12 @@ vioscsi_handle_read_6(struct vioscsi_dev *dev,
read_lba = ((read_6->addr[0] & SRW_TOPADDR) << 16 ) |
(read_6->addr[1] << 8) | read_6->addr[2];
- log_debug("%s: READ Addr 0x%08x Len %d (%d)",
+ DPRINTF("%s: READ Addr 0x%08x Len %d (%d)",
__func__, read_lba, read_6->length, read_6->length * dev->max_xfer);
/* check if lba is in range */
if (read_lba > dev->n_blocks - 1) {
- log_debug("%s: requested block out of range req: %ud max: %lld",
+ DPRINTF("%s: requested block out of range req: %ud max: %lld",
__func__, read_lba, dev->n_blocks);
vioscsi_prepare_resp(&resp,
@@ -1017,12 +1017,12 @@ vioscsi_handle_read_10(struct vioscsi_dev *dev,
read_10_len = _2btol(read_10->length);
chunk_offset = 0;
- log_debug("%s: READ_10 Addr 0x%08x Len %d (%d)",
+ DPRINTF("%s: READ_10 Addr 0x%08x Len %d (%d)",
__func__, read_lba, read_10_len, read_10_len * dev->max_xfer);
/* check if lba is in range */
if (read_lba > dev->n_blocks - 1) {
- log_debug("%s: requested block out of range req: %ud max: %lld",
+ DPRINTF("%s: requested block out of range req: %ud max: %lld",
__func__, read_lba, dev->n_blocks);
vioscsi_prepare_resp(&resp,
@@ -1153,9 +1153,9 @@ vioscsi_handle_prevent_allow(struct vioscsi_dev *dev,
vioscsi_prepare_resp(&resp, VIRTIO_SCSI_S_OK, SCSI_OK, 0, 0, 0);
if (dev->locked) {
- log_debug("%s: unlocking medium", __func__);
+ DPRINTF("%s: unlocking medium", __func__);
} else {
- log_debug("%s: locking medium", __func__);
+ DPRINTF("%s: locking medium", __func__);
}
dev->locked = dev->locked ? 0 : 1;
@@ -1187,7 +1187,7 @@ vioscsi_handle_mechanism_status(struct vioscsi_dev *dev,
memset(&resp, 0, sizeof(resp));
mech_status = (struct scsi_mechanism_status *)(req->cdb);
mech_status_len = (uint16_t)_2btol(mech_status->length);
- log_debug("%s: MECH_STATUS Len %u", __func__, mech_status_len);
+ DPRINTF("%s: MECH_STATUS Len %u", __func__, mech_status_len);
mech_status_header = calloc(1, sizeof(*mech_status_header));
@@ -1247,7 +1247,7 @@ vioscsi_handle_read_toc(struct vioscsi_dev *dev,
memset(&resp, 0, sizeof(resp));
toc = (struct scsi_read_toc *)(req->cdb);
toc_len = (uint16_t)_2btol(toc->data_len);
- log_debug("%s: %s - MSF %d Track 0x%02x Addr 0x%04x",
+ DPRINTF("%s: %s - MSF %d Track 0x%02x Addr 0x%04x",
__func__, vioscsi_op_names(toc->opcode),
((toc->byte2 >> 1) & 1), toc->from_track, toc_len);
@@ -1257,7 +1257,7 @@ vioscsi_handle_read_toc(struct vioscsi_dev *dev,
if (toc->from_track > 1 &&
toc->from_track != READ_TOC_LEAD_OUT_TRACK) {
/* illegal request */
- log_debug("%s: illegal request Track 0x%02x",
+ DPRINTF("%s: illegal request Track 0x%02x",
__func__, toc->from_track);
vioscsi_prepare_resp(&resp,
@@ -1385,7 +1385,7 @@ vioscsi_handle_read_disc_info(struct vioscsi_dev *dev,
memset(&resp, 0, sizeof(resp));
read_disc =
(struct scsi_read_disc_information *)(req->cdb);
- log_debug("%s: Disc Info %x", __func__, read_disc->byte2);
+ DPRINTF("%s: Disc Info %x", __func__, read_disc->byte2);
/* send back unsupported */
vioscsi_prepare_resp(&resp,
@@ -1424,7 +1424,7 @@ vioscsi_handle_gesn(struct vioscsi_dev *dev,
memset(&resp, 0, sizeof(resp));
gesn = (struct scsi_gesn *)(req->cdb);
- log_debug("%s: GESN Method %s", __func__,
+ DPRINTF("%s: GESN Method %s", __func__,
gesn->byte2 ? "Polling" : "Asynchronous");
if (gesn->byte2 == 0) {
@@ -1530,7 +1530,7 @@ vioscsi_handle_get_config(struct vioscsi_dev *dev,
get_configuration = (struct scsi_get_configuration *)(req->cdb);
get_conf_feature = (uint16_t)_2btol(get_configuration->feature);
get_conf_len = (uint16_t)_2btol(get_configuration->length);
- log_debug("%s: Conf RT %x Feature %d Len %d", __func__,
+ DPRINTF("%s: Conf RT %x Feature %d Len %d", __func__,
get_configuration->byte2, get_conf_feature, get_conf_len);
get_conf_reply = (uint8_t*)calloc(G_CONFIG_REPLY_SIZE, sizeof(uint8_t));
@@ -1664,7 +1664,7 @@ vioscsi_io(int dir, uint16_t reg, uint32_t *data, uint8_t *intr,
*intr = 0xFF;
- log_debug("%s: request %s reg %u,%s sz %u", __func__,
+ DPRINTF("%s: request %s reg %u,%s sz %u", __func__,
dir ? "READ" : "WRITE", reg, vioscsi_reg_name(reg), sz);
if (dir == 0) {
@@ -1677,7 +1677,7 @@ vioscsi_io(int dir, uint16_t reg, uint32_t *data, uint8_t *intr,
break;
case VIRTIO_CONFIG_GUEST_FEATURES:
dev->cfg.guest_feature = *data;
- log_debug("%s: guest feature set to %u",
+ DPRINTF("%s: guest feature set to %u",
__func__, dev->cfg.guest_feature);
break;
case VIRTIO_CONFIG_QUEUE_ADDRESS:
@@ -1695,10 +1695,10 @@ vioscsi_io(int dir, uint16_t reg, uint32_t *data, uint8_t *intr,
break;
case VIRTIO_CONFIG_DEVICE_STATUS:
dev->cfg.device_status = *data;
- log_debug("%s: device status set to %u",
+ DPRINTF("%s: device status set to %u",
__func__, dev->cfg.device_status);
if (dev->cfg.device_status == 0) {
- log_debug("%s: device reset", __func__);
+ DPRINTF("%s: device reset", __func__);
dev->cfg.guest_feature = 0;
dev->cfg.queue_address = 0;
vioscsi_update_qa(dev);
@@ -2156,7 +2156,7 @@ vioscsi_notifyq(struct vioscsi_dev *dev)
* respond with a BAD_TARGET response.
*/
if (req.lun[1] >= VIOSCSI_MAX_TARGET || req.lun[3] > 0) {
- log_debug("%s: Ignore CMD 0x%02x,%s on lun %u:%u:%u:%u",
+ DPRINTF("%s: Ignore CMD 0x%02x,%s on lun %u:%u:%u:%u",
__func__, req.cdb[0], vioscsi_op_names(req.cdb[0]),
req.lun[0], req.lun[1], req.lun[2], req.lun[3]);
/* Move index for response */
@@ -2187,7 +2187,7 @@ vioscsi_notifyq(struct vioscsi_dev *dev)
goto next_msg;
}
- log_debug("%s: Queue %d id 0x%llx lun %u:%u:%u:%u"
+ DPRINTF("%s: Queue %d id 0x%llx lun %u:%u:%u:%u"
" cdb OP 0x%02x,%s",
__func__, dev->cfg.queue_notify, req.id,
req.lun[0], req.lun[1], req.lun[2], req.lun[3],
diff --git a/usr.sbin/vmd/vmd.c b/usr.sbin/vmd/vmd.c
index da184629dc5..2b07a8463b7 100644
--- a/usr.sbin/vmd/vmd.c
+++ b/usr.sbin/vmd/vmd.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: vmd.c,v 1.97 2018/07/13 10:26:57 reyk Exp $ */
+/* $OpenBSD: vmd.c,v 1.98 2018/07/15 14:36:54 reyk Exp $ */
/*
* Copyright (c) 2015 Reyk Floeter <reyk@openbsd.org>
@@ -36,6 +36,7 @@
#include <signal.h>
#include <syslog.h>
#include <unistd.h>
+#include <util.h>
#include <ctype.h>
#include <pwd.h>
#include <grp.h>
@@ -1090,6 +1091,9 @@ vm_stop(struct vmd_vm *vm, int keeptty, const char *caller)
vm->vm_running = 0;
vm->vm_shutdown = 0;
+ user_inc(&vm->vm_params.vmc_params, vm->vm_user, 0);
+ user_put(vm->vm_user);
+
if (vm->vm_iev.ibuf.fd != -1) {
event_del(&vm->vm_iev.ev);
close(vm->vm_iev.ibuf.fd);
@@ -1140,6 +1144,7 @@ vm_remove(struct vmd_vm *vm, const char *caller)
TAILQ_REMOVE(env->vmd_vms, vm, vm_entry);
+ user_put(vm->vm_user);
vm_stop(vm, 0, caller);
free(vm);
}
@@ -1151,6 +1156,7 @@ vm_register(struct privsep *ps, struct vmop_create_params *vmc,
struct vmd_vm *vm = NULL, *vm_parent = NULL;
struct vm_create_params *vcp = &vmc->vmc_params;
struct vmop_owner *vmo = NULL;
+ struct vmd_user *usr = NULL;
uint32_t rng;
unsigned int i;
struct vmd_switch *sw;
@@ -1223,6 +1229,13 @@ vm_register(struct privsep *ps, struct vmop_create_params *vmc,
}
}
+ /* track active users */
+ if (uid != 0 && env->vmd_users != NULL &&
+ (usr = user_get(uid)) == NULL) {
+ log_warnx("could not add user");
+ goto fail;
+ }
+
if ((vm = calloc(1, sizeof(*vm))) == NULL)
goto fail;
@@ -1233,6 +1246,7 @@ vm_register(struct privsep *ps, struct vmop_create_params *vmc,
vm->vm_tty = -1;
vm->vm_receive_fd = -1;
vm->vm_paused = 0;
+ vm->vm_user = usr;
for (i = 0; i < vcp->vcp_ndisks; i++)
vm->vm_disks[i] = -1;
@@ -1765,6 +1779,104 @@ switch_getbyname(const char *name)
return (NULL);
}
+struct vmd_user *
+user_get(uid_t uid)
+{
+ struct vmd_user *usr;
+
+ if (uid == 0)
+ return (NULL);
+
+ /* first try to find an existing user */
+ TAILQ_FOREACH(usr, env->vmd_users, usr_entry) {
+ if (usr->usr_id.uid == uid)
+ goto done;
+ }
+
+ if ((usr = calloc(1, sizeof(*usr))) == NULL) {
+ log_warn("could not allocate user");
+ return (NULL);
+ }
+
+ usr->usr_id.uid = uid;
+ usr->usr_id.gid = -1;
+ TAILQ_INSERT_TAIL(env->vmd_users, usr, usr_entry);
+
+ done:
+ DPRINTF("%s: uid %d #%d +",
+ __func__, usr->usr_id.uid, usr->usr_refcnt + 1);
+ usr->usr_refcnt++;
+
+ return (usr);
+}
+
+void
+user_put(struct vmd_user *usr)
+{
+ if (usr == NULL)
+ return;
+
+ DPRINTF("%s: uid %d #%d -",
+ __func__, usr->usr_id.uid, usr->usr_refcnt - 1);
+
+ if (--usr->usr_refcnt > 0)
+ return;
+
+ TAILQ_REMOVE(env->vmd_users, usr, usr_entry);
+ free(usr);
+}
+
+void
+user_inc(struct vm_create_params *vcp, struct vmd_user *usr, int inc)
+{
+ char mem[FMT_SCALED_STRSIZE];
+
+ if (usr == NULL)
+ return;
+
+ /* increment or decrement counters */
+ inc = inc ? 1 : -1;
+
+ usr->usr_maxcpu += vcp->vcp_ncpus * inc;
+ usr->usr_maxmem += vcp->vcp_memranges[0].vmr_size * inc;
+ usr->usr_maxifs += vcp->vcp_nnics * inc;
+
+ if (log_getverbose() > 1) {
+ (void)fmt_scaled(usr->usr_maxmem * 1024 * 1024, mem);
+ log_debug("%s: %c uid %d ref %d cpu %llu mem %s ifs %llu",
+ __func__, inc == 1 ? '+' : '-',
+ usr->usr_id.uid, usr->usr_refcnt,
+ usr->usr_maxcpu, mem, usr->usr_maxifs);
+ }
+}
+
+int
+user_checklimit(struct vmd_user *usr, struct vm_create_params *vcp)
+{
+ const char *limit = "";
+
+ /* XXX make the limits configurable */
+ if (usr->usr_maxcpu > VM_DEFAULT_USER_MAXCPU) {
+ limit = "cpu ";
+ goto fail;
+ }
+ if (usr->usr_maxcpu > VM_DEFAULT_USER_MAXMEM) {
+ limit = "memory ";
+ goto fail;
+ }
+ if (usr->usr_maxifs > VM_DEFAULT_USER_MAXIFS) {
+ limit = "interface ";
+ goto fail;
+ }
+
+ return (0);
+
+ fail:
+ log_warnx("%s: user %d %slimit reached", vcp->vcp_name,
+ usr->usr_id.uid, limit);
+ return (-1);
+}
+
char *
get_string(uint8_t *ptr, size_t len)
{
diff --git a/usr.sbin/vmd/vmd.h b/usr.sbin/vmd/vmd.h
index bf670f5eb95..2b83bb42c71 100644
--- a/usr.sbin/vmd/vmd.h
+++ b/usr.sbin/vmd/vmd.h
@@ -1,4 +1,4 @@
-/* $OpenBSD: vmd.h,v 1.77 2018/07/13 10:26:57 reyk Exp $ */
+/* $OpenBSD: vmd.h,v 1.78 2018/07/15 14:36:54 reyk Exp $ */
/*
* Copyright (c) 2015 Mike Larkin <mlarkin@openbsd.org>
@@ -54,6 +54,11 @@
#define VMD_SWITCH_TYPE "bridge"
#define VM_DEFAULT_MEMORY 512
+/* default user instance limits */
+#define VM_DEFAULT_USER_MAXCPU 4
+#define VM_DEFAULT_USER_MAXMEM 2048
+#define VM_DEFAULT_USER_MAXIFS 8
+
/* vmd -> vmctl error codes */
#define VMD_BIOS_MISSING 1001
#define VMD_DISK_MISSING 1002
@@ -243,11 +248,23 @@ struct vmd_vm {
int vm_received;
int vm_paused;
int vm_receive_fd;
+ struct vmd_user *vm_user;
TAILQ_ENTRY(vmd_vm) vm_entry;
};
TAILQ_HEAD(vmlist, vmd_vm);
+struct vmd_user {
+ struct vmop_owner usr_id;
+ uint64_t usr_maxcpu;
+ uint64_t usr_maxmem;
+ uint64_t usr_maxifs;
+ int usr_refcnt;
+
+ TAILQ_ENTRY(vmd_user) usr_entry;
+};
+TAILQ_HEAD(userlist, vmd_user);
+
struct address {
struct sockaddr_storage ss;
int prefixlen;
@@ -274,6 +291,7 @@ struct vmd {
struct vmlist *vmd_vms;
uint32_t vmd_nswitches;
struct switchlist *vmd_switches;
+ struct userlist *vmd_users;
int vmd_fd;
int vmd_ptmfd;
@@ -329,6 +347,10 @@ int vm_opentty(struct vmd_vm *);
void vm_closetty(struct vmd_vm *);
void switch_remove(struct vmd_switch *);
struct vmd_switch *switch_getbyname(const char *);
+struct vmd_user *user_get(uid_t);
+void user_put(struct vmd_user *);
+void user_inc(struct vm_create_params *, struct vmd_user *, int);
+int user_checklimit(struct vmd_user *, struct vm_create_params *);
char *get_string(uint8_t *, size_t);
uint32_t prefixlen2mask(uint8_t);