Implement JSON configuration file
authorManuel Bachmann <manuel.bachmann@iot.bzh>
Fri, 8 Jul 2016 14:31:01 +0000 (16:31 +0200)
committerYannick Gicquel <yannick.gicquel@iot.bzh>
Tue, 11 Oct 2016 15:09:07 +0000 (17:09 +0200)
A JSON configuration file can now be read as "/etc/
pulseaudio-agl.cfg", and a sample one is provided by the
package. If it exists and is valid, it will override the
default configuration.

As a result, the module now depends on json-c.

Change-Id: I3f5deccc6f9b89cbd54bea7004271b8aec727538
Signed-off-by: Manuel Bachmann <manuel.bachmann@iot.bzh>
CMakeLists.txt
config.c
node.c
node.h

index 4f1ce33..f03a65d 100644 (file)
@@ -27,18 +27,18 @@ add_compile_options(-ffunction-sections -fdata-sections)
 
 ###########################################################################
 
-PKG_CHECK_MODULES(pulseaudio-module-devel REQUIRED pulseaudio-module-devel)
+PKG_CHECK_MODULES(dependencies REQUIRED json-c pulseaudio-module-devel)
 
-ADD_DEFINITIONS(${pulseaudio-module-devel_CFLAGS})
-SET(include_dirs ${INCLUDE_DIRS} ${pulseaudio-module-devel_INCLUDE_DIRS})
-SET(link_libraries ${LINK_LIBRARIES} ${pulseaudio-module-devel_LIBRARIES})
-STRING(REGEX REPLACE ";" " " link_flags "${pulseaudio-module-devel_LDFLAGS}" "")
+ADD_DEFINITIONS(${dependencies_CFLAGS})
+SET(include_dirs ${INCLUDE_DIRS} ${dependencies_INCLUDE_DIRS})
+SET(link_libraries ${LINK_LIBRARIES} ${dependencies_LIBRARIES})
+STRING(REGEX REPLACE ";" " " link_flags "${dependencies_LDFLAGS}" "")
 
 SET(plugin_install_dir ${CMAKE_INSTALL_LIBDIR}/pulse-6.0/modules)
 
 ############################################################
 
-ADD_LIBRARY(agl-audio-plugin MODULE module.c audiomgr.c classify.c config.c discover.c loopback.c node.c router.c socketif.c switch.c tracker.c utils.c zone.c)
+ADD_LIBRARY(agl-audio-plugin MODULE module.c audiomgr.c classify.c config.c discover.c loopback.c node.c router.c socketif.c switch.c tracker.c utils.c zone.c pulseaudio-agl.cfg)
 INCLUDE_DIRECTORIES(${include_dirs})
 TARGET_LINK_LIBRARIES(agl-audio-plugin ${link_libraries})
 SET_TARGET_PROPERTIES(agl-audio-plugin PROPERTIES PREFIX ""
@@ -46,3 +46,5 @@ SET_TARGET_PROPERTIES(agl-audio-plugin PROPERTIES PREFIX ""
 
 INSTALL(TARGETS agl-audio-plugin
         LIBRARY DESTINATION ${plugin_install_dir})
+INSTALL(FILES pulseaudio-agl.cfg
+        DESTINATION ${CMAKE_INSTALL_SYSCONFDIR}/pulse)
index bbd8110..c7ca146 100644 (file)
--- a/config.c
+++ b/config.c
 #include "config.h"
 #include "zone.h"
 
+#include <sys/mman.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <json/json.h>
+
+#include <pulsecore/core-util.h>
+#include <pulsecore/pulsecore-config.h>
+
 bool use_default_configuration (struct userdata *);
 
 const char *agl_config_file_get_path (const char *dir, const char *file, char *buf, size_t len)
@@ -58,8 +66,113 @@ bool agl_config_parse_file (struct userdata *u, const char *path)
 
 bool agl_config_dofile (struct userdata *u, const char *path)
 {
-       /* TODO */
-       return false;
+       int filefd;
+       struct stat filestat;
+       void *filemap;
+       struct json_object *fjson, *root, *sct, *elt, *selt;
+       const char *val;
+       int len, i;
+
+       pa_assert (u);
+       pa_assert (path);
+
+       filefd = open (path, O_RDONLY);
+       if (filefd == -1) {
+               pa_log_info ("could not find configuration file '%s'", path);
+               return false;
+       }
+       fstat (filefd, &filestat);
+
+       filemap = mmap (NULL, filestat.st_size, PROT_READ, MAP_PRIVATE, filefd, 0);
+       if (filemap == MAP_FAILED) {
+               pa_log_info ("could not map configuration file in memory");
+               return false;
+       }
+
+        /* is the file a JSON file, and if it is, does it have a "config" root ? */
+       fjson = json_tokener_parse (filemap);
+       root = json_object_object_get (fjson, "config");
+       if (!fjson || !root) {
+               pa_log_info ("could not parse JSON configuration file");
+               return false;
+       }
+
+        /* [zones] section */
+       sct = json_object_object_get (root, "zones");
+       if (!sct) return false;
+       len = json_object_array_length (sct);
+       for (i = 0; i < len; i++) {
+               elt = json_object_array_get_idx (sct, i);
+               val = json_object_get_string (elt);
+               agl_zoneset_add_zone (u, val, (uint32_t)i);
+       }
+
+        /* [rtgroups] section */
+       sct = json_object_object_get (root, "rtgroups");
+       if (!sct) return false;
+       len = json_object_array_length (sct);
+       for (i = 0; i < len; i++) {
+               const char *name, *type, *card, *accept_fct, *effect_fct;
+               elt = json_object_array_get_idx (sct, i);
+                name = json_object_get_string (json_object_object_get (elt, "name"));
+                type = json_object_get_string (json_object_object_get (elt, "type"));
+                card = json_object_get_string (json_object_object_get (elt, "card"));
+                accept_fct = json_object_get_string (json_object_object_get (elt, "accept_fct"));
+                effect_fct = json_object_get_string (json_object_object_get (elt, "effect_fct"));
+               agl_router_create_rtgroup (u, pa_streq(type, "OUTPUT") ? agl_output : agl_input,
+                                             name, card,
+                                             pa_streq(type, "phone") ? agl_router_phone_accept : NULL,
+                                             pa_streq(type, "phone") ? agl_router_phone_effect : NULL);
+       }
+
+        /* [classmap] section */
+       sct = json_object_object_get (root, "classmap");
+       if (!sct) return false;
+       len = json_object_array_length (sct);
+       for (i = 0; i < len; i++) {
+               const char *class, *type, *rtgroup;
+               int zone;
+               elt = json_object_array_get_idx (sct, i);
+                class = json_object_get_string (json_object_object_get (elt, "class"));
+                type = json_object_get_string (json_object_object_get (elt, "type"));
+                zone = json_object_get_int (json_object_object_get (elt, "zone"));
+                rtgroup = json_object_get_string (json_object_object_get (elt, "rtgroup"));
+               agl_router_assign_class_to_rtgroup (u, agl_node_type_from_str (class),
+                                                      zone,
+                                                      pa_streq(type, "OUTPUT") ? agl_output : agl_input,
+                                                      rtgroup);
+       }
+
+        /* [typemap] section */
+       sct = json_object_object_get (root, "typemap");
+       if (!sct) return false;
+       len = json_object_array_length (sct);
+       for (i = 0; i < len; i++) {
+               const char *id, *type;
+               elt = json_object_array_get_idx (sct, i);
+                id = json_object_get_string (json_object_object_get (elt, "id"));
+                type = json_object_get_string (json_object_object_get (elt, "type"));
+               agl_nodeset_add_role (u, id, agl_node_type_from_str (type), NULL);
+       }
+
+        /* [priormap] section */
+       sct = json_object_object_get (root, "priormap");
+       if (!sct) return false;
+       len = json_object_array_length (sct);
+       for (i = 0; i < len; i++) {
+               const char *class;
+               int priority;
+               elt = json_object_array_get_idx (sct, i);
+                class = json_object_get_string (json_object_object_get (elt, "class"));
+                priority = json_object_get_int (json_object_object_get (elt, "priority"));
+               agl_router_assign_class_priority (u, agl_node_type_from_str (class), priority);
+       }
+
+       json_object_object_del (fjson, "");
+       munmap (filemap, filestat.st_size);
+       close (filefd);
+
+       return true;
 }
 
 
@@ -82,6 +195,13 @@ static rtgroup_def rtgroups[] = {
          agl_router_phone_effect
        },
 
+       { agl_input,
+         "default",
+         "pci",
+         NULL,
+         NULL
+       },
+
        { 0, NULL, NULL, NULL, NULL }
 };
 
diff --git a/node.c b/node.c
index c85d512..c46f842 100644 (file)
--- a/node.c
+++ b/node.c
@@ -23,6 +23,8 @@
 #include "router.h"
 
 #include <pulsecore/idxset.h>
+#include <pulsecore/core-util.h>
+#include <pulsecore/pulsecore-config.h>
 
 agl_nodeset *agl_nodeset_init (struct userdata *u)
 {
@@ -146,6 +148,39 @@ void agl_node_destroy (struct userdata *u, agl_node *node)
        pa_xfree (node);
 }
 
+
+agl_node_type agl_node_type_from_str (const char *str)
+{
+       agl_node_type type;
+
+       pa_assert (str);
+
+       if (pa_streq (str, "agl_radio"))
+               type = agl_radio;
+       else if (pa_streq (str, "agl_music"))
+               type = agl_player;
+       else if (pa_streq (str, "agl_navigator"))
+               type = agl_navigator;
+       else if (pa_streq (str, "agl_game"))
+               type = agl_game;
+       else if (pa_streq (str, "agl_browser"))
+               type = agl_browser;
+       else if (pa_streq (str, "agl_camera"))
+               type = agl_camera;
+       else if (pa_streq (str, "agl_phone"))
+               type = agl_phone;
+       else if (pa_streq (str, "agl_alert"))
+               type = agl_alert;
+       else if (pa_streq (str, "agl_event"))
+               type = agl_event;
+       else if (pa_streq (str, "agl_system"))
+               type = agl_system;
+       else
+               type = agl_node_type_unknown;
+
+       return type;
+}
+
 const char *agl_node_type_str (agl_node_type type)
 {
        switch (type) {
diff --git a/node.h b/node.h
index 837ff5a..1e37d85 100644 (file)
--- a/node.h
+++ b/node.h
@@ -105,6 +105,7 @@ int agl_nodeset_add_role (struct userdata *, const char *, agl_node_type, agl_no
 
 agl_node *agl_node_create (struct userdata *, agl_node *);
 void agl_node_destroy (struct userdata *, agl_node *);
+agl_node_type agl_node_type_from_str (const char *);
 const char *agl_node_type_str (agl_node_type);
 const char *agl_node_direction_str (agl_direction);