| /*
* sai-virt - v-http-api.c
*
* Copyright (C) 2019 - 2026 Andy Green <andy@warmcat.com>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation:
* version 2.1 of the License.
*/
#include <libwebsockets.h>
#include <string.h>
#include "v-private.h"
static saiv_vm_t *
saiv_find_vm(const char *name)
{
lws_start_foreach_dll(struct lws_dll2 *, d, virt.plat_owner.head) {
saiv_plat_t *vp = lws_container_of(d, saiv_plat_t, list);
lws_start_foreach_dll(struct lws_dll2 *, v, vp->vm_owner.head) {
saiv_vm_t *vm = lws_container_of(v, saiv_vm_t, list);
if (!strcmp(vm->name, name))
return vm;
} lws_end_foreach_dll(v);
} lws_end_foreach_dll(d);
return NULL;
}
static int
saiv_http_reply_text(struct lws *wsi, const char *text)
{
uint8_t buf[LWS_PRE + 512], *start = buf + LWS_PRE, *p = start,
*end = buf + sizeof(buf);
size_t len = strlen(text);
if (lws_add_http_header_status(wsi, HTTP_STATUS_OK, &p, end) ||
lws_add_http_header_by_token(wsi, WSI_TOKEN_HTTP_CONTENT_TYPE,
(unsigned char *)"text/plain", 10, &p, end) ||
lws_add_http_header_content_length(wsi, len, &p, end) ||
lws_finalize_http_header(wsi, &p, end))
return -1;
if (lws_write(wsi, start, lws_ptr_diff_size_t(p, start),
LWS_WRITE_HTTP_HEADERS) < 0)
return -1;
if (lws_write(wsi, (uint8_t *)text, len, LWS_WRITE_HTTP_FINAL) !=
(int)len)
return -1;
return -1; /* hang up */
}
int
callback_virt_http(struct lws *wsi, enum lws_callback_reasons reason,
void *user, void *in, size_t len)
{
const char *path;
char vm_id[64];
switch (reason) {
case LWS_CALLBACK_HTTP:
path = (const char *)in;
if (len > 16 && !strncmp(path, "/auto-power-off/", 16)) {
saiv_vm_t *found_vm;
lws_strncpy(vm_id, path + 16, sizeof(vm_id));
lwsl_notice("%s: Received auto-power-off for %s\n", __func__, vm_id);
found_vm = saiv_find_vm(vm_id);
if (!found_vm)
/* builder must not wait for a power-off that won't come */
return saiv_http_reply_text(wsi, "NAK: unknown VM");
/* Delay destruction by 2s so sai-builder can cleanly flush its TCP FIN to sai-server */
lws_sul_schedule(virt.context, 0, &found_vm->sul_destroy,
saiv_vm_destroy_cb, 2 * LWS_US_PER_SEC);
/* sai-builder only proceeds on an "ACK:" reply, like sai-power's */
return saiv_http_reply_text(wsi, "ACK: destroying VM in 2s");
}
if (len > 6 && !strncmp(path, "/stay/", 6)) {
saiv_vm_t *found_vm;
lws_strncpy(vm_id, path + 6, sizeof(vm_id));
lwsl_info("%s: stay request for %s\n", __func__, vm_id);
found_vm = saiv_find_vm(vm_id);
if (found_vm)
/* Extend the safety timeout since the VM is alive and communicating */
lws_sul_schedule(virt.context, 0, &found_vm->sul_timeout,
saiv_vm_timeout_cb, SAIV_VM_STAY_TIMEOUT_US);
else
lwsl_warn("%s: stay request from unknown VM %s\n",
__func__, vm_id);
/* We never return stay = true for ephemeral VMs */
return saiv_http_reply_text(wsi, "0");
}
lws_return_http_status(wsi, HTTP_STATUS_NOT_FOUND, NULL);
return -1;
default:
break;
}
return lws_callback_http_dummy(wsi, reason, user, in, len);
}
const struct lws_protocols virt_protocols[] = {
{
"http-only",
callback_virt_http,
0,
0,
},
{ NULL, NULL, 0, 0 } /* terminator */
};
|