You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@mynewt.apache.org by cc...@apache.org on 2018/01/18 01:36:40 UTC
[mynewt-mcumgr] 02/11: mgmt - Code implementing the core `mgmt`
layer.
This is an automated email from the ASF dual-hosted git repository.
ccollins pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/mynewt-mcumgr.git
commit 9f3757c1f5b1938e0a323e5ddb243c21f0c6b1ed
Author: Christopher Collins <cc...@apache.org>
AuthorDate: Wed Jan 17 17:11:06 2018 -0800
mgmt - Code implementing the core `mgmt` layer.
The core of mcumgr; facilitates the passing of requests and responses
between the generic command handlers and the concrete transports and
transfer encodings.
---
mgmt/CMakeLists.txt | 8 +
mgmt/include/mgmt/endian.h | 58 +++++++
mgmt/include/mgmt/mgmt.h | 388 +++++++++++++++++++++++++++++++++++++++++++++
mgmt/pkg.yml | 29 ++++
mgmt/src/mgmt.c | 170 ++++++++++++++++++++
mgmt/syscfg.yml | 18 +++
6 files changed, 671 insertions(+)
diff --git a/mgmt/CMakeLists.txt b/mgmt/CMakeLists.txt
new file mode 100644
index 0000000..ffce8a5
--- /dev/null
+++ b/mgmt/CMakeLists.txt
@@ -0,0 +1,8 @@
+target_include_directories(MCUMGR INTERFACE
+ include
+ port/zephyr/include
+)
+
+zephyr_library_sources(
+ mgmt/src/mgmt.c
+)
diff --git a/mgmt/include/mgmt/endian.h b/mgmt/include/mgmt/endian.h
new file mode 100644
index 0000000..ad9bd34
--- /dev/null
+++ b/mgmt/include/mgmt/endian.h
@@ -0,0 +1,58 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+#ifndef H_MGMT_ENDIAN_
+#define H_MGMT_ENDIAN_
+
+#include <inttypes.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#if __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__
+
+#ifndef ntohs
+#define ntohs(x) (x)
+#endif
+
+#ifndef htons
+#define htons(x) (x)
+#endif
+
+#else
+/* Little endian. */
+
+#ifndef ntohs
+#define ntohs(x) ((uint16_t) \
+ ((((x) & 0xff00) >> 8) | \
+ (((x) & 0x00ff) << 8)))
+#endif
+
+#ifndef htons
+#define htons(x) (ntohs(x))
+#endif
+
+#endif
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/mgmt/include/mgmt/mgmt.h b/mgmt/include/mgmt/mgmt.h
new file mode 100644
index 0000000..549fcdc
--- /dev/null
+++ b/mgmt/include/mgmt/mgmt.h
@@ -0,0 +1,388 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+#ifndef H_MGMT_MGMT_
+#define H_MGMT_MGMT_
+
+#include <inttypes.h>
+#include "cbor.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/** Opcodes; encoded in first byte of header. */
+#define MGMT_OP_READ 0
+#define MGMT_OP_READ_RSP 1
+#define MGMT_OP_WRITE 2
+#define MGMT_OP_WRITE_RSP 3
+
+/**
+ * The first 64 groups are reserved for system level mcumgr commands.
+ * Per-user commands are then defined after group 64.
+ */
+#define MGMT_GROUP_ID_OS 0
+#define MGMT_GROUP_ID_IMAGE 1
+#define MGMT_GROUP_ID_STATS 2
+#define MGMT_GROUP_ID_CONFIG 3
+#define MGMT_GROUP_ID_LOGS 4
+#define MGMT_GROUP_ID_CRASH 5
+#define MGMT_GROUP_ID_SPLIT 6
+#define MGMT_GROUP_ID_RUN 7
+#define MGMT_GROUP_ID_FS 8
+#define MGMT_GROUP_ID_PERUSER 64
+
+/**
+ * mcumgr error codes.
+ */
+#define MGMT_ERR_EOK 0
+#define MGMT_ERR_EUNKNOWN 1
+#define MGMT_ERR_ENOMEM 2
+#define MGMT_ERR_EINVAL 3
+#define MGMT_ERR_ETIMEOUT 4
+#define MGMT_ERR_ENOENT 5
+#define MGMT_ERR_EBADSTATE 6 /* Current state disallows command. */
+#define MGMT_ERR_EMSGSIZE 7 /* Response too large. */
+#define MGMT_ERR_ENOTSUP 8 /* Command not supported. */
+#define MGMT_ERR_EPERUSER 256
+
+#define MGMT_HDR_SIZE 8
+
+struct mgmt_hdr {
+#if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__
+ uint8_t nh_op:3; /* MGMT_OP_[...] */
+ uint8_t _res1:5;
+#endif
+#if __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__
+ uint8_t _res1:5;
+ uint8_t nh_op:3; /* MGMT_OP_[...] */
+#endif
+ uint8_t nh_flags; /* Reserved for future flags */
+ uint16_t nh_len; /* Length of the payload */
+ uint16_t nh_group; /* MGMT_GROUP_ID_[...] */
+ uint8_t nh_seq; /* Sequence number */
+ uint8_t nh_id; /* Message ID within group */
+};
+
+/** @typedef mgmt_alloc_rsp_fn
+ * @brief Allocates a buffer suitable for holding a response.
+ *
+ * If a source buf is provided, its user data is copied into the new buffer.
+ *
+ * @param src_buf An optional source buffer to copy user data
+ * from.
+ * @param arg Optional streamer argument.
+ *
+ * @return Newly-allocated buffer on success
+ * NULL on failure.
+ */
+typedef void *mgmt_alloc_rsp_fn(const void *src_buf, void *arg);
+
+/** @typedef mgmt_trim_front_fn
+ * @brief Trims data from the front of a buffer.
+ *
+ * If the amount to trim exceeds the size of the buffer, the buffer is
+ * truncated to a length of 0.
+ *
+ * @param buf The buffer to trim.
+ * @param len The number of bytes to remove.
+ * @param arg Optional streamer argument.
+ */
+typedef void mgmt_trim_front_fn(void *buf, size_t len, void *arg);
+
+/** @typedef mgmt_reset_buf_fn
+ * @brief Resets a buffer to a length of 0.
+ *
+ * The buffer's user data remains, but its payload is cleared.
+ *
+ * @param buf The buffer to reset.
+ * @param arg Optional streamer argument.
+ */
+typedef void mgmt_reset_buf_fn(void *buf, void *arg);
+
+/** @typedef mgmt_write_at_fn
+ * @brief Writes data to a CBOR encoder.
+ *
+ * Any existing data at the specified offset is overwritten by the new data.
+ * Any new data that extends past the buffer's current length is appended.
+ *
+ * @param writer The encoder to write to.
+ * @param offset The byte offset to write to,
+ * @param data The data to write.
+ * @param len The number of bytes to write.
+ * @param arg Optional streamer argument.
+ *
+ * @return 0 on success, MGMT_ERR_[...] code on failure.
+ */
+typedef int mgmt_write_at_fn(struct cbor_encoder_writer *writer, size_t offset,
+ const void *data, size_t len, void *arg);
+
+/** @typedef mgmt_init_reader_fn
+ * @brief Initializes a CBOR reader with the specified buffer.
+ *
+ * @param reader The reader to initialize.
+ * @param buf The buffer to configure the reader with.
+ * @param arg Optional streamer argument.
+ *
+ * @return 0 on success, MGMT_ERR_[...] code on failure.
+ */
+typedef int mgmt_init_reader_fn(struct cbor_decoder_reader *reader, void *buf,
+ void *arg);
+
+/** @typedef mgmt_init_writer_fn
+ * @brief Initializes a CBOR writer with the specified buffer.
+ *
+ * @param writer The writer to initialize.
+ * @param buf The buffer to configure the writer with.
+ * @param arg Optional streamer argument.
+ *
+ * @return 0 on success, MGMT_ERR_[...] code on failure.
+ */
+typedef int mgmt_init_writer_fn(struct cbor_encoder_writer *writer, void *buf,
+ void *arg);
+
+/** @typedef mgmt_init_writer_fn
+ * @brief Frees the specified buffer.
+ *
+ * @param buf The buffer to free.
+ * @param arg Optional streamer argument.
+ */
+typedef void mgmt_free_buf_fn(void *buf, void *arg);
+
+/**
+ * @brief Configuration for constructing a mgmt_streamer object.
+ */
+struct mgmt_streamer_cfg {
+ mgmt_alloc_rsp_fn *alloc_rsp;
+ mgmt_trim_front_fn *trim_front;
+ mgmt_reset_buf_fn *reset_buf;
+ mgmt_write_at_fn *write_at;
+ mgmt_init_reader_fn *init_reader;
+ mgmt_init_writer_fn *init_writer;
+ mgmt_free_buf_fn *free_buf;
+};
+
+/**
+ * @brief Decodes requests and encodes responses for any mcumgr protocol.
+ */
+struct mgmt_streamer {
+ const struct mgmt_streamer_cfg *cfg;
+ void *cb_arg;
+ struct cbor_decoder_reader *reader;
+ struct cbor_encoder_writer *writer;
+};
+
+/**
+ * @brief Context required by command handlers for parsing requests and writing
+ * responses.
+ */
+struct mgmt_ctxt {
+ struct CborEncoder encoder;
+ struct CborParser parser;
+ struct CborValue it;
+};
+
+/** @typedef mgmt_handler_fn
+ * @brief Processes a request and writes the corresponding response.
+ *
+ * A separate handler is required for each supported op-ID pair.
+ *
+ * @param cbuf The mcumgr context to use.
+ *
+ * @return 0 if a response was successfully encoded,
+ * MGMT_ERR_[...] code on failure.
+ */
+typedef int mgmt_handler_fn(struct mgmt_ctxt *cbuf);
+
+/**
+ * @brief Read handler and write handler for a single command ID.
+ */
+struct mgmt_handler {
+ mgmt_handler_fn *mh_read;
+ mgmt_handler_fn *mh_write;
+};
+
+/**
+ * @brief A collection of handlers for an entire command group.
+ */
+struct mgmt_group {
+ /** Points to the next group in the list. */
+ struct mgmt_group *mg_next;
+
+ /** Array of handlers; one entry per command ID. */
+ const struct mgmt_handler *mg_handlers;
+ uint16_t mg_handlers_count;
+
+ /* The numeric ID of this group. */
+ uint16_t mg_group_id;
+};
+
+/**
+ * @brief Uses the specified streamer to allocates a response buffer.
+ *
+ * If a source buf is provided, its user data is copied into the new buffer.
+ *
+ * @param streamer The streamer providing the callback.
+ * @param src_buf An optional source buffer to copy user data
+ * from.
+ *
+ * @return Newly-allocated buffer on success
+ * NULL on failure.
+ */
+void *mgmt_streamer_alloc_rsp(struct mgmt_streamer *streamer,
+ const void *src_buf);
+
+/**
+ * @brief Uses the specified streamer to trim data from the front of a buffer.
+ *
+ * If the amount to trim exceeds the size of the buffer, the buffer is
+ * truncated to a length of 0.
+ *
+ * @param streamer The streamer providing the callback.
+ * @param buf The buffer to trim.
+ * @param len The number of bytes to remove.
+ */
+void mgmt_streamer_trim_front(struct mgmt_streamer *streamer, void *buf,
+ size_t len);
+
+/**
+ * @brief Uses the specified streamer to reset a buffer to a length of 0.
+ *
+ * The buffer's user data remains, but its payload is cleared.
+ *
+ * @param streamer The streamer providing the callback.
+ * @param buf The buffer to reset.
+ */
+void mgmt_streamer_reset_buf(struct mgmt_streamer *streamer, void *buf);
+
+/**
+ * @brief Uses the specified streamer to write data to a CBOR encoder.
+ *
+ * Any existing data at the specified offset is overwritten by the new data.
+ * Any new data that extends past the buffer's current length is appended.
+ *
+ * @param streamer The streamer providing the callback.
+ * @param writer The encoder to write to.
+ * @param offset The byte offset to write to,
+ * @param data The data to write.
+ * @param len The number of bytes to write.
+ *
+ * @return 0 on success, MGMT_ERR_[...] code on failure.
+ */
+int mgmt_streamer_write_at(struct mgmt_streamer *streamer, size_t offset,
+ const void *data, int len);
+
+/**
+ * @brief Uses the specified streamer to initialize a CBOR reader.
+ *
+ * @param streamer The streamer providing the callback.
+ * @param reader The reader to initialize.
+ * @param buf The buffer to configure the reader with.
+ *
+ * @return 0 on success, MGMT_ERR_[...] code on failure.
+ */
+int mgmt_streamer_init_reader(struct mgmt_streamer *streamer, void *buf);
+
+/**
+ * @brief Uses the specified streamer to initializes a CBOR writer.
+ *
+ * @param streamer The streamer providing the callback.
+ * @param writer The writer to initialize.
+ * @param buf The buffer to configure the writer with.
+ *
+ * @return 0 on success, MGMT_ERR_[...] code on failure.
+ */
+int mgmt_streamer_init_writer(struct mgmt_streamer *streamer, void *buf);
+
+/**
+ * @brief Uses the specified streamer to free a buffer.
+ *
+ * @param streamer The streamer providing the callback.
+ * @param buf The buffer to free.
+ */
+void mgmt_streamer_free_buf(struct mgmt_streamer *streamer, void *buf);
+
+/**
+ * @brief Registers a full command group.
+ *
+ * @param group The group to register.
+ */
+void mgmt_register_group(struct mgmt_group *group);
+
+/**
+ * @brief Finds a registered command handler.
+ *
+ * @param group_id The group of the command to find.
+ * @param command_id The ID of the command to find.
+ *
+ * @return The requested command handler on success;
+ * NULL on failure.
+ */
+const struct mgmt_handler *mgmt_find_handler(uint16_t group_id,
+ uint16_t command_id);
+
+/**
+ * @brief Encodes a response status into the specified management context.
+ *
+ * @param cbuf The management context to encode into.
+ * @param status The response status to write.
+ *
+ * @return 0 on success, MGMT_ERR_[...] code on failure.
+ */
+int mgmt_write_rsp_status(struct mgmt_ctxt *cbuf, int status);
+
+/**
+ * @brief Initializes a management context object with the specified streamer.
+ *
+ * @param cbuf The context object to initialize.
+ * @param streamer The streamer that will be used with the
+ * context.
+ *
+ * @return 0 on success, MGMT_ERR_[...] code on failure.
+ */
+int mgmt_ctxt_init(struct mgmt_ctxt *cbuf, struct mgmt_streamer *streamer);
+
+/**
+ * @brief Converts a CBOR status code to a MGMT_ERR_[...] code.
+ *
+ * @param cbor_status The CBOR status code to convert.
+ *
+ * @return The corresponding MGMT_ERR_[,,,] code.
+ */
+int mgmt_err_from_cbor(int cbor_status);
+
+/**
+ * @brief Byte-swaps an mcumgr header from network to host byte order.
+ *
+ * @param hdr The mcumgr header to byte-swap.
+ */
+void mgmt_ntoh_hdr(struct mgmt_hdr *hdr);
+
+/**
+ * @brief Byte-swaps an mcumgr header from host to network byte order.
+ *
+ * @param hdr The mcumgr header to byte-swap.
+ */
+void mgmt_hton_hdr(struct mgmt_hdr *hdr);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* MGMT_MGMT_H_ */
diff --git a/mgmt/pkg.yml b/mgmt/pkg.yml
new file mode 100644
index 0000000..94839bd
--- /dev/null
+++ b/mgmt/pkg.yml
@@ -0,0 +1,29 @@
+#
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+#
+
+pkg.name: mgmt
+pkg.description: 'System-wide mcumgr management interfaces.'
+pkg.author: "Apache Mynewt <de...@mynewt.apache.org>"
+pkg.homepage: "http://mynewt.apache.org/"
+pkg.keywords:
+
+pkg.deps:
+ - '@apache-mynewt-core/encoding/tinycbor'
+ - '@apache-mynewt-core/kernel/os'
+ - '@mynewt-mcumgr/mgmt'
diff --git a/mgmt/src/mgmt.c b/mgmt/src/mgmt.c
new file mode 100644
index 0000000..ccea65a
--- /dev/null
+++ b/mgmt/src/mgmt.c
@@ -0,0 +1,170 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+#include <string.h>
+#include "cbor.h"
+#include "mgmt/endian.h"
+#include "mgmt/mgmt.h"
+
+static struct mgmt_group *mgmt_group_list;
+static struct mgmt_group *mgmt_group_list_end;
+
+void *
+mgmt_streamer_alloc_rsp(struct mgmt_streamer *streamer, const void *req)
+{
+ return streamer->cfg->alloc_rsp(req, streamer->cb_arg);
+}
+
+void
+mgmt_streamer_trim_front(struct mgmt_streamer *streamer, void *buf, size_t len)
+{
+ streamer->cfg->trim_front(buf, len, streamer->cb_arg);
+}
+
+void
+mgmt_streamer_reset_buf(struct mgmt_streamer *streamer, void *buf)
+{
+ streamer->cfg->reset_buf(buf, streamer->cb_arg);
+}
+
+int
+mgmt_streamer_write_at(struct mgmt_streamer *streamer, size_t offset,
+ const void *data, int len)
+{
+ return streamer->cfg->write_at(streamer->writer, offset, data, len,
+ streamer->cb_arg);
+}
+
+int
+mgmt_streamer_init_reader(struct mgmt_streamer *streamer, void *buf)
+{
+ return streamer->cfg->init_reader(streamer->reader, buf, streamer->cb_arg);
+}
+
+int
+mgmt_streamer_init_writer(struct mgmt_streamer *streamer, void *buf)
+{
+ return streamer->cfg->init_writer(streamer->writer, buf, streamer->cb_arg);
+}
+
+void
+mgmt_streamer_free_buf(struct mgmt_streamer *streamer, void *buf)
+{
+ streamer->cfg->free_buf(buf, streamer->cb_arg);
+}
+
+void
+mgmt_register_group(struct mgmt_group *group)
+{
+ if (mgmt_group_list_end == NULL) {
+ mgmt_group_list = group;
+ } else {
+ mgmt_group_list_end->mg_next = group;
+ }
+ mgmt_group_list_end = group;
+}
+
+static struct mgmt_group *
+mgmt_find_group(uint16_t group_id)
+{
+ struct mgmt_group *group;
+
+ for (group = mgmt_group_list; group != NULL; group = group->mg_next) {
+ if (group->mg_group_id == group_id) {
+ return group;
+ }
+ }
+
+ return NULL;
+}
+
+const struct mgmt_handler *
+mgmt_find_handler(uint16_t group_id, uint16_t command_id)
+{
+ const struct mgmt_group *group;
+
+ group = mgmt_find_group(group_id);
+ if (group == NULL) {
+ return NULL;
+ }
+
+ if (command_id >= group->mg_handlers_count) {
+ return NULL;
+ }
+
+ return &group->mg_handlers[command_id];
+}
+
+int
+mgmt_write_rsp_status(struct mgmt_ctxt *cbuf, int errcode)
+{
+ int rc;
+
+ rc = cbor_encode_text_stringz(&cbuf->encoder, "rc");
+ if (rc != 0) {
+ return rc;
+ }
+
+ rc = cbor_encode_int(&cbuf->encoder, errcode);
+ if (rc != 0) {
+ return rc;
+ }
+
+ return 0;
+}
+
+int
+mgmt_err_from_cbor(int cbor_status)
+{
+ switch (cbor_status) {
+ case CborNoError: return MGMT_ERR_EOK;
+ case CborErrorOutOfMemory: return MGMT_ERR_ENOMEM;
+ default: return MGMT_ERR_EUNKNOWN;
+ }
+}
+
+int
+mgmt_ctxt_init(struct mgmt_ctxt *cbuf, struct mgmt_streamer *streamer)
+{
+ int rc;
+
+ rc = cbor_parser_cust_reader_init(streamer->reader, 0, &cbuf->parser,
+ &cbuf->it);
+ if (rc != CborNoError) {
+ return mgmt_err_from_cbor(rc);
+ }
+
+ cbor_encoder_cust_writer_init(&cbuf->encoder, streamer->writer, 0);
+
+ return 0;
+}
+
+void
+mgmt_ntoh_hdr(struct mgmt_hdr *hdr)
+{
+ hdr->nh_len = ntohs(hdr->nh_len);
+ hdr->nh_group = ntohs(hdr->nh_group);
+}
+
+void
+mgmt_hton_hdr(struct mgmt_hdr *hdr)
+{
+ hdr->nh_len = htons(hdr->nh_len);
+ hdr->nh_group = htons(hdr->nh_group);
+}
diff --git a/mgmt/syscfg.yml b/mgmt/syscfg.yml
new file mode 100644
index 0000000..30ccc02
--- /dev/null
+++ b/mgmt/syscfg.yml
@@ -0,0 +1,18 @@
+#
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+#
--
To stop receiving notification emails like this one, please contact
"commits@mynewt.apache.org" <co...@mynewt.apache.org>.