/* $NetBSD: xenbus_xs.c,v 1.28 2022/09/01 16:25:18 bouyer Exp $ */
/******************************************************************************
* xenbus_xs.c
*
* This is the kernel equivalent of the "xs" library. We don't need everything
* and we use xenbus_comms for communication.
*
* Copyright (C) 2005 Rusty Russell, IBM Corporation
*
* This file may be distributed separately from the Linux kernel, or
* incorporated into other software packages, subject to the following license:
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this source file (the "Software"), to deal in the Software without
* restriction, including without limitation the rights to use, copy, modify,
* merge, publish, distribute, sublicense, and/or sell copies of the Software,
* and to permit persons to whom the Software is furnished to do so, subject to
* the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
* IN THE SOFTWARE.
*/
struct xs_handle {
/* A list of replies. Currently only one will ever be outstanding. */
SIMPLEQ_HEAD(, xs_stored_msg) reply_list;
kmutex_t reply_lock;
kcondvar_t reply_cv;
kmutex_t xs_lock; /* serialize access to xenstore */
int suspend_spl;
};
static struct xs_handle xs_state;
/* List of registered watches, and a lock to protect it. */
static SLIST_HEAD(, xenbus_watch) watches;
static kmutex_t watches_lock;
/* List of pending watch callback events, and a lock to protect it. */
static SIMPLEQ_HEAD(, xs_stored_msg) watch_events;
static kmutex_t watch_events_lock;
static kcondvar_t watch_cv;
static int
get_error(const char *errorstring)
{
unsigned int i;
for (i = 0; !streq(errorstring, xsd_errors[i].errstring); i++) {
if (i == (sizeof(xsd_errors) / sizeof(xsd_errors[0]) - 1)) {
printf(
"XENBUS xen store gave: unknown error %s",
errorstring);
return EINVAL;
}
}
return xsd_errors[i].errnum;
}
static char **
split(char *strings, unsigned int len, unsigned int *num)
{
char *p, **ret;
/* Count the strings. */
*num = count_strings(strings, len);
/* Transfer to one big alloc for easy freeing. */
ret = malloc(*num * sizeof(char *) + len, M_DEVBUF, M_NOWAIT);
if (!ret) {
free(strings, M_DEVBUF);
return NULL;
}
memcpy(&ret[*num], strings, len);
free(strings, M_DEVBUF);
strings = (char *)&ret[*num];
for (p = strings, *num = 0; p < strings + len; p += strlen(p) + 1)
ret[(*num)++] = p;
return ret;
}
int
xenbus_directory(struct xenbus_transaction *t,
const char *dir, const char *node, unsigned int *num,
char ***retbuf)
{
char *strings, *path;
unsigned int len;
int err;
path = join(dir, node);
if (path == NULL)
return ENOMEM;
void
xenbus_directory_free(unsigned int num, char **dir)
{
free(dir, M_DEVBUF);
}
/* Check if a path exists. Return 1 if it does. */
int
xenbus_exists(struct xenbus_transaction *t,
const char *dir, const char *node)
{
char **d;
int dir_n, err;
/* Get the value of a single file.
* Returns a kmalloced value: call free() on it after use.
* len indicates length in bytes.
*/
int
xenbus_read(struct xenbus_transaction *t,
const char *dir, const char *node,
char *buffer, size_t bufsz)
{
char *path;
int err;
char *ret;
unsigned int len;
path = join(dir, node);
if (path == NULL)
return ENOMEM;
/* Read a node and convert it to unsigned long. */
int
xenbus_read_ul(struct xenbus_transaction *t,
const char *dir, const char *node, unsigned long *val,
int base)
{
char string[32], *ep;
int err;
/* Read a node and convert it to unsigned long long. */
int
xenbus_read_ull(struct xenbus_transaction *t,
const char *dir, const char *node, unsigned long long *val,
int base)
{
char string[32], *ep;
int err;
/* Write the value of a single file.
* Returns -err on failure.
*/
int
xenbus_write(struct xenbus_transaction *t,
const char *dir, const char *node, const char *string)
{
const char *path;
struct iovec iovec[2];
int ret;
path = join(dir, node);
if (path == NULL)
return ENOMEM;
/* Destroy a file or directory (directories must be empty). */
int xenbus_rm(struct xenbus_transaction *t, const char *dir, const char *node)
{
char *path;
int ret;
path = join(dir, node);
if (path == NULL)
return ENOMEM;
/* Start a transaction: changes by others will not be seen during this
* transaction, and changes will not be visible to others until end.
* MUST BE CALLED AT IPL_TTY !
*/
struct xenbus_transaction *
xenbus_transaction_start(void)
{
char *id_str;
unsigned long id, err;
id = strtoul(id_str, NULL, 0);
free(id_str, M_DEVBUF);
return (struct xenbus_transaction *)id;
}
/* End a transaction.
* If abandon is true, transaction is discarded instead of committed.
* MUST BE CALLED AT IPL_TTY !
*/
int xenbus_transaction_end(struct xenbus_transaction *t, int abort)
{
char abortstr[2];
int err;
if (abort)
strcpy(abortstr, "F");
else
strcpy(abortstr, "T");
SLIST_FOREACH(i, &watches, watch_next) {
if (i == cmp)
return i;
}
return NULL;
}
/* Register callback to watch this node. */
int
register_xenbus_watch(struct xenbus_watch *watch)
{
/* Pointer in ascii is the token. */
char token[sizeof(watch) * 2 + 1];
int err;