For nest-cam v350 release
Bug: 259322762
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..9b4b30d
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,73 @@
+*.o
+*.a
+*.lo
+*.la
+.deps
+.libs
+.dirstamp
+Makefile
+Makefile.in
+aclocal.m4
+config.guess
+config.h
+config.h.in
+config.log
+config.status
+config.sub
+configure
+depcomp
+compile
+install-sh
+libtool
+ltmain.sh
+missing
+stamp-h1
+autom4te.cache
+
+connman.pc
+include/connman
+include/version.h
+src/builtin.h
+src/connmand
+src/connman.conf
+src/connman.service
+src/*-connman.rules
+plugins/connman.policy
+scripts/connman
+scripts/openconnect-script
+scripts/openvpn-script
+client/connmanctl
+tools/wispr
+tools/dhcp-test
+tools/dhcp-server-test
+tools/addr-test
+tools/tap-test
+tools/web-test
+tools/wpad-test
+tools/resolv-test
+tools/polkit-test
+tools/iptables-test
+tools/supplicant-test
+tools/dbus-test
+tools/stats-tool
+tools/stats-ringbuffer-dump
+tools/private-network-test
+unit/test-session
+unit/test-ippool
+unit/test-nat
+
+doc/*.bak
+doc/*.stamp
+doc/connman.*
+!doc/connman.8
+!doc/connman.conf.5
+doc/connman-*.txt
+doc/*.sgml
+doc/version.xml
+doc/xml
+doc/html
+
+vpn/builtin.h
+vpn/connman-vpnd
+vpn/connman-vpn.service
+vpn/net.connman.vpn.service
diff --git a/.mailmap b/.mailmap
new file mode 100644
index 0000000..93fae59
--- /dev/null
+++ b/.mailmap
@@ -0,0 +1,4 @@
+Luiz Augusto von Dentz <luiz.dentz-von@nokia.com> <luiz.dentz-von@nokia.com>
+Leena Gunda <leena.gunda@wipro.com> <leena.gunda@wipro.com>
+Flávio Ceolin <flavio.ceolin@profusion.mobi> <flavio.ceolin@profusion.mobi>
+Daniel Wagner <daniel.wagner@bmw-carit.de> <daniel.wagner@bmw-carit.de>
diff --git a/AUTHORS b/AUTHORS
new file mode 100644
index 0000000..7035be6
--- /dev/null
+++ b/AUTHORS
@@ -0,0 +1,53 @@
+Marcel Holtmann <marcel@holtmann.org>
+Inaky Perez-Gonzalez <inaky@linux.intel.com>
+Samuel Ortiz <sameo@linux.intel.com>
+Joshua Lock <josh@linux.intel.com>
+Richard Purdie <rpurdie@linux.intel.com>
+Gustavo Sverzut Barbieri <barbieri@profusion.mobi>
+Martin Xu <martin.xu@intel.com>
+Sam Leffler <sleffler@google.com>
+Daniel Wagner <daniel.wagner@bmw-carit.de>
+Forest Bond <forest@alittletooquiet.net>
+Kalle Valo <kalle.valo@canonical.com>
+Fabien Marotte <fabienx.marotte@linux.intel.com>
+Pekka Pessi <pekka.pessi@nokia.com>
+Tomasz Bursztyka <tomasz.bursztyka@linux.intel.com>
+Cristiano Fernandes <cristiano.fernandes@hp.com>
+Joey Lee <jlee@novell.com>
+Leena Gunda <leena.gunda@wipro.com>
+Patrik Flykt <patrik.flykt@linux.intel.com>
+David Woodhouse <dwmw2@infradead.org>
+Gustavo Padovan <gustavo@padovan.org>
+Julien Massot <jmassot@aldebaran-robotics.com>
+Jukka Rissanen <jukka.rissanen@linux.intel.com>
+Grant Erickson <marathon96@gmail.com>
+Guillaume Lucas <guillaumex.lucas@intel.com>
+Henri Bragge <henri.bragge@ixonos.com>
+Alok Barsode <alok.barsode@intel.com>
+Sébastien Bianti <sebastien.bianti@linux.intel.com>
+Yu A Wang <yu.a.wang@intel.com>
+Thierry Boureille <thierry.boureille@gmail.com>
+Paolo Pellegrino <paolo.pellegrino@zirak.it>
+Bertrand Aygon <bertrand.aygon@intel.com>
+Jeff Zheng <jeff.zheng@intel.com>
+Philippe Nunes <philippe.nunes@linux.intel.com>
+Danny Jeongseok Seo <s.seo@samsung.com>
+Flávio Ceolin <flavio.ceolin@profusion.mobi>
+Arjan van de Ven <arjan@linux.intel.com>
+Daniel Mack <zonque@gmail.com>
+Guillaume Zajac <guillaume.zajac@linux.intel.com>
+Manfred Kober <manfred.kober@gmx.de>
+Mario Domenech Goulart <mario.goulart@gmail.com>
+Otavio Salvador <otavio@ossystems.com.br>
+Tim Sander <tim01@iss.tu-darmstadt.de>
+Adrien Bustany <adrien.bustany@nokia.com>
+Henrique Dante de Almeida <hdante@profusion.mobi>
+Lucas De Marchi <lucas.demarchi@profusion.mobi>
+Elena Tebesoi <elena.tebesoi@gmail.com>
+Mikel Astiz <mikel.astiz@bmw-carit.de>
+Paulo Pizarro <paulo.pizarro@gmail.com>
+Ross Burton <ross.burton@intel.com>
+Tudor Marcu <tudor.a.marcu@intel.com>
+Ceara Chewning <ceara.k.chewning@intel.com>
+Johannes Berg <johannes.berg@intel.com>
+Justin Maggard <jmaggard10@gmail.com>
diff --git a/COPYING b/COPYING
new file mode 100644
index 0000000..6d45519
--- /dev/null
+++ b/COPYING
@@ -0,0 +1,340 @@
+ GNU GENERAL PUBLIC LICENSE
+ Version 2, June 1991
+
+ Copyright (C) 1989, 1991 Free Software Foundation, Inc.
+ 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+ Preamble
+
+ The licenses for most software are designed to take away your
+freedom to share and change it. By contrast, the GNU General Public
+License is intended to guarantee your freedom to share and change free
+software--to make sure the software is free for all its users. This
+General Public License applies to most of the Free Software
+Foundation's software and to any other program whose authors commit to
+using it. (Some other Free Software Foundation software is covered by
+the GNU Library General Public License instead.) You can apply it to
+your programs, too.
+
+ When we speak of free software, we are referring to freedom, not
+price. Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+this service if you wish), that you receive source code or can get it
+if you want it, that you can change the software or use pieces of it
+in new free programs; and that you know you can do these things.
+
+ To protect your rights, we need to make restrictions that forbid
+anyone to deny you these rights or to ask you to surrender the rights.
+These restrictions translate to certain responsibilities for you if you
+distribute copies of the software, or if you modify it.
+
+ For example, if you distribute copies of such a program, whether
+gratis or for a fee, you must give the recipients all the rights that
+you have. You must make sure that they, too, receive or can get the
+source code. And you must show them these terms so they know their
+rights.
+
+ We protect your rights with two steps: (1) copyright the software, and
+(2) offer you this license which gives you legal permission to copy,
+distribute and/or modify the software.
+
+ Also, for each author's protection and ours, we want to make certain
+that everyone understands that there is no warranty for this free
+software. If the software is modified by someone else and passed on, we
+want its recipients to know that what they have is not the original, so
+that any problems introduced by others will not reflect on the original
+authors' reputations.
+
+ Finally, any free program is threatened constantly by software
+patents. We wish to avoid the danger that redistributors of a free
+program will individually obtain patent licenses, in effect making the
+program proprietary. To prevent this, we have made it clear that any
+patent must be licensed for everyone's free use or not licensed at all.
+
+ The precise terms and conditions for copying, distribution and
+modification follow.
+
+ GNU GENERAL PUBLIC LICENSE
+ TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+ 0. This License applies to any program or other work which contains
+a notice placed by the copyright holder saying it may be distributed
+under the terms of this General Public License. The "Program", below,
+refers to any such program or work, and a "work based on the Program"
+means either the Program or any derivative work under copyright law:
+that is to say, a work containing the Program or a portion of it,
+either verbatim or with modifications and/or translated into another
+language. (Hereinafter, translation is included without limitation in
+the term "modification".) Each licensee is addressed as "you".
+
+Activities other than copying, distribution and modification are not
+covered by this License; they are outside its scope. The act of
+running the Program is not restricted, and the output from the Program
+is covered only if its contents constitute a work based on the
+Program (independent of having been made by running the Program).
+Whether that is true depends on what the Program does.
+
+ 1. You may copy and distribute verbatim copies of the Program's
+source code as you receive it, in any medium, provided that you
+conspicuously and appropriately publish on each copy an appropriate
+copyright notice and disclaimer of warranty; keep intact all the
+notices that refer to this License and to the absence of any warranty;
+and give any other recipients of the Program a copy of this License
+along with the Program.
+
+You may charge a fee for the physical act of transferring a copy, and
+you may at your option offer warranty protection in exchange for a fee.
+
+ 2. You may modify your copy or copies of the Program or any portion
+of it, thus forming a work based on the Program, and copy and
+distribute such modifications or work under the terms of Section 1
+above, provided that you also meet all of these conditions:
+
+ a) You must cause the modified files to carry prominent notices
+ stating that you changed the files and the date of any change.
+
+ b) You must cause any work that you distribute or publish, that in
+ whole or in part contains or is derived from the Program or any
+ part thereof, to be licensed as a whole at no charge to all third
+ parties under the terms of this License.
+
+ c) If the modified program normally reads commands interactively
+ when run, you must cause it, when started running for such
+ interactive use in the most ordinary way, to print or display an
+ announcement including an appropriate copyright notice and a
+ notice that there is no warranty (or else, saying that you provide
+ a warranty) and that users may redistribute the program under
+ these conditions, and telling the user how to view a copy of this
+ License. (Exception: if the Program itself is interactive but
+ does not normally print such an announcement, your work based on
+ the Program is not required to print an announcement.)
+
+These requirements apply to the modified work as a whole. If
+identifiable sections of that work are not derived from the Program,
+and can be reasonably considered independent and separate works in
+themselves, then this License, and its terms, do not apply to those
+sections when you distribute them as separate works. But when you
+distribute the same sections as part of a whole which is a work based
+on the Program, the distribution of the whole must be on the terms of
+this License, whose permissions for other licensees extend to the
+entire whole, and thus to each and every part regardless of who wrote it.
+
+Thus, it is not the intent of this section to claim rights or contest
+your rights to work written entirely by you; rather, the intent is to
+exercise the right to control the distribution of derivative or
+collective works based on the Program.
+
+In addition, mere aggregation of another work not based on the Program
+with the Program (or with a work based on the Program) on a volume of
+a storage or distribution medium does not bring the other work under
+the scope of this License.
+
+ 3. You may copy and distribute the Program (or a work based on it,
+under Section 2) in object code or executable form under the terms of
+Sections 1 and 2 above provided that you also do one of the following:
+
+ a) Accompany it with the complete corresponding machine-readable
+ source code, which must be distributed under the terms of Sections
+ 1 and 2 above on a medium customarily used for software interchange; or,
+
+ b) Accompany it with a written offer, valid for at least three
+ years, to give any third party, for a charge no more than your
+ cost of physically performing source distribution, a complete
+ machine-readable copy of the corresponding source code, to be
+ distributed under the terms of Sections 1 and 2 above on a medium
+ customarily used for software interchange; or,
+
+ c) Accompany it with the information you received as to the offer
+ to distribute corresponding source code. (This alternative is
+ allowed only for noncommercial distribution and only if you
+ received the program in object code or executable form with such
+ an offer, in accord with Subsection b above.)
+
+The source code for a work means the preferred form of the work for
+making modifications to it. For an executable work, complete source
+code means all the source code for all modules it contains, plus any
+associated interface definition files, plus the scripts used to
+control compilation and installation of the executable. However, as a
+special exception, the source code distributed need not include
+anything that is normally distributed (in either source or binary
+form) with the major components (compiler, kernel, and so on) of the
+operating system on which the executable runs, unless that component
+itself accompanies the executable.
+
+If distribution of executable or object code is made by offering
+access to copy from a designated place, then offering equivalent
+access to copy the source code from the same place counts as
+distribution of the source code, even though third parties are not
+compelled to copy the source along with the object code.
+
+ 4. You may not copy, modify, sublicense, or distribute the Program
+except as expressly provided under this License. Any attempt
+otherwise to copy, modify, sublicense or distribute the Program is
+void, and will automatically terminate your rights under this License.
+However, parties who have received copies, or rights, from you under
+this License will not have their licenses terminated so long as such
+parties remain in full compliance.
+
+ 5. You are not required to accept this License, since you have not
+signed it. However, nothing else grants you permission to modify or
+distribute the Program or its derivative works. These actions are
+prohibited by law if you do not accept this License. Therefore, by
+modifying or distributing the Program (or any work based on the
+Program), you indicate your acceptance of this License to do so, and
+all its terms and conditions for copying, distributing or modifying
+the Program or works based on it.
+
+ 6. Each time you redistribute the Program (or any work based on the
+Program), the recipient automatically receives a license from the
+original licensor to copy, distribute or modify the Program subject to
+these terms and conditions. You may not impose any further
+restrictions on the recipients' exercise of the rights granted herein.
+You are not responsible for enforcing compliance by third parties to
+this License.
+
+ 7. If, as a consequence of a court judgment or allegation of patent
+infringement or for any other reason (not limited to patent issues),
+conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License. If you cannot
+distribute so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you
+may not distribute the Program at all. For example, if a patent
+license would not permit royalty-free redistribution of the Program by
+all those who receive copies directly or indirectly through you, then
+the only way you could satisfy both it and this License would be to
+refrain entirely from distribution of the Program.
+
+If any portion of this section is held invalid or unenforceable under
+any particular circumstance, the balance of the section is intended to
+apply and the section as a whole is intended to apply in other
+circumstances.
+
+It is not the purpose of this section to induce you to infringe any
+patents or other property right claims or to contest validity of any
+such claims; this section has the sole purpose of protecting the
+integrity of the free software distribution system, which is
+implemented by public license practices. Many people have made
+generous contributions to the wide range of software distributed
+through that system in reliance on consistent application of that
+system; it is up to the author/donor to decide if he or she is willing
+to distribute software through any other system and a licensee cannot
+impose that choice.
+
+This section is intended to make thoroughly clear what is believed to
+be a consequence of the rest of this License.
+
+ 8. If the distribution and/or use of the Program is restricted in
+certain countries either by patents or by copyrighted interfaces, the
+original copyright holder who places the Program under this License
+may add an explicit geographical distribution limitation excluding
+those countries, so that distribution is permitted only in or among
+countries not thus excluded. In such case, this License incorporates
+the limitation as if written in the body of this License.
+
+ 9. The Free Software Foundation may publish revised and/or new versions
+of the General Public License from time to time. Such new versions will
+be similar in spirit to the present version, but may differ in detail to
+address new problems or concerns.
+
+Each version is given a distinguishing version number. If the Program
+specifies a version number of this License which applies to it and "any
+later version", you have the option of following the terms and conditions
+either of that version or of any later version published by the Free
+Software Foundation. If the Program does not specify a version number of
+this License, you may choose any version ever published by the Free Software
+Foundation.
+
+ 10. If you wish to incorporate parts of the Program into other free
+programs whose distribution conditions are different, write to the author
+to ask for permission. For software which is copyrighted by the Free
+Software Foundation, write to the Free Software Foundation; we sometimes
+make exceptions for this. Our decision will be guided by the two goals
+of preserving the free status of all derivatives of our free software and
+of promoting the sharing and reuse of software generally.
+
+ NO WARRANTY
+
+ 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
+FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
+OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
+PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
+OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS
+TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE
+PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
+REPAIR OR CORRECTION.
+
+ 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
+REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
+INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
+OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
+TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
+YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
+PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGES.
+
+ END OF TERMS AND CONDITIONS
+
+ How to Apply These Terms to Your New Programs
+
+ If you develop a new program, and you want it to be of the greatest
+possible use to the public, the best way to achieve this is to make it
+free software which everyone can redistribute and change under these terms.
+
+ To do so, attach the following notices to the program. It is safest
+to attach them to the start of each source file to most effectively
+convey the exclusion of warranty; and each file should have at least
+the "copyright" line and a pointer to where the full notice is found.
+
+ <one line to give the program's name and a brief idea of what it does.>
+ Copyright (C) <year> <name of author>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+
+
+Also add information on how to contact you by electronic and paper mail.
+
+If the program is interactive, make it output a short notice like this
+when it starts in an interactive mode:
+
+ Gnomovision version 69, Copyright (C) year name of author
+ Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
+ This is free software, and you are welcome to redistribute it
+ under certain conditions; type `show c' for details.
+
+The hypothetical commands `show w' and `show c' should show the appropriate
+parts of the General Public License. Of course, the commands you use may
+be called something other than `show w' and `show c'; they could even be
+mouse-clicks or menu items--whatever suits your program.
+
+You should also get your employer (if you work as a programmer) or your
+school, if any, to sign a "copyright disclaimer" for the program, if
+necessary. Here is a sample; alter the names:
+
+ Yoyodyne, Inc., hereby disclaims all copyright interest in the program
+ `Gnomovision' (which makes passes at compilers) written by James Hacker.
+
+ <signature of Ty Coon>, 1 April 1989
+ Ty Coon, President of Vice
+
+This General Public License does not permit incorporating your program into
+proprietary programs. If your program is a subroutine library, you may
+consider it more useful to permit linking proprietary applications with the
+library. If this is what you want to do, use the GNU Library General
+Public License instead of this License.
diff --git a/ChangeLog b/ChangeLog
new file mode 100644
index 0000000..f77dede
--- /dev/null
+++ b/ChangeLog
@@ -0,0 +1,836 @@
+ver 1.11:
+ Fix issue with agent reference counting imbalance.
+ Fix issue with handling max number of SSID for scanning.
+ Fix issue with missing notification of online state changes.
+ Fix issue with not properly triggering auto-connect behavior.
+ Fix issue with disabling IPv6 in lower up interface states.
+ Fix issue with spurious error messages for interface handling.
+ Fix issue with wrong answer count in DNS responses.
+ Fix issue with crash in DNS lookup function.
+ Add support for BlueZ 5.x network interfaces.
+ Remove deprecated WiMAX support.
+
+ver 1.10:
+ Fix issue with not skipping service if settings loading fails.
+ Fix issue with not clearing address before starting DHCP.
+ Fix issue with not handling removal of GPRS context.
+ Fix issue with not closing UDP socket on error condition.
+ Fix issue with race condition when removing WiFi device.
+ Add support for separate VPN daemon.
+
+ver 1.9:
+ Fix issue with WISPr portal context handling.
+ Fix issue with DNS lookup from wrong queue.
+ Fix issue with DNS data reception after disconnect.
+ Fix issue with missing DNS host part length checking.
+ Fix issue with RFKILL and technology interaction.
+ Fix issue with tethering and disabled technologies.
+ Add support for single connected technology setting.
+
+ver 1.8:
+ Fix issue with NTP transmit time calculation.
+ Fix issue with WiFi Tethering and newer kernels.
+ Fix issue with Netlink messages from Wireless Extensions.
+ Fix issue with IPv6 nameserver refresh beeing applied to IPv4.
+ Fix issue with overwriting DNS proxy address information.
+ Fix issue with missing handling of RFKILL hard blocking.
+ Add support for disabling internal backtrace functionality.
+
+ver 1.7:
+ Fix issue with IPv4 address removal when setting interface down.
+ Fix issue with wrong error when setting tethering support option.
+ Fix issue with errors reported twice via agent to the client.
+ Fix issue with missing serialization of agent callbacks.
+ Add initial version of command line client tool.
+
+ver 1.6:
+ Fix issue with Bluetooth networking support.
+ Fix issue with technology enabling method returns.
+ Fix issue with wrong IP address for fixed configurations.
+ Fix issue with IP address setting when interface is down.
+ Fix issue with handling duplicate hidden WiFi networks.
+ Fix issue with missing scanning for hidden WiFi networks.
+ Fix issue with missing update of service properties.
+ Fix issue with missing clearing of service errors.
+ Add manual pages for daemon and configuration file.
+
+ver 1.5:
+ Fix issue with detecting Bluetooth networks when powered off.
+ Fix issue with connection attempts of non-favorite services.
+ Fix issue with connection attempts of disabled IP configurations.
+ Fix issue with missing auto-connection after changing IP method.
+ Fix issue with setting service state when changing IPv4 method.
+ Fix issue with IPv6 usage and static/manual configuration.
+ Add support for configuration option to disable hostname updates.
+ Add support for storing WiFi Tethering identifier and passphrase.
+ Add support for signaling changes of error property.
+
+ver 1.4:
+ Fix issue with WiFi scanning in Tethering mode.
+ Fix issue with WISPr operation and disconnects.
+ Fix issue with DHCP client and restart behavior.
+ Fix issue with DNS resolving and failing IPv6 records.
+ Fix issue with incorrect NTP leap-not-in-sync flag.
+ Fix issue with incorrect NTP transmit time value.
+ Fix issue with failing NTP server due to routing.
+ Fix issue with missing gateway change notification.
+ Fix issue with stale network interfaces at startup.
+ Fix issue with pending method reply and agent errors.
+ Add support for providing previous WPS PIN to agent.
+ Add support for WPA supplicant based auto-scanning.
+ Add support for per device regulatory domain setting.
+ Add support for provisioning hidden WiFi networks.
+
+ver 1.3:
+ Fix issue with default configuration values.
+ Fix issue with timeserver canonical name entries.
+ Fix issue with crash from cellular dummy context.
+ Fix issue with incorrect index for private networks.
+
+ver 1.2:
+ Fix issue with not handling WiFi security changes.
+ Fix issue with not stopping WiFi scanning on shutdown.
+ Fix issue with auto-scanning and network discovery.
+ Fix issue with D-Bus reply for hidden WiFi networks.
+ Fix issue with overlapping memory areas and DNS requests.
+ Add support for randomized DNS transaction identifiers.
+ Add support for DNS caching over TCP connections.
+ Add support for using default IPv6 privacy setting.
+ Add support for providing previous passphrase to agent.
+ Add support for configuration unprovisioning handling.
+ Add support for NetworkInterfaceBlacklist configuration.
+ Add support for Bluetooth DUN daemon (dundee).
+
+ver 1.1:
+ Fix issue with missing message type and DHCPv4 support.
+ Fix issue with potential NULL pointer in DHCPv6 handling.
+ Fix issue with potential NULL pointer in VPN handling.
+ Fix issue with potential NULL pointer for WiFi SSID.
+ Fix issue with missing conversion of raw WiFi PSK input.
+ Fix issue with missing stop for WiFi auto-scanning handling.
+ Fix issue with uninitialized IPv6 prefix length in oFono plugin.
+ Fix issue with domain search list handling according to RFC 6106.
+ Fix issue with domain name list notifications.
+ Fix issue with nameserver list notifications.
+ Fix issue with incorrect fixed IP configuration.
+ Fix issue with incorrect cleanup of resolver timers.
+ Fix issue with handling of RDNSS lifetime expiration.
+ Fix issue with crash on wrong domain length information.
+ Add support for favorite service database migration.
+ Add support for disabling WISPr functionality.
+ Add support for configurable agent timeouts.
+
+ver 1.0:
+ Fix issue with missing WiFi disconnecting flag.
+ Fix issue with missing GPRS context attached check.
+ Fix issue with potential crash and supplicant handling.
+ Fix issue with potential crash and VPN provider.
+ Fix issue with potential crash and host routes.
+
+ver 0.85:
+ Fix issue with duplicate service timeservers.
+ Fix issue with failure state when aborting agent request.
+ Fix issue with automatic scanning for hidden WiFi networks.
+ Fix issue with missing hostname/domainname validity checks.
+ Fix issue with missing DHCP packet length checks.
+ Add support for browser launching from WISPr login.
+
+ver 0.84:
+ Fix issue with handling changed WiFi security of access points.
+ Fix issue with state notification of NetworkManager compatibility.
+
+ver 0.83:
+ Fix issue with Ethernet not being enabled by default.
+ Fix issue with missing host routes for WISPr request.
+ Fix issue with missing HTTPS handling for WISPr support.
+ Fix issue with agent asking for passphrase for open service.
+ Fix issue with endless online check for preferred technology.
+ Fix issue with IPv6 RDNSS and DNS server creation.
+ Fix issue with WiFi roaming state change handling.
+ Fix issue with broken handling of WPS PBC method.
+
+ver 0.82:
+ Fix issue with global online state handling.
+ Fix issue with timeserver handling in case of VPN.
+ Fix issue with automatic WiFi scanning handling.
+ Fix issue with WPS PIN length of zero and PBC.
+
+ver 0.81:
+ Update default configuration options.
+ Add support for WPS PBC advertising handling.
+ Add support for wpa_supplicant background scanning.
+ Fix issue with showing cellular services without APN.
+ Fix issue with missing timeservers changed signal.
+
+ver 0.80:
+ Update to support additional D-Bus API changes.
+ Add support for preferred technologies switching.
+ Add support for default auto connect technologies option.
+ Add support for service specific timeserver configuration.
+ Add support for extended timeserver fallback functionality.
+ Add support for extended nameserver fallback functionality.
+ Add support for user supplied routes for providers.
+ Add support for checking WiFi passphrase validity.
+ Fix issue with WISPr support and proxy handling.
+ Fix issue with Ethernet and auto connect handling.
+ Fix issue with too early IPv6 auto connection.
+ Fix issue with too early 6to4 connection checks.
+ Fix issue with oFono interaction on disconnect.
+ Fix issue with DNS servers behind point-to-point links.
+ Fix issue with pending DNS proxy requests.
+ Fix multiple issues with VPN handling.
+ Fix multiple issues with default gateway handling.
+
+ver 0.79:
+ Update to support changed D-Bus API.
+ Add support for WiFi background scanning.
+ Add support for showing hidden WiFi networks as services.
+ Add support for generic stateless DHCPv6 handling.
+ Add support for DHCPv4 client identifier handling.
+ Add support for generic IP address pool handling.
+ Add support for global DNS cache handling.
+ Add support for internal NTP time handling.
+ Add support for updated oFono handling.
+
+ver 0.78:
+ Fix multiple issues with service connection states.
+ Fix multiple issues with technology handling.
+ Fix issue with DHCP file descriptor leakage.
+ Fix issue with open access points and WPS.
+ Fix issue with handling of cellular devices.
+ Fix issue with DNS proxy hostname resolving.
+ Add support for PPTP and L2TP VPN tunneling.
+ Add support for organized settings storage.
+ Add support for WiFi fast connect handling.
+ Add support for better WiFi error handling.
+ Add support for integrated WISPr handling.
+
+ver 0.77:
+ Fix issue with iptables API breakage.
+ Fix issue with agent input handling.
+ Fix issue with empty cellular operator name.
+ Fix issue with reference counting for network objects.
+ Fix issue with missing D-Bus signals for proxy changes.
+ Fix issue with group identifier and hidden WiFi networks.
+ Fix issue with setting wrong gateway for PPP connections.
+ Fix issue with mismatch of stored IP configuration settings.
+ Fix issue with not stopping DHCP for IPv4 configuration.
+ Add support for remembering last IP address from DHCP.
+ Add support for EAP-GTC authentication method.
+
+ver 0.76:
+ Fix issue with loopback interface setup.
+ Fix issue with /etc/localtime being a symlink.
+ Fix issue with not handling dummy network devices.
+ Fix issue with not provisioning existing services.
+ Fix issue with running WPAD on IPv6 connections.
+ Fix issue with client certificate for TTLS/PEAP.
+ Remove internal element infrastructure.
+
+ver 0.75:
+ Fix issue with 3G connect timeout handling.
+ Fix issue with WiFi raw key PSK handling.
+ Fix issue with DHCP renewal timeout handling.
+ Fix issue with DHCP and empty nameserver list.
+ Add support for unit testing.
+
+ver 0.74:
+ Fix issue with race condition in ready/online handling.
+ Fix issue with DHCP release callback handling.
+ Fix multiple issues with session API handling.
+ Add support for using DNS proxy for Tethering.
+ Add support for Private Network API.
+ Add support for Clock API.
+
+ver 0.73:
+ Update support for session API handling.
+ Add support for more advanced roaming policies.
+ Add support for EAP identity and passphrase queries.
+ Add support for IPv6 handling with cellular bearer.
+ Add support for main configuration file.
+ Remove deprecated profile interface.
+
+ver 0.72:
+ Fix issue with overwriting DNS servers from DHCP.
+ Fix issue with DHCP renewal with same configuration.
+ Fix issue with multiple memory leaks.
+ Add support for 6to4 tunneling.
+
+ver 0.71:
+ Fix issue with not storing IPv6 privacy settings.
+ Fix issue with storing fixed and manual IP settings.
+ Fix issue with service state when PAN connection fails.
+ Fix issue with tethering and WiFi bridge handling.
+ Fix issue with autonomously activated contexts.
+ Fix issue with nameserver array for PACrunner.
+ Fix issue with network information memory leak.
+ Fix issue with double-free in statistics handling.
+ Fix issue with handling malformed profiles.
+ Fix issue with pending DHCP client requests.
+ Add initial support for TI shared transport handling.
+
+ver 0.70:
+ Add support for reporting invalid WiFi passphrases.
+ Add support for IPv6 privacy extension.
+ Add support for IPv6 advanced features.
+ Add support for IPv6 nameserver settings.
+ Remove deprecated APN service settings.
+
+ver 0.69:
+ Fix issue with not handling DNS proxy failures gracefully.
+ Fix issue with double free in statistics handling.
+ Fix issue with early tethering bridge creation.
+ Add support for tethering property per technology.
+ Add support for initial WiFi tethering feature.
+ Add support for using PACrunner as proxy driver.
+ Add support for WPS as part of the security property.
+
+ver 0.68:
+ Fix issue with wrong name of PolicyKit configuration file.
+ Fix issue with inconsistency of WiFi scan states.
+ Fix issue with missing auto-reconnect and oFono.
+ Add support for vpnc based private networks.
+ Add support for WiFi Protected Setup handling.
+ Remove legacy WiFi plugin.
+
+ver 0.67:
+ Fix issue with oFono plugin and multiple online calls.
+ Fix issue with checking for AutoConnect service property.
+ Remove deprecated MCC and MNC service properties.
+
+ver 0.66:
+ Fix multiple set of memory leaks.
+ Fix issue with WPA supplicant phase2 values.
+ Fix issue with WiFi access points after kill/restart.
+ Fix issue with correct PACrunner configuration loading.
+ Add support for loading provision files at runtime.
+ Add support for setting proxy auto-configuration.
+ Add support for IPv6 auto-configured addresses.
+
+ver 0.65:
+ Use new WiFi plugin by default.
+ Fix issue with handling already powered devices.
+ Fix issue with handling proxy PAC option from DHCP.
+ Add support for handling regulatory domain settings.
+ Add support for handling IPv6 router advertisements.
+ Add support for handling IPv6 nameservers.
+ Add support for handling multiple search domains.
+ Add support for handling oFono modem lockdown.
+ Add support for handling IPv6 DNS connections.
+ Add support for IPv4 Link-Local negotiation.
+ Add support for USB CDC Tethering functionality.
+
+ver 0.64:
+ Update service name to net.connman domain.
+ Fix issue with disabling a technology twice.
+ Fix issue with using wrong string for Proxy.Method.
+ Fix issue with TCP connection lookup and DNS proxy.
+ Fix issue with TCP receive busy waits and DNS proxy.
+ Fix various issues with WPA Supplicant interaction.
+ Add support for chunk encoding to HTTP client.
+ Add support for internal HTTP client for portal detection.
+ Add support for internal DHCP server setup.
+ Add support for internal NAT and IP forwarding setup.
+ Add support for Bluetooth Tethering functionality.
+ Remove deprecated device and network D-Bus interfaces.
+ Remove support for dhclient plugin.
+
+ver 0.63:
+ Change to use nl80211/cfg80211 WiFi management by default.
+ Fix various issues with new WPA Supplicant interface.
+ Fix issue with not connecting failed networks at boot.
+ Fix issue with properly tracking RFKILL blocked state.
+ Fix issue with missing signals for IPv4/IPv6 gateway details.
+ Add support for using RTNL for setting IPv4/IPv6 details.
+ Add support for using PHONET_PIPE GPRS interfaces.
+ Add support for setting manual proxy configurations.
+ Add support for exporting proxy configurations to PACrunner.
+ Add support for combined home and roaming statistics.
+ Add support for OpenVPN connections.
+ Remove dependency on udev.
+
+ver 0.62:
+ Fix crash with non-existent or extra DHCP result options.
+ Fix crash when doing PEAP/TTLS authentication.
+ Fix issue with handling multiple data counters.
+ Fix issue with Bluetooth adapters without address.
+ Fix issue with multiple scanning attempts after disconnects.
+ Fix issue with VPN services when switching into offline mode.
+ Add support for storing statistics information in separate files.
+ Add support for verification of configuration file parameters.
+ Add support for handling time server values from DHCP.
+ Add support for allowing DNS over TCP within the DNS proxy.
+ Add support for loading proxy configurations into PACrunner.
+ Add support for WiFi plugin using new WPA Supplicant D-Bus API.
+ Add support for requesting passphrases via agents.
+ Remove default support for EDNS0 option.
+
+ver 0.61:
+ Add support for using the internal DHCP client by default.
+ Add support for latest PolicyKit framework.
+ Add support for new oFono D-Bus interfaces.
+
+ver 0.60:
+ Fix issue with missing reset of proxy settings.
+ Fix issue with missing Ethernet property changed signal.
+ Fix issue with offline operation on already blocked devices.
+ Fix issue with offline mode and device powered changes.
+ Fix issue with portal detection and DHCP renewals.
+ Fix issue with connection attempts for removed networks.
+ Fix issue with stale pointers of networks.
+
+ver 0.59:
+ Fix issue with D-Bus object paths of VPN providers.
+
+ver 0.58:
+ Fix various issues around offline mode.
+ Fix various issues with VPN nameserver handling.
+ Add support for home/roaming network statistics.
+ Add support for EAP-TTLS WiFi configuration.
+ Add support for creating backtraces.
+
+ver 0.57:
+ Fix missing default profile creation.
+ Fix missing service integration of VPN providers.
+ Fix missing export of PAC information retrieved from DHCP.
+ Fix issue with detection of new Bluetooth devices.
+ Fix issue with offline mode handling.
+ Fix issue with device power handling.
+
+ver 0.56:
+ Fix issues with offline mode handling.
+ Fix service integration with VPN providers.
+ Add internal asynchronous resolver library.
+ Add internal DHCP client library.
+ Add support for using internal DHCP client.
+ Add support for WPAD proxy auto-configuration.
+ Add support for static IPv6 configuration.
+ Add support for DHCP provided domain names.
+ Add initial support for on-demand connections.
+ Remove uDHCP and resolvconf plugins.
+
+ver 0.55:
+ Fix issue with 3G roaming status indication.
+ Fix issue with using -H option with dhclient.
+ Fix issue with loading WiFi SSID details for scanning.
+ Add support for setting host routes for DNS servers.
+ Add support for more detailed statistics counters.
+ Add support for internal DHCP client library.
+
+ver 0.54:
+ Fix issue with root requests and EDNS0 OPT records.
+ Fix issue with default gateway when route deletion fails.
+ Fix issue with group identifiers for cellular networks.
+ Fix issue with fixed IP settings from cellular networks.
+ Fix issue with nameserver settings and manual configuration.
+ Add support for cellular network name changes.
+ Add support for cellular signal strength changes.
+ Add support for actively scanning for hidden networks.
+ Add support for ASCII based WEP keys.
+ Add support for NTP timeserver updates.
+ Add support for PPP default route settings.
+
+ver 0.53:
+ Fix issue with supplicant and device scanning state cleaning.
+ Fix issue with Bluetooth PAN networks stay in connected state.
+ Fix issue with reference counting and connected state.
+ Fix issue with technology disabling on device removal.
+ Fix issue with two default gateways when using VPN.
+ Fix issue with static IPv4 configuration and signals.
+ Add support for splitting DHCP provided nameserver results.
+ Add support multiple nameservers in /etc/resolv.conf.
+ Add support for setting manual DNS server configuration.
+ Add support for exporting IPv4 gateway information.
+ Add support for newer versions of oFono API.
+
+ver 0.52:
+ Fix issue with new "connected" states.
+ Fix issue with hidden networks and PSK.
+ Fix issue with DHCP and Bluetooth PAN.
+ Fix issue when disconnecting PAN networks.
+ Add support for application sessions.
+ Add plugin for hh2serial GPS support.
+
+ver 0.51:
+ Fix issue with missing device power toggling.
+ Fix issue with D-Bus object path on device removal.
+ Add support for WiFi portal detection.
+ Add support for configuring static gateways.
+ Remove unneeded plugin for Option HSO support.
+ Remove unneeded plugin for Ericsson MBM support.
+
+ver 0.50:
+ Fix configuration loading for unknown services.
+ Fix IP method setting of Ethernet plugin.
+
+ver 0.49:
+ Fix issue with WiFi power changes.
+ Fix issue with Bluetooth device startup.
+ Fix issue with host route settings for VPN.
+ Fix issue with processing of RFKILL events.
+ Fix some WPA Enterprise privacy issues.
+ Add support for basic Ethernet information.
+ Add support for static IP settings.
+
+ver 0.48:
+ Fix signal strength calculation when quality is not provided.
+ Fix issues with wpa_supplicant state tracking.
+ Fix faulty removal of IP address from interface.
+ Fix permissions of newly created /etc/resolv.conf file.
+ Fix DNS proxy handling when in offline mode.
+ Add support for EDNS0 resolver option.
+ Add workaround for large EDNS0 queries.
+ Add workaround for DHCP startup failures with WiFi networks.
+ Add support for handling hostnames and domainnames.
+ Add support for IPv4 configuration via service interface.
+ Add support for fixed and manual IPv4 configuration.
+ Add support for default service changed notifier.
+ Add support for clearing failure state via service removal.
+ Add support for OpenConnect VPN connections.
+ Add support for IEEE 802.1x WiFi networks.
+ Add support for roaming between WPA and WPA2 networks.
+ Add various generic D-Bus helpers and use them.
+ Remove special handling of Ethernet devices.
+
+ver 0.47:
+ Fix segmentation fault on resolver shutdown.
+ Fix issue with adding nameserver that doesn't exist.
+ Fix issue when no broadcast address is given.
+ Fix issue with missing property changed signal.
+ Add checks for invalid supplicant state transitions.
+ Add initial version of oFono GPRS support.
+ Add support for dynamic debug framework.
+
+ver 0.46:
+ Fix reconnect issue when power off or disabling the device.
+ Remove problematic retry on failure code path.
+
+ver 0.45:
+ Fix crash with connect timeout and second connect attempt.
+ Fix reconnect issues after suspend or roaming attempt.
+
+ver 0.44:
+ Fix command line options for device filtering.
+ Fix issue with network reference in MBM support.
+ Fix handling when losing network access in MBM plugin.
+ Fix broken libiWmxSDK callback parameter handling.
+ Add work around Intel WiMAX SDK API breakage.
+
+ver 0.43:
+ Fix issue with missing scanning after power up.
+ Fix issue with udev versus /dev/rfkill event processing.
+ Fix issue with powered down device on connection attempt.
+ Add support for multiple connection attempts.
+ Add support for tracking the operation state.
+ Add full support for Ericsson MBM cellular devices.
+
+ver 0.42:
+ Fix issue with switching between hidden WiFi networks.
+ Fix issue with missing scanning after disconnect.
+ Fix issue with not triggering auto-connect in some cases.
+
+ver 0.41:
+ Fix race condition with WiFi devices and RFKILL.
+ Fix issue with WiFi connect/disconnect and some drivers.
+ Fix issue with WEP encryption and staging drivers.
+ Fix issue with wrong setup of loopback interfaces.
+
+ver 0.40:
+ Fix issue with wrong setting of initial AutoConnect value.
+ Fix issue with IP configuration and loopback devices.
+ Fix issue with build system and include directory.
+ Fix wrong variable for dhclient-script location.
+ Fix disconnect race condition with Bluetooth service.
+ Add support for ignoring bonding Ethernet interfaces.
+
+ver 0.39:
+ Fix file permissions for profile storage.
+ Fix service resorting when they are in different states.
+ Fix support for handling Bluetooth PAN devices.
+ Add support for AutoConnect property of services.
+ Add support for creating, modifying and removing profiles.
+ Add support for fully flexible task handling framework.
+ Add support for more generic RTNL handling and notifications.
+ Add support for full non-recursive build.
+
+ver 0.38:
+ Fix broken check for security modes.
+ Fix requirement of inotify when loopback support is disabled.
+
+ver 0.37:
+ Fix missing update of signal strength from scan results.
+ Fix error handling in case when passphrase is required.
+ Add support for PassphraseRequired property.
+ Add missing check for WiFi security modes.
+
+ver 0.36:
+ Fix missing reset of network reference when disconnecting.
+ Fix wrong variable reference when sending technology replies.
+ Fix wrong identifiers of D-Bus error names.
+
+ver 0.35:
+ Fix missing auto-connect trigger on Ethernet device removal.
+ Fix availability listing for devices without attached drivers.
+ Fix signals for connected and default technologies.
+ Fix notification to use service types instead of device types.
+ Fix potential pending scan result reply messages after removal.
+ Add support for blocking enable and disable technology changes.
+
+ver 0.34:
+ Fix setup of udev context before loading any plugins.
+ Fix rearming the scan trigger if a device got disabled.
+ Fix device power state changes tracking with RFKILL notifications.
+ Fix wrong usage of device types instead of service types.
+ Fix connect method to handle non-WiFi services.
+
+ver 0.33:
+ Add support for RFKILL changes of the WiFi subsystem.
+ Fix state value of Network Manager compatibility support.
+
+ver 0.32:
+ Fix broken device unregistration on removal.
+ Fix WiMAX device detection handling.
+
+ver 0.31:
+ Fix missing enforcement of offline mode for new devices.
+ Add support for persistent storage of offline mode.
+ Add support for persistent storage of device power state.
+ Remove deprecated and unused network storage callbacks.
+
+ver 0.30:
+ Fix issue where hidden network could show up in service list.
+ Fix issue with asynchronous notification of scan requests.
+ Fix message reference leak when adding interface fails.
+ Fix problem when removing network during inactive state.
+ Remove broken and unused callback for joining networks.
+ Remove deprecated device and network interface methods.
+ Remove test scripts for deprecated interface methods.
+
+ver 0.29:
+ Fix missing signal emission for offline mode changes.
+ Fix signal emission for changes in technology properties.
+ Rename Technologies property to AvailableTechnologies.
+
+ver 0.28:
+ Fix another reference counting imbalance when adding networks.
+ Revert supplicant change to always reset scanning after results.
+
+ver 0.27:
+ Fix missing disarming of the connection timeout.
+ Fix handling of multiple supplicant disconnect attempts.
+ Fix simultaneous connects from different technologies limitation.
+
+ver 0.26:
+ Fix broken handling of auto-connect logic.
+ Fix handling of out-of-range access points.
+ Fix support for connecting to hidden networks.
+ Fix reference counting for networks with same SSID.
+ Fix issue with WiFi interfaces not getting switched off.
+ Fix problems with delayed service list updates.
+ Fix disconnect/abort of connection attempts.
+
+ver 0.25:
+ Fix showing of WiFi networks with less than 25% signal strength.
+ Fix potential segmentation fault with network passphrases.
+
+ver 0.24:
+ Fix handling of initial device powered state.
+ Fix missing Powered property changed signals.
+ Fix canceling of a network connection attempt.
+ Fix stalled configuration issue with supplicant.
+ Fix detection of association errors from supplicant.
+ Fix issue with wrong scanning state information.
+ Fix hidden SSID detection routines.
+ Fix visible Ethernet services even without carrier.
+ Add global method call to request scanning.
+ Add support for global technologies list.
+ Add support for delaying service list updates.
+ Update the overall D-Bus API documentation.
+
+ver 0.23:
+ Fix dhclient probe/remove race condition.
+ Fix handling of disconnected services during auto-connect.
+ Add support for proper group name of hidden networks.
+ Add support for storing SSID details of hidden networks.
+
+ver 0.22:
+ Fix wrong auto-connect procedure after user connection.
+ Fix invalid update of already connected network.
+ Fix idle state handling after disconnecting device.
+ Fix disconnect race condition in WiFi supplicant.
+ Fix WiFi signal strength reporting.
+
+ver 0.21:
+ Add udev based network device detection.
+ Add support for global auto-connect feature.
+ Add support for basic service drag and drop.
+ Fix missing passphrase cleanup on service removal.
+ Fix potential duplicate network creation.
+ Fix handling of WEP shared keys.
+
+ver 0.20:
+ Add plugin for Intel WiMAX SDK support.
+ Add special handling for default vendor SSIDs.
+ Add support for default gateway in different network.
+ Add support for automatic switching of default gateway.
+ Add support for asynchronous handling of Powered property.
+ Add support for connecting/disconnecting Ethernet services.
+ Add support for more detailed error states of services.
+ Add support for clearing error state via ClearProperty.
+ Fix error code for invalid or unknown properties.
+ Fix various timeout handling issues.
+ Remove Policy and Priority device and network properties.
+
+ver 0.19:
+ Add hidden networks to the service list.
+ Add support for storing the service name.
+ Fix service list sorting for connected services.
+ Fix missing cancel command when operation times out.
+ Fix various issues with service favorite handling.
+ Remove Available and Remember network properties.
+
+ver 0.18:
+ Add support for asynchronous service connect method.
+ Fix broken storage of service favorite details.
+
+ver 0.17:
+ Add AT chat library implementation.
+ Fix service lookup for WiFi and WiMAX devices.
+ Fix service state signal emission and error handling.
+ Fix storing and loading of configured passphrases for services.
+
+ver 0.16:
+ Update Intel OSPM support to latest specification.
+ Add initial support for new service interface.
+ Add support for builtin plugins.
+ Add extra warning if no nameserver is defined.
+ Add error reporting for state and storage directory creation.
+ Add error message for network and device storing failures
+ Fix stale entry in gateway list after connection changes.
+ Fix handling of DHCP results with no nameserver.
+ Fix infinite loop for service lookup.
+ Fix various format string warnings.
+
+ver 0.15:
+ Detect VMware network interface and ignore them.
+ Fix setting of scan_ssid for hidden networks.
+ Fix empty network name property.
+
+ver 0.14:
+ Add support for detecting DHCP failures.
+ Add support for joining hidden WiFi networks.
+ Add support for device and network address property.
+ Add support for default /etc/resolv.conf generation.
+ Fix issue with wrong address setting for loopback.
+ Fix detection of WiFi access point changes.
+ Fix crash with blob properties.
+
+ver 0.13:
+ Add support for notification infrastructure.
+ Add fully dynamic property storage capabilities.
+ Fix broken loading of last network on bootup.
+ Fix crash when unplugging WiFi devices.
+ Rename OSPM plugin to Intel OSPM plugin.
+ Rename WiMAX plugin to Intel WiMAX SDK plugin.
+
+ver 0.12:
+ Fix connection state change handling.
+ Fix network list enumeration.
+ Fix broken driver matching for devices.
+ Fix issue with network identifier lookup.
+
+ver 0.11:
+ Add plugin priority handling.
+ Add network type for WiMAX.
+ Fix network protocol selection for Bluetooth PAN.
+ Fix parameters for Bluetooth PAN disconnect method.
+
+ver 0.10:
+ Fix races with connection signals.
+ Fix automatic switching of default connection.
+
+ver 0.9:
+ Rename FlightMode to OfflineMode.
+ Add static IPv4 setting support for Ethernet devices.
+ Add extra options to exclude devices and plugins.
+ Add support for toggling debug output.
+ Add support for ScanInterval property.
+ Fix handling of disconnect commands from applications.
+ Fix detection of networks that are out of range.
+ Fix setting network remember status.
+ Fix argument type checking of properties.
+
+ver 0.8:
+ Add Device and Network property to connection interface.
+ Add option to disable installation of data files.
+ Add command line option to show version number.
+ Fix signal emission for network changes.
+
+ver 0.7:
+ Add basic support for flight mode.
+ Add support for multiple storage drivers.
+ Add support for RTNL newlink watch API.
+ Add support for different security privileges.
+ Add support for device and network priorities.
+ Add functions for setting network properties.
+ Fix issue with listing devices without a driver.
+ Fix issue with WiFi scanning indication.
+ Fix detection of WiFi security changes.
+ Update WiFi driver to use new network helpers.
+ Install different D-Bus configuration for PolicyKit.
+
+ver 0.6:
+ Add CONNMAN_API_SUBJECT_TO_CHANGE definition.
+ Add detailed configuration options.
+ Add various D-Bus helper functions.
+ Add generic device driver infrastructure.
+ Add generic network driver infrastructure.
+ Add property for WiFi network mode.
+ Add property for network interface name.
+ Add property for global connection policy.
+ Add support for verbose compiler warnings.
+ Add support for device detection via udev.
+ Add support for systems with udhcpc.
+ Add support for Bluetooth PAN networks.
+ Fix WiFi issue with DHCP restart after handshake.
+ Fix exported symbols list creation.
+ Remove deprecated and unused plugins.
+
+ver 0.5:
+ Add support for handling Bluetooth adapters.
+ Add support for activating wpa_supplicant on demand.
+ Add Device property to network objects.
+ Add Scanning property to device objects.
+ Fix Name property of device objects.
+ Fix WiFi SSID to object path conversion.
+ Fix duplicate wireless scan results.
+ Fix built issue with libudev and uClibc.
+ Fix issues with element registration failures.
+
+ver 0.4:
+ Add DNS proxy resolver plugin.
+ Add support for default connections.
+ Add support for gateway change notifications.
+ Add signal strength property for connections.
+ Add property for connection type.
+ Fix issue with carrier detection.
+ Fix broken resolvconf plugin.
+
+ver 0.3:
+ Add support for automatically connecting known networks.
+ Add improved framework for handling resolver details.
+ Add generic signal strength property.
+ Fix bridge and WiMAX device detection.
+ Fix network listing for Ethernet devices.
+
+ver 0.2:
+ Add support for indicating network changes.
+ Add support for signal strength property.
+ Add support for unique device names.
+ Fix broken device enumeration.
+ Fix issue with device removal callback.
+ Fix issue with wpa_supplicant disconnecting.
+ Fix D-Bus access policy configuration.
+
+ver 0.1:
+ Initial public release.
diff --git a/HACKING b/HACKING
new file mode 100644
index 0000000..05fb69c
--- /dev/null
+++ b/HACKING
@@ -0,0 +1,144 @@
+Hacking on Connection Manager
+*****************************
+
+
+Build tools requirements
+========================
+
+When building and testing directly from the repository it is important to
+have at least automake version 1.10 or later installed. All modern
+distributions should default to the latest version, but it seems that
+Debian's default is still an earlier version:
+
+ Check version
+ # dpkg -l '*automake*'
+
+ Install new version
+ # apt-get install automake1.10
+ # update-alternatives --config automake
+
+
+Working with the source code repository
+=======================================
+
+The repository contains two extra scripts that accomplish the bootstrap
+process. One is called "bootstrap" which is the basic scripts that uses the
+autotools scripts to create the needed files for building and installing.
+It makes sure to call the right programs depending on the usage of shared or
+static libraries or translations etc.
+
+The second program is called "bootstrap-configure". This program will make
+sure to properly clean the repository, call the "bootstrap" script and then
+call configure with proper settings for development. It will use the best
+options and pass them over to configure. These options normally include
+the enabling the maintainer mode and the debugging features.
+
+So while in a normal source project the call "./configure ..." is used to
+configure the project with its settings like prefix and extra options. In
+case of bare repositories call "./bootstrap-configure" and it will bootstrap
+the repository and calls configure with all the correct options to make
+development easier.
+
+In case of preparing for a release with "make distcheck", don't use
+bootstrap-configure since it could export development specific settings.
+
+So the normal steps to checkout, build and install such a repository is
+like this:
+
+ Checkout repository
+ # git clone git://git.kernel.org/pub/scm/network/connman/connman.git
+ # cd connman
+
+ Configure and build
+ # ./bootstrap-configure
+ # make
+
+ Check installation
+ # make install DESTDIR=$PWD/x
+ # find x
+ # rm -rf x
+
+ Check distribution
+ # make distcheck
+
+ Final installation
+ # sudo make install
+
+ Remove autogenerated files
+ # make maintainer-clean
+
+
+Running from within the source code repository
+==============================================
+
+When using "./configure --enable-maintainer-mode" the automake scripts will
+use the plugins directly from within the repository. This removes the need
+to use "make install" when testing "connmand". The "bootstrap-configure"
+automatically includes this option.
+
+ Run daemon in foreground with debugging
+ # sudo ./src/connmand -n -d 'plugins/*'
+
+The debugging option -d takes an argument. This argument can be a comma
+separated list of file names like 'plugins/wifi.c,plugins/ethernet.c' to
+enable debugs in these files. Simple glob style pattern matching is
+supported in this list.
+
+For production installations or distribution packaging it is important that
+the "--enable-maintainer-mode" option is NOT used.
+
+Some times it is important to restrict the available interfaces. For example
+in cases where testing happens over a network connection. The "-i" command
+line switch allows to specify a glob pattern for the interface names.
+
+ Run daemon for wireless interfaces
+ # sudo ./src/connmand -n -i wlan*
+
+
+Debugging the D-Bus interface during runtime
+============================================
+
+Running the daemon with debugging information in the foreground is quite
+verbose and sometimes not really helpful. The "monitor-connman" script
+allows to monitor "PropertyChanged" D-Bus signals from various interfaces.
+
+Every "PropertyChanged" signal will generate a line of output. Some of them
+can get very complex. The first detail inside "{ ... }" is the interface
+name (without its service name prefix). The second detail inside "[ ... ]"
+is the object path. And after that it is followed by a key and value of
+the property that changed.
+
+
+Generating source code documentation
+====================================
+
+The source code is annotated using the gtk-doc style documentation. This
+allows an easy way of generating API documentation. The "bootstrap-configure"
+script will use the "--enable-gtk-doc" configure to enable the generation of
+the documentation.
+
+To make the gtk-doc process work, the gtk-doc tools need to be installed.
+Every distribution should provide a package for this, but the naming of the
+package might be different:
+
+ Debian
+ # apt-get install gtk-doc-tools
+
+ Ubuntu
+ # apt-get install gtk-doc-utils
+
+ Fedora
+ # yum install gtk-doc
+
+In case "bootstrap-configure" is not used, the manual steps for generating
+the documentation files are like this:
+
+ Configuring the repository
+ # ./configure --enable-gtk-doc
+
+ Generate the documentation
+ # cd doc && make
+
+ View documentation
+ # firefox doc/html/index.html
+
diff --git a/INSTALL b/INSTALL
new file mode 100644
index 0000000..56b077d
--- /dev/null
+++ b/INSTALL
@@ -0,0 +1,236 @@
+Installation Instructions
+*************************
+
+Copyright (C) 1994, 1995, 1996, 1999, 2000, 2001, 2002, 2004, 2005 Free
+Software Foundation, Inc.
+
+This file is free documentation; the Free Software Foundation gives
+unlimited permission to copy, distribute and modify it.
+
+Basic Installation
+==================
+
+These are generic installation instructions.
+
+ The `configure' shell script attempts to guess correct values for
+various system-dependent variables used during compilation. It uses
+those values to create a `Makefile' in each directory of the package.
+It may also create one or more `.h' files containing system-dependent
+definitions. Finally, it creates a shell script `config.status' that
+you can run in the future to recreate the current configuration, and a
+file `config.log' containing compiler output (useful mainly for
+debugging `configure').
+
+ It can also use an optional file (typically called `config.cache'
+and enabled with `--cache-file=config.cache' or simply `-C') that saves
+the results of its tests to speed up reconfiguring. (Caching is
+disabled by default to prevent problems with accidental use of stale
+cache files.)
+
+ If you need to do unusual things to compile the package, please try
+to figure out how `configure' could check whether to do them, and mail
+diffs or instructions to the address given in the `README' so they can
+be considered for the next release. If you are using the cache, and at
+some point `config.cache' contains results you don't want to keep, you
+may remove or edit it.
+
+ The file `configure.ac' (or `configure.in') is used to create
+`configure' by a program called `autoconf'. You only need
+`configure.ac' if you want to change it or regenerate `configure' using
+a newer version of `autoconf'.
+
+The simplest way to compile this package is:
+
+ 1. `cd' to the directory containing the package's source code and type
+ `./configure' to configure the package for your system. If you're
+ using `csh' on an old version of System V, you might need to type
+ `sh ./configure' instead to prevent `csh' from trying to execute
+ `configure' itself.
+
+ Running `configure' takes awhile. While running, it prints some
+ messages telling which features it is checking for.
+
+ 2. Type `make' to compile the package.
+
+ 3. Optionally, type `make check' to run any self-tests that come with
+ the package.
+
+ 4. Type `make install' to install the programs and any data files and
+ documentation.
+
+ 5. You can remove the program binaries and object files from the
+ source code directory by typing `make clean'. To also remove the
+ files that `configure' created (so you can compile the package for
+ a different kind of computer), type `make distclean'. There is
+ also a `make maintainer-clean' target, but that is intended mainly
+ for the package's developers. If you use it, you may have to get
+ all sorts of other programs in order to regenerate files that came
+ with the distribution.
+
+Compilers and Options
+=====================
+
+Some systems require unusual options for compilation or linking that the
+`configure' script does not know about. Run `./configure --help' for
+details on some of the pertinent environment variables.
+
+ You can give `configure' initial values for configuration parameters
+by setting variables in the command line or in the environment. Here
+is an example:
+
+ ./configure CC=c89 CFLAGS=-O2 LIBS=-lposix
+
+ *Note Defining Variables::, for more details.
+
+Compiling For Multiple Architectures
+====================================
+
+You can compile the package for more than one kind of computer at the
+same time, by placing the object files for each architecture in their
+own directory. To do this, you must use a version of `make' that
+supports the `VPATH' variable, such as GNU `make'. `cd' to the
+directory where you want the object files and executables to go and run
+the `configure' script. `configure' automatically checks for the
+source code in the directory that `configure' is in and in `..'.
+
+ If you have to use a `make' that does not support the `VPATH'
+variable, you have to compile the package for one architecture at a
+time in the source code directory. After you have installed the
+package for one architecture, use `make distclean' before reconfiguring
+for another architecture.
+
+Installation Names
+==================
+
+By default, `make install' will install the package's files in
+`/usr/local/bin', `/usr/local/man', etc. You can specify an
+installation prefix other than `/usr/local' by giving `configure' the
+option `--prefix=PREFIX'.
+
+ You can specify separate installation prefixes for
+architecture-specific files and architecture-independent files. If you
+give `configure' the option `--exec-prefix=PREFIX', the package will
+use PREFIX as the prefix for installing programs and libraries.
+Documentation and other data files will still use the regular prefix.
+
+ In addition, if you use an unusual directory layout you can give
+options like `--bindir=DIR' to specify different values for particular
+kinds of files. Run `configure --help' for a list of the directories
+you can set and what kinds of files go in them.
+
+ If the package supports it, you can cause programs to be installed
+with an extra prefix or suffix on their names by giving `configure' the
+option `--program-prefix=PREFIX' or `--program-suffix=SUFFIX'.
+
+Optional Features
+=================
+
+Some packages pay attention to `--enable-FEATURE' options to
+`configure', where FEATURE indicates an optional part of the package.
+They may also pay attention to `--with-PACKAGE' options, where PACKAGE
+is something like `gnu-as' or `x' (for the X Window System). The
+`README' should mention any `--enable-' and `--with-' options that the
+package recognizes.
+
+ For packages that use the X Window System, `configure' can usually
+find the X include and library files automatically, but if it doesn't,
+you can use the `configure' options `--x-includes=DIR' and
+`--x-libraries=DIR' to specify their locations.
+
+Specifying the System Type
+==========================
+
+There may be some features `configure' cannot figure out automatically,
+but needs to determine by the type of machine the package will run on.
+Usually, assuming the package is built to be run on the _same_
+architectures, `configure' can figure that out, but if it prints a
+message saying it cannot guess the machine type, give it the
+`--build=TYPE' option. TYPE can either be a short name for the system
+type, such as `sun4', or a canonical name which has the form:
+
+ CPU-COMPANY-SYSTEM
+
+where SYSTEM can have one of these forms:
+
+ OS KERNEL-OS
+
+ See the file `config.sub' for the possible values of each field. If
+`config.sub' isn't included in this package, then this package doesn't
+need to know the machine type.
+
+ If you are _building_ compiler tools for cross-compiling, you should
+use the `--target=TYPE' option to select the type of system they will
+produce code for.
+
+ If you want to _use_ a cross compiler, that generates code for a
+platform different from the build platform, you should specify the
+"host" platform (i.e., that on which the generated programs will
+eventually be run) with `--host=TYPE'.
+
+Sharing Defaults
+================
+
+If you want to set default values for `configure' scripts to share, you
+can create a site shell script called `config.site' that gives default
+values for variables like `CC', `cache_file', and `prefix'.
+`configure' looks for `PREFIX/share/config.site' if it exists, then
+`PREFIX/etc/config.site' if it exists. Or, you can set the
+`CONFIG_SITE' environment variable to the location of the site script.
+A warning: not all `configure' scripts look for a site script.
+
+Defining Variables
+==================
+
+Variables not defined in a site shell script can be set in the
+environment passed to `configure'. However, some packages may run
+configure again during the build, and the customized values of these
+variables may be lost. In order to avoid this problem, you should set
+them in the `configure' command line, using `VAR=value'. For example:
+
+ ./configure CC=/usr/local2/bin/gcc
+
+causes the specified `gcc' to be used as the C compiler (unless it is
+overridden in the site shell script). Here is a another example:
+
+ /bin/bash ./configure CONFIG_SHELL=/bin/bash
+
+Here the `CONFIG_SHELL=/bin/bash' operand causes subsequent
+configuration-related scripts to be executed by `/bin/bash'.
+
+`configure' Invocation
+======================
+
+`configure' recognizes the following options to control how it operates.
+
+`--help'
+`-h'
+ Print a summary of the options to `configure', and exit.
+
+`--version'
+`-V'
+ Print the version of Autoconf used to generate the `configure'
+ script, and exit.
+
+`--cache-file=FILE'
+ Enable the cache: use and save the results of the tests in FILE,
+ traditionally `config.cache'. FILE defaults to `/dev/null' to
+ disable caching.
+
+`--config-cache'
+`-C'
+ Alias for `--cache-file=config.cache'.
+
+`--quiet'
+`--silent'
+`-q'
+ Do not print messages saying which checks are being made. To
+ suppress all normal output, redirect it to `/dev/null' (any error
+ messages will still be shown).
+
+`--srcdir=DIR'
+ Look for the package's source code in directory DIR. Usually
+ `configure' can determine that directory automatically.
+
+`configure' also accepts some other, not widely useful, options. Run
+`configure --help' for more details.
+
diff --git a/Makefile.am b/Makefile.am
new file mode 100644
index 0000000..a88e987
--- /dev/null
+++ b/Makefile.am
@@ -0,0 +1,407 @@
+
+AM_MAKEFLAGS = --no-print-directory
+
+includedir = @includedir@/connman
+
+include_HEADERS = include/types.h include/log.h include/plugin.h \
+ include/notifier.h include/service.h \
+ include/resolver.h include/ipconfig.h \
+ include/device.h include/network.h include/inet.h \
+ include/storage.h include/provision.h \
+ include/session.h include/ipaddress.h include/agent.h \
+ include/inotify.h include/technology.h include/dbus.h
+
+nodist_include_HEADERS = include/version.h
+
+noinst_HEADERS = include/rtnl.h include/task.h \
+ include/option.h \
+ include/provider.h include/vpn-dbus.h \
+ include/utsname.h include/timeserver.h include/proxy.h \
+ include/setting.h
+
+local_headers = $(foreach file,$(include_HEADERS) $(nodist_include_HEADERS) \
+ $(noinst_HEADERS), include/connman/$(notdir $(file)))
+
+
+gdbus_sources = gdbus/gdbus.h gdbus/mainloop.c gdbus/watch.c \
+ gdbus/object.c gdbus/client.c gdbus/polkit.c
+
+gdhcp_sources = gdhcp/gdhcp.h gdhcp/common.h gdhcp/common.c gdhcp/client.c \
+ gdhcp/server.c gdhcp/ipv4ll.h gdhcp/ipv4ll.c gdhcp/timer.h \
+ gdhcp/timer.c gdhcp/unaligned.h
+
+gweb_sources = gweb/gweb.h gweb/gweb.c gweb/gresolv.h gweb/gresolv.c
+
+if WISPR
+gweb_sources += gweb/giognutls.h gweb/giognutls.c
+else
+gweb_sources += gweb/giognutls.h gweb/gionotls.c
+endif
+
+if DATAFILES
+
+if NMCOMPAT
+nmcompat_conf = plugins/connman-nmcompat.conf
+endif
+
+dbusconfdir = @DBUS_CONFDIR@
+
+dbusconf_DATA = src/connman.conf $(nmcompat_conf)
+
+if VPN
+dbusconf_DATA += vpn/connman-vpn-dbus.conf
+dbusservicedir = @DBUS_DATADIR@
+dbusservice_DATA = vpn/net.connman.vpn.service
+endif
+
+if SYSTEMD
+systemdunitdir = @SYSTEMD_UNITDIR@
+
+systemdunit_DATA = src/connman.service
+
+if VPN
+systemdunit_DATA += vpn/connman-vpn.service
+endif
+endif
+endif
+
+plugin_LTLIBRARIES =
+
+plugin_objects =
+
+builtin_modules =
+builtin_sources =
+builtin_libadd =
+builtin_cflags =
+
+noinst_PROGRAMS =
+
+unit_objects =
+
+MANUAL_PAGES =
+
+sbin_PROGRAMS = src/connmand
+
+src_connmand_SOURCES = $(gdbus_sources) $(gdhcp_sources) $(gweb_sources) \
+ $(builtin_sources) src/connman.ver \
+ src/main.c src/connman.h src/log.c \
+ src/error.c src/plugin.c src/task.c \
+ src/device.c src/network.c src/connection.c \
+ src/manager.c src/service.c \
+ src/clock.c src/timezone.c src/agent-connman.c \
+ src/agent.c src/notifier.c src/provider.c \
+ src/resolver.c src/ipconfig.c src/detect.c src/inet.c \
+ src/dhcp.c src/dhcpv6.c src/rtnl.c src/proxy.c \
+ src/utsname.c src/timeserver.c src/rfkill.c \
+ src/storage.c src/dbus.c src/config.c \
+ src/technology.c src/counter.c src/ntp.c \
+ src/session.c src/tethering.c src/wpad.c src/wispr.c \
+ src/stats.c src/iptables.c src/dnsproxy.c src/6to4.c \
+ src/ippool.c src/bridge.c src/nat.c src/ipaddress.c \
+ src/inotify.c \
+ src/rlimits.c
+
+src_connmand_LDADD = $(builtin_libadd) @GLIB_LIBS@ @DBUS_LIBS@ \
+ @XTABLES_LIBS@ @GNUTLS_LIBS@ -lresolv -ldl -lrt
+
+src_connmand_LDFLAGS = -Wl,--export-dynamic \
+ -Wl,--version-script=$(srcdir)/src/connman.ver
+
+if VPN
+vpn_plugin_LTLIBRARIES =
+
+vpn_plugin_objects =
+
+builtin_vpn_modules =
+builtin_vpn_sources =
+builtin_vpn_libadd =
+builtin_vpn_cflags =
+
+sbin_PROGRAMS += vpn/connman-vpnd
+
+vpn_connman_vpnd_SOURCES = $(gdbus_sources) $(builtin_vpn_sources) \
+ $(gweb_sources) vpn/vpn.ver vpn/main.c vpn/vpn.h \
+ src/log.c src/error.c src/plugin.c src/task.c \
+ vpn/vpn-manager.c vpn/vpn-provider.c \
+ vpn/vpn-provider.h vpn/vpn-rtnl.h \
+ vpn/vpn-ipconfig.c src/inet.c vpn/vpn-rtnl.c \
+ src/dbus.c src/storage.c src/ipaddress.c src/agent.c \
+ vpn/vpn-agent.c vpn/vpn-agent.h
+
+vpn_connman_vpnd_LDADD = $(builtin_vpn_libadd) @GLIB_LIBS@ @DBUS_LIBS@ \
+ @GNUTLS_LIBS@ -lresolv -ldl
+
+vpn_connman_vpnd_LDFLAGS = -Wl,--export-dynamic \
+ -Wl,--version-script=$(srcdir)/vpn/vpn.ver
+endif
+
+BUILT_SOURCES = $(local_headers) src/builtin.h
+
+if VPN
+BUILT_SOURCES += vpn/builtin.h
+endif
+
+CLEANFILES = src/connman.conf $(BUILT_SOURCES)
+
+statedir = $(localstatedir)/run/connman
+
+if VPN
+vpn_plugindir = $(libdir)/connman/plugins-vpn
+endif
+
+plugindir = $(libdir)/connman/plugins
+
+scriptdir = $(libdir)/connman/scripts
+
+storagedir = $(localstatedir)/lib/connman
+
+configdir = ${sysconfdir}/connman
+
+if MAINTAINER_MODE
+if VPN
+build_vpn_plugindir = $(abs_top_srcdir)/vpn/plugins/.libs
+endif
+build_plugindir = $(abs_top_srcdir)/plugins/.libs
+build_scriptdir = $(abs_top_srcdir)/scripts
+else
+build_plugindir = $(plugindir)
+build_scriptdir = $(scriptdir)
+if VPN
+build_vpn_plugindir = $(vpn_plugindir)
+endif
+endif
+
+AM_CFLAGS = @DBUS_CFLAGS@ @GLIB_CFLAGS@ @XTABLES_CFLAGS@ \
+ @GNUTLS_CFLAGS@ $(builtin_cflags) \
+ -DCONNMAN_PLUGIN_BUILTIN \
+ -DSTATEDIR=\""$(statedir)"\" \
+ -DPLUGINDIR=\""$(build_plugindir)"\" \
+ -DSCRIPTDIR=\""$(build_scriptdir)"\" \
+ -DSTORAGEDIR=\""$(storagedir)\"" \
+ -DCONFIGDIR=\""$(configdir)\""
+
+if VPN
+AM_CPPFLAGS = -I$(builddir)/include -I$(srcdir)/gdbus
+else
+AM_CPPFLAGS = -I$(builddir)/include -I$(builddir)/src -I$(srcdir)/gdbus
+endif
+
+src_connmand_CFLAGS = @DBUS_CFLAGS@ @GLIB_CFLAGS@ @XTABLES_CFLAGS@ \
+ @GNUTLS_CFLAGS@ $(builtin_cflags) \
+ -DCONNMAN_PLUGIN_BUILTIN \
+ -DSTATEDIR=\""$(statedir)"\" \
+ -DPLUGINDIR=\""$(build_plugindir)"\" \
+ -DSCRIPTDIR=\""$(build_scriptdir)"\" \
+ -DSTORAGEDIR=\""$(storagedir)\"" \
+ -DCONFIGDIR=\""$(configdir)\"" \
+ -I$(builddir)/src
+
+EXTRA_DIST = src/genbuiltin src/connman-dbus.conf src/connman-polkit.conf \
+ plugins/connman-nmcompat.conf
+
+if VPN
+vpn_connman_vpnd_CFLAGS = @DBUS_CFLAGS@ @GLIB_CFLAGS@ \
+ $(builtin_vpn_cflags) \
+ -DCONNMAN_PLUGIN_BUILTIN \
+ -DSTATEDIR=\""$(statedir)"\" \
+ -DPLUGINDIR=\""$(build_vpn_plugindir)"\" \
+ -DSCRIPTDIR=\""$(build_scriptdir)"\" \
+ -DSTORAGEDIR=\""$(storagedir)\"" \
+ -DCONFIGDIR=\""$(configdir)\"" \
+ -I$(builddir)/vpn
+
+endif
+
+EXTRA_DIST += vpn/vpn-dbus.conf vpn/vpn-polkit.conf
+
+script_DATA =
+script_PROGRAMS =
+script_LTLIBRARIES =
+
+include Makefile.plugins
+
+if CLIENT
+sbin_PROGRAMS += client/connmanctl
+
+MANUAL_PAGES += doc/connmanctl.1
+
+client_connmanctl_SOURCES = $(gdbus_sources) src/connman.h \
+ client/dbus.h client/dbus.c \
+ client/data_manager.h client/data_manager.c \
+ client/services.h client/services.c \
+ client/technology.h client/technology.c \
+ client/interactive.h client/interactive.c \
+ client/monitor.h client/monitor.c \
+ client/commands.c client/main.c
+
+client_connmanctl_LDADD = @DBUS_LIBS@ @GLIB_LIBS@ -lreadline -ldl
+endif
+
+if WISPR
+noinst_PROGRAMS += tools/wispr
+
+tools_wispr_SOURCES = $(gweb_sources) tools/wispr.c
+tools_wispr_LDADD = @GLIB_LIBS@ @GNUTLS_LIBS@ -lresolv
+endif
+
+if TOOLS
+noinst_PROGRAMS += tools/supplicant-test \
+ tools/dhcp-test tools/dhcp-server-test \
+ tools/addr-test tools/web-test tools/resolv-test \
+ tools/dbus-test tools/polkit-test \
+ tools/iptables-test tools/tap-test tools/wpad-test \
+ tools/stats-tool tools/private-network-test \
+ unit/test-session unit/test-ippool unit/test-nat
+
+tools_supplicant_test_SOURCES = $(gdbus_sources) tools/supplicant-test.c \
+ tools/supplicant-dbus.h tools/supplicant-dbus.c \
+ tools/supplicant.h tools/supplicant.c
+tools_supplicant_test_LDADD = @GLIB_LIBS@ @DBUS_LIBS@
+
+tools_web_test_SOURCES = $(gweb_sources) tools/web-test.c
+tools_web_test_LDADD = @GLIB_LIBS@ @GNUTLS_LIBS@ -lresolv
+
+tools_resolv_test_SOURCES = gweb/gresolv.h gweb/gresolv.c tools/resolv-test.c
+tools_resolv_test_LDADD = @GLIB_LIBS@ -lresolv
+
+tools_wpad_test_SOURCES = gweb/gresolv.h gweb/gresolv.c tools/wpad-test.c
+tools_wpad_test_LDADD = @GLIB_LIBS@ -lresolv
+
+tools_stats_tool_LDADD = @GLIB_LIBS@
+
+tools_dhcp_test_SOURCES = $(gdhcp_sources) tools/dhcp-test.c
+tools_dhcp_test_LDADD = @GLIB_LIBS@
+
+tools_dhcp_server_test_SOURCES = $(gdhcp_sources) tools/dhcp-server-test.c
+tools_dhcp_server_test_LDADD = @GLIB_LIBS@
+
+tools_dbus_test_SOURCES = $(gdbus_sources) tools/dbus-test.c
+tools_dbus_test_LDADD = @GLIB_LIBS@ @DBUS_LIBS@
+
+tools_polkit_test_LDADD = @DBUS_LIBS@
+
+tools_iptables_test_LDADD = @GLIB_LIBS@ @XTABLES_LIBS@
+
+tools_private_network_test_LDADD = @GLIB_LIBS@ @DBUS_LIBS@
+
+unit_test_session_SOURCES = $(gdbus_sources) src/log.c src/dbus.c \
+ unit/test-session.c unit/utils.c unit/manager-api.c \
+ unit/session-api.c unit/test-connman.h
+unit_test_session_LDADD = @GLIB_LIBS@ @DBUS_LIBS@ -ldl
+unit_objects += $(unit_test_session_OBJECTS)
+
+unit_test_ippool_SOURCES = $(gdbus_sources) src/log.c src/dbus.c \
+ src/ippool.c unit/test-ippool.c
+unit_test_ippool_LDADD = @GLIB_LIBS@ @DBUS_LIBS@ -ldl
+unit_objects += $(unit_test_ippool_OBJECTS)
+
+unit_test_nat_SOURCES = $(gdbus_sources) src/log.c src/dbus.c \
+ src/iptables.c src/nat.c unit/test-nat.c
+unit_test_nat_LDADD = @GLIB_LIBS@ @DBUS_LIBS@ @XTABLES_LIBS@ -ldl
+unit_objects += $(unit_nat_ippool_OBJECTS)
+endif
+
+test_scripts = test/get-state test/list-services \
+ test/monitor-services test/test-clock \
+ test/simple-agent test/show-introspection test/test-compat \
+ test/test-manager test/test-connman test/monitor-connman \
+ test/connect-provider test/remove-provider \
+ test/test-counter test/set-ipv4-method test/set-ipv6-method \
+ test/get-services test/get-proxy-autoconfig test/set-proxy \
+ test/enable-tethering test/disable-tethering test/backtrace \
+ test/test-session test/test-supplicant \
+ test/test-new-supplicant test/service-move-before \
+ test/set-global-timeservers test/get-global-timeservers \
+ test/set-nameservers test/set-domains test/set-timeservers
+
+test_scripts += test/vpn-connect test/vpn-disconnect test/vpn-get \
+ test/monitor-vpn
+
+if TEST
+testdir = $(pkglibdir)/test
+test_SCRIPTS = $(test_scripts)
+endif
+
+EXTRA_DIST += $(test_scripts)
+
+EXTRA_DIST += doc/overview-api.txt doc/behavior-api.txt \
+ doc/ipconfig-api.txt doc/plugin-api.txt \
+ doc/manager-api.txt doc/agent-api.txt \
+ doc/service-api.txt doc/technology-api.txt \
+ doc/counter-api.txt doc/config-format.txt \
+ doc/clock-api.txt doc/session-api.txt \
+ doc/session-overview.txt doc/backtrace.txt \
+ doc/advanced-configuration.txt \
+ doc/vpn-connection-api.txt \
+ doc/vpn-manager-api.txt doc/vpn-overview.txt
+
+MANUAL_PAGES += doc/connman.8 doc/connman.conf.5
+
+noinst_dist_man_MANS = $(MANUAL_PAGES)
+
+pkgconfigdir = $(libdir)/pkgconfig
+
+pkgconfig_DATA = connman.pc
+
+DISTCHECK_CONFIGURE_FLAGS = --disable-gtk-doc \
+ --disable-datafiles \
+ --enable-hh2serial-gps \
+ --enable-openconnect \
+ --enable-openvpn \
+ --enable-vpnc \
+ --enable-session-policy-local \
+ --enable-nmcompat \
+ --enable-polkit
+
+DISTCLEANFILES = $(pkgconfig_DATA)
+
+MAINTAINERCLEANFILES = Makefile.in \
+ aclocal.m4 configure config.h.in config.sub config.guess \
+ ltmain.sh depcomp compile missing install-sh mkinstalldirs
+
+
+src/builtin.h: src/genbuiltin $(builtin_sources)
+ $(AM_V_GEN)$(srcdir)/src/genbuiltin $(builtin_modules) > $@
+
+vpn/builtin.h: src/genbuiltin $(builtin_vpn_sources)
+ $(AM_V_GEN)$(srcdir)/src/genbuiltin $(builtin_vpn_modules) > $@
+
+src/connman.conf: src/connman-dbus.conf src/connman-polkit.conf
+if POLKIT
+ $(AM_V_GEN)cp $(srcdir)/src/connman-polkit.conf $@
+else
+ $(AM_V_GEN)cp $(srcdir)/src/connman-dbus.conf $@
+endif
+
+if VPN
+vpn/connman-vpn-dbus.conf: vpn/vpn-dbus.conf vpn/vpn-polkit.conf
+if POLKIT
+ $(AM_V_GEN)cp $(srcdir)/vpn/vpn-polkit.conf $@
+else
+ $(AM_V_GEN)cp $(srcdir)/vpn/vpn-dbus.conf $@
+endif
+endif
+
+if SELINUX
+if VPN
+EXTRA_DIST += connman-task.pp
+CLEANFILES += connman-task.pp
+endif
+
+connman-task.pp: vpn/connman-task.te
+ make -f /usr/share/selinux/devel/Makefile
+endif
+
+EXTRA_DIST += vpn/connman-task.te
+
+$(abs_top_builddir)/include/connman:
+ $(AM_V_at)$(MKDIR_P) $@
+
+include/connman/version.h: $(abs_top_builddir)/include/version.h | $(abs_top_builddir)/include/connman
+ $(AM_V_GEN)$(LN_S) $< $@
+
+include/connman/%.h: $(abs_top_srcdir)/include/%.h | $(abs_top_builddir)/include/connman
+ $(AM_V_GEN)$(LN_S) $< $@
+
+clean-local:
+ @$(RM) -rf include/connman
diff --git a/Makefile.plugins b/Makefile.plugins
new file mode 100644
index 0000000..2986b6b
--- /dev/null
+++ b/Makefile.plugins
@@ -0,0 +1,265 @@
+
+plugin_cflags = -fvisibility=hidden -I$(srcdir)/gdbus \
+ @DBUS_CFLAGS@ @GLIB_CFLAGS@
+plugin_ldflags = -no-undefined -module -avoid-version
+
+script_cflags = -fvisibility=hidden -I$(srcdir)/gdbus \
+ @DBUS_CFLAGS@
+
+if LOOPBACK
+builtin_modules += loopback
+builtin_sources += plugins/loopback.c
+endif
+
+if ETHERNET
+builtin_modules += ethernet
+builtin_sources += plugins/ethernet.c
+endif
+
+gsupplicant_sources = gsupplicant/gsupplicant.h gsupplicant/dbus.h \
+ gsupplicant/supplicant.c gsupplicant/dbus.c
+
+if WIFI
+builtin_modules += wifi
+builtin_sources += plugins/wifi.c $(gsupplicant_sources)
+endif
+
+if SLEEP
+plugin_LTLIBRARIES += plugins/sleepplugin.la
+plugin_objects += $(plugins_sleepplugin_la_OBJECTS)
+plugins_sleepplugin_la_CFLAGS = $(plugin_cflags)
+plugins_sleepplugin_la_LDFLAGS = $(plugin_ldflags)
+endif
+
+if BLUETOOTH
+builtin_modules += bluetooth_legacy
+builtin_sources += plugins/bluetooth_legacy.c
+builtin_modules += bluetooth
+builtin_sources += plugins/bluetooth.c
+endif
+
+if HH2SERIAL_GPS
+if HH2SERIAL_GPS_BUILTIN
+builtin_modules += hh2serial_gps
+builtin_sources += plugins/hh2serial-gps.c
+else
+plugin_LTLIBRARIES += plugins/hh2serial-gps.la
+plugin_objects += $(plugins_hh2serial_gps_la_OBJECTS)
+plugins_hh2serial_gps_la_CFLAGS = $(plugin_cflags)
+plugins_hh2serial_gps_la_LDFLAGS = $(plugin_ldflags)
+endif
+endif
+
+if OFONO
+builtin_modules += ofono
+builtin_sources += plugins/mcc.h plugins/ofono.c
+endif
+
+if DUNDEE
+builtin_modules += dundee
+builtin_sources += plugins/dundee.c
+endif
+
+if VPN
+builtin_modules += vpn
+builtin_sources += plugins/vpn.c
+
+if OPENCONNECT
+if OPENCONNECT_BUILTIN
+builtin_vpn_modules += openconnect
+builtin_vpn_sources += vpn/plugins/openconnect.c
+builtin_vpn_source = vpn/plugins/vpn.c vpn/plugins/vpn.h
+builtin_vpn_cflags += -DOPENCONNECT=\"@OPENCONNECT@\"
+else
+vpn_plugin_LTLIBRARIES += vpn/plugins/openconnect.la
+vpn_plugin_objects += $(plugins_openconnect_la_OBJECTS)
+vpn_plugins_openconnect_la_SOURCES = vpn/plugins/vpn.h vpn/plugins/vpn.c \
+ vpn/plugins/openconnect.c
+vpn_plugins_openconnect_la_CFLAGS = $(plugin_cflags) \
+ -DOPENCONNECT=\"@OPENCONNECT@\" \
+ -DSTATEDIR=\""$(statedir)"\" \
+ -DSCRIPTDIR=\""$(build_scriptdir)"\"
+vpn_plugins_openconnect_la_LDFLAGS = $(plugin_ldflags)
+endif
+endif
+
+if OPENVPN
+if OPENVPN_BUILTIN
+builtin_vpn_modules += openvpn
+builtin_vpn_sources += vpn/plugins/openvpn.c
+builtin_vpn_source = vpn/plugins/vpn.c vpn/plugins/vpn.h
+builtin_vpn_cflags += -DOPENVPN=\"@OPENVPN@\"
+else
+vpn_plugin_LTLIBRARIES += vpn/plugins/openvpn.la
+vpn_plugin_objects += $(plugins_openvpn_la_OBJECTS)
+vpn_plugins_openvpn_la_SOURCES = vpn/plugins/vpn.h vpn/plugins/vpn.c \
+ vpn/plugins/openvpn.c
+vpn_plugins_openvpn_la_CFLAGS = $(plugin_cflags) -DOPENVPN=\"@OPENVPN@\" \
+ -DSTATEDIR=\""$(statedir)"\" \
+ -DSCRIPTDIR=\""$(build_scriptdir)"\"
+vpn_plugins_openvpn_la_LDFLAGS = $(plugin_ldflags)
+endif
+endif
+
+if VPNC
+if VPNC_BUILTIN
+builtin_vpn_modules += vpnc
+builtin_vpn_sources += vpn/plugins/vpnc.c
+builtin_vpn_source = vpn/plugins/vpn.c vpn/plugins/vpn.h
+builtin_vpn_cflags += -DVPNC=\"@VPNC@\"
+else
+vpn_plugin_LTLIBRARIES += vpn/plugins/vpnc.la
+vpn_plugin_objects += $(plugins_vpnc_la_OBJECTS)
+vpn_plugins_vpnc_la_SOURCES = vpn/plugins/vpn.h vpn/plugins/vpn.c \
+ vpn/plugins/vpnc.c
+vpn_plugins_vpnc_la_CFLAGS = $(plugin_cflags) -DVPNC=\"@VPNC@\" \
+ -DSTATEDIR=\""$(statedir)"\" \
+ -DSCRIPTDIR=\""$(build_scriptdir)"\"
+vpn_plugins_vpnc_la_LDFLAGS = $(plugin_ldflags)
+endif
+endif
+
+if L2TP
+if L2TP_BUILTIN
+builtin_vpn_modules += l2tp
+builtin_vpn_sources += vpn/plugins/l2tp.c
+builtin_vpn_source = vpn/plugins/vpn.c vpn/plugins/vpn.h
+builtin_vpn_cflags += -DL2TP=\"@L2TP@\"
+else
+vpn_plugin_LTLIBRARIES += vpn/plugins/l2tp.la
+vpn_plugin_objects += $(plugins_l2tp_la_OBJECTS)
+vpn_plugins_l2tp_la_SOURCES = vpn/plugins/vpn.h vpn/plugins/vpn.c \
+ vpn/plugins/l2tp.c
+vpn_plugins_l2tp_la_CFLAGS = $(plugin_cflags) -DL2TP=\"@L2TP@\" \
+ -DSTATEDIR=\""$(statedir)"\" \
+ -DSCRIPTDIR=\""$(build_scriptdir)"\"
+vpn_plugins_l2tp_la_LDFLAGS = $(plugin_ldflags)
+endif
+endif
+
+if PPTP
+if PPTP_BUILTIN
+builtin_vpn_modules += pptp
+builtin_vpn_sources += vpn/plugins/pptp.c
+builtin_vpn_source = vpn/plugins/vpn.c vpn/plugins/vpn.h
+builtin_vpn_cflags += -DPPPD=\"@PPPD@\" -DPPTP=\"@PPTP@\"
+else
+vpn_plugin_LTLIBRARIES += vpn/plugins/pptp.la
+vpn_plugin_objects += $(plugins_pptp_la_OBJECTS)
+vpn_plugins_pptp_la_SOURCES = vpn/plugins/vpn.h vpn/plugins/vpn.c \
+ vpn/plugins/pptp.c
+vpn_plugins_pptp_la_CFLAGS = $(plugin_cflags) -DPPPD=\"@PPPD@\" \
+ -DPPTP=\"@PPTP@\" \
+ -DSTATEDIR=\""$(statedir)"\" \
+ -DSCRIPTDIR=\""$(build_scriptdir)"\"
+vpn_plugins_pptp_la_LDFLAGS = $(plugin_ldflags)
+endif
+endif
+
+if PPTP
+script_LTLIBRARIES += scripts/libppp-plugin.la
+scripts_libppp_plugin_la_LDFLAGS = $(script_cflags) @DBUS_CFLAGS@
+scripts_libppp_plugin_la_LIBADD = @DBUS_LIBS@
+else
+if L2TP
+script_LTLIBRARIES += scripts/libppp-plugin.la
+scripts_libppp_plugin_la_LDFLAGS = $(script_cflags) @DBUS_CFLAGS@
+scripts_libppp_plugin_la_LIBADD = @DBUS_LIBS@
+endif
+endif
+
+if VPN
+builtin_vpn_sources += $(builtin_vpn_source)
+endif
+endif
+
+if PACRUNNER
+builtin_modules += pacrunner
+builtin_sources += plugins/pacrunner.c
+endif
+
+if POLKIT
+builtin_modules += polkit
+builtin_sources += plugins/polkit.c
+
+if DATAFILES
+policydir = @POLKIT_DATADIR@
+
+policy_DATA = plugins/net.connman.policy
+
+if VPN
+policy_DATA += vpn/net.connman.vpn.policy
+endif
+endif
+endif
+
+if IOSPM
+plugin_LTLIBRARIES += plugins/iospm.la
+plugin_objects += $(plugins_iospm_la_OBJECTS)
+plugins_iospm_la_CFLAGS = $(plugin_cflags)
+plugins_iospm_la_LDFLAGS = $(plugin_ldflags)
+endif
+
+if OPENCONNECT
+script_PROGRAMS += scripts/openconnect-script
+
+scripts_openconnect_script_LDADD = @DBUS_LIBS@
+else
+if VPNC
+script_PROGRAMS += scripts/openconnect-script
+
+scripts_openconnect_script_LDADD = @DBUS_LIBS@
+endif
+endif
+
+if OPENVPN
+script_PROGRAMS += scripts/openvpn-script
+
+scripts_openvpn_script_LDADD = @DBUS_LIBS@
+endif
+
+if NMCOMPAT
+builtin_modules += nmcompat
+builtin_sources += plugins/nmcompat.c
+endif
+
+if TIST
+if TIST_BUILTIN
+builtin_modules += tist
+builtin_sources += plugins/tist.c
+else
+plugin_LTLIBRARIES += plugins/tist.la
+plugin_objects += $(plugins_tist_la_OBJECTS)
+plugins_tist_la_CFLAGS = $(plugin_cflags)
+plugins_tist_la_LDFLAGS = $(plugin_ldflags)
+endif
+endif
+
+if SESSION_POLICY_LOCAL
+if SESSION_POLICY_LOCAL_BUILTIN
+builtin_modules += session_policy_local
+builtin_sources += plugins/session_policy_local.c
+else
+plugin_LTLIBRARIES += plugins/session_policy_local.la
+plugin_objects += $(plugins_session_policy_local_la_OBJECTS)
+plugins_session_policy_local_la_CFLAGS = $(plugin_cflags) \
+ -DSTORAGEDIR=\""$(storagedir)\""
+plugins_session_policy_local_la_LDFLAGS = $(plugin_ldflags)
+endif
+endif
+
+EXTRA_DIST += plugins/polkit.policy
+
+plugins/net.connman.policy: plugins/polkit.policy
+if POLKIT
+ $(AM_V_GEN)cp $< $@
+endif
+
+EXTRA_DIST += vpn/vpn-polkit.policy
+
+if VPN
+vpn/net.connman.vpn.policy: vpn/vpn-polkit.policy
+if POLKIT
+ $(AM_V_GEN)cp $< $@
+endif
+endif
diff --git a/NEWS b/NEWS
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/NEWS
diff --git a/README b/README
new file mode 100644
index 0000000..0f86bc1
--- /dev/null
+++ b/README
@@ -0,0 +1,276 @@
+Connection Manager
+******************
+
+Copyright (C) 2007-2012 Intel Corporation. All rights reserved.
+
+
+Functionality and features
+==========================
+
+The following features are built-in into Connection Manager:
+ - Generic plugin infrastructure
+ - Device and network abstraction (with basic storage support)
+ - IPv4, IPv4-LL (link-local) and DHCP
+ - IPv6, DHCPv6 and 6to4 tunnels
+ - Advanced routing and DNS configuration
+ - Built-in DNS proxy and intelligent caching
+ - Built-in WISPr hotspot logins and portal detection
+ - Time and timezone configuration (manual and automatic with NTP)
+ - Proxy handling (manual and automatic with WPAD)
+ - Tethering support (USB, Bluetooth and WiFi AP mode)
+ - Detailed statistics handling (home and roaming)
+
+Various plugins can be enabled for networking support:
+ - Ethernet plugin
+ - WiFi plugin with WEP40/WEP128 and WPA/WPA2 (personal and enterprise)
+ - Bluetooth plugin (using BlueZ)
+ - 2G/3G/4G plugin (using oFono)
+
+Also plugins with additional features are available:
+ - Loopback interface setup
+ - PACrunner proxy handling
+ - PolicyKit authorization support
+
+Note that when ConnMan starts, it clears all network interfaces that are
+going to be used. If this is not desired, network interfaces can be ignored
+either by setting NetworkInterfaceBlacklist in the main.conf config file or
+by using the -I command line option.
+
+
+Compilation and installation
+============================
+
+In order to compile Connection Manager you need following software packages:
+ - GCC compiler
+ - GLib library
+ - D-Bus library
+ - IP-Tables library
+ - GnuTLS library (optional)
+ - PolicyKit (optional)
+ - readline (command line client)
+
+To configure run:
+ ./configure --prefix=/usr --sysconfdir=/etc --localstatedir=/var
+
+Configure automatically searches for all required components and packages.
+
+To compile and install run:
+ make && make install
+
+
+Configuration and options
+=========================
+
+For a working system, certain configuration options need to be enabled:
+
+ --disable-ethernet
+
+ Disable support for Ethernet network cards
+
+ By default Ethernet technology support is built-in and
+ enabled. This option can be used to build a small daemon
+ for a specific system if Ethernet support is not required.
+
+ --disable-wifi
+
+ Disable support for WiFi devices
+
+ By default WiFi technology support is built-in and
+ enabled. This option can be used to build a small daemon
+ for a specific system if WiFi support is not required.
+
+ It is safe to build a daemon with WiFi support and no
+ running wpa_supplicant. The start of wpa_supplicant is
+ automatically detected and only a runtime dependency. It
+ is not needed to build ConnMan.
+
+ --disable-bluetooth
+
+ Disable support for Bluetooth devices
+
+ By default Bluetooth technology support is built-in and
+ enabled. This option can be used to build a small daemon
+ for a specific system if Bluetooth support is not required.
+
+ It is safe to build a daemon with Bluetooth support and no
+ running bluetoothd. The start of bluetoothd is automatically
+ detected and only a runtime dependency. It is not needed to
+ build ConnMan.
+
+ --disable-ofono
+
+ Disable support for cellular 2G/3G/4G devices
+
+ By default oFono technology support is built-in and
+ enabled. This option can be used to build a small daemon
+ for a specific system where oFono is not used.
+
+ It is safe to build a daemon with oFono support and no
+ running ofonod. That start of ofonod is automatically
+ detected and only a runtime dependency. It is not needed to
+ build ConnMan.
+
+ --disable-dundee
+
+ Disable support for Bluetooth DUN devices
+
+ By default Bluetooth DUN technology (dundee) support is
+ built-in and enabled. This option can be used to build a
+ small daemon for a specific system where dundee is not used.
+
+ It is safe to build a daemon with dundee support and no
+ running dundee. That start of dundee is automatically
+ detected and only a runtime dependency. It is not needed to
+ build ConnMan.
+
+ --disable-pacrunner
+
+ Disable support for PACrunner proxy handling
+
+ By default PACrunner support is built-in and enabled. This
+ option can be used to build a small daemon for a specific
+ system where PACrunner is not used.
+
+ It is safe to build a daemon with PACrunner support and no
+ pacrunner daemon. It will detect and start a PACrunner
+ process if needed at runtime. The presence is not needed
+ to build ConnMan.
+
+ --disable-loopback
+
+ Disable setup of loopback device
+
+ For distributions with a really minimal init system and no
+ networking scripts this can take care of setting up the
+ loopback device and enabling it.
+
+ It is safe to leave this selected even if networking
+ scripts are in place. It detects an already configured
+ loopback device and leaves it as it is.
+
+ --disable-wispr
+
+ Disable support for WISPr hotspot logins
+
+ For systems with really minimal memory requirements, this
+ will disable the support for WISPr hotspot logins. The code
+ for WISPr will be still compiled into the daemon, but its
+ requirement on GnuTLS for secure connections will be lifted.
+
+ The missing GnuTLS support shrinks the memory requirements
+ by about 30% and for systems that are more stationary and do
+ not log into hotspots this might be a better trade off.
+
+ Disabling WISPr support is not disabling the portal detection
+ support. A portal will still be detected, but instead of being
+ asked for login credentials, the request for a browser session
+ will be made through the agent.
+
+ --enable-polkit
+
+ Enable support for PolicyKit authorization
+
+ This allows to check every D-Bus access against a security
+ policy and so restrict access to certain functionality.
+
+ --enable-nmcompat
+
+ Enable support for NetworkManager compatibility interfaces
+
+ This allows to expose a minimal set of NetworkManager
+ interfaces. It is useful for systems with applications
+ written to use NetworkManager to detect online/offline
+ status and have not yet been converted to use ConnMan.
+
+ --disable-client
+
+ Disable support for the command line client
+
+ By default the command line client is enabled and uses the
+ readline library. For specific systems where ConnMan is
+ configured by other means, the command line client can be
+ disabled and the dependency on readline is removed.
+
+ --enable-selinux
+
+ Enable support for compiling SElinux type enforcement rules
+
+ The TE rules are needed if host environment is in enforcing
+ mode. Without this option, the VPN client process cannot
+ send notification to connman-vpnd via net.connman.Task
+ interface. The compiled connman-task.pp module needs to
+ also installed using this command
+ # semodule -i connman-task.pp
+ in order to enable the dbus access.
+
+
+Kernel configuration
+====================
+
+In order to support tethering, the following kernel configuration options
+need to be enabled either as modules (m) or builtin (y):
+
+CONFIG_BRIDGE
+CONFIG_IP_NF_TARGET_MASQUERADE
+
+In order to enable CONFIG_IP_NF_TARGET_MASQUERADE, the following options need
+to be enabled also as modules (m) or builtin (y):
+
+CONFIG_NETFILTER
+CONFIG_NF_CONNTRACK_IPV4
+CONFIG_NF_NAT_IPV4
+
+
+wpa_supplicant configuration
+============================
+
+In order to get wpa_supplicant and Connection Manager working properly
+together you should edit wpa_supplicant .config file and set:
+
+CONFIG_WPS=y
+CONFIG_AP=y
+CONFIG_CTRL_IFACE_DBUS_NEW=y
+
+and, add:
+
+CONFIG_BGSCAN_SIMPLE=y
+
+This last option will enable the support of background scanning while being
+connected, which is necessary when roaming on wifi.
+
+It is recommended to use wpa_supplicant 0.8.x or 1.x or later.
+
+
+VPN
+===
+
+In order to compile pptp and l2tp VPN plugins, you need ppp development
+package.
+
+To run l2tp you will need
+ - xl2tpd, http://www.xelerance.com/services/software/xl2tpd
+
+To run pptp you will need
+ - pptp client, http://pptpclient.sourceforge.net
+
+Both l2tp and pptp also need pppd.
+
+
+OpenVPN
+=======
+
+Up to version 2.2 of OpenVPN, pushing additional routes from the
+server will not always work. Some of the symptons are that additional
+routes will not be set by ConnMan if the uplink is a cellular
+network. While the same setup works well for a WiFi or ethernet
+uplink.
+
+
+Information
+===========
+
+Mailing list:
+ connman@connman.net
+
+For additional information about the project visit ConnMan web site:
+ http://www.connman.net
diff --git a/TODO b/TODO
new file mode 100644
index 0000000..77ab29e
--- /dev/null
+++ b/TODO
@@ -0,0 +1,210 @@
+Background
+==========
+
+- Priority scale: High, Medium and Low
+
+- Complexity scale: C1, C2, C4 and C8.
+ The complexity scale is exponential, with complexity 1 being the
+ lowest complexity. Complexity is a function of both task 'complexity'
+ and task 'scope'.
+
+
+Core
+====
+
+- Session API implementation
+
+ Priority: High
+ Complexity: C4
+ Owner: Daniel Wagner <daniel.wagner@bmw-carit.de>
+ Owner: Patrik Flykt <patrik.flykt@linux.intel.com>
+
+ The session API should provide a connection abstraction in order to
+ prioritize applications network accesses, prevent or allow network
+ and bearer roaming, or provide applications with a way to request
+ for periodic network connections. On-demand connections will be
+ implemented through this API as well.
+ See http://www.mail-archive.com/connman@connman.net/msg01653.html
+
+
+- Personal firewall
+
+ Priority: Low
+ Complexity: C8
+ Owner: Tomasz Bursztyka <tomasz.bursztyka@linux.intel.com>
+
+ Discuss and implement a basic and safe firewalling strategy into
+ Connman. Provide a D-Bus API for personal firewalling.
+
+
+- PACRunner extensions
+
+ Priority: Low
+ Complexity: C4
+
+ Support more URI schemes, support multiple connections, tighter
+ security integration.
+
+
+- Check logging produced by connman_info()
+
+ Priority: Medium
+ Complexity: C1
+
+ Check that logging produced by connman_info() contains meaningful messages
+ and get rid of the unnecessary ones.
+
+
+- Support for multiple agents
+
+ Priority: Medium
+ Complexity: C2
+
+ Allow to register multiple agents. Each unique system bus name owner
+ however is only allowed to register one agent.
+
+ The selection of which agents is used should be matched by bus name
+ owner if possible or first come first serve. A graceful fallback to
+ the next agent should be also used in case of malfunctional agents.
+
+- Remove --nobacktrace option
+
+ Priority: Medium
+ Complexity: C1
+ When: 2.0
+
+ Remove the --nobacktrace option or change it to --backtrace depending on the
+ level of systemd integration or other factors.
+
+
+- Clean up type definitions
+
+ Priority: Medium
+ Complexity: C1
+
+ Go through variable types and use the following:
+ * uint8_t instead of connman_uint8_t, unsigned char
+ * uint16_t instead of connman_uint16_t, unsigned short
+ * bool from <stdbool.h> instead of connman_bool_t and gboolean, in the
+ latter case in those places it makes sense
+
+
+- Clean up data structure usage
+
+ Priority: Medium
+ Complexity: C4
+
+ Use hash tables, queues and lists in the code. Replace GSequences with
+ simpler structures. At the same time do a check on the currently used
+ data structures and see if something can be simplified.
+
+
+- Unit tests for DHCP, DNS and HTTP
+
+ Priority: Medium
+ Complexity: C4
+
+ Create unit tests for these components starting with DHCP. Use gtest
+ from GLib for this task similarly to what has been done for OBEX in Bluez
+ and oFono in general.
+
+
+WiFi
+====
+
+- Clean up WiFi data structure usage
+
+ Priority: Medium
+ Complexity: C2
+
+ Struct wifi_data is passed as a pointer in some of the wifi plugin
+ callbacks. For example removing a WiFi USB stick causes RTNL and
+ wpa_supplicant to call the wifi plugin at the same time causing the
+ freeing of the wifi data structure. Fix up the code to have proper
+ reference counting or other handling in place for the shared wifi data
+ and the members in the data structure.
+
+
+- EAP-AKA/SIM
+
+ Priority: Medium
+ Complexity: C2
+ Owner: Samuel Ortiz <sameo@linux.intel.com>
+
+ This EAP is needed for SIM card based network authentication.
+ ConnMan here plays a minor role: Once wpa_supplicant is set up for
+ starting and EAP-AKA/SIM authentication, it will talk to a SIM card
+ through its pcsc-lite API.
+
+
+- EAP-FAST
+
+ Priority: Low
+ Complexity: C1
+
+
+- WiFi p2p
+
+ Priority: Medium
+ Complexity: C2
+
+
+- Removing wpa_supplicant 0.7.x legacy support
+
+ Priority: Low
+ Complexity: C1
+ Owner: Tomasz Bursztyka <tomasz.bursztyka@linux.intel.com>
+
+ Removing global country property setter in gsupplicant, and removing
+ wifi's technology set_regdom implementation. Removing autoscan fallback.
+ (Note: should be done around the end 2012)
+
+Bluetooth
+=========
+
+- Remove Bluez 4.x support
+
+ Priority: Low
+ Complexity: C1
+
+ Remove plugins/bluetooth-legacy.c support in about 6 month (July 2013) or
+ when Bluez 4.x usage becomes minimal.
+
+Cellular
+========
+
+
+VPN
+===
+
+- IPsec
+
+ Priority: Medium
+ Complexity: C4
+ Owner: Jukka Rissanen <jukka.rissanen@linux.intel.com>
+
+
+Tools
+=====
+
+- Add Agent mode to connmanctl command line tool
+
+ Priority: Medium
+ Complexity: C2
+
+ connmanctl should implement agent prompting when started with a suitable
+ command line option. Agent mode should also be enabled when in interactive
+ mode.
+
+
+User Interface
+==============
+
+- GNOME3 UI
+
+ Priority: Low
+ Complexity: C4
+ Owner: Alok Barsode <alok.barsode@linux.intel.com>
+
+ A GNOME3 shell user interface would make it easier for mainstream distros
+ users to use ConnMan.
diff --git a/acinclude.m4 b/acinclude.m4
new file mode 100644
index 0000000..0a59871
--- /dev/null
+++ b/acinclude.m4
@@ -0,0 +1,61 @@
+AC_DEFUN([AC_PROG_CC_PIE], [
+ AC_CACHE_CHECK([whether ${CC-cc} accepts -fPIE], ac_cv_prog_cc_pie, [
+ echo 'void f(){}' > conftest.c
+ if test -z "`${CC-cc} -fPIE -pie -c conftest.c 2>&1`"; then
+ ac_cv_prog_cc_pie=yes
+ else
+ ac_cv_prog_cc_pie=no
+ fi
+ rm -rf conftest*
+ ])
+])
+
+AC_DEFUN([COMPILER_FLAGS], [
+ if (test "${CFLAGS}" = ""); then
+ CFLAGS="-Wall -O2 -D_FORTIFY_SOURCE=2"
+ fi
+ if (test "$USE_MAINTAINER_MODE" = "yes"); then
+ CFLAGS+=" -Werror -Wextra"
+ CFLAGS+=" -Wno-unused-parameter"
+ CFLAGS+=" -Wno-missing-field-initializers"
+ CFLAGS+=" -Wdeclaration-after-statement"
+ CFLAGS+=" -Wmissing-declarations"
+ CFLAGS+=" -Wredundant-decls"
+ CFLAGS+=" -Wcast-align"
+ CFLAGS="$CFLAGS -DG_DISABLE_DEPRECATED"
+ fi
+])
+
+AC_DEFUN([GTK_DOC_CHECK],
+[
+ AC_BEFORE([AC_PROG_LIBTOOL],[$0])dnl setup libtool first
+ AC_BEFORE([AM_PROG_LIBTOOL],[$0])dnl setup libtool first
+ dnl for overriding the documentation installation directory
+ AC_ARG_WITH([html-dir],
+ AS_HELP_STRING([--with-html-dir=PATH], [path to installed docs]),,
+ [with_html_dir='${datadir}/gtk-doc/html'])
+ HTML_DIR="$with_html_dir"
+ AC_SUBST([HTML_DIR])
+
+ dnl enable/disable documentation building
+ AC_ARG_ENABLE([gtk-doc],
+ AS_HELP_STRING([--enable-gtk-doc],
+ [use gtk-doc to build documentation [[default=no]]]),,
+ [enable_gtk_doc=no])
+
+ if test x$enable_gtk_doc = xyes; then
+ ifelse([$1],[],
+ [PKG_CHECK_EXISTS([gtk-doc],,
+ AC_MSG_ERROR([gtk-doc not installed and --enable-gtk-doc requested]))],
+ [PKG_CHECK_EXISTS([gtk-doc >= $1],,
+ AC_MSG_ERROR([You need to have gtk-doc >= $1 installed to build gtk-doc]))])
+ fi
+
+ AC_MSG_CHECKING([whether to build gtk-doc documentation])
+ AC_MSG_RESULT($enable_gtk_doc)
+
+ AC_PATH_PROGS(GTKDOC_CHECK,gtkdoc-check,)
+
+ AM_CONDITIONAL([ENABLE_GTK_DOC], [test x$enable_gtk_doc = xyes])
+ AM_CONDITIONAL([GTK_DOC_USE_LIBTOOL], [test -n "$LIBTOOL"])
+])
diff --git a/bootstrap b/bootstrap
new file mode 100755
index 0000000..fb2c692
--- /dev/null
+++ b/bootstrap
@@ -0,0 +1,9 @@
+#!/bin/sh
+
+cd "`dirname $0`"
+
+aclocal && \
+ autoheader && \
+ libtoolize --automake --copy --force && \
+ automake --add-missing --copy && \
+ autoconf
diff --git a/bootstrap-configure b/bootstrap-configure
new file mode 100755
index 0000000..0741bcd
--- /dev/null
+++ b/bootstrap-configure
@@ -0,0 +1,24 @@
+#!/bin/sh
+
+if [ -f config.status ]; then
+ make maintainer-clean
+fi
+
+if [ ! -f doc/gtk-doc.make ]; then
+ gtkdocize --copy --docdir doc
+fi
+
+./bootstrap && \
+ ./configure --enable-maintainer-mode \
+ --enable-debug \
+ --prefix=/usr \
+ --mandir=/usr/share/man \
+ --localstatedir=/var \
+ --sysconfdir=/etc \
+ --disable-datafiles \
+ --enable-openconnect=builtin \
+ --enable-openvpn=builtin \
+ --enable-vpnc=builtin \
+ --enable-session-policy-local=builtin \
+ --enable-nmcompat \
+ --enable-polkit $*
diff --git a/client/commands.c b/client/commands.c
new file mode 100644
index 0000000..75856cd
--- /dev/null
+++ b/client/commands.c
@@ -0,0 +1,538 @@
+/*
+ *
+ * Connection Manager
+ *
+ * Copyright (C) 2012 Intel Corporation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <getopt.h>
+
+#include <glib.h>
+#include <gdbus.h>
+
+#include "services.h"
+#include "technology.h"
+#include "data_manager.h"
+#include "monitor.h"
+#include "interactive.h"
+
+#define MANDATORY_ARGS 3
+
+static char *ipv4[] = {
+ "Method",
+ "Address",
+ "Netmask",
+ "Gateway",
+ NULL
+};
+
+static char *ipv6[] = {
+ "Method",
+ "Address",
+ "PrefixLength",
+ "Gateway",
+ "Privacy",
+ NULL
+};
+
+static char *proxy_simple[] = {
+ "Method",
+ "URL",
+ NULL
+};
+
+void show_help(void)
+{
+ printf("Usage: connmanctl <command> [args]\n"
+ " enable Enables given technology\n"
+ " <technology>\n"
+ " offlinemode Enables OfflineMode\n"
+ " disable Disables given technology\n"
+ " <technology>\n"
+ " offlinemode Disables OfflineMode\n"
+ " state Shows if the system is online or offline\n"
+ " services Display list of all services\n"
+ " --properties <service name> Show properties of service\n"
+ " technologies Current technology on the system\n"
+ " scan <technology> Scans for new services on the given technology\n"
+ " connect <service> Connect to a given service\n"
+ " disconnect <service> Disconnect from service\n"
+ " config <service> [arg] Set certain config options\n"
+ " --autoconnect=y/n Set autoconnect to service\n"
+ " --nameservers <names> Set manual name servers\n"
+ " --timeservers <names> Set manual time servers\n"
+ " --domains <domains> Set manual domains\n"
+ " --ipv4 Set ipv4 configuration\n"
+ " [METHOD|DHCP|AUTO|MANUAL] [IP] [NETMASK] [GATEWAY]\n"
+ " --ipv6 Set ipv6 configuration\n"
+ " [METHOD|AUTO|MANUAL|OFF] [IP] [PREFIXLENGTH] [GATEWAY]\n"
+ " [PRIVACY|DISABLED|ENABLED|PREFERED]\n"
+ " --proxy Set proxy configuration\n"
+ " [METHOD|URL|SERVERS|EXCLUDES]\n"
+ " if METHOD = manual, enter 'servers' then the list of servers\n"
+ " then enter 'excludes' then the list of excludes\n"
+ " --remove Remove the service from favorite\n"
+ " monitor Monitor signals from all Connman interfaces\n"
+ " --services Monitor signals from the Service interface\n"
+ " --tech Monitor signals from the Technology interface\n"
+ " --manager Monitor signals from the Manager interface\n"
+ " help, --help, (no arguments) Show this dialogue\n"
+ " exit, quit, q Quit interactive mode\n"
+ "\nNote: arguments and output are considered EXPERIMENTAL for now.\n\n");
+}
+
+int service_switch(int argc, char *argv[], int c, DBusConnection *conn,
+ struct service_data *service)
+{
+ const char *name;
+ DBusMessage *message;
+ int error = 0;
+
+ message = get_message(conn, "GetServices");
+ if (message == NULL)
+ return -ENOMEM;
+
+ switch (c) {
+ case 'p':
+ name = find_service(conn, message, argv[2], service);
+ if (name == NULL) {
+ error = -ENXIO;
+ break;
+ }
+
+ error = list_properties(conn, "GetServices", (char *) name);
+ break;
+ default:
+ fprintf(stderr, "Command not recognized, please check help\n");
+ error = -EINVAL;
+ break;
+ }
+
+ dbus_message_unref(message);
+
+ return error;
+}
+
+int config_switch(int argc, char *argv[], int c, DBusConnection *conn)
+{
+ DBusMessage *message;
+ int num_args = argc - MANDATORY_ARGS;
+ int error = 0;
+ dbus_bool_t val;
+
+ message = get_message(conn, "GetServices");
+ if (message == NULL)
+ return -ENOMEM;
+
+ switch (c) {
+ case 'a':
+ switch (*optarg) {
+ case 'y':
+ case '1':
+ case 't':
+ val = TRUE;
+ break;
+ case 'n':
+ case '0':
+ case 'f':
+ val = FALSE;
+ break;
+ default:
+ return -EINVAL;
+ }
+ error = set_service_property(conn, message, argv[1],
+ "AutoConnect", NULL,
+ &val, 0);
+ break;
+ case 'i':
+ error = set_service_property(conn, message, argv[1],
+ "IPv4.Configuration", ipv4,
+ argv + MANDATORY_ARGS, num_args);
+ break;
+ case 'v':
+ error = set_service_property(conn, message, argv[1],
+ "IPv6.Configuration", ipv6,
+ argv + MANDATORY_ARGS, num_args);
+ break;
+ case 'n':
+ error = set_service_property(conn, message, argv[1],
+ "Nameservers.Configuration", NULL,
+ argv + MANDATORY_ARGS, num_args);
+ break;
+ case 't':
+ error = set_service_property(conn, message, argv[1],
+ "Timeservers.Configuration", NULL,
+ argv + MANDATORY_ARGS, num_args);
+ break;
+ case 'd':
+ error = set_service_property(conn, message, argv[1],
+ "Domains.Configuration", NULL,
+ argv + MANDATORY_ARGS, num_args);
+ break;
+ case 'x':
+ if ((strcmp(argv[3], "direct") == 0 && argc < 5) ||
+ (strcmp(argv[3], "auto") == 0 && argc < 6)) {
+ error = set_service_property(conn, message, argv[1],
+ "Proxy.Configuration", proxy_simple,
+ argv + MANDATORY_ARGS, num_args);
+ } else if (strcmp(argv[3], "manual") == 0
+ && strcmp(argv[4], "servers") == 0
+ && argc > 5) {
+ argc -= 5;
+ error = store_proxy_input(conn, message, argv[1],
+ argc, &argv[5]);
+ } else {
+ fprintf(stderr, "Incorrect arguments\n");
+ error = -EINVAL;
+ }
+ break;
+ case 'r':
+ error = remove_service(conn, message, argv[1]);
+ break;
+ default:
+ fprintf(stderr, "Command not recognized, please check help\n");
+ error = -EINVAL;
+ break;
+ }
+
+ dbus_message_unref(message);
+
+ return error;
+}
+
+int monitor_switch(int argc, char *argv[], int c, DBusConnection *conn)
+{
+ int error;
+
+ switch (c) {
+ case 's':
+ error = monitor_connman(conn, "Service", "PropertyChanged");
+ if (error != 0)
+ return error;
+ if (dbus_connection_add_filter(conn, service_property_changed,
+ NULL, NULL) == FALSE)
+ return -ENOMEM;
+ printf("Now monitoring the service interface.\n");
+ break;
+ case 'c':
+ error = monitor_connman(conn, "Technology", "PropertyChanged");
+ if (error != 0)
+ return error;
+ if (dbus_connection_add_filter(conn, tech_property_changed,
+ NULL, NULL) == FALSE)
+ return -ENOMEM;
+ printf("Now monitoring the technology interface.\n");
+ break;
+ case 'm':
+ error = monitor_connman(conn, "Manager", "PropertyChanged");
+ if (error != 0)
+ return error;
+ error = monitor_connman(conn, "Manager", "TechnologyAdded");
+ if (error != 0)
+ return error;
+ error = monitor_connman(conn, "Manager", "TechnologyRemoved");
+ if (error != 0)
+ return error;
+ error = monitor_connman(conn, "Manager", "ServicesChanged");
+ if (error != 0)
+ return error;
+ if (dbus_connection_add_filter(conn, manager_property_changed,
+ NULL, NULL) == FALSE)
+ return -ENOMEM;
+ if (dbus_connection_add_filter(conn, tech_added_removed,
+ NULL, NULL) == FALSE)
+ return -ENOMEM;
+ if (dbus_connection_add_filter(conn, manager_services_changed,
+ NULL, NULL) == FALSE)
+ return -ENOMEM;
+ printf("Now monitoring the manager interface.\n");
+ break;
+ default:
+ fprintf(stderr, "Command not recognized, please check help\n");
+ return -EINVAL;
+ break;
+ }
+ return 0;
+}
+
+int commands_no_options(DBusConnection *connection, char *argv[], int argc)
+{
+ DBusMessage *message = NULL;
+ int error = 0;
+
+
+ if (strcmp(argv[0], "--help") == 0 || strcmp(argv[0], "help") == 0 ||
+ strcmp(argv[0], "h") == 0) {
+ show_help();
+ } else if (strcmp(argv[0], "state") == 0) {
+ if (argc != 1) {
+ fprintf(stderr, "State cannot accept an argument, "
+ "see help\n");
+ error = -EINVAL;
+ } else
+ error = list_properties(connection,
+ "GetProperties", NULL);
+ } else if (strcmp(argv[0], "technologies") == 0) {
+ if (argc != 1) {
+ fprintf(stderr, "Tech cannot accept an argument, "
+ "see help\n");
+ error = -EINVAL;
+ } else
+ error = list_properties(connection,
+ "GetTechnologies", NULL);
+ } else if (strcmp(argv[0], "connect") == 0) {
+ if (argc != 2) {
+ fprintf(stderr, "Connect requires a service name or "
+ "path, see help\n");
+ error = -EINVAL;
+ } else
+ error = connect_service(connection,
+ strip_service_path(argv[1]));
+ if (error == 0)
+ printf("Connected to: %s\n",
+ strip_service_path(argv[1]));
+ } else if (strcmp(argv[0], "disconnect") == 0) {
+ if (argc != 2) {
+ fprintf(stderr, "Disconnect requires a service name or "
+ "path, see help\n");
+ error = -EINVAL;
+ } else
+ error = disconnect_service(connection,
+ strip_service_path(argv[1]));
+ if (error == 0)
+ printf("Disconnected from: %s\n",
+ strip_service_path(argv[1]));
+ } else if (strcmp(argv[0], "scan") == 0) {
+ if (argc != 2) {
+ fprintf(stderr, "Scan requires a service name or path, "
+ "see help\n");
+ error = -EINVAL;
+ }
+ message = get_message(connection, "GetTechnologies");
+ if (message == NULL)
+ error = -ENOMEM;
+ else
+ error = scan_technology(connection, message, argv[1]);
+ } else if (strcmp(argv[0], "enable") == 0) {
+ if (argc != 2) {
+ fprintf(stderr, "Enable requires a technology name or "
+ "the argument 'offlinemode', see help\n");
+ error = -EINVAL;
+ } else if (strcmp(argv[1], "offlinemode") == 0) {
+ error = set_manager(connection, "OfflineMode", TRUE);
+ if (error == 0)
+ printf("OfflineMode is now enabled\n");
+ } else {
+ message = get_message(connection, "GetTechnologies");
+ if (message == NULL)
+ error = -ENOMEM;
+ else
+ error = set_technology(connection, message,
+ "Powered", argv[1], TRUE);
+ if (error == 0)
+ printf("Enabled %s technology\n", argv[1]);
+ }
+ } else if (strcmp(argv[0], "disable") == 0) {
+ if (argc != 2) {
+ fprintf(stderr, "Disable requires a technology name or "
+ "the argument 'offlinemode' see help\n");
+ error = -EINVAL;
+ } else if (strcmp(argv[1], "offlinemode") == 0) {
+ error = set_manager(connection, "OfflineMode", FALSE);
+ if (error == 0)
+ printf("OfflineMode is now disabled\n");
+ } else {
+ message = get_message(connection, "GetTechnologies");
+ if (message == NULL)
+ error = -ENOMEM;
+ else
+ error = set_technology(connection, message,
+ "Powered", argv[1], FALSE);
+ if (error == 0)
+ printf("Disabled %s technology\n", argv[1]);
+ }
+ } else
+ return -1;
+
+ if (message != NULL)
+ dbus_message_unref(message);
+
+ return error;
+}
+
+int commands_options(DBusConnection *connection, char *argv[], int argc)
+{
+ int error, c;
+ int option_index = 0;
+ struct service_data service;
+
+ static struct option service_options[] = {
+ {"properties", required_argument, 0, 'p'},
+ {0, 0, 0, 0}
+ };
+
+ static struct option config_options[] = {
+ {"nameservers", required_argument, 0, 'n'},
+ {"timeservers", required_argument, 0, 't'},
+ {"domains", required_argument, 0, 'd'},
+ {"ipv6", required_argument, 0, 'v'},
+ {"proxy", required_argument, 0, 'x'},
+ {"autoconnect", required_argument, 0, 'a'},
+ {"ipv4", required_argument, 0, 'i'},
+ {"remove", 0, 0, 'r'},
+ {0, 0, 0, 0}
+ };
+
+ static struct option monitor_options[] = {
+ {"services", no_argument, 0, 's'},
+ {"tech", no_argument, 0, 'c'},
+ {"manager", no_argument, 0, 'm'},
+ {0, 0, 0, 0}
+ };
+
+ if (strcmp(argv[0], "services") == 0) {
+ if (argc > 3) {
+ fprintf(stderr, "Too many arguments for services, "
+ "see help\n");
+ return -EINVAL;
+ }
+ if (argc < 2) {
+ printf("List of all services:\n");
+ error = list_properties(connection, "GetServices", NULL);
+ if (error != 0)
+ return error;
+ } else {
+ while ((c = getopt_long(argc, argv, "", service_options,
+ &option_index))) {
+ if (c == -1) {
+ if (option_index == 0) {
+ printf("Services takes an "
+ "option, see help.\n");
+ return -EINVAL;
+ }
+ break;
+ }
+ error = service_switch(argc, argv, c,
+ connection,
+ &service);
+ if (error != 0)
+ return error;
+ option_index++;
+ }
+ }
+ } else if (strcmp(argv[0], "config") == 0) {
+ if (argc < 3) {
+ fprintf(stderr, "Config requires an option, "
+ "see help\n");
+ return -EINVAL;
+ }
+ while ((c = getopt_long(argc, argv, "", config_options,
+ &option_index))) {
+ if (c == -1) {
+ if (option_index == 0) {
+ printf("Config requires an option, "
+ "see help\n");
+ return -EINVAL;
+ }
+ break;
+ }
+ error = config_switch(argc, argv, c, connection);
+ if (error != 0)
+ return error;
+ option_index++;
+ }
+ } else if (strcmp(argv[0], "monitor") == 0) {
+ if (argc > 2) {
+ fprintf(stderr, "Too many arguments for monitor, "
+ "see help\n");
+ return -EINVAL;
+ }
+ if (argc < 2) {
+ error = monitor_connman(connection, "Service",
+ "PropertyChanged");
+ if (error != 0)
+ return error;
+ error = monitor_connman(connection, "Technology",
+ "PropertyChanged");
+ if (error != 0)
+ return error;
+ error = monitor_connman(connection, "Manager",
+ "PropertyChanged");
+ if (error != 0)
+ return error;
+ error = monitor_connman(connection, "Manager",
+ "TechnologyAdded");
+ if (error != 0)
+ return error;
+ error = monitor_connman(connection, "Manager",
+ "TechnologyRemoved");
+ if (error != 0)
+ return error;
+ error = monitor_connman(connection, "Manager",
+ "ServicesChanged");
+ if (error != 0)
+ return error;
+ if (dbus_connection_add_filter(connection,
+ service_property_changed, NULL, NULL)
+ == FALSE)
+ return -ENOMEM;
+ if (dbus_connection_add_filter(connection,
+ tech_property_changed, NULL, NULL)
+ == FALSE)
+ return -ENOMEM;
+ if (dbus_connection_add_filter(connection,
+ tech_added_removed, NULL, NULL)
+ == FALSE)
+ return -ENOMEM;
+ if (dbus_connection_add_filter(connection,
+ manager_property_changed, NULL, NULL)
+ == FALSE)
+ return -ENOMEM;
+ if (dbus_connection_add_filter(connection,
+ manager_services_changed, NULL, NULL)
+ == FALSE)
+ return -ENOMEM;
+ printf("Now monitoring all interfaces.\n");
+ } else
+ while ((c = getopt_long(argc, argv, "", monitor_options,
+ &option_index))) {
+ if (c == -1) {
+ if (option_index == 0) {
+ printf("Monitor takes an "
+ "option, see help\n");
+ return -EINVAL;
+ }
+ break;
+ }
+ error = monitor_switch(argc, argv, c, connection);
+ if (error != 0)
+ return error;
+ option_index++;
+ }
+ } else
+ return -1;
+ return 0;
+}
diff --git a/client/data_manager.c b/client/data_manager.c
new file mode 100644
index 0000000..8dae718
--- /dev/null
+++ b/client/data_manager.c
@@ -0,0 +1,274 @@
+/*
+ *
+ * Connection Manager
+ *
+ * Copyright (C) 2012 Intel Corporation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdint.h>
+#include <unistd.h>
+#include <string.h>
+#include <errno.h>
+
+#include <glib.h>
+#include <gdbus.h>
+
+#include "services.h"
+#include "technology.h"
+#include "data_manager.h"
+#include "dbus.h"
+
+static void extract_manager_properties(DBusMessage *message)
+{
+ DBusMessageIter iter, array;
+
+ dbus_message_iter_init(message, &iter);
+ dbus_message_iter_recurse(&iter, &array);
+ extract_properties(&array);
+}
+
+int store_proxy_input(DBusConnection *connection, DBusMessage *message,
+ char *name, int num_args, char *argv[])
+{
+ int i, j, k;
+ int error = 0;
+ gchar **servers = NULL;
+ gchar **excludes = NULL;
+
+ for (i = 0; strcmp(argv[i], "excludes") != 0; i++) {
+ servers = g_try_realloc(servers, (i + 1) * sizeof(char *));
+ if (servers == NULL) {
+ fprintf(stderr, "Could not allocate memory for list\n");
+ return -ENOMEM;
+ }
+ servers[i] = g_strdup(argv[i]);
+ /* In case the user doesn't enter "excludes" */
+ if (i + 1 == num_args) {
+ i++;
+ j = 0;
+ goto free_servers;
+ }
+ }
+ for (j = 0; j + (i + 1) != num_args; j++) {
+ excludes = g_try_realloc(excludes, (j + 1) * sizeof(char *));
+ if (excludes == NULL) {
+ fprintf(stderr, "Could not allocate memory for list\n");
+ return -ENOMEM;
+ }
+ excludes[j] = g_strdup(argv[j + (i + 1)]);
+ }
+
+free_servers:
+ error = set_proxy_manual(connection, message, name, servers, excludes,
+ i, j);
+
+ for (k = 0; k < j - 1; k++)
+ g_free(excludes[k]);
+ g_free(excludes);
+
+ for (k = 0; k < i - 1; k++)
+ g_free(servers[k]);
+ g_free(servers);
+
+ return error;
+}
+
+int connect_service(DBusConnection *connection, char *name)
+{
+ DBusMessage *message, *message_connect = NULL;
+ struct service_data service;
+ char *path = NULL;
+ const char *path_name;
+ DBusError err;
+ int err_ret = 0;
+
+ message = get_message(connection, "GetServices");
+ if (message == NULL)
+ return -ENOMEM;
+
+ path_name = find_service(connection, message, name, &service);
+ if (path_name == NULL) {
+ err_ret = -ENXIO;
+ goto error;
+ }
+
+ path = g_strdup_printf("/net/connman/service/%s", path_name);
+ message_connect = dbus_message_new_method_call("net.connman", path,
+ "net.connman.Service",
+ "Connect");
+ if (message_connect == NULL) {
+ err_ret = -ENOMEM;
+ goto error;
+ }
+
+ dbus_error_init(&err);
+ dbus_connection_send_with_reply_and_block(connection, message_connect,
+ -1, &err);
+
+ if (dbus_error_is_set(&err)) {
+ printf("Connection failed; error: '%s'\n", err.message);
+ err_ret = -EINVAL;
+ goto error;
+ }
+
+ dbus_connection_send(connection, message_connect, NULL);
+ dbus_connection_flush(connection);
+
+error:
+ if (message != NULL)
+ dbus_message_unref(message);
+ if (message_connect != NULL)
+ dbus_message_unref(message_connect);
+ g_free(path);
+
+ return err_ret;
+}
+
+int disconnect_service(DBusConnection *connection, char *name)
+{
+ DBusMessage *message, *message_disconnect = NULL;
+ struct service_data service;
+ char *path = NULL;
+ const char *path_name;
+ DBusError err;
+ int err_ret = 0;
+
+ message = get_message(connection, "GetServices");
+ if (message == NULL)
+ return -ENOMEM;
+
+ path_name = find_service(connection, message, name, &service);
+ if (path_name == NULL) {
+ err_ret = -ENXIO;
+ goto error;
+ }
+
+ path = g_strdup_printf("/net/connman/service/%s", path_name);
+ printf("%s\n", path);
+ message_disconnect = dbus_message_new_method_call("net.connman", path,
+ "net.connman.Service",
+ "Disconnect");
+ if (message_disconnect == NULL) {
+ err_ret = -ENOMEM;
+ goto error;
+ }
+
+ dbus_error_init(&err);
+ dbus_connection_send_with_reply_and_block(connection,
+ message_disconnect,
+ -1, &err);
+
+ if (dbus_error_is_set(&err)) {
+ printf("Connection failed; error: '%s'\n", err.message);
+ err_ret = -EINVAL;
+ goto error;
+ }
+
+ dbus_connection_send(connection, message_disconnect, NULL);
+ dbus_connection_flush(connection);
+
+error:
+ if (message != NULL)
+ dbus_message_unref(message);
+ if (message_disconnect != NULL)
+ dbus_message_unref(message_disconnect);
+ g_free(path);
+
+ return err_ret;
+}
+
+int set_manager(DBusConnection *connection, char *key, dbus_bool_t value)
+{
+ DBusMessage *message;
+ DBusMessageIter iter;
+
+ message = dbus_message_new_method_call("net.connman", "/",
+ "net.connman.Manager",
+ "SetProperty");
+ if (message == NULL)
+ return -ENOMEM;
+
+ dbus_message_iter_init_append(message, &iter);
+ dbus_property_append_basic(&iter, (const char *) key,
+ DBUS_TYPE_BOOLEAN, &value);
+ dbus_connection_send(connection, message, NULL);
+ dbus_connection_flush(connection);
+ dbus_message_unref(message);
+
+ return 0;
+}
+
+/* Call with any given function we want connman to respond to */
+DBusMessage *get_message(DBusConnection *connection, char *function)
+{
+ DBusMessage *message, *reply;
+ DBusError error;
+
+ message = dbus_message_new_method_call(CONNMAN_SERVICE,
+ CONNMAN_MANAGER_PATH,
+ CONNMAN_MANAGER_INTERFACE,
+ function);
+ if (message == NULL)
+ return NULL;
+
+ dbus_error_init(&error);
+
+ reply = dbus_connection_send_with_reply_and_block(connection,
+ message, -1, &error);
+ if (dbus_error_is_set(&error) == TRUE) {
+ fprintf(stderr, "%s\n", error.message);
+ dbus_error_free(&error);
+ } else if (reply == NULL)
+ fprintf(stderr, "Failed to receive message\n");
+
+ dbus_message_unref(message);
+
+ return reply;
+}
+
+int list_properties(DBusConnection *connection, char *function,
+ char *service_name)
+{
+ DBusMessage *message;
+
+ message = get_message(connection, function);
+ if (message == NULL)
+ return -ENOMEM;
+
+ if (strcmp(function, "GetProperties") == 0)
+ extract_manager_properties(message);
+
+ else if (strcmp(function, "GetServices") == 0 && service_name != NULL)
+ extract_services(message, service_name);
+
+ else if (strcmp(function, "GetServices") == 0 && service_name == NULL)
+ get_services(message);
+
+ else if (strcmp(function, "GetTechnologies") == 0)
+ extract_tech(message);
+
+ dbus_message_unref(message);
+
+ return 0;
+}
diff --git a/client/data_manager.h b/client/data_manager.h
new file mode 100644
index 0000000..211eeb3
--- /dev/null
+++ b/client/data_manager.h
@@ -0,0 +1,45 @@
+/*
+ *
+ * Connection Manager
+ *
+ * Copyright (C) 2012 Intel Corporation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ */
+
+#include <dbus/dbus.h>
+
+#define SIGNAL_LISTEN_TIMEOUT 10
+
+struct signal_args {
+ DBusConnection *connection;
+ const char *signal_name;
+};
+
+struct proxy_input {
+ char *servers;
+ char *excludes;
+};
+
+DBusMessage *get_message(DBusConnection *connection, char *function);
+int store_proxy_input(DBusConnection *connection, DBusMessage *message,
+ char *name, int num_args, char *argv[]);
+int list_properties(DBusConnection *connection, char *function,
+ char *service_name);
+int connect_service(DBusConnection *connection, char *name);
+int disconnect_service(DBusConnection *connection, char *name);
+int set_manager(DBusConnection *connection, char *key, dbus_bool_t value);
+void listen_for_manager_signal(void *args);
diff --git a/client/dbus.c b/client/dbus.c
new file mode 100644
index 0000000..002d858
--- /dev/null
+++ b/client/dbus.c
@@ -0,0 +1,80 @@
+/*
+ *
+ * Connection Manager
+ *
+ * Copyright (C) 2012 Intel Corporation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <dbus/dbus.h>
+
+#include "dbus.h"
+
+void dbus_property_append_basic(DBusMessageIter *iter,
+ const char *key, int type, void *val)
+{
+ DBusMessageIter value;
+ const char *signature;
+
+ dbus_message_iter_append_basic(iter, DBUS_TYPE_STRING, &key);
+
+ switch (type) {
+ case DBUS_TYPE_BOOLEAN:
+ signature = DBUS_TYPE_BOOLEAN_AS_STRING;
+ break;
+ case DBUS_TYPE_STRING:
+ signature = DBUS_TYPE_STRING_AS_STRING;
+ break;
+ case DBUS_TYPE_BYTE:
+ signature = DBUS_TYPE_BYTE_AS_STRING;
+ break;
+ case DBUS_TYPE_UINT16:
+ signature = DBUS_TYPE_UINT16_AS_STRING;
+ break;
+ case DBUS_TYPE_INT16:
+ signature = DBUS_TYPE_INT16_AS_STRING;
+ break;
+ case DBUS_TYPE_UINT32:
+ signature = DBUS_TYPE_UINT32_AS_STRING;
+ break;
+ case DBUS_TYPE_INT32:
+ signature = DBUS_TYPE_INT32_AS_STRING;
+ break;
+ case DBUS_TYPE_UINT64:
+ signature = DBUS_TYPE_UINT64_AS_STRING;
+ break;
+ case DBUS_TYPE_INT64:
+ signature = DBUS_TYPE_INT64_AS_STRING;
+ break;
+ case DBUS_TYPE_OBJECT_PATH:
+ signature = DBUS_TYPE_OBJECT_PATH_AS_STRING;
+ break;
+ default:
+ signature = DBUS_TYPE_VARIANT_AS_STRING;
+ break;
+ }
+
+ dbus_message_iter_open_container(iter, DBUS_TYPE_VARIANT,
+ signature, &value);
+ dbus_message_iter_append_basic(&value, type, val);
+ dbus_message_iter_close_container(iter, &value);
+}
+
diff --git a/client/dbus.h b/client/dbus.h
new file mode 100644
index 0000000..3cde017
--- /dev/null
+++ b/client/dbus.h
@@ -0,0 +1,65 @@
+/*
+ *
+ * Connection Manager
+ *
+ * Copyright (C) 2012 Intel Corporation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ */
+
+#include <dbus/dbus.h>
+
+#define CONNMAN_SERVICE "net.connman"
+
+#define CONNMAN_MANAGER_INTERFACE CONNMAN_SERVICE ".Manager"
+#define CONNMAN_MANAGER_PATH "/"
+
+#define CONNMAN_SERVICE_INTERFACE CONNMAN_SERVICE ".Service"
+
+void dbus_property_append_basic(DBusMessageIter *iter,
+ const char *key, int type, void *val);
+
+static inline void dbus_dict_open(DBusMessageIter *iter, DBusMessageIter *dict)
+{
+ dbus_message_iter_open_container(iter, DBUS_TYPE_ARRAY,
+ DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING
+ DBUS_TYPE_STRING_AS_STRING DBUS_TYPE_VARIANT_AS_STRING
+ DBUS_DICT_ENTRY_END_CHAR_AS_STRING, dict);
+}
+
+static inline void dbus_dict_open_variant(DBusMessageIter *iter,
+ DBusMessageIter *dict)
+{
+ dbus_message_iter_open_container(iter, DBUS_TYPE_VARIANT,
+ DBUS_TYPE_ARRAY_AS_STRING
+ DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING
+ DBUS_TYPE_STRING_AS_STRING DBUS_TYPE_VARIANT_AS_STRING
+ DBUS_DICT_ENTRY_END_CHAR_AS_STRING, dict);
+}
+
+static inline void dbus_array_open(DBusMessageIter *iter, DBusMessageIter *dict)
+{
+ dbus_message_iter_open_container(iter, DBUS_TYPE_VARIANT,
+ DBUS_TYPE_ARRAY_AS_STRING
+ DBUS_TYPE_STRING_AS_STRING,
+ dict);
+}
+
+static inline void dbus_dict_close(DBusMessageIter *iter, DBusMessageIter *dict)
+{
+ dbus_message_iter_close_container(iter, dict);
+}
+
diff --git a/client/interactive.c b/client/interactive.c
new file mode 100644
index 0000000..485ae0f
--- /dev/null
+++ b/client/interactive.c
@@ -0,0 +1,148 @@
+/*
+ *
+ * Connection Manager
+ *
+ * Copyright (C) 2012 Intel Corporation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <readline/readline.h>
+#include <readline/history.h>
+#include <getopt.h>
+#include <unistd.h>
+#include <sys/time.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+
+#include <glib.h>
+#include <gdbus.h>
+
+#include "services.h"
+#include "technology.h"
+#include "data_manager.h"
+#include "monitor.h"
+#include "interactive.h"
+
+static DBusConnection *interactive_conn;
+
+static char **parse_long(char *input, int *num_args)
+{
+ int i;
+ char **token = NULL;
+
+ for (i = 0; input != NULL; i++) {
+ token = realloc(token, (i + 1) * sizeof(char *));
+ if (token == NULL)
+ return NULL;
+ token[i] = strdup(input);
+ input = strtok(NULL, " ");
+ }
+ *num_args = i;
+
+ return token;
+}
+
+static gboolean rl_handler(char *input)
+{
+ char **long_args;
+ int num_args, i, error=FALSE;
+ num_args = 0;
+
+ if (input == NULL) {
+ rl_newline(1, '\n');
+ exit(EXIT_FAILURE);
+ }
+
+ add_history(input);
+ input = strtok(input, " ");
+
+ if (input == NULL)
+ goto bail;
+ long_args = parse_long(input, &num_args);
+
+ if (long_args == NULL) {
+ free(input);
+ exit(EXIT_FAILURE);
+ } else {
+ error = commands_no_options(interactive_conn,
+ long_args, num_args);
+ if (error == -1)
+ error = commands_options(interactive_conn, long_args,
+ num_args);
+ else
+ goto bail;
+ }
+ if ((strcmp(long_args[0], "quit") == 0)
+ || (strcmp(long_args[0], "exit") == 0)
+ || (strcmp(long_args[0], "q") == 0)) {
+ for (i = 0; i < num_args; i++)
+ free(long_args[i]);
+ free(long_args);
+ exit(EXIT_SUCCESS);
+ }
+ if (error == -1) {
+ fprintf(stderr, "%s is not a valid command, check help.\n",
+ long_args[0]);
+ }
+
+ for (i = 0; i < num_args; i++)
+ free(long_args[i]);
+ free(long_args);
+ optind = 0;
+
+ error = TRUE;
+bail:
+ rl_callback_handler_install("connmanctl> ", (void *)rl_handler);
+ return error;
+}
+
+static gboolean readmonitor(GIOChannel *channel, GIOCondition condition,
+ gpointer user_data){
+ if (condition & (G_IO_HUP | G_IO_ERR | G_IO_NVAL)) {
+ g_io_channel_unref(channel);
+ return FALSE;
+ }
+ rl_callback_read_char();
+ return TRUE;
+}
+
+void show_interactive(DBusConnection *connection, GMainLoop *mainloop)
+{
+ GIOChannel *gchan;
+ int events;
+ gchan = g_io_channel_unix_new(fileno(stdin));
+ events = G_IO_IN | G_IO_ERR | G_IO_HUP | G_IO_NVAL;
+ interactive_conn = connection;
+
+ while (TRUE) {
+ g_io_add_watch(gchan, events, readmonitor, NULL);
+ rl_callback_handler_install("connmanctl> ", (void *)rl_handler);
+ g_main_loop_run(mainloop);
+
+ rl_callback_handler_remove();
+ g_io_channel_unref(gchan);
+ }
+}
diff --git a/client/interactive.h b/client/interactive.h
new file mode 100644
index 0000000..a2bd051
--- /dev/null
+++ b/client/interactive.h
@@ -0,0 +1,32 @@
+/*
+ *
+ * Connection Manager
+ *
+ * Copyright (C) 2012 Intel Corporation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ */
+
+#include <dbus/dbus.h>
+
+void show_interactive(DBusConnection *connection, GMainLoop *mainloop);
+int commands_no_options(DBusConnection *connection, char *argv[], int argc);
+int commands_options(DBusConnection *connection, char *argv[], int argc);
+void show_help(void);
+int monitor_switch(int argc, char *argv[], int c, DBusConnection *conn);
+int config_switch(int argc, char *argv[], int c, DBusConnection *conn);
+int service_switch(int argc, char *argv[], int c, DBusConnection *conn,
+ struct service_data *service);
diff --git a/client/main.c b/client/main.c
new file mode 100644
index 0000000..ab64277
--- /dev/null
+++ b/client/main.c
@@ -0,0 +1,126 @@
+/*
+ *
+ * Connection Manager
+ *
+ * Copyright (C) 2012 Intel Corporation. All rights reserved.
+ *
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <getopt.h>
+#include <string.h>
+#include <errno.h>
+#include <readline/readline.h>
+
+#include <glib.h>
+#include <gdbus.h>
+
+#include "data_manager.h"
+#include "services.h"
+#include "technology.h"
+#include "interactive.h"
+#include "monitor.h"
+
+static GMainLoop *main_loop;
+
+static gboolean timeout_wait(gpointer data)
+{
+ static int i;
+ i++;
+ /* Set to whatever number of retries is wanted/needed */
+ if (i == 1) {
+ g_main_loop_quit(data);
+ return FALSE;
+ }
+ return TRUE;
+}
+
+static void rl_handler(char *input)
+{
+
+ if (input == NULL)
+ exit(EXIT_FAILURE);
+ else
+ printf("Use ctrl-d to exit\n");
+}
+
+static gboolean readmonitor(GIOChannel *channel, GIOCondition condition,
+ gpointer user_data){
+ rl_callback_read_char();
+ return TRUE;
+}
+
+int main(int argc, char *argv[])
+{
+ DBusConnection *connection;
+ DBusError err;
+ int events, error;
+ GIOChannel *gchan;
+ main_loop = g_main_loop_new(NULL, FALSE);
+
+ dbus_error_init(&err);
+
+ connection = g_dbus_setup_bus(DBUS_BUS_SYSTEM, NULL, &err);
+
+ if (dbus_error_is_set(&err)) {
+ fprintf(stderr, "Connection Error: %s\n", err.message);
+ dbus_error_free(&err);
+ }
+
+ if (connection == NULL) {
+ fprintf(stderr, "Could not connect to system bus...exiting\n");
+ exit(EXIT_FAILURE);
+ }
+
+ if (argc < 2)
+ show_interactive(connection, main_loop);
+
+ error = commands_no_options(connection, argv + 1, argc - 1);
+ if (error == -1) {
+ error = commands_options(connection, argv + 1, argc - 1);
+ if (strcmp(argv[1], "monitor") != 0)
+ return error;
+ } else {
+ return error;
+ }
+
+ if (error == -1) {
+ fprintf(stderr, "%s is not a valid command, check help.\n",
+ argv[1]);
+ return -EINVAL;
+ }
+ gchan = g_io_channel_unix_new(fileno(stdin));
+ events = G_IO_IN | G_IO_ERR | G_IO_HUP | G_IO_NVAL;
+ g_io_add_watch(gchan, events, readmonitor, NULL);
+ rl_callback_handler_install("", rl_handler);
+
+ if (strcmp(argv[1], "monitor") != 0)
+ g_timeout_add_full(G_PRIORITY_DEFAULT, 100, timeout_wait,
+ main_loop, NULL);
+ g_main_loop_run(main_loop);
+ rl_callback_handler_remove();
+ g_io_channel_unref(gchan);
+ if (main_loop != NULL)
+ g_main_loop_unref(main_loop);
+ return 0;
+}
diff --git a/client/monitor.c b/client/monitor.c
new file mode 100644
index 0000000..5e8b392
--- /dev/null
+++ b/client/monitor.c
@@ -0,0 +1,254 @@
+/*
+ *
+ * Connection Manager
+ *
+ * Copyright (C) 2012 Intel Corporation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+#include <stdint.h>
+
+#include <glib.h>
+#include <gdbus.h>
+
+#include "monitor.h"
+#include "services.h"
+#include "technology.h"
+#include "data_manager.h"
+
+static const char *get_service_name(DBusMessage *message, char *dbus_path)
+{
+ DBusMessageIter iter, array;
+
+ dbus_message_iter_init(message, &iter);
+ dbus_message_iter_recurse(&iter, &array);
+
+ while (dbus_message_iter_get_arg_type(&array) == DBUS_TYPE_STRUCT) {
+ DBusMessageIter entry, dict;
+ struct service_data service;
+ char *path;
+
+ dbus_message_iter_recurse(&array, &entry);
+ dbus_message_iter_get_basic(&entry, &path);
+
+ if (g_strcmp0(path, dbus_path) == 0) {
+ dbus_message_iter_next(&entry);
+ dbus_message_iter_recurse(&entry, &dict);
+ extract_service_name(&dict, &service);
+ return service.name;
+ } else {
+ dbus_message_iter_next(&array);
+ }
+ }
+ return NULL;
+}
+
+static void extract_tech_signal(DBusMessage *message)
+{
+ DBusMessageIter iter, dict;
+ char *path;
+
+ dbus_message_iter_init(message, &iter);
+
+ if (dbus_message_iter_get_arg_type(&iter) == DBUS_TYPE_OBJECT_PATH) {
+ dbus_message_iter_get_basic(&iter, &path);
+ printf(" { %s }\n", path);
+ }
+ dbus_message_iter_next(&iter);
+
+ if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_INVALID) {
+ dbus_message_iter_recurse(&iter, &dict);
+ extract_properties(&dict);
+ }
+}
+
+static void extract_signal_args(DBusMessage *message)
+{
+ DBusMessageIter iter, array, dict;
+ char *string, *value;
+ uint16_t key_int;
+ dbus_bool_t bvalue;
+
+ value = NULL;
+ key_int = 0;
+
+ dbus_message_iter_init(message, &iter);
+
+ while (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_INVALID) {
+ if (dbus_message_iter_get_arg_type(&iter) == DBUS_TYPE_STRING) {
+ dbus_message_iter_get_basic(&iter, &string);
+ printf("\n[%s] = ",
+ string);
+ }
+ dbus_message_iter_next(&iter);
+ if (dbus_message_iter_get_arg_type(&iter) !=
+ DBUS_TYPE_INVALID) {
+ dbus_message_iter_recurse(&iter, &array);
+ if (dbus_message_iter_get_arg_type(&array) ==
+ DBUS_TYPE_STRING) {
+ dbus_message_iter_get_basic(&array, &value);
+ printf("%s\n", value);
+ continue;
+ } else if (dbus_message_iter_get_arg_type(&array) ==
+ DBUS_TYPE_BOOLEAN) {
+ dbus_message_iter_get_basic(&array, &bvalue);
+ printf("%s\n", bvalue == TRUE ?
+ "True" : "False");
+ continue;
+ } else if (dbus_message_iter_get_arg_type(&array) ==
+ DBUS_TYPE_ARRAY)
+ dbus_message_iter_recurse(&array, &dict);
+ if (dbus_message_iter_get_arg_type(&dict) ==
+ DBUS_TYPE_DICT_ENTRY) {
+ iterate_dict(&dict, value, key_int);
+ printf("\n");
+ } else {
+ iterate_array(&array);
+ printf("\n");
+ }
+ dbus_message_iter_next(&iter);
+ }
+ }
+}
+
+int monitor_connman(DBusConnection *connection, char *interface,
+ char *signal_name)
+{
+ char *rule = g_strdup_printf("type='signal',interface='net.connman.%s',"
+ "member='%s'", interface, signal_name);
+ DBusError err;
+
+ dbus_error_init(&err);
+ g_dbus_setup_bus(DBUS_BUS_SYSTEM, NULL, &err);
+ if (dbus_error_is_set(&err)) {
+ fprintf(stderr, "Bus setup error:%s\n", err.message);
+ return -1;
+ }
+ dbus_bus_add_match(connection, rule, &err);
+
+ if (dbus_error_is_set(&err)) {
+ fprintf(stderr, "Match Error: %s\n", err.message);
+ return -1;
+ }
+ return 0;
+}
+
+DBusHandlerResult service_property_changed(DBusConnection *connection,
+ DBusMessage *message,
+ void *user_data)
+{
+ DBusMessage *service_message;
+ struct service_data service;
+
+ if (dbus_message_is_signal(message, "net.connman.Service",
+ "PropertyChanged")) {
+ service_message = get_message(connection, "GetServices");
+ if (service_message == NULL)
+ return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
+
+ service.name = get_service_name(service_message,
+ (char *) dbus_message_get_path(message));
+ printf("\n");
+ g_message("Path = %s, Interface = %s\nService = %s",
+ dbus_message_get_path(message),
+ dbus_message_get_interface(message),
+ service.name);
+ extract_signal_args(message);
+
+ dbus_message_unref(service_message);
+ }
+
+ return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
+}
+
+DBusHandlerResult tech_property_changed(DBusConnection *connection,
+ DBusMessage *message, void *user_data)
+{
+ if (dbus_message_is_signal(message, "net.connman.Technology",
+ "PropertyChanged")) {
+ printf("\n");
+ g_message("Path = %s, Interface = %s",
+ dbus_message_get_path(message),
+ dbus_message_get_interface(message));
+ extract_signal_args(message);
+ }
+ return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
+}
+
+DBusHandlerResult tech_added_removed(DBusConnection *connection,
+ DBusMessage *message, void *user_data)
+{
+ if (dbus_message_is_signal(message, "net.connman.Manager",
+ "TechnologyAdded")) {
+ printf("\n");
+ g_message("Path = %s, Interface = %s",
+ dbus_message_get_path(message),
+ dbus_message_get_interface(message));
+ printf("New technology added:\n");
+ extract_tech_signal(message);
+ } else if (dbus_message_is_signal(message, "net.connman.Manager",
+ "TechnologyRemoved")) {
+ printf("\n");
+ g_message("Path = %s, Interface = %s",
+ dbus_message_get_path(message),
+ dbus_message_get_interface(message));
+ printf("Technology was removed:\n");
+ extract_tech_signal(message);
+ }
+
+ return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
+}
+
+DBusHandlerResult manager_services_changed(DBusConnection *connection,
+ DBusMessage *message, void *user_data)
+{
+ if (dbus_message_is_signal(message, "net.connman.Manager",
+ "ServicesChanged")) {
+ printf("\n");
+ g_message("Path = %s, Interface = %s",
+ dbus_message_get_path(message),
+ dbus_message_get_interface(message));
+ printf("Services Changed, displaying updated "
+ "list of services:\n");
+ list_properties(connection, "GetServices", NULL);
+ }
+
+ return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
+}
+
+DBusHandlerResult manager_property_changed(DBusConnection *connection,
+ DBusMessage *message, void *user_data)
+{
+ if (dbus_message_is_signal(message, "net.connman.Manager",
+ "PropertyChanged")) {
+ printf("\n");
+ g_message("Path = %s, Interface = %s",
+ dbus_message_get_path(message),
+ dbus_message_get_interface(message));
+ extract_signal_args(message);
+ }
+
+ return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
+}
diff --git a/client/monitor.h b/client/monitor.h
new file mode 100644
index 0000000..db64aa1
--- /dev/null
+++ b/client/monitor.h
@@ -0,0 +1,41 @@
+/*
+ *
+ * Connection Manager
+ *
+ * Copyright (C) 2012 Intel Corporation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ */
+
+#include <dbus/dbus.h>
+
+int monitor_connman(DBusConnection *connection, char *interface,
+ char *signal_name);
+
+DBusHandlerResult service_property_changed(DBusConnection *connection,
+ DBusMessage *message, void *user_data);
+
+DBusHandlerResult tech_property_changed(DBusConnection *connection,
+ DBusMessage *message, void *user_data);
+
+DBusHandlerResult tech_added_removed(DBusConnection *connection,
+ DBusMessage *message, void *user_data);
+
+DBusHandlerResult manager_property_changed(DBusConnection *connection,
+ DBusMessage *message, void *user_data);
+
+DBusHandlerResult manager_services_changed(DBusConnection *connection,
+ DBusMessage *message, void *user_data);
diff --git a/client/services.c b/client/services.c
new file mode 100644
index 0000000..7e6424f
--- /dev/null
+++ b/client/services.c
@@ -0,0 +1,535 @@
+/*
+ *
+ * Connection Manager
+ *
+ * Copyright (C) 2012 Intel Corporation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdint.h>
+#include <string.h>
+#include <unistd.h>
+#include <errno.h>
+
+#include <glib.h>
+
+#include "services.h"
+#include "dbus.h"
+
+static void append_property_array(DBusMessageIter *iter, char *property,
+ char **data, int num_args)
+{
+ DBusMessageIter value, array;
+ int i;
+
+ dbus_message_iter_append_basic(iter, DBUS_TYPE_STRING, &property);
+
+ dbus_array_open(iter, &value);
+ dbus_message_iter_open_container(&value, DBUS_TYPE_ARRAY,
+ DBUS_TYPE_STRING_AS_STRING, &array);
+
+ for (i = 0; i < num_args; i++) {
+ dbus_message_iter_append_basic(&array, DBUS_TYPE_STRING,
+ &data[i]);
+ printf("Added: %s\n", data[i]);
+ }
+ dbus_message_iter_close_container(&value, &array);
+ dbus_message_iter_close_container(iter, &value);
+}
+
+static void append_property_dict(DBusMessageIter *iter, char *property,
+ char **keys, char **data, int num_args)
+{
+ DBusMessageIter value, dict, entry, dict_key;
+ int i;
+ unsigned char prefix;
+
+ dbus_message_iter_append_basic(iter, DBUS_TYPE_STRING, &property);
+
+ /* Top most level is a{sv} */
+ dbus_dict_open_variant(iter, &value);
+
+ dbus_dict_open(&value, &dict);
+
+ for (i = 0; i < num_args; i++) {
+ dbus_message_iter_open_container(&dict, DBUS_TYPE_DICT_ENTRY,
+ NULL, &entry);
+ dbus_message_iter_append_basic(&entry, DBUS_TYPE_STRING,
+ &keys[i]);
+
+ if (strcmp(property, "IPv6.Configuration") == 0 &&
+ g_strcmp0(keys[i], "PrefixLength")) {
+ if (data[i] == NULL) {
+ fprintf(stderr, "No values entered!\n");
+ exit(EXIT_FAILURE);
+ }
+ prefix = atoi(data[i]);
+
+ dbus_message_iter_open_container(&entry,
+ DBUS_TYPE_VARIANT,
+ DBUS_TYPE_BYTE_AS_STRING,
+ &dict_key);
+ dbus_message_iter_append_basic(&dict_key,
+ DBUS_TYPE_BYTE, &prefix);
+ } else {
+ dbus_message_iter_open_container(&entry,
+ DBUS_TYPE_VARIANT,
+ DBUS_TYPE_STRING_AS_STRING,
+ &dict_key);
+ dbus_message_iter_append_basic(&dict_key,
+ DBUS_TYPE_STRING,
+ &data[i]);
+ }
+ dbus_message_iter_close_container(&entry, &dict_key);
+ dbus_message_iter_close_container(&dict, &entry);
+ }
+ /* Close {sv}, then close a{sv} */
+ dbus_dict_close(&value, &dict);
+ dbus_dict_close(iter, &value);
+}
+
+void iterate_array(DBusMessageIter *iter)
+{
+ DBusMessageIter array_item;
+ dbus_bool_t key_bool;
+ char *key_str;
+
+ dbus_message_iter_recurse(iter, &array_item);
+ /* Make sure the entry is not NULL! */
+ printf("[ ");
+ while (dbus_message_iter_get_arg_type(&array_item) !=
+ DBUS_TYPE_INVALID) {
+ if (dbus_message_iter_get_arg_type(&array_item) ==
+ DBUS_TYPE_STRING) {
+ dbus_message_iter_get_basic(&array_item,
+ &key_str);
+ printf("%s ", key_str);
+ } else if (dbus_message_iter_get_arg_type(&array_item) ==
+ DBUS_TYPE_BOOLEAN) {
+ dbus_message_iter_get_basic(&array_item, &key_bool);
+ printf("%s ", key_bool == TRUE ? "True"
+ : "False");
+ }
+ dbus_message_iter_next(&array_item);
+ }
+ if (dbus_message_iter_get_arg_type(&array_item) ==
+ DBUS_TYPE_INVALID)
+ printf("] ");
+}
+
+void iterate_dict(DBusMessageIter *dict, char *string, uint16_t key_int)
+{
+ DBusMessageIter dict_entry, sub_dict_entry;
+
+ printf("{ ");
+ while (dbus_message_iter_get_arg_type(dict) == DBUS_TYPE_DICT_ENTRY) {
+ dbus_message_iter_recurse(dict, &dict_entry);
+ dbus_message_iter_get_basic(&dict_entry, &string);
+ printf("%s=", string);
+ dbus_message_iter_next(&dict_entry);
+ while (dbus_message_iter_get_arg_type(&dict_entry)
+ != DBUS_TYPE_INVALID) {
+ dbus_message_iter_recurse(&dict_entry, &sub_dict_entry);
+ if (dbus_message_iter_get_arg_type(&sub_dict_entry)
+ == DBUS_TYPE_UINT16) {
+ dbus_message_iter_get_basic(&sub_dict_entry,
+ &key_int);
+ printf("%d ", key_int);
+ } else if (dbus_message_iter_get_arg_type(&sub_dict_entry)
+ == DBUS_TYPE_STRING) {
+ dbus_message_iter_get_basic(&sub_dict_entry,
+ &string);
+ printf("%s ", string);
+ } else if (dbus_message_iter_get_arg_type(&sub_dict_entry)
+ == DBUS_TYPE_ARRAY) {
+ iterate_array(&sub_dict_entry);
+ }
+ dbus_message_iter_next(&dict_entry);
+ }
+ dbus_message_iter_next(dict);
+ }
+ printf("}");
+}
+
+/* Get dictionary info about the current service and store it */
+static void extract_service_properties(DBusMessageIter *dict,
+ struct service_data *service)
+{
+ DBusMessageIter entry, value, array_item;
+ char *key;
+ char *key_str;
+ uint16_t key_uint16;
+ dbus_bool_t key_bool;
+
+ while (dbus_message_iter_get_arg_type(dict) == DBUS_TYPE_DICT_ENTRY) {
+ dbus_message_iter_recurse(dict, &entry);
+ dbus_message_iter_get_basic(&entry, &key);
+ printf("\n %s = ", key);
+ if (strcmp(key, "Name") == 0 && strlen(key) < 5)
+ service->name = key;
+
+ dbus_message_iter_next(&entry);
+ dbus_message_iter_recurse(&entry, &value);
+ /* Check if entry is a dictionary itself */
+ if (strcmp(key, "Ethernet") == 0 ||
+ /* if just strcmp, the .Configuration names don't match
+ * and they are iterated with iterate_array instead*/
+ strncmp(key, "IPv4", 4) == 0 ||
+ strncmp(key, "IPv6", 4) == 0 ||
+ strncmp(key, "Proxy", 5) == 0 ||
+ strcmp(key, "Provider") == 0) {
+ dbus_message_iter_recurse(&value, &array_item);
+ iterate_dict(&array_item, key_str, key_uint16);
+ } else
+ switch (dbus_message_iter_get_arg_type(&value)) {
+ case DBUS_TYPE_ARRAY:
+ iterate_array(&value);
+ break;
+ case DBUS_TYPE_BOOLEAN:
+ dbus_message_iter_get_basic(&value, &key_bool);
+ printf("%s", key_bool == TRUE ? "True" : "False");
+ break;
+ case DBUS_TYPE_BYTE:
+ dbus_message_iter_get_basic(&value, &key_uint16);
+ printf("%d", key_uint16);
+ break;
+ case DBUS_TYPE_STRING:
+ dbus_message_iter_get_basic(&value, &key_str);
+ printf("%s", key_str);
+ break;
+ }
+ dbus_message_iter_next(dict);
+ }
+ printf("\n\n");
+}
+
+static void match_service_name(DBusMessage *message, char *service_name,
+ struct service_data *service)
+{
+ DBusMessageIter iter, array;
+
+ dbus_message_iter_init(message, &iter);
+ dbus_message_iter_recurse(&iter, &array);
+
+ while (dbus_message_iter_get_arg_type(&array) == DBUS_TYPE_STRUCT) {
+ DBusMessageIter entry, dict;
+ char *path;
+
+ dbus_message_iter_recurse(&array, &entry);
+ dbus_message_iter_get_basic(&entry, &path);
+
+ service->path = strip_service_path(path);
+ dbus_message_iter_next(&entry);
+ dbus_message_iter_recurse(&entry, &dict);
+ extract_service_name(&dict, service);
+ if (g_strcmp0(service_name, service->name) == 0) {
+ printf(" Matched %s with %s\n\n", service->name,
+ service->path);
+ break;
+ }
+ dbus_message_iter_next(&array);
+ }
+}
+
+void extract_service_name(DBusMessageIter *dict, struct service_data *service)
+{
+ DBusMessageIter dict_entry, value;
+ const char *key;
+ const char *state;
+
+ while (dbus_message_iter_get_arg_type(dict) == DBUS_TYPE_DICT_ENTRY) {
+ dbus_message_iter_recurse(dict, &dict_entry);
+ dbus_message_iter_get_basic(&dict_entry, &key);
+ if (strcmp(key, "Name") == 0) {
+ dbus_message_iter_next(&dict_entry);
+ dbus_message_iter_recurse(&dict_entry, &value);
+ dbus_message_iter_get_basic(&value, &service->name);
+ }
+ if (strcmp(key, "AutoConnect") == 0) {
+ dbus_message_iter_next(&dict_entry);
+ dbus_message_iter_recurse(&dict_entry, &value);
+ dbus_message_iter_get_basic(&value, &service->autoconn);
+ }
+ if (strcmp(key, "State") == 0) {
+ dbus_message_iter_next(&dict_entry);
+ dbus_message_iter_recurse(&dict_entry, &value);
+ dbus_message_iter_get_basic(&value, &state);
+ if (strcmp(state, "ready") == 0) {
+ service->connected = TRUE;
+ service->online = FALSE;
+ } else if (strcmp(state, "online") == 0) {
+ service->connected = FALSE;
+ service->online = TRUE;
+ } else {
+ service->connected = FALSE;
+ service->online = FALSE;
+ }
+ }
+ if (strcmp(key, "Favorite") == 0) {
+ dbus_message_iter_next(&dict_entry);
+ dbus_message_iter_recurse(&dict_entry, &value);
+ dbus_message_iter_get_basic(&value, &service->favorite);
+ }
+ dbus_message_iter_next(dict);
+ }
+}
+
+/* Show detailed information about a service */
+void extract_services(DBusMessage *message, char *service_name)
+{
+ DBusMessageIter iter, array;
+
+ dbus_message_iter_init(message, &iter);
+ dbus_message_iter_recurse(&iter, &array);
+
+ while (dbus_message_iter_get_arg_type(&array) == DBUS_TYPE_STRUCT) {
+ DBusMessageIter entry, dict;
+ struct service_data service;
+ char *path;
+
+ dbus_message_iter_recurse(&array, &entry);
+ dbus_message_iter_get_basic(&entry, &path);
+
+ service.path = strip_service_path(path);
+ if (g_strcmp0(service.path, service_name) == 0) {
+ printf("[ %s ]\n", service.path);
+ dbus_message_iter_next(&entry);
+ dbus_message_iter_recurse(&entry, &dict);
+ extract_service_properties(&dict, &service);
+ }
+ dbus_message_iter_next(&array);
+ }
+}
+
+/* Support both string names and path names for connecting to services */
+char *strip_service_path(char *service)
+{
+ char *service_name;
+ service_name = strrchr(service, '/');
+ if (service_name == NULL)
+ return service;
+ else
+ return service_name + 1;
+}
+
+/* Show a simple list of service names only */
+void get_services(DBusMessage *message)
+{
+ DBusMessageIter iter, array;
+
+ dbus_message_iter_init(message, &iter);
+ dbus_message_iter_recurse(&iter, &array);
+
+ while (dbus_message_iter_get_arg_type(&array) == DBUS_TYPE_STRUCT) {
+ DBusMessageIter entry, dict;
+ struct service_data service;
+ char *path;
+
+ dbus_message_iter_recurse(&array, &entry);
+ dbus_message_iter_get_basic(&entry, &path);
+
+ service.path = strip_service_path(path);
+ dbus_message_iter_next(&entry);
+ dbus_message_iter_recurse(&entry, &dict);
+ extract_service_name(&dict, &service);
+ printf("%-1s%-1s%-1s %-20s { %s }\n",
+ service.favorite ? "*" : "",
+ service.autoconn ? "A" : "",
+ service.online ? "O" : (service.connected ? "R" : ""),
+ service.name, service.path);
+ dbus_message_iter_next(&array);
+ }
+}
+
+const char *find_service(DBusConnection *connection, DBusMessage *message,
+ char *service_name, struct service_data *service)
+{
+ DBusMessageIter iter, array, entry;
+ char *path;
+
+ service_name = strip_service_path(service_name);
+ match_service_name(message, service_name, service);
+ /* Service name did not match, so check if entry is a path */
+ if (g_strcmp0(service_name, service->name)) {
+ dbus_message_iter_init(message, &iter);
+ dbus_message_iter_recurse(&iter, &array);
+
+ while (dbus_message_iter_get_arg_type(&array) ==
+ DBUS_TYPE_STRUCT) {
+ dbus_message_iter_recurse(&array, &entry);
+ dbus_message_iter_get_basic(&entry, &path);
+
+ service->path = strip_service_path(path);
+ if (g_strcmp0(service->path, service_name) == 0)
+ return service->path;
+ dbus_message_iter_next(&array);
+ }
+ fprintf(stderr, "'%s' is not a valid service name or path.\n",
+ service_name);
+ fprintf(stderr, "Use the 'services' command to find available "
+ "services.\n");
+ return NULL;
+ } else
+ return service->path;
+}
+
+int set_proxy_manual(DBusConnection *connection, DBusMessage *message,
+ char *name, char **servers, char **excludes,
+ int num_servers, int num_excludes)
+{
+ DBusMessage *message_send;
+ DBusMessageIter iter, value, dict, entry, data;
+ struct service_data service;
+ char *path;
+ const char *path_name;
+ char *property = "Proxy.Configuration";
+ char *method = "Method";
+ char *manual = "manual";
+
+ path_name = find_service(connection, message, name, &service);
+ if (path_name == NULL)
+ return -ENXIO;
+
+ path = g_strdup_printf("/net/connman/service/%s", path_name);
+ message_send = dbus_message_new_method_call("net.connman", path,
+ "net.connman.Service",
+ "SetProperty");
+
+ if (message_send == NULL) {
+ g_free(path);
+ return -ENOMEM;
+ }
+
+ dbus_message_iter_init_append(message_send, &iter);
+ dbus_message_iter_append_basic(&iter, DBUS_TYPE_STRING, &property);
+ dbus_dict_open_variant(&iter, &value);
+ dbus_dict_open(&value, &dict);
+ dbus_message_iter_open_container(&dict, DBUS_TYPE_DICT_ENTRY, NULL,
+ &entry);
+ dbus_message_iter_append_basic(&entry, DBUS_TYPE_STRING, &method);
+ dbus_message_iter_open_container(&entry, DBUS_TYPE_VARIANT,
+ DBUS_TYPE_STRING_AS_STRING,
+ &data);
+ dbus_message_iter_append_basic(&data, DBUS_TYPE_STRING, &manual);
+ dbus_message_iter_close_container(&entry, &data);
+ dbus_message_iter_close_container(&dict, &entry);
+ dbus_message_iter_open_container(&dict, DBUS_TYPE_DICT_ENTRY, NULL,
+ &entry);
+ append_property_array(&entry, "Servers", servers, num_servers);
+ dbus_message_iter_close_container(&dict, &entry);
+
+ if (num_excludes != 0) {
+ dbus_message_iter_open_container(&dict, DBUS_TYPE_DICT_ENTRY,
+ NULL, &entry);
+ append_property_array(&entry, "Excludes", excludes,
+ num_excludes);
+ dbus_message_iter_close_container(&dict, &entry);
+ }
+
+ dbus_message_iter_close_container(&value, &dict);
+ dbus_message_iter_close_container(&iter, &value);
+ dbus_connection_send(connection, message_send, NULL);
+ dbus_connection_flush(connection);
+ dbus_message_unref(message_send);
+
+ g_free(path);
+
+ return 0;
+}
+
+int set_service_property(DBusConnection *connection, DBusMessage *message,
+ char *name, char *property, char **keys,
+ void *data, int num_args)
+{
+ DBusMessage *message_send;
+ DBusMessageIter iter;
+ struct service_data service;
+ char *path;
+ const char *path_name;
+
+ path_name = find_service(connection, message, name, &service);
+ if (path_name == NULL)
+ return -ENXIO;
+
+ path = g_strdup_printf("/net/connman/service/%s", path_name);
+ message_send = dbus_message_new_method_call("net.connman", path,
+ "net.connman.Service",
+ "SetProperty");
+
+ if (message_send == NULL) {
+ g_free(path);
+ return -ENOMEM;
+ }
+
+ dbus_message_iter_init_append(message_send, &iter);
+
+ if (strcmp(property, "AutoConnect") == 0)
+ dbus_property_append_basic(&iter, (const char *) property,
+ DBUS_TYPE_BOOLEAN, data);
+ else if ((strcmp(property, "Domains.Configuration") == 0)
+ || (strcmp(property, "Timeservers.Configuration") == 0)
+ || (strcmp(property, "Nameservers.Configuration") == 0))
+ append_property_array(&iter, property, data, num_args);
+ else if ((strcmp(property, "IPv4.Configuration") == 0)
+ || (strcmp(property, "IPv6.Configuration") == 0)
+ || (strcmp(property, "Proxy.Configuration") == 0))
+ append_property_dict(&iter, property, keys, data, num_args);
+
+ dbus_connection_send(connection, message_send, NULL);
+ dbus_connection_flush(connection);
+ dbus_message_unref(message_send);
+ g_free(path);
+
+ return 0;
+}
+
+int remove_service(DBusConnection *connection, DBusMessage *message,
+ char *name)
+{
+ struct service_data service;
+ DBusMessage *message_send;
+ const char *path_name;
+ char *path;
+
+ path_name = find_service(connection, message, name, &service);
+ if (path_name == NULL)
+ return -ENXIO;
+
+ if (service.favorite == FALSE)
+ return 0;
+
+ path = g_strdup_printf("/net/connman/service/%s", path_name);
+ message_send = dbus_message_new_method_call(CONNMAN_SERVICE, path,
+ CONNMAN_SERVICE_INTERFACE,
+ "Remove");
+ if (message_send == NULL) {
+ g_free(path);
+ return -ENOMEM;
+ }
+
+ dbus_connection_send(connection, message_send, NULL);
+ dbus_message_unref(message_send);
+ g_free(path);
+
+ return 0;
+}
diff --git a/client/services.h b/client/services.h
new file mode 100644
index 0000000..eccc60a
--- /dev/null
+++ b/client/services.h
@@ -0,0 +1,57 @@
+/*
+ *
+ * Connection Manager
+ *
+ * Copyright (C) 2012 Intel Corporation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ */
+
+#include <stdint.h>
+
+#include <dbus/dbus.h>
+
+struct service_data {
+ const char *path;
+ const char *name;
+ dbus_bool_t autoconn;
+ dbus_bool_t favorite;
+ dbus_bool_t connected;
+ dbus_bool_t online;
+};
+
+char *strip_service_path(char *service);
+void extract_service_name(DBusMessageIter *dict, struct service_data *service);
+int set_service_property(DBusConnection *connection, DBusMessage *message,
+ char *name, char *property, char **keys,
+ void *data, int num_args);
+int remove_service(DBusConnection *connection, DBusMessage *message,
+ char *name);
+int set_proxy_manual(DBusConnection *connection, DBusMessage *message,
+ char *name, char **servers, char **excludes,
+ int num_servers, int num_excludes);
+
+const char *find_service(DBusConnection *connection, DBusMessage *message,
+ char *service_name, struct service_data *service);
+void extract_services(DBusMessage *message, char *service_name);
+void get_services(DBusMessage *message);
+void iterate_dict(DBusMessageIter *dict, char *string, uint16_t key_int);
+int list_services(DBusConnection *connection, char *function);
+int list_services_properties(DBusConnection *connection, char *function,
+ char *service_name);
+int listen_for_service_signal(DBusConnection *connection, char *signal_name,
+ char *service_name);
+void iterate_array(DBusMessageIter *iter);
diff --git a/client/technology.c b/client/technology.c
new file mode 100644
index 0000000..1832a1d
--- /dev/null
+++ b/client/technology.c
@@ -0,0 +1,189 @@
+/*
+ *
+ * Connection Manager
+ *
+ * Copyright (C) 2012 Intel Corporation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdint.h>
+#include <string.h>
+#include <errno.h>
+
+#include <glib.h>
+
+#include "technology.h"
+#include "dbus.h"
+
+void extract_properties(DBusMessageIter *dict)
+{
+ while (dbus_message_iter_get_arg_type(dict) == DBUS_TYPE_DICT_ENTRY) {
+ DBusMessageIter entry, value;
+ const char *key, *sdata;
+ dbus_bool_t bdata;
+
+ dbus_message_iter_recurse(dict, &entry);
+ dbus_message_iter_get_basic(&entry, &key);
+ printf(" [%s] = ", key);
+
+ dbus_message_iter_next(&entry);
+
+ dbus_message_iter_recurse(&entry, &value);
+
+ if (dbus_message_iter_get_arg_type(&value) ==
+ DBUS_TYPE_BOOLEAN) {
+ dbus_message_iter_get_basic(&value, &bdata);
+ printf("%s\n", bdata ? "True" : "False");
+ } else if (dbus_message_iter_get_arg_type(&value) ==
+ DBUS_TYPE_STRING) {
+ dbus_message_iter_get_basic(&value, &sdata);
+ printf("%s\n", sdata);
+ }
+ dbus_message_iter_next(dict);
+ }
+}
+
+void match_tech_name(DBusMessage *message, char *tech_name,
+ struct tech_data *tech)
+{
+ DBusMessageIter iter, array;
+
+ dbus_message_iter_init(message, &iter);
+ dbus_message_iter_recurse(&iter, &array);
+ while (dbus_message_iter_get_arg_type(&array) == DBUS_TYPE_STRUCT) {
+ DBusMessageIter entry;
+ const char *path;
+ const char *name;
+
+ dbus_message_iter_recurse(&array, &entry);
+ dbus_message_iter_get_basic(&entry, &path);
+ tech->path = g_strdup(path);
+ name = strrchr(path, '/') + 1;
+ tech->name = g_strdup(name);
+ if (g_strcmp0(tech_name, tech->name) == 0) {
+ printf(" %-20s { %s } exists\n", tech->name,
+ tech->path);
+ break;
+ } else
+ dbus_message_iter_next(&array);
+ }
+
+}
+
+void extract_tech(DBusMessage *message)
+{
+ DBusMessageIter iter, array;
+
+ dbus_message_iter_init(message, &iter);
+ dbus_message_iter_recurse(&iter, &array);
+ while (dbus_message_iter_get_arg_type(&array) == DBUS_TYPE_STRUCT) {
+ DBusMessageIter entry, dict;
+
+ const char *path;
+
+ dbus_message_iter_recurse(&array, &entry);
+ dbus_message_iter_get_basic(&entry, &path);
+
+ printf("{ %s }\n", path);
+
+ dbus_message_iter_next(&entry);
+
+ dbus_message_iter_recurse(&entry, &dict);
+ extract_properties(&dict);
+
+ dbus_message_iter_next(&array);
+ }
+}
+
+int scan_technology(DBusConnection *connection, DBusMessage *message,
+ char *tech)
+{
+ DBusMessage *message_send;
+ struct tech_data technology;
+ DBusError err;
+
+ match_tech_name(message, tech, &technology);
+ if (g_strcmp0(tech, technology.name) != 0) {
+ fprintf(stderr, "%s does not exist on the system\n", tech);
+ fprintf(stderr, "Use the 'tech' command to find available "
+ "technologies on your system.\n");
+ return -ENXIO;
+ }
+
+ message_send = dbus_message_new_method_call("net.connman",
+ technology.path,
+ "net.connman.Technology",
+ "Scan");
+ if (message_send == NULL)
+ return -ENOMEM;
+
+ dbus_error_init(&err);
+ dbus_connection_send_with_reply_and_block(connection, message_send, -1,
+ &err);
+
+ if (dbus_error_is_set(&err)) {
+ printf("Scan failed; error: '%s'\n", err.message);
+ return -EINVAL;
+ }
+
+ dbus_message_unref(message_send);
+ printf("Scanned for new services on %s.\n", technology.name);
+ g_free(technology.name);
+ g_free(technology.path);
+
+ return 0;
+}
+
+int set_technology(DBusConnection *connection, DBusMessage *message, char *key,
+ char *tech, dbus_bool_t value)
+{
+ DBusMessage *message_send;
+ DBusMessageIter iter;
+ struct tech_data technology;
+
+ match_tech_name(message, tech, &technology);
+ if (g_strcmp0(tech, technology.name) != 0) {
+ fprintf(stderr, "%s does not exist on the system\n", tech);
+ fprintf(stderr, "Use the 'tech' command to find available "
+ "technologies on your system.\n");
+ return -ENXIO;
+ }
+
+ message_send = dbus_message_new_method_call("net.connman",
+ technology.path,
+ "net.connman.Technology",
+ "SetProperty");
+ if (message_send == NULL)
+ return -ENOMEM;
+
+ dbus_message_iter_init_append(message_send, &iter);
+ dbus_property_append_basic(&iter, (const char *) key,
+ DBUS_TYPE_BOOLEAN, &value);
+ dbus_connection_send(connection, message_send, NULL);
+ dbus_connection_flush(connection);
+ dbus_message_unref(message_send);
+ g_free(technology.name);
+ g_free(technology.path);
+
+ return 0;
+}
diff --git a/client/technology.h b/client/technology.h
new file mode 100644
index 0000000..75d1a84
--- /dev/null
+++ b/client/technology.h
@@ -0,0 +1,40 @@
+/*
+ *
+ * Connection Manager
+ *
+ * Copyright (C) 2012 Intel Corporation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ */
+
+#include <dbus/dbus.h>
+
+struct tech_data {
+ char *path;
+ char *name;
+ dbus_bool_t powered;
+ dbus_bool_t connected;
+};
+
+void extract_properties(DBusMessageIter *dict);
+void match_tech_name(DBusMessage *message, char *tech_name,
+ struct tech_data *tech);
+void extract_tech(DBusMessage *message);
+int list_tech(DBusConnection *connection, char *function);
+int set_technology(DBusConnection *connection, DBusMessage *message, char *key,
+ char *tech, dbus_bool_t value);
+int scan_technology(DBusConnection *connection, DBusMessage *message,
+ char *tech);
diff --git a/configure.ac b/configure.ac
new file mode 100644
index 0000000..442b8a6
--- /dev/null
+++ b/configure.ac
@@ -0,0 +1,390 @@
+AC_PREREQ(2.60)
+AC_INIT(connman, 1.11)
+
+AM_INIT_AUTOMAKE([foreign subdir-objects color-tests])
+AC_CONFIG_HEADERS([config.h])
+
+m4_ifdef([AM_SILENT_RULES], [AM_SILENT_RULES([yes])])
+
+AM_MAINTAINER_MODE
+
+AC_PREFIX_DEFAULT(/usr/local)
+
+PKG_PROG_PKG_CONFIG
+
+COMPILER_FLAGS
+
+AC_SUBST(abs_top_srcdir)
+AC_SUBST(abs_top_builddir)
+
+AC_LANG_C
+
+AC_PROG_CC
+AM_PROG_CC_C_O
+AC_PROG_CC_PIE
+AC_PROG_INSTALL
+AC_PROG_MKDIR_P
+
+m4_define([_LT_AC_TAGCONFIG], [])
+m4_ifdef([AC_LIBTOOL_TAGS], [AC_LIBTOOL_TAGS([])])
+
+AC_DISABLE_STATIC
+AC_PROG_LIBTOOL
+
+AC_ARG_ENABLE(optimization, AC_HELP_STRING([--disable-optimization],
+ [disable code optimization through compiler]), [
+ if (test "${enableval}" = "no"); then
+ CFLAGS="$CFLAGS -O0 -U_FORTIFY_SOURCE"
+ fi
+])
+
+GTK_DOC_CHECK
+
+AC_ARG_ENABLE(debug, AC_HELP_STRING([--enable-debug],
+ [enable compiling with debugging information]), [
+ if (test "${enableval}" = "yes" &&
+ test "${ac_cv_prog_cc_g}" = "yes"); then
+ CFLAGS="$CFLAGS -g"
+ AC_DEFINE(CONNMAN_DEBUG, 1, [Enable connman debugging code?])
+ fi
+])
+
+AC_ARG_ENABLE(pie, AC_HELP_STRING([--enable-pie],
+ [enable position independent executables flag]), [
+ if (test "${enableval}" = "yes" &&
+ test "${ac_cv_prog_cc_pie}" = "yes"); then
+ CFLAGS="$CFLAGS -fPIE"
+ LDFLAGS="$LDFLAGS -pie"
+ fi
+])
+
+AC_ARG_ENABLE(threads,
+ AC_HELP_STRING([--enable-threads], [enable threading support]),
+ [enable_threads=${enableval}], [enable_threads="no"])
+
+AC_ARG_ENABLE(hh2serial-gps,
+ AC_HELP_STRING([--enable-hh2serial-gps], [enable hh2serial GPS support]),
+ [enable_hh2serial_gps=${enableval}], [enable_hh2serial_gps="no"])
+AM_CONDITIONAL(HH2SERIAL_GPS, test "${enable_hh2serial_gps}" != "no")
+AM_CONDITIONAL(HH2SERIAL_GPS_BUILTIN, test "${enable_hh2serial_gps}" = "builtin")
+
+AC_ARG_WITH(openconnect, AC_HELP_STRING([--with-openconnect=PROGRAM],
+ [specify location of openconnect binary]), [path_openconnect=${withval}])
+
+AC_ARG_ENABLE(openconnect,
+ AC_HELP_STRING([--enable-openconnect], [enable openconnect support]),
+ [enable_openconnect=${enableval}], [enable_openconnect="no"])
+if (test "${enable_openconnect}" != "no"); then
+ if (test -z "${path_openconnect}"); then
+ AC_PATH_PROG(OPENCONNECT, [openconnect], [], $PATH:/sbin:/usr/sbin)
+ if (test -z "${OPENCONNECT}"); then
+ AC_MSG_ERROR(openconnect binary not found)
+ fi
+ else
+ OPENCONNECT="${path_openconnect}"
+ AC_SUBST(OPENCONNECT)
+ fi
+fi
+AM_CONDITIONAL(OPENCONNECT, test "${enable_openconnect}" != "no")
+AM_CONDITIONAL(OPENCONNECT_BUILTIN, test "${enable_openconnect}" = "builtin")
+
+AC_ARG_WITH(openvpn, AC_HELP_STRING([--with-openvpn=PROGRAM],
+ [specify location of openvpn binary]), [path_openvpn=${withval}])
+
+AC_ARG_ENABLE(openvpn,
+ AC_HELP_STRING([--enable-openvpn], [enable openvpn support]),
+ [enable_openvpn=${enableval}], [enable_openvpn="no"])
+if (test "${enable_openvpn}" != "no"); then
+ if (test -z "${path_openvpn}"); then
+ AC_PATH_PROG(OPENVPN, [openvpn], [], $PATH:/sbin:/usr/sbin)
+ if (test -z "${OPENVPN}"); then
+ AC_MSG_ERROR(openvpn binary not found)
+ fi
+ else
+ OPENVPN="${path_openvpn}"
+ AC_SUBST(OPENVPN)
+ fi
+fi
+AM_CONDITIONAL(OPENVPN, test "${enable_openvpn}" != "no")
+AM_CONDITIONAL(OPENVPN_BUILTIN, test "${enable_openvpn}" = "builtin")
+
+AC_ARG_WITH(vpnc, AC_HELP_STRING([--with-vpnc=PROGRAM],
+ [specify location of vpnc binary]), [path_vpnc=${withval}])
+
+AC_ARG_ENABLE(vpnc,
+ AC_HELP_STRING([--enable-vpnc], [enable vpnc support]),
+ [enable_vpnc=${enableval}], [enable_vpnc="no"])
+if (test "${enable_vpnc}" != "no"); then
+ if (test -z "${path_vpnc}"); then
+ AC_PATH_PROG(VPNC, [vpnc], [], $PATH:/sbin:/usr/sbin)
+ if (test -z "${VPNC}"); then
+ AC_MSG_ERROR(vpnc binary not found)
+ fi
+ else
+ VPNC="${path_vpnc}"
+ AC_SUBST(VPNC)
+ fi
+fi
+AM_CONDITIONAL(VPNC, test "${enable_vpnc}" != "no")
+AM_CONDITIONAL(VPNC_BUILTIN, test "${enable_vpnc}" = "builtin")
+
+AC_ARG_ENABLE(l2tp,
+ AC_HELP_STRING([--enable-l2tp], [enable l2tp support]),
+ [enable_l2tp=${enableval}], [enable_l2tp="no"])
+if (test "${enable_l2tp}" != "no"); then
+ if (test -z "${path_pppd}"); then
+ AC_PATH_PROG(PPPD, [pppd], [/usr/sbin/pppd], $PATH:/sbin:/usr/sbin)
+ else
+ PPPD="${path_pppd}"
+ AC_SUBST(PPPD)
+ fi
+ AC_CHECK_HEADERS(pppd/pppd.h, dummy=yes,
+ AC_MSG_ERROR(ppp header files are required))
+ if (test -z "${path_l2tp}"); then
+ AC_PATH_PROG(L2TP, [xl2tpd], [/usr/sbin/xl2tpd], $PATH:/sbin:/usr/sbin)
+ else
+ L2TP="${path_l2tp}"
+ AC_SUBST(L2TP)
+ fi
+fi
+AM_CONDITIONAL(L2TP, test "${enable_l2tp}" != "no")
+AM_CONDITIONAL(L2TP_BUILTIN, test "${enable_l2tp}" = "builtin")
+
+AC_ARG_ENABLE(pptp,
+ AC_HELP_STRING([--enable-pptp], [enable pptp support]),
+ [enable_pptp=${enableval}], [enable_pptp="no"])
+if (test "${enable_pptp}" != "no"); then
+ if (test -z "${path_pppd}"); then
+ AC_PATH_PROG(PPPD, [pppd], [/usr/sbin/pppd], $PATH:/sbin:/usr/sbin)
+ else
+ PPPD="${path_pppd}"
+ AC_SUBST(PPPD)
+ fi
+ AC_CHECK_HEADERS(pppd/pppd.h, dummy=yes,
+ AC_MSG_ERROR(ppp header files are required))
+ if (test -z "${path_pptp}"); then
+ AC_PATH_PROG(PPTP, [pptp], [/usr/sbin/pptp], $PATH:/sbin:/usr/sbin)
+ else
+ PPTP="${path_pptp}"
+ AC_SUBST(PPTP)
+ fi
+fi
+AM_CONDITIONAL(PPTP, test "${enable_pptp}" != "no")
+AM_CONDITIONAL(PPTP_BUILTIN, test "${enable_pptp}" = "builtin")
+
+AC_CHECK_HEADERS(resolv.h, dummy=yes,
+ AC_MSG_ERROR(resolver header files are required))
+AC_CHECK_LIB(resolv, ns_initparse, dummy=yes, [
+ AC_CHECK_LIB(resolv, __ns_initparse, dummy=yes,
+ AC_MSG_ERROR(resolver library support is required))
+])
+
+AC_CHECK_FUNC(signalfd, dummy=yes,
+ AC_MSG_ERROR(signalfd support is required))
+
+AC_CHECK_LIB(dl, dlopen, dummy=yes,
+ AC_MSG_ERROR(dynamic linking loader is required))
+
+AC_ARG_ENABLE(iospm, AC_HELP_STRING([--enable-iospm],
+ [enable Intel OSPM support]), [enable_iospm=${enableval}])
+AM_CONDITIONAL(IOSPM, test "${enable_iospm}" = "yes")
+
+AC_ARG_ENABLE(tist,
+ AC_HELP_STRING([--enable-tist], [enable TI Shared Transport support]),
+ [enable_tist=${enableval}], [enable_tist="no"])
+AM_CONDITIONAL(TIST, test "${enable_tist}" != "no")
+AM_CONDITIONAL(TIST_BUILTIN, test "${enable_tist}" = "builtin")
+
+AC_ARG_ENABLE(session-policy-local,
+ AC_HELP_STRING([--enable-session-policy-local], [enable local file Session policy configuration support]),
+ [enable_session_policy_local=${enableval}], [enable_session_policy_local="no"])
+AM_CONDITIONAL(SESSION_POLICY_LOCAL, test "${enable_session_policy_local}" != "no")
+AM_CONDITIONAL(SESSION_POLICY_LOCAL_BUILTIN, test "${enable_session_policy_local}" = "builtin")
+
+AC_ARG_WITH(stats-max-file-size, AC_HELP_STRING([--with-stats-max-file-size=SIZE],
+ [Maximal size of a statistics round robin file]),
+ [stats_max_file_size=${withval}])
+
+if (test -z "${stats_max_file_size}"); then
+ # default size is 16 kByte
+ stats_max_file_size="16 * 8 * 128"
+fi
+
+AC_DEFINE_UNQUOTED([STATS_MAX_FILE_SIZE], (${stats_max_file_size}), [Maximal size of a statistics round robin file])
+
+PKG_CHECK_MODULES(GLIB, glib-2.0 >= 2.28, dummy=yes,
+ AC_MSG_ERROR(GLib >= 2.28 is required))
+AC_SUBST(GLIB_CFLAGS)
+AC_SUBST(GLIB_LIBS)
+
+if (test "${enable_threads}" = "yes"); then
+ AC_DEFINE(NEED_THREADS, 1, [Define if threading support is required])
+ PKG_CHECK_MODULES(GTHREAD, gthread-2.0 >= 2.16, dummy=yes,
+ AC_MSG_ERROR(GThread >= 2.16 is required))
+ GLIB_CFLAGS="$GLIB_CFLAGS $GTHREAD_CFLAGS"
+ GLIB_LIBS="$GLIB_LIBS $GTHREAD_LIBS"
+fi
+
+PKG_CHECK_MODULES(DBUS, dbus-1 >= 1.4, dummy=yes,
+ AC_MSG_ERROR(D-Bus >= 1.4 is required))
+AC_SUBST(DBUS_CFLAGS)
+AC_SUBST(DBUS_LIBS)
+
+AC_ARG_WITH(dbusconfdir, AC_HELP_STRING([--with-dbusconfdir=PATH],
+ [path to D-Bus config directory]), [path_dbusconf=${withval}],
+ [path_dbusconf="`$PKG_CONFIG --variable=sysconfdir dbus-1`"])
+if (test -z "${path_dbusconf}"); then
+ DBUS_CONFDIR="${sysconfdir}/dbus-1/system.d"
+else
+ DBUS_CONFDIR="${path_dbusconf}/dbus-1/system.d"
+fi
+AC_SUBST(DBUS_CONFDIR)
+
+AC_ARG_WITH(dbusdatadir, AC_HELP_STRING([--with-dbusdatadir=PATH],
+ [path to D-Bus data directory]), [path_dbusdata=${withval}],
+ [path_dbusdata="`$PKG_CONFIG --variable=datadir dbus-1`"])
+if (test -z "${path_dbusdata}"); then
+ DBUS_DATADIR="${datadir}/dbus-1/system-services"
+else
+ DBUS_DATADIR="${path_dbusdata}/dbus-1/system-services"
+fi
+AC_SUBST(DBUS_DATADIR)
+
+AC_ARG_WITH([systemdunitdir], AC_HELP_STRING([--with-systemdunitdir=DIR],
+ [path to systemd service directory]), [path_systemdunit=${withval}],
+ [path_systemdunit="`$PKG_CONFIG --variable=systemdsystemunitdir systemd`"])
+if (test -n "${path_systemdunit}"); then
+ SYSTEMD_UNITDIR="${path_systemdunit}"
+ AC_SUBST(SYSTEMD_UNITDIR)
+fi
+AM_CONDITIONAL(SYSTEMD, test -n "${path_systemdunit}")
+
+PKG_CHECK_MODULES(XTABLES, xtables, dummy=yes,
+ AC_MSG_ERROR(Xtables library is required))
+AC_SUBST(XTABLES_CFLAGS)
+AC_SUBST(XTABLES_LIBS)
+
+AC_ARG_ENABLE(test, AC_HELP_STRING([--enable-test],
+ [enable test/example scripts]), [enable_test=${enableval}])
+AM_CONDITIONAL(TEST, test "${enable_test}" = "yes")
+
+AC_ARG_ENABLE(nmcompat, AC_HELP_STRING([--enable-nmcompat],
+ [enable Network Manager support]),
+ [enable_nmcompat=${enableval}], [enable_nmcompat="no"])
+AM_CONDITIONAL(NMCOMPAT, test "${enable_nmcompat}" != "no")
+
+AC_ARG_ENABLE(polkit, AC_HELP_STRING([--enable-polkit],
+ [enable PolicyKit support]),
+ [enable_polkit=${enableval}], [enable_polkit="no"])
+if (test "${enable_polkit}" != "no"); then
+ POLKIT_DATADIR="`$PKG_CONFIG --variable=actiondir polkit`"
+ POLKIT_DATADIR=""
+ if (test -z "${POLKIT_DATADIR}"); then
+ POLKIT_DATADIR="${datadir}/polkit-1/actions"
+ fi
+ AC_SUBST(POLKIT_DATADIR)
+fi
+AM_CONDITIONAL(POLKIT, test "${enable_polkit}" != "no")
+
+AC_ARG_ENABLE(selinux, AC_HELP_STRING([--enable-selinux],
+ [enable selinux support]),
+ [enable_selinux=${enableval}], [enable_selinux="no"])
+AM_CONDITIONAL(SELINUX, test "${enable_selinux}" != "no")
+
+AC_ARG_ENABLE(loopback, AC_HELP_STRING([--disable-loopback],
+ [disable loopback support]),
+ [enable_loopback=${enableval}])
+AM_CONDITIONAL(LOOPBACK, test "${enable_loopback}" != "no")
+
+AC_ARG_ENABLE(sleep, AC_HELP_STRING([--enable-sleep],
+ [enable system sleep]),
+ [enable_sleep=${enableval}])
+AM_CONDITIONAL(SLEEP, test "${enable_sleep}" != "no")
+
+AC_ARG_ENABLE(ethernet, AC_HELP_STRING([--disable-ethernet],
+ [disable Ethernet support]),
+ [enable_ethernet=${enableval}])
+AM_CONDITIONAL(ETHERNET, test "${enable_ethernet}" != "no")
+
+AC_ARG_ENABLE(wifi, AC_HELP_STRING([--disable-wifi],
+ [disable WiFi support]),
+ [enable_wifi=${enableval}])
+AM_CONDITIONAL(WIFI, test "${enable_wifi}" != "no")
+
+AC_ARG_ENABLE(bluetooth, AC_HELP_STRING([--disable-bluetooth],
+ [disable Bluetooth support]),
+ [enable_bluetooth=${enableval}])
+AM_CONDITIONAL(BLUETOOTH, test "${enable_bluetooth}" != "no")
+
+AC_ARG_ENABLE(ofono, AC_HELP_STRING([--disable-ofono],
+ [disable oFono support]),
+ [enable_ofono=${enableval}])
+AM_CONDITIONAL(OFONO, test "${enable_ofono}" != "no")
+
+AC_ARG_ENABLE(dundee, AC_HELP_STRING([--disable-dundee],
+ [disable dundee support (Bluetooth DUN)]),
+ [enable_dundee=${enableval}])
+AM_CONDITIONAL(DUNDEE, test "${enable_dundee}" != "no")
+
+AC_ARG_ENABLE(pacrunner, AC_HELP_STRING([--disable-pacrunner],
+ [disable PACrunner support]),
+ [enable_pacrunner=${enableval}])
+AM_CONDITIONAL(PACRUNNER, test "${enable_pacrunner}" != "no")
+
+AC_ARG_ENABLE(wispr, AC_HELP_STRING([--disable-wispr],
+ [disable WISPr support]),
+ [enable_wispr=${enableval}])
+AM_CONDITIONAL(WISPR, test "${enable_wispr}" != "no")
+
+AC_ARG_ENABLE(tools, AC_HELP_STRING([--disable-tools],
+ [disable testing tools]),
+ [enable_tools=${enableval}])
+AM_CONDITIONAL(TOOLS, test "${enable_tools}" != "no")
+
+AC_ARG_ENABLE(client, AC_HELP_STRING([--disable-client],
+ [disable command line client]),
+ [enable_client=${enableval}])
+AM_CONDITIONAL(CLIENT, test "${enable_client}" != "no")
+
+if (test "${enable_wispr}" != "no"); then
+ PKG_CHECK_MODULES(GNUTLS, gnutls, dummy=yes,
+ AC_MSG_ERROR(GnuTLS library is required))
+else
+ GNUTLS_CFLAGS=""
+ GNUTLS_LIBS=""
+fi
+AC_SUBST(GNUTLS_CFLAGS)
+AC_SUBST(GNUTLS_LIBS)
+
+if (test "${enable_loopback}" != "no"); then
+ AC_CHECK_HEADERS(sys/inotify.h, dummy=yes,
+ AC_MSG_ERROR(inotify header files are required))
+
+ AC_CHECK_LIB(c, inotify_init, dummy=yes,
+ AC_MSG_ERROR(inotify library support is required))
+fi
+
+if (test "${enable_wifi}" != "no"); then
+ AC_PATH_PROG(WPASUPPLICANT, [wpa_supplicant], [],
+ $PATH:/sbin:/usr/sbin)
+fi
+
+AC_ARG_ENABLE(datafiles, AC_HELP_STRING([--disable-datafiles],
+ [don't install configuration and data files]),
+ [enable_datafiles=${enableval}])
+AM_CONDITIONAL(DATAFILES, test "${enable_datafiles}" != "no")
+
+if (test "${enable_client}" != "no"); then
+ AC_CHECK_HEADERS(readline/readline.h, dummy=yes,
+ AC_MSG_ERROR(readline header files are required))
+fi
+
+AM_CONDITIONAL(VPN, test "${enable_openconnect}" != "no" -o \
+ "${enable_openvpn}" != "no" -o \
+ "${enable_vpnc}" != "no" -o \
+ "${enable_l2tp}" != "no" -o \
+ "${enable_pptp}" != "no")
+
+AC_OUTPUT(Makefile include/version.h src/connman.service
+ vpn/connman-vpn.service vpn/net.connman.vpn.service
+ scripts/connman doc/version.xml connman.pc)
diff --git a/connman.pc.in b/connman.pc.in
new file mode 100644
index 0000000..2af3c2b
--- /dev/null
+++ b/connman.pc.in
@@ -0,0 +1,14 @@
+prefix=@prefix@
+exec_prefix=@exec_prefix@
+libdir=@libdir@
+includedir=@includedir@
+
+plugindir=${libdir}/connman/plugins
+scriptdir=${libdir}/connman/scripts
+
+Name: connman
+Description: Connection Manager
+Requires: glib-2.0 dbus-1
+Version: @VERSION@
+Libs: -module -avoid-version -export-symbols-regex connman_plugin_desc
+Cflags: -I${includedir}
diff --git a/doc/Makefile.am b/doc/Makefile.am
new file mode 100644
index 0000000..ce3e433
--- /dev/null
+++ b/doc/Makefile.am
@@ -0,0 +1,38 @@
+
+DOC_MODULE = connman
+
+DOC_MAIN_SGML_FILE = $(DOC_MODULE)-docs.xml
+
+DOC_SOURCE_DIR = ../src
+
+SCAN_OPTIONS = --rebuild-sections --source-dir=../include
+
+MKDB_OPTIONS = --sgml-mode --output-format=xml --tmpl-dir=. \
+ --ignore-files=connman \
+ --source-dir=../include \
+ --source-suffixes=c,h
+
+MKTMPL_OPTIONS = --output-dir=.
+
+HFILE_GLOB = $(top_srcdir)/include/*.h
+CFILE_GLOB = $(top_srcdir)/src/*.c $(top_srcdir)/src/*.h
+
+IGNORE_HFILES = connman connman.h
+
+HTML_IMAGES =
+
+content_files = connman-introduction.xml
+
+INCLUDES = -I$(top_srcdir)/include -I$(top_srcdir)/gdbus \
+ $(GTHREAD_CFLAGS) $(GMODULE_CFLAGS) $(GLIB_CFLAGS) $(DBUS_CFLAGS)
+
+GTKDOC_LIBS = $(DBUS_LIBS) $(GLIB_LIBS) $(GMODULE_LIBS) $(GTHREAD_LIBS)
+
+MAINTAINERCLEANFILES = Makefile.in \
+ $(DOC_MODULE).types $(DOC_MODULE)-*.txt *.sgml *.bak
+
+if ENABLE_GTK_DOC
+include $(top_srcdir)/doc/gtk-doc.make
+else
+EXTRA_DIST = $(DOC_MAIN_SGML_FILE) connman-introduction.xml
+endif
diff --git a/doc/advanced-configuration.txt b/doc/advanced-configuration.txt
new file mode 100644
index 0000000..3c08c8f
--- /dev/null
+++ b/doc/advanced-configuration.txt
@@ -0,0 +1,61 @@
+Advanced configuration interface
+********************************
+
+
+Configuration basics
+====================
+
+The default configuration method for all servers is automatic or something
+like DHCP. In almost every case that should be just good enough, but if it
+is not, Connection Manager supports manual configuration for Ethernet and
+IP settings.
+
+
+Configuration interface
+=======================
+
+Every service contains two properties. One represents the current active
+configuration and the other one allows manual configuration via the user.
+
+For IPv4 they are named "IPv4" and IPv4.Configuration".
+
+[ /profile/default/wifi_001122334455_42696720696e204a6170616e_managed_psk ]
+ Type = wifi
+ Name = Big in Japan
+ Mode = managed
+ Strength = 82
+ Security = rsn
+ Favorite = true
+ State = ready
+ IPv4.Configuration = { Method=dhcp }
+ IPv4 = { Netmask=255.255.255.0 Method=dhcp Address=192.168.1.198 }
+
+The above WiFi network shows how the default configuration would look like
+with a connected service. The configuration method is DHCP and the current
+IP address is 192.168.1.198.
+
+The "IPv4" property is read-only and will emit PropertyChanged signals in
+case the IP address of this interface changes. The "IPv4.Configuration"
+property is read-write and allows changes. For example to use a static IP
+configuration this call could be used:
+
+ service.SetProperty("IPv4.Configuration", { "Method": "manual",
+ "Address": "192.168.1.100",
+ "Netmask": "255.255.255.0" })
+
+The configuration itself is a dictionary with various fields. Not all of
+them need to present. A lot of combinations are valid.
+
+For example the "Method" field has valid settings of "off", "fixed", "manual"
+and "dhcp". The "fixed" value however can not be set by any user program. It
+is an internal value that some 3G cards require. Switching to "off" will
+remove and IP configuration from the interface. The "manual" method allows
+for static address configuration. And "dhcp" will use DHCP to retrieve all
+required information automatically.
+
+With a manual configuration, the fields "Address" and "Netmask" should be
+given. In case "Netmask" is left out, the best netmask will be calculated.
+
+The "Gateway" field can be used to indicate the default route/gateway for
+this interface.
+
diff --git a/doc/agent-api.txt b/doc/agent-api.txt
new file mode 100644
index 0000000..a98343f
--- /dev/null
+++ b/doc/agent-api.txt
@@ -0,0 +1,256 @@
+Agent hierarchy
+===============
+
+Service unique name
+Interface net.connman.Agent
+Object path freely definable
+
+Methods void Release()
+
+ This method gets called when the service daemon
+ unregisters the agent. An agent can use it to do
+ cleanup tasks. There is no need to unregister the
+ agent, because when this method gets called it has
+ already been unregistered.
+
+ void ReportError(object service, string error)
+
+ This method gets called when an error has to be
+ reported to the user.
+
+ A special return value can be used to trigger a
+ retry of the failed transaction.
+
+ Possible Errors: net.connman.Agent.Error.Retry
+
+ void RequestBrowser(object service, string url)
+
+ This method gets called when it is required
+ to ask the user to open a website to procceed
+ with login handling.
+
+ This can happen if connected to a hotspot portal
+ page without WISPr support.
+
+ Possible Errors: net.connman.Agent.Error.Canceled
+
+ dict RequestInput(object service, dict fields)
+
+ This method gets called when trying to connect to
+ a service and some extra input is required. For
+ example a passphrase or the name of a hidden network.
+
+ The return value should be a dictionary where the
+ keys are the field names and the values are the
+ actual fields. Alternatively an error indicating that
+ the request got canceled can be returned.
+
+ Most common return field names are "Name" and of
+ course "Passphrase".
+
+ The dictionary arguments contains field names with
+ their input parameters.
+
+ In case of WISPr credentials requests and if the user
+ prefers to login through the browser by himself, agent
+ will have to return a LaunchBrowser error (see below).
+
+ Possible Errors: net.connman.Agent.Error.Canceled
+ net.connman.Agent.Error.LaunchBrowser
+
+ void Cancel()
+
+ This method gets called to indicate that the agent
+ request failed before a reply was returned.
+
+Fields string Name
+
+ The name of a network. This field will be requested
+ when trying to connect to a hidden network.
+
+ array{byte} SSID
+
+ This field is an alternative to "Name" for WiFi
+ networks and can be used to return the exact binary
+ representation of a network name.
+
+ Normally returning the "Name" field is the better
+ option here.
+
+ string Identity
+
+ Identity (username) for EAP authentication methods.
+
+ string Passphrase
+
+ The passphrase for authentication. For example a WEP
+ key, a PSK passphrase or a passphrase for EAP
+ authentication methods.
+
+ string PreviousPassphrase
+
+ The previous passphrase successfully saved, i.e.
+ which lead to a successfull connection. This field is
+ provided as an informational argument when connecting
+ with it does not work anymore, for instance when it
+ has been changed on the AP. Such argument appears when
+ a RequestInput is raised after a retry. In case of WPS
+ association through PIN method: when retrying, the
+ previous wpspin will be provided.
+
+ string WPS
+
+ This field requests the use of WPS to get associated.
+ This is an alternate choice against Passphrase when
+ requested service supports WPS. The reply can contain
+ either empty pin, if user wants to use push-button
+ method, or a pin code if user wants to use the pin
+ method.
+
+ string Username
+
+ Username for WISPr authentication. This field will be
+ requested when connecting to a WISPr-enabled hotspot.
+
+ string Password
+
+ Password for WISPr authentication. This field will be
+ requested when connecting to a WISPr-enabled hotspot.
+
+Arguments string Type
+
+ Contains the type of a field. For example "psk", "wep"
+ "passphrase", "response", "ssid", "wpspin" or plain
+ "string".
+
+ string Requirement
+
+ Contains the requirement option. Valid values are
+ "mandatory", "optional", "alternate" or
+ "informational".
+
+ The "alternate" value specifies that this field can be
+ returned as an alternative to another one. An example
+ would be the network name or SSID.
+
+ All "mandatory" fields must be returned, while the
+ "optional" can be returned if available.
+
+ Nothing needs to be returned for "informational", as it
+ is here only to provide an information so a value is
+ attached to it.
+
+ array{string} Alternates
+
+ Contains the list of alternate field names this
+ field can be represented by.
+
+ string Value
+
+ Contains data as a string, relatively to an
+ "informational" argument.
+
+Examples Requesting a passphrase for WPA2 network
+
+ RequestInput("/service1",
+ { "Passphrase" : { "Type" : "psk",
+ "Requirement" : "mandatory"
+ }
+ }
+ ==> { "Passphrase" : "secret123" }
+
+ Requesting a passphrase after an error on the previous one:
+
+ RequestInput("/service1",
+ { "Passphrase" : { "Type" : "psk",
+ "Requirement" : "mandatory"
+ },
+ "PreviousPassphrase" :
+ { "Type" : "psk",
+ "Requirement : "informational",
+ "Value" : "secret123"
+ }
+ }
+
+ Requesting name for hidden network
+
+ RequestInput("/service2",
+ { "Name" : { "Type" : "string",
+ "Requirement" : "mandatory",
+ "Alternates" : [ "SSID" ]
+ },
+ "SSID" : { "Type" : "ssid",
+ "Requirement" : "alternate"
+ }
+ }
+ ==> { "Name" : "My hidden network" }
+
+ Requesting a passphrase for a WPA2 network with WPS alternative:
+
+ RequestInput("/service3",
+ { "Passphrase" : { "Type" : "psk",
+ "Requirement" : "mandatory",
+ "Alternates" : [ "WPS" ]
+ },
+ "WPS" : { "Type" : "wpspin",
+ "Requirement" : "alternate"
+ }
+ }
+
+ ==> { "WPS" : "123456" }
+
+ Requesting a passphrase for a WPA2 network with WPS alternative
+ after an error on the previous one:
+
+ RequestInput("/service3",
+ { "Passphrase" : { "Type" : "psk",
+ "Requirement" : "mandatory",
+ "Alternates" : [ "WPS" ]
+ },
+ "WPS" : { "Type" : "wpspin",
+ "Requirement" : "alternate"
+ }
+ "PreviousPassphrase" :
+ { "Type" : "wpspin",
+ "Requirement : "informational",
+ "Value" : "123456"
+ }
+
+ Requesting passphrase for a WPA-Enterprise network:
+
+ RequestInput("/service4",
+ { "Identity" : { "Type" : "string",
+ "Requirement" : "mandatory"
+ },
+ "Passphrase" : { "Type" : "passphrase",
+ "Requirement" : "mandatory"
+ }
+ }
+
+ ==> { "Identity" : "alice", "Passphrase": "secret123" }
+
+ Requesting challenge response for a WPA-Enterprise network:
+
+ RequestInput("/service4",
+ { "Identity" : { "Type" : "string",
+ "Requirement" : "mandatory"
+ },
+ "Passphrase" : { "Type" : "response",
+ "Requirement" : "mandatory"
+ }
+ }
+
+ ==> { "Identity" : "bob", "Passphrase": "secret123" }
+
+ Requesting username and password for a WISPr-enabled hotspot:
+
+ RequestInput("/service5",
+ { "Username" : { "Type" : "string",
+ "Requirement" : "mandatory"
+ },
+ "Password" : { "Type" : "passphrase",
+ "Requirement" : "mandatory"
+ }
+ }
+
+ ==> { "Username" : "foo", "Password": "secret" }
diff --git a/doc/backtrace.txt b/doc/backtrace.txt
new file mode 100644
index 0000000..ac8472c
--- /dev/null
+++ b/doc/backtrace.txt
@@ -0,0 +1,28 @@
+ConnMan backtraces
+******************
+
+ConnMan dumps backtraces upon segmentation faults, bus errors and other
+crashing signals. Regardless of the debug level you started connmand with, the
+backtrace will be dumped to syslog.
+
+The ConnMan backtraces start with the following line:
+ -------- backtrace --------
+and will try to display function names if those can be resolved from the stack
+addresses. All static functions name will not appear for example.
+
+For a more complete and useful stack frame output you can use the
+test/backtrace script. It takes the actual binary that crashed and the
+connmand logs. The logs can contain any connman debug strings on top of the
+backtrace.
+
+Here is an example of the backtrace script usage:
+
+me@localhost:[~]$ backtrace /sbin/connmand connman.log
+-------- backtrace --------
+[0]: __connman_debug_list_available() [log.c:117]
+[1]: connman_driver_register() [element.c:515]
+[2]: __connman_driver_rescan() [element.c:490]
+[3]: disable_technology() [manager.c:391]
+[4]: generic_message() [object.c:262]
+-----------------------------------
+
diff --git a/doc/behavior-api.txt b/doc/behavior-api.txt
new file mode 100644
index 0000000..5feea76
--- /dev/null
+++ b/doc/behavior-api.txt
@@ -0,0 +1,11 @@
+Interface behavior description
+******************************
+
+
+Ethernet service
+================
+
+The Ethernet based service is a special case since it has no children, but
+still can be manually connected and disconnected while also has an implicit
+behavior when physically plugging in or removing an Ethernet cable.
+
diff --git a/doc/clock-api.txt b/doc/clock-api.txt
new file mode 100644
index 0000000..6818f5a
--- /dev/null
+++ b/doc/clock-api.txt
@@ -0,0 +1,87 @@
+Clock hierarchy
+===============
+
+Service net.connman
+Interface net.connman.Clock
+Object path /
+
+Methods dict GetProperties() [experimental]
+
+ Returns all system clock properties. See the
+ properties section for available properties.
+
+ Possible Errors: [service].Error.InvalidArguments
+
+ void SetProperty(string name, variant value) [experimental]
+
+ Changes the value of the specified property. Only
+ properties that are listed as read-write are
+ changeable. On success a PropertyChanged signal
+ will be emitted.
+
+ Possible Errors: [service].Error.InvalidArguments
+ [service].Error.InvalidProperty
+
+Signals PropertyChanged(string name, variant value) [experimental]
+
+ This signal indicates a changed value of the given
+ property.
+
+
+Properties uint64 Time [readonly or readwrite] [experimental]
+
+ Current system time in seconds since epoch.
+
+ This value is present for changing the system time
+ if TimeUpdates is set to manual.
+
+ It is not present for driving an updated display
+ of the system time. PropertyChanged signal for this
+ value are only send out if it gets changed or jumps
+ unexpectedly.
+
+ In general application interested in the current
+ time should be using gettimeofday() and related
+ system calls.
+
+ string TimeUpdates [readwrite] [experimental]
+
+ Possible values are "manual" and "auto" to indicate
+ time update policy.
+
+ With the "auto" setting the system tries to use as
+ many sources as possible to determine the correct
+ and updated time.
+
+ string Timezone [readonly or readwrite] [experimental]
+
+ Current system timezone string. Allowed values
+ are from the standard timezone data (tzdata)
+ package under /usr/share/zoneinfo. For example
+ strings like "America/Vancouver" or "Europe/Berlin".
+
+ This value is present for changing the timezone
+ if TimezoneUpdates is set to manual.
+
+ When the timezone gets changed a PropertyChanged
+ signal will be send out.
+
+ string TimezoneUpdates [readwrite] [experimental]
+
+ Possible values are "manual" and "auto" to indicate
+ timezone update policy.
+
+ With the "auto" setting the system tries to use as
+ many sources as possible to determine the correct
+ timezone.
+
+ array{string} Timeservers [readwrite] [experimental]
+
+ List of global default NTP servers. The list should
+ be sorted in order of preference.
+
+ If a service configuration provides NTP servers,
+ then they are preferred over the global ones.
+
+ This list of servers is used when TimeUpdates is set
+ to auto.
diff --git a/doc/coding-style.txt b/doc/coding-style.txt
new file mode 100644
index 0000000..30690b7
--- /dev/null
+++ b/doc/coding-style.txt
@@ -0,0 +1,344 @@
+Every project has its coding style, and ConnMan is not an exception. This
+document describes the preferred coding style for ConnMan code, in order to keep
+some level of consistency among developers so that code can be easily
+understood and maintained, and also to help your code survive under
+maintainer's fastidious eyes so that you can get a passport for your patch
+ASAP.
+
+First of all, ConnMan coding style must follow every rule for Linux kernel
+(http://www.kernel.org/doc/Documentation/CodingStyle). There also exists a tool
+named checkpatch.pl to help you check the compliance with it. Just type
+"checkpatch.pl --no-tree patch_name" to check your patch. In theory, you need
+to clean up all the warnings and errors except this one: "ERROR: Missing
+Signed-off-by: line(s)". ConnMan does not used Signed-Off lines, so including
+them is actually an error. In certain circumstances one can ignore the 80
+character per line limit. This is generally only allowed if the alternative
+would make the code even less readable.
+
+Besides the kernel coding style above, ConnMan has special flavors for its own.
+Some of them are mandatory (marked as 'M'), while some others are optional
+(marked as 'O'), but generally preferred.
+
+M1: Blank line before and after an if/while/do/for statement
+============================================================
+There should be a blank line before if statement unless the if is nested and
+not preceded by an expression or variable declaration.
+
+Example:
+1)
+a = 1;
+if (b) { // wrong
+
+2)
+a = 1
+
+if (b) {
+}
+a = 2; // wrong
+
+3)
+if (a) {
+ if (b) // correct
+
+4)
+b = 2;
+
+if (a) { // correct
+
+}
+
+b = 3;
+
+The only exception to this rule applies when a variable is being allocated:
+array = g_try_new0(int, 20);
+if (array == NULL) // Correct
+ return;
+
+
+M2: Multiple line comment
+=========================
+If your comments have more then one line, please start it from the second line.
+
+Example:
+/*
+ * first line comment // correct
+ * ...
+ * last line comment
+ */
+
+
+M3: Space before and after operator
+===================================
+There should be a space before and after each operator.
+
+Example:
+a + b; // correct
+
+
+M4: Wrap long lines
+===================
+If your condition in if, while, for statement or a function declaration is too
+long to fit in one line, the new line needs to be indented not aligned with the
+body.
+
+Example:
+1)
+if (call->status == CALL_STATUS_ACTIVE ||
+ call->status == CALL_STATUS_HELD) { // wrong
+ connman_dbus_dict_append();
+
+2)
+if (call->status == CALL_STATUS_ACTIVE ||
+ call->status == CALL_STATUS_HELD) { // correct
+ connman_dbus_dict_append();
+
+3)
+gboolean sim_ust_is_available(unsigned char *service_ust, unsigned char len,
+ num sim_ust_service index) // wrong
+{
+ int a;
+ ...
+}
+
+4)
+gboolean sim_ust_is_available(unsigned char *service_ust, unsigned char len,
+ enum sim_ust_service index) // correct
+{
+ int a;
+ ...
+}
+
+If the line being wrapped is a function call or function declaration, the
+preferred style is to indent at least past the opening parenthesis. Indenting
+further is acceptable as well (as long as you don't hit the 80 character
+limit).
+
+If this is not possible due to hitting the 80 character limit, then indenting
+as far as possible to the right without hitting the limit is preferred.
+
+Example:
+
+1)
+gboolean sim_ust_is_available(unsigned char *service_ust, unsigned char len,
+ enum sim_ust_service index); // worse
+
+2)
+gboolean sim_ust_is_available(unsigned char *service_ust, unsigned char len,
+ enum sim_ust_service index);
+ // better
+
+M5: Git commit message 50/72 formatting
+=======================================
+The commit message header should be within 50 characters. And if you have
+detailed explanatory text, wrap it to 72 character.
+
+
+M6: Space when doing type casting
+=================================
+There should be a space between new type and variable.
+
+Example:
+1)
+a = (int *)b; // wrong
+2)
+a = (int *) b; // correct
+
+
+M7: Don't initialize variable unnecessarily
+===========================================
+When declaring a variable, try not to initialize it unless necessary.
+
+Example:
+int i = 1; // wrong
+
+for (i = 0; i < 3; i++) {
+}
+
+
+M8: Use g_try_malloc instead of g_malloc
+========================================
+When g_malloc fails, the whole program would exit. Most of time, this is not
+the expected behavior, and you may want to use g_try_malloc instead.
+
+Example:
+additional = g_try_malloc(len - 1); // correct
+if (additional == NULL)
+ return FALSE;
+
+
+M9: Follow the order of include header elements
+===============================================
+When writing an include header the various elements should be in the following
+order:
+ - #includes
+ - forward declarations
+ - #defines
+ - enums
+ - typedefs
+ - function declarations and inline function definitions
+
+
+M10: Internal headers must not use include guards
+=================================================
+Any time when creating a new header file with non-public API, that header
+must not contain include guards.
+
+
+M11: Naming of enums
+====================
+
+Enums must have a descriptive name. The enum type should be small caps and
+it should not be typedef-ed. Enum contents should be in CAPITAL letters and
+prefixed by the enum type name.
+
+Example:
+
+enum animal_type {
+ ANIMAL_TYPE_FOUR_LEGS,
+ ANIMAL_TYPE_EIGHT_LEGS,
+ ANIMAL_TYPE_TWO_LEGS,
+};
+
+If the enum contents have values (e.g. from specification) the formatting
+should be as follows:
+
+enum animal_type {
+ ANIMAL_TYPE_FOUR_LEGS = 4,
+ ANIMAL_TYPE_EIGHT_LEGS = 8,
+ ANIMAL_TYPE_TWO_LEGS = 2,
+};
+
+M12: Enum as switch variable
+====================
+
+If the variable of a switch is an enum, you must not include a default in
+switch body. The reason for this is: If later on you modify the enum by adding
+a new type, and forget to change the switch accordingly, the compiler will
+complain the new added type hasn't been handled.
+
+Example:
+
+enum animal_type {
+ ANIMAL_TYPE_FOUR_LEGS = 4,
+ ANIMAL_TYPE_EIGHT_LEGS = 8,
+ ANIMAL_TYPE_TWO_LEGS = 2,
+};
+
+enum animal_type t;
+
+switch (t) {
+case ANIMAL_TYPE_FOUR_LEGS:
+ ...
+ break;
+case ANIMAL_TYPE_EIGHT_LEGS:
+ ...
+ break;
+case ANIMAL_TYPE_TWO_LEGS:
+ ...
+ break;
+default: // wrong
+ break;
+}
+
+However if the enum comes from an external header file outside ConnMan
+we cannot make any assumption of how the enum is defined and this
+rule might not apply.
+
+M13: Check for pointer being NULL
+=================================
+
+When checking if a pointer or a return value is NULL, explicitly compare to
+NULL rather than use the shorter check with "!" operator.
+
+Example:
+1)
+array = g_try_new0(int, 20);
+if (!array) // Wrong
+ return;
+
+2)
+if (!g_at_chat_get_slave(chat)) // Wrong
+ return -EINVAL;
+
+3)
+array = g_try_new0(int, 20);
+if (array == NULL) // Correct
+ return;
+
+
+M14: Always use parenthesis with sizeof
+=======================================
+The expression argument to the sizeof operator should always be in
+parenthesis, too.
+
+Example:
+1)
+memset(stuff, 0, sizeof(*stuff));
+
+2)
+memset(stuff, 0, sizeof *stuff); // Wrong
+
+
+M15: Use void if function has no parameters
+===========================================================
+A function with no parameters must use void in the parameter list.
+
+Example:
+1)
+void foo(void)
+{
+}
+
+2)
+void foo() // Wrong
+{
+}
+
+M16: Don't use hex value with shift operators
+==============================================
+The expression argument to the shift operators should not be in hex.
+
+Example:
+
+1)
+1 << y
+
+2)
+0x1 << y // Wrong
+
+O1: Shorten the name
+====================
+Better to use abbreviation, rather than full name, to name a variable,
+function, struct, etc.
+
+Example:
+supplementary_service // too long
+ss // better
+
+O2: Try to avoid complex if body
+================================
+It's better not to have a complicated statement for if. You may judge its
+contrary condition and return | break | continue | goto ASAP.
+
+Example:
+1)
+if (a) { // worse
+ struct voicecall *v;
+ call = synthesize_outgoing_call(vc, vc->pending);
+ v = voicecall_create(vc, call);
+ v->detect_time = time(NULL);
+ DBG("Registering new call: %d", call->id);
+ voicecall_dbus_register(v);
+} else
+ return;
+
+2)
+if (!a)
+ return;
+
+struct voicecall *v;
+call = synthesize_outgoing_call(vc, vc->pending);
+v = voicecall_create(vc, call);
+v->detect_time = time(NULL);
+DBG("Registering new call: %d", call->id);
+voicecall_dbus_register(v);
diff --git a/doc/config-format.txt b/doc/config-format.txt
new file mode 100644
index 0000000..d8146be
--- /dev/null
+++ b/doc/config-format.txt
@@ -0,0 +1,101 @@
+Connman configuration file format
+*********************************
+
+Connman uses configuration files to provision existing services. Connman will
+be looking for its configuration files at STORAGEDIR which by default points
+to /var/lib/connman/. Configuration file names must not include other
+characters than letters or numbers and must have a .config suffix.
+Those configuration files are text files with a simple format and we typically
+have one file per provisioned network.
+
+If the config file is removed, then Connman tries to remove the
+provisioned service. If individual service entry inside config is removed,
+then the corresponding provisioned service is removed. If service
+entry is changed, then corresponding service is removed and then
+immediately re-provisioned.
+
+
+Global entry [global]
+=====================
+
+These files can have an optional global entry describing the actual file.
+The 2 allowed fields for that entry are:
+- Name: Name of the network.
+- Description: Description of the network.
+- Protected: Configuration protection against being removed, modified or
+overwritten by a Manager.ProvisionService() call. If unset, this value defaults
+to TRUE, i.e. configs are protected by default.
+
+
+Service entry [service_*]
+=========================
+
+Each provisioned service must start with the [service_*] tag. Replace * with
+an identifier unique to the config file.
+
+Allowed fields:
+- Type: Service type. We currently only support wifi.
+- Name: A string representation of an 802.11 SSID. If the SSID field is
+ present, the Name field is ignored.
+- SSID: A hexadecimal representation of an 802.11 SSID. If the SSID field is
+ omitted, the Name field is used instead.
+- EAP: EAP type. We currently only support tls, ttls or peap.
+- CACertFile: File path to CA certificate file (PEM/DER).
+- ClientCertFile: File path to client certificate file (PEM/DER).
+- PrivateKeyFile: File path to client private key file (PEM/DER/PFX).
+- PrivateKeyPassphrase: Password/passphrase for private key file.
+- PrivateKeyPassphraseType: We only support the fsid passphrase type for now.
+ This is for private keys generated by using their own filesystem UUID as the
+ passphrase. The PrivateKeyPassphrase field is ignored when this field is set
+ to fsid.
+- Identity: Identity string for EAP.
+- Phase2: Phase2 (inner authentication with TLS tunnel) authentication method.
+ Prefix the value with "EAP-" to indicate the usage of an EAP-based inner
+ authentication method (should only be used with EAP = TTLS).
+- Passphrase: RSN/WPA/WPA2 Passphrase
+- Hidden: If set to true, then this AP is hidden. If missing or set to false,
+ then AP is not hidden.
+
+
+Example
+=======
+
+This is a configuration file for a network providing EAP-TLS, EAP-TTLS and
+EAP-PEAP services.
+The respective SSIDs are tls_ssid, ttls_ssid and peap_ssid and the file name
+is example.config.
+Please note that the SSID entry is for hexadecimal encoded SSID (e.g. "SSID =
+746c735f73736964"). If your SSID does not contain any exotic character then
+you should use the Name entry instead (e.g. "Name = tls_ssid").
+
+
+example@example:[~]$ cat /var/lib/connman/example.config
+[global]
+Name = Example
+Description = Example network configuration
+
+[service_tls]
+Type = wifi
+SSID = 746c735f73736964
+EAP = tls
+CACertFile = /home/user/.certs/ca.pem
+ClientCertFile = /home/user/devlp/.certs/client.pem
+PrivateKeyFile = /home/user/.certs/client.fsid.pem
+PrivateKeyPassphraseType = fsid
+Identity = user
+
+[service_ttls]
+Type = wifi
+Name = ttls_ssid
+EAP = ttls
+CACertFile = /home/user/.cert/ca.pem
+Phase2 = MSCHAPV2
+Identity = user
+
+[service_peap]
+Type = wifi
+Name = peap_ssid
+EAP = peap
+CACertFile = /home/user/.cert/ca.pem
+Phase2 = MSCHAPV2
+Identity = user
diff --git a/doc/connman-docs.xml b/doc/connman-docs.xml
new file mode 100644
index 0000000..d4059a4
--- /dev/null
+++ b/doc/connman-docs.xml
@@ -0,0 +1,121 @@
+<?xml version="1.0"?>
+<!DOCTYPE book PUBLIC "-//OASIS//DTD DocBook XML V4.1.2//EN"
+ "http://www.oasis-open.org/docbook/xml/4.1.2/docbookx.dtd" [
+<!ENTITY version SYSTEM "version.xml">
+]>
+<book id="index" xmlns:xi="http://www.w3.org/2003/XInclude">
+ <bookinfo>
+ <title>Connection Manager Reference Manual</title>
+ <releaseinfo>Version &version;</releaseinfo>
+ <authorgroup>
+ <author>
+ <firstname>Marcel</firstname>
+ <surname>Holtmann</surname>
+ <affiliation>
+ <address>
+ <email>marcel@holtmann.org</email>
+ </address>
+ </affiliation>
+ </author>
+ </authorgroup>
+
+ <copyright>
+ <year>2007-2008</year>
+ <holder>Intel Corporation. All rights reserved.</holder>
+ </copyright>
+
+ <legalnotice>
+ <para>
+ Permission is granted to copy, distribute and/or modify this
+ document under the terms of the <citetitle>GNU Free
+ Documentation License</citetitle>, Version 1.1 or any later
+ version published by the Free Software Foundation with no
+ Invariant Sections, no Front-Cover Texts, and no Back-Cover
+ Texts. You may obtain a copy of the <citetitle>GNU Free
+ Documentation License</citetitle> from the Free Software
+ Foundation by visiting <ulink type="http"
+ url="http://www.fsf.org">their Web site</ulink> or by writing
+ to:
+
+ <address>
+ The Free Software Foundation, Inc.,
+ <street>59 Temple Place</street> - Suite 330,
+ <city>Boston</city>, <state>MA</state> <postcode>02111-1307</postcode>,
+ <country>USA</country>
+ </address>
+ </para>
+ </legalnotice>
+ </bookinfo>
+
+ <reference id="design">
+ <title>Design Overview</title>
+ <partintro>
+ <para>
+ This part presents the design documentation for Connection Manager.
+ </para>
+ </partintro>
+ <xi:include href="connman-introduction.xml" />
+ </reference>
+
+ <reference id="manager">
+ <title>Manager interface</title>
+ <para>
+<programlisting><xi:include href="manager-api.txt" parse="text" /></programlisting>
+ </para>
+ </reference>
+
+ <reference id="device">
+ <title>Device interface</title>
+ <para>
+<programlisting><xi:include href="device-api.txt" parse="text" /></programlisting>
+ </para>
+ </reference>
+
+ <reference id="network">
+ <title>Network interface</title>
+ <para>
+<programlisting><xi:include href="network-api.txt" parse="text" /></programlisting>
+ </para>
+ </reference>
+
+ <reference id="service">
+ <title>Service interface</title>
+ <para>
+<programlisting><xi:include href="service-api.txt" parse="text" /></programlisting>
+ </para>
+ </reference>
+
+ <reference id="connection">
+ <title>Connection interface</title>
+ <para>
+<programlisting><xi:include href="connection-api.txt" parse="text" /></programlisting>
+ </para>
+ </reference>
+
+ <reference id="reference">
+ <title>Plugin API Reference</title>
+ <partintro>
+ <para>
+ This part presents the function reference for Connection Manager.
+ </para>
+ </partintro>
+ <xi:include href="xml/log.xml" />
+ <xi:include href="xml/plugin.xml" />
+ <xi:include href="xml/storage.xml" />
+ <xi:include href="xml/security.xml" />
+ <xi:include href="xml/resolver.xml" />
+ <!-- <xi:include href="xml/device.xml" /> -->
+ <!-- <xi:include href="xml/network.xml" /> -->
+ </reference>
+
+ <appendix id="license">
+ <title>License</title>
+ <para>
+<programlisting><xi:include href="../COPYING" parse="text" /></programlisting>
+ </para>
+ </appendix>
+
+ <index>
+ <title>Index</title>
+ </index>
+</book>
diff --git a/doc/connman-introduction.xml b/doc/connman-introduction.xml
new file mode 100644
index 0000000..4672c2c
--- /dev/null
+++ b/doc/connman-introduction.xml
@@ -0,0 +1,15 @@
+<?xml version="1.0"?>
+<!DOCTYPE book PUBLIC "-//OASIS//DTD DocBook XML V4.1.2//EN"
+ "http://www.oasis-open.org/docbook/xml/4.1.2/docbookx.dtd">
+
+<chapter id="introduction">
+ <title>Introduction</title>
+
+ <sect1 id="intro-about">
+ <title>About</title>
+
+ <para>
+ </para>
+ </sect1>
+
+</chapter>
diff --git a/doc/connman.8 b/doc/connman.8
new file mode 100644
index 0000000..7297cf8
--- /dev/null
+++ b/doc/connman.8
@@ -0,0 +1,79 @@
+.\" connman(8) manual page
+.\"
+.\" Copyright (C) 2012 Intel Corporation
+.\"
+.TH CONNMAN "8" "21 August 2012"
+.SH NAME
+ConnMan \- network management daemon
+.SH SYNOPSIS
+.B connmand [\-\-version] | [\-\-help]
+.PP
+.B connmand [\-\-config=<filename>] [\-\-debug=<file1>:<file2>:...] [\-\-device=<interface1>,<interface2>,...] [\-\-nodevice=<interface1>,<interface2>,..] [\-\-wifi=<driver1>,<driver2>,...] [\-\-plugin=<plugin1>,<plugin2>,...] [\-\-noplugin=<plugin1>,<plugin2>,...] [\-\-nodaemon] [\-\-nodnsproxy]
+.SH DESCRIPTION
+The \fIConnMan\fP provides a daemon for managing internet connections
+within devices running the Linux operating system. The Connection Manager is
+designed to be slim and to use as few resources as possible.
+It is a fully modular system that can be extended, through plug-ins,
+to support all kinds of wired or wireless technologies.
+Also, configuration methods, like DHCP and domain name resolving,
+are implemented using plug-ins.
+The plug-in approach allows for easy adaption and modification for various
+use cases.
+.P
+.SH OPTIONS
+The following options are supported:
+.TP
+.I "\-\-version"
+Print the ConnMan software version and exit.
+.TP
+.I "\-\-help"
+Print ConnMan's available options and exit.
+.TP
+.I "\-\-config=<filename>"
+Specify configuration file to set up various settings for ConnMan. If not
+specified, the default value of '<SYSCONFDIR>/connman/main.conf'
+is used; where <SYSCONFDIR> is dependent on your distribution (usually
+it's /etc). See \fBconnman.conf\fP(5) for more information on configuration
+file. The use of config file is optional and sane default values
+are used if config file is missing.
+.TP
+.I "\-\-debug=<file1>:<file2>:..."
+Sets how much information ConnMan sends to the log destination (usually
+syslog's "daemon" facility). If the file options are omitted, then debugging
+information from all the source files are printed. If file options are
+present, then only debug prints from that source file are printed.
+Example: --debug=src/service.c:plugins/wifi.c
+.TP
+.I "\-\-device=<interface1>,<interface2>,..."
+Only manage these network interfaces. By default all network interfaces
+are managed.
+.TP
+.I "\-\-nodevice=<interface1>,<interface2>,..."
+Never manage these network interfaces.
+.TP
+.I "\-\-plugin=<plugin1>,<plugin2>,..."
+Load these plugins only. The option can be a pattern containing
+"*" and "?" characters.
+.TP
+.I "\-\-noplugin=<plugin1>,<plugin2>,..."
+Never load these plugins. The option can be a pattern containing
+"*" and "?" characters.
+.TP
+.I "\-\-wifi=<driver1>,<driver2>,..."
+Wifi driver that WiFi/Supplicant should use. If omitted, then the value
+of "nl80211,wext" is used by default.
+.TP
+.I "\-\-nodaemon"
+Do not daemonize. This is useful for debugging, and directs log output to
+the controlling terminal in addition to syslog.
+.TP
+.I "\-\-nodnsproxy"
+Do not act as a DNS proxy. By default ConnMan will direct all DNS traffic
+to itself by setting nameserver to 127.0.0.1 in \fBresolv.conf\fP(5) file.
+If this is not desired and you want that all programs call directly some
+DNS server, then you can use the --nodnsproxy option.
+If this option is used, then ConnMan is not able to cache the DNS queries
+because the DNS traffic is not going through ConnMan and that can cause
+some extra network traffic.
+.SH SEE ALSO
+.BR connman.conf (5).
diff --git a/doc/connman.conf.5 b/doc/connman.conf.5
new file mode 100644
index 0000000..5500a17
--- /dev/null
+++ b/doc/connman.conf.5
@@ -0,0 +1,100 @@
+.\" connman.conf(5) manual page
+.\"
+.\" Copyright (C) 2012 Intel Corporation
+.\"
+.TH "connman.conf" "5" "21 August 2012" ""
+.SH NAME
+main.conf \- ConnMan configuration file
+.SH SYNOPSIS
+/etc/connman/main.conf
+.br
+or
+.br
+\fI<SYSCONFDIR>\fP/connman/main.conf
+.br
+where <SYSCONFDIR> depends on your distribution or build.
+.SH DESCRIPTION
+.P
+.I main.conf
+is a configuration file for ConnMan. The configuration file is
+optional but it can be used to set up various aspects of ConnMan's
+behavior. The location of the file may be changed through use of
+the "\-\-config=" argument for \fBconnman\fP (8).
+.SH "FILE FORMAT"
+.P
+The configuration file format is key file format.
+It consists of sections (groups) of key-value pairs.
+Lines beginning with a '#' and blank lines are considered comments.
+Sections are started by a header line containing the section enclosed
+in '[' and ']', and ended implicitly by the start of the next section
+or the end of the file. Each key-value pair must be contained in a section.
+.P
+Description of sections and available keys follows:
+.SS [General]
+This section is the only mandatory section of the configuration file.
+.TP
+.B InputRequestTimeout=\fPsecs\fP
+Set input request timeout. Default is 120 seconds
+The request for inputs like passphrase will timeout
+after certain amount of time. Use this setting to
+increase the value in case of different user
+interface designs.
+.TP
+.B BrowserLaunchTimeout=\fPsecs\fP
+Set browser launch timeout. Default is 300 seconds
+The request for launching a browser for portal pages
+will timeout after certain amount of time. Use this
+setting to increase the value in case of different
+user interface designs.
+.TP
+.B BackgroundScanning=\fPtrue|false\fP
+Enable background scanning. Default is true.
+Background scanning will start every 5 minutes unless
+the scan list is empty. In that case, a simple backoff
+mechanism starting from 10s up to 5 minutes will run.
+.TP
+.B FallbackTimeservers=\fPserver1,server2,...\fP
+List of Fallback timeservers separated by ",".
+These timeservers are used for NTP sync when there are
+no timeserver set by the user or by the service.
+These can contain mixed combination of fully qualified
+domain names, IPv4 and IPv6 addresses.
+.TP
+.B FallbackNameservers=\fPserver1,server2,...\fP
+List of fallback nameservers separated by "," appended
+to the list of nameservers given by the service. The
+nameserver entries must be in numeric format, host
+names are ignored.
+.TP
+.B DefaultAutoConnectTechnologies=\fPtechnology1,technology2,...\fP
+List of technologies that are marked autoconnectable
+by default, separated by commas ",". The default value
+for this entry when empty is ethernet,wifi,cellular.
+Services that are automatically connected must have been
+set up and saved to storage beforehand.
+.TP
+.B PreferredTechnologies=\fPtechnology1,technology2,...\fP
+List of preferred technologies from the most preferred
+one to the least preferred one separated by commas ",".
+Services of the listed technology type will be tried one
+by one in the order given, until one of them gets connected
+with state 'online' or they are all tried. A service of a
+preferred technology type in state 'ready' will get the
+default route when compared to a non-preferred type; a
+service of a preferred technology type in state 'online'
+will get the default route when compared to either a
+non-preferred type or a preferred type in state 'ready'.
+.TP
+.B NetworkInterfaceBlacklist=\fPinterface1,interface2,...\fP
+List of blacklisted network interfaces separated by ",".
+Found interfaces will be compared to the list and will
+not be handled by connman, if their first characters
+match any of the list entries. Default value is
+vmnet,vboxnet,virbr.
+.TP
+.B AllowHostnameUpdates=\fPtrue|false\fP
+Allow connman to change the system hostname. This can
+happen for example if we receive DHCP hostname option.
+Default value is true.
+.SH "SEE ALSO"
+.BR Connman (8)
diff --git a/doc/connmanctl.1 b/doc/connmanctl.1
new file mode 100644
index 0000000..b71c6e6
--- /dev/null
+++ b/doc/connmanctl.1
@@ -0,0 +1,190 @@
+.TH connmanctl 1 07/31/2012 "" "User Commands for Connman CLI"
+.SH
+NAME
+connmanctl \- Connman CLI
+.SH
+SYNOPSIS
+.BR connmanctl " ["
+.BR enable " <technology> | "
+.BR offlinemode "] ["
+.BR disable " <technology> | "
+.BR offlinemode "] ["
+.BR technologies "] ["
+.BR state "] ["
+.BR services " [\-\-properties <service>]] ["
+.BR scan " <technology>] ["
+.BR connect " <service>] ["
+.BR config " <service> \-\-<option> ARGS...] ["
+.BR help " | \-\-help]"
+.PP
+.SH
+DESCRIPTION
+Connmanctl is a Connman command line interface which can be run in two modes:
+a plain synchronous command input, and an asynchronous interactive shell.
+To run a specific command the user may enter connmanctl <command> [options]
+[args], or enter connmanctl; in this case, the program will drop into the
+interactive shell.
+.PP
+Connmantl can handle most simple network connections. It is able to enable/
+disable any technology that exists on the system, display a list of
+services available, connect to/disconnect from any unsecured networks,
+show properties of the system, the technologies, and any individual
+service, and configure all of the properties. It is also able to monitor
+changes in the properties of the services, technologies, and the system.
+.PP
+In the interactive shell, all of the same commands can be used. It
+provides quicker usage when needing to use connmanctl more extensively.
+.SH
+COMMANDS AND OPTIONS
+.TP
+.BR "help | \-\-help | " "(no arguments)"
+Shows the abbreviated help menu in the terminal.
+.PP
+.TP
+.BR enable " <technology>"
+Enables the given technology type (e.g. ethernet, wifi, 3g, etc.)
+Turns power on to the technology, but doesn't connect unless
+there is a service with autoconnect set to True.
+.PP
+.TP
+.BR disable " <technology>"
+Disables the given technology type. Turns power off to the
+technology and disconnects if it is already connected.
+.PP
+.TP
+.B enable offlinemode
+Enables offline mode. Disconnects and powers down all
+technologies system-wide, however each technology can be powered
+back on individually.
+.PP
+.TP
+.B disable offlinemode
+Disables offline mode. Technologies are powered back on according
+to their individual policies.
+.PP
+.TP
+.B technologies
+Shows a list of all technology types existing on the system and
+their properties. See the properties section of the Technology
+API for explanations of each property.
+.PP
+.TP
+.B state
+Shows the system properties. Includes ths online state of the
+system, offline mode, and session mode.
+.PP
+.TP
+.BR scan " <technology>"
+Scans for new services on the given technology.
+.PP
+.TP
+.B services
+Shows a list of all available service names. This includes the
+names of wifi networks, the wired ethernet connection, names of
+bluetooth devices, etc. These are the names used when a
+<service> command is called for. The service name
+(e.g. Joes-wifi), the service path (e.g.
+wifi_6834534139723_managed_none), or the full service path (e.g.
+/net/connman/Service/wifi_5467631...) are all accepted as valid
+input. An asterisk in front of the service indicates that the
+service is favorited, and a "C" indicates a service that is
+already connected.
+.PP
+.TP
+.BR "services \-\-properties" " <service>"
+Shows a list of all properties for that service. See the
+properties section of the Service API for explanations of each
+property.
+.PP
+.TP
+.BR connect " <service>"
+Connects to the given service if it is unsecured.
+.PP
+.TP
+.BR disconnect " <service>"
+Disconnects from the given service.
+.PP
+.TP
+.BR config " <service> " \-\-<option>
+Configures a writable property of the given service to the
+value(s) entered after --<option>.
+.PP
+.TP
+.BR monitor " [\-\-<option>]"
+Listens for and displays DBus signals sent by Connman. The option indicates
+which signals you want to subscribe to. If no option is entered, it displays
+all signals from all interfaces.
+.PP
+.SS
+Config Options:
+.PP
+.TP
+.B \-\-autoconnect=y/n
+Sets the autoconnect property of the service.
+.PP
+.TP
+.B \-\-ipv4
+Configures the IPv4 settings for the service. Enter the settings
+in the order "Method", "Address", "Netmask", then "Gateway"
+after the argument. See the properties section of the Service
+API for more information on these settings and the values
+accepted for them. It also displays a list of changes to both the
+IPv4 settings, and incidental changes to other values related to
+it.
+.PP
+.TP
+.B \-\-ipv6
+Configures the IPv6 settings for the service. Enter the settings
+in the order "Method", "Address", "PrefixLength", "Gateway", then
+"Privacy". See the properties section of the Service API for more
+information on these settings and the values accepted for them.
+It also displays a list of entered changes to the IPv6 settings,
+and incidental changes to other values related to it.
+.PP
+.TP
+.B \-\-nameservers
+Adds to the list of manually configured domain name servers.
+Enter the name servers after the argument separated by spaces.
+.PP
+.TP
+.B \-\-timeservers
+Adds to the list of manually configured time servers. Enter the
+time servers after the argument separated by spaces.
+.PP
+.TP
+.B \-\-domains
+Adds to the list of manually configured search domains. Enter
+the domains after the argument, separated by spaces.
+.PP
+.TP
+.B \-\-proxy
+Configures the IPv6 settings for the service. Enter the settings in the
+order "Method", "URL". If the Method is set to "direct", no other arguments
+are taken. If the Method is set to "auto", the URL is optional. To set the
+Servers and Excludes manually, enter "manual" followed by "servers" with a
+list of servers separated by spaces. Then, optionally, the word "excludes"
+followed by a list of excludes separated by spaces. e.g. "./connmanctl config
+joes-wifi \-\-proxy manual servers serv1 serv2 serv3 excludes excl1 excl2"
+.PP
+.SS
+Monitor Options:
+.PP
+.TP
+.B \-\-services
+Listens for and displays the PropertyChanged signal from the Service interface.
+Also displays the service name (e.g. Joes-wifi) that the property is part of.
+More information, including a list of possible properties can be found in the
+Service API.
+.PP
+.TP
+.B \-\-tech
+Listens for and displays the PropertyChanged signal from the Technology
+interface. More information, including a list of possible properties can be
+found in the Technology API.
+.PP
+.TP
+.B \-\-manager
+Listens for and displays the PropertyChanged, ServicesChanged, TechnologyAdded,
+and TechnologyRemoved signals from the Manager interface. More information on
+these signals and a list of possible properties can be found in the Manager API.
+.PP
diff --git a/doc/counter-api.txt b/doc/counter-api.txt
new file mode 100644
index 0000000..32411d5
--- /dev/null
+++ b/doc/counter-api.txt
@@ -0,0 +1,70 @@
+Counter hierarchy
+=================
+
+Service unique name
+Interface net.connman.Counter
+Object path freely definable
+
+Methods void Release()
+
+ This method gets called when the service daemon
+ unregisters the counter. A counter can use it to do
+ cleanup tasks. There is no need to unregister the
+ counter, because when this method gets called it has
+ already been unregistered.
+
+ void Usage(object service, dict home, dict roaming)
+
+ This signal indicates a change in the counter values
+ for the service object. The counter is reset by calling
+ the service ResetCounters method.
+
+ When registering a new counter this method will be
+ called once with all details for "home" and "roaming"
+ counters filled in. Every further method call will
+ only include the changed values.
+
+ When "home" counter is active, then "roaming" counter
+ will contain an empty dictionary and vise-versa.
+
+ The dictionary argument contains the following entries:
+
+ RX.Packets
+
+ Total number of packets received.
+
+ TX.Bytes
+
+ Total number of packets sent.
+
+ RX.Bytes
+
+ Total number of bytes received.
+
+ TX.Bytes
+
+ Total number of bytes sent.
+
+ RX.Errors
+
+ Total number of erronous packets
+ received.
+
+ TX.Errors
+
+ Total number of erronous packets
+ sent.
+
+ RX.Dropped
+
+ Total number of dropped packets
+ while receiving.
+
+ TX.Dropped
+
+ Total number of dropped packets
+ while sending.
+
+ Time
+
+ Total number of seconds online.
diff --git a/doc/gtk-doc.make b/doc/gtk-doc.make
new file mode 100644
index 0000000..354ffb7
--- /dev/null
+++ b/doc/gtk-doc.make
@@ -0,0 +1,173 @@
+# -*- mode: makefile -*-
+
+####################################
+# Everything below here is generic #
+####################################
+
+if GTK_DOC_USE_LIBTOOL
+GTKDOC_CC = $(LIBTOOL) --mode=compile $(CC) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS)
+GTKDOC_LD = $(LIBTOOL) --mode=link $(CC) $(AM_CFLAGS) $(CFLAGS) $(AM_LDFLAGS) $(LDFLAGS)
+else
+GTKDOC_CC = $(CC) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS)
+GTKDOC_LD = $(CC) $(AM_CFLAGS) $(CFLAGS) $(AM_LDFLAGS) $(LDFLAGS)
+endif
+
+# We set GPATH here; this gives us semantics for GNU make
+# which are more like other make's VPATH, when it comes to
+# whether a source that is a target of one rule is then
+# searched for in VPATH/GPATH.
+#
+GPATH = $(srcdir)
+
+TARGET_DIR=$(HTML_DIR)/$(DOC_MODULE)
+
+EXTRA_DIST = \
+ $(content_files) \
+ $(HTML_IMAGES) \
+ $(DOC_MAIN_SGML_FILE) \
+ $(DOC_MODULE)-sections.txt \
+ $(DOC_MODULE)-overrides.txt
+
+DOC_STAMPS=scan-build.stamp tmpl-build.stamp sgml-build.stamp html-build.stamp \
+ $(srcdir)/tmpl.stamp $(srcdir)/sgml.stamp $(srcdir)/html.stamp
+
+SCANOBJ_FILES = \
+ $(DOC_MODULE).args \
+ $(DOC_MODULE).hierarchy \
+ $(DOC_MODULE).interfaces \
+ $(DOC_MODULE).prerequisites \
+ $(DOC_MODULE).signals
+
+REPORT_FILES = \
+ $(DOC_MODULE)-undocumented.txt \
+ $(DOC_MODULE)-undeclared.txt \
+ $(DOC_MODULE)-unused.txt
+
+CLEANFILES = $(SCANOBJ_FILES) $(REPORT_FILES) $(DOC_STAMPS)
+
+if ENABLE_GTK_DOC
+all-local: html-build.stamp
+else
+all-local:
+endif
+
+docs: html-build.stamp
+
+#### scan ####
+
+scan-build.stamp: $(HFILE_GLOB) $(CFILE_GLOB)
+ @echo 'gtk-doc: Scanning header files'
+ @-chmod -R u+w $(srcdir)
+ cd $(srcdir) && \
+ gtkdoc-scan --module=$(DOC_MODULE) --source-dir=$(DOC_SOURCE_DIR) --ignore-headers="$(IGNORE_HFILES)" $(SCAN_OPTIONS) $(EXTRA_HFILES)
+ if grep -l '^..*$$' $(srcdir)/$(DOC_MODULE).types > /dev/null 2>&1 ; then \
+ CC="$(GTKDOC_CC)" LD="$(GTKDOC_LD)" CFLAGS="$(GTKDOC_CFLAGS)" LDFLAGS="$(GTKDOC_LIBS)" gtkdoc-scangobj $(SCANGOBJ_OPTIONS) --module=$(DOC_MODULE) --output-dir=$(srcdir) ; \
+ else \
+ cd $(srcdir) ; \
+ for i in $(SCANOBJ_FILES) ; do \
+ test -f $$i || touch $$i ; \
+ done \
+ fi
+ touch scan-build.stamp
+
+$(DOC_MODULE)-decl.txt $(SCANOBJ_FILES) $(DOC_MODULE)-sections.txt $(DOC_MODULE)-overrides.txt: scan-build.stamp
+ @true
+
+#### templates ####
+
+tmpl-build.stamp: $(DOC_MODULE)-decl.txt $(SCANOBJ_FILES) $(DOC_MODULE)-sections.txt $(DOC_MODULE)-overrides.txt
+ @echo 'gtk-doc: Rebuilding template files'
+ @-chmod -R u+w $(srcdir)
+ cd $(srcdir) && gtkdoc-mktmpl --module=$(DOC_MODULE) $(MKTMPL_OPTIONS)
+ touch tmpl-build.stamp
+
+tmpl.stamp: tmpl-build.stamp
+ @true
+
+tmpl/*.sgml:
+ @true
+
+
+#### xml ####
+
+sgml-build.stamp: tmpl.stamp $(HFILE_GLOB) $(CFILE_GLOB) $(DOC_MODULE)-sections.txt $(srcdir)/tmpl/*.sgml $(expand_content_files)
+ @echo 'gtk-doc: Building XML'
+ @-chmod -R u+w $(srcdir)
+ cd $(srcdir) && \
+ gtkdoc-mkdb --module=$(DOC_MODULE) --source-dir=$(DOC_SOURCE_DIR) --output-format=xml --expand-content-files="$(expand_content_files)" --main-sgml-file=$(DOC_MAIN_SGML_FILE) $(MKDB_OPTIONS)
+ touch sgml-build.stamp
+
+sgml.stamp: sgml-build.stamp
+ @true
+
+#### html ####
+
+html-build.stamp: sgml.stamp $(DOC_MAIN_SGML_FILE) $(content_files)
+ @echo 'gtk-doc: Building HTML'
+ @-chmod -R u+w $(srcdir)
+ rm -rf $(srcdir)/html
+ mkdir $(srcdir)/html
+ cd $(srcdir)/html && gtkdoc-mkhtml $(DOC_MODULE) ../$(DOC_MAIN_SGML_FILE)
+ test "x$(HTML_IMAGES)" = "x" || ( cd $(srcdir) && cp $(HTML_IMAGES) html )
+ @echo 'gtk-doc: Fixing cross-references'
+ cd $(srcdir) && gtkdoc-fixxref --module-dir=html --html-dir=$(HTML_DIR) $(FIXXREF_OPTIONS)
+ touch html-build.stamp
+
+##############
+
+clean-local:
+ rm -f *~ *.bak
+ rm -rf .libs
+
+distclean-local:
+ cd $(srcdir) && \
+ rm -rf xml $(REPORT_FILES) \
+ $(DOC_MODULE)-decl-list.txt $(DOC_MODULE)-decl.txt
+
+maintainer-clean-local: clean
+ cd $(srcdir) && rm -rf xml html
+
+install-data-local:
+ -installfiles=`echo $(srcdir)/html/*`; \
+ if test "$$installfiles" = '$(srcdir)/html/*'; \
+ then echo '-- Nothing to install' ; \
+ else \
+ $(mkinstalldirs) $(DESTDIR)$(TARGET_DIR); \
+ for i in $$installfiles; do \
+ echo '-- Installing '$$i ; \
+ $(INSTALL_DATA) $$i $(DESTDIR)$(TARGET_DIR); \
+ done; \
+ echo '-- Installing $(srcdir)/html/index.sgml' ; \
+ $(INSTALL_DATA) $(srcdir)/html/index.sgml $(DESTDIR)$(TARGET_DIR) || :; \
+ which gtkdoc-rebase >/dev/null && \
+ gtkdoc-rebase --relative --dest-dir=$(DESTDIR) --html-dir=$(DESTDIR)$(TARGET_DIR) ; \
+ fi
+
+
+uninstall-local:
+ rm -f $(DESTDIR)$(TARGET_DIR)/*
+
+#
+# Require gtk-doc when making dist
+#
+if ENABLE_GTK_DOC
+dist-check-gtkdoc:
+else
+dist-check-gtkdoc:
+ @echo "*** gtk-doc must be installed and enabled in order to make dist"
+ @false
+endif
+
+dist-hook: dist-check-gtkdoc dist-hook-local
+ mkdir $(distdir)/tmpl
+ mkdir $(distdir)/xml
+ mkdir $(distdir)/html
+ -cp $(srcdir)/tmpl/*.sgml $(distdir)/tmpl
+ -cp $(srcdir)/xml/*.xml $(distdir)/xml
+ cp $(srcdir)/html/* $(distdir)/html
+ -cp $(srcdir)/$(DOC_MODULE).types $(distdir)/
+ -cp $(srcdir)/$(DOC_MODULE)-sections.txt $(distdir)/
+ cd $(distdir) && rm -f $(DISTCLEANFILES)
+ -gtkdoc-rebase --online --relative --html-dir=$(distdir)/html
+
+.PHONY : dist-hook-local docs
diff --git a/doc/ipconfig-api.txt b/doc/ipconfig-api.txt
new file mode 100644
index 0000000..080fdac
--- /dev/null
+++ b/doc/ipconfig-api.txt
@@ -0,0 +1,41 @@
+IP configuration handling
+*************************
+
+
+IP basics
+=========
+
+The core IP handling is designed around network interfaces or more precisely
+what the Linux kernel handles as struct net_device. Via RTNL every interface
+is tracked and an IP device created for it.
+
+ +--------+ +---- eth0 -----+
+ | | | |
+ | RTNL +-----+---->| IP device |
+ | | | | |
+ +--------+ | +---------------+
+ |
+ | +---- wlan0 ----+
+ | | |
+ +---->| IP device |
+ | |
+ +---------------+
+
+The IP device tracks link configuration, IP address setting and routing
+information for that interface. Every IP device also contains a configuration
+element. That element contains an operation table for callbacks based on
+different events.
+
+ struct connman_ipconfig_ops {
+ void (*up) (struct connman_ipconfig *);
+ void (*down) (struct connman_ipconfig *);
+ void (*lower_up) (struct connman_ipconfig *);
+ void (*lower_down) (struct connman_ipconfig *);
+ void (*ip_bound) (struct connman_ipconfig *);
+ void (*ip_release) (struct connman_ipconfig *);
+ };
+
+All configuration objects created directly by RTNL are tightly bound to the
+IP device. They will trigger DHCP or other configuration helpers.
+
+
diff --git a/doc/manager-api.txt b/doc/manager-api.txt
new file mode 100644
index 0000000..ed23c8d
--- /dev/null
+++ b/doc/manager-api.txt
@@ -0,0 +1,238 @@
+Manager hierarchy
+=================
+
+Service net.connman
+Interface net.connman.Manager
+Object path /
+
+Methods dict GetProperties()
+
+ Returns all global system properties. See the
+ properties section for available properties.
+
+ Possible Errors: [service].Error.InvalidArguments
+
+ void SetProperty(string name, variant value)
+
+ Changes the value of the specified property. Only
+ properties that are listed as read-write are
+ changeable. On success a PropertyChanged signal
+ will be emitted.
+
+ Possible Errors: [service].Error.InvalidArguments
+ [service].Error.InvalidProperty
+
+ array{object,dict} GetTechnologies()
+
+ Returns a list of tuples with technology object
+ path and dictionary of technology properties.
+
+ Possible Errors: [service].Error.InvalidArguments
+
+ array{object,dict} GetServices()
+
+ Returns a sorted list of tuples with service
+ object path and dictionary of service properties.
+
+ This list will not contain sensitive information
+ like passphrases etc.
+
+ Possible Errors: [service].Error.InvalidArguments
+
+ object ConnectProvider(dict provider) [deprecated]
+
+ Connect to a VPN specified by the given provider
+ properties.
+
+ When successful this method will return the object
+ path of the VPN service object.
+
+ This method can also be used to connect to an
+ already existing VPN.
+
+ This method call will only return in case of an
+ error or when the service is fully connected. So
+ setting a longer D-Bus timeout might be a really
+ good idea.
+
+ When 'SessionMode' property is enabled, this method
+ call is disallowed.
+
+ This API is deprecated and should not be used.
+ The VPN configuration API is provided by ConnMan
+ VPN daemon and user should use that one instead.
+
+ Possible Errors: [service].Error.InvalidArguments
+
+ void RegisterAgent(object path)
+
+ Register new agent for handling user requests.
+
+ Possible Errors: [service].Error.InvalidArguments
+
+ void UnregisterAgent(object path)
+
+ Unregister an existing agent.
+
+ Possible Errors: [service].Error.InvalidArguments
+
+ void RegisterCounter(object path, uint32 accuracy, uint32 period) [experimental]
+
+ Register a new counter for user notifications.
+
+ The accuracy is specified in kilo-bytes and defines
+ a threshold for counter updates. Together with the
+ period value it defines how often user space needs
+ to be updated. The period value is in seconds.
+
+ This interface is not meant for time tracking. If
+ the time needs to be tracked down to the second, it
+ is better to have a real timer running inside the
+ application than using this interface.
+
+ Also getting notified for every kilo-byte is a bad
+ choice (even if the interface supports it). Something
+ like 10 kilo-byte units or better 1 mega-byte seems
+ to be a lot more reasonable and better for the user.
+
+ Possible Errors: [service].Error.InvalidArguments
+
+ void UnregisterCounter(object path) [experimental]
+
+ Unregister an existing counter.
+
+ Possible Errors: [service].Error.InvalidArguments
+
+ object CreateSession(dict settings, object notifier) [experimental]
+
+ Create a new session for the application. Every
+ application can create multiple session with
+ different settings. The settings are described
+ as part of the session interface.
+
+ The notifier allows asynchronous notification about
+ session specific changes. These changes can be
+ for online/offline state or IP address changes or
+ similar things the application is required to
+ handle.
+
+ Every application should at least create one session
+ to inform about its requirements and it purpose.
+
+ void DestroySession(object session) [experimental]
+
+ Remove the previously created session.
+
+ If an application exits unexpectatly the session
+ will be automatically destroyed.
+
+ object path, dict, fd RequestPrivateNetwork(dict options)
+ [experimental]
+
+ Request a new Private Network, which includes the
+ creation of a tun/tap interface, and IP
+ configuration, NAT and IP forwarding on that
+ interface.
+ An object path, a dictionnary and a file descriptor
+ with IP settings are returned.
+
+ Possible Errors: [service].Error.InvalidArguments
+ [service].Error.NotSupported
+
+ void ReleasePrivateNetwork(object path) [experimental]
+
+ Releases a private network.
+
+ Possible Errors: [service].Error.InvalidArguments
+
+Signals TechnologyAdded(object path, dict properties)
+
+ Signal that is sent when a new technology is added.
+
+ It contains the object path of the technology and
+ also its properties.
+
+ TechnologyRemoved(object path)
+
+ Signal that is sent when a technology has been removed.
+
+ The object path is no longer accessible after this
+ signal and only emitted for reference.
+
+ ServicesChanged(array{object, dict}, array{object})
+
+ Signals a list of services that have been changed
+ via the first array. And a list of service that
+ have been removed via the second array.
+
+ The list of added services is sorted. The dictionary
+ with the properties might be empty in case none of
+ the properties have changed. Or only contains the
+ properties that have changed.
+
+ For newly added services the whole set of properties
+ will be present.
+
+ The list of removed services can be empty.
+
+ This signal will only be triggered when the sort
+ order of the service list or the number of services
+ changes. It will not be emitted if only a property
+ of the service object changes. For that it is
+ required to watch the PropertyChanged signal of
+ the service object.
+
+ PropertyChanged(string name, variant value)
+
+ This signal indicates a changed value of the given
+ property.
+
+Properties string State [readonly]
+
+ The global connection state of a system. Possible
+ values are "offline", "idle", "ready" and "online".
+
+ If the device is in offline mode, the value "offline"
+ indicates this special global state. It can also be
+ retrieved via the OfflineMode property, but is kept
+ here for consistency and to differentiate from "idle".
+
+ However when OfflineMode property is true, the State
+ property can still be "idle", "ready" or "online"
+ since it is possible by the end user to re-enable
+ individual technologies like WiFi and Bluetooth while
+ in offline mode.
+
+ The states "idle", "ready" and "online" match to
+ states from the services. If no service is in
+ either "ready" or "online" state it will indicate
+ the "idle" state.
+
+ If at least one service is in "ready" state and no
+ service is in "online" state, then it will indicate
+ the "ready" state.
+
+ When at least one service is in "online" state,
+ this property will indicate "online" as well.
+
+ boolean OfflineMode [readwrite]
+
+ The offline mode indicates the global setting for
+ switching all radios on or off. Changing offline mode
+ to true results in powering down all devices. When
+ leaving offline mode the individual policy of each
+ device decides to switch the radio back on or not.
+
+ During offline mode, it is still possible to switch
+ certain technologies manually back on. For example
+ the limited usage of WiFi or Bluetooth devices might
+ be allowed in some situations.
+
+ boolean SessionMode [readwrite] [experminental]
+
+ This disables the auto connect feature. It should be
+ enabled when the Session API is used. When SessionMode
+ is enabled, 'ConnectService' and 'ConnectProvider'
+ method calls are disallowed.
+
+ The default value is false.
diff --git a/doc/overview-api.txt b/doc/overview-api.txt
new file mode 100644
index 0000000..14fec14
--- /dev/null
+++ b/doc/overview-api.txt
@@ -0,0 +1,392 @@
+Application programming interface
+*********************************
+
+
+Service basics
+==============
+
+Inside Connection Manager there exists one advanced interface to allow the
+user interface an easy access to networking details and user chosen
+preferences. This is the service list and interface.
+
+The basic idea is that Connection Manager maintains a single flat and sorted
+list of all available, preferred or previously used services. A service here
+can be either a Ethernet device, a WiFi network or a remote Bluetooth device
+(for example a mobile phone).
+
+This list of service is sorted by Connection Manager and there is no need
+for the user interface to implement its own sorting. User decisions will
+need to be done via Connection Manager and it is then responsible to update
+the order of services in this list.
+
+ +---------------------------------------+
+ | Ethernet |
+ +---------------------------------------+
+ | Bluetooth phone |
+ +---------------------------------------+
+ | Guest (strength 90, none) |
+ +---------------------------------------+
+ | My WiFi AP (strength 80, rsn) |
+ +---------------------------------------+
+ | Other AP (strength 70, rsn) |
+ +---------------------------------------+
+ | Friends AP (strength 70, wep) |
+ +---------------------------------------+
+
+If none of the services has been used before the sorting order will be done
+with these priorities:
+
+ 1. Ethernet (lower index numbers first)
+ 2. Bluetooth (last used devices first)
+ 3. GSM/UTMS/3G (if SIM card is present, activated and not roaming)
+ 3. WiFi (signal strength first, then more secure network
+ first)
+
+The Ethernet devices are always sorted first since they are physically built
+into the system and will be always present. In cases they are switched off
+manually they will not be showing in this list.
+
+Since every Bluetooth device has to be configured/paired first, the user
+already made a choice here that these are important. Connection Manager will
+only show devices with PAN or DUN profile support. While Bluetooth devices
+do have a signal strength, it is mostly unknown since background scanning
+in Bluetooth is too expensive. The choice here is to sort the last used
+Bluetooth device before the others.
+
+WiFi networks closer in the proximity should be shown first since it is more
+likely they are selected. The signal strength value is normalized to 0-100
+(effectively a percentage) and allows an easy sorting.
+
+WiFi networks with the same signal strength are then sorted by their security
+setting. WPA2 encrypted networks should be preferred over WPA/WEP and also
+unencrypted ones. After that they will be sorted by the SSID in alphabetical
+order.
+
+In the case the WiFi network uses WPS for setup and it is clearly detectable
+that a network waits for Connection Manager to connect to it (for example via
+a push-to-connect button press on the AP), then this network should be shown
+first before any other WiFi networks. The reason here is that the user already
+made a choice via the access point. However this depends on technical details
+if it is possible to detect these situations.
+
+
+Service order
+=============
+
+All unused services will have the internal order number of 0 and then will
+be sorted according to the rules above. For Bluetooth the user already made
+the decision to setup their device and by that means select it. However
+until the first connection attempt it might have been setup for total
+different reason (like audio usage) and thus it still counts as unused from
+a networking point of view.
+
+Selecting the "My WiFi AP" and successfully connecting to it makes it a
+favorite device and it will become an order number bigger than 0. All
+order numbers are internally. They are given only to service that are marked
+as favorite. For WiFi and Bluetooth a successful connection attempt makes
+these services automatically a favorite. For Ethernet the plugging of a cable
+makes it a favorite. Disconnecting from a network doesn't remove the favorite
+setting. It is a manual operation and is equal to users pressing
+delete/remove button.
+
+ +---------------------------------------+
+ | My WiFi AP (strength 80, rsn) | order=1 - favorite=yes
+ +---------------------------------------+
+ | Ethernet | order=0
+ +---------------------------------------+
+ | Guest (strength 90, none) | order=0
+ +---------------------------------------+
+ | |
+
+Ethernet is special here since the unplugging of the network cable will
+remove the favorite selection.
+
+ +---------------------------------------+
+ | Ethernet with cable | order=1 - favorite=yes
+ +---------------------------------------+
+ | Ethernet without cable | order=0 - favorite=no
+ +---------------------------------------+
+ | Guest (strength 90, none) | order=0
+ +---------------------------------------+
+ | |
+
+This means that all services with an order > 0 have favorite=yes and all
+others have favorite=no setting. The favorite setting is exposed via a
+property over the service interface. As mentioned above, the order number
+is only used internally.
+
+Within Connection Manager many services can be connected at the same time and
+also have an IP assignment. However only one can have the default route. The
+service with the default route will always be sorted at the top of the
+list.
+
+ +---------------------------------------+
+ | Ethernet | order=2 - connected=yes
+ +---------------------------------------+
+ | My WiFi AP (strength 80, rsn) | order=1 - connected=yes
+ +---------------------------------------+
+ | Guest (strength 90, none) | order=0
+ +---------------------------------------+
+ | |
+
+To change the default connection to your access point, the user needs to
+manually drag the access point service to the top of the list. Connection
+Manager will not take down default routes if there is no reason to do so.
+A working connection is considered top priority.
+
+ +---------------------------------------+
+ | My WiFi AP (strength 80, rsn) | order=2 - connected=yes
+ +---------------------------------------+
+ | Ethernet | order=1 - connected=yes
+ +---------------------------------------+
+ | Guest (strength 90, none) | order=0
+ +---------------------------------------+
+ | |
+
+Another possible user interaction would be to unplug the Ethernet cable and
+in this case the favorite setting will be removed and the service falls back
+down in the list.
+
+ +---------------------------------------+
+ | My WiFi AP (strength 80, rsn) | order=1 - connected=yes
+ +---------------------------------------+
+ | Ethernet | order=0
+ +---------------------------------------+
+ | Guest (strength 90, none) | order=0
+ +---------------------------------------+
+ | |
+
+If the service on the top of the list changes the default route will be
+automatically adjusted as needed. The user can trigger this by disconnecting
+from a network, if the network becomes unavailable (out of range) or if the
+cable gets unplugged.
+
+As described above, the pure case of disconnecting from a network will not
+remove the favorite setting. So previously selected networks are still present
+and are sorted above all others.
+
+ +---------------------------------------+
+ | Ethernet | order=2 - connected=yes
+ +---------------------------------------+
+ | My WiFi AP (strength 80, rsn) | order=1 - connected=no
+ +---------------------------------------+
+ | Guest (strength 90, none) | order=0
+ +---------------------------------------+
+ | |
+
+Unplugging the Ethernet cable will remove the favorite setting, but due to
+the basic ordering of services it will be at the top of the services with an
+order number of 0 (directly after all favorite services).
+
+ +---------------------------------------+
+ | My WiFi AP (strength 80, rsn) | order=1 - connected=no
+ +---------------------------------------+
+ | Ethernet | order=0 - connected=no
+ +---------------------------------------+
+ | Guest (strength 90, none) | order=0
+ +---------------------------------------+
+ | |
+
+
+Service tweaks
+==============
+
+The interfaces of Connection Manager will always export all services that are
+currently known. The Ethernet devices with no cable plugged are actually not
+included in this list. They will only show up once a carrier is detected.
+
+The service interface is not meant for basic device configuration task. So
+switching a device on and off (via RFKILL for example) should be done via
+the device interface.
+
+Due to limited screen size of small devices and the big amount of WiFi
+access points that are deployed right now it might be sensible to not show
+certain WiFi networks in the user interface.
+
+The choice to hide a WiFi network from the user interface should be purely
+done by the signal strength. The optimal cut-off value here still has to be
+determined, but in the end that is a user interface policy.
+
+
+Service naming
+==============
+
+Every service will have a name property that allows the user interface to
+display them directly. All names will be already converted into UTF-8. It
+derives from the netork details.
+
+In case of WiFi this will be the SSID value. The SSID is a binary array and
+will be converted into printable form. Unprintable characters are replaced
+with spaces.
+
+For Bluetooth the device alias is used. The alias is different since it
+can be overwritten by the user via the Bluetooth service. The identification
+is still done based on its address, but the display name might change. In
+most cases the alias is equal to the Bluetooth remote friendly name.
+
+For Ethernet device no name will be provided. The type property will indicate
+that this service is Ethernet and then it is up to the user interface to
+provide a proper localized name for it.
+
+
+Service states
+==============
+
+Every service can have multiple states that indicate what is currently
+going on with it. The choice to have multiple states instead of a simple
+connected yes/no value comes from the fact that it is important to let the
+user interface name if a service is in process of connecting/disconnecting.
+
+The basic state of every service is "idle". This means that this service
+is not in use at all at the moment. It also is not attempting to connect
+or do anything else.
+
+The "association" state indicates that this service tries to establish a
+low-level connection to the network. For example associating/connecting
+with a WiFi access point.
+
+With the "configuration" state the service indicates that it is trying
+to retrieve/configure IP settings.
+
+The "ready" state signals a successful connected device. This doesn't mean
+it has the default route, but basic IP operations will succeed.
+
+With the "disconnect" state a service indicates that it is going to terminate
+the current connection and will return to the "idle" state.
+
+In addition a "failure" state indicates a wrong behavior. It is similar to
+the "idle" state since the service is not connected.
+
+ +---------------+
+ | idle |<-------------------------------+
+ +---------------+ |
+ | |
+ | +-------------+ |
+ +----------------------| failure | |
+ | service.Connect() +-------------+ |
+ V A |
+ +---------------+ | |
+ | association |-----------------+ |
+ +---------------+ error | |
+ | | |
+ | success | |
+ V | |
+ +---------------+ | |
+ | configuration |-----------------+ |
+ +---------------+ error |
+ | |
+ | success |
+ V |
+ +---------------+ |
+ | ready | |
+ +---------------+ |
+ | |
+ | success |
+ | |
+ V |
+ +---------------+ |
+ | online |<----------------+ |
+ +---------------+ | |
+ | | |
+ | service.Disconnect() | |
+ V | |
+ +---------------+ | |
+ | disconnect |-----------------+ |
+ +---------------+ error |
+ | |
+ +------------------------------------------+
+
+The different states should no be used by the user interface to trigger
+advanced actions. The state transitions are provided for the sole purpose
+to give the user feedback on what is currently going on. Especially in
+cases where networks are flaky or DHCP servers take a long time these
+information are helpful for the user.
+
+Some services might require special authentication procedure like a web
+based confirmation. The LoginRequired property should be used to check
+for this.
+
+
+Application basics
+==================
+
+All applications should use D-Bus to communicate with Connection Manager. The
+main entry point is the manager object. Currently the manager object is
+located at "/", but this might change to allow full namespacing of the API
+in the future. The manager interface is documented in manager-api.txt and
+contains a set of global properties and methods.
+
+A simple way to retrieve all global properties looks like this:
+
+ bus = dbus.SystemBus()
+
+ manager = dbus.Interface(bus.get_object("net.connman", "/"),
+ "net.connman.Manager")
+
+ properties = manager.GetProperties()
+
+Changing a global property is also pretty simple. For example enabling the
+so called offline mode (aka flight mode) it is enough to just set that
+property:
+
+ manager.SetProperty("OfflineMode", dbus.Boolean(1))
+
+The manager object contains references to profiles, devices, services and
+connections. All these references represent other interfaces that allow
+detailed control of Connection Manager. The profiles and devices interfaces
+are more for advanced features and most applications don't need them at all.
+
+The services are represented as a list of object paths. Every of these object
+paths contains a service interface. A service is a global collection for
+Ethernet devices, WiFi networks, Bluetooth services etc. and all these
+different types are treated equally.
+
+Every local Ethernet card will show up as exactly one service. WiFi networks
+will be grouped by SSID, mode and security setting. Bluetooth PAN and DUN
+service will show up per remote device. This creates a simple list that can
+be directly displayed to the users since these are the exact details users
+should care about.
+
+ properties = manager.GetProperties()
+
+ for path in properties["Services"]:
+ service = dbus.Interface(bus.get_object("net.connman", path),
+ "net.connman.Service")
+
+ service_properties = service.GetProperties()
+
+The service interface is documented in service-api.txt and contains common
+properties valid for all services. It also contains method to connect or
+disconnect a specific service. This allows users to select a specific service.
+Connection Manager can also auto-connect services based on his policies or
+via external events (like plugging in an Ethernet cable).
+
+Connecting (or disconnecting) a specific service manually is as simple as
+just telling it to actually connect:
+
+ service.Connect() or service.Disconnect()
+
+It is possible to connect multiple services if the underlying technology
+allows it. For example it would be possible to connect to a WiFi network
+and a Bluetooth service at the same time. Trying to connect to a second WiFi
+network with the same WiFi hardware would result in an automatic disconnect
+of the currently connected network. Connection Manager handles all of this
+for the applications in the background. Trying to connect an Ethernet service
+will result in an error if no cable is plugged in. All connection attempts
+can fail for one reason or another. Application should be able to handle
+such errors and will also be notified of changes via signals.
+
+In future versions Connection Manager will interact with an agent to confirm
+certain transactions with the user. This functionality is currently not
+implemented.
+
+To monitor the current status of a service the state property can be used. It
+gives detailed information about the current progress.
+
+ properties = service.GetProperties()
+
+ print properties["State"]
+
+All state changes are also sent via the PropertyChanged signal on the
+service interface. This allows asynchronous monitoring without having to poll
+Connection Manager for changes.
diff --git a/doc/plugin-api.txt b/doc/plugin-api.txt
new file mode 100644
index 0000000..b39c043
--- /dev/null
+++ b/doc/plugin-api.txt
@@ -0,0 +1,24 @@
+Plugin programming interface
+****************************
+
+
+Plugin basics
+=============
+
+The Connection Manager supports plugins for various actions. The basic plugin
+contains of plugin description via CONNMAN_PLUGIN_DEFINE and also init/exit
+callbacks defined through that description.
+
+#include <connman/plugin.h>
+
+static int example_init(void)
+{
+ return 0;
+}
+
+static void example_exit(void)
+{
+}
+
+CONNMAN_PLUGIN_DEFINE(example, "Example plugin", CONNMAN_VERSION,
+ example_init, example_exit)
diff --git a/doc/service-api.txt b/doc/service-api.txt
new file mode 100644
index 0000000..b33eb6f
--- /dev/null
+++ b/doc/service-api.txt
@@ -0,0 +1,488 @@
+Service hierarchy
+=================
+
+Service net.connman
+Interface net.connman.Service
+Object path [variable prefix]/{service0,service1,...}
+
+Methods dict GetProperties() [deprecated]
+
+ Returns properties for the service object. See
+ the properties section for available properties.
+
+ Usage of this method is highly discouraged. Use
+ the Manager.GetServices() method instead.
+
+ Possible Errors: [service].Error.InvalidArguments
+
+ void SetProperty(string name, variant value)
+
+ Changes the value of the specified property. Only
+ properties that are listed as read-write are
+ changeable. On success a PropertyChanged signal
+ will be emitted.
+
+ Possible Errors: [service].Error.InvalidArguments
+ [service].Error.InvalidProperty
+
+ void ClearProperty(string name)
+
+ Clears the value of the specified property.
+
+ Possible Errors: [service].Error.InvalidArguments
+ [service].Error.InvalidProperty
+
+ void Connect()
+
+ Connect this service. It will attempt to connect
+ WiFi or Bluetooth services.
+
+ For Ethernet devices this method can only be used
+ if it has previously been disconnected. Otherwise
+ the plugging of a cable will trigger connecting
+ automatically. If no cable is plugged in this method
+ will fail.
+
+ This method call will only return in case of an
+ error or when the service is fully connected. So
+ setting a longer D-Bus timeout might be a really
+ good idea.
+
+ Possible Errors: [service].Error.InvalidArguments
+
+ void Disconnect()
+
+ Disconnect this service. If the service is not
+ connected an error message will be generated.
+
+ On Ethernet devices this will disconnect the IP
+ details from the service. It will not magically
+ unplug the cable. When no cable is plugged in this
+ method will fail.
+
+ This method can also be used to abort a previous
+ connectiong attempt via the Connect method.
+
+ Possible Errors: [service].Error.InvalidArguments
+
+ void Remove()
+
+ A successfully connected service with Favorite=true
+ can be removed this way. If it is connected, it will
+ be automatically disconnected first.
+
+ If the service requires a passphrase it will be
+ cleared and forgotten when removing.
+
+ This is similar to setting the Favorite property
+ to false, but that is currently not supported.
+
+ In the case a connection attempt failed and the
+ service is in the State=failure, this method can
+ also be used to reset the service.
+
+ Calling this method on Ethernet devices will cause
+ an error message. It is not possible to remove these
+ kind of devices.
+
+ Possible Errors: [service].Error.InvalidArguments
+
+ void MoveBefore(object service)
+
+ If a service has been used before, this allows a
+ reorder of the favorite services.
+
+ The target service object must be part of this
+ profile. Moving between profiles is not supported.
+
+ Possible Errors: [service].Error.InvalidArguments
+
+ void MoveAfter(object service)
+
+ If a service has been used before, this allows a
+ reorder of the favorite services.
+
+ The target service object must be part of this
+ profile. Moving between profiles is not supported.
+
+ Possible Errors: [service].Error.InvalidArguments
+
+ void ResetCounters() [experimental]
+
+ Reset the counter statistics.
+
+ Possible Errors: None
+
+Signals PropertyChanged(string name, variant value)
+
+ This signal indicates a changed value of the given
+ property.
+
+Properties string State [readonly]
+
+ The service state information.
+
+ Valid states are "idle", "failure", "association",
+ "configuration", "ready", "disconnect" and "online".
+
+ The "ready" state signals a successfully
+ connected device. "online" signals that an
+ Internet connection is available and has been
+ verified.
+
+ See doc/overview-api.txt for more information about
+ state transitions.
+
+ string Error [readonly]
+
+ The service error status details.
+
+ When error occur during connection or disconnection
+ the detailed information is represented in this
+ property to help the user interface to present the
+ user with alternate options.
+
+ This property is only valid when the service is in
+ the "failure" state. Otherwise it might be empty or
+ not present at all.
+
+ Current defined error code is "dhcp-failed".
+
+ string Name [readonly]
+
+ The service name (for example "Wireless" etc.)
+
+ This name can be used for directly displaying it in
+ the application. It has pure informational purpose
+ and no attempt should be made to translate it.
+
+ For Ethernet devices and hidden WiFi networks this
+ property is not present.
+
+ string Type [readonly]
+
+ The service type (for example "ethernet", "wifi" etc.)
+
+ This information should only be used to determine
+ advanced properties or showing the correct icon
+ to the user.
+
+ Together with a missing Name property, this can
+ be used to identify hidden WiFi networks.
+
+ array{string} Security [readonly]
+
+ If the service type is WiFi, then this property is
+ present and contains the list of security methods
+ or key management settings.
+
+ Possible values are "none", "wep", "psk", "ieee8021x"
+ and also "wps".
+
+ This property might be only present for WiFi
+ services.
+
+ uint8 Strength [readonly]
+
+ Indicates the signal strength of the service. This
+ is a normalized value between 0 and 100.
+
+ This property will not be present for Ethernet
+ devices.
+
+ boolean Favorite [readonly]
+
+ Will be true if a cable is plugged in or the user
+ selected and successfully connected to this service.
+
+ This value is automatically changed and to revert
+ it back to false the Remove() method needs to be
+ used.
+
+ boolean Immutable [readonly]
+
+ This value will be set to true if the service is
+ configured externally via a configuration file.
+
+ The only valid operation are Connect() and of
+ course Disconnect(). The Remove() method will
+ result in an error.
+
+ boolean AutoConnect [readwrite]
+
+ If set to true, this service will auto-connect
+ when no other connection is available.
+
+ The service won't auto-connect while roaming.
+
+ For favorite services it is possible to change
+ this value to prevent or permit automatic
+ connection attempts.
+
+ boolean Roaming [readonly]
+
+ This property indicates if this service is roaming.
+
+ In the case of Cellular services this normally
+ indicates connections to a foreign provider when
+ traveling abroad.
+
+ array{string} Nameservers [readonly]
+
+ The list of currently active nameservers for this
+ service. If the server is not in READY or ONLINE
+ state than this list will be empty.
+
+ Global nameservers are automatically added to this
+ list. The array represents a sorted list of the
+ current nameservers. The first one has the highest
+ priority and is used by default.
+
+ When using DHCP this array represents the nameservers
+ provided by the network. In case of manual settings,
+ the ones from Nameservers.Configuration are used.
+
+ array{string} Nameservers.Configuration [readwrite]
+
+ The list of manually configured domain name
+ servers. Some cellular networks don't provide
+ correct name servers and this allows for an
+ override.
+
+ This array is sorted by priority and the first
+ entry in the list represents the nameserver with
+ the highest priority.
+
+ When using manual configuration and no global
+ nameservers are configured, then it is useful
+ to configure this setting.
+
+ Changes to the domain name servers can be done
+ at any time. It will not cause a disconnect of
+ the service. However there might be small window
+ where name resolution might fail.
+
+ array{string} Timeservers [readonly]
+
+ The list of currently active timeservers for this
+ service. If the server is not in READY or ONLINE
+ state than this list will be empty.
+
+ array{string} Timeservers.Configuration [readwrite]
+
+ The list of manually configured time servers.
+
+ The first entry in the list represents the
+ timeserver with the highest priority.
+
+ When using manual configuration this setting
+ is useful to override all the other timeserver
+ settings. This is service specific, hence only
+ the values for the default service are used.
+
+ Changes to this property will result in restart
+ of NTP query.
+
+ array{string} Domains [readonly]
+
+ The list of currently used search domains taken
+ from Domains.Configurations if set, otherwise a
+ domain name if provided by DHCP or VPNs.
+
+ array{string} Domains.Configuration [readwrite]
+
+ The list of manually configured search domains.
+
+ dict IPv4 [readonly]
+
+ string Method [readonly]
+
+ Possible values are "dhcp", "manual"
+ and "off".
+
+ The value "fixed" indicates an IP address
+ that can not be modified. For example
+ cellular networks return fixed information.
+
+ string Address [readonly]
+
+ The current configured IPv4 address.
+
+ string Netmask [readonly]
+
+ The current configured IPv4 netmask.
+
+ string Gateway [readonly]
+
+ The current configured IPv4 gateway.
+
+ dict IPv4.Configuration [readwrite]
+
+ Same values as IPv4 property. The IPv4 represents
+ the actual system configuration while this allows
+ user configuration.
+
+ Changing these settings will cause a state change
+ of the service. The service will become unavailable
+ until the new configuration has been successfully
+ installed.
+
+ dict IPv6 [readonly]
+
+ string Method [readonly]
+
+ Possible values are "auto", "manual", "6to4"
+ and "off".
+
+ The value "fixed" indicates an IP address
+ that can not be modified. For example
+ cellular networks return fixed information.
+ The value "6to4" is returned if 6to4 tunnel
+ is created by connman. The tunnel can only be
+ created if method was set to "auto" by the
+ user. User cannot set the method to "6to4".
+
+ string Address [readonly]
+
+ The current configured IPv6 address.
+
+ uint8 PrefixLength [readonly]
+
+ The prefix length of the IPv6 address.
+
+ string Gateway [readonly]
+
+ The current configured IPv6 gateway.
+
+ string Privacy [readonly]
+
+ Enable or disable IPv6 privacy extension
+ that is described in RFC 4941. The value
+ has only meaning if Method is set to "auto".
+
+ Value "disabled" means that privacy extension
+ is disabled and normal autoconf addresses are
+ used.
+
+ Value "enabled" means that privacy extension is
+ enabled and system prefers to use public
+ addresses over temporary addresses.
+
+ Value "prefered" means that privacy extension is
+ enabled and system prefers temporary addresses
+ over public addresses.
+
+ Default value is "disabled".
+
+ dict IPv6.Configuration [readwrite]
+
+ Same values as IPv6 property. The IPv6 represents
+ the actual system configuration while this allows
+ user configuration.
+
+ Changing these settings will cause a state change
+ of the service. The service will become unavailable
+ until the new configuration has been successfully
+ installed.
+
+ dict Proxy [readonly]
+
+ string Method [readonly]
+
+ Possible values are "direct", "auto" and
+ "manual".
+
+ In case of "auto" method, the URL file can be
+ provided unless you want to let DHCP/WPAD
+ auto-discover to be tried. In such case if DHCP
+ and WPAD auto-discover methods fails then
+ method will be "direct".
+
+ In case of "direct" no additional information
+ are provided. For the "manual" method the
+ Servers have to be set, Excludes is optional.
+
+ string URL [readonly]
+
+ Automatic proxy configuration URL. Used by
+ "auto" method.
+
+ array{string} Servers [readonly]
+
+ Used when "manual" method is set.
+
+ List of proxy URIs. The URI without a protocol
+ will be interpreted as the generic proxy URI.
+ All others will target a specific protocol and
+ only once.
+
+ Example for generic proxy server entry would
+ be like this: "server.example.com:911".
+
+ array{string} Excludes [readonly]
+
+ Used when "manual" method is set.
+
+ List of hosts which can be accessed directly.
+
+ dict Proxy.Configuration [readwrite]
+
+ Same values as Proxy property. The Proxy represents
+ the actual system configuration while this allows
+ user configuration.
+
+ If "auto" method is set with an empty URL, then
+ DHCP/WPAD auto-discover will be tried. Otherwise the
+ specified URL will be used.
+
+ dict Provider [readonly]
+
+ string Host [readonly]
+
+ VPN host IP.
+
+ string Domain [readonly]
+
+ VPN Domain.
+
+ string Name [readonly]
+
+ VPN provider Name.
+
+ string Type [readonly]
+
+ VPN provider type.
+
+ dict Ethernet [readonly]
+
+ string Method [readonly]
+
+ Possible values are "auto" and "manual".
+
+ string Interface [readonly]
+
+ Interface name (for example eth0).
+
+ string Address [readonly]
+
+ Ethernet device address (MAC address).
+
+ uint16 MTU [readonly]
+
+ The Ethernet MTU (default is 1500).
+
+ uint16 Speed [readonly]
+
+ Selected speed of the line.
+
+ This information might not always be
+ available.
+
+ string Duplex [readonly]
+
+ Selected duplex settings of the line.
+
+ Possible values are "half" and "full".
+
+ This information might not always be
+ available.
diff --git a/doc/session-api.txt b/doc/session-api.txt
new file mode 100644
index 0000000..a0a328d
--- /dev/null
+++ b/doc/session-api.txt
@@ -0,0 +1,184 @@
+Service unique name
+Interface net.connman.Notification
+Object path freely definable
+
+Methods void Release()
+
+ This method gets called when the service daemon
+ unregisters the session. A counter can use it to do
+ cleanup tasks. There is no need to unregister the
+ session, because when this method gets called it has
+ already been unregistered.
+
+ void Update(dict settings)
+
+ Sends an update of changed settings. Only settings
+ that are changed will be included.
+
+ Initially on every session creation this method is
+ called once to inform about the current settings.
+
+
+Service net.connman
+Interface net.connman.Session
+Object path variable
+
+Methods void Destroy()
+
+ Close the current session. This is similar to
+ DestroySession method on the manager interface. It
+ is just provided for convenience depending on how
+ the application wants to track the session.
+
+ void Connect()
+
+ If not connected, then attempt to connect this
+ session.
+
+ The usage of this method depends a little bit on
+ the model of the application. Some application
+ should not try to call Connect on any session at
+ all. They should just monitor if it becomes online
+ or gets back offline.
+
+ Others might require an active connection right now.
+ So for example email notification should only check
+ for new emails when a connection is available. However
+ if the user presses the button for get email or wants
+ to send an email it should request to get online with
+ this method.
+
+ Depending on the bearer settings the current service
+ is used or a new service will be connected.
+
+ This method returns immediately after it has been
+ called. The application is informed through the update
+ notification about the state of the session.
+
+ It is also not guaranteed that a session stays online
+ after this method call. It can be taken offline at any
+ time. This might happen because of idle timeouts or
+ other reasons.
+
+ It is safe to call this method multiple times. The
+ actual usage will be sorted out for the application.
+
+ void Disconnect()
+
+ This method indicates that the current session does
+ not need a connection anymore.
+
+ This method returns immediately. The application is
+ informed through the update notification about the
+ state of the session.
+
+ void Change(string name, variant value)
+
+ Change the value of certain settings. Not all
+ settings can be changed. Normally this should not
+ be needed or an extra session should be created.
+ However in some cases it makes sense to change
+ a value and trigger different behavior.
+
+ A change of a setting will cause an update notification
+ to be sent. Some changes might cause the session to
+ be moved to offline state.
+
+Settings string State [readonly]
+
+ This indicates if the connection is disconnected,
+ connected or online. It is updated according to the
+ selected ConnectionType. The session will not be
+ in a useful shape (i.e.: providing a network connection
+ to the owner) until its State gets updated to connected
+ and/or online.
+
+ This maps to the useful port of the service state.
+ And it is only valid for the selected bearer
+ configuration. Otherwise it will be reported as
+ disconnected even if connected services are present.
+
+ In addition the State settings notification might
+ not happen right away. Notifications of this state
+ can be delayed based on the speed of the bearer. It
+ is done to avoid congestion on bearers like cellular
+ etc.
+
+ string Name [readonly]
+
+ The Service name to which the system is connected.
+ It should only be used for displaying it in the UI
+ and not for getting hold on session object.
+
+ string Bearer [readonly]
+
+ This indicates the current bearer that is used
+ for this session. Or an empty string if no bearer
+ if available.
+
+ string Interface [readonly]
+
+ Interface name used by the service object to connect.
+ This name can be used for SO_BINDTODEVICE in the
+ application.
+
+ dict IPv4 [readonly]
+
+ Current IPv4 configuration.
+
+ dict IPv6 [readonly]
+
+ Current IPv6 configuration.
+
+ array{string} AllowedBearers [readwrite]
+
+ A list of bearers that can be used for this session.
+ In general this list should be empty to indicate that
+ any bearer is acceptable.
+
+ The order of the entries in AllowedBearers matters.
+ The services are sorted in the order of the bearer
+ entries in this list.
+
+ Also "*" matches any bearer. This is usefull to prefer
+ certain bearers such as 'wifi' with a fallback to any
+ other available bearer.
+
+ Invalid bearer names will be ignored and removed
+ from the list. And empty AllowedBearers will
+ not match to any bearer, therefore the session
+ will never go online.
+
+ When a session is created and the provided settings
+ dictionary does not contain AllowedBearers, a default
+ session with "*" will be created.
+
+ string ConnectionType [readwrite]
+
+ This is used to indicate which connection is requested
+ from the session. The state of the session will be
+ updated accordingly. Values can be 'local',
+ 'internet' or 'any'.
+
+ 'local' means the session requests to be connected,
+ but does not require specifically to be online.
+ Therefore State property will be set to 'connected' if
+ underlying service gets ready and/or online.
+
+ 'online' means the session requests to be connected,
+ and online. State property will never get 'connected'
+ but instead will switch to 'online' if underlying
+ service gets online.
+
+ 'any' means either 'local' or 'internet'.
+
+ Invalid values will be ignored and removed. An
+ empty ConnectionType is an invalid configuration.
+
+ When a session is created and the provided settings
+ dictionary does not contain ConnectionType, a default
+ session with 'any' will be created.
+
+ (This setting will be removed when the unique process
+ identifaction problem is solved.)
+
diff --git a/doc/session-overview.txt b/doc/session-overview.txt
new file mode 100644
index 0000000..d42c6d1
--- /dev/null
+++ b/doc/session-overview.txt
@@ -0,0 +1,78 @@
+Session API
+***********
+
+
+Connection management algorithm basics
+======================================
+
+When a session is created, a sorted list of services is added to the
+session. The services are filtered and sorted according AllowedBearers.
+
+There are three triggers which lead to evaluate the connect
+algorithm:
+
+ - Session.Connect()
+ - Offline
+
+Connect algorithm:
+
+ Session.Connect()
+ |
+ +------+-------+
+ +-----+ECall Session ?+-----+
+ Yes| +--------------+ |No
+ | |
+ Connect to +--------------+
+ first available +---+AvoidHandover?+---+
+ Service | +--------------+ |
+ Yes| |No
+ +----------------+ |
+ +---+In service_list +---+ |
+ Yes| |and online? | |No |
+ | +----------------+ | |
+ | | |
+ Take that one Take first in
+ the service list
+
+Disconnect algorithm
+
+ - Session.Disconnect()
+
+Disconnect algorithm:
+
+ Session.Disconnect()
+ |
+ +--- Session.Change()
+ |
++-----------------+ Yes
+|service not used +-------------+
+|by other session?| |
++------.----------+ |
+ |No |
+ | |
+ Service.Disconnect() Do nothing
+
+Session.Disconnect() will be blocked whenever a ongoing
+emergency call is active.
+
+
+Session States and Transitions
+==============================
+
+There is only one state which is called Free Ride.
+
+The Free Ride state means that a session will go online if a matching
+service goes online without calling Service.Connect() itself. The idea
+behind this is that a session doesn't request a connection for itself
+instead waits until another session actively requires to go online.
+This is comparable to piggy-backing.
+
+Connnect()
+ +------+
+ | v
++------------+
+| Free Ride |
++------------+
+ | ^
+ +-----+
+ Disconnect()
diff --git a/doc/technology-api.txt b/doc/technology-api.txt
new file mode 100644
index 0000000..9fe66b3
--- /dev/null
+++ b/doc/technology-api.txt
@@ -0,0 +1,103 @@
+Technology hierarchy
+====================
+
+Service net.connman
+Interface net.connman.Technology
+Object path [variable prefix]/{technology0,technology1,...}
+
+Methods dict GetProperties() [deprecated]
+
+ Returns properties for the technology object. See
+ the properties section for available properties.
+
+ Usage of this method is highly discouraged. Use
+ the Manager.GetTechnologies() method instead.
+
+ Possible Errors: [service].Error.InvalidArguments
+
+ void SetProperty(string name, variant value)
+
+ Changes the value of the specified property. Only
+ properties that are listed as read-write are
+ changeable. On success a PropertyChanged signal
+ will be emitted.
+
+ Possible Errors: [service].Error.InvalidArguments
+ [service].Error.InvalidProperty
+
+ void Scan()
+
+ Trigger a scan for this specific technology. The
+ method call will return when a scan has been
+ finished and results are available. So setting
+ a longer D-Bus timeout might be a really good
+ idea.
+
+ Results will be signaled via the ServicesChanged
+ signal from the manager interface.
+
+Signals PropertyChanged(string name, variant value)
+
+ This signal indicates a changed value of the given
+ property.
+
+Properties boolean Powered [readwrite]
+
+ Boolean representing the power state of the
+ technology. False means that the technology is
+ off (and is available RF-Killed) while True means
+ that the technology is enabled.
+
+ boolean Connected [readonly]
+
+ Boolean representing if a technolgy is connected.
+
+ This is just a convience property for allowing the
+ UI to easily show if this technolgy has an active
+ connection or not.
+
+ If this property is True it means that at least one
+ service of this technology is in ready state.
+
+ string Name [readonly]
+
+ Name of this technology.
+
+ string Type [readonly]
+
+ The technology type (for example "ethernet" etc.)
+
+ This information should only be used to determine
+ advanced properties or showing the correct icon
+ to the user.
+
+ boolean Tethering [readwrite]
+
+ This option allows to enable or disable the support
+ for tethering. When tethering is enabled then the
+ default service is bridged to all clients connected
+ through the technology.
+
+ string TetheringIdentifier [readwrite]
+
+ The tethering broadcasted identifier.
+
+ This property is only valid for the WiFi technology,
+ and is then mapped to the WiFi AP SSID clients will
+ have to join in order to gain internet connectivity.
+
+ string TetheringPassphrase [readwrite]
+
+ The tethering connection passphrase.
+
+ This property is only valid for the WiFi technology,
+ and is then mapped to the WPA pre-shared key clients
+ will have to use in order to establish a connection.
+
+ uint32 IdleTimeout [readwrite] [experimental]
+
+ If the technology is idle for given period then it
+ will go offline if no Service with this technology
+ has AutoConnect set to True.
+
+ If the timeout is 0, this feature is disabled.
diff --git a/doc/valgrind.suppressions b/doc/valgrind.suppressions
new file mode 100644
index 0000000..c1c7bca
--- /dev/null
+++ b/doc/valgrind.suppressions
@@ -0,0 +1,235 @@
+{
+ <syslog error>
+ Memcheck:Cond
+ obj:/lib/libc-*.so
+ ...
+ fun:localtime_r
+ fun:__vsyslog_chk
+ fun:__syslog_chk
+ fun:__connman_log_init
+ ...
+}
+{
+ <iconv open>
+ Memcheck:Addr4
+ obj:/lib/libc-*.so
+ obj:/lib/libglib-2.0.so*
+ fun:g_iconv_open
+ ...
+ fun:g_convert
+ fun:g_locale_to_utf8
+ fun:g_strerror
+ fun:g_key_file_load_from_file
+ ...
+}
+{
+ <ioctl ADDRT/DELRT>
+ Memcheck:Param
+ ioctl(SIOCADDRT/DELRT)
+ obj:/lib/ld-*.so
+ ...
+}
+{
+ <g_main_loop>
+ Memcheck:Leak
+ fun:memalign
+ ...
+ fun:g_slice_alloc
+ ...
+ fun:g_main_loop_new
+ ...
+}
+{
+ <g_option_context_parse>
+ Memcheck:Leak
+ ...
+ fun:g_slice_alloc
+ ...
+ fun:g_option_context_parse
+ ...
+}
+{
+ <g_key_file_load_from_data>
+ Memcheck:Leak
+ ...
+ fun:g_slice_alloc
+ ...
+ fun:g_key_file_load_from_data
+ ...
+}
+{
+ <g_key_file_new 1>
+ Memcheck:Leak
+ ...
+ fun:g_slice_alloc
+ ...
+ fun:g_key_file_new
+ ...
+}
+{
+ <g_key_file_new 2>
+ Memcheck:Leak
+ fun:*alloc
+ ...
+ fun:g_key_file_new
+ fun:main
+}
+{
+ <connman plugin cleanup>
+ Memcheck:Leak
+ ...
+ fun:__connman_plugin_cleanup
+ ...
+}
+{
+ <cmd line option parsing>
+ Memcheck:Leak
+ fun:malloc
+ fun:g_malloc
+ fun:g_strdup
+ fun:g_set_prgname
+ fun:g_option_context_parse
+ fun:main
+}
+{
+ <dbus system bus setup 1>
+ Memcheck:Leak
+ ...
+ fun:dbus_malloc*
+ ...
+ fun:g_dbus_setup_bus
+ fun:main
+}
+{
+ <dbus system bus setup 2>
+ Memcheck:Leak
+ ...
+ fun:g_malloc*
+ ...
+ fun:dbus_connection_set_watch_functions
+ fun:setup_bus
+ ...
+}
+{
+ <key file get charset>
+ Memcheck:Leak
+ ...
+ fun:g_*alloc*
+ ...
+ fun:g_strerror
+ fun:g_key_file_load_from_file
+ fun:main
+}
+{
+ <dbus disconnect func set>
+ Memcheck:Leak
+ ...
+ fun:filter_data_get
+ fun:g_dbus_add_signal_watch
+ fun:g_dbus_set_disconnect_function
+ fun:main
+}
+{
+ <plugin dlopen>
+ Memcheck:Leak
+ ...
+ fun:dlopen
+ fun:__connman_plugin_init
+ fun:main
+}
+{
+ <dbus system bus setup 3>
+ Memcheck:Leak
+ ...
+ fun:dbus_malloc0
+ ...
+ fun:dbus_parse_address
+ ...
+ fun:g_dbus_setup_bus
+ fun:main
+}
+{
+ <libdbus internals 1>
+ Memcheck:Leak
+ fun:*malloc
+ ...
+ obj:/lib/libdbus-1.so.3.5.3
+}
+{
+ <dbus system bus setup 4>
+ Memcheck:Leak
+ fun:*alloc
+ ...
+ fun:dbus_*alloc*
+ ...
+ fun:g_dbus_setup_bus
+ fun:main
+}
+{
+ <dbus system bus setup 5>
+ Memcheck:Leak
+ fun:calloc
+ fun:g_malloc0
+ ...
+ fun:g_dbus_set_disconnect_function
+ fun:main
+}
+{
+ <dbus bus remove match>
+ Memcheck:Leak
+ fun:malloc
+ fun:g_malloc
+ fun:g_source_set_callback
+ fun:g_timeout_add_full
+ fun:g_timeout_add
+ ...
+ fun:dbus_pending_call_block
+ fun:dbus_connection_send_with_reply_and_block
+ ...
+ fun:dbus_bus_remove_match
+}
+{
+ <g_main_loop_run/new>
+ Memcheck:Leak
+ fun:*alloc
+ ...
+ fun:g_main_loop_*
+ fun:main
+}
+{
+ <g_main_context_dispatch>
+ Memcheck:Leak
+ fun:*alloc
+ ...
+ fun:g_main_context_dispatch
+}
+{
+ <libdbus internals 2>
+ Memcheck:Leak
+ fun:realloc
+ fun:dbus_realloc
+ ...
+ fun:dbus_message_set_reply_serial
+ fun:dbus_message_new_error
+ ...
+}
+{
+ <libdbus internals 3>
+ Memcheck:Leak
+ fun:realloc
+ fun:dbus_realloc
+ ...
+ fun:dbus_message_new_signal
+ ...
+}
+{
+ <dbus_bus_register>
+ Memcheck:Leak
+ fun:malloc
+ fun:realloc
+ fun:dbus_realloc
+ ...
+ fun:dbus_pending_call_block
+ fun:dbus_connection_send_with_reply_and_block
+ fun:dbus_bus_register
+}
diff --git a/doc/version.xml.in b/doc/version.xml.in
new file mode 100644
index 0000000..d78bda9
--- /dev/null
+++ b/doc/version.xml.in
@@ -0,0 +1 @@
+@VERSION@
diff --git a/doc/vpn-agent-api.txt b/doc/vpn-agent-api.txt
new file mode 100644
index 0000000..bdef9f3
--- /dev/null
+++ b/doc/vpn-agent-api.txt
@@ -0,0 +1,137 @@
+Agent hierarchy
+===============
+
+Service unique name
+Interface net.connman.vpn.Agent
+Object path freely definable
+
+Methods void Release()
+
+ This method gets called when the service daemon
+ unregisters the agent. An agent can use it to do
+ cleanup tasks. There is no need to unregister the
+ agent, because when this method gets called it has
+ already been unregistered.
+
+ void ReportError(object service, string error)
+
+ This method gets called when an error has to be
+ reported to the user.
+
+ A special return value can be used to trigger a
+ retry of the failed transaction.
+
+ Possible Errors: net.connman.vpn.Agent.Error.Retry
+
+ dict RequestInput(object service, dict fields)
+
+ This method gets called when trying to connect to
+ a service and some extra input is required. For
+ example a password or username.
+
+ The return value should be a dictionary where the
+ keys are the field names and the values are the
+ actual fields. Alternatively an error indicating that
+ the request got canceled can be returned.
+
+ Most common return field names are "Username" and of
+ course "Password".
+
+ The dictionary arguments contains field names with
+ their input parameters.
+
+ Possible Errors: net.connman.vpn.Agent.Error.Canceled
+
+ void Cancel()
+
+ This method gets called to indicate that the agent
+ request failed before a reply was returned.
+
+Fields string Username
+
+ Username for authentication. This field will be
+ requested when connecting to L2TP and PPTP.
+
+ string Password
+
+ Password for authentication.
+
+ boolean SaveCredentials
+
+ Tells if the user wants the user credentials
+ be saved by connman-vpnd.
+
+ string Host
+
+ End point of this VPN link i.e., the VPN gateway
+ we are trying to connect to.
+
+ string Name
+
+ Name of the VPN connection we are trying to connect to.
+
+ string OpenConnect.Cookie
+
+ Return the OpenConnect cookie value that is used to
+ authenticate the user and is specific to this user.
+
+Arguments string Type
+
+ Contains the type of a field. For example "password",
+ "response", "boolean" or plain "string".
+
+ string Requirement
+
+ Contains the requirement option. Valid values are
+ "mandatory", "optional", "alternate" or
+ "informational".
+
+ The "alternate" value specifies that this field can be
+ returned as an alternative to another one.
+
+ All "mandatory" fields must be returned, while the
+ "optional" can be returned if available.
+
+ Nothing needs to be returned for "informational", as it
+ is here only to provide an information so a value is
+ attached to it.
+
+ array{string} Alternates
+
+ Contains the list of alternate field names this
+ field can be represented by.
+
+ string Value
+
+ Contains data as a string, relatively to an
+ "informational" argument.
+
+Examples Requesting a username and password for L2TP network
+
+ RequestInput("/vpn1",
+ { "Username" : { "Type" : "string",
+ "Requirement" : "mandatory"
+ } }
+ { "Password" : { "Type" : "password",
+ "Requirement" : "mandatory"
+ } }
+ { "SaveCredentials" : { "Type" : "boolean",
+ "Requirement" : "optional"
+ }
+ }
+ ==> { "Username" : "foo", "Password" : "secret123",
+ "SaveCredentials" : true }
+
+ Requesting a OpenConnect cookie
+
+ RequestInput("/vpn2",
+ { "OpenConnect.Cookie" : { "Type" : "string",
+ "Requirement" : "mandatory"
+ } }
+ { "Host" : { "Type" : "string",
+ "Requirement" : "informational"
+ } }
+ { "Name" : { "Type" : "string",
+ "Requirement" : "informational"
+ } }
+ ==> { "OpenConnect.Cookie" : "0123456@adfsf@asasdf" }
diff --git a/doc/vpn-connection-api.txt b/doc/vpn-connection-api.txt
new file mode 100644
index 0000000..9faed8a
--- /dev/null
+++ b/doc/vpn-connection-api.txt
@@ -0,0 +1,166 @@
+vpn connection
+==============
+
+Service net.connman.vpn
+Interface net.connman.vpn.Connection
+Object path [variable prefix]/{connection0,connection1,...}
+
+Method void SetProperty(string name, variant value) [experimental]
+
+ Changes the value of the specified property. Only
+ properties that are listed as read-write are
+ changeable. On success a PropertyChanged signal
+ will be emitted.
+
+ Possible Errors: [connection].Error.InvalidArguments
+ [connection].Error.InvalidProperty
+
+ void ClearProperty(string name) [experimental]
+
+ Clears the value of the specified property.
+
+ Possible Errors: [connection].Error.InvalidArguments
+ [connection].Error.InvalidProperty
+
+ void Connect() [experimental]
+
+ Connect this VPN connection. It will attempt to connect
+ to the VPN connection. The Connect() will wait until
+ the connection is created or there is an error. The
+ error description is returned in dbus error.
+
+ Possible Errors: [connection].Error.InvalidArguments
+ [connection].Error.InProgress
+
+ void Disconnect() [experimental]
+
+ Disconnect this VPN connection. If the connection is
+ not connected an error message will be generated.
+
+ Possible Errors: [connection].Error.InvalidArguments
+
+Signals PropertyChanged(string name, variant value) [experimental]
+
+ This signal indicates a changed value of the given
+ property.
+
+Properties string State [readonly]
+
+ The connection state information.
+
+ Valid states are "idle", "failure", "configuration",
+ "ready", "disconnect".
+
+ string Type [readonly]
+
+ The VPN type (for example "openvpn", "vpnc" etc.)
+
+ string Name [readonly]
+
+ The VPN name.
+
+ string Domain [readonly]
+
+ The domain name used behind the VPN connection.
+ This is optional for most VPN technologies.
+
+ string Host [readonly]
+
+ The VPN host (server) address.
+
+ int Index [readonly]
+
+ The index of the VPN network tunneling interface.
+ If there is no tunneling device, then this value
+ is not returned.
+
+ dict IPv4 [readonly]
+
+ string Address
+
+ The current configured IPv4 address.
+
+ string Netmask
+
+ The current configured IPv4 netmask.
+
+ string Gateway
+
+ The current configured IPv4 gateway.
+
+ string Peer
+
+ The current configured VPN tunnel endpoint
+ IPv4 address.
+
+ dict IPv6 [readonly]
+
+ string Address
+
+ The current configured IPv6 address.
+
+ string PrefixLength
+
+ The prefix length of the IPv6 address.
+
+ string Gateway
+
+ The current configured IPv6 gateway.
+
+ string Peer
+
+ The current configured VPN tunnel endpoint
+ IPv6 address.
+
+ array{string} Nameservers [readonly]
+
+ The list of nameservers set by VPN.
+
+ array{dict} UserRoutes [readwrite]
+
+ int ProtocolFamily
+
+ Protocol family of the route. Set to 4
+ if IPv4 and 6 if IPv6 route.
+
+ string Network
+
+ The network part of the route.
+
+ string Netmask
+
+ The netmask of the route.
+
+ string Gateway
+
+ Gateway address of the route.
+
+ The list of currently active user activated
+ routes.
+
+ array{dict} ServerRoutes [readonly]
+
+ int ProtocolFamily
+
+ Protocol family of the route. Set to 4
+ if IPv4 and 6 if IPv6 route.
+
+ string Network
+
+ The network part of the route.
+
+ string Netmask
+
+ The netmask of the route.
+
+ string Gateway
+
+ Gateway address of the route.
+
+ The VPN server activated route. These routes
+ are pushed to connman by VPN server.
+
+ There can be other properties also but as the VPN
+ technologies are so different, they have different
+ kind of options that they need, so not all options
+ are mentioned in this document.
diff --git a/doc/vpn-manager-api.txt b/doc/vpn-manager-api.txt
new file mode 100644
index 0000000..66c5bee
--- /dev/null
+++ b/doc/vpn-manager-api.txt
@@ -0,0 +1,50 @@
+vpn manager
+===========
+
+Service net.connman.vpn
+Interface net.connman.vpn.Manager
+Object path /
+
+Method object Create(dict settings) [experimental]
+
+ Create a new VPN connection and configuration using
+ the supplied settings.
+
+ void Remove(object vpn) [experimental]
+
+ Remove the previously created VPN configuration.
+
+ array{object,dict} GetConnections() [experimental]
+
+ Returns a list of tuples with VPN connection object
+ path and dictionary of their properties.
+
+ Possible Errors: [manager].Error.InvalidArguments
+
+ void RegisterAgent(object path) [experimental]
+
+ Register new agent for handling user requests.
+
+ Possible Errors: [manager].Error.InvalidArguments
+
+ void UnregisterAgent(object path) [experimental]
+
+ Unregister an existing agent.
+
+ Possible Errors: [manager].Error.InvalidArguments
+
+Signals ConnectionAdded(object path, dict properties) [experimental]
+
+ Signal that is sent when a new VPN connection
+ is added.
+
+ It contains the object path of the VPN connection
+ and also its properties.
+
+ ConnectionRemoved(object path) [experimental]
+
+ Signal that is sent when a VPN connection
+ has been removed.
+
+ The object path is no longer accessible after this
+ signal and only emitted for reference.
diff --git a/doc/vpn-overview.txt b/doc/vpn-overview.txt
new file mode 100644
index 0000000..42b6e94
--- /dev/null
+++ b/doc/vpn-overview.txt
@@ -0,0 +1,60 @@
+VPN daemon overview
+*******************
+
+
+Manager interface
+=================
+
+Manager interface described in vpn-manager-api.txt is to be used
+by both the connectivity UI and by ConnMan. The Create(),
+Remove(), RegisterAgent() and UnregisterAgent() functions are for
+UI usage. The GetConnections() method and ConnectionAdded() and
+ConnectionRemoved() signals are for ConnMan VPN plugin to use.
+
+The UI should use the methods like this:
+- Ask VPN properties (like certs, usernames etc) from the user.
+- Call Manager.Create() to create a VPN connection (note that
+ the system does not yet try to connect to VPN at this point)
+- Register an agent to vpnd so that vpnd can ask any extra
+ parameters etc from the user if needed.
+- If the user wants to connect to VPN gateway, then the
+ connection attempt should be done in ConnMan side as
+ there will be a service created there.
+- If the user wishes to remove the VPN configuration, the UI
+ can call the Manager.Remove() which removes the VPN connection.
+ If the VPN was in use, the VPN connection is also disconnected.
+- When UI is terminated, the UI should call the UnregisterAgent()
+
+The ConnMan calls VPN daemon like this:
+- There is a VPN plugin which at startup starts to listen the
+ ConnectionAdded() and ConnectionRemoved() signals.
+- The VPN plugin will call GetConnections() in order to get
+ available VPN connections. It will then create a provider service
+ for each VPN connection that is returned.
+- User can then connect to the VPN by calling the service Connect()
+ method
+- The existing ConnMan Manager.ConnectProvider() interface can still
+ work by calling vpn.Manager.Create() and then call vpn.Connection.Connect()
+ but this ConnectProvider() interface will be deprecated at some
+ point.
+
+
+
+Connection interface
+====================
+
+The Manager.Create() will return the object path of the created
+vpn.Connection object and place it in idle state. Note that
+vpn.Connection.PropertyChanged signal is not called when Connection
+object is created because the same parameters are returned via
+vpn.Manager.ConnectionAdded() signal.
+The vpn.Connection object can be connected using the Connect() method
+and disconnected by calling Disconnect() method. When the connection
+is established (meaning VPN client has managed to create a connection
+to VPN server), then State property is set to "ready" and PropertyChanged
+signal is sent. If the connection cannot be established, then
+State property is set to "failure".
+After successfull connection, the relevant connection properties are sent
+by PropertyChanged signal; like IPv[4|6] information, the index of the
+VPN tunneling interface (if there is any), nameserver information,
+server specified routes etc.
diff --git a/gdbus/client.c b/gdbus/client.c
new file mode 100644
index 0000000..c03e3a4
--- /dev/null
+++ b/gdbus/client.c
@@ -0,0 +1,1350 @@
+/*
+ *
+ * D-Bus helper library
+ *
+ * Copyright (C) 2004-2011 Marcel Holtmann <marcel@holtmann.org>
+ *
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <glib.h>
+#include <dbus/dbus.h>
+
+#include "gdbus.h"
+
+#define METHOD_CALL_TIMEOUT (300 * 1000)
+
+struct GDBusClient {
+ gint ref_count;
+ DBusConnection *dbus_conn;
+ char *service_name;
+ char *unique_name;
+ char *base_path;
+ GPtrArray *match_rules;
+ DBusPendingCall *pending_call;
+ GDBusWatchFunction connect_func;
+ void *connect_data;
+ GDBusWatchFunction disconn_func;
+ void *disconn_data;
+ GDBusMessageFunction signal_func;
+ void *signal_data;
+ GDBusProxyFunction proxy_added;
+ GDBusProxyFunction proxy_removed;
+ GDBusPropertyFunction property_changed;
+ void *user_data;
+ GList *proxy_list;
+};
+
+struct GDBusProxy {
+ gint ref_count;
+ GDBusClient *client;
+ char *obj_path;
+ char *interface;
+ GHashTable *prop_list;
+ char *match_rule;
+ GDBusPropertyFunction prop_func;
+ void *prop_data;
+};
+
+struct prop_entry {
+ char *name;
+ int type;
+ DBusMessage *msg;
+};
+
+static void modify_match_reply(DBusPendingCall *call, void *user_data)
+{
+ DBusMessage *reply = dbus_pending_call_steal_reply(call);
+ DBusError error;
+
+ dbus_error_init(&error);
+
+ if (dbus_set_error_from_message(&error, reply) == TRUE)
+ dbus_error_free(&error);
+
+ dbus_message_unref(reply);
+}
+
+static gboolean modify_match(DBusConnection *conn, const char *member,
+ const char *rule)
+{
+ DBusMessage *msg;
+ DBusPendingCall *call;
+
+ msg = dbus_message_new_method_call(DBUS_SERVICE_DBUS, DBUS_PATH_DBUS,
+ DBUS_INTERFACE_DBUS, member);
+ if (msg == NULL)
+ return FALSE;
+
+ dbus_message_append_args(msg, DBUS_TYPE_STRING, &rule,
+ DBUS_TYPE_INVALID);
+
+ if (dbus_connection_send_with_reply(conn, msg, &call, -1) == FALSE) {
+ dbus_message_unref(msg);
+ return FALSE;
+ }
+
+ dbus_pending_call_set_notify(call, modify_match_reply, NULL, NULL);
+ dbus_pending_call_unref(call);
+
+ dbus_message_unref(msg);
+
+ return TRUE;
+}
+
+static void iter_append_iter(DBusMessageIter *base, DBusMessageIter *iter)
+{
+ int type;
+
+ type = dbus_message_iter_get_arg_type(iter);
+
+ if (dbus_type_is_basic(type)) {
+ const void *value;
+
+ dbus_message_iter_get_basic(iter, &value);
+ dbus_message_iter_append_basic(base, type, &value);
+ } else if (dbus_type_is_container(type)) {
+ DBusMessageIter iter_sub, base_sub;
+ char *sig;
+
+ dbus_message_iter_recurse(iter, &iter_sub);
+
+ switch (type) {
+ case DBUS_TYPE_ARRAY:
+ case DBUS_TYPE_VARIANT:
+ sig = dbus_message_iter_get_signature(&iter_sub);
+ break;
+ default:
+ sig = NULL;
+ break;
+ }
+
+ dbus_message_iter_open_container(base, type, sig, &base_sub);
+
+ if (sig != NULL)
+ dbus_free(sig);
+
+ while (dbus_message_iter_get_arg_type(&iter_sub) !=
+ DBUS_TYPE_INVALID) {
+ iter_append_iter(&base_sub, &iter_sub);
+ dbus_message_iter_next(&iter_sub);
+ }
+
+ dbus_message_iter_close_container(base, &base_sub);
+ }
+}
+
+static void prop_entry_update(struct prop_entry *prop, DBusMessageIter *iter)
+{
+ DBusMessage *msg;
+ DBusMessageIter base;
+
+ msg = dbus_message_new(DBUS_MESSAGE_TYPE_METHOD_RETURN);
+ if (msg == NULL)
+ return;
+
+ dbus_message_iter_init_append(msg, &base);
+ iter_append_iter(&base, iter);
+
+ if (prop->msg != NULL)
+ dbus_message_unref(prop->msg);
+
+ prop->msg = dbus_message_copy(msg);
+ dbus_message_unref(msg);
+}
+
+static struct prop_entry *prop_entry_new(const char *name,
+ DBusMessageIter *iter)
+{
+ struct prop_entry *prop;
+
+ prop = g_try_new0(struct prop_entry, 1);
+ if (prop == NULL)
+ return NULL;
+
+ prop->name = g_strdup(name);
+ prop->type = dbus_message_iter_get_arg_type(iter);
+
+ prop_entry_update(prop, iter);
+
+ return prop;
+}
+
+static void prop_entry_free(gpointer data)
+{
+ struct prop_entry *prop = data;
+
+ if (prop->msg != NULL)
+ dbus_message_unref(prop->msg);
+
+ g_free(prop->name);
+
+ g_free(prop);
+}
+
+static void add_property(GDBusProxy *proxy, const char *name,
+ DBusMessageIter *iter)
+{
+ DBusMessageIter value;
+ struct prop_entry *prop;
+
+ if (dbus_message_iter_get_arg_type(iter) != DBUS_TYPE_VARIANT)
+ return;
+
+ dbus_message_iter_recurse(iter, &value);
+
+ prop = g_hash_table_lookup(proxy->prop_list, name);
+ if (prop != NULL) {
+ GDBusClient *client = proxy->client;
+
+ prop_entry_update(prop, &value);
+
+ if (proxy->prop_func)
+ proxy->prop_func(proxy, name, &value, proxy->prop_data);
+
+ if (client == NULL)
+ return;
+
+ if (client->property_changed)
+ client->property_changed(proxy, name, &value,
+ client->user_data);
+ return;
+ }
+
+ prop = prop_entry_new(name, &value);
+ if (prop == NULL)
+ return;
+
+ g_hash_table_replace(proxy->prop_list, prop->name, prop);
+
+ if (proxy->prop_func)
+ proxy->prop_func(proxy, name, &value, proxy->prop_data);
+}
+
+static void update_properties(GDBusProxy *proxy, DBusMessageIter *iter)
+{
+ DBusMessageIter dict;
+
+ if (dbus_message_iter_get_arg_type(iter) != DBUS_TYPE_ARRAY)
+ return;
+
+ dbus_message_iter_recurse(iter, &dict);
+
+ while (dbus_message_iter_get_arg_type(&dict) == DBUS_TYPE_DICT_ENTRY) {
+ DBusMessageIter entry;
+ const char *name;
+
+ dbus_message_iter_recurse(&dict, &entry);
+
+ if (dbus_message_iter_get_arg_type(&entry) != DBUS_TYPE_STRING)
+ break;
+
+ dbus_message_iter_get_basic(&entry, &name);
+ dbus_message_iter_next(&entry);
+
+ add_property(proxy, name, &entry);
+
+ dbus_message_iter_next(&dict);
+ }
+}
+
+static void get_all_properties_reply(DBusPendingCall *call, void *user_data)
+{
+ GDBusProxy *proxy = user_data;
+ GDBusClient *client = proxy->client;
+ DBusMessage *reply = dbus_pending_call_steal_reply(call);
+ DBusMessageIter iter;
+ DBusError error;
+
+ dbus_error_init(&error);
+
+ if (dbus_set_error_from_message(&error, reply) == TRUE) {
+ dbus_error_free(&error);
+ goto done;
+ }
+
+ dbus_message_iter_init(reply, &iter);
+
+ update_properties(proxy, &iter);
+
+done:
+ if (g_list_find(client->proxy_list, proxy) == NULL) {
+ if (client->proxy_added)
+ client->proxy_added(proxy, client->user_data);
+
+ client->proxy_list = g_list_append(client->proxy_list, proxy);
+ }
+
+ dbus_message_unref(reply);
+
+ g_dbus_client_unref(client);
+}
+
+static void get_all_properties(GDBusProxy *proxy)
+{
+ GDBusClient *client = proxy->client;
+ const char *service_name = client->service_name;
+ DBusMessage *msg;
+ DBusPendingCall *call;
+
+ msg = dbus_message_new_method_call(service_name, proxy->obj_path,
+ DBUS_INTERFACE_PROPERTIES, "GetAll");
+ if (msg == NULL)
+ return;
+
+ dbus_message_append_args(msg, DBUS_TYPE_STRING, &proxy->interface,
+ DBUS_TYPE_INVALID);
+
+ if (dbus_connection_send_with_reply(client->dbus_conn, msg,
+ &call, -1) == FALSE) {
+ dbus_message_unref(msg);
+ return;
+ }
+
+ g_dbus_client_ref(client);
+
+ dbus_pending_call_set_notify(call, get_all_properties_reply,
+ proxy, NULL);
+ dbus_pending_call_unref(call);
+
+ dbus_message_unref(msg);
+}
+
+static GDBusProxy *proxy_lookup(GDBusClient *client, const char *path,
+ const char *interface)
+{
+ GList *list;
+
+ for (list = g_list_first(client->proxy_list); list;
+ list = g_list_next(list)) {
+ GDBusProxy *proxy = list->data;
+
+ if (g_str_equal(proxy->interface, interface) == TRUE &&
+ g_str_equal(proxy->obj_path, path) == TRUE)
+ return proxy;
+ }
+
+ return NULL;
+}
+
+static GDBusProxy *proxy_new(GDBusClient *client, const char *path,
+ const char *interface)
+{
+ GDBusProxy *proxy;
+
+ proxy = g_try_new0(GDBusProxy, 1);
+ if (proxy == NULL)
+ return NULL;
+
+ proxy->client = client;
+ proxy->obj_path = g_strdup(path);
+ proxy->interface = g_strdup(interface);
+
+ proxy->prop_list = g_hash_table_new_full(g_str_hash, g_str_equal,
+ NULL, prop_entry_free);
+
+ proxy->match_rule = g_strdup_printf("type='signal',"
+ "sender='%s',path='%s',interface='%s',"
+ "member='PropertiesChanged',arg0='%s'",
+ client->service_name, proxy->obj_path,
+ DBUS_INTERFACE_PROPERTIES, proxy->interface);
+
+ modify_match(client->dbus_conn, "AddMatch", proxy->match_rule);
+
+ return g_dbus_proxy_ref(proxy);
+}
+
+static void proxy_free(gpointer data)
+{
+ GDBusProxy *proxy = data;
+
+ if (proxy->client) {
+ GDBusClient *client = proxy->client;
+
+ if (client->proxy_removed)
+ client->proxy_removed(proxy, client->user_data);
+
+ modify_match(client->dbus_conn, "RemoveMatch",
+ proxy->match_rule);
+
+ g_free(proxy->match_rule);
+ proxy->match_rule = NULL;
+
+ g_hash_table_remove_all(proxy->prop_list);
+
+ proxy->client = NULL;
+ }
+
+ g_dbus_proxy_unref(proxy);
+}
+
+static void proxy_remove(GDBusClient *client, const char *path,
+ const char *interface)
+{
+ GList *list;
+
+ for (list = g_list_first(client->proxy_list); list;
+ list = g_list_next(list)) {
+ GDBusProxy *proxy = list->data;
+
+ if (g_str_equal(proxy->interface, interface) == TRUE &&
+ g_str_equal(proxy->obj_path, path) == TRUE) {
+ client->proxy_list =
+ g_list_delete_link(client->proxy_list, list);
+ proxy_free(proxy);
+ break;
+ }
+ }
+}
+
+GDBusProxy *g_dbus_proxy_new(GDBusClient *client, const char *path,
+ const char *interface)
+{
+ GDBusProxy *proxy;
+
+ if (client == NULL)
+ return NULL;
+
+ proxy = proxy_lookup(client, path, interface);
+ if (proxy)
+ return g_dbus_proxy_ref(proxy);
+
+ proxy = proxy_new(client, path, interface);
+ if (proxy == NULL)
+ return NULL;
+
+ get_all_properties(proxy);
+
+ return g_dbus_proxy_ref(proxy);
+}
+
+GDBusProxy *g_dbus_proxy_ref(GDBusProxy *proxy)
+{
+ if (proxy == NULL)
+ return NULL;
+
+ g_atomic_int_inc(&proxy->ref_count);
+
+ return proxy;
+}
+
+void g_dbus_proxy_unref(GDBusProxy *proxy)
+{
+ if (proxy == NULL)
+ return;
+
+ if (g_atomic_int_dec_and_test(&proxy->ref_count) == FALSE)
+ return;
+
+ g_hash_table_destroy(proxy->prop_list);
+
+ g_free(proxy->obj_path);
+ g_free(proxy->interface);
+
+ g_free(proxy);
+}
+
+const char *g_dbus_proxy_get_path(GDBusProxy *proxy)
+{
+ if (proxy == NULL)
+ return NULL;
+
+ return proxy->obj_path;
+}
+
+const char *g_dbus_proxy_get_interface(GDBusProxy *proxy)
+{
+ if (proxy == NULL)
+ return NULL;
+
+ return proxy->interface;
+}
+
+gboolean g_dbus_proxy_get_property(GDBusProxy *proxy, const char *name,
+ DBusMessageIter *iter)
+{
+ struct prop_entry *prop;
+
+ if (proxy == NULL || name == NULL)
+ return FALSE;
+
+ prop = g_hash_table_lookup(proxy->prop_list, name);
+ if (prop == NULL)
+ return FALSE;
+
+ if (prop->msg == NULL)
+ return FALSE;
+
+ if (dbus_message_iter_init(prop->msg, iter) == FALSE)
+ return FALSE;
+
+ return TRUE;
+}
+
+struct refresh_property_data {
+ GDBusProxy *proxy;
+ char *name;
+};
+
+static void refresh_property_free(gpointer user_data)
+{
+ struct refresh_property_data *data = user_data;
+
+ g_free(data->name);
+ g_free(data);
+}
+
+static void refresh_property_reply(DBusPendingCall *call, void *user_data)
+{
+ struct refresh_property_data *data = user_data;
+ DBusMessage *reply = dbus_pending_call_steal_reply(call);
+ DBusError error;
+
+ dbus_error_init(&error);
+
+ if (dbus_set_error_from_message(&error, reply) == FALSE) {
+ DBusMessageIter iter;
+
+ dbus_message_iter_init(reply, &iter);
+
+ add_property(data->proxy, data->name, &iter);
+ } else
+ dbus_error_free(&error);
+
+ dbus_message_unref(reply);
+}
+
+gboolean g_dbus_proxy_refresh_property(GDBusProxy *proxy, const char *name)
+{
+ struct refresh_property_data *data;
+ GDBusClient *client;
+ DBusMessage *msg;
+ DBusMessageIter iter;
+ DBusPendingCall *call;
+
+ if (proxy == NULL || name == NULL)
+ return FALSE;
+
+ client = proxy->client;
+ if (client == NULL)
+ return FALSE;
+
+ data = g_try_new0(struct refresh_property_data, 1);
+ if (data == NULL)
+ return FALSE;
+
+ data->proxy = proxy;
+ data->name = g_strdup(name);
+
+ msg = dbus_message_new_method_call(client->service_name,
+ proxy->obj_path, DBUS_INTERFACE_PROPERTIES, "Get");
+ if (msg == NULL) {
+ refresh_property_free(data);
+ return FALSE;
+ }
+
+ dbus_message_iter_init_append(msg, &iter);
+ dbus_message_iter_append_basic(&iter, DBUS_TYPE_STRING,
+ &proxy->interface);
+ dbus_message_iter_append_basic(&iter, DBUS_TYPE_STRING, &name);
+
+ if (dbus_connection_send_with_reply(client->dbus_conn, msg,
+ &call, -1) == FALSE) {
+ dbus_message_unref(msg);
+ refresh_property_free(data);
+ return FALSE;
+ }
+
+ dbus_pending_call_set_notify(call, refresh_property_reply,
+ data, refresh_property_free);
+ dbus_pending_call_unref(call);
+
+ dbus_message_unref(msg);
+
+ return TRUE;
+}
+
+struct set_property_data {
+ GDBusResultFunction function;
+ void *user_data;
+ GDBusDestroyFunction destroy;
+};
+
+static void set_property_reply(DBusPendingCall *call, void *user_data)
+{
+ struct set_property_data *data = user_data;
+ DBusMessage *reply = dbus_pending_call_steal_reply(call);
+ DBusError error;
+
+ dbus_error_init(&error);
+
+ dbus_set_error_from_message(&error, reply);
+
+ if (data->function)
+ data->function(&error, data->user_data);
+
+ if (data->destroy)
+ data->destroy(data->user_data);
+
+ dbus_error_free(&error);
+
+ dbus_message_unref(reply);
+}
+
+gboolean g_dbus_proxy_set_property_basic(GDBusProxy *proxy,
+ const char *name, int type, const void *value,
+ GDBusResultFunction function, void *user_data,
+ GDBusDestroyFunction destroy)
+{
+ struct set_property_data *data;
+ GDBusClient *client;
+ DBusMessage *msg;
+ DBusMessageIter iter, variant;
+ DBusPendingCall *call;
+ char type_as_str[2];
+
+ if (proxy == NULL || name == NULL || value == NULL)
+ return FALSE;
+
+ if (dbus_type_is_basic(type) == FALSE)
+ return FALSE;
+
+ client = proxy->client;
+ if (client == NULL)
+ return FALSE;
+
+ data = g_try_new0(struct set_property_data, 1);
+ if (data == NULL)
+ return FALSE;
+
+ data->function = function;
+ data->user_data = user_data;
+ data->destroy = destroy;
+
+ msg = dbus_message_new_method_call(client->service_name,
+ proxy->obj_path, DBUS_INTERFACE_PROPERTIES, "Set");
+ if (msg == NULL) {
+ g_free(data);
+ return FALSE;
+ }
+
+ type_as_str[0] = (char) type;
+ type_as_str[1] = '\0';
+
+ dbus_message_iter_init_append(msg, &iter);
+ dbus_message_iter_append_basic(&iter, DBUS_TYPE_STRING,
+ &proxy->interface);
+ dbus_message_iter_append_basic(&iter, DBUS_TYPE_STRING, &name);
+
+ dbus_message_iter_open_container(&iter, DBUS_TYPE_VARIANT,
+ type_as_str, &variant);
+ dbus_message_iter_append_basic(&variant, type, value);
+ dbus_message_iter_close_container(&iter, &variant);
+
+ if (dbus_connection_send_with_reply(client->dbus_conn, msg,
+ &call, -1) == FALSE) {
+ dbus_message_unref(msg);
+ g_free(data);
+ return FALSE;
+ }
+
+ dbus_pending_call_set_notify(call, set_property_reply, data, g_free);
+ dbus_pending_call_unref(call);
+
+ dbus_message_unref(msg);
+
+ return TRUE;
+}
+
+struct method_call_data {
+ GDBusReturnFunction function;
+ void *user_data;
+ GDBusDestroyFunction destroy;
+};
+
+static void method_call_reply(DBusPendingCall *call, void *user_data)
+{
+ struct method_call_data *data = user_data;
+ DBusMessage *reply = dbus_pending_call_steal_reply(call);
+
+ if (data->function)
+ data->function(reply, data->user_data);
+
+ if (data->destroy)
+ data->destroy(data->user_data);
+
+ dbus_message_unref(reply);
+}
+
+gboolean g_dbus_proxy_method_call(GDBusProxy *proxy, const char *method,
+ GDBusSetupFunction setup,
+ GDBusReturnFunction function, void *user_data,
+ GDBusDestroyFunction destroy)
+{
+ struct method_call_data *data;
+ GDBusClient *client;
+ DBusMessage *msg;
+ DBusPendingCall *call;
+
+ if (proxy == NULL || method == NULL)
+ return FALSE;
+
+ client = proxy->client;
+ if (client == NULL)
+ return FALSE;
+
+ data = g_try_new0(struct method_call_data, 1);
+ if (data == NULL)
+ return FALSE;
+
+ data->function = function;
+ data->user_data = user_data;
+ data->destroy = destroy;
+
+ msg = dbus_message_new_method_call(client->service_name,
+ proxy->obj_path, proxy->interface, method);
+ if (msg == NULL) {
+ g_free(data);
+ return FALSE;
+ }
+
+ if (setup) {
+ DBusMessageIter iter;
+
+ dbus_message_iter_init_append(msg, &iter);
+ setup(&iter, data->user_data);
+ }
+
+ if (dbus_connection_send_with_reply(client->dbus_conn, msg,
+ &call, METHOD_CALL_TIMEOUT) == FALSE) {
+ dbus_message_unref(msg);
+ g_free(data);
+ return FALSE;
+ }
+
+ dbus_pending_call_set_notify(call, method_call_reply, data, g_free);
+ dbus_pending_call_unref(call);
+
+ dbus_message_unref(msg);
+
+ return TRUE;
+}
+
+gboolean g_dbus_proxy_set_property_watch(GDBusProxy *proxy,
+ GDBusPropertyFunction function, void *user_data)
+{
+ if (proxy == NULL)
+ return FALSE;
+
+ proxy->prop_func = function;
+ proxy->prop_data = user_data;
+
+ return TRUE;
+}
+
+static void refresh_properties(GDBusClient *client)
+{
+ GList *list;
+
+ for (list = g_list_first(client->proxy_list); list;
+ list = g_list_next(list)) {
+ GDBusProxy *proxy = list->data;
+
+ get_all_properties(proxy);
+ }
+}
+
+static void properties_changed(GDBusClient *client, const char *path,
+ DBusMessage *msg)
+{
+ GDBusProxy *proxy = NULL;
+ DBusMessageIter iter, entry;
+ const char *interface;
+ GList *list;
+
+ if (dbus_message_iter_init(msg, &iter) == FALSE)
+ return;
+
+ if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_STRING)
+ return;
+
+ dbus_message_iter_get_basic(&iter, &interface);
+ dbus_message_iter_next(&iter);
+
+ for (list = g_list_first(client->proxy_list); list;
+ list = g_list_next(list)) {
+ GDBusProxy *data = list->data;
+
+ if (g_str_equal(data->interface, interface) == TRUE &&
+ g_str_equal(data->obj_path, path) == TRUE) {
+ proxy = data;
+ break;
+ }
+ }
+
+ if (proxy == NULL)
+ return;
+
+ update_properties(proxy, &iter);
+
+ dbus_message_iter_next(&iter);
+
+ if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_ARRAY)
+ return;
+
+ dbus_message_iter_recurse(&iter, &entry);
+
+ while (dbus_message_iter_get_arg_type(&entry) == DBUS_TYPE_STRING) {
+ const char *name;
+
+ dbus_message_iter_get_basic(&entry, &name);
+
+ g_hash_table_remove(proxy->prop_list, name);
+
+ if (proxy->prop_func)
+ proxy->prop_func(proxy, name, NULL, proxy->prop_data);
+
+ if (client->property_changed)
+ client->property_changed(proxy, name, NULL,
+ client->user_data);
+
+ dbus_message_iter_next(&entry);
+ }
+}
+
+static void parse_properties(GDBusClient *client, const char *path,
+ const char *interface, DBusMessageIter *iter)
+{
+ GDBusProxy *proxy;
+
+ if (g_str_equal(interface, DBUS_INTERFACE_INTROSPECTABLE) == TRUE)
+ return;
+
+ if (g_str_equal(interface, DBUS_INTERFACE_PROPERTIES) == TRUE)
+ return;
+
+ proxy = proxy_lookup(client, path, interface);
+ if (proxy) {
+ update_properties(proxy, iter);
+ return;
+ }
+
+ proxy = proxy_new(client, path, interface);
+ if (proxy == NULL)
+ return;
+
+ update_properties(proxy, iter);
+
+ if (client->proxy_added)
+ client->proxy_added(proxy, client->user_data);
+
+ client->proxy_list = g_list_append(client->proxy_list, proxy);
+}
+
+static void parse_interfaces(GDBusClient *client, const char *path,
+ DBusMessageIter *iter)
+{
+ DBusMessageIter dict;
+
+ if (dbus_message_iter_get_arg_type(iter) != DBUS_TYPE_ARRAY)
+ return;
+
+ dbus_message_iter_recurse(iter, &dict);
+
+ while (dbus_message_iter_get_arg_type(&dict) == DBUS_TYPE_DICT_ENTRY) {
+ DBusMessageIter entry;
+ const char *interface;
+
+ dbus_message_iter_recurse(&dict, &entry);
+
+ if (dbus_message_iter_get_arg_type(&entry) != DBUS_TYPE_STRING)
+ break;
+
+ dbus_message_iter_get_basic(&entry, &interface);
+ dbus_message_iter_next(&entry);
+
+ parse_properties(client, path, interface, &entry);
+
+ dbus_message_iter_next(&dict);
+ }
+}
+
+static void interfaces_added(GDBusClient *client, DBusMessage *msg)
+{
+ DBusMessageIter iter;
+ const char *path;
+
+ if (dbus_message_iter_init(msg, &iter) == FALSE)
+ return;
+
+ if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_OBJECT_PATH)
+ return;
+
+ dbus_message_iter_get_basic(&iter, &path);
+ dbus_message_iter_next(&iter);
+
+ g_dbus_client_ref(client);
+
+ parse_interfaces(client, path, &iter);
+
+ g_dbus_client_unref(client);
+}
+
+static void interfaces_removed(GDBusClient *client, DBusMessage *msg)
+{
+ DBusMessageIter iter, entry;
+ const char *path;
+
+ if (dbus_message_iter_init(msg, &iter) == FALSE)
+ return;
+
+ if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_OBJECT_PATH)
+ return;
+
+ dbus_message_iter_get_basic(&iter, &path);
+ dbus_message_iter_next(&iter);
+
+ if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_ARRAY)
+ return;
+
+ dbus_message_iter_recurse(&iter, &entry);
+
+ g_dbus_client_ref(client);
+
+ while (dbus_message_iter_get_arg_type(&entry) == DBUS_TYPE_STRING) {
+ const char *interface;
+
+ dbus_message_iter_get_basic(&entry, &interface);
+ proxy_remove(client, path, interface);
+ dbus_message_iter_next(&entry);
+ }
+
+ g_dbus_client_unref(client);
+}
+
+static void parse_managed_objects(GDBusClient *client, DBusMessage *msg)
+{
+ DBusMessageIter iter, dict;
+
+ if (dbus_message_iter_init(msg, &iter) == FALSE)
+ return;
+
+ if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_ARRAY)
+ return;
+
+ dbus_message_iter_recurse(&iter, &dict);
+
+ while (dbus_message_iter_get_arg_type(&dict) == DBUS_TYPE_DICT_ENTRY) {
+ DBusMessageIter entry;
+ const char *path;
+
+ dbus_message_iter_recurse(&dict, &entry);
+
+ if (dbus_message_iter_get_arg_type(&entry) !=
+ DBUS_TYPE_OBJECT_PATH)
+ break;
+
+ dbus_message_iter_get_basic(&entry, &path);
+ dbus_message_iter_next(&entry);
+
+ parse_interfaces(client, path, &entry);
+
+ dbus_message_iter_next(&dict);
+ }
+}
+
+static void get_managed_objects_reply(DBusPendingCall *call, void *user_data)
+{
+ GDBusClient *client = user_data;
+ DBusMessage *reply = dbus_pending_call_steal_reply(call);
+ DBusError error;
+
+ dbus_error_init(&error);
+
+ if (dbus_set_error_from_message(&error, reply) == TRUE) {
+ dbus_error_free(&error);
+ goto done;
+ }
+
+ parse_managed_objects(client, reply);
+
+done:
+ dbus_message_unref(reply);
+
+ g_dbus_client_unref(client);
+}
+
+static void get_managed_objects(GDBusClient *client)
+{
+ DBusMessage *msg;
+ DBusPendingCall *call;
+
+ if (!client->proxy_added && !client->proxy_removed) {
+ refresh_properties(client);
+ return;
+ }
+
+ msg = dbus_message_new_method_call(client->service_name, "/",
+ DBUS_INTERFACE_DBUS ".ObjectManager",
+ "GetManagedObjects");
+ if (msg == NULL)
+ return;
+
+ dbus_message_append_args(msg, DBUS_TYPE_INVALID);
+
+ if (dbus_connection_send_with_reply(client->dbus_conn, msg,
+ &call, -1) == FALSE) {
+ dbus_message_unref(msg);
+ return;
+ }
+
+ g_dbus_client_ref(client);
+
+ dbus_pending_call_set_notify(call, get_managed_objects_reply,
+ client, NULL);
+ dbus_pending_call_unref(call);
+
+ dbus_message_unref(msg);
+}
+
+static void get_name_owner_reply(DBusPendingCall *call, void *user_data)
+{
+ GDBusClient *client = user_data;
+ DBusMessage *reply = dbus_pending_call_steal_reply(call);
+ DBusError error;
+ const char *name;
+
+ g_dbus_client_ref(client);
+
+ dbus_error_init(&error);
+
+ if (dbus_set_error_from_message(&error, reply) == TRUE) {
+ dbus_error_free(&error);
+ goto done;
+ }
+
+ if (dbus_message_get_args(reply, NULL, DBUS_TYPE_STRING, &name,
+ DBUS_TYPE_INVALID) == FALSE)
+ goto done;
+
+ if (client->unique_name == NULL) {
+ client->unique_name = g_strdup(name);
+
+ if (client->connect_func)
+ client->connect_func(client->dbus_conn,
+ client->connect_data);
+
+ get_managed_objects(client);
+ }
+
+done:
+ dbus_message_unref(reply);
+
+ dbus_pending_call_unref(client->pending_call);
+ client->pending_call = NULL;
+
+ g_dbus_client_unref(client);
+}
+
+static void get_name_owner(GDBusClient *client, const char *name)
+{
+ DBusMessage *msg;
+
+ msg = dbus_message_new_method_call(DBUS_SERVICE_DBUS, DBUS_PATH_DBUS,
+ DBUS_INTERFACE_DBUS, "GetNameOwner");
+ if (msg == NULL)
+ return;
+
+ dbus_message_append_args(msg, DBUS_TYPE_STRING, &name,
+ DBUS_TYPE_INVALID);
+
+ if (dbus_connection_send_with_reply(client->dbus_conn, msg,
+ &client->pending_call, -1) == FALSE) {
+ dbus_message_unref(msg);
+ return;
+ }
+
+ dbus_pending_call_set_notify(client->pending_call,
+ get_name_owner_reply, client, NULL);
+
+ dbus_message_unref(msg);
+}
+
+static DBusHandlerResult message_filter(DBusConnection *connection,
+ DBusMessage *message, void *user_data)
+{
+ GDBusClient *client = user_data;
+ const char *sender;
+
+ if (dbus_message_get_type(message) != DBUS_MESSAGE_TYPE_SIGNAL)
+ return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
+
+ sender = dbus_message_get_sender(message);
+
+ if (g_str_equal(sender, DBUS_SERVICE_DBUS) == TRUE) {
+ const char *interface, *member;
+ const char *name, *old, *new;
+
+ interface = dbus_message_get_interface(message);
+
+ if (g_str_equal(interface, DBUS_INTERFACE_DBUS) == FALSE)
+ return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
+
+ member = dbus_message_get_member(message);
+
+ if (g_str_equal(member, "NameOwnerChanged") == FALSE)
+ return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
+
+ if (dbus_message_get_args(message, NULL,
+ DBUS_TYPE_STRING, &name,
+ DBUS_TYPE_STRING, &old,
+ DBUS_TYPE_STRING, &new,
+ DBUS_TYPE_INVALID) == FALSE)
+ return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
+
+ if (g_str_equal(name, client->service_name) == FALSE)
+ return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
+
+ if (*new == '\0' && client->unique_name != NULL &&
+ g_str_equal(old, client->unique_name) == TRUE) {
+ if (client->disconn_func)
+ client->disconn_func(client->dbus_conn,
+ client->disconn_data);
+
+ g_free(client->unique_name);
+ client->unique_name = NULL;
+ } else if (*old == '\0' && client->unique_name == NULL) {
+ client->unique_name = g_strdup(new);
+
+ if (client->connect_func)
+ client->connect_func(client->dbus_conn,
+ client->connect_data);
+
+ get_managed_objects(client);
+ }
+
+ return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
+ }
+
+ if (client->unique_name == NULL)
+ return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
+
+ if (g_str_equal(sender, client->unique_name) == TRUE) {
+ const char *path, *interface, *member;
+
+ path = dbus_message_get_path(message);
+ interface = dbus_message_get_interface(message);
+ member = dbus_message_get_member(message);
+
+ if (g_str_equal(path, "/") == TRUE) {
+ if (g_str_equal(interface, DBUS_INTERFACE_DBUS
+ ".ObjectManager") == FALSE)
+ return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
+
+ if (g_str_equal(member, "InterfacesAdded") == TRUE) {
+ interfaces_added(client, message);
+ return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
+ }
+
+ if (g_str_equal(member, "InterfacesRemoved") == TRUE) {
+ interfaces_removed(client, message);
+ return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
+ }
+
+ return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
+ }
+
+ if (g_str_has_prefix(path, client->base_path) == FALSE)
+ return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
+
+ if (g_str_equal(interface, DBUS_INTERFACE_PROPERTIES) == TRUE) {
+ if (g_str_equal(member, "PropertiesChanged") == TRUE)
+ properties_changed(client, path, message);
+
+ return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
+ }
+
+ if (client->signal_func)
+ client->signal_func(client->dbus_conn,
+ message, client->signal_data);
+ }
+
+ return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
+}
+
+GDBusClient *g_dbus_client_new(DBusConnection *connection,
+ const char *service, const char *path)
+{
+ GDBusClient *client;
+ unsigned int i;
+
+ if (connection == NULL)
+ return NULL;
+
+ client = g_try_new0(GDBusClient, 1);
+ if (client == NULL)
+ return NULL;
+
+ if (dbus_connection_add_filter(connection, message_filter,
+ client, NULL) == FALSE) {
+ g_free(client);
+ return NULL;
+ }
+
+ client->dbus_conn = dbus_connection_ref(connection);
+ client->service_name = g_strdup(service);
+ client->base_path = g_strdup(path);
+
+ get_name_owner(client, client->service_name);
+
+ client->match_rules = g_ptr_array_sized_new(4);
+ g_ptr_array_set_free_func(client->match_rules, g_free);
+
+ g_ptr_array_add(client->match_rules, g_strdup_printf("type='signal',"
+ "sender='%s',path='%s',interface='%s',"
+ "member='NameOwnerChanged',arg0='%s'",
+ DBUS_SERVICE_DBUS, DBUS_PATH_DBUS,
+ DBUS_INTERFACE_DBUS, client->service_name));
+ g_ptr_array_add(client->match_rules, g_strdup_printf("type='signal',"
+ "sender='%s',"
+ "path='/',interface='%s.ObjectManager',"
+ "member='InterfacesAdded'",
+ client->service_name, DBUS_INTERFACE_DBUS));
+ g_ptr_array_add(client->match_rules, g_strdup_printf("type='signal',"
+ "sender='%s',"
+ "path='/',interface='%s.ObjectManager',"
+ "member='InterfacesRemoved'",
+ client->service_name, DBUS_INTERFACE_DBUS));
+ g_ptr_array_add(client->match_rules, g_strdup_printf("type='signal',"
+ "sender='%s',path_namespace='%s'",
+ client->service_name, client->base_path));
+
+ for (i = 0; i < client->match_rules->len; i++) {
+ modify_match(client->dbus_conn, "AddMatch",
+ g_ptr_array_index(client->match_rules, i));
+ }
+
+ return g_dbus_client_ref(client);
+}
+
+GDBusClient *g_dbus_client_ref(GDBusClient *client)
+{
+ if (client == NULL)
+ return NULL;
+
+ g_atomic_int_inc(&client->ref_count);
+
+ return client;
+}
+
+void g_dbus_client_unref(GDBusClient *client)
+{
+ unsigned int i;
+
+ if (client == NULL)
+ return;
+
+ if (g_atomic_int_dec_and_test(&client->ref_count) == FALSE)
+ return;
+
+ if (client->pending_call != NULL) {
+ dbus_pending_call_cancel(client->pending_call);
+ dbus_pending_call_unref(client->pending_call);
+ }
+
+ for (i = 0; i < client->match_rules->len; i++) {
+ modify_match(client->dbus_conn, "RemoveMatch",
+ g_ptr_array_index(client->match_rules, i));
+ }
+
+ g_ptr_array_free(client->match_rules, TRUE);
+
+ dbus_connection_remove_filter(client->dbus_conn,
+ message_filter, client);
+
+ g_list_free_full(client->proxy_list, proxy_free);
+
+ if (client->disconn_func)
+ client->disconn_func(client->dbus_conn, client->disconn_data);
+
+ dbus_connection_unref(client->dbus_conn);
+
+ g_free(client->service_name);
+ g_free(client->unique_name);
+ g_free(client->base_path);
+
+ g_free(client);
+}
+
+gboolean g_dbus_client_set_connect_watch(GDBusClient *client,
+ GDBusWatchFunction function, void *user_data)
+{
+ if (client == NULL)
+ return FALSE;
+
+ client->connect_func = function;
+ client->connect_data = user_data;
+
+ return TRUE;
+}
+
+gboolean g_dbus_client_set_disconnect_watch(GDBusClient *client,
+ GDBusWatchFunction function, void *user_data)
+{
+ if (client == NULL)
+ return FALSE;
+
+ client->disconn_func = function;
+ client->disconn_data = user_data;
+
+ return TRUE;
+}
+
+gboolean g_dbus_client_set_signal_watch(GDBusClient *client,
+ GDBusMessageFunction function, void *user_data)
+{
+ if (client == NULL)
+ return FALSE;
+
+ client->signal_func = function;
+ client->signal_data = user_data;
+
+ return TRUE;
+}
+
+gboolean g_dbus_client_set_proxy_handlers(GDBusClient *client,
+ GDBusProxyFunction proxy_added,
+ GDBusProxyFunction proxy_removed,
+ GDBusPropertyFunction property_changed,
+ void *user_data)
+{
+ if (client == NULL)
+ return FALSE;
+
+ client->proxy_added = proxy_added;
+ client->proxy_removed = proxy_removed;
+ client->property_changed = property_changed;
+ client->user_data = user_data;
+
+ get_managed_objects(client);
+
+ return TRUE;
+}
diff --git a/gdbus/gdbus.h b/gdbus/gdbus.h
new file mode 100644
index 0000000..6f5a012
--- /dev/null
+++ b/gdbus/gdbus.h
@@ -0,0 +1,367 @@
+/*
+ *
+ * D-Bus helper library
+ *
+ * Copyright (C) 2004-2011 Marcel Holtmann <marcel@holtmann.org>
+ *
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ */
+
+#ifndef __GDBUS_H
+#define __GDBUS_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <dbus/dbus.h>
+#include <glib.h>
+
+typedef enum GDBusMethodFlags GDBusMethodFlags;
+typedef enum GDBusSignalFlags GDBusSignalFlags;
+typedef enum GDBusPropertyFlags GDBusPropertyFlags;
+typedef enum GDBusSecurityFlags GDBusSecurityFlags;
+
+typedef struct GDBusArgInfo GDBusArgInfo;
+typedef struct GDBusMethodTable GDBusMethodTable;
+typedef struct GDBusSignalTable GDBusSignalTable;
+typedef struct GDBusPropertyTable GDBusPropertyTable;
+typedef struct GDBusSecurityTable GDBusSecurityTable;
+
+typedef void (* GDBusWatchFunction) (DBusConnection *connection,
+ void *user_data);
+
+typedef void (* GDBusMessageFunction) (DBusConnection *connection,
+ DBusMessage *message, void *user_data);
+
+typedef gboolean (* GDBusSignalFunction) (DBusConnection *connection,
+ DBusMessage *message, void *user_data);
+
+DBusConnection *g_dbus_setup_bus(DBusBusType type, const char *name,
+ DBusError *error);
+
+DBusConnection *g_dbus_setup_private(DBusBusType type, const char *name,
+ DBusError *error);
+
+gboolean g_dbus_request_name(DBusConnection *connection, const char *name,
+ DBusError *error);
+
+gboolean g_dbus_set_disconnect_function(DBusConnection *connection,
+ GDBusWatchFunction function,
+ void *user_data, DBusFreeFunction destroy);
+
+typedef void (* GDBusDestroyFunction) (void *user_data);
+
+typedef DBusMessage * (* GDBusMethodFunction) (DBusConnection *connection,
+ DBusMessage *message, void *user_data);
+
+typedef gboolean (*GDBusPropertyGetter)(const GDBusPropertyTable *property,
+ DBusMessageIter *iter, void *data);
+
+typedef guint32 GDBusPendingPropertySet;
+
+typedef void (*GDBusPropertySetter)(const GDBusPropertyTable *property,
+ DBusMessageIter *value, GDBusPendingPropertySet id,
+ void *data);
+
+typedef gboolean (*GDBusPropertyExists)(const GDBusPropertyTable *property,
+ void *data);
+
+typedef guint32 GDBusPendingReply;
+
+typedef void (* GDBusSecurityFunction) (DBusConnection *connection,
+ const char *action,
+ gboolean interaction,
+ GDBusPendingReply pending);
+
+enum GDBusFlags {
+ G_DBUS_FLAG_ENABLE_EXPERIMENTAL = (1 << 0),
+};
+
+enum GDBusMethodFlags {
+ G_DBUS_METHOD_FLAG_DEPRECATED = (1 << 0),
+ G_DBUS_METHOD_FLAG_NOREPLY = (1 << 1),
+ G_DBUS_METHOD_FLAG_ASYNC = (1 << 2),
+ G_DBUS_METHOD_FLAG_EXPERIMENTAL = (1 << 3),
+};
+
+enum GDBusSignalFlags {
+ G_DBUS_SIGNAL_FLAG_DEPRECATED = (1 << 0),
+ G_DBUS_SIGNAL_FLAG_EXPERIMENTAL = (1 << 1),
+};
+
+enum GDBusPropertyFlags {
+ G_DBUS_PROPERTY_FLAG_DEPRECATED = (1 << 0),
+ G_DBUS_PROPERTY_FLAG_EXPERIMENTAL = (1 << 1),
+};
+
+enum GDBusSecurityFlags {
+ G_DBUS_SECURITY_FLAG_DEPRECATED = (1 << 0),
+ G_DBUS_SECURITY_FLAG_BUILTIN = (1 << 1),
+ G_DBUS_SECURITY_FLAG_ALLOW_INTERACTION = (1 << 2),
+};
+
+struct GDBusArgInfo {
+ const char *name;
+ const char *signature;
+};
+
+struct GDBusMethodTable {
+ const char *name;
+ GDBusMethodFunction function;
+ GDBusMethodFlags flags;
+ unsigned int privilege;
+ const GDBusArgInfo *in_args;
+ const GDBusArgInfo *out_args;
+};
+
+struct GDBusSignalTable {
+ const char *name;
+ GDBusSignalFlags flags;
+ const GDBusArgInfo *args;
+};
+
+struct GDBusPropertyTable {
+ const char *name;
+ const char *type;
+ GDBusPropertyGetter get;
+ GDBusPropertySetter set;
+ GDBusPropertyExists exists;
+ GDBusPropertyFlags flags;
+};
+
+struct GDBusSecurityTable {
+ unsigned int privilege;
+ const char *action;
+ GDBusSecurityFlags flags;
+ GDBusSecurityFunction function;
+};
+
+#define GDBUS_ARGS(args...) (const GDBusArgInfo[]) { args, { } }
+
+#define GDBUS_METHOD(_name, _in_args, _out_args, _function) \
+ .name = _name, \
+ .in_args = _in_args, \
+ .out_args = _out_args, \
+ .function = _function
+
+#define GDBUS_ASYNC_METHOD(_name, _in_args, _out_args, _function) \
+ .name = _name, \
+ .in_args = _in_args, \
+ .out_args = _out_args, \
+ .function = _function, \
+ .flags = G_DBUS_METHOD_FLAG_ASYNC
+
+#define GDBUS_DEPRECATED_METHOD(_name, _in_args, _out_args, _function) \
+ .name = _name, \
+ .in_args = _in_args, \
+ .out_args = _out_args, \
+ .function = _function, \
+ .flags = G_DBUS_METHOD_FLAG_DEPRECATED
+
+#define GDBUS_DEPRECATED_ASYNC_METHOD(_name, _in_args, _out_args, _function) \
+ .name = _name, \
+ .in_args = _in_args, \
+ .out_args = _out_args, \
+ .function = _function, \
+ .flags = G_DBUS_METHOD_FLAG_ASYNC | G_DBUS_METHOD_FLAG_DEPRECATED
+
+#define GDBUS_EXPERIMENTAL_METHOD(_name, _in_args, _out_args, _function) \
+ .name = _name, \
+ .in_args = _in_args, \
+ .out_args = _out_args, \
+ .function = _function, \
+ .flags = G_DBUS_METHOD_FLAG_EXPERIMENTAL
+
+#define GDBUS_EXPERIMENTAL_ASYNC_METHOD(_name, _in_args, _out_args, _function) \
+ .name = _name, \
+ .in_args = _in_args, \
+ .out_args = _out_args, \
+ .function = _function, \
+ .flags = G_DBUS_METHOD_FLAG_ASYNC | G_DBUS_METHOD_FLAG_EXPERIMENTAL
+
+#define GDBUS_NOREPLY_METHOD(_name, _in_args, _out_args, _function) \
+ .name = _name, \
+ .in_args = _in_args, \
+ .out_args = _out_args, \
+ .function = _function, \
+ .flags = G_DBUS_METHOD_FLAG_NOREPLY
+
+#define GDBUS_SIGNAL(_name, _args) \
+ .name = _name, \
+ .args = _args
+
+#define GDBUS_DEPRECATED_SIGNAL(_name, _args) \
+ .name = _name, \
+ .args = _args, \
+ .flags = G_DBUS_SIGNAL_FLAG_DEPRECATED
+
+#define GDBUS_EXPERIMENTAL_SIGNAL(_name, _args) \
+ .name = _name, \
+ .args = _args, \
+ .flags = G_DBUS_SIGNAL_FLAG_EXPERIMENTAL
+
+void g_dbus_set_flags(int flags);
+
+gboolean g_dbus_register_interface(DBusConnection *connection,
+ const char *path, const char *name,
+ const GDBusMethodTable *methods,
+ const GDBusSignalTable *signals,
+ const GDBusPropertyTable *properties,
+ void *user_data,
+ GDBusDestroyFunction destroy);
+gboolean g_dbus_unregister_interface(DBusConnection *connection,
+ const char *path, const char *name);
+
+gboolean g_dbus_register_security(const GDBusSecurityTable *security);
+gboolean g_dbus_unregister_security(const GDBusSecurityTable *security);
+
+void g_dbus_pending_success(DBusConnection *connection,
+ GDBusPendingReply pending);
+void g_dbus_pending_error(DBusConnection *connection,
+ GDBusPendingReply pending,
+ const char *name, const char *format, ...)
+ __attribute__((format(printf, 4, 5)));
+void g_dbus_pending_error_valist(DBusConnection *connection,
+ GDBusPendingReply pending, const char *name,
+ const char *format, va_list args);
+
+DBusMessage *g_dbus_create_error(DBusMessage *message, const char *name,
+ const char *format, ...)
+ __attribute__((format(printf, 3, 4)));
+DBusMessage *g_dbus_create_error_valist(DBusMessage *message, const char *name,
+ const char *format, va_list args);
+DBusMessage *g_dbus_create_reply(DBusMessage *message, int type, ...);
+DBusMessage *g_dbus_create_reply_valist(DBusMessage *message,
+ int type, va_list args);
+
+gboolean g_dbus_send_message(DBusConnection *connection, DBusMessage *message);
+gboolean g_dbus_send_error(DBusConnection *connection, DBusMessage *message,
+ const char *name, const char *format, ...)
+ __attribute__((format(printf, 4, 5)));
+gboolean g_dbus_send_error_valist(DBusConnection *connection,
+ DBusMessage *message, const char *name,
+ const char *format, va_list args);
+gboolean g_dbus_send_reply(DBusConnection *connection,
+ DBusMessage *message, int type, ...);
+gboolean g_dbus_send_reply_valist(DBusConnection *connection,
+ DBusMessage *message, int type, va_list args);
+
+gboolean g_dbus_emit_signal(DBusConnection *connection,
+ const char *path, const char *interface,
+ const char *name, int type, ...);
+gboolean g_dbus_emit_signal_valist(DBusConnection *connection,
+ const char *path, const char *interface,
+ const char *name, int type, va_list args);
+
+guint g_dbus_add_service_watch(DBusConnection *connection, const char *name,
+ GDBusWatchFunction connect,
+ GDBusWatchFunction disconnect,
+ void *user_data, GDBusDestroyFunction destroy);
+guint g_dbus_add_disconnect_watch(DBusConnection *connection, const char *name,
+ GDBusWatchFunction function,
+ void *user_data, GDBusDestroyFunction destroy);
+guint g_dbus_add_signal_watch(DBusConnection *connection,
+ const char *sender, const char *path,
+ const char *interface, const char *member,
+ GDBusSignalFunction function, void *user_data,
+ GDBusDestroyFunction destroy);
+guint g_dbus_add_properties_watch(DBusConnection *connection,
+ const char *sender, const char *path,
+ const char *interface,
+ GDBusSignalFunction function, void *user_data,
+ GDBusDestroyFunction destroy);
+gboolean g_dbus_remove_watch(DBusConnection *connection, guint tag);
+void g_dbus_remove_all_watches(DBusConnection *connection);
+
+void g_dbus_pending_property_success(GDBusPendingPropertySet id);
+void g_dbus_pending_property_error_valist(GDBusPendingReply id,
+ const char *name, const char *format, va_list args);
+void g_dbus_pending_property_error(GDBusPendingReply id, const char *name,
+ const char *format, ...);
+void g_dbus_emit_property_changed(DBusConnection *connection,
+ const char *path, const char *interface,
+ const char *name);
+gboolean g_dbus_get_properties(DBusConnection *connection, const char *path,
+ const char *interface, DBusMessageIter *iter);
+
+gboolean g_dbus_attach_object_manager(DBusConnection *connection);
+gboolean g_dbus_detach_object_manager(DBusConnection *connection);
+
+typedef struct GDBusClient GDBusClient;
+typedef struct GDBusProxy GDBusProxy;
+
+GDBusProxy *g_dbus_proxy_new(GDBusClient *client, const char *path,
+ const char *interface);
+
+GDBusProxy *g_dbus_proxy_ref(GDBusProxy *proxy);
+void g_dbus_proxy_unref(GDBusProxy *proxy);
+
+const char *g_dbus_proxy_get_path(GDBusProxy *proxy);
+const char *g_dbus_proxy_get_interface(GDBusProxy *proxy);
+
+gboolean g_dbus_proxy_get_property(GDBusProxy *proxy, const char *name,
+ DBusMessageIter *iter);
+
+gboolean g_dbus_proxy_refresh_property(GDBusProxy *proxy, const char *name);
+
+typedef void (* GDBusResultFunction) (const DBusError *error, void *user_data);
+
+gboolean g_dbus_proxy_set_property_basic(GDBusProxy *proxy,
+ const char *name, int type, const void *value,
+ GDBusResultFunction function, void *user_data,
+ GDBusDestroyFunction destroy);
+
+typedef void (* GDBusSetupFunction) (DBusMessageIter *iter, void *user_data);
+typedef void (* GDBusReturnFunction) (DBusMessage *message, void *user_data);
+
+gboolean g_dbus_proxy_method_call(GDBusProxy *proxy, const char *method,
+ GDBusSetupFunction setup,
+ GDBusReturnFunction function, void *user_data,
+ GDBusDestroyFunction destroy);
+
+typedef void (* GDBusProxyFunction) (GDBusProxy *proxy, void *user_data);
+typedef void (* GDBusPropertyFunction) (GDBusProxy *proxy, const char *name,
+ DBusMessageIter *iter, void *user_data);
+
+gboolean g_dbus_proxy_set_property_watch(GDBusProxy *proxy,
+ GDBusPropertyFunction function, void *user_data);
+
+GDBusClient *g_dbus_client_new(DBusConnection *connection,
+ const char *service, const char *path);
+
+GDBusClient *g_dbus_client_ref(GDBusClient *client);
+void g_dbus_client_unref(GDBusClient *client);
+
+gboolean g_dbus_client_set_connect_watch(GDBusClient *client,
+ GDBusWatchFunction function, void *user_data);
+gboolean g_dbus_client_set_disconnect_watch(GDBusClient *client,
+ GDBusWatchFunction function, void *user_data);
+gboolean g_dbus_client_set_signal_watch(GDBusClient *client,
+ GDBusMessageFunction function, void *user_data);
+
+gboolean g_dbus_client_set_proxy_handlers(GDBusClient *client,
+ GDBusProxyFunction proxy_added,
+ GDBusProxyFunction proxy_removed,
+ GDBusPropertyFunction property_changed,
+ void *user_data);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __GDBUS_H */
diff --git a/gdbus/mainloop.c b/gdbus/mainloop.c
new file mode 100644
index 0000000..099b67f
--- /dev/null
+++ b/gdbus/mainloop.c
@@ -0,0 +1,380 @@
+/*
+ *
+ * D-Bus helper library
+ *
+ * Copyright (C) 2004-2011 Marcel Holtmann <marcel@holtmann.org>
+ *
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <glib.h>
+#include <dbus/dbus.h>
+
+#include "gdbus.h"
+
+#define DISPATCH_TIMEOUT 0
+
+#define info(fmt...)
+#define error(fmt...)
+#define debug(fmt...)
+
+struct timeout_handler {
+ guint id;
+ DBusTimeout *timeout;
+};
+
+struct watch_info {
+ guint id;
+ DBusWatch *watch;
+ DBusConnection *conn;
+};
+
+struct disconnect_data {
+ GDBusWatchFunction function;
+ void *user_data;
+};
+
+static gboolean disconnected_signal(DBusConnection *conn,
+ DBusMessage *msg, void *data)
+{
+ struct disconnect_data *dc_data = data;
+
+ error("Got disconnected from the system message bus");
+
+ dc_data->function(conn, dc_data->user_data);
+
+ dbus_connection_unref(conn);
+
+ return TRUE;
+}
+
+static gboolean message_dispatch(void *data)
+{
+ DBusConnection *conn = data;
+
+ dbus_connection_ref(conn);
+
+ /* Dispatch messages */
+ while (dbus_connection_dispatch(conn) == DBUS_DISPATCH_DATA_REMAINS);
+
+ dbus_connection_unref(conn);
+
+ return FALSE;
+}
+
+static inline void queue_dispatch(DBusConnection *conn,
+ DBusDispatchStatus status)
+{
+ if (status == DBUS_DISPATCH_DATA_REMAINS)
+ g_timeout_add(DISPATCH_TIMEOUT, message_dispatch, conn);
+}
+
+static gboolean watch_func(GIOChannel *chan, GIOCondition cond, gpointer data)
+{
+ struct watch_info *info = data;
+ unsigned int flags = 0;
+ DBusDispatchStatus status;
+ DBusConnection *conn;
+
+ conn = dbus_connection_ref(info->conn);
+
+ if (cond & G_IO_IN) flags |= DBUS_WATCH_READABLE;
+ if (cond & G_IO_OUT) flags |= DBUS_WATCH_WRITABLE;
+ if (cond & G_IO_HUP) flags |= DBUS_WATCH_HANGUP;
+ if (cond & G_IO_ERR) flags |= DBUS_WATCH_ERROR;
+
+ dbus_watch_handle(info->watch, flags);
+
+ status = dbus_connection_get_dispatch_status(conn);
+ queue_dispatch(conn, status);
+
+ dbus_connection_unref(conn);
+
+ return TRUE;
+}
+
+static void watch_info_free(void *data)
+{
+ struct watch_info *info = data;
+
+ if (info->id > 0) {
+ g_source_remove(info->id);
+ info->id = 0;
+ }
+
+ dbus_connection_unref(info->conn);
+
+ g_free(info);
+}
+
+static dbus_bool_t add_watch(DBusWatch *watch, void *data)
+{
+ DBusConnection *conn = data;
+ GIOCondition cond = G_IO_HUP | G_IO_ERR;
+ GIOChannel *chan;
+ struct watch_info *info;
+ unsigned int flags;
+ int fd;
+
+ if (!dbus_watch_get_enabled(watch))
+ return TRUE;
+
+ info = g_new0(struct watch_info, 1);
+
+ fd = dbus_watch_get_unix_fd(watch);
+ chan = g_io_channel_unix_new(fd);
+
+ info->watch = watch;
+ info->conn = dbus_connection_ref(conn);
+
+ dbus_watch_set_data(watch, info, watch_info_free);
+
+ flags = dbus_watch_get_flags(watch);
+
+ if (flags & DBUS_WATCH_READABLE) cond |= G_IO_IN;
+ if (flags & DBUS_WATCH_WRITABLE) cond |= G_IO_OUT;
+
+ info->id = g_io_add_watch(chan, cond, watch_func, info);
+
+ g_io_channel_unref(chan);
+
+ return TRUE;
+}
+
+static void remove_watch(DBusWatch *watch, void *data)
+{
+ if (dbus_watch_get_enabled(watch))
+ return;
+
+ /* will trigger watch_info_free() */
+ dbus_watch_set_data(watch, NULL, NULL);
+}
+
+static void watch_toggled(DBusWatch *watch, void *data)
+{
+ /* Because we just exit on OOM, enable/disable is
+ * no different from add/remove */
+ if (dbus_watch_get_enabled(watch))
+ add_watch(watch, data);
+ else
+ remove_watch(watch, data);
+}
+
+static gboolean timeout_handler_dispatch(gpointer data)
+{
+ struct timeout_handler *handler = data;
+
+ handler->id = 0;
+
+ /* if not enabled should not be polled by the main loop */
+ if (!dbus_timeout_get_enabled(handler->timeout))
+ return FALSE;
+
+ dbus_timeout_handle(handler->timeout);
+
+ return FALSE;
+}
+
+static void timeout_handler_free(void *data)
+{
+ struct timeout_handler *handler = data;
+
+ if (handler->id > 0) {
+ g_source_remove(handler->id);
+ handler->id = 0;
+ }
+
+ g_free(handler);
+}
+
+static dbus_bool_t add_timeout(DBusTimeout *timeout, void *data)
+{
+ int interval = dbus_timeout_get_interval(timeout);
+ struct timeout_handler *handler;
+
+ if (!dbus_timeout_get_enabled(timeout))
+ return TRUE;
+
+ handler = g_new0(struct timeout_handler, 1);
+
+ handler->timeout = timeout;
+
+ dbus_timeout_set_data(timeout, handler, timeout_handler_free);
+
+ handler->id = g_timeout_add(interval, timeout_handler_dispatch,
+ handler);
+
+ return TRUE;
+}
+
+static void remove_timeout(DBusTimeout *timeout, void *data)
+{
+ /* will trigger timeout_handler_free() */
+ dbus_timeout_set_data(timeout, NULL, NULL);
+}
+
+static void timeout_toggled(DBusTimeout *timeout, void *data)
+{
+ if (dbus_timeout_get_enabled(timeout))
+ add_timeout(timeout, data);
+ else
+ remove_timeout(timeout, data);
+}
+
+static void dispatch_status(DBusConnection *conn,
+ DBusDispatchStatus status, void *data)
+{
+ if (!dbus_connection_get_is_connected(conn))
+ return;
+
+ queue_dispatch(conn, status);
+}
+
+static inline void setup_dbus_with_main_loop(DBusConnection *conn)
+{
+ dbus_connection_set_watch_functions(conn, add_watch, remove_watch,
+ watch_toggled, conn, NULL);
+
+ dbus_connection_set_timeout_functions(conn, add_timeout, remove_timeout,
+ timeout_toggled, NULL, NULL);
+
+ dbus_connection_set_dispatch_status_function(conn, dispatch_status,
+ NULL, NULL);
+}
+
+static gboolean setup_bus(DBusConnection *conn, const char *name,
+ DBusError *error)
+{
+ gboolean result;
+ DBusDispatchStatus status;
+
+ if (name != NULL) {
+ result = g_dbus_request_name(conn, name, error);
+
+ if (error != NULL) {
+ if (dbus_error_is_set(error) == TRUE)
+ return FALSE;
+ }
+
+ if (result == FALSE)
+ return FALSE;
+ }
+
+ setup_dbus_with_main_loop(conn);
+
+ status = dbus_connection_get_dispatch_status(conn);
+ queue_dispatch(conn, status);
+
+ return TRUE;
+}
+
+DBusConnection *g_dbus_setup_bus(DBusBusType type, const char *name,
+ DBusError *error)
+{
+ DBusConnection *conn;
+
+ conn = dbus_bus_get(type, error);
+
+ if (error != NULL) {
+ if (dbus_error_is_set(error) == TRUE)
+ return NULL;
+ }
+
+ if (conn == NULL)
+ return NULL;
+
+ if (setup_bus(conn, name, error) == FALSE) {
+ dbus_connection_unref(conn);
+ return NULL;
+ }
+
+ return conn;
+}
+
+DBusConnection *g_dbus_setup_private(DBusBusType type, const char *name,
+ DBusError *error)
+{
+ DBusConnection *conn;
+
+ conn = dbus_bus_get_private(type, error);
+
+ if (error != NULL) {
+ if (dbus_error_is_set(error) == TRUE)
+ return NULL;
+ }
+
+ if (conn == NULL)
+ return NULL;
+
+ if (setup_bus(conn, name, error) == FALSE) {
+ dbus_connection_unref(conn);
+ return NULL;
+ }
+
+ return conn;
+}
+
+gboolean g_dbus_request_name(DBusConnection *connection, const char *name,
+ DBusError *error)
+{
+ int result;
+
+ result = dbus_bus_request_name(connection, name,
+ DBUS_NAME_FLAG_DO_NOT_QUEUE, error);
+
+ if (error != NULL) {
+ if (dbus_error_is_set(error) == TRUE)
+ return FALSE;
+ }
+
+ if (result != DBUS_REQUEST_NAME_REPLY_PRIMARY_OWNER) {
+ if (error != NULL)
+ dbus_set_error(error, name, "Name already in use");
+
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+gboolean g_dbus_set_disconnect_function(DBusConnection *connection,
+ GDBusWatchFunction function,
+ void *user_data, DBusFreeFunction destroy)
+{
+ struct disconnect_data *dc_data;
+
+ dc_data = g_new0(struct disconnect_data, 1);
+
+ dc_data->function = function;
+ dc_data->user_data = user_data;
+
+ dbus_connection_set_exit_on_disconnect(connection, FALSE);
+
+ if (g_dbus_add_signal_watch(connection, NULL, NULL,
+ DBUS_INTERFACE_LOCAL, "Disconnected",
+ disconnected_signal, dc_data, g_free) == 0) {
+ error("Failed to add watch for D-Bus Disconnected signal");
+ g_free(dc_data);
+ return FALSE;
+ }
+
+ return TRUE;
+}
diff --git a/gdbus/object.c b/gdbus/object.c
new file mode 100644
index 0000000..3327cf4
--- /dev/null
+++ b/gdbus/object.c
@@ -0,0 +1,1776 @@
+/*
+ *
+ * D-Bus helper library
+ *
+ * Copyright (C) 2004-2011 Marcel Holtmann <marcel@holtmann.org>
+ *
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <string.h>
+
+#include <glib.h>
+#include <dbus/dbus.h>
+
+#include "gdbus.h"
+
+#define info(fmt...)
+#define error(fmt...)
+#define debug(fmt...)
+
+#define DBUS_INTERFACE_OBJECT_MANAGER "org.freedesktop.DBus.ObjectManager"
+
+#ifndef DBUS_ERROR_UNKNOWN_PROPERTY
+#define DBUS_ERROR_UNKNOWN_PROPERTY "org.freedesktop.DBus.Error.UnknownProperty"
+#endif
+
+#ifndef DBUS_ERROR_PROPERTY_READ_ONLY
+#define DBUS_ERROR_PROPERTY_READ_ONLY "org.freedesktop.DBus.Error.PropertyReadOnly"
+#endif
+
+struct generic_data {
+ unsigned int refcount;
+ DBusConnection *conn;
+ char *path;
+ GSList *interfaces;
+ GSList *objects;
+ GSList *added;
+ GSList *removed;
+ guint process_id;
+ gboolean pending_prop;
+ char *introspect;
+ struct generic_data *parent;
+};
+
+struct interface_data {
+ char *name;
+ const GDBusMethodTable *methods;
+ const GDBusSignalTable *signals;
+ const GDBusPropertyTable *properties;
+ GSList *pending_prop;
+ void *user_data;
+ GDBusDestroyFunction destroy;
+};
+
+struct security_data {
+ GDBusPendingReply pending;
+ DBusMessage *message;
+ const GDBusMethodTable *method;
+ void *iface_user_data;
+};
+
+struct property_data {
+ DBusConnection *conn;
+ GDBusPendingPropertySet id;
+ DBusMessage *message;
+};
+
+static int global_flags = 0;
+static struct generic_data *root;
+
+static gboolean process_changes(gpointer user_data);
+static void process_properties_from_interface(struct generic_data *data,
+ struct interface_data *iface);
+static void process_property_changes(struct generic_data *data);
+
+static void print_arguments(GString *gstr, const GDBusArgInfo *args,
+ const char *direction)
+{
+ for (; args && args->name; args++) {
+ g_string_append_printf(gstr,
+ "<arg name=\"%s\" type=\"%s\"",
+ args->name, args->signature);
+
+ if (direction)
+ g_string_append_printf(gstr,
+ " direction=\"%s\"/>\n", direction);
+ else
+ g_string_append_printf(gstr, "/>\n");
+
+ }
+}
+
+#define G_DBUS_ANNOTATE(name_, value_) \
+ "<annotation name=\"org.freedesktop.DBus." name_ "\" " \
+ "value=\"" value_ "\"/>"
+
+#define G_DBUS_ANNOTATE_DEPRECATED \
+ G_DBUS_ANNOTATE("Deprecated", "true")
+
+#define G_DBUS_ANNOTATE_NOREPLY \
+ G_DBUS_ANNOTATE("Method.NoReply", "true")
+
+static gboolean check_experimental(int flags, int flag)
+{
+ if (!(flags & flag))
+ return FALSE;
+
+ return !(global_flags & G_DBUS_FLAG_ENABLE_EXPERIMENTAL);
+}
+
+static void generate_interface_xml(GString *gstr, struct interface_data *iface)
+{
+ const GDBusMethodTable *method;
+ const GDBusSignalTable *signal;
+ const GDBusPropertyTable *property;
+
+ for (method = iface->methods; method && method->name; method++) {
+ if (check_experimental(method->flags,
+ G_DBUS_METHOD_FLAG_EXPERIMENTAL))
+ continue;
+
+ g_string_append_printf(gstr, "<method name=\"%s\">",
+ method->name);
+ print_arguments(gstr, method->in_args, "in");
+ print_arguments(gstr, method->out_args, "out");
+
+ if (method->flags & G_DBUS_METHOD_FLAG_DEPRECATED)
+ g_string_append_printf(gstr,
+ G_DBUS_ANNOTATE_DEPRECATED);
+
+ if (method->flags & G_DBUS_METHOD_FLAG_NOREPLY)
+ g_string_append_printf(gstr, G_DBUS_ANNOTATE_NOREPLY);
+
+ g_string_append_printf(gstr, "</method>");
+ }
+
+ for (signal = iface->signals; signal && signal->name; signal++) {
+ if (check_experimental(signal->flags,
+ G_DBUS_SIGNAL_FLAG_EXPERIMENTAL))
+ continue;
+
+ g_string_append_printf(gstr, "<signal name=\"%s\">",
+ signal->name);
+ print_arguments(gstr, signal->args, NULL);
+
+ if (signal->flags & G_DBUS_SIGNAL_FLAG_DEPRECATED)
+ g_string_append_printf(gstr,
+ G_DBUS_ANNOTATE_DEPRECATED);
+
+ g_string_append_printf(gstr, "</signal>\n");
+ }
+
+ for (property = iface->properties; property && property->name;
+ property++) {
+ if (check_experimental(property->flags,
+ G_DBUS_PROPERTY_FLAG_EXPERIMENTAL))
+ continue;
+
+ g_string_append_printf(gstr, "<property name=\"%s\""
+ " type=\"%s\" access=\"%s%s\">",
+ property->name, property->type,
+ property->get ? "read" : "",
+ property->set ? "write" : "");
+
+ if (property->flags & G_DBUS_PROPERTY_FLAG_DEPRECATED)
+ g_string_append_printf(gstr,
+ G_DBUS_ANNOTATE_DEPRECATED);
+
+ g_string_append_printf(gstr, "</property>");
+ }
+}
+
+static void generate_introspection_xml(DBusConnection *conn,
+ struct generic_data *data, const char *path)
+{
+ GSList *list;
+ GString *gstr;
+ char **children;
+ int i;
+
+ g_free(data->introspect);
+
+ gstr = g_string_new(DBUS_INTROSPECT_1_0_XML_DOCTYPE_DECL_NODE);
+
+ g_string_append_printf(gstr, "<node>");
+
+ for (list = data->interfaces; list; list = list->next) {
+ struct interface_data *iface = list->data;
+
+ g_string_append_printf(gstr, "<interface name=\"%s\">",
+ iface->name);
+
+ generate_interface_xml(gstr, iface);
+
+ g_string_append_printf(gstr, "</interface>");
+ }
+
+ if (!dbus_connection_list_registered(conn, path, &children))
+ goto done;
+
+ for (i = 0; children[i]; i++)
+ g_string_append_printf(gstr, "<node name=\"%s\"/>",
+ children[i]);
+
+ dbus_free_string_array(children);
+
+done:
+ g_string_append_printf(gstr, "</node>");
+
+ data->introspect = g_string_free(gstr, FALSE);
+}
+
+static DBusMessage *introspect(DBusConnection *connection,
+ DBusMessage *message, void *user_data)
+{
+ struct generic_data *data = user_data;
+ DBusMessage *reply;
+
+ if (data->introspect == NULL)
+ generate_introspection_xml(connection, data,
+ dbus_message_get_path(message));
+
+ reply = dbus_message_new_method_return(message);
+ if (reply == NULL)
+ return NULL;
+
+ dbus_message_append_args(reply, DBUS_TYPE_STRING, &data->introspect,
+ DBUS_TYPE_INVALID);
+
+ return reply;
+}
+
+static DBusHandlerResult process_message(DBusConnection *connection,
+ DBusMessage *message, const GDBusMethodTable *method,
+ void *iface_user_data)
+{
+ DBusMessage *reply;
+
+ reply = method->function(connection, message, iface_user_data);
+
+ if (method->flags & G_DBUS_METHOD_FLAG_NOREPLY) {
+ if (reply != NULL)
+ dbus_message_unref(reply);
+ return DBUS_HANDLER_RESULT_HANDLED;
+ }
+
+ if (method->flags & G_DBUS_METHOD_FLAG_ASYNC) {
+ if (reply == NULL)
+ return DBUS_HANDLER_RESULT_HANDLED;
+ }
+
+ if (reply == NULL)
+ return DBUS_HANDLER_RESULT_NEED_MEMORY;
+
+ dbus_connection_send(connection, reply, NULL);
+ dbus_message_unref(reply);
+
+ return DBUS_HANDLER_RESULT_HANDLED;
+}
+
+static GDBusPendingReply next_pending = 1;
+static GSList *pending_security = NULL;
+
+static const GDBusSecurityTable *security_table = NULL;
+
+void g_dbus_pending_success(DBusConnection *connection,
+ GDBusPendingReply pending)
+{
+ GSList *list;
+
+ for (list = pending_security; list; list = list->next) {
+ struct security_data *secdata = list->data;
+
+ if (secdata->pending != pending)
+ continue;
+
+ pending_security = g_slist_remove(pending_security, secdata);
+
+ process_message(connection, secdata->message,
+ secdata->method, secdata->iface_user_data);
+
+ dbus_message_unref(secdata->message);
+ g_free(secdata);
+ return;
+ }
+}
+
+void g_dbus_pending_error_valist(DBusConnection *connection,
+ GDBusPendingReply pending, const char *name,
+ const char *format, va_list args)
+{
+ GSList *list;
+
+ for (list = pending_security; list; list = list->next) {
+ struct security_data *secdata = list->data;
+ DBusMessage *reply;
+
+ if (secdata->pending != pending)
+ continue;
+
+ pending_security = g_slist_remove(pending_security, secdata);
+
+ reply = g_dbus_create_error_valist(secdata->message,
+ name, format, args);
+ if (reply != NULL) {
+ dbus_connection_send(connection, reply, NULL);
+ dbus_message_unref(reply);
+ }
+
+ dbus_message_unref(secdata->message);
+ g_free(secdata);
+ return;
+ }
+}
+
+void g_dbus_pending_error(DBusConnection *connection,
+ GDBusPendingReply pending,
+ const char *name, const char *format, ...)
+{
+ va_list args;
+
+ va_start(args, format);
+
+ g_dbus_pending_error_valist(connection, pending, name, format, args);
+
+ va_end(args);
+}
+
+int polkit_check_authorization(DBusConnection *conn,
+ const char *action, gboolean interaction,
+ void (*function) (dbus_bool_t authorized,
+ void *user_data),
+ void *user_data, int timeout);
+
+struct builtin_security_data {
+ DBusConnection *conn;
+ GDBusPendingReply pending;
+};
+
+static void builtin_security_result(dbus_bool_t authorized, void *user_data)
+{
+ struct builtin_security_data *data = user_data;
+
+ if (authorized == TRUE)
+ g_dbus_pending_success(data->conn, data->pending);
+ else
+ g_dbus_pending_error(data->conn, data->pending,
+ DBUS_ERROR_AUTH_FAILED, NULL);
+
+ g_free(data);
+}
+
+static void builtin_security_function(DBusConnection *conn,
+ const char *action,
+ gboolean interaction,
+ GDBusPendingReply pending)
+{
+ struct builtin_security_data *data;
+
+ data = g_new0(struct builtin_security_data, 1);
+ data->conn = conn;
+ data->pending = pending;
+
+ if (polkit_check_authorization(conn, action, interaction,
+ builtin_security_result, data, 30000) < 0)
+ g_dbus_pending_error(conn, pending, NULL, NULL);
+}
+
+static gboolean check_privilege(DBusConnection *conn, DBusMessage *msg,
+ const GDBusMethodTable *method, void *iface_user_data)
+{
+ const GDBusSecurityTable *security;
+
+ for (security = security_table; security && security->privilege;
+ security++) {
+ struct security_data *secdata;
+ gboolean interaction;
+
+ if (security->privilege != method->privilege)
+ continue;
+
+ secdata = g_new(struct security_data, 1);
+ secdata->pending = next_pending++;
+ secdata->message = dbus_message_ref(msg);
+ secdata->method = method;
+ secdata->iface_user_data = iface_user_data;
+
+ pending_security = g_slist_prepend(pending_security, secdata);
+
+ if (security->flags & G_DBUS_SECURITY_FLAG_ALLOW_INTERACTION)
+ interaction = TRUE;
+ else
+ interaction = FALSE;
+
+ if (!(security->flags & G_DBUS_SECURITY_FLAG_BUILTIN) &&
+ security->function)
+ security->function(conn, security->action,
+ interaction, secdata->pending);
+ else
+ builtin_security_function(conn, security->action,
+ interaction, secdata->pending);
+
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+static GDBusPendingPropertySet next_pending_property = 1;
+static GSList *pending_property_set;
+
+static struct property_data *remove_pending_property_data(
+ GDBusPendingPropertySet id)
+{
+ struct property_data *propdata;
+ GSList *l;
+
+ for (l = pending_property_set; l != NULL; l = l->next) {
+ propdata = l->data;
+ if (propdata->id != id)
+ continue;
+
+ break;
+ }
+
+ if (l == NULL)
+ return NULL;
+
+ pending_property_set = g_slist_delete_link(pending_property_set, l);
+
+ return propdata;
+}
+
+void g_dbus_pending_property_success(GDBusPendingPropertySet id)
+{
+ struct property_data *propdata;
+
+ propdata = remove_pending_property_data(id);
+ if (propdata == NULL)
+ return;
+
+ g_dbus_send_reply(propdata->conn, propdata->message,
+ DBUS_TYPE_INVALID);
+ dbus_message_unref(propdata->message);
+ g_free(propdata);
+}
+
+void g_dbus_pending_property_error_valist(GDBusPendingReply id,
+ const char *name, const char *format,
+ va_list args)
+{
+ struct property_data *propdata;
+ DBusMessage *reply;
+
+ propdata = remove_pending_property_data(id);
+ if (propdata == NULL)
+ return;
+
+ reply = g_dbus_create_error_valist(propdata->message, name, format,
+ args);
+ if (reply != NULL) {
+ dbus_connection_send(propdata->conn, reply, NULL);
+ dbus_message_unref(reply);
+ }
+
+ dbus_message_unref(propdata->message);
+ g_free(propdata);
+}
+
+void g_dbus_pending_property_error(GDBusPendingReply id, const char *name,
+ const char *format, ...)
+{
+ va_list args;
+
+ va_start(args, format);
+
+ g_dbus_pending_property_error_valist(id, name, format, args);
+
+ va_end(args);
+}
+
+static void reset_parent(gpointer data, gpointer user_data)
+{
+ struct generic_data *child = data;
+ struct generic_data *parent = user_data;
+
+ child->parent = parent;
+}
+
+static void append_property(struct interface_data *iface,
+ const GDBusPropertyTable *p, DBusMessageIter *dict)
+{
+ DBusMessageIter entry, value;
+
+ dbus_message_iter_open_container(dict, DBUS_TYPE_DICT_ENTRY, NULL,
+ &entry);
+ dbus_message_iter_append_basic(&entry, DBUS_TYPE_STRING, &p->name);
+ dbus_message_iter_open_container(&entry, DBUS_TYPE_VARIANT, p->type,
+ &value);
+
+ p->get(p, &value, iface->user_data);
+
+ dbus_message_iter_close_container(&entry, &value);
+ dbus_message_iter_close_container(dict, &entry);
+}
+
+static void append_properties(struct interface_data *data,
+ DBusMessageIter *iter)
+{
+ DBusMessageIter dict;
+ const GDBusPropertyTable *p;
+
+ dbus_message_iter_open_container(iter, DBUS_TYPE_ARRAY,
+ DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING
+ DBUS_TYPE_STRING_AS_STRING
+ DBUS_TYPE_VARIANT_AS_STRING
+ DBUS_DICT_ENTRY_END_CHAR_AS_STRING, &dict);
+
+ for (p = data->properties; p && p->name; p++) {
+ if (check_experimental(p->flags,
+ G_DBUS_PROPERTY_FLAG_EXPERIMENTAL))
+ continue;
+
+ if (p->get == NULL)
+ continue;
+
+ if (p->exists != NULL && !p->exists(p, data->user_data))
+ continue;
+
+ append_property(data, p, &dict);
+ }
+
+ dbus_message_iter_close_container(iter, &dict);
+}
+
+static void append_interface(gpointer data, gpointer user_data)
+{
+ struct interface_data *iface = data;
+ DBusMessageIter *array = user_data;
+ DBusMessageIter entry;
+
+ dbus_message_iter_open_container(array, DBUS_TYPE_DICT_ENTRY, NULL,
+ &entry);
+ dbus_message_iter_append_basic(&entry, DBUS_TYPE_STRING, &iface->name);
+ append_properties(data, &entry);
+ dbus_message_iter_close_container(array, &entry);
+}
+
+static void emit_interfaces_added(struct generic_data *data)
+{
+ DBusMessage *signal;
+ DBusMessageIter iter, array;
+
+ if (root == NULL || data == root)
+ return;
+
+ signal = dbus_message_new_signal(root->path,
+ DBUS_INTERFACE_OBJECT_MANAGER,
+ "InterfacesAdded");
+ if (signal == NULL)
+ return;
+
+ dbus_message_iter_init_append(signal, &iter);
+ dbus_message_iter_append_basic(&iter, DBUS_TYPE_OBJECT_PATH,
+ &data->path);
+
+ dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY,
+ DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING
+ DBUS_TYPE_STRING_AS_STRING
+ DBUS_TYPE_ARRAY_AS_STRING
+ DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING
+ DBUS_TYPE_STRING_AS_STRING
+ DBUS_TYPE_VARIANT_AS_STRING
+ DBUS_DICT_ENTRY_END_CHAR_AS_STRING
+ DBUS_DICT_ENTRY_END_CHAR_AS_STRING, &array);
+
+ g_slist_foreach(data->added, append_interface, &array);
+ g_slist_free(data->added);
+ data->added = NULL;
+
+ dbus_message_iter_close_container(&iter, &array);
+
+ g_dbus_send_message(data->conn, signal);
+}
+
+static struct interface_data *find_interface(GSList *interfaces,
+ const char *name)
+{
+ GSList *list;
+
+ if (name == NULL)
+ return NULL;
+
+ for (list = interfaces; list; list = list->next) {
+ struct interface_data *iface = list->data;
+ if (!strcmp(name, iface->name))
+ return iface;
+ }
+
+ return NULL;
+}
+
+static gboolean g_dbus_args_have_signature(const GDBusArgInfo *args,
+ DBusMessage *message)
+{
+ const char *sig = dbus_message_get_signature(message);
+ const char *p = NULL;
+
+ for (; args && args->signature && *sig; args++) {
+ p = args->signature;
+
+ for (; *sig && *p; sig++, p++) {
+ if (*p != *sig)
+ return FALSE;
+ }
+ }
+
+ if (*sig || (p && *p) || (args && args->signature))
+ return FALSE;
+
+ return TRUE;
+}
+
+static gboolean remove_interface(struct generic_data *data, const char *name)
+{
+ struct interface_data *iface;
+
+ iface = find_interface(data->interfaces, name);
+ if (iface == NULL)
+ return FALSE;
+
+ process_properties_from_interface(data, iface);
+
+ data->interfaces = g_slist_remove(data->interfaces, iface);
+
+ if (iface->destroy) {
+ iface->destroy(iface->user_data);
+ iface->user_data = NULL;
+ }
+
+ /*
+ * Interface being removed was just added, on the same mainloop
+ * iteration? Don't send any signal
+ */
+ if (g_slist_find(data->added, iface)) {
+ data->added = g_slist_remove(data->added, iface);
+ g_free(iface->name);
+ g_free(iface);
+ return TRUE;
+ }
+
+ if (data->parent == NULL) {
+ g_free(iface->name);
+ g_free(iface);
+ return TRUE;
+ }
+
+ data->removed = g_slist_prepend(data->removed, iface->name);
+ g_free(iface);
+
+ if (data->process_id > 0)
+ return TRUE;
+
+ data->process_id = g_idle_add(process_changes, data);
+
+ return TRUE;
+}
+
+static struct generic_data *invalidate_parent_data(DBusConnection *conn,
+ const char *child_path)
+{
+ struct generic_data *data = NULL, *child = NULL, *parent = NULL;
+ char *parent_path, *slash;
+
+ parent_path = g_strdup(child_path);
+ slash = strrchr(parent_path, '/');
+ if (slash == NULL)
+ goto done;
+
+ if (slash == parent_path && parent_path[1] != '\0')
+ parent_path[1] = '\0';
+ else
+ *slash = '\0';
+
+ if (!strlen(parent_path))
+ goto done;
+
+ if (dbus_connection_get_object_path_data(conn, parent_path,
+ (void *) &data) == FALSE) {
+ goto done;
+ }
+
+ parent = invalidate_parent_data(conn, parent_path);
+
+ if (data == NULL) {
+ data = parent;
+ if (data == NULL)
+ goto done;
+ }
+
+ g_free(data->introspect);
+ data->introspect = NULL;
+
+ if (!dbus_connection_get_object_path_data(conn, child_path,
+ (void *) &child))
+ goto done;
+
+ if (child == NULL || g_slist_find(data->objects, child) != NULL)
+ goto done;
+
+ data->objects = g_slist_prepend(data->objects, child);
+ child->parent = data;
+
+done:
+ g_free(parent_path);
+ return data;
+}
+
+static inline const GDBusPropertyTable *find_property(const GDBusPropertyTable *properties,
+ const char *name)
+{
+ const GDBusPropertyTable *p;
+
+ for (p = properties; p && p->name; p++) {
+ if (strcmp(name, p->name) != 0)
+ continue;
+
+ if (check_experimental(p->flags,
+ G_DBUS_PROPERTY_FLAG_EXPERIMENTAL))
+ break;
+
+ return p;
+ }
+
+ return NULL;
+}
+
+static DBusMessage *properties_get(DBusConnection *connection,
+ DBusMessage *message, void *user_data)
+{
+ struct generic_data *data = user_data;
+ struct interface_data *iface;
+ const GDBusPropertyTable *property;
+ const char *interface, *name;
+ DBusMessageIter iter, value;
+ DBusMessage *reply;
+
+ if (!dbus_message_get_args(message, NULL,
+ DBUS_TYPE_STRING, &interface,
+ DBUS_TYPE_STRING, &name,
+ DBUS_TYPE_INVALID))
+ return NULL;
+
+ iface = find_interface(data->interfaces, interface);
+ if (iface == NULL)
+ return g_dbus_create_error(message, DBUS_ERROR_INVALID_ARGS,
+ "No such interface '%s'", interface);
+
+ property = find_property(iface->properties, name);
+ if (property == NULL)
+ return g_dbus_create_error(message, DBUS_ERROR_INVALID_ARGS,
+ "No such property '%s'", name);
+
+ if (property->exists != NULL &&
+ !property->exists(property, iface->user_data))
+ return g_dbus_create_error(message, DBUS_ERROR_INVALID_ARGS,
+ "No such property '%s'", name);
+
+ if (property->get == NULL)
+ return g_dbus_create_error(message, DBUS_ERROR_INVALID_ARGS,
+ "Property '%s' is not readable", name);
+
+ reply = dbus_message_new_method_return(message);
+ if (reply == NULL)
+ return NULL;
+
+ dbus_message_iter_init_append(reply, &iter);
+ dbus_message_iter_open_container(&iter, DBUS_TYPE_VARIANT,
+ property->type, &value);
+
+ if (!property->get(property, &value, iface->user_data)) {
+ dbus_message_unref(reply);
+ return NULL;
+ }
+
+ dbus_message_iter_close_container(&iter, &value);
+
+ return reply;
+}
+
+static DBusMessage *properties_get_all(DBusConnection *connection,
+ DBusMessage *message, void *user_data)
+{
+ struct generic_data *data = user_data;
+ struct interface_data *iface;
+ const char *interface;
+ DBusMessageIter iter;
+ DBusMessage *reply;
+
+ if (!dbus_message_get_args(message, NULL,
+ DBUS_TYPE_STRING, &interface,
+ DBUS_TYPE_INVALID))
+ return NULL;
+
+ iface = find_interface(data->interfaces, interface);
+ if (iface == NULL)
+ return g_dbus_create_error(message, DBUS_ERROR_INVALID_ARGS,
+ "No such interface '%s'", interface);
+
+ reply = dbus_message_new_method_return(message);
+ if (reply == NULL)
+ return NULL;
+
+ dbus_message_iter_init_append(reply, &iter);
+
+ append_properties(iface, &iter);
+
+ return reply;
+}
+
+static DBusMessage *properties_set(DBusConnection *connection,
+ DBusMessage *message, void *user_data)
+{
+ struct generic_data *data = user_data;
+ DBusMessageIter iter, sub;
+ struct interface_data *iface;
+ const GDBusPropertyTable *property;
+ const char *name, *interface;
+ struct property_data *propdata;
+ gboolean valid_signature;
+ char *signature;
+
+ if (!dbus_message_iter_init(message, &iter))
+ return g_dbus_create_error(message, DBUS_ERROR_INVALID_ARGS,
+ "No arguments given");
+
+ if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_STRING)
+ return g_dbus_create_error(message, DBUS_ERROR_INVALID_ARGS,
+ "Invalid argument type: '%c'",
+ dbus_message_iter_get_arg_type(&iter));
+
+ dbus_message_iter_get_basic(&iter, &interface);
+ dbus_message_iter_next(&iter);
+
+ if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_STRING)
+ return g_dbus_create_error(message, DBUS_ERROR_INVALID_ARGS,
+ "Invalid argument type: '%c'",
+ dbus_message_iter_get_arg_type(&iter));
+
+ dbus_message_iter_get_basic(&iter, &name);
+ dbus_message_iter_next(&iter);
+
+ if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_VARIANT)
+ return g_dbus_create_error(message, DBUS_ERROR_INVALID_ARGS,
+ "Invalid argument type: '%c'",
+ dbus_message_iter_get_arg_type(&iter));
+
+ dbus_message_iter_recurse(&iter, &sub);
+
+ iface = find_interface(data->interfaces, interface);
+ if (iface == NULL)
+ return g_dbus_create_error(message, DBUS_ERROR_INVALID_ARGS,
+ "No such interface '%s'", interface);
+
+ property = find_property(iface->properties, name);
+ if (property == NULL)
+ return g_dbus_create_error(message,
+ DBUS_ERROR_UNKNOWN_PROPERTY,
+ "No such property '%s'", name);
+
+ if (property->set == NULL)
+ return g_dbus_create_error(message,
+ DBUS_ERROR_PROPERTY_READ_ONLY,
+ "Property '%s' is not writable", name);
+
+ if (property->exists != NULL &&
+ !property->exists(property, iface->user_data))
+ return g_dbus_create_error(message,
+ DBUS_ERROR_UNKNOWN_PROPERTY,
+ "No such property '%s'", name);
+
+ signature = dbus_message_iter_get_signature(&sub);
+ valid_signature = strcmp(signature, property->type) ? FALSE : TRUE;
+ dbus_free(signature);
+ if (!valid_signature)
+ return g_dbus_create_error(message,
+ DBUS_ERROR_INVALID_SIGNATURE,
+ "Invalid signature for '%s'", name);
+
+ propdata = g_new(struct property_data, 1);
+ propdata->id = next_pending_property++;
+ propdata->message = dbus_message_ref(message);
+ propdata->conn = connection;
+ pending_property_set = g_slist_prepend(pending_property_set, propdata);
+
+ property->set(property, &sub, propdata->id, iface->user_data);
+
+ return NULL;
+}
+
+static const GDBusMethodTable properties_methods[] = {
+ { GDBUS_METHOD("Get",
+ GDBUS_ARGS({ "interface", "s" }, { "name", "s" }),
+ GDBUS_ARGS({ "value", "v" }),
+ properties_get) },
+ { GDBUS_ASYNC_METHOD("Set",
+ GDBUS_ARGS({ "interface", "s" }, { "name", "s" },
+ { "value", "v" }),
+ NULL,
+ properties_set) },
+ { GDBUS_METHOD("GetAll",
+ GDBUS_ARGS({ "interface", "s" }),
+ GDBUS_ARGS({ "properties", "a{sv}" }),
+ properties_get_all) },
+ { }
+};
+
+static const GDBusSignalTable properties_signals[] = {
+ { GDBUS_SIGNAL("PropertiesChanged",
+ GDBUS_ARGS({ "interface", "s" },
+ { "changed_properties", "a{sv}" },
+ { "invalidated_properties", "as"})) },
+ { }
+};
+
+static void append_name(gpointer data, gpointer user_data)
+{
+ char *name = data;
+ DBusMessageIter *iter = user_data;
+
+ dbus_message_iter_append_basic(iter, DBUS_TYPE_STRING, &name);
+}
+
+static void emit_interfaces_removed(struct generic_data *data)
+{
+ DBusMessage *signal;
+ DBusMessageIter iter, array;
+
+ if (root == NULL || data == root)
+ return;
+
+ signal = dbus_message_new_signal(root->path,
+ DBUS_INTERFACE_OBJECT_MANAGER,
+ "InterfacesRemoved");
+ if (signal == NULL)
+ return;
+
+ dbus_message_iter_init_append(signal, &iter);
+ dbus_message_iter_append_basic(&iter, DBUS_TYPE_OBJECT_PATH,
+ &data->path);
+ dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY,
+ DBUS_TYPE_STRING_AS_STRING, &array);
+
+ g_slist_foreach(data->removed, append_name, &array);
+ g_slist_free_full(data->removed, g_free);
+ data->removed = NULL;
+
+ dbus_message_iter_close_container(&iter, &array);
+
+ g_dbus_send_message(data->conn, signal);
+}
+
+static gboolean process_changes(gpointer user_data)
+{
+ struct generic_data *data = user_data;
+
+ data->process_id = 0;
+
+ if (data->added != NULL)
+ emit_interfaces_added(data);
+
+ /* Flush pending properties */
+ if (data->pending_prop == TRUE)
+ process_property_changes(data);
+
+ if (data->removed != NULL)
+ emit_interfaces_removed(data);
+
+ return FALSE;
+}
+
+static void generic_unregister(DBusConnection *connection, void *user_data)
+{
+ struct generic_data *data = user_data;
+ struct generic_data *parent = data->parent;
+
+ if (parent != NULL)
+ parent->objects = g_slist_remove(parent->objects, data);
+
+ if (data->process_id > 0) {
+ g_source_remove(data->process_id);
+ process_changes(data);
+ }
+
+ g_slist_foreach(data->objects, reset_parent, data->parent);
+ g_slist_free(data->objects);
+
+ dbus_connection_unref(data->conn);
+ g_free(data->introspect);
+ g_free(data->path);
+ g_free(data);
+}
+
+static DBusHandlerResult generic_message(DBusConnection *connection,
+ DBusMessage *message, void *user_data)
+{
+ struct generic_data *data = user_data;
+ struct interface_data *iface;
+ const GDBusMethodTable *method;
+ const char *interface;
+
+ interface = dbus_message_get_interface(message);
+
+ iface = find_interface(data->interfaces, interface);
+ if (iface == NULL)
+ return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
+
+ for (method = iface->methods; method &&
+ method->name && method->function; method++) {
+
+ if (dbus_message_is_method_call(message, iface->name,
+ method->name) == FALSE)
+ continue;
+
+ if (check_experimental(method->flags,
+ G_DBUS_METHOD_FLAG_EXPERIMENTAL))
+ return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
+
+ if (g_dbus_args_have_signature(method->in_args,
+ message) == FALSE)
+ continue;
+
+ if (check_privilege(connection, message, method,
+ iface->user_data) == TRUE)
+ return DBUS_HANDLER_RESULT_HANDLED;
+
+ return process_message(connection, message, method,
+ iface->user_data);
+ }
+
+ return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
+}
+
+static DBusObjectPathVTable generic_table = {
+ .unregister_function = generic_unregister,
+ .message_function = generic_message,
+};
+
+static const GDBusMethodTable introspect_methods[] = {
+ { GDBUS_METHOD("Introspect", NULL,
+ GDBUS_ARGS({ "xml", "s" }), introspect) },
+ { }
+};
+
+static void append_interfaces(struct generic_data *data, DBusMessageIter *iter)
+{
+ DBusMessageIter array;
+ GSList *l;
+
+ dbus_message_iter_open_container(iter, DBUS_TYPE_ARRAY,
+ DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING
+ DBUS_TYPE_STRING_AS_STRING
+ DBUS_TYPE_ARRAY_AS_STRING
+ DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING
+ DBUS_TYPE_STRING_AS_STRING
+ DBUS_TYPE_VARIANT_AS_STRING
+ DBUS_DICT_ENTRY_END_CHAR_AS_STRING
+ DBUS_DICT_ENTRY_END_CHAR_AS_STRING, &array);
+
+ for (l = data->interfaces; l != NULL; l = l->next) {
+ if (g_slist_find(data->added, l->data))
+ continue;
+
+ append_interface(l->data, &array);
+ }
+
+ dbus_message_iter_close_container(iter, &array);
+}
+
+static void append_object(gpointer data, gpointer user_data)
+{
+ struct generic_data *child = data;
+ DBusMessageIter *array = user_data;
+ DBusMessageIter entry;
+
+ dbus_message_iter_open_container(array, DBUS_TYPE_DICT_ENTRY, NULL,
+ &entry);
+ dbus_message_iter_append_basic(&entry, DBUS_TYPE_OBJECT_PATH,
+ &child->path);
+ append_interfaces(child, &entry);
+ dbus_message_iter_close_container(array, &entry);
+
+ g_slist_foreach(child->objects, append_object, user_data);
+}
+
+static DBusMessage *get_objects(DBusConnection *connection,
+ DBusMessage *message, void *user_data)
+{
+ struct generic_data *data = user_data;
+ DBusMessage *reply;
+ DBusMessageIter iter;
+ DBusMessageIter array;
+
+ reply = dbus_message_new_method_return(message);
+ if (reply == NULL)
+ return NULL;
+
+ dbus_message_iter_init_append(reply, &iter);
+
+ dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY,
+ DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING
+ DBUS_TYPE_OBJECT_PATH_AS_STRING
+ DBUS_TYPE_ARRAY_AS_STRING
+ DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING
+ DBUS_TYPE_STRING_AS_STRING
+ DBUS_TYPE_ARRAY_AS_STRING
+ DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING
+ DBUS_TYPE_STRING_AS_STRING
+ DBUS_TYPE_VARIANT_AS_STRING
+ DBUS_DICT_ENTRY_END_CHAR_AS_STRING
+ DBUS_DICT_ENTRY_END_CHAR_AS_STRING
+ DBUS_DICT_ENTRY_END_CHAR_AS_STRING,
+ &array);
+
+ g_slist_foreach(data->objects, append_object, &array);
+
+ dbus_message_iter_close_container(&iter, &array);
+
+ return reply;
+}
+
+static const GDBusMethodTable manager_methods[] = {
+ { GDBUS_METHOD("GetManagedObjects", NULL,
+ GDBUS_ARGS({ "objects", "a{oa{sa{sv}}}" }), get_objects) },
+ { }
+};
+
+static const GDBusSignalTable manager_signals[] = {
+ { GDBUS_SIGNAL("InterfacesAdded",
+ GDBUS_ARGS({ "object", "o" },
+ { "interfaces", "a{sa{sv}}" })) },
+ { GDBUS_SIGNAL("InterfacesRemoved",
+ GDBUS_ARGS({ "object", "o" }, { "interfaces", "as" })) },
+ { }
+};
+
+static gboolean add_interface(struct generic_data *data,
+ const char *name,
+ const GDBusMethodTable *methods,
+ const GDBusSignalTable *signals,
+ const GDBusPropertyTable *properties,
+ void *user_data,
+ GDBusDestroyFunction destroy)
+{
+ struct interface_data *iface;
+ const GDBusMethodTable *method;
+ const GDBusSignalTable *signal;
+ const GDBusPropertyTable *property;
+
+ for (method = methods; method && method->name; method++) {
+ if (!check_experimental(method->flags,
+ G_DBUS_METHOD_FLAG_EXPERIMENTAL))
+ goto done;
+ }
+
+ for (signal = signals; signal && signal->name; signal++) {
+ if (!check_experimental(signal->flags,
+ G_DBUS_SIGNAL_FLAG_EXPERIMENTAL))
+ goto done;
+ }
+
+ for (property = properties; property && property->name; property++) {
+ if (!check_experimental(property->flags,
+ G_DBUS_PROPERTY_FLAG_EXPERIMENTAL))
+ goto done;
+ }
+
+ /* Nothing to register */
+ return FALSE;
+
+done:
+ iface = g_new0(struct interface_data, 1);
+ iface->name = g_strdup(name);
+ iface->methods = methods;
+ iface->signals = signals;
+ iface->properties = properties;
+ iface->user_data = user_data;
+ iface->destroy = destroy;
+
+ data->interfaces = g_slist_append(data->interfaces, iface);
+ if (data->parent == NULL)
+ return TRUE;
+
+ data->added = g_slist_append(data->added, iface);
+ if (data->process_id > 0)
+ return TRUE;
+
+ data->process_id = g_idle_add(process_changes, data);
+
+ return TRUE;
+}
+
+static struct generic_data *object_path_ref(DBusConnection *connection,
+ const char *path)
+{
+ struct generic_data *data;
+
+ if (dbus_connection_get_object_path_data(connection, path,
+ (void *) &data) == TRUE) {
+ if (data != NULL) {
+ data->refcount++;
+ return data;
+ }
+ }
+
+ data = g_new0(struct generic_data, 1);
+ data->conn = dbus_connection_ref(connection);
+ data->path = g_strdup(path);
+ data->refcount = 1;
+
+ data->introspect = g_strdup(DBUS_INTROSPECT_1_0_XML_DOCTYPE_DECL_NODE "<node></node>");
+
+ if (!dbus_connection_register_object_path(connection, path,
+ &generic_table, data)) {
+ g_free(data->introspect);
+ g_free(data);
+ return NULL;
+ }
+
+ invalidate_parent_data(connection, path);
+
+ add_interface(data, DBUS_INTERFACE_INTROSPECTABLE, introspect_methods,
+ NULL, NULL, data, NULL);
+
+ return data;
+}
+
+static void object_path_unref(DBusConnection *connection, const char *path)
+{
+ struct generic_data *data = NULL;
+
+ if (dbus_connection_get_object_path_data(connection, path,
+ (void *) &data) == FALSE)
+ return;
+
+ if (data == NULL)
+ return;
+
+ data->refcount--;
+
+ if (data->refcount > 0)
+ return;
+
+ remove_interface(data, DBUS_INTERFACE_INTROSPECTABLE);
+ remove_interface(data, DBUS_INTERFACE_PROPERTIES);
+
+ invalidate_parent_data(data->conn, data->path);
+
+ dbus_connection_unregister_object_path(data->conn, data->path);
+}
+
+static gboolean check_signal(DBusConnection *conn, const char *path,
+ const char *interface, const char *name,
+ const GDBusArgInfo **args)
+{
+ struct generic_data *data = NULL;
+ struct interface_data *iface;
+ const GDBusSignalTable *signal;
+
+ *args = NULL;
+ if (!dbus_connection_get_object_path_data(conn, path,
+ (void *) &data) || data == NULL) {
+ error("dbus_connection_emit_signal: path %s isn't registered",
+ path);
+ return FALSE;
+ }
+
+ iface = find_interface(data->interfaces, interface);
+ if (iface == NULL) {
+ error("dbus_connection_emit_signal: %s does not implement %s",
+ path, interface);
+ return FALSE;
+ }
+
+ for (signal = iface->signals; signal && signal->name; signal++) {
+ if (strcmp(signal->name, name) != 0)
+ continue;
+
+ if (signal->flags & G_DBUS_SIGNAL_FLAG_EXPERIMENTAL) {
+ const char *env = g_getenv("GDBUS_EXPERIMENTAL");
+ if (g_strcmp0(env, "1") != 0)
+ break;
+ }
+
+ *args = signal->args;
+ return TRUE;
+ }
+
+ error("No signal named %s on interface %s", name, interface);
+ return FALSE;
+}
+
+static dbus_bool_t emit_signal_valist(DBusConnection *conn,
+ const char *path,
+ const char *interface,
+ const char *name,
+ int first,
+ va_list var_args)
+{
+ DBusMessage *signal;
+ dbus_bool_t ret;
+ const GDBusArgInfo *args;
+
+ if (!check_signal(conn, path, interface, name, &args))
+ return FALSE;
+
+ signal = dbus_message_new_signal(path, interface, name);
+ if (signal == NULL) {
+ error("Unable to allocate new %s.%s signal", interface, name);
+ return FALSE;
+ }
+
+ ret = dbus_message_append_args_valist(signal, first, var_args);
+ if (!ret)
+ goto fail;
+
+ if (g_dbus_args_have_signature(args, signal) == FALSE) {
+ error("%s.%s: got unexpected signature '%s'", interface, name,
+ dbus_message_get_signature(signal));
+ ret = FALSE;
+ goto fail;
+ }
+
+ ret = dbus_connection_send(conn, signal, NULL);
+
+fail:
+ dbus_message_unref(signal);
+
+ return ret;
+}
+
+gboolean g_dbus_register_interface(DBusConnection *connection,
+ const char *path, const char *name,
+ const GDBusMethodTable *methods,
+ const GDBusSignalTable *signals,
+ const GDBusPropertyTable *properties,
+ void *user_data,
+ GDBusDestroyFunction destroy)
+{
+ struct generic_data *data;
+
+ data = object_path_ref(connection, path);
+ if (data == NULL)
+ return FALSE;
+
+ if (find_interface(data->interfaces, name)) {
+ object_path_unref(connection, path);
+ return FALSE;
+ }
+
+ if (!add_interface(data, name, methods, signals, properties, user_data,
+ destroy)) {
+ object_path_unref(connection, path);
+ return FALSE;
+ }
+
+ if (properties != NULL && !find_interface(data->interfaces,
+ DBUS_INTERFACE_PROPERTIES))
+ add_interface(data, DBUS_INTERFACE_PROPERTIES,
+ properties_methods, properties_signals, NULL,
+ data, NULL);
+
+ g_free(data->introspect);
+ data->introspect = NULL;
+
+ return TRUE;
+}
+
+gboolean g_dbus_unregister_interface(DBusConnection *connection,
+ const char *path, const char *name)
+{
+ struct generic_data *data = NULL;
+
+ if (path == NULL)
+ return FALSE;
+
+ if (dbus_connection_get_object_path_data(connection, path,
+ (void *) &data) == FALSE)
+ return FALSE;
+
+ if (data == NULL)
+ return FALSE;
+
+ if (remove_interface(data, name) == FALSE)
+ return FALSE;
+
+ g_free(data->introspect);
+ data->introspect = NULL;
+
+ object_path_unref(connection, data->path);
+
+ return TRUE;
+}
+
+gboolean g_dbus_register_security(const GDBusSecurityTable *security)
+{
+ if (security_table != NULL)
+ return FALSE;
+
+ security_table = security;
+
+ return TRUE;
+}
+
+gboolean g_dbus_unregister_security(const GDBusSecurityTable *security)
+{
+ security_table = NULL;
+
+ return TRUE;
+}
+
+DBusMessage *g_dbus_create_error_valist(DBusMessage *message, const char *name,
+ const char *format, va_list args)
+{
+ char str[1024];
+
+ vsnprintf(str, sizeof(str), format, args);
+
+ return dbus_message_new_error(message, name, str);
+}
+
+DBusMessage *g_dbus_create_error(DBusMessage *message, const char *name,
+ const char *format, ...)
+{
+ va_list args;
+ DBusMessage *reply;
+
+ va_start(args, format);
+
+ reply = g_dbus_create_error_valist(message, name, format, args);
+
+ va_end(args);
+
+ return reply;
+}
+
+DBusMessage *g_dbus_create_reply_valist(DBusMessage *message,
+ int type, va_list args)
+{
+ DBusMessage *reply;
+
+ reply = dbus_message_new_method_return(message);
+ if (reply == NULL)
+ return NULL;
+
+ if (dbus_message_append_args_valist(reply, type, args) == FALSE) {
+ dbus_message_unref(reply);
+ return NULL;
+ }
+
+ return reply;
+}
+
+DBusMessage *g_dbus_create_reply(DBusMessage *message, int type, ...)
+{
+ va_list args;
+ DBusMessage *reply;
+
+ va_start(args, type);
+
+ reply = g_dbus_create_reply_valist(message, type, args);
+
+ va_end(args);
+
+ return reply;
+}
+
+gboolean g_dbus_send_message(DBusConnection *connection, DBusMessage *message)
+{
+ dbus_bool_t result;
+
+ if (dbus_message_get_type(message) == DBUS_MESSAGE_TYPE_METHOD_CALL)
+ dbus_message_set_no_reply(message, TRUE);
+ else if (dbus_message_get_type(message) == DBUS_MESSAGE_TYPE_SIGNAL) {
+ const char *path = dbus_message_get_path(message);
+ const char *interface = dbus_message_get_interface(message);
+ const char *name = dbus_message_get_member(message);
+ const GDBusArgInfo *args;
+
+ if (!check_signal(connection, path, interface, name, &args))
+ return FALSE;
+ }
+
+ result = dbus_connection_send(connection, message, NULL);
+
+ dbus_message_unref(message);
+
+ return result;
+}
+
+gboolean g_dbus_send_error_valist(DBusConnection *connection,
+ DBusMessage *message, const char *name,
+ const char *format, va_list args)
+{
+ DBusMessage *error;
+ char str[1024];
+
+ vsnprintf(str, sizeof(str), format, args);
+
+ error = dbus_message_new_error(message, name, str);
+ if (error == NULL)
+ return FALSE;
+
+ return g_dbus_send_message(connection, error);
+}
+
+gboolean g_dbus_send_error(DBusConnection *connection, DBusMessage *message,
+ const char *name, const char *format, ...)
+{
+ va_list args;
+ gboolean result;
+
+ va_start(args, format);
+
+ result = g_dbus_send_error_valist(connection, message, name,
+ format, args);
+
+ va_end(args);
+
+ return result;
+}
+
+gboolean g_dbus_send_reply_valist(DBusConnection *connection,
+ DBusMessage *message, int type, va_list args)
+{
+ DBusMessage *reply;
+
+ reply = dbus_message_new_method_return(message);
+ if (reply == NULL)
+ return FALSE;
+
+ if (dbus_message_append_args_valist(reply, type, args) == FALSE) {
+ dbus_message_unref(reply);
+ return FALSE;
+ }
+
+ return g_dbus_send_message(connection, reply);
+}
+
+gboolean g_dbus_send_reply(DBusConnection *connection,
+ DBusMessage *message, int type, ...)
+{
+ va_list args;
+ gboolean result;
+
+ va_start(args, type);
+
+ result = g_dbus_send_reply_valist(connection, message, type, args);
+
+ va_end(args);
+
+ return result;
+}
+
+gboolean g_dbus_emit_signal(DBusConnection *connection,
+ const char *path, const char *interface,
+ const char *name, int type, ...)
+{
+ va_list args;
+ gboolean result;
+
+ va_start(args, type);
+
+ result = emit_signal_valist(connection, path, interface,
+ name, type, args);
+
+ va_end(args);
+
+ return result;
+}
+
+gboolean g_dbus_emit_signal_valist(DBusConnection *connection,
+ const char *path, const char *interface,
+ const char *name, int type, va_list args)
+{
+ return emit_signal_valist(connection, path, interface,
+ name, type, args);
+}
+
+static void process_properties_from_interface(struct generic_data *data,
+ struct interface_data *iface)
+{
+ GSList *l;
+ DBusMessage *signal;
+ DBusMessageIter iter, dict, array;
+ GSList *invalidated;
+
+ if (iface->pending_prop == NULL)
+ return;
+
+ signal = dbus_message_new_signal(data->path,
+ DBUS_INTERFACE_PROPERTIES, "PropertiesChanged");
+ if (signal == NULL) {
+ error("Unable to allocate new " DBUS_INTERFACE_PROPERTIES
+ ".PropertiesChanged signal");
+ return;
+ }
+
+ iface->pending_prop = g_slist_reverse(iface->pending_prop);
+
+ dbus_message_iter_init_append(signal, &iter);
+ dbus_message_iter_append_basic(&iter, DBUS_TYPE_STRING, &iface->name);
+ dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY,
+ DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING
+ DBUS_TYPE_STRING_AS_STRING DBUS_TYPE_VARIANT_AS_STRING
+ DBUS_DICT_ENTRY_END_CHAR_AS_STRING, &dict);
+
+ invalidated = NULL;
+
+ for (l = iface->pending_prop; l != NULL; l = l->next) {
+ GDBusPropertyTable *p = l->data;
+
+ if (p->get == NULL)
+ continue;
+
+ if (p->exists != NULL && !p->exists(p, iface->user_data)) {
+ invalidated = g_slist_prepend(invalidated, p);
+ continue;
+ }
+
+ append_property(iface, p, &dict);
+ }
+
+ dbus_message_iter_close_container(&iter, &dict);
+
+ dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY,
+ DBUS_TYPE_STRING_AS_STRING, &array);
+ for (l = invalidated; l != NULL; l = g_slist_next(l)) {
+ GDBusPropertyTable *p = l->data;
+
+ dbus_message_iter_append_basic(&array, DBUS_TYPE_STRING,
+ &p->name);
+ }
+ g_slist_free(invalidated);
+ dbus_message_iter_close_container(&iter, &array);
+
+ g_dbus_send_message(data->conn, signal);
+
+ g_slist_free(iface->pending_prop);
+ iface->pending_prop = NULL;
+}
+
+static void process_property_changes(struct generic_data *data)
+{
+ GSList *l;
+
+ for (l = data->interfaces; l != NULL; l = l->next) {
+ struct interface_data *iface = l->data;
+
+ process_properties_from_interface(data, iface);
+ }
+
+ data->pending_prop = FALSE;
+}
+
+void g_dbus_emit_property_changed(DBusConnection *connection,
+ const char *path, const char *interface,
+ const char *name)
+{
+ const GDBusPropertyTable *property;
+ struct generic_data *data;
+ struct interface_data *iface;
+
+ if (path == NULL)
+ return;
+
+ if (!dbus_connection_get_object_path_data(connection, path,
+ (void **) &data) || data == NULL)
+ return;
+
+ iface = find_interface(data->interfaces, interface);
+ if (iface == NULL)
+ return;
+
+ property = find_property(iface->properties, name);
+ if (property == NULL) {
+ error("Could not find property %s in %p", name,
+ iface->properties);
+ return;
+ }
+
+ if (g_slist_find(iface->pending_prop, (void *) property) != NULL)
+ return;
+
+ data->pending_prop = TRUE;
+ iface->pending_prop = g_slist_prepend(iface->pending_prop,
+ (void *) property);
+
+ if (!data->process_id) {
+ data->process_id = g_idle_add(process_changes, data);
+ return;
+ }
+}
+
+gboolean g_dbus_get_properties(DBusConnection *connection, const char *path,
+ const char *interface, DBusMessageIter *iter)
+{
+ struct generic_data *data;
+ struct interface_data *iface;
+
+ if (path == NULL)
+ return FALSE;
+
+ if (!dbus_connection_get_object_path_data(connection, path,
+ (void **) &data) || data == NULL)
+ return FALSE;
+
+ iface = find_interface(data->interfaces, interface);
+ if (iface == NULL)
+ return FALSE;
+
+ append_properties(iface, iter);
+
+ return TRUE;
+}
+
+gboolean g_dbus_attach_object_manager(DBusConnection *connection)
+{
+ struct generic_data *data;
+
+ data = object_path_ref(connection, "/");
+ if (data == NULL)
+ return FALSE;
+
+ add_interface(data, DBUS_INTERFACE_OBJECT_MANAGER,
+ manager_methods, manager_signals,
+ NULL, data, NULL);
+ root = data;
+
+ return TRUE;
+}
+
+gboolean g_dbus_detach_object_manager(DBusConnection *connection)
+{
+ if (!g_dbus_unregister_interface(connection, "/",
+ DBUS_INTERFACE_OBJECT_MANAGER))
+ return FALSE;
+
+ root = NULL;
+
+ return TRUE;
+}
+
+void g_dbus_set_flags(int flags)
+{
+ global_flags = flags;
+}
diff --git a/gdbus/polkit.c b/gdbus/polkit.c
new file mode 100644
index 0000000..9e95fa3
--- /dev/null
+++ b/gdbus/polkit.c
@@ -0,0 +1,202 @@
+/*
+ *
+ * D-Bus helper library
+ *
+ * Copyright (C) 2004-2011 Marcel Holtmann <marcel@holtmann.org>
+ *
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <errno.h>
+
+#include <dbus/dbus.h>
+
+#include <glib.h>
+
+int polkit_check_authorization(DBusConnection *conn,
+ const char *action, gboolean interaction,
+ void (*function) (dbus_bool_t authorized,
+ void *user_data),
+ void *user_data, int timeout);
+
+static void add_dict_with_string_value(DBusMessageIter *iter,
+ const char *key, const char *str)
+{
+ DBusMessageIter dict, entry, value;
+
+ dbus_message_iter_open_container(iter, DBUS_TYPE_ARRAY,
+ DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING
+ DBUS_TYPE_STRING_AS_STRING DBUS_TYPE_VARIANT_AS_STRING
+ DBUS_DICT_ENTRY_END_CHAR_AS_STRING, &dict);
+ dbus_message_iter_open_container(&dict, DBUS_TYPE_DICT_ENTRY,
+ NULL, &entry);
+
+ dbus_message_iter_append_basic(&entry, DBUS_TYPE_STRING, &key);
+
+ dbus_message_iter_open_container(&entry, DBUS_TYPE_VARIANT,
+ DBUS_TYPE_STRING_AS_STRING, &value);
+ dbus_message_iter_append_basic(&value, DBUS_TYPE_STRING, &str);
+ dbus_message_iter_close_container(&entry, &value);
+
+ dbus_message_iter_close_container(&dict, &entry);
+ dbus_message_iter_close_container(iter, &dict);
+}
+
+static void add_empty_string_dict(DBusMessageIter *iter)
+{
+ DBusMessageIter dict;
+
+ dbus_message_iter_open_container(iter, DBUS_TYPE_ARRAY,
+ DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING
+ DBUS_TYPE_STRING_AS_STRING DBUS_TYPE_STRING_AS_STRING
+ DBUS_DICT_ENTRY_END_CHAR_AS_STRING, &dict);
+
+ dbus_message_iter_close_container(iter, &dict);
+}
+
+static void add_arguments(DBusConnection *conn, DBusMessageIter *iter,
+ const char *action, dbus_uint32_t flags)
+{
+ const char *busname = dbus_bus_get_unique_name(conn);
+ const char *kind = "system-bus-name";
+ const char *cancel = "";
+ DBusMessageIter subject;
+
+ dbus_message_iter_open_container(iter, DBUS_TYPE_STRUCT,
+ NULL, &subject);
+ dbus_message_iter_append_basic(&subject, DBUS_TYPE_STRING, &kind);
+ add_dict_with_string_value(&subject, "name", busname);
+ dbus_message_iter_close_container(iter, &subject);
+
+ dbus_message_iter_append_basic(iter, DBUS_TYPE_STRING, &action);
+ add_empty_string_dict(iter);
+ dbus_message_iter_append_basic(iter, DBUS_TYPE_UINT32, &flags);
+ dbus_message_iter_append_basic(iter, DBUS_TYPE_STRING, &cancel);
+}
+
+static dbus_bool_t parse_result(DBusMessageIter *iter)
+{
+ DBusMessageIter result;
+ dbus_bool_t authorized, challenge;
+
+ dbus_message_iter_recurse(iter, &result);
+
+ dbus_message_iter_get_basic(&result, &authorized);
+ dbus_message_iter_get_basic(&result, &challenge);
+
+ return authorized;
+}
+
+struct authorization_data {
+ void (*function) (dbus_bool_t authorized, void *user_data);
+ void *user_data;
+};
+
+static void authorization_reply(DBusPendingCall *call, void *user_data)
+{
+ struct authorization_data *data = user_data;
+ DBusMessage *reply;
+ DBusMessageIter iter;
+ dbus_bool_t authorized = FALSE;
+
+ reply = dbus_pending_call_steal_reply(call);
+
+ if (dbus_message_get_type(reply) == DBUS_MESSAGE_TYPE_ERROR)
+ goto done;
+
+ if (dbus_message_has_signature(reply, "(bba{ss})") == FALSE)
+ goto done;
+
+ dbus_message_iter_init(reply, &iter);
+
+ authorized = parse_result(&iter);
+
+done:
+ if (data->function != NULL)
+ data->function(authorized, data->user_data);
+
+ dbus_message_unref(reply);
+
+ dbus_pending_call_unref(call);
+}
+
+#define AUTHORITY_DBUS "org.freedesktop.PolicyKit1"
+#define AUTHORITY_INTF "org.freedesktop.PolicyKit1.Authority"
+#define AUTHORITY_PATH "/org/freedesktop/PolicyKit1/Authority"
+
+int polkit_check_authorization(DBusConnection *conn,
+ const char *action, gboolean interaction,
+ void (*function) (dbus_bool_t authorized,
+ void *user_data),
+ void *user_data, int timeout)
+{
+ struct authorization_data *data;
+ DBusMessage *msg;
+ DBusMessageIter iter;
+ DBusPendingCall *call;
+ dbus_uint32_t flags = 0x00000000;
+
+ if (conn == NULL)
+ return -EINVAL;
+
+ data = dbus_malloc0(sizeof(*data));
+ if (data == NULL)
+ return -ENOMEM;
+
+ msg = dbus_message_new_method_call(AUTHORITY_DBUS, AUTHORITY_PATH,
+ AUTHORITY_INTF, "CheckAuthorization");
+ if (msg == NULL) {
+ dbus_free(data);
+ return -ENOMEM;
+ }
+
+ if (interaction == TRUE)
+ flags |= 0x00000001;
+
+ if (action == NULL)
+ action = "org.freedesktop.policykit.exec";
+
+ dbus_message_iter_init_append(msg, &iter);
+ add_arguments(conn, &iter, action, flags);
+
+ if (dbus_connection_send_with_reply(conn, msg,
+ &call, timeout) == FALSE) {
+ dbus_message_unref(msg);
+ dbus_free(data);
+ return -EIO;
+ }
+
+ if (call == NULL) {
+ dbus_message_unref(msg);
+ dbus_free(data);
+ return -EIO;
+ }
+
+ data->function = function;
+ data->user_data = user_data;
+
+ dbus_pending_call_set_notify(call, authorization_reply,
+ data, dbus_free);
+
+ dbus_message_unref(msg);
+
+ return 0;
+}
diff --git a/gdbus/watch.c b/gdbus/watch.c
new file mode 100644
index 0000000..9e4f994
--- /dev/null
+++ b/gdbus/watch.c
@@ -0,0 +1,815 @@
+/*
+ *
+ * D-Bus helper library
+ *
+ * Copyright (C) 2004-2011 Marcel Holtmann <marcel@holtmann.org>
+ *
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <string.h>
+
+#include <glib.h>
+#include <dbus/dbus.h>
+
+#include "gdbus.h"
+
+#define info(fmt...)
+#define error(fmt...)
+#define debug(fmt...)
+
+static DBusHandlerResult message_filter(DBusConnection *connection,
+ DBusMessage *message, void *user_data);
+
+static guint listener_id = 0;
+static GSList *listeners = NULL;
+
+struct service_data {
+ DBusConnection *conn;
+ DBusPendingCall *call;
+ char *name;
+ const char *owner;
+ guint id;
+ struct filter_callback *callback;
+};
+
+struct filter_callback {
+ GDBusWatchFunction conn_func;
+ GDBusWatchFunction disc_func;
+ GDBusSignalFunction signal_func;
+ GDBusDestroyFunction destroy_func;
+ struct service_data *data;
+ void *user_data;
+ guint id;
+};
+
+struct filter_data {
+ DBusConnection *connection;
+ DBusHandleMessageFunction handle_func;
+ char *name;
+ char *owner;
+ char *path;
+ char *interface;
+ char *member;
+ char *argument;
+ GSList *callbacks;
+ GSList *processed;
+ guint name_watch;
+ gboolean lock;
+ gboolean registered;
+};
+
+static struct filter_data *filter_data_find_match(DBusConnection *connection,
+ const char *name,
+ const char *owner,
+ const char *path,
+ const char *interface,
+ const char *member,
+ const char *argument)
+{
+ GSList *current;
+
+ for (current = listeners;
+ current != NULL; current = current->next) {
+ struct filter_data *data = current->data;
+
+ if (connection != data->connection)
+ continue;
+
+ if (g_strcmp0(name, data->name) != 0)
+ continue;
+
+ if (g_strcmp0(owner, data->owner) != 0)
+ continue;
+
+ if (g_strcmp0(path, data->path) != 0)
+ continue;
+
+ if (g_strcmp0(interface, data->interface) != 0)
+ continue;
+
+ if (g_strcmp0(member, data->member) != 0)
+ continue;
+
+ if (g_strcmp0(argument, data->argument) != 0)
+ continue;
+
+ return data;
+ }
+
+ return NULL;
+}
+
+static struct filter_data *filter_data_find(DBusConnection *connection)
+{
+ GSList *current;
+
+ for (current = listeners;
+ current != NULL; current = current->next) {
+ struct filter_data *data = current->data;
+
+ if (connection != data->connection)
+ continue;
+
+ return data;
+ }
+
+ return NULL;
+}
+
+static void format_rule(struct filter_data *data, char *rule, size_t size)
+{
+ const char *sender;
+ int offset;
+
+ offset = snprintf(rule, size, "type='signal'");
+ sender = data->name ? : data->owner;
+
+ if (sender)
+ offset += snprintf(rule + offset, size - offset,
+ ",sender='%s'", sender);
+ if (data->path)
+ offset += snprintf(rule + offset, size - offset,
+ ",path='%s'", data->path);
+ if (data->interface)
+ offset += snprintf(rule + offset, size - offset,
+ ",interface='%s'", data->interface);
+ if (data->member)
+ offset += snprintf(rule + offset, size - offset,
+ ",member='%s'", data->member);
+ if (data->argument)
+ snprintf(rule + offset, size - offset,
+ ",arg0='%s'", data->argument);
+}
+
+static gboolean add_match(struct filter_data *data,
+ DBusHandleMessageFunction filter)
+{
+ DBusError err;
+ char rule[DBUS_MAXIMUM_MATCH_RULE_LENGTH];
+
+ format_rule(data, rule, sizeof(rule));
+ dbus_error_init(&err);
+
+ dbus_bus_add_match(data->connection, rule, &err);
+ if (dbus_error_is_set(&err)) {
+ error("Adding match rule \"%s\" failed: %s", rule,
+ err.message);
+ dbus_error_free(&err);
+ return FALSE;
+ }
+
+ data->handle_func = filter;
+ data->registered = TRUE;
+
+ return TRUE;
+}
+
+static gboolean remove_match(struct filter_data *data)
+{
+ DBusError err;
+ char rule[DBUS_MAXIMUM_MATCH_RULE_LENGTH];
+
+ format_rule(data, rule, sizeof(rule));
+
+ dbus_error_init(&err);
+
+ dbus_bus_remove_match(data->connection, rule, &err);
+ if (dbus_error_is_set(&err)) {
+ error("Removing owner match rule for %s failed: %s",
+ rule, err.message);
+ dbus_error_free(&err);
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+static struct filter_data *filter_data_get(DBusConnection *connection,
+ DBusHandleMessageFunction filter,
+ const char *sender,
+ const char *path,
+ const char *interface,
+ const char *member,
+ const char *argument)
+{
+ struct filter_data *data;
+ const char *name = NULL, *owner = NULL;
+
+ if (filter_data_find(connection) == NULL) {
+ if (!dbus_connection_add_filter(connection,
+ message_filter, NULL, NULL)) {
+ error("dbus_connection_add_filter() failed");
+ return NULL;
+ }
+ }
+
+ if (sender == NULL)
+ goto proceed;
+
+ if (sender[0] == ':')
+ owner = sender;
+ else
+ name = sender;
+
+proceed:
+ data = filter_data_find_match(connection, name, owner, path,
+ interface, member, argument);
+ if (data)
+ return data;
+
+ data = g_new0(struct filter_data, 1);
+
+ data->connection = dbus_connection_ref(connection);
+ data->name = g_strdup(name);
+ data->owner = g_strdup(owner);
+ data->path = g_strdup(path);
+ data->interface = g_strdup(interface);
+ data->member = g_strdup(member);
+ data->argument = g_strdup(argument);
+
+ if (!add_match(data, filter)) {
+ g_free(data);
+ return NULL;
+ }
+
+ listeners = g_slist_append(listeners, data);
+
+ return data;
+}
+
+static struct filter_callback *filter_data_find_callback(
+ struct filter_data *data,
+ guint id)
+{
+ GSList *l;
+
+ for (l = data->callbacks; l; l = l->next) {
+ struct filter_callback *cb = l->data;
+ if (cb->id == id)
+ return cb;
+ }
+ for (l = data->processed; l; l = l->next) {
+ struct filter_callback *cb = l->data;
+ if (cb->id == id)
+ return cb;
+ }
+
+ return NULL;
+}
+
+static void filter_data_free(struct filter_data *data)
+{
+ GSList *l;
+
+ for (l = data->callbacks; l != NULL; l = l->next)
+ g_free(l->data);
+
+ g_slist_free(data->callbacks);
+ g_dbus_remove_watch(data->connection, data->name_watch);
+ g_free(data->name);
+ g_free(data->owner);
+ g_free(data->path);
+ g_free(data->interface);
+ g_free(data->member);
+ g_free(data->argument);
+ dbus_connection_unref(data->connection);
+ g_free(data);
+}
+
+static void filter_data_call_and_free(struct filter_data *data)
+{
+ GSList *l;
+
+ for (l = data->callbacks; l != NULL; l = l->next) {
+ struct filter_callback *cb = l->data;
+ if (cb->disc_func)
+ cb->disc_func(data->connection, cb->user_data);
+ if (cb->destroy_func)
+ cb->destroy_func(cb->user_data);
+ g_free(cb);
+ }
+
+ filter_data_free(data);
+}
+
+static struct filter_callback *filter_data_add_callback(
+ struct filter_data *data,
+ GDBusWatchFunction connect,
+ GDBusWatchFunction disconnect,
+ GDBusSignalFunction signal,
+ GDBusDestroyFunction destroy,
+ void *user_data)
+{
+ struct filter_callback *cb = NULL;
+
+ cb = g_new0(struct filter_callback, 1);
+
+ cb->conn_func = connect;
+ cb->disc_func = disconnect;
+ cb->signal_func = signal;
+ cb->destroy_func = destroy;
+ cb->user_data = user_data;
+ cb->id = ++listener_id;
+
+ if (data->lock)
+ data->processed = g_slist_append(data->processed, cb);
+ else
+ data->callbacks = g_slist_append(data->callbacks, cb);
+
+ return cb;
+}
+
+static void service_data_free(struct service_data *data)
+{
+ struct filter_callback *callback = data->callback;
+
+ dbus_connection_unref(data->conn);
+
+ if (data->call)
+ dbus_pending_call_unref(data->call);
+
+ if (data->id)
+ g_source_remove(data->id);
+
+ g_free(data->name);
+ g_free(data);
+
+ callback->data = NULL;
+}
+
+static gboolean filter_data_remove_callback(struct filter_data *data,
+ struct filter_callback *cb)
+{
+ DBusConnection *connection;
+
+ data->callbacks = g_slist_remove(data->callbacks, cb);
+ data->processed = g_slist_remove(data->processed, cb);
+
+ /* Cancel pending operations */
+ if (cb->data) {
+ if (cb->data->call)
+ dbus_pending_call_cancel(cb->data->call);
+ service_data_free(cb->data);
+ }
+
+ if (cb->destroy_func)
+ cb->destroy_func(cb->user_data);
+
+ g_free(cb);
+
+ /* Don't remove the filter if other callbacks exist or data is lock
+ * processing callbacks */
+ if (data->callbacks || data->lock)
+ return TRUE;
+
+ if (data->registered && !remove_match(data))
+ return FALSE;
+
+ connection = dbus_connection_ref(data->connection);
+ listeners = g_slist_remove(listeners, data);
+
+ /* Remove filter if there are no listeners left for the connection */
+ if (filter_data_find(connection) == NULL)
+ dbus_connection_remove_filter(connection, message_filter,
+ NULL);
+
+ filter_data_free(data);
+ dbus_connection_unref(connection);
+
+ return TRUE;
+}
+
+static DBusHandlerResult signal_filter(DBusConnection *connection,
+ DBusMessage *message, void *user_data)
+{
+ struct filter_data *data = user_data;
+ struct filter_callback *cb;
+
+ while (data->callbacks) {
+ cb = data->callbacks->data;
+
+ if (cb->signal_func && !cb->signal_func(connection, message,
+ cb->user_data)) {
+ filter_data_remove_callback(data, cb);
+ continue;
+ }
+
+ /* Check if the watch was removed/freed by the callback
+ * function */
+ if (!g_slist_find(data->callbacks, cb))
+ continue;
+
+ data->callbacks = g_slist_remove(data->callbacks, cb);
+ data->processed = g_slist_append(data->processed, cb);
+ }
+
+ return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
+}
+
+static void update_name_cache(const char *name, const char *owner)
+{
+ GSList *l;
+
+ for (l = listeners; l != NULL; l = l->next) {
+ struct filter_data *data = l->data;
+
+ if (g_strcmp0(data->name, name) != 0)
+ continue;
+
+ g_free(data->owner);
+ data->owner = g_strdup(owner);
+ }
+}
+
+static const char *check_name_cache(const char *name)
+{
+ GSList *l;
+
+ for (l = listeners; l != NULL; l = l->next) {
+ struct filter_data *data = l->data;
+
+ if (g_strcmp0(data->name, name) != 0)
+ continue;
+
+ return data->owner;
+ }
+
+ return NULL;
+}
+
+static DBusHandlerResult service_filter(DBusConnection *connection,
+ DBusMessage *message, void *user_data)
+{
+ struct filter_data *data = user_data;
+ struct filter_callback *cb;
+ char *name, *old, *new;
+
+ if (!dbus_message_get_args(message, NULL,
+ DBUS_TYPE_STRING, &name,
+ DBUS_TYPE_STRING, &old,
+ DBUS_TYPE_STRING, &new,
+ DBUS_TYPE_INVALID)) {
+ error("Invalid arguments for NameOwnerChanged signal");
+ return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
+ }
+
+ update_name_cache(name, new);
+
+ while (data->callbacks) {
+ cb = data->callbacks->data;
+
+ if (*new == '\0') {
+ if (cb->disc_func)
+ cb->disc_func(connection, cb->user_data);
+ } else {
+ if (cb->conn_func)
+ cb->conn_func(connection, cb->user_data);
+ }
+
+ /* Check if the watch was removed/freed by the callback
+ * function */
+ if (!g_slist_find(data->callbacks, cb))
+ continue;
+
+ /* Only auto remove if it is a bus name watch */
+ if (data->argument[0] == ':' &&
+ (cb->conn_func == NULL || cb->disc_func == NULL)) {
+ filter_data_remove_callback(data, cb);
+ continue;
+ }
+
+ data->callbacks = g_slist_remove(data->callbacks, cb);
+ data->processed = g_slist_append(data->processed, cb);
+ }
+
+ return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
+}
+
+
+static DBusHandlerResult message_filter(DBusConnection *connection,
+ DBusMessage *message, void *user_data)
+{
+ struct filter_data *data;
+ const char *sender, *path, *iface, *member, *arg = NULL;
+ GSList *current, *delete_listener = NULL;
+
+ /* Only filter signals */
+ if (dbus_message_get_type(message) != DBUS_MESSAGE_TYPE_SIGNAL)
+ return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
+
+ sender = dbus_message_get_sender(message);
+ path = dbus_message_get_path(message);
+ iface = dbus_message_get_interface(message);
+ member = dbus_message_get_member(message);
+ dbus_message_get_args(message, NULL, DBUS_TYPE_STRING, &arg, DBUS_TYPE_INVALID);
+
+ /* Sender is always the owner */
+
+ for (current = listeners; current != NULL; current = current->next) {
+ data = current->data;
+
+ if (connection != data->connection)
+ continue;
+
+ if (data->owner && g_str_equal(sender, data->owner) == FALSE)
+ continue;
+
+ if (data->path && g_str_equal(path, data->path) == FALSE)
+ continue;
+
+ if (data->interface && g_str_equal(iface,
+ data->interface) == FALSE)
+ continue;
+
+ if (data->member && g_str_equal(member, data->member) == FALSE)
+ continue;
+
+ if (data->argument && g_str_equal(arg,
+ data->argument) == FALSE)
+ continue;
+
+ if (data->handle_func) {
+ data->lock = TRUE;
+
+ data->handle_func(connection, message, data);
+
+ data->callbacks = data->processed;
+ data->processed = NULL;
+ data->lock = FALSE;
+ }
+
+ if (!data->callbacks)
+ delete_listener = g_slist_prepend(delete_listener,
+ current);
+ }
+
+ for (current = delete_listener; current != NULL;
+ current = delete_listener->next) {
+ GSList *l = current->data;
+
+ data = l->data;
+
+ /* Has any other callback added callbacks back to this data? */
+ if (data->callbacks != NULL)
+ continue;
+
+ remove_match(data);
+ listeners = g_slist_delete_link(listeners, l);
+
+ filter_data_free(data);
+ }
+
+ g_slist_free(delete_listener);
+
+ /* Remove filter if there are no listeners left for the connection */
+ if (filter_data_find(connection) == NULL)
+ dbus_connection_remove_filter(connection, message_filter,
+ NULL);
+
+ return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
+}
+
+static gboolean update_service(void *user_data)
+{
+ struct service_data *data = user_data;
+ struct filter_callback *cb = data->callback;
+
+ update_name_cache(data->name, data->owner);
+ if (cb->conn_func)
+ cb->conn_func(data->conn, cb->user_data);
+
+ service_data_free(data);
+
+ return FALSE;
+}
+
+static void service_reply(DBusPendingCall *call, void *user_data)
+{
+ struct service_data *data = user_data;
+ DBusMessage *reply;
+ DBusError err;
+
+ reply = dbus_pending_call_steal_reply(call);
+ if (reply == NULL)
+ return;
+
+ dbus_error_init(&err);
+
+ if (dbus_set_error_from_message(&err, reply))
+ goto fail;
+
+ if (dbus_message_get_args(reply, &err,
+ DBUS_TYPE_STRING, &data->owner,
+ DBUS_TYPE_INVALID) == FALSE)
+ goto fail;
+
+ update_service(data);
+
+ goto done;
+
+fail:
+ error("%s", err.message);
+ dbus_error_free(&err);
+ service_data_free(data);
+done:
+ dbus_message_unref(reply);
+}
+
+static void check_service(DBusConnection *connection,
+ const char *name,
+ struct filter_callback *callback)
+{
+ DBusMessage *message;
+ struct service_data *data;
+
+ data = g_try_malloc0(sizeof(*data));
+ if (data == NULL) {
+ error("Can't allocate data structure");
+ return;
+ }
+
+ data->conn = dbus_connection_ref(connection);
+ data->name = g_strdup(name);
+ data->callback = callback;
+ callback->data = data;
+
+ data->owner = check_name_cache(name);
+ if (data->owner != NULL) {
+ data->id = g_idle_add(update_service, data);
+ return;
+ }
+
+ message = dbus_message_new_method_call(DBUS_SERVICE_DBUS,
+ DBUS_PATH_DBUS, DBUS_INTERFACE_DBUS, "GetNameOwner");
+ if (message == NULL) {
+ error("Can't allocate new message");
+ g_free(data);
+ return;
+ }
+
+ dbus_message_append_args(message, DBUS_TYPE_STRING, &name,
+ DBUS_TYPE_INVALID);
+
+ if (dbus_connection_send_with_reply(connection, message,
+ &data->call, -1) == FALSE) {
+ error("Failed to execute method call");
+ g_free(data);
+ goto done;
+ }
+
+ if (data->call == NULL) {
+ error("D-Bus connection not available");
+ g_free(data);
+ goto done;
+ }
+
+ dbus_pending_call_set_notify(data->call, service_reply, data, NULL);
+
+done:
+ dbus_message_unref(message);
+}
+
+guint g_dbus_add_service_watch(DBusConnection *connection, const char *name,
+ GDBusWatchFunction connect,
+ GDBusWatchFunction disconnect,
+ void *user_data, GDBusDestroyFunction destroy)
+{
+ struct filter_data *data;
+ struct filter_callback *cb;
+
+ if (name == NULL)
+ return 0;
+
+ data = filter_data_get(connection, service_filter, NULL, NULL,
+ DBUS_INTERFACE_DBUS, "NameOwnerChanged",
+ name);
+ if (data == NULL)
+ return 0;
+
+ cb = filter_data_add_callback(data, connect, disconnect, NULL, destroy,
+ user_data);
+ if (cb == NULL)
+ return 0;
+
+ if (connect)
+ check_service(connection, name, cb);
+
+ return cb->id;
+}
+
+guint g_dbus_add_disconnect_watch(DBusConnection *connection, const char *name,
+ GDBusWatchFunction func,
+ void *user_data, GDBusDestroyFunction destroy)
+{
+ return g_dbus_add_service_watch(connection, name, NULL, func,
+ user_data, destroy);
+}
+
+guint g_dbus_add_signal_watch(DBusConnection *connection,
+ const char *sender, const char *path,
+ const char *interface, const char *member,
+ GDBusSignalFunction function, void *user_data,
+ GDBusDestroyFunction destroy)
+{
+ struct filter_data *data;
+ struct filter_callback *cb;
+
+ data = filter_data_get(connection, signal_filter, sender, path,
+ interface, member, NULL);
+ if (data == NULL)
+ return 0;
+
+ cb = filter_data_add_callback(data, NULL, NULL, function, destroy,
+ user_data);
+ if (cb == NULL)
+ return 0;
+
+ if (data->name != NULL && data->name_watch == 0)
+ data->name_watch = g_dbus_add_service_watch(connection,
+ data->name, NULL,
+ NULL, NULL, NULL);
+
+ return cb->id;
+}
+
+guint g_dbus_add_properties_watch(DBusConnection *connection,
+ const char *sender, const char *path,
+ const char *interface,
+ GDBusSignalFunction function, void *user_data,
+ GDBusDestroyFunction destroy)
+{
+ struct filter_data *data;
+ struct filter_callback *cb;
+
+ data = filter_data_get(connection, signal_filter, sender, path,
+ DBUS_INTERFACE_PROPERTIES, "PropertiesChanged",
+ interface);
+ if (data == NULL)
+ return 0;
+
+ cb = filter_data_add_callback(data, NULL, NULL, function, destroy,
+ user_data);
+ if (cb == NULL)
+ return 0;
+
+ if (data->name != NULL && data->name_watch == 0)
+ data->name_watch = g_dbus_add_service_watch(connection,
+ data->name, NULL,
+ NULL, NULL, NULL);
+
+ return cb->id;
+}
+
+gboolean g_dbus_remove_watch(DBusConnection *connection, guint id)
+{
+ struct filter_data *data;
+ struct filter_callback *cb;
+ GSList *ldata;
+
+ if (id == 0)
+ return FALSE;
+
+ for (ldata = listeners; ldata; ldata = ldata->next) {
+ data = ldata->data;
+
+ cb = filter_data_find_callback(data, id);
+ if (cb) {
+ filter_data_remove_callback(data, cb);
+ return TRUE;
+ }
+ }
+
+ return FALSE;
+}
+
+void g_dbus_remove_all_watches(DBusConnection *connection)
+{
+ struct filter_data *data;
+
+ while ((data = filter_data_find(connection))) {
+ listeners = g_slist_remove(listeners, data);
+ filter_data_call_and_free(data);
+ }
+
+ dbus_connection_remove_filter(connection, message_filter, NULL);
+}
diff --git a/gdhcp/client.c b/gdhcp/client.c
new file mode 100644
index 0000000..f423f52
--- /dev/null
+++ b/gdhcp/client.c
@@ -0,0 +1,2934 @@
+/*
+ *
+ * DHCP client library with GLib integration
+ *
+ * Copyright (C) 2009-2012 Intel Corporation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#define _GNU_SOURCE
+#include <stdio.h>
+#include <errno.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/ioctl.h>
+#include <arpa/inet.h>
+
+#include <netpacket/packet.h>
+#include <netinet/if_ether.h>
+#include <net/ethernet.h>
+
+#include <linux/if.h>
+#include <linux/filter.h>
+
+#include <glib.h>
+
+#include "gdhcp.h"
+#include "common.h"
+#include "ipv4ll.h"
+#include "timer.h"
+
+/* Set to 0 to temporarily rollback to old timer model for DHCP */
+#define FEATURE_USE_RT_TIMERS 0
+
+#define DISCOVER_TIMEOUT 3
+#define DISCOVER_RETRIES 10
+
+#define REQUEST_TIMEOUT 3
+#define REQUEST_RETRIES 5
+#define LEASE_TIME_ONE_WEEK_SECONDS (60 * 60 * 24 * 7)
+
+typedef enum _listen_mode {
+ L_NONE,
+ L2,
+ L3,
+ L_ARP,
+} ListenMode;
+
+typedef enum _dhcp_client_state {
+ INIT_SELECTING,
+ REQUESTING,
+ BOUND,
+ RENEWING,
+ REBINDING,
+ RELEASED,
+ IPV4LL_PROBE,
+ IPV4LL_ANNOUNCE,
+ IPV4LL_MONITOR,
+ IPV4LL_DEFEND,
+ INFORMATION_REQ,
+ SOLICITATION,
+ REQUEST,
+ RENEW,
+ REBIND,
+ RELEASE,
+} ClientState;
+
+struct _GDHCPClient {
+ int ref_count;
+ GDHCPType type;
+ ClientState state;
+ int ifindex;
+ char *interface;
+ uint8_t mac_address[6];
+ uint32_t xid;
+ uint32_t server_ip;
+ uint32_t requested_ip;
+ char *assigned_ip;
+ time_t start;
+ uint32_t lease_seconds;
+ ListenMode listen_mode;
+ int listener_sockfd;
+ uint8_t retry_times;
+ uint8_t ack_retry_times;
+ uint8_t conflicts;
+ guint timeout;
+ guint listener_watch;
+ GIOChannel *listener_channel;
+ GList *require_list;
+ GList *request_list;
+ GHashTable *code_value_hash;
+ GHashTable *send_value_hash;
+ GDHCPClientEventFunc lease_available_cb;
+ gpointer lease_available_data;
+ GDHCPClientEventFunc ipv4ll_available_cb;
+ gpointer ipv4ll_available_data;
+ GDHCPClientEventFunc no_lease_cb;
+ gpointer no_lease_data;
+ GDHCPClientEventFunc lease_lost_cb;
+ gpointer lease_lost_data;
+ GDHCPClientEventFunc ipv4ll_lost_cb;
+ gpointer ipv4ll_lost_data;
+ GDHCPClientEventFunc address_conflict_cb;
+ gpointer address_conflict_data;
+ GDHCPDebugFunc debug_func;
+ gpointer debug_data;
+ GDHCPClientEventFunc information_req_cb;
+ gpointer information_req_data;
+ GDHCPClientEventFunc solicitation_cb;
+ gpointer solicitation_data;
+ GDHCPClientEventFunc advertise_cb;
+ gpointer advertise_data;
+ GDHCPClientEventFunc request_cb;
+ gpointer request_data;
+ GDHCPClientEventFunc renew_cb;
+ gpointer renew_data;
+ GDHCPClientEventFunc rebind_cb;
+ gpointer rebind_data;
+ GDHCPClientEventFunc release_cb;
+ gpointer release_data;
+ GDHCPClientEventFunc wake_event_cb;
+ gpointer wake_event_data;
+ char *last_address;
+ unsigned char *duid;
+ int duid_len;
+ unsigned char *server_duid;
+ int server_duid_len;
+ uint16_t status_code;
+ uint32_t iaid;
+ uint32_t T1, T2;
+ uint32_t next_event;
+ int can_sleep;
+ struct in6_addr ia_na;
+ struct in6_addr ia_ta;
+ time_t last_renew;
+ time_t last_rebind;
+ time_t expire;
+};
+
+static inline void debug(GDHCPClient *client, const char *format, ...)
+{
+ char str[256];
+ va_list ap;
+
+ if (client->debug_func == NULL)
+ return;
+
+ va_start(ap, format);
+
+ if (vsnprintf(str, sizeof(str), format, ap) > 0)
+ client->debug_func(str, client->debug_data);
+
+ va_end(ap);
+}
+
+static void timer_source_remove(GDHCPClient *dhcp_client)
+{
+#if FEATURE_USE_RT_TIMERS
+ g_rttimeout_source_remove(dhcp_client->timeout);
+#else
+ g_source_remove(dhcp_client->timeout);
+#endif
+}
+
+
+static guint timeout_add_full (gint priority, guint32 interval,
+ GSourceFunc function, gpointer data,
+ GDestroyNotify notify)
+{
+#if FEATURE_USE_RT_TIMERS
+ return g_rttimeout_add_full(G_CLOCK_REALTIME, priority, interval,
+ function, data, notify);
+#else
+ return g_timeout_add_full(priority, interval,
+ function, data, notify);
+#endif
+}
+
+static guint timeout_add_seconds_full (gint priority, guint32 interval,
+ GSourceFunc function, gpointer data,
+ GDestroyNotify notify)
+{
+#if FEATURE_USE_RT_TIMERS
+ return g_rttimeout_add_seconds_full(G_CLOCK_REALTIME,
+ priority, interval, function,
+ data, notify);
+#else
+ return g_timeout_add_seconds_full(priority, interval,
+ function, data, notify);
+#endif
+}
+
+static void remove_timer(GDHCPClient *dhcp_client)
+{
+ if (dhcp_client->timeout > 0) {
+ timer_source_remove(dhcp_client);
+ dhcp_client->timeout = 0;
+ }
+}
+
+/* Initialize the packet with the proper defaults */
+static void init_packet(GDHCPClient *dhcp_client, gpointer pkt, char type)
+{
+ if (dhcp_client->type == G_DHCP_IPV6)
+ dhcpv6_init_header(pkt, type);
+ else {
+ struct dhcp_packet *packet = pkt;
+
+ dhcp_init_header(packet, type);
+ memcpy(packet->chaddr, dhcp_client->mac_address, 6);
+ }
+}
+
+static void add_request_options(GDHCPClient *dhcp_client,
+ struct dhcp_packet *packet)
+{
+ int len = 0;
+ GList *list;
+ uint8_t code;
+ int end = dhcp_end_option(packet->options);
+
+ for (list = dhcp_client->request_list; list; list = list->next) {
+ code = (uint8_t) GPOINTER_TO_INT(list->data);
+
+ packet->options[end + OPT_DATA + len] = code;
+ len++;
+ }
+
+ if (len) {
+ packet->options[end + OPT_CODE] = DHCP_PARAM_REQ;
+ packet->options[end + OPT_LEN] = len;
+ packet->options[end + OPT_DATA + len] = DHCP_END;
+ }
+}
+
+struct hash_params {
+ unsigned char *buf;
+ int max_buf;
+ unsigned char **ptr_buf;
+};
+
+static void add_dhcpv6_binary_option(gpointer key, gpointer value,
+ gpointer user_data)
+{
+ uint8_t *option = value;
+ uint16_t len;
+ struct hash_params *params = user_data;
+
+ /* option[0][1] contains option code */
+ len = option[2] << 8 | option[3];
+
+ if ((*params->ptr_buf + len + 2 + 2) > (params->buf + params->max_buf))
+ return;
+
+ memcpy(*params->ptr_buf, option, len + 2 + 2);
+ (*params->ptr_buf) += len + 2 + 2;
+}
+
+static void add_dhcpv6_send_options(GDHCPClient *dhcp_client,
+ unsigned char *buf, int max_buf,
+ unsigned char **ptr_buf)
+{
+ struct hash_params params = {
+ .buf = buf,
+ .max_buf = max_buf,
+ .ptr_buf = ptr_buf
+ };
+
+ if (dhcp_client->type == G_DHCP_IPV4)
+ return;
+
+ g_hash_table_foreach(dhcp_client->send_value_hash,
+ add_dhcpv6_binary_option, ¶ms);
+
+ *ptr_buf = *params.ptr_buf;
+}
+
+static void copy_option(uint8_t *buf, uint16_t code, uint16_t len,
+ uint8_t *msg)
+{
+ buf[0] = code >> 8;
+ buf[1] = code & 0xff;
+ buf[2] = len >> 8;
+ buf[3] = len & 0xff;
+ if (len > 0 && msg != NULL)
+ memcpy(&buf[4], msg, len);
+}
+
+static void set_wake(GDHCPClient *dhcp_client, uint32_t timeout)
+{
+ debug(dhcp_client, "Stay awake for %d", timeout);
+
+ dhcp_client->next_event = timeout + time(NULL);
+ dhcp_client->can_sleep = FALSE;
+ if (dhcp_client->wake_event_cb)
+ dhcp_client->wake_event_cb(dhcp_client,
+ dhcp_client->wake_event_data);
+}
+
+static void release_wake(GDHCPClient *dhcp_client, uint32_t timeout)
+{
+ debug(dhcp_client, "may sleep for %d", timeout);
+ dhcp_client->next_event = timeout + time(NULL);
+ dhcp_client->can_sleep = TRUE;
+ if (dhcp_client->wake_event_cb)
+ dhcp_client->wake_event_cb(dhcp_client,
+ dhcp_client->wake_event_data);
+}
+
+int g_dhcp_get_next_event(GDHCPClient *dhcp_client, time_t *next_event)
+{
+ *next_event = dhcp_client->next_event;
+
+ return dhcp_client->can_sleep;
+}
+
+static void add_dhcpv6_request_options(GDHCPClient *dhcp_client,
+ struct dhcpv6_packet *packet,
+ unsigned char *buf, int max_buf,
+ unsigned char **ptr_buf)
+{
+ GList *list;
+ uint16_t code;
+ int len;
+
+ if (dhcp_client->type == G_DHCP_IPV4)
+ return;
+
+ for (list = dhcp_client->request_list; list; list = list->next) {
+ code = (uint16_t) GPOINTER_TO_INT(list->data);
+
+ switch (code) {
+ case G_DHCPV6_CLIENTID:
+ if (dhcp_client->duid == NULL)
+ return;
+
+ len = 2 + 2 + dhcp_client->duid_len;
+ if ((*ptr_buf + len) > (buf + max_buf)) {
+ debug(dhcp_client, "Too long dhcpv6 message "
+ "when writing client id option");
+ return;
+ }
+
+ copy_option(*ptr_buf, G_DHCPV6_CLIENTID,
+ dhcp_client->duid_len, dhcp_client->duid);
+ (*ptr_buf) += len;
+ break;
+
+ case G_DHCPV6_SERVERID:
+ if (dhcp_client->server_duid == NULL)
+ return;
+
+ len = 2 + 2 + dhcp_client->server_duid_len;
+ if ((*ptr_buf + len) > (buf + max_buf)) {
+ debug(dhcp_client, "Too long dhcpv6 message "
+ "when writing server id option");
+ return;
+ }
+
+ copy_option(*ptr_buf, G_DHCPV6_SERVERID,
+ dhcp_client->server_duid_len,
+ dhcp_client->server_duid);
+ (*ptr_buf) += len;
+ break;
+
+ case G_DHCPV6_RAPID_COMMIT:
+ len = 2 + 2;
+ if ((*ptr_buf + len) > (buf + max_buf)) {
+ debug(dhcp_client, "Too long dhcpv6 message "
+ "when writing rapid commit option");
+ return;
+ }
+
+ copy_option(*ptr_buf, G_DHCPV6_RAPID_COMMIT, 0, 0);
+ (*ptr_buf) += len;
+ break;
+
+ case G_DHCPV6_ORO:
+ break;
+
+ case G_DHCPV6_DNS_SERVERS:
+ break;
+
+ case G_DHCPV6_SNTP_SERVERS:
+ break;
+
+ default:
+ break;
+ }
+ }
+}
+
+static void add_binary_option(gpointer key, gpointer value, gpointer user_data)
+{
+ uint8_t *option = value;
+ struct dhcp_packet *packet = user_data;
+
+ dhcp_add_binary_option(packet, option);
+}
+
+static void add_send_options(GDHCPClient *dhcp_client,
+ struct dhcp_packet *packet)
+{
+ g_hash_table_foreach(dhcp_client->send_value_hash,
+ add_binary_option, packet);
+}
+
+static const char *get_message_type(uint8_t type)
+{
+ switch (type) {
+ case DHCPDISCOVER:
+ return "DISCOVER";
+ case DHCPOFFER:
+ return "OFFER";
+ case DHCPREQUEST:
+ return "REQUEST";
+ case DHCPDECLINE:
+ return "DECLINE";
+ case DHCPACK:
+ return "ACK";
+ case DHCPNAK:
+ return "NAK";
+ case DHCPRELEASE:
+ return "RELEASE";
+ case DHCPINFORM:
+ return "INFORM";
+ default:
+ return "";
+ }
+}
+/*
+ * Return an RFC 951- and 2131-complaint BOOTP 'secs' value that
+ * represents the number of seconds elapsed from the start of
+ * attempting DHCP to satisfy some DHCP servers that allow for an
+ * "authoritative" reply before responding.
+ */
+static uint16_t dhcp_attempt_secs(GDHCPClient *dhcp_client)
+{
+ return htons(MIN(time(NULL) - dhcp_client->start, UINT16_MAX));
+}
+
+static int send_discover(GDHCPClient *dhcp_client, uint32_t requested)
+{
+ const uint8_t type = DHCPDISCOVER;
+ struct dhcp_packet packet;
+ struct in_addr dest;
+ char destbuf[INET_ADDRSTRLEN];
+
+ init_packet(dhcp_client, &packet, type);
+
+ packet.xid = dhcp_client->xid;
+ packet.secs = dhcp_attempt_secs(dhcp_client);
+
+ if (requested)
+ dhcp_add_option_uint32(&packet, DHCP_REQUESTED_IP, requested);
+
+ /* Explicitly saying that we want RFC-compliant packets helps
+ * some buggy DHCP servers to NOT send bigger packets */
+ dhcp_add_option_uint16(&packet, DHCP_MAX_SIZE, 576);
+
+ add_request_options(dhcp_client, &packet);
+
+ add_send_options(dhcp_client, &packet);
+
+ dest.s_addr = INADDR_BROADCAST;
+
+ debug(dhcp_client, "%s on %s to %s port %d interval %d",
+ get_message_type(type),
+ dhcp_client->interface,
+ inet_ntop(AF_INET, &dest, destbuf, sizeof(destbuf)),
+ SERVER_PORT,
+ DISCOVER_TIMEOUT);
+
+ return dhcp_send_raw_packet(&packet, INADDR_ANY, CLIENT_PORT,
+ dest.s_addr, SERVER_PORT,
+ MAC_BCAST_ADDR, dhcp_client->ifindex);
+}
+
+static int send_select(GDHCPClient *dhcp_client)
+{
+ const uint8_t type = DHCPREQUEST;
+ struct dhcp_packet packet;
+ struct in_addr request, dest;
+ char requestbuf[INET_ADDRSTRLEN], destbuf[INET_ADDRSTRLEN];
+
+ init_packet(dhcp_client, &packet, type);
+
+ packet.xid = dhcp_client->xid;
+ packet.secs = dhcp_attempt_secs(dhcp_client);
+
+ dhcp_add_option_uint32(&packet, DHCP_REQUESTED_IP,
+ dhcp_client->requested_ip);
+ dhcp_add_option_uint32(&packet, DHCP_SERVER_ID,
+ dhcp_client->server_ip);
+
+ add_request_options(dhcp_client, &packet);
+
+ add_send_options(dhcp_client, &packet);
+
+ request.s_addr = dhcp_client->requested_ip;
+ dest.s_addr = INADDR_BROADCAST;
+
+ debug(dhcp_client, "%s of %s on %s to %s port %d interval %d",
+ get_message_type(type),
+ inet_ntop(AF_INET, &request, requestbuf, sizeof(requestbuf)),
+ dhcp_client->interface,
+ inet_ntop(AF_INET, &dest, destbuf, sizeof(destbuf)),
+ SERVER_PORT,
+ REQUEST_TIMEOUT);
+
+ return dhcp_send_raw_packet(&packet, INADDR_ANY, CLIENT_PORT,
+ dest.s_addr, SERVER_PORT,
+ MAC_BCAST_ADDR, dhcp_client->ifindex);
+}
+
+static int send_renew(GDHCPClient *dhcp_client)
+{
+ const uint8_t type = DHCPREQUEST;
+ struct dhcp_packet packet;
+ struct in_addr request, dest;
+ char requestbuf[INET_ADDRSTRLEN], destbuf[INET_ADDRSTRLEN];
+
+ init_packet(dhcp_client , &packet, type);
+ packet.xid = dhcp_client->xid;
+ packet.ciaddr = htonl(dhcp_client->requested_ip);
+
+ add_request_options(dhcp_client, &packet);
+
+ add_send_options(dhcp_client, &packet);
+
+ request.s_addr = dhcp_client->requested_ip;
+ dest.s_addr = dhcp_client->server_ip;
+
+ debug(dhcp_client, "%s of %s on %s to %s port %d interval %d",
+ get_message_type(type),
+ inet_ntop(AF_INET, &request, requestbuf, sizeof(requestbuf)),
+ dhcp_client->interface,
+ inet_ntop(AF_INET, &dest, destbuf, sizeof(destbuf)),
+ SERVER_PORT,
+ REQUEST_TIMEOUT);
+
+ return dhcp_send_kernel_packet(&packet,
+ request.s_addr, CLIENT_PORT,
+ dest.s_addr, SERVER_PORT);
+}
+
+static int send_rebound(GDHCPClient *dhcp_client)
+{
+ const uint8_t type = DHCPREQUEST;
+ struct dhcp_packet packet;
+ struct in_addr request, dest;
+ char requestbuf[INET_ADDRSTRLEN], destbuf[INET_ADDRSTRLEN];
+
+ init_packet(dhcp_client , &packet, type);
+ packet.xid = dhcp_client->xid;
+ packet.ciaddr = htonl(dhcp_client->requested_ip);
+
+ add_request_options(dhcp_client, &packet);
+
+ add_send_options(dhcp_client, &packet);
+
+ request.s_addr = dhcp_client->requested_ip;
+ dest.s_addr = INADDR_BROADCAST;
+
+ debug(dhcp_client, "%s of %s on %s to %s port %d interval %d",
+ get_message_type(type),
+ inet_ntop(AF_INET, &request, requestbuf, sizeof(requestbuf)),
+ dhcp_client->interface,
+ inet_ntop(AF_INET, &dest, destbuf, sizeof(destbuf)),
+ SERVER_PORT,
+ REQUEST_TIMEOUT);
+
+ return dhcp_send_raw_packet(&packet, INADDR_ANY, CLIENT_PORT,
+ INADDR_BROADCAST, SERVER_PORT,
+ MAC_BCAST_ADDR, dhcp_client->ifindex);
+}
+
+static int send_release(GDHCPClient *dhcp_client,
+ uint32_t server, uint32_t ciaddr)
+{
+ const uint8_t type = DHCPRELEASE;
+ struct dhcp_packet packet;
+ struct in_addr release, dest;
+ char releasebuf[INET_ADDRSTRLEN], destbuf[INET_ADDRSTRLEN];
+
+ init_packet(dhcp_client, &packet, type);
+ packet.xid = rand();
+ packet.ciaddr = htonl(ciaddr);
+
+ dhcp_add_option_uint32(&packet, DHCP_SERVER_ID, server);
+
+ release.s_addr = ciaddr;
+ dest.s_addr = server;
+
+ debug(dhcp_client, "%s of %s on %s to %s port %d",
+ get_message_type(type),
+ inet_ntop(AF_INET, &release, releasebuf, sizeof(releasebuf)),
+ dhcp_client->interface,
+ inet_ntop(AF_INET, &dest, destbuf, sizeof(destbuf)),
+ SERVER_PORT);
+
+ return dhcp_send_kernel_packet(&packet, release.s_addr, CLIENT_PORT,
+ dest.s_addr, SERVER_PORT);
+}
+
+static gboolean ipv4ll_probe_timeout(gpointer dhcp_data);
+static int switch_listening_mode(GDHCPClient *dhcp_client,
+ ListenMode listen_mode);
+
+static gboolean send_probe_packet(gpointer dhcp_data)
+{
+ GDHCPClient *dhcp_client;
+ guint timeout;
+
+ dhcp_client = dhcp_data;
+ /* if requested_ip is not valid, pick a new address*/
+ if (dhcp_client->requested_ip == 0) {
+ debug(dhcp_client, "pick a new random address");
+ dhcp_client->requested_ip = ipv4ll_random_ip(0);
+ }
+
+ debug(dhcp_client, "sending IPV4LL probe request");
+
+ if (dhcp_client->retry_times == 1) {
+ dhcp_client->state = IPV4LL_PROBE;
+ switch_listening_mode(dhcp_client, L_ARP);
+ }
+ ipv4ll_send_arp_packet(dhcp_client->mac_address, 0,
+ dhcp_client->requested_ip, dhcp_client->ifindex);
+
+ if (dhcp_client->retry_times < PROBE_NUM) {
+ /*add a random timeout in range of PROBE_MIN to PROBE_MAX*/
+ timeout = ipv4ll_random_delay_ms(PROBE_MAX-PROBE_MIN);
+ timeout += PROBE_MIN*1000;
+ } else
+ timeout = (ANNOUNCE_WAIT * 1000);
+
+ dhcp_client->timeout =
+ timeout_add_full(G_PRIORITY_HIGH,
+ timeout,
+ ipv4ll_probe_timeout,
+ dhcp_client,
+ NULL);
+ return FALSE;
+}
+
+static gboolean ipv4ll_announce_timeout(gpointer dhcp_data);
+static gboolean ipv4ll_defend_timeout(gpointer dhcp_data);
+
+static gboolean send_announce_packet(gpointer dhcp_data)
+{
+ GDHCPClient *dhcp_client;
+
+ dhcp_client = dhcp_data;
+
+ debug(dhcp_client, "sending IPV4LL announce request");
+
+ ipv4ll_send_arp_packet(dhcp_client->mac_address,
+ dhcp_client->requested_ip,
+ dhcp_client->requested_ip,
+ dhcp_client->ifindex);
+
+ remove_timer(dhcp_client);
+
+ if (dhcp_client->state == IPV4LL_DEFEND) {
+ dhcp_client->timeout =
+ timeout_add_seconds_full(G_PRIORITY_HIGH,
+ DEFEND_INTERVAL,
+ ipv4ll_defend_timeout,
+ dhcp_client,
+ NULL);
+ set_wake(dhcp_client, DEFEND_INTERVAL);
+ return TRUE;
+ } else
+ dhcp_client->timeout =
+ timeout_add_seconds_full(G_PRIORITY_HIGH,
+ ANNOUNCE_INTERVAL,
+ ipv4ll_announce_timeout,
+ dhcp_client,
+ NULL);
+ set_wake(dhcp_client, ANNOUNCE_INTERVAL);
+ return TRUE;
+}
+
+static void get_interface_mac_address(int index, uint8_t *mac_address)
+{
+ struct ifreq ifr;
+ int sk, err;
+
+ sk = socket(PF_INET, SOCK_DGRAM | SOCK_CLOEXEC, 0);
+ if (sk < 0) {
+ perror("Open socket error");
+ return;
+ }
+
+ memset(&ifr, 0, sizeof(ifr));
+ ifr.ifr_ifindex = index;
+
+ err = ioctl(sk, SIOCGIFNAME, &ifr);
+ if (err < 0) {
+ perror("Get interface name error");
+ goto done;
+ }
+
+ err = ioctl(sk, SIOCGIFHWADDR, &ifr);
+ if (err < 0) {
+ perror("Get mac address error");
+ goto done;
+ }
+
+ memcpy(mac_address, ifr.ifr_hwaddr.sa_data, 6);
+
+done:
+ close(sk);
+}
+
+int g_dhcpv6_create_duid(GDHCPDuidType duid_type, int index, int type,
+ unsigned char **duid, int *duid_len)
+{
+ time_t duid_time;
+
+ switch (duid_type) {
+ case G_DHCPV6_DUID_LLT:
+ *duid_len = 2 + 2 + 4 + ETH_ALEN;
+ *duid = g_try_malloc(*duid_len);
+ if (*duid == NULL)
+ return -ENOMEM;
+
+ (*duid)[0] = 0;
+ (*duid)[1] = 1;
+ get_interface_mac_address(index, &(*duid)[2 + 2 + 4]);
+ (*duid)[2] = 0;
+ (*duid)[3] = type;
+ duid_time = time(NULL) - DUID_TIME_EPOCH;
+ (*duid)[4] = duid_time >> 24;
+ (*duid)[5] = duid_time >> 16;
+ (*duid)[6] = duid_time >> 8;
+ (*duid)[7] = duid_time & 0xff;
+ break;
+ case G_DHCPV6_DUID_EN:
+ return -EINVAL;
+ case G_DHCPV6_DUID_LL:
+ *duid_len = 2 + 2 + ETH_ALEN;
+ *duid = g_try_malloc(*duid_len);
+ if (*duid == NULL)
+ return -ENOMEM;
+
+ (*duid)[0] = 0;
+ (*duid)[1] = 3;
+ get_interface_mac_address(index, &(*duid)[2 + 2]);
+ (*duid)[2] = 0;
+ (*duid)[3] = type;
+ break;
+ }
+
+ return 0;
+}
+
+int g_dhcpv6_client_set_duid(GDHCPClient *dhcp_client, unsigned char *duid,
+ int duid_len)
+{
+ if (dhcp_client == NULL || dhcp_client->type == G_DHCP_IPV4)
+ return -EINVAL;
+
+ g_free(dhcp_client->duid);
+
+ dhcp_client->duid = duid;
+ dhcp_client->duid_len = duid_len;
+
+ return 0;
+}
+
+uint32_t g_dhcpv6_client_get_iaid(GDHCPClient *dhcp_client)
+{
+ if (dhcp_client == NULL || dhcp_client->type == G_DHCP_IPV4)
+ return 0;
+
+ return dhcp_client->iaid;
+}
+
+void g_dhcpv6_client_create_iaid(GDHCPClient *dhcp_client, int index,
+ unsigned char *iaid)
+{
+ uint8_t buf[6];
+
+ get_interface_mac_address(index, buf);
+
+ memcpy(iaid, &buf[2], 4);
+ dhcp_client->iaid = iaid[0] << 24 |
+ iaid[1] << 16 | iaid[2] << 8 | iaid[3];
+}
+
+int g_dhcpv6_client_get_timeouts(GDHCPClient *dhcp_client,
+ uint32_t *T1, uint32_t *T2,
+ time_t *last_renew, time_t *last_rebind,
+ time_t *expire)
+{
+ if (dhcp_client == NULL || dhcp_client->type == G_DHCP_IPV4)
+ return -EINVAL;
+
+ if (T1 != NULL)
+ *T1 = dhcp_client->T1;
+
+ if (T2 != NULL)
+ *T2 = dhcp_client->T2;
+
+ if (last_renew != NULL)
+ *last_renew = dhcp_client->last_renew;
+
+ if (last_rebind != NULL)
+ *last_rebind = dhcp_client->last_rebind;
+
+ if (expire != NULL)
+ *expire = dhcp_client->expire;
+
+ return 0;
+}
+
+static uint8_t *create_iaaddr(GDHCPClient *dhcp_client, uint8_t *buf,
+ uint16_t len)
+{
+ buf[0] = 0;
+ buf[1] = G_DHCPV6_IAADDR;
+ buf[2] = 0;
+ buf[3] = len;
+ memcpy(&buf[4], &dhcp_client->ia_na, 16);
+ memset(&buf[20], 0, 4); /* preferred */
+ memset(&buf[24], 0, 4); /* valid */
+ return buf;
+}
+
+static void put_iaid(GDHCPClient *dhcp_client, int index, uint8_t *buf)
+{
+ uint32_t iaid;
+
+ iaid = g_dhcpv6_client_get_iaid(dhcp_client);
+ if (iaid == 0) {
+ g_dhcpv6_client_create_iaid(dhcp_client, index, buf);
+ return;
+ }
+
+ buf[0] = iaid >> 24;
+ buf[1] = iaid >> 16;
+ buf[2] = iaid >> 8;
+ buf[3] = iaid;
+}
+
+int g_dhcpv6_client_set_ia(GDHCPClient *dhcp_client, int index,
+ int code, uint32_t *T1, uint32_t *T2,
+ gboolean add_iaaddr)
+{
+ if (code == G_DHCPV6_IA_TA) {
+ uint8_t ia_options[4];
+
+ put_iaid(dhcp_client, index, ia_options);
+
+ g_dhcp_client_set_request(dhcp_client, G_DHCPV6_IA_TA);
+ g_dhcpv6_client_set_send(dhcp_client, G_DHCPV6_IA_TA,
+ ia_options, sizeof(ia_options));
+
+ } else if (code == G_DHCPV6_IA_NA) {
+
+ g_dhcp_client_set_request(dhcp_client, G_DHCPV6_IA_NA);
+
+ if (add_iaaddr == TRUE) {
+#define IAADDR_LEN (16+4+4)
+ uint8_t ia_options[4+4+4+2+2+IAADDR_LEN];
+
+ put_iaid(dhcp_client, index, ia_options);
+
+ if (T1 != NULL) {
+ ia_options[4] = *T1 >> 24;
+ ia_options[5] = *T1 >> 16;
+ ia_options[6] = *T1 >> 8;
+ ia_options[7] = *T1;
+ } else
+ memset(&ia_options[4], 0x00, 4);
+
+ if (T2 != NULL) {
+ ia_options[8] = *T2 >> 24;
+ ia_options[9] = *T2 >> 16;
+ ia_options[10] = *T2 >> 8;
+ ia_options[11] = *T2;
+ } else
+ memset(&ia_options[8], 0x00, 4);
+
+ create_iaaddr(dhcp_client, &ia_options[12],
+ IAADDR_LEN);
+
+ g_dhcpv6_client_set_send(dhcp_client, G_DHCPV6_IA_NA,
+ ia_options, sizeof(ia_options));
+ } else {
+ uint8_t ia_options[4+4+4];
+
+ put_iaid(dhcp_client, index, ia_options);
+
+ memset(&ia_options[4], 0x00, 4); /* T1 (4 bytes) */
+ memset(&ia_options[8], 0x00, 4); /* T2 (4 bytes) */
+
+ g_dhcpv6_client_set_send(dhcp_client, G_DHCPV6_IA_NA,
+ ia_options, sizeof(ia_options));
+ }
+
+ } else
+ return -EINVAL;
+
+ return 0;
+}
+
+int g_dhcpv6_client_set_oro(GDHCPClient *dhcp_client, int args, ...)
+{
+ va_list va;
+ int i, j, len = sizeof(uint16_t) * args;
+ uint8_t *values;
+
+ values = g_try_malloc(len);
+ if (values == NULL)
+ return -ENOMEM;
+
+ va_start(va, args);
+ for (i = 0, j = 0; i < args; i++) {
+ uint16_t value = va_arg(va, int);
+ values[j++] = value >> 8;
+ values[j++] = value & 0xff;
+ }
+ va_end(va);
+
+ g_dhcpv6_client_set_send(dhcp_client, G_DHCPV6_ORO, values, len);
+
+ g_free(values);
+
+ return 0;
+}
+
+static int send_dhcpv6_msg(GDHCPClient *dhcp_client, int type, char *msg)
+{
+ struct dhcpv6_packet *packet;
+ uint8_t buf[MAX_DHCPV6_PKT_SIZE];
+ unsigned char *ptr;
+ int ret, max_buf;
+
+ memset(buf, 0, sizeof(buf));
+ packet = (struct dhcpv6_packet *)&buf[0];
+ ptr = buf + sizeof(struct dhcpv6_packet);
+
+ debug(dhcp_client, "sending DHCPv6 %s message", msg);
+
+ init_packet(dhcp_client, packet, type);
+
+ dhcp_client->xid = packet->transaction_id[0] << 16 |
+ packet->transaction_id[1] << 8 |
+ packet->transaction_id[2];
+
+ max_buf = MAX_DHCPV6_PKT_SIZE - sizeof(struct dhcpv6_packet);
+
+ add_dhcpv6_request_options(dhcp_client, packet, buf, max_buf, &ptr);
+
+ add_dhcpv6_send_options(dhcp_client, buf, max_buf, &ptr);
+
+ ret = dhcpv6_send_packet(dhcp_client->ifindex, packet, ptr - buf);
+
+ debug(dhcp_client, "sent %d pkt %p len %d", ret, packet, ptr - buf);
+ return ret;
+}
+
+static int send_solicitation(GDHCPClient *dhcp_client)
+{
+ return send_dhcpv6_msg(dhcp_client, DHCPV6_SOLICIT, "solicit");
+}
+
+static int send_dhcpv6_request(GDHCPClient *dhcp_client)
+{
+ return send_dhcpv6_msg(dhcp_client, DHCPV6_REQUEST, "request");
+}
+
+static int send_dhcpv6_renew(GDHCPClient *dhcp_client)
+{
+ return send_dhcpv6_msg(dhcp_client, DHCPV6_RENEW, "renew");
+}
+
+static int send_dhcpv6_rebind(GDHCPClient *dhcp_client)
+{
+ return send_dhcpv6_msg(dhcp_client, DHCPV6_REBIND, "rebind");
+}
+
+static int send_dhcpv6_release(GDHCPClient *dhcp_client)
+{
+ return send_dhcpv6_msg(dhcp_client, DHCPV6_RELEASE, "release");
+}
+
+static int send_information_req(GDHCPClient *dhcp_client)
+{
+ return send_dhcpv6_msg(dhcp_client, DHCPV6_INFORMATION_REQ,
+ "information-req");
+}
+
+static void remove_value(gpointer data, gpointer user_data)
+{
+ char *value = data;
+ g_free(value);
+}
+
+static void remove_option_value(gpointer data)
+{
+ GList *option_value = data;
+
+ g_list_foreach(option_value, remove_value, NULL);
+}
+
+GDHCPClient *g_dhcp_client_new(GDHCPType type,
+ int ifindex, GDHCPClientError *error)
+{
+ GDHCPClient *dhcp_client;
+
+ if (ifindex < 0) {
+ *error = G_DHCP_CLIENT_ERROR_INVALID_INDEX;
+ return NULL;
+ }
+
+ dhcp_client = g_try_new0(GDHCPClient, 1);
+ if (dhcp_client == NULL) {
+ *error = G_DHCP_CLIENT_ERROR_NOMEM;
+ return NULL;
+ }
+
+ dhcp_client->interface = get_interface_name(ifindex);
+ if (dhcp_client->interface == NULL) {
+ *error = G_DHCP_CLIENT_ERROR_INTERFACE_UNAVAILABLE;
+ goto error;
+ }
+
+ if (interface_is_up(ifindex) == FALSE) {
+ *error = G_DHCP_CLIENT_ERROR_INTERFACE_DOWN;
+ goto error;
+ }
+
+ get_interface_mac_address(ifindex, dhcp_client->mac_address);
+
+ dhcp_client->listener_sockfd = -1;
+ dhcp_client->listener_channel = NULL;
+ dhcp_client->listen_mode = L_NONE;
+ dhcp_client->ref_count = 1;
+ dhcp_client->type = type;
+ dhcp_client->ifindex = ifindex;
+ dhcp_client->lease_available_cb = NULL;
+ dhcp_client->ipv4ll_available_cb = NULL;
+ dhcp_client->no_lease_cb = NULL;
+ dhcp_client->lease_lost_cb = NULL;
+ dhcp_client->ipv4ll_lost_cb = NULL;
+ dhcp_client->address_conflict_cb = NULL;
+ dhcp_client->wake_event_cb = NULL;
+ dhcp_client->listener_watch = 0;
+ dhcp_client->retry_times = 0;
+ dhcp_client->ack_retry_times = 0;
+ dhcp_client->code_value_hash = g_hash_table_new_full(g_direct_hash,
+ g_direct_equal, NULL, remove_option_value);
+ dhcp_client->send_value_hash = g_hash_table_new_full(g_direct_hash,
+ g_direct_equal, NULL, g_free);
+ dhcp_client->request_list = NULL;
+ dhcp_client->require_list = NULL;
+ dhcp_client->duid = NULL;
+ dhcp_client->duid_len = 0;
+ dhcp_client->last_renew = dhcp_client->last_rebind = time(NULL);
+ dhcp_client->expire = 0;
+
+ *error = G_DHCP_CLIENT_ERROR_NONE;
+
+ return dhcp_client;
+
+error:
+ g_free(dhcp_client->interface);
+ g_free(dhcp_client);
+ return NULL;
+}
+
+#define SERVER_AND_CLIENT_PORTS ((67 << 16) + 68)
+
+static int dhcp_l2_socket(int ifindex)
+{
+ int fd;
+ struct sockaddr_ll sock;
+
+ /*
+ * Comment:
+ *
+ * I've selected not to see LL header, so BPF doesn't see it, too.
+ * The filter may also pass non-IP and non-ARP packets, but we do
+ * a more complete check when receiving the message in userspace.
+ *
+ * and filter shamelessly stolen from:
+ *
+ * http://www.flamewarmaster.de/software/dhcpclient/
+ *
+ * There are a few other interesting ideas on that page (look under
+ * "Motivation"). Use of netlink events is most interesting. Think
+ * of various network servers listening for events and reconfiguring.
+ * That would obsolete sending HUP signals and/or make use of restarts.
+ *
+ * Copyright: 2006, 2007 Stefan Rompf <sux@loplof.de>.
+ * License: GPL v2.
+ *
+ * TODO: make conditional?
+ */
+ static const struct sock_filter filter_instr[] = {
+ /* check for udp */
+ BPF_STMT(BPF_LD|BPF_B|BPF_ABS, 9),
+ /* L5, L1, is UDP? */
+ BPF_JUMP(BPF_JMP|BPF_JEQ|BPF_K, IPPROTO_UDP, 2, 0),
+ /* ugly check for arp on ethernet-like and IPv4 */
+ BPF_STMT(BPF_LD|BPF_W|BPF_ABS, 2), /* L1: */
+ BPF_JUMP(BPF_JMP|BPF_JEQ|BPF_K, 0x08000604, 3, 4),/* L3, L4 */
+ /* skip IP header */
+ BPF_STMT(BPF_LDX|BPF_B|BPF_MSH, 0), /* L5: */
+ /* check udp source and destination ports */
+ BPF_STMT(BPF_LD|BPF_W|BPF_IND, 0),
+ /* L3, L4 */
+ BPF_JUMP(BPF_JMP|BPF_JEQ|BPF_K, SERVER_AND_CLIENT_PORTS, 0, 1),
+ /* returns */
+ BPF_STMT(BPF_RET|BPF_K, 0x0fffffff), /* L3: pass */
+ BPF_STMT(BPF_RET|BPF_K, 0), /* L4: reject */
+ };
+
+ static const struct sock_fprog filter_prog = {
+ .len = sizeof(filter_instr) / sizeof(filter_instr[0]),
+ /* casting const away: */
+ .filter = (struct sock_filter *) filter_instr,
+ };
+
+ fd = socket(PF_PACKET, SOCK_DGRAM | SOCK_CLOEXEC, htons(ETH_P_IP));
+ if (fd < 0)
+ return fd;
+
+ if (SERVER_PORT == 67 && CLIENT_PORT == 68)
+ /* Use only if standard ports are in use */
+ setsockopt(fd, SOL_SOCKET, SO_ATTACH_FILTER, &filter_prog,
+ sizeof(filter_prog));
+
+ memset(&sock, 0, sizeof(sock));
+ sock.sll_family = AF_PACKET;
+ sock.sll_protocol = htons(ETH_P_IP);
+ sock.sll_ifindex = ifindex;
+
+ if (bind(fd, (struct sockaddr *) &sock, sizeof(sock)) != 0) {
+ close(fd);
+ return -errno;
+ }
+
+ return fd;
+}
+
+static gboolean sanity_check(struct ip_udp_dhcp_packet *packet, int bytes)
+{
+ if (packet->ip.protocol != IPPROTO_UDP)
+ return FALSE;
+
+ if (packet->ip.version != IPVERSION)
+ return FALSE;
+
+ if (packet->ip.ihl != sizeof(packet->ip) >> 2)
+ return FALSE;
+
+ if (packet->udp.dest != htons(CLIENT_PORT))
+ return FALSE;
+
+ if (ntohs(packet->udp.len) != (uint16_t)(bytes - sizeof(packet->ip)))
+ return FALSE;
+
+ return TRUE;
+}
+
+static int dhcp_recv_l2_packet(struct dhcp_packet *dhcp_pkt, int fd)
+{
+ int bytes;
+ struct ip_udp_dhcp_packet packet;
+ uint16_t check;
+
+ memset(&packet, 0, sizeof(packet));
+
+ bytes = read(fd, &packet, sizeof(packet));
+ if (bytes < 0)
+ return -1;
+
+ if (bytes < (int) (sizeof(packet.ip) + sizeof(packet.udp)))
+ return -1;
+
+ if (bytes < ntohs(packet.ip.tot_len))
+ /* packet is bigger than sizeof(packet), we did partial read */
+ return -1;
+
+ /* ignore any extra garbage bytes */
+ bytes = ntohs(packet.ip.tot_len);
+
+ if (sanity_check(&packet, bytes) == FALSE)
+ return -1;
+
+ check = packet.ip.check;
+ packet.ip.check = 0;
+ if (check != dhcp_checksum(&packet.ip, sizeof(packet.ip)))
+ return -1;
+
+ /* verify UDP checksum. IP header has to be modified for this */
+ memset(&packet.ip, 0, offsetof(struct iphdr, protocol));
+ /* ip.xx fields which are not memset: protocol, check, saddr, daddr */
+ packet.ip.tot_len = packet.udp.len; /* yes, this is needed */
+ check = packet.udp.check;
+ packet.udp.check = 0;
+ if (check && check != dhcp_checksum(&packet, bytes))
+ return -1;
+
+ memcpy(dhcp_pkt, &packet.data, bytes - (sizeof(packet.ip) +
+ sizeof(packet.udp)));
+
+ if (dhcp_pkt->cookie != htonl(DHCP_MAGIC))
+ return -1;
+
+ return bytes - (sizeof(packet.ip) + sizeof(packet.udp));
+}
+
+static void ipv4ll_start(GDHCPClient *dhcp_client)
+{
+ guint timeout;
+ int seed;
+
+ remove_timer(dhcp_client);
+
+ switch_listening_mode(dhcp_client, L_NONE);
+ dhcp_client->type = G_DHCP_IPV4LL;
+ dhcp_client->retry_times = 0;
+ dhcp_client->requested_ip = 0;
+
+ /*try to start with a based mac address ip*/
+ seed = (dhcp_client->mac_address[4] << 8 | dhcp_client->mac_address[4]);
+ dhcp_client->requested_ip = ipv4ll_random_ip(seed);
+
+ /*first wait a random delay to avoid storm of arp request on boot*/
+ timeout = ipv4ll_random_delay_ms(PROBE_WAIT);
+
+ dhcp_client->retry_times++;
+ dhcp_client->timeout = timeout_add_full(G_PRIORITY_HIGH,
+ timeout,
+ send_probe_packet,
+ dhcp_client,
+ NULL);
+}
+
+static void ipv4ll_stop(GDHCPClient *dhcp_client)
+{
+
+ switch_listening_mode(dhcp_client, L_NONE);
+
+ remove_timer(dhcp_client);
+
+ if (dhcp_client->listener_watch > 0) {
+ g_source_remove(dhcp_client->listener_watch);
+ dhcp_client->listener_watch = 0;
+ }
+
+ dhcp_client->state = IPV4LL_PROBE;
+ dhcp_client->retry_times = 0;
+ dhcp_client->requested_ip = 0;
+
+ g_free(dhcp_client->assigned_ip);
+ dhcp_client->assigned_ip = NULL;
+}
+
+static int ipv4ll_recv_arp_packet(GDHCPClient *dhcp_client)
+{
+ int bytes;
+ struct ether_arp arp;
+ uint32_t ip_requested;
+ int source_conflict;
+ int target_conflict;
+
+ memset(&arp, 0, sizeof(arp));
+ bytes = read(dhcp_client->listener_sockfd, &arp, sizeof(arp));
+ if (bytes < 0)
+ return bytes;
+
+ if (arp.arp_op != htons(ARPOP_REPLY) &&
+ arp.arp_op != htons(ARPOP_REQUEST))
+ return -EINVAL;
+
+ ip_requested = htonl(dhcp_client->requested_ip);
+ source_conflict = !memcmp(arp.arp_spa, &ip_requested,
+ sizeof(ip_requested));
+
+ target_conflict = !memcmp(arp.arp_tpa, &ip_requested,
+ sizeof(ip_requested));
+
+ if (!source_conflict && !target_conflict)
+ return 0;
+
+ dhcp_client->conflicts++;
+
+ debug(dhcp_client, "IPV4LL conflict detected");
+
+ if (dhcp_client->state == IPV4LL_MONITOR) {
+ if (!source_conflict)
+ return 0;
+ dhcp_client->state = IPV4LL_DEFEND;
+ debug(dhcp_client, "DEFEND mode conflicts : %d",
+ dhcp_client->conflicts);
+ /*Try to defend with a single announce*/
+ send_announce_packet(dhcp_client);
+ return 0;
+ }
+
+ if (dhcp_client->state == IPV4LL_DEFEND) {
+ if (!source_conflict)
+ return 0;
+ else if (dhcp_client->ipv4ll_lost_cb != NULL)
+ dhcp_client->ipv4ll_lost_cb(dhcp_client,
+ dhcp_client->ipv4ll_lost_data);
+ }
+
+ ipv4ll_stop(dhcp_client);
+
+ if (dhcp_client->conflicts < MAX_CONFLICTS) {
+ /*restart whole state machine*/
+ dhcp_client->retry_times++;
+ dhcp_client->timeout =
+ timeout_add_full(G_PRIORITY_HIGH,
+ ipv4ll_random_delay_ms(PROBE_WAIT),
+ send_probe_packet,
+ dhcp_client,
+ NULL);
+ }
+ /* Here we got a lot of conflicts, RFC3927 states that we have
+ * to wait RATE_LIMIT_INTERVAL before retrying,
+ * but we just report failure.
+ */
+ else if (dhcp_client->no_lease_cb != NULL)
+ dhcp_client->no_lease_cb(dhcp_client,
+ dhcp_client->no_lease_data);
+
+ return 0;
+}
+
+static gboolean check_package_owner(GDHCPClient *dhcp_client, gpointer pkt)
+{
+ if (dhcp_client->type