├── .gitignore ├── COPYING ├── LICENSE_gdhcp ├── LICENSE_htable ├── LICENSE_lgpl ├── Makefile.am ├── NEWS ├── README ├── autogen.sh ├── configure.ac ├── res ├── org.freedesktop.miracle.conf └── wpa.conf ├── src ├── ctl │ ├── ctl-cli.c │ ├── ctl-wifi.c │ ├── ctl.h │ └── wifictl.c ├── dhcp │ ├── client.c │ ├── common.c │ ├── common.h │ ├── dhcp.c │ ├── gdhcp.h │ ├── ipv4ll.c │ ├── ipv4ll.h │ ├── server.c │ └── unaligned.h ├── miracled.c ├── miracled.h ├── shared │ ├── shl_dlist.h │ ├── shl_htable.c │ ├── shl_htable.h │ ├── shl_log.c │ ├── shl_log.h │ ├── shl_macro.h │ ├── shl_util.c │ ├── shl_util.h │ ├── util.h │ ├── wpas.c │ └── wpas.h └── wifi │ ├── wifid-dbus.c │ ├── wifid-link.c │ ├── wifid-peer.c │ ├── wifid-supplicant.c │ ├── wifid.c │ └── wifid.h └── test ├── test_common.h └── test_wpas.c /.gitignore: -------------------------------------------------------------------------------- 1 | *.la 2 | *.lo 3 | *.log 4 | *.o 5 | *.swp 6 | *.tar.xz 7 | *.trs 8 | .deps/ 9 | .dirstamp 10 | .libs/ 11 | Makefile 12 | Makefile.in 13 | aclocal.m4 14 | autom4te.cache/ 15 | build-aux/ 16 | config.h 17 | config.h.in 18 | config.h.in~ 19 | config.log 20 | config.status 21 | configure 22 | libtool 23 | m4/ 24 | miracle-dhcp 25 | miracle-wifictl 26 | miracle-wifid 27 | miraclectl 28 | miracled 29 | stamp-h1 30 | test-suite.log 31 | test_wpas 32 | wpa_cli 33 | wpa_supplicant 34 | -------------------------------------------------------------------------------- /COPYING: -------------------------------------------------------------------------------- 1 | = Authors = 2 | 3 | This software was written by: 4 | David Herrmann 5 | 6 | = Copyright Notice = 7 | 8 | This software is licensed under the terms of the LGPL. Please see each source 9 | file for the related copyright notice and license. 10 | 11 | If a file does not contain a copright notice, the following notice shall apply: 12 | 13 | MiracleCast - Wifi-Display/Miracast Implementation 14 | 15 | Copyright (c) 2013-2014 David Herrmann 16 | 17 | MiracleCast is free software; you can redistribute it and/or modify it 18 | under the terms of the GNU Lesser General Public License as published by 19 | the Free Software Foundation; either version 2.1 of the License, or 20 | (at your option) any later version. 21 | 22 | MiracleCast is distributed in the hope that it will be useful, but 23 | WITHOUT ANY WARRANTY; without even the implied warranty of 24 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 25 | Lesser General Public License for more details. 26 | 27 | You should have received a copy of the GNU Lesser General Public License 28 | along with MiracleCast; If not, see . 29 | 30 | == Third-Party Source == 31 | 32 | src/shl_htable.[ch]: Released under the conditions of the LGPLv2+. 33 | Please see LICENSE_htable for more. 34 | 35 | src/dhcp/*: Released under the conditions of the GPLv2. 36 | Please see LICENSE_gdhcp for more. 37 | -------------------------------------------------------------------------------- /LICENSE_gdhcp: -------------------------------------------------------------------------------- 1 | GNU GENERAL PUBLIC LICENSE 2 | Version 2, June 1991 3 | 4 | Copyright (C) 1989, 1991 Free Software Foundation, Inc. 5 | 51 Franklin St, 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 Library 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 | convey 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 | 294 | Copyright (C) 295 | 296 | This program is free software; you can redistribute it and/or modify 297 | it under the terms of the GNU General Public License as published by 298 | the Free Software Foundation; either version 2 of the License, or 299 | (at your option) any later version. 300 | 301 | This program is distributed in the hope that it will be useful, 302 | but WITHOUT ANY WARRANTY; without even the implied warranty of 303 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 304 | GNU General Public License for more details. 305 | 306 | You should have received a copy of the GNU General Public License 307 | along with this program; if not, write to the Free Software 308 | Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA 309 | 310 | 311 | Also add information on how to contact you by electronic and paper mail. 312 | 313 | If the program is interactive, make it output a short notice like this 314 | when it starts in an interactive mode: 315 | 316 | Gnomovision version 69, Copyright (C) year name of author 317 | Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. 318 | This is free software, and you are welcome to redistribute it 319 | under certain conditions; type `show c' for details. 320 | 321 | The hypothetical commands `show w' and `show c' should show the appropriate 322 | parts of the General Public License. Of course, the commands you use may 323 | be called something other than `show w' and `show c'; they could even be 324 | mouse-clicks or menu items--whatever suits your program. 325 | 326 | You should also get your employer (if you work as a programmer) or your 327 | school, if any, to sign a "copyright disclaimer" for the program, if 328 | necessary. Here is a sample; alter the names: 329 | 330 | Yoyodyne, Inc., hereby disclaims all copyright interest in the program 331 | `Gnomovision' (which makes passes at compilers) written by James Hacker. 332 | 333 | , 1 April 1989 334 | Ty Coon, President of Vice 335 | 336 | This General Public License does not permit incorporating your program into 337 | proprietary programs. If your program is a subroutine library, you may 338 | consider it more useful to permit linking proprietary applications with the 339 | library. If this is what you want to do, use the GNU Library General 340 | Public License instead of this License. 341 | -------------------------------------------------------------------------------- /Makefile.am: -------------------------------------------------------------------------------- 1 | # 2 | # MiracleCast - Global Makefile 3 | # Copyright (c) 2013-2014 David Herrmann 4 | # 5 | 6 | # 7 | # Global Configurations and Initializations 8 | # 9 | 10 | ACLOCAL_AMFLAGS = -I m4 ${ACLOCAL_FLAGS} 11 | AM_MAKEFLAGS = --no-print-directory 12 | AUTOMAKE_OPTIONS = color-tests 13 | 14 | SUBDIRS = . 15 | 16 | .DELETE_ON_ERROR: 17 | 18 | include_HEADERS = 19 | EXTRA_DIST = \ 20 | README \ 21 | COPYING \ 22 | NEWS \ 23 | res/wpa.conf 24 | CLEANFILES = 25 | pkgconfigdir = $(libdir)/pkgconfig 26 | pkgconfig_DATA = 27 | TPHONY = 28 | 29 | TESTS = 30 | bin_PROGRAMS = 31 | check_PROGRAMS = 32 | lib_LTLIBRARIES = 33 | noinst_LTLIBRARIES = 34 | 35 | # 36 | # Default CFlags 37 | # Make all files include "config.h" by default. This shouldn't cause any 38 | # problems and we cannot forget to include it anymore. 39 | # Also make the linker discard all unused symbols. 40 | # 41 | 42 | AM_CFLAGS = \ 43 | -Wall \ 44 | -pipe \ 45 | -fno-common \ 46 | -ffast-math \ 47 | -fdiagnostics-show-option \ 48 | -fno-strict-aliasing \ 49 | -fvisibility=hidden \ 50 | -ffunction-sections \ 51 | -fdata-sections 52 | AM_CPPFLAGS = \ 53 | -include $(top_builddir)/config.h \ 54 | -I $(srcdir)/src \ 55 | -I $(srcdir)/src/shared \ 56 | -I /usr/local/include \ 57 | -DBUILD_ENABLE_DEBUG \ 58 | '-DBUILD_BINDIR="$(bindir)"' 59 | AM_LDFLAGS = \ 60 | -Wl,--as-needed \ 61 | -Wl,--gc-sections \ 62 | -Wl,-z,relro \ 63 | -Wl,-z,now 64 | AM_LIBADD = \ 65 | -lsystemd 66 | 67 | # 68 | # Shared Helpers 69 | # 70 | 71 | noinst_LTLIBRARIES += libmiracle-shared.la 72 | 73 | libmiracle_shared_la_SOURCES = \ 74 | src/shared/shl_dlist.h \ 75 | src/shared/shl_htable.h \ 76 | src/shared/shl_htable.c \ 77 | src/shared/shl_log.h \ 78 | src/shared/shl_log.c \ 79 | src/shared/shl_macro.h \ 80 | src/shared/shl_util.h \ 81 | src/shared/shl_util.c \ 82 | src/shared/util.h \ 83 | src/shared/wpas.h \ 84 | src/shared/wpas.c 85 | libmiracle_shared_la_CPPFLAGS = $(AM_CPPFLAGS) 86 | libmiracle_shared_la_LDFLAGS = $(AM_LDFLAGS) 87 | libmiracle_shared_la_LIBADD = $(AM_LIBADD) 88 | 89 | # 90 | # miracle-wifid 91 | # 92 | 93 | bin_PROGRAMS += miracle-wifid 94 | 95 | miracle_wifid_SOURCES = \ 96 | src/wifi/wifid.h \ 97 | src/wifi/wifid.c \ 98 | src/wifi/wifid-dbus.c \ 99 | src/wifi/wifid-link.c \ 100 | src/wifi/wifid-peer.c \ 101 | src/wifi/wifid-supplicant.c 102 | miracle_wifid_CPPFLAGS = \ 103 | $(AM_CPPFLAGS) \ 104 | $(DEPS_CFLAGS) 105 | miracle_wifid_LDADD = \ 106 | libmiracle-shared.la \ 107 | $(DEPS_LIBS) 108 | miracle_wifid_LDFLAGS = $(AM_LDFLAGS) 109 | 110 | # 111 | # miracle-wifictl 112 | # 113 | 114 | bin_PROGRAMS += miracle-wifictl 115 | 116 | miracle_wifictl_SOURCES = \ 117 | src/ctl/ctl.h \ 118 | src/ctl/ctl-cli.c \ 119 | src/ctl/ctl-wifi.c \ 120 | src/ctl/wifictl.c 121 | miracle_wifictl_CPPFLAGS = \ 122 | $(AM_CPPFLAGS) \ 123 | $(DEPS_CFLAGS) 124 | miracle_wifictl_LDADD = \ 125 | libmiracle-shared.la \ 126 | -lreadline \ 127 | $(DEPS_LIBS) 128 | miracle_wifictl_LDFLAGS = $(AM_LDFLAGS) 129 | 130 | # 131 | # miracle-dhcp 132 | # 133 | 134 | bin_PROGRAMS += miracle-dhcp 135 | 136 | miracle_dhcp_SOURCES = \ 137 | src/dhcp/dhcp.c \ 138 | src/dhcp/gdhcp.h \ 139 | src/dhcp/unaligned.h \ 140 | src/dhcp/common.h \ 141 | src/dhcp/common.c \ 142 | src/dhcp/ipv4ll.h \ 143 | src/dhcp/ipv4ll.c \ 144 | src/dhcp/client.c \ 145 | src/dhcp/server.c 146 | miracle_dhcp_CPPFLAGS = \ 147 | $(AM_CPPFLAGS) \ 148 | $(DEPS_CFLAGS) \ 149 | $(GDHCP_CFLAGS) 150 | miracle_dhcp_LDADD = \ 151 | libmiracle-shared.la \ 152 | $(DEPS_LIBS) \ 153 | $(GDHCP_LIBS) 154 | miracle_dhcp_LDFLAGS = $(AM_LDFLAGS) 155 | 156 | # 157 | # miracled 158 | # 159 | 160 | bin_PROGRAMS += miracled 161 | 162 | miracled_SOURCES = \ 163 | src/miracled.h \ 164 | src/miracled.c 165 | miracled_CPPFLAGS = \ 166 | $(AM_CPPFLAGS) \ 167 | $(DEPS_CFLAGS) 168 | miracled_LDADD = \ 169 | libmiracle-shared.la \ 170 | $(DEPS_LIBS) 171 | miracled_LDFLAGS = $(AM_LDFLAGS) 172 | 173 | # 174 | # Tests 175 | # 176 | 177 | tests = \ 178 | test_wpas 179 | 180 | if BUILD_HAVE_CHECK 181 | check_PROGRAMS += $(tests) 182 | TESTS += $(tests) 183 | endif 184 | 185 | test_sources = \ 186 | test/test_common.h 187 | test_libs = \ 188 | libmiracle-shared.la \ 189 | $(DEPS_LIBS) \ 190 | $(CHECK_LIBS) 191 | test_cflags = \ 192 | $(AM_CPPFLAGS) \ 193 | $(DEPS_CFLAGS) \ 194 | $(CHECK_CFLAGS) 195 | test_lflags = \ 196 | $(AM_LDFLAGS) 197 | 198 | test_wpas_SOURCES = test/test_wpas.c $(test_sources) 199 | test_wpas_CPPFLAGS = $(test_cflags) 200 | test_wpas_LDADD = $(test_libs) 201 | test_wpas_LDFLAGS = $(test_lflags) 202 | 203 | # 204 | # Phony targets 205 | # 206 | 207 | .PHONY: $(TPHONY) 208 | 209 | # 210 | # Empty .SECONDARY target causes alle intermediate files to be treated as 211 | # secondary files. That is, they don't get deleted after make finished. 212 | # 213 | 214 | .SECONDARY: 215 | -------------------------------------------------------------------------------- /NEWS: -------------------------------------------------------------------------------- 1 | = MiracleCast Release News = 2 | 3 | CHANGES WITH 1: 4 | * TODO 5 | -------------------------------------------------------------------------------- /README: -------------------------------------------------------------------------------- 1 | = MiracleCast - Wifi-Display/Miracast Implementation = 2 | 3 | The MiracleCast project provides software to connect external monitors to your 4 | system via Wifi. It is compatible to the Wifi-Display specification also known 5 | as Miracast. MiracleCast implements the Display-Source as well as Display-Sink 6 | side. 7 | 8 | The Display-Source side allows you to connect external displays to your system 9 | and stream local content to the device. A lot of effort is put into making this 10 | as easy as connecting external displays via HDMI. 11 | On the other hand, the Display-Sink side allows you to create wifi-capable 12 | external displays yourself. You can use it on your embedded devices or even on 13 | full desktops to allow other systems to use your device as external display. 14 | 15 | Website: 16 | http://www.freedesktop.org/wiki/Software/miracle 17 | 18 | == Requirements == 19 | 20 | The MiracleCast projects requires the following software to be installed: 21 | - libwfd: A stand-alone library implementing the Wifi-Display protocol. 22 | http://www.freedesktop.org/wiki/Software/miracle/libwfd 23 | required: >=libwfd-1 24 | 25 | - systemd: A system management daemon. It is used for device-management 26 | (udev), dbus management (sd-bus) and service management. 27 | required: >=systemd-209 28 | 29 | - glib: A utility library. Used by the current DHCP implementation. Will 30 | be removed once sd-dns gains DHCP-server capabilities. 31 | required: ~=glib2-2.38 (might work with older releases, untested..) 32 | 33 | - check: Test-suite for C programs. Used for optional tests of the 34 | MiracleCast code base. 35 | optional: ~=check-0.9.11 (might work with older releases, untested..) 36 | 37 | Note that systemd-209 is not released, yet. However, several unreleased 38 | interfaces of it are used. Please see the MiracleCast website for hints how 39 | you can use it right now. 40 | 41 | == Download == 42 | 43 | Released tarballs can be found at: 44 | http://www.freedesktop.org/software/miracle/releases 45 | 46 | == Install == 47 | 48 | To compile MiracleCast, run the standard autotools commands: 49 | $ test -f ./configure || NOCONFIGURE=1 ./autogen.sh 50 | $ ./configure --prefix=/usr/local 51 | $ make 52 | $ sudo make install 53 | To compile and run the test applications, use: 54 | $ make check 55 | 56 | == Documentation == 57 | 58 | WIP - Please see the MiracleCast website 59 | 60 | == License == 61 | 62 | This software is licensed under the terms of an MIT-like license. Please see 63 | ./COPYING for further information. 64 | 65 | == Contact == 66 | 67 | This software is maintained by: 68 | David Herrmann 69 | If you have any questions, do not hesitate to contact one of the maintainers. 70 | -------------------------------------------------------------------------------- /autogen.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | set -e 3 | 4 | srcdir=`dirname $0` 5 | test -z "$srcdir" && srcdir=. 6 | 7 | origdir=`pwd` 8 | cd $srcdir 9 | 10 | mkdir -p m4 11 | autoreconf -is --force 12 | 13 | cd $origdir 14 | 15 | if test -z "$NOCONFIGURE" ; then 16 | exec $srcdir/configure "$@" 17 | fi 18 | -------------------------------------------------------------------------------- /configure.ac: -------------------------------------------------------------------------------- 1 | # 2 | # MiracleCast - build configuration script 3 | # Copyright (c) 2013-2014 David Herrmann 4 | # 5 | 6 | AC_PREREQ(2.68) 7 | 8 | AC_INIT([miracle], 9 | [1], 10 | [http://www.freedesktop.org/wiki/Software/miracle], 11 | [miracle], 12 | [http://www.freedesktop.org/wiki/Software/miracle]) 13 | AC_CONFIG_SRCDIR([src/miracled.h]) 14 | AC_CONFIG_AUX_DIR([build-aux]) 15 | AC_CONFIG_MACRO_DIR([m4]) 16 | AC_CONFIG_HEADER(config.h) 17 | AC_USE_SYSTEM_EXTENSIONS 18 | AC_SYS_LARGEFILE 19 | AC_CANONICAL_HOST 20 | 21 | AM_INIT_AUTOMAKE([foreign 1.11 subdir-objects dist-xz no-dist-gzip tar-pax -Wall -Werror -Wno-portability]) 22 | AM_SILENT_RULES([yes]) 23 | 24 | AC_SUBST(PACKAGE_DESCRIPTION, ["Wifi-Display/Miracast Implementation"]) 25 | 26 | AC_PROG_CC 27 | AC_PROG_CC_C99 28 | AM_PROG_CC_C_O 29 | m4_ifdef([AM_PROG_AR], [AM_PROG_AR]) 30 | AC_PROG_SED 31 | AC_PROG_MKDIR_P 32 | AC_PROG_LN_S 33 | AC_PROG_GREP 34 | AC_PROG_AWK 35 | 36 | LT_PREREQ(2.2) 37 | LT_INIT 38 | 39 | # 40 | # Mandatory dependencies 41 | # 42 | 43 | PKG_CHECK_MODULES([DEPS], [libwfd libudev libsystemd-daemon >= 208]) 44 | PKG_CHECK_MODULES([GDHCP], [glib-2.0]) 45 | 46 | AC_CHECK_HEADERS(readline/readline.h,, AC_MSG_ERROR(GNU readline not found)) 47 | 48 | # 49 | # Test for "check" which we use for our test-suite. If not found, we disable 50 | # all tests. 51 | # 52 | 53 | PKG_CHECK_MODULES([CHECK], [check], 54 | [have_check=yes], [have_check=no]) 55 | AM_CONDITIONAL([BUILD_HAVE_CHECK], [test "x$have_check" = "xyes"]) 56 | 57 | # 58 | # Makefile vars 59 | # After everything is configured, we create all makefiles. 60 | # 61 | 62 | AC_CONFIG_FILES([Makefile]) 63 | AC_OUTPUT 64 | 65 | # 66 | # Configuration output 67 | # Show configuration to the user so they can check whether everything was 68 | # configured as expected. 69 | # 70 | 71 | AC_MSG_NOTICE([Build configuration: 72 | 73 | prefix: $prefix 74 | exec-prefix: $exec_prefix 75 | bindir: $bindir 76 | libdir: $libdir 77 | includedir: $includedir 78 | 79 | Miscellaneous Options: 80 | building tests: $have_check 81 | 82 | Run "${MAKE-make}" to start compilation process]) 83 | -------------------------------------------------------------------------------- /res/org.freedesktop.miracle.conf: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 26 | 28 | 29 | 31 | 33 | 34 | 36 | 38 | 39 | 42 | 45 | 46 | 49 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | -------------------------------------------------------------------------------- /res/wpa.conf: -------------------------------------------------------------------------------- 1 | # 2 | # MiracleCast - Wifi-Display/Miracast Implementation 3 | # Copyright (c) 2013-2014 David Herrmann 4 | # 5 | 6 | # 7 | # This is an example configuration for wpa_supplicant to work with miracled. You 8 | # can modify it to your needs and then run wpa_supplicant *as root* on it. If 9 | # you want to run it in foreground for debugging, you might wanna use: 10 | # ./wpa_supplicant -dd -iwlan0 -Dnl80211 -c wpa.conf 11 | # 12 | # wpa_supplicant is *not* spawned by miracled, you need to do that yourself and 13 | # afterwards add the device as wifi-link to miracled via: 14 | # ./miraclectl add-link wifi:wlan0 15 | # 16 | # For debugging you usually want to connect a wpa_cli interface so you can 17 | # follow the events and interact with wpas: 18 | # ./wpa_cli -iwlan0 19 | # 20 | 21 | # Directory where control-interface sockets are stored. Must not be changed! 22 | ctrl_interface=/run/wpa_supplicant 23 | 24 | # Allow manual AP scans 25 | ap_scan=1 26 | 27 | # Set default device-name. Overwritten by miracled with current hostname. If 28 | # you want to change it, use "./miraclectl set-link-name " 29 | device_name=my-device 30 | 31 | # Device type for desktop systems. Usually better left untouched.. 32 | device_type=1-0050F204-1 33 | 34 | # Supported config-methods: 35 | # pbc: Supports Push-Button-Configuration 36 | # display: Can display PINs 37 | # label: Has hard-coded label which can be used as PIN 38 | # keypad: Has keyboard for dynamic PIN input 39 | # 40 | # Usually PBC is the preferred method for short-lived ad-hoc connections like 41 | # Miracast as it doesn't require any PIN input on either side. Unfortunately, 42 | # not all devices support it so you might wanna fall back to "display" and 43 | # "keypad". 44 | # Note that wpas supports multiple options separated by spaces. But my remote 45 | # devices tend to choose "keypad" then, which is annoying for testing so I just 46 | # list pbc here as default. 47 | config_methods=pbc 48 | -------------------------------------------------------------------------------- /src/ctl/ctl-cli.c: -------------------------------------------------------------------------------- 1 | /* 2 | * MiracleCast - Wifi-Display/Miracast Implementation 3 | * 4 | * Copyright (c) 2013-2014 David Herrmann 5 | * 6 | * MiracleCast is free software; you can redistribute it and/or modify it 7 | * under the terms of the GNU Lesser General Public License as published by 8 | * the Free Software Foundation; either version 2.1 of the License, or 9 | * (at your option) any later version. 10 | * 11 | * MiracleCast is distributed in the hope that it will be useful, but 12 | * WITHOUT ANY WARRANTY; without even the implied warranty of 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 14 | * Lesser General Public License for more details. 15 | * 16 | * You should have received a copy of the GNU Lesser General Public License 17 | * along with MiracleCast; If not, see . 18 | */ 19 | 20 | #include 21 | #include 22 | #include 23 | #include 24 | #include 25 | #include 26 | #include 27 | #include 28 | #include 29 | #include 30 | #include 31 | #include "ctl.h" 32 | #include "shl_macro.h" 33 | #include "shl_util.h" 34 | 35 | /* *sigh* readline doesn't include all their deps, so put them last */ 36 | #include 37 | #include 38 | 39 | /* 40 | * Helpers for interactive commands 41 | */ 42 | 43 | static sd_event *cli_event; 44 | static sd_bus *cli_bus; 45 | static sd_event_source *cli_sigs[_NSIG]; 46 | static sd_event_source *cli_stdin; 47 | static bool cli_rl; 48 | static const struct cli_cmd *cli_cmds; 49 | int cli_max_sev = LOG_NOTICE; 50 | 51 | static bool is_cli(void) 52 | { 53 | return cli_rl; 54 | } 55 | 56 | void cli_printv(const char *fmt, va_list args) 57 | { 58 | SHL_PROTECT_ERRNO; 59 | _shl_free_ char *line = NULL; 60 | int point; 61 | bool async; 62 | 63 | /* In case we print messages during readline() activity, we need to 64 | * correctly save and restore RL-internal state. */ 65 | async = is_cli() && !RL_ISSTATE(RL_STATE_DONE); 66 | 67 | if (async) { 68 | point = rl_point; 69 | line = rl_copy_text(0, rl_end); 70 | rl_save_prompt(); 71 | rl_replace_line("", 0); 72 | rl_redisplay(); 73 | } 74 | 75 | vprintf(fmt, args); 76 | 77 | if (async) { 78 | rl_restore_prompt(); 79 | rl_replace_line(line, 0); 80 | rl_point = point; 81 | rl_redisplay(); 82 | } 83 | } 84 | 85 | void cli_printf(const char *fmt, ...) 86 | { 87 | SHL_PROTECT_ERRNO; 88 | va_list args; 89 | 90 | va_start(args, fmt); 91 | cli_printv(fmt, args); 92 | va_end(args); 93 | } 94 | 95 | int cli_help(const struct cli_cmd *cmds) 96 | { 97 | unsigned int i; 98 | 99 | if (is_cli()) { 100 | cli_printf("Available commands:\n"); 101 | } else { 102 | cli_fn_help(); 103 | } 104 | 105 | for (i = 0; cmds[i].cmd; ++i) { 106 | if (!cmds[i].desc) 107 | continue; 108 | if (is_cli() && cmds[i].cli_cmp == CLI_N) 109 | continue; 110 | if (!is_cli() && cmds[i].cli_cmp == CLI_Y) 111 | continue; 112 | 113 | cli_printf(" %s %-*s %s\n", 114 | cmds[i].cmd, 115 | (int)(40 - strlen(cmds[i].cmd)), 116 | cmds[i].args ? : "", 117 | cmds[i].desc ? : ""); 118 | } 119 | 120 | return 0; 121 | } 122 | 123 | int cli_do(const struct cli_cmd *cmds, char **args, unsigned int n) 124 | { 125 | unsigned int i; 126 | const char *cmd; 127 | int r; 128 | 129 | if (!n) 130 | return -EAGAIN; 131 | 132 | cmd = *args++; 133 | --n; 134 | 135 | for (i = 0; cmds[i].cmd; ++i) { 136 | if (strcmp(cmd, cmds[i].cmd)) 137 | continue; 138 | if (is_cli() && cmds[i].cli_cmp == CLI_N) 139 | continue; 140 | if (!is_cli() && cmds[i].cli_cmp == CLI_Y) 141 | continue; 142 | 143 | switch (cmds[i].argc_cmp) { 144 | case CLI_EQUAL: 145 | if (n != cmds[i].argc) { 146 | cli_printf("Invalid number of arguments\n"); 147 | return -EINVAL; 148 | } 149 | 150 | break; 151 | case CLI_MORE: 152 | if (n < cmds[i].argc) { 153 | cli_printf("too few arguments\n"); 154 | return -EINVAL; 155 | } 156 | 157 | break; 158 | case CLI_LESS: 159 | if (n > cmds[i].argc) { 160 | cli_printf("too many arguments\n"); 161 | return -EINVAL; 162 | } 163 | 164 | break; 165 | } 166 | 167 | if (cmds[i].fn) { 168 | r = cmds[i].fn(args, n); 169 | return (r == -EAGAIN) ? -EINVAL : r; 170 | } 171 | 172 | break; 173 | } 174 | 175 | if (!strcmp(cmd, "help")) 176 | return cli_help(cmds); 177 | 178 | return -EAGAIN; 179 | } 180 | 181 | static void cli_handler_fn(char *input) 182 | { 183 | _shl_free_ char *original = input; 184 | _shl_strv_free_ char **args = NULL; 185 | int r; 186 | 187 | if (!input) { 188 | rl_insert_text("quit"); 189 | rl_redisplay(); 190 | rl_crlf(); 191 | sd_event_exit(cli_event, 0); 192 | return; 193 | } 194 | 195 | r = shl_qstr_tokenize(input, &args); 196 | if (r < 0) 197 | return cli_vENOMEM(); 198 | else if (!r) 199 | return; 200 | 201 | add_history(original); 202 | r = cli_do(cli_cmds, args, r); 203 | if (r != -EAGAIN) 204 | return; 205 | 206 | cli_printf("Command not found\n"); 207 | } 208 | 209 | static int cli_stdin_fn(sd_event_source *source, 210 | int fd, 211 | uint32_t mask, 212 | void *data) 213 | { 214 | if (mask & EPOLLIN) { 215 | rl_callback_read_char(); 216 | return 0; 217 | } 218 | 219 | if (mask & (EPOLLHUP | EPOLLERR)) 220 | sd_event_exit(cli_event, 0); 221 | 222 | return 0; 223 | } 224 | 225 | static int cli_signal_fn(sd_event_source *source, 226 | const struct signalfd_siginfo *ssi, 227 | void *data) 228 | { 229 | if (ssi->ssi_signo == SIGCHLD) { 230 | cli_debug("caught SIGCHLD for %d", (int)ssi->ssi_pid); 231 | } else if (ssi->ssi_signo == SIGINT) { 232 | rl_replace_line("", 0); 233 | rl_crlf(); 234 | rl_on_new_line(); 235 | rl_redisplay(); 236 | } else { 237 | cli_notice("caught signal %d, exiting..", 238 | (int)ssi->ssi_signo); 239 | sd_event_exit(cli_event, 0); 240 | } 241 | 242 | return 0; 243 | } 244 | 245 | void cli_destroy(void) 246 | { 247 | unsigned int i; 248 | 249 | if (!cli_event) 250 | return; 251 | 252 | if (cli_rl) { 253 | cli_rl = false; 254 | 255 | rl_replace_line("", 0); 256 | rl_crlf(); 257 | rl_on_new_line(); 258 | rl_redisplay(); 259 | 260 | rl_message(""); 261 | rl_callback_handler_remove(); 262 | } 263 | 264 | sd_event_source_unref(cli_stdin); 265 | cli_stdin = NULL; 266 | 267 | for (i = 0; cli_sigs[i]; ++i) { 268 | sd_event_source_unref(cli_sigs[i]); 269 | cli_sigs[i] = NULL; 270 | } 271 | 272 | cli_cmds = NULL; 273 | sd_bus_detach_event(cli_bus); 274 | cli_bus = NULL; 275 | sd_event_unref(cli_event); 276 | cli_event = NULL; 277 | } 278 | 279 | int cli_init(sd_bus *bus, const struct cli_cmd *cmds) 280 | { 281 | static const int sigs[] = { 282 | SIGINT, SIGTERM, SIGQUIT, SIGHUP, SIGPIPE, SIGCHLD, 0 283 | }; 284 | unsigned int i; 285 | sigset_t mask; 286 | int r; 287 | 288 | if (cli_event) 289 | return cli_EINVAL(); 290 | 291 | r = sd_event_default(&cli_event); 292 | if (r < 0) { 293 | cli_vERR(r); 294 | goto error; 295 | } 296 | 297 | cli_cmds = cmds; 298 | cli_bus = bus; 299 | 300 | r = sd_bus_attach_event(cli_bus, cli_event, 0); 301 | if (r < 0) { 302 | cli_vERR(r); 303 | goto error; 304 | } 305 | 306 | for (i = 0; sigs[i]; ++i) { 307 | sigemptyset(&mask); 308 | sigaddset(&mask, sigs[i]); 309 | sigprocmask(SIG_BLOCK, &mask, NULL); 310 | 311 | r = sd_event_add_signal(cli_event, 312 | &cli_sigs[i], 313 | sigs[i], 314 | cli_signal_fn, 315 | NULL); 316 | if (r < 0) { 317 | cli_vERR(r); 318 | goto error; 319 | } 320 | } 321 | 322 | r = sd_event_add_io(cli_event, 323 | &cli_stdin, 324 | fileno(stdin), 325 | EPOLLHUP | EPOLLERR | EPOLLIN, 326 | cli_stdin_fn, 327 | NULL); 328 | if (r < 0) { 329 | cli_vERR(r); 330 | goto error; 331 | } 332 | 333 | cli_rl = true; 334 | 335 | rl_erase_empty_line = 1; 336 | rl_callback_handler_install(NULL, cli_handler_fn); 337 | 338 | rl_set_prompt(CLI_PROMPT); 339 | printf("\r"); 340 | rl_on_new_line(); 341 | rl_redisplay(); 342 | 343 | return 0; 344 | 345 | error: 346 | cli_destroy(); 347 | return r; 348 | } 349 | 350 | int cli_run(void) 351 | { 352 | if (!cli_event) 353 | return cli_EINVAL(); 354 | 355 | return sd_event_loop(cli_event); 356 | } 357 | 358 | void cli_exit(void) 359 | { 360 | if (!cli_event) 361 | return cli_vEINVAL(); 362 | 363 | sd_event_exit(cli_event, 0); 364 | } 365 | 366 | bool cli_running(void) 367 | { 368 | return is_cli(); 369 | } 370 | -------------------------------------------------------------------------------- /src/ctl/ctl.h: -------------------------------------------------------------------------------- 1 | /* 2 | * MiracleCast - Wifi-Display/Miracast Implementation 3 | * 4 | * Copyright (c) 2013-2014 David Herrmann 5 | * 6 | * MiracleCast is free software; you can redistribute it and/or modify it 7 | * under the terms of the GNU Lesser General Public License as published by 8 | * the Free Software Foundation; either version 2.1 of the License, or 9 | * (at your option) any later version. 10 | * 11 | * MiracleCast is distributed in the hope that it will be useful, but 12 | * WITHOUT ANY WARRANTY; without even the implied warranty of 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 14 | * Lesser General Public License for more details. 15 | * 16 | * You should have received a copy of the GNU Lesser General Public License 17 | * along with MiracleCast; If not, see . 18 | */ 19 | 20 | #include 21 | #include 22 | #include 23 | #include 24 | #include 25 | #include 26 | #include "shl_dlist.h" 27 | 28 | #ifndef CTL_CTL_H 29 | #define CTL_CTL_H 30 | 31 | struct ctl_wifi; 32 | struct ctl_link; 33 | struct ctl_peer; 34 | 35 | /* wifi handling */ 36 | 37 | struct ctl_peer { 38 | struct shl_dlist list; 39 | char *label; 40 | struct ctl_link *l; 41 | 42 | /* properties */ 43 | char *p2p_mac; 44 | char *friendly_name; 45 | bool connected; 46 | char *interface; 47 | char *local_address; 48 | char *remote_address; 49 | }; 50 | 51 | #define peer_from_dlist(_p) shl_dlist_entry((_p), struct ctl_peer, list); 52 | 53 | int ctl_peer_connect(struct ctl_peer *p, const char *prov, const char *pin); 54 | int ctl_peer_disconnect(struct ctl_peer *p); 55 | 56 | struct ctl_link { 57 | struct shl_dlist list; 58 | char *label; 59 | struct ctl_wifi *w; 60 | 61 | struct shl_dlist peers; 62 | 63 | bool have_p2p_scan; 64 | 65 | /* properties */ 66 | unsigned int ifindex; 67 | char *ifname; 68 | char *friendly_name; 69 | bool p2p_scanning; 70 | }; 71 | 72 | #define link_from_dlist(_l) shl_dlist_entry((_l), struct ctl_link, list); 73 | 74 | int ctl_link_set_friendly_name(struct ctl_link *l, const char *name); 75 | int ctl_link_set_p2p_scanning(struct ctl_link *l, bool val); 76 | 77 | struct ctl_wifi { 78 | sd_bus *bus; 79 | 80 | struct shl_dlist links; 81 | }; 82 | 83 | int ctl_wifi_new(struct ctl_wifi **out, sd_bus *bus); 84 | void ctl_wifi_free(struct ctl_wifi *w); 85 | int ctl_wifi_fetch(struct ctl_wifi *w); 86 | 87 | struct ctl_link *ctl_wifi_find_link(struct ctl_wifi *w, 88 | const char *label); 89 | struct ctl_link *ctl_wifi_search_link(struct ctl_wifi *w, 90 | const char *label); 91 | struct ctl_link *ctl_wifi_find_link_by_peer(struct ctl_wifi *w, 92 | const char *label); 93 | struct ctl_link *ctl_wifi_search_link_by_peer(struct ctl_wifi *w, 94 | const char *label); 95 | struct ctl_peer *ctl_wifi_find_peer(struct ctl_wifi *w, 96 | const char *label); 97 | struct ctl_peer *ctl_wifi_search_peer(struct ctl_wifi *w, 98 | const char *real_label); 99 | 100 | /* CLI handling */ 101 | 102 | extern int cli_max_sev; 103 | void cli_printv(const char *fmt, va_list args); 104 | void cli_printf(const char *fmt, ...); 105 | 106 | enum { 107 | #ifndef LOG_FATAL 108 | LOG_FATAL = 0, 109 | #endif 110 | #ifndef LOG_ALERT 111 | LOG_ALERT = 1, 112 | #endif 113 | #ifndef LOG_CRITICAL 114 | LOG_CRITICAL = 2, 115 | #endif 116 | #ifndef LOG_ERROR 117 | LOG_ERROR = 3, 118 | #endif 119 | #ifndef LOG_WARNING 120 | LOG_WARNING = 4, 121 | #endif 122 | #ifndef LOG_NOTICE 123 | LOG_NOTICE = 5, 124 | #endif 125 | #ifndef LOG_INFO 126 | LOG_INFO = 6, 127 | #endif 128 | #ifndef LOG_DEBUG 129 | LOG_DEBUG = 7, 130 | #endif 131 | LOG_SEV_NUM, 132 | }; 133 | 134 | #define cli_log(_fmt, ...) \ 135 | cli_printf(_fmt "\n", ##__VA_ARGS__) 136 | #define cli_log_fn(_fmt, ...) \ 137 | cli_printf(_fmt " (%s() in %s:%d)\n", ##__VA_ARGS__, __func__, __FILE__, __LINE__) 138 | #define cli_error(_fmt, ...) \ 139 | ((LOG_ERROR <= cli_max_sev) ? \ 140 | cli_log_fn("ERROR: " _fmt, ##__VA_ARGS__) : (void)0) 141 | #define cli_warning(_fmt, ...) \ 142 | ((LOG_WARNING <= cli_max_sev) ? \ 143 | cli_log_fn("WARNING: " _fmt, ##__VA_ARGS__) : (void)0) 144 | #define cli_notice(_fmt, ...) \ 145 | ((LOG_NOTICE <= cli_max_sev) ? \ 146 | cli_log("NOTICE: " _fmt, ##__VA_ARGS__) : (void)0) 147 | #define cli_debug(_fmt, ...) \ 148 | ((LOG_DEBUG <= cli_max_sev) ? \ 149 | cli_log_fn("DEBUG: " _fmt, ##__VA_ARGS__) : (void)0) 150 | 151 | #define cli_EINVAL() \ 152 | (cli_error("invalid arguments"), -EINVAL) 153 | #define cli_vEINVAL() \ 154 | ((void)cli_EINVAL()) 155 | 156 | #define cli_EFAULT() \ 157 | (cli_error("internal operation failed"), -EFAULT) 158 | #define cli_vEFAULT() \ 159 | ((void)cli_EFAULT()) 160 | 161 | #define cli_ENOMEM() \ 162 | (cli_error("out of memory"), -ENOMEM) 163 | #define cli_vENOMEM() \ 164 | ((void)cli_ENOMEM()) 165 | 166 | #define cli_EPIPE() \ 167 | (cli_error("fd closed unexpectedly"), -EPIPE) 168 | #define cli_vEPIPE() \ 169 | ((void)cli_EPIPE()) 170 | 171 | #define cli_ERRNO() \ 172 | (cli_error("syscall failed (%d): %m", errno), -errno) 173 | #define cli_vERRNO() \ 174 | ((void)cli_ERRNO()) 175 | 176 | #define cli_ERR(_r) \ 177 | (errno = -(_r), cli_error("syscall failed (%d): %m", (_r)), (_r)) 178 | #define cli_vERR(_r) \ 179 | ((void)cli_ERR(_r)) 180 | 181 | #define cli_log_parser(_r) \ 182 | (cli_error("cannot parse dbus message: %s", \ 183 | strerror((_r) < 0 ? -(_r) : (_r))), (_r)) 184 | 185 | #define cli_log_create(_r) \ 186 | (cli_error("cannot create dbus message: %s", \ 187 | strerror((_r) < 0 ? -(_r) : (_r))), (_r)) 188 | 189 | #define CLI_DEFAULT "\x1B[0m" 190 | #define CLI_RED "\x1B[0;91m" 191 | #define CLI_GREEN "\x1B[0;92m" 192 | #define CLI_YELLOW "\x1B[0;93m" 193 | #define CLI_BLUE "\x1B[0;94m" 194 | #define CLI_BOLDGRAY "\x1B[1;30m" 195 | #define CLI_BOLDWHITE "\x1B[1;37m" 196 | #define CLI_PROMPT CLI_BLUE "[miraclectl] # " CLI_DEFAULT 197 | 198 | struct cli_cmd { 199 | const char *cmd; 200 | const char *args; 201 | enum { 202 | CLI_N, /* no */ 203 | CLI_M, /* maybe */ 204 | CLI_Y, /* yes */ 205 | } cli_cmp; 206 | enum { 207 | CLI_MORE, 208 | CLI_LESS, 209 | CLI_EQUAL, 210 | } argc_cmp; 211 | int argc; 212 | int (*fn) (char **args, unsigned int n); 213 | const char *desc; 214 | }; 215 | 216 | int cli_init(sd_bus *bus, const struct cli_cmd *cmds); 217 | void cli_destroy(void); 218 | int cli_run(void); 219 | void cli_exit(void); 220 | bool cli_running(void); 221 | 222 | int cli_help(const struct cli_cmd *cmds); 223 | int cli_do(const struct cli_cmd *cmds, char **args, unsigned int n); 224 | 225 | /* callback functions */ 226 | 227 | void ctl_fn_peer_new(struct ctl_peer *p); 228 | void ctl_fn_peer_free(struct ctl_peer *p); 229 | void ctl_fn_peer_provision_discovery(struct ctl_peer *p, 230 | const char *prov, 231 | const char *pin); 232 | void ctl_fn_peer_connected(struct ctl_peer *p); 233 | void ctl_fn_peer_disconnected(struct ctl_peer *p); 234 | void ctl_fn_link_new(struct ctl_link *l); 235 | void ctl_fn_link_free(struct ctl_link *l); 236 | 237 | void cli_fn_help(void); 238 | 239 | #endif /* CTL_CTL_H */ 240 | -------------------------------------------------------------------------------- /src/ctl/wifictl.c: -------------------------------------------------------------------------------- 1 | /* 2 | * MiracleCast - Wifi-Display/Miracast Implementation 3 | * 4 | * Copyright (c) 2013-2014 David Herrmann 5 | * 6 | * MiracleCast is free software; you can redistribute it and/or modify it 7 | * under the terms of the GNU Lesser General Public License as published by 8 | * the Free Software Foundation; either version 2.1 of the License, or 9 | * (at your option) any later version. 10 | * 11 | * MiracleCast is distributed in the hope that it will be useful, but 12 | * WITHOUT ANY WARRANTY; without even the implied warranty of 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 14 | * Lesser General Public License for more details. 15 | * 16 | * You should have received a copy of the GNU Lesser General Public License 17 | * along with MiracleCast; If not, see . 18 | */ 19 | 20 | #include 21 | #include 22 | #include 23 | #include 24 | #include 25 | #include 26 | #include 27 | #include 28 | #include 29 | #include 30 | #include 31 | #include "ctl.h" 32 | #include "shl_macro.h" 33 | #include "shl_util.h" 34 | 35 | static sd_bus *bus; 36 | static struct ctl_wifi *wifi; 37 | 38 | static struct ctl_link *selected_link; 39 | 40 | /* 41 | * cmd list 42 | */ 43 | 44 | static int cmd_list(char **args, unsigned int n) 45 | { 46 | size_t link_cnt = 0, peer_cnt = 0; 47 | struct shl_dlist *i, *j; 48 | struct ctl_link *l; 49 | struct ctl_peer *p; 50 | 51 | /* list links */ 52 | 53 | cli_printf("%6s %-24s %-30s\n", 54 | "LINK", "INTERFACE", "FRIENDLY-NAME"); 55 | 56 | shl_dlist_for_each(i, &wifi->links) { 57 | l = link_from_dlist(i); 58 | ++link_cnt; 59 | 60 | cli_printf("%6s %-24s %-30s\n", 61 | l->label, 62 | shl_isempty(l->ifname) ? 63 | "" : l->ifname, 64 | shl_isempty(l->friendly_name) ? 65 | "" : l->friendly_name); 66 | } 67 | 68 | cli_printf("\n"); 69 | 70 | /* list peers */ 71 | 72 | cli_printf("%6s %-24s %-30s %-10s\n", 73 | "LINK", "PEER-ID", "FRIENDLY-NAME", "CONNECTED"); 74 | 75 | shl_dlist_for_each(i, &wifi->links) { 76 | l = link_from_dlist(i); 77 | 78 | shl_dlist_for_each(j, &l->peers) { 79 | p = peer_from_dlist(j); 80 | ++peer_cnt; 81 | 82 | cli_printf("%6s %-24s %-30s %-10s\n", 83 | p->l->label, 84 | p->label, 85 | shl_isempty(p->friendly_name) ? 86 | "" : p->friendly_name, 87 | p->connected ? "yes" : "no"); 88 | } 89 | } 90 | 91 | cli_printf("\n %u peers and %u links listed.\n", peer_cnt, link_cnt); 92 | 93 | return 0; 94 | } 95 | 96 | /* 97 | * cmd: select 98 | */ 99 | 100 | static int cmd_select(char **args, unsigned int n) 101 | { 102 | struct ctl_link *l; 103 | 104 | if (!n) { 105 | if (selected_link) { 106 | cli_printf("link %s deselected\n", 107 | selected_link->label); 108 | selected_link = NULL; 109 | } 110 | 111 | return 0; 112 | } 113 | 114 | l = ctl_wifi_search_link(wifi, args[0]); 115 | if (!l) { 116 | cli_error("unknown link %s", args[0]); 117 | return 0; 118 | } 119 | 120 | selected_link = l; 121 | cli_printf("link %s selected\n", selected_link->label); 122 | 123 | return 0; 124 | } 125 | 126 | /* 127 | * cmd: show 128 | */ 129 | 130 | static int cmd_show(char **args, unsigned int n) 131 | { 132 | struct ctl_link *l = NULL; 133 | struct ctl_peer *p = NULL; 134 | 135 | if (n > 0) { 136 | if (!(l = ctl_wifi_find_link(wifi, args[0])) && 137 | !(p = ctl_wifi_find_peer(wifi, args[0])) && 138 | !(l = ctl_wifi_search_link(wifi, args[0])) && 139 | !(p = ctl_wifi_search_peer(wifi, args[0]))) { 140 | cli_error("unknown link or peer %s", args[0]); 141 | return 0; 142 | } 143 | } else { 144 | l = selected_link; 145 | } 146 | 147 | if (l) { 148 | cli_printf("Link=%s\n", l->label); 149 | if (l->ifindex > 0) 150 | cli_printf("InterfaceIndex=%u\n", l->ifindex); 151 | if (l->ifname && *l->ifname) 152 | cli_printf("InterfaceName=%s\n", l->ifname); 153 | if (l->friendly_name && *l->friendly_name) 154 | cli_printf("FriendlyName=%s\n", l->friendly_name); 155 | cli_printf("P2PScanning=%d\n", l->p2p_scanning); 156 | } else if (p) { 157 | cli_printf("Peer=%s\n", p->label); 158 | if (p->p2p_mac && *p->p2p_mac) 159 | cli_printf("P2PMac=%s\n", p->p2p_mac); 160 | if (p->friendly_name && *p->friendly_name) 161 | cli_printf("FriendlyName=%s\n", p->friendly_name); 162 | cli_printf("Connected=%d\n", p->connected); 163 | if (p->interface && *p->interface) 164 | cli_printf("Interface=%s\n", p->interface); 165 | if (p->local_address && *p->local_address) 166 | cli_printf("LocalAddress=%s\n", p->local_address); 167 | if (p->remote_address && *p->remote_address) 168 | cli_printf("RemoteAddress=%s\n", p->remote_address); 169 | } else { 170 | cli_printf("Show what?\n"); 171 | return 0; 172 | } 173 | 174 | return 0; 175 | } 176 | 177 | /* 178 | * cmd: set-friendly-name 179 | */ 180 | 181 | static int cmd_set_friendly_name(char **args, unsigned int n) 182 | { 183 | struct ctl_link *l = NULL; 184 | const char *name; 185 | 186 | if (n < 1) { 187 | cli_printf("To what?\n"); 188 | return 0; 189 | } 190 | 191 | if (n > 1) { 192 | l = ctl_wifi_search_link(wifi, args[0]); 193 | if (!l) { 194 | cli_error("unknown link %s", args[0]); 195 | return 0; 196 | } 197 | 198 | name = args[1]; 199 | } else { 200 | name = args[0]; 201 | } 202 | 203 | l = l ? : selected_link; 204 | if (!l) { 205 | cli_error("no link selected"); 206 | return 0; 207 | } 208 | 209 | return ctl_link_set_friendly_name(l, name); 210 | } 211 | 212 | /* 213 | * cmd: p2p-scan 214 | */ 215 | 216 | static int cmd_p2p_scan(char **args, unsigned int n) 217 | { 218 | struct ctl_link *l = NULL; 219 | unsigned int i; 220 | bool stop = false; 221 | 222 | for (i = 0; i < n; ++i) { 223 | if (!strcmp(args[i], "stop")) { 224 | stop = true; 225 | } else { 226 | l = ctl_wifi_search_link(wifi, args[i]); 227 | if (!l) { 228 | cli_error("unknown link %s", args[i]); 229 | return 0; 230 | } 231 | } 232 | } 233 | 234 | l = l ? : selected_link; 235 | if (!l) { 236 | cli_error("no link selected"); 237 | return 0; 238 | } 239 | 240 | return ctl_link_set_p2p_scanning(l, !stop); 241 | } 242 | 243 | /* 244 | * cmd: connect 245 | */ 246 | 247 | static bool is_valid_prov(const char *prov) 248 | { 249 | return prov && (!strcmp(prov, "auto") || 250 | !strcmp(prov, "pbc") || 251 | !strcmp(prov, "display") || 252 | !strcmp(prov, "pin")); 253 | } 254 | 255 | static int cmd_connect(char **args, unsigned int n) 256 | { 257 | struct ctl_peer *p; 258 | const char *prov, *pin; 259 | 260 | if (n < 1) { 261 | cli_printf("To whom?\n"); 262 | return 0; 263 | } 264 | 265 | p = ctl_wifi_search_peer(wifi, args[0]); 266 | if (!p) { 267 | cli_error("unknown peer %s", args[0]); 268 | return 0; 269 | } 270 | 271 | if (n > 2) { 272 | prov = args[1]; 273 | pin = args[2]; 274 | } else if (n > 1) { 275 | if (is_valid_prov(args[1])) { 276 | prov = args[1]; 277 | pin = ""; 278 | } else { 279 | prov = "auto"; 280 | pin = args[1]; 281 | } 282 | } else { 283 | prov = "auto"; 284 | pin = ""; 285 | } 286 | 287 | return ctl_peer_connect(p, prov, pin); 288 | } 289 | 290 | /* 291 | * cmd: disconnect 292 | */ 293 | 294 | static int cmd_disconnect(char **args, unsigned int n) 295 | { 296 | struct ctl_peer *p; 297 | 298 | if (n < 1) { 299 | cli_printf("From whom?\n"); 300 | return 0; 301 | } 302 | 303 | p = ctl_wifi_search_peer(wifi, args[0]); 304 | if (!p) { 305 | cli_error("unknown peer %s", args[0]); 306 | return 0; 307 | } 308 | 309 | return ctl_peer_disconnect(p); 310 | } 311 | 312 | /* 313 | * cmd: quit/exit 314 | */ 315 | 316 | static int cmd_quit(char **args, unsigned int n) 317 | { 318 | cli_exit(); 319 | return 0; 320 | } 321 | 322 | /* 323 | * main 324 | */ 325 | 326 | static const struct cli_cmd cli_cmds[] = { 327 | { "list", NULL, CLI_M, CLI_LESS, 0, cmd_list, "List all objects" }, 328 | { "select", "[link]", CLI_Y, CLI_LESS, 1, cmd_select, "Select default link" }, 329 | { "show", "[link|peer]", CLI_M, CLI_LESS, 1, cmd_show, "Show detailed object information" }, 330 | { "set-friendly-name", "[link] ", CLI_M, CLI_LESS, 2, cmd_set_friendly_name, "Set friendly name of an object" }, 331 | { "p2p-scan", "[link] [stop]", CLI_Y, CLI_LESS, 2, cmd_p2p_scan, "Control neighborhood P2P scanning" }, 332 | { "connect", " [provision] [pin]", CLI_M, CLI_LESS, 3, cmd_connect, "Connect to peer" }, 333 | { "disconnect", "", CLI_M, CLI_EQUAL, 1, cmd_disconnect, "Disconnect from peer" }, 334 | { "quit", NULL, CLI_Y, CLI_MORE, 0, cmd_quit, "Quit program" }, 335 | { "exit", NULL, CLI_Y, CLI_MORE, 0, cmd_quit, NULL }, 336 | { "help", NULL, CLI_M, CLI_MORE, 0, NULL, "Print help" }, 337 | { }, 338 | }; 339 | 340 | void ctl_fn_peer_new(struct ctl_peer *p) 341 | { 342 | if (cli_running()) 343 | cli_printf("[" CLI_GREEN "ADD" CLI_DEFAULT "] Peer: %s\n", 344 | p->label); 345 | } 346 | 347 | void ctl_fn_peer_free(struct ctl_peer *p) 348 | { 349 | if (cli_running()) 350 | cli_printf("[" CLI_RED "REMOVE" CLI_DEFAULT "] Peer: %s\n", 351 | p->label); 352 | } 353 | 354 | void ctl_fn_peer_provision_discovery(struct ctl_peer *p, 355 | const char *prov, 356 | const char *pin) 357 | { 358 | if (cli_running()) 359 | cli_printf("[" CLI_YELLOW "PROV" CLI_DEFAULT "] Peer: %s Type: %s PIN: %s\n", 360 | p->label, prov, pin); 361 | } 362 | 363 | void ctl_fn_peer_connected(struct ctl_peer *p) 364 | { 365 | if (cli_running()) 366 | cli_printf("[" CLI_GREEN "CONNECT" CLI_DEFAULT "] Peer: %s\n", 367 | p->label); 368 | } 369 | 370 | void ctl_fn_peer_disconnected(struct ctl_peer *p) 371 | { 372 | if (cli_running()) 373 | cli_printf("[" CLI_YELLOW "DISCONNECT" CLI_DEFAULT "] Peer: %s\n", 374 | p->label); 375 | } 376 | 377 | void ctl_fn_link_new(struct ctl_link *l) 378 | { 379 | if (cli_running()) 380 | cli_printf("[" CLI_GREEN "ADD" CLI_DEFAULT "] Link: %s\n", 381 | l->label); 382 | } 383 | 384 | void ctl_fn_link_free(struct ctl_link *l) 385 | { 386 | if (l == selected_link) { 387 | cli_printf("link %s deselected\n", 388 | selected_link->label); 389 | selected_link = NULL; 390 | } 391 | 392 | if (cli_running()) 393 | cli_printf("[" CLI_RED "REMOVE" CLI_DEFAULT "] Link: %s\n", 394 | l->label); 395 | } 396 | 397 | void cli_fn_help() 398 | { 399 | /* 400 | * 80-char barrier: 401 | * 01234567890123456789012345678901234567890123456789012345678901234567890123456789 402 | */ 403 | printf("%s [OPTIONS...] {COMMAND} ...\n\n" 404 | "Send control command to or query the MiracleCast Wifi-Manager. If no arguments\n" 405 | "are given, an interactive command-line tool is provided.\n\n" 406 | " -h --help Show this help\n" 407 | " --version Show package version\n" 408 | " --log-level Maximum level for log messages\n" 409 | "\n" 410 | "Commands:\n" 411 | , program_invocation_short_name); 412 | /* 413 | * 80-char barrier: 414 | * 01234567890123456789012345678901234567890123456789012345678901234567890123456789 415 | */ 416 | } 417 | 418 | static int ctl_interactive(void) 419 | { 420 | struct shl_dlist *i; 421 | struct ctl_link *l; 422 | int r; 423 | 424 | r = cli_init(bus, cli_cmds); 425 | if (r < 0) 426 | return r; 427 | 428 | r = ctl_wifi_fetch(wifi); 429 | if (r < 0) 430 | goto error; 431 | 432 | r = cli_run(); 433 | 434 | /* stop managed scans, but only in interactive mode */ 435 | shl_dlist_for_each(i, &wifi->links) { 436 | l = link_from_dlist(i); 437 | if (l->have_p2p_scan) 438 | ctl_link_set_p2p_scanning(l, false); 439 | } 440 | 441 | error: 442 | cli_destroy(); 443 | return r; 444 | } 445 | 446 | static int ctl_single(char **argv, int argc) 447 | { 448 | int r; 449 | 450 | r = ctl_wifi_fetch(wifi); 451 | if (r < 0) 452 | return r; 453 | 454 | r = cli_do(cli_cmds, argv, argc); 455 | if (r == -EAGAIN) 456 | cli_error("unknown operation %s", argv[0]); 457 | 458 | return r; 459 | } 460 | 461 | static int ctl_main(int argc, char *argv[]) 462 | { 463 | int r, left; 464 | 465 | r = ctl_wifi_new(&wifi, bus); 466 | if (r < 0) 467 | return r; 468 | 469 | left = argc - optind; 470 | if (left <= 0) 471 | r = ctl_interactive(); 472 | else 473 | r = ctl_single(argv + optind, left); 474 | 475 | ctl_wifi_free(wifi); 476 | return r; 477 | } 478 | 479 | static int parse_argv(int argc, char *argv[]) 480 | { 481 | enum { 482 | ARG_VERSION = 0x100, 483 | ARG_LOG_LEVEL, 484 | }; 485 | static const struct option options[] = { 486 | { "help", no_argument, NULL, 'h' }, 487 | { "version", no_argument, NULL, ARG_VERSION }, 488 | { "log-level", required_argument, NULL, ARG_LOG_LEVEL }, 489 | {} 490 | }; 491 | int c; 492 | 493 | while ((c = getopt_long(argc, argv, "h", options, NULL)) >= 0) { 494 | switch (c) { 495 | case 'h': 496 | return cli_help(cli_cmds); 497 | case ARG_VERSION: 498 | puts(PACKAGE_STRING); 499 | return 0; 500 | case ARG_LOG_LEVEL: 501 | cli_max_sev = atoi(optarg); 502 | break; 503 | case '?': 504 | return -EINVAL; 505 | } 506 | } 507 | 508 | return 1; 509 | } 510 | 511 | int main(int argc, char **argv) 512 | { 513 | int r; 514 | 515 | setlocale(LC_ALL, ""); 516 | 517 | r = parse_argv(argc, argv); 518 | if (r < 0) 519 | return EXIT_FAILURE; 520 | if (!r) 521 | return EXIT_SUCCESS; 522 | 523 | r = sd_bus_default_system(&bus); 524 | if (r < 0) { 525 | cli_error("cannot connect to system bus: %s", strerror(-r)); 526 | return EXIT_FAILURE; 527 | } 528 | 529 | r = ctl_main(argc, argv); 530 | sd_bus_unref(bus); 531 | 532 | return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS; 533 | } 534 | -------------------------------------------------------------------------------- /src/dhcp/common.h: -------------------------------------------------------------------------------- 1 | /* 2 | * 3 | * DHCP client library with GLib integration 4 | * 5 | * Copyright (C) 2009-2012 Intel Corporation. All rights reserved. 6 | * 7 | * This program is free software; you can redistribute it and/or modify 8 | * it under the terms of the GNU General Public License version 2 as 9 | * published by the Free Software Foundation. 10 | * 11 | * This program is distributed in the hope that it will be useful, 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | * GNU General Public License for more details. 15 | * 16 | * You should have received a copy of the GNU General Public License 17 | * along with this program; if not, write to the Free Software 18 | * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA 19 | * 20 | */ 21 | 22 | #include 23 | #include 24 | 25 | #include 26 | 27 | #include "unaligned.h" 28 | #include "gdhcp.h" 29 | 30 | #define CLIENT_PORT 68 31 | #define SERVER_PORT 67 32 | 33 | #define DHCPV6_CLIENT_PORT 546 34 | #define DHCPV6_SERVER_PORT 547 35 | #define MAX_DHCPV6_PKT_SIZE 1500 36 | 37 | #define EXTEND_FOR_BUGGY_SERVERS 80 38 | 39 | static const uint8_t MAC_BCAST_ADDR[ETH_ALEN] __attribute__((aligned(2))) = { 40 | 0xff, 0xff, 0xff, 0xff, 0xff, 0xff 41 | }; 42 | 43 | static const uint8_t MAC_ANY_ADDR[ETH_ALEN] __attribute__((aligned(2))) = { 44 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 45 | }; 46 | 47 | /* DHCP packet */ 48 | #define DHCP_MAGIC 0x63825363 49 | #define DHCP_OPTIONS_BUFSIZE 308 50 | #define BOOTREQUEST 1 51 | #define BOOTREPLY 2 52 | 53 | #define BROADCAST_FLAG 0x8000 54 | 55 | /* See RFC 2131 */ 56 | struct dhcp_packet { 57 | uint8_t op; 58 | uint8_t htype; 59 | uint8_t hlen; 60 | uint8_t hops; 61 | uint32_t xid; 62 | uint16_t secs; 63 | uint16_t flags; 64 | uint32_t ciaddr; 65 | uint32_t yiaddr; 66 | uint32_t siaddr_nip; 67 | uint32_t gateway_nip; 68 | uint8_t chaddr[16]; 69 | uint8_t sname[64]; 70 | uint8_t file[128]; 71 | uint32_t cookie; 72 | uint8_t options[DHCP_OPTIONS_BUFSIZE + EXTEND_FOR_BUGGY_SERVERS]; 73 | } __attribute__((packed)); 74 | 75 | struct ip_udp_dhcp_packet { 76 | struct iphdr ip; 77 | struct udphdr udp; 78 | struct dhcp_packet data; 79 | } __attribute__((packed)); 80 | 81 | /* See RFC 3315 */ 82 | struct dhcpv6_packet { 83 | uint8_t message; 84 | uint8_t transaction_id[3]; 85 | uint8_t options[]; 86 | } __attribute__((packed)); 87 | 88 | 89 | /* See RFC 2132 */ 90 | #define DHCP_PADDING 0x00 91 | #define DHCP_SUBNET 0x01 92 | #define DHCP_ROUTER 0x03 93 | #define DHCP_TIME_SERVER 0x04 94 | #define DHCP_NAME_SERVER 0x05 95 | #define DHCP_DNS_SERVER 0x06 96 | #define DHCP_HOST_NAME 0x0c 97 | #define DHCP_DOMAIN_NAME 0x0f 98 | #define DHCP_NTP_SERVER 0x2a 99 | #define DHCP_REQUESTED_IP 0x32 100 | #define DHCP_LEASE_TIME 0x33 101 | #define DHCP_OPTION_OVERLOAD 0x34 102 | #define DHCP_MESSAGE_TYPE 0x35 103 | #define DHCP_SERVER_ID 0x36 104 | #define DHCP_PARAM_REQ 0x37 105 | #define DHCP_ERR_MESSAGE 0x38 106 | #define DHCP_MAX_SIZE 0x39 107 | #define DHCP_VENDOR 0x3c 108 | #define DHCP_CLIENT_ID 0x3d 109 | #define DHCP_END 0xff 110 | 111 | #define OPT_CODE 0 112 | #define OPT_LEN 1 113 | #define OPT_DATA 2 114 | #define OPTION_FIELD 0 115 | #define FILE_FIELD 1 116 | #define SNAME_FIELD 2 117 | 118 | /* DHCP_MESSAGE_TYPE values */ 119 | #define DHCPDISCOVER 1 120 | #define DHCPOFFER 2 121 | #define DHCPREQUEST 3 122 | #define DHCPDECLINE 4 123 | #define DHCPACK 5 124 | #define DHCPNAK 6 125 | #define DHCPRELEASE 7 126 | #define DHCPINFORM 8 127 | #define DHCP_MINTYPE DHCPDISCOVER 128 | #define DHCP_MAXTYPE DHCPINFORM 129 | 130 | /* Message types for DHCPv6, RFC 3315 sec 5.3 */ 131 | #define DHCPV6_SOLICIT 1 132 | #define DHCPV6_ADVERTISE 2 133 | #define DHCPV6_REQUEST 3 134 | #define DHCPV6_CONFIRM 4 135 | #define DHCPV6_RENEW 5 136 | #define DHCPV6_REBIND 6 137 | #define DHCPV6_REPLY 7 138 | #define DHCPV6_RELEASE 8 139 | #define DHCPV6_DECLINE 9 140 | #define DHCPV6_RECONFIGURE 10 141 | #define DHCPV6_INFORMATION_REQ 11 142 | 143 | /* 144 | * DUID time starts 2000-01-01. 145 | */ 146 | #define DUID_TIME_EPOCH 946684800 147 | 148 | typedef enum { 149 | OPTION_UNKNOWN, 150 | OPTION_IP, 151 | OPTION_STRING, 152 | OPTION_U8, 153 | OPTION_U16, 154 | OPTION_U32, 155 | OPTION_TYPE_MASK = 0x0f, 156 | OPTION_LIST = 0x10, 157 | } GDHCPOptionType; 158 | 159 | typedef struct dhcp_option { 160 | GDHCPOptionType type; 161 | uint8_t code; 162 | } DHCPOption; 163 | 164 | /* Length of the option types in binary form */ 165 | static const uint8_t dhcp_option_lengths[] = { 166 | [OPTION_IP] = 4, 167 | [OPTION_STRING] = 1, 168 | [OPTION_U8] = 1, 169 | [OPTION_U16] = 2, 170 | [OPTION_U32] = 4, 171 | }; 172 | 173 | uint8_t *dhcp_get_option(struct dhcp_packet *packet, int code); 174 | uint8_t *dhcpv6_get_option(struct dhcpv6_packet *packet, uint16_t pkt_len, 175 | int code, uint16_t *option_len, int *option_count); 176 | uint8_t *dhcpv6_get_sub_option(unsigned char *option, uint16_t max_len, 177 | uint16_t *code, uint16_t *option_len); 178 | int dhcp_end_option(uint8_t *optionptr); 179 | void dhcp_add_binary_option(struct dhcp_packet *packet, uint8_t *addopt); 180 | void dhcpv6_add_binary_option(struct dhcpv6_packet *packet, uint16_t max_len, 181 | uint16_t *pkt_len, uint8_t *addopt); 182 | void dhcp_add_option_uint8(struct dhcp_packet *packet, 183 | uint8_t code, uint8_t data); 184 | void dhcp_add_option_uint16(struct dhcp_packet *packet, 185 | uint8_t code, uint16_t data); 186 | void dhcp_add_option_uint32(struct dhcp_packet *packet, 187 | uint8_t code, uint32_t data); 188 | GDHCPOptionType dhcp_get_code_type(uint8_t code); 189 | GDHCPOptionType dhcpv6_get_code_type(uint16_t code); 190 | 191 | uint16_t dhcp_checksum(void *addr, int count); 192 | 193 | void dhcp_init_header(struct dhcp_packet *packet, char type); 194 | void dhcpv6_init_header(struct dhcpv6_packet *packet, uint8_t type); 195 | 196 | int dhcp_send_raw_packet(struct dhcp_packet *dhcp_pkt, 197 | uint32_t source_ip, int source_port, 198 | uint32_t dest_ip, int dest_port, 199 | const uint8_t *dest_arp, int ifindex); 200 | int dhcpv6_send_packet(int index, struct dhcpv6_packet *dhcp_pkt, int len); 201 | int dhcp_send_kernel_packet(struct dhcp_packet *dhcp_pkt, 202 | uint32_t source_ip, int source_port, 203 | uint32_t dest_ip, int dest_port); 204 | int dhcp_l3_socket(int port, const char *interface, int family); 205 | int dhcp_recv_l3_packet(struct dhcp_packet *packet, int fd); 206 | int dhcpv6_recv_l3_packet(struct dhcpv6_packet **packet, unsigned char *buf, 207 | int buf_len, int fd); 208 | int dhcp_l3_socket_send(int index, int port, int family); 209 | 210 | char *get_interface_name(int index); 211 | bool interface_is_up(int index); 212 | -------------------------------------------------------------------------------- /src/dhcp/gdhcp.h: -------------------------------------------------------------------------------- 1 | /* 2 | * 3 | * DHCP library with GLib integration 4 | * 5 | * Copyright (C) 2009-2012 Intel Corporation. All rights reserved. 6 | * 7 | * This program is free software; you can redistribute it and/or modify 8 | * it under the terms of the GNU General Public License version 2 as 9 | * published by the Free Software Foundation. 10 | * 11 | * This program is distributed in the hope that it will be useful, 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | * GNU General Public License for more details. 15 | * 16 | * You should have received a copy of the GNU General Public License 17 | * along with this program; if not, write to the Free Software 18 | * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA 19 | * 20 | */ 21 | 22 | #ifndef __G_DHCP_H 23 | #define __G_DHCP_H 24 | 25 | #include 26 | #include 27 | #include 28 | 29 | #include 30 | 31 | #ifdef __cplusplus 32 | extern "C" { 33 | #endif 34 | 35 | /* DHCP Client part*/ 36 | struct _GDHCPClient; 37 | 38 | typedef struct _GDHCPClient GDHCPClient; 39 | 40 | typedef enum { 41 | G_DHCP_CLIENT_ERROR_NONE, 42 | G_DHCP_CLIENT_ERROR_INTERFACE_UNAVAILABLE, 43 | G_DHCP_CLIENT_ERROR_INTERFACE_IN_USE, 44 | G_DHCP_CLIENT_ERROR_INTERFACE_DOWN, 45 | G_DHCP_CLIENT_ERROR_NOMEM, 46 | G_DHCP_CLIENT_ERROR_INVALID_INDEX, 47 | G_DHCP_CLIENT_ERROR_INVALID_OPTION 48 | } GDHCPClientError; 49 | 50 | typedef enum { 51 | G_DHCP_CLIENT_EVENT_LEASE_AVAILABLE, 52 | G_DHCP_CLIENT_EVENT_IPV4LL_AVAILABLE, 53 | G_DHCP_CLIENT_EVENT_NO_LEASE, 54 | G_DHCP_CLIENT_EVENT_LEASE_LOST, 55 | G_DHCP_CLIENT_EVENT_IPV4LL_LOST, 56 | G_DHCP_CLIENT_EVENT_ADDRESS_CONFLICT, 57 | G_DHCP_CLIENT_EVENT_INFORMATION_REQ, 58 | G_DHCP_CLIENT_EVENT_SOLICITATION, 59 | G_DHCP_CLIENT_EVENT_ADVERTISE, 60 | G_DHCP_CLIENT_EVENT_REQUEST, 61 | G_DHCP_CLIENT_EVENT_RENEW, 62 | G_DHCP_CLIENT_EVENT_REBIND, 63 | G_DHCP_CLIENT_EVENT_RELEASE, 64 | G_DHCP_CLIENT_EVENT_CONFIRM, 65 | G_DHCP_CLIENT_EVENT_DECLINE, 66 | } GDHCPClientEvent; 67 | 68 | typedef enum { 69 | G_DHCP_IPV4, 70 | G_DHCP_IPV6, 71 | G_DHCP_IPV4LL, 72 | } GDHCPType; 73 | 74 | #define G_DHCP_SUBNET 0x01 75 | #define G_DHCP_ROUTER 0x03 76 | #define G_DHCP_TIME_SERVER 0x04 77 | #define G_DHCP_DNS_SERVER 0x06 78 | #define G_DHCP_DOMAIN_NAME 0x0f 79 | #define G_DHCP_HOST_NAME 0x0c 80 | #define G_DHCP_NTP_SERVER 0x2a 81 | #define G_DHCP_CLIENT_ID 0x3d 82 | 83 | #define G_DHCPV6_CLIENTID 1 84 | #define G_DHCPV6_SERVERID 2 85 | #define G_DHCPV6_IA_NA 3 86 | #define G_DHCPV6_IA_TA 4 87 | #define G_DHCPV6_IAADDR 5 88 | #define G_DHCPV6_ORO 6 89 | #define G_DHCPV6_PREFERENCE 7 90 | #define G_DHCPV6_ELAPSED_TIME 8 91 | #define G_DHCPV6_STATUS_CODE 13 92 | #define G_DHCPV6_RAPID_COMMIT 14 93 | #define G_DHCPV6_DNS_SERVERS 23 94 | #define G_DHCPV6_DOMAIN_LIST 24 95 | #define G_DHCPV6_IA_PD 25 96 | #define G_DHCPV6_IA_PREFIX 26 97 | #define G_DHCPV6_SNTP_SERVERS 31 98 | 99 | #define G_DHCPV6_ERROR_SUCCESS 0 100 | #define G_DHCPV6_ERROR_FAILURE 1 101 | #define G_DHCPV6_ERROR_NO_ADDR 2 102 | #define G_DHCPV6_ERROR_BINDING 3 103 | #define G_DHCPV6_ERROR_LINK 4 104 | #define G_DHCPV6_ERROR_MCAST 5 105 | #define G_DHCPV6_ERROR_NO_PREFIX 6 106 | 107 | typedef enum { 108 | G_DHCPV6_DUID_LLT = 1, 109 | G_DHCPV6_DUID_EN = 2, 110 | G_DHCPV6_DUID_LL = 3, 111 | } GDHCPDuidType; 112 | 113 | typedef struct { 114 | /* 115 | * Note that no field in this struct can be allocated 116 | * from heap or there will be a memory leak when the 117 | * struct is freed by client.c:remove_option_value() 118 | */ 119 | struct in6_addr prefix; 120 | unsigned char prefixlen; 121 | uint32_t preferred; 122 | uint32_t valid; 123 | time_t expire; 124 | } GDHCPIAPrefix; 125 | 126 | typedef void (*GDHCPClientEventFunc) (GDHCPClient *client, gpointer user_data); 127 | 128 | typedef void (*GDHCPDebugFunc)(const char *str, gpointer user_data); 129 | 130 | GDHCPClient *g_dhcp_client_new(GDHCPType type, int index, 131 | GDHCPClientError *error); 132 | 133 | int g_dhcp_client_start(GDHCPClient *client, const char *last_address); 134 | void g_dhcp_client_stop(GDHCPClient *client); 135 | 136 | GDHCPClient *g_dhcp_client_ref(GDHCPClient *client); 137 | void g_dhcp_client_unref(GDHCPClient *client); 138 | 139 | void g_dhcp_client_register_event(GDHCPClient *client, 140 | GDHCPClientEvent event, 141 | GDHCPClientEventFunc func, 142 | gpointer user_data); 143 | 144 | GDHCPClientError g_dhcp_client_set_request(GDHCPClient *client, 145 | unsigned int option_code); 146 | void g_dhcp_client_clear_requests(GDHCPClient *dhcp_client); 147 | void g_dhcp_client_clear_values(GDHCPClient *dhcp_client); 148 | GDHCPClientError g_dhcp_client_set_id(GDHCPClient *client); 149 | GDHCPClientError g_dhcp_client_set_send(GDHCPClient *client, 150 | unsigned char option_code, 151 | const char *option_value); 152 | 153 | char *g_dhcp_client_get_address(GDHCPClient *client); 154 | char *g_dhcp_client_get_netmask(GDHCPClient *client); 155 | GList *g_dhcp_client_get_option(GDHCPClient *client, 156 | unsigned char option_code); 157 | int g_dhcp_client_get_index(GDHCPClient *client); 158 | 159 | void g_dhcp_client_set_debug(GDHCPClient *client, 160 | GDHCPDebugFunc func, gpointer user_data); 161 | int g_dhcpv6_create_duid(GDHCPDuidType duid_type, int index, int type, 162 | unsigned char **duid, int *duid_len); 163 | int g_dhcpv6_client_set_duid(GDHCPClient *dhcp_client, unsigned char *duid, 164 | int duid_len); 165 | int g_dhcpv6_client_set_pd(GDHCPClient *dhcp_client, uint32_t *T1, uint32_t *T2, 166 | GSList *prefixes); 167 | GSList *g_dhcpv6_copy_prefixes(GSList *prefixes); 168 | gboolean g_dhcpv6_client_clear_send(GDHCPClient *dhcp_client, uint16_t code); 169 | void g_dhcpv6_client_set_send(GDHCPClient *dhcp_client, uint16_t option_code, 170 | uint8_t *option_value, uint16_t option_len); 171 | uint16_t g_dhcpv6_client_get_status(GDHCPClient *dhcp_client); 172 | int g_dhcpv6_client_set_oro(GDHCPClient *dhcp_client, int args, ...); 173 | void g_dhcpv6_client_create_iaid(GDHCPClient *dhcp_client, int index, 174 | unsigned char *iaid); 175 | int g_dhcpv6_client_get_timeouts(GDHCPClient *dhcp_client, 176 | uint32_t *T1, uint32_t *T2, 177 | time_t *started, time_t *expire); 178 | uint32_t g_dhcpv6_client_get_iaid(GDHCPClient *dhcp_client); 179 | void g_dhcpv6_client_set_iaid(GDHCPClient *dhcp_client, uint32_t iaid); 180 | int g_dhcpv6_client_set_ia(GDHCPClient *dhcp_client, int index, 181 | int code, uint32_t *T1, uint32_t *T2, 182 | bool add_addresses, const char *address); 183 | int g_dhcpv6_client_set_ias(GDHCPClient *dhcp_client, int index, 184 | int code, uint32_t *T1, uint32_t *T2, 185 | GSList *addresses); 186 | void g_dhcpv6_client_reset_request(GDHCPClient *dhcp_client); 187 | void g_dhcpv6_client_set_retransmit(GDHCPClient *dhcp_client); 188 | void g_dhcpv6_client_clear_retransmit(GDHCPClient *dhcp_client); 189 | 190 | /* DHCP Server */ 191 | typedef enum { 192 | G_DHCP_SERVER_ERROR_NONE, 193 | G_DHCP_SERVER_ERROR_INTERFACE_UNAVAILABLE, 194 | G_DHCP_SERVER_ERROR_INTERFACE_IN_USE, 195 | G_DHCP_SERVER_ERROR_INTERFACE_DOWN, 196 | G_DHCP_SERVER_ERROR_NOMEM, 197 | G_DHCP_SERVER_ERROR_INVALID_INDEX, 198 | G_DHCP_SERVER_ERROR_INVALID_OPTION, 199 | G_DHCP_SERVER_ERROR_IP_ADDRESS_INVALID 200 | } GDHCPServerError; 201 | 202 | typedef void (*GDHCPSaveLeaseFunc) (unsigned char *mac, 203 | unsigned int nip, unsigned int expire); 204 | typedef void (*g_dhcp_event_fn) (const char *mac, const char *ip, void *data); 205 | struct _GDHCPServer; 206 | 207 | typedef struct _GDHCPServer GDHCPServer; 208 | 209 | GDHCPServer *g_dhcp_server_new(GDHCPType type, 210 | int ifindex, GDHCPServerError *error, 211 | g_dhcp_event_fn event_fn, void *fn_data); 212 | int g_dhcp_server_start(GDHCPServer *server); 213 | void g_dhcp_server_stop(GDHCPServer *server); 214 | 215 | GDHCPServer *g_dhcp_server_ref(GDHCPServer *server); 216 | void g_dhcp_server_unref(GDHCPServer *server); 217 | 218 | int g_dhcp_server_set_option(GDHCPServer *server, 219 | unsigned char option_code, const char *option_value); 220 | int g_dhcp_server_set_ip_range(GDHCPServer *server, 221 | const char *start_ip, const char *end_ip); 222 | void g_dhcp_server_set_debug(GDHCPServer *server, 223 | GDHCPDebugFunc func, gpointer user_data); 224 | void g_dhcp_server_set_lease_time(GDHCPServer *dhcp_server, 225 | unsigned int lease_time); 226 | void g_dhcp_server_set_save_lease(GDHCPServer *dhcp_server, 227 | GDHCPSaveLeaseFunc func, gpointer user_data); 228 | #ifdef __cplusplus 229 | } 230 | #endif 231 | 232 | #endif /* __G_DHCP_H */ 233 | -------------------------------------------------------------------------------- /src/dhcp/ipv4ll.c: -------------------------------------------------------------------------------- 1 | /* 2 | * 3 | * IPV4 Local Link library with GLib integration 4 | * 5 | * Copyright (C) 2009-2010 Aldebaran Robotics. All rights reserved. 6 | * 7 | * This program is free software; you can redistribute it and/or modify 8 | * it under the terms of the GNU General Public License version 2 as 9 | * published by the Free Software Foundation. 10 | * 11 | * This program is distributed in the hope that it will be useful, 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | * GNU General Public License for more details. 15 | * 16 | * You should have received a copy of the GNU General Public License 17 | * along with this program; if not, write to the Free Software 18 | * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA 19 | * 20 | */ 21 | #include 22 | #include 23 | #include 24 | #include 25 | 26 | #include 27 | #include 28 | #include 29 | #include 30 | #include 31 | #include 32 | 33 | #include 34 | 35 | #include 36 | #include "ipv4ll.h" 37 | 38 | /** 39 | * Return a random link local IP (in host byte order) 40 | */ 41 | uint32_t ipv4ll_random_ip(int seed) 42 | { 43 | unsigned tmp; 44 | 45 | if (seed) 46 | srand(seed); 47 | else { 48 | struct timeval tv; 49 | gettimeofday(&tv, NULL); 50 | srand(tv.tv_usec); 51 | } 52 | do { 53 | tmp = rand(); 54 | tmp = tmp & IN_CLASSB_HOST; 55 | } while (tmp > (IN_CLASSB_HOST - 0x0200)); 56 | return ((LINKLOCAL_ADDR + 0x0100) + tmp); 57 | } 58 | 59 | /** 60 | * Return a random delay in range of zero to secs*1000 61 | */ 62 | guint ipv4ll_random_delay_ms(guint secs) 63 | { 64 | struct timeval tv; 65 | guint tmp; 66 | 67 | gettimeofday(&tv, NULL); 68 | srand(tv.tv_usec); 69 | tmp = rand(); 70 | return tmp % (secs * 1000); 71 | } 72 | 73 | int ipv4ll_send_arp_packet(uint8_t* source_eth, uint32_t source_ip, 74 | uint32_t target_ip, int ifindex) 75 | { 76 | struct sockaddr_ll dest; 77 | struct ether_arp p; 78 | uint32_t ip_source; 79 | uint32_t ip_target; 80 | int fd, n; 81 | 82 | fd = socket(PF_PACKET, SOCK_DGRAM | SOCK_CLOEXEC, htons(ETH_P_ARP)); 83 | if (fd < 0) 84 | return -errno; 85 | 86 | memset(&dest, 0, sizeof(dest)); 87 | memset(&p, 0, sizeof(p)); 88 | 89 | dest.sll_family = AF_PACKET; 90 | dest.sll_protocol = htons(ETH_P_ARP); 91 | dest.sll_ifindex = ifindex; 92 | dest.sll_halen = ETH_ALEN; 93 | memset(dest.sll_addr, 0xFF, ETH_ALEN); 94 | if (bind(fd, (struct sockaddr *)&dest, sizeof(dest)) < 0) { 95 | close(fd); 96 | return -errno; 97 | } 98 | 99 | ip_source = htonl(source_ip); 100 | ip_target = htonl(target_ip); 101 | p.arp_hrd = htons(ARPHRD_ETHER); 102 | p.arp_pro = htons(ETHERTYPE_IP); 103 | p.arp_hln = ETH_ALEN; 104 | p.arp_pln = 4; 105 | p.arp_op = htons(ARPOP_REQUEST); 106 | 107 | memcpy(&p.arp_sha, source_eth, ETH_ALEN); 108 | memcpy(&p.arp_spa, &ip_source, sizeof(p.arp_spa)); 109 | memcpy(&p.arp_tpa, &ip_target, sizeof(p.arp_tpa)); 110 | 111 | n = sendto(fd, &p, sizeof(p), 0, 112 | (struct sockaddr*) &dest, sizeof(dest)); 113 | if (n < 0) 114 | n = -errno; 115 | 116 | close(fd); 117 | 118 | return n; 119 | } 120 | 121 | int ipv4ll_arp_socket(int ifindex) 122 | { 123 | int fd; 124 | struct sockaddr_ll sock; 125 | fd = socket(PF_PACKET, SOCK_DGRAM | SOCK_CLOEXEC, htons(ETH_P_ARP)); 126 | if (fd < 0) 127 | return fd; 128 | 129 | sock.sll_family = AF_PACKET; 130 | sock.sll_protocol = htons(ETH_P_ARP); 131 | sock.sll_ifindex = ifindex; 132 | 133 | if (bind(fd, (struct sockaddr *) &sock, sizeof(sock)) != 0) { 134 | close(fd); 135 | return -errno; 136 | } 137 | 138 | return fd; 139 | } 140 | -------------------------------------------------------------------------------- /src/dhcp/ipv4ll.h: -------------------------------------------------------------------------------- 1 | /* 2 | * 3 | * IPV4 Local Link library with GLib integration 4 | * 5 | * Copyright (C) 2009-2010 Aldebaran Robotics. All rights reserved. 6 | * 7 | * This program is free software; you can redistribute it and/or modify 8 | * it under the terms of the GNU General Public License version 2 as 9 | * published by the Free Software Foundation. 10 | * 11 | * This program is distributed in the hope that it will be useful, 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | * GNU General Public License for more details. 15 | * 16 | * You should have received a copy of the GNU General Public License 17 | * along with this program; if not, write to the Free Software 18 | * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA 19 | * 20 | */ 21 | 22 | #ifndef __G_IPV4LL_H 23 | #define __G_IPV4LL_H 24 | 25 | #include 26 | 27 | #ifdef __cplusplus 28 | extern "C" { 29 | #endif 30 | 31 | /* 169.254.0.0 */ 32 | #define LINKLOCAL_ADDR 0xa9fe0000 33 | 34 | /* See RFC 3927 */ 35 | #define PROBE_WAIT 1 36 | #define PROBE_NUM 3 37 | #define PROBE_MIN 1 38 | #define PROBE_MAX 2 39 | #define ANNOUNCE_WAIT 2 40 | #define ANNOUNCE_NUM 2 41 | #define ANNOUNCE_INTERVAL 2 42 | #define MAX_CONFLICTS 10 43 | #define RATE_LIMIT_INTERVAL 60 44 | #define DEFEND_INTERVAL 10 45 | 46 | uint32_t ipv4ll_random_ip(int seed); 47 | guint ipv4ll_random_delay_ms(guint secs); 48 | int ipv4ll_send_arp_packet(uint8_t* source_eth, uint32_t source_ip, 49 | uint32_t target_ip, int ifindex); 50 | int ipv4ll_arp_socket(int ifindex); 51 | 52 | #ifdef __cplusplus 53 | } 54 | #endif 55 | #endif /* !IPV4LL_H_ */ 56 | -------------------------------------------------------------------------------- /src/dhcp/unaligned.h: -------------------------------------------------------------------------------- 1 | /* 2 | * 3 | * Connection Manager 4 | * 5 | * Copyright (C) 2012 Intel Corporation. All rights reserved. 6 | * 7 | * This program is free software; you can redistribute it and/or modify 8 | * it under the terms of the GNU General Public License version 2 as 9 | * published by the Free Software Foundation. 10 | * 11 | * This program is distributed in the hope that it will be useful, 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | * GNU General Public License for more details. 15 | * 16 | * You should have received a copy of the GNU General Public License 17 | * along with this program; if not, write to the Free Software 18 | * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA 19 | * 20 | */ 21 | 22 | #include 23 | #include 24 | 25 | #define get_unaligned(ptr) \ 26 | ({ \ 27 | struct __attribute__((packed)) { \ 28 | typeof(*(ptr)) __v; \ 29 | } *__p = (typeof(__p)) (ptr); \ 30 | __p->__v; \ 31 | }) 32 | 33 | #define put_unaligned(val, ptr) \ 34 | do { \ 35 | struct __attribute__((packed)) { \ 36 | typeof(*(ptr)) __v; \ 37 | } *__p = (typeof(__p)) (ptr); \ 38 | __p->__v = (val); \ 39 | } while(0) 40 | 41 | #if __BYTE_ORDER == __LITTLE_ENDIAN 42 | static inline uint64_t get_le64(const void *ptr) 43 | { 44 | return get_unaligned((const uint64_t *) ptr); 45 | } 46 | 47 | static inline uint64_t get_be64(const void *ptr) 48 | { 49 | return bswap_64(get_unaligned((const uint64_t *) ptr)); 50 | } 51 | 52 | static inline uint32_t get_le32(const void *ptr) 53 | { 54 | return get_unaligned((const uint32_t *) ptr); 55 | } 56 | 57 | static inline uint32_t get_be32(const void *ptr) 58 | { 59 | return bswap_32(get_unaligned((const uint32_t *) ptr)); 60 | } 61 | 62 | static inline uint16_t get_le16(const void *ptr) 63 | { 64 | return get_unaligned((const uint16_t *) ptr); 65 | } 66 | 67 | static inline uint16_t get_be16(const void *ptr) 68 | { 69 | return bswap_16(get_unaligned((const uint16_t *) ptr)); 70 | } 71 | 72 | static inline void put_be16(uint16_t val, void *ptr) 73 | { 74 | put_unaligned(bswap_16(val), (uint16_t *) ptr); 75 | } 76 | 77 | static inline void put_be32(uint32_t val, void *ptr) 78 | { 79 | put_unaligned(bswap_32(val), (uint32_t *) ptr); 80 | } 81 | 82 | static inline void put_le16(uint16_t val, void *ptr) 83 | { 84 | put_unaligned(val, (uint16_t *) ptr); 85 | } 86 | 87 | static inline void put_le32(uint32_t val, void *ptr) 88 | { 89 | put_unaligned(val, (uint32_t *) ptr); 90 | } 91 | 92 | static inline void put_be64(uint64_t val, void *ptr) 93 | { 94 | put_unaligned(bswap_64(val), (uint64_t *) ptr); 95 | } 96 | 97 | static inline void put_le64(uint64_t val, void *ptr) 98 | { 99 | put_unaligned(val, (uint64_t *) ptr); 100 | } 101 | #elif __BYTE_ORDER == __BIG_ENDIAN 102 | static inline uint64_t get_le64(const void *ptr) 103 | { 104 | return bswap_64(get_unaligned((const uint64_t *) ptr)); 105 | } 106 | 107 | static inline uint64_t get_be64(const void *ptr) 108 | { 109 | return get_unaligned((const uint64_t *) ptr); 110 | } 111 | 112 | static inline uint32_t get_le32(const void *ptr) 113 | { 114 | return bswap_32(get_unaligned((const uint32_t *) ptr)); 115 | } 116 | 117 | static inline uint32_t get_be32(const void *ptr) 118 | { 119 | return get_unaligned((const uint32_t *) ptr); 120 | } 121 | 122 | static inline uint16_t get_le16(const void *ptr) 123 | { 124 | return bswap_16(get_unaligned((const uint16_t *) ptr)); 125 | } 126 | 127 | static inline uint16_t get_be16(const void *ptr) 128 | { 129 | return get_unaligned((const uint16_t *) ptr); 130 | } 131 | 132 | static inline void put_be16(uint16_t val, void *ptr) 133 | { 134 | put_unaligned(val, (uint16_t *) ptr); 135 | } 136 | 137 | static inline void put_be32(uint32_t val, void *ptr) 138 | { 139 | put_unaligned(val, (uint32_t *) ptr); 140 | } 141 | 142 | static inline void put_le16(uint16_t val, void *ptr) 143 | { 144 | put_unaligned(bswap_16(val), (uint16_t *) ptr); 145 | } 146 | 147 | static inline void put_le32(uint32_t val, void *ptr) 148 | { 149 | put_unaligned(bswap_32(val), (uint32_t *) ptr); 150 | } 151 | 152 | static inline void put_be64(uint64_t val, void *ptr) 153 | { 154 | put_unaligned(val, (uint64_t *) ptr); 155 | } 156 | 157 | static inline void put_le64(uint64_t val, void *ptr) 158 | { 159 | put_unaligned(bswap_64(val), (uint64_t *) ptr); 160 | } 161 | #else 162 | #error "Unknown byte order" 163 | #endif 164 | -------------------------------------------------------------------------------- /src/miracled.c: -------------------------------------------------------------------------------- 1 | /* 2 | * MiracleCast - Wifi-Display/Miracast Implementation 3 | * 4 | * Copyright (c) 2013-2014 David Herrmann 5 | * 6 | * MiracleCast is free software; you can redistribute it and/or modify it 7 | * under the terms of the GNU Lesser General Public License as published by 8 | * the Free Software Foundation; either version 2.1 of the License, or 9 | * (at your option) any later version. 10 | * 11 | * MiracleCast is distributed in the hope that it will be useful, but 12 | * WITHOUT ANY WARRANTY; without even the implied warranty of 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 14 | * Lesser General Public License for more details. 15 | * 16 | * You should have received a copy of the GNU Lesser General Public License 17 | * along with MiracleCast; If not, see . 18 | */ 19 | 20 | #include 21 | #include 22 | #include 23 | #include 24 | #include 25 | #include 26 | #include 27 | #include 28 | #include 29 | #include 30 | #include 31 | #include 32 | #include 33 | #include "miracled.h" 34 | #include "shl_macro.h" 35 | #include "shl_log.h" 36 | #include "shl_util.h" 37 | 38 | static void manager_free(struct manager *m) 39 | { 40 | if (!m) 41 | return; 42 | 43 | free(m); 44 | } 45 | 46 | static int manager_new(struct manager **out) 47 | { 48 | return -EINVAL; 49 | } 50 | 51 | static int manager_run(struct manager *m) 52 | { 53 | return 0; 54 | } 55 | 56 | static int help(void) 57 | { 58 | /* 59 | * 80-char barrier: 60 | * 01234567890123456789012345678901234567890123456789012345678901234567890123456789 61 | */ 62 | printf("%s [OPTIONS...] ...\n\n" 63 | "Remote-display Management-daemon.\n\n" 64 | " -h --help Show this help\n" 65 | " --version Show package version\n" 66 | " --log-level Maximum level for log messages\n" 67 | " --log-time Prefix log-messages with timestamp\n" 68 | , program_invocation_short_name); 69 | /* 70 | * 80-char barrier: 71 | * 01234567890123456789012345678901234567890123456789012345678901234567890123456789 72 | */ 73 | 74 | return 0; 75 | } 76 | 77 | static int parse_argv(int argc, char *argv[]) 78 | { 79 | enum { 80 | ARG_VERSION = 0x100, 81 | ARG_LOG_LEVEL, 82 | ARG_LOG_TIME, 83 | }; 84 | static const struct option options[] = { 85 | { "help", no_argument, NULL, 'h' }, 86 | { "version", no_argument, NULL, ARG_VERSION }, 87 | { "log-level", required_argument, NULL, ARG_LOG_LEVEL }, 88 | { "log-time", no_argument, NULL, ARG_LOG_TIME }, 89 | {} 90 | }; 91 | int c; 92 | 93 | while ((c = getopt_long(argc, argv, "h", options, NULL)) >= 0) { 94 | switch (c) { 95 | case 'h': 96 | return help(); 97 | case ARG_VERSION: 98 | puts(PACKAGE_STRING); 99 | return 0; 100 | case ARG_LOG_LEVEL: 101 | log_max_sev = atoi(optarg); 102 | break; 103 | case ARG_LOG_TIME: 104 | log_init_time(); 105 | break; 106 | case '?': 107 | return -EINVAL; 108 | } 109 | } 110 | 111 | if (optind < argc) { 112 | log_error("unparsed remaining arguments starting with: %s", 113 | argv[optind]); 114 | return -EINVAL; 115 | } 116 | 117 | log_format(LOG_DEFAULT_BASE, NULL, LOG_INFO, 118 | "miracled - revision %s %s %s", 119 | "some-rev-TODO-xyz", __DATE__, __TIME__); 120 | 121 | return 1; 122 | } 123 | 124 | int main(int argc, char **argv) 125 | { 126 | struct manager *m = NULL; 127 | int r; 128 | 129 | srand(time(NULL)); 130 | 131 | r = parse_argv(argc, argv); 132 | if (r < 0) 133 | return EXIT_FAILURE; 134 | if (!r) 135 | return EXIT_SUCCESS; 136 | 137 | r = manager_new(&m); 138 | if (r < 0) 139 | goto finish; 140 | 141 | r = sd_notify(false, "READY=1\n" 142 | "STATUS=Running.."); 143 | if (r < 0) { 144 | log_vERR(r); 145 | goto finish; 146 | } 147 | 148 | r = manager_run(m); 149 | 150 | finish: 151 | sd_notify(false, "STATUS=Exiting.."); 152 | manager_free(m); 153 | 154 | log_debug("exiting.."); 155 | return abs(r); 156 | } 157 | -------------------------------------------------------------------------------- /src/miracled.h: -------------------------------------------------------------------------------- 1 | /* 2 | * MiracleCast - Wifi-Display/Miracast Implementation 3 | * 4 | * Copyright (c) 2013-2014 David Herrmann 5 | * 6 | * MiracleCast is free software; you can redistribute it and/or modify it 7 | * under the terms of the GNU Lesser General Public License as published by 8 | * the Free Software Foundation; either version 2.1 of the License, or 9 | * (at your option) any later version. 10 | * 11 | * MiracleCast is distributed in the hope that it will be useful, but 12 | * WITHOUT ANY WARRANTY; without even the implied warranty of 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 14 | * Lesser General Public License for more details. 15 | * 16 | * You should have received a copy of the GNU Lesser General Public License 17 | * along with MiracleCast; If not, see . 18 | */ 19 | 20 | #include 21 | #include 22 | #include 23 | #include 24 | #include 25 | 26 | #ifndef MIRACLED_H 27 | #define MIRACLED_H 28 | 29 | /* manager */ 30 | 31 | struct manager { 32 | sd_event *event; 33 | }; 34 | 35 | #endif /* MIRACLED_H */ 36 | -------------------------------------------------------------------------------- /src/shared/shl_dlist.h: -------------------------------------------------------------------------------- 1 | /* 2 | * SHL - Double Linked List 3 | * 4 | * Copyright (c) 2010-2013 David Herrmann 5 | * Dedicated to the Public Domain 6 | */ 7 | 8 | /* 9 | * A simple double linked list implementation 10 | * This list API does not provide type-safety! It is a simple circular 11 | * double-linked list. Objects need to embed "struct shl_dlist". This is used to 12 | * link and iterate a list. You can get the object back from a shl_dlist pointer 13 | * via shl_dlist_entry(). This breaks any type-safety, though. You need to make 14 | * sure you call this on the right objects. 15 | */ 16 | 17 | #ifndef SHL_DLIST_H 18 | #define SHL_DLIST_H 19 | 20 | #include 21 | #include 22 | #include 23 | 24 | /* miscellaneous */ 25 | 26 | #define shl_dlist_offsetof(pointer, type, member) ({ \ 27 | const typeof(((type*)0)->member) *__ptr = (pointer); \ 28 | (type*)(((char*)__ptr) - offsetof(type, member)); \ 29 | }) 30 | 31 | /* double linked list */ 32 | 33 | struct shl_dlist { 34 | struct shl_dlist *next; 35 | struct shl_dlist *prev; 36 | }; 37 | 38 | #define SHL_DLIST_INIT(head) { &(head), &(head) } 39 | 40 | static inline void shl_dlist_init(struct shl_dlist *list) 41 | { 42 | list->next = list; 43 | list->prev = list; 44 | } 45 | 46 | static inline void shl_dlist__link(struct shl_dlist *prev, 47 | struct shl_dlist *next, 48 | struct shl_dlist *n) 49 | { 50 | next->prev = n; 51 | n->next = next; 52 | n->prev = prev; 53 | prev->next = n; 54 | } 55 | 56 | static inline void shl_dlist_link(struct shl_dlist *head, 57 | struct shl_dlist *n) 58 | { 59 | return shl_dlist__link(head, head->next, n); 60 | } 61 | 62 | static inline void shl_dlist_link_tail(struct shl_dlist *head, 63 | struct shl_dlist *n) 64 | { 65 | return shl_dlist__link(head->prev, head, n); 66 | } 67 | 68 | static inline void shl_dlist__unlink(struct shl_dlist *prev, 69 | struct shl_dlist *next) 70 | { 71 | next->prev = prev; 72 | prev->next = next; 73 | } 74 | 75 | static inline void shl_dlist_unlink(struct shl_dlist *e) 76 | { 77 | if (e->prev && e->next) { 78 | shl_dlist__unlink(e->prev, e->next); 79 | e->prev = NULL; 80 | e->next = NULL; 81 | } 82 | } 83 | 84 | static inline bool shl_dlist_linked(struct shl_dlist *e) 85 | { 86 | return e->next && e->prev; 87 | } 88 | 89 | static inline bool shl_dlist_empty(struct shl_dlist *head) 90 | { 91 | return head->next == head; 92 | } 93 | 94 | static inline struct shl_dlist *shl_dlist_first(struct shl_dlist *head) 95 | { 96 | return head->next; 97 | } 98 | 99 | static inline struct shl_dlist *shl_dlist_last(struct shl_dlist *head) 100 | { 101 | return head->prev; 102 | } 103 | 104 | #define shl_dlist_entry(ptr, type, member) \ 105 | shl_dlist_offsetof((ptr), type, member) 106 | 107 | #define shl_dlist_first_entry(head, type, member) \ 108 | shl_dlist_entry(shl_dlist_first(head), type, member) 109 | 110 | #define shl_dlist_last_entry(head, type, member) \ 111 | shl_dlist_entry(shl_dlist_last(head), type, member) 112 | 113 | #define shl_dlist_for_each(iter, head) \ 114 | for (iter = (head)->next; iter != (head); iter = iter->next) 115 | 116 | #define shl_dlist_for_each_but_one(iter, start, head) \ 117 | for (iter = ((start)->next == (head)) ? \ 118 | (start)->next->next : \ 119 | (start)->next; \ 120 | iter != (start); \ 121 | iter = (iter->next == (head) && (start) != (head)) ? \ 122 | iter->next->next : \ 123 | iter->next) 124 | 125 | #define shl_dlist_for_each_safe(iter, tmp, head) \ 126 | for (iter = (head)->next, tmp = iter->next; iter != (head); \ 127 | iter = tmp, tmp = iter->next) 128 | 129 | #define shl_dlist_for_each_reverse(iter, head) \ 130 | for (iter = (head)->prev; iter != (head); iter = iter->prev) 131 | 132 | #define shl_dlist_for_each_reverse_but_one(iter, start, head) \ 133 | for (iter = ((start)->prev == (head)) ? \ 134 | (start)->prev->prev : \ 135 | (start)->prev; \ 136 | iter != (start); \ 137 | iter = (iter->prev == (head) && (start) != (head)) ? \ 138 | iter->prev->prev : \ 139 | iter->prev) 140 | 141 | #define shl_dlist_for_each_reverse_safe(iter, tmp, head) \ 142 | for (iter = (head)->prev, tmp = iter->prev; iter != (head); \ 143 | iter = tmp, tmp = iter->prev) 144 | 145 | #endif /* SHL_DLIST_H */ 146 | -------------------------------------------------------------------------------- /src/shared/shl_htable.c: -------------------------------------------------------------------------------- 1 | /* 2 | * SHL - Dynamic hash-table 3 | * 4 | * Written-by: Rusty Russell 5 | * Adjusted-by: David Herrmann 6 | * Licensed under LGPLv2+ - see LICENSE_htable file for details 7 | */ 8 | 9 | /* 10 | * Please see ccan/htable/_info at: 11 | * https://github.com/rustyrussell/ccan/tree/master/ccan/htable 12 | * for information on the hashtable algorithm. This file copies the code inline 13 | * and is released under the same conditions. 14 | * 15 | * At the end of the file you can find some helpers to use this htable to store 16 | * objects with "unsigned long" or "char*" keys. 17 | */ 18 | 19 | #include 20 | #include 21 | #include 22 | #include 23 | #include 24 | #include 25 | #include 26 | #include "shl_htable.h" 27 | 28 | #define COLD __attribute__((cold)) 29 | 30 | struct htable { 31 | /* KEEP IN SYNC WITH "struct shl_htable_int" */ 32 | size_t (*rehash)(const void *elem, void *priv); 33 | void *priv; 34 | unsigned int bits; 35 | size_t elems, deleted, max, max_with_deleted; 36 | /* These are the bits which are the same in all pointers. */ 37 | uintptr_t common_mask, common_bits; 38 | uintptr_t perfect_bit; 39 | uintptr_t *table; 40 | }; 41 | 42 | #define HTABLE_INITIALIZER(name, rehash, priv) \ 43 | { rehash, priv, 0, 0, 0, 0, 0, -1, 0, 0, &name.perfect_bit } 44 | 45 | struct htable_iter { 46 | size_t off; 47 | }; 48 | 49 | /* 50 | * INLINE COPY OF ccan/htable.c 51 | */ 52 | 53 | /* We use 0x1 as deleted marker. */ 54 | #define HTABLE_DELETED (0x1) 55 | 56 | /* We clear out the bits which are always the same, and put metadata there. */ 57 | static inline uintptr_t get_extra_ptr_bits(const struct htable *ht, 58 | uintptr_t e) 59 | { 60 | return e & ht->common_mask; 61 | } 62 | 63 | static inline void *get_raw_ptr(const struct htable *ht, uintptr_t e) 64 | { 65 | return (void *)((e & ~ht->common_mask) | ht->common_bits); 66 | } 67 | 68 | static inline uintptr_t make_hval(const struct htable *ht, 69 | const void *p, uintptr_t bits) 70 | { 71 | return ((uintptr_t)p & ~ht->common_mask) | bits; 72 | } 73 | 74 | static inline bool entry_is_valid(uintptr_t e) 75 | { 76 | return e > HTABLE_DELETED; 77 | } 78 | 79 | static inline uintptr_t get_hash_ptr_bits(const struct htable *ht, 80 | size_t hash) 81 | { 82 | /* Shuffling the extra bits (as specified in mask) down the 83 | * end is quite expensive. But the lower bits are redundant, so 84 | * we fold the value first. */ 85 | return (hash ^ (hash >> ht->bits)) 86 | & ht->common_mask & ~ht->perfect_bit; 87 | } 88 | 89 | static void htable_init(struct htable *ht, 90 | size_t (*rehash)(const void *elem, void *priv), 91 | void *priv) 92 | { 93 | struct htable empty = HTABLE_INITIALIZER(empty, NULL, NULL); 94 | *ht = empty; 95 | ht->rehash = rehash; 96 | ht->priv = priv; 97 | ht->table = &ht->perfect_bit; 98 | } 99 | 100 | static void htable_clear(struct htable *ht, 101 | void (*free_cb) (void *entry, void *ctx), 102 | void *ctx) 103 | { 104 | size_t i; 105 | 106 | if (ht->table != &ht->perfect_bit) { 107 | if (free_cb) { 108 | for (i = 0; i < (size_t)1 << ht->bits; ++i) { 109 | if (entry_is_valid(ht->table[i])) 110 | free_cb(get_raw_ptr(ht, ht->table[i]), 111 | ctx); 112 | } 113 | } 114 | 115 | free((void *)ht->table); 116 | } 117 | 118 | htable_init(ht, ht->rehash, ht->priv); 119 | } 120 | 121 | size_t shl_htable_this_or_next(struct shl_htable *htable, size_t i) 122 | { 123 | struct htable *ht = (void*)&htable->htable; 124 | 125 | if (ht->table != &ht->perfect_bit) 126 | for ( ; i < (size_t)1 << ht->bits; ++i) 127 | if (entry_is_valid(ht->table[i])) 128 | return i; 129 | 130 | return SIZE_MAX; 131 | } 132 | 133 | void *shl_htable_get_entry(struct shl_htable *htable, size_t i) 134 | { 135 | struct htable *ht = (void*)&htable->htable; 136 | 137 | if (i < (size_t)1 << ht->bits) 138 | if (entry_is_valid(ht->table[i])) 139 | return get_raw_ptr(ht, ht->table[i]); 140 | 141 | return NULL; 142 | } 143 | 144 | static void htable_visit(struct htable *ht, 145 | void (*visit_cb) (void *elem, void *ctx), 146 | void *ctx) 147 | { 148 | size_t i; 149 | 150 | if (visit_cb && ht->table != &ht->perfect_bit) { 151 | for (i = 0; i < (size_t)1 << ht->bits; ++i) { 152 | if (entry_is_valid(ht->table[i])) 153 | visit_cb(get_raw_ptr(ht, ht->table[i]), ctx); 154 | } 155 | } 156 | } 157 | 158 | static size_t hash_bucket(const struct htable *ht, size_t h) 159 | { 160 | return h & ((1 << ht->bits)-1); 161 | } 162 | 163 | static void *htable_val(const struct htable *ht, 164 | struct htable_iter *i, size_t hash, uintptr_t perfect) 165 | { 166 | uintptr_t h2 = get_hash_ptr_bits(ht, hash) | perfect; 167 | 168 | while (ht->table[i->off]) { 169 | if (ht->table[i->off] != HTABLE_DELETED) { 170 | if (get_extra_ptr_bits(ht, ht->table[i->off]) == h2) 171 | return get_raw_ptr(ht, ht->table[i->off]); 172 | } 173 | i->off = (i->off + 1) & ((1 << ht->bits)-1); 174 | h2 &= ~perfect; 175 | } 176 | return NULL; 177 | } 178 | 179 | static void *htable_firstval(const struct htable *ht, 180 | struct htable_iter *i, size_t hash) 181 | { 182 | i->off = hash_bucket(ht, hash); 183 | return htable_val(ht, i, hash, ht->perfect_bit); 184 | } 185 | 186 | static void *htable_nextval(const struct htable *ht, 187 | struct htable_iter *i, size_t hash) 188 | { 189 | i->off = (i->off + 1) & ((1 << ht->bits)-1); 190 | return htable_val(ht, i, hash, 0); 191 | } 192 | 193 | /* This does not expand the hash table, that's up to caller. */ 194 | static void ht_add(struct htable *ht, const void *new, size_t h) 195 | { 196 | size_t i; 197 | uintptr_t perfect = ht->perfect_bit; 198 | 199 | i = hash_bucket(ht, h); 200 | 201 | while (entry_is_valid(ht->table[i])) { 202 | perfect = 0; 203 | i = (i + 1) & ((1 << ht->bits)-1); 204 | } 205 | ht->table[i] = make_hval(ht, new, get_hash_ptr_bits(ht, h)|perfect); 206 | } 207 | 208 | static COLD bool double_table(struct htable *ht) 209 | { 210 | unsigned int i; 211 | size_t oldnum = (size_t)1 << ht->bits; 212 | uintptr_t *oldtable, e; 213 | 214 | oldtable = ht->table; 215 | ht->table = calloc(1 << (ht->bits+1), sizeof(size_t)); 216 | if (!ht->table) { 217 | ht->table = oldtable; 218 | return false; 219 | } 220 | ht->bits++; 221 | ht->max = ((size_t)3 << ht->bits) / 4; 222 | ht->max_with_deleted = ((size_t)9 << ht->bits) / 10; 223 | 224 | /* If we lost our "perfect bit", get it back now. */ 225 | if (!ht->perfect_bit && ht->common_mask) { 226 | for (i = 0; i < sizeof(ht->common_mask) * CHAR_BIT; i++) { 227 | if (ht->common_mask & ((size_t)1 << i)) { 228 | ht->perfect_bit = (size_t)1 << i; 229 | break; 230 | } 231 | } 232 | } 233 | 234 | if (oldtable != &ht->perfect_bit) { 235 | for (i = 0; i < oldnum; i++) { 236 | if (entry_is_valid(e = oldtable[i])) { 237 | void *p = get_raw_ptr(ht, e); 238 | ht_add(ht, p, ht->rehash(p, ht->priv)); 239 | } 240 | } 241 | free(oldtable); 242 | } 243 | ht->deleted = 0; 244 | return true; 245 | } 246 | 247 | static COLD void rehash_table(struct htable *ht) 248 | { 249 | size_t start, i; 250 | uintptr_t e; 251 | 252 | /* Beware wrap cases: we need to start from first empty bucket. */ 253 | for (start = 0; ht->table[start]; start++); 254 | 255 | for (i = 0; i < (size_t)1 << ht->bits; i++) { 256 | size_t h = (i + start) & ((1 << ht->bits)-1); 257 | e = ht->table[h]; 258 | if (!e) 259 | continue; 260 | if (e == HTABLE_DELETED) 261 | ht->table[h] = 0; 262 | else if (!(e & ht->perfect_bit)) { 263 | void *p = get_raw_ptr(ht, e); 264 | ht->table[h] = 0; 265 | ht_add(ht, p, ht->rehash(p, ht->priv)); 266 | } 267 | } 268 | ht->deleted = 0; 269 | } 270 | 271 | /* We stole some bits, now we need to put them back... */ 272 | static COLD void update_common(struct htable *ht, const void *p) 273 | { 274 | unsigned int i; 275 | uintptr_t maskdiff, bitsdiff; 276 | 277 | if (ht->elems == 0) { 278 | /* Always reveal one bit of the pointer in the bucket, 279 | * so it's not zero or HTABLE_DELETED (1), even if 280 | * hash happens to be 0. Assumes (void *)1 is not a 281 | * valid pointer. */ 282 | for (i = sizeof(uintptr_t)*CHAR_BIT - 1; i > 0; i--) { 283 | if ((uintptr_t)p & ((uintptr_t)1 << i)) 284 | break; 285 | } 286 | 287 | ht->common_mask = ~((uintptr_t)1 << i); 288 | ht->common_bits = ((uintptr_t)p & ht->common_mask); 289 | ht->perfect_bit = 1; 290 | return; 291 | } 292 | 293 | /* Find bits which are unequal to old common set. */ 294 | maskdiff = ht->common_bits ^ ((uintptr_t)p & ht->common_mask); 295 | 296 | /* These are the bits which go there in existing entries. */ 297 | bitsdiff = ht->common_bits & maskdiff; 298 | 299 | for (i = 0; i < (size_t)1 << ht->bits; i++) { 300 | if (!entry_is_valid(ht->table[i])) 301 | continue; 302 | /* Clear the bits no longer in the mask, set them as 303 | * expected. */ 304 | ht->table[i] &= ~maskdiff; 305 | ht->table[i] |= bitsdiff; 306 | } 307 | 308 | /* Take away those bits from our mask, bits and perfect bit. */ 309 | ht->common_mask &= ~maskdiff; 310 | ht->common_bits &= ~maskdiff; 311 | ht->perfect_bit &= ~maskdiff; 312 | } 313 | 314 | static bool htable_add(struct htable *ht, size_t hash, const void *p) 315 | { 316 | if (ht->elems+1 > ht->max && !double_table(ht)) 317 | return false; 318 | if (ht->elems+1 + ht->deleted > ht->max_with_deleted) 319 | rehash_table(ht); 320 | assert(p); 321 | if (((uintptr_t)p & ht->common_mask) != ht->common_bits) 322 | update_common(ht, p); 323 | 324 | ht_add(ht, p, hash); 325 | ht->elems++; 326 | return true; 327 | } 328 | 329 | static void htable_delval(struct htable *ht, struct htable_iter *i) 330 | { 331 | assert(i->off < (size_t)1 << ht->bits); 332 | assert(entry_is_valid(ht->table[i->off])); 333 | 334 | ht->elems--; 335 | ht->table[i->off] = HTABLE_DELETED; 336 | ht->deleted++; 337 | } 338 | 339 | /* 340 | * Wrapper code to make it easier to use this hash-table as map. 341 | */ 342 | 343 | void shl_htable_init(struct shl_htable *htable, 344 | bool (*compare) (const void *a, const void *b), 345 | size_t (*rehash)(const void *elem, void *priv), 346 | void *priv) 347 | { 348 | struct htable *ht = (void*)&htable->htable; 349 | 350 | htable->compare = compare; 351 | htable_init(ht, rehash, priv); 352 | } 353 | 354 | void shl_htable_clear(struct shl_htable *htable, 355 | void (*free_cb) (void *elem, void *ctx), 356 | void *ctx) 357 | { 358 | struct htable *ht = (void*)&htable->htable; 359 | 360 | htable_clear(ht, free_cb, ctx); 361 | } 362 | 363 | void shl_htable_visit(struct shl_htable *htable, 364 | void (*visit_cb) (void *elem, void *ctx), 365 | void *ctx) 366 | { 367 | struct htable *ht = (void*)&htable->htable; 368 | 369 | htable_visit(ht, visit_cb, ctx); 370 | } 371 | 372 | bool shl_htable_lookup(struct shl_htable *htable, const void *obj, size_t hash, 373 | void **out) 374 | { 375 | struct htable *ht = (void*)&htable->htable; 376 | struct htable_iter i; 377 | void *c; 378 | 379 | for (c = htable_firstval(ht, &i, hash); 380 | c; 381 | c = htable_nextval(ht, &i, hash)) { 382 | if (htable->compare(obj, c)) { 383 | if (out) 384 | *out = c; 385 | return true; 386 | } 387 | } 388 | 389 | return false; 390 | } 391 | 392 | int shl_htable_insert(struct shl_htable *htable, const void *obj, size_t hash) 393 | { 394 | struct htable *ht = (void*)&htable->htable; 395 | bool b; 396 | 397 | b = htable_add(ht, hash, (void*)obj); 398 | return b ? 0 : -ENOMEM; 399 | } 400 | 401 | bool shl_htable_remove(struct shl_htable *htable, const void *obj, size_t hash, 402 | void **out) 403 | { 404 | struct htable *ht = (void*)&htable->htable; 405 | struct htable_iter i; 406 | void *c; 407 | 408 | for (c = htable_firstval(ht, &i, hash); 409 | c; 410 | c = htable_nextval(ht, &i, hash)) { 411 | if (htable->compare(obj, c)) { 412 | if (out) 413 | *out = c; 414 | htable_delval(ht, &i); 415 | return true; 416 | } 417 | } 418 | 419 | return false; 420 | } 421 | 422 | /* 423 | * Helpers 424 | */ 425 | 426 | bool shl_htable_compare_uint(const void *a, const void *b) 427 | { 428 | return *(const unsigned int*)a == *(const unsigned int*)b; 429 | } 430 | 431 | size_t shl_htable_rehash_uint(const void *elem, void *priv) 432 | { 433 | return (size_t)*(const unsigned int*)elem; 434 | } 435 | 436 | bool shl_htable_compare_ulong(const void *a, const void *b) 437 | { 438 | return *(const unsigned long*)a == *(const unsigned long*)b; 439 | } 440 | 441 | size_t shl_htable_rehash_ulong(const void *elem, void *priv) 442 | { 443 | return (size_t)*(const unsigned long*)elem; 444 | } 445 | 446 | bool shl_htable_compare_str(const void *a, const void *b) 447 | { 448 | if (!*(char**)a || !*(char**)b) 449 | return *(char**)a == *(char**)b; 450 | else 451 | return !strcmp(*(char**)a, *(char**)b); 452 | } 453 | 454 | /* DJB's hash function */ 455 | size_t shl_htable_rehash_str(const void *elem, void *priv) 456 | { 457 | const char *str = *(char**)elem; 458 | size_t hash = 5381; 459 | 460 | for ( ; str && *str; ++str) 461 | hash = (hash << 5) + hash + (size_t)*str; 462 | 463 | return hash; 464 | } 465 | -------------------------------------------------------------------------------- /src/shared/shl_htable.h: -------------------------------------------------------------------------------- 1 | /* 2 | * SHL - Dynamic hash-table 3 | * 4 | * Copyright (c) 2010-2013 David Herrmann 5 | * Licensed under LGPLv2+ - see LICENSE_htable file for details 6 | */ 7 | 8 | /* 9 | * Dynamic hash-table 10 | * Implementation of a self-resizing hashtable to store arbitrary objects. 11 | * Entries are not allocated by the table itself but are user-allocated. A 12 | * single entry can be stored multiple times in the hashtable. No 13 | * maintenance-members need to be embedded in user-allocated objects. However, 14 | * the key (and optionally the hash) must be stored in the objects. 15 | * 16 | * Uses internally the htable from CCAN. See LICENSE_htable. 17 | */ 18 | 19 | #ifndef SHL_HTABLE_H 20 | #define SHL_HTABLE_H 21 | 22 | #include 23 | #include 24 | #include 25 | #include 26 | #include 27 | 28 | /* miscellaneous */ 29 | 30 | #define shl_htable_offsetof(pointer, type, member) ({ \ 31 | const typeof(((type*)0)->member) *__ptr = (pointer); \ 32 | (type*)(((char*)__ptr) - offsetof(type, member)); \ 33 | }) 34 | 35 | /* htable */ 36 | 37 | struct shl_htable_int { 38 | size_t (*rehash)(const void *elem, void *priv); 39 | void *priv; 40 | unsigned int bits; 41 | size_t elems, deleted, max, max_with_deleted; 42 | /* These are the bits which are the same in all pointers. */ 43 | uintptr_t common_mask, common_bits; 44 | uintptr_t perfect_bit; 45 | uintptr_t *table; 46 | }; 47 | 48 | struct shl_htable { 49 | bool (*compare) (const void *a, const void *b); 50 | struct shl_htable_int htable; 51 | }; 52 | 53 | #define SHL_HTABLE_INIT(_obj, _compare, _rehash, _priv) \ 54 | { \ 55 | .compare = (_compare), \ 56 | .htable = { \ 57 | .rehash = (_rehash), \ 58 | .priv = (_priv), \ 59 | .bits = 0, \ 60 | .elems = 0, \ 61 | .deleted = 0, \ 62 | .max = 0, \ 63 | .max_with_deleted = 0, \ 64 | .common_mask = -1, \ 65 | .common_bits = 0, \ 66 | .perfect_bit = 0, \ 67 | .table = &(_obj).htable.perfect_bit \ 68 | } \ 69 | } 70 | 71 | void shl_htable_init(struct shl_htable *htable, 72 | bool (*compare) (const void *a, const void *b), 73 | size_t (*rehash)(const void *elem, void *priv), 74 | void *priv); 75 | void shl_htable_clear(struct shl_htable *htable, 76 | void (*free_cb) (void *elem, void *ctx), 77 | void *ctx); 78 | void shl_htable_visit(struct shl_htable *htable, 79 | void (*visit_cb) (void *elem, void *ctx), 80 | void *ctx); 81 | bool shl_htable_lookup(struct shl_htable *htable, const void *obj, size_t hash, 82 | void **out); 83 | int shl_htable_insert(struct shl_htable *htable, const void *obj, size_t hash); 84 | bool shl_htable_remove(struct shl_htable *htable, const void *obj, size_t hash, 85 | void **out); 86 | 87 | size_t shl_htable_this_or_next(struct shl_htable *htable, size_t i); 88 | void *shl_htable_get_entry(struct shl_htable *htable, size_t i); 89 | 90 | #define SHL_HTABLE_FOREACH(_iter, _ht) for ( \ 91 | size_t htable__i = shl_htable_this_or_next((_ht), 0); \ 92 | (_iter = shl_htable_get_entry((_ht), htable__i)); \ 93 | htable__i = shl_htable_this_or_next((_ht), htable__i + 1) \ 94 | ) 95 | 96 | #define SHL_HTABLE_FOREACH_MACRO(_iter, _ht, _accessor) for ( \ 97 | size_t htable__i = shl_htable_this_or_next((_ht), 0); \ 98 | (_iter = shl_htable_get_entry((_ht), htable__i), \ 99 | _iter = _iter ? _accessor((void*)_iter) : NULL); \ 100 | htable__i = shl_htable_this_or_next((_ht), htable__i + 1) \ 101 | ) 102 | 103 | #define SHL_HTABLE_FIRST(_ht) \ 104 | shl_htable_get_entry((_ht), shl_htable_this_or_next((_ht), 0)) 105 | 106 | #define SHL_HTABLE_FIRST_MACRO(_ht, _accessor) ({ \ 107 | void *htable__i = shl_htable_get_entry((_ht), \ 108 | shl_htable_this_or_next((_ht), 0)); \ 109 | htable__i ? _accessor(htable__i) : NULL; }) 110 | 111 | /* uint htables */ 112 | 113 | #if SIZE_MAX < UINT_MAX 114 | # error "'size_t' is smaller than 'unsigned int'" 115 | #endif 116 | 117 | bool shl_htable_compare_uint(const void *a, const void *b); 118 | size_t shl_htable_rehash_uint(const void *elem, void *priv); 119 | 120 | #define SHL_HTABLE_INIT_UINT(_obj) \ 121 | SHL_HTABLE_INIT((_obj), shl_htable_compare_uint, \ 122 | shl_htable_rehash_uint, \ 123 | NULL) 124 | 125 | static inline void shl_htable_init_uint(struct shl_htable *htable) 126 | { 127 | shl_htable_init(htable, shl_htable_compare_uint, 128 | shl_htable_rehash_uint, NULL); 129 | } 130 | 131 | static inline void shl_htable_clear_uint(struct shl_htable *htable, 132 | void (*cb) (unsigned int *elem, 133 | void *ctx), 134 | void *ctx) 135 | { 136 | shl_htable_clear(htable, (void (*) (void*, void*))cb, ctx); 137 | } 138 | 139 | static inline void shl_htable_visit_uint(struct shl_htable *htable, 140 | void (*cb) (unsigned int *elem, 141 | void *ctx), 142 | void *ctx) 143 | { 144 | shl_htable_visit(htable, (void (*) (void*, void*))cb, ctx); 145 | } 146 | 147 | static inline bool shl_htable_lookup_uint(struct shl_htable *htable, 148 | unsigned int key, 149 | unsigned int **out) 150 | { 151 | return shl_htable_lookup(htable, (const void*)&key, (size_t)key, 152 | (void**)out); 153 | } 154 | 155 | static inline int shl_htable_insert_uint(struct shl_htable *htable, 156 | const unsigned int *key) 157 | { 158 | return shl_htable_insert(htable, (const void*)key, (size_t)*key); 159 | } 160 | 161 | static inline bool shl_htable_remove_uint(struct shl_htable *htable, 162 | unsigned int key, 163 | unsigned int **out) 164 | { 165 | return shl_htable_remove(htable, (const void*)&key, (size_t)key, 166 | (void**)out); 167 | } 168 | 169 | /* ulong htables */ 170 | 171 | #if SIZE_MAX < ULONG_MAX 172 | # error "'size_t' is smaller than 'unsigned long'" 173 | #endif 174 | 175 | bool shl_htable_compare_ulong(const void *a, const void *b); 176 | size_t shl_htable_rehash_ulong(const void *elem, void *priv); 177 | 178 | #define SHL_HTABLE_INIT_ULONG(_obj) \ 179 | SHL_HTABLE_INIT((_obj), shl_htable_compare_ulong, \ 180 | shl_htable_rehash_ulong, \ 181 | NULL) 182 | 183 | static inline void shl_htable_init_ulong(struct shl_htable *htable) 184 | { 185 | shl_htable_init(htable, shl_htable_compare_ulong, 186 | shl_htable_rehash_ulong, NULL); 187 | } 188 | 189 | static inline void shl_htable_clear_ulong(struct shl_htable *htable, 190 | void (*cb) (unsigned long *elem, 191 | void *ctx), 192 | void *ctx) 193 | { 194 | shl_htable_clear(htable, (void (*) (void*, void*))cb, ctx); 195 | } 196 | 197 | static inline void shl_htable_visit_ulong(struct shl_htable *htable, 198 | void (*cb) (unsigned long *elem, 199 | void *ctx), 200 | void *ctx) 201 | { 202 | shl_htable_visit(htable, (void (*) (void*, void*))cb, ctx); 203 | } 204 | 205 | static inline bool shl_htable_lookup_ulong(struct shl_htable *htable, 206 | unsigned long key, 207 | unsigned long **out) 208 | { 209 | return shl_htable_lookup(htable, (const void*)&key, (size_t)key, 210 | (void**)out); 211 | } 212 | 213 | static inline int shl_htable_insert_ulong(struct shl_htable *htable, 214 | const unsigned long *key) 215 | { 216 | return shl_htable_insert(htable, (const void*)key, (size_t)*key); 217 | } 218 | 219 | static inline bool shl_htable_remove_ulong(struct shl_htable *htable, 220 | unsigned long key, 221 | unsigned long **out) 222 | { 223 | return shl_htable_remove(htable, (const void*)&key, (size_t)key, 224 | (void**)out); 225 | } 226 | 227 | /* string htables */ 228 | 229 | bool shl_htable_compare_str(const void *a, const void *b); 230 | size_t shl_htable_rehash_str(const void *elem, void *priv); 231 | 232 | #define SHL_HTABLE_INIT_STR(_obj) \ 233 | SHL_HTABLE_INIT((_obj), shl_htable_compare_str, \ 234 | shl_htable_rehash_str, \ 235 | NULL) 236 | 237 | static inline void shl_htable_init_str(struct shl_htable *htable) 238 | { 239 | shl_htable_init(htable, shl_htable_compare_str, 240 | shl_htable_rehash_str, NULL); 241 | } 242 | 243 | static inline void shl_htable_clear_str(struct shl_htable *htable, 244 | void (*cb) (char **elem, 245 | void *ctx), 246 | void *ctx) 247 | { 248 | shl_htable_clear(htable, (void (*) (void*, void*))cb, ctx); 249 | } 250 | 251 | static inline void shl_htable_visit_str(struct shl_htable *htable, 252 | void (*cb) (char **elem, 253 | void *ctx), 254 | void *ctx) 255 | { 256 | shl_htable_visit(htable, (void (*) (void*, void*))cb, ctx); 257 | } 258 | 259 | static inline size_t shl_htable_hash_str(struct shl_htable *htable, 260 | const char *str, size_t *hash) 261 | { 262 | size_t h; 263 | 264 | if (hash && *hash) { 265 | h = *hash; 266 | } else { 267 | h = htable->htable.rehash((const void*)&str, NULL); 268 | if (hash) 269 | *hash = h; 270 | } 271 | 272 | return h; 273 | } 274 | 275 | static inline bool shl_htable_lookup_str(struct shl_htable *htable, 276 | const char *str, size_t *hash, 277 | char ***out) 278 | { 279 | size_t h; 280 | 281 | h = shl_htable_hash_str(htable, str, hash); 282 | return shl_htable_lookup(htable, (const void*)&str, h, (void**)out); 283 | } 284 | 285 | static inline int shl_htable_insert_str(struct shl_htable *htable, 286 | char **str, size_t *hash) 287 | { 288 | size_t h; 289 | 290 | h = shl_htable_hash_str(htable, *str, hash); 291 | return shl_htable_insert(htable, (const void*)str, h); 292 | } 293 | 294 | static inline bool shl_htable_remove_str(struct shl_htable *htable, 295 | const char *str, size_t *hash, 296 | char ***out) 297 | { 298 | size_t h; 299 | 300 | h = shl_htable_hash_str(htable, str, hash); 301 | return shl_htable_remove(htable, (const void*)&str, h, (void **)out); 302 | } 303 | 304 | #endif /* SHL_HTABLE_H */ 305 | -------------------------------------------------------------------------------- /src/shared/shl_log.c: -------------------------------------------------------------------------------- 1 | /* 2 | * SHL - Log/Debug Interface 3 | * 4 | * Copyright (c) 2010-2013 David Herrmann 5 | * Dedicated to the Public Domain 6 | */ 7 | 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include "shl_log.h" 16 | 17 | /* 18 | * Locking 19 | * Dummies to implement locking. If we ever want lock-protected logging, these 20 | * need to be provided by the user. 21 | */ 22 | 23 | static inline void log_lock() 24 | { 25 | } 26 | 27 | static inline void log_unlock() 28 | { 29 | } 30 | 31 | /* 32 | * Time Management 33 | * We print seconds and microseconds since application start for each 34 | * log-message in case log_init_time() has been called. 35 | */ 36 | 37 | static struct timeval log__ftime; 38 | 39 | static bool log__have_time(void) 40 | { 41 | return !(log__ftime.tv_sec == 0 && log__ftime.tv_usec == 0); 42 | } 43 | 44 | void log_init_time(void) 45 | { 46 | if (!log__have_time()) 47 | gettimeofday(&log__ftime, NULL); 48 | } 49 | 50 | static void log__time(long long *sec, long long *usec) 51 | { 52 | struct timeval t; 53 | 54 | /* In case this is called in parallel to log_init_time(), we need to 55 | * catch negative time-diffs. Other than that, this can be called 56 | * unlocked. */ 57 | 58 | gettimeofday(&t, NULL); 59 | *sec = t.tv_sec - log__ftime.tv_sec; 60 | *usec = (long long)t.tv_usec - (long long)log__ftime.tv_usec; 61 | if (*usec < 0) { 62 | *sec -= 1; 63 | if (*sec < 0) 64 | *sec = 0; 65 | *usec = 1000000 + *usec; 66 | } 67 | } 68 | 69 | /* 70 | * Default Values 71 | * Several logging-parameters may be omitted by applications. To provide sane 72 | * default values we provide constants here. 73 | * 74 | * LOG_SUBSYSTEM: By default no subsystem is specified 75 | */ 76 | 77 | const char *LOG_SUBSYSTEM = NULL; 78 | 79 | /* 80 | * Max Severity 81 | * Messages with severities between log_max_sev and LOG_SEV_NUM (exclusive) 82 | * are not logged, but discarded. 83 | */ 84 | 85 | unsigned int log_max_sev = LOG_NOTICE; 86 | 87 | /* 88 | * Forward declaration so we can use the locked-versions in other functions 89 | * here. Be careful to avoid deadlocks, though. 90 | * Also set default log-subsystem to "log" for all logging inside this API. 91 | */ 92 | 93 | static void log__submit(const char *file, 94 | int line, 95 | const char *func, 96 | const char *subs, 97 | unsigned int sev, 98 | const char *format, 99 | va_list args); 100 | 101 | #define LOG_SUBSYSTEM "log" 102 | 103 | /* 104 | * Basic logger 105 | * The log__submit function writes the message into the current log-target. It 106 | * must be called with log__mutex locked. 107 | * By default the current time elapsed since the first message was logged is 108 | * prepended to the message. file, line and func information are appended to the 109 | * message if sev == LOG_DEBUG. 110 | * The subsystem, if not NULL, is prepended as "SUBS: " to the message and a 111 | * newline is always appended by default. Multiline-messages are not allowed. 112 | */ 113 | 114 | static const char *log__sev2str[LOG_SEV_NUM] = { 115 | [LOG_DEBUG] = "DEBUG", 116 | [LOG_INFO] = "INFO", 117 | [LOG_NOTICE] = "NOTICE", 118 | [LOG_WARNING] = "WARNING", 119 | [LOG_ERROR] = "ERROR", 120 | [LOG_CRITICAL] = "CRITICAL", 121 | [LOG_ALERT] = "ALERT", 122 | [LOG_FATAL] = "FATAL", 123 | }; 124 | 125 | static void log__submit(const char *file, 126 | int line, 127 | const char *func, 128 | const char *subs, 129 | unsigned int sev, 130 | const char *format, 131 | va_list args) 132 | { 133 | int saved_errno = errno; 134 | const char *prefix = NULL; 135 | FILE *out; 136 | long long sec, usec; 137 | 138 | out = stderr; 139 | log__time(&sec, &usec); 140 | 141 | if (sev < LOG_SEV_NUM && sev > log_max_sev) 142 | return; 143 | 144 | if (sev < LOG_SEV_NUM) 145 | prefix = log__sev2str[sev]; 146 | 147 | if (prefix) { 148 | if (subs) { 149 | if (log__have_time()) 150 | fprintf(out, "[%.4lld.%.6lld] %s: %s: ", 151 | sec, usec, prefix, subs); 152 | else 153 | fprintf(out, "%s: %s: ", prefix, subs); 154 | } else { 155 | if (log__have_time()) 156 | fprintf(out, "[%.4lld.%.6lld] %s: ", 157 | sec, usec, prefix); 158 | else 159 | fprintf(out, "%s: ", prefix); 160 | } 161 | } else { 162 | if (subs) { 163 | if (log__have_time()) 164 | fprintf(out, "[%.4lld.%.6lld] %s: ", 165 | sec, usec, subs); 166 | else 167 | fprintf(out, "%s: ", subs); 168 | } else { 169 | if (log__have_time()) 170 | fprintf(out, "[%.4lld.%.6lld] ", sec, usec); 171 | } 172 | } 173 | 174 | errno = saved_errno; 175 | vfprintf(out, format, args); 176 | 177 | if (sev == LOG_DEBUG || sev <= LOG_WARNING) { 178 | if (!func) 179 | func = ""; 180 | if (!file) 181 | file = ""; 182 | if (line < 0) 183 | line = 0; 184 | fprintf(out, " (%s() in %s:%d)\n", func, file, line); 185 | } else { 186 | fprintf(out, "\n"); 187 | } 188 | } 189 | 190 | void log_submit(const char *file, 191 | int line, 192 | const char *func, 193 | const char *subs, 194 | unsigned int sev, 195 | const char *format, 196 | va_list args) 197 | { 198 | int saved_errno = errno; 199 | 200 | log_lock(); 201 | errno = saved_errno; 202 | log__submit(file, line, func, subs, sev, format, args); 203 | log_unlock(); 204 | 205 | errno = saved_errno; 206 | } 207 | 208 | void log_format(const char *file, 209 | int line, 210 | const char *func, 211 | const char *subs, 212 | unsigned int sev, 213 | const char *format, 214 | ...) 215 | { 216 | int saved_errno = errno; 217 | va_list list; 218 | 219 | va_start(list, format); 220 | log_lock(); 221 | errno = saved_errno; 222 | log__submit(file, line, func, subs, sev, format, list); 223 | log_unlock(); 224 | va_end(list); 225 | 226 | errno = saved_errno; 227 | } 228 | 229 | void log_llog(void *data, 230 | const char *file, 231 | int line, 232 | const char *func, 233 | const char *subs, 234 | unsigned int sev, 235 | const char *format, 236 | va_list args) 237 | { 238 | log_submit(file, line, func, subs, sev, format, args); 239 | } 240 | -------------------------------------------------------------------------------- /src/shared/shl_log.h: -------------------------------------------------------------------------------- 1 | /* 2 | * SHL - Log/Debug Interface 3 | * 4 | * Copyright (c) 2010-2013 David Herrmann 5 | * Dedicated to the Public Domain 6 | */ 7 | 8 | /* 9 | * Log/Debug Interface 10 | * This interface provides basic logging to stderr. 11 | * 12 | * Define BUILD_ENABLE_DEBUG before including this header to enable 13 | * debug-messages for this file. 14 | */ 15 | 16 | #ifndef SHL_LOG_H 17 | #define SHL_LOG_H 18 | 19 | #include 20 | #include 21 | #include 22 | #include 23 | 24 | enum log_severity { 25 | #ifndef LOG_FATAL 26 | LOG_FATAL = 0, 27 | #endif 28 | #ifndef LOG_ALERT 29 | LOG_ALERT = 1, 30 | #endif 31 | #ifndef LOG_CRITICAL 32 | LOG_CRITICAL = 2, 33 | #endif 34 | #ifndef LOG_ERROR 35 | LOG_ERROR = 3, 36 | #endif 37 | #ifndef LOG_WARNING 38 | LOG_WARNING = 4, 39 | #endif 40 | #ifndef LOG_NOTICE 41 | LOG_NOTICE = 5, 42 | #endif 43 | #ifndef LOG_INFO 44 | LOG_INFO = 6, 45 | #endif 46 | #ifndef LOG_DEBUG 47 | LOG_DEBUG = 7, 48 | #endif 49 | LOG_SEV_NUM, 50 | }; 51 | 52 | /* 53 | * Max Severity 54 | * Messages with severities between log_max_sev and LOG_SEV_NUM (exclusive) 55 | * are not logged, but discarded. 56 | * Default: LOG_NOTICE 57 | */ 58 | 59 | extern unsigned int log_max_sev; 60 | 61 | /* 62 | * Timestamping 63 | * Call this to initialize timestamps and cause all log-messages to be prefixed 64 | * with a timestamp. If not called, no timestamps are added. 65 | */ 66 | 67 | void log_init_time(void); 68 | 69 | /* 70 | * Log-Functions 71 | * These functions pass a log-message to the log-subsystem. Handy helpers are 72 | * provided below. You almost never use these directly. 73 | * 74 | * log_submit: 75 | * Submit the message to the log-subsystem. This is the backend of all other 76 | * loggers. 77 | * 78 | * log_format: 79 | * Same as log_submit but first converts the arguments into a va_list object. 80 | * 81 | * log_llog: 82 | * Same as log_submit but used as connection to llog. 83 | * 84 | * log_dummyf: 85 | * Dummy logger used for gcc var-arg validation. 86 | */ 87 | 88 | __attribute__((format(printf, 6, 0))) 89 | void log_submit(const char *file, 90 | int line, 91 | const char *func, 92 | const char *subs, 93 | unsigned int sev, 94 | const char *format, 95 | va_list args); 96 | 97 | __attribute__((format(printf, 6, 7))) 98 | void log_format(const char *file, 99 | int line, 100 | const char *func, 101 | const char *subs, 102 | unsigned int sev, 103 | const char *format, 104 | ...); 105 | 106 | __attribute__((format(printf, 7, 0))) 107 | void log_llog(void *data, 108 | const char *file, 109 | int line, 110 | const char *func, 111 | const char *subs, 112 | unsigned int sev, 113 | const char *format, 114 | va_list args); 115 | 116 | static inline __attribute__((format(printf, 2, 3))) 117 | void log_dummyf(unsigned int sev, const char *format, ...) 118 | { 119 | } 120 | 121 | /* 122 | * Default values 123 | * All helpers automatically pick-up the file, line, func and subsystem 124 | * parameters for a log-message. file, line and func are generated with 125 | * __FILE__, __LINE__ and __func__ and should almost never be replaced. 126 | * The subsystem is by default an empty string. To overwrite this, add this 127 | * line to the top of your source file: 128 | * #define LOG_SUBSYSTEM "mysubsystem" 129 | * Then all following log-messages will use this string as subsystem. You can 130 | * define it before or after including this header. 131 | * 132 | * If you want to change one of these, you need to directly use log_submit and 133 | * log_format. If you want the defaults for file, line and func you can use: 134 | * log_format(LOG_DEFAULT_BASE, subsys, sev, format, ...); 135 | * If you want all default values, use: 136 | * log_format(LOG_DEFAULT, sev, format, ...); 137 | * 138 | * If you want to change a single value, this is the default line that is used 139 | * internally. Adjust it to your needs: 140 | * log_format(__FILE__, __LINE__, __func__, LOG_SUBSYSTEM, LOG_ERROR, 141 | * "your format string: %s %d", "some args", 5, ...); 142 | * 143 | * log_printf is the same as log_format(LOG_DEFAULT, sev, format, ...) and is 144 | * the most basic wrapper that you can use. 145 | */ 146 | 147 | #ifndef LOG_SUBSYSTEM 148 | extern const char *LOG_SUBSYSTEM; 149 | #endif 150 | 151 | #define LOG_DEFAULT_BASE __FILE__, __LINE__, __func__ 152 | #define LOG_DEFAULT LOG_DEFAULT_BASE, LOG_SUBSYSTEM 153 | 154 | #define log_printf(sev, format, ...) \ 155 | log_format(LOG_DEFAULT, (sev), (format), ##__VA_ARGS__) 156 | 157 | /* 158 | * Helpers 159 | * These pick up all the default values and submit the message to the 160 | * log-subsystem. The log_debug() function produces zero-code if 161 | * BUILD_ENABLE_DEBUG is not defined. Therefore, it can be heavily used for 162 | * debugging and will not have any side-effects. 163 | * Even if disabled, parameters are evaluated! So it only produces zero code 164 | * if there are no side-effects and the compiler can optimized it away. 165 | */ 166 | 167 | #ifdef BUILD_ENABLE_DEBUG 168 | #define log_debug(format, ...) \ 169 | log_printf(LOG_DEBUG, (format), ##__VA_ARGS__) 170 | #else 171 | #define log_debug(format, ...) \ 172 | log_dummyf(LOG_DEBUG, (format), ##__VA_ARGS__) 173 | #endif 174 | 175 | #define log_info(format, ...) \ 176 | log_printf(LOG_INFO, (format), ##__VA_ARGS__) 177 | #define log_notice(format, ...) \ 178 | log_printf(LOG_NOTICE, (format), ##__VA_ARGS__) 179 | #define log_warning(format, ...) \ 180 | log_printf(LOG_WARNING, (format), ##__VA_ARGS__) 181 | #define log_error(format, ...) \ 182 | log_printf(LOG_ERROR, (format), ##__VA_ARGS__) 183 | #define log_critical(format, ...) \ 184 | log_printf(LOG_CRITICAL, (format), ##__VA_ARGS__) 185 | #define log_alert(format, ...) \ 186 | log_printf(LOG_ALERT, (format), ##__VA_ARGS__) 187 | #define log_fatal(format, ...) \ 188 | log_printf(LOG_FATAL, (format), ##__VA_ARGS__) 189 | 190 | #define log_EINVAL() \ 191 | (log_error("invalid arguments"), -EINVAL) 192 | #define log_vEINVAL() \ 193 | ((void)log_EINVAL()) 194 | 195 | #define log_EFAULT() \ 196 | (log_error("internal operation failed"), -EFAULT) 197 | #define log_vEFAULT() \ 198 | ((void)log_EFAULT()) 199 | 200 | #define log_ENOMEM() \ 201 | (log_error("out of memory"), -ENOMEM) 202 | #define log_vENOMEM() \ 203 | ((void)log_ENOMEM()) 204 | 205 | #define log_EPIPE() \ 206 | (log_error("fd closed unexpectedly"), -EPIPE) 207 | #define log_vEPIPE() \ 208 | ((void)log_EPIPE()) 209 | 210 | #define log_ERRNO() \ 211 | (log_error("syscall failed (%d): %m", errno), -errno) 212 | #define log_vERRNO() \ 213 | ((void)log_ERRNO()) 214 | 215 | #define log_ERR(_r) \ 216 | (errno = -(_r), log_error("syscall failed (%d): %m", (_r)), (_r)) 217 | #define log_vERR(_r) \ 218 | ((void)log_ERR(_r)) 219 | 220 | #endif /* SHL_LOG_H */ 221 | -------------------------------------------------------------------------------- /src/shared/shl_macro.h: -------------------------------------------------------------------------------- 1 | /* 2 | * SHL - Macros 3 | * 4 | * Copyright (c) 2011-2013 David Herrmann 5 | * Dedicated to the Public Domain 6 | */ 7 | 8 | /* 9 | * Macros 10 | */ 11 | 12 | #ifndef SHL_MACRO_H 13 | #define SHL_MACRO_H 14 | 15 | #include 16 | #include 17 | #include 18 | #include 19 | #include 20 | #include 21 | 22 | /* sanity checks required for some macros */ 23 | #if __SIZEOF_POINTER__ != 4 && __SIZEOF_POINTER__ != 8 24 | #error "Pointer size is neither 4 nor 8 bytes" 25 | #endif 26 | 27 | /* gcc attributes; look them up for more information */ 28 | #define _shl_printf_(_a, _b) __attribute__((__format__(printf, _a, _b))) 29 | #define _shl_alloc_(...) __attribute__((__alloc_size__(__VA_ARGS__))) 30 | #define _shl_sentinel_ __attribute__((__sentinel__)) 31 | #define _shl_noreturn_ __attribute__((__noreturn__)) 32 | #define _shl_unused_ __attribute__((__unused__)) 33 | #define _shl_pure_ __attribute__((__pure__)) 34 | #define _shl_const_ __attribute__((__const__)) 35 | #define _shl_deprecated_ __attribute__((__deprecated__)) 36 | #define _shl_packed_ __attribute__((__packed__)) 37 | #define _shl_malloc_ __attribute__((__malloc__)) 38 | #define _shl_weak_ __attribute__((__weak__)) 39 | #define _shl_likely_(_val) (__builtin_expect(!!(_val), 1)) 40 | #define _shl_unlikely_(_val) (__builtin_expect(!!(_val), 0)) 41 | #define _shl_public_ __attribute__((__visibility__("default"))) 42 | #define _shl_hidden_ __attribute__((__visibility__("hidden"))) 43 | #define _shl_weakref_(_val) __attribute__((__weakref__(#_val))) 44 | #define _shl_cleanup_(_val) __attribute__((__cleanup__(_val))) 45 | 46 | static inline void shl_freep(void *p) 47 | { 48 | free(*(void**)p); 49 | } 50 | 51 | #define _shl_free_ _shl_cleanup_(shl_freep) 52 | 53 | static inline void shl_set_errno(int *r) 54 | { 55 | errno = *r; 56 | } 57 | 58 | #define SHL_PROTECT_ERRNO \ 59 | _shl_cleanup_(shl_set_errno) _shl_unused_ int shl__errno = errno 60 | 61 | /* 2-level stringify helper */ 62 | #define SHL__STRINGIFY(_val) #_val 63 | #define SHL_STRINGIFY(_val) SHL__STRINGIFY(_val) 64 | 65 | /* 2-level concatenate helper */ 66 | #define SHL__CONCATENATE(_a, _b) _a ## _b 67 | #define SHL_CONCATENATE(_a, _b) SHL__CONCATENATE(_a, _b) 68 | 69 | /* unique identifier with prefix */ 70 | #define SHL_UNIQUE(_prefix) SHL_CONCATENATE(_prefix, __COUNTER__) 71 | 72 | /* array element count */ 73 | #define SHL_ARRAY_LENGTH(_array) (sizeof(_array)/sizeof(*(_array))) 74 | 75 | /* get parent pointer by container-type, member and member-pointer */ 76 | #define shl_container_of(_ptr, _type, _member) \ 77 | ({ \ 78 | const typeof( ((_type *)0)->_member ) *__mptr = (_ptr); \ 79 | (_type *)( (char *)__mptr - offsetof(_type, _member) ); \ 80 | }) 81 | 82 | /* return maximum of two values and do strict type checking */ 83 | #define shl_max(_a, _b) \ 84 | ({ \ 85 | typeof(_a) __a = (_a); \ 86 | typeof(_b) __b = (_b); \ 87 | (void) (&__a == &__b); \ 88 | __a > __b ? __a : __b; \ 89 | }) 90 | 91 | /* same as shl_max() but perform explicit cast beforehand */ 92 | #define shl_max_t(_type, _a, _b) \ 93 | ({ \ 94 | _type __a = (_type)(_a); \ 95 | _type __b = (_type)(_b); \ 96 | __a > __b ? __a : __b; \ 97 | }) 98 | 99 | /* return minimum of two values and do strict type checking */ 100 | #define shl_min(_a, _b) \ 101 | ({ \ 102 | typeof(_a) __a = (_a); \ 103 | typeof(_b) __b = (_b); \ 104 | (void) (&__a == &__b); \ 105 | __a < __b ? __a : __b; \ 106 | }) 107 | 108 | /* same as shl_min() but perform explicit cast beforehand */ 109 | #define shl_min_t(_type, _a, _b) \ 110 | ({ \ 111 | _type __a = (_type)(_a); \ 112 | _type __b = (_type)(_b); \ 113 | __a < __b ? __a : __b; \ 114 | }) 115 | 116 | /* clamp value between low and high barriers */ 117 | #define shl_clamp(_val, _low, _high) \ 118 | ({ \ 119 | typeof(_val) __v = (_val); \ 120 | typeof(_low) __l = (_low); \ 121 | typeof(_high) __h = (_high); \ 122 | (void) (&__v == &__l); \ 123 | (void) (&__v == &__h); \ 124 | ((__v > __h) ? __h : ((__v < __l) ? __l : __v)); \ 125 | }) 126 | 127 | /* align to next higher power-of-2 (except for: 0 => 0, overflow => 0) */ 128 | static inline size_t SHL_ALIGN_POWER2(size_t u) 129 | { 130 | return 1ULL << ((sizeof(u) * 8ULL) - __builtin_clzll(u - 1ULL)); 131 | } 132 | 133 | /* zero memory or type */ 134 | #define shl_memzero(_ptr, _size) (memset((_ptr), 0, (_size))) 135 | #define shl_zero(_ptr) (shl_memzero(&(_ptr), sizeof(_ptr))) 136 | 137 | /* ptr <=> uint casts */ 138 | #define SHL_PTR_TO_TYPE(_type, _ptr) ((_type)((uintptr_t)(_ptr))) 139 | #define SHL_TYPE_TO_PTR(_type, _int) ((void*)((uintptr_t)(_int))) 140 | #define SHL_PTR_TO_INT(_ptr) SHL_PTR_TO_TYPE(int, (_ptr)) 141 | #define SHL_INT_TO_PTR(_ptr) SHL_TYPE_TO_PTR(int, (_ptr)) 142 | #define SHL_PTR_TO_UINT(_ptr) SHL_PTR_TO_TYPE(unsigned int, (_ptr)) 143 | #define SHL_UINT_TO_PTR(_ptr) SHL_TYPE_TO_PTR(unsigned int, (_ptr)) 144 | #define SHL_PTR_TO_LONG(_ptr) SHL_PTR_TO_TYPE(long, (_ptr)) 145 | #define SHL_LONG_TO_PTR(_ptr) SHL_TYPE_TO_PTR(long, (_ptr)) 146 | #define SHL_PTR_TO_ULONG(_ptr) SHL_PTR_TO_TYPE(unsigned long, (_ptr)) 147 | #define SHL_ULONG_TO_PTR(_ptr) SHL_TYPE_TO_PTR(unsigned long, (_ptr)) 148 | #define SHL_PTR_TO_S32(_ptr) SHL_PTR_TO_TYPE(int32_t, (_ptr)) 149 | #define SHL_S32_TO_PTR(_ptr) SHL_TYPE_TO_PTR(int32_t, (_ptr)) 150 | #define SHL_PTR_TO_U32(_ptr) SHL_PTR_TO_TYPE(uint32_t, (_ptr)) 151 | #define SHL_U32_TO_PTR(_ptr) SHL_TYPE_TO_PTR(uint32_t, (_ptr)) 152 | #define SHL_PTR_TO_S64(_ptr) SHL_PTR_TO_TYPE(int64_t, (_ptr)) 153 | #define SHL_S64_TO_PTR(_ptr) SHL_TYPE_TO_PTR(int64_t, (_ptr)) 154 | #define SHL_PTR_TO_U64(_ptr) SHL_PTR_TO_TYPE(uint64_t, (_ptr)) 155 | #define SHL_U64_TO_PTR(_ptr) SHL_TYPE_TO_PTR(uint64_t, (_ptr)) 156 | 157 | /* compile-time assertions */ 158 | #define shl_assert_cc(_expr) static_assert(_expr, #_expr) 159 | 160 | /* 161 | * Safe Multiplications 162 | * Multiplications are subject to overflows. These helpers guarantee that the 163 | * multiplication can be done safely and return -ERANGE if not. 164 | * 165 | * Note: This is horribly slow for ull/uint64_t as we need a division to test 166 | * for overflows. Take that into account when using these. For smaller integers, 167 | * we can simply use an upcast-multiplication which gcc should be smart enough 168 | * to optimize. 169 | */ 170 | 171 | #define SHL__REAL_MULT(_max, _val, _factor) \ 172 | ({ \ 173 | (_factor == 0 || *(_val) <= (_max) / (_factor)) ? \ 174 | ((*(_val) *= (_factor)), 0) : \ 175 | -ERANGE; \ 176 | }) 177 | 178 | #define SHL__UPCAST_MULT(_type, _max, _val, _factor) \ 179 | ({ \ 180 | _type v = *(_val) * (_type)(_factor); \ 181 | (v <= (_max)) ? \ 182 | ((*(_val) = v), 0) : \ 183 | -ERANGE; \ 184 | }) 185 | 186 | static inline int shl_mult_ull(unsigned long long *val, 187 | unsigned long long factor) 188 | { 189 | return SHL__REAL_MULT(ULLONG_MAX, val, factor); 190 | } 191 | 192 | static inline int shl_mult_ul(unsigned long *val, unsigned long factor) 193 | { 194 | #if ULONG_MAX < ULLONG_MAX 195 | return SHL__UPCAST_MULT(unsigned long long, ULONG_MAX, val, factor); 196 | #else 197 | shl_assert_cc(sizeof(unsigned long) == sizeof(unsigned long long)); 198 | return shl_mult_ull((unsigned long long*)val, factor); 199 | #endif 200 | } 201 | 202 | static inline int shl_mult_u(unsigned int *val, unsigned int factor) 203 | { 204 | #if UINT_MAX < ULONG_MAX 205 | return SHL__UPCAST_MULT(unsigned long, UINT_MAX, val, factor); 206 | #elif UINT_MAX < ULLONG_MAX 207 | return SHL__UPCAST_MULT(unsigned long long, UINT_MAX, val, factor); 208 | #else 209 | shl_assert_cc(sizeof(unsigned int) == sizeof(unsigned long long)); 210 | return shl_mult_ull(val, factor); 211 | #endif 212 | } 213 | 214 | static inline int shl_mult_u64(uint64_t *val, uint64_t factor) 215 | { 216 | return SHL__REAL_MULT(UINT64_MAX, val, factor); 217 | } 218 | 219 | static inline int shl_mult_u32(uint32_t *val, uint32_t factor) 220 | { 221 | return SHL__UPCAST_MULT(uint_fast64_t, UINT32_MAX, val, factor); 222 | } 223 | 224 | static inline int shl_mult_u16(uint16_t *val, uint16_t factor) 225 | { 226 | return SHL__UPCAST_MULT(uint_fast32_t, UINT16_MAX, val, factor); 227 | } 228 | 229 | static inline int shl_mult_u8(uint8_t *val, uint8_t factor) 230 | { 231 | return SHL__UPCAST_MULT(uint_fast16_t, UINT8_MAX, val, factor); 232 | } 233 | 234 | #endif /* SHL_MACRO_H */ 235 | -------------------------------------------------------------------------------- /src/shared/shl_util.h: -------------------------------------------------------------------------------- 1 | /* 2 | * SHL - Utility Helpers 3 | * 4 | * Copyright (c) 2011-2013 David Herrmann 5 | * Dedicated to the Public Domain 6 | */ 7 | 8 | /* 9 | * Utility Helpers 10 | */ 11 | 12 | #ifndef SHL_UTIL_H 13 | #define SHL_UTIL_H 14 | 15 | #include 16 | #include 17 | #include 18 | #include 19 | #include 20 | #include 21 | #include "shl_macro.h" 22 | 23 | /* strict atoi */ 24 | 25 | int shl_ctoi(char ch, unsigned int base); 26 | 27 | int shl_atoi_ulln(const char *str, 28 | size_t len, 29 | unsigned int base, 30 | const char **next, 31 | unsigned long long *out); 32 | int shl_atoi_uln(const char *str, 33 | size_t len, 34 | unsigned int base, 35 | const char **next, 36 | unsigned long *out); 37 | int shl_atoi_un(const char *str, 38 | size_t len, 39 | unsigned int base, 40 | const char **next, 41 | unsigned int *out); 42 | int shl_atoi_zn(const char *str, 43 | size_t len, 44 | unsigned int base, 45 | const char **next, 46 | size_t *out); 47 | 48 | static inline int shl_atoi_ull(const char *str, 49 | unsigned int base, 50 | const char **next, 51 | unsigned long long *out) 52 | { 53 | return shl_atoi_ulln(str, strlen(str), base, next, out); 54 | } 55 | 56 | static inline int shl_atoi_ul(const char *str, 57 | unsigned int base, 58 | const char **next, 59 | unsigned long *out) 60 | { 61 | return shl_atoi_uln(str, strlen(str), base, next, out); 62 | } 63 | 64 | static inline int shl_atoi_u(const char *str, 65 | unsigned int base, 66 | const char **next, 67 | unsigned int *out) 68 | { 69 | return shl_atoi_un(str, strlen(str), base, next, out); 70 | } 71 | 72 | static inline int shl_atoi_z(const char *str, 73 | unsigned int base, 74 | const char **next, 75 | size_t *out) 76 | { 77 | return shl_atoi_zn(str, strlen(str), base, next, out); 78 | } 79 | 80 | /* greedy alloc */ 81 | 82 | void *shl_greedy_realloc(void **mem, size_t *size, size_t need); 83 | void *shl_greedy_realloc0(void **mem, size_t *size, size_t need); 84 | void *shl_greedy_realloc_t(void **arr, size_t *cnt, size_t need, size_t ts); 85 | void *shl_greedy_realloc0_t(void **arr, size_t *cnt, size_t need, size_t ts); 86 | 87 | #define SHL_GREEDY_REALLOC_T(array, count, need) \ 88 | shl_greedy_realloc_t((void**)&(array), &count, need, sizeof(*(array))) 89 | #define SHL_GREEDY_REALLOC0_T(array, count, need) \ 90 | shl_greedy_realloc0_t((void**)&(array), &count, need, sizeof(*(array))) 91 | 92 | /* string helpers */ 93 | 94 | char *shl_strcat(const char *first, const char *second); 95 | _shl_sentinel_ char *shl_strjoin(const char *first, ...); 96 | int shl_strsplit_n(const char *str, size_t len, const char *sep, char ***out); 97 | int shl_strsplit(const char *str, const char *sep, char ***out); 98 | 99 | static inline bool shl_isempty(const char *str) 100 | { 101 | return !str || !*str; 102 | } 103 | 104 | static inline char *shl_startswith(const char *str, const char *prefix) 105 | { 106 | if (!strncmp(str, prefix, strlen(prefix))) 107 | return (char*)str + strlen(prefix); 108 | else 109 | return NULL; 110 | } 111 | 112 | /* strv */ 113 | 114 | void shl_strv_free(char **strv); 115 | 116 | static inline void shl_strv_freep(char ***strv) 117 | { 118 | shl_strv_free(*strv); 119 | } 120 | 121 | #define _shl_strv_free_ _shl_cleanup_(shl_strv_freep) 122 | 123 | /* quoted strings */ 124 | 125 | char shl_qstr_unescape_char(char c); 126 | void shl_qstr_decode_n(char *str, size_t length); 127 | int shl_qstr_tokenize_n(const char *str, size_t length, char ***out); 128 | int shl_qstr_tokenize(const char *str, char ***out); 129 | int shl_qstr_join(char **strv, char **out); 130 | 131 | /* mkdir */ 132 | 133 | int shl_mkdir_p(const char *path, mode_t mode); 134 | int shl_mkdir_p_prefix(const char *prefix, const char *path, mode_t mode); 135 | 136 | /* time */ 137 | 138 | uint64_t shl_now(clockid_t clock); 139 | 140 | /* ratelimit */ 141 | 142 | struct shl_ratelimit { 143 | uint64_t interval; 144 | uint64_t begin; 145 | unsigned burst; 146 | unsigned num; 147 | }; 148 | 149 | #define SHL_RATELIMIT_DEFINE(_name, _interval, _burst) \ 150 | struct shl_ratelimit _name = { (_interval), (_burst), 0, 0 } 151 | 152 | #define SHL_RATELIMIT_INIT(_v, _interval, _burst) do { \ 153 | struct shl_ratelimit *_r = &(_v); \ 154 | _r->interval = (_interval); \ 155 | _r->burst = (_burst); \ 156 | _r->num = 0; \ 157 | _r->begin = 0; \ 158 | } while (false) 159 | 160 | #define SHL_RATELIMIT_RESET(_v) do { \ 161 | struct shl_ratelimit *_r = &(_v); \ 162 | _r->num = 0; \ 163 | _r->begin = 0; \ 164 | } while (false) 165 | 166 | bool shl_ratelimit_test(struct shl_ratelimit *r); 167 | 168 | #endif /* SHL_UTIL_H */ 169 | -------------------------------------------------------------------------------- /src/shared/util.h: -------------------------------------------------------------------------------- 1 | /* 2 | * MiracleCast - Wifi-Display/Miracast Implementation 3 | * 4 | * Copyright (c) 2013-2014 David Herrmann 5 | * 6 | * MiracleCast is free software; you can redistribute it and/or modify it 7 | * under the terms of the GNU Lesser General Public License as published by 8 | * the Free Software Foundation; either version 2.1 of the License, or 9 | * (at your option) any later version. 10 | * 11 | * MiracleCast is distributed in the hope that it will be useful, but 12 | * WITHOUT ANY WARRANTY; without even the implied warranty of 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 14 | * Lesser General Public License for more details. 15 | * 16 | * You should have received a copy of the GNU Lesser General Public License 17 | * along with MiracleCast; If not, see . 18 | */ 19 | 20 | #ifndef MIRACLE_UTIL_H 21 | #define MIRACLE_UTIL_H 22 | 23 | #include 24 | #include 25 | #include 26 | #include 27 | #include 28 | #include 29 | #include 30 | #include 31 | #include 32 | #include "shl_macro.h" 33 | 34 | static inline void cleanup_sd_bus_message(sd_bus_message **ptr) 35 | { 36 | sd_bus_message_unref(*ptr); 37 | } 38 | 39 | static inline void cleanup_udev_device(struct udev_device **ptr) 40 | { 41 | udev_device_unref(*ptr); 42 | } 43 | 44 | static inline void cleanup_udev_enumerate(struct udev_enumerate **ptr) 45 | { 46 | udev_enumerate_unref(*ptr); 47 | } 48 | 49 | #define _cleanup_sd_bus_error_ _shl_cleanup_(sd_bus_error_free) 50 | #define _sd_bus_error_free_ _shl_cleanup_(sd_bus_error_free) 51 | #define _cleanup_sd_bus_message_ _shl_cleanup_(cleanup_sd_bus_message) 52 | #define _sd_bus_message_unref_ _shl_cleanup_(cleanup_sd_bus_message) 53 | #define _cleanup_udev_device_ _shl_cleanup_(cleanup_udev_device) 54 | #define _cleanup_udev_enumerate_ _shl_cleanup_(cleanup_udev_enumerate) 55 | 56 | #define strv_from_stdarg_alloca(first) ({ \ 57 | char **_l; \ 58 | if (!first) { \ 59 | _l = (char**)&first; \ 60 | } else { \ 61 | unsigned _n; \ 62 | va_list _ap; \ 63 | _n = 1; \ 64 | va_start(_ap, first); \ 65 | while (va_arg(_ap, char*)) \ 66 | _n++; \ 67 | va_end(_ap); \ 68 | _l = alloca(sizeof(char*) * (_n + 1)); \ 69 | _l[_n = 0] = (char*)first; \ 70 | va_start(_ap, first); \ 71 | for (;;) { \ 72 | _l[++_n] = va_arg(_ap, char*); \ 73 | if (!_l[_n]) \ 74 | break; \ 75 | } \ 76 | va_end(_ap); \ 77 | } \ 78 | _l; \ 79 | }) 80 | 81 | static inline unsigned int ifindex_from_udev_device(struct udev_device *d) 82 | { 83 | const char *val; 84 | 85 | val = udev_device_get_property_value(d, "IFINDEX"); 86 | if (!val) 87 | return 0; 88 | 89 | return (unsigned int)atoi(val); 90 | } 91 | 92 | static inline int64_t now(clockid_t clock_id) 93 | { 94 | struct timespec ts; 95 | 96 | clock_gettime(clock_id, &ts); 97 | 98 | return (int64_t)ts.tv_sec * 1000000LL + 99 | (int64_t)ts.tv_nsec / 1000LL; 100 | } 101 | 102 | #define MAC_STRLEN 18 103 | 104 | static inline void reformat_mac(char *dst, const char *src) 105 | { 106 | unsigned char x1 = 0, x2 = 0, x3 = 0, x4 = 0, x5 = 0, x6 = 0; 107 | 108 | sscanf(src, "%2hhx:%2hhx:%2hhx:%2hhx:%2hhx:%2hhx", 109 | &x1, &x2, &x3, &x4, &x5, &x6); 110 | sprintf(dst, "%2hhx:%2hhx:%2hhx:%2hhx:%2hhx:%2hhx", 111 | x1, x2, x3, x4, x5, x6); 112 | } 113 | 114 | static inline const char *bus_error_message(const sd_bus_error *e, int error) 115 | { 116 | if (e) { 117 | if (sd_bus_error_has_name(e, SD_BUS_ERROR_ACCESS_DENIED)) 118 | return "Access denied"; 119 | if (e->message) 120 | return e->message; 121 | } 122 | 123 | return strerror(error < 0 ? -error : error); 124 | } 125 | 126 | static inline int bus_message_read_basic_variant(sd_bus_message *m, 127 | const char *sig, 128 | void *ptr) 129 | { 130 | int r; 131 | 132 | if (!sig || !*sig || sig[1] || !ptr) 133 | return -EINVAL; 134 | 135 | r = sd_bus_message_enter_container(m, 'v', sig); 136 | if (r < 0) 137 | return r; 138 | 139 | r = sd_bus_message_read(m, sig, ptr); 140 | if (r < 0) 141 | return r; 142 | 143 | r = sd_bus_message_exit_container(m); 144 | if (r < 0) 145 | return r; 146 | 147 | return 0; 148 | } 149 | 150 | #endif /* MIRACLE_UTIL_H */ 151 | -------------------------------------------------------------------------------- /src/shared/wpas.h: -------------------------------------------------------------------------------- 1 | /* 2 | * MiracleCast - Wifi-Display/Miracast Implementation 3 | * 4 | * Copyright (c) 2013-2014 David Herrmann 5 | * 6 | * MiracleCast is free software; you can redistribute it and/or modify it 7 | * under the terms of the GNU Lesser General Public License as published by 8 | * the Free Software Foundation; either version 2.1 of the License, or 9 | * (at your option) any later version. 10 | * 11 | * MiracleCast is distributed in the hope that it will be useful, but 12 | * WITHOUT ANY WARRANTY; without even the implied warranty of 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 14 | * Lesser General Public License for more details. 15 | * 16 | * You should have received a copy of the GNU Lesser General Public License 17 | * along with MiracleCast; If not, see . 18 | */ 19 | 20 | #ifndef MIRACLE_WPAS_H 21 | #define MIRACLE_WPAS_H 22 | 23 | #include 24 | #include 25 | #include 26 | #include 27 | #include 28 | 29 | /* types */ 30 | 31 | struct wpas; 32 | struct wpas_message; 33 | 34 | typedef int (*wpas_callback_fn) (struct wpas *w, 35 | struct wpas_message *m, 36 | void *data); 37 | 38 | enum { 39 | WPAS_MESSAGE_UNKNOWN, 40 | WPAS_MESSAGE_EVENT, 41 | WPAS_MESSAGE_REQUEST, 42 | WPAS_MESSAGE_REPLY, 43 | WPAS_TYPE_CNT, 44 | }; 45 | 46 | enum { 47 | WPAS_LEVEL_UNKNOWN, 48 | WPAS_LEVEL_MSGDUMP, 49 | WPAS_LEVEL_DEBUG, 50 | WPAS_LEVEL_INFO, 51 | WPAS_LEVEL_WARNING, 52 | WPAS_LEVEL_ERROR, 53 | WPAS_LEVEL_CNT 54 | }; 55 | 56 | #define WPAS_TYPE_STRING 's' 57 | #define WPAS_TYPE_INT32 'i' 58 | #define WPAS_TYPE_UINT32 'u' 59 | #define WPAS_TYPE_DICT 'e' 60 | 61 | /* bus */ 62 | 63 | int wpas_open(const char *ctrl_path, struct wpas **out); 64 | int wpas_create(const char *ctrl_path, struct wpas **out); 65 | void wpas_ref(struct wpas *w); 66 | void wpas_unref(struct wpas *w); 67 | 68 | int wpas_call_async(struct wpas *w, 69 | struct wpas_message *m, 70 | wpas_callback_fn cb_fn, 71 | void *data, 72 | uint64_t timeout, 73 | uint64_t *cookie); 74 | void wpas_call_async_cancel(struct wpas *w, uint64_t cookie); 75 | int wpas_send(struct wpas *w, 76 | struct wpas_message *m, 77 | uint64_t timeout); 78 | 79 | int wpas_attach_event(struct wpas *w, sd_event *event, int priority); 80 | void wpas_detach_event(struct wpas *w); 81 | 82 | int wpas_add_match(struct wpas *w, wpas_callback_fn cb_fn, void *data); 83 | void wpas_remove_match(struct wpas *w, wpas_callback_fn cb_fn, void *data); 84 | 85 | bool wpas_is_dead(struct wpas *w); 86 | bool wpas_is_server(struct wpas *w); 87 | 88 | static inline void wpas_unref_p(struct wpas **w) 89 | { 90 | wpas_unref(*w); 91 | } 92 | 93 | #define _wpas_unref_ __attribute__((__cleanup__(wpas_unref_p))) 94 | 95 | /* messages */ 96 | 97 | int wpas_message_new_event(struct wpas *w, 98 | const char *name, 99 | unsigned int level, 100 | struct wpas_message **out); 101 | int wpas_message_new_request(struct wpas *w, 102 | const char *name, 103 | struct wpas_message **out); 104 | int wpas_message_new_reply(struct wpas *w, 105 | struct wpas_message **out); 106 | int wpas_message_new_reply_for(struct wpas *w, 107 | struct wpas_message *request, 108 | struct wpas_message **out); 109 | void wpas_message_ref(struct wpas_message *m); 110 | void wpas_message_unref(struct wpas_message *m); 111 | 112 | bool wpas_message_is_event(struct wpas_message *msg, const char *name); 113 | bool wpas_message_is_request(struct wpas_message *msg, const char *name); 114 | bool wpas_message_is_reply(struct wpas_message *msg); 115 | bool wpas_message_is_ok(struct wpas_message *msg); 116 | bool wpas_message_is_fail(struct wpas_message *msg); 117 | 118 | uint64_t wpas_message_get_cookie(struct wpas_message *msg); 119 | struct wpas *wpas_message_get_bus(struct wpas_message *msg); 120 | unsigned int wpas_message_get_type(struct wpas_message *msg); 121 | unsigned int wpas_message_get_level(struct wpas_message *msg); 122 | const char *wpas_message_get_name(struct wpas_message *msg); 123 | const char *wpas_message_get_raw(struct wpas_message *msg); 124 | const char *wpas_message_get_ifname(struct wpas_message *msg); 125 | bool wpas_message_is_sealed(struct wpas_message *msg); 126 | 127 | const char *wpas_message_get_peer(struct wpas_message *msg); 128 | char *wpas_message_get_escaped_peer(struct wpas_message *msg); 129 | void wpas_message_set_peer(struct wpas_message *msg, const char *peer); 130 | 131 | int wpas_message_append_basic(struct wpas_message *m, char type, ...); 132 | int wpas_message_appendv_basic(struct wpas_message *m, 133 | char type, 134 | va_list args); 135 | int wpas_message_append(struct wpas_message *m, const char *types, ...); 136 | int wpas_message_appendv(struct wpas_message *m, 137 | const char *types, 138 | va_list args); 139 | int wpas_message_seal(struct wpas_message *m); 140 | 141 | int wpas_message_read_basic(struct wpas_message *m, char type, void *out); 142 | int wpas_message_read(struct wpas_message *m, const char *types, ...); 143 | int wpas_message_skip_basic(struct wpas_message *m, char type); 144 | int wpas_message_skip(struct wpas_message *m, const char *types); 145 | void wpas_message_rewind(struct wpas_message *m); 146 | 147 | int wpas_message_argv_read(struct wpas_message *m, 148 | unsigned int pos, 149 | char type, 150 | void *out); 151 | int wpas_message_dict_read(struct wpas_message *m, 152 | const char *name, 153 | char type, 154 | void *out); 155 | 156 | static inline void wpas_message_unref_p(struct wpas_message **m) 157 | { 158 | wpas_message_unref(*m); 159 | } 160 | 161 | #define _wpas_message_unref_ __attribute__((__cleanup__(wpas_message_unref_p))) 162 | 163 | #endif /* MIRACLE_WPAS_H */ 164 | -------------------------------------------------------------------------------- /src/wifi/wifid-link.c: -------------------------------------------------------------------------------- 1 | /* 2 | * MiracleCast - Wifi-Display/Miracast Implementation 3 | * 4 | * Copyright (c) 2013-2014 David Herrmann 5 | * 6 | * MiracleCast is free software; you can redistribute it and/or modify it 7 | * under the terms of the GNU Lesser General Public License as published by 8 | * the Free Software Foundation; either version 2.1 of the License, or 9 | * (at your option) any later version. 10 | * 11 | * MiracleCast is distributed in the hope that it will be useful, but 12 | * WITHOUT ANY WARRANTY; without even the implied warranty of 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 14 | * Lesser General Public License for more details. 15 | * 16 | * You should have received a copy of the GNU Lesser General Public License 17 | * along with MiracleCast; If not, see . 18 | */ 19 | 20 | #define LOG_SUBSYSTEM "link" 21 | 22 | #include 23 | #include 24 | #include 25 | #include 26 | #include "shl_dlist.h" 27 | #include "shl_log.h" 28 | #include "shl_util.h" 29 | #include "util.h" 30 | #include "wifid.h" 31 | 32 | /* 33 | * Link Handling 34 | */ 35 | 36 | struct peer *link_find_peer(struct link *l, const char *p2p_mac) 37 | { 38 | char **elem; 39 | bool res; 40 | 41 | res = shl_htable_lookup_str(&l->peers, p2p_mac, NULL, &elem); 42 | if (!res) 43 | return NULL; 44 | 45 | return peer_from_htable(elem); 46 | } 47 | 48 | struct peer *link_find_peer_by_label(struct link *l, const char *label) 49 | { 50 | char mac[MAC_STRLEN]; 51 | 52 | reformat_mac(mac, label); 53 | 54 | return link_find_peer(l, mac); 55 | } 56 | 57 | int link_new(struct manager *m, 58 | unsigned int ifindex, 59 | const char *ifname, 60 | struct link **out) 61 | { 62 | struct link *l; 63 | int r; 64 | 65 | if (!m || !ifindex || !ifname) 66 | return log_EINVAL(); 67 | 68 | if (shl_htable_lookup_uint(&m->links, ifindex, NULL)) 69 | return -EALREADY; 70 | 71 | log_debug("new link: %s (%u)", ifname, ifindex); 72 | 73 | l = calloc(1, sizeof(*l)); 74 | if (!l) 75 | return log_ENOMEM(); 76 | 77 | l->m = m; 78 | l->ifindex = ifindex; 79 | shl_htable_init_str(&l->peers); 80 | 81 | l->ifname = strdup(ifname); 82 | if (!l->ifname) { 83 | r = log_ENOMEM(); 84 | goto error; 85 | } 86 | 87 | r = supplicant_new(l, &l->s); 88 | if (r < 0) 89 | goto error; 90 | 91 | r = shl_htable_insert_uint(&m->links, &l->ifindex); 92 | if (r < 0) { 93 | log_vERR(r); 94 | goto error; 95 | } 96 | 97 | ++m->link_cnt; 98 | log_info("add link: %s", l->ifname); 99 | 100 | if (out) 101 | *out = l; 102 | 103 | return 0; 104 | 105 | error: 106 | link_free(l); 107 | return r; 108 | } 109 | 110 | void link_free(struct link *l) 111 | { 112 | if (!l) 113 | return; 114 | 115 | log_debug("free link: %s (%u)", l->ifname, l->ifindex); 116 | 117 | link_set_managed(l, false); 118 | 119 | if (shl_htable_remove_uint(&l->m->links, l->ifindex, NULL)) { 120 | log_info("remove link: %s", l->ifname); 121 | --l->m->link_cnt; 122 | } 123 | 124 | supplicant_free(l->s); 125 | 126 | /* link_set_managed(l, false) already removed all peers */ 127 | shl_htable_clear_str(&l->peers, NULL, NULL); 128 | 129 | free(l->friendly_name); 130 | free(l->ifname); 131 | free(l); 132 | } 133 | 134 | void link_set_managed(struct link *l, bool set) 135 | { 136 | int r; 137 | 138 | if (!l) 139 | return log_vEINVAL(); 140 | if (l->managed == set) 141 | return; 142 | 143 | if (set) { 144 | log_info("manage link %s", l->ifname); 145 | 146 | r = supplicant_start(l->s); 147 | if (r < 0) { 148 | log_error("cannot start supplicant on %s", l->ifname); 149 | return; 150 | } 151 | } else { 152 | log_info("link %s no longer managed", l->ifname); 153 | supplicant_stop(l->s); 154 | } 155 | 156 | l->managed = set; 157 | } 158 | 159 | int link_renamed(struct link *l, const char *ifname) 160 | { 161 | char *t; 162 | 163 | if (!l || !ifname) 164 | return log_EINVAL(); 165 | if (!strcmp(l->ifname, ifname)) 166 | return 0; 167 | 168 | log_info("link %s (%u) was renamed to %s", 169 | l->ifname, l->ifindex, ifname); 170 | 171 | t = strdup(ifname); 172 | if (!t) 173 | return log_ENOMEM(); 174 | 175 | free(l->ifname); 176 | l->ifname = t; 177 | 178 | link_dbus_properties_changed(l, "InterfaceName", NULL); 179 | 180 | return 0; 181 | } 182 | 183 | int link_set_friendly_name(struct link *l, const char *name) 184 | { 185 | char *t; 186 | int r; 187 | 188 | if (!l || !name || !*name) 189 | return log_EINVAL(); 190 | 191 | t = strdup(name); 192 | if (!t) 193 | return log_ENOMEM(); 194 | 195 | if (supplicant_is_ready(l->s)) { 196 | r = supplicant_set_friendly_name(l->s, name); 197 | if (r < 0) { 198 | free(t); 199 | return r; 200 | } 201 | } 202 | 203 | free(l->friendly_name); 204 | l->friendly_name = t; 205 | link_dbus_properties_changed(l, "FriendlyName", NULL); 206 | 207 | return 0; 208 | } 209 | 210 | const char *link_get_friendly_name(struct link *l) 211 | { 212 | if (!l) 213 | return NULL; 214 | 215 | return l->friendly_name; 216 | } 217 | 218 | int link_set_p2p_scanning(struct link *l, bool set) 219 | { 220 | if (!l) 221 | return log_EINVAL(); 222 | 223 | if (set) { 224 | return supplicant_p2p_start_scan(l->s); 225 | } else { 226 | supplicant_p2p_stop_scan(l->s); 227 | return 0; 228 | } 229 | } 230 | 231 | bool link_get_p2p_scanning(struct link *l) 232 | { 233 | return l && supplicant_p2p_scanning(l->s); 234 | } 235 | 236 | void link_supplicant_started(struct link *l) 237 | { 238 | if (!l || l->public) 239 | return; 240 | 241 | log_debug("link %s started", l->ifname); 242 | l->public = true; 243 | link_dbus_added(l); 244 | } 245 | 246 | void link_supplicant_stopped(struct link *l) 247 | { 248 | if (!l || !l->public) 249 | return; 250 | 251 | log_debug("link %s stopped", l->ifname); 252 | link_dbus_removed(l); 253 | l->public = false; 254 | } 255 | 256 | void link_supplicant_p2p_scan_changed(struct link *l, bool new_value) 257 | { 258 | if (!l) 259 | return; 260 | 261 | link_dbus_properties_changed(l, "P2PScanning", NULL); 262 | } 263 | -------------------------------------------------------------------------------- /src/wifi/wifid-peer.c: -------------------------------------------------------------------------------- 1 | /* 2 | * MiracleCast - Wifi-Display/Miracast Implementation 3 | * 4 | * Copyright (c) 2013-2014 David Herrmann 5 | * 6 | * MiracleCast is free software; you can redistribute it and/or modify it 7 | * under the terms of the GNU Lesser General Public License as published by 8 | * the Free Software Foundation; either version 2.1 of the License, or 9 | * (at your option) any later version. 10 | * 11 | * MiracleCast is distributed in the hope that it will be useful, but 12 | * WITHOUT ANY WARRANTY; without even the implied warranty of 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 14 | * Lesser General Public License for more details. 15 | * 16 | * You should have received a copy of the GNU Lesser General Public License 17 | * along with MiracleCast; If not, see . 18 | */ 19 | 20 | #define LOG_SUBSYSTEM "peer" 21 | 22 | #include 23 | #include 24 | #include 25 | #include 26 | #include "shl_dlist.h" 27 | #include "shl_log.h" 28 | #include "shl_util.h" 29 | #include "util.h" 30 | #include "wifid.h" 31 | 32 | /* 33 | * Peer Handling 34 | */ 35 | 36 | int peer_new(struct link *l, 37 | const char *p2p_mac, 38 | struct peer **out) 39 | { 40 | char mac[MAC_STRLEN]; 41 | struct peer *p; 42 | int r; 43 | 44 | if (!l || !p2p_mac) 45 | return log_EINVAL(); 46 | 47 | reformat_mac(mac, p2p_mac); 48 | 49 | if (shl_htable_lookup_str(&l->peers, mac, NULL, NULL)) 50 | return -EALREADY; 51 | 52 | log_debug("new peer: %s @ %s", mac, l->ifname); 53 | 54 | p = calloc(1, sizeof(*p)); 55 | if (!p) 56 | return log_ENOMEM(); 57 | 58 | p->l = l; 59 | p->p2p_mac = calloc(1, MAC_STRLEN); 60 | if (!p->p2p_mac) { 61 | r = log_ENOMEM(); 62 | goto error; 63 | } 64 | strncpy(p->p2p_mac, mac, MAC_STRLEN - 1); 65 | 66 | r = shl_htable_insert_str(&l->peers, &p->p2p_mac, NULL); 67 | if (r < 0) { 68 | log_vERR(r); 69 | goto error; 70 | } 71 | 72 | ++l->peer_cnt; 73 | log_info("add peer: %s", p->p2p_mac); 74 | 75 | if (out) 76 | *out = p; 77 | 78 | return 0; 79 | 80 | error: 81 | peer_free(p); 82 | return r; 83 | } 84 | 85 | void peer_free(struct peer *p) 86 | { 87 | if (!p) 88 | return; 89 | 90 | log_debug("free peer: %s @ %s", p->p2p_mac, p->l->ifname); 91 | 92 | if (shl_htable_remove_str(&p->l->peers, p->p2p_mac, NULL, NULL)) { 93 | log_info("remove peer: %s", p->p2p_mac); 94 | --p->l->peer_cnt; 95 | } 96 | 97 | free(p->p2p_mac); 98 | free(p); 99 | } 100 | 101 | const char *peer_get_friendly_name(struct peer *p) 102 | { 103 | if (!p) 104 | return NULL; 105 | 106 | return supplicant_peer_get_friendly_name(p->sp); 107 | } 108 | 109 | const char *peer_get_interface(struct peer *p) 110 | { 111 | if (!p || !p->connected) 112 | return NULL; 113 | 114 | return supplicant_peer_get_interface(p->sp); 115 | } 116 | 117 | const char *peer_get_local_address(struct peer *p) 118 | { 119 | if (!p || !p->connected) 120 | return NULL; 121 | 122 | return supplicant_peer_get_local_address(p->sp); 123 | } 124 | 125 | const char *peer_get_remote_address(struct peer *p) 126 | { 127 | if (!p || !p->connected) 128 | return NULL; 129 | 130 | return supplicant_peer_get_remote_address(p->sp); 131 | } 132 | 133 | int peer_connect(struct peer *p, const char *prov, const char *pin) 134 | { 135 | if (!p) 136 | return log_EINVAL(); 137 | 138 | return supplicant_peer_connect(p->sp, prov, pin); 139 | } 140 | 141 | void peer_disconnect(struct peer *p) 142 | { 143 | if (!p) 144 | return log_vEINVAL(); 145 | 146 | supplicant_peer_disconnect(p->sp); 147 | } 148 | 149 | void peer_supplicant_started(struct peer *p) 150 | { 151 | if (!p || p->public) 152 | return; 153 | 154 | log_debug("peer %s @ %s started", p->p2p_mac, p->l->ifname); 155 | p->public = true; 156 | peer_dbus_added(p); 157 | } 158 | 159 | void peer_supplicant_stopped(struct peer *p) 160 | { 161 | if (!p || !p->public) 162 | return; 163 | 164 | log_debug("peer %s @ %s stopped", p->p2p_mac, p->l->ifname); 165 | peer_dbus_removed(p); 166 | p->public = false; 167 | } 168 | 169 | void peer_supplicant_friendly_name_changed(struct peer *p) 170 | { 171 | if (!p || !p->public) 172 | return; 173 | 174 | peer_dbus_properties_changed(p, "FriendlyName", NULL); 175 | } 176 | 177 | void peer_supplicant_provision_discovery(struct peer *p, 178 | const char *prov, 179 | const char *pin) 180 | { 181 | if (!p || !p->public) 182 | return; 183 | 184 | peer_dbus_provision_discovery(p, prov, pin); 185 | } 186 | 187 | void peer_supplicant_connected_changed(struct peer *p, bool connected) 188 | { 189 | if (!p || p->connected == connected) 190 | return; 191 | 192 | p->connected = connected; 193 | peer_dbus_properties_changed(p, "Connected", 194 | "Interface", 195 | "LocalAddress", 196 | "RemoteAddress", 197 | NULL); 198 | } 199 | -------------------------------------------------------------------------------- /src/wifi/wifid.c: -------------------------------------------------------------------------------- 1 | /* 2 | * MiracleCast - Wifi-Display/Miracast Implementation 3 | * 4 | * Copyright (c) 2013-2014 David Herrmann 5 | * 6 | * MiracleCast is free software; you can redistribute it and/or modify it 7 | * under the terms of the GNU Lesser General Public License as published by 8 | * the Free Software Foundation; either version 2.1 of the License, or 9 | * (at your option) any later version. 10 | * 11 | * MiracleCast is distributed in the hope that it will be useful, but 12 | * WITHOUT ANY WARRANTY; without even the implied warranty of 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 14 | * Lesser General Public License for more details. 15 | * 16 | * You should have received a copy of the GNU Lesser General Public License 17 | * along with MiracleCast; If not, see . 18 | */ 19 | 20 | #include 21 | #include 22 | #include 23 | #include 24 | #include 25 | #include 26 | #include 27 | #include 28 | #include 29 | #include 30 | #include 31 | #include 32 | #include 33 | #include 34 | #include 35 | #include "shl_htable.h" 36 | #include "shl_macro.h" 37 | #include "shl_log.h" 38 | #include "shl_util.h" 39 | #include "util.h" 40 | #include "wifid.h" 41 | 42 | const char *arg_wpa_bindir = "/usr/bin"; 43 | unsigned int arg_wpa_loglevel = LOG_NOTICE; 44 | 45 | /* 46 | * Manager Handling 47 | */ 48 | 49 | struct link *manager_find_link(struct manager *m, unsigned int ifindex) 50 | { 51 | unsigned int *elem; 52 | bool res; 53 | 54 | res = shl_htable_lookup_uint(&m->links, ifindex, &elem); 55 | if (!res) 56 | return NULL; 57 | 58 | return link_from_htable(elem); 59 | } 60 | 61 | struct link *manager_find_link_by_label(struct manager *m, const char *label) 62 | { 63 | const char *next; 64 | unsigned int idx; 65 | int r; 66 | 67 | r = shl_atoi_u(label, 10, &next, &idx); 68 | if (r < 0 || *next) 69 | return NULL; 70 | 71 | return manager_find_link(m, idx); 72 | } 73 | 74 | static void manager_add_udev_link(struct manager *m, 75 | struct udev_device *d) 76 | { 77 | struct link *l; 78 | unsigned int ifindex; 79 | const char *ifname; 80 | int r; 81 | 82 | ifindex = ifindex_from_udev_device(d); 83 | if (!ifindex) 84 | return; 85 | 86 | ifname = udev_device_get_property_value(d, "INTERFACE"); 87 | if (!ifname) 88 | return; 89 | 90 | /* ignore dynamic p2p interfaces */ 91 | if (shl_startswith(ifname, "p2p-")) 92 | return; 93 | 94 | r = link_new(m, ifindex, ifname, &l); 95 | if (r < 0) 96 | return; 97 | 98 | link_set_friendly_name(l, m->friendly_name); 99 | 100 | if (udev_device_has_tag(d, "miracle")) 101 | link_set_managed(l, true); 102 | } 103 | 104 | static int manager_udev_fn(sd_event_source *source, 105 | int fd, 106 | uint32_t mask, 107 | void *data) 108 | { 109 | _cleanup_udev_device_ struct udev_device *d = NULL; 110 | struct manager *m = data; 111 | const char *action, *ifname; 112 | unsigned int ifindex; 113 | struct link *l; 114 | 115 | d = udev_monitor_receive_device(m->udev_mon); 116 | if (!d) 117 | return 0; 118 | 119 | ifindex = ifindex_from_udev_device(d); 120 | if (!ifindex) 121 | return 0; 122 | 123 | l = manager_find_link(m, ifindex); 124 | 125 | action = udev_device_get_action(d); 126 | if (action && !strcmp(action, "remove")) { 127 | if (l) 128 | link_free(l); 129 | } else if (l) { 130 | if (action && !strcmp(action, "move")) { 131 | ifname = udev_device_get_property_value(d, "INTERFACE"); 132 | if (ifname) 133 | link_renamed(l, ifname); 134 | } 135 | 136 | if (udev_device_has_tag(d, "miracle")) 137 | link_set_managed(l, true); 138 | else 139 | link_set_managed(l, false); 140 | } else { 141 | manager_add_udev_link(m, d); 142 | } 143 | 144 | return 0; 145 | } 146 | 147 | static int manager_signal_fn(sd_event_source *source, 148 | const struct signalfd_siginfo *ssi, 149 | void *data) 150 | { 151 | struct manager *m = data; 152 | 153 | if (ssi->ssi_signo == SIGCHLD) { 154 | log_debug("caught SIGCHLD for %ld, reaping child", (long)ssi->ssi_pid); 155 | waitid(P_PID, ssi->ssi_pid, NULL, WNOHANG|WEXITED); 156 | return 0; 157 | } else if (ssi->ssi_signo == SIGPIPE) { 158 | /* ignore SIGPIPE */ 159 | return 0; 160 | } 161 | 162 | log_notice("caught signal %d, exiting..", (int)ssi->ssi_signo); 163 | sd_event_exit(m->event, 0); 164 | 165 | return 0; 166 | } 167 | 168 | static void manager_free(struct manager *m) 169 | { 170 | unsigned int i; 171 | struct link *l; 172 | 173 | if (!m) 174 | return; 175 | 176 | while ((l = MANAGER_FIRST_LINK(m))) 177 | link_free(l); 178 | 179 | manager_dbus_disconnect(m); 180 | 181 | shl_htable_clear_uint(&m->links, NULL, NULL); 182 | 183 | sd_event_source_unref(m->udev_mon_source); 184 | udev_monitor_unref(m->udev_mon); 185 | udev_unref(m->udev); 186 | 187 | for (i = 0; m->sigs[i]; ++i) 188 | sd_event_source_unref(m->sigs[i]); 189 | 190 | sd_bus_unref(m->bus); 191 | sd_event_unref(m->event); 192 | 193 | free(m->friendly_name); 194 | free(m); 195 | } 196 | 197 | static int manager_new(struct manager **out) 198 | { 199 | struct manager *m; 200 | static const int sigs[] = { 201 | SIGINT, SIGTERM, SIGQUIT, SIGHUP, SIGPIPE, SIGCHLD, 0 202 | }; 203 | unsigned int i; 204 | sigset_t mask; 205 | int r; 206 | 207 | m = calloc(1, sizeof(*m)); 208 | if (!m) 209 | return log_ENOMEM(); 210 | 211 | shl_htable_init_uint(&m->links); 212 | 213 | r = sd_event_default(&m->event); 214 | if (r < 0) { 215 | log_vERR(r); 216 | goto error; 217 | } 218 | 219 | r = sd_event_set_watchdog(m->event, true); 220 | if (r < 0) { 221 | log_vERR(r); 222 | goto error; 223 | } 224 | 225 | r = sd_bus_default_system(&m->bus); 226 | if (r < 0) { 227 | log_error("cannot connect to system bus: %d", r); 228 | goto error; 229 | } 230 | 231 | r = sd_bus_attach_event(m->bus, m->event, 0); 232 | if (r < 0) { 233 | log_vERR(r); 234 | goto error; 235 | } 236 | 237 | for (i = 0; sigs[i]; ++i) { 238 | sigemptyset(&mask); 239 | sigaddset(&mask, sigs[i]); 240 | sigprocmask(SIG_BLOCK, &mask, NULL); 241 | 242 | r = sd_event_add_signal(m->event, 243 | &m->sigs[i], 244 | sigs[i], 245 | manager_signal_fn, 246 | m); 247 | if (r < 0) { 248 | log_vERR(r); 249 | goto error; 250 | } 251 | 252 | /* low-priority to allow others to handle it first */ 253 | sd_event_source_set_priority(m->sigs[i], 100); 254 | } 255 | 256 | m->udev = udev_new(); 257 | if (!m->udev) { 258 | r = log_ENOMEM(); 259 | goto error; 260 | } 261 | 262 | m->udev_mon = udev_monitor_new_from_netlink(m->udev, "udev"); 263 | if (!m->udev_mon) { 264 | r = log_ENOMEM(); 265 | goto error; 266 | } 267 | 268 | r = udev_monitor_filter_add_match_subsystem_devtype(m->udev_mon, 269 | "net", 270 | "wlan"); 271 | if (r < 0) { 272 | log_vERR(r); 273 | goto error; 274 | } 275 | 276 | r = udev_monitor_enable_receiving(m->udev_mon); 277 | if (r < 0) { 278 | log_vERR(r); 279 | goto error; 280 | } 281 | 282 | r = sd_event_add_io(m->event, 283 | &m->udev_mon_source, 284 | udev_monitor_get_fd(m->udev_mon), 285 | EPOLLHUP | EPOLLERR | EPOLLIN, 286 | manager_udev_fn, 287 | m); 288 | if (r < 0) { 289 | log_vERR(r); 290 | goto error; 291 | } 292 | 293 | r = manager_dbus_connect(m); 294 | if (r < 0) 295 | goto error; 296 | 297 | if (out) 298 | *out = m; 299 | 300 | return 0; 301 | 302 | error: 303 | manager_free(m); 304 | return r; 305 | } 306 | 307 | static void manager_read_name(struct manager *m) 308 | { 309 | _cleanup_sd_bus_error_ sd_bus_error err = SD_BUS_ERROR_NULL; 310 | _cleanup_sd_bus_message_ sd_bus_message *rep = NULL; 311 | const char *name; 312 | char *str; 313 | int r; 314 | 315 | r = sd_bus_call_method(m->bus, 316 | "org.freedesktop.hostname1", 317 | "/org/freedesktop/hostname1", 318 | "org.freedesktop.DBus.Properties", 319 | "Get", 320 | &err, 321 | &rep, 322 | "ss", "org.freedesktop.hostname1", "Hostname"); 323 | if (r < 0) 324 | goto error; 325 | 326 | r = sd_bus_message_enter_container(rep, 'v', "s"); 327 | if (r < 0) 328 | goto error; 329 | 330 | r = sd_bus_message_read(rep, "s", &name); 331 | if (r < 0) 332 | goto error; 333 | 334 | if (shl_isempty(name)) { 335 | log_warning("no hostname set on systemd.hostname1, using: %s", 336 | m->friendly_name); 337 | return; 338 | } 339 | 340 | str = strdup(name); 341 | if (!str) 342 | return log_vENOMEM(); 343 | 344 | free(m->friendly_name); 345 | m->friendly_name = str; 346 | log_debug("friendly-name from local hostname: %s", str); 347 | 348 | return; 349 | 350 | error: 351 | log_warning("cannot read hostname from systemd.hostname1: %s", 352 | bus_error_message(&err, r)); 353 | } 354 | 355 | static void manager_read_links(struct manager *m) 356 | { 357 | _cleanup_udev_enumerate_ struct udev_enumerate *e = NULL; 358 | struct udev_list_entry *l; 359 | struct udev_device *d; 360 | int r; 361 | 362 | e = udev_enumerate_new(m->udev); 363 | if (!e) 364 | goto error; 365 | 366 | r = udev_enumerate_add_match_subsystem(e, "net"); 367 | if (r < 0) 368 | goto error; 369 | 370 | r = udev_enumerate_add_match_property(e, "DEVTYPE", "wlan"); 371 | if (r < 0) 372 | goto error; 373 | 374 | r = udev_enumerate_add_match_is_initialized(e); 375 | if (r < 0) 376 | goto error; 377 | 378 | r = udev_enumerate_scan_devices(e); 379 | if (r < 0) 380 | goto error; 381 | 382 | udev_list_entry_foreach(l, udev_enumerate_get_list_entry(e)) { 383 | d = udev_device_new_from_syspath(m->udev, 384 | udev_list_entry_get_name(l)); 385 | if (!d) 386 | goto error; 387 | 388 | manager_add_udev_link(m, d); 389 | udev_device_unref(d); 390 | } 391 | 392 | return; 393 | 394 | error: 395 | log_warning("cannot enumerate links via udev"); 396 | } 397 | 398 | static int manager_startup(struct manager *m) 399 | { 400 | int r; 401 | 402 | r = shl_mkdir_p_prefix("/run", "/run/miracle", 0755); 403 | if (r >= 0) 404 | r = shl_mkdir_p_prefix("/run/miracle", 405 | "/run/miracle/wifi", 406 | 0700); 407 | if (r < 0) { 408 | log_error("cannot create maintenance directories in /run: %d", 409 | r); 410 | return r; 411 | } 412 | 413 | manager_read_name(m); 414 | manager_read_links(m); 415 | 416 | return 0; 417 | } 418 | 419 | static int manager_run(struct manager *m) 420 | { 421 | return sd_event_loop(m->event); 422 | } 423 | 424 | static int help(void) 425 | { 426 | /* 427 | * 80-char barrier: 428 | * 01234567890123456789012345678901234567890123456789012345678901234567890123456789 429 | */ 430 | printf("%s [OPTIONS...] ...\n\n" 431 | "Wifi Management Daemon.\n\n" 432 | " -h --help Show this help\n" 433 | " --version Show package version\n" 434 | " --log-level Maximum level for log messages\n" 435 | " --log-time Prefix log-messages with timestamp\n" 436 | "\n" 437 | " --wpa-bindir wpa_supplicant binary dir [/usr/bin]\n" 438 | " --wpa-loglevel wpa_supplicant log-level\n" 439 | , program_invocation_short_name); 440 | /* 441 | * 80-char barrier: 442 | * 01234567890123456789012345678901234567890123456789012345678901234567890123456789 443 | */ 444 | 445 | return 0; 446 | } 447 | 448 | static int parse_argv(int argc, char *argv[]) 449 | { 450 | enum { 451 | ARG_VERSION = 0x100, 452 | ARG_LOG_LEVEL, 453 | ARG_LOG_TIME, 454 | 455 | ARG_WPA_BINDIR, 456 | ARG_WPA_LOGLEVEL, 457 | }; 458 | static const struct option options[] = { 459 | { "help", no_argument, NULL, 'h' }, 460 | { "version", no_argument, NULL, ARG_VERSION }, 461 | { "log-level", required_argument, NULL, ARG_LOG_LEVEL }, 462 | { "log-time", no_argument, NULL, ARG_LOG_TIME }, 463 | 464 | { "wpa-bindir", required_argument, NULL, ARG_WPA_BINDIR }, 465 | { "wpa-loglevel", required_argument, NULL, ARG_WPA_LOGLEVEL }, 466 | {} 467 | }; 468 | int c; 469 | 470 | while ((c = getopt_long(argc, argv, "h", options, NULL)) >= 0) { 471 | switch (c) { 472 | case 'h': 473 | return help(); 474 | case ARG_VERSION: 475 | puts(PACKAGE_STRING); 476 | return 0; 477 | case ARG_LOG_LEVEL: 478 | log_max_sev = atoi(optarg); 479 | break; 480 | case ARG_LOG_TIME: 481 | log_init_time(); 482 | break; 483 | 484 | case ARG_WPA_BINDIR: 485 | arg_wpa_bindir = optarg; 486 | break; 487 | case ARG_WPA_LOGLEVEL: 488 | arg_wpa_loglevel = atoi(optarg); 489 | break; 490 | case '?': 491 | return -EINVAL; 492 | } 493 | } 494 | 495 | if (optind < argc) { 496 | log_error("unparsed remaining arguments starting with: %s", 497 | argv[optind]); 498 | return -EINVAL; 499 | } 500 | 501 | log_format(LOG_DEFAULT_BASE, NULL, LOG_INFO, 502 | "miracle-wifid - revision %s %s %s", 503 | "some-rev-TODO-xyz", __DATE__, __TIME__); 504 | 505 | return 1; 506 | } 507 | 508 | int main(int argc, char **argv) 509 | { 510 | struct manager *m = NULL; 511 | int r; 512 | 513 | srand(time(NULL)); 514 | 515 | r = parse_argv(argc, argv); 516 | if (r < 0) 517 | return EXIT_FAILURE; 518 | if (!r) 519 | return EXIT_SUCCESS; 520 | 521 | r = manager_new(&m); 522 | if (r < 0) 523 | goto finish; 524 | 525 | r = manager_startup(m); 526 | if (r < 0) 527 | goto finish; 528 | 529 | r = sd_notify(false, "READY=1\n" 530 | "STATUS=Running.."); 531 | if (r < 0) { 532 | log_vERR(r); 533 | goto finish; 534 | } 535 | 536 | r = manager_run(m); 537 | 538 | finish: 539 | sd_notify(false, "STATUS=Exiting.."); 540 | manager_free(m); 541 | 542 | log_debug("exiting.."); 543 | return abs(r); 544 | } 545 | -------------------------------------------------------------------------------- /src/wifi/wifid.h: -------------------------------------------------------------------------------- 1 | /* 2 | * MiracleCast - Wifi-Display/Miracast Implementation 3 | * 4 | * Copyright (c) 2013-2014 David Herrmann 5 | * 6 | * MiracleCast is free software; you can redistribute it and/or modify it 7 | * under the terms of the GNU Lesser General Public License as published by 8 | * the Free Software Foundation; either version 2.1 of the License, or 9 | * (at your option) any later version. 10 | * 11 | * MiracleCast is distributed in the hope that it will be useful, but 12 | * WITHOUT ANY WARRANTY; without even the implied warranty of 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 14 | * Lesser General Public License for more details. 15 | * 16 | * You should have received a copy of the GNU Lesser General Public License 17 | * along with MiracleCast; If not, see . 18 | */ 19 | 20 | #include 21 | #include 22 | #include 23 | #include 24 | #include 25 | #include 26 | #include "shl_dlist.h" 27 | #include "shl_htable.h" 28 | 29 | #ifndef WIFID_H 30 | #define WIFID_H 31 | 32 | struct manager; 33 | struct link; 34 | struct peer; 35 | struct supplicant; 36 | struct supplicant_peer; 37 | 38 | /* supplicant */ 39 | 40 | int supplicant_new(struct link *l, 41 | struct supplicant **out); 42 | void supplicant_free(struct supplicant *s); 43 | 44 | int supplicant_start(struct supplicant *s); 45 | void supplicant_stop(struct supplicant *s); 46 | bool supplicant_is_running(struct supplicant *s); 47 | bool supplicant_is_ready(struct supplicant *s); 48 | 49 | int supplicant_set_friendly_name(struct supplicant *s, const char *name); 50 | int supplicant_p2p_start_scan(struct supplicant *s); 51 | void supplicant_p2p_stop_scan(struct supplicant *s); 52 | bool supplicant_p2p_scanning(struct supplicant *s); 53 | 54 | /* supplicant peer */ 55 | 56 | const char *supplicant_peer_get_friendly_name(struct supplicant_peer *sp); 57 | const char *supplicant_peer_get_interface(struct supplicant_peer *sp); 58 | const char *supplicant_peer_get_local_address(struct supplicant_peer *sp); 59 | const char *supplicant_peer_get_remote_address(struct supplicant_peer *sp); 60 | int supplicant_peer_connect(struct supplicant_peer *sp, 61 | const char *prov_type, 62 | const char *pin); 63 | void supplicant_peer_disconnect(struct supplicant_peer *sp); 64 | 65 | /* peer */ 66 | 67 | struct peer { 68 | struct link *l; 69 | char *p2p_mac; 70 | struct supplicant_peer *sp; 71 | 72 | bool public : 1; 73 | bool connected : 1; 74 | }; 75 | 76 | #define peer_from_htable(_p) \ 77 | shl_htable_offsetof((_p), struct peer, p2p_mac) 78 | 79 | int peer_new(struct link *l, 80 | const char *p2p_mac, 81 | struct peer **out); 82 | void peer_free(struct peer *p); 83 | 84 | const char *peer_get_friendly_name(struct peer *p); 85 | const char *peer_get_interface(struct peer *p); 86 | const char *peer_get_local_address(struct peer *p); 87 | const char *peer_get_remote_address(struct peer *p); 88 | int peer_connect(struct peer *p, const char *prov, const char *pin); 89 | void peer_disconnect(struct peer *p); 90 | 91 | int peer_allow(struct peer *p); 92 | void peer_reject(struct peer *p); 93 | 94 | void peer_supplicant_started(struct peer *p); 95 | void peer_supplicant_stopped(struct peer *p); 96 | void peer_supplicant_friendly_name_changed(struct peer *p); 97 | void peer_supplicant_provision_discovery(struct peer *p, 98 | const char *prov, 99 | const char *pin); 100 | void peer_supplicant_connected_changed(struct peer *p, bool connected); 101 | 102 | _shl_sentinel_ 103 | void peer_dbus_properties_changed(struct peer *p, const char *prop, ...); 104 | void peer_dbus_provision_discovery(struct peer *p, 105 | const char *prov, 106 | const char *pin); 107 | void peer_dbus_added(struct peer *p); 108 | void peer_dbus_removed(struct peer *p); 109 | 110 | /* link */ 111 | 112 | struct link { 113 | struct manager *m; 114 | unsigned int ifindex; 115 | struct supplicant *s; 116 | 117 | char *ifname; 118 | char *friendly_name; 119 | 120 | size_t peer_cnt; 121 | struct shl_htable peers; 122 | 123 | bool managed : 1; 124 | bool public : 1; 125 | }; 126 | 127 | #define link_from_htable(_l) \ 128 | shl_htable_offsetof((_l), struct link, ifindex) 129 | #define LINK_FIRST_PEER(_l) \ 130 | SHL_HTABLE_FIRST_MACRO(&(_l)->peers, peer_from_htable) 131 | #define LINK_FOREACH_PEER(_i, _l) \ 132 | SHL_HTABLE_FOREACH_MACRO(_i, &(_l)->peers, peer_from_htable) 133 | 134 | struct peer *link_find_peer(struct link *l, const char *p2p_mac); 135 | struct peer *link_find_peer_by_label(struct link *l, const char *label); 136 | 137 | int link_new(struct manager *m, 138 | unsigned int ifindex, 139 | const char *ifname, 140 | struct link **out); 141 | void link_free(struct link *l); 142 | 143 | void link_set_managed(struct link *l, bool set); 144 | int link_renamed(struct link *l, const char *ifname); 145 | 146 | int link_set_friendly_name(struct link *l, const char *name); 147 | const char *link_get_friendly_name(struct link *l); 148 | int link_set_p2p_scanning(struct link *l, bool set); 149 | bool link_get_p2p_scanning(struct link *l); 150 | 151 | void link_supplicant_started(struct link *l); 152 | void link_supplicant_stopped(struct link *l); 153 | void link_supplicant_p2p_scan_changed(struct link *l, bool new_value); 154 | 155 | _shl_sentinel_ 156 | void link_dbus_properties_changed(struct link *l, const char *prop, ...); 157 | void link_dbus_added(struct link *l); 158 | void link_dbus_removed(struct link *l); 159 | 160 | /* manager */ 161 | 162 | struct manager { 163 | sd_event *event; 164 | sd_bus *bus; 165 | sd_event_source *sigs[_NSIG]; 166 | struct udev *udev; 167 | struct udev_monitor *udev_mon; 168 | sd_event_source *udev_mon_source; 169 | 170 | char *friendly_name; 171 | 172 | size_t link_cnt; 173 | struct shl_htable links; 174 | }; 175 | 176 | #define MANAGER_FIRST_LINK(_m) \ 177 | SHL_HTABLE_FIRST_MACRO(&(_m)->links, link_from_htable) 178 | #define MANAGER_FOREACH_LINK(_i, _m) \ 179 | SHL_HTABLE_FOREACH_MACRO(_i, &(_m)->links, link_from_htable) 180 | 181 | struct link *manager_find_link(struct manager *m, unsigned int ifindex); 182 | struct link *manager_find_link_by_label(struct manager *m, const char *label); 183 | 184 | /* dbus */ 185 | 186 | int manager_dbus_connect(struct manager *m); 187 | void manager_dbus_disconnect(struct manager *m); 188 | 189 | /* cli arguments */ 190 | 191 | extern const char *arg_wpa_bindir; 192 | extern unsigned int arg_wpa_loglevel; 193 | 194 | #endif /* WIFID_H */ 195 | -------------------------------------------------------------------------------- /test/test_common.h: -------------------------------------------------------------------------------- 1 | /* 2 | * MiracleCast - Wifi-Display/Miracast Implementation 3 | * 4 | * Copyright (c) 2013-2014 David Herrmann 5 | * 6 | * MiracleCast is free software; you can redistribute it and/or modify it 7 | * under the terms of the GNU Lesser General Public License as published by 8 | * the Free Software Foundation; either version 2.1 of the License, or 9 | * (at your option) any later version. 10 | * 11 | * MiracleCast is distributed in the hope that it will be useful, but 12 | * WITHOUT ANY WARRANTY; without even the implied warranty of 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 14 | * Lesser General Public License for more details. 15 | * 16 | * You should have received a copy of the GNU Lesser General Public License 17 | * along with MiracleCast; If not, see . 18 | */ 19 | 20 | /* 21 | * Test Helper 22 | * This header includes all kinds of helpers for testing. It tries to include 23 | * everything required and provides simple macros to avoid duplicating code in 24 | * each test. We try to keep tests as small as possible and move everything that 25 | * might be common here. 26 | * 27 | * We avoid sticking to our usual coding conventions (including headers in 28 | * source files, etc. ..) and instead make this the most convenient we can. 29 | */ 30 | 31 | #ifndef TEST_COMMON_H 32 | #define TEST_COMMON_H 33 | 34 | #include 35 | #include 36 | #include 37 | #include 38 | #include 39 | #include 40 | #include 41 | #include 42 | #include 43 | #include 44 | #include 45 | #include 46 | #include 47 | #include 48 | #include "shl_dlist.h" 49 | #include "shl_htable.h" 50 | #include "shl_log.h" 51 | #include "shl_macro.h" 52 | #include "shl_util.h" 53 | 54 | /* lower address-space is protected from user-allocation, so this is invalid */ 55 | #define TEST_INVALID_PTR ((void*)0x10) 56 | 57 | #define TEST_DEFINE_CASE(_name) \ 58 | static TCase *test_create_case_##_name(void) \ 59 | { \ 60 | TCase *tc; \ 61 | \ 62 | tc = tcase_create(#_name); \ 63 | 64 | #define TEST(_name) tcase_add_test(tc, _name); 65 | 66 | #define TEST_END_CASE \ 67 | return tc; \ 68 | } \ 69 | 70 | #define TEST_END NULL 71 | 72 | #define TEST_CASE(_name) test_create_case_##_name 73 | 74 | static inline Suite *test_create_suite(const char *name, ...) 75 | { 76 | Suite *s; 77 | va_list list; 78 | TCase *(*fn)(void); 79 | 80 | s = suite_create(name); 81 | 82 | va_start(list, name); 83 | while ((fn = va_arg(list, TCase *(*)(void)))) 84 | suite_add_tcase(s, fn()); 85 | va_end(list); 86 | 87 | return s; 88 | } 89 | 90 | #define TEST_SUITE(_name, ...) test_create_suite((#_name), ##__VA_ARGS__) 91 | 92 | static inline int test_run_suite(Suite *s) 93 | { 94 | int ret; 95 | SRunner *sr; 96 | 97 | sr = srunner_create(s); 98 | srunner_run_all(sr, CK_NORMAL); 99 | ret = srunner_ntests_failed(sr); 100 | srunner_free(sr); 101 | 102 | return ret; 103 | } 104 | 105 | #define TEST_DEFINE(_suite) \ 106 | int main(int argc, char **argv) \ 107 | { \ 108 | return test_run_suite(_suite); \ 109 | } 110 | 111 | #endif /* TEST_COMMON_H */ 112 | -------------------------------------------------------------------------------- /test/test_wpas.c: -------------------------------------------------------------------------------- 1 | /* 2 | * MiracleCast - Wifi-Display/Miracast Implementation 3 | * 4 | * Copyright (c) 2013-2014 David Herrmann 5 | * 6 | * MiracleCast is free software; you can redistribute it and/or modify it 7 | * under the terms of the GNU Lesser General Public License as published by 8 | * the Free Software Foundation; either version 2.1 of the License, or 9 | * (at your option) any later version. 10 | * 11 | * MiracleCast is distributed in the hope that it will be useful, but 12 | * WITHOUT ANY WARRANTY; without even the implied warranty of 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 14 | * Lesser General Public License for more details. 15 | * 16 | * You should have received a copy of the GNU Lesser General Public License 17 | * along with MiracleCast; If not, see . 18 | */ 19 | 20 | #include "test_common.h" 21 | #include "wpas.h" 22 | 23 | static struct wpas *server, *client; 24 | static sd_event *event; 25 | 26 | static struct wpas *start_test_client(void) 27 | { 28 | const char *spath = "/tmp/miracle-test-sock"; 29 | int r; 30 | 31 | r = sd_event_default(&event); 32 | ck_assert_int_ge(r, 0); 33 | 34 | r = wpas_create(spath, &server); 35 | ck_assert_int_ge(r, 0); 36 | r = wpas_attach_event(server, NULL, 0); 37 | ck_assert_int_ge(r, 0); 38 | 39 | r = wpas_open(spath, &client); 40 | ck_assert_int_ge(r, 0); 41 | r = wpas_attach_event(client, NULL, 0); 42 | ck_assert_int_ge(r, 0); 43 | 44 | return client; 45 | } 46 | 47 | static void stop_test_client(void) 48 | { 49 | wpas_unref(client); 50 | client = NULL; 51 | wpas_unref(server); 52 | server = NULL; 53 | } 54 | 55 | START_TEST(bus_invalid_open) 56 | { 57 | const char *ipath = "/tmp/miracle/invalid-test-dir/invalid-test-path"; 58 | struct wpas *w; 59 | int r; 60 | 61 | /* test invalid client */ 62 | 63 | w = TEST_INVALID_PTR; 64 | r = wpas_open(NULL, NULL); 65 | ck_assert_int_lt(r, 0); 66 | 67 | r = wpas_open(ipath, NULL); 68 | ck_assert_int_lt(r, 0); 69 | 70 | r = wpas_open(NULL, &w); 71 | ck_assert_int_lt(r, 0); 72 | ck_assert(w == TEST_INVALID_PTR); 73 | 74 | r = wpas_open(ipath, &w); 75 | ck_assert_int_lt(r, 0); 76 | ck_assert(w == TEST_INVALID_PTR); 77 | } 78 | END_TEST 79 | 80 | START_TEST(bus_invalid_create) 81 | { 82 | const char *ipath = "/tmp/miracle/invalid-test-dir/invalid-test-path"; 83 | struct wpas *s; 84 | int r; 85 | 86 | /* test invalid server */ 87 | 88 | s = TEST_INVALID_PTR; 89 | r = wpas_create(NULL, NULL); 90 | ck_assert_int_lt(r, 0); 91 | 92 | r = wpas_create(ipath, NULL); 93 | ck_assert_int_lt(r, 0); 94 | 95 | r = wpas_create(NULL, &s); 96 | ck_assert_int_lt(r, 0); 97 | ck_assert(s == TEST_INVALID_PTR); 98 | } 99 | END_TEST 100 | 101 | START_TEST(bus_create) 102 | { 103 | const char *spath = "/tmp/miracle-test-sock"; 104 | struct wpas *w, *s; 105 | int r; 106 | 107 | /* test server creation */ 108 | 109 | s = TEST_INVALID_PTR; 110 | w = TEST_INVALID_PTR; 111 | 112 | unlink(spath); 113 | ck_assert_int_lt(access(spath, F_OK), 0); 114 | 115 | r = wpas_create(spath, &s); 116 | ck_assert_int_ge(r, 0); 117 | ck_assert(s != TEST_INVALID_PTR); 118 | ck_assert(s != NULL); 119 | 120 | ck_assert_int_ge(access(spath, F_OK), 0); 121 | 122 | r = wpas_create(spath, &w); 123 | ck_assert_int_eq(r, -EADDRINUSE); 124 | ck_assert(w == TEST_INVALID_PTR); 125 | 126 | ck_assert_int_ge(access(spath, F_OK), 0); 127 | 128 | wpas_unref(s); 129 | s = TEST_INVALID_PTR; 130 | 131 | ck_assert_int_lt(access(spath, F_OK), 0); 132 | 133 | /* test again with pre-existing but unused file */ 134 | ck_assert_int_ge(open(spath, O_RDWR | O_CREAT | O_CLOEXEC, S_IRWXU), 0); 135 | ck_assert_int_ge(access(spath, F_OK), 0); 136 | 137 | r = wpas_create(spath, &s); 138 | ck_assert_int_ge(r, 0); 139 | ck_assert(s != TEST_INVALID_PTR); 140 | ck_assert(s != NULL); 141 | 142 | ck_assert_int_ge(access(spath, F_OK), 0); 143 | 144 | wpas_unref(s); 145 | s = TEST_INVALID_PTR; 146 | 147 | ck_assert_int_lt(access(spath, F_OK), 0); 148 | } 149 | END_TEST 150 | 151 | START_TEST(bus_open) 152 | { 153 | const char *spath = "/tmp/miracle-test-sock"; 154 | struct wpas *w, *s; 155 | int r; 156 | 157 | /* test client connection */ 158 | 159 | s = TEST_INVALID_PTR; 160 | w = TEST_INVALID_PTR; 161 | 162 | r = wpas_create(spath, &s); 163 | ck_assert_int_ge(r, 0); 164 | 165 | ck_assert_int_ge(access(spath, F_OK), 0); 166 | 167 | r = wpas_open(spath, &w); 168 | ck_assert_int_ge(r, 0); 169 | 170 | wpas_unref(w); 171 | wpas_unref(s); 172 | 173 | ck_assert_int_lt(access(spath, F_OK), 0); 174 | } 175 | END_TEST 176 | 177 | TEST_DEFINE_CASE(bus) 178 | TEST(bus_invalid_open) 179 | TEST(bus_invalid_create) 180 | TEST(bus_create) 181 | TEST(bus_open) 182 | TEST_END_CASE 183 | 184 | START_TEST(msg_invalid_new) 185 | { 186 | struct wpas_message *m; 187 | struct wpas *w; 188 | int r; 189 | 190 | w = start_test_client(); 191 | 192 | m = TEST_INVALID_PTR; 193 | 194 | r = wpas_message_new_event(NULL, NULL, 0, NULL); 195 | ck_assert_int_lt(r, 0); 196 | ck_assert(m == TEST_INVALID_PTR); 197 | 198 | r = wpas_message_new_event(w, NULL, 0, NULL); 199 | ck_assert_int_lt(r, 0); 200 | ck_assert(m == TEST_INVALID_PTR); 201 | 202 | r = wpas_message_new_event(NULL, "name", 0, NULL); 203 | ck_assert_int_lt(r, 0); 204 | ck_assert(m == TEST_INVALID_PTR); 205 | 206 | r = wpas_message_new_event(NULL, NULL, 0, &m); 207 | ck_assert_int_lt(r, 0); 208 | ck_assert(m == TEST_INVALID_PTR); 209 | 210 | r = wpas_message_new_event(w, NULL, 0, &m); 211 | ck_assert_int_lt(r, 0); 212 | ck_assert(m == TEST_INVALID_PTR); 213 | 214 | r = wpas_message_new_event(w, "", 0, &m); 215 | ck_assert_int_lt(r, 0); 216 | ck_assert(m == TEST_INVALID_PTR); 217 | 218 | r = wpas_message_new_event(w, "name", 0, NULL); 219 | ck_assert_int_lt(r, 0); 220 | ck_assert(m == TEST_INVALID_PTR); 221 | 222 | r = wpas_message_new_event(NULL, "name", 0, &m); 223 | ck_assert_int_lt(r, 0); 224 | ck_assert(m == TEST_INVALID_PTR); 225 | 226 | stop_test_client(); 227 | } 228 | END_TEST 229 | 230 | START_TEST(msg_new_event) 231 | { 232 | struct wpas_message *m; 233 | struct wpas *w; 234 | int r; 235 | 236 | w = start_test_client(); 237 | 238 | m = TEST_INVALID_PTR; 239 | 240 | r = wpas_message_new_event(w, "name", 5, &m); 241 | ck_assert_int_ge(r, 0); 242 | ck_assert(m != TEST_INVALID_PTR); 243 | ck_assert(m != NULL); 244 | 245 | ck_assert_int_eq(wpas_message_is_event(m, NULL), 1); 246 | ck_assert_int_eq(wpas_message_is_event(m, "name"), 1); 247 | ck_assert_int_eq(wpas_message_is_event(m, "names"), 0); 248 | ck_assert_int_eq(wpas_message_is_event(m, "nam"), 0); 249 | ck_assert_int_eq(wpas_message_is_event(m, ""), 0); 250 | ck_assert_int_eq(wpas_message_is_request(m, NULL), 0); 251 | ck_assert_int_eq(wpas_message_is_reply(m), 0); 252 | 253 | ck_assert_int_eq(wpas_message_get_cookie(m), 0); 254 | ck_assert_ptr_eq(wpas_message_get_bus(m), w); 255 | ck_assert_int_eq(wpas_message_get_type(m), WPAS_MESSAGE_EVENT); 256 | ck_assert_int_eq(wpas_message_get_level(m), 5); 257 | ck_assert_str_eq(wpas_message_get_name(m), "name"); 258 | ck_assert_ptr_eq((void*)wpas_message_get_raw(m), NULL); 259 | 260 | wpas_message_unref(m); 261 | 262 | stop_test_client(); 263 | } 264 | END_TEST 265 | 266 | START_TEST(msg_new_request) 267 | { 268 | struct wpas_message *m; 269 | struct wpas *w; 270 | int r; 271 | 272 | w = start_test_client(); 273 | 274 | m = TEST_INVALID_PTR; 275 | 276 | r = wpas_message_new_request(w, "name", &m); 277 | ck_assert_int_ge(r, 0); 278 | ck_assert(m != TEST_INVALID_PTR); 279 | ck_assert(m != NULL); 280 | 281 | ck_assert_int_eq(wpas_message_is_request(m, NULL), 1); 282 | ck_assert_int_eq(wpas_message_is_request(m, "name"), 1); 283 | ck_assert_int_eq(wpas_message_is_request(m, "names"), 0); 284 | ck_assert_int_eq(wpas_message_is_request(m, "nam"), 0); 285 | ck_assert_int_eq(wpas_message_is_request(m, ""), 0); 286 | ck_assert_int_eq(wpas_message_is_event(m, NULL), 0); 287 | ck_assert_int_eq(wpas_message_is_reply(m), 0); 288 | 289 | ck_assert_int_eq(wpas_message_get_cookie(m), 0); 290 | ck_assert_ptr_eq(wpas_message_get_bus(m), w); 291 | ck_assert_int_eq(wpas_message_get_type(m), WPAS_MESSAGE_REQUEST); 292 | ck_assert_int_eq(wpas_message_get_level(m), 0); 293 | ck_assert_str_eq(wpas_message_get_name(m), "name"); 294 | ck_assert_ptr_eq((void*)wpas_message_get_raw(m), NULL); 295 | 296 | wpas_message_unref(m); 297 | 298 | stop_test_client(); 299 | } 300 | END_TEST 301 | 302 | START_TEST(msg_new_reply) 303 | { 304 | struct wpas_message *m; 305 | struct wpas *w; 306 | int r; 307 | 308 | w = start_test_client(); 309 | 310 | m = TEST_INVALID_PTR; 311 | 312 | r = wpas_message_new_reply(w, &m); 313 | ck_assert_int_ge(r, 0); 314 | ck_assert(m != TEST_INVALID_PTR); 315 | ck_assert(m != NULL); 316 | 317 | ck_assert_int_eq(wpas_message_is_reply(m), 1); 318 | ck_assert_int_eq(wpas_message_is_event(m, NULL), 0); 319 | ck_assert_int_eq(wpas_message_is_request(m, NULL), 0); 320 | 321 | ck_assert_int_eq(wpas_message_get_cookie(m), 0); 322 | ck_assert_ptr_eq(wpas_message_get_bus(m), w); 323 | ck_assert_int_eq(wpas_message_get_type(m), WPAS_MESSAGE_REPLY); 324 | ck_assert_int_eq(wpas_message_get_level(m), 0); 325 | ck_assert_ptr_eq((void*)wpas_message_get_name(m), NULL); 326 | ck_assert_ptr_eq((void*)wpas_message_get_raw(m), NULL); 327 | 328 | wpas_message_unref(m); 329 | 330 | stop_test_client(); 331 | } 332 | END_TEST 333 | 334 | START_TEST(msg_peer) 335 | { 336 | struct wpas_message *m; 337 | struct wpas *w; 338 | char *t; 339 | int r; 340 | 341 | w = start_test_client(); 342 | 343 | r = wpas_message_new_event(w, "name", 5, &m); 344 | ck_assert_int_ge(r, 0); 345 | 346 | ck_assert_ptr_eq((void*)wpas_message_get_peer(m), NULL); 347 | t = wpas_message_get_escaped_peer(m); 348 | ck_assert_str_eq(t, ""); 349 | free(t); 350 | 351 | wpas_message_set_peer(m, "/some/path"); 352 | ck_assert_str_eq(wpas_message_get_peer(m), "/some/path"); 353 | t = wpas_message_get_escaped_peer(m); 354 | ck_assert_str_eq(t, "/some/path"); 355 | free(t); 356 | 357 | wpas_message_set_peer(m, "\0/some/path"); 358 | ck_assert_str_eq(wpas_message_get_peer(m), ""); 359 | ck_assert_str_eq(wpas_message_get_peer(m) + 1, "/some/path"); 360 | t = wpas_message_get_escaped_peer(m); 361 | ck_assert_str_eq(t, "@abstract:/some/path"); 362 | free(t); 363 | 364 | wpas_message_set_peer(m, NULL); 365 | ck_assert_ptr_eq((void*)wpas_message_get_peer(m), NULL); 366 | t = wpas_message_get_escaped_peer(m); 367 | ck_assert_str_eq(t, ""); 368 | free(t); 369 | 370 | wpas_message_unref(m); 371 | 372 | stop_test_client(); 373 | } 374 | END_TEST 375 | 376 | START_TEST(msg_append) 377 | { 378 | struct wpas_message *m; 379 | struct wpas *w; 380 | int r; 381 | 382 | w = start_test_client(); 383 | 384 | r = wpas_message_new_event(w, "name", 5, &m); 385 | ck_assert_int_ge(r, 0); 386 | 387 | r = wpas_message_seal(m); 388 | ck_assert_int_ge(r, 0); 389 | 390 | ck_assert_str_eq(wpas_message_get_raw(m), "<5>name"); 391 | 392 | wpas_message_unref(m); 393 | 394 | r = wpas_message_new_event(w, "name", 5, &m); 395 | ck_assert_int_ge(r, 0); 396 | 397 | r = wpas_message_append(m, 398 | "suie", 399 | "string", 400 | (uint32_t)5, 401 | (int32_t)1, 402 | "key", 403 | "value"); 404 | ck_assert_int_ge(r, 0); 405 | 406 | r = wpas_message_seal(m); 407 | ck_assert_int_ge(r, 0); 408 | 409 | ck_assert_str_eq(wpas_message_get_raw(m), 410 | "<5>name string 5 1 key=value"); 411 | 412 | r = wpas_message_skip(m, "suie"); 413 | ck_assert_int_ge(r, 0); 414 | 415 | wpas_message_unref(m); 416 | 417 | stop_test_client(); 418 | } 419 | END_TEST 420 | 421 | TEST_DEFINE_CASE(msg) 422 | TEST(msg_invalid_new) 423 | TEST(msg_new_event) 424 | TEST(msg_new_request) 425 | TEST(msg_new_reply) 426 | TEST(msg_peer) 427 | TEST(msg_append) 428 | TEST_END_CASE 429 | 430 | START_TEST(run_invalid_msg) 431 | { 432 | struct wpas_message *m; 433 | int r; 434 | 435 | start_test_client(); 436 | 437 | r = wpas_message_new_reply(client, &m); 438 | ck_assert_int_ge(r, 0); 439 | r = wpas_call_async(client, m, NULL, NULL, 0, NULL); 440 | ck_assert_int_lt(r, 0); 441 | r = wpas_call_async(server, m, NULL, NULL, 0, NULL); 442 | ck_assert_int_lt(r, 0); 443 | r = wpas_send(server, m, 0); 444 | ck_assert_int_lt(r, 0); 445 | wpas_message_unref(m); 446 | 447 | r = wpas_message_new_event(client, "sth", 0, &m); 448 | ck_assert_int_ge(r, 0); 449 | r = wpas_call_async(client, m, NULL, NULL, 0, NULL); 450 | ck_assert_int_lt(r, 0); 451 | r = wpas_call_async(server, m, NULL, NULL, 0, NULL); 452 | ck_assert_int_lt(r, 0); 453 | r = wpas_send(server, m, 0); 454 | ck_assert_int_lt(r, 0); 455 | wpas_message_unref(m); 456 | 457 | r = wpas_message_new_request(client, "sth", &m); 458 | ck_assert_int_ge(r, 0); 459 | r = wpas_call_async(server, m, NULL, NULL, 0, NULL); 460 | ck_assert_int_lt(r, 0); 461 | r = wpas_send(server, m, 0); 462 | ck_assert_int_lt(r, 0); 463 | wpas_message_set_peer(m, "/some/path"); 464 | r = wpas_call_async(client, m, NULL, NULL, 0, NULL); 465 | ck_assert_int_lt(r, 0); 466 | wpas_message_unref(m); 467 | 468 | r = wpas_message_new_reply(server, &m); 469 | ck_assert_int_ge(r, 0); 470 | r = wpas_call_async(client, m, NULL, NULL, 0, NULL); 471 | ck_assert_int_lt(r, 0); 472 | r = wpas_call_async(server, m, NULL, NULL, 0, NULL); 473 | ck_assert_int_lt(r, 0); 474 | r = wpas_send(server, m, 0); 475 | ck_assert_int_lt(r, 0); 476 | wpas_message_unref(m); 477 | 478 | r = wpas_message_new_event(server, "sth", 0, &m); 479 | ck_assert_int_ge(r, 0); 480 | r = wpas_call_async(client, m, NULL, NULL, 0, NULL); 481 | ck_assert_int_lt(r, 0); 482 | r = wpas_call_async(server, m, NULL, NULL, 0, NULL); 483 | ck_assert_int_lt(r, 0); 484 | r = wpas_send(server, m, 0); 485 | ck_assert_int_lt(r, 0); 486 | wpas_message_unref(m); 487 | 488 | r = wpas_message_new_request(server, "sth", &m); 489 | ck_assert_int_ge(r, 0); 490 | r = wpas_call_async(server, m, NULL, NULL, 0, NULL); 491 | ck_assert_int_lt(r, 0); 492 | r = wpas_send(server, m, 0); 493 | ck_assert_int_lt(r, 0); 494 | r = wpas_call_async(client, m, NULL, NULL, 0, NULL); 495 | ck_assert_int_lt(r, 0); 496 | wpas_message_set_peer(m, "/some/path"); 497 | r = wpas_call_async(client, m, NULL, NULL, 0, NULL); 498 | ck_assert_int_lt(r, 0); 499 | wpas_message_unref(m); 500 | 501 | stop_test_client(); 502 | } 503 | END_TEST 504 | 505 | START_TEST(run_msg) 506 | { 507 | struct wpas_message *m; 508 | int r; 509 | 510 | start_test_client(); 511 | 512 | r = wpas_message_new_reply(client, &m); 513 | ck_assert_int_ge(r, 0); 514 | r = wpas_send(client, m, 0); 515 | ck_assert_int_ge(r, 0); 516 | r = wpas_send(client, m, 0); 517 | ck_assert_int_lt(r, 0); 518 | wpas_message_unref(m); 519 | 520 | r = wpas_message_new_reply(client, &m); 521 | ck_assert_int_ge(r, 0); 522 | wpas_message_set_peer(m, "/some/peer"); 523 | r = wpas_send(client, m, 0); 524 | ck_assert_int_ge(r, 0); 525 | r = wpas_send(client, m, 0); 526 | ck_assert_int_lt(r, 0); 527 | wpas_message_unref(m); 528 | 529 | r = wpas_message_new_reply(server, &m); 530 | ck_assert_int_ge(r, 0); 531 | r = wpas_send(server, m, 0); 532 | ck_assert_int_lt(r, 0); 533 | wpas_message_set_peer(m, "/some/peer"); 534 | r = wpas_send(server, m, 0); 535 | ck_assert_int_ge(r, 0); 536 | r = wpas_send(server, m, 0); 537 | ck_assert_int_lt(r, 0); 538 | wpas_message_unref(m); 539 | 540 | r = wpas_message_new_request(server, "sth", &m); 541 | ck_assert_int_ge(r, 0); 542 | r = wpas_call_async(server, m, NULL, NULL, 0, NULL); 543 | ck_assert_int_lt(r, 0); 544 | r = wpas_send(server, m, 0); 545 | ck_assert_int_lt(r, 0); 546 | wpas_message_set_peer(m, "/some/peer"); 547 | r = wpas_call_async(server, m, NULL, NULL, 0, NULL); 548 | ck_assert_int_lt(r, 0); 549 | r = wpas_send(server, m, 0); 550 | ck_assert_int_ge(r, 0); 551 | r = wpas_send(server, m, 0); 552 | ck_assert_int_lt(r, 0); 553 | wpas_message_unref(m); 554 | 555 | r = wpas_message_new_request(client, "sth", &m); 556 | ck_assert_int_ge(r, 0); 557 | r = wpas_call_async(client, m, NULL, NULL, 0, NULL); 558 | ck_assert_int_ge(r, 0); 559 | r = wpas_call_async(client, m, NULL, NULL, 0, NULL); 560 | ck_assert_int_lt(r, 0); 561 | wpas_message_unref(m); 562 | 563 | stop_test_client(); 564 | } 565 | END_TEST 566 | 567 | static int match_fail(struct wpas *w, 568 | struct wpas_message *m, 569 | void *data) 570 | { 571 | ck_assert_msg(0, "no CB expected"); 572 | return 0; 573 | } 574 | 575 | static int match_count(struct wpas *w, 576 | struct wpas_message *m, 577 | void *data) 578 | { 579 | int *expected = data; 580 | 581 | if (!m) 582 | ck_assert_msg(0, "HUP not expected"); 583 | 584 | if (!--*expected) 585 | sd_event_exit(event, 0); 586 | 587 | return 0; 588 | } 589 | 590 | START_TEST(run_send) 591 | { 592 | struct wpas_message *m; 593 | int r, expected; 594 | 595 | start_test_client(); 596 | 597 | r = wpas_add_match(client, NULL, NULL); 598 | ck_assert_int_lt(r, 0); 599 | r = wpas_add_match(client, match_count, &expected); 600 | ck_assert_int_ge(r, 0); 601 | r = wpas_add_match(server, match_count, &expected); 602 | ck_assert_int_ge(r, 0); 603 | 604 | expected = 2; 605 | 606 | r = wpas_message_new_event(client, "sth", 0, &m); 607 | ck_assert_int_ge(r, 0); 608 | r = wpas_send(client, m, 0); 609 | ck_assert_int_ge(r, 0); 610 | wpas_message_unref(m); 611 | 612 | r = wpas_message_new_request(client, "sth-more", &m); 613 | ck_assert_int_ge(r, 0); 614 | r = wpas_call_async(client, m, match_fail, NULL, 0, NULL); 615 | ck_assert_int_ge(r, 0); 616 | wpas_message_unref(m); 617 | 618 | r = sd_event_loop(event); 619 | ck_assert_int_ge(r, 0); 620 | 621 | stop_test_client(); 622 | } 623 | END_TEST 624 | 625 | static int match_msg(struct wpas *w, 626 | struct wpas_message *m, 627 | void *data) 628 | { 629 | struct wpas_message **orig = data; 630 | 631 | if (!m) 632 | ck_assert_msg(0, "HUP not expected"); 633 | 634 | ck_assert_str_eq(wpas_message_get_raw(m), 635 | wpas_message_get_raw(*orig)); 636 | 637 | sd_event_exit(event, 0); 638 | 639 | return 0; 640 | } 641 | 642 | START_TEST(run_parse) 643 | { 644 | struct wpas_message *m; 645 | int r; 646 | 647 | start_test_client(); 648 | 649 | r = wpas_add_match(server, match_msg, &m); 650 | ck_assert_int_ge(r, 0); 651 | 652 | r = wpas_message_new_request(client, "sth", &m); 653 | ck_assert_int_ge(r, 0); 654 | r = wpas_message_append(m, 655 | "ssie", 656 | "some random string\\''\"\"bla", 657 | "more-string\\data", 658 | (int32_t)65537, 659 | "key", 660 | "value=value=value"); 661 | ck_assert_int_ge(r, 0); 662 | r = wpas_send(client, m, 0); 663 | ck_assert_int_ge(r, 0); 664 | 665 | r = sd_event_loop(event); 666 | ck_assert_int_ge(r, 0); 667 | 668 | wpas_message_unref(m); 669 | 670 | stop_test_client(); 671 | } 672 | END_TEST 673 | 674 | TEST_DEFINE_CASE(run) 675 | TEST(run_invalid_msg) 676 | TEST(run_msg) 677 | TEST(run_send) 678 | TEST(run_parse) 679 | TEST_END_CASE 680 | 681 | TEST_DEFINE( 682 | TEST_SUITE(wpa, 683 | TEST_CASE(bus), 684 | TEST_CASE(msg), 685 | TEST_CASE(run), 686 | TEST_END 687 | ) 688 | ) 689 | --------------------------------------------------------------------------------