1
0
Fork 0
mirror of https://github.com/warmcat/libwebsockets.git synced 2025-03-09 00:00:04 +01:00

lws_state and system state

Introduce a generic lws_state object with notification handlers
that may be registered in a chain.

Implement one of those in the context to manage the "system state".

Allow other pieces of lws and user code to register notification
handlers on a context list.  Handlers can object to or take over
responsibility to move forward and retry system state changes if
they know that some dependent action must succeed first.

For example if the system time is invalid, we cannot move on to
a state where anything can do tls until that has been corrected.
This commit is contained in:
Andy Green 2019-09-19 09:48:17 +01:00
parent 6a6f365ce7
commit bce1f01370
23 changed files with 1197 additions and 514 deletions

View file

@ -1013,6 +1013,7 @@ if (LWS_WITH_NETWORK)
lib/core-net/pollfd.c
lib/core-net/service.c
lib/core-net/sorted-usec-list.c
lib/core-net/state.c
lib/core-net/stats.c
lib/core-net/wsi.c
lib/core-net/wsi-timeout.c

View file

@ -16,6 +16,36 @@ various scenarios, CC0-licensed (public domain) for cut-and-paste, allow you to
News
----
## `lws_system`: system state and notification handlers
Lws now has the concept of systemwide state held in the context... this is to
manage that there may be multiple steps that need the network before it's possible
for the user code to operate normally. The steps defined are
`CONTEXT_CREATED`, `INITIALIZED`, `IFACE_COLDPLUG`, `DHCP`, `TIME_VALID`, `POLICY_VALID`,
`REGISTERED`, `AUTH1`, `AUTH2`, `OPERATIONAL` and `POLICY_INVALID`. OPERATIONAL is the
state where user code can run normally.
User and other parts of lws can hook notifier callbacks to receive and be able to
veto system state changes, either definitively or because they have been triggered
to perform a step asynchronously and will move the state on themselves when it
completes.
By default just after context creation, lws attempts to move straight to OPERATIONAL.
If no notifier interecepts it, it will succeed to do that and operate in a
backwards-compatible way.
See `READMEs/README.lws_system.md` for details.
## `lws_system`: HAL ops struct
Lws allows you to define a standardized ops struct at context creation time so your
user code can get various information like device serial number without embedding
system-specific code throughout the user code. It can also perform some generic
functions like requesting a device reboot.
See `READMEs/README.lws_system.md` for details.
## Connection Validity tracking
Lws now allows you to apply a policy for how long a network connection may go
@ -30,10 +60,10 @@ that it observed traffic that must mean the connection is passing traffic in
both directions to and from the peer. In the absence of these confirmations
lws will generate PINGs and take PONGs as the indication of validity.
## Async DNS support
## `lws_system`: Async DNS support
Master now provides optional Asynchronous (ie, nonblocking) DNS resolving. Enable
with `-DLWS_WITH_ASYNC_DNS=1` at cmake. This provides a quite sophisticated
with `-DLWS_WITH_SYS_ASYNC_DNS=1` at cmake. This provides a quite sophisticated
ipv4 + ipv6 capable resolver that autodetects the dns server on several platforms
and operates a UDP socket to its port 53 to produce and parse DNS packets
from the event loop. And of course, it's extremely compact.

View file

@ -0,0 +1,76 @@
# `lws_system`
See `include/libwebsockets/lws-system.h` for function and object prototypes.
## System integration api
`lws_system` allows you to set a `system_ops` struct at context creation time,
which can write up some function callbacks for system integration. The goal
is the user code calls these by getting the ops struct pointer from the
context using `lws_system_get_ops(context)` or `lws_system_get_info()` and
so does not spread system dependencies around the user code.
```
typedef struct lws_system_ops {
int (*get_info)(lws_system_item_t i, lws_system_arg_t *arg);
int (*reboot)(void);
int (*set_clock)(lws_usec_t us);
} lws_system_ops_t;
```
### `get_info`
This allows the user code to query some common system values without introducing
any system dependencies in the code itself. The defined item numbers are
|Item|Meaning|
|---|---|
|`LWS_SYSI_HRS_DEVICE_MODEL`|String describing device model|
|`LWS_SYSI_HRS_DEVICE_SERIAL`|String for the device serial number|
|`LWS_SYSI_HRS_FIRMWARE_VERSION`|String describing the current firmware version|
|`LWS_SYSI_HRS_NTP_SERVER`|String with the ntp server address (defaults to pool.ntp.org)|
### `reboot`
Reboots the device
### `set_clock`
Set the system clock to us-resolution Unix time in seconds
## System state and notifiers
Lws implements a state in the context that reflects the readiness of the system
for various steps leading up to normal operation. By default it acts in a
backwards-compatible way and directly reaches the OPERATIONAL state just after
the context is created.
However other pieces of lws, and user, code may define notification handlers
that get called back when the state changes incrementally, and may veto or delay
the changes until work necessary for the new state has completed asynchronously.
The generic states defined are:
|State|Meaning|
|---|---|
|`LWS_SYSTATE_CONTEXT_CREATED`|The context was just created.|
|`LWS_SYSTATE_INITIALIZED`|The vhost protocols have been initialized|
|`LWS_SYSTATE_IFACE_COLDPLUG`|Existing network interfaces have been iterated|
|`LWS_SYSTATE_DHCP`|Network identity is available|
|`LWS_SYSTATE_TIME_VALID`|The system knows the time|
|`LWS_SYSTATE_POLICY_VALID`|If the system needs information about how to act from the net, it has it|
|`LWS_SYSTATE_REGISTERED`|The device has a registered identity|
|`LWS_SYSTATE_AUTH1`|The device identity has produced a time-limited access token|
|`LWS_SYSTATE_AUTH2`|Optional second access token for different services|
|`LWS_SYSTATE_OPERATIONAL`|The system is ready for user code to work normally|
|`LWS_SYSTATE_POLICY_INVALID`|All connections are being dropped because policy information is changing. It will transition back to `LWS_SYSTATE_INITIALIZED` and onward to `OPERATIONAL` again afterwards with the new policy|
### Inserting a notifier
You should create an object `lws_system_notify_link_t` in non-const memory and zero it down.
Set the `notify_cb` member and the `name` member and then register it using either
`lws_system_reg_notifier()` or the `.register_notifier_list`
member of the context creation info struct to make sure it will exist early
enough to see all events. The context creation info method takes a list of
pointers to notify_link structs ending with a NULL entry.

View file

@ -524,11 +524,15 @@ struct lws_pollargs {
struct lws_extension; /* needed even with ws exts disabled for create context */
struct lws_token_limits;
struct lws_protocols;
struct lws_context;
struct lws_tokens;
struct lws_vhost;
struct lws;
#include <libwebsockets/lws-dll2.h>
#include <libwebsockets/lws-timeout-timer.h>
#include <libwebsockets/lws-state.h>
#include <libwebsockets/lws-retry.h>
#include <libwebsockets/lws-system.h>
#include <libwebsockets/lws-detailed-latency.h>
@ -545,7 +549,6 @@ struct lws;
#include <libwebsockets/lws-purify.h>
#include <libwebsockets/lws-misc.h>
#include <libwebsockets/lws-dsh.h>
#include <libwebsockets/lws-timeout-timer.h>
#include <libwebsockets/lws-service.h>
#include <libwebsockets/lws-write.h>
#include <libwebsockets/lws-writeable.h>

View file

@ -225,6 +225,8 @@
struct lws_plat_file_ops;
typedef int (*lws_context_ready_cb_t)(struct lws_context *context);
/** struct lws_context_creation_info - parameters to create context and /or vhost with
*
* This is also used to create vhosts.... if LWS_SERVER_OPTION_EXPLICIT_VHOSTS
@ -692,6 +694,10 @@ struct lws_context_creation_info {
/**< VHOST: optional retry and idle policy to apply to this vhost.
* Currently only the idle parts are applied to the connections.
*/
lws_state_notify_link_t **register_notifier_list;
/**< CONTEXT: NULL, or pointer to an array of notifiers that should
* be registered during context creation, so they can see state change
* events from very early on. The array should end with a NULL. */
/* Add new things just above here ---^
* This is part of the ABI, don't needlessly break compatibility

View file

@ -0,0 +1,341 @@
/*
* libwebsockets - small server side websockets and web server implementation
*
* Copyright (C) 2010 - 2019 Andy Green <andy@warmcat.com>
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (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.
*/
/** \defgroup ll linked-lists
* ##Linked list apis
*
* simple single and doubly-linked lists
*/
///@{
/**
* lws_start_foreach_ll(): linkedlist iterator helper start
*
* \param type: type of iteration, eg, struct xyz *
* \param it: iterator var name to create
* \param start: start of list
*
* This helper creates an iterator and starts a while (it) {
* loop. The iterator runs through the linked list starting at start and
* ends when it gets a NULL.
* The while loop should be terminated using lws_start_foreach_ll().
*/
#define lws_start_foreach_ll(type, it, start)\
{ \
type it = start; \
while (it) {
/**
* lws_end_foreach_ll(): linkedlist iterator helper end
*
* \param it: same iterator var name given when starting
* \param nxt: member name in the iterator pointing to next list element
*
* This helper is the partner for lws_start_foreach_ll() that ends the
* while loop.
*/
#define lws_end_foreach_ll(it, nxt) \
it = it->nxt; \
} \
}
/**
* lws_start_foreach_ll_safe(): linkedlist iterator helper start safe against delete
*
* \param type: type of iteration, eg, struct xyz *
* \param it: iterator var name to create
* \param start: start of list
* \param nxt: member name in the iterator pointing to next list element
*
* This helper creates an iterator and starts a while (it) {
* loop. The iterator runs through the linked list starting at start and
* ends when it gets a NULL.
* The while loop should be terminated using lws_end_foreach_ll_safe().
* Performs storage of next increment for situations where iterator can become invalidated
* during iteration.
*/
#define lws_start_foreach_ll_safe(type, it, start, nxt)\
{ \
type it = start; \
while (it) { \
type next_##it = it->nxt;
/**
* lws_end_foreach_ll_safe(): linkedlist iterator helper end (pre increment storage)
*
* \param it: same iterator var name given when starting
*
* This helper is the partner for lws_start_foreach_ll_safe() that ends the
* while loop. It uses the precreated next_ variable already stored during
* start.
*/
#define lws_end_foreach_ll_safe(it) \
it = next_##it; \
} \
}
/**
* lws_start_foreach_llp(): linkedlist pointer iterator helper start
*
* \param type: type of iteration, eg, struct xyz **
* \param it: iterator var name to create
* \param start: start of list
*
* This helper creates an iterator and starts a while (it) {
* loop. The iterator runs through the linked list starting at the
* address of start and ends when it gets a NULL.
* The while loop should be terminated using lws_start_foreach_llp().
*
* This helper variant iterates using a pointer to the previous linked-list
* element. That allows you to easily delete list members by rewriting the
* previous pointer to the element's next pointer.
*/
#define lws_start_foreach_llp(type, it, start)\
{ \
type it = &(start); \
while (*(it)) {
#define lws_start_foreach_llp_safe(type, it, start, nxt)\
{ \
type it = &(start); \
type next; \
while (*(it)) { \
next = &((*(it))->nxt); \
/**
* lws_end_foreach_llp(): linkedlist pointer iterator helper end
*
* \param it: same iterator var name given when starting
* \param nxt: member name in the iterator pointing to next list element
*
* This helper is the partner for lws_start_foreach_llp() that ends the
* while loop.
*/
#define lws_end_foreach_llp(it, nxt) \
it = &(*(it))->nxt; \
} \
}
#define lws_end_foreach_llp_safe(it) \
it = next; \
} \
}
#define lws_ll_fwd_insert(\
___new_object, /* pointer to new object */ \
___m_list, /* member for next list object ptr */ \
___list_head /* list head */ \
) {\
___new_object->___m_list = ___list_head; \
___list_head = ___new_object; \
}
#define lws_ll_fwd_remove(\
___type, /* type of listed object */ \
___m_list, /* member for next list object ptr */ \
___target, /* object to remove from list */ \
___list_head /* list head */ \
) { \
lws_start_foreach_llp(___type **, ___ppss, ___list_head) { \
if (*___ppss == ___target) { \
*___ppss = ___target->___m_list; \
break; \
} \
} lws_end_foreach_llp(___ppss, ___m_list); \
}
/*
* doubly linked-list
*/
#if defined (LWS_WITH_DEPRECATED_LWS_DLL)
/*
* This is going away in v4.1. You can set the cmake option above to keep it
* around temporarily. Migrate your stuff to the more capable and robust
* lws_dll2 below
*/
struct lws_dll {
struct lws_dll *prev;
struct lws_dll *next;
};
/*
* these all point to the composed list objects... you have to use the
* lws_container_of() helper to recover the start of the containing struct
*/
#define lws_dll_add_front lws_dll_add_head
LWS_VISIBLE LWS_EXTERN void
lws_dll_add_head(struct lws_dll *d, struct lws_dll *phead);
LWS_VISIBLE LWS_EXTERN void
lws_dll_add_tail(struct lws_dll *d, struct lws_dll *phead);
LWS_VISIBLE LWS_EXTERN void
lws_dll_insert(struct lws_dll *d, struct lws_dll *target,
struct lws_dll *phead, int before);
static LWS_INLINE struct lws_dll *
lws_dll_get_head(struct lws_dll *phead) { return phead->next; }
static LWS_INLINE struct lws_dll *
lws_dll_get_tail(struct lws_dll *phead) { return phead->prev; }
/*
* caution, this doesn't track the tail in the head struct. Use
* lws_dll_remove_track_tail() instead of this if you want tail tracking. Using
* this means you can't use lws_dll_add_tail() amd
*/
LWS_VISIBLE LWS_EXTERN void
lws_dll_remove(struct lws_dll *d) LWS_WARN_DEPRECATED;
LWS_VISIBLE LWS_EXTERN void
lws_dll_remove_track_tail(struct lws_dll *d, struct lws_dll *phead);
/* another way to do lws_start_foreach_dll_safe() on a list via a cb */
LWS_VISIBLE LWS_EXTERN int
lws_dll_foreach_safe(struct lws_dll *phead, void *user,
int (*cb)(struct lws_dll *d, void *user));
#define lws_dll_is_detached(___dll, __head) \
(!(___dll)->prev && !(___dll)->next && (__head)->prev != (___dll))
#endif
/*
* lws_dll2_owner / lws_dll2 : more capable version of lws_dll. Differences:
*
* - there's an explicit lws_dll2_owner struct which holds head, tail and
* count of members.
*
* - list members all hold a pointer to their owner. So user code does not
* have to track anything about exactly what lws_dll2_owner list the object
* is a member of.
*
* - you can use lws_dll unless you want the member count or the ability to
* not track exactly which list it's on.
*
* - layout is compatible with lws_dll (but lws_dll apis will not update the
* new stuff)
*/
struct lws_dll2;
struct lws_dll2_owner;
typedef struct lws_dll2 {
struct lws_dll2 *prev;
struct lws_dll2 *next;
struct lws_dll2_owner *owner;
} lws_dll2_t;
typedef struct lws_dll2_owner {
struct lws_dll2 *tail;
struct lws_dll2 *head;
uint32_t count;
} lws_dll2_owner_t;
static LWS_INLINE int
lws_dll2_is_detached(const struct lws_dll2 *d) { return !d->owner; }
static LWS_INLINE const struct lws_dll2_owner *
lws_dll2_owner(const struct lws_dll2 *d) { return d->owner; }
static LWS_INLINE struct lws_dll2 *
lws_dll2_get_head(struct lws_dll2_owner *owner) { return owner->head; }
static LWS_INLINE struct lws_dll2 *
lws_dll2_get_tail(struct lws_dll2_owner *owner) { return owner->tail; }
LWS_VISIBLE LWS_EXTERN void
lws_dll2_add_head(struct lws_dll2 *d, struct lws_dll2_owner *owner);
LWS_VISIBLE LWS_EXTERN void
lws_dll2_add_tail(struct lws_dll2 *d, struct lws_dll2_owner *owner);
LWS_VISIBLE LWS_EXTERN void
lws_dll2_remove(struct lws_dll2 *d);
LWS_VISIBLE LWS_EXTERN int
lws_dll2_foreach_safe(struct lws_dll2_owner *owner, void *user,
int (*cb)(struct lws_dll2 *d, void *user));
LWS_VISIBLE LWS_EXTERN void
lws_dll2_clear(struct lws_dll2 *d);
LWS_VISIBLE LWS_EXTERN void
lws_dll2_owner_clear(struct lws_dll2_owner *d);
LWS_VISIBLE LWS_EXTERN void
lws_dll2_add_before(struct lws_dll2 *d, struct lws_dll2 *after);
LWS_VISIBLE LWS_EXTERN void
lws_dll2_add_sorted(lws_dll2_t *d, lws_dll2_owner_t *own,
int (*compare)(const lws_dll2_t *d, const lws_dll2_t *i));
#if defined(_DEBUG)
void
lws_dll2_describe(struct lws_dll2_owner *owner, const char *desc);
#else
#define lws_dll2_describe(x, y)
#endif
/*
* these are safe against the current container object getting deleted,
* since the hold his next in a temp and go to that next. ___tmp is
* the temp.
*/
#define lws_start_foreach_dll_safe(___type, ___it, ___tmp, ___start) \
{ \
___type ___it = ___start; \
while (___it) { \
___type ___tmp = (___it)->next;
#define lws_end_foreach_dll_safe(___it, ___tmp) \
___it = ___tmp; \
} \
}
#define lws_start_foreach_dll(___type, ___it, ___start) \
{ \
___type ___it = ___start; \
while (___it) {
#define lws_end_foreach_dll(___it) \
___it = (___it)->next; \
} \
}
///@}

View file

@ -29,313 +29,6 @@
*/
///@{
/**
* lws_start_foreach_ll(): linkedlist iterator helper start
*
* \param type: type of iteration, eg, struct xyz *
* \param it: iterator var name to create
* \param start: start of list
*
* This helper creates an iterator and starts a while (it) {
* loop. The iterator runs through the linked list starting at start and
* ends when it gets a NULL.
* The while loop should be terminated using lws_start_foreach_ll().
*/
#define lws_start_foreach_ll(type, it, start)\
{ \
type it = start; \
while (it) {
/**
* lws_end_foreach_ll(): linkedlist iterator helper end
*
* \param it: same iterator var name given when starting
* \param nxt: member name in the iterator pointing to next list element
*
* This helper is the partner for lws_start_foreach_ll() that ends the
* while loop.
*/
#define lws_end_foreach_ll(it, nxt) \
it = it->nxt; \
} \
}
/**
* lws_start_foreach_ll_safe(): linkedlist iterator helper start safe against delete
*
* \param type: type of iteration, eg, struct xyz *
* \param it: iterator var name to create
* \param start: start of list
* \param nxt: member name in the iterator pointing to next list element
*
* This helper creates an iterator and starts a while (it) {
* loop. The iterator runs through the linked list starting at start and
* ends when it gets a NULL.
* The while loop should be terminated using lws_end_foreach_ll_safe().
* Performs storage of next increment for situations where iterator can become invalidated
* during iteration.
*/
#define lws_start_foreach_ll_safe(type, it, start, nxt)\
{ \
type it = start; \
while (it) { \
type next_##it = it->nxt;
/**
* lws_end_foreach_ll_safe(): linkedlist iterator helper end (pre increment storage)
*
* \param it: same iterator var name given when starting
*
* This helper is the partner for lws_start_foreach_ll_safe() that ends the
* while loop. It uses the precreated next_ variable already stored during
* start.
*/
#define lws_end_foreach_ll_safe(it) \
it = next_##it; \
} \
}
/**
* lws_start_foreach_llp(): linkedlist pointer iterator helper start
*
* \param type: type of iteration, eg, struct xyz **
* \param it: iterator var name to create
* \param start: start of list
*
* This helper creates an iterator and starts a while (it) {
* loop. The iterator runs through the linked list starting at the
* address of start and ends when it gets a NULL.
* The while loop should be terminated using lws_start_foreach_llp().
*
* This helper variant iterates using a pointer to the previous linked-list
* element. That allows you to easily delete list members by rewriting the
* previous pointer to the element's next pointer.
*/
#define lws_start_foreach_llp(type, it, start)\
{ \
type it = &(start); \
while (*(it)) {
#define lws_start_foreach_llp_safe(type, it, start, nxt)\
{ \
type it = &(start); \
type next; \
while (*(it)) { \
next = &((*(it))->nxt); \
/**
* lws_end_foreach_llp(): linkedlist pointer iterator helper end
*
* \param it: same iterator var name given when starting
* \param nxt: member name in the iterator pointing to next list element
*
* This helper is the partner for lws_start_foreach_llp() that ends the
* while loop.
*/
#define lws_end_foreach_llp(it, nxt) \
it = &(*(it))->nxt; \
} \
}
#define lws_end_foreach_llp_safe(it) \
it = next; \
} \
}
#define lws_ll_fwd_insert(\
___new_object, /* pointer to new object */ \
___m_list, /* member for next list object ptr */ \
___list_head /* list head */ \
) {\
___new_object->___m_list = ___list_head; \
___list_head = ___new_object; \
}
#define lws_ll_fwd_remove(\
___type, /* type of listed object */ \
___m_list, /* member for next list object ptr */ \
___target, /* object to remove from list */ \
___list_head /* list head */ \
) { \
lws_start_foreach_llp(___type **, ___ppss, ___list_head) { \
if (*___ppss == ___target) { \
*___ppss = ___target->___m_list; \
break; \
} \
} lws_end_foreach_llp(___ppss, ___m_list); \
}
/*
* doubly linked-list
*/
#if defined (LWS_WITH_DEPRECATED_LWS_DLL)
/*
* This is going away in v4.1. You can set the cmake option above to keep it
* around temporarily. Migrate your stuff to the more capable and robust
* lws_dll2 below
*/
struct lws_dll {
struct lws_dll *prev;
struct lws_dll *next;
};
/*
* these all point to the composed list objects... you have to use the
* lws_container_of() helper to recover the start of the containing struct
*/
#define lws_dll_add_front lws_dll_add_head
LWS_VISIBLE LWS_EXTERN void
lws_dll_add_head(struct lws_dll *d, struct lws_dll *phead);
LWS_VISIBLE LWS_EXTERN void
lws_dll_add_tail(struct lws_dll *d, struct lws_dll *phead);
LWS_VISIBLE LWS_EXTERN void
lws_dll_insert(struct lws_dll *d, struct lws_dll *target,
struct lws_dll *phead, int before);
static LWS_INLINE struct lws_dll *
lws_dll_get_head(struct lws_dll *phead) { return phead->next; }
static LWS_INLINE struct lws_dll *
lws_dll_get_tail(struct lws_dll *phead) { return phead->prev; }
/*
* caution, this doesn't track the tail in the head struct. Use
* lws_dll_remove_track_tail() instead of this if you want tail tracking. Using
* this means you can't use lws_dll_add_tail() amd
*/
LWS_VISIBLE LWS_EXTERN void
lws_dll_remove(struct lws_dll *d) LWS_WARN_DEPRECATED;
LWS_VISIBLE LWS_EXTERN void
lws_dll_remove_track_tail(struct lws_dll *d, struct lws_dll *phead);
/* another way to do lws_start_foreach_dll_safe() on a list via a cb */
LWS_VISIBLE LWS_EXTERN int
lws_dll_foreach_safe(struct lws_dll *phead, void *user,
int (*cb)(struct lws_dll *d, void *user));
#define lws_dll_is_detached(___dll, __head) \
(!(___dll)->prev && !(___dll)->next && (__head)->prev != (___dll))
#endif
/*
* lws_dll2_owner / lws_dll2 : more capable version of lws_dll. Differences:
*
* - there's an explicit lws_dll2_owner struct which holds head, tail and
* count of members.
*
* - list members all hold a pointer to their owner. So user code does not
* have to track anything about exactly what lws_dll2_owner list the object
* is a member of.
*
* - you can use lws_dll unless you want the member count or the ability to
* not track exactly which list it's on.
*
* - layout is compatible with lws_dll (but lws_dll apis will not update the
* new stuff)
*/
struct lws_dll2;
struct lws_dll2_owner;
typedef struct lws_dll2 {
struct lws_dll2 *prev;
struct lws_dll2 *next;
struct lws_dll2_owner *owner;
} lws_dll2_t;
typedef struct lws_dll2_owner {
struct lws_dll2 *tail;
struct lws_dll2 *head;
uint32_t count;
} lws_dll2_owner_t;
static LWS_INLINE int
lws_dll2_is_detached(const struct lws_dll2 *d) { return !d->owner; }
static LWS_INLINE const struct lws_dll2_owner *
lws_dll2_owner(const struct lws_dll2 *d) { return d->owner; }
static LWS_INLINE struct lws_dll2 *
lws_dll2_get_head(struct lws_dll2_owner *owner) { return owner->head; }
static LWS_INLINE struct lws_dll2 *
lws_dll2_get_tail(struct lws_dll2_owner *owner) { return owner->tail; }
LWS_VISIBLE LWS_EXTERN void
lws_dll2_add_head(struct lws_dll2 *d, struct lws_dll2_owner *owner);
LWS_VISIBLE LWS_EXTERN void
lws_dll2_add_tail(struct lws_dll2 *d, struct lws_dll2_owner *owner);
LWS_VISIBLE LWS_EXTERN void
lws_dll2_remove(struct lws_dll2 *d);
LWS_VISIBLE LWS_EXTERN int
lws_dll2_foreach_safe(struct lws_dll2_owner *owner, void *user,
int (*cb)(struct lws_dll2 *d, void *user));
LWS_VISIBLE LWS_EXTERN void
lws_dll2_clear(struct lws_dll2 *d);
LWS_VISIBLE LWS_EXTERN void
lws_dll2_owner_clear(struct lws_dll2_owner *d);
LWS_VISIBLE LWS_EXTERN void
lws_dll2_add_before(struct lws_dll2 *d, struct lws_dll2 *after);
LWS_VISIBLE LWS_EXTERN void
lws_dll2_add_sorted(lws_dll2_t *d, lws_dll2_owner_t *own,
int (*compare)(const lws_dll2_t *d, const lws_dll2_t *i));
#if defined(_DEBUG)
void
lws_dll2_describe(struct lws_dll2_owner *owner, const char *desc);
#else
#define lws_dll2_describe(x, y)
#endif
/*
* these are safe against the current container object getting deleted,
* since the hold his next in a temp and go to that next. ___tmp is
* the temp.
*/
#define lws_start_foreach_dll_safe(___type, ___it, ___tmp, ___start) \
{ \
___type ___it = ___start; \
while (___it) { \
___type ___tmp = (___it)->next;
#define lws_end_foreach_dll_safe(___it, ___tmp) \
___it = ___tmp; \
} \
}
#define lws_start_foreach_dll(___type, ___it, ___start) \
{ \
___type ___it = ___start; \
while (___it) {
#define lws_end_foreach_dll(___it) \
___it = (___it)->next; \
} \
}
struct lws_buflist;
/**

View file

@ -54,3 +54,20 @@ LWS_VISIBLE LWS_EXTERN unsigned int
lws_retry_get_delay_ms(struct lws_context *context, const lws_retry_bo_t *retry,
uint16_t *ctry, char *conceal);
/**
* lws_retry_sul_schedule() - schedule a sul according to the backoff table
*
* \param lws_context: the lws context (used for getting random)
* \param sul: pointer to the sul to schedule
* \param retry: the retry backoff table we are using, or NULL for default
* \param cb: the callback for when the sul schedule time arrives
* \param ctry: pointer to the try counter
*
* Helper that combines interpreting the retry table with scheduling a sul to
* the computed delay. If conceal is not set, it will not schedule the sul
* and return 1. Otherwise the sul is scheduled and it returns 0.
*/
LWS_VISIBLE LWS_EXTERN int
lws_retry_sul_schedule(struct lws_context *context, int tid,
lws_sorted_usec_list_t *sul, const lws_retry_bo_t *retry,
sul_cb_t cb, uint16_t *ctry);

View file

@ -0,0 +1,97 @@
/*
* libwebsockets - small server side websockets and web server implementation
*
* Copyright (C) 2010 - 2019 Andy Green <andy@warmcat.com>
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (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 lws_state_notify_link;
struct lws_state_manager;
typedef int (*lws_state_notify_t)(struct lws_state_manager *mgr,
struct lws_state_notify_link *link,
int current, int target);
typedef struct lws_state_notify_link {
lws_dll2_t list;
lws_state_notify_t notify_cb;
const char *name;
} lws_state_notify_link_t;
typedef struct lws_state_manager {
lws_dll2_owner_t notify_list;
void *parent;
/**< optional opaque pointer to owning object... useful to make such
* a pointer available to a notification callback. Ignored by lws */
const char **state_names; /* may be NULL */
const char *name;
int state;
} lws_state_manager_t;
/**
* lws_state_reg_notifier() - add dep handler for state notifications
*
* \param context: the lws_context
* \param nl: the handler to add to the notifier linked-list
*
* Add \p notify_link to the context's list of notification handlers for system
* state changes. The handlers can defeat or take over responsibility for
* retrying the change after they have initiated some dependency.
*/
LWS_EXTERN LWS_VISIBLE void
lws_state_reg_notifier(lws_state_manager_t *mgr, lws_state_notify_link_t *nl);
/**
* lws_state_reg_notifier_list() - add dep handlers for state notifications
*
* \param context: the lws_context
* \param nl: list of notification handlers
*
* Add a NULL-terminated list of notification handler pointers to a notification
* manager object
*/
LWS_EXTERN LWS_VISIBLE void
lws_state_reg_notifier_list(lws_state_manager_t *mgr, lws_state_notify_link_t **nl);
/**
* lws_state_transition_steps() - move to state via starting any deps
*
* \param mgr: the state manager object
* \param target: the state we wish to move to
*
* Advance state by state towards state \p target. At each state, notifiers
* may veto the change and be triggered to perform dependencies, stopping the
* advance towards the target state.
*/
LWS_EXTERN LWS_VISIBLE int
lws_state_transition_steps(lws_state_manager_t *mgr, int target);
/**
* lws_state_transition() - move to state via starting any deps
*
* \param mgr: the state manager object
* \param target: the state we wish to move to
*
* Jump to state target atomically. Notifiers may veto it.
*/
LWS_EXTERN LWS_VISIBLE int
lws_state_transition(lws_state_manager_t *mgr, int target);

View file

@ -34,21 +34,72 @@ typedef enum {
LWS_SYSI_HRS_DEVICE_MODEL = 1,
LWS_SYSI_HRS_DEVICE_SERIAL,
LWS_SYSI_HRS_FIRMWARE_VERSION,
LWS_SYSI_HRS_NTP_SERVER,
LWS_SYSI_USER_BASE = 100
} lws_system_item_t;
typedef union {
const char *hrs; /* human readable string */
void *data;
time_t t;
typedef struct lws_system_arg {
union {
const char *hrs; /* human readable string */
void *data;
time_t t;
} u;
size_t len;
} lws_system_arg_t;
/*
* Lws view of system state... normal operation from user code perspective is
* dependent on implicit (eg, knowing the date for cert validation) and
* explicit dependencies.
*
* Bit of lws and user code can register notification handlers that can enforce
* dependent operations before state transitions can complete.
*/
typedef enum { /* keep system_state_names[] in sync in context.c */
LWS_SYSTATE_UNKNOWN,
LWS_SYSTATE_CONTEXT_CREATED, /* context was just created */
LWS_SYSTATE_INITIALIZED, /* protocols initialized. Lws itself
* can operate normally */
LWS_SYSTATE_IFACE_COLDPLUG, /* existing net ifaces iterated */
LWS_SYSTATE_DHCP, /* at least one net iface configured */
LWS_SYSTATE_TIME_VALID, /* ntpclient ran, or hw time valid...
* tls cannot work until we reach here
*/
LWS_SYSTATE_POLICY_VALID, /* user code knows how to operate... */
LWS_SYSTATE_REGISTERED, /* device has an identity... */
LWS_SYSTATE_AUTH1, /* identity used for main auth token */
LWS_SYSTATE_AUTH2, /* identity used for optional auth */
LWS_SYSTATE_OPERATIONAL, /* user code can operate normally */
LWS_SYSTATE_POLICY_INVALID, /* user code is changing its policies
* drop everything done with old
* policy, switch to new then enter
* LWS_SYSTATE_POLICY_VALID */
} lws_system_states_t;
typedef struct lws_system_ops {
int (*get_info)(lws_system_item_t i, lws_system_arg_t arg, size_t *len);
int (*get_info)(lws_system_item_t i, lws_system_arg_t *arg);
int (*reboot)(void);
int (*set_clock)(lws_usec_t us);
} lws_system_ops_t;
/**
* lws_system_get_state_manager() - return the state mgr object for system state
*
* \param context: the lws_context
*
* The returned pointer can be used with the lws_state_ apis
*/
LWS_EXTERN LWS_VISIBLE lws_state_manager_t *
lws_system_get_state_manager(struct lws_context *context);
/* wrappers handle NULL members or no ops struct set at all cleanly */
/**
@ -57,29 +108,28 @@ typedef struct lws_system_ops {
* \param context: the lws_context
* \param item: which information to fetch
* \param arg: where to place the result
* \param len: incoming: max length of result, outgoing: used length of result
*
* This queries a standardized information-fetching ops struct that can be
* applied to the context... the advantage is it allows you to get common items
* of information like a device serial number writing the code once, even if the
* actual serial number muse be fetched in wildly different ways depending on
* actual serial number must be fetched in wildly different ways depending on
* the exact platform it's running on.
*
* Set arg and *len on entry to be the result location and the max length that
* can be used there, on seccessful exit *len is set to the actual length and
* 0 is returned. On error, 1 is returned.
* Point arg to your lws_system_arg_t, on return it will be set. It doesn't
* copy the content just sets pointer and length.
*/
LWS_EXTERN LWS_VISIBLE int
lws_system_get_info(struct lws_context *context, lws_system_item_t item,
lws_system_arg_t arg, size_t *len);
lws_system_arg_t *arg);
/**
* lws_system_reboot() - if provided, use the lws_system ops to reboot
* lws_system_get_ops() - get ahold of the system ops struct from the context
*
* \param context: the lws_context
*
* If possible, the system will reboot. Otherwise returns 1.
* Returns the system ops struct. It may return NULL and if not, anything in
* there may be NULL.
*/
LWS_EXTERN LWS_VISIBLE int
lws_system_reboot(struct lws_context *context);
LWS_EXTERN LWS_VISIBLE const lws_system_ops_t *
lws_system_get_ops(struct lws_context *context);

View file

@ -31,6 +31,7 @@ lws_get_idlest_tsi(struct lws_context *context)
int n = 0, hit = -1;
for (; n < context->count_threads; n++) {
lwsl_notice("%s: %d %d\n", __func__, context->pt[n].fds_count, context->fd_limit_per_thread - 1);
if ((unsigned int)context->pt[n].fds_count !=
context->fd_limit_per_thread - 1 &&
(unsigned int)context->pt[n].fds_count < lowest) {
@ -540,7 +541,8 @@ lws_create_adopt_udp(struct lws_vhost *vhost, const char *ads, int port,
lwsl_info("%s: getaddrinfo error: %s\n", __func__,
gai_strerror(n));
#else
lwsl_info("%s: getaddrinfo error: %s\n", __func__, strerror(n));
lwsl_info("%s: getaddrinfo error: %s\n", __func__,
strerror(n));
#endif
freeaddrinfo(r);
goto bail1;
@ -566,7 +568,12 @@ lws_create_adopt_udp(struct lws_vhost *vhost, const char *ads, int port,
lws_create_adopt_udp2, wsi, NULL) ==
LADNS_RET_FAILED) {
lwsl_err("%s: async dns failed\n", __func__);
goto bail1;
wsi = NULL;
/*
* It was already closed by calling callback with error
* from lws_async_dns_query()
*/
goto bail;
}
} else
wsi = lws_create_adopt_udp2(wsi, ads, NULL, 0, NULL);
@ -575,10 +582,10 @@ lws_create_adopt_udp(struct lws_vhost *vhost, const char *ads, int port,
return wsi;
#endif
#if !defined(LWS_WITH_SYS_ASYNC_DNS)
bail1:
lws_close_free_wsi(wsi, LWS_CLOSE_STATUS_NOSTATUS, "udp create fail");
wsi = NULL;
lws_close_free_wsi(wsi, LWS_CLOSE_STATUS_NOSTATUS, "adopt udp2 fail");
#endif
bail:
return wsi;
#else

View file

@ -88,7 +88,7 @@ __lws_reset_wsi(struct lws *wsi)
lws_dll2_remove(&wsi->dll_cli_active_conns);
#endif
#if defined(LWS_WITH_ASYNC_DNS)
#if defined(LWS_WITH_SYS_ASYNC_DNS)
lws_async_dns_cancel(wsi);
#endif

View file

@ -71,9 +71,12 @@ lws_client_connect_via_info(const struct lws_client_connect_info *i)
#endif
wsi->vhost = NULL;
if (!i->vhost)
lws_vhost_bind_wsi(i->context->vhost_list, wsi);
else
if (!i->vhost) {
struct lws_vhost *v = i->context->vhost_list;
if (v && !strcmp(v->name, "system"))
v = v->vhost_next;
lws_vhost_bind_wsi(v, wsi);
} else
lws_vhost_bind_wsi(i->vhost, wsi);
if (!wsi->vhost) {

View file

@ -415,6 +415,22 @@ lws_retry_get_delay_ms(struct lws_context *context,
return ms;
}
int
lws_retry_sul_schedule(struct lws_context *context, int tid,
lws_sorted_usec_list_t *sul,
const lws_retry_bo_t *retry, sul_cb_t cb, uint16_t *ctry)
{
char conceal;
uint64_t ms = lws_retry_get_delay_ms(context, retry, ctry, &conceal);
if (!conceal)
return 1;
lws_sul_schedule(context, tid, sul, cb, ms * 1000);
return 0;
}
#if defined(LWS_WITH_IPV6)
LWS_EXTERN unsigned long
lws_get_addr_scope(const char *ipaddr)
@ -812,3 +828,9 @@ lws_sa46_compare_ads(const lws_sockaddr46 *sa46a, const lws_sockaddr46 *sa46b)
return sa46a->sa4.sin_addr.s_addr != sa46b->sa4.sin_addr.s_addr;
}
lws_state_manager_t *
lws_system_get_state_manager(struct lws_context *context)
{
return &context->mgr_system;
}

View file

@ -1235,6 +1235,9 @@ void
lws_async_dns_deinit(lws_async_dns_t *dns);
#endif
int
lws_protocol_init_vhost(struct lws_vhost *vh, int *any);
#ifdef __cplusplus
};
#endif

View file

@ -695,12 +695,6 @@ handled:
#endif
pollfd->revents = 0;
if (!context->protocol_init_done)
if (lws_protocol_init(context)) {
lwsl_err("%s: lws_protocol_init failed\n", __func__);
return -1;
}
return 0;
}

134
lib/core-net/state.c Normal file
View file

@ -0,0 +1,134 @@
/*
* libwebsockets - small server side websockets and web server implementation
*
* Copyright (C) 2010 - 2019 Andy Green <andy@warmcat.com>
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (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.
*/
#include "private-lib-core.h"
void
lws_state_reg_notifier(lws_state_manager_t *mgr,
lws_state_notify_link_t *notify_link)
{
lws_dll2_add_head(&notify_link->list, &mgr->notify_list);
}
void
lws_state_reg_notifier_list(lws_state_manager_t *mgr,
lws_state_notify_link_t **notify_link_array)
{
if (notify_link_array)
while (*notify_link_array)
lws_state_reg_notifier(mgr, *notify_link_array++);
}
#if defined(_DEBUG)
static const char *
_systnm(lws_state_manager_t *mgr, int state, char *temp8)
{
if (!mgr->state_names) {
lws_snprintf(temp8, 8, "%d", state);
return temp8;
}
return mgr->state_names[state];
}
#endif
static int
_report(lws_state_manager_t *mgr, int a, int b)
{
#if defined(_DEBUG)
char temp8[8];
#endif
lws_start_foreach_dll(struct lws_dll2 *, d, mgr->notify_list.head) {
lws_state_notify_link_t *l =
lws_container_of(d, lws_state_notify_link_t, list);
if (l->notify_cb(mgr, l, a, b)) {
/* a dependency took responsibility for retry */
#if defined(_DEBUG)
lwsl_info("%s: %s: %s: rejected '%s' -> '%s'\n",
__func__, mgr->name, l->name,
_systnm(mgr, a, temp8),
_systnm(mgr, b, temp8));
#endif
return 1;
}
} lws_end_foreach_dll(d);
return 0;
}
static int
_lws_state_transition(lws_state_manager_t *mgr, int target)
{
#if defined(_DEBUG)
char temp8[8];
#endif
if (_report(mgr, mgr->state, target))
return 1;
#if defined(_DEBUG)
lwsl_debug("%s: %s: changed %d '%s' -> %d '%s'\n", __func__, mgr->name,
mgr->state, _systnm(mgr, mgr->state, temp8), target,
_systnm(mgr, target, temp8));
#endif
mgr->state = target;
/* Indicate success by calling the notifers again with both args same */
_report(mgr, target, target);
return 0;
}
int
lws_state_transition_steps(lws_state_manager_t *mgr, int target)
{
int n = 0;
#if defined(_DEBUG)
int i = mgr->state;
char temp8[8];
#endif
while (!n && mgr->state != target)
n = _lws_state_transition(mgr, mgr->state + 1);
#if defined(_DEBUG)
lwsl_info("%s: %s -> %s\n", __func__, _systnm(mgr, i, temp8),
_systnm(mgr, mgr->state, temp8));
#endif
return 0;
}
int
lws_state_transition(lws_state_manager_t *mgr, int target)
{
if (mgr->state != target)
_lws_state_transition(mgr, target);
return 0;
}

View file

@ -280,6 +280,91 @@ lws_vhost_protocol_options(struct lws_vhost *vh, const char *name)
return NULL;
}
int
lws_protocol_init_vhost(struct lws_vhost *vh, int *any)
{
const struct lws_protocol_vhost_options *pvo, *pvo1;
struct lws *wsi = vh->context->pt[0].fake_wsi;
int n;
wsi->context = vh->context;
wsi->vhost = vh;
/* initialize supported protocols on this vhost */
for (n = 0; n < vh->count_protocols; n++) {
wsi->protocol = &vh->protocols[n];
if (!vh->protocols[n].name)
continue;
pvo = lws_vhost_protocol_options(vh, vh->protocols[n].name);
if (pvo) {
/*
* linked list of options specific to
* vh + protocol
*/
pvo1 = pvo;
pvo = pvo1->options;
while (pvo) {
lwsl_debug(
" vhost \"%s\", "
"protocol \"%s\", "
"option \"%s\"\n",
vh->name,
vh->protocols[n].name,
pvo->name);
if (!strcmp(pvo->name, "default")) {
lwsl_info("Setting default "
"protocol for vh %s to %s\n",
vh->name,
vh->protocols[n].name);
vh->default_protocol_index = n;
}
if (!strcmp(pvo->name, "raw")) {
lwsl_info("Setting raw "
"protocol for vh %s to %s\n",
vh->name,
vh->protocols[n].name);
vh->raw_protocol_index = n;
}
pvo = pvo->next;
}
pvo = pvo1->options;
}
#if defined(LWS_WITH_TLS)
if (any)
*any |= !!vh->tls.ssl_ctx;
#endif
/*
* inform all the protocols that they are doing their
* one-time initialization if they want to.
*
* NOTE the wsi is all zeros except for the context, vh
* + protocol ptrs so lws_get_context(wsi) etc can work
*/
if (vh->protocols[n].callback(wsi,
LWS_CALLBACK_PROTOCOL_INIT, NULL,
(void *)pvo, 0)) {
if (vh->protocol_vh_privs[n]) {
lws_free(vh->protocol_vh_privs[n]);
vh->protocol_vh_privs[n] = NULL;
}
lwsl_err("%s: protocol %s failed init\n",
__func__, vh->protocols[n].name);
return 1;
}
}
vh->created_vhost_protocols = 1;
return 0;
}
/*
* inform every vhost that hasn't already done it, that
* his protocols are initializing
@ -288,98 +373,24 @@ LWS_VISIBLE int
lws_protocol_init(struct lws_context *context)
{
struct lws_vhost *vh = context->vhost_list;
const struct lws_protocol_vhost_options *pvo, *pvo1;
struct lws *wsi = context->pt[0].fake_wsi;
int n, any = 0;
int any = 0;
if (context->doing_protocol_init)
return 0;
context->doing_protocol_init = 1;
wsi->context = context;
lwsl_info("%s\n", __func__);
while (vh) {
wsi->vhost = vh;
/* only do the protocol init once for a given vhost */
if (vh->created_vhost_protocols ||
(lws_check_opt(vh->options, LWS_SERVER_OPTION_SKIP_PROTOCOL_INIT)))
goto next;
/* initialize supported protocols on this vhost */
for (n = 0; n < vh->count_protocols; n++) {
wsi->protocol = &vh->protocols[n];
if (!vh->protocols[n].name)
continue;
pvo = lws_vhost_protocol_options(vh,
vh->protocols[n].name);
if (pvo) {
/*
* linked list of options specific to
* vh + protocol
*/
pvo1 = pvo;
pvo = pvo1->options;
while (pvo) {
lwsl_debug(
" vhost \"%s\", "
"protocol \"%s\", "
"option \"%s\"\n",
vh->name,
vh->protocols[n].name,
pvo->name);
if (!strcmp(pvo->name, "default")) {
lwsl_info("Setting default "
"protocol for vh %s to %s\n",
vh->name,
vh->protocols[n].name);
vh->default_protocol_index = n;
}
if (!strcmp(pvo->name, "raw")) {
lwsl_info("Setting raw "
"protocol for vh %s to %s\n",
vh->name,
vh->protocols[n].name);
vh->raw_protocol_index = n;
}
pvo = pvo->next;
}
pvo = pvo1->options;
}
#if defined(LWS_WITH_TLS)
any |= !!vh->tls.ssl_ctx;
#endif
/*
* inform all the protocols that they are doing their
* one-time initialization if they want to.
*
* NOTE the wsi is all zeros except for the context, vh
* + protocol ptrs so lws_get_context(wsi) etc can work
*/
if (vh->protocols[n].callback(wsi,
LWS_CALLBACK_PROTOCOL_INIT, NULL,
(void *)pvo, 0)) {
if (vh->protocol_vh_privs[n]) {
lws_free(vh->protocol_vh_privs[n]);
vh->protocol_vh_privs[n] = NULL;
}
lwsl_err("%s: protocol %s failed init\n",
__func__, vh->protocols[n].name);
return 1;
}
}
vh->created_vhost_protocols = 1;
if (lws_protocol_init_vhost(vh, &any))
return 1;
next:
vh = vh->vhost_next;
}

View file

@ -73,6 +73,60 @@ lws_sul_peer_limits_cb(lws_sorted_usec_list_t *sul)
}
#endif
#if defined(LWS_WITH_NETWORK)
#if defined(_DEBUG)
static const char * system_state_names[] = {
"undef",
"CONTEXT_CREATED",
"INITIALIZED",
"IFACE_COLDPLUG",
"DHCP",
"TIME_VALID",
"POLICY_VALID",
"REGISTERED",
"AUTH1",
"AUTH2",
"OPERATIONAL",
"POLICY_INVALID"
};
#endif
/*
* Handle provoking protocol init when we pass through the right system state
*/
static int
lws_state_notify_protocol_init(struct lws_state_manager *mgr,
struct lws_state_notify_link *link, int current,
int target)
{
struct lws_context *context = lws_container_of(mgr, struct lws_context,
mgr_system);
if (context->protocol_init_done)
return 0;
if (target != LWS_SYSTATE_POLICY_VALID)
return 0;
lwsl_info("%s: doing protocol init on POLICY_VALID\n", __func__);
lws_protocol_init(context);
return 0;
}
static void
lws_context_creation_completion_cb(lws_sorted_usec_list_t *sul)
{
struct lws_context *context = lws_container_of(sul, struct lws_context,
sul_system_state);
/* if nothing is there to intercept anything, go all the way */
lws_state_transition_steps(&context->mgr_system,
LWS_SYSTATE_OPERATIONAL);
}
#endif
LWS_VISIBLE struct lws_context *
lws_create_context(const struct lws_context_creation_info *info)
@ -92,6 +146,16 @@ lws_create_context(const struct lws_context_creation_info *info)
struct rlimit rt;
#endif
size_t s1 = 4096, size = sizeof(struct lws_context);
int lpf = info->fd_limit_per_thread;
if (lpf) {
#if defined(LWS_WITH_SYS_ASYNC_DNS)
lpf++;
#endif
#if defined(LWS_WITH_SYS_NTPCLIENT)
lpf++;
#endif
}
lwsl_info("Initial logging level %d\n", log_level);
lwsl_info("Libwebsockets version: %s\n", library_version);
@ -264,7 +328,7 @@ lws_create_context(const struct lws_context_creation_info *info)
* figure out what if this is something it can deal with.
*/
if (info->fd_limit_per_thread) {
int mf = info->fd_limit_per_thread * context->count_threads;
int mf = lpf * context->count_threads;
if (mf < context->max_fds) {
context->max_fds_unrelated_to_ulimit = 1;
@ -366,7 +430,7 @@ lws_create_context(const struct lws_context_creation_info *info)
context->max_http_header_pool = context->max_fds;
if (info->fd_limit_per_thread)
context->fd_limit_per_thread = info->fd_limit_per_thread;
context->fd_limit_per_thread = lpf;
else
context->fd_limit_per_thread = context->max_fds /
context->count_threads;
@ -514,29 +578,7 @@ lws_create_context(const struct lws_context_creation_info *info)
lws_context_init_ssl_library(info);
context->user_space = info->user;
#if defined(LWS_WITH_NETWORK)
/*
* if he's not saying he'll make his own vhosts later then act
* compatibly and make a default vhost using the data in the info
*/
if (!lws_check_opt(info->options, LWS_SERVER_OPTION_EXPLICIT_VHOSTS))
if (!lws_create_vhost(context, info)) {
lwsl_err("Failed to create default vhost\n");
#if defined(LWS_WITH_PEER_LIMITS)
lws_free_set_NULL(context->pl_hash_table);
#endif
lws_free_set_NULL(context->pt[0].fds);
lws_plat_context_late_destroy(context);
lws_free_set_NULL(context);
return NULL;
}
lws_context_init_extensions(info, context);
lwsl_info(" mem: per-conn: %5lu bytes + protocol rx buf\n",
(unsigned long)sizeof(struct lws));
#endif
#if defined(LWS_WITH_SERVER)
strcpy(context->canonical_hostname, "unknown");
@ -561,6 +603,104 @@ lws_create_context(const struct lws_context_creation_info *info)
context->count_caps = info->count_caps;
#endif
#if defined(LWS_WITH_NETWORK)
#if defined(LWS_WITH_SYS_ASYNC_DNS) || defined(LWS_WITH_SYS_NTPCLIENT)
{
/*
* system vhost
*/
struct lws_context_creation_info ii;
const struct lws_protocols *pp[3];
struct lws_vhost *vh;
#if defined(LWS_WITH_SYS_ASYNC_DNS)
extern const struct lws_protocols lws_async_dns_protocol;
#endif
#if defined(LWS_WITH_SYS_NTPCLIENT)
extern const struct lws_protocols lws_system_protocol_ntpc;
#endif
n = 0;
#if defined(LWS_WITH_SYS_ASYNC_DNS)
pp[n++] = &lws_async_dns_protocol;
#endif
#if defined(LWS_WITH_SYS_NTPCLIENT)
pp[n++] = &lws_system_protocol_ntpc;
#endif
pp[n] = NULL;
memset(&ii, 0, sizeof(ii));
ii.vhost_name = "system";
ii.pprotocols = pp;
vh = lws_create_vhost(context, &ii);
if (!vh) {
lwsl_err("%s: failed to create system vhost\n",
__func__);
goto bail;
}
context->vhost_system = vh;
if (lws_protocol_init_vhost(vh, NULL)) {
lwsl_err("%s: failed to init system vhost\n", __func__);
goto bail;
}
#if defined(LWS_WITH_SYS_ASYNC_DNS)
if (lws_async_dns_init(context))
goto bail;
#endif
}
#endif
/*
* init the lws_state mgr for the system state
*/
#if defined(_DEBUG)
context->mgr_system.state_names = system_state_names;
#endif
context->mgr_system.name = "system";
context->mgr_system.state = LWS_SYSTATE_CONTEXT_CREATED;
context->mgr_system.parent = context;
context->protocols_notify.name = "prot_init";
context->protocols_notify.notify_cb = lws_state_notify_protocol_init;
lws_state_reg_notifier(&context->mgr_system, &context->protocols_notify);
/*
* insert user notifiers here so they can participate with vetoing us
* trying to jump straight to operational, or at least observe us
* reaching 'operational', before we returned from context creation.
*/
lws_state_reg_notifier_list(&context->mgr_system,
info->register_notifier_list);
/*
* if he's not saying he'll make his own vhosts later then act
* compatibly and make a default vhost using the data in the info
*/
if (!lws_check_opt(info->options, LWS_SERVER_OPTION_EXPLICIT_VHOSTS))
if (!lws_create_vhost(context, info)) {
lwsl_err("Failed to create default vhost\n");
#if defined(LWS_WITH_PEER_LIMITS)
lws_free_set_NULL(context->pl_hash_table);
#endif
lws_free_set_NULL(context->pt[0].fds);
lws_plat_context_late_destroy(context);
lws_free_set_NULL(context);
return NULL;
}
lws_context_init_extensions(info, context);
lwsl_info(" mem: per-conn: %5lu bytes + protocol rx buf\n",
(unsigned long)sizeof(struct lws));
/*
* drop any root privs for this process
* to listen on port < 1023 we would have needed root, but now we are
@ -570,7 +710,18 @@ lws_create_context(const struct lws_context_creation_info *info)
if (lws_plat_drop_app_privileges(context, 1))
goto bail;
#if defined(LWS_WITH_NETWORK)
/*
* We want to move on the syste, state as far as it can go towards
* OPERATIONAL now. But we have to return from here first so the user
* code that called us can set its copy of context, which it may be
* relying on to perform operations triggered by the state change.
*
* We set up a sul to come back immediately and do the state change.
*/
lws_sul_schedule(context, 0, &context->sul_system_state,
lws_context_creation_completion_cb, 1);
/* expedite post-context init (eg, protocols) */
lws_cancel_service(context);
#endif
@ -826,6 +977,7 @@ lws_context_destroy(struct lws_context *context)
context->requested_kill = 1;
#if defined(LWS_WITH_NETWORK)
lws_state_transition(&context->mgr_system, LWS_SYSTATE_POLICY_INVALID);
m = context->count_threads;
while (m--) {
@ -910,4 +1062,3 @@ lws_context_destroy(struct lws_context *context)
lws_context_destroy2(context);
}

View file

@ -1041,19 +1041,16 @@ lws_humanize(char *p, int len, uint64_t v, const lws_humanize_unit_t *schema)
int
lws_system_get_info(struct lws_context *context, lws_system_item_t item,
lws_system_arg_t arg, size_t *len)
lws_system_arg_t *arg)
{
if (!context->system_ops || !context->system_ops->get_info)
return 1;
return context->system_ops->get_info(item, arg, len);
return context->system_ops->get_info(item, arg);
}
int
lws_system_reboot(struct lws_context *context)
const lws_system_ops_t *
lws_system_get_ops(struct lws_context *context)
{
if (!context->system_ops || !context->system_ops->reboot)
return 1;
return context->system_ops->reboot();
return context->system_ops;
}

View file

@ -275,9 +275,9 @@ struct lws_context {
#endif
#if defined(LWS_WITH_NETWORK)
struct lws_context_per_thread pt[LWS_MAX_SMP];
lws_retry_bo_t default_retry;
lws_sorted_usec_list_t sul_system_state;
#if defined(LWS_WITH_HTTP2)
struct http2_settings set;
@ -309,11 +309,17 @@ struct lws_context {
lws_async_dns_t async_dns;
#endif
#if defined(LWS_WITH_NETWORK)
lws_state_manager_t mgr_system;
lws_state_notify_link_t protocols_notify;
#endif
/* pointers */
struct lws_vhost *vhost_list;
struct lws_vhost *no_listener_vhost_list;
struct lws_vhost *vhost_pending_destruction_list;
struct lws_vhost *vhost_system;
#if defined(LWS_WITH_SERVER)
const char *server_string;

13
lib/system/README.md Normal file
View file

@ -0,0 +1,13 @@
# LWS System Helpers
Lws now has a little collection of helper utilities for common network-based
functions necessary for normal device operation, eg, async DNS, ntpclient
(necessary for tls validation), and DHCP client.
## Conventions
If any system helper is enabled for build, lws creates an additional vhost
"system" at Context Creation time. Wsi that are created for the system
features are bound to this. In the context object, this is available as
`.vhost_system`.

View file

@ -37,7 +37,7 @@ callback_http(struct lws *wsi, enum lws_callback_reasons reason,
case LWS_CALLBACK_CLIENT_CONNECTION_ERROR:
lwsl_err("CLIENT_CONNECTION_ERROR: %s\n",
in ? (char *)in : "(null)");
client_wsi = NULL;
interrupted = 1;
break;
case LWS_CALLBACK_ESTABLISHED_CLIENT_HTTP:
@ -86,13 +86,13 @@ callback_http(struct lws *wsi, enum lws_callback_reasons reason,
case LWS_CALLBACK_COMPLETED_CLIENT_HTTP:
lwsl_user("LWS_CALLBACK_COMPLETED_CLIENT_HTTP\n");
client_wsi = NULL;
interrupted = 1;
bad = status != 200;
lws_cancel_service(lws_get_context(wsi)); /* abort poll wait */
break;
case LWS_CALLBACK_CLOSED_CLIENT_HTTP:
client_wsi = NULL;
interrupted = 1;
bad = status != 200;
lws_cancel_service(lws_get_context(wsi)); /* abort poll wait */
break;
@ -120,12 +120,92 @@ sigint_handler(int sig)
interrupted = 1;
}
struct args {
int argc;
const char **argv;
};
static int
system_notify_cb(lws_state_manager_t *mgr, lws_state_notify_link_t *link,
int current, int target)
{
struct lws_context *context = mgr->parent;
struct lws_client_connect_info i;
struct args *a = lws_context_user(context);
const char *p;
if (current != LWS_SYSTATE_OPERATIONAL || target != LWS_SYSTATE_OPERATIONAL)
return 0;
lwsl_info("%s: operational\n", __func__);
memset(&i, 0, sizeof i); /* otherwise uninitialized garbage */
i.context = context;
if (!lws_cmdline_option(a->argc, a->argv, "-n")) {
i.ssl_connection = LCCSCF_USE_SSL;
#if defined(LWS_WITH_HTTP2)
/* requires h2 */
if (lws_cmdline_option(a->argc, a->argv, "--long-poll")) {
lwsl_user("%s: long poll mode\n", __func__);
long_poll = 1;
}
#endif
}
if (lws_cmdline_option(a->argc, a->argv, "-l")) {
i.port = 7681;
i.address = "localhost";
i.ssl_connection |= LCCSCF_ALLOW_SELFSIGNED;
} else {
i.port = 443;
i.address = "warmcat.com";
}
if (lws_cmdline_option(a->argc, a->argv, "--h1"))
i.alpn = "http/1.1";
if ((p = lws_cmdline_option(a->argc, a->argv, "-p")))
i.port = atoi(p);
if (lws_cmdline_option(a->argc, a->argv, "-j"))
i.ssl_connection |= LCCSCF_ALLOW_SELFSIGNED;
if (lws_cmdline_option(a->argc, a->argv, "-k"))
i.ssl_connection |= LCCSCF_ALLOW_INSECURE;
if (lws_cmdline_option(a->argc, a->argv, "-m"))
i.ssl_connection |= LCCSCF_SKIP_SERVER_CERT_HOSTNAME_CHECK;
if (lws_cmdline_option(a->argc, a->argv, "-e"))
i.ssl_connection |= LCCSCF_ALLOW_EXPIRED;
/* the default validity check is 5m / 5m10s... -v = 3s / 10s */
if (lws_cmdline_option(a->argc, a->argv, "-v"))
i.retry_and_idle_policy = &retry;
if ((p = lws_cmdline_option(a->argc, a->argv, "--server")))
i.address = p;
i.path = "/";
i.host = i.address;
i.origin = i.address;
i.method = "GET";
i.protocol = protocols[0].name;
i.pwsi = &client_wsi;
return !lws_client_connect_via_info(&i);
}
int main(int argc, const char **argv)
{
lws_state_notify_link_t notifier = { {}, system_notify_cb, "app" };
lws_state_notify_link_t *na[] = { &notifier, NULL };
struct lws_context_creation_info info;
struct lws_client_connect_info i;
struct lws_context *context;
const char *p;
struct args args;
int n = 0, logs = LLL_USER | LLL_ERR | LLL_WARN | LLL_NOTICE
/*
* For LLL_ verbosity above NOTICE to be built into lws,
@ -136,6 +216,9 @@ int main(int argc, const char **argv)
* LLL_CLIENT | LLL_LATENCY | LLL_DEBUG
*/ ;
args.argc = argc;
args.argv = argv;
signal(SIGINT, sigint_handler);
if ((p = lws_cmdline_option(argc, argv, "-d")))
@ -148,6 +231,8 @@ int main(int argc, const char **argv)
info.options = LWS_SERVER_OPTION_DO_SSL_GLOBAL_INIT;
info.port = CONTEXT_PORT_NO_LISTEN; /* we do not run any server */
info.protocols = protocols;
info.user = &args;
info.register_notifier_list = na;
/*
* since we know this lws context is only ever going to be used with
@ -172,64 +257,7 @@ int main(int argc, const char **argv)
return 1;
}
memset(&i, 0, sizeof i); /* otherwise uninitialized garbage */
i.context = context;
if (!lws_cmdline_option(argc, argv, "-n")) {
i.ssl_connection = LCCSCF_USE_SSL;
#if defined(LWS_WITH_HTTP2)
/* requires h2 */
if (lws_cmdline_option(argc, argv, "--long-poll")) {
lwsl_user("%s: long poll mode\n", __func__);
long_poll = 1;
}
#endif
}
if (lws_cmdline_option(argc, argv, "-l")) {
i.port = 7681;
i.address = "localhost";
i.ssl_connection |= LCCSCF_ALLOW_SELFSIGNED;
} else {
i.port = 443;
i.address = "warmcat.com";
}
if (lws_cmdline_option(argc, argv, "--h1"))
i.alpn = "http/1.1";
if ((p = lws_cmdline_option(argc, argv, "-p")))
i.port = atoi(p);
if (lws_cmdline_option(argc, argv, "-j"))
i.ssl_connection |= LCCSCF_ALLOW_SELFSIGNED;
if (lws_cmdline_option(argc, argv, "-k"))
i.ssl_connection |= LCCSCF_ALLOW_INSECURE;
if (lws_cmdline_option(argc, argv, "-m"))
i.ssl_connection |= LCCSCF_SKIP_SERVER_CERT_HOSTNAME_CHECK;
if (lws_cmdline_option(argc, argv, "-e"))
i.ssl_connection |= LCCSCF_ALLOW_EXPIRED;
/* the default validity check is 5m / 5m10s... -v = 3s / 10s */
if (lws_cmdline_option(argc, argv, "-v"))
i.retry_and_idle_policy = &retry;
if ((p = lws_cmdline_option(argc, argv, "--server")))
i.address = p;
i.path = "/";
i.host = i.address;
i.origin = i.address;
i.method = "GET";
i.protocol = protocols[0].name;
i.pwsi = &client_wsi;
lws_client_connect_via_info(&i);
while (n >= 0 && client_wsi && !interrupted)
while (n >= 0 && !interrupted)
n = lws_service(context, 0);
lws_context_destroy(context);