diff --git a/packages/base/any/onlp/src/onlplib/.module b/packages/base/any/onlp/src/onlplib/.module
index f3d0bc49..1d11f804 100644
--- a/packages/base/any/onlp/src/onlplib/.module
+++ b/packages/base/any/onlp/src/onlplib/.module
@@ -1,2 +1,2 @@
name: onlplib
-depends: cjson_util
+depends: [ cjson_util, BigList ]
diff --git a/packages/base/any/onlp/src/onlplib/module/inc/onlplib/file_uds.h b/packages/base/any/onlp/src/onlplib/module/inc/onlplib/file_uds.h
new file mode 100644
index 00000000..3760ad46
--- /dev/null
+++ b/packages/base/any/onlp/src/onlplib/module/inc/onlplib/file_uds.h
@@ -0,0 +1,109 @@
+/************************************************************
+ *
+ *
+ * Copyright 2017 Big Switch Networks, Inc.
+ *
+ * Licensed under the Eclipse Public License, Version 1.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.eclipse.org/legal/epl-v10.html
+ *
+ * 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.
+ *
+ *
+ ************************************************************
+ *
+ * This module provides a domain socket registration and handling
+ * service.
+ *
+ * Clients register a filesystem path they wish to publish
+ * as a domain socket their handlers are called when
+ * the domain socket is accessed.
+ *
+ * Some ONLP data can only be retreived by accessing another process
+ * Which cannot be part of the ONLP layer itself for various reasons.
+ *
+ * The ONLP File APIs support unix domain sockets for all operations
+ * as if they were regular files. This module provides a common
+ * framework for clients to implement the server side of the
+ * domain socket as well.
+ *
+ * Some examples of how this is used:
+ * - Reporting the switch internal thermal temperature
+ * - This can only be accessed by the code managing the switch.
+ * - In this case the switch management agent exports a domain socket
+ * that reports the temperature when the socket is read and the
+ * thermali implementation uses that domain socket to satisfy
+ * the request for the OID.
+ *
+ * - SFP Access through the switch
+ * - Some platforms implement SFP I2C access through a bus connected
+ * to the switch itself.
+ * - Only the agent running the switch can access the SFP eeproms.
+ * - In this case the strategy is for the switch agent to export domain
+ * sockets for each SFP which can be used to read the SFP status/eeprom
+ * etc. The SFPI interface then reads these domain sockets to get the
+ * required information.
+ *
+ * Standardizing on this method allows all system ONLP clients to access
+ * all data, even if that data is present only in seperate processes.
+ *
+ *
+ ***********************************************************/
+#ifndef __ONLPLIB_FILE_UDS_H__
+#define __ONLPLIB_FILE_UDS_H__
+
+#include
+
+/**
+ * @brief This is the handle for the service object.
+ */
+typedef struct onlp_file_uds_s onlp_file_uds_t;
+
+
+/**
+ * @brief Create a domain socket service manager.
+ * @param fuds Receives the service object pointer.
+ */
+int onlp_file_uds_create(onlp_file_uds_t** fuds);
+
+/**
+ * @brief This is the prototype for your service handler function.
+ * @param fd The client file descriptor. This is the descriptor accepted
+ * on your behalf by the service manager when someone attempts to open your domain socket.
+ * @param cookie Private callback pointer.
+ */
+typedef int (*onlp_file_uds_handler_t)(int fd, void* cookie);
+
+/**
+ * @brief Add a domain socket service path to an existing service manager.
+ * @param fuds The service manager
+ * @param path The domain socket filesystem path you would like to register.
+ * @param handler The connection handler for the domain socket.
+ * @param cookie Cookie for you connection handler.
+ */
+int onlp_file_uds_add(onlp_file_uds_t* fuds,
+ const char* path,
+ onlp_file_uds_handler_t handler, void* cookie);
+
+/**
+ * @brief Remove a domain socket service path from an existing service manager.
+ * @param fuds The service manager.
+ * @param path The domain socket service path to remove.
+ */
+void onlp_file_uds_remove(onlp_file_uds_t* fuds, const char* path);
+
+/**
+ * @brief Destroy a service manager object.
+ * @param fuds The object pointer.
+ * @notes All registered services will be destroyed.
+ */
+void onlp_file_uds_destroy(onlp_file_uds_t* fuds);
+
+#endif /* __ONLPLIB_FILE_UDS_H__ */
diff --git a/packages/base/any/onlp/src/onlplib/module/src/file_uds.c b/packages/base/any/onlp/src/onlplib/module/src/file_uds.c
new file mode 100644
index 00000000..bd344d89
--- /dev/null
+++ b/packages/base/any/onlp/src/onlplib/module/src/file_uds.c
@@ -0,0 +1,405 @@
+/************************************************************
+ *
+ *
+ * Copyright 2017 Big Switch Networks, Inc.
+ *
+ * Licensed under the Eclipse Public License, Version 1.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.eclipse.org/legal/epl-v10.html
+ *
+ * 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
+#include "onlplib_log.h"
+
+#include
+#include
+
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+/**
+ * Read/write an eventfd.
+ */
+static void
+eventfd_write__(int fd)
+{
+ uint64_t val = 1;
+ write(fd, &val, sizeof(val));
+}
+static void
+eventfd_read__(int fd)
+{
+ uint64_t val;
+ read(fd, &val, sizeof(val));
+}
+
+
+/**
+ * This represents a single domain socket service.
+ */
+typedef struct onlp_file_uds_service_s {
+ /** domain socket file path */
+ const char* path;
+
+ /** Listening descriptor */
+ int lfd;
+
+ /** client handler */
+ onlp_file_uds_handler_t handler;
+ void* cookie;
+
+ /** service is active. */
+ int active;
+
+} onlp_file_uds_service_t;
+
+/**
+ * Destroy a file service.
+ */
+static void
+onlp_file_uds_service_clear__(onlp_file_uds_service_t* p)
+{
+ if(p) {
+ if(p->lfd > 0) {
+ close(p->lfd);
+ }
+ if(p->path) {
+ aim_free((char*)p->path);
+ }
+ memset(p, 0, sizeof(*p));
+ }
+}
+static void
+onlp_file_uds_service_destroy__(onlp_file_uds_service_t* p)
+{
+ if(p) {
+ onlp_file_uds_service_clear__(p);
+ aim_free(p);
+ }
+}
+
+/**
+ * Create a file service.
+ */
+static int
+onlp_file_uds_service_create__(onlp_file_uds_service_t** rvp,
+ const char* path,
+ onlp_file_uds_handler_t handler, void* cookie)
+{
+ struct sockaddr_un addr;
+
+ onlp_file_uds_service_t* rv = aim_zmalloc(sizeof(*rv));
+
+ rv->path = aim_strdup(path);
+ char* cmd = aim_fstrdup("mkdir -p `dirname %s`", path);
+ if(system(cmd) != 0) {
+ AIM_LOG_ERROR("Failed to create uds directory for %s", path);
+ aim_free(cmd);
+ goto failed;
+ }
+ aim_free(cmd);
+
+ if ((rv->lfd = socket(AF_UNIX, SOCK_STREAM | SOCK_NONBLOCK, 0)) == -1) {
+ AIM_LOG_ERROR("socket: %{errno}", errno);
+ goto failed;
+ }
+
+ memset(&addr, 0, sizeof(addr));
+ addr.sun_family = AF_UNIX;
+ strncpy(addr.sun_path, path, sizeof(addr.sun_path)-1);
+ unlink(path);
+
+ if(bind(rv->lfd, (struct sockaddr*)&addr, sizeof(addr)) == -1) {
+ AIM_LOG_ERROR("bind: %{errno}", errno);
+ goto failed;
+ }
+
+ if (listen(rv->lfd, 1) == -1) {
+ AIM_LOG_ERROR("listen: %{errno}", errno);
+ goto failed;
+ }
+
+ rv->handler = handler;
+ rv->cookie = cookie;
+ *rvp = rv;
+
+ return 0;
+
+ failed:
+ onlp_file_uds_service_destroy__(rv);
+ return -1;
+}
+
+/**
+ * This is the control object for a UDS service group.
+ */
+struct onlp_file_uds_s {
+
+ /** Thread signal. Used to wake up the service thread when required. */
+ int eventfd;
+
+ /** Service worker thread */
+ pthread_t thread;
+ volatile int running;
+ volatile int terminate;
+
+ /** Service client list */
+ biglist_locked_t* list;
+};
+
+
+/**
+ * Add a descriptor to an epoll set.
+ */
+static int
+epoll_add__(int epoll_fd, int add_fd, uint32_t events, void* data,
+ int* counter, const char* name)
+{
+ struct epoll_event ev = {0};
+ ev.data.ptr = data;
+ ev.events = events;
+ if(epoll_ctl(epoll_fd, EPOLL_CTL_ADD, add_fd, &ev) != 0) {
+ if(errno != EEXIST) {
+ AIM_LOG_ERROR("epoll_ctl returned %{errno} for %s", errno, name);
+ return -1;
+ }
+ }
+
+ if(counter) {
+ (*counter)++;
+ }
+
+ return 0;
+}
+
+/**
+ * Service a connection.
+ */
+static void
+accept__(onlp_file_uds_service_t* ufp)
+{
+ int fd;
+
+ if((fd = accept(ufp->lfd, NULL, 0)) > 0) {
+ ufp->handler(fd, ufp->cookie);
+ }
+ close(fd);
+}
+
+/**
+ * This is the service list entry.
+ */
+
+/**
+ * The service worker thread.
+ *
+ * All registered services are polled for incoming connections
+ * and handled in series.
+ *
+ * These are designed for simple transactions and not
+ * long-lived connections.
+ */
+static void*
+uds_thread_worker__(void* p)
+{
+ volatile onlp_file_uds_t* control = (onlp_file_uds_t*)p;
+
+ int epollfd;
+ if((epollfd = epoll_create(256)) == -1) {
+ AIM_LOG_ERROR("epoll_create(): %{errno}", errno);
+ return NULL;
+ }
+
+ control->running = 1;
+ for(;;) {
+
+ biglist_t* ble;
+ onlp_file_uds_service_t* ufp;
+ int nfd = 0;
+
+ if(control->terminate) {
+ /** Request for termination. */
+ break;
+ }
+
+ /** control->eventfd wakes us up */
+ if(epoll_add__(epollfd, control->eventfd, EPOLLIN, NULL, &nfd, "eventfd") < 0) {
+ return NULL;
+ }
+
+ /** Add all active descriptors. */
+ biglist_lock(control->list);
+ BIGLIST_FOREACH_DATA(ble, control->list->list, onlp_file_uds_service_t*, ufp) {
+ switch(ufp->active)
+ {
+ case 1:
+ /* Service is active. Wait on it. */
+ epoll_add__(epollfd, ufp->lfd, EPOLLIN, ufp, &nfd, ufp->path);
+ break;
+ case -1:
+ /* Service deletion request. */
+ AIM_LOG_MSG("Removing %s...", ufp->path);
+ epoll_ctl(epollfd, EPOLL_CTL_DEL, ufp->lfd, NULL);
+ onlp_file_uds_service_clear__(ufp);
+ break;
+ case 0:
+ /* Service is inactive. */
+ break;
+ }
+ }
+
+ biglist_unlock(control->list);
+
+ struct epoll_event* events = aim_zmalloc(sizeof(*events)*nfd);
+ int rv = epoll_wait(epollfd, events, nfd, -1);
+
+ if(rv < 0) {
+ if(errno != EINTR) {
+ AIM_LOG_ERROR("epoll_wait() returned %{errno}", errno);
+ break;
+ }
+ }
+ else if(rv == 0) {
+ /** Shouldn't happen with infinite timeout */
+ }
+ else {
+ int i;
+ for(i = 0; i < rv; i++) {
+ if(events[i].events & EPOLLIN) {
+ onlp_file_uds_service_t* ufp = (onlp_file_uds_service_t*)events[i].data.ptr;
+ if(ufp == NULL) {
+ eventfd_read__(control->eventfd);
+ }
+ else {
+ if(ufp->active == 1) {
+ accept__(ufp);
+ }
+ }
+ }
+ }
+ }
+ aim_free(events);
+ }
+ control->running = 0;
+ return NULL;
+}
+
+int
+onlp_file_uds_create(onlp_file_uds_t** rvp)
+{
+ onlp_file_uds_t* rv = aim_zmalloc(sizeof(*rv));
+ if((rv->eventfd = eventfd(0, 0)) == -1) {
+ AIM_LOG_ERROR("eventfd: %{errno}", errno);
+ goto failed;
+ }
+ if((rv->list = biglist_locked_create()) == NULL) {
+ goto failed;
+ }
+
+ rv->running = 0;
+
+ if(pthread_create(&rv->thread, NULL, uds_thread_worker__, rv) != 0) {
+ AIM_LOG_ERROR("pthread_create failed: %{errno}", errno);
+ goto failed;
+ }
+
+ *rvp = rv;
+ return 0;
+
+ failed:
+ onlp_file_uds_destroy(rv);
+ return -1;
+}
+
+void
+onlp_file_uds_destroy(onlp_file_uds_t* p)
+{
+ if(p) {
+ if(p->running == 1) {
+ p->terminate = 1;
+ eventfd_write__(p->eventfd);
+ pthread_join(p->thread, NULL);
+ }
+ biglist_locked_free_all(p->list, (biglist_free_f)onlp_file_uds_service_destroy__);
+ aim_free(p);
+ }
+}
+
+static onlp_file_uds_service_t*
+find_uds_locked__(biglist_t* list, const char* path)
+{
+ biglist_t* ble;
+ onlp_file_uds_service_t* ufp;
+ BIGLIST_FOREACH_DATA(ble, list, onlp_file_uds_service_t*, ufp) {
+ if(ufp->path) {
+ if(!strcmp(path, ufp->path)) {
+ return ufp;
+ }
+ }
+ }
+ return NULL;
+}
+
+int
+onlp_file_uds_add(onlp_file_uds_t* fuds, const char* path,
+ onlp_file_uds_handler_t handler, void* cookie)
+{
+ int rv = 0;
+ biglist_lock(fuds->list);
+ if(find_uds_locked__(fuds->list->list, path)) {
+ AIM_LOG_ERROR("Cannot add duplicate UDS for %s", path);
+ rv = -1;
+ }
+ else {
+ onlp_file_uds_service_t* ufp;
+ if(onlp_file_uds_service_create__(&ufp, path, handler, cookie) >= 0) {
+ ufp->active = 1;
+ fuds->list->list = biglist_append(fuds->list->list, ufp);
+ }
+ }
+ biglist_unlock(fuds->list);
+ eventfd_write__(fuds->eventfd);
+ return rv;
+}
+
+void
+onlp_file_uds_remove(onlp_file_uds_t* fuds, const char* path)
+{
+ biglist_lock(fuds->list);
+ onlp_file_uds_service_t* ufp = find_uds_locked__(fuds->list->list, path);
+ if(ufp) {
+ /** Request deactivation */
+ ufp->active = -1;
+ }
+ biglist_unlock(fuds->list);
+}