├── .gitignore ├── CMakeLists.txt ├── COPYING ├── README.md ├── build ├── .gitignore └── modules │ ├── FindLIBROXML.cmake │ ├── FindLIBUBOX.cmake │ ├── FindLIBUBUS.cmake │ └── FindUCI.cmake ├── config └── freenetconfd ├── include └── freenetconfd │ ├── datastore.h │ ├── freenetconfd.h │ ├── netconf.h │ └── plugin.h └── src ├── config.c ├── config.h ├── connection.c ├── connection.h ├── datastore.c ├── freenetconfd.c ├── messages.h ├── methods.c ├── methods.h ├── modules.c ├── modules.h ├── netconf.c ├── netconf.h ├── ubus.c └── ubus.h /.gitignore: -------------------------------------------------------------------------------- 1 | # NOTE! Don't add files that are generated in specific 2 | # subdirectories here. Add them in the ".gitignore" file 3 | # in that subdirectory instead. 4 | # 5 | # Normal rules 6 | # 7 | 8 | *.rej 9 | *.orig 10 | *.a 11 | *.o 12 | *.su 13 | *~ 14 | *.swp 15 | *.patch 16 | *.bin 17 | *.so 18 | 19 | # 20 | # Generated files 21 | # 22 | 23 | /build/CMakeCache.txt 24 | /build/CMakeFiles 25 | /build/cmake_install.cmake 26 | /build/Makefile 27 | /CMakeCache.txt 28 | /CMakeFiles 29 | /cmake_install.cmake 30 | /Makefile 31 | /bin 32 | 33 | # stgit 34 | patches-* 35 | .stgit-edit.txt 36 | 37 | # quilt 38 | patches 39 | series 40 | 41 | # ctags 42 | tags 43 | 44 | # kdevelop 45 | .kdev4 46 | freenetconfd.kdev4 47 | -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 2.6) 2 | PROJECT(freenetconfd) 3 | ADD_DEFINITIONS(-Os -Wall --std=gnu11 -Wmissing-declarations -D_GNU_SOURCE) 4 | INCLUDE_DIRECTORIES(include) 5 | 6 | FILE(MAKE_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/bin) 7 | 8 | SET(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${CMAKE_SOURCE_DIR}/build/modules") 9 | SET(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/bin) 10 | SET(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/lib/freenetconfd) 11 | 12 | SET(SOURCES 13 | src/freenetconfd.c 14 | include/freenetconfd/freenetconfd.h 15 | src/connection.c 16 | src/connection.h 17 | src/ubus.c 18 | src/ubus.h 19 | src/methods.c 20 | src/methods.h 21 | src/modules.c 22 | src/modules.h 23 | src/config.c 24 | src/config.h 25 | src/netconf.c 26 | src/netconf.h 27 | src/datastore.c 28 | include/freenetconfd/datastore.h 29 | include/freenetconfd/plugin.h 30 | include/freenetconfd/netconf.h 31 | ) 32 | 33 | ADD_EXECUTABLE(freenetconfd ${SOURCES}) 34 | TARGET_LINK_LIBRARIES(freenetconfd ${CMAKE_DL_LIBS}) 35 | 36 | FIND_PACKAGE(LIBUBOX REQUIRED) 37 | INCLUDE_DIRECTORIES(${LIBUBOX_INCLUDE_DIR}) 38 | TARGET_LINK_LIBRARIES(freenetconfd ${LIBUBOX_LIBRARIES}) 39 | 40 | FIND_PACKAGE(LIBUBUS REQUIRED) 41 | INCLUDE_DIRECTORIES(${LIBUBUS_INCLUDE_DIR}) 42 | TARGET_LINK_LIBRARIES(freenetconfd ${LIBUBUS_LIBRARIES}) 43 | 44 | FIND_PACKAGE(LIBROXML REQUIRED) 45 | INCLUDE_DIRECTORIES(${LIBROXML_INCLUDE_DIR}) 46 | TARGET_LINK_LIBRARIES(freenetconfd ${LIBROXML_LIBRARIES}) 47 | 48 | FIND_PACKAGE(UCI REQUIRED) 49 | INCLUDE_DIRECTORIES(${UCI_INCLUDE_DIR}) 50 | TARGET_LINK_LIBRARIES(freenetconfd ${UCI_LIBRARIES} ${CMAKE_DL_LIBS}) 51 | 52 | 53 | SET(PLUGIN_INCLUDE_FILES 54 | include/freenetconfd/freenetconfd.h 55 | include/freenetconfd/datastore.h 56 | include/freenetconfd/netconf.h 57 | include/freenetconfd/plugin.h 58 | ) 59 | INSTALL(FILES ${PLUGIN_INCLUDE_FILES} DESTINATION usr/include/freenetconfd) 60 | 61 | INSTALL(TARGETS freenetconfd RUNTIME DESTINATION usr/bin) 62 | -------------------------------------------------------------------------------- /COPYING: -------------------------------------------------------------------------------- 1 | GNU GENERAL PUBLIC LICENSE 2 | Version 2, June 1991 3 | 4 | Copyright (C) 1989, 1991 Free Software Foundation, Inc., 5 | 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 6 | Everyone is permitted to copy and distribute verbatim copies 7 | of this license document, but changing it is not allowed. 8 | 9 | Preamble 10 | 11 | The licenses for most software are designed to take away your 12 | freedom to share and change it. By contrast, the GNU General Public 13 | License is intended to guarantee your freedom to share and change free 14 | software--to make sure the software is free for all its users. This 15 | General Public License applies to most of the Free Software 16 | Foundation's software and to any other program whose authors commit to 17 | using it. (Some other Free Software Foundation software is covered by 18 | the GNU Lesser General Public License instead.) You can apply it to 19 | your programs, too. 20 | 21 | When we speak of free software, we are referring to freedom, not 22 | price. Our General Public Licenses are designed to make sure that you 23 | have the freedom to distribute copies of free software (and charge for 24 | this service if you wish), that you receive source code or can get it 25 | if you want it, that you can change the software or use pieces of it 26 | in new free programs; and that you know you can do these things. 27 | 28 | To protect your rights, we need to make restrictions that forbid 29 | anyone to deny you these rights or to ask you to surrender the rights. 30 | These restrictions translate to certain responsibilities for you if you 31 | distribute copies of the software, or if you modify it. 32 | 33 | For example, if you distribute copies of such a program, whether 34 | gratis or for a fee, you must give the recipients all the rights that 35 | you have. You must make sure that they, too, receive or can get the 36 | source code. And you must show them these terms so they know their 37 | rights. 38 | 39 | We protect your rights with two steps: (1) copyright the software, and 40 | (2) offer you this license which gives you legal permission to copy, 41 | distribute and/or modify the software. 42 | 43 | Also, for each author's protection and ours, we want to make certain 44 | that everyone understands that there is no warranty for this free 45 | software. If the software is modified by someone else and passed on, we 46 | want its recipients to know that what they have is not the original, so 47 | that any problems introduced by others will not reflect on the original 48 | authors' reputations. 49 | 50 | Finally, any free program is threatened constantly by software 51 | patents. We wish to avoid the danger that redistributors of a free 52 | program will individually obtain patent licenses, in effect making the 53 | program proprietary. To prevent this, we have made it clear that any 54 | patent must be licensed for everyone's free use or not licensed at all. 55 | 56 | The precise terms and conditions for copying, distribution and 57 | modification follow. 58 | 59 | GNU GENERAL PUBLIC LICENSE 60 | TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 61 | 62 | 0. This License applies to any program or other work which contains 63 | a notice placed by the copyright holder saying it may be distributed 64 | under the terms of this General Public License. The "Program", below, 65 | refers to any such program or work, and a "work based on the Program" 66 | means either the Program or any derivative work under copyright law: 67 | that is to say, a work containing the Program or a portion of it, 68 | either verbatim or with modifications and/or translated into another 69 | language. (Hereinafter, translation is included without limitation in 70 | the term "modification".) Each licensee is addressed as "you". 71 | 72 | Activities other than copying, distribution and modification are not 73 | covered by this License; they are outside its scope. The act of 74 | running the Program is not restricted, and the output from the Program 75 | is covered only if its contents constitute a work based on the 76 | Program (independent of having been made by running the Program). 77 | Whether that is true depends on what the Program does. 78 | 79 | 1. You may copy and distribute verbatim copies of the Program's 80 | source code as you receive it, in any medium, provided that you 81 | conspicuously and appropriately publish on each copy an appropriate 82 | copyright notice and disclaimer of warranty; keep intact all the 83 | notices that refer to this License and to the absence of any warranty; 84 | and give any other recipients of the Program a copy of this License 85 | along with the Program. 86 | 87 | You may charge a fee for the physical act of transferring a copy, and 88 | you may at your option offer warranty protection in exchange for a fee. 89 | 90 | 2. You may modify your copy or copies of the Program or any portion 91 | of it, thus forming a work based on the Program, and copy and 92 | distribute such modifications or work under the terms of Section 1 93 | above, provided that you also meet all of these conditions: 94 | 95 | a) You must cause the modified files to carry prominent notices 96 | stating that you changed the files and the date of any change. 97 | 98 | b) You must cause any work that you distribute or publish, that in 99 | whole or in part contains or is derived from the Program or any 100 | part thereof, to be licensed as a whole at no charge to all third 101 | parties under the terms of this License. 102 | 103 | c) If the modified program normally reads commands interactively 104 | when run, you must cause it, when started running for such 105 | interactive use in the most ordinary way, to print or display an 106 | announcement including an appropriate copyright notice and a 107 | notice that there is no warranty (or else, saying that you provide 108 | a warranty) and that users may redistribute the program under 109 | these conditions, and telling the user how to view a copy of this 110 | License. (Exception: if the Program itself is interactive but 111 | does not normally print such an announcement, your work based on 112 | the Program is not required to print an announcement.) 113 | 114 | These requirements apply to the modified work as a whole. If 115 | identifiable sections of that work are not derived from the Program, 116 | and can be reasonably considered independent and separate works in 117 | themselves, then this License, and its terms, do not apply to those 118 | sections when you distribute them as separate works. But when you 119 | distribute the same sections as part of a whole which is a work based 120 | on the Program, the distribution of the whole must be on the terms of 121 | this License, whose permissions for other licensees extend to the 122 | entire whole, and thus to each and every part regardless of who wrote it. 123 | 124 | Thus, it is not the intent of this section to claim rights or contest 125 | your rights to work written entirely by you; rather, the intent is to 126 | exercise the right to control the distribution of derivative or 127 | collective works based on the Program. 128 | 129 | In addition, mere aggregation of another work not based on the Program 130 | with the Program (or with a work based on the Program) on a volume of 131 | a storage or distribution medium does not bring the other work under 132 | the scope of this License. 133 | 134 | 3. You may copy and distribute the Program (or a work based on it, 135 | under Section 2) in object code or executable form under the terms of 136 | Sections 1 and 2 above provided that you also do one of the following: 137 | 138 | a) Accompany it with the complete corresponding machine-readable 139 | source code, which must be distributed under the terms of Sections 140 | 1 and 2 above on a medium customarily used for software interchange; or, 141 | 142 | b) Accompany it with a written offer, valid for at least three 143 | years, to give any third party, for a charge no more than your 144 | cost of physically performing source distribution, a complete 145 | machine-readable copy of the corresponding source code, to be 146 | distributed under the terms of Sections 1 and 2 above on a medium 147 | customarily used for software interchange; or, 148 | 149 | c) Accompany it with the information you received as to the offer 150 | to distribute corresponding source code. (This alternative is 151 | allowed only for noncommercial distribution and only if you 152 | received the program in object code or executable form with such 153 | an offer, in accord with Subsection b above.) 154 | 155 | The source code for a work means the preferred form of the work for 156 | making modifications to it. For an executable work, complete source 157 | code means all the source code for all modules it contains, plus any 158 | associated interface definition files, plus the scripts used to 159 | control compilation and installation of the executable. However, as a 160 | special exception, the source code distributed need not include 161 | anything that is normally distributed (in either source or binary 162 | form) with the major components (compiler, kernel, and so on) of the 163 | operating system on which the executable runs, unless that component 164 | itself accompanies the executable. 165 | 166 | If distribution of executable or object code is made by offering 167 | access to copy from a designated place, then offering equivalent 168 | access to copy the source code from the same place counts as 169 | distribution of the source code, even though third parties are not 170 | compelled to copy the source along with the object code. 171 | 172 | 4. You may not copy, modify, sublicense, or distribute the Program 173 | except as expressly provided under this License. Any attempt 174 | otherwise to copy, modify, sublicense or distribute the Program is 175 | void, and will automatically terminate your rights under this License. 176 | However, parties who have received copies, or rights, from you under 177 | this License will not have their licenses terminated so long as such 178 | parties remain in full compliance. 179 | 180 | 5. You are not required to accept this License, since you have not 181 | signed it. However, nothing else grants you permission to modify or 182 | distribute the Program or its derivative works. These actions are 183 | prohibited by law if you do not accept this License. Therefore, by 184 | modifying or distributing the Program (or any work based on the 185 | Program), you indicate your acceptance of this License to do so, and 186 | all its terms and conditions for copying, distributing or modifying 187 | the Program or works based on it. 188 | 189 | 6. Each time you redistribute the Program (or any work based on the 190 | Program), the recipient automatically receives a license from the 191 | original licensor to copy, distribute or modify the Program subject to 192 | these terms and conditions. You may not impose any further 193 | restrictions on the recipients' exercise of the rights granted herein. 194 | You are not responsible for enforcing compliance by third parties to 195 | this License. 196 | 197 | 7. If, as a consequence of a court judgment or allegation of patent 198 | infringement or for any other reason (not limited to patent issues), 199 | conditions are imposed on you (whether by court order, agreement or 200 | otherwise) that contradict the conditions of this License, they do not 201 | excuse you from the conditions of this License. If you cannot 202 | distribute so as to satisfy simultaneously your obligations under this 203 | License and any other pertinent obligations, then as a consequence you 204 | may not distribute the Program at all. For example, if a patent 205 | license would not permit royalty-free redistribution of the Program by 206 | all those who receive copies directly or indirectly through you, then 207 | the only way you could satisfy both it and this License would be to 208 | refrain entirely from distribution of the Program. 209 | 210 | If any portion of this section is held invalid or unenforceable under 211 | any particular circumstance, the balance of the section is intended to 212 | apply and the section as a whole is intended to apply in other 213 | circumstances. 214 | 215 | It is not the purpose of this section to induce you to infringe any 216 | patents or other property right claims or to contest validity of any 217 | such claims; this section has the sole purpose of protecting the 218 | integrity of the free software distribution system, which is 219 | implemented by public license practices. Many people have made 220 | generous contributions to the wide range of software distributed 221 | through that system in reliance on consistent application of that 222 | system; it is up to the author/donor to decide if he or she is willing 223 | to distribute software through any other system and a licensee cannot 224 | impose that choice. 225 | 226 | This section is intended to make thoroughly clear what is believed to 227 | be a consequence of the rest of this License. 228 | 229 | 8. If the distribution and/or use of the Program is restricted in 230 | certain countries either by patents or by copyrighted interfaces, the 231 | original copyright holder who places the Program under this License 232 | may add an explicit geographical distribution limitation excluding 233 | those countries, so that distribution is permitted only in or among 234 | countries not thus excluded. In such case, this License incorporates 235 | the limitation as if written in the body of this License. 236 | 237 | 9. The Free Software Foundation may publish revised and/or new versions 238 | of the General Public License from time to time. Such new versions will 239 | be similar in spirit to the present version, but may differ in detail to 240 | address new problems or concerns. 241 | 242 | Each version is given a distinguishing version number. If the Program 243 | specifies a version number of this License which applies to it and "any 244 | later version", you have the option of following the terms and conditions 245 | either of that version or of any later version published by the Free 246 | Software Foundation. If the Program does not specify a version number of 247 | this License, you may choose any version ever published by the Free Software 248 | Foundation. 249 | 250 | 10. If you wish to incorporate parts of the Program into other free 251 | programs whose distribution conditions are different, write to the author 252 | to ask for permission. For software which is copyrighted by the Free 253 | Software Foundation, write to the Free Software Foundation; we sometimes 254 | make exceptions for this. Our decision will be guided by the two goals 255 | of preserving the free status of all derivatives of our free software and 256 | of promoting the sharing and reuse of software generally. 257 | 258 | NO WARRANTY 259 | 260 | 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY 261 | FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN 262 | OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES 263 | PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED 264 | OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF 265 | MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS 266 | TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE 267 | PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, 268 | REPAIR OR CORRECTION. 269 | 270 | 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING 271 | WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR 272 | REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, 273 | INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING 274 | OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED 275 | TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY 276 | YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER 277 | PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE 278 | POSSIBILITY OF SUCH DAMAGES. 279 | 280 | END OF TERMS AND CONDITIONS 281 | 282 | How to Apply These Terms to Your New Programs 283 | 284 | If you develop a new program, and you want it to be of the greatest 285 | possible use to the public, the best way to achieve this is to make it 286 | free software which everyone can redistribute and change under these terms. 287 | 288 | To do so, attach the following notices to the program. It is safest 289 | to attach them to the start of each source file to most effectively 290 | state the exclusion of warranty; and each file should have at least 291 | the "copyright" line and a pointer to where the full notice is found. 292 | 293 | freenetconfd is GPLv2 licensed NETCONF server implementation 294 | Copyright (C) 2014 Sartura, Ltd. 295 | Copyright (C) 2014 Cisco Systems, Inc. 296 | 297 | This program is free software: you can redistribute it and/or modify 298 | it under the terms of the GNU General Public License as published by 299 | the Free Software Foundation, either version 2 of the License, or 300 | (at your option) any later version. 301 | 302 | This program is distributed in the hope that it will be useful, 303 | but WITHOUT ANY WARRANTY; without even the implied warranty of 304 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 305 | GNU General Public License for more details. 306 | 307 | You should have received a copy of the GNU General Public License 308 | along with this program. If not, see . 309 | 310 | freenetconfd Copyright (C) 2014 Sartura, Ltd. 311 | This program comes with ABSOLUTELY NO WARRANTY. 312 | This is free software, and you are welcome to redistribute it 313 | under certain conditions. 314 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | freenetconfd 2 | ============ 3 | 4 | freenetconfd is a NETCONF server and it enables device provisioning through 5 | NETCONF protocol. 6 | 7 | - written in C 8 | - extensible with plugins 9 | - interoperable with Tail-f NCS, OpenDaylight (ODL) and testconf 10 | 11 | ### dependencies 12 | 13 | Install *autoconf*, *cmake*, *git*, *json-c*, *libtool*, *pkg-config* and 14 | *zlib* using your package manager while the following dependancies one will 15 | likely need to install from source: 16 | 17 | - [*libubox*](http://git.openwrt.org/?p=project/libubox.git;a=summary) 18 | - [*uci*](http://nbd.name/gitweb.cgi?p=uci.git;a=summary) 19 | - [*libroxml*](http://www.libroxml.net/) 20 | - [*ubus*](http://wiki.openwrt.org/doc/techref/ubus) 21 | 22 | In order to have functional NETCONF server a SSH subsystem "translator", 23 | such as [*freesub*](https://github.com/freenetconf/freesub), is needed too. 24 | 25 | ### building freenetconfd 26 | 27 | The build procedure itself is simple: 28 | 29 | ``` 30 | git clone https://github.com/freenetconf/freenetconfd.git 31 | cd freenetconfd/build 32 | cmake .. -DCMAKE_INSTALL_PREFIX:PATH=/usr 33 | make 34 | ``` 35 | 36 | ### configuring freenetconfd 37 | 38 | The *freenetconfd* configuration can be found in 39 | `/etc/config/freenetconfd`. The defaults can be copied from the source: 40 | 41 | ``` 42 | config freenetconfd 43 | option addr '127.0.0.1' 44 | option port '1831' 45 | option yang_dir '/etc/yang' 46 | option modules_dir "/usr/lib/freenetconfd/" 47 | ``` 48 | 49 | ### running freenetconfd 50 | 51 | Before starting *freenetconfd* make sure that *ubusd* is running. On 52 | development machines it can done like this: 53 | 54 | ``` 55 | sudo ubusd & 56 | disown 57 | sudo chown `whoami` /run/ubus.sock 58 | ``` 59 | 60 | When everything is configured *freenetconfd* can be simply started like 61 | this: 62 | 63 | ``` 64 | ./bin/freenetconfd 65 | ``` 66 | -------------------------------------------------------------------------------- /build/.gitignore: -------------------------------------------------------------------------------- 1 | install_manifest.txt 2 | -------------------------------------------------------------------------------- /build/modules/FindLIBROXML.cmake: -------------------------------------------------------------------------------- 1 | # LIBROXML_FOUND - true if library and headers were found 2 | # LIBROXML_INCLUDE_DIRS - include directories 3 | # LIBROXML_LIBRARIES - library directories 4 | 5 | find_package(PkgConfig) 6 | pkg_check_modules(PC_LIBROXML QUIET libroxml) 7 | 8 | find_path(LIBROXML_INCLUDE_DIR roxml.h 9 | HINTS ${PC_LIBROXML_INCLUDEDIR} ${PC_LIBROXML_INCLUDE_DIRS}) 10 | 11 | find_library(LIBROXML_LIBRARY NAMES roxml libroxml 12 | HINTS ${PC_LIBROXML_LIBDIR} ${PC_LIBROXML_LIBRARY_DIRS}) 13 | 14 | set(LIBROXML_LIBRARIES ${LIBROXML_LIBRARY}) 15 | set(LIBROXML_INCLUDE_DIRS ${LIBROXML_INCLUDE_DIR}) 16 | 17 | include(FindPackageHandleStandardArgs) 18 | 19 | find_package_handle_standard_args(LIBROXML DEFAULT_MSG LIBROXML_LIBRARY LIBROXML_INCLUDE_DIR) 20 | 21 | mark_as_advanced(LIBROXML_INCLUDE_DIR LIBROXML_LIBRARY) 22 | -------------------------------------------------------------------------------- /build/modules/FindLIBUBOX.cmake: -------------------------------------------------------------------------------- 1 | # LIBUBOX_FOUND - true if library and headers were found 2 | # LIBUBOX_INCLUDE_DIRS - include directories 3 | # LIBUBOX_LIBRARIES - library directories 4 | 5 | find_package(PkgConfig) 6 | pkg_check_modules(PC_LIBUBOX QUIET libubox) 7 | 8 | find_path(LIBUBOX_INCLUDE_DIR libubox/uloop.h 9 | HINTS ${PC_LIBUBOX_INCLUDEDIR} ${PC_LIBUBOX_INCLUDE_DIRS} PATH_SUFFIXES libubox) 10 | 11 | find_library(LIBUBOX_LIBRARY NAMES ubox libubox 12 | HINTS ${PC_LIBUBOX_LIBDIR} ${PC_LIBUBOX_LIBRARY_DIRS}) 13 | 14 | set(LIBUBOX_LIBRARIES ${LIBUBOX_LIBRARY}) 15 | set(LIBUBOX_INCLUDE_DIRS ${LIBUBOX_INCLUDE_DIR}) 16 | 17 | include(FindPackageHandleStandardArgs) 18 | 19 | find_package_handle_standard_args(LIBUBOX DEFAULT_MSG LIBUBOX_LIBRARY LIBUBOX_INCLUDE_DIR) 20 | 21 | mark_as_advanced(LIBUBOX_INCLUDE_DIR LIBUBOX_LIBRARY) 22 | -------------------------------------------------------------------------------- /build/modules/FindLIBUBUS.cmake: -------------------------------------------------------------------------------- 1 | # LIBUBUS_FOUND - true if library and headers were found 2 | # LIBUBUS_INCLUDE_DIRS - include directories 3 | # LIBUBUS_LIBRARIES - library directories 4 | 5 | find_package(PkgConfig) 6 | pkg_check_modules(PC_LIBUBUS QUIET ubus) 7 | 8 | find_path(LIBUBUS_INCLUDE_DIR libubus.h 9 | HINTS ${PC_LIBUBUS_INCLUDEDIR} ${PC_LIBUBUS_INCLUDE_DIRS}) 10 | 11 | find_library(LIBUBUS_LIBRARY NAMES ubus libubus 12 | HINTS ${PC_LIBUBUS_LIBDIR} ${PC_LIBUBUS_LIBRARY_DIRS}) 13 | 14 | set(LIBUBUS_LIBRARIES ${LIBUBUS_LIBRARY}) 15 | set(LIBUBUS_INCLUDE_DIRS ${LIBUBUS_INCLUDE_DIR}) 16 | 17 | include(FindPackageHandleStandardArgs) 18 | 19 | find_package_handle_standard_args(LIBUBUS DEFAULT_MSG LIBUBUS_LIBRARY LIBUBUS_INCLUDE_DIR) 20 | 21 | mark_as_advanced(LIBUBUS_INCLUDE_DIR LIBUBUS_LIBRARY) 22 | -------------------------------------------------------------------------------- /build/modules/FindUCI.cmake: -------------------------------------------------------------------------------- 1 | # UCI_FOUND - true if library and headers were found 2 | # UCI_INCLUDE_DIRS - include directories 3 | # UCI_LIBRARIES - library directories 4 | 5 | find_package(PkgConfig) 6 | pkg_check_modules(PC_UCI QUIET uci) 7 | 8 | find_path(UCI_INCLUDE_DIR uci.h 9 | HINTS ${PC_UCI_INCLUDEDIR} ${PC_UCI_INCLUDE_DIRS}) 10 | 11 | find_library(UCI_LIBRARY NAMES uci libuci 12 | HINTS ${PC_UCI_LIBDIR} ${PC_UCI_LIBRARY_DIRS}) 13 | 14 | set(UCI_LIBRARIES ${UCI_LIBRARY}) 15 | set(UCI_INCLUDE_DIRS ${UCI_INCLUDE_DIR}) 16 | 17 | include(FindPackageHandleStandardArgs) 18 | 19 | find_package_handle_standard_args(UCI DEFAULT_MSG UCI_LIBRARY UCI_INCLUDE_DIR) 20 | 21 | mark_as_advanced(UCI_INCLUDE_DIR UCI_LIBRARY) 22 | -------------------------------------------------------------------------------- /config/freenetconfd: -------------------------------------------------------------------------------- 1 | # 2 | # freenetconfd uci configuration 3 | # 4 | 5 | config freenetconfd 6 | option addr '127.0.0.1' 7 | option port '1831' 8 | option yang_dir '/etc/yang/' 9 | option modules_dir '/usr/lib/freenetconfd/' 10 | -------------------------------------------------------------------------------- /include/freenetconfd/datastore.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2014 Cisco Systems, Inc. 3 | * Copyright (C) 2014 Sartura, Ltd. 4 | * 5 | * Author: Zvonimir Fras 6 | * Author: Luka Perkov 7 | * Author: Petar Koretic 8 | * 9 | * freenetconfd is free software: you can redistribute it and/or modify 10 | * it under the terms of the GNU General Public License as published by 11 | * the Free Software Foundation, either version 2 of the License, or 12 | * (at your option) any later version. 13 | * 14 | * You should have received a copy of the GNU General Public License 15 | * along with freenetconfd. If not, see . 16 | */ 17 | 18 | #ifndef __FREENETCONFD_DATASTORE_H__ 19 | #define __FREENETCONFD_DATASTORE_H__ 20 | 21 | #define DATASTORE_ROOT_DEFAULT { "root", NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, 1, 0, 0 } 22 | 23 | #include 24 | 25 | #include 26 | 27 | #ifndef ARRAY_SIZE 28 | #define ARRAY_SIZE(a) (sizeof(a) / sizeof(*(a))) 29 | #endif 30 | 31 | typedef struct datastore 32 | { 33 | char *name; 34 | char *value; 35 | char *ns; 36 | struct datastore *parent; 37 | struct datastore *child; // first child 38 | struct datastore *prev; // previous in the list 39 | struct datastore *next; // next in the list 40 | char *(*get) (struct datastore *self); 41 | void (*update) (struct datastore *self); 42 | /** 43 | * set() - sets appropriate system value 44 | * 45 | * @value new value of appropriate system setting 46 | * 47 | * Return: you should return 0 on success, if you want to let 48 | * freenetconf to update datastore value for you, 49 | * -1 on error 50 | */ 51 | int (*set) (struct datastore *self, char *value); 52 | int (*set_multiple) (struct datastore *self, node_t *filter); 53 | /** 54 | * del() - called when delete of node self is requested 55 | * 56 | * @self caller node 57 | * 58 | * If you're implementing del(), you should probably also 59 | * implement create_child() on parent 60 | */ 61 | int (*del) (struct datastore *self, void *data); 62 | /** 63 | * create_child() - called when freenetconfd wants to create child to datastore self 64 | * 65 | * @self caller node 66 | * See ds_add_child_create for other parameters 67 | * 68 | * Return: correctly created and initialized datastore 69 | * 70 | * You should implement this for any node whos child has delete callback. 71 | * Consider implementing anyway for any node whos children have their properties 72 | * set to anything else other than default. 73 | * 74 | * Usual implementation consists of calling ds_add_child_create() with all the received 75 | * parameters and initializing callback pointers and properties. 76 | * See 'filer' example for additional info. 77 | */ 78 | struct datastore *(*create_child) (struct datastore *self, char *name, char *value, char *ns, char *target_name, int target_position); 79 | int is_config; 80 | int is_list; 81 | int is_key; 82 | 83 | /** 84 | * choice_group - identifies the specific case in choice statement 85 | * 86 | * Defaults to 0, which means datastore_t doesn't belong to a choice_group 87 | * 88 | * Assign same positive value for all the cases in the same choice, but 89 | * different positive values for choices in the same container. 90 | * 91 | * You will just want to set it to 1 for most choices you encounter. 92 | */ 93 | int choice_group; 94 | } datastore_t; 95 | 96 | 97 | typedef struct ds_key 98 | { 99 | char *name; 100 | char *value; 101 | struct ds_key *next; 102 | } ds_key_t; 103 | 104 | typedef struct ds_nip 105 | { 106 | node_t *node; 107 | struct ds_nip *next; 108 | int is_head; 109 | } ds_nip_t; 110 | 111 | enum ds_operation {OPERATION_MERGE, OPERATION_REPLACE = 0, OPERATION_CREATE, OPERATION_DELETE, OPERATION_REMOVE}; 112 | 113 | enum ds_operation ds_get_operation(node_t *node); 114 | 115 | /** 116 | * ds_print_key() - prints all key names and values on standard output 117 | * in a human readable format 118 | * 119 | * @key key to print 120 | */ 121 | void ds_print_key(ds_key_t *key); 122 | 123 | /** 124 | * ds_free_key() - recursively frees memory for all key parts 125 | * 126 | * @key key to free 127 | * 128 | * Requires next to be correctly initialized to NULL to mark the list end. 129 | * Only frees ds_key_t structures, does NOT free strings, 130 | * you're going to have to free those yourself if you dynamically allocated 131 | * them manually. 132 | * Normally, roxml and ds_free should handle this. 133 | */ 134 | void ds_free_key(ds_key_t *key); 135 | 136 | /** 137 | * ds_get_key_from_xml() - locates nodes with values to be used as keys 138 | * 139 | * @root xml node_t where the search starts 140 | * @our_root datastore_t node to compare what key values are 141 | * 142 | * Return: ds_key_t created from found xml data 143 | * 144 | * locates nodes with values to be used as keys, 145 | * creates the ds_key_t and returns the pointer to it. You're responsible 146 | * for freeing it (using ds_free_key()) 147 | * 148 | * our_root is useful for edit-config requests where we can't destinguish 149 | * between key values and values to set, if set to NULL, function will 150 | * use only root node_t for key extraction (this is ok for get funtions) 151 | */ 152 | ds_key_t *ds_get_key_from_xml(node_t *root, datastore_t *our_root); 153 | 154 | /** 155 | * ds_init() - inits datastore with name, value and namespace 156 | * 157 | * @datastore pointer to a datastore you'd like to init 158 | * @name string you'd like to use for a name 159 | * @value value it has 160 | * @ns namespace it belongs to 161 | * 162 | * Inits datastore with name, value and namespace you'd like to use and 163 | * puts all other values to default values so you don't have to worry about 164 | * them. 165 | */ 166 | void ds_init(datastore_t *datastore, char *name, char *value, char *ns); 167 | 168 | /** 169 | * ds_free() - frees the datastore you created with datastore_create() 170 | * 171 | * @datastore datastore you'd like to free 172 | * @free_siblings set to 1 when freeing the whole datastore, 0 for freeing just that node 173 | * 174 | * set the datastore to NULL after this call to be on the safe side 175 | */ 176 | void ds_free(datastore_t *datastore, int free_siblings); 177 | 178 | /** 179 | * ds_create() - creates datastore and inits it 180 | * 181 | * @name string you'd like to use for a name 182 | * @value value it has 183 | * @ns namespace it belongs to 184 | * 185 | * Return: pointer to the requested datastore 186 | */ 187 | datastore_t *ds_create(char *name, char *value, char *ns); 188 | 189 | /** 190 | * ds_create_path() - creates the same path in root, as that of path_endpoint 191 | * 192 | * Return: node in datastore that matches that of path_endpoint 193 | */ 194 | datastore_t *ds_create_path(datastore_t *root, node_t *path_endpoint); 195 | 196 | /** 197 | * ds_purge_choice_group() - removes all nodes with choice_group from parent 198 | * 199 | * Call it *before* creating new node(s) from case 200 | * 201 | * Return: 0 on OK, -1 on error 202 | */ 203 | int ds_purge_choice_group(datastore_t *parent, int choice_group); 204 | 205 | /** 206 | * ds_set_value() - sets value to datastore 207 | * 208 | * @datastore datastore node to set value to 209 | * @value value to set 210 | * 211 | * Return: 0 on success, -1 on error 212 | * 213 | * Takes care of previous value and allocates memory for 214 | * new value. ds_free() takes care of freeing it so you're ok 215 | */ 216 | int ds_set_value(datastore_t *datastore, char *value); 217 | 218 | /** 219 | * ds_set_is_config() - sets datastore is_donfig property 220 | * @datastore datastore node you're setting is_config on 221 | * @is_config 1 for true, 0 for false 222 | * @set_siblings set it to 0 when calling manually 223 | * 224 | * You should use this to set is_config instead of setting it 225 | * manually. It takes care of setting is_config to false on 226 | * children. 227 | */ 228 | void ds_set_is_config(datastore_t *datastore, int is_config, int set_siblings); 229 | 230 | /** 231 | * ds_add_child() - adds a child to datastore node 232 | * 233 | * @self datastore to add to 234 | * @child child you want to add 235 | * @target_name name to start counting from, can be NULL 236 | * @target_position position in the list of children, 0 to add on end 237 | * 238 | * You should use this function whenever you want to add a child. 239 | * It handles all the inner working of the datastore. 240 | */ 241 | void ds_add_child(datastore_t *self, datastore_t *child, char *target_name, int target_position); 242 | 243 | datastore_t *ds_add_child_create(datastore_t *datastore, char *name, char *value, char *ns, char *target_name, int target_position); 244 | 245 | datastore_t *ds_add_from_filter(datastore_t *datastore, node_t *filter_root, ds_nip_t *nip); 246 | 247 | datastore_t *ds_find_sibling(datastore_t *root, char *name, char *value); 248 | 249 | /** 250 | * ds_find_child() - finds roots child by name and value 251 | * 252 | * @root datastore node to start your search 253 | * @name name of the node you're searching for 254 | * @value value node contains, if NULL, finds first child with requested name 255 | */ 256 | datastore_t *ds_find_child(datastore_t *root, char *name, char *value); 257 | 258 | /** 259 | * element_has_key_part() - Checks if an element of a list has a key part with "name" and "value" 260 | * @elem element of a list 261 | * @name name of the key tag 262 | * @value value of the key tag 263 | * 264 | * Return: 1 on key part found, 0 on key part not found 265 | * if value set to NULL, check if name tag is_key 266 | */ 267 | int ds_element_has_key_part(datastore_t *elem, char *name, char *value); 268 | 269 | /** 270 | * element_has_key() - Checks if the element of a list has a key 271 | * @elem element of a list 272 | * @key key to check for 273 | * 274 | * Return: 1 on has key, 0 on doesn't have key 275 | */ 276 | int ds_element_has_key(datastore_t *elem, ds_key_t *key); 277 | 278 | /** 279 | * list_has_key() - Checks if list has key 280 | * @list list to check 281 | * 282 | * Return: 1 on has key, 0 on doesn't have at least one key part 283 | */ 284 | int ds_list_has_key(datastore_t *list); 285 | 286 | 287 | datastore_t *ds_find_node_by_key(datastore_t *our_root, ds_key_t *key); 288 | 289 | void ds_get_all(datastore_t *our_root, node_t *out, int get_config, int check_siblings); 290 | 291 | void ds_get_all_keys(datastore_t *our_root, node_t *out, int get_config); 292 | 293 | void ds_get_list_data(node_t *filter_root, datastore_t *node, node_t *out, int get_config); 294 | 295 | void ds_get_filtered(node_t *filter_root, datastore_t *our_root, node_t *out, int get_config); 296 | 297 | /** 298 | * ds_edit_config() 299 | * 300 | * @nodes_in_processing set it to NULL when manually calling ds_edit_config 301 | */ 302 | int ds_edit_config(node_t *filter_root, datastore_t *our_root, ds_nip_t *nodes_in_processing); 303 | 304 | #endif /* __FREENETCONFD_DATASTORE_H__ */ 305 | -------------------------------------------------------------------------------- /include/freenetconfd/freenetconfd.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2014 Sartura, Ltd. 3 | * Copyright (C) 2014 Cisco Systems, Inc. 4 | * 5 | * Author: Luka Perkov 6 | * Author: Petar Koretic 7 | * 8 | * freenetconfd is free software: you can redistribute it and/or modify 9 | * it under the terms of the GNU General Public License as published by 10 | * the Free Software Foundation, either version 2 of the License, or 11 | * (at your option) any later version. 12 | * 13 | * You should have received a copy of the GNU General Public License 14 | * along with freenetconfd. If not, see . 15 | */ 16 | 17 | #ifndef __FREENETCONFD_H__ 18 | #define __FREENETCONFD_H__ 19 | 20 | #define PROJECT_NAME "freenetconfd" 21 | 22 | #include 23 | #include 24 | 25 | #define DEBUG(fmt, ...) do { \ 26 | fprintf(stderr, "freenetconfd: %s(%d): " fmt, __FILE__, __LINE__, ## __VA_ARGS__); \ 27 | } while (0) 28 | 29 | #define LOG(fmt, ...) do { \ 30 | syslog(0, fmt, ## __VA_ARGS__); \ 31 | fprintf(stderr, "freenetconfd: "fmt, ## __VA_ARGS__); \ 32 | } while (0) 33 | 34 | #define ERROR(fmt, ...) do { \ 35 | syslog(0, fmt, ## __VA_ARGS__); \ 36 | fprintf(stderr, "freenetconfd: "fmt, ## __VA_ARGS__); \ 37 | } while (0) 38 | 39 | #ifndef typeof 40 | #define typeof __typeof 41 | #endif 42 | 43 | #ifndef container_of 44 | #define container_of(ptr, type, member) ( \ 45 | (type *)( (char *)ptr - offsetof(type,member) )) 46 | #endif 47 | 48 | #ifndef __unused 49 | #define __unused __attribute__((unused)) 50 | #endif 51 | 52 | #endif /* __FREENETCONFD_H__ */ 53 | -------------------------------------------------------------------------------- /include/freenetconfd/netconf.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2014 Sartura, Ltd. 3 | * Copyright (C) 2014 Cisco Systems, Inc. 4 | * 5 | * Author: Luka Perkov 6 | * Author: Petar Koretic 7 | * 8 | * freenetconfd is free software: you can redistribute it and/or modify 9 | * it under the terms of the GNU General Public License as published by 10 | * the Free Software Foundation, either version 2 of the License, or 11 | * (at your option) any later version. 12 | * 13 | * You should have received a copy of the GNU General Public License 14 | * along with freenetconfd. If not, see . 15 | */ 16 | 17 | #ifndef __FREENETCONFD_NETCONF_H__ 18 | #define __FREENETCONFD_NETCONF_H__ 19 | 20 | /* RFC: http://tools.ietf.org/html/rfc6241#appendix-A */ 21 | typedef enum rpc_error_tag 22 | { 23 | RPC_ERROR_TAG_OPERATION_FAILED, 24 | RPC_ERROR_TAG_OPERATION_NOT_SUPPORTED, 25 | RPC_ERROR_TAG_IN_USE, 26 | RPC_ERROR_TAG_INVALID_VALUE, 27 | RPC_ERROR_TAG_DATA_MISSING, 28 | RPC_ERROR_TAG_DATA_EXISTS, 29 | __RPC_ERROR_TAG_COUNT 30 | } rpc_error_tag_t; 31 | 32 | typedef enum rpc_error_type 33 | { 34 | RPC_ERROR_TYPE_TRANSPORT, 35 | RPC_ERROR_TYPE_RPC, 36 | RPC_ERROR_TYPE_PROTOCOL, 37 | RPC_ERROR_TYPE_APPLICATION, 38 | __RPC_ERROR_TYPE_COUNT 39 | } rpc_error_type_t; 40 | 41 | typedef enum rpc_error_severity 42 | { 43 | RPC_ERROR_SEVERITY_ERROR, 44 | RPC_ERROR_SEVERITY_WARN, 45 | __RPC_ERROR_SEVERITY_COUNT 46 | } rpc_error_severity_t; 47 | char *netconf_rpc_error(char *msg, rpc_error_tag_t rpc_error_tag, rpc_error_type_t rpc_error_type, rpc_error_severity_t rpc_error_severity, char *error_app_tag); 48 | 49 | 50 | #endif /* __FREENETCONFD_NETCONF_H__ */ 51 | -------------------------------------------------------------------------------- /include/freenetconfd/plugin.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2014 Sartura, Ltd. 3 | * Copyright (C) 2014 Cisco Systems, Inc. 4 | * 5 | * Author: Luka Perkov 6 | * Author: Petar Koretic 7 | * 8 | * freenetconfd is free software: you can redistribute it and/or modify 9 | * it under the terms of the GNU General Public License as published by 10 | * the Free Software Foundation, either version 2 of the License, or 11 | * (at your option) any later version. 12 | * 13 | * You should have received a copy of the GNU General Public License 14 | * along with freenetconfd. If not, see . 15 | */ 16 | 17 | #ifndef __FREENETCONFD_PLUGIN_H__ 18 | #define __FREENETCONFD_PLUGIN_H__ 19 | 20 | #include 21 | 22 | #include 23 | #include 24 | 25 | enum response {RPC_OK, RPC_OK_CLOSE, RPC_DATA, RPC_ERROR, RPC_DATA_EXISTS, RPC_DATA_MISSING}; 26 | 27 | struct rpc_data 28 | { 29 | node_t *in; 30 | node_t *out; 31 | char *error; 32 | int get_config; 33 | }; 34 | 35 | struct rpc_method 36 | { 37 | // rpc name for rpc_methods, xpath otherwise 38 | const char *query; 39 | int (*handler) (struct rpc_data *data); 40 | }; 41 | 42 | struct module 43 | { 44 | const struct rpc_method *rpcs; 45 | int rpc_count; 46 | char *ns; 47 | struct datastore *datastore; 48 | }; 49 | 50 | struct module_list 51 | { 52 | struct list_head list; 53 | char *name; 54 | void *lib; 55 | const struct module *m; 56 | }; 57 | 58 | #endif /* __FREENETCONFD_PLUGIN_H__ */ 59 | -------------------------------------------------------------------------------- /src/config.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2014 Sartura, Ltd. 3 | * Copyright (C) 2014 Cisco Systems, Inc. 4 | * 5 | * Author: Luka Perkov 6 | * Author: Petar Koretic 7 | * 8 | * freenetconfd is free software: you can redistribute it and/or modify 9 | * it under the terms of the GNU General Public License as published by 10 | * the Free Software Foundation, either version 2 of the License, or 11 | * (at your option) any later version. 12 | * 13 | * You should have received a copy of the GNU General Public License 14 | * along with freenetconfd. If not, see . 15 | */ 16 | 17 | #include 18 | #include 19 | #include 20 | #include 21 | #include 22 | 23 | #include 24 | #include 25 | 26 | #include "freenetconfd/freenetconfd.h" 27 | 28 | #include "config.h" 29 | 30 | enum 31 | { 32 | ADDR, 33 | PORT, 34 | YANG_DIR, 35 | MODULES_DIR, 36 | __OPTIONS_COUNT 37 | }; 38 | 39 | const struct blobmsg_policy config_policy[__OPTIONS_COUNT] = 40 | { 41 | [ADDR] = { .name = "addr", .type = BLOBMSG_TYPE_STRING }, 42 | [PORT] = { .name = "port", .type = BLOBMSG_TYPE_STRING }, 43 | [YANG_DIR] = { .name = "yang_dir", .type = BLOBMSG_TYPE_STRING }, 44 | [MODULES_DIR] = { .name = "modules_dir", .type = BLOBMSG_TYPE_STRING } 45 | }; 46 | const struct uci_blob_param_list config_attr_list = 47 | { 48 | .n_params = __OPTIONS_COUNT, 49 | .params = config_policy 50 | }; 51 | 52 | /* 53 | * config_load() - load uci config file 54 | * 55 | * Load and parse uci config file. If config file is found, parse configs to 56 | * internal structure defined in config.h. 57 | */ 58 | int config_load(void) 59 | { 60 | struct uci_context *uci = uci_alloc_context(); 61 | struct uci_package *conf = NULL; 62 | struct blob_attr *tb[__OPTIONS_COUNT], *c; 63 | static struct blob_buf buf; 64 | 65 | if (uci_load(uci, "freenetconfd", &conf)) 66 | { 67 | uci_free_context(uci); 68 | return -1; 69 | } 70 | 71 | blob_buf_init(&buf, 0); 72 | 73 | struct uci_element *section_elem; 74 | uci_foreach_element(&conf->sections, section_elem) 75 | { 76 | struct uci_section *s = uci_to_section(section_elem); 77 | uci_to_blob(&buf, s, &config_attr_list); 78 | } 79 | 80 | blobmsg_parse(config_policy, __OPTIONS_COUNT, tb, blob_data(buf.head), blob_len(buf.head)); 81 | 82 | /* defaults */ 83 | config.addr = NULL; 84 | config.port = NULL; 85 | config.yang_dir = NULL; 86 | config.modules_dir = NULL; 87 | 88 | if ((c = tb[ADDR])) 89 | config.addr = strdup(blobmsg_get_string(c)); 90 | 91 | if ((c = tb[PORT])) 92 | config.port = strdup(blobmsg_get_string(c)); 93 | 94 | if ((c = tb[YANG_DIR])) 95 | config.yang_dir = strdup(blobmsg_get_string(c)); 96 | 97 | if ((c = tb[MODULES_DIR])) 98 | config.modules_dir = strdup(blobmsg_get_string(c)); 99 | 100 | if (!(config.modules_dir)) 101 | { 102 | ERROR("modules directory must be set\n"); 103 | uci_unload(uci, conf); 104 | uci_free_context(uci); 105 | return -1; 106 | } 107 | 108 | blob_buf_free(&buf); 109 | uci_unload(uci, conf); 110 | uci_free_context(uci); 111 | 112 | return 0; 113 | } 114 | 115 | void config_exit(void) 116 | { 117 | free(config.addr); 118 | free(config.port); 119 | free(config.yang_dir); 120 | free(config.modules_dir); 121 | } 122 | -------------------------------------------------------------------------------- /src/config.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2014 Sartura, Ltd. 3 | * Copyright (C) 2014 Cisco Systems, Inc. 4 | * 5 | * Author: Luka Perkov 6 | * Author: Petar Koretic 7 | * 8 | * freenetconfd is free software: you can redistribute it and/or modify 9 | * it under the terms of the GNU General Public License as published by 10 | * the Free Software Foundation, either version 2 of the License, or 11 | * (at your option) any later version. 12 | * 13 | * You should have received a copy of the GNU General Public License 14 | * along with freenetconfd. If not, see . 15 | */ 16 | 17 | #ifndef __FREENETCONFD_CONFIG_H__ 18 | #define __FREENETCONFD_CONFIG_H__ 19 | 20 | #include 21 | #include 22 | 23 | int config_load(void); 24 | void config_exit(void); 25 | 26 | struct config_t 27 | { 28 | char *addr; 29 | char *port; 30 | char *yang_dir; 31 | char *modules_dir; 32 | } config; 33 | 34 | #endif /* __FREENETCONFD_CONFIG_H__ */ 35 | -------------------------------------------------------------------------------- /src/connection.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2014 Cisco Systems, Inc. 3 | * 4 | * Author: Luka Perkov 5 | * Author: Petar Koretic 6 | * 7 | * freenetconfd is free software: you can redistribute it and/or modify 8 | * it under the terms of the GNU General Public License as published by 9 | * the Free Software Foundation, either version 2 of the License, or 10 | * (at your option) any later version. 11 | * 12 | * You should have received a copy of the GNU General Public License 13 | * along with freenetconfd. If not, see . 14 | */ 15 | 16 | #include 17 | #include 18 | #include 19 | #include 20 | #include 21 | 22 | #include 23 | #include 24 | #include 25 | 26 | #include "freenetconfd/freenetconfd.h" 27 | 28 | #include "config.h" 29 | #include "messages.h" 30 | #include "connection.h" 31 | #include "methods.h" 32 | 33 | static void connection_accept_cb(struct uloop_fd *fd, unsigned int events); 34 | static void connection_close(struct ustream *s); 35 | 36 | static struct uloop_fd server = { .cb = connection_accept_cb }; 37 | static struct connection *next_connection = NULL; 38 | 39 | enum netconf_msg_step 40 | { 41 | NETCONF_MSG_STEP_HELLO, 42 | NETCONF_MSG_STEP_HEADER_0, 43 | NETCONF_MSG_STEP_HEADER_1, 44 | NETCONF_MSG_STEP_HEADER_1_BUF, 45 | NETCONF_MSG_STEP_DATA_0, 46 | NETCONF_MSG_STEP_DATA_0_BUF, 47 | NETCONF_MSG_STEP_DATA_1, 48 | NETCONF_MSG_STEP_DATA_1_BUF, 49 | __NETCONF_MSG_STEP_MAX 50 | }; 51 | 52 | struct connection 53 | { 54 | struct sockaddr_in sin; 55 | struct ustream_fd us; 56 | int step; 57 | int base; 58 | uint64_t msg_len; 59 | char *buf; 60 | }; 61 | 62 | static void notify_state(struct ustream *s) 63 | { 64 | struct connection *c = container_of(s, struct connection, us.stream); 65 | 66 | ustream_free(&c->us.stream); 67 | close(c->us.fd.fd); 68 | 69 | free(c); 70 | 71 | LOG("connection closed\n"); 72 | } 73 | 74 | 75 | static void notify_read(struct ustream *s, int bytes) 76 | { 77 | struct connection *c = container_of(s, struct connection, us.stream); 78 | 79 | char *data, *buf = NULL, *buf1 = NULL, *buf2 = NULL; 80 | int data_len, rc; 81 | 82 | DEBUG("starting to read incoming data\n"); 83 | 84 | do 85 | { 86 | data = ustream_get_read_buf(s, &data_len); 87 | 88 | if (!data) break; 89 | 90 | switch (c->step) 91 | { 92 | case NETCONF_MSG_STEP_HELLO: 93 | DEBUG("handling hello\n"); 94 | 95 | buf1 = strchr(data, '<'); 96 | 97 | if (!buf1) 98 | { 99 | LOG("start of hello message was not found\n"); 100 | connection_close(s); 101 | return; 102 | } 103 | 104 | if (buf1 != data) 105 | { 106 | LOG("start of hello message not found where expected\n"); 107 | connection_close(s); 108 | return; 109 | } 110 | 111 | buf2 = strstr(buf1, XML_NETCONF_BASE_1_0_END); 112 | 113 | if (!buf2) 114 | { 115 | DEBUG("end of netconf message was not found in this buffer\n"); 116 | return; 117 | } 118 | 119 | *buf2 = '\0'; 120 | 121 | rc = method_analyze_message_hello(buf1, &c->base); 122 | 123 | if (rc) 124 | { 125 | connection_close(s); 126 | return; 127 | } 128 | 129 | ustream_consume(s, buf2 + strlen(XML_NETCONF_BASE_1_0_END) - data); 130 | 131 | if (c->base) 132 | c->step = NETCONF_MSG_STEP_HEADER_1; 133 | else 134 | c->step = NETCONF_MSG_STEP_HEADER_0; 135 | 136 | break; 137 | 138 | default: 139 | break; 140 | } 141 | 142 | data = ustream_get_read_buf(s, &data_len); 143 | 144 | if (!data) break; 145 | 146 | switch (c->step) 147 | { 148 | case NETCONF_MSG_STEP_HEADER_1: 149 | DEBUG("handling data header (1.1)\n"); 150 | buf1 = strchr(data, '#'); 151 | 152 | if (!buf1) 153 | { 154 | ustream_consume(s, data_len); 155 | return; 156 | } 157 | 158 | buf2 = strstr(buf1, "\n<"); 159 | 160 | if (!buf2) 161 | { 162 | connection_close(s); 163 | return; 164 | } 165 | 166 | *buf1 = ' '; 167 | c->msg_len = strtoull(data, NULL, 10); 168 | DEBUG("expecting incoming message length: %" PRIu64 "\n", c->msg_len); 169 | 170 | ustream_consume(s, buf2 + 1 - data); 171 | c->step = NETCONF_MSG_STEP_DATA_1; 172 | 173 | break; 174 | 175 | case NETCONF_MSG_STEP_HEADER_1_BUF: 176 | DEBUG("handling data header buf (1.1)\n"); 177 | 178 | /* FIXME */ 179 | connection_close(s); 180 | 181 | break; 182 | 183 | default: 184 | break; 185 | } 186 | 187 | data = ustream_get_read_buf(s, &data_len); 188 | 189 | if (!data) break; 190 | 191 | switch (c->step) 192 | { 193 | case NETCONF_MSG_STEP_DATA_1: 194 | DEBUG("handling data (1.1)\n"); 195 | 196 | if (c->msg_len > (data_len + strlen(XML_NETCONF_BASE_1_1_END))) 197 | { 198 | c->step = NETCONF_MSG_STEP_DATA_1_BUF; 199 | break; 200 | } 201 | 202 | if (data_len < strlen(XML_NETCONF_BASE_1_1_END)) 203 | { 204 | /* leftovers from netcat testing */ 205 | ustream_consume(s, data_len); 206 | break; 207 | } 208 | 209 | buf2 = strstr(data, XML_NETCONF_BASE_1_1_END); 210 | 211 | if (!buf2) 212 | { 213 | connection_close(s); 214 | return; 215 | } 216 | 217 | *buf2 = '\0'; 218 | 219 | DEBUG("received rpc\n\n %s\n\n", data); 220 | rc = method_handle_message_rpc(data, &buf); 221 | 222 | if (rc == -1) 223 | { 224 | /* FIXME */ 225 | connection_close(s); 226 | return; 227 | } 228 | 229 | DEBUG("sending rpc-reply\n\n %s\n\n", buf); 230 | ustream_printf(s, "\n#%zu\n%s%s", strlen(buf), buf, XML_NETCONF_BASE_1_1_END); 231 | free(buf); 232 | 233 | ustream_consume(s, buf2 + strlen(XML_NETCONF_BASE_1_1_END) - data); 234 | c->msg_len = 0; 235 | 236 | if (rc == 1) 237 | { 238 | connection_close(s); 239 | return; 240 | } 241 | 242 | if (c->base) 243 | c->step = NETCONF_MSG_STEP_HEADER_1; 244 | else 245 | c->step = NETCONF_MSG_STEP_HEADER_0; 246 | 247 | break; 248 | 249 | case NETCONF_MSG_STEP_DATA_1_BUF: 250 | DEBUG("handling data buf (1.1)\n"); 251 | 252 | /* FIXME */ 253 | connection_close(s); 254 | 255 | break; 256 | 257 | default: 258 | break; 259 | } 260 | } 261 | while (1); 262 | } 263 | 264 | static void connection_accept_cb(struct uloop_fd *fd, unsigned int events) 265 | { 266 | struct connection *c; 267 | unsigned int sl = sizeof(struct sockaddr_in); 268 | int sfd, rc; 269 | char *hello_message = NULL; 270 | 271 | LOG("received new connection\n"); 272 | 273 | if (!next_connection) 274 | { 275 | next_connection = calloc(1, sizeof(*next_connection)); 276 | } 277 | 278 | if (!next_connection) 279 | { 280 | ERROR("not enough memory to accept connection\n"); 281 | return; 282 | } 283 | 284 | c = next_connection; 285 | sfd = accept(server.fd, (struct sockaddr *) &c->sin, &sl); 286 | 287 | if (sfd < 0) 288 | { 289 | ERROR("failed accepting connection\n"); 290 | return; 291 | } 292 | 293 | DEBUG("configuring connection parameters\n"); 294 | 295 | c->us.stream.string_data = true; 296 | c->us.stream.notify_read = notify_read; 297 | c->us.stream.notify_state = notify_state; 298 | c->us.stream.r.buffer_len = 16384; 299 | c->step = NETCONF_MSG_STEP_HELLO; 300 | 301 | DEBUG("crafting hello message\n"); 302 | rc = method_create_message_hello(&hello_message); 303 | 304 | if (rc) 305 | { 306 | ERROR("failed to create hello message\n"); 307 | close(sfd); 308 | return; 309 | } 310 | 311 | ustream_fd_init(&c->us, sfd); 312 | next_connection = NULL; 313 | 314 | DEBUG("sending hello message\n"); 315 | ustream_printf(&c->us.stream, "%s%s", hello_message, XML_NETCONF_BASE_1_0_END); 316 | free(hello_message); 317 | } 318 | 319 | static void 320 | connection_close(struct ustream *s) 321 | { 322 | struct connection *c = container_of(s, struct connection, us.stream); 323 | 324 | char *data; 325 | int data_len; 326 | 327 | data = ustream_get_read_buf(s, &data_len); 328 | 329 | if (data) 330 | { 331 | ustream_consume(s, data_len); 332 | } 333 | 334 | ustream_set_read_blocked(s, true); 335 | 336 | close(c->us.fd.fd); 337 | 338 | LOG("closing connection\n"); 339 | } 340 | 341 | int 342 | server_init() 343 | { 344 | server.fd = usock(USOCK_TCP | USOCK_SERVER, config.addr, config.port); 345 | 346 | if (server.fd < 0) 347 | { 348 | ERROR("unable to open socket %s:%s\n", config.addr, config.port); 349 | return -1; 350 | } 351 | 352 | uloop_fd_add(&server, ULOOP_READ); 353 | 354 | return 0; 355 | } 356 | -------------------------------------------------------------------------------- /src/connection.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2014 Cisco Systems, Inc. 3 | * 4 | * Author: Luka Perkov 5 | * Author: Petar Koretic 6 | * 7 | * freenetconfd is free software: you can redistribute it and/or modify 8 | * it under the terms of the GNU General Public License as published by 9 | * the Free Software Foundation, either version 2 of the License, or 10 | * (at your option) any later version. 11 | * 12 | * You should have received a copy of the GNU General Public License 13 | * along with freenetconfd. If not, see . 14 | */ 15 | 16 | #ifndef __FREENETCONFD_CONNECTION_H__ 17 | #define __FREENETCONFD_CONNECTION_H__ 18 | 19 | int server_init(); 20 | 21 | #endif /* __FREENETCONFD_CONNECTION_H__ */ 22 | -------------------------------------------------------------------------------- /src/datastore.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2014 Cisco Systems, Inc. 3 | * Copyright (C) 2014 Sartura, Ltd. 4 | * 5 | * Author: Zvonimir Fras 6 | * Author: Luka Perkov 7 | * Author: Petar Koretic 8 | * 9 | * freenetconfd is free software: you can redistribute it and/or modify 10 | * it under the terms of the GNU General Public License as published by 11 | * the Free Software Foundation, either version 2 of the License, or 12 | * (at your option) any later version. 13 | * 14 | * You should have received a copy of the GNU General Public License 15 | * along with freenetconfd. If not, see . 16 | */ 17 | 18 | #include 19 | 20 | #include "freenetconfd/freenetconfd.h" 21 | #include "freenetconfd/datastore.h" 22 | #include "freenetconfd/plugin.h" 23 | 24 | // nodes in processing implementation 25 | 26 | static ds_nip_t *ds_nip_has_node(ds_nip_t *nip_list_head, node_t *node) 27 | { 28 | if (!nip_list_head) 29 | return NULL; 30 | 31 | for (ds_nip_t *cur = nip_list_head->next; cur; cur = cur->next) 32 | { 33 | if (cur->node == node) 34 | return cur; 35 | } 36 | 37 | return NULL; 38 | } 39 | 40 | /** 41 | * ds_nip_add() adds node to nip_list_head 42 | * 43 | * @nip_list_head nip_list_head representing the list 44 | * @node node to add to the list 45 | * 46 | * Return: pointer to nip_list_head, NULL on error 47 | * 48 | * Creates new list if nip_list_head == NULL 49 | */ 50 | static ds_nip_t *ds_nip_add(ds_nip_t *nip_list_head, node_t *node) 51 | { 52 | ds_nip_t *nip = malloc(sizeof(ds_nip_t)); 53 | 54 | if (!nip) 55 | { 56 | ERROR("not enough memory\n"); 57 | return NULL; 58 | } 59 | 60 | nip->node = node; 61 | nip->is_head = 0; 62 | 63 | if (!nip_list_head) 64 | { 65 | nip_list_head = malloc(sizeof(ds_nip_t)); 66 | 67 | if (!nip_list_head) 68 | { 69 | ERROR("not enough memory\n"); 70 | return NULL; 71 | } 72 | 73 | nip_list_head->is_head = 1; 74 | nip->next = NULL; 75 | } 76 | else 77 | { 78 | nip->next = nip_list_head->next; 79 | } 80 | 81 | nip_list_head->next = nip; 82 | 83 | return nip_list_head; 84 | } 85 | 86 | static ds_nip_t *ds_nip_add_unique(ds_nip_t *nip_list_head, node_t *node) 87 | { 88 | if (!ds_nip_has_node(nip_list_head, node)) 89 | return ds_nip_add(nip_list_head, node); 90 | 91 | return nip_list_head; 92 | } 93 | 94 | static void ds_free_nip(ds_nip_t *nip_list_head) 95 | { 96 | if (!nip_list_head) 97 | return; 98 | 99 | ds_free_nip(nip_list_head->next); 100 | nip_list_head->next = NULL; 101 | free(nip_list_head); 102 | } 103 | 104 | /** 105 | * ds_nip_delete() deletes node from nip_list 106 | * 107 | * Return: 0 on success, -1 on not found 108 | */ 109 | static int ds_nip_delete(ds_nip_t *nip_list_head, node_t *node) 110 | { 111 | if (!nip_list_head) 112 | return -1; 113 | 114 | ds_nip_t *cur, *prev; 115 | 116 | for (prev = nip_list_head, cur = nip_list_head->next; prev && cur; cur = cur->next, prev = prev->next) 117 | { 118 | if (cur->node == node) 119 | { 120 | prev->next = cur->next; 121 | free(cur); 122 | break; 123 | } 124 | } 125 | 126 | return 0; 127 | } 128 | 129 | enum ds_operation ds_get_operation(node_t *node) 130 | { 131 | if (!node) 132 | return OPERATION_MERGE; 133 | 134 | node_t *operation_attr = roxml_get_attr(node, "operation", 0); 135 | char *operation = roxml_get_content(operation_attr, NULL, 0, NULL); 136 | 137 | if (!operation) 138 | return OPERATION_MERGE; 139 | 140 | if (!strcmp(operation, "delete")) 141 | return OPERATION_DELETE; 142 | 143 | if (!strcmp(operation, "remove")) 144 | return OPERATION_REMOVE; 145 | 146 | if (!strcmp(operation, "create")) 147 | return OPERATION_CREATE; 148 | 149 | if (!strcmp(operation, "merge")) 150 | return OPERATION_MERGE; 151 | 152 | if (!strcmp(operation, "replace")) 153 | return OPERATION_REPLACE; 154 | 155 | return OPERATION_MERGE; 156 | } 157 | 158 | void ds_print_key(ds_key_t *key) 159 | { 160 | printf("KEY:\n"); 161 | 162 | for (ds_key_t *cur = key; cur != NULL; cur = cur->next) 163 | { 164 | printf("name: %s\nvalue: %s\n-----------------\n", cur->name, cur->value); 165 | } 166 | } 167 | 168 | void ds_free_key(ds_key_t *key) 169 | { 170 | if (key && key->next) 171 | ds_free_key(key->next); 172 | 173 | free(key); 174 | } 175 | 176 | 177 | // makes key from xml data 178 | ds_key_t *ds_get_key_from_xml(node_t *root, datastore_t *our_root) 179 | { 180 | int child_count = roxml_get_chld_nb(root); 181 | 182 | if (!child_count) 183 | return NULL; 184 | 185 | ds_key_t *rc = NULL; 186 | ds_key_t *cur_key = NULL; 187 | 188 | for (int i = 0; i < child_count; i++) 189 | { 190 | node_t *child = roxml_get_chld(root, NULL, i); 191 | char *key_name = roxml_get_name(child, NULL, 0); 192 | char *key_value = roxml_get_content(child, NULL, 0, NULL); 193 | 194 | // if it seems to be actual key 195 | if (key_name && key_name[0] != '\0' && key_value && key_value[0] != '\0') 196 | { 197 | // if datastore provided see if tag with key_name has is_key flag 198 | if (our_root && !ds_element_has_key_part(our_root, key_name, NULL)) 199 | continue; 200 | 201 | // make sure key part is correctly allocated 202 | if (!rc) 203 | { 204 | rc = malloc(sizeof(ds_key_t)); 205 | 206 | if (!rc) 207 | { 208 | ERROR("not enough memory\n"); 209 | return NULL; 210 | } 211 | 212 | cur_key = rc; 213 | } 214 | else 215 | { 216 | cur_key->next = malloc(sizeof(ds_key_t)); 217 | 218 | if (!cur_key->next) 219 | { 220 | ERROR("not enough memory\n"); 221 | return NULL; 222 | } 223 | 224 | cur_key = cur_key->next; 225 | } 226 | 227 | cur_key->name = roxml_get_name(child, NULL, 0); 228 | cur_key->value = key_value; 229 | cur_key->next = NULL; 230 | } 231 | } 232 | 233 | return rc; 234 | } 235 | 236 | void ds_init(datastore_t *datastore, char *name, char *value, char *ns) 237 | { 238 | datastore->name = name; 239 | datastore->value = value; 240 | datastore->ns = ns; 241 | datastore->parent = datastore->child = datastore->prev = datastore->next = NULL; 242 | datastore->get = NULL; 243 | datastore->set = NULL; 244 | datastore->set_multiple = NULL; 245 | datastore->del = NULL; 246 | datastore->create_child = NULL; 247 | datastore->update = NULL; 248 | datastore->is_config = 1; 249 | datastore->is_list = datastore->is_key = 0; 250 | datastore->choice_group = 0; 251 | } 252 | 253 | void ds_free(datastore_t *datastore, int free_siblings) 254 | { 255 | if (!datastore) 256 | return; 257 | 258 | // make a clear end if we're not deleting root node 259 | if (datastore->parent && !datastore->prev) 260 | datastore->parent->child = datastore->next; 261 | 262 | free(datastore->name); 263 | datastore->name = NULL; 264 | free(datastore->value); 265 | datastore->value = NULL; 266 | free(datastore->ns); 267 | datastore->ns = NULL; 268 | datastore->is_config = 1; 269 | datastore->is_list = datastore->is_key = 0; 270 | datastore->choice_group = 0; 271 | 272 | if (free_siblings) 273 | { 274 | ds_free(datastore->next, 1); 275 | datastore->next = NULL; 276 | } 277 | 278 | // keep the datastore clean and running 279 | if (datastore->next) 280 | { 281 | if (datastore->prev) 282 | { 283 | datastore->prev->next = datastore->next; 284 | datastore->next->prev = datastore->prev; 285 | } 286 | else 287 | { 288 | datastore->next->prev = NULL; 289 | } 290 | } 291 | else if (datastore->prev) 292 | { 293 | datastore->prev->next = NULL; 294 | } 295 | 296 | ds_free(datastore->child, 1); 297 | datastore->child = NULL; 298 | free(datastore); 299 | } 300 | 301 | datastore_t *ds_create(char *name, char *value, char *ns) 302 | { 303 | datastore_t *datastore = malloc(sizeof(datastore_t)); 304 | 305 | if (!datastore) 306 | return 0; 307 | 308 | ds_init(datastore, 309 | name ? strdup(name) : NULL, 310 | value ? strdup(value) : NULL, 311 | ns ? strdup(ns) : NULL); 312 | 313 | return datastore; 314 | } 315 | 316 | datastore_t *ds_create_path(datastore_t *root, node_t *path_endpoint) 317 | { 318 | if (!root || !path_endpoint) 319 | return root; 320 | 321 | ds_nip_t *node_list = NULL; 322 | 323 | // get the list of nodes (path_endpoint's parent) in root-down order 324 | for (node_t *cur = path_endpoint; strcmp(roxml_get_name(cur, NULL, 0), "config"); cur = roxml_get_parent(cur)) 325 | { 326 | DEBUG("post-nip.add( %s )\n", roxml_get_name(cur, NULL, 0)); 327 | node_list = ds_nip_add(node_list, cur); 328 | } 329 | 330 | // get the real root of the plugin 331 | root = root->parent; 332 | 333 | // go down and create one by one 334 | for (ds_nip_t *cur = node_list->next; cur; cur = cur->next) 335 | { 336 | char *cur_name = roxml_get_name(cur->node, NULL, 0); 337 | char *cur_value = roxml_get_content(cur->node, NULL, 0, NULL); 338 | 339 | if (!strlen(cur_value)) cur_value = NULL; 340 | 341 | DEBUG("ds.create( %s, %s )\n", cur_name, cur_value); 342 | 343 | datastore_t *child = ds_find_child(root, cur_name, cur_value); 344 | 345 | if (child && child->is_list && ds_list_has_key(child)) 346 | { 347 | ds_key_t *key = ds_get_key_from_xml(path_endpoint, child); 348 | datastore_t *node = ds_find_node_by_key(child, key); 349 | enum ds_operation operation = ds_get_operation(path_endpoint); 350 | if (OPERATION_CREATE == operation && !node) 351 | child = NULL; 352 | ds_free_key(key); 353 | } 354 | 355 | if (!child) 356 | { 357 | root = root->create_child ? (datastore_t *) root->create_child(root, cur_name, cur_value, NULL, NULL, 0) 358 | : ds_add_child_create(root, cur_name, cur_value, NULL, NULL, 0); 359 | if (root->set) 360 | root->set(root, cur_value); 361 | } 362 | else 363 | { 364 | root = child; 365 | } 366 | 367 | if (!child) 368 | DEBUG("\tcreated %s->%s\n", root->parent->name, root->name); 369 | } 370 | 371 | ds_free_nip(node_list); 372 | 373 | return root; 374 | } 375 | 376 | int ds_purge_choice_group(datastore_t *parent, int choice_group) 377 | { 378 | if (!parent) 379 | return -1; 380 | 381 | if(!choice_group) 382 | return 0; 383 | 384 | for (datastore_t *child = parent->child; child; ) 385 | { 386 | datastore_t *tmp = child; 387 | child = child->next; 388 | 389 | if (tmp->choice_group == choice_group) 390 | ds_free(tmp, 0); 391 | } 392 | 393 | return 0; 394 | } 395 | 396 | int ds_set_value(datastore_t *datastore, char *value) 397 | { 398 | if (!datastore || !value) 399 | return -1; 400 | 401 | if (datastore->set) 402 | { 403 | int sr = datastore->set(datastore, value); 404 | 405 | if (sr) 406 | return RPC_ERROR; // TODO error-option 407 | } 408 | 409 | free(datastore->value); 410 | datastore->value = strdup(value); 411 | 412 | if (!datastore->value) 413 | return -1; 414 | 415 | DEBUG("ds_set_value( %s, %s )\n", datastore->name, value); 416 | 417 | return 0; 418 | } 419 | 420 | void ds_set_is_config(datastore_t *datastore, int is_config, int set_siblings) 421 | { 422 | if (!datastore) 423 | return; 424 | 425 | datastore->is_config = is_config; 426 | 427 | if (is_config) 428 | return; // is_config is only recursively set if it's false 429 | 430 | if (set_siblings) 431 | ds_set_is_config(datastore->next, is_config, 1); 432 | 433 | ds_set_is_config(datastore->child, is_config, 1); 434 | } 435 | 436 | 437 | void ds_add_child(datastore_t *self, datastore_t *child, char *target_name, int target_position) 438 | { 439 | if (self->child) 440 | { 441 | datastore_t *cur; 442 | int cur_pos = 0; 443 | 444 | if (target_name) 445 | { 446 | for (cur = self->child; cur->next != 0; cur = cur->next) 447 | { 448 | if (!strcmp(cur->name, target_name)) 449 | { 450 | // if it's last of its name 451 | if (strcmp(cur->next->name, target_name)) 452 | break; 453 | 454 | // if at desired position 455 | if (target_position && ++cur_pos >= target_position) 456 | break; 457 | } 458 | } 459 | } 460 | else 461 | { 462 | for (cur = self->child; cur->next != 0; cur = cur->next) 463 | { 464 | if (target_position && ++cur_pos >= target_position) 465 | break; 466 | } 467 | } 468 | 469 | child->next = cur->next; 470 | 471 | if (cur->next) cur->next->prev = child; 472 | 473 | cur->next = child; 474 | child->prev = cur; 475 | } 476 | else 477 | { 478 | self->child = child; 479 | } 480 | 481 | child->parent = self; 482 | 483 | ds_set_is_config(child, self->is_config, 0); 484 | } 485 | 486 | datastore_t *ds_add_child_create(datastore_t *datastore, char *name, char *value, char *ns, char *target_name, int target_position) 487 | { 488 | datastore_t *child = ds_create(name, value, ns); 489 | 490 | if (!child) 491 | return 0; 492 | 493 | ds_add_child(datastore, child, target_name, target_position); 494 | 495 | return child; 496 | } 497 | 498 | datastore_t *ds_add_from_filter(datastore_t *datastore, node_t *filter_root, ds_nip_t *nip) 499 | { 500 | if (!datastore || !filter_root) 501 | return NULL; 502 | 503 | char *name = roxml_get_name(filter_root, NULL, 0); 504 | DEBUG("add_from_filter( %s, %s )\n", name, roxml_get_content(filter_root, NULL, 0, NULL)); 505 | DEBUG("\tadding_to %s->%s\n", datastore->parent->name, datastore->name); 506 | 507 | char *value = roxml_get_content(filter_root, NULL, 0, NULL); 508 | char *ns = roxml_get_content(roxml_get_ns(filter_root), NULL, 0, NULL); 509 | 510 | datastore_t *rc = datastore->create_child ? (datastore_t *) datastore->create_child(datastore, name, value, ns, name, 0) 511 | : ds_add_child_create(datastore, name, value, ns, name, 0); 512 | if (rc->set) 513 | rc->set(rc, value); 514 | 515 | ds_nip_delete(nip, filter_root); 516 | 517 | int child_count = roxml_get_chld_nb(filter_root); 518 | 519 | for (int i = 0; i < child_count; i++) 520 | { 521 | node_t *child = roxml_get_chld(filter_root, NULL, i); 522 | 523 | ds_add_from_filter(rc, child, nip); 524 | } 525 | 526 | return rc; 527 | } 528 | 529 | 530 | datastore_t *ds_find_sibling(datastore_t *root, char *name, char *value) 531 | { 532 | for (datastore_t *cur = root; cur != NULL; cur = cur->next) 533 | { 534 | // check name 535 | if (cur->name && !strcmp(cur->name, name)) 536 | { 537 | // check value if requested 538 | if (value) 539 | { 540 | if (cur->value && !strcmp(cur->value, value)) 541 | return cur; 542 | } 543 | else 544 | { 545 | return cur; 546 | } 547 | } 548 | } 549 | 550 | return NULL; 551 | } 552 | 553 | datastore_t *ds_find_child(datastore_t *root, char *name, char *value) 554 | { 555 | return ds_find_sibling(root->child, name, value); 556 | } 557 | 558 | int ds_element_has_key_part(datastore_t *elem, char *name, char *value) 559 | { 560 | if (!name) 561 | return 0; 562 | 563 | for (datastore_t *cur = elem->child; cur != NULL; cur = cur->next) 564 | { 565 | if (cur->name && !strcmp(cur->name, name)) 566 | { 567 | // names match 568 | if (!value || (cur->value && !strcmp(cur->value, value))) 569 | { 570 | // if we don't need value or values match 571 | return cur->is_key; // key found 572 | } 573 | } 574 | } 575 | 576 | return 0; // no key found 577 | } 578 | 579 | int ds_element_has_key(datastore_t *elem, ds_key_t *key) 580 | { 581 | if (!elem || !key) 582 | return 0; 583 | 584 | for (ds_key_t *key_part = key; key_part != NULL; key_part = key_part->next) 585 | { 586 | if (!ds_element_has_key_part(elem, key_part->name, key_part->value)) 587 | return 0; // doesn't have key if misses at least one key part 588 | } 589 | 590 | return 1; // found all key parts so has key 591 | } 592 | 593 | int ds_list_has_key(datastore_t *list) 594 | { 595 | if (!list) 596 | return 0; 597 | 598 | for (datastore_t *cur = list->child; cur != NULL; cur = cur->next) 599 | { 600 | if (cur->is_key) 601 | return 1; 602 | } 603 | 604 | return 0; 605 | } 606 | 607 | datastore_t *ds_find_node_by_key(datastore_t *our_root, ds_key_t *key) 608 | { 609 | for (datastore_t *cur = our_root; cur != NULL; cur = cur->next) 610 | { 611 | if (ds_element_has_key(cur, key)) 612 | return cur; 613 | } 614 | 615 | return NULL; 616 | } 617 | 618 | 619 | void ds_get_all(datastore_t *our_root, node_t *out, int get_config, int check_siblings) 620 | { 621 | if (!our_root) 622 | return; 623 | 624 | // skip non-configurable nodes if only configurable are requested 625 | if (get_config && !our_root->is_config) 626 | { 627 | // still have to check siblings, they may be configurable 628 | if (check_siblings) 629 | ds_get_all(our_root->next, out, get_config, 1); 630 | 631 | return; 632 | } 633 | 634 | if (our_root->update) 635 | our_root->update(our_root); 636 | 637 | // use get() if available 638 | char *value; 639 | 640 | if (our_root->get) 641 | value = our_root->get(our_root); 642 | else 643 | value = our_root->value; 644 | 645 | node_t *nn = roxml_add_node(out, 0, ROXML_ELM_NODE, our_root->name, value); 646 | 647 | if (our_root->ns) 648 | roxml_add_node(nn, 0, ROXML_ATTR_NODE, "xmlns", our_root->ns); // add namespace 649 | 650 | // free value if returned with get (get always allocates) 651 | if (our_root->get) 652 | free(value); 653 | 654 | if (check_siblings) 655 | ds_get_all(our_root->next, out, get_config, 1); 656 | 657 | ds_get_all(our_root->child, nn, get_config, 1); 658 | } 659 | 660 | void ds_get_all_keys(datastore_t *our_root, node_t *out, int get_config) 661 | { 662 | if (!our_root || !out) 663 | return; 664 | 665 | // skip non-configurable nodes if only configurable are requested 666 | if (get_config && !our_root->is_config) 667 | return; 668 | 669 | if (our_root->update) 670 | our_root->update(our_root); 671 | 672 | for (datastore_t *parent_cur = our_root; parent_cur != NULL; parent_cur = parent_cur->next) 673 | { 674 | if (get_config && !parent_cur->is_config) 675 | continue; // skip non-configurable nodes if only configurable are requested 676 | 677 | node_t *parent_xml = roxml_add_node(out, 0, ROXML_ELM_NODE, parent_cur->name, NULL); 678 | 679 | for (datastore_t *cur = parent_cur->child; cur != NULL; cur = cur->next) 680 | { 681 | if (get_config && !cur->is_config) 682 | continue; // skip non-configurable nodes if only configurable are requested 683 | 684 | if (cur->is_key) 685 | { 686 | char *value; 687 | 688 | if (cur->get) 689 | value = cur->get(cur); // use get() if available 690 | else 691 | value = cur->value; 692 | 693 | roxml_add_node(parent_xml, 0, ROXML_ELM_NODE, cur->name, value); 694 | 695 | if (cur->get) 696 | free(value); // free value if returned with get (get always allocates) 697 | } 698 | } 699 | } 700 | } 701 | 702 | void ds_get_list_data(node_t *filter_root, datastore_t *node, node_t *out, int get_config) 703 | { 704 | // skip non-configurable nodes if only configurable are requested 705 | if (get_config && !node->is_config) 706 | return; 707 | 708 | int child_count = roxml_get_chld_nb(filter_root); 709 | 710 | for (int i = 0; i < child_count; i++) 711 | { 712 | node_t *cur = roxml_get_chld(filter_root, NULL, i); 713 | 714 | char *name = roxml_get_name(cur, NULL, 0); 715 | char *value = roxml_get_content(cur, NULL, 0, NULL); 716 | 717 | if (value && strlen(value)) 718 | continue; // skip if key has value 719 | 720 | datastore_t *our_cur = ds_find_child(node, name, NULL); 721 | ds_get_all(our_cur, out, get_config, 0); 722 | } 723 | } 724 | 725 | void ds_get_filtered(node_t *filter_root, datastore_t *our_root, node_t *out, int get_config) 726 | { 727 | if (!our_root) 728 | return; 729 | 730 | // recursively check siblings 731 | node_t *filter_root_sibling = roxml_get_next_sibling(filter_root); 732 | 733 | if (filter_root_sibling && our_root->next) 734 | { 735 | ds_get_filtered(filter_root_sibling, our_root->next, out, get_config); 736 | } 737 | 738 | // skip non-configurable nodes if only configurable are requested 739 | if (get_config && !our_root->is_config) 740 | return; 741 | 742 | node_t *filter_root_child = roxml_get_chld(filter_root, NULL, 0); 743 | 744 | if (our_root->is_list && filter_root_child) 745 | { 746 | // handle list filtering 747 | if (!ds_list_has_key(our_root)) 748 | return; // this shouldn't happen 749 | 750 | ds_key_t *key = ds_get_key_from_xml(filter_root, NULL); 751 | ds_print_key(key); 752 | 753 | if (!key) 754 | { 755 | ds_get_all_keys(our_root, out, get_config); 756 | return; 757 | } 758 | 759 | datastore_t *node = ds_find_node_by_key(our_root, key); 760 | ds_free_key(key); 761 | 762 | if (!node) 763 | DEBUG("node IS NULL\n"); 764 | else 765 | DEBUG("node name: %s\nfilter_root name: %s\n", node->name, roxml_get_name(filter_root, NULL, 0)); 766 | 767 | ds_get_list_data(filter_root, node, out, get_config); 768 | } 769 | else if (filter_root_child) 770 | { 771 | // we're not calling update() sooner because ds_get_all and ds_get_all_keys 772 | // will call it too and we don't want to call it twice in the same get 773 | if (our_root->update) 774 | our_root->update(our_root); 775 | 776 | out = roxml_add_node(out, 0, ROXML_ELM_NODE, our_root->name, NULL); 777 | 778 | if (our_root->ns) 779 | roxml_add_node(out, 0, ROXML_ATTR_NODE, "xmlns", our_root->ns); // add namespace 780 | 781 | datastore_t *our_child = ds_find_child(our_root, roxml_get_name(filter_root_child, NULL, 0), NULL); 782 | 783 | if (our_child) 784 | { 785 | ds_get_filtered(filter_root_child, our_child, out, get_config); 786 | } 787 | else 788 | { 789 | // when fetching multiple nodes and some of them are missing, we have to check their siblings anyway 790 | node_t *filter_root_child_sibling; 791 | while ((filter_root_child_sibling = roxml_get_next_sibling(filter_root_child))) 792 | { 793 | datastore_t *child = ds_find_child(our_root, roxml_get_name(filter_root_child_sibling, NULL, 0), NULL); 794 | if (child) 795 | { 796 | ds_get_filtered(filter_root_child_sibling, child, out, get_config); 797 | break; 798 | } 799 | } 800 | } 801 | } 802 | else if (our_root->is_list) 803 | { 804 | // leaf list 805 | 806 | // we're not calling update() sooner because ds_get_all and ds_get_all_keys 807 | // will call it too and we don't want to call it twice in the same get 808 | if (our_root->update) 809 | our_root->update(our_root); 810 | 811 | for (datastore_t *cur = our_root; cur != NULL; cur = cur->next) 812 | { 813 | if (!strcmp(cur->name, our_root->name)) 814 | { 815 | char *value; 816 | 817 | if (cur->get) 818 | value = cur->get(cur); // use get() if available 819 | else 820 | value = cur->value; 821 | 822 | roxml_add_node(out, 0, ROXML_ELM_NODE, our_root->name, value); 823 | 824 | if (cur->get) 825 | free(value); // free value if returned with get (get always allocates) 826 | } 827 | } 828 | } 829 | else 830 | { 831 | ds_get_all(our_root, out, get_config, 0); 832 | } 833 | } 834 | 835 | int ds_edit_config(node_t *filter_root, datastore_t *our_root, ds_nip_t *nodes_in_processing) 836 | { 837 | if (!filter_root) 838 | return 0; 839 | 840 | // always add nodes to nip if they exist 841 | ds_nip_t *nip = ds_nip_add_unique(nodes_in_processing, filter_root); 842 | 843 | if(!our_root) 844 | return 0; 845 | 846 | // finding match 847 | char *filter_name = roxml_get_name(filter_root, NULL, 0); 848 | 849 | if (!filter_name || !our_root->name) 850 | return -1; 851 | 852 | int rc = 0; 853 | 854 | DEBUG("\t\tfilter: %s\t our: %s\n", filter_name, our_root->name); 855 | 856 | // names differ 857 | if (strcmp(filter_name, our_root->name)) 858 | { 859 | // search in next or child element 860 | rc = ds_edit_config(filter_root, our_root->next ? our_root->next : our_root->child, nip); 861 | } 862 | else 863 | { 864 | // names match 865 | 866 | // check operation delete or remove, 867 | // no need to check other operations here since we can't do anything about it 868 | // and have to go in anyway 869 | enum ds_operation operation = ds_get_operation(filter_root); 870 | 871 | if (operation == OPERATION_DELETE || operation == OPERATION_REMOVE) 872 | { 873 | datastore_t *child = our_root; 874 | 875 | if (our_root->is_list) 876 | { 877 | if (ds_list_has_key(our_root)) 878 | { 879 | ds_key_t *key = ds_get_key_from_xml(filter_root, our_root); 880 | child = ds_find_node_by_key(our_root, key); 881 | ds_free_key(key); 882 | } 883 | else 884 | { 885 | child = ds_find_child(our_root->parent, 886 | roxml_get_name(filter_root, NULL, 0), 887 | roxml_get_content(filter_root, NULL, 0, NULL) 888 | ); 889 | } 890 | } 891 | 892 | if (!child) 893 | { 894 | if (operation == OPERATION_DELETE) 895 | return RPC_DATA_MISSING; 896 | 897 | return 0; 898 | } 899 | 900 | DEBUG("delete( %s, %s )\n", child->name, child->value); 901 | 902 | if (child->del) 903 | child->del(child, NULL); // TODO figure out what del() does and what it needs to take as arguments 904 | 905 | ds_free(child, 0); 906 | ds_nip_delete(nip, filter_root); 907 | 908 | return 0; 909 | } 910 | 911 | if (operation == OPERATION_CREATE && 912 | !(our_root->is_list && our_root->value && !ds_find_child(our_root->parent, filter_name, roxml_get_content(filter_root, NULL, 0, NULL))) ) 913 | { 914 | 915 | ds_key_t *key = ds_get_key_from_xml(filter_root, our_root); 916 | datastore_t *node = ds_find_node_by_key(our_root, key); 917 | ds_free_key(key); 918 | if (node) 919 | { 920 | // OPERATION_CREATE && not (our_root is leaf list and cannot find the leaf with desired value) 921 | // when new value gets added to leaf list, we don't report that data exists 922 | DEBUG("%s exists and cannot be created\n", roxml_get_name(filter_root, NULL, 0)); 923 | 924 | ds_nip_delete(nip, filter_root); 925 | return RPC_DATA_EXISTS; 926 | } 927 | } 928 | 929 | if (our_root->set_multiple) 930 | { 931 | int smr = our_root->set_multiple(our_root, filter_root); 932 | DEBUG("set_multiple( %s, %s )\n", our_root->name, roxml_get_name(filter_root, NULL, 0)); 933 | 934 | if (smr) 935 | return RPC_ERROR; // TODO error-option 936 | } 937 | 938 | if (our_root->is_list) 939 | { 940 | if (ds_list_has_key(our_root)) 941 | { 942 | // list 943 | ds_key_t *key = ds_get_key_from_xml(filter_root, our_root); 944 | datastore_t *node = ds_find_node_by_key(our_root, key); 945 | 946 | if (!node) 947 | { 948 | // if we're here, operation is merge 949 | // if the node with specified key doesn't exist, 950 | // we should create it 951 | // since filter_root is already in nip, we gracefully exit 952 | // node will be created 953 | ds_free_key(key); 954 | return 0; 955 | } 956 | 957 | // replace values in datastore for all the values in filter 958 | // remove key from xml 959 | for (ds_key_t *key_part = key; key_part; key_part = key_part->next) 960 | { 961 | roxml_del_node(roxml_get_nodes(filter_root, ROXML_ELM_NODE, key_part->name, 0)); 962 | } 963 | 964 | // foreach elem in xml first layer 965 | int child_count = roxml_get_chld_nb(filter_root); 966 | 967 | for (int i = 0; i < child_count; i++) 968 | { 969 | node_t *elem = roxml_get_chld(filter_root, NULL, i); 970 | 971 | // recursive call to edit configs based on filter 972 | rc = ds_edit_config(elem, node->child, nip); 973 | 974 | if (rc) 975 | { 976 | DEBUG("!!!!!!!!!!!rc\n"); 977 | ds_free_key(key); 978 | goto exit_edit; // immediatelly return on error // TODO error-option 979 | } 980 | } 981 | 982 | ds_free_key(key); 983 | } 984 | else 985 | { 986 | // leaf list (actually list without a key) 987 | ds_add_from_filter(our_root->parent, filter_root, nip); 988 | } 989 | 990 | ds_nip_delete(nip, filter_root); 991 | } 992 | else 993 | { 994 | int child_count = roxml_get_chld_nb(filter_root); 995 | 996 | if (child_count) 997 | { 998 | for (int i = 0; i < child_count; i++) 999 | { 1000 | node_t *elem = roxml_get_chld(filter_root, NULL, i); 1001 | 1002 | // recursive call to edit configs based on filter 1003 | rc = ds_edit_config(elem, our_root->child, nip); 1004 | 1005 | if (rc) 1006 | { 1007 | ds_nip_delete(nip, filter_root); 1008 | goto exit_edit; // immediatelly return on error // TODO error-option 1009 | } 1010 | } 1011 | } 1012 | else 1013 | { 1014 | // "normal" 1015 | char *value = roxml_get_content(filter_root, NULL, 0, NULL); 1016 | 1017 | DEBUG("set( %s, %s )\n", our_root->name, value); 1018 | 1019 | ds_set_value(our_root, value); 1020 | } 1021 | 1022 | ds_nip_delete(nip, filter_root); 1023 | } 1024 | } 1025 | 1026 | exit_edit: 1027 | 1028 | if (!nodes_in_processing) // original call, recursion is done! 1029 | { 1030 | for (ds_nip_t *cur = nip->next; cur; cur = cur->next) 1031 | { 1032 | DEBUG("processing %s->%s\n", roxml_get_name(roxml_get_parent(cur->node), NULL, 0), roxml_get_name(cur->node, NULL, 0)); 1033 | enum ds_operation cur_operation = ds_get_operation(cur->node); 1034 | 1035 | if (cur_operation == OPERATION_DELETE) 1036 | { 1037 | // we should have deleted this but it doesn't exist in datastore 1038 | ds_free_nip(nip); 1039 | return RPC_DATA_MISSING; // TODO handle error-option 1040 | } 1041 | else if (cur_operation == OPERATION_REMOVE) 1042 | { 1043 | continue; 1044 | } 1045 | else // create or merge or replace but needs to create the node 1046 | { 1047 | datastore_t *nn = ds_create_path(our_root, cur->node); 1048 | ds_set_value(nn, roxml_get_content(cur->node, NULL, 0, NULL)); 1049 | 1050 | // add whole trees if they are missing 1051 | int child_count = roxml_get_chld_nb(cur->node); 1052 | for (int i = 0; i < child_count; i++) 1053 | { 1054 | ds_add_from_filter(nn, roxml_get_chld(cur->node, NULL, i), nip); 1055 | } 1056 | } 1057 | } 1058 | 1059 | ds_free_nip(nip); 1060 | } 1061 | 1062 | return rc; 1063 | } 1064 | -------------------------------------------------------------------------------- /src/freenetconfd.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2014 Sartura, Ltd. 3 | * Copyright (C) 2014 Cisco Systems, Inc. 4 | * 5 | * Author: Luka Perkov 6 | * Author: Petar Koretic 7 | * 8 | * freenetconfd is free software: you can redistribute it and/or modify 9 | * it under the terms of the GNU General Public License as published by 10 | * the Free Software Foundation, either version 2 of the License, or 11 | * (at your option) any later version. 12 | * 13 | * You should have received a copy of the GNU General Public License 14 | * along with freenetconfd. If not, see . 15 | */ 16 | 17 | #include 18 | #include 19 | #include 20 | 21 | #include 22 | 23 | #include "freenetconfd/freenetconfd.h" 24 | 25 | #include "connection.h" 26 | #include "config.h" 27 | #include "modules.h" 28 | #include "ubus.h" 29 | 30 | int 31 | main(int argc, char **argv) 32 | { 33 | int rc = 0; 34 | 35 | rc = config_load(); 36 | 37 | if (rc) 38 | { 39 | ERROR("configuration loading failed\n"); 40 | goto exit; 41 | } 42 | 43 | rc = uloop_init(); 44 | 45 | if (rc) 46 | { 47 | ERROR("uloop init failed\n"); 48 | goto exit; 49 | } 50 | 51 | rc = server_init(); 52 | 53 | if (rc) 54 | { 55 | ERROR("server init failed\n"); 56 | goto exit; 57 | } 58 | 59 | rc = ubus_init(); 60 | 61 | if (rc) 62 | { 63 | ERROR("ubus init failed\n"); 64 | goto exit; 65 | } 66 | 67 | rc = modules_init(); 68 | 69 | if (rc) 70 | { 71 | ERROR("module loading failed\n"); 72 | goto exit; 73 | } 74 | 75 | LOG("%s is accepting connections on '%s:%s'\n", PROJECT_NAME, config.addr, config.port); 76 | 77 | /* main loop */ 78 | uloop_run(); 79 | 80 | rc = EXIT_SUCCESS; 81 | exit: 82 | /* FIXME: implement netconf_exit() */ 83 | 84 | uloop_done(); 85 | 86 | ubus_exit(); 87 | 88 | config_exit(); 89 | 90 | modules_unload(); 91 | 92 | return rc; 93 | } 94 | -------------------------------------------------------------------------------- /src/messages.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2014 Sartura, Ltd. 3 | * Copyright (C) 2014 Cisco Systems, Inc. 4 | * 5 | * Author: Luka Perkov 6 | * Author: Petar Koretic 7 | * 8 | * freenetconfd is free software: you can redistribute it and/or modify 9 | * it under the terms of the GNU General Public License as published by 10 | * the Free Software Foundation, either version 2 of the License, or 11 | * (at your option) any later version. 12 | * 13 | * You should have received a copy of the GNU General Public License 14 | * along with freenetconfd. If not, see . 15 | */ 16 | 17 | #ifndef _FREENETCONFD_MESSAGES_H__ 18 | #define _FREENETCONFD_MESSAGES_H__ 19 | 20 | #define XML_NETCONF_BASE_1_0_END "]]>]]>" 21 | #define XML_NETCONF_BASE_1_1_END "\n##\n" 22 | 23 | #define XML_NETCONF_HELLO \ 24 | "" \ 25 | "" \ 26 | "" \ 27 | "urn:ietf:params:netconf:base:1.0" \ 28 | "urn:ietf:params:netconf:base:1.1" \ 29 | "urn:ietf:params:netconf:capability:writable-running:1.0" \ 30 | "" \ 31 | "" 32 | 33 | #define XML_NETCONF_REPLY_TEMPLATE \ 34 | "" \ 35 | "" 36 | 37 | #define YANG_NAMESPACE "urn:ietf:params:xml:ns:yang" 38 | 39 | #endif /* _FREENETCONFD_MESSAGES_H__ */ 40 | -------------------------------------------------------------------------------- /src/methods.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2014 Sartura, Ltd. 3 | * Copyright (C) 2014 Cisco Systems, Inc. 4 | * 5 | * Author: Luka Perkov 6 | * Author: Petar Koretic 7 | * 8 | * freenetconfd is free software: you can redistribute it and/or modify 9 | * it under the terms of the GNU General Public License as published by 10 | * the Free Software Foundation, either version 2 of the License, or 11 | * (at your option) any later version. 12 | * 13 | * You should have received a copy of the GNU General Public License 14 | * along with freenetconfd. If not, see . 15 | */ 16 | 17 | #include 18 | #include 19 | #include 20 | #include 21 | 22 | #include "freenetconfd/freenetconfd.h" 23 | #include "freenetconfd/datastore.h" 24 | #include "freenetconfd/netconf.h" 25 | 26 | #include "netconf.h" 27 | #include "methods.h" 28 | #include "messages.h" 29 | #include "config.h" 30 | #include "modules.h" 31 | 32 | #ifndef ARRAY_SIZE 33 | #define ARRAY_SIZE(a) (sizeof(a) / sizeof(*(a))) 34 | #endif 35 | 36 | static int get(struct rpc_data *data, datastore_t *datastore); 37 | static int method_handle_get(struct rpc_data *data); 38 | static int method_handle_get_config(struct rpc_data *data); 39 | static int method_handle_edit_config(struct rpc_data *data); 40 | static int method_handle_copy_config(struct rpc_data *data); 41 | static int method_handle_delete_config(struct rpc_data *data); 42 | static int method_handle_lock(struct rpc_data *data); 43 | static int method_handle_unlock(struct rpc_data *data); 44 | static int method_handle_close_session(struct rpc_data *data); 45 | static int method_handle_kill_session(struct rpc_data *data); 46 | static int method_handle_get_schema(struct rpc_data *data); 47 | 48 | const struct rpc_method rpc_methods[] = 49 | { 50 | { "get", method_handle_get }, 51 | { "get-config", method_handle_get_config }, 52 | { "get-schema", method_handle_get_schema }, 53 | { "edit-config", method_handle_edit_config }, 54 | { "copy-config", method_handle_copy_config }, 55 | { "delete-config", method_handle_delete_config }, 56 | { "lock", method_handle_lock }, 57 | { "unlock", method_handle_unlock }, 58 | { "close-session", method_handle_close_session }, 59 | { "kill-session", method_handle_kill_session }, 60 | }; 61 | 62 | /* 63 | * method_analyze_message_hello() - analyze rpc hello message 64 | * 65 | * @char*: xml message for parsing 66 | * @int*: netconf 'base' we deduce from message 67 | * 68 | * Checks if rpc message is a valid hello message and parse rcp base version 69 | * client supports. 70 | */ 71 | int method_analyze_message_hello(char *xml_in, int *base) 72 | { 73 | int rc = -1, num_nodes = 0; 74 | node_t **nodes; 75 | int tbase = -1; 76 | 77 | node_t *root = roxml_load_buf(xml_in); 78 | 79 | if (!root) goto exit; 80 | 81 | node_t *hello = roxml_get_nodes(root, ROXML_ELM_NODE, "hello", 0); 82 | 83 | if (!hello) goto exit; 84 | 85 | /* rfc: must not have */ 86 | node_t *session_id = roxml_get_nodes(hello, ROXML_ELM_NODE, "session-id", 0); 87 | 88 | if (session_id) goto exit; 89 | 90 | nodes = roxml_xpath(root, "//capabilities/capability", &num_nodes); 91 | 92 | for (int i = 0; i < num_nodes; i++) 93 | { 94 | if (!nodes[i]) continue; 95 | 96 | char *value = roxml_get_content(nodes[i], NULL, 0, NULL); 97 | 98 | if (strcmp(value, "urn:ietf:params:netconf:base:1.1") == 0) 99 | { 100 | tbase = 1; 101 | } 102 | else if (strcmp(value, "urn:ietf:params:netconf:base:1.0") == 0) 103 | { 104 | tbase = 0; 105 | } 106 | } 107 | 108 | if (tbase == -1) 109 | goto exit; 110 | 111 | *base = tbase; 112 | 113 | rc = 0; 114 | 115 | exit: 116 | 117 | roxml_release(RELEASE_ALL); 118 | roxml_close(root); 119 | 120 | return rc; 121 | } 122 | 123 | int method_create_message_hello(char **xml_out) 124 | { 125 | int rc = -1, len; 126 | char c_session_id[BUFSIZ]; 127 | static uint32_t session_id = 0; 128 | 129 | /* prevent variable overflow */ 130 | if (++session_id == 0) 131 | session_id = 1; 132 | 133 | node_t *root = roxml_load_buf(XML_NETCONF_HELLO); 134 | netconf_capabilites_from_yang(config.yang_dir, root); 135 | 136 | if (!root) 137 | { 138 | ERROR("unable to load 'netconf hello' message template\n"); 139 | goto exit; 140 | } 141 | 142 | node_t *n_hello = roxml_get_chld(root, NULL, 0); 143 | 144 | if (!n_hello) 145 | { 146 | ERROR("unable to parse 'netconf hello' message template\n"); 147 | goto exit; 148 | } 149 | 150 | len = snprintf(c_session_id, BUFSIZ, "%d", session_id); 151 | 152 | if (len <= 0) 153 | { 154 | ERROR("unable to convert session_id\n"); 155 | goto exit; 156 | } 157 | 158 | node_t *n_session_id = roxml_add_node(n_hello, 0, ROXML_ELM_NODE, "session-id", c_session_id); 159 | 160 | if (!n_session_id) 161 | { 162 | ERROR("unable to add session id node\n"); 163 | goto exit; 164 | } 165 | 166 | len = roxml_commit_changes(root, NULL, xml_out, 0); 167 | 168 | if (len <= 0) 169 | { 170 | ERROR("unable to create 'netconf hello' message\n"); 171 | goto exit; 172 | } 173 | 174 | rc = 0; 175 | 176 | exit: 177 | 178 | roxml_close(root); 179 | 180 | return rc; 181 | } 182 | 183 | /* 184 | * method_handle_message - handle all rpc messages 185 | * 186 | * @char*: xml message for parsing 187 | * @char**: xml message we create for response 188 | * 189 | * Get netconf method from rpc message and call apropriate rpc method which 190 | * will parse and return response message. 191 | */ 192 | int method_handle_message_rpc(char *xml_in, char **xml_out) 193 | { 194 | int rc = -1; 195 | char *operation_name = NULL; 196 | char *ns = NULL; 197 | struct rpc_data data = { NULL, NULL, NULL, 0}; 198 | 199 | node_t *root_in = roxml_load_buf(xml_in); 200 | 201 | if (!root_in) goto exit; 202 | 203 | node_t *rpc_in = roxml_get_chld(root_in, NULL, 0); 204 | 205 | if (!rpc_in) goto exit; 206 | 207 | node_t *operation = roxml_get_chld(rpc_in, NULL, 0); 208 | 209 | if (!operation) goto exit; 210 | 211 | node_t *n_ns = roxml_get_ns(operation); 212 | 213 | if (!n_ns) goto exit; 214 | 215 | operation_name = roxml_get_name(operation, NULL, 0); 216 | ns = roxml_get_content(n_ns, NULL, 0, NULL); 217 | 218 | if (!operation_name || !ns) 219 | { 220 | ERROR("unable to extract rpc and namespace\n"); 221 | goto exit; 222 | } 223 | 224 | DEBUG("received rpc '%s' (%s)\n", operation_name, ns); 225 | 226 | data.out = roxml_load_buf(XML_NETCONF_REPLY_TEMPLATE); 227 | node_t *rpc_out = roxml_get_chld(data.out, NULL, 0); 228 | 229 | /* copy all arguments from rpc to rpc-reply */ 230 | int args = roxml_get_attr_nb(rpc_in); 231 | 232 | for (int i = 0; i < args; i++) 233 | { 234 | 235 | int flags = ROXML_ATTR_NODE; 236 | node_t *n_arg = roxml_get_attr(rpc_in, NULL, i); 237 | 238 | char *name = roxml_get_name(n_arg, NULL, 0); 239 | 240 | // default namespace 241 | if (!strcmp(name, "")) 242 | flags |= ROXML_NS_NODE; 243 | 244 | char *value = roxml_get_content(n_arg, NULL, 0, NULL); 245 | 246 | roxml_add_node(rpc_out, 0, flags, name, value); 247 | } 248 | 249 | data.in = operation; 250 | data.out = rpc_out; 251 | 252 | const struct rpc_method *method = NULL; 253 | 254 | for (int i = 0; i < ARRAY_SIZE(rpc_methods); i++) 255 | { 256 | if (!strcmp(operation_name, rpc_methods[i].query)) 257 | { 258 | method = &rpc_methods[i]; 259 | break; 260 | } 261 | } 262 | 263 | /* process modules */ 264 | if (!method) 265 | { 266 | int found = 0; 267 | struct list_head *modules = get_modules(); 268 | struct module_list *elem; 269 | list_for_each_entry(elem, modules, list) 270 | { 271 | if (found) break; 272 | 273 | DEBUG("module: %s\n", elem->name); 274 | 275 | for (int i = 0; i < elem->m->rpc_count; i++) 276 | { 277 | if (!strcmp(elem->m->rpcs[i].query, operation_name) && 278 | !strcmp(elem->m->ns, ns)) 279 | { 280 | DEBUG("method found in module: %s (%s)\n", elem->m->rpcs[i].query, elem->m->ns); 281 | method = &elem->m->rpcs[i]; 282 | found = 1; 283 | break; 284 | } 285 | } 286 | } 287 | } 288 | 289 | if (!method) 290 | { 291 | ERROR("method not supported\n"); 292 | data.error = netconf_rpc_error("method not supported", RPC_ERROR_TAG_OPERATION_NOT_SUPPORTED, 0, 0, NULL); 293 | rc = RPC_ERROR; 294 | } 295 | else 296 | { 297 | rc = method->handler(&data); 298 | } 299 | 300 | switch (rc) 301 | { 302 | case RPC_OK: 303 | roxml_add_node(data.out, 0, ROXML_ELM_NODE, "ok", NULL); 304 | rc = 0; 305 | break; 306 | 307 | case RPC_OK_CLOSE: 308 | roxml_add_node(data.out, 0, ROXML_ELM_NODE, "ok", NULL); 309 | rc = 1; 310 | break; 311 | 312 | case RPC_DATA: 313 | rc = 0; 314 | break; 315 | 316 | case RPC_ERROR: 317 | if (!data.error) 318 | data.error = netconf_rpc_error("UNKNOWN ERROR", 0, 0, 0, NULL); 319 | 320 | roxml_add_node(data.out, 0, ROXML_ELM_NODE, "rpc-error", data.error); 321 | 322 | free(data.error); 323 | data.error = NULL; 324 | 325 | rc = 0; 326 | break; 327 | 328 | case RPC_DATA_EXISTS: 329 | if (!data.error) 330 | data.error = netconf_rpc_error("Data exists!", RPC_ERROR_TAG_DATA_EXISTS, RPC_ERROR_TYPE_RPC, RPC_ERROR_SEVERITY_ERROR, NULL); 331 | 332 | roxml_add_node(data.out, 0, ROXML_ELM_NODE, "rpc-error", data.error); 333 | 334 | free(data.error); 335 | data.error = NULL; 336 | 337 | rc = 0; 338 | break; 339 | 340 | case RPC_DATA_MISSING: 341 | if (!data.error) 342 | data.error = netconf_rpc_error("Data missing!", RPC_ERROR_TAG_DATA_MISSING, RPC_ERROR_TYPE_RPC, RPC_ERROR_SEVERITY_ERROR, NULL); 343 | 344 | roxml_add_node(data.out, 0, ROXML_ELM_NODE, "rpc-error", data.error); 345 | 346 | free(data.error); 347 | data.error = NULL; 348 | 349 | rc = 0; 350 | break; 351 | } 352 | 353 | exit: 354 | 355 | if (data.out) 356 | { 357 | roxml_commit_changes(data.out, NULL, xml_out, 0); 358 | roxml_close(data.out); 359 | } 360 | 361 | roxml_release(RELEASE_ALL); 362 | roxml_close(root_in); 363 | 364 | return rc; 365 | } 366 | 367 | static int 368 | method_handle_get(struct rpc_data *data) 369 | { 370 | node_t *n_data = roxml_add_node(data->out, 0, ROXML_ELM_NODE, "data", NULL); 371 | 372 | int nb = 0; 373 | node_t **n_filter, *n; 374 | 375 | struct list_head *modules = get_modules(); 376 | struct module_list *elem; 377 | 378 | if ((n_filter = roxml_xpath(data->in, "//filter", &nb))) 379 | { 380 | nb = roxml_get_chld_nb(n_filter[0]); 381 | 382 | /* empty filter */ 383 | if (!nb) 384 | return RPC_DATA; 385 | 386 | while (--nb >= 0) 387 | { 388 | n = roxml_get_chld(n_filter[0], NULL, nb); 389 | char *module = roxml_get_name(n, NULL, 0); 390 | char *ns = roxml_get_content(roxml_get_ns(n), NULL, 0, NULL); 391 | DEBUG("filter for module: %s (%s)\n", module, ns); 392 | 393 | list_for_each_entry(elem, modules, list) 394 | { 395 | DEBUG("module: %s\n", elem->name); 396 | 397 | if (!strcmp(ns, elem->m->ns)) 398 | { 399 | DEBUG("calling module: %s (%s) \n", module, ns); 400 | struct rpc_data d = {n, n_data, NULL, data->get_config}; 401 | 402 | get(&d, elem->m->datastore); 403 | 404 | break; 405 | } 406 | } 407 | } 408 | } 409 | else 410 | { 411 | DEBUG("no filter requested, processing all modules\n"); 412 | list_for_each_entry(elem, modules, list) 413 | { 414 | DEBUG("calling module: %s\n", elem->name); 415 | n = data->in; 416 | struct rpc_data d = {n, n_data, NULL, data->get_config}; 417 | get(&d, elem->m->datastore); 418 | } 419 | } 420 | 421 | return RPC_DATA; 422 | } 423 | 424 | static int get(struct rpc_data *data, datastore_t *datastore) 425 | { 426 | node_t *ro_root = data->in; 427 | char *ro_root_name = roxml_get_name(ro_root, NULL, 0); 428 | 429 | // client requested get all 430 | if (ro_root_name && (!strcmp("get", ro_root_name) || !strcmp("get-config", ro_root_name))) 431 | { 432 | ds_get_all(datastore->child, data->out, data->get_config, 1); 433 | 434 | return RPC_DATA; 435 | } 436 | 437 | // client requested filtered get 438 | datastore_t *our_root = ds_find_child(datastore, ro_root_name, NULL); 439 | ds_get_filtered(ro_root, our_root, data->out, data->get_config); 440 | 441 | return RPC_DATA; 442 | } 443 | 444 | 445 | static int 446 | method_handle_get_config(struct rpc_data *data) 447 | { 448 | // TODO: merge with get 449 | data->get_config = 1; 450 | return method_handle_get(data); 451 | } 452 | 453 | static int 454 | method_handle_edit_config(struct rpc_data *data) 455 | { 456 | node_t *config = roxml_get_chld(data->in, "config", 0); 457 | 458 | if (!config) return RPC_ERROR; 459 | 460 | struct list_head *modules = get_modules(); 461 | struct module_list *elem; 462 | 463 | int rc = RPC_OK; 464 | 465 | int child_count = roxml_get_chld_nb(config); 466 | 467 | for (int i = 0; i < child_count; i++) 468 | { 469 | node_t *cur = roxml_get_chld(config, NULL, i); 470 | 471 | char *module = roxml_get_name(cur, NULL, 0); 472 | char *ns = roxml_get_content(roxml_get_ns(cur), NULL, 0, NULL); 473 | 474 | DEBUG("edit_config for module: %s (%s)\n", module, ns); 475 | 476 | list_for_each_entry(elem, modules, list) 477 | { 478 | DEBUG("module: %s\n", elem->name); 479 | 480 | if (!strcmp(ns, elem->m->ns)) 481 | { 482 | DEBUG("calling module: %s (%s) \n", module, ns); 483 | rc = ds_edit_config(cur, elem->m->datastore->child, NULL); 484 | break; 485 | } 486 | } 487 | } 488 | 489 | return rc; 490 | } 491 | 492 | static int 493 | method_handle_copy_config(struct rpc_data *data) 494 | { 495 | return RPC_OK; 496 | } 497 | 498 | static int 499 | method_handle_delete_config(struct rpc_data *data) 500 | { 501 | return RPC_OK; 502 | } 503 | 504 | static int 505 | method_handle_lock(struct rpc_data *data) 506 | { 507 | return RPC_OK; 508 | } 509 | 510 | static int 511 | method_handle_unlock(struct rpc_data *data) 512 | { 513 | return RPC_OK; 514 | } 515 | 516 | static int 517 | method_handle_close_session(struct rpc_data *data) 518 | { 519 | return RPC_OK_CLOSE; 520 | } 521 | 522 | static int 523 | method_handle_kill_session(struct rpc_data *data) 524 | { 525 | return RPC_OK_CLOSE; 526 | } 527 | 528 | static int method_handle_get_schema(struct rpc_data *data) 529 | { 530 | FILE *yang_module = NULL; 531 | char yang_module_filename[BUFSIZ]; 532 | char *yang_module_content = NULL; 533 | long yang_module_size; 534 | node_t *n_identifier, *n_version, *n_format; 535 | char *c_identifier, *c_version, *c_format; 536 | char *xml_entities[5][2] = {{"&", "&"}, 537 | {"\"", """}, 538 | {"\'", "'"}, 539 | {"<", "<"}, 540 | {">", ">"} 541 | }; 542 | 543 | if (!config.yang_dir) 544 | { 545 | ERROR ("yang dir not specified\n"); 546 | goto exit; 547 | } 548 | 549 | LOG("yang directory: %s\n", config.yang_dir); 550 | 551 | n_identifier = roxml_get_chld(data->in, "identifier", 0); 552 | c_identifier = roxml_get_content(n_identifier, NULL, 0, NULL); 553 | 554 | if (!n_identifier || !c_identifier) 555 | { 556 | ERROR("yang module identifier not specified\n"); 557 | goto exit; 558 | } 559 | 560 | n_version = roxml_get_chld(data->in, "version", 0); 561 | c_version = roxml_get_content(n_version, NULL, 0, NULL); 562 | 563 | /* 'yang' format if ommited */ 564 | n_format = roxml_get_chld(data->in, "format", 0); 565 | c_format = roxml_get_content(n_format, NULL, 0, NULL); 566 | 567 | DEBUG("yang format:%s\n", c_format); 568 | 569 | /* TODO: return rpc-error */ 570 | if (n_format && c_format && !strstr(c_format, "yang")) 571 | { 572 | ERROR("yang format not valid or supported\n"); 573 | goto exit; 574 | } 575 | 576 | snprintf(yang_module_filename, BUFSIZ, "%s/%s", config.yang_dir, c_identifier); 577 | 578 | if (c_version) 579 | { 580 | snprintf(yang_module_filename + strlen(yang_module_filename), BUFSIZ, "@%s", c_version); 581 | } 582 | 583 | strncat(yang_module_filename, ".yang", 5); 584 | 585 | DEBUG("yang filename:%s\n", yang_module_filename); 586 | 587 | yang_module = fopen (yang_module_filename, "rb"); 588 | 589 | if (!yang_module) 590 | { 591 | ERROR("yang module:%s not found\n", yang_module_filename); 592 | goto exit; 593 | } 594 | 595 | fseek (yang_module, 0, SEEK_END); 596 | yang_module_size = ftell (yang_module); 597 | fseek (yang_module, 0, SEEK_SET); 598 | yang_module_content = malloc (yang_module_size); 599 | 600 | if (!yang_module_content) 601 | { 602 | ERROR("unable to load yang module\n"); 603 | goto exit; 604 | } 605 | 606 | /* escape xml from yang module */ 607 | int c, pos = 0; 608 | 609 | while ((c = fgetc(yang_module)) != EOF) 610 | { 611 | char ch = (char) c; 612 | int escape_found = 0; 613 | 614 | for (int i = 0; i < 5; i++) 615 | { 616 | char r = xml_entities[i][0][0]; 617 | char *s = xml_entities[i][1]; 618 | int len = strlen(s); 619 | 620 | if (r == ch) 621 | { 622 | yang_module_size += len; 623 | yang_module_content = realloc(yang_module_content, yang_module_size); 624 | strncpy(yang_module_content + pos, s, len); 625 | pos += len; 626 | escape_found = 1; 627 | break; 628 | } 629 | } 630 | 631 | if (!escape_found) 632 | yang_module_content[pos++] = ch; 633 | } 634 | 635 | yang_module_content[pos] = 0; 636 | 637 | node_t *n_schema = roxml_add_node(data->out, 0, ROXML_ELM_NODE, "data", yang_module_content); 638 | 639 | if (!n_schema) 640 | { 641 | ERROR("unable to add data node\n"); 642 | goto exit; 643 | } 644 | 645 | node_t *n_attr = roxml_add_node(n_schema, 0, ROXML_ATTR_NODE, "xmlns", "urn:ietf:params:xml:ns:yang:ietf-netconf-monitoring"); 646 | 647 | if (!n_attr) 648 | { 649 | ERROR("unable to set attribute\n"); 650 | goto exit; 651 | } 652 | 653 | exit: 654 | 655 | if (yang_module) 656 | fclose(yang_module); 657 | 658 | free(yang_module_content); 659 | 660 | return RPC_DATA; 661 | } 662 | -------------------------------------------------------------------------------- /src/methods.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2014 Sartura, Ltd. 3 | * Copyright (C) 2014 Cisco Systems, Inc. 4 | * 5 | * Author: Luka Perkov 6 | * Author: Petar Koretic 7 | * 8 | * freenetconfd is free software: you can redistribute it and/or modify 9 | * it under the terms of the GNU General Public License as published by 10 | * the Free Software Foundation, either version 2 of the License, or 11 | * (at your option) any later version. 12 | * 13 | * You should have received a copy of the GNU General Public License 14 | * along with freenetconfd. If not, see . 15 | */ 16 | 17 | #ifndef __FREENETCONFD_METHODS_H__ 18 | #define __FREENETCONFD_METHODS_H__ 19 | 20 | int method_analyze_message_hello(char *method_in, int *base); 21 | int method_create_message_hello(char **method_out); 22 | int method_handle_message_rpc(char *method_in, char **method_out); 23 | 24 | #endif /* __FREENETCONFD_METHODS_H__ */ 25 | -------------------------------------------------------------------------------- /src/modules.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2014 Cisco Systems, Inc. 3 | * 4 | * Author: Petar Koretic 5 | * Author: Luka Perkov 6 | * Author: Zvonimir Fras 7 | * 8 | * freenetconfd is free software: you can redistribute it and/or modify 9 | * it under the terms of the GNU General Public License as published by 10 | * the Free Software Foundation, either version 2 of the License, or 11 | * (at your option) any later version. 12 | * 13 | * You should have received a copy of the GNU General Public License 14 | * along with freenetconfd. If not, see . 15 | */ 16 | 17 | #include 18 | #include 19 | #include 20 | #include 21 | #include 22 | 23 | #include "freenetconfd/freenetconfd.h" 24 | 25 | #include "modules.h" 26 | #include "config.h" 27 | 28 | LIST_HEAD(module_list); 29 | 30 | /* handle our internal list above */ 31 | struct list_head *get_modules() 32 | { 33 | return &module_list; 34 | } 35 | 36 | int modules_init() 37 | { 38 | return modules_load(config.modules_dir, &module_list); 39 | } 40 | 41 | static int module_load(char *modules_path, char *name, struct module_list **e) 42 | { 43 | if (!name) 44 | return 1; 45 | 46 | struct module* (*init)(); 47 | void *lib; 48 | 49 | *e = malloc(sizeof (struct module_list)); 50 | 51 | if (!e) 52 | { 53 | ERROR("not enough memory to load module\n"); 54 | return -1; 55 | } 56 | 57 | char module_path[BUFSIZ]; 58 | snprintf(module_path, BUFSIZ, "%s/%s", modules_path, name); 59 | 60 | lib = dlopen(module_path, RTLD_LAZY); 61 | 62 | if (!lib) 63 | { 64 | ERROR("%s\n", dlerror()); 65 | 66 | return 1; 67 | } 68 | 69 | init = dlsym(lib, "init"); 70 | 71 | if (!init) 72 | { 73 | dlclose(lib); 74 | 75 | return 2; 76 | } 77 | 78 | // load module data 79 | (*e)->m = init(); 80 | 81 | if (!(*e)->m) 82 | { 83 | dlclose(lib); 84 | free(*e); 85 | 86 | return 3; 87 | } 88 | 89 | (*e)->name = strdup(name); 90 | (*e)->lib = lib; 91 | 92 | return 0; 93 | } 94 | 95 | static void module_unload(struct module_list **elem) 96 | { 97 | void (*destroy)(); 98 | destroy = dlsym((*elem)->lib, "destroy"); 99 | 100 | if (destroy) destroy(); 101 | 102 | free((*elem)->name); 103 | dlclose((*elem)->lib); 104 | list_del(&(*elem)->list); 105 | free(*elem); 106 | } 107 | 108 | int modules_load(char *modules_path, struct list_head *module_list) 109 | { 110 | DIR *dir; 111 | struct dirent *file; 112 | int rc = 0; 113 | 114 | if ((dir = opendir(modules_path)) == NULL) 115 | { 116 | ERROR("unable to open modules dir: %s\n", modules_path); 117 | return 1; 118 | } 119 | 120 | while ((file = readdir(dir)) != NULL) 121 | { 122 | if (!strstr(file->d_name, ".so")) 123 | continue; 124 | 125 | struct module_list *e; 126 | rc = module_load(modules_path, file->d_name, &e); 127 | 128 | if (rc) 129 | { 130 | ERROR("unable to load module: '%s' (%d)\n", file->d_name, rc); 131 | closedir (dir); 132 | return 1; 133 | } 134 | 135 | list_add(&e->list, module_list); 136 | 137 | DEBUG("loaded module: '%s'\n", file->d_name); 138 | } 139 | 140 | closedir(dir); 141 | 142 | return 0; 143 | } 144 | 145 | int modules_unload() 146 | { 147 | struct module_list *elem, *next; 148 | list_for_each_entry_safe(elem, next, &module_list, list) 149 | { 150 | DEBUG("unloading module: '%s'\n", elem->name); 151 | module_unload(&elem); 152 | } 153 | 154 | return 0; 155 | } 156 | 157 | int modules_reload(char *module_name) 158 | { 159 | struct module_list *elem, *next; 160 | list_for_each_entry_safe(elem, next, &module_list, list) 161 | { 162 | // reload all modules 163 | if (!module_name) 164 | { 165 | module_unload(&elem); 166 | } 167 | // reload specific module if exists 168 | else if (!strcmp(module_name, elem->name)) 169 | { 170 | module_unload(&elem); 171 | 172 | return module_load(config.modules_dir, module_name, &elem); 173 | } 174 | } 175 | 176 | return 1; 177 | } 178 | -------------------------------------------------------------------------------- /src/modules.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2014 Cisco Systems, Inc. 3 | * 4 | * Author: Petar Koretic 5 | * Author: Luka Perkov 6 | * Author: Zvonimir Fras 7 | * 8 | * freenetconfd is free software: you can redistribute it and/or modify 9 | * it under the terms of the GNU General Public License as published by 10 | * the Free Software Foundation, either version 2 of the License, or 11 | * (at your option) any later version. 12 | * 13 | * You should have received a copy of the GNU General Public License 14 | * along with freenetconfd. If not, see . 15 | */ 16 | 17 | #ifndef __FREENETCONFD_MODULES_H_ 18 | #define __FREENETCONFD_MODULES_H_ 19 | 20 | #include 21 | #include 22 | 23 | int modules_load(char *modules_path, struct list_head *modules); 24 | int modules_unload(); 25 | int modules_reload(char *module_name); 26 | 27 | int modules_init(); 28 | struct list_head *get_modules(); 29 | 30 | #endif /* __FREENETCONFD_MODULES_H_ */ 31 | -------------------------------------------------------------------------------- /src/netconf.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2014 Cisco Systems, Inc. 3 | * Copyright (C) 2014 Sartura, Ltd. 4 | * 5 | * Author: Zvonimir Fras 6 | * Author: Luka Perkov 7 | * Author: Petar Koretic 8 | * 9 | * freenetconfd is free software: you can redistribute it and/or modify 10 | * it under the terms of the GNU General Public License as published by 11 | * the Free Software Foundation, either version 2 of the License, or 12 | * (at your option) any later version. 13 | * 14 | * You should have received a copy of the GNU General Public License 15 | * along with freenetconfd. If not, see . 16 | */ 17 | 18 | #include "freenetconfd/freenetconfd.h" 19 | #include "freenetconfd/netconf.h" 20 | 21 | #include "netconf.h" 22 | #include "messages.h" 23 | 24 | #include 25 | #include 26 | #include 27 | 28 | int netconf_capabilites_from_yang(char *yang_dir, node_t *root) 29 | { 30 | DIR *dir; 31 | struct dirent *file; 32 | int rc = 0; 33 | char *revision; 34 | 35 | if (!root) 36 | { 37 | ERROR("xml root not specified\n"); 38 | return 1; 39 | } 40 | 41 | if (!yang_dir) 42 | { 43 | ERROR("yang dir not specified\n"); 44 | return 1; 45 | } 46 | 47 | // get capabilities node 48 | node_t *capabilities = roxml_get_chld(roxml_get_chld(root, "hello", 0), "capabilities", 0); 49 | 50 | if (!capabilities) 51 | { 52 | ERROR("error in xml, not found\n"); 53 | return 1; 54 | } 55 | 56 | if ((dir = opendir(yang_dir)) == NULL) 57 | { 58 | ERROR("openning yang directory failed:%s\n", yang_dir); 59 | return 1; 60 | } 61 | 62 | while ((file = readdir (dir)) != NULL) 63 | { 64 | // list only yang files 65 | char *ext = strstr(file->d_name, ".yang"); 66 | 67 | if (!ext) 68 | continue; 69 | 70 | DEBUG("yang module %s\n", file->d_name); 71 | 72 | // remove extension 73 | ext[0] = 0; 74 | revision = strstr(file->d_name, "@"); 75 | 76 | char *name = file->d_name; 77 | 78 | char *capability_content = NULL; 79 | 80 | if (!revision) 81 | { 82 | asprintf(&capability_content, "%s:%s?module=%s", YANG_NAMESPACE, name, name); 83 | } 84 | else 85 | { 86 | *revision = '\0'; 87 | asprintf(&capability_content, "%s:%s?module=%s&revision=%s", YANG_NAMESPACE, name, name, revision + 1); 88 | } 89 | 90 | roxml_add_node(capabilities, 0, ROXML_ELM_NODE, "capability", capability_content); 91 | free (capability_content); 92 | capability_content = NULL; 93 | } 94 | 95 | closedir(dir); 96 | free(file); 97 | 98 | return rc; 99 | } 100 | 101 | char *rpc_error_tags[__RPC_ERROR_TAG_COUNT] = 102 | { 103 | "operation-failed", 104 | "operation-not-supported", 105 | "in-use", 106 | "invalid-value", 107 | "data-missing", 108 | "data-exists" 109 | }; 110 | 111 | char *rpc_error_types[__RPC_ERROR_TYPE_COUNT] = 112 | { 113 | "transport", 114 | "rpc", 115 | "protocol", 116 | "application" 117 | }; 118 | 119 | 120 | char *rpc_error_severities[__RPC_ERROR_SEVERITY_COUNT] = 121 | { 122 | "error", 123 | "warning" 124 | }; 125 | 126 | char *netconf_rpc_error(char *msg, rpc_error_tag_t rpc_error_tag, rpc_error_type_t rpc_error_type, rpc_error_severity_t rpc_error_severity, char *error_app_tag) 127 | { 128 | // defaults 129 | char *tag = "operation-failed"; 130 | char *type = "rpc"; 131 | char *severity = "error"; 132 | 133 | char *rpc_error = NULL; 134 | 135 | // truncate too big messages 136 | if (!msg || strlen(msg) > 400) 137 | msg = ""; 138 | 139 | if (rpc_error_tag > 0 && rpc_error_tag < __RPC_ERROR_TAG_COUNT) 140 | tag = rpc_error_tags[rpc_error_tag]; 141 | 142 | if (rpc_error_type > 0 && rpc_error_type < __RPC_ERROR_TYPE_COUNT) 143 | type = rpc_error_types[rpc_error_type]; 144 | 145 | if (rpc_error_severity > 0 && rpc_error_severity < __RPC_ERROR_SEVERITY_COUNT) 146 | severity = rpc_error_severities[rpc_error_severity]; 147 | 148 | char *error_app_tag_buff = NULL; 149 | 150 | if (error_app_tag) 151 | asprintf(&error_app_tag_buff, "%s", error_app_tag); 152 | 153 | asprintf(&rpc_error, "%s%s" 154 | "%s%s%s", type, tag, severity, msg, error_app_tag_buff ? error_app_tag_buff : ""); 155 | 156 | free(error_app_tag_buff); 157 | 158 | return rpc_error; 159 | } 160 | -------------------------------------------------------------------------------- /src/netconf.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2014 Sartura, Ltd. 3 | * Copyright (C) 2014 Cisco Systems, Inc. 4 | * 5 | * Author: Luka Perkov 6 | * Author: Petar Koretic 7 | * 8 | * freenetconfd is free software: you can redistribute it and/or modify 9 | * it under the terms of the GNU General Public License as published by 10 | * the Free Software Foundation, either version 2 of the License, or 11 | * (at your option) any later version. 12 | * 13 | * You should have received a copy of the GNU General Public License 14 | * along with freenetconfd. If not, see . 15 | */ 16 | 17 | #ifndef __FREENETCONFD_SRC_NETCONF_H__ 18 | #define __FREENETCONFD_SRC_NETCONF_H__ 19 | 20 | #include 21 | 22 | int netconf_capabilites_from_yang(char *yang_dir, node_t *root); 23 | 24 | /* RFC: http://tools.ietf.org/html/rfc6241#appendix-A */ 25 | 26 | #endif /* __FREENETCONFD_SRC_NETCONF_H__ */ 27 | -------------------------------------------------------------------------------- /src/ubus.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2014 Sartura, Ltd. 3 | * Copyright (C) 2014 Cisco Systems, Inc. 4 | * 5 | * Author: Luka Perkov 6 | * Author: Petar Koretic 7 | * 8 | * freenetconfd is free software: you can redistribute it and/or modify 9 | * it under the terms of the GNU General Public License as published by 10 | * the Free Software Foundation, either version 2 of the License, or 11 | * (at your option) any later version. 12 | * 13 | * You should have received a copy of the GNU General Public License 14 | * along with freenetconfd. If not, see . 15 | */ 16 | 17 | #include 18 | #include 19 | #include 20 | 21 | #include "freenetconfd/freenetconfd.h" 22 | 23 | #include "ubus.h" 24 | 25 | static struct ubus_context *ubus = NULL; 26 | static struct ubus_object main_object; 27 | 28 | static const struct ubus_method fnd_methods[] = {}; 29 | 30 | static struct ubus_object_type main_object_type = 31 | UBUS_OBJECT_TYPE("freenetconfd", fnd_methods); 32 | 33 | static struct ubus_object main_object = 34 | { 35 | .name = "netconf", 36 | .type = &main_object_type, 37 | .methods = fnd_methods, 38 | .n_methods = ARRAY_SIZE(fnd_methods), 39 | }; 40 | 41 | int 42 | ubus_init(void) 43 | { 44 | ubus = ubus_connect(NULL); 45 | 46 | if (!ubus) return -1; 47 | 48 | ubus_add_uloop(ubus); 49 | 50 | if (ubus_add_object(ubus, &main_object)) return -1; 51 | 52 | return 0; 53 | } 54 | 55 | void 56 | ubus_exit(void) 57 | { 58 | if (ubus) ubus_free(ubus); 59 | } 60 | -------------------------------------------------------------------------------- /src/ubus.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2014 Sartura, Ltd. 3 | * Copyright (C) 2014 Cisco Systems, Inc. 4 | * 5 | * Author: Luka Perkov 6 | * Author: Petar Koretic 7 | * 8 | * freenetconfd is free software: you can redistribute it and/or modify 9 | * it under the terms of the GNU General Public License as published by 10 | * the Free Software Foundation, either version 2 of the License, or 11 | * (at your option) any later version. 12 | * 13 | * You should have received a copy of the GNU General Public License 14 | * along with freenetconfd. If not, see . 15 | */ 16 | 17 | #ifndef __FREENETCONFD_UBUS_H__ 18 | #define __FREENETCONFD_UBUS_H__ 19 | 20 | int ubus_init(void); 21 | void ubus_exit(void); 22 | 23 | #endif /* __FREENETCONFD_UBUS_H__ */ 24 | --------------------------------------------------------------------------------