Merge commit 'garage/master' maemo-0.19
authorJukka Rissanen <jukka.rissanen@nokia.com>
Wed, 24 Jun 2009 18:32:05 +0000 (21:32 +0300)
committerJukka Rissanen <jukka.rissanen@nokia.com>
Wed, 24 Jun 2009 18:32:05 +0000 (21:32 +0300)
179 files changed:
.gitignore [new file with mode: 0644]
AUTHORS [new file with mode: 0644]
COPYING [new file with mode: 0644]
ChangeLog [new file with mode: 0644]
HACKING [new file with mode: 0644]
INSTALL [new file with mode: 0644]
Makefile.am [new file with mode: 0644]
NEWS [new file with mode: 0644]
README [new file with mode: 0644]
TODO [new file with mode: 0644]
acinclude.m4 [new file with mode: 0644]
bootstrap [new file with mode: 0755]
bootstrap-configure [new file with mode: 0755]
client/Makefile.am [new file with mode: 0644]
client/main.c [new file with mode: 0644]
configure.ac [new file with mode: 0644]
connman.pc.in [new file with mode: 0644]
debian/changelog [new file with mode: 0644]
debian/compat [new file with mode: 0644]
debian/connman-dev.install [new file with mode: 0644]
debian/connman-doc.install [new file with mode: 0644]
debian/connman-test.install [new file with mode: 0644]
debian/connman.default [new file with mode: 0644]
debian/connman.init [new file with mode: 0644]
debian/connman.install [new file with mode: 0644]
debian/connman.upstart [new file with mode: 0644]
debian/control [new file with mode: 0644]
debian/rules [new file with mode: 0755]
doc/Makefile.am [new file with mode: 0644]
doc/agent-api.txt [new file with mode: 0644]
doc/behavior-api.txt [new file with mode: 0644]
doc/connection-api.txt [new file with mode: 0644]
doc/connman-docs.xml [new file with mode: 0644]
doc/connman-introduction.xml [new file with mode: 0644]
doc/device-api.txt [new file with mode: 0644]
doc/gtk-doc.make [new file with mode: 0644]
doc/manager-api.txt [new file with mode: 0644]
doc/network-api.txt [new file with mode: 0644]
doc/overview-api.txt [new file with mode: 0644]
doc/plugin-api.txt [new file with mode: 0644]
doc/profile-api.txt [new file with mode: 0644]
doc/rfc1035.txt [new file with mode: 0644]
doc/rfc2131.txt [new file with mode: 0644]
doc/rfc2132.txt [new file with mode: 0644]
doc/service-api.txt [new file with mode: 0644]
doc/version.xml.in [new file with mode: 0644]
gatchat/Makefile.am [new file with mode: 0644]
gatchat/gatchat.c [new file with mode: 0644]
gatchat/gatchat.h [new file with mode: 0644]
gatchat/gatresult.c [new file with mode: 0644]
gatchat/gatresult.h [new file with mode: 0644]
gatchat/ringbuffer.c [new file with mode: 0644]
gatchat/ringbuffer.h [new file with mode: 0644]
gdbus/Makefile.am [new file with mode: 0644]
gdbus/gdbus.h [new file with mode: 0644]
gdbus/mainloop.c [new file with mode: 0644]
gdbus/object.c [new file with mode: 0644]
gdbus/watch.c [new file with mode: 0644]
include/Makefile.am [new file with mode: 0644]
include/dbus.h [new file with mode: 0644]
include/device.h [new file with mode: 0644]
include/driver.h [new file with mode: 0644]
include/element.h [new file with mode: 0644]
include/inet.h [new file with mode: 0644]
include/ipconfig.h [new file with mode: 0644]
include/ipv4.h [new file with mode: 0644]
include/log.h [new file with mode: 0644]
include/network.h [new file with mode: 0644]
include/notifier.h [new file with mode: 0644]
include/option.h [new file with mode: 0644]
include/plugin.h [new file with mode: 0644]
include/property.h [new file with mode: 0644]
include/resolver.h [new file with mode: 0644]
include/rfkill.h [new file with mode: 0644]
include/rtnl.h [new file with mode: 0644]
include/security.h [new file with mode: 0644]
include/service.h [new file with mode: 0644]
include/storage.h [new file with mode: 0644]
include/types.h [new file with mode: 0644]
include/version.h.in [new file with mode: 0644]
plugins/Makefile.am [new file with mode: 0644]
plugins/bluetooth.c [new file with mode: 0644]
plugins/dhclient.c [new file with mode: 0644]
plugins/dnsproxy.c [new file with mode: 0644]
plugins/ethernet.c [new file with mode: 0644]
plugins/fake.c [new file with mode: 0644]
plugins/hso.c [new file with mode: 0644]
plugins/huawei.c [new file with mode: 0644]
plugins/iospm.c [new file with mode: 0644]
plugins/iwmx.c [new file with mode: 0644]
plugins/iwmx.h [new file with mode: 0644]
plugins/iwmxsdk.c [new file with mode: 0644]
plugins/loopback.c [new file with mode: 0644]
plugins/modem.c [new file with mode: 0644]
plugins/modem.h [new file with mode: 0644]
plugins/modemmgr.c [new file with mode: 0644]
plugins/novatel.c [new file with mode: 0644]
plugins/ofono.c [new file with mode: 0644]
plugins/polkit.c [new file with mode: 0644]
plugins/polkit.policy [new file with mode: 0644]
plugins/pppd.c [new file with mode: 0644]
plugins/resolvconf.c [new file with mode: 0644]
plugins/supplicant.c [new file with mode: 0644]
plugins/supplicant.h [new file with mode: 0644]
plugins/task.c [new file with mode: 0644]
plugins/task.h [new file with mode: 0644]
plugins/udhcp.c [new file with mode: 0644]
plugins/wifi.c [new file with mode: 0644]
scripts/Makefile.am [new file with mode: 0644]
scripts/connman.in [new file with mode: 0644]
scripts/dhclient-script.c [new file with mode: 0644]
scripts/dhclient.conf [new file with mode: 0644]
scripts/pppd-plugin.c [new file with mode: 0644]
scripts/udhcpc-script.c [new file with mode: 0644]
shave-libtool.in [new file with mode: 0644]
shave.in [new file with mode: 0644]
src/Makefile.am [new file with mode: 0644]
src/agent.c [new file with mode: 0644]
src/connection.c [new file with mode: 0644]
src/connman-dbus.conf [new file with mode: 0644]
src/connman-polkit.conf [new file with mode: 0644]
src/connman.h [new file with mode: 0644]
src/connman.rules [new file with mode: 0644]
src/connman.service.in [new file with mode: 0644]
src/dbus.c [new file with mode: 0644]
src/detect.c [new file with mode: 0644]
src/device.c [new file with mode: 0644]
src/element.c [new file with mode: 0644]
src/error.c [new file with mode: 0644]
src/inet.c [new file with mode: 0644]
src/ipconfig.c [new file with mode: 0644]
src/ipv4.c [new file with mode: 0644]
src/log.c [new file with mode: 0644]
src/main.c [new file with mode: 0644]
src/manager.c [new file with mode: 0644]
src/network.c [new file with mode: 0644]
src/notifier.c [new file with mode: 0644]
src/plugin.c [new file with mode: 0644]
src/profile.c [new file with mode: 0644]
src/resolver.c [new file with mode: 0644]
src/rfkill.c [new file with mode: 0644]
src/rtnl.c [new file with mode: 0644]
src/security.c [new file with mode: 0644]
src/selftest.c [new file with mode: 0644]
src/service.c [new file with mode: 0644]
src/storage.c [new file with mode: 0644]
src/udev.c [new file with mode: 0644]
test/Makefile.am [new file with mode: 0644]
test/connect-network [new file with mode: 0755]
test/create-network [new file with mode: 0755]
test/debug-connman [new file with mode: 0755]
test/disable-device [new file with mode: 0755]
test/disable-network [new file with mode: 0755]
test/disconnect-network [new file with mode: 0755]
test/enable-device [new file with mode: 0755]
test/get-state [new file with mode: 0755]
test/join-network [new file with mode: 0755]
test/list-connections [new file with mode: 0755]
test/list-devices [new file with mode: 0755]
test/list-networks [new file with mode: 0755]
test/list-profiles [new file with mode: 0755]
test/list-services [new file with mode: 0755]
test/monitor-connman [new file with mode: 0755]
test/monitor-services [new file with mode: 0755]
test/select-connection [new file with mode: 0755]
test/select-network [new file with mode: 0755]
test/set-address [new file with mode: 0755]
test/set-passphrase [new file with mode: 0755]
test/set-policy [new file with mode: 0755]
test/set-priority [new file with mode: 0755]
test/show-introspection [new file with mode: 0755]
test/simple-agent [new file with mode: 0755]
test/start-scanning [new file with mode: 0755]
test/test-compat [new file with mode: 0755]
test/test-connman [new file with mode: 0755]
test/test-manager [new file with mode: 0755]
test/test-supplicant [new file with mode: 0755]
tools/Makefile.am [new file with mode: 0644]
tools/wifi-scan.c [new file with mode: 0644]

diff --git a/.gitignore b/.gitignore
new file mode 100644 (file)
index 0000000..e8a49e0
--- /dev/null
@@ -0,0 +1,51 @@
+*.o
+*.a
+*.lo
+*.la
+.deps
+.libs
+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
+shave
+shave-libtool
+
+connman.pc
+include/connman
+include/version.h
+src/connmand
+src/connman.exp
+src/connman.ver
+src/connman.conf
+src/connman.service
+src/*-connman.rules
+plugins/connman.policy
+plugins/builtin.h
+scripts/connman
+scripts/udhcpc-script
+scripts/dhclient-script
+client/cm
+tools/wifi-scan
+doc/*.bak
+doc/*.stamp
+doc/connman.*
+doc/connman-*.txt
+doc/*.sgml
+doc/version.xml
+doc/xml
+doc/html
diff --git a/AUTHORS b/AUTHORS
new file mode 100644 (file)
index 0000000..ca398ed
--- /dev/null
+++ b/AUTHORS
@@ -0,0 +1,2 @@
+Marcel Holtmann <marcel@holtmann.org>
+Inaky Perez-Gonzalez <inaky@linux.intel.com>
diff --git a/COPYING b/COPYING
new file mode 100644 (file)
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 (file)
index 0000000..ba7f17c
--- /dev/null
+++ b/ChangeLog
@@ -0,0 +1,153 @@
+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 (file)
index 0000000..61b9eb3
--- /dev/null
+++ b/HACKING
@@ -0,0 +1,149 @@
+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
+
+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.
+
+  During start of daemon
+    {Manager} [/] Devices = dbus.Array([dbus.ObjectPath('/dev_00_90_CC ...
+    {Device} [/dev_00_90_CC_xx_xx_xx] Powered = 1
+    {Device} [/dev_00_90_CC_xx_xx_xx] Networks = dbus.Array( ...
+
+  During shutdown of daemon
+    {Device} [/dev_00_90_CC_xx_xx_xx] Networks = dbus.Array( ...
+    {Device} [/dev_00_90_CC_xx_xx_xx] Powered = 0
+    {Manager} [/] Devices = dbus.Array([], ...
+
+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 (file)
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 (file)
index 0000000..809bf56
--- /dev/null
@@ -0,0 +1,40 @@
+
+SUBDIRS = gdbus gatchat include plugins src client tools scripts test doc
+
+pkgconfigdir = $(libdir)/pkgconfig
+
+pkgconfig_DATA = connman.pc
+
+DISTCHECK_CONFIGURE_FLAGS = --disable-gtk-doc \
+                               --disable-datafiles \
+                               --enable-loopback \
+                               --enable-ethernet \
+                               --enable-wifi \
+                               --enable-bluetooth \
+                               --enable-udhcp \
+                               --enable-dhclient \
+                               --enable-resolvconf \
+                               --enable-dnsproxy \
+                               --enable-novatel \
+                               --enable-huawei \
+                               --enable-hso \
+                               --enable-client \
+                               --enable-tools
+
+DISTCLEANFILES = $(pkgconfig_DATA)
+
+MAINTAINERCLEANFILES = Makefile.in \
+       aclocal.m4 configure config.h.in config.sub config.guess \
+       ltmain.sh depcomp compile missing install-sh mkinstalldirs \
+       shave shave-libtool
+
+# get rid of debian build dirs
+maintainer-clean-local:
+       -rm -rf debian/build debian/tmp \
+               debian/connman \
+               debian/connman-dbg \
+               debian/connman-doc \
+               debian/connman-dev \
+               debian/connman-test \
+               debian/*.debhelper debian/files debian/*.substvars \
+               build-stamp configure-stamp stamp-h.in
diff --git a/NEWS b/NEWS
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/README b/README
new file mode 100644 (file)
index 0000000..37130dd
--- /dev/null
+++ b/README
@@ -0,0 +1,145 @@
+Connection Manager
+******************
+
+Copyright (C) 2007-2009  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, routing and DNS configuration
+
+Various plugins can be enabled for networking support:
+       - Ethernet plugin
+       - WiFi plugin with WEP40/WEP128 and WPA/WPA2 (personal only) support
+       - Bluetooth plugin
+
+Also plugins with additional features are available:
+       - DHCP plugins (uDHCP and dhclient)
+       - Resolver plugins (resolvconf and DNS proxy)
+       - Loopback setup
+       - PolicyKit support
+
+
+Compilation and installation
+============================
+
+In order to compile Connection Manager you need following software packages:
+       - GCC compiler
+       - GLib library
+       - D-Bus library
+       - udev library (optional)
+       - PolicyKit (optional)
+       - PPP support (optional)
+
+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:
+
+       --enable-ethernet
+
+               Enable support for Ethernet network cards
+
+       --enable-wifi
+
+               Enable support for WiFi devices (requires wpa_supplicant)
+
+
+       --enable-bluetooth
+
+               Enable support for Bluetooth devices (requires BlueZ)
+
+       --enable-ppp
+
+               Enable PPP support for dialup connections (requires pppd)
+
+               The location of the pppd binary is auto-detected, but it
+               can be overwritten via --with-pppd=<path-to-binary>.
+
+       --enable-udhcp
+
+               Enable DHCP client support for BusyBox based systems
+
+               The location of the udhcpc binary is auto-detected, but it
+               can be overwritten via --with-udhcpc=<path-to-binary>.
+
+       --enable-dhclient
+
+               Enable DHCP client support for ISC dhclient based systems
+
+               The location of the dhclient binary is auto-detected, but it
+               can be overwritten via --with-dhclient=<path-to-binary>.
+
+               At least one DHCP client option should be selected. It is
+               possible to select both and then uDHCP will be tried first
+               before falling back to dhclient.
+
+       --enable-dnsproxy
+
+               Enable DNS proxy support for /etc/resolv.conf abstraction
+
+               The best solution for multiple connections and proper DNS
+               handling is a DNS proxy server. This binds a DNS proxy
+               server to port 53 on the loopback interface (127.0.0.1).
+
+               The /etc/resolv.conf file needs a "nameserver 127.0.0.1"
+               entry, but can now set the immutable bit or be on a read-only
+               filesystem. No further modification to that file will be made.
+
+               It is important that this is not used together with other
+               DNS proxy solution like dnsmasq.
+
+       --enable-resolvconf
+
+               Enable resolvconf support for Debian/Ubuntu based systems
+
+               The resolvconf package from Debian can be used to handle
+               configuration of the /etc/resolv.conf file.
+
+               It is safe to select this option even when resolvconf is not
+               installed. A missing resolvconf will be detected and in that
+               case it falls back to modifying /etc/resolv.conf directly.
+
+               The location of the resolvconf binary is auto-detected, but it
+               can be overwritten via --with-resolvconf=<path-to-binary>.
+
+       --enable-loopback
+
+               Enable 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 select this option even if networking scripts
+               are in place. It detects an already configured loopback
+               device and leaves it as it is.
+
+       --enable-udev
+
+               Enable device detection support via udev
+
+               Network devices are by default detected via the builtin RTNL
+               functionality. This allows to detect TTY based modem devices
+               via udev.
+
+       --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.
+
diff --git a/TODO b/TODO
new file mode 100644 (file)
index 0000000..ee6a01e
--- /dev/null
+++ b/TODO
@@ -0,0 +1,32 @@
+
+  o Support for WPA/WPA2 Enterprise WiFi authentication
+
+       With wpa_supplicant the support for WPA/WPA2 Enterprise is already
+       present, but it needs properly hooked up. This involves a correct
+       security architecture since it uses personalized credentials.
+
+  o Support for WPS based WiFi authentication
+
+       Newer versions of wpa_supplicant contain support for WPS. For full
+       integration of WPS support, the agent concept needs to be extended.
+
+  o Support for multiple profiles
+
+       Currently only one active profile (the default profile) is supported.
+
+  o Support for static IPv4 configuration
+
+       This is in theory possible, but no D-Bus API has been defined on how
+       to configure it.
+
+  o Support for interface statistics
+
+       Information about carrier speed etc. should be exported.
+
+  o Support for handling RFKILL events
+
+       The RFKILL events are received via the udev infrastructure, but not
+       acted upon. Current problem is to assign the event to the correct
+       physical device. In case of platform RFKILL switches this is a real
+       problem and not solved yet.
+
diff --git a/acinclude.m4 b/acinclude.m4
new file mode 100644 (file)
index 0000000..7b83602
--- /dev/null
@@ -0,0 +1,115 @@
+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"
+       fi
+])
+
+AC_DEFUN([SHAVE_ARG_ENABLE],
+[
+  AC_ARG_ENABLE([shave],
+    AS_HELP_STRING(
+      [--enable-shave],
+      [use shave to make the build pretty [[default=no]]]),,
+      [enable_shave=no]
+    )
+  AC_CONFIG_FILES(shave shave-libtool)
+])
+
+AC_DEFUN([SHAVE_INIT],
+[
+  if test x"$enable_shave" = xyes; then
+    dnl where can we find the shave scripts?
+    m4_if([$1],,
+      [shavedir="$ac_pwd"],
+      [shavedir="$ac_pwd/$1"])
+    AC_SUBST(shavedir)
+
+    dnl make is now quiet
+    AC_SUBST([MAKEFLAGS], [-s])
+    AC_SUBST([AM_MAKEFLAGS], ['`test -z $V && echo -s`'])
+
+    dnl we need sed
+    AC_CHECK_PROG(SED,sed,sed,false)
+
+    dnl substitute libtool
+    SHAVE_SAVED_LIBTOOL=$LIBTOOL
+    LIBTOOL="${SHELL} ${shavedir}/shave-libtool '${SHAVE_SAVED_LIBTOOL}'"
+    AC_SUBST(LIBTOOL)
+
+    dnl substitute cc/cxx
+    SHAVE_SAVED_CC=$CC
+    SHAVE_SAVED_CXX=$CXX
+    SHAVE_SAVED_FC=$FC
+    SHAVE_SAVED_F77=$F77
+    CC="${SHELL} ${shavedir}/shave cc ${SHAVE_SAVED_CC}"
+    CXX="${SHELL} ${shavedir}/shave cxx ${SHAVE_SAVED_CXX}"
+    FC="${SHELL} ${shavedir}/shave fc ${SHAVE_SAVED_FC}"
+    F77="${SHELL} ${shavedir}/shave f77 ${SHAVE_SAVED_F77}"
+    AC_SUBST(CC)
+    AC_SUBST(CXX)
+    AC_SUBST(FC)
+    AC_SUBST(F77)
+
+    V=@
+  else
+    V=1
+  fi
+  Q='$(V:1=)'
+  AC_SUBST(V)
+  AC_SUBST(Q)
+])
+
+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 (executable)
index 0000000..0dd71d9
--- /dev/null
+++ b/bootstrap
@@ -0,0 +1,7 @@
+#!/bin/sh
+
+aclocal && \
+    autoheader && \
+        libtoolize --automake --copy --force && \
+           automake --add-missing --copy && \
+               autoconf
diff --git a/bootstrap-configure b/bootstrap-configure
new file mode 100755 (executable)
index 0000000..541d663
--- /dev/null
@@ -0,0 +1,38 @@
+#!/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-loopback=builtin \
+               --enable-ethernet=builtin \
+               --enable-wifi=builtin \
+               --enable-bluetooth=builtin \
+               --enable-ofono=builtin \
+               --enable-modemmgr=builtin \
+               --enable-udhcp=builtin \
+               --enable-dhclient=builtin \
+               --enable-resolvconf=builtin \
+               --enable-dnsproxy=builtin \
+               --enable-novatel \
+               --enable-huawei \
+               --enable-hso=builtin \
+               --enable-ppp \
+               --enable-udev \
+               --enable-iwmx \
+               --enable-iospm \
+               --enable-polkit=builtin \
+               --enable-client \
+               --enable-tools $*
diff --git a/client/Makefile.am b/client/Makefile.am
new file mode 100644 (file)
index 0000000..4dc1608
--- /dev/null
@@ -0,0 +1,12 @@
+
+if CLIENT
+noinst_PROGRAMS = cm
+
+cm_SOURCES = main.c
+
+cm_LDADD = @DBUS_LIBS@
+endif
+
+AM_CFLAGS = @DBUS_CFLAGS@
+
+MAINTAINERCLEANFILES = Makefile.in
diff --git a/client/main.c b/client/main.c
new file mode 100644 (file)
index 0000000..b317c9c
--- /dev/null
@@ -0,0 +1,221 @@
+/*
+ *
+ *  Connection Manager
+ *
+ *  Copyright (C) 2007-2009  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 <errno.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <dbus/dbus.h>
+
+#define CONNMAN_SERVICE                        "org.moblin.connman"
+
+#define CONNMAN_MANAGER_INTERFACE      CONNMAN_SERVICE ".Manager"
+#define CONNMAN_MANAGER_PATH           "/"
+
+static DBusMessage *get_properties(DBusConnection *connection)
+{
+       DBusMessage *message, *reply;
+       DBusError error;
+
+       message = dbus_message_new_method_call(CONNMAN_SERVICE,
+                                               CONNMAN_MANAGER_PATH,
+                                               CONNMAN_MANAGER_INTERFACE,
+                                                       "GetProperties");
+       if (message == NULL)
+               return NULL;
+
+       dbus_error_init(&error);
+
+       reply = dbus_connection_send_with_reply_and_block(connection,
+                                                       message, -1, &error);
+       if (reply == NULL) {
+               if (dbus_error_is_set(&error) == TRUE) {
+                       fprintf(stderr, "%s\n", error.message);
+                       dbus_error_free(&error);
+               } else
+                       fprintf(stderr, "Failed to get properties\n");
+               dbus_message_unref(message);
+               return NULL;
+       }
+
+       dbus_message_unref(message);
+
+       return reply;
+}
+
+static const char *extract_state(DBusMessage *message)
+{
+       DBusMessageIter array, dict;
+
+       dbus_message_iter_init(message, &array);
+       dbus_message_iter_recurse(&array, &dict);
+
+       while (dbus_message_iter_get_arg_type(&dict) == DBUS_TYPE_DICT_ENTRY) {
+               DBusMessageIter entry, value;
+               const char *key;
+
+               dbus_message_iter_recurse(&dict, &entry);
+               dbus_message_iter_get_basic(&entry, &key);
+
+               dbus_message_iter_next(&entry);
+
+               dbus_message_iter_recurse(&entry, &value);
+
+               //type = dbus_message_iter_get_arg_type(&value);
+               //dbus_message_iter_get_basic(&value, &val);
+
+               if (strcmp(key, "State") == 0) {
+                       const char *val;
+                       dbus_message_iter_get_basic(&value, &val);
+                       return val;
+               }
+
+               dbus_message_iter_next(&dict);
+       }
+
+       return NULL;
+}
+
+static void print_objects(DBusMessageIter *array)
+{
+       DBusMessageIter value;
+
+       if (dbus_message_iter_get_arg_type(array) != DBUS_TYPE_ARRAY)
+               return;
+
+       dbus_message_iter_recurse(array, &value);
+
+       while (dbus_message_iter_get_arg_type(&value) == DBUS_TYPE_OBJECT_PATH) {
+               const char *path;
+
+               dbus_message_iter_get_basic(&value, &path);
+
+               printf("%s\n", path);
+
+               dbus_message_iter_next(&value);
+       }
+}
+
+static void extract_devices(DBusMessage *message)
+{
+       DBusMessageIter array, dict;
+
+       dbus_message_iter_init(message, &array);
+       dbus_message_iter_recurse(&array, &dict);
+
+       while (dbus_message_iter_get_arg_type(&dict) == DBUS_TYPE_DICT_ENTRY) {
+               DBusMessageIter entry, value;
+               const char *key;
+
+               dbus_message_iter_recurse(&dict, &entry);
+               dbus_message_iter_get_basic(&entry, &key);
+
+               dbus_message_iter_next(&entry);
+
+               dbus_message_iter_recurse(&entry, &value);
+
+               //type = dbus_message_iter_get_arg_type(&value);
+               //dbus_message_iter_get_basic(&value, &val);
+
+               if (strcmp(key, "Devices") == 0) {
+                       print_objects(&value);
+                       return;
+               }
+
+               dbus_message_iter_next(&dict);
+       }
+}
+
+static int cmd_status(DBusConnection *connection)
+{
+       DBusMessage *message;
+       const char *state;
+
+       message = get_properties(connection);
+
+       state = extract_state(message);
+
+       dbus_message_unref(message);
+
+       if (state == NULL)
+               return -EINVAL;
+
+       printf("System is %s\n", state);
+
+       return 0;
+}
+
+static int cmd_devices(DBusConnection *connection)
+{
+       DBusMessage *message;
+
+       message = get_properties(connection);
+
+       extract_devices(message);
+
+       dbus_message_unref(message);
+
+       return 0;
+}
+
+static void usage(const char *program)
+{
+       printf("ConnMan utility ver %s\n\n", VERSION);
+
+       printf("Usage:\n"
+               "\t%s <command>\n\n", program);
+
+       printf("Commands:\n"
+               "\thelp\n"
+               "\tdev\n"
+               "\n");
+}
+
+int main(int argc, char *argv[])
+{
+       DBusConnection *conn;
+
+       if (argc > 1 && strcmp(argv[1], "help") == 0) {
+               usage(argv[0]);
+               exit(0);
+       }
+
+       conn = dbus_bus_get(DBUS_BUS_SYSTEM, NULL);
+       if (!conn) {
+               fprintf(stderr, "Can't get on system bus\n");
+               exit(1);
+       }
+
+       if (argc > 1) {
+               if (strcmp(argv[1], "dev") == 0)
+                       cmd_devices(conn);
+       } else
+               cmd_status(conn);
+
+       dbus_connection_unref(conn);
+
+       return 0;
+}
diff --git a/configure.ac b/configure.ac
new file mode 100644 (file)
index 0000000..c1b0014
--- /dev/null
@@ -0,0 +1,317 @@
+AC_PREREQ(2.60)
+AC_INIT()
+
+AM_INIT_AUTOMAKE(connman, 0.19)
+AM_CONFIG_HEADER(config.h)
+
+AM_MAINTAINER_MODE
+
+AC_PREFIX_DEFAULT(/usr/local)
+
+COMPILER_FLAGS
+
+AC_LANG_C
+
+AC_PROG_CC
+AM_PROG_CC_C_O
+AC_PROG_CC_PIE
+AC_PROG_INSTALL
+
+m4_define([_LT_AC_TAGCONFIG], [])
+m4_ifdef([AC_LIBTOOL_TAGS], [AC_LIBTOOL_TAGS([])])
+
+AC_DISABLE_STATIC
+AC_PROG_LIBTOOL
+
+GTK_DOC_CHECK
+
+SHAVE_ARG_ENABLE
+
+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 -O0"
+       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(loopback,
+       AC_HELP_STRING([--enable-loopback], [enable loopback support]),
+                       [enable_loopback=${enableval}], [enable_loopback="no"])
+AM_CONDITIONAL(LOOPBACK, test "${enable_loopback}" != "no")
+AM_CONDITIONAL(LOOPBACK_BUILTIN, test "${enable_loopback}" = "builtin")
+
+AC_ARG_ENABLE(ethernet,
+       AC_HELP_STRING([--enable-ethernet], [enable Ethernet support]),
+                       [enable_ethernet=${enableval}], [enable_ethernet="no"])
+AM_CONDITIONAL(ETHERNET, test "${enable_ethernet}" != "no")
+AM_CONDITIONAL(ETHERNET_BUILTIN, test "${enable_ethernet}" = "builtin")
+
+AC_ARG_ENABLE(wifi,
+       AC_HELP_STRING([--enable-wifi], [enable WiFi support]),
+                       [enable_wifi=${enableval}], [enable_wifi="no"])
+if (test "${enable_wifi}" != "no"); then
+       AC_PATH_PROG(WPASUPPLICANT, [wpa_supplicant], [],
+                                               $PATH:/sbin:/usr/sbin)
+fi
+AM_CONDITIONAL(WIFI, test "${enable_wifi}" != "no")
+AM_CONDITIONAL(WIFI_BUILTIN, test "${enable_wifi}" = "builtin")
+
+AC_ARG_ENABLE(bluetooth,
+       AC_HELP_STRING([--enable-bluetooth], [enable Bluetooth support]),
+                       [enable_bluetooth=${enableval}], [enable_bluetooth="no"])
+AM_CONDITIONAL(BLUETOOTH, test "${enable_bluetooth}" != "no")
+AM_CONDITIONAL(BLUETOOTH_BUILTIN, test "${enable_bluetooth}" = "builtin")
+
+AC_ARG_ENABLE(ofono,
+       AC_HELP_STRING([--enable-ofono], [enable oFono support]),
+                       [enable_ofono=${enableval}], [enable_ofono="no"])
+AM_CONDITIONAL(OFONO, test "${enable_ofono}" != "no")
+AM_CONDITIONAL(OFONO_BUILTIN, test "${enable_ofono}" = "builtin")
+
+AC_ARG_ENABLE(modemmgr,
+       AC_HELP_STRING([--enable-modemmgr], [enable Modem Manager support]),
+                       [enable_modemmgr=${enableval}], [enable_modemmgr="no"])
+AM_CONDITIONAL(MODEMMGR, test "${enable_modemmgr}" != "no")
+AM_CONDITIONAL(MODEMMGR_BUILTIN, test "${enable_modemmgr}" = "builtin")
+
+AC_ARG_WITH(udhcpc, AC_HELP_STRING([--with-udhcpc=PROGRAM],
+       [specify location of udhcpc binary]), [path_udhcpc=${withval}])
+
+AC_ARG_ENABLE(udhcp,
+       AC_HELP_STRING([--enable-udhcp], [enable uDHCP support]),
+                       [enable_udhcp=${enableval}], [enable_udhcp="no"])
+if (test "${enable_udhcp}" != "no"); then
+       if (test -z "${path_udhcpc}"); then
+               AC_PATH_PROG(UDHCPC, [udhcpc], [], $PATH:/sbin:/usr/sbin)
+       else
+               UDHCPC="${path_udhcpc}"
+               AC_SUBST(UDHCPC)
+       fi
+fi
+AM_CONDITIONAL(UDHCP, test "${enable_udhcp}" != "no")
+AM_CONDITIONAL(UDHCP_BUILTIN, test "${enable_udhcp}" = "builtin")
+
+AC_ARG_WITH(dhclient, AC_HELP_STRING([--with-dhclient=PROGRAM],
+       [specify location of dhclient binary]), [path_dhclient=${withval}])
+
+AC_ARG_ENABLE(dhclient,
+       AC_HELP_STRING([--enable-dhclient], [enable dhclient support]),
+                       [enable_dhclient=${enableval}], [enable_dhclient="no"])
+if (test "${enable_dhclient}" != "no"); then
+       if (test -z "${path_dhclient}"); then
+               AC_PATH_PROG(DHCLIENT, [dhclient], [], $PATH:/sbin:/usr/sbin)
+       else
+               DHCLIENT="${path_dhclient}"
+               AC_SUBST(DHCLIENT)
+       fi
+fi
+AM_CONDITIONAL(DHCLIENT, test "${enable_dhclient}" != "no")
+AM_CONDITIONAL(DHCLIENT_BUILTIN, test "${enable_dhclient}" = "builtin")
+
+AC_ARG_WITH(resolvconf, AC_HELP_STRING([--with-resolvconf=PROGRAM],
+       [specify location of resolvconf binary]), [path_resolvconf=${withval}])
+
+AC_ARG_ENABLE(resolvconf,
+       AC_HELP_STRING([--enable-resolvconf], [enable resolvconf support]),
+                       [enable_resolvconf=${enableval}], [enable_resolvconf="no"])
+if (test "${enable_resolvconf}" != "no"); then
+       if (test -z "${path_resolvconf}"); then
+               AC_PATH_PROG(RESOLVCONF, [resolvconf], [], $PATH:/sbin:/usr/sbin)
+       else
+               RESOLVCONF="${path_resolvconf}"
+               AC_SUBST(RESOLVCONF)
+       fi
+fi
+AM_CONDITIONAL(RESOLVCONF, test "${enable_resolvconf}" != "no")
+AM_CONDITIONAL(RESOLVCONF_BUILTIN, test "${enable_resolvconf}" = "builtin")
+
+AC_ARG_ENABLE(dnsproxy,
+       AC_HELP_STRING([--enable-dnsproxy], [enable DNS proxy support]),
+                       [enable_dnsproxy=${enableval}], [enable_dnsproxy="no"])
+AM_CONDITIONAL(DNSPROXY, test "${enable_dnsproxy}" != "no")
+AM_CONDITIONAL(DNSPROXY_BUILTIN, test "${enable_dnsproxy}" = "builtin")
+
+AC_ARG_ENABLE(novatel, AC_HELP_STRING([--enable-novatel],
+               [enable Novatel support]), [enable_novatel=${enableval}])
+AM_CONDITIONAL(NOVATEL, test "${enable_novatel}" = "yes")
+
+AC_ARG_ENABLE(huawei, AC_HELP_STRING([--enable-huawei],
+               [enable HUAWEI support]), [enable_huawei=${enableval}])
+AM_CONDITIONAL(HUAWEI, test "${enable_huawei}" = "yes")
+
+AC_ARG_ENABLE(hso,
+       AC_HELP_STRING([--enable-hso], [enable HSO support]),
+                       [enable_hso=${enableval}], [enable_hso="no"])
+AM_CONDITIONAL(HSO, test "${enable_hso}" != "no")
+AM_CONDITIONAL(HSO_BUILTIN, test "${enable_hso}" = "builtin")
+
+AC_ARG_WITH(pppd, AC_HELP_STRING([--with-pppd=PROGRAM],
+       [specify location of pppd binary]), [path_pppd=${withval}])
+
+AC_ARG_ENABLE(ppp, AC_HELP_STRING([--enable-ppp],
+               [enable PPP support]), [enable_ppp=${enableval}])
+if (test "${enable_ppp}" = "yes" || test "${enable_novatel}" = "yes" ||
+                                       test "${enable_huawei}" = "yes"); then
+       if (test -z "${path_pppd}"); then
+               AC_PATH_PROG(PPPD, [pppd], [], $PATH:/sbin:/usr/sbin)
+       else
+               PPPD="${path_pppd}"
+               AC_SUBST(PPPD)
+       fi
+       AC_CHECK_HEADERS(pppd/pppd.h, enable_ppp=yes,
+                       AC_MSG_ERROR(PPP development files are required))
+fi
+AM_CONDITIONAL(PPPD, test "${enable_ppp}" = "yes")
+
+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))
+
+AC_CHECK_LIB(dl, dlopen, dummy=yes,
+                       AC_MSG_ERROR(dynamic linking loader is required))
+
+AC_ARG_ENABLE(udev, AC_HELP_STRING([--enable-udev],
+               [enable udev support]), [enable_udev=${enableval}])
+if (test "${enable_udev}" = "yes"); then
+       AC_DEFINE(HAVE_UDEV, 1, [Define if udev support is available])
+       PKG_CHECK_MODULES(UDEV, libudev >= 129, dummy=yes,
+                               AC_MSG_ERROR(udev >= 129 is required))
+       AC_CHECK_LIB(udev, udev_enumerate_add_match_property, dummy=yes,
+               AC_DEFINE(NEED_UDEV_ENUMERATE_ADD_MATCH_PROPERTY, 1,
+                       [Define to 1 if you need the
+                               udev_enumerate_add_match_property() function.]))
+       AC_CHECK_LIB(udev, udev_device_get_parent_with_subsystem_devtype, dummy=yes,
+               AC_DEFINE(NEED_UDEV_DEVICE_GET_PARENT_WITH_SUBSYSTEM_DEVTYPE, 1,
+                       [Define to 1 if you need the
+                               udev_device_get_parent_with_subsystem_devtype()
+                                                                       function.]))
+       UDEV_DATADIR="`$PKG_CONFIG --variable=rulesdir libudev`"
+       if (test -z "${UDEV_DATADIR}"); then
+               UDEV_DATADIR="${sysconfdir}/udev/rules.d"
+       fi
+       AC_SUBST(UDEV_DATADIR)
+fi
+AC_SUBST(UDEV_CFLAGS)
+AC_SUBST(UDEV_LIBS)
+AM_CONDITIONAL(UDEV, test "${enable_udev}" = "yes")
+
+AC_ARG_WITH(iwmxsdk, AC_HELP_STRING([--with-iwmxsdk=PATH],
+                                               [path to Intel WiMAX SDK]),
+                               [pkgconfig_iwmxsdk=${withval}/lib/pkgconfig])
+
+AC_ARG_ENABLE(iwmx, AC_HELP_STRING([--enable-iwmx],
+               [enable Intel WiMAX support]), [enable_iwmx=${enableval}])
+if (test "${enable_iwmx}" = "yes"); then
+       enable_threads="yes"
+       export PKG_CONFIG_PATH="${pkgconfig_iwmxsdk}"
+       PKG_CHECK_MODULES(IWMXSDK, libiWmxSdk-0, dummy=yes,
+                               AC_MSG_ERROR(Intel WiMAX SDK is required))
+       PKG_CONFIG_PATH=""
+       AC_SUBST(IWMXSDK_CFLAGS)
+       AC_SUBST(IWMXSDK_LIBS)
+fi
+AM_CONDITIONAL(IWMX, test "${enable_iwmx}" = "yes")
+
+AC_ARG_ENABLE(iospm, AC_HELP_STRING([--enable-iospm],
+               [enable Intel OSPM support]), [enable_iospm=${enableval}])
+AM_CONDITIONAL(IOSPM, test "${enable_iospm}" = "yes")
+
+PKG_CHECK_MODULES(GLIB, glib-2.0 >= 2.16, dummy=yes,
+                               AC_MSG_ERROR(GLib >= 2.16 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.0, dummy=yes,
+                               AC_MSG_ERROR(D-Bus >= 1.0 is required))
+AC_CHECK_LIB(dbus-1, dbus_watch_get_unix_fd, dummy=yes,
+       AC_DEFINE(NEED_DBUS_WATCH_GET_UNIX_FD, 1,
+               [Define to 1 if you need the dbus_watch_get_unix_fd() function.]))
+AC_SUBST(DBUS_CFLAGS)
+AC_SUBST(DBUS_LIBS)
+DBUS_DATADIR="`$PKG_CONFIG --variable=sysconfdir dbus-1`"
+if (test -z "{DBUS_DATADIR}"); then
+       DBUS_DATADIR="${sysconfdir}/dbus-1/system.d"
+else
+       DBUS_DATADIR="$DBUS_DATADIR/dbus-1/system.d"
+fi
+AC_SUBST(DBUS_DATADIR)
+
+AC_SUBST([GDBUS_CFLAGS], ['$(DBUS_CFLAGS) -I$(top_srcdir)/gdbus'])
+AC_SUBST([GDBUS_LIBS], ['$(top_builddir)/gdbus/libgdbus.la $(DBUS_LIBS)'])
+
+AC_SUBST([GATCHAT_CFLAGS], ['-I$(top_srcdir)/gatchat'])
+AC_SUBST([GATCHAT_LIBS], ['$(top_builddir)/gatchat/libgatchat.la'])
+
+AC_ARG_ENABLE(polkit,
+       AC_HELP_STRING([--enable-polkit], [enable PolicyKit support]),
+                       [enable_polkit=${enableval}], [enable_polkit="no"])
+if (test "${enable_polkit}" != "no"); then
+       PKG_CHECK_MODULES(POLKIT, polkit-dbus >= 0.7, dummy=yes,
+                               AC_MSG_ERROR(PolicyKit >= 0.7 is required))
+       AC_SUBST(POLKIT_CFLAGS)
+       AC_SUBST(POLKIT_LIBS)
+       POLKIT_DATADIR="`$PKG_CONFIG --variable=policydir polkit`"
+       if (test -z "${POLKIT_DATADIR}"); then
+               POLKIT_DATADIR="${datadir}/PolicyKit/policy"
+       fi
+       AC_SUBST(POLKIT_DATADIR)
+fi
+AM_CONDITIONAL(POLKIT, test "${enable_polkit}" != "no")
+AM_CONDITIONAL(POLKIT_BUILTIN, test "${enable_polkit}" = "builtin")
+
+AC_ARG_ENABLE(client, AC_HELP_STRING([--enable-client],
+               [enable command line client]), [enable_client=${enableval}])
+AM_CONDITIONAL(CLIENT, test "${enable_client}" = "yes")
+
+AC_ARG_ENABLE(tools, AC_HELP_STRING([--enable-tools],
+               [enable testing tools]), [enable_tools=${enableval}])
+if (test "${enable_tools}" = "yes"); then
+       PKG_CHECK_MODULES(NETLINK, libnl-1, dummy=yes,
+                               AC_MSG_ERROR(Netlink library is required))
+       AC_SUBST(NETLINK_CFLAGS)
+       AC_SUBST(NETLINK_LIBS)
+fi
+AM_CONDITIONAL(TOOLS, test "${enable_tools}" = "yes")
+
+AC_ARG_ENABLE(fake, AC_HELP_STRING([--enable-fake],
+               [enable fake device support]), [enable_fake=${enableval}])
+AM_CONDITIONAL(FAKE, test "${enable_fake}" = "yes")
+
+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")
+
+SHAVE_INIT
+
+AC_OUTPUT(Makefile gdbus/Makefile gatchat/Makefile
+                               include/Makefile include/version.h
+                               src/Makefile src/connman.service
+                               scripts/connman scripts/Makefile
+                               plugins/Makefile client/Makefile
+                               tools/Makefile test/Makefile
+                               doc/Makefile doc/version.xml connman.pc)
diff --git a/connman.pc.in b/connman.pc.in
new file mode 100644 (file)
index 0000000..2af3c2b
--- /dev/null
@@ -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/debian/changelog b/debian/changelog
new file mode 100644 (file)
index 0000000..2c62f42
--- /dev/null
@@ -0,0 +1,5 @@
+connman (0.19) unstable; urgency=low
+
+  * Initial maemo support.
+
+ -- Jukka Rissanen <jukka.rissanen@nokia.com>  Wed, 24 Jun 2009 20:55:50 +0300
diff --git a/debian/compat b/debian/compat
new file mode 100644 (file)
index 0000000..b8626c4
--- /dev/null
@@ -0,0 +1 @@
+4
diff --git a/debian/connman-dev.install b/debian/connman-dev.install
new file mode 100644 (file)
index 0000000..65bdff8
--- /dev/null
@@ -0,0 +1,2 @@
+usr/include/connman
+usr/lib/pkgconfig
diff --git a/debian/connman-doc.install b/debian/connman-doc.install
new file mode 100644 (file)
index 0000000..89d3ec9
--- /dev/null
@@ -0,0 +1 @@
+usr/share/gtk-doc/html/connman
diff --git a/debian/connman-test.install b/debian/connman-test.install
new file mode 100644 (file)
index 0000000..afe4012
--- /dev/null
@@ -0,0 +1,2 @@
+usr/bin/*-*
+
diff --git a/debian/connman.default b/debian/connman.default
new file mode 100644 (file)
index 0000000..df0e3f3
--- /dev/null
@@ -0,0 +1,2 @@
+# Set connman options here
+DAEMON_OPTS="-i wlan*"
diff --git a/debian/connman.init b/debian/connman.init
new file mode 100644 (file)
index 0000000..b7de060
--- /dev/null
@@ -0,0 +1,62 @@
+#!/bin/sh
+
+DAEMON=/usr/sbin/connmand
+PIDFILE=/var/run/connmand.pid
+DESC="Connection Manager"
+NAME=connman
+
+. /lib/lsb/init-functions
+
+if [ -f /etc/default/connman ] ; then
+       . /etc/default/connman
+fi
+
+set -e
+
+unset USE_UPSTART
+INITCTL=/sbin/initctl
+test -x /sbin/initctl && USE_UPSTART=1
+
+do_start() {
+       if [ -z "$USE_UPSTART" ]; then
+           start-stop-daemon --start --oknodo \
+               --pidfile $PIDFILE --exec $DAEMON -- $DAEMON_OPTS
+       else
+           $INITCTL start $NAME
+       fi
+}
+
+do_stop() {
+       if [ -z "$USE_UPSTART" ]; then
+           start-stop-daemon --stop --oknodo --quiet \
+               --pidfile $PIDFILE --exec $DAEMON
+       else
+           $INITCTL stop $NAME
+       fi
+}
+
+case "$1" in
+  start)
+       log_daemon_msg "Starting $DESC"
+       do_start
+       log_end_msg $?
+       ;;
+  stop)
+       log_daemon_msg "Stopping $DESC"
+       do_stop
+       log_end_msg $?
+       ;;
+  restart|force-reload)
+       log_daemon_msg "Restarting $DESC"
+       do_stop
+       sleep 1
+       do_start
+       log_end_msg $?
+       ;;
+  *)
+       log_success_msg "Usage: $0 {start|stop|restart|force-reload}" >&2
+       exit 1
+       ;;
+esac
+
+exit 0
diff --git a/debian/connman.install b/debian/connman.install
new file mode 100644 (file)
index 0000000..40aebec
--- /dev/null
@@ -0,0 +1,7 @@
+etc/dbus-1/system.d/connman.conf
+usr/sbin/connmand
+usr/lib/connman/plugins/*.so
+usr/lib/connman/scripts
+usr/share/dbus-1
+var/lib/connman
+var/run/connman
diff --git a/debian/connman.upstart b/debian/connman.upstart
new file mode 100644 (file)
index 0000000..511fe10
--- /dev/null
@@ -0,0 +1,29 @@
+description "starting connman"
+author "Jukka Rissanen"
+
+console none
+
+# We do not start automatically yet
+#start on started xsession
+stop on ACT_DEAD
+stop on stopping dbus
+
+pre-start script
+       /usr/sbin/waitdbus system
+       initctl stop icd2
+       initctl stop wlancond
+       initctl stop dnsmasq
+end script
+
+post-stop script
+       # stop dnsmasq, wlancond and icd2 because
+       # they are in use in this system at the moment
+       initctl start dnsmasq
+       initctl start wlancond
+       initctl start icd2
+       killall wpa_supplicant
+end script
+
+exec /usr/sbin/connmand -n -i wlan*
+
+respawn
diff --git a/debian/control b/debian/control
new file mode 100644 (file)
index 0000000..40e1cce
--- /dev/null
@@ -0,0 +1,43 @@
+Source: connman
+Priority: optional
+Maintainer: Jukka Rissanen <jukka.rissanen@nokia.com>
+Build-Depends: debhelper (>> 3.0.0), autotools-dev, libglib2.0-dev, libdbus-glib-1-dev,
+ upstart-dev (>= 0.3.8-18)
+Standards-Version: 3.7.2
+Section: net
+
+Package: connman
+Section: net
+Provides: osso-ic
+Replaces: osso-ic
+Architecture: any
+Depends: dbus, dbus-1-utils, ${shlibs:Depends}
+Description: Connection manager
+ This package contains the connection manager daemon
+
+Package: connman-dbg
+Section: net
+Architecture: any
+Depends: connman (= ${Source-Version})
+Description: Debug symbols for connman
+ Debug symbols for connman
+
+Package: connman-doc
+Section: net
+Architecture: any
+Description: Documentation for connman
+ This package contains the documentation for the connman
+
+Package: connman-dev
+Section: net
+Architecture: any
+Depends: libglib2.0-dev
+Description: Development files for connman
+ This package contains header and other development files for the connman
+
+Package: connman-test
+Section: net
+Architecture: any
+Depends: python, connman
+Description: Test scripts for connman
+ This package contains test files for the connman
diff --git a/debian/rules b/debian/rules
new file mode 100755 (executable)
index 0000000..73d3976
--- /dev/null
@@ -0,0 +1,147 @@
+#!/usr/bin/make -f
+# Sample debian/rules that uses debhelper.
+# GNU copyright 1997 to 1999 by Joey Hess.
+
+# Uncomment this to turn on verbose mode.
+#export DH_VERBOSE=1
+
+# These are used for cross-compiling and for saving the configure script
+# from having to guess our platform (since we know it already)
+DEB_HOST_GNU_TYPE   ?= $(shell dpkg-architecture -qDEB_HOST_GNU_TYPE)
+DEB_BUILD_GNU_TYPE  ?= $(shell dpkg-architecture -qDEB_BUILD_GNU_TYPE)
+
+# FOR AUTOCONF 2.52 AND NEWER ONLY
+ifeq ($(DEB_BUILD_GNU_TYPE), $(DEB_HOST_GNU_TYPE))
+  confflags += --build $(DEB_HOST_GNU_TYPE)
+else
+  confflags += --build $(DEB_BUILD_GNU_TYPE) --host $(DEB_HOST_GNU_TYPE)
+endif
+
+# nostrip option
+ifeq (,$(findstring nostrip,$(DEB_BUILD_OPTIONS)))
+       INSTALL_PROGRAM += -s
+endif
+
+# use thumb mode if it's enabled
+ifneq (,$(findstring thumb,$(DEB_BUILD_OPTIONS)))
+       CFLAGS += -mthumb
+endif
+
+ifneq (,$(findstring debug,$(DEB_BUILD_OPTIONS)))
+       confflags += --enable-debug
+endif
+
+ifneq (,$(findstring docs,$(DEB_BUILD_OPTIONS)))
+       confflags += --enable-gtk-doc
+endif
+
+ifneq (,$(findstring dmalloc,$(DEB_BUILD_OPTIONS)))
+       CONFIG_DMALLOC=--enable-dmalloc
+       CFLAGS += -DDMALLOC_ENABLE
+endif
+
+# default flags
+CFLAGS += -g -O2
+
+configure: configure.ac debian/changelog
+       ./bootstrap
+
+config.status: configure
+       dh_testdir
+ifneq (,$(findstring docs,$(DEB_BUILD_OPTIONS)))
+       if [ ! -f doc/gtk-doc.make ]; then gtkdocize --copy --docdir doc; fi
+endif
+       # Add here commands to configure the package.
+        CFLAGS="$(CFLAGS)" ./configure $(confflags) \
+               --prefix=/usr \
+               --mandir=\$${prefix}/share/man \
+               --localstatedir=/var \
+               --sysconfdir=/etc \
+               --disable-fake \
+               --enable-loopback \
+               --enable-wifi \
+               --enable-bluetooth \
+               --enable-udhcp \
+               --with-udhcpc=/sbin/udhcpc \
+               --enable-dhclient \
+               --enable-resolvconf \
+               --enable-dnsproxy \
+               $(CONFIG_DMALLOC) $*
+#              --enable-client
+#              --enable-ethernet
+#              --enable-ppp 
+#              --enable-udev
+
+build: build-stamp
+
+build-stamp:  config.status
+       dh_testdir
+       # Add here commands to compile the package.
+       $(MAKE)
+       touch build-stamp
+
+clean:
+       dh_testdir
+       dh_testroot
+       rm -f build-stamp 
+       # Add here commands to clean up after the build process.
+       -if [ -f Makefile ] ; then $(MAKE) distclean ; fi
+       -test -r /usr/share/misc/config.sub && \
+         cp -f /usr/share/misc/config.sub config.sub
+       -test -r /usr/share/misc/config.guess && \
+         cp -f /usr/share/misc/config.guess config.guess
+       dh_clean
+
+install: build
+       dh_testdir
+       dh_testroot
+       dh_clean -k
+       dh_installdirs
+       # Add here commands to install the package into debian/tmp
+       $(MAKE) install DESTDIR=$(CURDIR)/debian/tmp
+       # Create document dir although it is only used if docs are built
+       -mkdir -p debian/tmp/usr/share/gtk-doc/html/connman
+       # Services file is copied separately
+       # the system-services dir is in use in Maemo Fremantle release
+       -mkdir -p debian/tmp/usr/share/dbus-1/system-services
+       -cp src/connman.service debian/tmp/usr/share/dbus-1/system-services/org.moblin.connman.service
+
+       # the services dir is in use in Maemo Diablo release
+       #-mkdir -p debian/tmp/usr/share/dbus-1/services
+       #-cp src/connman.service debian/tmp/usr/share/dbus-1/services/
+
+       # test scripts to usr/bin
+       -mkdir -p debian/tmp/usr/bin
+       -cp -a test/*-* debian/tmp/usr/bin/
+
+       # connman temp dirs
+       -mkdir -p debian/tmp/var/lib/connman
+       -mkdir -p debian/tmp/var/run/connman
+
+# Build architecture-independent files here.
+binary-indep: build install
+# We have nothing to do by default.
+
+# Build architecture-dependent files here.
+binary-arch: build install
+       dh_testdir
+       dh_testroot
+       dh_installdocs
+       dh_installchangelogs
+       dh_install --sourcedir=debian/tmp --list-missing -v
+       dh_installinit --  start 59 2 3 4 . stop 15 0 1 5 6 .
+       dh_installupstart
+       dh_link
+       dh_strip --dbg-package=connman
+       dh_compress
+       dh_fixperms
+       dh_makeshlibs
+       dh_installdeb
+       dh_shlibdeps
+       dh_gencontrol
+       dh_md5sums
+       dh_builddeb
+
+binary: binary-indep binary-arch
+
+.PHONY: build clean binary-indep binary-arch binary install 
diff --git a/doc/Makefile.am b/doc/Makefile.am
new file mode 100644 (file)
index 0000000..9828237
--- /dev/null
@@ -0,0 +1,42 @@
+
+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
+
+EXTRA_DIST += overview-api.txt behavior-api.txt manager-api.txt \
+               device-api.txt network-api.txt service-api.txt \
+               connection-api.txt profile-api.txt agent-api.txt plugin-api.txt
diff --git a/doc/agent-api.txt b/doc/agent-api.txt
new file mode 100644 (file)
index 0000000..f39c966
--- /dev/null
@@ -0,0 +1,14 @@
+Agent hierarchy
+===============
+
+Service                unique name
+Interface      org.moblin.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.
diff --git a/doc/behavior-api.txt b/doc/behavior-api.txt
new file mode 100644 (file)
index 0000000..5feea76
--- /dev/null
@@ -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/connection-api.txt b/doc/connection-api.txt
new file mode 100644 (file)
index 0000000..8e2e8bc
--- /dev/null
@@ -0,0 +1,62 @@
+Connection hierarchy
+====================
+
+Service                org.moblin.connman
+Interface      org.moblin.connman.Connection
+Object path    [variable prefix]/{connection0,connection1,...}
+
+Methods                dict GetProperties()
+
+                       Returns properties for the connection object. See
+                       the properties section for available properties.
+
+                       Possible Errors: [service].Error.InvalidArguments
+
+Signals                PropertyChanged(string name, variant value)
+
+                       This signal indicates a changed value of the given
+                       property.
+
+Properties     string Type [readonly]
+
+                       The connection type (for example wifi etc.)
+
+               string Interface [readonly]
+
+                       The connection interface (for example "eth0" etc.)
+
+                       This value is for pure informational purposes. It
+                       is not guaranteed that it is always present.
+
+               uint8 Strength [readonly]
+
+                       Indicates the signal strength of the connection.
+
+                       This property is optional and not always present.
+
+               boolean Default [readonly]
+
+                       Indicates if it is a default connection. It is
+                       possible to have multiple default connections.
+
+               object Device [readonly]
+
+                       The object path of the device this connection has
+                       been established with.
+
+               object Network [readonly]
+
+                       The object path of the network this connection
+                       belongs to.
+
+                       This property is optional and not always present.
+
+               string IPv4.Method [readonly]
+
+                       Indicates the way how the IPv4 settings were
+                       configured. Possible values here are "dhcp"
+                       and "static".
+
+               string IPv4.Address [readonly]
+
+                       Shows the current configured IPv4 address.
diff --git a/doc/connman-docs.xml b/doc/connman-docs.xml
new file mode 100644 (file)
index 0000000..d4059a4
--- /dev/null
@@ -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 (file)
index 0000000..4672c2c
--- /dev/null
@@ -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/device-api.txt b/doc/device-api.txt
new file mode 100644 (file)
index 0000000..55625cc
--- /dev/null
@@ -0,0 +1,151 @@
+Device hierarchy
+================
+
+Service                org.moblin.connman
+Interface      org.moblin.connman.Device
+Object path    [variable prefix]/{device0,device1,...}
+
+Methods                dict GetProperties()
+
+                       Returns properties for the device object. 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.DoesNotExist
+
+               void JoinNetwork(dict network)
+
+                       Join network specified by the given properties. The
+                       properties for WiFi networks can be WiFi.SSID,
+                       WiFi.Security and WiFi.Passphrase.
+
+                       Possible Errors: [service].Error.InvalidArguments
+
+               object CreateNetwork(dict network)
+
+                       Creates a network object from the specified
+                       properties. Valid properties are WiFi.SSID,
+                       WiFi.Security and WiFi.Passphrase. Check the
+                       network interface description for details.
+
+                       Possible Errors: [service].Error.InvalidArguments
+
+               void RemoveNetwork(object network)
+
+                       Removes a previously created network object.
+
+                       Possible Errors: [service].Error.InvalidArguments
+                                        [service].Error.DoesNotExist
+
+               void ProposeScan()
+
+                       Proposes to trigger a scan transaction.
+
+                       Possible Errors: [service].Error.InvalidArguments
+
+Signals                PropertyChanged(string name, variant value)
+
+                       This signal indicates a changed value of the given
+                       property.
+
+Properties     string Address [readonly]
+
+                       The address of the device.
+
+               string Name [readonly]
+
+                       The device name (for example "Wireless" etc.)
+
+                       This name can be used for directly displaying it in
+                       the application. It has pure informational purpose.
+
+               string Type [readonly]
+
+                       The device type (for example "ethernet", "wifi" etc.)
+
+               string Interface [readonly]
+
+                       The device interface (for example "eth0" etc.)
+
+                       This value is for pure informational purposes. It
+                       is not guaranteed that it is always present.
+
+               string Policy [readwrite]
+
+                       Setting of the device power and connection policy.
+                       Possible values are "ignore", "off", "auto"
+                       and "manual".
+
+                       The policy defines on how the device is initialized
+                       when brought up and how it connects. The actual
+                       device power state can be changed independently to
+                       this value.
+
+                       If a device is switched off and the policy is changed
+                       to "auto" or "manual", the device will be switched
+                       on. For a current active device changing the policy
+                       to "off" results in powering down the device.
+
+                       The "ignore" policy can be set for devices that are
+                       detected, but managed by a different entity on the
+                       system. For example for complex network setups.
+
+                       Devices that can connect to various networks, the
+                       difference between "auto" or "manual" defines if
+                       known networks are connected automatically or not.
+                       For simple devices like Ethernet cards, setting
+                       the "manual" policy might fail.
+
+               uint8 Priority [readwrite]
+
+                       The device priority. Higher values indicate the
+                       preference for this device.
+
+               boolean Powered [readwrite]
+
+                       Switch a device on or off. This will also modify
+                       the list of networks in range. All known networks
+                       will be still available via the Networks property.
+
+                       Changing this value doesn't change the value of the
+                       Policy property.
+
+                       The value of this property can be changed by other
+                       parts of the system (including the kernel). An
+                       example would be modifications via the "ifconfig"
+                       command line utility.
+
+               uint16 ScanInterval [readwrite]
+
+                       The scan interval describes the time in seconds
+                       between automated scan attempts. Setting this
+                       value to 0 will disable the background scanning.
+
+                       The default value is 300 and so every 5 minutes
+                       a scan procedure will be triggered.
+
+                       This property is not available with all types
+                       of devices. Some might not support background
+                       scanning at all.
+
+               boolean Scanning [readonly]
+
+                       Indicates if a device is scanning. Not all device
+                       types might support this. Also some hardware might
+                       execute background scanning without notifying the
+                       driver about it. Use this property only for visual
+                       indication.
+
+               array{object} Networks [readonly]
+
+                       List of networks objects paths. Every object path
+                       represents a network in range or a known network.
diff --git a/doc/gtk-doc.make b/doc/gtk-doc.make
new file mode 100644 (file)
index 0000000..354ffb7
--- /dev/null
@@ -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/manager-api.txt b/doc/manager-api.txt
new file mode 100644 (file)
index 0000000..7f1924e
--- /dev/null
@@ -0,0 +1,139 @@
+Manager hierarchy
+=================
+
+Service                org.moblin.connman
+Interface      org.moblin.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.DoesNotExist
+
+               object AddProfile(string name)
+
+                       Add a new profile with the specified name.
+
+                       It is possible to create two profiles with the same
+                       name. The identification is done via the object path
+                       and not the name of the profile.
+
+                       Possible Errors: [service].Error.InvalidArguments
+
+               void RemoveProfile(object path)
+
+                       Remove profile with specified object path.
+
+                       It is not possible to remove the current active
+                       profile. To remove the active profile a different
+                       one must be selected via ActiveProfile property
+                       first.
+
+                       At minimum one profile must be available all the time.
+
+                       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
+
+Signals                PropertyChanged(string name, variant value)
+
+                       This signal indicates a changed value of the given
+                       property.
+
+               StateChanged(string state)
+
+                       This signal is similar to the PropertyChanged signal
+                       for the State property.
+
+                       It exists for application state only care about the
+                       current state and so can avoid to be woken up when
+                       other details changes.
+
+Properties     string State [readonly]
+
+                       The global connection state of a system. Possible
+                       values are "online" if at least one connection exists
+                       and "offline" if no device is connected.
+
+                       In certain situations the state might change to
+                       the value "connected". This can only be seen if
+                       previously no connection was present.
+
+               string Policy [readwrite]
+
+                       The global connection policy of a system. This
+                       allows to configure how connections are established
+                       and also when they are taken down again.
+
+                       Possible values are "single", "multiple" and "ask".
+
+                       For the single policy, the priority setting of the
+                       device defines which becomes the default connection
+                       when multiple are available.
+
+               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.
+
+               object ActiveProfile [readwrite]
+
+                       Object path of the current active profile.
+
+               array{object} Profiles [readonly]
+
+                       List of profile object paths.
+
+               array{object} Devices [readonly]
+
+                       List of device object paths.
+
+               array{object} Services [readonly]
+
+                       List of service object paths. The list is sorted
+                       internally to have the service with the default
+                       route always first and then the favorite services
+                       followed by scan results.
+
+                       This list represents the available services for the
+                       current selected profile. If the profile gets changed
+                       then this list will be updated.
+
+                       The same list is available via the profile object
+                       itself. It is just provided here for convenience of
+                       applications only dealing with the current active
+                       profile.
+
+               array{object} Connections [readonly]
+
+                       List of active connection object paths.
diff --git a/doc/network-api.txt b/doc/network-api.txt
new file mode 100644 (file)
index 0000000..1bb0f22
--- /dev/null
@@ -0,0 +1,92 @@
+Network hierarchy
+=================
+
+Service                org.moblin.connman
+Interface      org.moblin.connman.Network
+Object path    [variable prefix]/{network0,network1,...}
+
+Methods                dict GetProperties()
+
+                       Returns properties for the network object. 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.DoesNotExist
+
+Signals                PropertyChanged(string name, variant value)
+
+                       This signal indicates a changed value of the given
+                       property.
+
+Properties     string Address [readonly]
+
+                       The address of the network.
+
+               string Name [readonly]
+
+                       The pretty/long version of the network name. For
+                       example in case of WiFi this should be the UTF-8
+                       valid version of the SSID.
+
+                       This property might not be available for every
+                       network. For example hidden WiFi networks will
+                       not include it.
+
+               boolean Connected [readonly]
+
+                       Indicates that this network is currently connected.
+
+               uint8 Strength [readonly]
+
+                       Indicates the signal strength of the network. This
+                       is a normalized value between 0 and 100.
+
+               object Device [readonly]
+
+                       The object path of the device this networks
+                       belongs to.
+
+               array{byte} WiFi.SSID [readonly]
+
+                       If the network type is WiFi, then this property is
+                       present and contains the binary SSID value.
+
+               string WiFi.Mode [readonly, readwrite]
+
+                       If the network type is WiFi, then this property is
+                       present and contains the mode of the network. The
+                       possible values are "managed" or "adhoc".
+
+                       For scanned networks this value is read only, but in
+                       case the network was manually created it is also
+                       changeable.
+
+               string WiFi.Security [readonly, readwrite]
+
+                       If the network type is WiFi, then this property is
+                       present and contains the security method or key
+                       management setting.
+
+                       For scanned networks this value is read only, but in
+                       case the network was manually created it is also
+                       changeable.
+
+                       Possible values are "none", "wep", "wpa" and "rsn".
+
+               string WiFi.Passphrase [readwrite]
+
+                       If the network type is WiFi and a passhrase is
+                       requires, then this property is present and contains
+                       the passphrase in clear text.
+
+                       For systems using PolicyKit, the access to this value
+                       will be protected by the security policy.
diff --git a/doc/overview-api.txt b/doc/overview-api.txt
new file mode 100644 (file)
index 0000000..cfc2771
--- /dev/null
@@ -0,0 +1,418 @@
+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, a WiMAX service provider
+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)   |
+       +---------------------------------------+
+       | Clear WiMAX      (strength 70)        |
+       +---------------------------------------+
+       | Other AP         (strength 70, rsn)   |
+       +---------------------------------------+
+       | Friends AP       (strength 70, wep)   |
+       +---------------------------------------+
+       | Other WiMAX      (strength 50)        |
+       +---------------------------------------+
+
+If non 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/WiMAX   (signal strength first, then prefer WiMAX over WiFi,
+                        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.
+
+The WiFi and WiMAX networks are treated equally since both provide signal
+strength information and networks closer in the proximity should be shown
+before others since it is more likely they are selected first. The signal
+strength value is normalized to 0-100 (effectively a percentage) and allows
+an easy sorting.
+
+If the signal strength is identical than the WiMAX network should be shown
+first since it is a licensed spectrum and more reliable. Also the number
+of WiMAX networks will be smaller than the number of WiFi since that operates
+in an unlicensed spectrum.
+
+WiFi networks with the same signal strength are then sorted by its 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 or WiMAX network. 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, WiMAX 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
+other 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 service 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 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. This includes Ethernet devices with no cable plugged into
+them. This is of course suboptimal since the user doesn't need to be bothered
+with a device that he/she can actually physically see.
+
+So in this case the user interface can choose to just not show Ethernet
+devices with a favorite=no setting. This is an advanced tweak that is up
+to the user interface. However it is highly recommended to not show Ethernet
+device until Connection Manager marks them as favorite.
+
+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 WiMAX networks the provide name like Clear or X-OHM will be used. This
+name either comes directly from the networks itself or from a provisioning
+database of the WiMAX service.
+
+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.
+
+With Ethernet services a special "carrier" state is available. It indicates
+that the cable has been plugged in. This state is only used when the device
+is from type Ethernet.
+
+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.
+
+Some service might require special authentication procedure like a web based
+confirmation. The "login" should be used for this in the future. Currently
+this is not implemented.
+
+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          |<-------------------------------+
+               +---------------+                                |
+                     |    |                                     |
+                     |    +----------------------+              |
+                     |                           |              |
+                     |                           V              |
+                     |                      +-------------+     |
+                     +----------------------| carrier     |<----+
+                     |                      +-------------+     |
+                     |                                          |
+                     |                      +-------------+     |
+                     +----------------------| failure     |     |
+                     | service.Connect()    +-------------+     |
+                     V                           A              |
+               +---------------+                 |              |
+               | association   |-----------------+              |
+               +---------------+      error      |              |
+                     |                           |              |
+                     | success                   |              |
+                     V                           |              |
+               +---------------+                 |              |
+               | configuration |-----------------+              |
+               +---------------+      error                     |
+                     |                                          |
+                     | success                                  |
+                     V                                          |
+               +---------------+                                |
+               | ready         |<----------------+              |
+               +---------------+                 |              |
+                     |                           |              |
+                     | 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.
+
+
+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("org.moblin.connman", "/"),
+                                               "org.moblin.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, WiMAX providers 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. For WiMAX the provider name will
+be used for grouping different base stations and access providers. 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("org.moblin.connman", path),
+                                                       "org.moblin.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 service 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 transaction 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 send via the PropertyChanged signal on the
+service interface. This allows asynchronous monitoring with having to poll
+Connection Manager for changes.
diff --git a/doc/plugin-api.txt b/doc/plugin-api.txt
new file mode 100644 (file)
index 0000000..696e2c1
--- /dev/null
@@ -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 definied 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/profile-api.txt b/doc/profile-api.txt
new file mode 100644 (file)
index 0000000..f807070
--- /dev/null
@@ -0,0 +1,26 @@
+Profile hierarchy
+=================
+
+Service                org.moblin.connman
+Interface      org.moblin.connman.Profile
+Object path    [variable prefix]/{profile0,profile1,...}
+
+Methods                dict GetProperties()
+
+                       Returns properties for the profile object. See
+                       the properties section for available properties.
+
+                       Possible Errors: [service].Error.InvalidArguments
+
+Signals                PropertyChanged(string name, variant value)
+
+                       This signal indicates a changed value of the given
+                       property.
+
+Properties     string Name [readonly]
+
+                       Name of this profile.
+
+               array{object} Services [readonly]
+
+                       List of service objects.
diff --git a/doc/rfc1035.txt b/doc/rfc1035.txt
new file mode 100644 (file)
index 0000000..b1a9bf5
--- /dev/null
@@ -0,0 +1,3077 @@
+Network Working Group                                     P. Mockapetris
+Request for Comments: 1035                                           ISI
+                                                           November 1987
+Obsoletes: RFCs 882, 883, 973
+
+            DOMAIN NAMES - IMPLEMENTATION AND SPECIFICATION
+
+
+1. STATUS OF THIS MEMO
+
+This RFC describes the details of the domain system and protocol, and
+assumes that the reader is familiar with the concepts discussed in a
+companion RFC, "Domain Names - Concepts and Facilities" [RFC-1034].
+
+The domain system is a mixture of functions and data types which are an
+official protocol and functions and data types which are still
+experimental.  Since the domain system is intentionally extensible, new
+data types and experimental behavior should always be expected in parts
+of the system beyond the official protocol.  The official protocol parts
+include standard queries, responses and the Internet class RR data
+formats (e.g., host addresses).  Since the previous RFC set, several
+definitions have changed, so some previous definitions are obsolete.
+
+Experimental or obsolete features are clearly marked in these RFCs, and
+such information should be used with caution.
+
+The reader is especially cautioned not to depend on the values which
+appear in examples to be current or complete, since their purpose is
+primarily pedagogical.  Distribution of this memo is unlimited.
+
+                           Table of Contents
+
+  1. STATUS OF THIS MEMO                                              1
+  2. INTRODUCTION                                                     3
+      2.1. Overview                                                   3
+      2.2. Common configurations                                      4
+      2.3. Conventions                                                7
+          2.3.1. Preferred name syntax                                7
+          2.3.2. Data Transmission Order                              8
+          2.3.3. Character Case                                       9
+          2.3.4. Size limits                                         10
+  3. DOMAIN NAME SPACE AND RR DEFINITIONS                            10
+      3.1. Name space definitions                                    10
+      3.2. RR definitions                                            11
+          3.2.1. Format                                              11
+          3.2.2. TYPE values                                         12
+          3.2.3. QTYPE values                                        12
+          3.2.4. CLASS values                                        13
+
+
+
+Mockapetris                                                     [Page 1]
+\f
+RFC 1035        Domain Implementation and Specification    November 1987
+
+
+          3.2.5. QCLASS values                                       13
+      3.3. Standard RRs                                              13
+          3.3.1. CNAME RDATA format                                  14
+          3.3.2. HINFO RDATA format                                  14
+          3.3.3. MB RDATA format (EXPERIMENTAL)                      14
+          3.3.4. MD RDATA format (Obsolete)                          15
+          3.3.5. MF RDATA format (Obsolete)                          15
+          3.3.6. MG RDATA format (EXPERIMENTAL)                      16
+          3.3.7. MINFO RDATA format (EXPERIMENTAL)                   16
+          3.3.8. MR RDATA format (EXPERIMENTAL)                      17
+          3.3.9. MX RDATA format                                     17
+          3.3.10. NULL RDATA format (EXPERIMENTAL)                   17
+          3.3.11. NS RDATA format                                    18
+          3.3.12. PTR RDATA format                                   18
+          3.3.13. SOA RDATA format                                   19
+          3.3.14. TXT RDATA format                                   20
+      3.4. ARPA Internet specific RRs                                20
+          3.4.1. A RDATA format                                      20
+          3.4.2. WKS RDATA format                                    21
+      3.5. IN-ADDR.ARPA domain                                       22
+      3.6. Defining new types, classes, and special namespaces       24
+  4. MESSAGES                                                        25
+      4.1. Format                                                    25
+          4.1.1. Header section format                               26
+          4.1.2. Question section format                             28
+          4.1.3. Resource record format                              29
+          4.1.4. Message compression                                 30
+      4.2. Transport                                                 32
+          4.2.1. UDP usage                                           32
+          4.2.2. TCP usage                                           32
+  5. MASTER FILES                                                    33
+      5.1. Format                                                    33
+      5.2. Use of master files to define zones                       35
+      5.3. Master file example                                       36
+  6. NAME SERVER IMPLEMENTATION                                      37
+      6.1. Architecture                                              37
+          6.1.1. Control                                             37
+          6.1.2. Database                                            37
+          6.1.3. Time                                                39
+      6.2. Standard query processing                                 39
+      6.3. Zone refresh and reload processing                        39
+      6.4. Inverse queries (Optional)                                40
+          6.4.1. The contents of inverse queries and responses       40
+          6.4.2. Inverse query and response example                  41
+          6.4.3. Inverse query processing                            42
+
+
+
+
+
+
+Mockapetris                                                     [Page 2]
+\f
+RFC 1035        Domain Implementation and Specification    November 1987
+
+
+      6.5. Completion queries and responses                          42
+  7. RESOLVER IMPLEMENTATION                                         43
+      7.1. Transforming a user request into a query                  43
+      7.2. Sending the queries                                       44
+      7.3. Processing responses                                      46
+      7.4. Using the cache                                           47
+  8. MAIL SUPPORT                                                    47
+      8.1. Mail exchange binding                                     48
+      8.2. Mailbox binding (Experimental)                            48
+  9. REFERENCES and BIBLIOGRAPHY                                     50
+  Index                                                              54
+
+2. INTRODUCTION
+
+2.1. Overview
+
+The goal of domain names is to provide a mechanism for naming resources
+in such a way that the names are usable in different hosts, networks,
+protocol families, internets, and administrative organizations.
+
+From the user's point of view, domain names are useful as arguments to a
+local agent, called a resolver, which retrieves information associated
+with the domain name.  Thus a user might ask for the host address or
+mail information associated with a particular domain name.  To enable
+the user to request a particular type of information, an appropriate
+query type is passed to the resolver with the domain name.  To the user,
+the domain tree is a single information space; the resolver is
+responsible for hiding the distribution of data among name servers from
+the user.
+
+From the resolver's point of view, the database that makes up the domain
+space is distributed among various name servers.  Different parts of the
+domain space are stored in different name servers, although a particular
+data item will be stored redundantly in two or more name servers.  The
+resolver starts with knowledge of at least one name server.  When the
+resolver processes a user query it asks a known name server for the
+information; in return, the resolver either receives the desired
+information or a referral to another name server.  Using these
+referrals, resolvers learn the identities and contents of other name
+servers.  Resolvers are responsible for dealing with the distribution of
+the domain space and dealing with the effects of name server failure by
+consulting redundant databases in other servers.
+
+Name servers manage two kinds of data.  The first kind of data held in
+sets called zones; each zone is the complete database for a particular
+"pruned" subtree of the domain space.  This data is called
+authoritative.  A name server periodically checks to make sure that its
+zones are up to date, and if not, obtains a new copy of updated zones
+
+
+
+Mockapetris                                                     [Page 3]
+\f
+RFC 1035        Domain Implementation and Specification    November 1987
+
+
+from master files stored locally or in another name server.  The second
+kind of data is cached data which was acquired by a local resolver.
+This data may be incomplete, but improves the performance of the
+retrieval process when non-local data is repeatedly accessed.  Cached
+data is eventually discarded by a timeout mechanism.
+
+This functional structure isolates the problems of user interface,
+failure recovery, and distribution in the resolvers and isolates the
+database update and refresh problems in the name servers.
+
+2.2. Common configurations
+
+A host can participate in the domain name system in a number of ways,
+depending on whether the host runs programs that retrieve information
+from the domain system, name servers that answer queries from other
+hosts, or various combinations of both functions.  The simplest, and
+perhaps most typical, configuration is shown below:
+
+                 Local Host                        |  Foreign
+                                                   |
+    +---------+               +----------+         |  +--------+
+    |         | user queries  |          |queries  |  |        |
+    |  User   |-------------->|          |---------|->|Foreign |
+    | Program |               | Resolver |         |  |  Name  |
+    |         |<--------------|          |<--------|--| Server |
+    |         | user responses|          |responses|  |        |
+    +---------+               +----------+         |  +--------+
+                                |     A            |
+                cache additions |     | references |
+                                V     |            |
+                              +----------+         |
+                              |  cache   |         |
+                              +----------+         |
+
+User programs interact with the domain name space through resolvers; the
+format of user queries and user responses is specific to the host and
+its operating system.  User queries will typically be operating system
+calls, and the resolver and its cache will be part of the host operating
+system.  Less capable hosts may choose to implement the resolver as a
+subroutine to be linked in with every program that needs its services.
+Resolvers answer user queries with information they acquire via queries
+to foreign name servers and the local cache.
+
+Note that the resolver may have to make several queries to several
+different foreign name servers to answer a particular user query, and
+hence the resolution of a user query may involve several network
+accesses and an arbitrary amount of time.  The queries to foreign name
+servers and the corresponding responses have a standard format described
+
+
+
+Mockapetris                                                     [Page 4]
+\f
+RFC 1035        Domain Implementation and Specification    November 1987
+
+
+in this memo, and may be datagrams.
+
+Depending on its capabilities, a name server could be a stand alone
+program on a dedicated machine or a process or processes on a large
+timeshared host.  A simple configuration might be:
+
+                 Local Host                        |  Foreign
+                                                   |
+      +---------+                                  |
+     /         /|                                  |
+    +---------+ |             +----------+         |  +--------+
+    |         | |             |          |responses|  |        |
+    |         | |             |   Name   |---------|->|Foreign |
+    |  Master |-------------->|  Server  |         |  |Resolver|
+    |  files  | |             |          |<--------|--|        |
+    |         |/              |          | queries |  +--------+
+    +---------+               +----------+         |
+
+Here a primary name server acquires information about one or more zones
+by reading master files from its local file system, and answers queries
+about those zones that arrive from foreign resolvers.
+
+The DNS requires that all zones be redundantly supported by more than
+one name server.  Designated secondary servers can acquire zones and
+check for updates from the primary server using the zone transfer
+protocol of the DNS.  This configuration is shown below:
+
+                 Local Host                        |  Foreign
+                                                   |
+      +---------+                                  |
+     /         /|                                  |
+    +---------+ |             +----------+         |  +--------+
+    |         | |             |          |responses|  |        |
+    |         | |             |   Name   |---------|->|Foreign |
+    |  Master |-------------->|  Server  |         |  |Resolver|
+    |  files  | |             |          |<--------|--|        |
+    |         |/              |          | queries |  +--------+
+    +---------+               +----------+         |
+                                A     |maintenance |  +--------+
+                                |     +------------|->|        |
+                                |      queries     |  |Foreign |
+                                |                  |  |  Name  |
+                                +------------------|--| Server |
+                             maintenance responses |  +--------+
+
+In this configuration, the name server periodically establishes a
+virtual circuit to a foreign name server to acquire a copy of a zone or
+to check that an existing copy has not changed.  The messages sent for
+
+
+
+Mockapetris                                                     [Page 5]
+\f
+RFC 1035        Domain Implementation and Specification    November 1987
+
+
+these maintenance activities follow the same form as queries and
+responses, but the message sequences are somewhat different.
+
+The information flow in a host that supports all aspects of the domain
+name system is shown below:
+
+                 Local Host                        |  Foreign
+                                                   |
+    +---------+               +----------+         |  +--------+
+    |         | user queries  |          |queries  |  |        |
+    |  User   |-------------->|          |---------|->|Foreign |
+    | Program |               | Resolver |         |  |  Name  |
+    |         |<--------------|          |<--------|--| Server |
+    |         | user responses|          |responses|  |        |
+    +---------+               +----------+         |  +--------+
+                                |     A            |
+                cache additions |     | references |
+                                V     |            |
+                              +----------+         |
+                              |  Shared  |         |
+                              | database |         |
+                              +----------+         |
+                                A     |            |
+      +---------+     refreshes |     | references |
+     /         /|               |     V            |
+    +---------+ |             +----------+         |  +--------+
+    |         | |             |          |responses|  |        |
+    |         | |             |   Name   |---------|->|Foreign |
+    |  Master |-------------->|  Server  |         |  |Resolver|
+    |  files  | |             |          |<--------|--|        |
+    |         |/              |          | queries |  +--------+
+    +---------+               +----------+         |
+                                A     |maintenance |  +--------+
+                                |     +------------|->|        |
+                                |      queries     |  |Foreign |
+                                |                  |  |  Name  |
+                                +------------------|--| Server |
+                             maintenance responses |  +--------+
+
+The shared database holds domain space data for the local name server
+and resolver.  The contents of the shared database will typically be a
+mixture of authoritative data maintained by the periodic refresh
+operations of the name server and cached data from previous resolver
+requests.  The structure of the domain data and the necessity for
+synchronization between name servers and resolvers imply the general
+characteristics of this database, but the actual format is up to the
+local implementor.
+
+
+
+
+Mockapetris                                                     [Page 6]
+\f
+RFC 1035        Domain Implementation and Specification    November 1987
+
+
+Information flow can also be tailored so that a group of hosts act
+together to optimize activities.  Sometimes this is done to offload less
+capable hosts so that they do not have to implement a full resolver.
+This can be appropriate for PCs or hosts which want to minimize the
+amount of new network code which is required.  This scheme can also
+allow a group of hosts can share a small number of caches rather than
+maintaining a large number of separate caches, on the premise that the
+centralized caches will have a higher hit ratio.  In either case,
+resolvers are replaced with stub resolvers which act as front ends to
+resolvers located in a recursive server in one or more name servers
+known to perform that service:
+
+                   Local Hosts                     |  Foreign
+                                                   |
+    +---------+                                    |
+    |         | responses                          |
+    | Stub    |<--------------------+              |
+    | Resolver|                     |              |
+    |         |----------------+    |              |
+    +---------+ recursive      |    |              |
+                queries        |    |              |
+                               V    |              |
+    +---------+ recursive     +----------+         |  +--------+
+    |         | queries       |          |queries  |  |        |
+    | Stub    |-------------->| Recursive|---------|->|Foreign |
+    | Resolver|               | Server   |         |  |  Name  |
+    |         |<--------------|          |<--------|--| Server |
+    +---------+ responses     |          |responses|  |        |
+                              +----------+         |  +--------+
+                              |  Central |         |
+                              |   cache  |         |
+                              +----------+         |
+
+In any case, note that domain components are always replicated for
+reliability whenever possible.
+
+2.3. Conventions
+
+The domain system has several conventions dealing with low-level, but
+fundamental, issues.  While the implementor is free to violate these
+conventions WITHIN HIS OWN SYSTEM, he must observe these conventions in
+ALL behavior observed from other hosts.
+
+2.3.1. Preferred name syntax
+
+The DNS specifications attempt to be as general as possible in the rules
+for constructing domain names.  The idea is that the name of any
+existing object can be expressed as a domain name with minimal changes.
+
+
+
+Mockapetris                                                     [Page 7]
+\f
+RFC 1035        Domain Implementation and Specification    November 1987
+
+
+However, when assigning a domain name for an object, the prudent user
+will select a name which satisfies both the rules of the domain system
+and any existing rules for the object, whether these rules are published
+or implied by existing programs.
+
+For example, when naming a mail domain, the user should satisfy both the
+rules of this memo and those in RFC-822.  When creating a new host name,
+the old rules for HOSTS.TXT should be followed.  This avoids problems
+when old software is converted to use domain names.
+
+The following syntax will result in fewer problems with many
+
+applications that use domain names (e.g., mail, TELNET).
+
+<domain> ::= <subdomain> | " "
+
+<subdomain> ::= <label> | <subdomain> "." <label>
+
+<label> ::= <letter> [ [ <ldh-str> ] <let-dig> ]
+
+<ldh-str> ::= <let-dig-hyp> | <let-dig-hyp> <ldh-str>
+
+<let-dig-hyp> ::= <let-dig> | "-"
+
+<let-dig> ::= <letter> | <digit>
+
+<letter> ::= any one of the 52 alphabetic characters A through Z in
+upper case and a through z in lower case
+
+<digit> ::= any one of the ten digits 0 through 9
+
+Note that while upper and lower case letters are allowed in domain
+names, no significance is attached to the case.  That is, two names with
+the same spelling but different case are to be treated as if identical.
+
+The labels must follow the rules for ARPANET host names.  They must
+start with a letter, end with a letter or digit, and have as interior
+characters only letters, digits, and hyphen.  There are also some
+restrictions on the length.  Labels must be 63 characters or less.
+
+For example, the following strings identify hosts in the Internet:
+
+A.ISI.EDU XX.LCS.MIT.EDU SRI-NIC.ARPA
+
+2.3.2. Data Transmission Order
+
+The order of transmission of the header and data described in this
+document is resolved to the octet level.  Whenever a diagram shows a
+
+
+
+Mockapetris                                                     [Page 8]
+\f
+RFC 1035        Domain Implementation and Specification    November 1987
+
+
+group of octets, the order of transmission of those octets is the normal
+order in which they are read in English.  For example, in the following
+diagram, the octets are transmitted in the order they are numbered.
+
+     0                   1
+     0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5
+    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+    |       1       |       2       |
+    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+    |       3       |       4       |
+    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+    |       5       |       6       |
+    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+
+Whenever an octet represents a numeric quantity, the left most bit in
+the diagram is the high order or most significant bit.  That is, the bit
+labeled 0 is the most significant bit.  For example, the following
+diagram represents the value 170 (decimal).
+
+     0 1 2 3 4 5 6 7
+    +-+-+-+-+-+-+-+-+
+    |1 0 1 0 1 0 1 0|
+    +-+-+-+-+-+-+-+-+
+
+Similarly, whenever a multi-octet field represents a numeric quantity
+the left most bit of the whole field is the most significant bit.  When
+a multi-octet quantity is transmitted the most significant octet is
+transmitted first.
+
+2.3.3. Character Case
+
+For all parts of the DNS that are part of the official protocol, all
+comparisons between character strings (e.g., labels, domain names, etc.)
+are done in a case-insensitive manner.  At present, this rule is in
+force throughout the domain system without exception.  However, future
+additions beyond current usage may need to use the full binary octet
+capabilities in names, so attempts to store domain names in 7-bit ASCII
+or use of special bytes to terminate labels, etc., should be avoided.
+
+When data enters the domain system, its original case should be
+preserved whenever possible.  In certain circumstances this cannot be
+done.  For example, if two RRs are stored in a database, one at x.y and
+one at X.Y, they are actually stored at the same place in the database,
+and hence only one casing would be preserved.  The basic rule is that
+case can be discarded only when data is used to define structure in a
+database, and two names are identical when compared in a case
+insensitive manner.
+
+
+
+
+Mockapetris                                                     [Page 9]
+\f
+RFC 1035        Domain Implementation and Specification    November 1987
+
+
+Loss of case sensitive data must be minimized.  Thus while data for x.y
+and X.Y may both be stored under a single location x.y or X.Y, data for
+a.x and B.X would never be stored under A.x, A.X, b.x, or b.X.  In
+general, this preserves the case of the first label of a domain name,
+but forces standardization of interior node labels.
+
+Systems administrators who enter data into the domain database should
+take care to represent the data they supply to the domain system in a
+case-consistent manner if their system is case-sensitive.  The data
+distribution system in the domain system will ensure that consistent
+representations are preserved.
+
+2.3.4. Size limits
+
+Various objects and parameters in the DNS have size limits.  They are
+listed below.  Some could be easily changed, others are more
+fundamental.
+
+labels          63 octets or less
+
+names           255 octets or less
+
+TTL             positive values of a signed 32 bit number.
+
+UDP messages    512 octets or less
+
+3. DOMAIN NAME SPACE AND RR DEFINITIONS
+
+3.1. Name space definitions
+
+Domain names in messages are expressed in terms of a sequence of labels.
+Each label is represented as a one octet length field followed by that
+number of octets.  Since every domain name ends with the null label of
+the root, a domain name is terminated by a length byte of zero.  The
+high order two bits of every length octet must be zero, and the
+remaining six bits of the length field limit the label to 63 octets or
+less.
+
+To simplify implementations, the total length of a domain name (i.e.,
+label octets and label length octets) is restricted to 255 octets or
+less.
+
+Although labels can contain any 8 bit values in octets that make up a
+label, it is strongly recommended that labels follow the preferred
+syntax described elsewhere in this memo, which is compatible with
+existing host naming conventions.  Name servers and resolvers must
+compare labels in a case-insensitive manner (i.e., A=a), assuming ASCII
+with zero parity.  Non-alphabetic codes must match exactly.
+
+
+
+Mockapetris                                                    [Page 10]
+\f
+RFC 1035        Domain Implementation and Specification    November 1987
+
+
+3.2. RR definitions
+
+3.2.1. Format
+
+All RRs have the same top level format shown below:
+
+                                    1  1  1  1  1  1
+      0  1  2  3  4  5  6  7  8  9  0  1  2  3  4  5
+    +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+    |                                               |
+    /                                               /
+    /                      NAME                     /
+    |                                               |
+    +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+    |                      TYPE                     |
+    +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+    |                     CLASS                     |
+    +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+    |                      TTL                      |
+    |                                               |
+    +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+    |                   RDLENGTH                    |
+    +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--|
+    /                     RDATA                     /
+    /                                               /
+    +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+
+
+where:
+
+NAME            an owner name, i.e., the name of the node to which this
+                resource record pertains.
+
+TYPE            two octets containing one of the RR TYPE codes.
+
+CLASS           two octets containing one of the RR CLASS codes.
+
+TTL             a 32 bit signed integer that specifies the time interval
+                that the resource record may be cached before the source
+                of the information should again be consulted.  Zero
+                values are interpreted to mean that the RR can only be
+                used for the transaction in progress, and should not be
+                cached.  For example, SOA records are always distributed
+                with a zero TTL to prohibit caching.  Zero values can
+                also be used for extremely volatile data.
+
+RDLENGTH        an unsigned 16 bit integer that specifies the length in
+                octets of the RDATA field.
+
+
+
+Mockapetris                                                    [Page 11]
+\f
+RFC 1035        Domain Implementation and Specification    November 1987
+
+
+RDATA           a variable length string of octets that describes the
+                resource.  The format of this information varies
+                according to the TYPE and CLASS of the resource record.
+
+3.2.2. TYPE values
+
+TYPE fields are used in resource records.  Note that these types are a
+subset of QTYPEs.
+
+TYPE            value and meaning
+
+A               1 a host address
+
+NS              2 an authoritative name server
+
+MD              3 a mail destination (Obsolete - use MX)
+
+MF              4 a mail forwarder (Obsolete - use MX)
+
+CNAME           5 the canonical name for an alias
+
+SOA             6 marks the start of a zone of authority
+
+MB              7 a mailbox domain name (EXPERIMENTAL)
+
+MG              8 a mail group member (EXPERIMENTAL)
+
+MR              9 a mail rename domain name (EXPERIMENTAL)
+
+NULL            10 a null RR (EXPERIMENTAL)
+
+WKS             11 a well known service description
+
+PTR             12 a domain name pointer
+
+HINFO           13 host information
+
+MINFO           14 mailbox or mail list information
+
+MX              15 mail exchange
+
+TXT             16 text strings
+
+3.2.3. QTYPE values
+
+QTYPE fields appear in the question part of a query.  QTYPES are a
+superset of TYPEs, hence all TYPEs are valid QTYPEs.  In addition, the
+following QTYPEs are defined:
+
+
+
+Mockapetris                                                    [Page 12]
+\f
+RFC 1035        Domain Implementation and Specification    November 1987
+
+
+AXFR            252 A request for a transfer of an entire zone
+
+MAILB           253 A request for mailbox-related records (MB, MG or MR)
+
+MAILA           254 A request for mail agent RRs (Obsolete - see MX)
+
+*               255 A request for all records
+
+3.2.4. CLASS values
+
+CLASS fields appear in resource records.  The following CLASS mnemonics
+and values are defined:
+
+IN              1 the Internet
+
+CS              2 the CSNET class (Obsolete - used only for examples in
+                some obsolete RFCs)
+
+CH              3 the CHAOS class
+
+HS              4 Hesiod [Dyer 87]
+
+3.2.5. QCLASS values
+
+QCLASS fields appear in the question section of a query.  QCLASS values
+are a superset of CLASS values; every CLASS is a valid QCLASS.  In
+addition to CLASS values, the following QCLASSes are defined:
+
+*               255 any class
+
+3.3. Standard RRs
+
+The following RR definitions are expected to occur, at least
+potentially, in all classes.  In particular, NS, SOA, CNAME, and PTR
+will be used in all classes, and have the same format in all classes.
+Because their RDATA format is known, all domain names in the RDATA
+section of these RRs may be compressed.
+
+<domain-name> is a domain name represented as a series of labels, and
+terminated by a label with zero length.  <character-string> is a single
+length octet followed by that number of characters.  <character-string>
+is treated as binary information, and can be up to 256 characters in
+length (including the length octet).
+
+
+
+
+
+
+
+
+Mockapetris                                                    [Page 13]
+\f
+RFC 1035        Domain Implementation and Specification    November 1987
+
+
+3.3.1. CNAME RDATA format
+
+    +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+    /                     CNAME                     /
+    /                                               /
+    +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+
+where:
+
+CNAME           A <domain-name> which specifies the canonical or primary
+                name for the owner.  The owner name is an alias.
+
+CNAME RRs cause no additional section processing, but name servers may
+choose to restart the query at the canonical name in certain cases.  See
+the description of name server logic in [RFC-1034] for details.
+
+3.3.2. HINFO RDATA format
+
+    +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+    /                      CPU                      /
+    +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+    /                       OS                      /
+    +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+
+where:
+
+CPU             A <character-string> which specifies the CPU type.
+
+OS              A <character-string> which specifies the operating
+                system type.
+
+Standard values for CPU and OS can be found in [RFC-1010].
+
+HINFO records are used to acquire general information about a host.  The
+main use is for protocols such as FTP that can use special procedures
+when talking between machines or operating systems of the same type.
+
+3.3.3. MB RDATA format (EXPERIMENTAL)
+
+    +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+    /                   MADNAME                     /
+    /                                               /
+    +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+
+where:
+
+MADNAME         A <domain-name> which specifies a host which has the
+                specified mailbox.
+
+
+
+Mockapetris                                                    [Page 14]
+\f
+RFC 1035        Domain Implementation and Specification    November 1987
+
+
+MB records cause additional section processing which looks up an A type
+RRs corresponding to MADNAME.
+
+3.3.4. MD RDATA format (Obsolete)
+
+    +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+    /                   MADNAME                     /
+    /                                               /
+    +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+
+where:
+
+MADNAME         A <domain-name> which specifies a host which has a mail
+                agent for the domain which should be able to deliver
+                mail for the domain.
+
+MD records cause additional section processing which looks up an A type
+record corresponding to MADNAME.
+
+MD is obsolete.  See the definition of MX and [RFC-974] for details of
+the new scheme.  The recommended policy for dealing with MD RRs found in
+a master file is to reject them, or to convert them to MX RRs with a
+preference of 0.
+
+3.3.5. MF RDATA format (Obsolete)
+
+    +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+    /                   MADNAME                     /
+    /                                               /
+    +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+
+where:
+
+MADNAME         A <domain-name> which specifies a host which has a mail
+                agent for the domain which will accept mail for
+                forwarding to the domain.
+
+MF records cause additional section processing which looks up an A type
+record corresponding to MADNAME.
+
+MF is obsolete.  See the definition of MX and [RFC-974] for details ofw
+the new scheme.  The recommended policy for dealing with MD RRs found in
+a master file is to reject them, or to convert them to MX RRs with a
+preference of 10.
+
+
+
+
+
+
+
+Mockapetris                                                    [Page 15]
+\f
+RFC 1035        Domain Implementation and Specification    November 1987
+
+
+3.3.6. MG RDATA format (EXPERIMENTAL)
+
+    +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+    /                   MGMNAME                     /
+    /                                               /
+    +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+
+where:
+
+MGMNAME         A <domain-name> which specifies a mailbox which is a
+                member of the mail group specified by the domain name.
+
+MG records cause no additional section processing.
+
+3.3.7. MINFO RDATA format (EXPERIMENTAL)
+
+    +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+    /                    RMAILBX                    /
+    +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+    /                    EMAILBX                    /
+    +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+
+where:
+
+RMAILBX         A <domain-name> which specifies a mailbox which is
+                responsible for the mailing list or mailbox.  If this
+                domain name names the root, the owner of the MINFO RR is
+                responsible for itself.  Note that many existing mailing
+                lists use a mailbox X-request for the RMAILBX field of
+                mailing list X, e.g., Msgroup-request for Msgroup.  This
+                field provides a more general mechanism.
+
+
+EMAILBX         A <domain-name> which specifies a mailbox which is to
+                receive error messages related to the mailing list or
+                mailbox specified by the owner of the MINFO RR (similar
+                to the ERRORS-TO: field which has been proposed).  If
+                this domain name names the root, errors should be
+                returned to the sender of the message.
+
+MINFO records cause no additional section processing.  Although these
+records can be associated with a simple mailbox, they are usually used
+with a mailing list.
+
+
+
+
+
+
+
+
+Mockapetris                                                    [Page 16]
+\f
+RFC 1035        Domain Implementation and Specification    November 1987
+
+
+3.3.8. MR RDATA format (EXPERIMENTAL)
+
+    +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+    /                   NEWNAME                     /
+    /                                               /
+    +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+
+where:
+
+NEWNAME         A <domain-name> which specifies a mailbox which is the
+                proper rename of the specified mailbox.
+
+MR records cause no additional section processing.  The main use for MR
+is as a forwarding entry for a user who has moved to a different
+mailbox.
+
+3.3.9. MX RDATA format
+
+    +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+    |                  PREFERENCE                   |
+    +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+    /                   EXCHANGE                    /
+    /                                               /
+    +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+
+where:
+
+PREFERENCE      A 16 bit integer which specifies the preference given to
+                this RR among others at the same owner.  Lower values
+                are preferred.
+
+EXCHANGE        A <domain-name> which specifies a host willing to act as
+                a mail exchange for the owner name.
+
+MX records cause type A additional section processing for the host
+specified by EXCHANGE.  The use of MX RRs is explained in detail in
+[RFC-974].
+
+3.3.10. NULL RDATA format (EXPERIMENTAL)
+
+    +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+    /                  <anything>                   /
+    /                                               /
+    +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+
+Anything at all may be in the RDATA field so long as it is 65535 octets
+or less.
+
+
+
+
+Mockapetris                                                    [Page 17]
+\f
+RFC 1035        Domain Implementation and Specification    November 1987
+
+
+NULL records cause no additional section processing.  NULL RRs are not
+allowed in master files.  NULLs are used as placeholders in some
+experimental extensions of the DNS.
+
+3.3.11. NS RDATA format
+
+    +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+    /                   NSDNAME                     /
+    /                                               /
+    +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+
+where:
+
+NSDNAME         A <domain-name> which specifies a host which should be
+                authoritative for the specified class and domain.
+
+NS records cause both the usual additional section processing to locate
+a type A record, and, when used in a referral, a special search of the
+zone in which they reside for glue information.
+
+The NS RR states that the named host should be expected to have a zone
+starting at owner name of the specified class.  Note that the class may
+not indicate the protocol family which should be used to communicate
+with the host, although it is typically a strong hint.  For example,
+hosts which are name servers for either Internet (IN) or Hesiod (HS)
+class information are normally queried using IN class protocols.
+
+3.3.12. PTR RDATA format
+
+    +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+    /                   PTRDNAME                    /
+    +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+
+where:
+
+PTRDNAME        A <domain-name> which points to some location in the
+                domain name space.
+
+PTR records cause no additional section processing.  These RRs are used
+in special domains to point to some other location in the domain space.
+These records are simple data, and don't imply any special processing
+similar to that performed by CNAME, which identifies aliases.  See the
+description of the IN-ADDR.ARPA domain for an example.
+
+
+
+
+
+
+
+
+Mockapetris                                                    [Page 18]
+\f
+RFC 1035        Domain Implementation and Specification    November 1987
+
+
+3.3.13. SOA RDATA format
+
+    +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+    /                     MNAME                     /
+    /                                               /
+    +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+    /                     RNAME                     /
+    +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+    |                    SERIAL                     |
+    |                                               |
+    +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+    |                    REFRESH                    |
+    |                                               |
+    +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+    |                     RETRY                     |
+    |                                               |
+    +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+    |                    EXPIRE                     |
+    |                                               |
+    +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+    |                    MINIMUM                    |
+    |                                               |
+    +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+
+where:
+
+MNAME           The <domain-name> of the name server that was the
+                original or primary source of data for this zone.
+
+RNAME           A <domain-name> which specifies the mailbox of the
+                person responsible for this zone.
+
+SERIAL          The unsigned 32 bit version number of the original copy
+                of the zone.  Zone transfers preserve this value.  This
+                value wraps and should be compared using sequence space
+                arithmetic.
+
+REFRESH         A 32 bit time interval before the zone should be
+                refreshed.
+
+RETRY           A 32 bit time interval that should elapse before a
+                failed refresh should be retried.
+
+EXPIRE          A 32 bit time value that specifies the upper limit on
+                the time interval that can elapse before the zone is no
+                longer authoritative.
+
+
+
+
+
+Mockapetris                                                    [Page 19]
+\f
+RFC 1035        Domain Implementation and Specification    November 1987
+
+
+MINIMUM         The unsigned 32 bit minimum TTL field that should be
+                exported with any RR from this zone.
+
+SOA records cause no additional section processing.
+
+All times are in units of seconds.
+
+Most of these fields are pertinent only for name server maintenance
+operations.  However, MINIMUM is used in all query operations that
+retrieve RRs from a zone.  Whenever a RR is sent in a response to a
+query, the TTL field is set to the maximum of the TTL field from the RR
+and the MINIMUM field in the appropriate SOA.  Thus MINIMUM is a lower
+bound on the TTL field for all RRs in a zone.  Note that this use of
+MINIMUM should occur when the RRs are copied into the response and not
+when the zone is loaded from a master file or via a zone transfer.  The
+reason for this provison is to allow future dynamic update facilities to
+change the SOA RR with known semantics.
+
+
+3.3.14. TXT RDATA format
+
+    +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+    /                   TXT-DATA                    /
+    +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+
+where:
+
+TXT-DATA        One or more <character-string>s.
+
+TXT RRs are used to hold descriptive text.  The semantics of the text
+depends on the domain where it is found.
+
+3.4. Internet specific RRs
+
+3.4.1. A RDATA format
+
+    +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+    |                    ADDRESS                    |
+    +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+
+where:
+
+ADDRESS         A 32 bit Internet address.
+
+Hosts that have multiple Internet addresses will have multiple A
+records.
+
+
+
+
+
+Mockapetris                                                    [Page 20]
+\f
+RFC 1035        Domain Implementation and Specification    November 1987
+
+
+A records cause no additional section processing.  The RDATA section of
+an A line in a master file is an Internet address expressed as four
+decimal numbers separated by dots without any imbedded spaces (e.g.,
+"10.2.0.52" or "192.0.5.6").
+
+3.4.2. WKS RDATA format
+
+    +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+    |                    ADDRESS                    |
+    +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+    |       PROTOCOL        |                       |
+    +--+--+--+--+--+--+--+--+                       |
+    |                                               |
+    /                   <BIT MAP>                   /
+    /                                               /
+    +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+
+where:
+
+ADDRESS         An 32 bit Internet address
+
+PROTOCOL        An 8 bit IP protocol number
+
+<BIT MAP>       A variable length bit map.  The bit map must be a
+                multiple of 8 bits long.
+
+The WKS record is used to describe the well known services supported by
+a particular protocol on a particular internet address.  The PROTOCOL
+field specifies an IP protocol number, and the bit map has one bit per
+port of the specified protocol.  The first bit corresponds to port 0,
+the second to port 1, etc.  If the bit map does not include a bit for a
+protocol of interest, that bit is assumed zero.  The appropriate values
+and mnemonics for ports and protocols are specified in [RFC-1010].
+
+For example, if PROTOCOL=TCP (6), the 26th bit corresponds to TCP port
+25 (SMTP).  If this bit is set, a SMTP server should be listening on TCP
+port 25; if zero, SMTP service is not supported on the specified
+address.
+
+The purpose of WKS RRs is to provide availability information for
+servers for TCP and UDP.  If a server supports both TCP and UDP, or has
+multiple Internet addresses, then multiple WKS RRs are used.
+
+WKS RRs cause no additional section processing.
+
+In master files, both ports and protocols are expressed using mnemonics
+or decimal numbers.
+
+
+
+
+Mockapetris                                                    [Page 21]
+\f
+RFC 1035        Domain Implementation and Specification    November 1987
+
+
+3.5. IN-ADDR.ARPA domain
+
+The Internet uses a special domain to support gateway location and
+Internet address to host mapping.  Other classes may employ a similar
+strategy in other domains.  The intent of this domain is to provide a
+guaranteed method to perform host address to host name mapping, and to
+facilitate queries to locate all gateways on a particular network in the
+Internet.
+
+Note that both of these services are similar to functions that could be
+performed by inverse queries; the difference is that this part of the
+domain name space is structured according to address, and hence can
+guarantee that the appropriate data can be located without an exhaustive
+search of the domain space.
+
+The domain begins at IN-ADDR.ARPA and has a substructure which follows
+the Internet addressing structure.
+
+Domain names in the IN-ADDR.ARPA domain are defined to have up to four
+labels in addition to the IN-ADDR.ARPA suffix.  Each label represents
+one octet of an Internet address, and is expressed as a character string
+for a decimal value in the range 0-255 (with leading zeros omitted
+except in the case of a zero octet which is represented by a single
+zero).
+
+Host addresses are represented by domain names that have all four labels
+specified.  Thus data for Internet address 10.2.0.52 is located at
+domain name 52.0.2.10.IN-ADDR.ARPA.  The reversal, though awkward to
+read, allows zones to be delegated which are exactly one network of
+address space.  For example, 10.IN-ADDR.ARPA can be a zone containing
+data for the ARPANET, while 26.IN-ADDR.ARPA can be a separate zone for
+MILNET.  Address nodes are used to hold pointers to primary host names
+in the normal domain space.
+
+Network numbers correspond to some non-terminal nodes at various depths
+in the IN-ADDR.ARPA domain, since Internet network numbers are either 1,
+2, or 3 octets.  Network nodes are used to hold pointers to the primary
+host names of gateways attached to that network.  Since a gateway is, by
+definition, on more than one network, it will typically have two or more
+network nodes which point at it.  Gateways will also have host level
+pointers at their fully qualified addresses.
+
+Both the gateway pointers at network nodes and the normal host pointers
+at full address nodes use the PTR RR to point back to the primary domain
+names of the corresponding hosts.
+
+For example, the IN-ADDR.ARPA domain will contain information about the
+ISI gateway between net 10 and 26, an MIT gateway from net 10 to MIT's
+
+
+
+Mockapetris                                                    [Page 22]
+\f
+RFC 1035        Domain Implementation and Specification    November 1987
+
+
+net 18, and hosts A.ISI.EDU and MULTICS.MIT.EDU.  Assuming that ISI
+gateway has addresses 10.2.0.22 and 26.0.0.103, and a name MILNET-
+GW.ISI.EDU, and the MIT gateway has addresses 10.0.0.77 and 18.10.0.4
+and a name GW.LCS.MIT.EDU, the domain database would contain:
+
+    10.IN-ADDR.ARPA.           PTR MILNET-GW.ISI.EDU.
+    10.IN-ADDR.ARPA.           PTR GW.LCS.MIT.EDU.
+    18.IN-ADDR.ARPA.           PTR GW.LCS.MIT.EDU.
+    26.IN-ADDR.ARPA.           PTR MILNET-GW.ISI.EDU.
+    22.0.2.10.IN-ADDR.ARPA.    PTR MILNET-GW.ISI.EDU.
+    103.0.0.26.IN-ADDR.ARPA.   PTR MILNET-GW.ISI.EDU.
+    77.0.0.10.IN-ADDR.ARPA.    PTR GW.LCS.MIT.EDU.
+    4.0.10.18.IN-ADDR.ARPA.    PTR GW.LCS.MIT.EDU.
+    103.0.3.26.IN-ADDR.ARPA.   PTR A.ISI.EDU.
+    6.0.0.10.IN-ADDR.ARPA.     PTR MULTICS.MIT.EDU.
+
+Thus a program which wanted to locate gateways on net 10 would originate
+a query of the form QTYPE=PTR, QCLASS=IN, QNAME=10.IN-ADDR.ARPA.  It
+would receive two RRs in response:
+
+    10.IN-ADDR.ARPA.           PTR MILNET-GW.ISI.EDU.
+    10.IN-ADDR.ARPA.           PTR GW.LCS.MIT.EDU.
+
+The program could then originate QTYPE=A, QCLASS=IN queries for MILNET-
+GW.ISI.EDU. and GW.LCS.MIT.EDU. to discover the Internet addresses of
+these gateways.
+
+A resolver which wanted to find the host name corresponding to Internet
+host address 10.0.0.6 would pursue a query of the form QTYPE=PTR,
+QCLASS=IN, QNAME=6.0.0.10.IN-ADDR.ARPA, and would receive:
+
+    6.0.0.10.IN-ADDR.ARPA.     PTR MULTICS.MIT.EDU.
+
+Several cautions apply to the use of these services:
+   - Since the IN-ADDR.ARPA special domain and the normal domain
+     for a particular host or gateway will be in different zones,
+     the possibility exists that that the data may be inconsistent.
+
+   - Gateways will often have two names in separate domains, only
+     one of which can be primary.
+
+   - Systems that use the domain database to initialize their
+     routing tables must start with enough gateway information to
+     guarantee that they can access the appropriate name server.
+
+   - The gateway data only reflects the existence of a gateway in a
+     manner equivalent to the current HOSTS.TXT file.  It doesn't
+     replace the dynamic availability information from GGP or EGP.
+
+
+
+Mockapetris                                                    [Page 23]
+\f
+RFC 1035        Domain Implementation and Specification    November 1987
+
+
+3.6. Defining new types, classes, and special namespaces
+
+The previously defined types and classes are the ones in use as of the
+date of this memo.  New definitions should be expected.  This section
+makes some recommendations to designers considering additions to the
+existing facilities.  The mailing list NAMEDROPPERS@SRI-NIC.ARPA is the
+forum where general discussion of design issues takes place.
+
+In general, a new type is appropriate when new information is to be
+added to the database about an existing object, or we need new data
+formats for some totally new object.  Designers should attempt to define
+types and their RDATA formats that are generally applicable to all
+classes, and which avoid duplication of information.  New classes are
+appropriate when the DNS is to be used for a new protocol, etc which
+requires new class-specific data formats, or when a copy of the existing
+name space is desired, but a separate management domain is necessary.
+
+New types and classes need mnemonics for master files; the format of the
+master files requires that the mnemonics for type and class be disjoint.
+
+TYPE and CLASS values must be a proper subset of QTYPEs and QCLASSes
+respectively.
+
+The present system uses multiple RRs to represent multiple values of a
+type rather than storing multiple values in the RDATA section of a
+single RR.  This is less efficient for most applications, but does keep
+RRs shorter.  The multiple RRs assumption is incorporated in some
+experimental work on dynamic update methods.
+
+The present system attempts to minimize the duplication of data in the
+database in order to insure consistency.  Thus, in order to find the
+address of the host for a mail exchange, you map the mail domain name to
+a host name, then the host name to addresses, rather than a direct
+mapping to host address.  This approach is preferred because it avoids
+the opportunity for inconsistency.
+
+In defining a new type of data, multiple RR types should not be used to
+create an ordering between entries or express different formats for
+equivalent bindings, instead this information should be carried in the
+body of the RR and a single type used.  This policy avoids problems with
+caching multiple types and defining QTYPEs to match multiple types.
+
+For example, the original form of mail exchange binding used two RR
+types one to represent a "closer" exchange (MD) and one to represent a
+"less close" exchange (MF).  The difficulty is that the presence of one
+RR type in a cache doesn't convey any information about the other
+because the query which acquired the cached information might have used
+a QTYPE of MF, MD, or MAILA (which matched both).  The redesigned
+
+
+
+Mockapetris                                                    [Page 24]
+\f
+RFC 1035        Domain Implementation and Specification    November 1987
+
+
+service used a single type (MX) with a "preference" value in the RDATA
+section which can order different RRs.  However, if any MX RRs are found
+in the cache, then all should be there.
+
+4. MESSAGES
+
+4.1. Format
+
+All communications inside of the domain protocol are carried in a single
+format called a message.  The top level format of message is divided
+into 5 sections (some of which are empty in certain cases) shown below:
+
+    +---------------------+
+    |        Header       |
+    +---------------------+
+    |       Question      | the question for the name server
+    +---------------------+
+    |        Answer       | RRs answering the question
+    +---------------------+
+    |      Authority      | RRs pointing toward an authority
+    +---------------------+
+    |      Additional     | RRs holding additional information
+    +---------------------+
+
+The header section is always present.  The header includes fields that
+specify which of the remaining sections are present, and also specify
+whether the message is a query or a response, a standard query or some
+other opcode, etc.
+
+The names of the sections after the header are derived from their use in
+standard queries.  The question section contains fields that describe a
+question to a name server.  These fields are a query type (QTYPE), a
+query class (QCLASS), and a query domain name (QNAME).  The last three
+sections have the same format: a possibly empty list of concatenated
+resource records (RRs).  The answer section contains RRs that answer the
+question; the authority section contains RRs that point toward an
+authoritative name server; the additional records section contains RRs
+which relate to the query, but are not strictly answers for the
+question.
+
+
+
+
+
+
+
+
+
+
+
+
+Mockapetris                                                    [Page 25]
+\f
+RFC 1035        Domain Implementation and Specification    November 1987
+
+
+4.1.1. Header section format
+
+The header contains the following fields:
+
+                                    1  1  1  1  1  1
+      0  1  2  3  4  5  6  7  8  9  0  1  2  3  4  5
+    +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+    |                      ID                       |
+    +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+    |QR|   Opcode  |AA|TC|RD|RA|   Z    |   RCODE   |
+    +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+    |                    QDCOUNT                    |
+    +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+    |                    ANCOUNT                    |
+    +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+    |                    NSCOUNT                    |
+    +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+    |                    ARCOUNT                    |
+    +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+
+where:
+
+ID              A 16 bit identifier assigned by the program that
+                generates any kind of query.  This identifier is copied
+                the corresponding reply and can be used by the requester
+                to match up replies to outstanding queries.
+
+QR              A one bit field that specifies whether this message is a
+                query (0), or a response (1).
+
+OPCODE          A four bit field that specifies kind of query in this
+                message.  This value is set by the originator of a query
+                and copied into the response.  The values are:
+
+                0               a standard query (QUERY)
+
+                1               an inverse query (IQUERY)
+
+                2               a server status request (STATUS)
+
+                3-15            reserved for future use
+
+AA              Authoritative Answer - this bit is valid in responses,
+                and specifies that the responding name server is an
+                authority for the domain name in question section.
+
+                Note that the contents of the answer section may have
+                multiple owner names because of aliases.  The AA bit
+
+
+
+Mockapetris                                                    [Page 26]
+\f
+RFC 1035        Domain Implementation and Specification    November 1987
+
+
+                corresponds to the name which matches the query name, or
+                the first owner name in the answer section.
+
+TC              TrunCation - specifies that this message was truncated
+                due to length greater than that permitted on the
+                transmission channel.
+
+RD              Recursion Desired - this bit may be set in a query and
+                is copied into the response.  If RD is set, it directs
+                the name server to pursue the query recursively.
+                Recursive query support is optional.
+
+RA              Recursion Available - this be is set or cleared in a
+                response, and denotes whether recursive query support is
+                available in the name server.
+
+Z               Reserved for future use.  Must be zero in all queries
+                and responses.
+
+RCODE           Response code - this 4 bit field is set as part of
+                responses.  The values have the following
+                interpretation:
+
+                0               No error condition
+
+                1               Format error - The name server was
+                                unable to interpret the query.
+
+                2               Server failure - The name server was
+                                unable to process this query due to a
+                                problem with the name server.
+
+                3               Name Error - Meaningful only for
+                                responses from an authoritative name
+                                server, this code signifies that the
+                                domain name referenced in the query does
+                                not exist.
+
+                4               Not Implemented - The name server does
+                                not support the requested kind of query.
+
+                5               Refused - The name server refuses to
+                                perform the specified operation for
+                                policy reasons.  For example, a name
+                                server may not wish to provide the
+                                information to the particular requester,
+                                or a name server may not wish to perform
+                                a particular operation (e.g., zone
+
+
+
+Mockapetris                                                    [Page 27]
+\f
+RFC 1035        Domain Implementation and Specification    November 1987
+
+
+                                transfer) for particular data.
+
+                6-15            Reserved for future use.
+
+QDCOUNT         an unsigned 16 bit integer specifying the number of
+                entries in the question section.
+
+ANCOUNT         an unsigned 16 bit integer specifying the number of
+                resource records in the answer section.
+
+NSCOUNT         an unsigned 16 bit integer specifying the number of name
+                server resource records in the authority records
+                section.
+
+ARCOUNT         an unsigned 16 bit integer specifying the number of
+                resource records in the additional records section.
+
+4.1.2. Question section format
+
+The question section is used to carry the "question" in most queries,
+i.e., the parameters that define what is being asked.  The section
+contains QDCOUNT (usually 1) entries, each of the following format:
+
+                                    1  1  1  1  1  1
+      0  1  2  3  4  5  6  7  8  9  0  1  2  3  4  5
+    +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+    |                                               |
+    /                     QNAME                     /
+    /                                               /
+    +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+    |                     QTYPE                     |
+    +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+    |                     QCLASS                    |
+    +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+
+where:
+
+QNAME           a domain name represented as a sequence of labels, where
+                each label consists of a length octet followed by that
+                number of octets.  The domain name terminates with the
+                zero length octet for the null label of the root.  Note
+                that this field may be an odd number of octets; no
+                padding is used.
+
+QTYPE           a two octet code which specifies the type of the query.
+                The values for this field include all codes valid for a
+                TYPE field, together with some more general codes which
+                can match more than one type of RR.
+
+
+
+Mockapetris                                                    [Page 28]
+\f
+RFC 1035        Domain Implementation and Specification    November 1987
+
+
+QCLASS          a two octet code that specifies the class of the query.
+                For example, the QCLASS field is IN for the Internet.
+
+4.1.3. Resource record format
+
+The answer, authority, and additional sections all share the same
+format: a variable number of resource records, where the number of
+records is specified in the corresponding count field in the header.
+Each resource record has the following format:
+                                    1  1  1  1  1  1
+      0  1  2  3  4  5  6  7  8  9  0  1  2  3  4  5
+    +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+    |                                               |
+    /                                               /
+    /                      NAME                     /
+    |                                               |
+    +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+    |                      TYPE                     |
+    +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+    |                     CLASS                     |
+    +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+    |                      TTL                      |
+    |                                               |
+    +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+    |                   RDLENGTH                    |
+    +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--|
+    /                     RDATA                     /
+    /                                               /
+    +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+
+where:
+
+NAME            a domain name to which this resource record pertains.
+
+TYPE            two octets containing one of the RR type codes.  This
+                field specifies the meaning of the data in the RDATA
+                field.
+
+CLASS           two octets which specify the class of the data in the
+                RDATA field.
+
+TTL             a 32 bit unsigned integer that specifies the time
+                interval (in seconds) that the resource record may be
+                cached before it should be discarded.  Zero values are
+                interpreted to mean that the RR can only be used for the
+                transaction in progress, and should not be cached.
+
+
+
+
+
+Mockapetris                                                    [Page 29]
+\f
+RFC 1035        Domain Implementation and Specification    November 1987
+
+
+RDLENGTH        an unsigned 16 bit integer that specifies the length in
+                octets of the RDATA field.
+
+RDATA           a variable length string of octets that describes the
+                resource.  The format of this information varies
+                according to the TYPE and CLASS of the resource record.
+                For example, the if the TYPE is A and the CLASS is IN,
+                the RDATA field is a 4 octet ARPA Internet address.
+
+4.1.4. Message compression
+
+In order to reduce the size of messages, the domain system utilizes a
+compression scheme which eliminates the repetition of domain names in a
+message.  In this scheme, an entire domain name or a list of labels at
+the end of a domain name is replaced with a pointer to a prior occurance
+of the same name.
+
+The pointer takes the form of a two octet sequence:
+
+    +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+    | 1  1|                OFFSET                   |
+    +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+
+The first two bits are ones.  This allows a pointer to be distinguished
+from a label, since the label must begin with two zero bits because
+labels are restricted to 63 octets or less.  (The 10 and 01 combinations
+are reserved for future use.)  The OFFSET field specifies an offset from
+the start of the message (i.e., the first octet of the ID field in the
+domain header).  A zero offset specifies the first byte of the ID field,
+etc.
+
+The compression scheme allows a domain name in a message to be
+represented as either:
+
+   - a sequence of labels ending in a zero octet
+
+   - a pointer
+
+   - a sequence of labels ending with a pointer
+
+Pointers can only be used for occurances of a domain name where the
+format is not class specific.  If this were not the case, a name server
+or resolver would be required to know the format of all RRs it handled.
+As yet, there are no such cases, but they may occur in future RDATA
+formats.
+
+If a domain name is contained in a part of the message subject to a
+length field (such as the RDATA section of an RR), and compression is
+
+
+
+Mockapetris                                                    [Page 30]
+\f
+RFC 1035        Domain Implementation and Specification    November 1987
+
+
+used, the length of the compressed name is used in the length
+calculation, rather than the length of the expanded name.
+
+Programs are free to avoid using pointers in messages they generate,
+although this will reduce datagram capacity, and may cause truncation.
+However all programs are required to understand arriving messages that
+contain pointers.
+
+For example, a datagram might need to use the domain names F.ISI.ARPA,
+FOO.F.ISI.ARPA, ARPA, and the root.  Ignoring the other fields of the
+message, these domain names might be represented as:
+
+       +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+    20 |           1           |           F           |
+       +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+    22 |           3           |           I           |
+       +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+    24 |           S           |           I           |
+       +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+    26 |           4           |           A           |
+       +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+    28 |           R           |           P           |
+       +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+    30 |           A           |           0           |
+       +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+
+       +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+    40 |           3           |           F           |
+       +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+    42 |           O           |           O           |
+       +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+    44 | 1  1|                20                       |
+       +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+
+       +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+    64 | 1  1|                26                       |
+       +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+
+       +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+    92 |           0           |                       |
+       +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+
+The domain name for F.ISI.ARPA is shown at offset 20.  The domain name
+FOO.F.ISI.ARPA is shown at offset 40; this definition uses a pointer to
+concatenate a label for FOO to the previously defined F.ISI.ARPA.  The
+domain name ARPA is defined at offset 64 using a pointer to the ARPA
+component of the name F.ISI.ARPA at 20; note that this pointer relies on
+ARPA being the last label in the string at 20.  The root domain name is
+
+
+
+Mockapetris                                                    [Page 31]
+\f
+RFC 1035        Domain Implementation and Specification    November 1987
+
+
+defined by a single octet of zeros at 92; the root domain name has no
+labels.
+
+4.2. Transport
+
+The DNS assumes that messages will be transmitted as datagrams or in a
+byte stream carried by a virtual circuit.  While virtual circuits can be
+used for any DNS activity, datagrams are preferred for queries due to
+their lower overhead and better performance.  Zone refresh activities
+must use virtual circuits because of the need for reliable transfer.
+
+The Internet supports name server access using TCP [RFC-793] on server
+port 53 (decimal) as well as datagram access using UDP [RFC-768] on UDP
+port 53 (decimal).
+
+4.2.1. UDP usage
+
+Messages sent using UDP user server port 53 (decimal).
+
+Messages carried by UDP are restricted to 512 bytes (not counting the IP
+or UDP headers).  Longer messages are truncated and the TC bit is set in
+the header.
+
+UDP is not acceptable for zone transfers, but is the recommended method
+for standard queries in the Internet.  Queries sent using UDP may be
+lost, and hence a retransmission strategy is required.  Queries or their
+responses may be reordered by the network, or by processing in name
+servers, so resolvers should not depend on them being returned in order.
+
+The optimal UDP retransmission policy will vary with performance of the
+Internet and the needs of the client, but the following are recommended:
+
+   - The client should try other servers and server addresses
+     before repeating a query to a specific address of a server.
+
+   - The retransmission interval should be based on prior
+     statistics if possible.  Too aggressive retransmission can
+     easily slow responses for the community at large.  Depending
+     on how well connected the client is to its expected servers,
+     the minimum retransmission interval should be 2-5 seconds.
+
+More suggestions on server selection and retransmission policy can be
+found in the resolver section of this memo.
+
+4.2.2. TCP usage
+
+Messages sent over TCP connections use server port 53 (decimal).  The
+message is prefixed with a two byte length field which gives the message
+
+
+
+Mockapetris                                                    [Page 32]
+\f
+RFC 1035        Domain Implementation and Specification    November 1987
+
+
+length, excluding the two byte length field.  This length field allows
+the low-level processing to assemble a complete message before beginning
+to parse it.
+
+Several connection management policies are recommended:
+
+   - The server should not block other activities waiting for TCP
+     data.
+
+   - The server should support multiple connections.
+
+   - The server should assume that the client will initiate
+     connection closing, and should delay closing its end of the
+     connection until all outstanding client requests have been
+     satisfied.
+
+   - If the server needs to close a dormant connection to reclaim
+     resources, it should wait until the connection has been idle
+     for a period on the order of two minutes.  In particular, the
+     server should allow the SOA and AXFR request sequence (which
+     begins a refresh operation) to be made on a single connection.
+     Since the server would be unable to answer queries anyway, a
+     unilateral close or reset may be used instead of a graceful
+     close.
+
+5. MASTER FILES
+
+Master files are text files that contain RRs in text form.  Since the
+contents of a zone can be expressed in the form of a list of RRs a
+master file is most often used to define a zone, though it can be used
+to list a cache's contents.  Hence, this section first discusses the
+format of RRs in a master file, and then the special considerations when
+a master file is used to create a zone in some name server.
+
+5.1. Format
+
+The format of these files is a sequence of entries.  Entries are
+predominantly line-oriented, though parentheses can be used to continue
+a list of items across a line boundary, and text literals can contain
+CRLF within the text.  Any combination of tabs and spaces act as a
+delimiter between the separate items that make up an entry.  The end of
+any line in the master file can end with a comment.  The comment starts
+with a ";" (semicolon).
+
+The following entries are defined:
+
+    <blank>[<comment>]
+
+
+
+
+Mockapetris                                                    [Page 33]
+\f
+RFC 1035        Domain Implementation and Specification    November 1987
+
+
+    $ORIGIN <domain-name> [<comment>]
+
+    $INCLUDE <file-name> [<domain-name>] [<comment>]
+
+    <domain-name><rr> [<comment>]
+
+    <blank><rr> [<comment>]
+
+Blank lines, with or without comments, are allowed anywhere in the file.
+
+Two control entries are defined: $ORIGIN and $INCLUDE.  $ORIGIN is
+followed by a domain name, and resets the current origin for relative
+domain names to the stated name.  $INCLUDE inserts the named file into
+the current file, and may optionally specify a domain name that sets the
+relative domain name origin for the included file.  $INCLUDE may also
+have a comment.  Note that a $INCLUDE entry never changes the relative
+origin of the parent file, regardless of changes to the relative origin
+made within the included file.
+
+The last two forms represent RRs.  If an entry for an RR begins with a
+blank, then the RR is assumed to be owned by the last stated owner.  If
+an RR entry begins with a <domain-name>, then the owner name is reset.
+
+<rr> contents take one of the following forms:
+
+    [<TTL>] [<class>] <type> <RDATA>
+
+    [<class>] [<TTL>] <type> <RDATA>
+
+The RR begins with optional TTL and class fields, followed by a type and
+RDATA field appropriate to the type and class.  Class and type use the
+standard mnemonics, TTL is a decimal integer.  Omitted class and TTL
+values are default to the last explicitly stated values.  Since type and
+class mnemonics are disjoint, the parse is unique.  (Note that this
+order is different from the order used in examples and the order used in
+the actual RRs; the given order allows easier parsing and defaulting.)
+
+<domain-name>s make up a large share of the data in the master file.
+The labels in the domain name are expressed as character strings and
+separated by dots.  Quoting conventions allow arbitrary characters to be
+stored in domain names.  Domain names that end in a dot are called
+absolute, and are taken as complete.  Domain names which do not end in a
+dot are called relative; the actual domain name is the concatenation of
+the relative part with an origin specified in a $ORIGIN, $INCLUDE, or as
+an argument to the master file loading routine.  A relative name is an
+error when no origin is available.
+
+
+
+
+
+Mockapetris                                                    [Page 34]
+\f
+RFC 1035        Domain Implementation and Specification    November 1987
+
+
+<character-string> is expressed in one or two ways: as a contiguous set
+of characters without interior spaces, or as a string beginning with a "
+and ending with a ".  Inside a " delimited string any character can
+occur, except for a " itself, which must be quoted using \ (back slash).
+
+Because these files are text files several special encodings are
+necessary to allow arbitrary data to be loaded.  In particular:
+
+                of the root.
+
+@               A free standing @ is used to denote the current origin.
+
+\X              where X is any character other than a digit (0-9), is
+                used to quote that character so that its special meaning
+                does not apply.  For example, "\." can be used to place
+                a dot character in a label.
+
+\DDD            where each D is a digit is the octet corresponding to
+                the decimal number described by DDD.  The resulting
+                octet is assumed to be text and is not checked for
+                special meaning.
+
+( )             Parentheses are used to group data that crosses a line
+                boundary.  In effect, line terminations are not
+                recognized within parentheses.
+
+;               Semicolon is used to start a comment; the remainder of
+                the line is ignored.
+
+5.2. Use of master files to define zones
+
+When a master file is used to load a zone, the operation should be
+suppressed if any errors are encountered in the master file.  The
+rationale for this is that a single error can have widespread
+consequences.  For example, suppose that the RRs defining a delegation
+have syntax errors; then the server will return authoritative name
+errors for all names in the subzone (except in the case where the
+subzone is also present on the server).
+
+Several other validity checks that should be performed in addition to
+insuring that the file is syntactically correct:
+
+   1. All RRs in the file should have the same class.
+
+   2. Exactly one SOA RR should be present at the top of the zone.
+
+   3. If delegations are present and glue information is required,
+      it should be present.
+
+
+
+Mockapetris                                                    [Page 35]
+\f
+RFC 1035        Domain Implementation and Specification    November 1987
+
+
+   4. Information present outside of the authoritative nodes in the
+      zone should be glue information, rather than the result of an
+      origin or similar error.
+
+5.3. Master file example
+
+The following is an example file which might be used to define the
+ISI.EDU zone.and is loaded with an origin of ISI.EDU:
+
+@   IN  SOA     VENERA      Action\.domains (
+                                 20     ; SERIAL
+                                 7200   ; REFRESH
+                                 600    ; RETRY
+                                 3600000; EXPIRE
+                                 60)    ; MINIMUM
+
+        NS      A.ISI.EDU.
+        NS      VENERA
+        NS      VAXA
+        MX      10      VENERA
+        MX      20      VAXA
+
+A       A       26.3.0.103
+
+VENERA  A       10.1.0.52
+        A       128.9.0.32
+
+VAXA    A       10.2.0.27
+        A       128.9.0.33
+
+
+$INCLUDE <SUBSYS>ISI-MAILBOXES.TXT
+
+Where the file <SUBSYS>ISI-MAILBOXES.TXT is:
+
+    MOE     MB      A.ISI.EDU.
+    LARRY   MB      A.ISI.EDU.
+    CURLEY  MB      A.ISI.EDU.
+    STOOGES MG      MOE
+            MG      LARRY
+            MG      CURLEY
+
+Note the use of the \ character in the SOA RR to specify the responsible
+person mailbox "Action.domains@E.ISI.EDU".
+
+
+
+
+
+
+
+Mockapetris                                                    [Page 36]
+\f
+RFC 1035        Domain Implementation and Specification    November 1987
+
+
+6. NAME SERVER IMPLEMENTATION
+
+6.1. Architecture
+
+The optimal structure for the name server will depend on the host
+operating system and whether the name server is integrated with resolver
+operations, either by supporting recursive service, or by sharing its
+database with a resolver.  This section discusses implementation
+considerations for a name server which shares a database with a
+resolver, but most of these concerns are present in any name server.
+
+6.1.1. Control
+
+A name server must employ multiple concurrent activities, whether they
+are implemented as separate tasks in the host's OS or multiplexing
+inside a single name server program.  It is simply not acceptable for a
+name server to block the service of UDP requests while it waits for TCP
+data for refreshing or query activities.  Similarly, a name server
+should not attempt to provide recursive service without processing such
+requests in parallel, though it may choose to serialize requests from a
+single client, or to regard identical requests from the same client as
+duplicates.  A name server should not substantially delay requests while
+it reloads a zone from master files or while it incorporates a newly
+refreshed zone into its database.
+
+6.1.2. Database
+
+While name server implementations are free to use any internal data
+structures they choose, the suggested structure consists of three major
+parts:
+
+   - A "catalog" data structure which lists the zones available to
+     this server, and a "pointer" to the zone data structure.  The
+     main purpose of this structure is to find the nearest ancestor
+     zone, if any, for arriving standard queries.
+
+   - Separate data structures for each of the zones held by the
+     name server.
+
+   - A data structure for cached data. (or perhaps separate caches
+     for different classes)
+
+All of these data structures can be implemented an identical tree
+structure format, with different data chained off the nodes in different
+parts: in the catalog the data is pointers to zones, while in the zone
+and cache data structures, the data will be RRs.  In designing the tree
+framework the designer should recognize that query processing will need
+to traverse the tree using case-insensitive label comparisons; and that
+
+
+
+Mockapetris                                                    [Page 37]
+\f
+RFC 1035        Domain Implementation and Specification    November 1987
+
+
+in real data, a few nodes have a very high branching factor (100-1000 or
+more), but the vast majority have a very low branching factor (0-1).
+
+One way to solve the case problem is to store the labels for each node
+in two pieces: a standardized-case representation of the label where all
+ASCII characters are in a single case, together with a bit mask that
+denotes which characters are actually of a different case.  The
+branching factor diversity can be handled using a simple linked list for
+a node until the branching factor exceeds some threshold, and
+transitioning to a hash structure after the threshold is exceeded.  In
+any case, hash structures used to store tree sections must insure that
+hash functions and procedures preserve the casing conventions of the
+DNS.
+
+The use of separate structures for the different parts of the database
+is motivated by several factors:
+
+   - The catalog structure can be an almost static structure that
+     need change only when the system administrator changes the
+     zones supported by the server.  This structure can also be
+     used to store parameters used to control refreshing
+     activities.
+
+   - The individual data structures for zones allow a zone to be
+     replaced simply by changing a pointer in the catalog.  Zone
+     refresh operations can build a new structure and, when
+     complete, splice it into the database via a simple pointer
+     replacement.  It is very important that when a zone is
+     refreshed, queries should not use old and new data
+     simultaneously.
+
+   - With the proper search procedures, authoritative data in zones
+     will always "hide", and hence take precedence over, cached
+     data.
+
+   - Errors in zone definitions that cause overlapping zones, etc.,
+     may cause erroneous responses to queries, but problem
+     determination is simplified, and the contents of one "bad"
+     zone can't corrupt another.
+
+   - Since the cache is most frequently updated, it is most
+     vulnerable to corruption during system restarts.  It can also
+     become full of expired RR data.  In either case, it can easily
+     be discarded without disturbing zone data.
+
+A major aspect of database design is selecting a structure which allows
+the name server to deal with crashes of the name server's host.  State
+information which a name server should save across system crashes
+
+
+
+Mockapetris                                                    [Page 38]
+\f
+RFC 1035        Domain Implementation and Specification    November 1987
+
+
+includes the catalog structure (including the state of refreshing for
+each zone) and the zone data itself.
+
+6.1.3. Time
+
+Both the TTL data for RRs and the timing data for refreshing activities
+depends on 32 bit timers in units of seconds.  Inside the database,
+refresh timers and TTLs for cached data conceptually "count down", while
+data in the zone stays with constant TTLs.
+
+A recommended implementation strategy is to store time in two ways:  as
+a relative increment and as an absolute time.  One way to do this is to
+use positive 32 bit numbers for one type and negative numbers for the
+other.  The RRs in zones use relative times; the refresh timers and
+cache data use absolute times.  Absolute numbers are taken with respect
+to some known origin and converted to relative values when placed in the
+response to a query.  When an absolute TTL is negative after conversion
+to relative, then the data is expired and should be ignored.
+
+6.2. Standard query processing
+
+The major algorithm for standard query processing is presented in
+[RFC-1034].
+
+When processing queries with QCLASS=*, or some other QCLASS which
+matches multiple classes, the response should never be authoritative
+unless the server can guarantee that the response covers all classes.
+
+When composing a response, RRs which are to be inserted in the
+additional section, but duplicate RRs in the answer or authority
+sections, may be omitted from the additional section.
+
+When a response is so long that truncation is required, the truncation
+should start at the end of the response and work forward in the
+datagram.  Thus if there is any data for the authority section, the
+answer section is guaranteed to be unique.
+
+The MINIMUM value in the SOA should be used to set a floor on the TTL of
+data distributed from a zone.  This floor function should be done when
+the data is copied into a response.  This will allow future dynamic
+update protocols to change the SOA MINIMUM field without ambiguous
+semantics.
+
+6.3. Zone refresh and reload processing
+
+In spite of a server's best efforts, it may be unable to load zone data
+from a master file due to syntax errors, etc., or be unable to refresh a
+zone within the its expiration parameter.  In this case, the name server
+
+
+
+Mockapetris                                                    [Page 39]
+\f
+RFC 1035        Domain Implementation and Specification    November 1987
+
+
+should answer queries as if it were not supposed to possess the zone.
+
+If a master is sending a zone out via AXFR, and a new version is created
+during the transfer, the master should continue to send the old version
+if possible.  In any case, it should never send part of one version and
+part of another.  If completion is not possible, the master should reset
+the connection on which the zone transfer is taking place.
+
+6.4. Inverse queries (Optional)
+
+Inverse queries are an optional part of the DNS.  Name servers are not
+required to support any form of inverse queries.  If a name server
+receives an inverse query that it does not support, it returns an error
+response with the "Not Implemented" error set in the header.  While
+inverse query support is optional, all name servers must be at least
+able to return the error response.
+
+6.4.1. The contents of inverse queries and responses          Inverse
+queries reverse the mappings performed by standard query operations;
+while a standard query maps a domain name to a resource, an inverse
+query maps a resource to a domain name.  For example, a standard query
+might bind a domain name to a host address; the corresponding inverse
+query binds the host address to a domain name.
+
+Inverse queries take the form of a single RR in the answer section of
+the message, with an empty question section.  The owner name of the
+query RR and its TTL are not significant.  The response carries
+questions in the question section which identify all names possessing
+the query RR WHICH THE NAME SERVER KNOWS.  Since no name server knows
+about all of the domain name space, the response can never be assumed to
+be complete.  Thus inverse queries are primarily useful for database
+management and debugging activities.  Inverse queries are NOT an
+acceptable method of mapping host addresses to host names; use the IN-
+ADDR.ARPA domain instead.
+
+Where possible, name servers should provide case-insensitive comparisons
+for inverse queries.  Thus an inverse query asking for an MX RR of
+"Venera.isi.edu" should get the same response as a query for
+"VENERA.ISI.EDU"; an inverse query for HINFO RR "IBM-PC UNIX" should
+produce the same result as an inverse query for "IBM-pc unix".  However,
+this cannot be guaranteed because name servers may possess RRs that
+contain character strings but the name server does not know that the
+data is character.
+
+When a name server processes an inverse query, it either returns:
+
+   1. zero, one, or multiple domain names for the specified
+      resource as QNAMEs in the question section
+
+
+
+Mockapetris                                                    [Page 40]
+\f
+RFC 1035        Domain Implementation and Specification    November 1987
+
+
+   2. an error code indicating that the name server doesn't support
+      inverse mapping of the specified resource type.
+
+When the response to an inverse query contains one or more QNAMEs, the
+owner name and TTL of the RR in the answer section which defines the
+inverse query is modified to exactly match an RR found at the first
+QNAME.
+
+RRs returned in the inverse queries cannot be cached using the same
+mechanism as is used for the replies to standard queries.  One reason
+for this is that a name might have multiple RRs of the same type, and
+only one would appear.  For example, an inverse query for a single
+address of a multiply homed host might create the impression that only
+one address existed.
+
+6.4.2. Inverse query and response example          The overall structure
+of an inverse query for retrieving the domain name that corresponds to
+Internet address 10.1.0.52 is shown below:
+
+                         +-----------------------------------------+
+           Header        |          OPCODE=IQUERY, ID=997          |
+                         +-----------------------------------------+
+          Question       |                 <empty>                 |
+                         +-----------------------------------------+
+           Answer        |        <anyname> A IN 10.1.0.52         |
+                         +-----------------------------------------+
+          Authority      |                 <empty>                 |
+                         +-----------------------------------------+
+         Additional      |                 <empty>                 |
+                         +-----------------------------------------+
+
+This query asks for a question whose answer is the Internet style
+address 10.1.0.52.  Since the owner name is not known, any domain name
+can be used as a placeholder (and is ignored).  A single octet of zero,
+signifying the root, is usually used because it minimizes the length of
+the message.  The TTL of the RR is not significant.  The response to
+this query might be:
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+Mockapetris                                                    [Page 41]
+\f
+RFC 1035        Domain Implementation and Specification    November 1987
+
+
+                         +-----------------------------------------+
+           Header        |         OPCODE=RESPONSE, ID=997         |
+                         +-----------------------------------------+
+          Question       |QTYPE=A, QCLASS=IN, QNAME=VENERA.ISI.EDU |
+                         +-----------------------------------------+
+           Answer        |  VENERA.ISI.EDU  A IN 10.1.0.52         |
+                         +-----------------------------------------+
+          Authority      |                 <empty>                 |
+                         +-----------------------------------------+
+         Additional      |                 <empty>                 |
+                         +-----------------------------------------+
+
+Note that the QTYPE in a response to an inverse query is the same as the
+TYPE field in the answer section of the inverse query.  Responses to
+inverse queries may contain multiple questions when the inverse is not
+unique.  If the question section in the response is not empty, then the
+RR in the answer section is modified to correspond to be an exact copy
+of an RR at the first QNAME.
+
+6.4.3. Inverse query processing
+
+Name servers that support inverse queries can support these operations
+through exhaustive searches of their databases, but this becomes
+impractical as the size of the database increases.  An alternative
+approach is to invert the database according to the search key.
+
+For name servers that support multiple zones and a large amount of data,
+the recommended approach is separate inversions for each zone.  When a
+particular zone is changed during a refresh, only its inversions need to
+be redone.
+
+Support for transfer of this type of inversion may be included in future
+versions of the domain system, but is not supported in this version.
+
+6.5. Completion queries and responses
+
+The optional completion services described in RFC-882 and RFC-883 have
+been deleted.  Redesigned services may become available in the future.
+
+
+
+
+
+
+
+
+
+
+
+
+
+Mockapetris                                                    [Page 42]
+\f
+RFC 1035        Domain Implementation and Specification    November 1987
+
+
+7. RESOLVER IMPLEMENTATION
+
+The top levels of the recommended resolver algorithm are discussed in
+[RFC-1034].  This section discusses implementation details assuming the
+database structure suggested in the name server implementation section
+of this memo.
+
+7.1. Transforming a user request into a query
+
+The first step a resolver takes is to transform the client's request,
+stated in a format suitable to the local OS, into a search specification
+for RRs at a specific name which match a specific QTYPE and QCLASS.
+Where possible, the QTYPE and QCLASS should correspond to a single type
+and a single class, because this makes the use of cached data much
+simpler.  The reason for this is that the presence of data of one type
+in a cache doesn't confirm the existence or non-existence of data of
+other types, hence the only way to be sure is to consult an
+authoritative source.  If QCLASS=* is used, then authoritative answers
+won't be available.
+
+Since a resolver must be able to multiplex multiple requests if it is to
+perform its function efficiently, each pending request is usually
+represented in some block of state information.  This state block will
+typically contain:
+
+   - A timestamp indicating the time the request began.
+     The timestamp is used to decide whether RRs in the database
+     can be used or are out of date.  This timestamp uses the
+     absolute time format previously discussed for RR storage in
+     zones and caches.  Note that when an RRs TTL indicates a
+     relative time, the RR must be timely, since it is part of a
+     zone.  When the RR has an absolute time, it is part of a
+     cache, and the TTL of the RR is compared against the timestamp
+     for the start of the request.
+
+     Note that using the timestamp is superior to using a current
+     time, since it allows RRs with TTLs of zero to be entered in
+     the cache in the usual manner, but still used by the current
+     request, even after intervals of many seconds due to system
+     load, query retransmission timeouts, etc.
+
+   - Some sort of parameters to limit the amount of work which will
+     be performed for this request.
+
+     The amount of work which a resolver will do in response to a
+     client request must be limited to guard against errors in the
+     database, such as circular CNAME references, and operational
+     problems, such as network partition which prevents the
+
+
+
+Mockapetris                                                    [Page 43]
+\f
+RFC 1035        Domain Implementation and Specification    November 1987
+
+
+     resolver from accessing the name servers it needs.  While
+     local limits on the number of times a resolver will retransmit
+     a particular query to a particular name server address are
+     essential, the resolver should have a global per-request
+     counter to limit work on a single request.  The counter should
+     be set to some initial value and decremented whenever the
+     resolver performs any action (retransmission timeout,
+     retransmission, etc.)  If the counter passes zero, the request
+     is terminated with a temporary error.
+
+     Note that if the resolver structure allows one request to
+     start others in parallel, such as when the need to access a
+     name server for one request causes a parallel resolve for the
+     name server's addresses, the spawned request should be started
+     with a lower counter.  This prevents circular references in
+     the database from starting a chain reaction of resolver
+     activity.
+
+   - The SLIST data structure discussed in [RFC-1034].
+
+     This structure keeps track of the state of a request if it
+     must wait for answers from foreign name servers.
+
+7.2. Sending the queries
+
+As described in [RFC-1034], the basic task of the resolver is to
+formulate a query which will answer the client's request and direct that
+query to name servers which can provide the information.  The resolver
+will usually only have very strong hints about which servers to ask, in
+the form of NS RRs, and may have to revise the query, in response to
+CNAMEs, or revise the set of name servers the resolver is asking, in
+response to delegation responses which point the resolver to name
+servers closer to the desired information.  In addition to the
+information requested by the client, the resolver may have to call upon
+its own services to determine the address of name servers it wishes to
+contact.
+
+In any case, the model used in this memo assumes that the resolver is
+multiplexing attention between multiple requests, some from the client,
+and some internally generated.  Each request is represented by some
+state information, and the desired behavior is that the resolver
+transmit queries to name servers in a way that maximizes the probability
+that the request is answered, minimizes the time that the request takes,
+and avoids excessive transmissions.  The key algorithm uses the state
+information of the request to select the next name server address to
+query, and also computes a timeout which will cause the next action
+should a response not arrive.  The next action will usually be a
+transmission to some other server, but may be a temporary error to the
+
+
+
+Mockapetris                                                    [Page 44]
+\f
+RFC 1035        Domain Implementation and Specification    November 1987
+
+
+client.
+
+The resolver always starts with a list of server names to query (SLIST).
+This list will be all NS RRs which correspond to the nearest ancestor
+zone that the resolver knows about.  To avoid startup problems, the
+resolver should have a set of default servers which it will ask should
+it have no current NS RRs which are appropriate.  The resolver then adds
+to SLIST all of the known addresses for the name servers, and may start
+parallel requests to acquire the addresses of the servers when the
+resolver has the name, but no addresses, for the name servers.
+
+To complete initialization of SLIST, the resolver attaches whatever
+history information it has to the each address in SLIST.  This will
+usually consist of some sort of weighted averages for the response time
+of the address, and the batting average of the address (i.e., how often
+the address responded at all to the request).  Note that this
+information should be kept on a per address basis, rather than on a per
+name server basis, because the response time and batting average of a
+particular server may vary considerably from address to address.  Note
+also that this information is actually specific to a resolver address /
+server address pair, so a resolver with multiple addresses may wish to
+keep separate histories for each of its addresses.  Part of this step
+must deal with addresses which have no such history; in this case an
+expected round trip time of 5-10 seconds should be the worst case, with
+lower estimates for the same local network, etc.
+
+Note that whenever a delegation is followed, the resolver algorithm
+reinitializes SLIST.
+
+The information establishes a partial ranking of the available name
+server addresses.  Each time an address is chosen and the state should
+be altered to prevent its selection again until all other addresses have
+been tried.  The timeout for each transmission should be 50-100% greater
+than the average predicted value to allow for variance in response.
+
+Some fine points:
+
+   - The resolver may encounter a situation where no addresses are
+     available for any of the name servers named in SLIST, and
+     where the servers in the list are precisely those which would
+     normally be used to look up their own addresses.  This
+     situation typically occurs when the glue address RRs have a
+     smaller TTL than the NS RRs marking delegation, or when the
+     resolver caches the result of a NS search.  The resolver
+     should detect this condition and restart the search at the
+     next ancestor zone, or alternatively at the root.
+
+
+
+
+
+Mockapetris                                                    [Page 45]
+\f
+RFC 1035        Domain Implementation and Specification    November 1987
+
+
+   - If a resolver gets a server error or other bizarre response
+     from a name server, it should remove it from SLIST, and may
+     wish to schedule an immediate transmission to the next
+     candidate server address.
+
+7.3. Processing responses
+
+The first step in processing arriving response datagrams is to parse the
+response.  This procedure should include:
+
+   - Check the header for reasonableness.  Discard datagrams which
+     are queries when responses are expected.
+
+   - Parse the sections of the message, and insure that all RRs are
+     correctly formatted.
+
+   - As an optional step, check the TTLs of arriving data looking
+     for RRs with excessively long TTLs.  If a RR has an
+     excessively long TTL, say greater than 1 week, either discard
+     the whole response, or limit all TTLs in the response to 1
+     week.
+
+The next step is to match the response to a current resolver request.
+The recommended strategy is to do a preliminary matching using the ID
+field in the domain header, and then to verify that the question section
+corresponds to the information currently desired.  This requires that
+the transmission algorithm devote several bits of the domain ID field to
+a request identifier of some sort.  This step has several fine points:
+
+   - Some name servers send their responses from different
+     addresses than the one used to receive the query.  That is, a
+     resolver cannot rely that a response will come from the same
+     address which it sent the corresponding query to.  This name
+     server bug is typically encountered in UNIX systems.
+
+   - If the resolver retransmits a particular request to a name
+     server it should be able to use a response from any of the
+     transmissions.  However, if it is using the response to sample
+     the round trip time to access the name server, it must be able
+     to determine which transmission matches the response (and keep
+     transmission times for each outgoing message), or only
+     calculate round trip times based on initial transmissions.
+
+   - A name server will occasionally not have a current copy of a
+     zone which it should have according to some NS RRs.  The
+     resolver should simply remove the name server from the current
+     SLIST, and continue.
+
+
+
+
+Mockapetris                                                    [Page 46]
+\f
+RFC 1035        Domain Implementation and Specification    November 1987
+
+
+7.4. Using the cache
+
+In general, we expect a resolver to cache all data which it receives in
+responses since it may be useful in answering future client requests.
+However, there are several types of data which should not be cached:
+
+   - When several RRs of the same type are available for a
+     particular owner name, the resolver should either cache them
+     all or none at all.  When a response is truncated, and a
+     resolver doesn't know whether it has a complete set, it should
+     not cache a possibly partial set of RRs.
+
+   - Cached data should never be used in preference to
+     authoritative data, so if caching would cause this to happen
+     the data should not be cached.
+
+   - The results of an inverse query should not be cached.
+
+   - The results of standard queries where the QNAME contains "*"
+     labels if the data might be used to construct wildcards.  The
+     reason is that the cache does not necessarily contain existing
+     RRs or zone boundary information which is necessary to
+     restrict the application of the wildcard RRs.
+
+   - RR data in responses of dubious reliability.  When a resolver
+     receives unsolicited responses or RR data other than that
+     requested, it should discard it without caching it.  The basic
+     implication is that all sanity checks on a packet should be
+     performed before any of it is cached.
+
+In a similar vein, when a resolver has a set of RRs for some name in a
+response, and wants to cache the RRs, it should check its cache for
+already existing RRs.  Depending on the circumstances, either the data
+in the response or the cache is preferred, but the two should never be
+combined.  If the data in the response is from authoritative data in the
+answer section, it is always preferred.
+
+8. MAIL SUPPORT
+
+The domain system defines a standard for mapping mailboxes into domain
+names, and two methods for using the mailbox information to derive mail
+routing information.  The first method is called mail exchange binding
+and the other method is mailbox binding.  The mailbox encoding standard
+and mail exchange binding are part of the DNS official protocol, and are
+the recommended method for mail routing in the Internet.  Mailbox
+binding is an experimental feature which is still under development and
+subject to change.
+
+
+
+
+Mockapetris                                                    [Page 47]
+\f
+RFC 1035        Domain Implementation and Specification    November 1987
+
+
+The mailbox encoding standard assumes a mailbox name of the form
+"<local-part>@<mail-domain>".  While the syntax allowed in each of these
+sections varies substantially between the various mail internets, the
+preferred syntax for the ARPA Internet is given in [RFC-822].
+
+The DNS encodes the <local-part> as a single label, and encodes the
+<mail-domain> as a domain name.  The single label from the <local-part>
+is prefaced to the domain name from <mail-domain> to form the domain
+name corresponding to the mailbox.  Thus the mailbox HOSTMASTER@SRI-
+NIC.ARPA is mapped into the domain name HOSTMASTER.SRI-NIC.ARPA.  If the
+<local-part> contains dots or other special characters, its
+representation in a master file will require the use of backslash
+quoting to ensure that the domain name is properly encoded.  For
+example, the mailbox Action.domains@ISI.EDU would be represented as
+Action\.domains.ISI.EDU.
+
+8.1. Mail exchange binding
+
+Mail exchange binding uses the <mail-domain> part of a mailbox
+specification to determine where mail should be sent.  The <local-part>
+is not even consulted.  [RFC-974] specifies this method in detail, and
+should be consulted before attempting to use mail exchange support.
+
+One of the advantages of this method is that it decouples mail
+destination naming from the hosts used to support mail service, at the
+cost of another layer of indirection in the lookup function.  However,
+the addition layer should eliminate the need for complicated "%", "!",
+etc encodings in <local-part>.
+
+The essence of the method is that the <mail-domain> is used as a domain
+name to locate type MX RRs which list hosts willing to accept mail for
+<mail-domain>, together with preference values which rank the hosts
+according to an order specified by the administrators for <mail-domain>.
+
+In this memo, the <mail-domain> ISI.EDU is used in examples, together
+with the hosts VENERA.ISI.EDU and VAXA.ISI.EDU as mail exchanges for
+ISI.EDU.  If a mailer had a message for Mockapetris@ISI.EDU, it would
+route it by looking up MX RRs for ISI.EDU.  The MX RRs at ISI.EDU name
+VENERA.ISI.EDU and VAXA.ISI.EDU, and type A queries can find the host
+addresses.
+
+8.2. Mailbox binding (Experimental)
+
+In mailbox binding, the mailer uses the entire mail destination
+specification to construct a domain name.  The encoded domain name for
+the mailbox is used as the QNAME field in a QTYPE=MAILB query.
+
+Several outcomes are possible for this query:
+
+
+
+Mockapetris                                                    [Page 48]
+\f
+RFC 1035        Domain Implementation and Specification    November 1987
+
+
+   1. The query can return a name error indicating that the mailbox
+      does not exist as a domain name.
+
+      In the long term, this would indicate that the specified
+      mailbox doesn't exist.  However, until the use of mailbox
+      binding is universal, this error condition should be
+      interpreted to mean that the organization identified by the
+      global part does not support mailbox binding.  The
+      appropriate procedure is to revert to exchange binding at
+      this point.
+
+   2. The query can return a Mail Rename (MR) RR.
+
+      The MR RR carries new mailbox specification in its RDATA
+      field.  The mailer should replace the old mailbox with the
+      new one and retry the operation.
+
+   3. The query can return a MB RR.
+
+      The MB RR carries a domain name for a host in its RDATA
+      field.  The mailer should deliver the message to that host
+      via whatever protocol is applicable, e.g., b,SMTP.
+
+   4. The query can return one or more Mail Group (MG) RRs.
+
+      This condition means that the mailbox was actually a mailing
+      list or mail group, rather than a single mailbox.  Each MG RR
+      has a RDATA field that identifies a mailbox that is a member
+      of the group.  The mailer should deliver a copy of the
+      message to each member.
+
+   5. The query can return a MB RR as well as one or more MG RRs.
+
+      This condition means the the mailbox was actually a mailing
+      list.  The mailer can either deliver the message to the host
+      specified by the MB RR, which will in turn do the delivery to
+      all members, or the mailer can use the MG RRs to do the
+      expansion itself.
+
+In any of these cases, the response may include a Mail Information
+(MINFO) RR.  This RR is usually associated with a mail group, but is
+legal with a MB.  The MINFO RR identifies two mailboxes.  One of these
+identifies a responsible person for the original mailbox name.  This
+mailbox should be used for requests to be added to a mail group, etc.
+The second mailbox name in the MINFO RR identifies a mailbox that should
+receive error messages for mail failures.  This is particularly
+appropriate for mailing lists when errors in member names should be
+reported to a person other than the one who sends a message to the list.
+
+
+
+Mockapetris                                                    [Page 49]
+\f
+RFC 1035        Domain Implementation and Specification    November 1987
+
+
+New fields may be added to this RR in the future.
+
+
+9. REFERENCES and BIBLIOGRAPHY
+
+[Dyer 87]       S. Dyer, F. Hsu, "Hesiod", Project Athena
+                Technical Plan - Name Service, April 1987, version 1.9.
+
+                Describes the fundamentals of the Hesiod name service.
+
+[IEN-116]       J. Postel, "Internet Name Server", IEN-116,
+                USC/Information Sciences Institute, August 1979.
+
+                A name service obsoleted by the Domain Name System, but
+                still in use.
+
+[Quarterman 86] J. Quarterman, and J. Hoskins, "Notable Computer Networks",
+                Communications of the ACM, October 1986, volume 29, number
+                10.
+
+[RFC-742]       K. Harrenstien, "NAME/FINGER", RFC-742, Network
+                Information Center, SRI International, December 1977.
+
+[RFC-768]       J. Postel, "User Datagram Protocol", RFC-768,
+                USC/Information Sciences Institute, August 1980.
+
+[RFC-793]       J. Postel, "Transmission Control Protocol", RFC-793,
+                USC/Information Sciences Institute, September 1981.
+
+[RFC-799]       D. Mills, "Internet Name Domains", RFC-799, COMSAT,
+                September 1981.
+
+                Suggests introduction of a hierarchy in place of a flat
+                name space for the Internet.
+
+[RFC-805]       J. Postel, "Computer Mail Meeting Notes", RFC-805,
+                USC/Information Sciences Institute, February 1982.
+
+[RFC-810]       E. Feinler, K. Harrenstien, Z. Su, and V. White, "DOD
+                Internet Host Table Specification", RFC-810, Network
+                Information Center, SRI International, March 1982.
+
+                Obsolete.  See RFC-952.
+
+[RFC-811]       K. Harrenstien, V. White, and E. Feinler, "Hostnames
+                Server", RFC-811, Network Information Center, SRI
+                International, March 1982.
+
+
+
+
+Mockapetris                                                    [Page 50]
+\f
+RFC 1035        Domain Implementation and Specification    November 1987
+
+
+                Obsolete.  See RFC-953.
+
+[RFC-812]       K. Harrenstien, and V. White, "NICNAME/WHOIS", RFC-812,
+                Network Information Center, SRI International, March
+                1982.
+
+[RFC-819]       Z. Su, and J. Postel, "The Domain Naming Convention for
+                Internet User Applications", RFC-819, Network
+                Information Center, SRI International, August 1982.
+
+                Early thoughts on the design of the domain system.
+                Current implementation is completely different.
+
+[RFC-821]       J. Postel, "Simple Mail Transfer Protocol", RFC-821,
+                USC/Information Sciences Institute, August 1980.
+
+[RFC-830]       Z. Su, "A Distributed System for Internet Name Service",
+                RFC-830, Network Information Center, SRI International,
+                October 1982.
+
+                Early thoughts on the design of the domain system.
+                Current implementation is completely different.
+
+[RFC-882]       P. Mockapetris, "Domain names - Concepts and
+                Facilities," RFC-882, USC/Information Sciences
+                Institute, November 1983.
+
+                Superceeded by this memo.
+
+[RFC-883]       P. Mockapetris, "Domain names - Implementation and
+                Specification," RFC-883, USC/Information Sciences
+                Institute, November 1983.
+
+                Superceeded by this memo.
+
+[RFC-920]       J. Postel and J. Reynolds, "Domain Requirements",
+                RFC-920, USC/Information Sciences Institute,
+                October 1984.
+
+                Explains the naming scheme for top level domains.
+
+[RFC-952]       K. Harrenstien, M. Stahl, E. Feinler, "DoD Internet Host
+                Table Specification", RFC-952, SRI, October 1985.
+
+                Specifies the format of HOSTS.TXT, the host/address
+                table replaced by the DNS.
+
+
+
+
+
+Mockapetris                                                    [Page 51]
+\f
+RFC 1035        Domain Implementation and Specification    November 1987
+
+
+[RFC-953]       K. Harrenstien, M. Stahl, E. Feinler, "HOSTNAME Server",
+                RFC-953, SRI, October 1985.
+
+                This RFC contains the official specification of the
+                hostname server protocol, which is obsoleted by the DNS.
+                This TCP based protocol accesses information stored in
+                the RFC-952 format, and is used to obtain copies of the
+                host table.
+
+[RFC-973]       P. Mockapetris, "Domain System Changes and
+                Observations", RFC-973, USC/Information Sciences
+                Institute, January 1986.
+
+                Describes changes to RFC-882 and RFC-883 and reasons for
+                them.
+
+[RFC-974]       C. Partridge, "Mail routing and the domain system",
+                RFC-974, CSNET CIC BBN Labs, January 1986.
+
+                Describes the transition from HOSTS.TXT based mail
+                addressing to the more powerful MX system used with the
+                domain system.
+
+[RFC-1001]      NetBIOS Working Group, "Protocol standard for a NetBIOS
+                service on a TCP/UDP transport: Concepts and Methods",
+                RFC-1001, March 1987.
+
+                This RFC and RFC-1002 are a preliminary design for
+                NETBIOS on top of TCP/IP which proposes to base NetBIOS
+                name service on top of the DNS.
+
+[RFC-1002]      NetBIOS Working Group, "Protocol standard for a NetBIOS
+                service on a TCP/UDP transport: Detailed
+                Specifications", RFC-1002, March 1987.
+
+[RFC-1010]      J. Reynolds, and J. Postel, "Assigned Numbers", RFC-1010,
+                USC/Information Sciences Institute, May 1987.
+
+                Contains socket numbers and mnemonics for host names,
+                operating systems, etc.
+
+[RFC-1031]      W. Lazear, "MILNET Name Domain Transition", RFC-1031,
+                November 1987.
+
+                Describes a plan for converting the MILNET to the DNS.
+
+[RFC-1032]      M. Stahl, "Establishing a Domain - Guidelines for
+                Administrators", RFC-1032, November 1987.
+
+
+
+Mockapetris                                                    [Page 52]
+\f
+RFC 1035        Domain Implementation and Specification    November 1987
+
+
+                Describes the registration policies used by the NIC to
+                administer the top level domains and delegate subzones.
+
+[RFC-1033]      M. Lottor, "Domain Administrators Operations Guide",
+                RFC-1033, November 1987.
+
+                A cookbook for domain administrators.
+
+[Solomon 82]    M. Solomon, L. Landweber, and D. Neuhengen, "The CSNET
+                Name Server", Computer Networks, vol 6, nr 3, July 1982.
+
+                Describes a name service for CSNET which is independent
+                from the DNS and DNS use in the CSNET.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+Mockapetris                                                    [Page 53]
+\f
+RFC 1035        Domain Implementation and Specification    November 1987
+
+
+Index
+
+          *   13
+
+          ;   33, 35
+
+          <character-string>   35
+          <domain-name>   34
+
+          @   35
+
+          \   35
+
+          A   12
+
+          Byte order   8
+
+          CH   13
+          Character case   9
+          CLASS   11
+          CNAME   12
+          Completion   42
+          CS   13
+
+          Hesiod   13
+          HINFO   12
+          HS   13
+
+          IN   13
+          IN-ADDR.ARPA domain   22
+          Inverse queries   40
+
+          Mailbox names   47
+          MB   12
+          MD   12
+          MF   12
+          MG   12
+          MINFO   12
+          MINIMUM   20
+          MR   12
+          MX   12
+
+          NS   12
+          NULL   12
+
+          Port numbers   32
+          Primary server   5
+          PTR   12, 18
+
+
+
+Mockapetris                                                    [Page 54]
+\f
+RFC 1035        Domain Implementation and Specification    November 1987
+
+
+          QCLASS   13
+          QTYPE   12
+
+          RDATA   12
+          RDLENGTH  11
+
+          Secondary server   5
+          SOA   12
+          Stub resolvers   7
+
+          TCP   32
+          TXT   12
+          TYPE   11
+
+          UDP   32
+
+          WKS   12
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+Mockapetris                                                    [Page 55]
+\f
diff --git a/doc/rfc2131.txt b/doc/rfc2131.txt
new file mode 100644 (file)
index 0000000..f45d9b8
--- /dev/null
@@ -0,0 +1,2523 @@
+
+
+
+
+
+
+Network Working Group                                           R. Droms
+Request for Comments: 2131                           Bucknell University
+Obsoletes: 1541                                               March 1997
+Category: Standards Track
+
+                  Dynamic Host Configuration Protocol
+
+Status of this memo
+
+   This document specifies an Internet standards track protocol for the
+   Internet community, and requests discussion and suggestions for
+   improvements.  Please refer to the current edition of the "Internet
+   Official Protocol Standards" (STD 1) for the standardization state
+   and status of this protocol.  Distribution of this memo is unlimited.
+
+Abstract
+
+   The Dynamic Host Configuration Protocol (DHCP) provides a framework
+   for passing configuration information to hosts on a TCPIP network.
+   DHCP is based on the Bootstrap Protocol (BOOTP) [7], adding the
+   capability of automatic allocation of reusable network addresses and
+   additional configuration options [19].  DHCP captures the behavior of
+   BOOTP relay agents [7, 21], and DHCP participants can interoperate
+   with BOOTP participants [9].
+
+Table of Contents
+
+   1.  Introduction. . . . . . . . . . . . . . . . . . . . . . . . .  2
+   1.1 Changes to RFC1541. . . . . . . . . . . . . . . . . . . . . .  3
+   1.2 Related Work. . . . . . . . . . . . . . . . . . . . . . . . .  4
+   1.3 Problem definition and issues . . . . . . . . . . . . . . . .  4
+   1.4 Requirements. . . . . . . . . . . . . . . . . . . . . . . . .  5
+   1.5 Terminology . . . . . . . . . . . . . . . . . . . . . . . . .  6
+   1.6 Design goals. . . . . . . . . . . . . . . . . . . . . . . . .  6
+   2.  Protocol Summary. . . . . . . . . . . . . . . . . . . . . . .  8
+   2.1 Configuration parameters repository . . . . . . . . . . . . . 11
+   2.2 Dynamic allocation of network addresses . . . . . . . . . . . 12
+   3.  The Client-Server Protocol. . . . . . . . . . . . . . . . . . 13
+   3.1 Client-server interaction - allocating a network address. . . 13
+   3.2 Client-server interaction - reusing a  previously allocated
+       network address . . . . . . . . . . . . . . . . . . . . . . . 17
+   3.3 Interpretation and representation of time values. . . . . . . 20
+   3.4 Obtaining parameters with externally configured network
+       address . . . . . . . . . . . . . . . . . . . . . . . . . . . 20
+   3.5 Client parameters in DHCP . . . . . . . . . . . . . . . . . . 21
+   3.6 Use of DHCP in clients with multiple interfaces . . . . . . . 22
+   3.7 When clients should use DHCP. . . . . . . . . . . . . . . . . 22
+   4.  Specification of the DHCP client-server protocol. . . . . . . 22
+
+
+
+Droms                       Standards Track                     [Page 1]
+\f
+RFC 2131          Dynamic Host Configuration Protocol         March 1997
+
+
+   4.1 Constructing and sending DHCP messages. . . . . . . . . . . . 22
+   4.2 DHCP server administrative controls . . . . . . . . . . . . . 25
+   4.3 DHCP server behavior. . . . . . . . . . . . . . . . . . . . . 26
+   4.4 DHCP client behavior. . . . . . . . . . . . . . . . . . . . . 34
+   5.  Acknowledgments. . . . . . . . . . . . . . . . . . . . . . . .42
+   6.  References . . . . . . . . . . . . . . . . . . . . . . . . . .42
+   7.  Security Considerations. . . . . . . . . . . . . . . . . . . .43
+   8.  Author's Address . . . . . . . . . . . . . . . . . . . . . . .44
+   A.  Host Configuration Parameters  . . . . . . . . . . . . . . . .45
+List of Figures
+   1. Format of a DHCP message . . . . . . . . . . . . . . . . . . .  9
+   2. Format of the 'flags' field. . . . . . . . . . . . . . . . . . 11
+   3. Timeline diagram of messages exchanged between DHCP client and
+      servers when allocating a new network address. . . . . . . . . 15
+   4. Timeline diagram of messages exchanged between DHCP client and
+      servers when reusing a previously allocated network address. . 18
+   5. State-transition diagram for DHCP clients. . . . . . . . . . . 34
+List of Tables
+   1. Description of fields in a DHCP message. . . . . . . . . . . . 10
+   2. DHCP messages. . . . . . . . . . . . . . . . . . . . . . . . . 14
+   3. Fields and options used by DHCP servers. . . . . . . . . . . . 28
+   4. Client messages from various states. . . . . . . . . . . . . . 33
+   5. Fields and options used by DHCP clients. . . . . . . . . . . . 37
+
+1. Introduction
+
+   The Dynamic Host Configuration Protocol (DHCP) provides configuration
+   parameters to Internet hosts.  DHCP consists of two components: a
+   protocol for delivering host-specific configuration parameters from a
+   DHCP server to a host and a mechanism for allocation of network
+   addresses to hosts.
+
+   DHCP is built on a client-server model, where designated DHCP server
+   hosts allocate network addresses and deliver configuration parameters
+   to dynamically configured hosts.  Throughout the remainder of this
+   document, the term "server" refers to a host providing initialization
+   parameters through DHCP, and the term "client" refers to a host
+   requesting initialization parameters from a DHCP server.
+
+   A host should not act as a DHCP server unless explicitly configured
+   to do so by a system administrator.  The diversity of hardware and
+   protocol implementations in the Internet would preclude reliable
+   operation if random hosts were allowed to respond to DHCP requests.
+   For example, IP requires the setting of many parameters within the
+   protocol implementation software.  Because IP can be used on many
+   dissimilar kinds of network hardware, values for those parameters
+   cannot be guessed or assumed to have correct defaults.  Also,
+   distributed address allocation schemes depend on a polling/defense
+
+
+
+Droms                       Standards Track                     [Page 2]
+\f
+RFC 2131          Dynamic Host Configuration Protocol         March 1997
+
+
+   mechanism for discovery of addresses that are already in use.  IP
+   hosts may not always be able to defend their network addresses, so
+   that such a distributed address allocation scheme cannot be
+   guaranteed to avoid allocation of duplicate network addresses.
+
+   DHCP supports three mechanisms for IP address allocation.  In
+   "automatic allocation", DHCP assigns a permanent IP address to a
+   client.  In "dynamic allocation", DHCP assigns an IP address to a
+   client for a limited period of time (or until the client explicitly
+   relinquishes the address).  In "manual allocation", a client's IP
+   address is assigned by the network administrator, and DHCP is used
+   simply to convey the assigned address to the client.  A particular
+   network will use one or more of these mechanisms, depending on the
+   policies of the network administrator.
+
+   Dynamic allocation is the only one of the three mechanisms that
+   allows automatic reuse of an address that is no longer needed by the
+   client to which it was assigned.  Thus, dynamic allocation is
+   particularly useful for assigning an address to a client that will be
+   connected to the network only temporarily or for sharing a limited
+   pool of IP addresses among a group of clients that do not need
+   permanent IP addresses.  Dynamic allocation may also be a good choice
+   for assigning an IP address to a new client being permanently
+   connected to a network where IP addresses are sufficiently scarce
+   that it is important to reclaim them when old clients are retired.
+   Manual allocation allows DHCP to be used to eliminate the error-prone
+   process of manually configuring hosts with IP addresses in
+   environments where (for whatever reasons) it is desirable to manage
+   IP address assignment outside of the DHCP mechanisms.
+
+   The format of DHCP messages is based on the format of BOOTP messages,
+   to capture the BOOTP relay agent behavior described as part of the
+   BOOTP specification [7, 21] and to allow interoperability of existing
+   BOOTP clients with DHCP servers.  Using BOOTP relay agents eliminates
+   the necessity of having a DHCP server on each physical network
+   segment.
+
+1.1 Changes to RFC 1541
+
+   This document updates the DHCP protocol specification that appears in
+   RFC1541.  A new DHCP message type, DHCPINFORM, has been added; see
+   section 3.4, 4.3 and 4.4 for details.  The classing mechanism for
+   identifying DHCP clients to DHCP servers has been extended to include
+   "vendor" classes as defined in sections 4.2 and 4.3.  The minimum
+   lease time restriction has been removed.  Finally, many editorial
+   changes have been made to clarify the text as a result of experience
+   gained in DHCP interoperability tests.
+
+
+
+
+Droms                       Standards Track                     [Page 3]
+\f
+RFC 2131          Dynamic Host Configuration Protocol         March 1997
+
+
+1.2 Related Work
+
+   There are several Internet protocols and related mechanisms that
+   address some parts of the dynamic host configuration problem.  The
+   Reverse Address Resolution Protocol (RARP) [10] (through the
+   extensions defined in the Dynamic RARP (DRARP) [5]) explicitly
+   addresses the problem of network address discovery, and includes an
+   automatic IP address assignment mechanism.  The Trivial File Transfer
+   Protocol (TFTP) [20] provides for transport of a boot image from a
+   boot server.  The Internet Control Message Protocol (ICMP) [16]
+   provides for informing hosts of additional routers via "ICMP
+   redirect" messages.  ICMP also can provide subnet mask information
+   through the "ICMP mask request" message and other information through
+   the (obsolete) "ICMP information request" message.  Hosts can locate
+   routers through the ICMP router discovery mechanism [8].
+
+   BOOTP is a transport mechanism for a collection of configuration
+   information.  BOOTP is also extensible, and official extensions [17]
+   have been defined for several configuration parameters.  Morgan has
+   proposed extensions to BOOTP for dynamic IP address assignment [15].
+   The Network Information Protocol (NIP), used by the Athena project at
+   MIT, is a distributed mechanism for dynamic IP address assignment
+   [19].  The Resource Location Protocol RLP [1] provides for location
+   of higher level services.  Sun Microsystems diskless workstations use
+   a boot procedure that employs RARP, TFTP and an RPC mechanism called
+   "bootparams" to deliver configuration information and operating
+   system code to diskless hosts.  (Sun Microsystems, Sun Workstation
+   and SunOS are trademarks of Sun Microsystems, Inc.)  Some Sun
+   networks also use DRARP and an auto-installation mechanism to
+   automate the configuration of new hosts in an existing network.
+
+   In other related work, the path minimum transmission unit (MTU)
+   discovery algorithm can determine the MTU of an arbitrary internet
+   path [14].  The Address Resolution Protocol (ARP) has been proposed
+   as a transport protocol for resource location and selection [6].
+   Finally, the Host Requirements RFCs [3, 4] mention specific
+   requirements for host reconfiguration and suggest a scenario for
+   initial configuration of diskless hosts.
+
+1.3 Problem definition and issues
+
+   DHCP is designed to supply DHCP clients with the configuration
+   parameters defined in the Host Requirements RFCs.  After obtaining
+   parameters via DHCP, a DHCP client should be able to exchange packets
+   with any other host in the Internet.  The TCP/IP stack parameters
+   supplied by DHCP are listed in Appendix A.
+
+
+
+
+
+Droms                       Standards Track                     [Page 4]
+\f
+RFC 2131          Dynamic Host Configuration Protocol         March 1997
+
+
+   Not all of these parameters are required for a newly initialized
+   client.  A client and server may negotiate for the transmission of
+   only those parameters required by the client or specific to a
+   particular subnet.
+
+   DHCP allows but does not require the configuration of client
+   parameters not directly related to the IP protocol.  DHCP also does
+   not address registration of newly configured clients with the Domain
+   Name System (DNS) [12, 13].
+
+   DHCP is not intended for use in configuring routers.
+
+1.4 Requirements
+
+   Throughout this document, the words that are used to define the
+   significance of particular requirements are capitalized.  These words
+   are:
+
+      o "MUST"
+
+        This word or the adjective "REQUIRED" means that the
+        item is an absolute requirement of this specification.
+
+      o "MUST NOT"
+
+        This phrase means that the item is an absolute prohibition
+        of this specification.
+
+      o "SHOULD"
+
+        This word or the adjective "RECOMMENDED" means that there
+        may exist valid reasons in particular circumstances to ignore
+        this item, but the full implications should be understood and
+        the case carefully weighed before choosing a different course.
+
+      o "SHOULD NOT"
+
+        This phrase means that there may exist valid reasons in
+        particular circumstances when the listed behavior is acceptable
+        or even useful, but the full implications should be understood
+        and the case carefully weighed before implementing any behavior
+        described with this label.
+
+
+
+
+
+
+
+
+
+Droms                       Standards Track                     [Page 5]
+\f
+RFC 2131          Dynamic Host Configuration Protocol         March 1997
+
+
+      o "MAY"
+
+        This word or the adjective "OPTIONAL" means that this item is
+        truly optional.  One vendor may choose to include the item
+        because a particular marketplace requires it or because it
+        enhances the product, for example; another vendor may omit the
+        same item.
+
+1.5 Terminology
+
+   This document uses the following terms:
+
+      o "DHCP client"
+
+      A DHCP client is an Internet host using DHCP to obtain
+      configuration parameters such as a network address.
+
+      o "DHCP server"
+
+      A DHCP server is an Internet host that returns configuration
+      parameters to DHCP clients.
+
+      o "BOOTP relay agent"
+
+      A BOOTP relay agent or relay agent is an Internet host or router
+      that passes DHCP messages between DHCP clients and DHCP servers.
+      DHCP is designed to use the same relay agent behavior as specified
+      in the BOOTP protocol specification.
+
+      o "binding"
+
+      A binding is a collection of configuration parameters, including
+      at least an IP address, associated with or "bound to" a DHCP
+      client.  Bindings are managed by DHCP servers.
+
+1.6 Design goals
+
+   The following list gives general design goals for DHCP.
+
+      o DHCP should be a mechanism rather than a policy.  DHCP must
+        allow local system administrators control over configuration
+        parameters where desired; e.g., local system administrators
+        should be able to enforce local policies concerning allocation
+        and access to local resources where desired.
+
+
+
+
+
+
+
+Droms                       Standards Track                     [Page 6]
+\f
+RFC 2131          Dynamic Host Configuration Protocol         March 1997
+
+
+      o Clients should require no manual configuration.  Each client
+        should be able to discover appropriate local configuration
+        parameters without user intervention and incorporate those
+        parameters into its own configuration.
+
+      o Networks should require no manual configuration for individual
+        clients.  Under normal circumstances, the network manager
+        should not have to enter any per-client configuration
+        parameters.
+
+      o DHCP should not require a server on each subnet.  To allow for
+        scale and economy, DHCP must work across routers or through the
+        intervention of BOOTP relay agents.
+
+      o A DHCP client must be prepared to receive multiple responses
+        to a request for configuration parameters.  Some installations
+        may include multiple, overlapping DHCP servers to enhance
+        reliability and increase performance.
+
+      o DHCP must coexist with statically configured, non-participating
+        hosts and with existing network protocol implementations.
+
+      o DHCP must interoperate with the BOOTP relay agent behavior as
+        described by RFC 951 and by RFC 1542 [21].
+
+      o DHCP must provide service to existing BOOTP clients.
+
+   The following list gives design goals specific to the transmission of
+   the network layer parameters.  DHCP must:
+
+      o Guarantee that any specific network address will not be in
+        use by more than one DHCP client at a time,
+
+      o Retain DHCP client configuration across DHCP client reboot.  A
+        DHCP client should, whenever possible, be assigned the same
+        configuration parameters (e.g., network address) in response
+        to each request,
+
+      o Retain DHCP client configuration across server reboots, and,
+        whenever possible, a DHCP client should be assigned the same
+        configuration parameters despite restarts of the DHCP mechanism,
+
+      o Allow automated assignment of configuration parameters to new
+        clients to avoid hand configuration for new clients,
+
+      o Support fixed or permanent allocation of configuration
+        parameters to specific clients.
+
+
+
+
+Droms                       Standards Track                     [Page 7]
+\f
+RFC 2131          Dynamic Host Configuration Protocol         March 1997
+
+
+2. Protocol Summary
+
+   From the client's point of view, DHCP is an extension of the BOOTP
+   mechanism.  This behavior allows existing BOOTP clients to
+   interoperate with DHCP servers without requiring any change to the
+   clients' initialization software.  RFC 1542 [2] details the
+   interactions between BOOTP and DHCP clients and servers [9].  There
+   are some new, optional transactions that optimize the interaction
+   between DHCP clients and servers that are described in sections 3 and
+   4.
+
+   Figure 1 gives the format of a DHCP message and table 1 describes
+   each of the fields in the DHCP message.  The numbers in parentheses
+   indicate the size of each field in octets.  The names for the fields
+   given in the figure will be used throughout this document to refer to
+   the fields in DHCP messages.
+
+   There are two primary differences between DHCP and BOOTP.  First,
+   DHCP defines mechanisms through which clients can be assigned a
+   network address for a finite lease, allowing for serial reassignment
+   of network addresses to different clients.  Second, DHCP provides the
+   mechanism for a client to acquire all of the IP configuration
+   parameters that it needs in order to operate.
+
+   DHCP introduces a small change in terminology intended to clarify the
+   meaning of one of the fields.  What was the "vendor extensions" field
+   in BOOTP has been re-named the "options" field in DHCP. Similarly,
+   the tagged data items that were used inside the BOOTP "vendor
+   extensions" field, which were formerly referred to as "vendor
+   extensions," are now termed simply "options."
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+Droms                       Standards Track                     [Page 8]
+\f
+RFC 2131          Dynamic Host Configuration Protocol         March 1997
+
+
+   0                   1                   2                   3
+   0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+   |     op (1)    |   htype (1)   |   hlen (1)    |   hops (1)    |
+   +---------------+---------------+---------------+---------------+
+   |                            xid (4)                            |
+   +-------------------------------+-------------------------------+
+   |           secs (2)            |           flags (2)           |
+   +-------------------------------+-------------------------------+
+   |                          ciaddr  (4)                          |
+   +---------------------------------------------------------------+
+   |                          yiaddr  (4)                          |
+   +---------------------------------------------------------------+
+   |                          siaddr  (4)                          |
+   +---------------------------------------------------------------+
+   |                          giaddr  (4)                          |
+   +---------------------------------------------------------------+
+   |                                                               |
+   |                          chaddr  (16)                         |
+   |                                                               |
+   |                                                               |
+   +---------------------------------------------------------------+
+   |                                                               |
+   |                          sname   (64)                         |
+   +---------------------------------------------------------------+
+   |                                                               |
+   |                          file    (128)                        |
+   +---------------------------------------------------------------+
+   |                                                               |
+   |                          options (variable)                   |
+   +---------------------------------------------------------------+
+
+                  Figure 1:  Format of a DHCP message
+
+   DHCP defines a new 'client identifier' option that is used to pass an
+   explicit client identifier to a DHCP server.  This change eliminates
+   the overloading of the 'chaddr' field in BOOTP messages, where
+   'chaddr' is used both as a hardware address for transmission of BOOTP
+   reply messages and as a client identifier.  The 'client identifier'
+   is an opaque key, not to be interpreted by the server; for example,
+   the 'client identifier' may contain a hardware address, identical to
+   the contents of the 'chaddr' field, or it may contain another type of
+   identifier, such as a DNS name.  The 'client identifier' chosen by a
+   DHCP client MUST be unique to that client within the subnet to which
+   the client is attached. If the client uses a 'client identifier' in
+   one message, it MUST use that same identifier in all subsequent
+   messages, to ensure that all servers correctly identify the client.
+
+
+
+
+Droms                       Standards Track                     [Page 9]
+\f
+RFC 2131          Dynamic Host Configuration Protocol         March 1997
+
+
+   DHCP clarifies the interpretation of the 'siaddr' field as the
+   address of the server to use in the next step of the client's
+   bootstrap process.  A DHCP server may return its own address in the
+   'siaddr' field, if the server is prepared to supply the next
+   bootstrap service (e.g., delivery of an operating system executable
+   image).  A DHCP server always returns its own address in the 'server
+   identifier' option.
+
+   FIELD      OCTETS       DESCRIPTION
+   -----      ------       -----------
+
+   op            1  Message op code / message type.
+                    1 = BOOTREQUEST, 2 = BOOTREPLY
+   htype         1  Hardware address type, see ARP section in "Assigned
+                    Numbers" RFC; e.g., '1' = 10mb ethernet.
+   hlen          1  Hardware address length (e.g.  '6' for 10mb
+                    ethernet).
+   hops          1  Client sets to zero, optionally used by relay agents
+                    when booting via a relay agent.
+   xid           4  Transaction ID, a random number chosen by the
+                    client, used by the client and server to associate
+                    messages and responses between a client and a
+                    server.
+   secs          2  Filled in by client, seconds elapsed since client
+                    began address acquisition or renewal process.
+   flags         2  Flags (see figure 2).
+   ciaddr        4  Client IP address; only filled in if client is in
+                    BOUND, RENEW or REBINDING state and can respond
+                    to ARP requests.
+   yiaddr        4  'your' (client) IP address.
+   siaddr        4  IP address of next server to use in bootstrap;
+                    returned in DHCPOFFER, DHCPACK by server.
+   giaddr        4  Relay agent IP address, used in booting via a
+                    relay agent.
+   chaddr       16  Client hardware address.
+   sname        64  Optional server host name, null terminated string.
+   file        128  Boot file name, null terminated string; "generic"
+                    name or null in DHCPDISCOVER, fully qualified
+                    directory-path name in DHCPOFFER.
+   options     var  Optional parameters field.  See the options
+                    documents for a list of defined options.
+
+           Table 1:  Description of fields in a DHCP message
+
+   The 'options' field is now variable length. A DHCP client must be
+   prepared to receive DHCP messages with an 'options' field of at least
+   length 312 octets.  This requirement implies that a DHCP client must
+   be prepared to receive a message of up to 576 octets, the minimum IP
+
+
+
+Droms                       Standards Track                    [Page 10]
+\f
+RFC 2131          Dynamic Host Configuration Protocol         March 1997
+
+
+   datagram size an IP host must be prepared to accept [3].  DHCP
+   clients may negotiate the use of larger DHCP messages through the
+   'maximum DHCP message size' option.  The options field may be further
+   extended into the 'file' and 'sname' fields.
+
+   In the case of a client using DHCP for initial configuration (before
+   the client's TCP/IP software has been completely configured), DHCP
+   requires creative use of the client's TCP/IP software and liberal
+   interpretation of RFC 1122.  The TCP/IP software SHOULD accept and
+   forward to the IP layer any IP packets delivered to the client's
+   hardware address before the IP address is configured; DHCP servers
+   and BOOTP relay agents may not be able to deliver DHCP messages to
+   clients that cannot accept hardware unicast datagrams before the
+   TCP/IP software is configured.
+
+   To work around some clients that cannot accept IP unicast datagrams
+   before the TCP/IP software is configured as discussed in the previous
+   paragraph, DHCP uses the 'flags' field [21].  The leftmost bit is
+   defined as the BROADCAST (B) flag.  The semantics of this flag are
+   discussed in section 4.1 of this document.  The remaining bits of the
+   flags field are reserved for future use.  They MUST be set to zero by
+   clients and ignored by servers and relay agents.  Figure 2 gives the
+   format of the 'flags' field.
+
+                                    1 1 1 1 1 1
+                0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5
+                +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+                |B|             MBZ             |
+                +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+
+                B:  BROADCAST flag
+
+                MBZ:  MUST BE ZERO (reserved for future use)
+
+                Figure 2:  Format of the 'flags' field
+
+2.1 Configuration parameters repository
+
+   The first service provided by DHCP is to provide persistent storage
+   of network parameters for network clients.  The model of DHCP
+   persistent storage is that the DHCP service stores a key-value entry
+   for each client, where the key is some unique identifier (for
+   example, an IP subnet number and a unique identifier within the
+   subnet) and the value contains the configuration parameters for the
+   client.
+
+   For example, the key might be the pair (IP-subnet-number, hardware-
+   address) (note that the "hardware-address" should be typed by the
+
+
+
+Droms                       Standards Track                    [Page 11]
+\f
+RFC 2131          Dynamic Host Configuration Protocol         March 1997
+
+
+   type of hardware to accommodate possible duplication of hardware
+   addresses resulting from bit-ordering problems in a mixed-media,
+   bridged network) allowing for serial or concurrent reuse of a
+   hardware address on different subnets, and for hardware addresses
+   that may not be globally unique.  Alternately, the key might be the
+   pair (IP-subnet-number, hostname), allowing the server to assign
+   parameters intelligently to a DHCP client that has been moved to a
+   different subnet or has changed hardware addresses (perhaps because
+   the network interface failed and was replaced). The protocol defines
+   that the key will be (IP-subnet-number, hardware-address) unless the
+   client explicitly supplies an identifier using the 'client
+   identifier' option.           A client can query the DHCP service to
+   retrieve its configuration parameters.  The client interface to the
+   configuration parameters repository consists of protocol messages to
+   request configuration parameters and responses from the server
+   carrying the configuration parameters.
+
+2.2 Dynamic allocation of network addresses
+
+   The second service provided by DHCP is the allocation of temporary or
+   permanent network (IP) addresses to clients.  The basic mechanism for
+   the dynamic allocation of network addresses is simple: a client
+   requests the use of an address for some period of time.  The
+   allocation mechanism (the collection of DHCP servers) guarantees not
+   to reallocate that address within the requested time and attempts to
+   return the same network address each time the client requests an
+   address.  In this document, the period over which a network address
+   is allocated to a client is referred to as a "lease" [11].  The
+   client may extend its lease with subsequent requests.  The client may
+   issue a message to release the address back to the server when the
+   client no longer needs the address.  The client may ask for a
+   permanent assignment by asking for an infinite lease.  Even when
+   assigning "permanent" addresses, a server may choose to give out
+   lengthy but non-infinite leases to allow detection of the fact that
+   the client has been retired.
+
+   In some environments it will be necessary to reassign network
+   addresses due to exhaustion of available addresses.  In such
+   environments, the allocation mechanism will reuse addresses whose
+   lease has expired.  The server should use whatever information is
+   available in the configuration information repository to choose an
+   address to reuse.  For example, the server may choose the least
+   recently assigned address.  As a consistency check, the allocating
+   server SHOULD probe the reused address before allocating the address,
+   e.g., with an ICMP echo request, and the client SHOULD probe the
+   newly received address, e.g., with ARP.
+
+
+
+
+
+Droms                       Standards Track                    [Page 12]
+\f
+RFC 2131          Dynamic Host Configuration Protocol         March 1997
+
+
+3. The Client-Server Protocol
+
+   DHCP uses the BOOTP message format defined in RFC 951 and given in
+   table 1 and figure 1.  The 'op' field of each DHCP message sent from
+   a client to a server contains BOOTREQUEST. BOOTREPLY is used in the
+   'op' field of each DHCP message sent from a server to a client.
+
+   The first four octets of the 'options' field of the DHCP message
+   contain the (decimal) values 99, 130, 83 and 99, respectively (this
+   is the same magic cookie as is defined in RFC 1497 [17]).  The
+   remainder of the 'options' field consists of a list of tagged
+   parameters that are called "options".  All of the "vendor extensions"
+   listed in RFC 1497 are also DHCP options.  RFC 1533 gives the
+   complete set of options defined for use with DHCP.
+
+   Several options have been defined so far.  One particular option -
+   the "DHCP message type" option - must be included in every DHCP
+   message.  This option defines the "type" of the DHCP message.
+   Additional options may be allowed, required, or not allowed,
+   depending on the DHCP message type.
+
+   Throughout this document, DHCP messages that include a 'DHCP message
+   type' option will be referred to by the type of the message; e.g., a
+   DHCP message with 'DHCP message type' option type 1 will be referred
+   to as a "DHCPDISCOVER" message.
+
+3.1 Client-server interaction - allocating a network address
+
+   The following summary of the protocol exchanges between clients and
+   servers refers to the DHCP messages described in table 2.  The
+   timeline diagram in figure 3 shows the timing relationships in a
+   typical client-server interaction.  If the client already knows its
+   address, some steps may be omitted; this abbreviated interaction is
+   described in section 3.2.
+
+   1. The client broadcasts a DHCPDISCOVER message on its local physical
+      subnet.  The DHCPDISCOVER message MAY include options that suggest
+      values for the network address and lease duration.  BOOTP relay
+      agents may pass the message on to DHCP servers not on the same
+      physical subnet.
+
+   2. Each server may respond with a DHCPOFFER message that includes an
+      available network address in the 'yiaddr' field (and other
+      configuration parameters in DHCP options).  Servers need not
+      reserve the offered network address, although the protocol will
+      work more efficiently if the server avoids allocating the offered
+      network address to another client.  When allocating a new address,
+      servers SHOULD check that the offered network address is not
+
+
+
+Droms                       Standards Track                    [Page 13]
+\f
+RFC 2131          Dynamic Host Configuration Protocol         March 1997
+
+
+      already in use; e.g., the server may probe the offered address
+      with an ICMP Echo Request.  Servers SHOULD be implemented so that
+      network administrators MAY choose to disable probes of newly
+      allocated addresses.  The server transmits the DHCPOFFER message
+      to the client, using the BOOTP relay agent if necessary.
+
+   Message         Use
+   -------         ---
+
+   DHCPDISCOVER -  Client broadcast to locate available servers.
+
+   DHCPOFFER    -  Server to client in response to DHCPDISCOVER with
+                   offer of configuration parameters.
+
+   DHCPREQUEST  -  Client message to servers either (a) requesting
+                   offered parameters from one server and implicitly
+                   declining offers from all others, (b) confirming
+                   correctness of previously allocated address after,
+                   e.g., system reboot, or (c) extending the lease on a
+                   particular network address.
+
+   DHCPACK      -  Server to client with configuration parameters,
+                   including committed network address.
+
+   DHCPNAK      -  Server to client indicating client's notion of network
+                   address is incorrect (e.g., client has moved to new
+                   subnet) or client's lease as expired
+
+   DHCPDECLINE  -  Client to server indicating network address is already
+                   in use.
+
+   DHCPRELEASE  -  Client to server relinquishing network address and
+                   cancelling remaining lease.
+
+   DHCPINFORM   -  Client to server, asking only for local configuration
+                   parameters; client already has externally configured
+                   network address.
+
+                          Table 2:  DHCP messages
+
+
+
+
+
+
+
+
+
+
+
+
+Droms                       Standards Track                    [Page 14]
+\f
+RFC 2131          Dynamic Host Configuration Protocol         March 1997
+
+
+                Server          Client          Server
+            (not selected)                    (selected)
+
+                  v               v               v
+                  |               |               |
+                  |     Begins initialization     |
+                  |               |               |
+                  | _____________/|\____________  |
+                  |/DHCPDISCOVER | DHCPDISCOVER  \|
+                  |               |               |
+              Determines          |          Determines
+             configuration        |         configuration
+                  |               |               |
+                  |\             |  ____________/ |
+                  | \________    | /DHCPOFFER     |
+                  | DHCPOFFER\   |/               |
+                  |           \  |                |
+                  |       Collects replies        |
+                  |             \|                |
+                  |     Selects configuration     |
+                  |               |               |
+                  | _____________/|\____________  |
+                  |/ DHCPREQUEST  |  DHCPREQUEST\ |
+                  |               |               |
+                  |               |     Commits configuration
+                  |               |               |
+                  |               | _____________/|
+                  |               |/ DHCPACK      |
+                  |               |               |
+                  |    Initialization complete    |
+                  |               |               |
+                  .               .               .
+                  .               .               .
+                  |               |               |
+                  |      Graceful shutdown        |
+                  |               |               |
+                  |               |\ ____________ |
+                  |               | DHCPRELEASE  \|
+                  |               |               |
+                  |               |        Discards lease
+                  |               |               |
+                  v               v               v
+     Figure 3: Timeline diagram of messages exchanged between DHCP
+               client and servers when allocating a new network address
+
+
+
+
+
+
+
+Droms                       Standards Track                    [Page 15]
+\f
+RFC 2131          Dynamic Host Configuration Protocol         March 1997
+
+
+  3. The client receives one or more DHCPOFFER messages from one or more
+     servers.  The client may choose to wait for multiple responses.
+     The client chooses one server from which to request configuration
+     parameters, based on the configuration parameters offered in the
+     DHCPOFFER messages.  The client broadcasts a DHCPREQUEST message
+     that MUST include the 'server identifier' option to indicate which
+     server it has selected, and that MAY include other options
+     specifying desired configuration values.  The 'requested IP
+     address' option MUST be set to the value of 'yiaddr' in the
+     DHCPOFFER message from the server.  This DHCPREQUEST message is
+     broadcast and relayed through DHCP/BOOTP relay agents.  To help
+     ensure that any BOOTP relay agents forward the DHCPREQUEST message
+     to the same set of DHCP servers that received the original
+     DHCPDISCOVER message, the DHCPREQUEST message MUST use the same
+     value in the DHCP message header's 'secs' field and be sent to the
+     same IP broadcast address as the original DHCPDISCOVER message.
+     The client times out and retransmits the DHCPDISCOVER message if
+     the client receives no DHCPOFFER messages.
+
+  4. The servers receive the DHCPREQUEST broadcast from the client.
+     Those servers not selected by the DHCPREQUEST message use the
+     message as notification that the client has declined that server's
+     offer.  The server selected in the DHCPREQUEST message commits the
+     binding for the client to persistent storage and responds with a
+     DHCPACK message containing the configuration parameters for the
+     requesting client.  The combination of 'client identifier' or
+     'chaddr' and assigned network address constitute a unique
+     identifier for the client's lease and are used by both the client
+     and server to identify a lease referred to in any DHCP messages.
+     Any configuration parameters in the DHCPACK message SHOULD NOT
+     conflict with those in the earlier DHCPOFFER message to which the
+     client is responding.  The server SHOULD NOT check the offered
+     network address at this point. The 'yiaddr' field in the DHCPACK
+     messages is filled in with the selected network address.
+
+     If the selected server is unable to satisfy the DHCPREQUEST message
+     (e.g., the requested network address has been allocated), the
+     server SHOULD respond with a DHCPNAK message.
+
+     A server MAY choose to mark addresses offered to clients in
+     DHCPOFFER messages as unavailable.  The server SHOULD mark an
+     address offered to a client in a DHCPOFFER message as available if
+     the server receives no DHCPREQUEST message from that client.
+
+  5. The client receives the DHCPACK message with configuration
+     parameters.  The client SHOULD perform a final check on the
+     parameters (e.g., ARP for allocated network address), and notes the
+     duration of the lease specified in the DHCPACK message.  At this
+
+
+
+Droms                       Standards Track                    [Page 16]
+\f
+RFC 2131          Dynamic Host Configuration Protocol         March 1997
+
+
+     point, the client is configured.  If the client detects that the
+     address is already in use (e.g., through the use of ARP), the
+     client MUST send a DHCPDECLINE message to the server and restarts
+     the configuration process.  The client SHOULD wait a minimum of ten
+     seconds before restarting the configuration process to avoid
+     excessive network traffic in case of looping.
+
+     If the client receives a DHCPNAK message, the client restarts the
+     configuration process.
+
+     The client times out and retransmits the DHCPREQUEST message if the
+     client receives neither a DHCPACK or a DHCPNAK message.  The client
+     retransmits the DHCPREQUEST according to the retransmission
+     algorithm in section 4.1.  The client should choose to retransmit
+     the DHCPREQUEST enough times to give adequate probability of
+     contacting the server without causing the client (and the user of
+     that client) to wait overly long before giving up; e.g., a client
+     retransmitting as described in section 4.1 might retransmit the
+     DHCPREQUEST message four times, for a total delay of 60 seconds,
+     before restarting the initialization procedure.  If the client
+     receives neither a DHCPACK or a DHCPNAK message after employing the
+     retransmission algorithm, the client reverts to INIT state and
+     restarts the initialization process.  The client SHOULD notify the
+     user that the initialization process has failed and is restarting.
+
+  6. The client may choose to relinquish its lease on a network address
+     by sending a DHCPRELEASE message to the server.  The client
+     identifies the lease to be released with its 'client identifier',
+     or 'chaddr' and network address in the DHCPRELEASE message. If the
+     client used a 'client identifier' when it obtained the lease, it
+     MUST use the same 'client identifier' in the DHCPRELEASE message.
+
+3.2 Client-server interaction - reusing a previously allocated network
+    address
+
+   If a client remembers and wishes to reuse a previously allocated
+   network address, a client may choose to omit some of the steps
+   described in the previous section.  The timeline diagram in figure 4
+   shows the timing relationships in a typical client-server interaction
+   for a client reusing a previously allocated network address.
+
+
+
+
+
+
+
+
+
+
+
+Droms                       Standards Track                    [Page 17]
+\f
+RFC 2131          Dynamic Host Configuration Protocol         March 1997
+
+
+   1. The client broadcasts a DHCPREQUEST message on its local subnet.
+      The message includes the client's network address in the
+      'requested IP address' option. As the client has not received its
+      network address, it MUST NOT fill in the 'ciaddr' field. BOOTP
+      relay agents pass the message on to DHCP servers not on the same
+      subnet.  If the client used a 'client identifier' to obtain its
+      address, the client MUST use the same 'client identifier' in the
+      DHCPREQUEST message.
+
+   2. Servers with knowledge of the client's configuration parameters
+      respond with a DHCPACK message to the client.  Servers SHOULD NOT
+      check that the client's network address is already in use; the
+      client may respond to ICMP Echo Request messages at this point.
+
+                Server          Client          Server
+
+                  v               v               v
+                  |                |               |
+                  |              Begins            |
+                  |          initialization        |
+                  |                |               |
+                  |                /|\             |
+                  |   _________ __/ | \__________  |
+                  | /DHCPREQU EST  |  DHCPREQUEST\ |
+                  |/               |              \|
+                  |                |               |
+               Locates             |            Locates
+            configuration          |         configuration
+                  |                |               |
+                  |\               |              /|
+                  | \              |  ___________/ |
+                  |  \             | /  DHCPACK    |
+                  |   \ _______    |/              |
+                  |     DHCPACK\   |               |
+                  |          Initialization        |
+                  |             complete           |
+                  |               \|               |
+                  |                |               |
+                  |           (Subsequent          |
+                  |             DHCPACKS           |
+                  |             ignored)           |
+                  |                |               |
+                  |                |               |
+                  v                v               v
+
+     Figure 4: Timeline diagram of messages exchanged between DHCP
+               client and servers when reusing a previously allocated
+               network address
+
+
+
+Droms                       Standards Track                    [Page 18]
+\f
+RFC 2131          Dynamic Host Configuration Protocol         March 1997
+
+
+      If the client's request is invalid (e.g., the client has moved
+      to a new subnet), servers SHOULD respond with a DHCPNAK message to
+      the client. Servers SHOULD NOT respond if their information is not
+      guaranteed to be accurate.  For example, a server that identifies a
+      request for an expired binding that is owned by another server SHOULD
+      NOT respond with a DHCPNAK unless the servers are using an explicit
+      mechanism to maintain coherency among the servers.
+
+      If 'giaddr' is 0x0 in the DHCPREQUEST message, the client is on
+      the same subnet as the server.  The server MUST
+      broadcast the DHCPNAK message to the 0xffffffff broadcast address
+      because the client may not have a correct network address or subnet
+      mask, and the client may not be answering ARP requests.
+      Otherwise, the server MUST send the DHCPNAK message to the IP
+      address of the BOOTP relay agent, as recorded in 'giaddr'.  The
+      relay agent will, in turn, forward the message directly to the
+      client's hardware address, so that the DHCPNAK can be delivered even
+      if the client has moved to a new network.
+
+   3. The client receives the DHCPACK message with configuration
+      parameters.  The client performs a final check on the parameters
+      (as in section 3.1), and notes the duration of the lease specified
+      in the DHCPACK message.  The specific lease is implicitly identified
+      by the 'client identifier' or 'chaddr' and the network address.  At
+      this point, the client is configured.
+
+      If the client detects that the IP address in the DHCPACK message
+      is already in use, the client MUST send a DHCPDECLINE message to the
+      server and restarts the configuration process by requesting a
+      new network address.  This action corresponds to the client
+      moving to the INIT state in the DHCP state diagram, which is
+      described in section 4.4.
+
+      If the client receives a DHCPNAK message, it cannot reuse its
+      remembered network address.  It must instead request a new
+      address by restarting the configuration process, this time
+      using the (non-abbreviated) procedure described in section
+      3.1.  This action also corresponds to the client moving to
+      the INIT state in the DHCP state diagram.
+
+      The client times out and retransmits the DHCPREQUEST message if
+      the client receives neither a DHCPACK nor a DHCPNAK message.  The
+      client retransmits the DHCPREQUEST according to the retransmission
+      algorithm in section 4.1.  The client should choose to retransmit
+      the DHCPREQUEST enough times to give adequate probability of
+      contacting the server without causing the client (and the user of
+      that client) to wait overly long before giving up; e.g., a client
+      retransmitting as described in section 4.1 might retransmit the
+
+
+
+Droms                       Standards Track                    [Page 19]
+\f
+RFC 2131          Dynamic Host Configuration Protocol         March 1997
+
+
+      DHCPREQUEST message four times, for a total delay of 60 seconds,
+      before restarting the initialization procedure.  If the client
+      receives neither a DHCPACK or a DHCPNAK message after employing
+      the retransmission algorithm, the client MAY choose to use the
+      previously allocated network address and configuration parameters
+      for the remainder of the unexpired lease.  This corresponds to
+      moving to BOUND state in the client state transition diagram shown
+      in figure 5.
+
+   4. The client may choose to relinquish its lease on a network
+      address by sending a DHCPRELEASE message to the server.  The
+      client identifies the lease to be released with its
+      'client identifier', or 'chaddr' and network address in the
+      DHCPRELEASE message.
+
+      Note that in this case, where the client retains its network
+      address locally, the client will not normally relinquish its
+      lease during a graceful shutdown.  Only in the case where the
+      client explicitly needs to relinquish its lease, e.g., the client
+      is about to be moved to a different subnet, will the client send
+      a DHCPRELEASE message.
+
+3.3 Interpretation and representation of time values
+
+   A client acquires a lease for a network address for a fixed period of
+   time (which may be infinite).  Throughout the protocol, times are to
+   be represented in units of seconds.  The time value of 0xffffffff is
+   reserved to represent "infinity".
+
+   As clients and servers may not have synchronized clocks, times are
+   represented in DHCP messages as relative times, to be interpreted
+   with respect to the client's local clock.  Representing relative
+   times in units of seconds in an unsigned 32 bit word gives a range of
+   relative times from 0 to approximately 100 years, which is sufficient
+   for the relative times to be measured using DHCP.
+
+   The algorithm for lease duration interpretation given in the previous
+   paragraph assumes that client and server clocks are stable relative
+   to each other.  If there is drift between the two clocks, the server
+   may consider the lease expired before the client does.  To
+   compensate, the server may return a shorter lease duration to the
+   client than the server commits to its local database of client
+   information.
+
+3.4 Obtaining parameters with externally configured network address
+
+   If a client has obtained a network address through some other means
+   (e.g., manual configuration), it may use a DHCPINFORM request message
+
+
+
+Droms                       Standards Track                    [Page 20]
+\f
+RFC 2131          Dynamic Host Configuration Protocol         March 1997
+
+
+   to obtain other local configuration parameters.  Servers receiving a
+   DHCPINFORM message construct a DHCPACK message with any local
+   configuration parameters appropriate for the client without:
+   allocating a new address, checking for an existing binding, filling
+   in 'yiaddr' or including lease time parameters.  The servers SHOULD
+   unicast the DHCPACK reply to the address given in the 'ciaddr' field
+   of the DHCPINFORM message.
+
+   The server SHOULD check the network address in a DHCPINFORM message
+   for consistency, but MUST NOT check for an existing lease.  The
+   server forms a DHCPACK message containing the configuration
+   parameters for the requesting client and sends the DHCPACK message
+   directly to the client.
+
+3.5 Client parameters in DHCP
+
+   Not all clients require initialization of all parameters listed in
+   Appendix A.  Two techniques are used to reduce the number of
+   parameters transmitted from the server to the client.  First, most of
+   the parameters have defaults defined in the Host Requirements RFCs;
+   if the client receives no parameters from the server that override
+   the defaults, a client uses those default values.  Second, in its
+   initial DHCPDISCOVER or DHCPREQUEST message, a client may provide the
+   server with a list of specific parameters the client is interested
+   in.  If the client includes a list of parameters in a DHCPDISCOVER
+   message, it MUST include that list in any subsequent DHCPREQUEST
+   messages.
+
+   The client SHOULD include the 'maximum DHCP message size' option to
+   let the server know how large the server may make its DHCP messages.
+   The parameters returned to a client may still exceed the space
+   allocated to options in a DHCP message.  In this case, two additional
+   options flags (which must appear in the 'options' field of the
+   message) indicate that the 'file' and 'sname' fields are to be used
+   for options.
+
+   The client can inform the server which configuration parameters the
+   client is interested in by including the 'parameter request list'
+   option.  The data portion of this option explicitly lists the options
+   requested by tag number.
+
+   In addition, the client may suggest values for the network address
+   and lease time in the DHCPDISCOVER message.  The client may include
+   the 'requested IP address' option to suggest that a particular IP
+   address be assigned, and may include the 'IP address lease time'
+   option to suggest the lease time it would like.  Other options
+   representing "hints" at configuration parameters are allowed in a
+   DHCPDISCOVER or DHCPREQUEST message.  However, additional options may
+
+
+
+Droms                       Standards Track                    [Page 21]
+\f
+RFC 2131          Dynamic Host Configuration Protocol         March 1997
+
+
+   be ignored by servers, and multiple servers may, therefore, not
+   return identical values for some options.  The 'requested IP address'
+   option is to be filled in only in a DHCPREQUEST message when the
+   client is verifying network parameters obtained previously. The
+   client fills in the 'ciaddr' field only when correctly configured
+   with an IP address in BOUND, RENEWING or REBINDING state.
+
+   If a server receives a DHCPREQUEST message with an invalid 'requested
+   IP address', the server SHOULD respond to the client with a DHCPNAK
+   message and may choose to report the problem to the system
+   administrator.  The server may include an error message in the
+   'message' option.
+
+3.6 Use of DHCP in clients with multiple interfaces
+
+   A client with multiple network interfaces must use DHCP through each
+   interface independently to obtain configuration information
+   parameters for those separate interfaces.
+
+3.7 When clients should use DHCP
+
+   A client SHOULD use DHCP to reacquire or verify its IP address and
+   network parameters whenever the local network parameters may have
+   changed; e.g., at system boot time or after a disconnection from the
+   local network, as the local network configuration may change without
+   the client's or user's knowledge.
+
+   If a client has knowledge of a previous network address and is unable
+   to contact a local DHCP server, the client may continue to use the
+   previous network address until the lease for that address expires.
+   If the lease expires before the client can contact a DHCP server, the
+   client must immediately discontinue use of the previous network
+   address and may inform local users of the problem.
+
+4. Specification of the DHCP client-server protocol
+
+   In this section, we assume that a DHCP server has a block of network
+   addresses from which it can satisfy requests for new addresses.  Each
+   server also maintains a database of allocated addresses and leases in
+   local permanent storage.
+
+4.1 Constructing and sending DHCP messages
+
+   DHCP clients and servers both construct DHCP messages by filling in
+   fields in the fixed format section of the message and appending
+   tagged data items in the variable length option area.  The options
+   area includes first a four-octet 'magic cookie' (which was described
+   in section 3), followed by the options.  The last option must always
+
+
+
+Droms                       Standards Track                    [Page 22]
+\f
+RFC 2131          Dynamic Host Configuration Protocol         March 1997
+
+
+   be the 'end' option.
+
+   DHCP uses UDP as its transport protocol.  DHCP messages from a client
+   to a server are sent to the 'DHCP server' port (67), and DHCP
+   messages from a server to a client are sent to the 'DHCP client' port
+   (68). A server with multiple network address (e.g., a multi-homed
+   host) MAY use any of its network addresses in outgoing DHCP messages.
+
+   The 'server identifier' field is used both to identify a DHCP server
+   in a DHCP message and as a destination address from clients to
+   servers.  A server with multiple network addresses MUST be prepared
+   to to accept any of its network addresses as identifying that server
+   in a DHCP message.  To accommodate potentially incomplete network
+   connectivity, a server MUST choose an address as a 'server
+   identifier' that, to the best of the server's knowledge, is reachable
+   from the client.  For example, if the DHCP server and the DHCP client
+   are connected to the same subnet (i.e., the 'giaddr' field in the
+   message from the client is zero), the server SHOULD select the IP
+   address the server is using for communication on that subnet as the
+   'server identifier'.  If the server is using multiple IP addresses on
+   that subnet, any such address may be used.  If the server has
+   received a message through a DHCP relay agent, the server SHOULD
+   choose an address from the interface on which the message was
+   recieved as the 'server identifier' (unless the server has other,
+   better information on which to make its choice).  DHCP clients MUST
+   use the IP address provided in the 'server identifier' option for any
+   unicast requests to the DHCP server.
+
+   DHCP messages broadcast by a client prior to that client obtaining
+   its IP address must have the source address field in the IP header
+   set to 0.
+
+   If the 'giaddr' field in a DHCP message from a client is non-zero,
+   the server sends any return messages to the 'DHCP server' port on the
+   BOOTP relay agent whose address appears in 'giaddr'. If the 'giaddr'
+   field is zero and the 'ciaddr' field is nonzero, then the server
+   unicasts DHCPOFFER and DHCPACK messages to the address in 'ciaddr'.
+   If 'giaddr' is zero and 'ciaddr' is zero, and the broadcast bit is
+   set, then the server broadcasts DHCPOFFER and DHCPACK messages to
+   0xffffffff. If the broadcast bit is not set and 'giaddr' is zero and
+   'ciaddr' is zero, then the server unicasts DHCPOFFER and DHCPACK
+   messages to the client's hardware address and 'yiaddr' address.  In
+   all cases, when 'giaddr' is zero, the server broadcasts any DHCPNAK
+   messages to 0xffffffff.
+
+   If the options in a DHCP message extend into the 'sname' and 'file'
+   fields, the 'option overload' option MUST appear in the 'options'
+   field, with value 1, 2 or 3, as specified in RFC 1533.  If the
+
+
+
+Droms                       Standards Track                    [Page 23]
+\f
+RFC 2131          Dynamic Host Configuration Protocol         March 1997
+
+
+   'option overload' option is present in the 'options' field, the
+   options in the 'options' field MUST be terminated by an 'end' option,
+   and MAY contain one or more 'pad' options to fill the options field.
+   The options in the 'sname' and 'file' fields (if in use as indicated
+   by the 'options overload' option) MUST begin with the first octet of
+   the field, MUST be terminated by an 'end' option, and MUST be
+   followed by 'pad' options to fill the remainder of the field.  Any
+   individual option in the 'options', 'sname' and 'file' fields MUST be
+   entirely contained in that field.  The options in the 'options' field
+   MUST be interpreted first, so that any 'option overload' options may
+   be interpreted.  The 'file' field MUST be interpreted next (if the
+   'option overload' option indicates that the 'file' field contains
+   DHCP options), followed by the 'sname' field.
+
+   The values to be passed in an 'option' tag may be too long to fit in
+   the 255 octets available to a single option (e.g., a list of routers
+   in a 'router' option [21]).  Options may appear only once, unless
+   otherwise specified in the options document.  The client concatenates
+   the values of multiple instances of the same option into a single
+   parameter list for configuration.
+
+   DHCP clients are responsible for all message retransmission.  The
+   client MUST adopt a retransmission strategy that incorporates a
+   randomized exponential backoff algorithm to determine the delay
+   between retransmissions.  The delay between retransmissions SHOULD be
+   chosen to allow sufficient time for replies from the server to be
+   delivered based on the characteristics of the internetwork between
+   the client and the server.  For example, in a 10Mb/sec Ethernet
+   internetwork, the delay before the first retransmission SHOULD be 4
+   seconds randomized by the value of a uniform random number chosen
+   from the range -1 to +1.  Clients with clocks that provide resolution
+   granularity of less than one second may choose a non-integer
+   randomization value.  The delay before the next retransmission SHOULD
+   be 8 seconds randomized by the value of a uniform number chosen from
+   the range -1 to +1.  The retransmission delay SHOULD be doubled with
+   subsequent retransmissions up to a maximum of 64 seconds.  The client
+   MAY provide an indication of retransmission attempts to the user as
+   an indication of the progress of the configuration process.
+
+   The 'xid' field is used by the client to match incoming DHCP messages
+   with pending requests.  A DHCP client MUST choose 'xid's in such a
+   way as to minimize the chance of using an 'xid' identical to one used
+   by another client. For example, a client may choose a different,
+   random initial 'xid' each time the client is rebooted, and
+   subsequently use sequential 'xid's until the next reboot.  Selecting
+   a new 'xid' for each retransmission is an implementation decision.  A
+   client may choose to reuse the same 'xid' or select a new 'xid' for
+   each retransmitted message.
+
+
+
+Droms                       Standards Track                    [Page 24]
+\f
+RFC 2131          Dynamic Host Configuration Protocol         March 1997
+
+
+   Normally, DHCP servers and BOOTP relay agents attempt to deliver
+   DHCPOFFER, DHCPACK and DHCPNAK messages directly to the client using
+   uicast delivery.  The IP destination address (in the IP header) is
+   set to the DHCP 'yiaddr' address and the link-layer destination
+   address is set to the DHCP 'chaddr' address.  Unfortunately, some
+   client implementations are unable to receive such unicast IP
+   datagrams until the implementation has been configured with a valid
+   IP address (leading to a deadlock in which the client's IP address
+   cannot be delivered until the client has been configured with an IP
+   address).
+
+   A client that cannot receive unicast IP datagrams until its protocol
+   software has been configured with an IP address SHOULD set the
+   BROADCAST bit in the 'flags' field to 1 in any DHCPDISCOVER or
+   DHCPREQUEST messages that client sends.  The BROADCAST bit will
+   provide a hint to the DHCP server and BOOTP relay agent to broadcast
+   any messages to the client on the client's subnet.  A client that can
+   receive unicast IP datagrams before its protocol software has been
+   configured SHOULD clear the BROADCAST bit to 0.  The BOOTP
+   clarifications document discusses the ramifications of the use of the
+   BROADCAST bit [21].
+
+   A server or relay agent sending or relaying a DHCP message directly
+   to a DHCP client (i.e., not to a relay agent specified in the
+   'giaddr' field) SHOULD examine the BROADCAST bit in the 'flags'
+   field.  If this bit is set to 1, the DHCP message SHOULD be sent as
+   an IP broadcast using an IP broadcast address (preferably 0xffffffff)
+   as the IP destination address and the link-layer broadcast address as
+   the link-layer destination address.  If the BROADCAST bit is cleared
+   to 0, the message SHOULD be sent as an IP unicast to the IP address
+   specified in the 'yiaddr' field and the link-layer address specified
+   in the 'chaddr' field.  If unicasting is not possible, the message
+   MAY be sent as an IP broadcast using an IP broadcast address
+   (preferably 0xffffffff) as the IP destination address and the link-
+   layer broadcast address as the link-layer destination address.
+
+4.2 DHCP server administrative controls
+
+   DHCP servers are not required to respond to every DHCPDISCOVER and
+   DHCPREQUEST message they receive.  For example, a network
+   administrator, to retain stringent control over the clients attached
+   to the network, may choose to configure DHCP servers to respond only
+   to clients that have been previously registered through some external
+   mechanism.  The DHCP specification describes only the interactions
+   between clients and servers when the clients and servers choose to
+   interact; it is beyond the scope of the DHCP specification to
+   describe all of the administrative controls that system
+   administrators might want to use.  Specific DHCP server
+
+
+
+Droms                       Standards Track                    [Page 25]
+\f
+RFC 2131          Dynamic Host Configuration Protocol         March 1997
+
+
+   implementations may incorporate any controls or policies desired by a
+   network administrator.
+
+   In some environments, a DHCP server will have to consider the values
+   of the vendor class options included in DHCPDISCOVER or DHCPREQUEST
+   messages when determining the correct parameters for a particular
+   client.
+
+   A DHCP server needs to use some unique identifier to associate a
+   client with its lease.  The client MAY choose to explicitly provide
+   the identifier through the 'client identifier' option.  If the client
+   supplies a 'client identifier', the client MUST use the same 'client
+   identifier' in all subsequent messages, and the server MUST use that
+   identifier to identify the client.  If the client does not provide a
+   'client identifier' option, the server MUST use the contents of the
+   'chaddr' field to identify the client. It is crucial for a DHCP
+   client to use an identifier unique within the subnet to which the
+   client is attached in the 'client identifier' option.  Use of
+   'chaddr' as the client's unique identifier may cause unexpected
+   results, as that identifier may be associated with a hardware
+   interface that could be moved to a new client.  Some sites may choose
+   to use a manufacturer's serial number as the 'client identifier', to
+   avoid unexpected changes in a clients network address due to transfer
+   of hardware interfaces among computers.  Sites may also choose to use
+   a DNS name as the 'client identifier', causing address leases to be
+   associated with the DNS name rather than a specific hardware box.
+
+   DHCP clients are free to use any strategy in selecting a DHCP server
+   among those from which the client receives a DHCPOFFER message.  The
+   client implementation of DHCP SHOULD provide a mechanism for the user
+   to select directly the 'vendor class identifier' values.
+
+4.3 DHCP server behavior
+
+   A DHCP server processes incoming DHCP messages from a client based on
+   the current state of the binding for that client.  A DHCP server can
+   receive the following messages from a client:
+
+      o DHCPDISCOVER
+
+      o DHCPREQUEST
+
+      o DHCPDECLINE
+
+      o DHCPRELEASE
+
+      o DHCPINFORM
+
+
+
+
+Droms                       Standards Track                    [Page 26]
+\f
+RFC 2131          Dynamic Host Configuration Protocol         March 1997
+
+
+   Table 3 gives the use of the fields and options in a DHCP message by
+   a server.  The remainder of this section describes the action of the
+   DHCP server for each possible incoming message.
+
+4.3.1 DHCPDISCOVER message
+
+   When a server receives a DHCPDISCOVER message from a client, the
+   server chooses a network address for the requesting client.  If no
+   address is available, the server may choose to report the problem to
+   the system administrator. If an address is available, the new address
+   SHOULD be chosen as follows:
+
+      o The client's current address as recorded in the client's current
+        binding, ELSE
+
+      o The client's previous address as recorded in the client's (now
+        expired or released) binding, if that address is in the server's
+        pool of available addresses and not already allocated, ELSE
+
+      o The address requested in the 'Requested IP Address' option, if that
+        address is valid and not already allocated, ELSE
+
+      o A new address allocated from the server's pool of available
+        addresses; the address is selected based on the subnet from which
+        the message was received (if 'giaddr' is 0) or on the address of
+        the relay agent that forwarded the message ('giaddr' when not 0).
+
+   As described in section 4.2, a server MAY, for administrative
+   reasons, assign an address other than the one requested, or may
+   refuse to allocate an address to a particular client even though free
+   addresses are available.
+
+   Note that, in some network architectures (e.g., internets with more
+   than one IP subnet assigned to a physical network segment), it may be
+   the case that the DHCP client should be assigned an address from a
+   different subnet than the address recorded in 'giaddr'.  Thus, DHCP
+   does not require that the client be assigned as address from the
+   subnet in 'giaddr'.  A server is free to choose some other subnet,
+   and it is beyond the scope of the DHCP specification to describe ways
+   in which the assigned IP address might be chosen.
+
+   While not required for correct operation of DHCP, the server SHOULD
+   NOT reuse the selected network address before the client responds to
+   the server's DHCPOFFER message.  The server may choose to record the
+   address as offered to the client.
+
+   The server must also choose an expiration time for the lease, as
+   follows:
+
+
+
+Droms                       Standards Track                    [Page 27]
+\f
+RFC 2131          Dynamic Host Configuration Protocol         March 1997
+
+
+   o IF the client has not requested a specific lease in the
+     DHCPDISCOVER message and the client already has an assigned network
+     address, the server returns the lease expiration time previously
+     assigned to that address (note that the client must explicitly
+     request a specific lease to extend the expiration time on a
+     previously assigned address), ELSE
+
+   o IF the client has not requested a specific lease in the
+     DHCPDISCOVER message and the client does not have an assigned
+     network address, the server assigns a locally configured default
+     lease time, ELSE
+
+   o IF the client has requested a specific lease in the DHCPDISCOVER
+     message (regardless of whether the client has an assigned network
+     address), the server may choose either to return the requested
+     lease (if the lease is acceptable to local policy) or select
+     another lease.
+
+Field      DHCPOFFER            DHCPACK             DHCPNAK
+-----      ---------            -------             -------
+'op'       BOOTREPLY            BOOTREPLY           BOOTREPLY
+'htype'    (From "Assigned Numbers" RFC)
+'hlen'     (Hardware address length in octets)
+'hops'     0                    0                   0
+'xid'      'xid' from client    'xid' from client   'xid' from client
+           DHCPDISCOVER         DHCPREQUEST         DHCPREQUEST
+           message              message             message
+'secs'     0                    0                   0
+'ciaddr'   0                    'ciaddr' from       0
+                                DHCPREQUEST or 0
+'yiaddr'   IP address offered   IP address          0
+           to client            assigned to client
+'siaddr'   IP address of next   IP address of next  0
+           bootstrap server     bootstrap server
+'flags'    'flags' from         'flags' from        'flags' from
+           client DHCPDISCOVER  client DHCPREQUEST  client DHCPREQUEST
+           message              message             message
+'giaddr'   'giaddr' from        'giaddr' from       'giaddr' from
+           client DHCPDISCOVER  client DHCPREQUEST  client DHCPREQUEST
+           message              message             message
+'chaddr'   'chaddr' from        'chaddr' from       'chaddr' from
+           client DHCPDISCOVER  client DHCPREQUEST  client DHCPREQUEST
+           message              message             message
+'sname'    Server host name     Server host name    (unused)
+           or options           or options
+'file'     Client boot file     Client boot file    (unused)
+           name or options      name or options
+'options'  options              options
+
+
+
+Droms                       Standards Track                    [Page 28]
+\f
+RFC 2131          Dynamic Host Configuration Protocol         March 1997
+
+
+Option                    DHCPOFFER    DHCPACK            DHCPNAK
+------                    ---------    -------            -------
+Requested IP address      MUST NOT     MUST NOT           MUST NOT
+IP address lease time     MUST         MUST (DHCPREQUEST) MUST NOT
+                                       MUST NOT (DHCPINFORM)
+Use 'file'/'sname' fields MAY          MAY                MUST NOT
+DHCP message type         DHCPOFFER    DHCPACK            DHCPNAK
+Parameter request list    MUST NOT     MUST NOT           MUST NOT
+Message                   SHOULD       SHOULD             SHOULD
+Client identifier         MUST NOT     MUST NOT           MAY
+Vendor class identifier   MAY          MAY                MAY
+Server identifier         MUST         MUST               MUST
+Maximum message size      MUST NOT     MUST NOT           MUST NOT
+All others                MAY          MAY                MUST NOT
+
+           Table 3:  Fields and options used by DHCP servers
+
+   Once the network address and lease have been determined, the server
+   constructs a DHCPOFFER message with the offered configuration
+   parameters.  It is important for all DHCP servers to return the same
+   parameters (with the possible exception of a newly allocated network
+   address) to ensure predictable client behavior regardless of which
+   server the client selects.  The configuration parameters MUST be
+   selected by applying the following rules in the order given below.
+   The network administrator is responsible for configuring multiple
+   DHCP servers to ensure uniform responses from those servers.  The
+   server MUST return to the client:
+
+   o The client's network address, as determined by the rules given
+     earlier in this section,
+
+   o The expiration time for the client's lease, as determined by the
+     rules given earlier in this section,
+
+   o Parameters requested by the client, according to the following
+     rules:
+
+        -- IF the server has been explicitly configured with a default
+           value for the parameter, the server MUST include that value
+           in an appropriate option in the 'option' field, ELSE
+
+        -- IF the server recognizes the parameter as a parameter
+           defined in the Host Requirements Document, the server MUST
+           include the default value for that parameter as given in the
+           Host Requirements Document in an appropriate option in the
+           'option' field, ELSE
+
+        -- The server MUST NOT return a value for that parameter,
+
+
+
+Droms                       Standards Track                    [Page 29]
+\f
+RFC 2131          Dynamic Host Configuration Protocol         March 1997
+
+
+     The server MUST supply as many of the requested parameters as
+     possible and MUST omit any parameters it cannot provide.  The
+     server MUST include each requested parameter only once unless
+     explicitly allowed in the DHCP Options and BOOTP Vendor
+     Extensions document.
+
+   o Any parameters from the existing binding that differ from the Host
+     Requirements Document defaults,
+
+   o Any parameters specific to this client (as identified by
+     the contents of 'chaddr' or 'client identifier' in the DHCPDISCOVER
+     or DHCPREQUEST message), e.g., as configured by the network
+     administrator,
+
+   o Any parameters specific to this client's class (as identified
+     by the contents of the 'vendor class identifier'
+     option in the DHCPDISCOVER or DHCPREQUEST message),
+     e.g., as configured by the network administrator; the parameters
+     MUST be identified by an exact match between the client's vendor
+     class identifiers and the client's classes identified in the
+     server,
+
+   o Parameters with non-default values on the client's subnet.
+
+   The server MAY choose to return the 'vendor class identifier' used to
+   determine the parameters in the DHCPOFFER message to assist the
+   client in selecting which DHCPOFFER to accept.  The server inserts
+   the 'xid' field from the DHCPDISCOVER message into the 'xid' field of
+   the DHCPOFFER message and sends the DHCPOFFER message to the
+   requesting client.
+
+4.3.2 DHCPREQUEST message
+
+   A DHCPREQUEST message may come from a client responding to a
+   DHCPOFFER message from a server, from a client verifying a previously
+   allocated IP address or from a client extending the lease on a
+   network address.  If the DHCPREQUEST message contains a 'server
+   identifier' option, the message is in response to a DHCPOFFER
+   message.  Otherwise, the message is a request to verify or extend an
+   existing lease.  If the client uses a 'client identifier' in a
+   DHCPREQUEST message, it MUST use that same 'client identifier' in all
+   subsequent messages. If the client included a list of requested
+   parameters in a DHCPDISCOVER message, it MUST include that list in
+   all subsequent messages.
+
+
+
+
+
+
+
+Droms                       Standards Track                    [Page 30]
+\f
+RFC 2131          Dynamic Host Configuration Protocol         March 1997
+
+
+   Any configuration parameters in the DHCPACK message SHOULD NOT
+   conflict with those in the earlier DHCPOFFER message to which the
+   client is responding.  The client SHOULD use the parameters in the
+   DHCPACK message for configuration.
+
+   Clients send DHCPREQUEST messages as follows:
+
+   o DHCPREQUEST generated during SELECTING state:
+
+      Client inserts the address of the selected server in 'server
+      identifier', 'ciaddr' MUST be zero, 'requested IP address' MUST be
+      filled in with the yiaddr value from the chosen DHCPOFFER.
+
+      Note that the client may choose to collect several DHCPOFFER
+      messages and select the "best" offer.  The client indicates its
+      selection by identifying the offering server in the DHCPREQUEST
+      message.  If the client receives no acceptable offers, the client
+      may choose to try another DHCPDISCOVER message.  Therefore, the
+      servers may not receive a specific DHCPREQUEST from which they can
+      decide whether or not the client has accepted the offer.  Because
+      the servers have not committed any network address assignments on
+      the basis of a DHCPOFFER, servers are free to reuse offered
+      network addresses in response to subsequent requests.  As an
+      implementation detail, servers SHOULD NOT reuse offered addresses
+      and may use an implementation-specific timeout mechanism to decide
+      when to reuse an offered address.
+
+   o DHCPREQUEST generated during INIT-REBOOT state:
+
+      'server identifier' MUST NOT be filled in, 'requested IP address'
+      option MUST be filled in with client's notion of its previously
+      assigned address. 'ciaddr' MUST be zero. The client is seeking to
+      verify a previously allocated, cached configuration. Server SHOULD
+      send a DHCPNAK message to the client if the 'requested IP address'
+      is incorrect, or is on the wrong network.
+
+      Determining whether a client in the INIT-REBOOT state is on the
+      correct network is done by examining the contents of 'giaddr', the
+      'requested IP address' option, and a database lookup. If the DHCP
+      server detects that the client is on the wrong net (i.e., the
+      result of applying the local subnet mask or remote subnet mask (if
+      'giaddr' is not zero) to 'requested IP address' option value
+      doesn't match reality), then the server SHOULD send a DHCPNAK
+      message to the client.
+
+
+
+
+
+
+
+Droms                       Standards Track                    [Page 31]
+\f
+RFC 2131          Dynamic Host Configuration Protocol         March 1997
+
+
+      If the network is correct, then the DHCP server should check if
+      the client's notion of its IP address is correct. If not, then the
+      server SHOULD send a DHCPNAK message to the client. If the DHCP
+      server has no record of this client, then it MUST remain silent,
+      and MAY output a warning to the network administrator. This
+      behavior is necessary for peaceful coexistence of non-
+      communicating DHCP servers on the same wire.
+
+      If 'giaddr' is 0x0 in the DHCPREQUEST message, the client is on
+      the same subnet as the server.  The server MUST broadcast the
+      DHCPNAK message to the 0xffffffff broadcast address because the
+      client may not have a correct network address or subnet mask, and
+      the client may not be answering ARP requests.
+
+      If 'giaddr' is set in the DHCPREQUEST message, the client is on a
+      different subnet.  The server MUST set the broadcast bit in the
+      DHCPNAK, so that the relay agent will broadcast the DHCPNAK to the
+      client, because the client may not have a correct network address
+      or subnet mask, and the client may not be answering ARP requests.
+
+   o DHCPREQUEST generated during RENEWING state:
+
+      'server identifier' MUST NOT be filled in, 'requested IP address'
+      option MUST NOT be filled in, 'ciaddr' MUST be filled in with
+      client's IP address. In this situation, the client is completely
+      configured, and is trying to extend its lease. This message will
+      be unicast, so no relay agents will be involved in its
+      transmission.  Because 'giaddr' is therefore not filled in, the
+      DHCP server will trust the value in 'ciaddr', and use it when
+      replying to the client.
+
+      A client MAY choose to renew or extend its lease prior to T1.  The
+      server may choose not to extend the lease (as a policy decision by
+      the network administrator), but should return a DHCPACK message
+      regardless.
+
+   o DHCPREQUEST generated during REBINDING state:
+
+      'server identifier' MUST NOT be filled in, 'requested IP address'
+      option MUST NOT be filled in, 'ciaddr' MUST be filled in with
+      client's IP address. In this situation, the client is completely
+      configured, and is trying to extend its lease. This message MUST
+      be broadcast to the 0xffffffff IP broadcast address.  The DHCP
+      server SHOULD check 'ciaddr' for correctness before replying to
+      the DHCPREQUEST.
+
+
+
+
+
+
+Droms                       Standards Track                    [Page 32]
+\f
+RFC 2131          Dynamic Host Configuration Protocol         March 1997
+
+
+      The DHCPREQUEST from a REBINDING client is intended to accommodate
+      sites that have multiple DHCP servers and a mechanism for
+      maintaining consistency among leases managed by multiple servers.
+      A DHCP server MAY extend a client's lease only if it has local
+      administrative authority to do so.
+
+4.3.3 DHCPDECLINE message
+
+   If the server receives a DHCPDECLINE message, the client has
+   discovered through some other means that the suggested network
+   address is already in use.  The server MUST mark the network address
+   as not available and SHOULD notify the local system administrator of
+   a possible configuration problem.
+
+4.3.4 DHCPRELEASE message
+
+   Upon receipt of a DHCPRELEASE message, the server marks the network
+   address as not allocated.  The server SHOULD retain a record of the
+   client's initialization parameters for possible reuse in response to
+   subsequent requests from the client.
+
+4.3.5 DHCPINFORM message
+
+   The server responds to a DHCPINFORM message by sending a DHCPACK
+   message directly to the address given in the 'ciaddr' field of the
+   DHCPINFORM message.  The server MUST NOT send a lease expiration time
+   to the client and SHOULD NOT fill in 'yiaddr'.  The server includes
+   other parameters in the DHCPACK message as defined in section 4.3.1.
+
+4.3.6 Client messages
+
+   Table 4 details the differences between messages from clients in
+   various states.
+
+   ---------------------------------------------------------------------
+   |              |INIT-REBOOT  |SELECTING    |RENEWING     |REBINDING |
+   ---------------------------------------------------------------------
+   |broad/unicast |broadcast    |broadcast    |unicast      |broadcast |
+   |server-ip     |MUST NOT     |MUST         |MUST NOT     |MUST NOT  |
+   |requested-ip  |MUST         |MUST         |MUST NOT     |MUST NOT  |
+   |ciaddr        |zero         |zero         |IP address   |IP address|
+   ---------------------------------------------------------------------
+
+              Table 4: Client messages from different states
+
+
+
+
+
+
+
+Droms                       Standards Track                    [Page 33]
+\f
+RFC 2131          Dynamic Host Configuration Protocol         March 1997
+
+
+4.4 DHCP client behavior
+
+   Figure 5 gives a state-transition diagram for a DHCP client.  A
+   client can receive the following messages from a server:
+
+         o DHCPOFFER
+
+         o DHCPACK
+
+         o DHCPNAK
+
+   The DHCPINFORM message is not shown in figure 5.  A client simply
+   sends the DHCPINFORM and waits for DHCPACK messages.  Once the client
+   has selected its parameters, it has completed the configuration
+   process.
+
+   Table 5 gives the use of the fields and options in a DHCP message by
+   a client.  The remainder of this section describes the action of the
+   DHCP client for each possible incoming message.  The description in
+   the following section corresponds to the full configuration procedure
+   previously described in section 3.1, and the text in the subsequent
+   section corresponds to the abbreviated configuration procedure
+   described in section 3.2.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+Droms                       Standards Track                    [Page 34]
+\f
+RFC 2131          Dynamic Host Configuration Protocol         March 1997
+
+
+ --------                               -------
+|        | +-------------------------->|       |<-------------------+
+| INIT-  | |     +-------------------->| INIT  |                    |
+| REBOOT |DHCPNAK/         +---------->|       |<---+               |
+|        |Restart|         |            -------     |               |
+ --------  |  DHCPNAK/     |               |                        |
+    |      Discard offer   |      -/Send DHCPDISCOVER               |
+-/Send DHCPREQUEST         |               |                        |
+    |      |     |      DHCPACK            v        |               |
+ -----------     |   (not accept.)/   -----------   |               |
+|           |    |  Send DHCPDECLINE |           |                  |
+| REBOOTING |    |         |         | SELECTING |<----+            |
+|           |    |        /          |           |     |DHCPOFFER/  |
+ -----------     |       /            -----------   |  |Collect     |
+    |            |      /                  |   |       |  replies   |
+DHCPACK/         |     /  +----------------+   +-------+            |
+Record lease, set|    |   v   Select offer/                         |
+timers T1, T2   ------------  send DHCPREQUEST      |               |
+    |   +----->|            |             DHCPNAK, Lease expired/   |
+    |   |      | REQUESTING |                  Halt network         |
+    DHCPOFFER/ |            |                       |               |
+    Discard     ------------                        |               |
+    |   |        |        |                   -----------           |
+    |   +--------+     DHCPACK/              |           |          |
+    |              Record lease, set    -----| REBINDING |          |
+    |                timers T1, T2     /     |           |          |
+    |                     |        DHCPACK/   -----------           |
+    |                     v     Record lease, set   ^               |
+    +----------------> -------      /timers T1,T2   |               |
+               +----->|       |<---+                |               |
+               |      | BOUND |<---+                |               |
+  DHCPOFFER, DHCPACK, |       |    |            T2 expires/   DHCPNAK/
+   DHCPNAK/Discard     -------     |             Broadcast  Halt network
+               |       | |         |            DHCPREQUEST         |
+               +-------+ |        DHCPACK/          |               |
+                    T1 expires/   Record lease, set |               |
+                 Send DHCPREQUEST timers T1, T2     |               |
+                 to leasing server |                |               |
+                         |   ----------             |               |
+                         |  |          |------------+               |
+                         +->| RENEWING |                            |
+                            |          |----------------------------+
+                             ----------
+          Figure 5:  State-transition diagram for DHCP clients
+
+
+
+
+
+
+
+Droms                       Standards Track                    [Page 35]
+\f
+RFC 2131          Dynamic Host Configuration Protocol         March 1997
+
+
+4.4.1 Initialization and allocation of network address
+
+   The client begins in INIT state and forms a DHCPDISCOVER message.
+   The client SHOULD wait a random time between one and ten seconds to
+   desynchronize the use of DHCP at startup.  The client sets 'ciaddr'
+   to 0x00000000.  The client MAY request specific parameters by
+   including the 'parameter request list' option.  The client MAY
+   suggest a network address and/or lease time by including the
+   'requested IP address' and 'IP address lease time' options.  The
+   client MUST include its hardware address in the 'chaddr' field, if
+   necessary for delivery of DHCP reply messages.  The client MAY
+   include a different unique identifier in the 'client identifier'
+   option, as discussed in section 4.2.  If the client included a list
+   of requested parameters in a DHCPDISCOVER message, it MUST include
+   that list in all subsequent messages.
+
+   The client generates and records a random transaction identifier and
+   inserts that identifier into the 'xid' field.  The client records its
+   own local time for later use in computing the lease expiration.  The
+   client then broadcasts the DHCPDISCOVER on the local hardware
+   broadcast address to the 0xffffffff IP broadcast address and 'DHCP
+   server' UDP port.
+
+   If the 'xid' of an arriving DHCPOFFER message does not match the
+   'xid' of the most recent DHCPDISCOVER message, the DHCPOFFER message
+   must be silently discarded.  Any arriving DHCPACK messages must be
+   silently discarded.
+
+   The client collects DHCPOFFER messages over a period of time, selects
+   one DHCPOFFER message from the (possibly many) incoming DHCPOFFER
+   messages (e.g., the first DHCPOFFER message or the DHCPOFFER message
+   from the previously used server) and extracts the server address from
+   the 'server identifier' option in the DHCPOFFER message.  The time
+   over which the client collects messages and the mechanism used to
+   select one DHCPOFFER are implementation dependent.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+Droms                       Standards Track                    [Page 36]
+\f
+RFC 2131          Dynamic Host Configuration Protocol         March 1997
+
+
+Field      DHCPDISCOVER          DHCPREQUEST           DHCPDECLINE,
+           DHCPINFORM                                  DHCPRELEASE
+-----      ------------          -----------           -----------
+'op'       BOOTREQUEST           BOOTREQUEST           BOOTREQUEST
+'htype'    (From "Assigned Numbers" RFC)
+'hlen'     (Hardware address length in octets)
+'hops'     0                     0                     0
+'xid'      selected by client    'xid' from server     selected by
+                                 DHCPOFFER message     client
+'secs'     0 or seconds since    0 or seconds since    0
+           DHCP process started  DHCP process started
+'flags'    Set 'BROADCAST'       Set 'BROADCAST'       0
+           flag if client        flag if client
+           requires broadcast    requires broadcast
+           reply                 reply
+'ciaddr'   0 (DHCPDISCOVER)      0 or client's         0 (DHCPDECLINE)
+           client's              network address       client's network
+           network address       (BOUND/RENEW/REBIND)  address
+           (DHCPINFORM)                                (DHCPRELEASE)
+'yiaddr'   0                     0                     0
+'siaddr'   0                     0                     0
+'giaddr'   0                     0                     0
+'chaddr'   client's hardware     client's hardware     client's hardware
+           address               address               address
+'sname'    options, if           options, if           (unused)
+           indicated in          indicated in
+           'sname/file'          'sname/file'
+           option; otherwise     option; otherwise
+           unused                unused
+'file'     options, if           options, if           (unused)
+           indicated in          indicated in
+           'sname/file'          'sname/file'
+           option; otherwise     option; otherwise
+           unused                unused
+'options'  options               options               (unused)
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+Droms                       Standards Track                    [Page 37]
+\f
+RFC 2131          Dynamic Host Configuration Protocol         March 1997
+
+
+Option                     DHCPDISCOVER  DHCPREQUEST      DHCPDECLINE,
+                           DHCPINFORM                     DHCPRELEASE
+------                     ------------  -----------      -----------
+Requested IP address       MAY           MUST (in         MUST
+                           (DISCOVER)    SELECTING or     (DHCPDECLINE),
+                           MUST NOT      INIT-REBOOT)     MUST NOT
+                           (INFORM)      MUST NOT (in     (DHCPRELEASE)
+                                         BOUND or
+                                         RENEWING)
+IP address lease time      MAY           MAY              MUST NOT
+                           (DISCOVER)
+                           MUST NOT
+                           (INFORM)
+Use 'file'/'sname' fields  MAY           MAY              MAY
+DHCP message type          DHCPDISCOVER/ DHCPREQUEST      DHCPDECLINE/
+                           DHCPINFORM                     DHCPRELEASE
+Client identifier          MAY           MAY              MAY
+Vendor class identifier    MAY           MAY              MUST NOT
+Server identifier          MUST NOT      MUST (after      MUST
+                                         SELECTING)
+                                         MUST NOT (after
+                                         INIT-REBOOT,
+                                         BOUND, RENEWING
+                                         or REBINDING)
+Parameter request list     MAY           MAY              MUST NOT
+Maximum message size       MAY           MAY              MUST NOT
+Message                    SHOULD NOT    SHOULD NOT       SHOULD
+Site-specific              MAY           MAY              MUST NOT
+All others                 MAY           MAY              MUST NOT
+
+             Table 5:  Fields and options used by DHCP clients
+
+   If the parameters are acceptable, the client records the address of
+   the server that supplied the parameters from the 'server identifier'
+   field and sends that address in the 'server identifier' field of a
+   DHCPREQUEST broadcast message.  Once the DHCPACK message from the
+   server arrives, the client is initialized and moves to BOUND state.
+   The DHCPREQUEST message contains the same 'xid' as the DHCPOFFER
+   message.  The client records the lease expiration time as the sum of
+   the time at which the original request was sent and the duration of
+   the lease from the DHCPACK message.    The client SHOULD perform a
+   check on the suggested address to ensure that the address is not
+   already in use.  For example, if the client is on a network that
+   supports ARP, the client may issue an ARP request for the suggested
+   request.  When broadcasting an ARP request for the suggested address,
+   the client must fill in its own hardware address as the sender's
+   hardware address, and 0 as the sender's IP address, to avoid
+   confusing ARP caches in other hosts on the same subnet.  If the
+
+
+
+Droms                       Standards Track                    [Page 38]
+\f
+RFC 2131          Dynamic Host Configuration Protocol         March 1997
+
+
+   network address appears to be in use, the client MUST send a
+   DHCPDECLINE message to the server. The client SHOULD broadcast an ARP
+   reply to announce the client's new IP address and clear any outdated
+   ARP cache entries in hosts on the client's subnet.
+
+4.4.2 Initialization with known network address
+
+   The client begins in INIT-REBOOT state and sends a DHCPREQUEST
+   message.  The client MUST insert its known network address as a
+   'requested IP address' option in the DHCPREQUEST message.  The client
+   may request specific configuration parameters by including the
+   'parameter request list' option.  The client generates and records a
+   random transaction identifier and inserts that identifier into the
+   'xid' field.  The client records its own local time for later use in
+   computing the lease expiration.  The client MUST NOT include a
+   'server identifier' in the DHCPREQUEST message.  The client then
+   broadcasts the DHCPREQUEST on the local hardware broadcast address to
+   the 'DHCP server' UDP port.
+
+   Once a DHCPACK message with an 'xid' field matching that in the
+   client's DHCPREQUEST message arrives from any server, the client is
+   initialized and moves to BOUND state.  The client records the lease
+   expiration time as the sum of the time at which the DHCPREQUEST
+   message was sent and the duration of the lease from the DHCPACK
+   message.
+
+4.4.3 Initialization with an externally assigned network address
+
+   The client sends a DHCPINFORM message. The client may request
+   specific configuration parameters by including the 'parameter request
+   list' option. The client generates and records a random transaction
+   identifier and inserts that identifier into the 'xid' field. The
+   client places its own network address in the 'ciaddr' field. The
+   client SHOULD NOT request lease time parameters.
+
+   The client then unicasts the DHCPINFORM to the DHCP server if it
+   knows the server's address, otherwise it broadcasts the message to
+   the limited (all 1s) broadcast address.  DHCPINFORM messages MUST be
+   directed to the 'DHCP server' UDP port.
+
+   Once a DHCPACK message with an 'xid' field matching that in the
+   client's DHCPINFORM message arrives from any server, the client is
+   initialized.
+
+   If the client does not receive a DHCPACK within a reasonable period
+   of time (60 seconds or 4 tries if using timeout suggested in section
+   4.1), then it SHOULD display a message informing the user of the
+   problem, and then SHOULD begin network processing using suitable
+
+
+
+Droms                       Standards Track                    [Page 39]
+\f
+RFC 2131          Dynamic Host Configuration Protocol         March 1997
+
+
+   defaults as per Appendix A.
+
+4.4.4 Use of broadcast and unicast
+
+   The DHCP client broadcasts DHCPDISCOVER, DHCPREQUEST and DHCPINFORM
+   messages, unless the client knows the address of a DHCP server.  The
+   client unicasts DHCPRELEASE messages to the server.  Because the
+   client is declining the use of the IP address supplied by the server,
+   the client broadcasts DHCPDECLINE messages.
+
+   When the DHCP client knows the address of a DHCP server, in either
+   INIT or REBOOTING state, the client may use that address in the
+   DHCPDISCOVER or DHCPREQUEST rather than the IP broadcast address.
+   The client may also use unicast to send DHCPINFORM messages to a
+   known DHCP server.  If the client receives no response to DHCP
+   messages sent to the IP address of a known DHCP server, the DHCP
+   client reverts to using the IP broadcast address.
+
+4.4.5 Reacquisition and expiration
+
+   The client maintains two times, T1 and T2, that specify the times at
+   which the client tries to extend its lease on its network address.
+   T1 is the time at which the client enters the RENEWING state and
+   attempts to contact the server that originally issued the client's
+   network address.  T2 is the time at which the client enters the
+   REBINDING state and attempts to contact any server. T1 MUST be
+   earlier than T2, which, in turn, MUST be earlier than the time at
+   which the client's lease will expire.
+
+   To avoid the need for synchronized clocks, T1 and T2 are expressed in
+   options as relative times [2].
+
+   At time T1 the client moves to RENEWING state and sends (via unicast)
+   a DHCPREQUEST message to the server to extend its lease.  The client
+   sets the 'ciaddr' field in the DHCPREQUEST to its current network
+   address. The client records the local time at which the DHCPREQUEST
+   message is sent for computation of the lease expiration time.  The
+   client MUST NOT include a 'server identifier' in the DHCPREQUEST
+   message.
+
+   Any DHCPACK messages that arrive with an 'xid' that does not match
+   the 'xid' of the client's DHCPREQUEST message are silently discarded.
+   When the client receives a DHCPACK from the server, the client
+   computes the lease expiration time as the sum of the time at which
+   the client sent the DHCPREQUEST message and the duration of the lease
+   in the DHCPACK message.  The client has successfully reacquired its
+   network address, returns to BOUND state and may continue network
+   processing.
+
+
+
+Droms                       Standards Track                    [Page 40]
+\f
+RFC 2131          Dynamic Host Configuration Protocol         March 1997
+
+
+   If no DHCPACK arrives before time T2, the client moves to REBINDING
+   state and sends (via broadcast) a DHCPREQUEST message to extend its
+   lease.  The client sets the 'ciaddr' field in the DHCPREQUEST to its
+   current network address.  The client MUST NOT include a 'server
+   identifier' in the DHCPREQUEST message.
+
+   Times T1 and T2 are configurable by the server through options.  T1
+   defaults to (0.5 * duration_of_lease).  T2 defaults to (0.875 *
+   duration_of_lease).  Times T1 and T2 SHOULD be chosen with some
+   random "fuzz" around a fixed value, to avoid synchronization of
+   client reacquisition.
+
+   A client MAY choose to renew or extend its lease prior to T1.  The
+   server MAY choose to extend the client's lease according to policy
+   set by the network administrator.  The server SHOULD return T1 and
+   T2, and their values SHOULD be adjusted from their original values to
+   take account of the time remaining on the lease.
+
+   In both RENEWING and REBINDING states, if the client receives no
+   response to its DHCPREQUEST message, the client SHOULD wait one-half
+   of the remaining time until T2 (in RENEWING state) and one-half of
+   the remaining lease time (in REBINDING state), down to a minimum of
+   60 seconds, before retransmitting the DHCPREQUEST message.
+
+   If the lease expires before the client receives a DHCPACK, the client
+   moves to INIT state, MUST immediately stop any other network
+   processing and requests network initialization parameters as if the
+   client were uninitialized.  If the client then receives a DHCPACK
+   allocating that client its previous network address, the client
+   SHOULD continue network processing.  If the client is given a new
+   network address, it MUST NOT continue using the previous network
+   address and SHOULD notify the local users of the problem.
+
+4.4.6 DHCPRELEASE
+
+   If the client no longer requires use of its assigned network address
+   (e.g., the client is gracefully shut down), the client sends a
+   DHCPRELEASE message to the server.  Note that the correct operation
+   of DHCP does not depend on the transmission of DHCPRELEASE messages.
+
+
+
+
+
+
+
+
+
+
+
+
+Droms                       Standards Track                    [Page 41]
+\f
+RFC 2131          Dynamic Host Configuration Protocol         March 1997
+
+
+5. Acknowledgments
+
+   The author thanks the many (and too numerous to mention!) members of
+   the DHC WG for their tireless and ongoing efforts in the development
+   of DHCP and this document.
+
+   The efforts of J Allard, Mike Carney, Dave Lapp, Fred Lien and John
+   Mendonca in organizing DHCP interoperability testing sessions are
+   gratefully acknowledged.
+
+   The development of this document was supported in part by grants from
+   the Corporation for National Research Initiatives (CNRI), Bucknell
+   University and Sun Microsystems.
+
+6. References
+
+   [1] Acetta, M., "Resource Location Protocol", RFC 887, CMU, December
+       1983.
+
+   [2] Alexander, S., and R. Droms, "DHCP Options and BOOTP Vendor
+       Extensions", RFC 1533, Lachman Technology, Inc., Bucknell
+       University, October 1993.
+
+   [3] Braden, R., Editor, "Requirements for Internet Hosts --
+       Communication Layers", STD 3, RFC 1122, USC/Information Sciences
+       Institute, October 1989.
+
+   [4] Braden, R., Editor, "Requirements for Internet Hosts --
+       Application and Support, STD 3, RFC 1123, USC/Information
+       Sciences Institute, October 1989.
+
+   [5] Brownell, D, "Dynamic Reverse Address Resolution Protocol
+       (DRARP)", Work in Progress.
+
+   [6] Comer, D., and R. Droms, "Uniform Access to Internet Directory
+       Services", Proc. of ACM SIGCOMM '90 (Special issue of Computer
+       Communications Review), 20(4):50--59, 1990.
+
+   [7] Croft, B., and J. Gilmore, "Bootstrap Protocol (BOOTP)", RFC 951,
+       Stanford and SUN Microsystems, September 1985.
+
+   [8] Deering, S., "ICMP Router Discovery Messages", RFC 1256, Xerox
+       PARC, September 1991.
+
+   [9] Droms, D., "Interoperation between DHCP and BOOTP", RFC 1534,
+       Bucknell University, October 1993.
+
+
+
+
+
+Droms                       Standards Track                    [Page 42]
+\f
+RFC 2131          Dynamic Host Configuration Protocol         March 1997
+
+
+   [10] Finlayson, R., Mann, T., Mogul, J., and M. Theimer, "A Reverse
+        Address Resolution Protocol", RFC 903, Stanford, June 1984.
+
+   [11] Gray C., and D. Cheriton, "Leases: An Efficient Fault-Tolerant
+        Mechanism for Distributed File Cache Consistency", In Proc. of
+        the Twelfth ACM Symposium on Operating Systems Design, 1989.
+
+   [12] Mockapetris, P., "Domain Names -- Concepts and Facilities", STD
+        13, RFC 1034, USC/Information Sciences Institute, November 1987.
+
+   [13] Mockapetris, P., "Domain Names -- Implementation and
+        Specification", STD 13, RFC 1035, USC/Information Sciences
+        Institute, November 1987.
+
+   [14] Mogul J., and S. Deering, "Path MTU Discovery", RFC 1191,
+        November 1990.
+
+   [15] Morgan, R., "Dynamic IP Address Assignment for Ethernet Attached
+        Hosts", Work in Progress.
+
+   [16] Postel, J., "Internet Control Message Protocol", STD 5, RFC 792,
+        USC/Information Sciences Institute, September 1981.
+
+   [17] Reynolds, J., "BOOTP Vendor Information Extensions", RFC 1497,
+        USC/Information Sciences Institute, August 1993.
+
+   [18] Reynolds, J., and J. Postel, "Assigned Numbers", STD 2, RFC 1700,
+        USC/Information Sciences Institute, October 1994.
+
+   [19] Jeffrey Schiller and Mark Rosenstein. A Protocol for the Dynamic
+        Assignment of IP Addresses for use on an Ethernet. (Available
+        from the Athena Project, MIT), 1989.
+
+   [20] Sollins, K., "The TFTP Protocol (Revision 2)",  RFC 783, NIC,
+        June 1981.
+
+   [21] Wimer, W., "Clarifications and Extensions for the Bootstrap
+        Protocol", RFC 1542, Carnegie Mellon University, October 1993.
+
+7. Security Considerations
+
+   DHCP is built directly on UDP and IP which are as yet inherently
+   insecure.  Furthermore, DHCP is generally intended to make
+   maintenance of remote and/or diskless hosts easier.  While perhaps
+   not impossible, configuring such hosts with passwords or keys may be
+   difficult and inconvenient.  Therefore, DHCP in its current form is
+   quite insecure.
+
+
+
+
+Droms                       Standards Track                    [Page 43]
+\f
+RFC 2131          Dynamic Host Configuration Protocol         March 1997
+
+
+   Unauthorized DHCP servers may be easily set up.  Such servers can
+   then send false and potentially disruptive information to clients
+   such as incorrect or duplicate IP addresses, incorrect routing
+   information (including spoof routers, etc.), incorrect domain
+   nameserver addresses (such as spoof nameservers), and so on.
+   Clearly, once this seed information is in place, an attacker can
+   further compromise affected systems.
+
+   Malicious DHCP clients could masquerade as legitimate clients and
+   retrieve information intended for those legitimate clients.  Where
+   dynamic allocation of resources is used, a malicious client could
+   claim all resources for itself, thereby denying resources to
+   legitimate clients.
+
+8. Author's Address
+
+      Ralph Droms
+      Computer Science Department
+      323 Dana Engineering
+      Bucknell University
+      Lewisburg, PA 17837
+
+      Phone: (717) 524-1145
+      EMail: droms@bucknell.edu
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+Droms                       Standards Track                    [Page 44]
+\f
+RFC 2131          Dynamic Host Configuration Protocol         March 1997
+
+
+A. Host Configuration Parameters
+
+   IP-layer_parameters,_per_host:_
+
+   Be a router                     on/off                 HRC 3.1
+   Non-local source routing        on/off                 HRC 3.3.5
+   Policy filters for
+   non-local source routing        (list)                 HRC 3.3.5
+   Maximum reassembly size         integer                HRC 3.3.2
+   Default TTL                     integer                HRC 3.2.1.7
+   PMTU aging timeout              integer                MTU 6.6
+   MTU plateau table               (list)                 MTU 7
+   IP-layer_parameters,_per_interface:_
+   IP address                      (address)              HRC 3.3.1.6
+   Subnet mask                     (address mask)         HRC 3.3.1.6
+   MTU                             integer                HRC 3.3.3
+   All-subnets-MTU                 on/off                 HRC 3.3.3
+   Broadcast address flavor        0x00000000/0xffffffff  HRC 3.3.6
+   Perform mask discovery          on/off                 HRC 3.2.2.9
+   Be a mask supplier              on/off                 HRC 3.2.2.9
+   Perform router discovery        on/off                 RD 5.1
+   Router solicitation address     (address)              RD 5.1
+   Default routers, list of:
+           router address          (address)              HRC 3.3.1.6
+           preference level        integer                HRC 3.3.1.6
+   Static routes, list of:
+           destination             (host/subnet/net)      HRC 3.3.1.2
+           destination mask        (address mask)         HRC 3.3.1.2
+           type-of-service         integer                HRC 3.3.1.2
+           first-hop router        (address)              HRC 3.3.1.2
+           ignore redirects        on/off                 HRC 3.3.1.2
+           PMTU                    integer                MTU 6.6
+           perform PMTU discovery  on/off                 MTU 6.6
+
+   Link-layer_parameters,_per_interface:_
+   Trailers                       on/off                 HRC 2.3.1
+   ARP cache timeout              integer                HRC 2.3.2.1
+   Ethernet encapsulation         (RFC 894/RFC 1042)     HRC 2.3.3
+
+   TCP_parameters,_per_host:_
+   TTL                            integer                HRC 4.2.2.19
+   Keep-alive interval            integer                HRC 4.2.3.6
+   Keep-alive data size           0/1                    HRC 4.2.3.6
+
+Key:
+
+   MTU = Path MTU Discovery (RFC 1191, Proposed Standard)
+   RD = Router Discovery (RFC 1256, Proposed Standard)
+
+
+
+Droms                       Standards Track                    [Page 45]
+\f
diff --git a/doc/rfc2132.txt b/doc/rfc2132.txt
new file mode 100644 (file)
index 0000000..e9c4f4b
--- /dev/null
@@ -0,0 +1,1907 @@
+
+
+
+
+
+
+Network Working Group                                       S. Alexander
+Request for Comments: 2132                        Silicon Graphics, Inc.
+Obsoletes: 1533                                                 R. Droms
+Category: Standards Track                            Bucknell University
+                                                              March 1997
+
+                DHCP Options and BOOTP Vendor Extensions
+
+Status of this memo
+
+   This document specifies an Internet standards track protocol for the
+   Internet community, and requests discussion and suggestions for
+   improvements.  Please refer to the current edition of the "Internet
+   Official Protocol Standards" (STD 1) for the standardization state
+   and status of this protocol.  Distribution of this memo is unlimited.
+
+Abstract
+
+   The Dynamic Host Configuration Protocol (DHCP) [1] provides a
+   framework for passing configuration information to hosts on a TCP/IP
+   network.  Configuration parameters and other control information are
+   carried in tagged data items that are stored in the 'options' field
+   of the DHCP message.  The data items themselves are also called
+   "options."
+
+   This document specifies the current set of DHCP options.  Future
+   options will be specified in separate RFCs.  The current list of
+   valid options is also available in ftp://ftp.isi.edu/in-
+   notes/iana/assignments [22].
+
+   All of the vendor information extensions defined in RFC 1497 [2] may
+   be used as DHCP options.  The definitions given in RFC 1497 are
+   included in this document, which supersedes RFC 1497.  All of the
+   DHCP options defined in this document, except for those specific to
+   DHCP as defined in section 9, may be used as BOOTP vendor information
+   extensions.
+
+Table of Contents
+
+    1.  Introduction ..............................................  2
+    2.  BOOTP Extension/DHCP Option Field Format ..................  4
+    3.  RFC 1497 Vendor Extensions ................................  5
+    4.  IP Layer Parameters per Host .............................. 11
+    5.  IP Layer Parameters per Interface ........................  13
+    6.  Link Layer Parameters per Interface ....................... 16
+    7.  TCP Parameters ............................................ 17
+    8.  Application and Service Parameters ........................ 18
+    9.  DHCP Extensions ........................................... 25
+
+
+
+Alexander & Droms           Standards Track                     [Page 1]
+\f
+RFC 2132        DHCP Options and BOOTP Vendor Extensions      March 1997
+
+
+   10.  Defining new extensions ................................... 31
+   11.  Acknowledgements .......................................... 31
+   12.  References ................................................ 32
+   13.  Security Considerations ................................... 33
+   14.  Authors' Addresses ........................................ 34
+
+1. Introduction
+
+   This document specifies options for use with both the Dynamic Host
+   Configuration Protocol and the Bootstrap Protocol.
+
+   The full description of DHCP packet formats may be found in the DHCP
+   specification document [1], and the full description of BOOTP packet
+   formats may be found in the BOOTP specification document [3].  This
+   document defines the format of information in the last field of DHCP
+   packets ('options') and of BOOTP packets ('vend').  The remainder of
+   this section defines a generalized use of this area for giving
+   information useful to a wide class of machines, operating systems and
+   configurations. Sites with a single DHCP or BOOTP server that is
+   shared among heterogeneous clients may choose to define other, site-
+   specific formats for the use of the 'options' field.
+
+   Section 2 of this memo describes the formats of DHCP options and
+   BOOTP vendor extensions.  Section 3 describes options defined in
+   previous documents for use with BOOTP (all may also be used with
+   DHCP).  Sections 4-8 define new options intended for use with both
+   DHCP and BOOTP. Section 9 defines options used only in DHCP.
+
+   References further describing most of the options defined in sections
+   2-6 can be found in section 12.  The use of the options defined in
+   section 9 is described in the DHCP specification [1].
+
+   Information on registering new options is contained in section 10.
+
+   This document updates the definition of DHCP/BOOTP options that
+   appears in RFC1533.  The classing mechanism has been extended to
+   include vendor classes as described in section 8.4 and 9.13.  The new
+   procedure for defining new DHCP/BOOTP options in described in section
+   10.  Several new options, including NIS+ domain and servers, Mobile
+   IP home agent, SMTP server, TFTP server and Bootfile server, have
+   been added.  Text giving definitions used throughout the document has
+   been added in section 1.1.  Text emphasizing the need for uniqueness
+   of client-identifiers has been added to section 9.14.
+
+
+
+
+
+
+
+
+Alexander & Droms           Standards Track                     [Page 2]
+\f
+RFC 2132        DHCP Options and BOOTP Vendor Extensions      March 1997
+
+
+1.1 Requirements
+
+   Throughout this document, the words that are used to define the
+   significance of particular requirements are capitalized.  These words
+   are:
+
+      o "MUST"
+
+       This word or the adjective "REQUIRED" means that the item is an
+       absolute requirement of this specification.
+
+      o "MUST NOT"
+
+       This phrase means that the item is an absolute prohibition of
+       this specification.
+
+      o "SHOULD"
+
+       This word or the adjective "RECOMMENDED" means that there may
+       exist valid reasons in particular circumstances to ignore this
+       item, but the full implications should be understood and the case
+       carefully weighed before choosing a different course.
+
+      o "SHOULD NOT"
+
+       This phrase means that there may exist valid reasons in
+       particular circumstances when the listed behavior is acceptable
+       or even useful, but the full implications should be understood
+       and the case carefully weighed before implementing any behavior
+       described with this label.
+
+      o "MAY"
+
+       This word or the adjective "OPTIONAL" means that this item is
+       truly optional.  One vendor may choose to include the item
+       because a particular marketplace requires it or because it
+       enhances the product, for example; another vendor may omit the
+       same item.
+
+1.2 Terminology
+
+   This document uses the following terms:
+
+      o "DHCP client"
+
+       A DHCP client or "client" is an Internet host using DHCP to
+       obtain configuration parameters such as a network address.
+
+
+
+
+Alexander & Droms           Standards Track                     [Page 3]
+\f
+RFC 2132        DHCP Options and BOOTP Vendor Extensions      March 1997
+
+
+      o "DHCP server"
+
+       A DHCP server of "server"is an Internet host that returns
+       configuration parameters to DHCP clients.
+
+      o "binding"
+
+       A binding is a collection of configuration parameters, including
+       at least an IP address, associated with or "bound to" a DHCP
+       client.  Bindings are managed by DHCP servers.
+
+2. BOOTP Extension/DHCP Option Field Format
+
+
+   DHCP options have the same format as the BOOTP 'vendor extensions'
+   defined in RFC 1497 [2].  Options may be fixed length or variable
+   length.  All options begin with a tag octet, which uniquely
+   identifies the option.  Fixed-length options without data consist of
+   only a tag octet.  Only options 0 and 255 are fixed length.  All
+   other options are variable-length with a length octet following the
+   tag octet.  The value of the length octet does not include the two
+   octets specifying the tag and length.  The length octet is followed
+   by "length" octets of data.  Options containing NVT ASCII data SHOULD
+   NOT include a trailing NULL; however, the receiver of such options
+   MUST be prepared to delete trailing nulls if they exist.  The
+   receiver MUST NOT require that a trailing null be included in the
+   data.  In the case of some variable-length options the length field
+   is a constant but must still be specified.
+
+   Any options defined subsequent to this document MUST contain a length
+   octet even if the length is fixed or zero.
+
+   All multi-octet quantities are in network byte-order.
+
+   When used with BOOTP, the first four octets of the vendor information
+   field have been assigned to the "magic cookie" (as suggested in RFC
+   951).  This field identifies the mode in which the succeeding data is
+   to be interpreted.  The value of the magic cookie is the 4 octet
+   dotted decimal 99.130.83.99 (or hexadecimal number 63.82.53.63) in
+   network byte order.
+
+   All of the "vendor extensions" defined in RFC 1497 are also DHCP
+   options.
+
+   Option codes 128 to 254 (decimal) are reserved for site-specific
+   options.
+
+
+
+
+
+Alexander & Droms           Standards Track                     [Page 4]
+\f
+RFC 2132        DHCP Options and BOOTP Vendor Extensions      March 1997
+
+
+   Except for the options in section 9, all options may be used with
+   either DHCP or BOOTP.
+
+   Many of these options have their default values specified in other
+   documents.  In particular, RFC 1122 [4] specifies default values for
+   most IP and TCP configuration parameters.
+
+   Many options supply one or more 32-bit IP address.  Use of IP
+   addresses rather than fully-qualified Domain Names (FQDNs) may make
+   future renumbering of IP hosts more difficult.  Use of these
+   addresses is discouraged at sites that may require renumbering.
+
+3. RFC 1497 Vendor Extensions
+
+   This section lists the vendor extensions as defined in RFC 1497.
+   They are defined here for completeness.
+
+3.1. Pad Option
+
+   The pad option can be used to cause subsequent fields to align on
+   word boundaries.
+
+   The code for the pad option is 0, and its length is 1 octet.
+
+    Code
+   +-----+
+   |  0  |
+   +-----+
+
+3.2. End Option
+
+   The end option marks the end of valid information in the vendor
+   field.  Subsequent octets should be filled with pad options.
+
+   The code for the end option is 255, and its length is 1 octet.
+
+    Code
+   +-----+
+   | 255 |
+   +-----+
+
+3.3. Subnet Mask
+
+   The subnet mask option specifies the client's subnet mask as per RFC
+   950 [5].
+
+   If both the subnet mask and the router option are specified in a DHCP
+   reply, the subnet mask option MUST be first.
+
+
+
+Alexander & Droms           Standards Track                     [Page 5]
+\f
+RFC 2132        DHCP Options and BOOTP Vendor Extensions      March 1997
+
+
+   The code for the subnet mask option is 1, and its length is 4 octets.
+
+    Code   Len        Subnet Mask
+   +-----+-----+-----+-----+-----+-----+
+   |  1  |  4  |  m1 |  m2 |  m3 |  m4 |
+   +-----+-----+-----+-----+-----+-----+
+
+3.4. Time Offset
+
+   The time offset field specifies the offset of the client's subnet in
+   seconds from Coordinated Universal Time (UTC).  The offset is
+   expressed as a two's complement 32-bit integer.  A positive offset
+   indicates a location east of the zero meridian and a negative offset
+   indicates a location west of the zero meridian.
+
+   The code for the time offset option is 2, and its length is 4 octets.
+
+    Code   Len        Time Offset
+   +-----+-----+-----+-----+-----+-----+
+   |  2  |  4  |  n1 |  n2 |  n3 |  n4 |
+   +-----+-----+-----+-----+-----+-----+
+
+3.5. Router Option
+
+   The router option specifies a list of IP addresses for routers on the
+   client's subnet.  Routers SHOULD be listed in order of preference.
+
+   The code for the router option is 3.  The minimum length for the
+   router option is 4 octets, and the length MUST always be a multiple
+   of 4.
+
+    Code   Len         Address 1               Address 2
+   +-----+-----+-----+-----+-----+-----+-----+-----+--
+   |  3  |  n  |  a1 |  a2 |  a3 |  a4 |  a1 |  a2 |  ...
+   +-----+-----+-----+-----+-----+-----+-----+-----+--
+
+3.6. Time Server Option
+
+   The time server option specifies a list of RFC 868 [6] time servers
+   available to the client.  Servers SHOULD be listed in order of
+   preference.
+
+   The code for the time server option is 4.  The minimum length for
+   this option is 4 octets, and the length MUST always be a multiple of
+   4.
+
+
+
+
+
+
+Alexander & Droms           Standards Track                     [Page 6]
+\f
+RFC 2132        DHCP Options and BOOTP Vendor Extensions      March 1997
+
+
+    Code   Len         Address 1               Address 2
+   +-----+-----+-----+-----+-----+-----+-----+-----+--
+   |  4  |  n  |  a1 |  a2 |  a3 |  a4 |  a1 |  a2 |  ...
+   +-----+-----+-----+-----+-----+-----+-----+-----+--
+
+3.7. Name Server Option
+
+   The name server option specifies a list of IEN 116 [7] name servers
+   available to the client.  Servers SHOULD be listed in order of
+   preference.
+
+   The code for the name server option is 5.  The minimum length for
+   this option is 4 octets, and the length MUST always be a multiple of
+   4.
+
+    Code   Len         Address 1               Address 2
+   +-----+-----+-----+-----+-----+-----+-----+-----+--
+   |  5  |  n  |  a1 |  a2 |  a3 |  a4 |  a1 |  a2 |  ...
+   +-----+-----+-----+-----+-----+-----+-----+-----+--
+
+3.8. Domain Name Server Option
+
+   The domain name server option specifies a list of Domain Name System
+   (STD 13, RFC 1035 [8]) name servers available to the client.  Servers
+   SHOULD be listed in order of preference.
+
+   The code for the domain name server option is 6.  The minimum length
+   for this option is 4 octets, and the length MUST always be a multiple
+   of 4.
+
+    Code   Len         Address 1               Address 2
+   +-----+-----+-----+-----+-----+-----+-----+-----+--
+   |  6  |  n  |  a1 |  a2 |  a3 |  a4 |  a1 |  a2 |  ...
+   +-----+-----+-----+-----+-----+-----+-----+-----+--
+
+3.9. Log Server Option
+
+   The log server option specifies a list of MIT-LCS UDP log servers
+   available to the client.  Servers SHOULD be listed in order of
+   preference.
+
+   The code for the log server option is 7.  The minimum length for this
+   option is 4 octets, and the length MUST always be a multiple of 4.
+
+    Code   Len         Address 1               Address 2
+   +-----+-----+-----+-----+-----+-----+-----+-----+--
+   |  7  |  n  |  a1 |  a2 |  a3 |  a4 |  a1 |  a2 |  ...
+   +-----+-----+-----+-----+-----+-----+-----+-----+--
+
+
+
+Alexander & Droms           Standards Track                     [Page 7]
+\f
+RFC 2132        DHCP Options and BOOTP Vendor Extensions      March 1997
+
+
+3.10. Cookie Server Option
+
+   The cookie server option specifies a list of RFC 865 [9] cookie
+   servers available to the client.  Servers SHOULD be listed in order
+   of preference.
+
+   The code for the log server option is 8.  The minimum length for this
+   option is 4 octets, and the length MUST always be a multiple of 4.
+
+    Code   Len         Address 1               Address 2
+   +-----+-----+-----+-----+-----+-----+-----+-----+--
+   |  8  |  n  |  a1 |  a2 |  a3 |  a4 |  a1 |  a2 |  ...
+   +-----+-----+-----+-----+-----+-----+-----+-----+--
+
+3.11. LPR Server Option
+
+   The LPR server option specifies a list of RFC 1179 [10] line printer
+   servers available to the client.  Servers SHOULD be listed in order
+   of preference.
+
+   The code for the LPR server option is 9.  The minimum length for this
+   option is 4 octets, and the length MUST always be a multiple of 4.
+
+    Code   Len         Address 1               Address 2
+   +-----+-----+-----+-----+-----+-----+-----+-----+--
+   |  9  |  n  |  a1 |  a2 |  a3 |  a4 |  a1 |  a2 |  ...
+   +-----+-----+-----+-----+-----+-----+-----+-----+--
+
+3.12. Impress Server Option
+
+   The Impress server option specifies a list of Imagen Impress servers
+   available to the client.  Servers SHOULD be listed in order of
+   preference.
+
+   The code for the Impress server option is 10.  The minimum length for
+   this option is 4 octets, and the length MUST always be a multiple of
+   4.
+
+    Code   Len         Address 1               Address 2
+   +-----+-----+-----+-----+-----+-----+-----+-----+--
+   |  10 |  n  |  a1 |  a2 |  a3 |  a4 |  a1 |  a2 |  ...
+   +-----+-----+-----+-----+-----+-----+-----+-----+--
+
+3.13. Resource Location Server Option
+
+   This option specifies a list of RFC 887 [11] Resource Location
+   servers available to the client.  Servers SHOULD be listed in order
+   of preference.
+
+
+
+Alexander & Droms           Standards Track                     [Page 8]
+\f
+RFC 2132        DHCP Options and BOOTP Vendor Extensions      March 1997
+
+
+   The code for this option is 11.  The minimum length for this option
+   is 4 octets, and the length MUST always be a multiple of 4.
+
+    Code   Len         Address 1               Address 2
+   +-----+-----+-----+-----+-----+-----+-----+-----+--
+   |  11 |  n  |  a1 |  a2 |  a3 |  a4 |  a1 |  a2 |  ...
+   +-----+-----+-----+-----+-----+-----+-----+-----+--
+
+3.14. Host Name Option
+
+   This option specifies the name of the client.  The name may or may
+   not be qualified with the local domain name (see section 3.17 for the
+   preferred way to retrieve the domain name).  See RFC 1035 for
+   character set restrictions.
+
+   The code for this option is 12, and its minimum length is 1.
+
+    Code   Len                 Host Name
+   +-----+-----+-----+-----+-----+-----+-----+-----+--
+   |  12 |  n  |  h1 |  h2 |  h3 |  h4 |  h5 |  h6 |  ...
+   +-----+-----+-----+-----+-----+-----+-----+-----+--
+
+3.15. Boot File Size Option
+
+   This option specifies the length in 512-octet blocks of the default
+   boot image for the client.  The file length is specified as an
+   unsigned 16-bit integer.
+
+   The code for this option is 13, and its length is 2.
+
+    Code   Len   File Size
+   +-----+-----+-----+-----+
+   |  13 |  2  |  l1 |  l2 |
+   +-----+-----+-----+-----+
+
+3.16. Merit Dump File
+
+   This option specifies the path-name of a file to which the client's
+   core image should be dumped in the event the client crashes.  The
+   path is formatted as a character string consisting of characters from
+   the NVT ASCII character set.
+
+   The code for this option is 14.  Its minimum length is 1.
+
+    Code   Len      Dump File Pathname
+   +-----+-----+-----+-----+-----+-----+---
+   |  14 |  n  |  n1 |  n2 |  n3 |  n4 | ...
+   +-----+-----+-----+-----+-----+-----+---
+
+
+
+Alexander & Droms           Standards Track                     [Page 9]
+\f
+RFC 2132        DHCP Options and BOOTP Vendor Extensions      March 1997
+
+
+3.17. Domain Name
+
+   This option specifies the domain name that client should use when
+   resolving hostnames via the Domain Name System.
+
+   The code for this option is 15.  Its minimum length is 1.
+
+    Code   Len        Domain Name
+   +-----+-----+-----+-----+-----+-----+--
+   |  15 |  n  |  d1 |  d2 |  d3 |  d4 |  ...
+   +-----+-----+-----+-----+-----+-----+--
+
+3.18. Swap Server
+
+   This specifies the IP address of the client's swap server.
+
+   The code for this option is 16 and its length is 4.
+
+    Code   Len    Swap Server Address
+   +-----+-----+-----+-----+-----+-----+
+   |  16 |  n  |  a1 |  a2 |  a3 |  a4 |
+   +-----+-----+-----+-----+-----+-----+
+
+3.19. Root Path
+
+   This option specifies the path-name that contains the client's root
+   disk.  The path is formatted as a character string consisting of
+   characters from the NVT ASCII character set.
+
+   The code for this option is 17.  Its minimum length is 1.
+
+    Code   Len      Root Disk Pathname
+   +-----+-----+-----+-----+-----+-----+---
+   |  17 |  n  |  n1 |  n2 |  n3 |  n4 | ...
+   +-----+-----+-----+-----+-----+-----+---
+
+3.20. Extensions Path
+
+   A string to specify a file, retrievable via TFTP, which contains
+   information which can be interpreted in the same way as the 64-octet
+   vendor-extension field within the BOOTP response, with the following
+   exceptions:
+
+          - the length of the file is unconstrained;
+          - all references to Tag 18 (i.e., instances of the
+            BOOTP Extensions Path field) within the file are
+            ignored.
+
+
+
+
+Alexander & Droms           Standards Track                    [Page 10]
+\f
+RFC 2132        DHCP Options and BOOTP Vendor Extensions      March 1997
+
+
+   The code for this option is 18.  Its minimum length is 1.
+
+    Code   Len      Extensions Pathname
+   +-----+-----+-----+-----+-----+-----+---
+   |  18 |  n  |  n1 |  n2 |  n3 |  n4 | ...
+   +-----+-----+-----+-----+-----+-----+---
+
+4. IP Layer Parameters per Host
+
+   This section details the options that affect the operation of the IP
+   layer on a per-host basis.
+
+4.1. IP Forwarding Enable/Disable Option
+
+   This option specifies whether the client should configure its IP
+   layer for packet forwarding.  A value of 0 means disable IP
+   forwarding, and a value of 1 means enable IP forwarding.
+
+   The code for this option is 19, and its length is 1.
+
+    Code   Len  Value
+   +-----+-----+-----+
+   |  19 |  1  | 0/1 |
+   +-----+-----+-----+
+
+4.2. Non-Local Source Routing Enable/Disable Option
+
+   This option specifies whether the client should configure its IP
+   layer to allow forwarding of datagrams with non-local source routes
+   (see Section 3.3.5 of [4] for a discussion of this topic).  A value
+   of 0 means disallow forwarding of such datagrams, and a value of 1
+   means allow forwarding.
+
+   The code for this option is 20, and its length is 1.
+
+    Code   Len  Value
+   +-----+-----+-----+
+   |  20 |  1  | 0/1 |
+   +-----+-----+-----+
+
+4.3. Policy Filter Option
+
+   This option specifies policy filters for non-local source routing.
+   The filters consist of a list of IP addresses and masks which specify
+   destination/mask pairs with which to filter incoming source routes.
+
+   Any source routed datagram whose next-hop address does not match one
+   of the filters should be discarded by the client.
+
+
+
+Alexander & Droms           Standards Track                    [Page 11]
+\f
+RFC 2132        DHCP Options and BOOTP Vendor Extensions      March 1997
+
+
+   See [4] for further information.
+
+   The code for this option is 21.  The minimum length of this option is
+   8, and the length MUST be a multiple of 8.
+
+    Code   Len         Address 1                  Mask 1
+   +-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+
+   |  21 |  n  |  a1 |  a2 |  a3 |  a4 |  m1 |  m2 |  m3 |  m4 |
+   +-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+
+           Address 2                  Mask 2
+   +-----+-----+-----+-----+-----+-----+-----+-----+---
+   |  a1 |  a2 |  a3 |  a4 |  m1 |  m2 |  m3 |  m4 | ...
+   +-----+-----+-----+-----+-----+-----+-----+-----+---
+
+4.4. Maximum Datagram Reassembly Size
+
+   This option specifies the maximum size datagram that the client
+   should be prepared to reassemble.  The size is specified as a 16-bit
+   unsigned integer.  The minimum value legal value is 576.
+
+   The code for this option is 22, and its length is 2.
+
+    Code   Len      Size
+   +-----+-----+-----+-----+
+   |  22 |  2  |  s1 |  s2 |
+   +-----+-----+-----+-----+
+
+4.5. Default IP Time-to-live
+
+   This option specifies the default time-to-live that the client should
+   use on outgoing datagrams.  The TTL is specified as an octet with a
+   value between 1 and 255.
+
+   The code for this option is 23, and its length is 1.
+
+    Code   Len   TTL
+   +-----+-----+-----+
+   |  23 |  1  | ttl |
+   +-----+-----+-----+
+
+4.6. Path MTU Aging Timeout Option
+
+   This option specifies the timeout (in seconds) to use when aging Path
+   MTU values discovered by the mechanism defined in RFC 1191 [12].  The
+   timeout is specified as a 32-bit unsigned integer.
+
+   The code for this option is 24, and its length is 4.
+
+
+
+
+Alexander & Droms           Standards Track                    [Page 12]
+\f
+RFC 2132        DHCP Options and BOOTP Vendor Extensions      March 1997
+
+
+    Code   Len           Timeout
+   +-----+-----+-----+-----+-----+-----+
+   |  24 |  4  |  t1 |  t2 |  t3 |  t4 |
+   +-----+-----+-----+-----+-----+-----+
+
+4.7. Path MTU Plateau Table Option
+
+   This option specifies a table of MTU sizes to use when performing
+   Path MTU Discovery as defined in RFC 1191.  The table is formatted as
+   a list of 16-bit unsigned integers, ordered from smallest to largest.
+   The minimum MTU value cannot be smaller than 68.
+
+   The code for this option is 25.  Its minimum length is 2, and the
+   length MUST be a multiple of 2.
+
+    Code   Len     Size 1      Size 2
+   +-----+-----+-----+-----+-----+-----+---
+   |  25 |  n  |  s1 |  s2 |  s1 |  s2 | ...
+   +-----+-----+-----+-----+-----+-----+---
+
+5. IP Layer Parameters per Interface
+
+   This section details the options that affect the operation of the IP
+   layer on a per-interface basis.  It is expected that a client can
+   issue multiple requests, one per interface, in order to configure
+   interfaces with their specific parameters.
+
+5.1. Interface MTU Option
+
+   This option specifies the MTU to use on this interface.  The MTU is
+   specified as a 16-bit unsigned integer.  The minimum legal value for
+   the MTU is 68.
+
+   The code for this option is 26, and its length is 2.
+
+    Code   Len      MTU
+   +-----+-----+-----+-----+
+   |  26 |  2  |  m1 |  m2 |
+   +-----+-----+-----+-----+
+
+5.2. All Subnets are Local Option
+
+   This option specifies whether or not the client may assume that all
+   subnets of the IP network to which the client is connected use the
+   same MTU as the subnet of that network to which the client is
+   directly connected.  A value of 1 indicates that all subnets share
+   the same MTU.  A value of 0 means that the client should assume that
+   some subnets of the directly connected network may have smaller MTUs.
+
+
+
+Alexander & Droms           Standards Track                    [Page 13]
+\f
+RFC 2132        DHCP Options and BOOTP Vendor Extensions      March 1997
+
+
+   The code for this option is 27, and its length is 1.
+
+    Code   Len  Value
+   +-----+-----+-----+
+   |  27 |  1  | 0/1 |
+   +-----+-----+-----+
+
+5.3. Broadcast Address Option
+
+   This option specifies the broadcast address in use on the client's
+   subnet.  Legal values for broadcast addresses are specified in
+   section 3.2.1.3 of [4].
+
+   The code for this option is 28, and its length is 4.
+
+    Code   Len     Broadcast Address
+   +-----+-----+-----+-----+-----+-----+
+   |  28 |  4  |  b1 |  b2 |  b3 |  b4 |
+   +-----+-----+-----+-----+-----+-----+
+
+5.4. Perform Mask Discovery Option
+
+   This option specifies whether or not the client should perform subnet
+   mask discovery using ICMP.  A value of 0 indicates that the client
+   should not perform mask discovery.  A value of 1 means that the
+   client should perform mask discovery.
+
+   The code for this option is 29, and its length is 1.
+
+    Code   Len  Value
+   +-----+-----+-----+
+   |  29 |  1  | 0/1 |
+   +-----+-----+-----+
+
+5.5. Mask Supplier Option
+
+   This option specifies whether or not the client should respond to
+   subnet mask requests using ICMP.  A value of 0 indicates that the
+   client should not respond.  A value of 1 means that the client should
+   respond.
+
+   The code for this option is 30, and its length is 1.
+
+    Code   Len  Value
+   +-----+-----+-----+
+   |  30 |  1  | 0/1 |
+   +-----+-----+-----+
+
+
+
+
+Alexander & Droms           Standards Track                    [Page 14]
+\f
+RFC 2132        DHCP Options and BOOTP Vendor Extensions      March 1997
+
+
+5.6. Perform Router Discovery Option
+
+   This option specifies whether or not the client should solicit
+   routers using the Router Discovery mechanism defined in RFC 1256
+   [13].  A value of 0 indicates that the client should not perform
+   router discovery.  A value of 1 means that the client should perform
+   router discovery.
+
+   The code for this option is 31, and its length is 1.
+
+    Code   Len  Value
+   +-----+-----+-----+
+   |  31 |  1  | 0/1 |
+   +-----+-----+-----+
+
+5.7. Router Solicitation Address Option
+
+   This option specifies the address to which the client should transmit
+   router solicitation requests.
+
+   The code for this option is 32, and its length is 4.
+
+    Code   Len            Address
+   +-----+-----+-----+-----+-----+-----+
+   |  32 |  4  |  a1 |  a2 |  a3 |  a4 |
+   +-----+-----+-----+-----+-----+-----+
+
+5.8. Static Route Option
+
+   This option specifies a list of static routes that the client should
+   install in its routing cache.  If multiple routes to the same
+   destination are specified, they are listed in descending order of
+   priority.
+
+   The routes consist of a list of IP address pairs.  The first address
+   is the destination address, and the second address is the router for
+   the destination.
+
+   The default route (0.0.0.0) is an illegal destination for a static
+   route.  See section 3.5 for information about the router option.
+
+   The code for this option is 33.  The minimum length of this option is
+   8, and the length MUST be a multiple of 8.
+
+
+
+
+
+
+
+
+Alexander & Droms           Standards Track                    [Page 15]
+\f
+RFC 2132        DHCP Options and BOOTP Vendor Extensions      March 1997
+
+
+    Code   Len         Destination 1           Router 1
+   +-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+
+   |  33 |  n  |  d1 |  d2 |  d3 |  d4 |  r1 |  r2 |  r3 |  r4 |
+   +-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+
+           Destination 2           Router 2
+   +-----+-----+-----+-----+-----+-----+-----+-----+---
+   |  d1 |  d2 |  d3 |  d4 |  r1 |  r2 |  r3 |  r4 | ...
+   +-----+-----+-----+-----+-----+-----+-----+-----+---
+
+6. Link Layer Parameters per Interface
+
+   This section lists the options that affect the operation of the data
+   link layer on a per-interface basis.
+
+6.1. Trailer Encapsulation Option
+
+   This option specifies whether or not the client should negotiate the
+   use of trailers (RFC 893 [14]) when using the ARP protocol.  A value
+   of 0 indicates that the client should not attempt to use trailers.  A
+   value of 1 means that the client should attempt to use trailers.
+
+   The code for this option is 34, and its length is 1.
+
+    Code   Len  Value
+   +-----+-----+-----+
+   |  34 |  1  | 0/1 |
+   +-----+-----+-----+
+
+6.2. ARP Cache Timeout Option
+
+   This option specifies the timeout in seconds for ARP cache entries.
+   The time is specified as a 32-bit unsigned integer.
+
+   The code for this option is 35, and its length is 4.
+
+    Code   Len           Time
+   +-----+-----+-----+-----+-----+-----+
+   |  35 |  4  |  t1 |  t2 |  t3 |  t4 |
+   +-----+-----+-----+-----+-----+-----+
+
+6.3. Ethernet Encapsulation Option
+
+   This option specifies whether or not the client should use Ethernet
+   Version 2 (RFC 894 [15]) or IEEE 802.3 (RFC 1042 [16]) encapsulation
+   if the interface is an Ethernet.  A value of 0 indicates that the
+   client should use RFC 894 encapsulation.  A value of 1 means that the
+   client should use RFC 1042 encapsulation.
+
+
+
+
+Alexander & Droms           Standards Track                    [Page 16]
+\f
+RFC 2132        DHCP Options and BOOTP Vendor Extensions      March 1997
+
+
+   The code for this option is 36, and its length is 1.
+
+    Code   Len  Value
+   +-----+-----+-----+
+   |  36 |  1  | 0/1 |
+   +-----+-----+-----+
+
+7. TCP Parameters
+
+   This section lists the options that affect the operation of the TCP
+   layer on a per-interface basis.
+
+7.1. TCP Default TTL Option
+
+   This option specifies the default TTL that the client should use when
+   sending TCP segments.  The value is represented as an 8-bit unsigned
+   integer.  The minimum value is 1.
+
+   The code for this option is 37, and its length is 1.
+
+    Code   Len   TTL
+   +-----+-----+-----+
+   |  37 |  1  |  n  |
+   +-----+-----+-----+
+
+7.2. TCP Keepalive Interval Option
+
+   This option specifies the interval (in seconds) that the client TCP
+   should wait before sending a keepalive message on a TCP connection.
+   The time is specified as a 32-bit unsigned integer.  A value of zero
+   indicates that the client should not generate keepalive messages on
+   connections unless specifically requested by an application.
+
+   The code for this option is 38, and its length is 4.
+
+    Code   Len           Time
+   +-----+-----+-----+-----+-----+-----+
+   |  38 |  4  |  t1 |  t2 |  t3 |  t4 |
+   +-----+-----+-----+-----+-----+-----+
+
+7.3. TCP Keepalive Garbage Option
+
+   This option specifies the whether or not the client should send TCP
+   keepalive messages with a octet of garbage for compatibility with
+   older implementations.  A value of 0 indicates that a garbage octet
+   should not be sent. A value of 1 indicates that a garbage octet
+   should be sent.
+
+
+
+
+Alexander & Droms           Standards Track                    [Page 17]
+\f
+RFC 2132        DHCP Options and BOOTP Vendor Extensions      March 1997
+
+
+   The code for this option is 39, and its length is 1.
+
+    Code   Len  Value
+   +-----+-----+-----+
+   |  39 |  1  | 0/1 |
+   +-----+-----+-----+
+
+8. Application and Service Parameters
+
+   This section details some miscellaneous options used to configure
+   miscellaneous applications and services.
+
+8.1. Network Information Service Domain Option
+
+   This option specifies the name of the client's NIS [17] domain.  The
+   domain is formatted as a character string consisting of characters
+   from the NVT ASCII character set.
+
+   The code for this option is 40.  Its minimum length is 1.
+
+    Code   Len      NIS Domain Name
+   +-----+-----+-----+-----+-----+-----+---
+   |  40 |  n  |  n1 |  n2 |  n3 |  n4 | ...
+   +-----+-----+-----+-----+-----+-----+---
+
+8.2. Network Information Servers Option
+
+   This option specifies a list of IP addresses indicating NIS servers
+   available to the client.  Servers SHOULD be listed in order of
+   preference.
+
+   The code for this option is 41.  Its minimum length is 4, and the
+   length MUST be a multiple of 4.
+
+    Code   Len         Address 1               Address 2
+   +-----+-----+-----+-----+-----+-----+-----+-----+--
+   |  41 |  n  |  a1 |  a2 |  a3 |  a4 |  a1 |  a2 |  ...
+   +-----+-----+-----+-----+-----+-----+-----+-----+--
+
+8.3. Network Time Protocol Servers Option
+
+   This option specifies a list of IP addresses indicating NTP [18]
+   servers available to the client.  Servers SHOULD be listed in order
+   of preference.
+
+   The code for this option is 42.  Its minimum length is 4, and the
+   length MUST be a multiple of 4.
+
+
+
+
+Alexander & Droms           Standards Track                    [Page 18]
+\f
+RFC 2132        DHCP Options and BOOTP Vendor Extensions      March 1997
+
+
+    Code   Len         Address 1               Address 2
+   +-----+-----+-----+-----+-----+-----+-----+-----+--
+   |  42 |  n  |  a1 |  a2 |  a3 |  a4 |  a1 |  a2 |  ...
+   +-----+-----+-----+-----+-----+-----+-----+-----+--
+
+8.4. Vendor Specific Information
+
+   This option is used by clients and servers to exchange vendor-
+   specific information.  The information is an opaque object of n
+   octets, presumably interpreted by vendor-specific code on the clients
+   and servers.  The definition of this information is vendor specific.
+   The vendor is indicated in the vendor class identifier option.
+   Servers not equipped to interpret the vendor-specific information
+   sent by a client MUST ignore it (although it may be reported).
+   Clients which do not receive desired vendor-specific information
+   SHOULD make an attempt to operate without it, although they may do so
+   (and announce they are doing so) in a degraded mode.
+
+   If a vendor potentially encodes more than one item of information in
+   this option, then the vendor SHOULD encode the option using
+   "Encapsulated vendor-specific options" as described below:
+
+   The Encapsulated vendor-specific options field SHOULD be encoded as a
+   sequence of code/length/value fields of identical syntax to the DHCP
+   options field with the following exceptions:
+
+      1) There SHOULD NOT be a "magic cookie" field in the encapsulated
+         vendor-specific extensions field.
+
+      2) Codes other than 0 or 255 MAY be redefined by the vendor within
+         the encapsulated vendor-specific extensions field, but SHOULD
+         conform to the tag-length-value syntax defined in section 2.
+
+      3) Code 255 (END), if present, signifies the end of the
+         encapsulated vendor extensions, not the end of the vendor
+         extensions field. If no code 255 is present, then the end of
+         the enclosing vendor-specific information field is taken as the
+         end of the encapsulated vendor-specific extensions field.
+
+   The code for this option is 43 and its minimum length is 1.
+
+   Code   Len   Vendor-specific information
+   +-----+-----+-----+-----+---
+   |  43 |  n  |  i1 |  i2 | ...
+   +-----+-----+-----+-----+---
+
+
+
+
+
+
+Alexander & Droms           Standards Track                    [Page 19]
+\f
+RFC 2132        DHCP Options and BOOTP Vendor Extensions      March 1997
+
+
+   When encapsulated vendor-specific extensions are used, the
+   information bytes 1-n have the following format:
+
+    Code   Len   Data item        Code   Len   Data item       Code
+   +-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+
+   |  T1 |  n  |  d1 |  d2 | ... |  T2 |  n  |  D1 |  D2 | ... | ... |
+   +-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+
+
+8.5. NetBIOS over TCP/IP Name Server Option
+
+   The NetBIOS name server (NBNS) option specifies a list of RFC
+   1001/1002 [19] [20] NBNS name servers listed in order of preference.
+
+   The code for this option is 44.  The minimum length of the option is
+   4 octets, and the length must always be a multiple of 4.
+
+    Code   Len           Address 1              Address 2
+   +-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+----
+   |  44 |  n  |  a1 |  a2 |  a3 |  a4 |  b1 |  b2 |  b3 |  b4 | ...
+   +-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+----
+
+8.6. NetBIOS over TCP/IP Datagram Distribution Server Option
+
+   The NetBIOS datagram distribution server (NBDD) option specifies a
+   list of RFC 1001/1002 NBDD servers listed in order of preference. The
+   code for this option is 45.  The minimum length of the option is 4
+   octets, and the length must always be a multiple of 4.
+
+    Code   Len           Address 1              Address 2
+   +-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+----
+   |  45 |  n  |  a1 |  a2 |  a3 |  a4 |  b1 |  b2 |  b3 |  b4 | ...
+   +-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+----
+
+8.7. NetBIOS over TCP/IP Node Type Option
+
+   The NetBIOS node type option allows NetBIOS over TCP/IP clients which
+   are configurable to be configured as described in RFC 1001/1002.  The
+   value is specified as a single octet which identifies the client type
+   as follows:
+
+      Value         Node Type
+      -----         ---------
+      0x1           B-node
+      0x2           P-node
+      0x4           M-node
+      0x8           H-node
+
+
+
+
+
+Alexander & Droms           Standards Track                    [Page 20]
+\f
+RFC 2132        DHCP Options and BOOTP Vendor Extensions      March 1997
+
+
+   In the above chart, the notation '0x' indicates a number in base-16
+   (hexadecimal).
+
+   The code for this option is 46.  The length of this option is always
+   1.
+
+    Code   Len  Node Type
+   +-----+-----+-----------+
+   |  46 |  1  | see above |
+   +-----+-----+-----------+
+
+8.8. NetBIOS over TCP/IP Scope Option
+
+   The NetBIOS scope option specifies the NetBIOS over TCP/IP scope
+   parameter for the client as specified in RFC 1001/1002. See [19],
+   [20], and [8] for character-set restrictions.
+
+   The code for this option is 47.  The minimum length of this option is
+   1.
+
+    Code   Len       NetBIOS Scope
+   +-----+-----+-----+-----+-----+-----+----
+   |  47 |  n  |  s1 |  s2 |  s3 |  s4 | ...
+   +-----+-----+-----+-----+-----+-----+----
+
+8.9. X Window System Font Server Option
+
+   This option specifies a list of X Window System [21] Font servers
+   available to the client. Servers SHOULD be listed in order of
+   preference.
+
+   The code for this option is 48.  The minimum length of this option is
+   4 octets, and the length MUST be a multiple of 4.
+
+    Code   Len         Address 1               Address 2
+   +-----+-----+-----+-----+-----+-----+-----+-----+---
+   |  48 |  n  |  a1 |  a2 |  a3 |  a4 |  a1 |  a2 |   ...
+   +-----+-----+-----+-----+-----+-----+-----+-----+---
+
+8.10. X Window System Display Manager Option
+
+   This option specifies a list of IP addresses of systems that are
+   running the X Window System Display Manager and are available to the
+   client.
+
+   Addresses SHOULD be listed in order of preference.
+
+
+
+
+
+Alexander & Droms           Standards Track                    [Page 21]
+\f
+RFC 2132        DHCP Options and BOOTP Vendor Extensions      March 1997
+
+
+   The code for the this option is 49. The minimum length of this option
+   is 4, and the length MUST be a multiple of 4.
+
+    Code   Len         Address 1               Address 2
+
+   +-----+-----+-----+-----+-----+-----+-----+-----+---
+   |  49 |  n  |  a1 |  a2 |  a3 |  a4 |  a1 |  a2 |   ...
+   +-----+-----+-----+-----+-----+-----+-----+-----+---
+
+8.11. Network Information Service+ Domain Option
+
+   This option specifies the name of the client's NIS+ [17] domain.  The
+   domain is formatted as a character string consisting of characters
+   from the NVT ASCII character set.
+
+   The code for this option is 64.  Its minimum length is 1.
+
+    Code   Len      NIS Client Domain Name
+   +-----+-----+-----+-----+-----+-----+---
+   |  64 |  n  |  n1 |  n2 |  n3 |  n4 | ...
+   +-----+-----+-----+-----+-----+-----+---
+
+8.12. Network Information Service+ Servers Option
+
+   This option specifies a list of IP addresses indicating NIS+ servers
+   available to the client.  Servers SHOULD be listed in order of
+   preference.
+
+   The code for this option is 65.  Its minimum length is 4, and the
+   length MUST be a multiple of 4.
+
+    Code   Len         Address 1               Address 2
+   +-----+-----+-----+-----+-----+-----+-----+-----+--
+   |  65 |  n  |  a1 |  a2 |  a3 |  a4 |  a1 |  a2 |  ...
+   +-----+-----+-----+-----+-----+-----+-----+-----+--
+
+8.13. Mobile IP Home Agent option
+
+   This option specifies a list of IP addresses indicating mobile IP
+   home agents available to the client.  Agents SHOULD be listed in
+   order of preference.
+
+   The code for this option is 68.  Its minimum length is 0 (indicating
+   no home agents are available) and the length MUST be a multiple of 4.
+   It is expected that the usual length will be four octets, containing
+   a single home agent's address.
+
+
+
+
+
+Alexander & Droms           Standards Track                    [Page 22]
+\f
+RFC 2132        DHCP Options and BOOTP Vendor Extensions      March 1997
+
+
+    Code Len    Home Agent Addresses (zero or more)
+   +-----+-----+-----+-----+-----+-----+--
+   | 68  |  n  | a1  | a2  | a3  | a4  | ...
+   +-----+-----+-----+-----+-----+-----+--
+
+8.14. Simple Mail Transport Protocol (SMTP) Server Option
+
+   The SMTP server option specifies a list of SMTP servers available to
+   the client.  Servers SHOULD be listed in order of preference.
+
+   The code for the SMTP server option is 69.  The minimum length for
+   this option is 4 octets, and the length MUST always be a multiple of
+   4.
+
+    Code   Len         Address 1               Address 2
+   +-----+-----+-----+-----+-----+-----+-----+-----+--
+   | 69  |  n  |  a1 |  a2 |  a3 |  a4 |  a1 |  a2 |  ...
+   +-----+-----+-----+-----+-----+-----+-----+-----+--
+
+8.15. Post Office Protocol (POP3) Server Option
+
+   The POP3 server option specifies a list of POP3 available to the
+   client.  Servers SHOULD be listed in order of preference.
+
+   The code for the POP3 server option is 70.  The minimum length for
+   this option is 4 octets, and the length MUST always be a multiple of
+   4.
+
+    Code   Len         Address 1               Address 2
+   +-----+-----+-----+-----+-----+-----+-----+-----+--
+   | 70  |  n  |  a1 |  a2 |  a3 |  a4 |  a1 |  a2 |  ...
+   +-----+-----+-----+-----+-----+-----+-----+-----+--
+
+8.16. Network News Transport Protocol (NNTP) Server Option
+
+   The NNTP server option specifies a list of NNTP available to the
+   client.  Servers SHOULD be listed in order of preference.
+
+   The code for the NNTP server option is 71. The minimum length for
+   this option is 4 octets, and the length MUST always be a multiple of
+   4.
+
+    Code   Len         Address 1               Address 2
+   +-----+-----+-----+-----+-----+-----+-----+-----+--
+   | 71  |  n  |  a1 |  a2 |  a3 |  a4 |  a1 |  a2 |  ...
+   +-----+-----+-----+-----+-----+-----+-----+-----+--
+
+
+
+
+
+Alexander & Droms           Standards Track                    [Page 23]
+\f
+RFC 2132        DHCP Options and BOOTP Vendor Extensions      March 1997
+
+
+8.17. Default World Wide Web (WWW) Server Option
+
+   The WWW server option specifies a list of WWW available to the
+   client.  Servers SHOULD be listed in order of preference.
+
+   The code for the WWW server option is 72.  The minimum length for
+   this option is 4 octets, and the length MUST always be a multiple of
+   4.
+
+    Code   Len         Address 1               Address 2
+   +-----+-----+-----+-----+-----+-----+-----+-----+--
+   | 72  |  n  |  a1 |  a2 |  a3 |  a4 |  a1 |  a2 |  ...
+   +-----+-----+-----+-----+-----+-----+-----+-----+--
+
+8.18. Default Finger Server Option
+
+   The Finger server option specifies a list of Finger available to the
+   client.  Servers SHOULD be listed in order of preference.
+
+   The code for the Finger server option is 73.  The minimum length for
+   this option is 4 octets, and the length MUST always be a multiple of
+   4.
+
+    Code   Len         Address 1               Address 2
+   +-----+-----+-----+-----+-----+-----+-----+-----+--
+   | 73  |  n  |  a1 |  a2 |  a3 |  a4 |  a1 |  a2 |  ...
+   +-----+-----+-----+-----+-----+-----+-----+-----+--
+
+8.19. Default Internet Relay Chat (IRC) Server Option
+
+   The IRC server option specifies a list of IRC available to the
+   client.  Servers SHOULD be listed in order of preference.
+
+   The code for the IRC server option is 74.  The minimum length for
+   this option is 4 octets, and the length MUST always be a multiple of
+   4.
+
+    Code   Len         Address 1               Address 2
+   +-----+-----+-----+-----+-----+-----+-----+-----+--
+   | 74  |  n  |  a1 |  a2 |  a3 |  a4 |  a1 |  a2 |  ...
+   +-----+-----+-----+-----+-----+-----+-----+-----+--
+
+8.20. StreetTalk Server Option
+
+   The StreetTalk server option specifies a list of StreetTalk servers
+   available to the client.  Servers SHOULD be listed in order of
+   preference.
+
+
+
+
+Alexander & Droms           Standards Track                    [Page 24]
+\f
+RFC 2132        DHCP Options and BOOTP Vendor Extensions      March 1997
+
+
+   The code for the StreetTalk server option is 75.  The minimum length
+   for this option is 4 octets, and the length MUST always be a multiple
+   of 4.
+
+    Code   Len         Address 1               Address 2
+   +-----+-----+-----+-----+-----+-----+-----+-----+--
+   | 75  |  n  |  a1 |  a2 |  a3 |  a4 |  a1 |  a2 |  ...
+   +-----+-----+-----+-----+-----+-----+-----+-----+--
+
+8.21. StreetTalk Directory Assistance (STDA) Server Option
+
+   The StreetTalk Directory Assistance (STDA) server option specifies a
+   list of STDA servers available to the client.  Servers SHOULD be
+   listed in order of preference.
+
+   The code for the StreetTalk Directory Assistance server option is 76.
+   The minimum length for this option is 4 octets, and the length MUST
+   always be a multiple of 4.
+
+    Code   Len         Address 1               Address 2
+   +-----+-----+-----+-----+-----+-----+-----+-----+--
+   | 76  |  n  |  a1 |  a2 |  a3 |  a4 |  a1 |  a2 |  ...
+   +-----+-----+-----+-----+-----+-----+-----+-----+--
+
+9. DHCP Extensions
+
+   This section details the options that are specific to DHCP.
+
+9.1. Requested IP Address
+
+   This option is used in a client request (DHCPDISCOVER) to allow the
+   client to request that a particular IP address be assigned.
+
+   The code for this option is 50, and its length is 4.
+
+    Code   Len          Address
+   +-----+-----+-----+-----+-----+-----+
+   |  50 |  4  |  a1 |  a2 |  a3 |  a4 |
+   +-----+-----+-----+-----+-----+-----+
+
+9.2. IP Address Lease Time
+
+   This option is used in a client request (DHCPDISCOVER or DHCPREQUEST)
+   to allow the client to request a lease time for the IP address.  In a
+   server reply (DHCPOFFER), a DHCP server uses this option to specify
+   the lease time it is willing to offer.
+
+
+
+
+
+Alexander & Droms           Standards Track                    [Page 25]
+\f
+RFC 2132        DHCP Options and BOOTP Vendor Extensions      March 1997
+
+
+   The time is in units of seconds, and is specified as a 32-bit
+   unsigned integer.
+
+   The code for this option is 51, and its length is 4.
+
+    Code   Len         Lease Time
+   +-----+-----+-----+-----+-----+-----+
+   |  51 |  4  |  t1 |  t2 |  t3 |  t4 |
+   +-----+-----+-----+-----+-----+-----+
+
+9.3. Option Overload
+
+   This option is used to indicate that the DHCP 'sname' or 'file'
+   fields are being overloaded by using them to carry DHCP options. A
+   DHCP server inserts this option if the returned parameters will
+   exceed the usual space allotted for options.
+
+   If this option is present, the client interprets the specified
+   additional fields after it concludes interpretation of the standard
+   option fields.
+
+   The code for this option is 52, and its length is 1.  Legal values
+   for this option are:
+
+           Value   Meaning
+           -----   --------
+             1     the 'file' field is used to hold options
+             2     the 'sname' field is used to hold options
+             3     both fields are used to hold options
+
+    Code   Len  Value
+   +-----+-----+-----+
+   |  52 |  1  |1/2/3|
+   +-----+-----+-----+
+
+9.4 TFTP server name
+
+   This option is used to identify a TFTP server when the 'sname' field
+   in the DHCP header has been used for DHCP options.
+
+   The code for this option is 66, and its minimum length is 1.
+
+       Code  Len   TFTP server
+      +-----+-----+-----+-----+-----+---
+      | 66  |  n  |  c1 |  c2 |  c3 | ...
+      +-----+-----+-----+-----+-----+---
+
+
+
+
+
+Alexander & Droms           Standards Track                    [Page 26]
+\f
+RFC 2132        DHCP Options and BOOTP Vendor Extensions      March 1997
+
+
+9.5 Bootfile name
+
+   This option is used to identify a bootfile when the 'file' field in
+   the DHCP header has been used for DHCP options.
+
+   The code for this option is 67, and its minimum length is 1.
+
+       Code  Len   Bootfile name
+      +-----+-----+-----+-----+-----+---
+      | 67  |  n  |  c1 |  c2 |  c3 | ...
+      +-----+-----+-----+-----+-----+---
+
+9.6. DHCP Message Type
+
+   This option is used to convey the type of the DHCP message.  The code
+   for this option is 53, and its length is 1.  Legal values for this
+   option are:
+
+           Value   Message Type
+           -----   ------------
+             1     DHCPDISCOVER
+             2     DHCPOFFER
+             3     DHCPREQUEST
+             4     DHCPDECLINE
+             5     DHCPACK
+             6     DHCPNAK
+             7     DHCPRELEASE
+             8     DHCPINFORM
+
+    Code   Len  Type
+   +-----+-----+-----+
+   |  53 |  1  | 1-9 |
+   +-----+-----+-----+
+
+9.7. Server Identifier
+
+   This option is used in DHCPOFFER and DHCPREQUEST messages, and may
+   optionally be included in the DHCPACK and DHCPNAK messages.  DHCP
+   servers include this option in the DHCPOFFER in order to allow the
+   client to distinguish between lease offers.  DHCP clients use the
+   contents of the 'server identifier' field as the destination address
+   for any DHCP messages unicast to the DHCP server.  DHCP clients also
+   indicate which of several lease offers is being accepted by including
+   this option in a DHCPREQUEST message.
+
+   The identifier is the IP address of the selected server.
+
+   The code for this option is 54, and its length is 4.
+
+
+
+Alexander & Droms           Standards Track                    [Page 27]
+\f
+RFC 2132        DHCP Options and BOOTP Vendor Extensions      March 1997
+
+
+    Code   Len            Address
+   +-----+-----+-----+-----+-----+-----+
+   |  54 |  4  |  a1 |  a2 |  a3 |  a4 |
+   +-----+-----+-----+-----+-----+-----+
+
+9.8. Parameter Request List
+
+   This option is used by a DHCP client to request values for specified
+   configuration parameters.  The list of requested parameters is
+   specified as n octets, where each octet is a valid DHCP option code
+   as defined in this document.
+
+   The client MAY list the options in order of preference.  The DHCP
+   server is not required to return the options in the requested order,
+   but MUST try to insert the requested options in the order requested
+   by the client.
+
+   The code for this option is 55.  Its minimum length is 1.
+
+    Code   Len   Option Codes
+   +-----+-----+-----+-----+---
+   |  55 |  n  |  c1 |  c2 | ...
+   +-----+-----+-----+-----+---
+
+9.9. Message
+
+   This option is used by a DHCP server to provide an error message to a
+   DHCP client in a DHCPNAK message in the event of a failure. A client
+   may use this option in a DHCPDECLINE message to indicate the why the
+   client declined the offered parameters.  The message consists of n
+   octets of NVT ASCII text, which the client may display on an
+   available output device.
+
+   The code for this option is 56 and its minimum length is 1.
+
+    Code   Len     Text
+   +-----+-----+-----+-----+---
+   |  56 |  n  |  c1 |  c2 | ...
+   +-----+-----+-----+-----+---
+
+9.10. Maximum DHCP Message Size
+
+   This option specifies the maximum length DHCP message that it is
+   willing to accept.  The length is specified as an unsigned 16-bit
+   integer.  A client may use the maximum DHCP message size option in
+   DHCPDISCOVER or DHCPREQUEST messages, but should not use the option
+   in DHCPDECLINE messages.
+
+
+
+
+Alexander & Droms           Standards Track                    [Page 28]
+\f
+RFC 2132        DHCP Options and BOOTP Vendor Extensions      March 1997
+
+
+   The code for this option is 57, and its length is 2.  The minimum
+   legal value is 576 octets.
+
+    Code   Len     Length
+   +-----+-----+-----+-----+
+   |  57 |  2  |  l1 |  l2 |
+   +-----+-----+-----+-----+
+
+9.11. Renewal (T1) Time Value
+
+   This option specifies the time interval from address assignment until
+   the client transitions to the RENEWING state.
+
+   The value is in units of seconds, and is specified as a 32-bit
+   unsigned integer.
+
+   The code for this option is 58, and its length is 4.
+
+    Code   Len         T1 Interval
+   +-----+-----+-----+-----+-----+-----+
+   |  58 |  4  |  t1 |  t2 |  t3 |  t4 |
+   +-----+-----+-----+-----+-----+-----+
+
+9.12. Rebinding (T2) Time Value
+
+   This option specifies the time interval from address assignment until
+   the client transitions to the REBINDING state.
+
+   The value is in units of seconds, and is specified as a 32-bit
+   unsigned integer.
+
+   The code for this option is 59, and its length is 4.
+
+    Code   Len         T2 Interval
+   +-----+-----+-----+-----+-----+-----+
+   |  59 |  4  |  t1 |  t2 |  t3 |  t4 |
+   +-----+-----+-----+-----+-----+-----+
+
+9.13. Vendor class identifier
+
+   This option is used by DHCP clients to optionally identify the vendor
+   type and configuration of a DHCP client.  The information is a string
+   of n octets, interpreted by servers.  Vendors may choose to define
+   specific vendor class identifiers to convey particular configuration
+   or other identification information about a client.  For example, the
+   identifier may encode the client's hardware configuration.  Servers
+   not equipped to interpret the class-specific information sent by a
+   client MUST ignore it (although it may be reported). Servers that
+
+
+
+Alexander & Droms           Standards Track                    [Page 29]
+\f
+RFC 2132        DHCP Options and BOOTP Vendor Extensions      March 1997
+
+
+   respond SHOULD only use option 43 to return the vendor-specific
+   information to the client.
+
+   The code for this option is 60, and its minimum length is 1.
+
+   Code   Len   Vendor class Identifier
+   +-----+-----+-----+-----+---
+   |  60 |  n  |  i1 |  i2 | ...
+   +-----+-----+-----+-----+---
+
+9.14. Client-identifier
+
+   This option is used by DHCP clients to specify their unique
+   identifier.  DHCP servers use this value to index their database of
+   address bindings.  This value is expected to be unique for all
+   clients in an administrative domain.
+
+   Identifiers SHOULD be treated as opaque objects by DHCP servers.
+
+   The client identifier MAY consist of type-value pairs similar to the
+   'htype'/'chaddr' fields defined in [3]. For instance, it MAY consist
+   of a hardware type and hardware address. In this case the type field
+   SHOULD be one of the ARP hardware types defined in STD2 [22].  A
+   hardware type of 0 (zero) should be used when the value field
+   contains an identifier other than a hardware address (e.g. a fully
+   qualified domain name).
+
+   For correct identification of clients, each client's client-
+   identifier MUST be unique among the client-identifiers used on the
+   subnet to which the client is attached.  Vendors and system
+   administrators are responsible for choosing client-identifiers that
+   meet this requirement for uniqueness.
+
+   The code for this option is 61, and its minimum length is 2.
+
+   Code   Len   Type  Client-Identifier
+   +-----+-----+-----+-----+-----+---
+   |  61 |  n  |  t1 |  i1 |  i2 | ...
+   +-----+-----+-----+-----+-----+---
+
+
+
+
+
+
+
+
+
+
+
+
+Alexander & Droms           Standards Track                    [Page 30]
+\f
+RFC 2132        DHCP Options and BOOTP Vendor Extensions      March 1997
+
+
+10. Defining new extensions
+
+   The author of a new DHCP option will follow these steps to obtain
+   acceptance of the option as a part of the DHCP Internet Standard:
+
+   1. The author devises the new option.
+   2. The author requests a number for the new option from IANA by
+      contacting:
+      Internet Assigned Numbers Authority (IANA)
+      USC/Information Sciences Institute
+      4676 Admiralty Way
+      Marina del Rey, California  90292-6695
+
+      or by email as: iana@iana.org
+
+   3. The author documents the new option, using the newly obtained
+      option number, as an Internet Draft.
+   4. The author submits the Internet Draft for review through the IETF
+      standards process as defined in "Internet Official Protocol
+      Standards" (STD 1).  The new option will be submitted for eventual
+      acceptance as an Internet Standard.
+   5. The new option progresses through the IETF standards process; the
+      new option will be reviewed by the Dynamic Host Configuration
+      Working Group (if that group still exists), or as an Internet
+      Draft not submitted by an IETF working group.
+   6. If the new option fails to gain acceptance as an Internet
+      Standard, the assigned option number will be returned to IANA for
+      reassignment.
+
+      This procedure for defining new extensions will ensure that:
+
+      * allocation of new option numbers is coordinated from a single
+        authority,
+      * new options are reviewed for technical correctness and
+        appropriateness, and
+      * documentation for new options is complete and published.
+
+11. Acknowledgements
+
+   The author thanks the many (and too numerous to mention!) members of
+   the DHC WG for their tireless and ongoing efforts in the development
+   of DHCP and this document.
+
+   The efforts of J Allard, Mike Carney, Dave Lapp, Fred Lien and John
+   Mendonca in organizing DHCP interoperability testing sessions are
+   gratefully acknowledged.
+
+
+
+
+
+Alexander & Droms           Standards Track                    [Page 31]
+\f
+RFC 2132        DHCP Options and BOOTP Vendor Extensions      March 1997
+
+
+   The development of this document was supported in part by grants from
+   the Corporation for National Research Initiatives (CNRI), Bucknell
+   University and Sun Microsystems.
+
+12. References
+
+   [1] Droms, R., "Dynamic Host Configuration Protocol", RFC 2131,
+       Bucknell University, March 1997.
+
+   [2] Reynolds, J., "BOOTP Vendor Information Extensions", RFC 1497,
+       USC/Information Sciences Institute, August 1993.
+
+   [3] Croft, W., and J. Gilmore, "Bootstrap Protocol", RFC 951,
+       Stanford University and Sun Microsystems, September 1985.
+
+   [4] Braden, R., Editor, "Requirements for Internet Hosts -
+       Communication Layers", STD 3, RFC 1122, USC/Information Sciences
+       Institute, October 1989.
+
+   [5] Mogul, J., and J. Postel, "Internet Standard Subnetting
+       Procedure", STD 5, RFC 950, USC/Information Sciences Institute,
+       August 1985.
+
+   [6] Postel, J., and K. Harrenstien, "Time Protocol", STD 26, RFC
+       868, USC/Information Sciences Institute, SRI, May 1983.
+
+   [7] Postel, J., "Name Server", IEN 116, USC/Information Sciences
+       Institute, August 1979.
+
+   [8] Mockapetris, P., "Domain Names - Implementation and
+       Specification", STD 13, RFC 1035, USC/Information Sciences
+       Institute, November 1987.
+
+   [9] Postel, J., "Quote of the Day Protocol", STD 23, RFC 865,
+       USC/Information Sciences Institute, May 1983.
+
+   [10] McLaughlin, L., "Line Printer Daemon Protocol", RFC 1179, The
+        Wollongong Group, August 1990.
+
+   [11] Accetta, M., "Resource Location Protocol", RFC 887, CMU,
+        December 1983.
+
+   [12] Mogul, J. and S. Deering, "Path MTU Discovery", RFC 1191,
+        DECWRL,  Stanford University, November 1990.
+
+   [13] Deering, S., "ICMP Router Discovery Messages", RFC 1256,
+        Xerox PARC, September 1991.
+
+
+
+
+Alexander & Droms           Standards Track                    [Page 32]
+\f
+RFC 2132        DHCP Options and BOOTP Vendor Extensions      March 1997
+
+
+   [14] Leffler, S. and M. Karels, "Trailer Encapsulations", RFC 893,
+        U. C. Berkeley, April 1984.
+
+   [15] Hornig, C., "Standard for the Transmission of IP Datagrams over
+        Ethernet Networks", RFC 894, Symbolics, April 1984.
+
+   [16] Postel, J. and J. Reynolds, "Standard for the Transmission of
+        IP Datagrams Over IEEE 802 Networks", RFC 1042,  USC/Information
+        Sciences Institute, February 1988.
+
+   [17] Sun Microsystems, "System and Network Administration", March
+        1990.
+
+   [18] Mills, D., "Internet Time Synchronization: The Network Time
+        Protocol", RFC 1305, UDEL, March 1992.
+
+   [19] NetBIOS Working Group, "Protocol Standard for a NetBIOS Service
+        on a TCP/UDP transport: Concepts and Methods", STD 19, RFC 1001,
+        March 1987.
+
+   [20] NetBIOS Working Group, "Protocol Standard for a NetBIOS Service
+        on a TCP/UDP transport: Detailed Specifications", STD 19, RFC
+        1002, March 1987.
+
+   [21] Scheifler, R., "FYI On the X Window System", FYI 6, RFC 1198,
+        MIT Laboratory for Computer Science, January 1991.
+
+   [22] Reynolds, J., and J. Postel, "Assigned Numbers", STD 2, RFC 1700,
+        USC/Information Sciences Institute, July 1992.
+
+13. Security Considerations
+
+   Security issues are not discussed in this memo.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+Alexander & Droms           Standards Track                    [Page 33]
+\f
+RFC 2132        DHCP Options and BOOTP Vendor Extensions      March 1997
+
+
+14. Authors' Addresses
+
+   Steve Alexander
+   Silicon Graphics, Inc.
+   2011 N. Shoreline Boulevard
+   Mailstop 510
+   Mountain View, CA 94043-1389
+
+   Phone: (415) 933-6172
+   EMail: sca@engr.sgi.com
+
+
+   Ralph Droms
+   Bucknell University
+   Lewisburg, PA 17837
+
+   Phone: (717) 524-1145
+   EMail: droms@bucknell.edu
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+Alexander & Droms           Standards Track                    [Page 34]
+\f
diff --git a/doc/service-api.txt b/doc/service-api.txt
new file mode 100644 (file)
index 0000000..ab1aa34
--- /dev/null
@@ -0,0 +1,149 @@
+Service hierarchy
+=================
+
+Service                org.moblin.connman
+Interface      org.moblin.connman.Service
+Object path    [variable prefix]/{service0,service1,...}
+
+Methods                dict GetProperties()
+
+                       Returns properties for the service object. See
+                       the properties section for available properties.
+
+                       Possible Errors: [service].Error.InvalidArguments
+
+               void Connect()
+
+                       Connect this service. It will attempt to connect
+                       WiFi, WiMAX 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.
+
+                       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.
+
+                       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.
+
+                       This is similar to setting the Favorite property
+                       to false, but that is currently not supported.
+
+                       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
+
+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" and "ready".
+
+               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.
+
+                       For Ethernet devices and hidden WiFi networks  it is
+                       not guaranteed that this property is 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.
+
+               string Mode [readonly]
+
+                       If the service type is WiFi, then this property is
+                       present and contains the mode of the network. The
+                       possible values are "managed" or "adhoc".
+
+                       This property might be only present for WiFi
+                       services.
+
+               string Security [readonly]
+
+                       If the service type is WiFi, then this property is
+                       present and contains the security method or key
+                       management setting.
+
+                       Possible values are "none", "wep", "wpa" and "rsn".
+
+                       This property might be only present for WiFi
+                       services.
+
+               string Passphrase [readwrite]
+
+                       If the service type is WiFi, then this property
+                       can be used to store a passphrase.
+
+                       This property is still experimental and might be
+                       removed in future version.
+
+               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.
+
+                       Setting this property to true has no effect at all
+                       and setting it to false is similar to the Remove()
+                       method. So for now it will be considered a read
+                       only property.
diff --git a/doc/version.xml.in b/doc/version.xml.in
new file mode 100644 (file)
index 0000000..d78bda9
--- /dev/null
@@ -0,0 +1 @@
+@VERSION@
diff --git a/gatchat/Makefile.am b/gatchat/Makefile.am
new file mode 100644 (file)
index 0000000..9f1da11
--- /dev/null
@@ -0,0 +1,9 @@
+
+noinst_LTLIBRARIES = libgatchat.la
+
+libgatchat_la_SOURCES = gatchat.h gatchat.c gatresult.h gatresult.c \
+                                               ringbuffer.h ringbuffer.c
+
+AM_CFLAGS = @GLIB_CFLAGS@
+
+MAINTAINERCLEANFILES = Makefile.in
diff --git a/gatchat/gatchat.c b/gatchat/gatchat.c
new file mode 100644 (file)
index 0000000..af2147b
--- /dev/null
@@ -0,0 +1,1143 @@
+/*
+ *
+ *  AT chat library with GLib integration
+ *
+ *  Copyright (C) 2008-2009  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 <string.h>
+#include <assert.h>
+
+#include <glib.h>
+
+#include "ringbuffer.h"
+#include "gatresult.h"
+#include "gatchat.h"
+
+/* #define WRITE_SCHEDULER_DEBUG 1 */
+
+static void g_at_chat_wakeup_writer(GAtChat *chat);
+
+enum chat_state {
+       PARSER_STATE_IDLE = 0,
+       PARSER_STATE_INITIAL_CR,
+       PARSER_STATE_INITIAL_LF,
+       PARSER_STATE_RESPONSE,
+       PARSER_STATE_TERMINATOR_CR,
+       PARSER_STATE_RESPONSE_COMPLETE,
+       PARSER_STATE_GUESS_MULTILINE_RESPONSE,
+       PARSER_STATE_MULTILINE_RESPONSE,
+       PARSER_STATE_MULTILINE_TERMINATOR_CR,
+       PARSER_STATE_MULTILINE_COMPLETE,
+       PARSER_STATE_PDU,
+       PARSER_STATE_PDU_CR,
+       PARSER_STATE_PDU_COMPLETE,
+       PARSER_STATE_PROMPT,
+       PARSER_STATE_PROMPT_COMPLETE
+};
+
+struct at_command {
+       char *cmd;
+       char **prefixes;
+       guint id;
+       GAtResultFunc callback;
+       gpointer user_data;
+       GDestroyNotify notify;
+};
+
+struct at_notify_node {
+       guint id;
+       GAtNotifyFunc callback;
+       gpointer user_data;
+       GDestroyNotify notify;
+};
+
+struct at_notify {
+       GSList *nodes;
+       gboolean pdu;
+};
+
+struct _GAtChat {
+       gint ref_count;                         /* Ref count */
+       guint next_cmd_id;                      /* Next command id */
+       guint next_notify_id;                   /* Next notify id */
+       guint read_watch;                       /* GSource read id, 0 if none */
+       guint write_watch;                      /* GSource write id, 0 if none */
+       GIOChannel *channel;                    /* channel */
+       GQueue *command_queue;                  /* Command queue */
+       guint cmd_bytes_written;                /* bytes written from cmd */
+       GHashTable *notify_list;                /* List of notification reg */
+       GAtDisconnectFunc user_disconnect;      /* user disconnect func */
+       gpointer user_disconnect_data;          /* user disconnect data */
+       struct ring_buffer *buf;                /* Current read buffer */
+       guint read_so_far;                      /* Number of bytes processed */
+       gboolean disconnecting;                 /* Whether we're disconnecting */
+       enum chat_state state;          /* Current chat state */
+       int flags;
+       char *pdu_notify;                       /* Unsolicited Resp w/ PDU */
+       GSList *response_lines;                 /* char * lines of the response */
+       char *wakeup;                           /* command sent to wakeup modem */
+       gdouble inactivity_time;                /* Period of inactivity */
+       guint wakeup_timeout;                   /* How long to wait for resp */
+       GTimer *wakeup_timer;                   /* Keep track of elapsed time */
+};
+
+static gint at_notify_node_compare_by_id(gconstpointer a, gconstpointer b)
+{
+       const struct at_notify_node *node = a;
+       guint id = GPOINTER_TO_UINT(b);
+
+       if (node->id < id)
+               return -1;
+
+       if (node->id > id)
+               return 1;
+
+       return 0;
+}
+
+static void at_notify_node_destroy(struct at_notify_node *node)
+{
+       if (node->notify)
+               node->notify(node->user_data);
+
+       g_free(node);
+}
+
+static void at_notify_destroy(struct at_notify *notify)
+{
+       g_slist_foreach(notify->nodes, (GFunc) at_notify_node_destroy, NULL);
+       g_free(notify);
+}
+
+static gint at_command_compare_by_id(gconstpointer a, gconstpointer b)
+{
+       const struct at_command *command = a;
+       guint id = GPOINTER_TO_UINT(b);
+
+       if (command->id < id)
+               return -1;
+
+       if (command->id > id)
+               return 1;
+
+       return 0;
+}
+
+static struct at_command *at_command_create(const char *cmd,
+                                               const char **prefix_list,
+                                               GAtResultFunc func,
+                                               gpointer user_data,
+                                               GDestroyNotify notify)
+{
+       struct at_command *c;
+       gsize len;
+       char **prefixes = NULL;
+
+       if (prefix_list) {
+               int num_prefixes = 0;
+               int i;
+
+               while (prefix_list[num_prefixes])
+                       num_prefixes += 1;
+
+               prefixes = g_new(char *, num_prefixes + 1);
+
+               for (i = 0; i < num_prefixes; i++)
+                       prefixes[i] = strdup(prefix_list[i]);
+
+               prefixes[num_prefixes] = NULL;
+       }
+
+       c = g_try_new0(struct at_command, 1);
+
+       if (!c)
+               return 0;
+
+       len = strlen(cmd);
+       c->cmd = g_try_new(char, len + 2);
+
+       if (!c->cmd) {
+               g_free(c);
+               return 0;
+       }
+
+       memcpy(c->cmd, cmd, len);
+
+       /* If we have embedded '\r' then this is a command expecting a prompt
+        * from the modem.  Embed Ctrl-Z at the very end automatically
+        */
+       if (strchr(cmd, '\r'))
+               c->cmd[len] = 26;
+       else
+               c->cmd[len] = '\r';
+
+       c->cmd[len+1] = '\0';
+
+       c->prefixes = prefixes;
+       c->callback = func;
+       c->user_data = user_data;
+       c->notify = notify;
+
+       return c;
+}
+
+static void at_command_destroy(struct at_command *cmd)
+{
+       if (cmd->notify)
+               cmd->notify(cmd->user_data);
+
+       g_strfreev(cmd->prefixes);
+       g_free(cmd->cmd);
+       g_free(cmd);
+}
+
+static void g_at_chat_cleanup(GAtChat *chat)
+{
+       struct at_command *c;
+
+       ring_buffer_free(chat->buf);
+       chat->buf = NULL;
+
+       /* Cleanup pending commands */
+       while ((c = g_queue_pop_head(chat->command_queue)))
+               at_command_destroy(c);
+
+       g_queue_free(chat->command_queue);
+       chat->command_queue = NULL;
+
+       /* Cleanup any response lines we have pending */
+       g_slist_foreach(chat->response_lines, (GFunc)g_free, NULL);
+       g_slist_free(chat->response_lines);
+       chat->response_lines = NULL;
+
+       /* Cleanup registered notifications */
+       g_hash_table_destroy(chat->notify_list);
+       chat->notify_list = NULL;
+
+       if (chat->pdu_notify) {
+               g_free(chat->pdu_notify);
+               chat->pdu_notify = NULL;
+       }
+
+       if (chat->wakeup) {
+               g_free(chat->wakeup);
+               chat->wakeup = NULL;
+       }
+
+       if (chat->wakeup_timer) {
+               g_timer_destroy(chat->wakeup_timer);
+               chat->wakeup_timer = 0;
+       }
+}
+
+static void read_watcher_destroy_notify(GAtChat *chat)
+{
+       chat->read_watch = 0;
+
+       if (chat->disconnecting)
+               return;
+
+       chat->channel = NULL;
+
+       g_at_chat_cleanup(chat);
+
+       if (chat->user_disconnect)
+               chat->user_disconnect(chat->user_disconnect_data);
+}
+
+static void write_watcher_destroy_notify(GAtChat *chat)
+{
+       chat->write_watch = 0;
+}
+
+static void at_notify_call_callback(gpointer data, gpointer user_data)
+{
+       struct at_notify_node *node = data;
+       GAtResult *result = user_data;
+
+       node->callback(result, node->user_data);
+}
+
+static gboolean g_at_chat_match_notify(GAtChat *chat, char *line)
+{
+       GHashTableIter iter;
+       struct at_notify *notify;
+       char *prefix;
+       gpointer key, value;
+       gboolean ret = FALSE;
+       GAtResult result;
+
+       g_hash_table_iter_init(&iter, chat->notify_list);
+       result.lines = 0;
+       result.final_or_pdu = 0;
+
+       while (g_hash_table_iter_next(&iter, &key, &value)) {
+               prefix = key;
+               notify = value;
+
+               if (!g_str_has_prefix(line, key))
+                       continue;
+
+               if (notify->pdu) {
+                       chat->pdu_notify = line;
+                       chat->state = PARSER_STATE_PDU;
+                       return TRUE;
+               }
+
+               if (!result.lines)
+                       result.lines = g_slist_prepend(NULL, line);
+
+               g_slist_foreach(notify->nodes, at_notify_call_callback,
+                                       &result);
+               ret = TRUE;
+       }
+
+       if (ret) {
+               g_slist_free(result.lines);
+               g_free(line);
+               chat->state = PARSER_STATE_IDLE;
+       }
+
+       return ret;
+}
+
+static void g_at_chat_finish_command(GAtChat *p, gboolean ok,
+                                               char *final)
+{
+       struct at_command *cmd = g_queue_pop_head(p->command_queue);
+
+       /* Cannot happen, but lets be paranoid */
+       if (!cmd)
+               return;
+
+       if (cmd->callback) {
+               GAtResult result;
+
+               p->response_lines = g_slist_reverse(p->response_lines);
+
+               result.final_or_pdu = final;
+               result.lines = p->response_lines;
+
+               cmd->callback(ok, &result, cmd->user_data);
+       }
+
+       g_slist_foreach(p->response_lines, (GFunc)g_free, NULL);
+       g_slist_free(p->response_lines);
+       p->response_lines = NULL;
+
+       g_free(final);
+
+       at_command_destroy(cmd);
+
+       p->cmd_bytes_written = 0;
+
+       if (g_queue_peek_head(p->command_queue))
+               g_at_chat_wakeup_writer(p);
+}
+
+struct terminator_info {
+       const char *terminator;
+       int len;
+       gboolean success;
+};
+
+static struct terminator_info terminator_table[] = {
+       { "OK", -1, TRUE },
+       { "ERROR", -1, FALSE },
+       { "NO DIALTONE", -1, FALSE },
+       { "BUSY", -1, FALSE },
+       { "NO CARRIER", -1, FALSE },
+       { "CONNECT", -1, TRUE },
+       { "NO ANSWER", -1, FALSE },
+       { "+CMS ERROR:", 11, FALSE },
+       { "+CME ERROR:", 11, FALSE },
+       { "+EXT ERROR:", 11, FALSE }
+};
+
+static gboolean g_at_chat_handle_command_response(GAtChat *p,
+                                                       struct at_command *cmd,
+                                                       char *line)
+{
+       int i;
+       int size = sizeof(terminator_table) / sizeof(struct terminator_info);
+
+       p->state = PARSER_STATE_IDLE;
+
+       for (i = 0; i < size; i++) {
+               struct terminator_info *info = &terminator_table[i];
+
+               if (info->len == -1 && !strcmp(line, info->terminator)) {
+                       g_at_chat_finish_command(p, info->success, line);
+                       return TRUE;
+               }
+
+               if (info->len > 0 &&
+                       !strncmp(line, info->terminator, info->len)) {
+                       g_at_chat_finish_command(p, info->success, line);
+                       return TRUE;
+               }
+       }
+
+       if (cmd->prefixes) {
+               int i;
+
+               for (i = 0; cmd->prefixes[i]; i++)
+                       if (g_str_has_prefix(line, cmd->prefixes[i]))
+                               goto out;
+
+               return FALSE;
+       }
+
+out:
+       if (!(p->flags & G_AT_CHAT_FLAG_NO_LEADING_CRLF))
+               p->state = PARSER_STATE_GUESS_MULTILINE_RESPONSE;
+
+       p->response_lines = g_slist_prepend(p->response_lines,
+                                               line);
+
+       return TRUE;
+}
+
+static void have_line(GAtChat *p, gboolean strip_preceding)
+{
+       /* We're not going to copy terminal <CR><LF> */
+       unsigned int len = p->read_so_far - 2;
+       char *str;
+       struct at_command *cmd;
+
+       /* If we have preceding <CR><LF> modify the len */
+       if (strip_preceding)
+               len -= 2;
+
+       /* Make sure we have terminal null */
+       str = g_try_new(char, len + 1);
+
+       if (!str) {
+               ring_buffer_drain(p->buf, p->read_so_far);
+               return;
+       }
+
+       if (strip_preceding)
+               ring_buffer_drain(p->buf, 2);
+       ring_buffer_read(p->buf, str, len);
+       ring_buffer_drain(p->buf, 2);
+
+       str[len] = '\0';
+
+       /* Check for echo, this should not happen, but lets be paranoid */
+       if (!strncmp(str, "AT", 2) == TRUE)
+               goto done;
+
+       cmd = g_queue_peek_head(p->command_queue);
+
+       if (cmd && p->cmd_bytes_written == strlen(cmd->cmd) &&
+               g_at_chat_handle_command_response(p, cmd, str))
+               return;
+
+       if (g_at_chat_match_notify(p, str) == TRUE)
+               return;
+
+done:
+       /* No matches & no commands active, ignore line */
+       g_free(str);
+       p->state = PARSER_STATE_IDLE;
+}
+
+static void have_pdu(GAtChat *p)
+{
+       unsigned int len = p->read_so_far - 2;
+       char *pdu;
+       GHashTableIter iter;
+       struct at_notify *notify;
+       char *prefix;
+       gpointer key, value;
+       GAtResult result;
+
+       pdu = g_try_new(char, len + 1);
+
+       if (!pdu) {
+               ring_buffer_drain(p->buf, p->read_so_far);
+               goto out;
+       }
+
+       ring_buffer_read(p->buf, pdu, len);
+       ring_buffer_drain(p->buf, 2);
+
+       pdu[len] = '\0';
+
+       result.lines = g_slist_prepend(NULL, p->pdu_notify);
+       result.final_or_pdu = pdu;
+
+       g_hash_table_iter_init(&iter, p->notify_list);
+
+       while (g_hash_table_iter_next(&iter, &key, &value)) {
+               prefix = key;
+               notify = value;
+
+               if (!g_str_has_prefix(p->pdu_notify, prefix))
+                       continue;
+
+               if (!notify->pdu)
+                       continue;
+
+               g_slist_foreach(notify->nodes, at_notify_call_callback,
+                                       &result);
+       }
+
+       g_slist_free(result.lines);
+
+out:
+       g_free(p->pdu_notify);
+       p->pdu_notify = NULL;
+
+       if (pdu)
+               g_free(pdu);
+
+       p->state = PARSER_STATE_IDLE;
+}
+
+static inline void parse_char(GAtChat *chat, char byte)
+{
+       switch (chat->state) {
+       case PARSER_STATE_IDLE:
+               if (byte == '\r')
+                       chat->state = PARSER_STATE_INITIAL_CR;
+               else if (chat->flags & G_AT_CHAT_FLAG_NO_LEADING_CRLF) {
+                       if (byte == '>')
+                               chat->state = PARSER_STATE_PROMPT;
+                       else
+                               chat->state = PARSER_STATE_RESPONSE;
+               }
+               break;
+
+       case PARSER_STATE_INITIAL_CR:
+               if (byte == '\n')
+                       chat->state = PARSER_STATE_INITIAL_LF;
+               else if (byte != '\r' && /* Echo & no <CR><LF>?! */
+                       (chat->flags & G_AT_CHAT_FLAG_NO_LEADING_CRLF))
+                       chat->state = PARSER_STATE_RESPONSE;
+               else if (byte != '\r')
+                       chat->state = PARSER_STATE_IDLE;
+               break;
+
+       case PARSER_STATE_INITIAL_LF:
+               if (byte == '\r')
+                       chat->state = PARSER_STATE_TERMINATOR_CR;
+               else if (byte == '>')
+                       chat->state = PARSER_STATE_PROMPT;
+               else
+                       chat->state = PARSER_STATE_RESPONSE;
+               break;
+
+       case PARSER_STATE_RESPONSE:
+               if (byte == '\r')
+                       chat->state = PARSER_STATE_TERMINATOR_CR;
+               break;
+
+       case PARSER_STATE_TERMINATOR_CR:
+               if (byte == '\n')
+                       chat->state = PARSER_STATE_RESPONSE_COMPLETE;
+               else
+                       chat->state = PARSER_STATE_IDLE;
+               break;
+
+       case PARSER_STATE_GUESS_MULTILINE_RESPONSE:
+               if (byte == '\r')
+                       chat->state = PARSER_STATE_INITIAL_CR;
+               else
+                       chat->state = PARSER_STATE_MULTILINE_RESPONSE;
+               break;
+
+       case PARSER_STATE_MULTILINE_RESPONSE:
+               if (byte == '\r')
+                       chat->state = PARSER_STATE_MULTILINE_TERMINATOR_CR;
+               break;
+
+       case PARSER_STATE_MULTILINE_TERMINATOR_CR:
+               if (byte == '\n')
+                       chat->state = PARSER_STATE_MULTILINE_COMPLETE;
+               break;
+
+       case PARSER_STATE_PDU:
+               if (byte == '\r')
+                       chat->state = PARSER_STATE_PDU_CR;
+               break;
+
+       case PARSER_STATE_PDU_CR:
+               if (byte == '\n')
+                       chat->state = PARSER_STATE_PDU_COMPLETE;
+               break;
+
+       case PARSER_STATE_PROMPT:
+               if (byte == ' ')
+                       chat->state = PARSER_STATE_PROMPT_COMPLETE;
+               else
+                       chat->state = PARSER_STATE_RESPONSE;
+
+       case PARSER_STATE_RESPONSE_COMPLETE:
+       case PARSER_STATE_PDU_COMPLETE:
+       case PARSER_STATE_MULTILINE_COMPLETE:
+       default:
+               /* This really shouldn't happen */
+               assert(TRUE);
+               return;
+       }
+}
+
+static void new_bytes(GAtChat *p)
+{
+       unsigned int len = ring_buffer_len(p->buf);
+       unsigned int wrap = ring_buffer_len_no_wrap(p->buf);
+       unsigned char *buf = ring_buffer_read_ptr(p->buf, p->read_so_far);
+
+       while (p->read_so_far < len) {
+               parse_char(p, *buf);
+
+               buf += 1;
+               p->read_so_far += 1;
+
+               if (p->read_so_far == wrap) {
+                       buf = ring_buffer_read_ptr(p->buf, p->read_so_far);
+                       wrap = len;
+               }
+
+               if (p->state == PARSER_STATE_RESPONSE_COMPLETE) {
+                       gboolean strip_preceding;
+
+                       if (p->flags & G_AT_CHAT_FLAG_NO_LEADING_CRLF)
+                               strip_preceding = FALSE;
+                       else
+                               strip_preceding = TRUE;
+
+                       len -= p->read_so_far;
+                       wrap -= p->read_so_far;
+
+                       have_line(p, strip_preceding);
+
+                       p->read_so_far = 0;
+               } else if (p->state == PARSER_STATE_MULTILINE_COMPLETE) {
+                       len -= p->read_so_far;
+                       wrap -= p->read_so_far;
+
+                       have_line(p, FALSE);
+
+                       p->read_so_far = 0;
+               } else if (p->state == PARSER_STATE_PDU_COMPLETE) {
+                       len -= p->read_so_far;
+                       wrap -= p->read_so_far;
+
+                       have_pdu(p);
+
+                       p->read_so_far = 0;
+               } else if (p->state == PARSER_STATE_INITIAL_CR) {
+                       len -= p->read_so_far - 1;
+                       wrap -= p->read_so_far - 1;
+
+                       ring_buffer_drain(p->buf, p->read_so_far - 1);
+
+                       p->read_so_far = 1;
+               } else if (p->state == PARSER_STATE_PROMPT_COMPLETE) {
+                       len -= p->read_so_far;
+                       wrap -= p->read_so_far;
+
+                       g_at_chat_wakeup_writer(p);
+
+                       ring_buffer_drain(p->buf, p->read_so_far);
+
+                       p->read_so_far = 0;
+               }
+       }
+
+       if (p->state == PARSER_STATE_IDLE && p->read_so_far > 0) {
+               ring_buffer_drain(p->buf, p->read_so_far);
+               p->read_so_far = 0;
+       }
+}
+
+static gboolean received_data(GIOChannel *channel, GIOCondition cond,
+                               gpointer data)
+{
+       unsigned char *buf;
+       GAtChat *chat = data;
+       GIOError err;
+       gsize rbytes;
+       gsize toread;
+       gsize total_read = 0;
+
+       if (cond & G_IO_NVAL)
+               return FALSE;
+
+       /* Regardless of condition, try to read all the data available */
+       do {
+               rbytes = 0;
+
+               toread = ring_buffer_avail_no_wrap(chat->buf);
+
+               /* We're going to start overflowing the buffer
+                * this cannot happen under normal circumstances, so probably
+                * the channel is getting garbage, drop off
+                */
+               if (toread == 0) {
+                       if (chat->state == PARSER_STATE_RESPONSE)
+                               return FALSE;
+
+                       err = G_IO_ERROR_AGAIN;
+                       break;
+               }
+
+               buf = ring_buffer_write_ptr(chat->buf);
+
+               err = g_io_channel_read(channel, (char *) buf, toread, &rbytes);
+
+               total_read += rbytes;
+
+               if (rbytes > 0)
+                       ring_buffer_write_advance(chat->buf, rbytes);
+
+       } while (err == G_IO_ERROR_NONE && rbytes > 0);
+
+       if (total_read > 0)
+               new_bytes(chat);
+
+       if (cond & (G_IO_HUP | G_IO_ERR))
+               return FALSE;
+
+       if (err == G_IO_ERROR_NONE && rbytes == 0)
+               return FALSE;
+
+       if (err != G_IO_ERROR_NONE && err != G_IO_ERROR_AGAIN)
+               return FALSE;
+
+       return TRUE;
+}
+
+static gboolean wakeup_no_response(gpointer user)
+{
+       GAtChat *chat = user;
+
+       g_at_chat_finish_command(chat, FALSE, NULL);
+
+       return FALSE;
+}
+
+static gboolean can_write_data(GIOChannel *channel, GIOCondition cond,
+                               gpointer data)
+{
+       GAtChat *chat = data;
+       struct at_command *cmd;
+       GIOError err;
+       gsize bytes_written;
+       gsize towrite;
+       gsize len;
+       char *cr;
+       gboolean wakeup_first = FALSE;
+#ifdef WRITE_SCHEDULER_DEBUG
+       int limiter;
+#endif
+
+       if (cond & (G_IO_NVAL | G_IO_HUP | G_IO_ERR))
+               return FALSE;
+
+       /* Grab the first command off the queue and write as
+        * much of it as we can
+        */
+       cmd = g_queue_peek_head(chat->command_queue);
+
+       /* For some reason command queue is empty, cancel write watcher */
+       if (cmd == NULL)
+               return FALSE;
+
+       len = strlen(cmd->cmd);
+
+       /* For some reason write watcher fired, but we've already
+        * written the entire command out to the io channel,
+        * cancel write watcher
+        */
+       if (chat->cmd_bytes_written >= len)
+               return FALSE;
+
+       if (chat->wakeup) {
+               if (!chat->wakeup_timer) {
+                       wakeup_first = TRUE;
+                       chat->wakeup_timer = g_timer_new();
+
+               } else if (g_timer_elapsed(chat->wakeup_timer, NULL) >
+                               chat->inactivity_time)
+                       wakeup_first = TRUE;
+       }
+
+       if (chat->cmd_bytes_written == 0 && wakeup_first == TRUE) {
+               cmd = at_command_create(chat->wakeup, NULL, NULL, NULL, NULL);
+
+               if (!cmd)
+                       return FALSE;
+
+               g_queue_push_head(chat->command_queue, cmd);
+
+               len = strlen(chat->wakeup);
+
+               g_timeout_add(chat->wakeup_timeout, wakeup_no_response,
+                               chat);
+       }
+
+       towrite = len - chat->cmd_bytes_written;
+
+       cr = strchr(cmd->cmd + chat->cmd_bytes_written, '\r');
+
+       if (cr)
+               towrite = cr - (cmd->cmd + chat->cmd_bytes_written) + 1;
+
+#ifdef WRITE_SCHEDULER_DEBUG
+       limiter = towrite;
+
+       if (limiter > 5)
+               limiter = 5;
+#endif
+
+       err = g_io_channel_write(chat->channel,
+                       cmd->cmd + chat->cmd_bytes_written,
+#ifdef WRITE_SCHEDULER_DEBUG
+                       limiter,
+#else
+                       towrite,
+#endif
+                       &bytes_written);
+
+       if (err != G_IO_ERROR_NONE) {
+               g_at_chat_shutdown(chat);
+               return FALSE;
+       }
+
+       chat->cmd_bytes_written += bytes_written;
+
+       if (bytes_written < towrite)
+               return TRUE;
+
+       /* Full command submitted, update timer */
+       if (chat->wakeup_timer)
+               g_timer_start(chat->wakeup_timer);
+
+       return FALSE;
+}
+
+static void g_at_chat_wakeup_writer(GAtChat *chat)
+{
+       if (chat->write_watch != 0)
+               return;
+
+       chat->write_watch = g_io_add_watch_full(chat->channel,
+                               G_PRIORITY_DEFAULT,
+                               G_IO_OUT | G_IO_HUP | G_IO_ERR | G_IO_NVAL,
+                               can_write_data, chat,
+                               (GDestroyNotify)write_watcher_destroy_notify);
+}
+
+GAtChat *g_at_chat_new(GIOChannel *channel, int flags)
+{
+       GAtChat *chat;
+       GIOFlags io_flags;
+
+       if (!channel)
+               return NULL;
+
+       chat = g_try_new0(GAtChat, 1);
+
+       if (!chat)
+               return chat;
+
+       chat->next_cmd_id = 1;
+       chat->next_notify_id = 1;
+       chat->flags = flags;
+
+       chat->buf = ring_buffer_new(4096);
+
+       if (!chat->buf)
+               goto error;
+
+       chat->command_queue = g_queue_new();
+
+       if (!chat->command_queue)
+               goto error;
+
+       chat->notify_list = g_hash_table_new_full(g_str_hash, g_str_equal,
+                               g_free, (GDestroyNotify)at_notify_destroy);
+
+       if (g_io_channel_set_encoding(channel, NULL, NULL) !=
+                       G_IO_STATUS_NORMAL)
+               goto error;
+
+       io_flags = g_io_channel_get_flags(channel);
+
+       io_flags |= G_IO_FLAG_NONBLOCK;
+
+       if (g_io_channel_set_flags(channel, io_flags, NULL) !=
+                       G_IO_STATUS_NORMAL)
+               goto error;
+
+       g_io_channel_set_close_on_unref(channel, TRUE);
+
+       chat->channel = channel;
+       chat->read_watch = g_io_add_watch_full(channel, G_PRIORITY_DEFAULT,
+                               G_IO_IN | G_IO_HUP | G_IO_ERR | G_IO_NVAL,
+                               received_data, chat,
+                               (GDestroyNotify)read_watcher_destroy_notify);
+
+       return chat;
+
+error:
+       if (chat->buf)
+               ring_buffer_free(chat->buf);
+
+       if (chat->command_queue)
+               g_queue_free(chat->command_queue);
+
+       if (chat->notify_list)
+               g_hash_table_destroy(chat->notify_list);
+
+       g_free(chat);
+       return NULL;
+}
+
+GAtChat *g_at_chat_ref(GAtChat *chat)
+{
+       if (chat == NULL)
+               return NULL;
+
+       g_atomic_int_inc(&chat->ref_count);
+
+       return chat;
+}
+
+void g_at_chat_unref(GAtChat *chat)
+{
+       gboolean is_zero;
+
+       if (chat == NULL)
+               return;
+
+       is_zero = g_atomic_int_dec_and_test(&chat->ref_count);
+
+       if (is_zero) {
+               g_at_chat_shutdown(chat);
+
+               g_at_chat_cleanup(chat);
+               g_free(chat);
+       }
+}
+
+gboolean g_at_chat_shutdown(GAtChat *chat)
+{
+       if (chat->channel == NULL)
+               return FALSE;
+
+       chat->disconnecting = TRUE;
+
+       if (chat->read_watch)
+               g_source_remove(chat->read_watch);
+
+       if (chat->write_watch)
+               g_source_remove(chat->write_watch);
+
+       return TRUE;
+}
+
+gboolean g_at_chat_set_disconnect_function(GAtChat *chat,
+                       GAtDisconnectFunc disconnect, gpointer user_data)
+{
+       if (chat == NULL)
+               return FALSE;
+
+       chat->user_disconnect = disconnect;
+       chat->user_disconnect_data = user_data;
+
+       return TRUE;
+}
+
+guint g_at_chat_send(GAtChat *chat, const char *cmd,
+                       const char **prefix_list, GAtResultFunc func,
+                       gpointer user_data, GDestroyNotify notify)
+{
+       struct at_command *c;
+
+       if (chat == NULL || chat->command_queue == NULL)
+               return 0;
+
+       c = at_command_create(cmd, prefix_list, func, user_data, notify);
+
+       if (!c)
+               return 0;
+
+       c->id = chat->next_cmd_id++;
+
+       g_queue_push_tail(chat->command_queue, c);
+
+       if (g_queue_get_length(chat->command_queue) == 1)
+               g_at_chat_wakeup_writer(chat);
+
+       return c->id;
+}
+
+gboolean g_at_chat_cancel(GAtChat *chat, guint id)
+{
+       GList *l;
+
+       if (chat == NULL || chat->command_queue == NULL)
+               return FALSE;
+
+       l = g_queue_find_custom(chat->command_queue, GUINT_TO_POINTER(id),
+                               at_command_compare_by_id);
+
+       if (!l)
+               return FALSE;
+
+       if (l == g_queue_peek_head(chat->command_queue)) {
+               struct at_command *c = l->data;
+
+               /* We can't actually remove it since it is most likely
+                * already in progress, just null out the callback
+                * so it won't be called
+                */
+               c->callback = NULL;
+       } else {
+               at_command_destroy(l->data);
+               g_queue_remove(chat->command_queue, l->data);
+       }
+
+       return TRUE;
+}
+
+static struct at_notify *at_notify_create(GAtChat *chat, const char *prefix,
+                                               gboolean pdu)
+{
+       struct at_notify *notify;
+       char *key;
+
+       key = g_strdup(prefix);
+
+       if (!key)
+               return 0;
+
+       notify = g_try_new0(struct at_notify, 1);
+
+       if (!notify) {
+               g_free(key);
+               return 0;
+       }
+
+       notify->pdu = pdu;
+
+       g_hash_table_insert(chat->notify_list, key, notify);
+
+       return notify;
+}
+
+guint g_at_chat_register(GAtChat *chat, const char *prefix,
+                               GAtNotifyFunc func, gboolean expect_pdu,
+                               gpointer user_data,
+                               GDestroyNotify destroy_notify)
+{
+       struct at_notify *notify;
+       struct at_notify_node *node;
+
+       if (chat == NULL || chat->notify_list == NULL)
+               return 0;
+
+       if (func == NULL)
+               return 0;
+
+       if (prefix == NULL || strlen(prefix) == 0)
+               return 0;
+
+       notify = g_hash_table_lookup(chat->notify_list, prefix);
+
+       if (!notify)
+               notify = at_notify_create(chat, prefix, expect_pdu);
+
+       if (!notify || notify->pdu != expect_pdu)
+               return 0;
+
+       node = g_try_new0(struct at_notify_node, 1);
+
+       if (!node)
+               return 0;
+
+       node->id = chat->next_notify_id++;
+       node->callback = func;
+       node->user_data = user_data;
+       node->notify = destroy_notify;
+
+       notify->nodes = g_slist_prepend(notify->nodes, node);
+
+       return node->id;
+}
+
+gboolean g_at_chat_unregister(GAtChat *chat, guint id)
+{
+       GHashTableIter iter;
+       struct at_notify *notify;
+       char *prefix;
+       gpointer key, value;
+       GSList *l;
+
+       if (chat == NULL || chat->notify_list == NULL)
+               return FALSE;
+
+       g_hash_table_iter_init(&iter, chat->notify_list);
+
+       while (g_hash_table_iter_next(&iter, &key, &value)) {
+               prefix = key;
+               notify = value;
+
+               l = g_slist_find_custom(notify->nodes, GUINT_TO_POINTER(id),
+                                       at_notify_node_compare_by_id);
+
+               if (!l)
+                       continue;
+
+               at_notify_node_destroy(l->data);
+               notify->nodes = g_slist_remove(notify->nodes, l->data);
+
+               if (notify->nodes == NULL)
+                       g_hash_table_iter_remove(&iter);
+
+               return TRUE;
+       }
+
+       return TRUE;
+}
+
+gboolean g_at_chat_set_wakeup_command(GAtChat *chat, const char *cmd,
+                                       unsigned int timeout, unsigned int msec)
+{
+       if (chat == NULL)
+               return FALSE;
+
+       if (chat->wakeup)
+               g_free(chat->wakeup);
+
+       chat->wakeup = g_strdup(cmd);
+       chat->inactivity_time = (gdouble)msec / 1000;
+       chat->wakeup_timeout = timeout;
+
+       return TRUE;
+}
diff --git a/gatchat/gatchat.h b/gatchat/gatchat.h
new file mode 100644 (file)
index 0000000..58ca911
--- /dev/null
@@ -0,0 +1,105 @@
+/*
+ *
+ *  AT chat library with GLib integration
+ *
+ *  Copyright (C) 2008-2009  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
+ *
+ */
+
+#ifndef __GATCHAT_H
+#define __GATCHAT_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include "gatresult.h"
+
+struct _GAtChat;
+
+typedef struct _GAtChat GAtChat;
+
+typedef void (*GAtResultFunc)(gboolean success, GAtResult *result,
+                               gpointer user_data);
+typedef void (*GAtNotifyFunc)(GAtResult *result, gpointer user_data);
+typedef void (*GAtDisconnectFunc)(gpointer user_data);
+
+enum _GAtChatFlags {
+       G_AT_CHAT_FLAG_NO_LEADING_CRLF = 1,     /* Some emulators are broken */
+};
+
+typedef enum _GAtChatFlags GAtChatFlags;
+
+GAtChat *g_at_chat_new(GIOChannel *channel, int flags);
+
+GAtChat *g_at_chat_ref(GAtChat *chat);
+void g_at_chat_unref(GAtChat *chat);
+
+gboolean g_at_chat_shutdown(GAtChat *chat);
+
+gboolean g_at_chat_set_disconnect_function(GAtChat *chat,
+                       GAtDisconnectFunc disconnect, gpointer user_data);
+
+/*!
+ * Queue an AT command for execution.  The command contents are given
+ * in cmd.  Once the command executes, the callback function given by
+ * func is called with user provided data in user_data.
+ *
+ * Returns an id of the queued command which can be canceled using
+ * g_at_chat_cancel.  If an error occurred, an id of 0 is returned.
+ *
+ * This function can be used in three ways:
+ *     - Send a simple command such as g_at_chat_send(p, "AT+CGMI?", ...
+ *
+ *     - Send a compound command: g_at_chat_send(p, "AT+CMD1;+CMD2", ...
+ *
+ *     - Send a command requiring a prompt.  The command up to '\r' is sent
+ *       after which time a '> ' prompt is expected from the modem.  Further
+ *       contents of the command are sent until a '\r' or end of string is
+ *       encountered.  If end of string is encountered, the Ctrl-Z character
+ *       is sent automatically.  There is no need to include the Ctrl-Z
+ *       by the caller.
+ *
+ * The valid_resp field can be used to send an array of strings which will
+ * be accepted as a valid response for this command.  This is treated as a
+ * simple prefix match.  If a response line comes in from the modem and it
+ * does not match any of the prefixes in valid_resp, it is treated as an
+ * unsolicited notification.  If valid_resp is NULL, then all response
+ * lines after command submission and final response line are treated as
+ * part of the command response.  This can be used to get around broken
+ * modems which send unsolicited notifications during command processing.
+ */
+guint g_at_chat_send(GAtChat *chat, const char *cmd,
+                               const char **valid_resp, GAtResultFunc func,
+                               gpointer user_data, GDestroyNotify notify);
+
+gboolean g_at_chat_cancel(GAtChat *chat, guint id);
+
+guint g_at_chat_register(GAtChat *chat, const char *prefix,
+                               GAtNotifyFunc func, gboolean expect_pdu,
+                               gpointer user_data, GDestroyNotify notify);
+
+gboolean g_at_chat_unregister(GAtChat *chat, guint id);
+
+gboolean g_at_chat_set_wakeup_command(GAtChat *chat, const char *cmd,
+                                       guint timeout, guint msec);
+
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __GATCHAT_H */
diff --git a/gatchat/gatresult.c b/gatchat/gatresult.c
new file mode 100644 (file)
index 0000000..8da8821
--- /dev/null
@@ -0,0 +1,430 @@
+/*
+ *
+ *  AT chat library with GLib integration
+ *
+ *  Copyright (C) 2008-2009  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 <string.h>
+#include <stdio.h>
+
+#include <glib.h>
+
+#include "gatresult.h"
+
+void g_at_result_iter_init(GAtResultIter *iter, GAtResult *result)
+{
+       iter->result = result;
+       iter->pre.next = result->lines;
+       iter->pre.data = NULL;
+       iter->l = &iter->pre;
+       iter->line_pos = 0;
+}
+
+gboolean g_at_result_iter_next(GAtResultIter *iter, const char *prefix)
+{
+       char *line;
+       int prefix_len = prefix ? strlen(prefix) : 0;
+
+       while ((iter->l = iter->l->next)) {
+               line = iter->l->data;
+
+               if (prefix_len == 0) {
+                       iter->line_pos = 0;
+                       return TRUE;
+               }
+
+               if (g_str_has_prefix(line, prefix) == FALSE)
+                       continue;
+
+               iter->line_pos = prefix_len;
+
+               while (iter->line_pos < strlen(line) &&
+                       line[iter->line_pos] == ' ')
+                       iter->line_pos += 1;
+
+               return TRUE;
+       }
+
+       return FALSE;
+}
+
+const char *g_at_result_iter_raw_line(GAtResultIter *iter)
+{
+       const char *line;
+
+       if (!iter)
+               return NULL;
+
+       if (!iter->l)
+               return NULL;
+
+       line = iter->l->data;
+
+       line += iter->line_pos;
+
+       return line;
+}
+
+static inline int skip_to_next_field(const char *line, int pos, int len)
+{
+       if (pos < len && line[pos] == ',')
+               pos += 1;
+
+       while (pos < len && line[pos] == ' ')
+               pos += 1;
+
+       return pos;
+}
+
+gboolean g_at_result_iter_next_string(GAtResultIter *iter, const char **str)
+{
+       unsigned int pos;
+       unsigned int end;
+       unsigned int len;
+       char *line;
+
+       if (!iter)
+               return FALSE;
+
+       if (!iter->l)
+               return FALSE;
+
+       line = iter->l->data;
+       len = strlen(line);
+
+       pos = iter->line_pos;
+
+       /* Omitted string */
+       if (line[pos] == ',') {
+               end = pos;
+               memset(iter->buf, 0, sizeof(iter->buf));
+               goto out;
+       }
+
+       if (line[pos++] != '"')
+               return FALSE;
+
+       end = pos;
+
+       while (end < len && line[end] != '"')
+               end += 1;
+
+       if (line[end] != '"')
+               return FALSE;
+
+       if (end - pos >= sizeof(iter->buf))
+               return FALSE;
+
+       strncpy(iter->buf, line+pos, end-pos);
+       memset(iter->buf + end - pos, 0, sizeof(iter->buf) - end + pos);
+
+       /* Skip " */
+       end += 1;
+
+out:
+       iter->line_pos = skip_to_next_field(line, end, len);
+
+       if (str)
+               *str = iter->buf;
+
+       return TRUE;
+}
+
+gboolean g_at_result_iter_next_hexstring(GAtResultIter *iter,
+               const guint8 **str, gint *length)
+{
+       unsigned int pos;
+       unsigned int end;
+       unsigned int len;
+       char *line;
+       char *bufpos;
+
+       if (!iter)
+               return FALSE;
+
+       if (!iter->l)
+               return FALSE;
+
+       line = iter->l->data;
+       len = strlen(line);
+
+       pos = iter->line_pos;
+
+       /* Omitted string */
+       if (line[pos] == ',') {
+               end = pos;
+               memset(iter->buf, 0, sizeof(iter->buf));
+               goto out;
+       }
+
+       end = pos;
+
+       while (end < len && g_ascii_isxdigit(line[end]))
+               end += 1;
+
+       if ((end - pos) & 1)
+               return FALSE;
+
+       if ((end - pos) / 2 >= sizeof(iter->buf))
+               return FALSE;
+       *length = (end - pos) / 2;
+
+       for (bufpos = iter->buf; pos < end; pos += 2)
+               sscanf(line + pos, "%02hhx", bufpos++);
+       memset(bufpos, 0, sizeof(iter->buf) - (bufpos - iter->buf));
+
+out:
+       iter->line_pos = skip_to_next_field(line, end, len);
+
+       if (str)
+               *str = (guint8 *) iter->buf;
+
+       return TRUE;
+}
+
+gboolean g_at_result_iter_next_number(GAtResultIter *iter, gint *number)
+{
+       int pos;
+       int end;
+       int len;
+       int value = 0;
+       char *line;
+
+       if (!iter)
+               return FALSE;
+
+       if (!iter->l)
+               return FALSE;
+
+       line = iter->l->data;
+       len = strlen(line);
+
+       pos = iter->line_pos;
+       end = pos;
+
+       while (line[end] >= '0' && line[end] <= '9') {
+               value = value * 10 + (int)(line[end] - '0');
+               end += 1;
+       }
+
+       if (pos == end)
+               return FALSE;
+
+       iter->line_pos = skip_to_next_field(line, end, len);
+
+       if (number)
+               *number = value;
+
+       return TRUE;
+}
+
+gboolean g_at_result_iter_next_range(GAtResultIter *iter, gint *min, gint *max)
+{
+       int pos;
+       int end;
+       int len;
+       int low = 0;
+       int high = 0;
+       char *line;
+
+       if (!iter)
+               return FALSE;
+
+       if (!iter->l)
+               return FALSE;
+
+       line = iter->l->data;
+       len = strlen(line);
+
+       pos = iter->line_pos;
+
+       while (pos < len && line[pos] == ' ')
+               pos += 1;
+
+       end = pos;
+
+       while (line[end] >= '0' && line[end] <= '9') {
+               low = low * 10 + (int)(line[end] - '0');
+               end += 1;
+       }
+
+       if (pos == end)
+               return FALSE;
+
+       if (line[end] == ',') {
+               high = low;
+               goto out;
+       }
+
+       if (line[end] == '-')
+               pos = end = end + 1;
+       else
+               return FALSE;
+
+       while (line[end] >= '0' && line[end] <= '9') {
+               high = high * 10 + (int)(line[end] - '0');
+               end += 1;
+       }
+
+       if (pos == end)
+               return FALSE;
+
+out:
+       iter->line_pos = skip_to_next_field(line, end, len);
+
+       if (min)
+               *min = low;
+
+       if (max)
+               *max = high;
+
+       return TRUE;
+}
+
+static gint skip_until(const char *line, int start, const char delim)
+{
+       int len = strlen(line);
+       int i = start;
+
+       while (i < len) {
+               if (line[i] == delim)
+                       return i;
+
+               if (line[i] != '(') {
+                       i += 1;
+                       continue;
+               }
+
+               i = skip_until(line, i+1, ')');
+
+               if (i < len)
+                       i += 1;
+       }
+
+       return i;
+}
+
+gboolean g_at_result_iter_skip_next(GAtResultIter *iter)
+{
+       unsigned int skipped_to;
+       char *line;
+
+       if (!iter)
+               return FALSE;
+
+       if (!iter->l)
+               return FALSE;
+
+       line = iter->l->data;
+
+       skipped_to = skip_until(line, iter->line_pos, ',');
+
+       if (skipped_to == iter->line_pos && line[skipped_to] != ',')
+               return FALSE;
+
+       iter->line_pos = skip_to_next_field(line, skipped_to, strlen(line));
+
+       return TRUE;
+}
+
+gboolean g_at_result_iter_open_list(GAtResultIter *iter)
+{
+       char *line;
+       unsigned int len;
+
+       if (!iter)
+               return FALSE;
+
+       if (!iter->l)
+               return FALSE;
+
+       line = iter->l->data;
+       len = strlen(line);
+
+       if (iter->line_pos >= len)
+               return FALSE;
+
+       if (line[iter->line_pos] != '(')
+               return FALSE;
+
+       iter->line_pos += 1;
+
+       while (iter->line_pos < strlen(line) &&
+               line[iter->line_pos] == ' ')
+               iter->line_pos += 1;
+
+       return TRUE;
+}
+
+gboolean g_at_result_iter_close_list(GAtResultIter *iter)
+{
+       char *line;
+       unsigned int len;
+
+       if (!iter)
+               return FALSE;
+
+       if (!iter->l)
+               return FALSE;
+
+       line = iter->l->data;
+       len = strlen(line);
+
+       if (iter->line_pos >= len)
+               return FALSE;
+
+       if (line[iter->line_pos] != ')')
+               return FALSE;
+
+       iter->line_pos += 1;
+
+       iter->line_pos = skip_to_next_field(line, iter->line_pos, len);
+
+       return TRUE;
+}
+
+const char *g_at_result_final_response(GAtResult *result)
+{
+       if (!result)
+               return NULL;
+
+       return result->final_or_pdu;
+}
+
+const char *g_at_result_pdu(GAtResult *result)
+{
+       if (!result)
+               return NULL;
+
+       return result->final_or_pdu;
+}
+
+gint g_at_result_num_response_lines(GAtResult *result)
+{
+       if (!result)
+               return 0;
+
+       if (!result->lines)
+               return 0;
+
+       return g_slist_length(result->lines);
+}
diff --git a/gatchat/gatresult.h b/gatchat/gatresult.h
new file mode 100644 (file)
index 0000000..cbe6d05
--- /dev/null
@@ -0,0 +1,71 @@
+/*
+ *
+ *  AT chat library with GLib integration
+ *
+ *  Copyright (C) 2008-2009  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
+ *
+ */
+
+#ifndef __GATCHAT_RESULT_H
+#define __GATCHAT_RESULT_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+struct _GAtResult {
+       GSList *lines;
+       char *final_or_pdu;
+};
+
+typedef struct _GAtResult GAtResult;
+
+struct _GAtResultIter {
+       GAtResult *result;
+       GSList *l;
+       char buf[2048];
+       unsigned int line_pos;
+       GSList pre;
+};
+
+typedef struct _GAtResultIter GAtResultIter;
+
+void g_at_result_iter_init(GAtResultIter *iter, GAtResult *result);
+
+gboolean g_at_result_iter_next(GAtResultIter *iter, const char *prefix);
+gboolean g_at_result_iter_open_list(GAtResultIter *iter);
+gboolean g_at_result_iter_close_list(GAtResultIter *iter);
+
+gboolean g_at_result_iter_skip_next(GAtResultIter *iter);
+
+gboolean g_at_result_iter_next_range(GAtResultIter *iter, gint *min, gint *max);
+gboolean g_at_result_iter_next_string(GAtResultIter *iter, const char **str);
+gboolean g_at_result_iter_next_number(GAtResultIter *iter, gint *number);
+gboolean g_at_result_iter_next_hexstring(GAtResultIter *iter,
+               const guint8 **str, gint *length);
+
+const char *g_at_result_iter_raw_line(GAtResultIter *iter);
+
+const char *g_at_result_final_response(GAtResult *result);
+const char *g_at_result_pdu(GAtResult *result);
+
+gint g_at_result_num_response_lines(GAtResult *result);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __GATCHAT_RESULT_H */
diff --git a/gatchat/ringbuffer.c b/gatchat/ringbuffer.c
new file mode 100644 (file)
index 0000000..0b1860f
--- /dev/null
@@ -0,0 +1,199 @@
+/*
+ *
+ *  AT chat library with GLib integration
+ *
+ *  Copyright (C) 2008-2009  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 <string.h>
+
+#include <glib.h>
+
+#include "ringbuffer.h"
+
+#define MAX_SIZE 262144
+
+struct ring_buffer *ring_buffer_new(unsigned int size)
+{
+       unsigned int real_size = 1;
+       struct ring_buffer *buffer;
+
+       /* Find the next power of two for size */
+       while (real_size < size && real_size < MAX_SIZE)
+               real_size = real_size << 1;
+
+       if (real_size > MAX_SIZE)
+               return NULL;
+
+       buffer = g_new(struct ring_buffer, 1);
+
+       if (!buffer)
+               return NULL;
+
+       buffer->buffer = g_new(unsigned char, real_size);
+
+       if (!buffer->buffer) {
+               g_free(buffer);
+               return NULL;
+       }
+
+       buffer->size = real_size;
+       buffer->in = 0;
+       buffer->out = 0;
+
+       return buffer;
+}
+
+int ring_buffer_write(struct ring_buffer *buf, const void *data,
+                       unsigned int len)
+{
+       unsigned int end;
+       unsigned int offset;
+       const unsigned char *d = data; /* Needed to satisfy non-gcc compilers */
+
+       /* Determine how much we can actually write */
+       len = MIN(len, buf->size - buf->in + buf->out);
+
+       /* Determine how much to write before wrapping */
+       offset = buf->in % buf->size;
+       end = MIN(len, buf->size - offset);
+       memcpy(buf->buffer+offset, d, end);
+
+       /* Now put the remainder on the beginning of the buffer */
+       memcpy(buf->buffer, d + end, len - end);
+
+       buf->in += len;
+
+       return len;
+}
+
+unsigned char *ring_buffer_write_ptr(struct ring_buffer *buf)
+{
+       return buf->buffer + buf->in % buf->size;
+}
+
+int ring_buffer_avail_no_wrap(struct ring_buffer *buf)
+{
+       unsigned int offset = buf->in % buf->size;
+       unsigned int len = buf->size - buf->in + buf->out;
+
+       return MIN(len, buf->size - offset);
+}
+
+int ring_buffer_write_advance(struct ring_buffer *buf, unsigned int len)
+{
+       len = MIN(len, buf->size - buf->in + buf->out);
+       buf->in += len;
+
+       return len;
+}
+
+int ring_buffer_read(struct ring_buffer *buf, void *data, unsigned int len)
+{
+       unsigned int end;
+       unsigned int offset;
+       unsigned char *d = data;
+
+       len = MIN(len, buf->in - buf->out);
+
+       /* Grab data from buffer starting at offset until the end */
+       offset = buf->out % buf->size;
+       end = MIN(len, buf->size - offset);
+       memcpy(d, buf->buffer + offset, end);
+
+       /* Now grab remainder from the beginning */
+       memcpy(d + end, buf->buffer, len - end);
+
+       buf->out += len;
+
+       if (buf->out == buf->in)
+               buf->out = buf->in = 0;
+
+       return len;
+}
+
+int ring_buffer_drain(struct ring_buffer *buf, unsigned int len)
+{
+       len = MIN(len, buf->in - buf->out);
+
+       buf->out += len;
+
+       if (buf->out == buf->in)
+               buf->out = buf->in = 0;
+
+       return len;
+}
+
+int ring_buffer_len_no_wrap(struct ring_buffer *buf)
+{
+       unsigned int offset = buf->out % buf->size;
+       unsigned int len = buf->in - buf->out;
+
+       return MIN(len, buf->size - offset);
+}
+
+unsigned char *ring_buffer_read_ptr(struct ring_buffer *buf,
+                                       unsigned int offset)
+{
+       return buf->buffer + (buf->out + offset) % buf->size;
+}
+
+int ring_buffer_len(struct ring_buffer *buf)
+{
+       if (!buf)
+               return -1;
+
+       return buf->in - buf->out;
+}
+
+void ring_buffer_reset(struct ring_buffer *buf)
+{
+       if (!buf)
+               return;
+
+       buf->in = 0;
+       buf->out = 0;
+}
+
+int ring_buffer_avail(struct ring_buffer *buf)
+{
+       if (!buf)
+               return -1;
+
+       return buf->size - buf->in + buf->out;
+}
+
+int ring_buffer_capacity(struct ring_buffer *buf)
+{
+       if (!buf)
+               return -1;
+
+       return buf->size;
+}
+
+void ring_buffer_free(struct ring_buffer *buf)
+{
+       if (!buf)
+               return;
+
+       g_free(buf->buffer);
+       g_free(buf);
+}
diff --git a/gatchat/ringbuffer.h b/gatchat/ringbuffer.h
new file mode 100644 (file)
index 0000000..b77c428
--- /dev/null
@@ -0,0 +1,124 @@
+/*
+ *
+ *  AT chat library with GLib integration
+ *
+ *  Copyright (C) 2008-2009  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
+ *
+ */
+
+#ifndef __GATCHAT_RINGBUFFER_H
+#define __GATCHAT_RINGBUFFER_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+struct ring_buffer {
+       unsigned char *buffer;
+       unsigned int size;
+       unsigned int in;
+       unsigned int out;
+};
+
+/*!
+ * Creates a new ring buffer with capacity size
+ */
+struct ring_buffer *ring_buffer_new(unsigned int size);
+
+/*!
+ * Frees the resources allocated for the ring buffer
+ */
+void ring_buffer_free(struct ring_buffer *buf);
+
+/*!
+ * Returns the capacity of the ring buffer
+ */
+int ring_buffer_capacity(struct ring_buffer *buf);
+
+/*!
+ * Resets the ring buffer, all data inside the buffer is lost
+ */
+void ring_buffer_reset(struct ring_buffer *buf);
+
+/*!
+ * Writes data of size len into the ring buffer buf.  Returns -1 if the
+ * write failed or the number of bytes written
+ */
+int ring_buffer_write(struct ring_buffer *buf, const void *data,
+                       unsigned int len);
+
+/*!
+ * Advances the write counter by len, this is meant to be used with
+ * the ring_buffer_write_ptr function.  Returns the number of bytes
+ * actually advanced (the capacity of the buffer)
+ */
+int ring_buffer_write_advance(struct ring_buffer *buf, unsigned int len);
+
+/*!
+ * Returns the write pointer.  Careful not to write past the end of the
+ * buffer.  Use the ring_buffer_avail_no_wrap function,
+ * ring_buffer_write_advance.
+ */
+unsigned char *ring_buffer_write_ptr(struct ring_buffer *buf);
+
+/*!
+ * Returns the number of free bytes available in the buffer
+ */
+int ring_buffer_avail(struct ring_buffer *buf);
+
+/*!
+ * Returns the number of free bytes available in the buffer without wrapping
+ */
+int ring_buffer_avail_no_wrap(struct ring_buffer *buf);
+
+/*!
+ * Reads data from the ring buffer buf into memory region pointed to by data.
+ * A maximum of len bytes will be read.  Returns -1 if the read failed or
+ * the number of bytes read
+ */
+int ring_buffer_read(struct ring_buffer *buf, void *data,
+                       unsigned int len);
+
+/*!
+ * Returns the read pointer with read offset specified by offset.  No bounds
+ * checking is performed.  Be careful not to read past the end of the buffer.
+ * Use the ring_buffer_len_no_wrap function, and ring_buffer_drain.
+ */
+unsigned char *ring_buffer_read_ptr(struct ring_buffer *buf,
+                                       unsigned int offset);
+
+/*!
+ * Returns the number of bytes currently available to be read in the buffer
+ */
+int ring_buffer_len(struct ring_buffer *buf);
+
+/*!
+ * Returns the number of bytes currently available to be read in the buffer
+ * without wrapping.
+ */
+int ring_buffer_len_no_wrap(struct ring_buffer *buf);
+
+/*!
+ * Drains the ring buffer of len bytes.  Returns the number of bytes the
+ * read counter was actually advanced.
+ */
+int ring_buffer_drain(struct ring_buffer *buf, unsigned int len);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __GATCHAT_RINGBUFFER_H */
diff --git a/gdbus/Makefile.am b/gdbus/Makefile.am
new file mode 100644 (file)
index 0000000..9447555
--- /dev/null
@@ -0,0 +1,8 @@
+
+noinst_LTLIBRARIES = libgdbus.la
+
+libgdbus_la_SOURCES = gdbus.h mainloop.c object.c watch.c
+
+AM_CFLAGS = @DBUS_CFLAGS@ @GLIB_CFLAGS@
+
+MAINTAINERCLEANFILES = Makefile.in
diff --git a/gdbus/gdbus.h b/gdbus/gdbus.h
new file mode 100644 (file)
index 0000000..fa618a5
--- /dev/null
@@ -0,0 +1,139 @@
+/*
+ *
+ *  D-Bus helper library
+ *
+ *  Copyright (C) 2004-2009  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 void (* GDBusWatchFunction) (DBusConnection *connection,
+                                                       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);
+
+gboolean g_dbus_request_name(DBusConnection *connection, const char *name,
+                                                       DBusError *error);
+
+gboolean g_dbus_check_service(DBusConnection *connection, const char *name);
+
+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 enum {
+       G_DBUS_METHOD_FLAG_DEPRECATED = (1 << 0),
+       G_DBUS_METHOD_FLAG_NOREPLY    = (1 << 1),
+       G_DBUS_METHOD_FLAG_ASYNC      = (1 << 2),
+} GDBusMethodFlags;
+
+typedef enum {
+       G_DBUS_SIGNAL_FLAG_DEPRECATED = (1 << 0),
+} GDBusSignalFlags;
+
+typedef enum {
+       G_DBUS_PROPERTY_FLAG_DEPRECATED = (1 << 0),
+} GDBusPropertyFlags;
+
+typedef struct {
+       const char *name;
+       const char *signature;
+       const char *reply;
+       GDBusMethodFunction function;
+       GDBusMethodFlags flags;
+} GDBusMethodTable;
+
+typedef struct {
+       const char *name;
+       const char *signature;
+       GDBusSignalFlags flags;
+} GDBusSignalTable;
+
+typedef struct {
+       const char *name;
+       const char *type;
+       GDBusPropertyFlags flags;
+} GDBusPropertyTable;
+
+gboolean g_dbus_register_interface(DBusConnection *connection,
+                                       const char *path, const char *name,
+                                       GDBusMethodTable *methods,
+                                       GDBusSignalTable *signals,
+                                       GDBusPropertyTable *properties,
+                                       void *user_data,
+                                       GDBusDestroyFunction destroy);
+gboolean g_dbus_unregister_interface(DBusConnection *connection,
+                                       const char *path, const char *name);
+
+DBusMessage *g_dbus_create_error(DBusMessage *message, const char *name,
+                                               const char *format, ...);
+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_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 *rule, GDBusSignalFunction function,
+                               void *user_data, GDBusDestroyFunction destroy);
+gboolean g_dbus_remove_watch(DBusConnection *connection, guint tag);
+void g_dbus_remove_all_watches(DBusConnection *connection);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __GDBUS_H */
diff --git a/gdbus/mainloop.c b/gdbus/mainloop.c
new file mode 100644 (file)
index 0000000..eaba42e
--- /dev/null
@@ -0,0 +1,348 @@
+/*
+ *
+ *  D-Bus helper library
+ *
+ *  Copyright (C) 2004-2009  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 <stdint.h>
+
+#include <glib.h>
+#include <dbus/dbus.h>
+
+#ifdef NEED_DBUS_WATCH_GET_UNIX_FD
+#define dbus_watch_get_unix_fd dbus_watch_get_fd
+#endif
+
+#include "gdbus.h"
+
+#define DISPATCH_TIMEOUT  0
+
+#define info(fmt...)
+#define error(fmt...)
+#define debug(fmt...)
+
+typedef struct {
+       uint32_t id;
+       DBusTimeout *timeout;
+} timeout_handler_t;
+
+struct watch_info {
+       guint watch_id;
+       GIOChannel *io;
+       DBusConnection *conn;
+};
+
+struct server_info {
+       guint watch_id;
+       GIOChannel *io;
+       DBusServer *server;
+};
+
+struct disconnect_data {
+       GDBusWatchFunction disconnect_cb;
+       void *user_data;
+};
+
+static DBusHandlerResult disconnect_filter(DBusConnection *conn,
+                                               DBusMessage *msg, void *data)
+{
+       struct disconnect_data *dc_data = data;
+
+       if (dbus_message_is_signal(msg,
+                       DBUS_INTERFACE_LOCAL, "Disconnected") == TRUE) {
+               error("Got disconnected from the system message bus");
+               dc_data->disconnect_cb(conn, dc_data->user_data);
+               dbus_connection_unref(conn);
+       }
+
+       return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
+}
+
+static gboolean message_dispatch_cb(void *data)
+{
+       DBusConnection *connection = data;
+
+       dbus_connection_ref(connection);
+
+       /* Dispatch messages */
+       while (dbus_connection_dispatch(connection) == DBUS_DISPATCH_DATA_REMAINS);
+
+       dbus_connection_unref(connection);
+
+       return FALSE;
+}
+
+static gboolean watch_func(GIOChannel *chan, GIOCondition cond, gpointer data)
+{
+       DBusWatch *watch = data;
+       struct watch_info *info = dbus_watch_get_data(watch);
+       int flags = 0;
+
+       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(watch, flags);
+
+       if (dbus_connection_get_dispatch_status(info->conn) == DBUS_DISPATCH_DATA_REMAINS)
+               g_timeout_add(DISPATCH_TIMEOUT, message_dispatch_cb, info->conn);
+
+       return TRUE;
+}
+
+static dbus_bool_t add_watch(DBusWatch *watch, void *data)
+{
+       GIOCondition cond = G_IO_HUP | G_IO_ERR;
+       DBusConnection *conn = data;
+       struct watch_info *info;
+       int fd, flags;
+
+       if (!dbus_watch_get_enabled(watch))
+               return TRUE;
+
+       info = g_new(struct watch_info, 1);
+
+       fd = dbus_watch_get_unix_fd(watch);
+       info->io = g_io_channel_unix_new(fd);
+       info->conn = dbus_connection_ref(conn);
+
+       dbus_watch_set_data(watch, info, NULL);
+
+       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->watch_id = g_io_add_watch(info->io, cond, watch_func, watch);
+
+       return TRUE;
+}
+
+static void remove_watch(DBusWatch *watch, void *data)
+{
+       struct watch_info *info = dbus_watch_get_data(watch);
+
+       dbus_watch_set_data(watch, NULL, NULL);
+
+       if (info) {
+               g_source_remove(info->watch_id);
+               g_io_channel_unref(info->io);
+               dbus_connection_unref(info->conn);
+               g_free(info);
+       }
+}
+
+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)
+{
+       timeout_handler_t *handler = data;
+
+       /* if not enabled should not be polled by the main loop */
+       if (dbus_timeout_get_enabled(handler->timeout) != TRUE)
+               return FALSE;
+
+       dbus_timeout_handle(handler->timeout);
+
+       return FALSE;
+}
+
+static void timeout_handler_free(void *data)
+{
+       timeout_handler_t *handler = data;
+       if (!handler)
+               return;
+
+       g_source_remove(handler->id);
+       g_free(handler);
+}
+
+static dbus_bool_t add_timeout(DBusTimeout *timeout, void *data)
+{
+       timeout_handler_t *handler;
+
+       if (!dbus_timeout_get_enabled(timeout))
+               return TRUE;
+
+       handler = g_new0(timeout_handler_t, 1);
+
+       handler->timeout = timeout;
+       handler->id = g_timeout_add(dbus_timeout_get_interval(timeout),
+                                       timeout_handler_dispatch, handler);
+
+       dbus_timeout_set_data(timeout, handler, timeout_handler_free);
+
+       return TRUE;
+}
+
+static void remove_timeout(DBusTimeout *timeout, void *data)
+{
+}
+
+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_cb(DBusConnection *conn,
+                               DBusDispatchStatus new_status, void *data)
+{
+       if (!dbus_connection_get_is_connected(conn))
+               return;
+
+       if (new_status == DBUS_DISPATCH_DATA_REMAINS)
+               g_timeout_add(DISPATCH_TIMEOUT, message_dispatch_cb, data);
+}
+
+static 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, conn, NULL);
+
+       dbus_connection_set_dispatch_status_function(conn, dispatch_status_cb,
+                                                               conn, NULL);
+}
+
+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 (name != NULL) {
+               if (dbus_bus_request_name(conn, name,
+                               DBUS_NAME_FLAG_DO_NOT_QUEUE, error) !=
+                               DBUS_REQUEST_NAME_REPLY_PRIMARY_OWNER ) {
+                       dbus_connection_unref(conn);
+                       return NULL;
+               }
+
+               if (error != NULL) {
+                       if (dbus_error_is_set(error) == TRUE) {
+                               dbus_connection_unref(conn);
+                               return NULL;
+                       }
+               }
+       }
+
+       setup_dbus_with_main_loop(conn);
+
+       return conn;
+}
+
+gboolean g_dbus_request_name(DBusConnection *connection, const char *name,
+                                                       DBusError *error)
+{
+       return TRUE;
+}
+
+gboolean g_dbus_check_service(DBusConnection *connection, const char *name)
+{
+       DBusMessage *message, *reply;
+       const char **names;
+       int i, count;
+       gboolean result = FALSE;
+
+       message = dbus_message_new_method_call(DBUS_SERVICE_DBUS,
+                       DBUS_PATH_DBUS, DBUS_INTERFACE_DBUS, "ListNames");
+       if (message == NULL) {
+               error("Can't allocate new message");
+               return FALSE;
+       }
+
+       reply = dbus_connection_send_with_reply_and_block(connection,
+                                                       message, -1, NULL);
+
+       dbus_message_unref(message);
+
+       if (reply == NULL) {
+               error("Failed to execute method call");
+               return FALSE;
+       }
+
+       if (dbus_message_get_args(reply, NULL,
+                               DBUS_TYPE_ARRAY, DBUS_TYPE_STRING,
+                               &names, &count, DBUS_TYPE_INVALID) == FALSE) {
+               error("Failed to read name list");
+               goto done;
+       }
+
+       for (i = 0; i < count; i++)
+               if (g_str_equal(names[i], name) == TRUE) {
+                       result = TRUE;
+                       break;
+               }
+
+done:
+       dbus_message_unref(reply);
+
+       return result;
+}
+
+gboolean g_dbus_set_disconnect_function(DBusConnection *connection,
+                               GDBusWatchFunction function,
+                               void *user_data, DBusFreeFunction destroy)
+{
+       struct disconnect_data *dc_data;
+
+       dc_data = g_new(struct disconnect_data, 1);
+
+       dc_data->disconnect_cb = function;
+       dc_data->user_data = user_data;
+
+       dbus_connection_set_exit_on_disconnect(connection, FALSE);
+
+       if (dbus_connection_add_filter(connection, disconnect_filter,
+                                               dc_data, g_free) == FALSE) {
+               error("Can't add D-Bus disconnect filter");
+               g_free(dc_data);
+               return FALSE;
+       }
+
+       return TRUE;
+}
diff --git a/gdbus/object.c b/gdbus/object.c
new file mode 100644 (file)
index 0000000..3186921
--- /dev/null
@@ -0,0 +1,658 @@
+/*
+ *
+ *  D-Bus helper library
+ *
+ *  Copyright (C) 2004-2009  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...)
+
+struct generic_data {
+       unsigned int refcount;
+       GSList *interfaces;
+       char *introspect;
+};
+
+struct interface_data {
+       char *name;
+       GDBusMethodTable *methods;
+       GDBusSignalTable *signals;
+       GDBusPropertyTable *properties;
+       void *user_data;
+       GDBusDestroyFunction destroy;
+};
+
+static void print_arguments(GString *gstr, const char *sig,
+                                               const char *direction)
+{
+       int i;
+
+       for (i = 0; sig[i]; i++) {
+               char type[32];
+               int struct_level, dict_level;
+               unsigned int len;
+               gboolean complete;
+
+               complete = FALSE;
+               struct_level = dict_level = 0;
+               memset(type, 0, sizeof(type));
+
+               /* Gather enough data to have a single complete type */
+               for (len = 0; len < (sizeof(type) - 1) && sig[i]; len++, i++) {
+                       switch (sig[i]){
+                       case '(':
+                               struct_level++;
+                               break;
+                       case ')':
+                               struct_level--;
+                               if (struct_level <= 0 && dict_level <= 0)
+                                       complete = TRUE;
+                               break;
+                       case '{':
+                               dict_level++;
+                               break;
+                       case '}':
+                               dict_level--;
+                               if (struct_level <= 0 && dict_level <= 0)
+                                       complete = TRUE;
+                               break;
+                       case 'a':
+                               break;
+                       default:
+                               if (struct_level <= 0 && dict_level <= 0)
+                                       complete = TRUE;
+                               break;
+                       }
+
+                       type[len] = sig[i];
+
+                       if (complete)
+                               break;
+               }
+
+
+               if (direction)
+                       g_string_append_printf(gstr,
+                                       "\t\t\t<arg type=\"%s\" direction=\"%s\"/>\n",
+                                       type, direction);
+               else
+                       g_string_append_printf(gstr,
+                                       "\t\t\t<arg type=\"%s\"/>\n",
+                                       type);
+       }
+}
+
+static void generate_interface_xml(GString *gstr, struct interface_data *iface)
+{
+       GDBusMethodTable *method;
+       GDBusSignalTable *signal;
+
+       for (method = iface->methods; method && method->name; method++) {
+               if (!strlen(method->signature) && !strlen(method->reply))
+                       g_string_append_printf(gstr, "\t\t<method name=\"%s\"/>\n",
+                                                               method->name);
+               else {
+                       g_string_append_printf(gstr, "\t\t<method name=\"%s\">\n",
+                                                               method->name);
+                       print_arguments(gstr, method->signature, "in");
+                       print_arguments(gstr, method->reply, "out");
+                       g_string_append_printf(gstr, "\t\t</method>\n");
+               }
+       }
+
+       for (signal = iface->signals; signal && signal->name; signal++) {
+               if (!strlen(signal->signature))
+                       g_string_append_printf(gstr, "\t\t<signal name=\"%s\"/>\n",
+                                                               signal->name);
+               else {
+                       g_string_append_printf(gstr, "\t\t<signal name=\"%s\">\n",
+                                                               signal->name);
+                       print_arguments(gstr, signal->signature, NULL);
+                       g_string_append_printf(gstr, "\t\t</signal>\n");
+               }
+       }
+}
+
+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 name=\"%s\">\n", path);
+
+       for (list = data->interfaces; list; list = list->next) {
+               struct interface_data *iface = list->data;
+
+               g_string_append_printf(gstr, "\t<interface name=\"%s\">\n",
+                                                               iface->name);
+
+               generate_interface_xml(gstr, iface);
+
+               g_string_append_printf(gstr, "\t</interface>\n");
+       }
+
+       if (!dbus_connection_list_registered(conn, path, &children))
+               goto done;
+
+       for (i = 0; children[i]; i++)
+               g_string_append_printf(gstr, "\t<node name=\"%s\"/>\n",
+                                                               children[i]);
+
+       dbus_free_string_array(children);
+
+done:
+       g_string_append_printf(gstr, "</node>\n");
+
+       data->introspect = g_string_free(gstr, FALSE);
+}
+
+static DBusHandlerResult introspect(DBusConnection *connection,
+                               DBusMessage *message, struct generic_data *data)
+{
+       DBusMessage *reply;
+
+       if (!dbus_message_has_signature(message, DBUS_TYPE_INVALID_AS_STRING)) {
+               error("Unexpected signature to introspect call");
+               return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
+       }
+
+       if (!data->introspect)
+               generate_introspection_xml(connection, data,
+                                               dbus_message_get_path(message));
+
+       reply = dbus_message_new_method_return(message);
+       if (!reply)
+               return DBUS_HANDLER_RESULT_NEED_MEMORY;
+
+       dbus_message_append_args(reply, DBUS_TYPE_STRING, &data->introspect,
+                                       DBUS_TYPE_INVALID);
+
+       dbus_connection_send(connection, reply, NULL);
+
+       dbus_message_unref(reply);
+
+       return DBUS_HANDLER_RESULT_HANDLED;
+}
+
+static void generic_unregister(DBusConnection *connection, void *user_data)
+{
+       struct generic_data *data = user_data;
+
+       g_free(data->introspect);
+       g_free(data);
+}
+
+static struct interface_data *find_interface(GSList *interfaces,
+                                               const char *name)
+{
+       GSList *list;
+
+       if (!name)
+               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 DBusHandlerResult generic_message(DBusConnection *connection,
+                                       DBusMessage *message, void *user_data)
+{
+       struct generic_data *data = user_data;
+       struct interface_data *iface;
+       GDBusMethodTable *method;
+       const char *interface;
+
+       if (dbus_message_is_method_call(message,
+                                       DBUS_INTERFACE_INTROSPECTABLE,
+                                                               "Introspect"))
+               return introspect(connection, message, data);
+
+       interface = dbus_message_get_interface(message);
+
+       iface = find_interface(data->interfaces, interface);
+       if (!iface)
+               return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
+
+       for (method = iface->methods; method &&
+                       method->name && method->function; method++) {
+               DBusMessage *reply;
+
+               if (dbus_message_is_method_call(message, iface->name,
+                                                       method->name) == FALSE)
+                       continue;
+
+               if (dbus_message_has_signature(message,
+                                               method->signature) == FALSE)
+                       continue;
+
+               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;
+       }
+
+       return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
+}
+
+static DBusObjectPathVTable generic_table = {
+       .unregister_function    = generic_unregister,
+       .message_function       = generic_message,
+};
+
+static void invalidate_parent_data(DBusConnection *conn, const char *child_path)
+{
+       struct generic_data *data = NULL;
+       char *parent_path, *slash;
+
+       parent_path = g_strdup(child_path);
+       slash = strrchr(parent_path, '/');
+       if (!slash)
+               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))
+               goto done;
+
+       if (!data)
+               goto done;
+
+       g_free(data->introspect);
+       data->introspect = NULL;
+
+done:
+       g_free(parent_path);
+}
+
+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->introspect = g_strdup(DBUS_INTROSPECT_1_0_XML_DOCTYPE_DECL_NODE "<node></node>");
+
+       data->refcount = 1;
+
+       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);
+
+       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;
+
+       invalidate_parent_data(connection, path);
+
+       dbus_connection_unregister_object_path(connection, path);
+}
+
+static gboolean check_signal(DBusConnection *conn, const char *path,
+                               const char *interface, const char *name,
+                               const char **args)
+{
+       struct generic_data *data = NULL;
+       struct interface_data *iface;
+       GDBusSignalTable *signal;
+
+       *args = NULL;
+       if (!dbus_connection_get_object_path_data(conn, path,
+                                       (void *) &data) || !data) {
+               error("dbus_connection_emit_signal: path %s isn't registered",
+                               path);
+               return FALSE;
+       }
+
+       iface = find_interface(data->interfaces, interface);
+       if (!iface) {
+               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)) {
+                       *args = signal->signature;
+                       break;
+               }
+       }
+
+       if (!*args) {
+               error("No signal named %s on interface %s", name, interface);
+               return FALSE;
+       }
+
+       return TRUE;
+}
+
+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 char *signature, *args;
+
+       if (!check_signal(conn, path, interface, name, &args))
+               return FALSE;
+
+       signal = dbus_message_new_signal(path, interface, name);
+       if (!signal) {
+               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;
+
+       signature = dbus_message_get_signature(signal);
+       if (strcmp(args, signature) != 0) {
+               error("%s.%s: expected signature'%s' but got '%s'",
+                               interface, name, args, signature);
+               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,
+                                       GDBusMethodTable *methods,
+                                       GDBusSignalTable *signals,
+                                       GDBusPropertyTable *properties,
+                                       void *user_data,
+                                       GDBusDestroyFunction destroy)
+{
+       struct generic_data *data;
+       struct interface_data *iface;
+
+       data = object_path_ref(connection, path);
+       if (data == NULL)
+               return FALSE;
+
+       if (find_interface(data->interfaces, name))
+               return FALSE;
+
+       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);
+
+       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;
+       struct interface_data *iface;
+
+       if (!path)
+               return FALSE;
+
+       if (dbus_connection_get_object_path_data(connection, path,
+                                               (void *) &data) == FALSE)
+               return FALSE;
+
+       if (data == NULL)
+               return FALSE;
+
+       iface = find_interface(data->interfaces, name);
+       if (!iface)
+               return FALSE;
+
+       data->interfaces = g_slist_remove(data->interfaces, iface);
+
+       if (iface->destroy)
+               iface->destroy(iface->user_data);
+
+       g_free(iface->name);
+       g_free(iface);
+
+       g_free(data->introspect);
+       data->introspect = NULL;
+
+       object_path_unref(connection, path);
+
+       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);
+
+       result = dbus_connection_send(connection, message, NULL);
+
+       dbus_message_unref(message);
+
+       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);
+}
diff --git a/gdbus/watch.c b/gdbus/watch.c
new file mode 100644 (file)
index 0000000..c7a4e69
--- /dev/null
@@ -0,0 +1,422 @@
+/*
+ *
+ *  D-Bus helper library
+ *
+ *  Copyright (C) 2004-2009  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 name_exit_filter(DBusConnection *connection,
+                                       DBusMessage *message, void *user_data);
+
+static guint listener_id = 0;
+static GSList *name_listeners = NULL;
+
+struct name_callback {
+       GDBusWatchFunction conn_func;
+       GDBusWatchFunction disc_func;
+       void *user_data;
+       guint id;
+};
+
+struct name_data {
+       DBusConnection *connection;
+       char *name;
+       GSList *callbacks;
+       GSList *processed;
+       gboolean lock;
+};
+
+static struct name_data *name_data_find(DBusConnection *connection,
+                                                       const char *name)
+{
+       GSList *current;
+
+       for (current = name_listeners;
+                       current != NULL; current = current->next) {
+               struct name_data *data = current->data;
+
+               if (connection != data->connection)
+                       continue;
+
+               if (name == NULL || g_str_equal(name, data->name))
+                       return data;
+       }
+
+       return NULL;
+}
+
+static struct name_callback *name_callback_find(GSList *callbacks, guint id)
+{
+       GSList *current;
+
+       for (current = callbacks; current != NULL; current = current->next) {
+               struct name_callback *cb = current->data;
+               if (cb->id == id)
+                       return cb;
+       }
+
+       return NULL;
+}
+
+static void name_data_call_and_free(struct name_data *data)
+{
+       GSList *l;
+
+       for (l = data->callbacks; l != NULL; l = l->next) {
+               struct name_callback *cb = l->data;
+               if (cb->disc_func)
+                       cb->disc_func(data->connection, cb->user_data);
+               g_free(cb);
+       }
+
+       g_slist_free(data->callbacks);
+       g_free(data->name);
+       g_free(data);
+}
+
+static void name_data_free(struct name_data *data)
+{
+       GSList *l;
+
+       for (l = data->callbacks; l != NULL; l = l->next)
+               g_free(l->data);
+
+       g_slist_free(data->callbacks);
+       g_free(data->name);
+       g_free(data);
+}
+
+static int name_data_add(DBusConnection *connection, const char *name,
+                                               GDBusWatchFunction connect,
+                                               GDBusWatchFunction disconnect,
+                                               void *user_data, guint id)
+{
+       int first = 1;
+       struct name_data *data = NULL;
+       struct name_callback *cb = NULL;
+
+       cb = g_new(struct name_callback, 1);
+
+       cb->conn_func = connect;
+       cb->disc_func = disconnect;
+       cb->user_data = user_data;
+       cb->id = id;
+
+       data = name_data_find(connection, name);
+       if (data) {
+               first = 0;
+               goto done;
+       }
+
+       data = g_new0(struct name_data, 1);
+
+       data->connection = connection;
+       data->name = g_strdup(name);
+
+       name_listeners = g_slist_append(name_listeners, data);
+
+done:
+       if (data->lock)
+               data->processed = g_slist_append(data->processed, cb);
+       else
+               data->callbacks = g_slist_append(data->callbacks, cb);
+
+       return first;
+}
+
+static void name_data_remove(DBusConnection *connection,
+                                       const char *name, guint id)
+{
+       struct name_data *data;
+       struct name_callback *cb = NULL;
+
+       data = name_data_find(connection, name);
+       if (!data)
+               return;
+
+       cb = name_callback_find(data->callbacks, id);
+       if (cb) {
+               data->callbacks = g_slist_remove(data->callbacks, cb);
+               g_free(cb);
+       }
+
+       if (data->callbacks)
+               return;
+
+       name_listeners = g_slist_remove(name_listeners, data);
+       name_data_free(data);
+
+       /* Remove filter if there are no listeners left for the connection */
+       data = name_data_find(connection, NULL);
+       if (!data)
+               dbus_connection_remove_filter(connection,
+                                               name_exit_filter,
+                                               NULL);
+}
+
+static gboolean add_match(DBusConnection *connection, const char *name)
+{
+       DBusError err;
+       char match_string[128];
+
+       snprintf(match_string, sizeof(match_string),
+                       "interface=%s,member=NameOwnerChanged,arg0=%s",
+                       DBUS_INTERFACE_DBUS, name);
+
+       dbus_error_init(&err);
+
+       dbus_bus_add_match(connection, match_string, &err);
+
+       if (dbus_error_is_set(&err)) {
+               error("Adding match rule \"%s\" failed: %s", match_string,
+                               err.message);
+               dbus_error_free(&err);
+               return FALSE;
+       }
+
+       return TRUE;
+}
+
+static gboolean remove_match(DBusConnection *connection, const char *name)
+{
+       DBusError err;
+       char match_string[128];
+
+       snprintf(match_string, sizeof(match_string),
+                       "interface=%s,member=NameOwnerChanged,arg0=%s",
+                       DBUS_INTERFACE_DBUS, name);
+
+       dbus_error_init(&err);
+
+       dbus_bus_remove_match(connection, match_string, &err);
+
+       if (dbus_error_is_set(&err)) {
+               error("Removing owner match rule for %s failed: %s",
+                               name, err.message);
+               dbus_error_free(&err);
+               return FALSE;
+       }
+
+       return TRUE;
+}
+
+static DBusHandlerResult name_exit_filter(DBusConnection *connection,
+                                       DBusMessage *message, void *user_data)
+{
+       struct name_data *data;
+       struct name_callback *cb;
+       char *name, *old, *new;
+
+       if (!dbus_message_is_signal(message, DBUS_INTERFACE_DBUS,
+                                                       "NameOwnerChanged"))
+               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)) {
+               error("Invalid arguments for NameOwnerChanged signal");
+               return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
+       }
+
+       data = name_data_find(connection, name);
+       if (!data) {
+               error("Got NameOwnerChanged signal for %s which has no listeners", name);
+               return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
+       }
+
+       data->lock = TRUE;
+
+       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;
+
+               data->callbacks = g_slist_remove(data->callbacks, cb);
+
+               if (!cb->conn_func || !cb->disc_func) {
+                       g_free(cb);
+                       continue;
+               }
+
+               data->processed = g_slist_append(data->processed, cb);
+       }
+
+       data->callbacks = data->processed;
+       data->processed = NULL;
+       data->lock = FALSE;
+
+       if (data->callbacks)
+               return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
+
+       name_listeners = g_slist_remove(name_listeners, data);
+       name_data_free(data);
+
+       /* Remove filter if there no listener left for the connection */
+       data = name_data_find(connection, NULL);
+       if (!data)
+               dbus_connection_remove_filter(connection, name_exit_filter,
+                                               NULL);
+
+       remove_match(connection, name);
+
+       return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
+}
+
+guint g_dbus_add_service_watch(DBusConnection *connection, const char *name,
+                               GDBusWatchFunction connect,
+                               GDBusWatchFunction disconnect,
+                               void *user_data, GDBusDestroyFunction destroy)
+{
+       int first;
+
+       if (!name_data_find(connection, NULL)) {
+               if (!dbus_connection_add_filter(connection,
+                                       name_exit_filter, NULL, NULL)) {
+                       error("dbus_connection_add_filter() failed");
+                       return 0;
+               }
+       }
+
+       listener_id++;
+       first = name_data_add(connection, name, connect, disconnect,
+                                               user_data, listener_id);
+       /* The filter is already added if this is not the first callback
+        * registration for the name */
+       if (!first)
+               return listener_id;
+
+       if (name) {
+               debug("name_listener_add(%s)", name);
+
+               if (!add_match(connection, name)) {
+                       name_data_remove(connection, name, listener_id);
+                       return 0;
+               }
+       }
+
+       return listener_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 *rule, GDBusSignalFunction function,
+                               void *user_data, GDBusDestroyFunction destroy)
+{
+       return 0;
+}
+
+gboolean g_dbus_remove_watch(DBusConnection *connection, guint id)
+{
+       struct name_data *data;
+       struct name_callback *cb;
+       GSList *ldata, *lcb;
+
+       if (id == 0)
+               return FALSE;
+
+       for (ldata = name_listeners; ldata; ldata = ldata->next) {
+               data = ldata->data;
+               for (lcb = data->callbacks; lcb; lcb = lcb->next) {
+                       cb = lcb->data;
+                       if (cb->id == id)
+                               goto remove;
+               }
+               for (lcb = data->processed; lcb; lcb = lcb->next) {
+                       cb = lcb->data;
+                       if (cb->id == id)
+                               goto remove;
+               }
+       }
+
+       return FALSE;
+
+remove:
+       data->callbacks = g_slist_remove(data->callbacks, cb);
+       data->processed = g_slist_remove(data->processed, cb);
+       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->name) {
+               if (!remove_match(data->connection, data->name))
+                       return FALSE;
+       }
+
+       name_listeners = g_slist_remove(name_listeners, data);
+       name_data_free(data);
+
+       /* Remove filter if there are no listeners left for the connection */
+       data = name_data_find(connection, NULL);
+       if (!data)
+               dbus_connection_remove_filter(connection, name_exit_filter,
+                                               NULL);
+
+       return TRUE;
+}
+
+void g_dbus_remove_all_watches(DBusConnection *connection)
+{
+       struct name_data *data;
+
+       while ((data = name_data_find(connection, NULL))) {
+               name_listeners = g_slist_remove(name_listeners, data);
+               name_data_call_and_free(data);
+       }
+
+       dbus_connection_remove_filter(connection, name_exit_filter, NULL);
+}
diff --git a/include/Makefile.am b/include/Makefile.am
new file mode 100644 (file)
index 0000000..4030c2e
--- /dev/null
@@ -0,0 +1,27 @@
+
+includedir = @includedir@/connman
+
+include_HEADERS = types.h log.h plugin.h security.h notifier.h \
+                               storage.h device.h network.h inet.h
+
+nodist_include_HEADERS = version.h
+
+noinst_HEADERS = driver.h element.h property.h ipv4.h rtnl.h dbus.h \
+                       rfkill.h resolver.h ipconfig.h service.h option.h
+
+abs_top_srcdir=$(shell pwd)/..
+abs_top_builddir=$(shell pwd)/..
+
+MAINTAINERCLEANFILES = Makefile.in
+
+all-local:
+       @if [ ! -e connman ]; then \
+         mkdir connman; \
+         list='$(include_HEADERS) $(noinst_HEADERS)'; for i in $$list; \
+           do $(LN_S) $(abs_top_srcdir)/include/$$i connman/$$i; done; \
+         list='$(nodist_include_HEADERS)'; for i in $$list; \
+           do $(LN_S) $(abs_top_builddir)/include/$$i connman/$$i; done; \
+       fi
+
+clean-local:
+       @rm -rf connman
diff --git a/include/dbus.h b/include/dbus.h
new file mode 100644 (file)
index 0000000..4007a14
--- /dev/null
@@ -0,0 +1,62 @@
+/*
+ *
+ *  Connection Manager
+ *
+ *  Copyright (C) 2007-2009  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
+ *
+ */
+
+#ifndef __CONNMAN_DBUS_H
+#define __CONNMAN_DBUS_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <dbus/dbus.h>
+
+#define CONNMAN_SERVICE                        "org.moblin.connman"
+
+#define CONNMAN_DEBUG_INTERFACE                CONNMAN_SERVICE ".Debug"
+#define CONNMAN_ERROR_INTERFACE                CONNMAN_SERVICE ".Error"
+#define CONNMAN_AGENT_INTERFACE                CONNMAN_SERVICE ".Agent"
+
+#define CONNMAN_MANAGER_INTERFACE      CONNMAN_SERVICE ".Manager"
+#define CONNMAN_MANAGER_PATH           "/"
+
+#define CONNMAN_PROFILE_INTERFACE      CONNMAN_SERVICE ".Profile"
+#define CONNMAN_SERVICE_INTERFACE      CONNMAN_SERVICE ".Service"
+#define CONNMAN_DEVICE_INTERFACE       CONNMAN_SERVICE ".Device"
+#define CONNMAN_NETWORK_INTERFACE      CONNMAN_SERVICE ".Network"
+#define CONNMAN_CONNECTION_INTERFACE   CONNMAN_SERVICE ".Connection"
+
+extern DBusConnection *connman_dbus_get_connection(void);
+
+extern void connman_dbus_property_append_variant(DBusMessageIter *property,
+                                       const char *key, int type, void *val);
+
+extern void connman_dbus_dict_append_array(DBusMessageIter *dict,
+                               const char *key, int type, void *val, int len);
+extern void connman_dbus_dict_append_variant(DBusMessageIter *dict,
+                                       const char *key, int type, void *val);
+
+extern char *connman_dbus_encode_string(const char *value);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __CONNMAN_DBUS_H */
diff --git a/include/device.h b/include/device.h
new file mode 100644 (file)
index 0000000..8311607
--- /dev/null
@@ -0,0 +1,143 @@
+/*
+ *
+ *  Connection Manager
+ *
+ *  Copyright (C) 2007-2009  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
+ *
+ */
+
+#ifndef __CONNMAN_DEVICE_H
+#define __CONNMAN_DEVICE_H
+
+#include <connman/types.h>
+#include <connman/network.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * SECTION:device
+ * @title: Device premitives
+ * @short_description: Functions for handling devices
+ */
+
+enum connman_device_type {
+       CONNMAN_DEVICE_TYPE_UNKNOWN   = 0,
+       CONNMAN_DEVICE_TYPE_ETHERNET  = 1,
+       CONNMAN_DEVICE_TYPE_WIFI      = 2,
+       CONNMAN_DEVICE_TYPE_WIMAX     = 3,
+       CONNMAN_DEVICE_TYPE_BLUETOOTH = 4,
+       CONNMAN_DEVICE_TYPE_GPS       = 5,
+       CONNMAN_DEVICE_TYPE_HSO       = 15,
+       CONNMAN_DEVICE_TYPE_NOZOMI    = 16,
+       CONNMAN_DEVICE_TYPE_HUAWEI    = 17,
+       CONNMAN_DEVICE_TYPE_NOVATEL   = 18,
+       CONNMAN_DEVICE_TYPE_VENDOR    = 10000,
+};
+
+enum connman_device_mode {
+       CONNMAN_DEVICE_MODE_UNKNOWN          = 0,
+       CONNMAN_DEVICE_MODE_NETWORK_SINGLE   = 1,
+       CONNMAN_DEVICE_MODE_NETWORK_MULTIPLE = 2,
+       CONNMAN_DEVICE_MODE_TRANSPORT_IP     = 3,
+};
+
+enum connman_device_policy {
+       CONNMAN_DEVICE_POLICY_UNKNOWN = 0,
+       CONNMAN_DEVICE_POLICY_IGNORE  = 1,
+       CONNMAN_DEVICE_POLICY_OFF     = 2,
+       CONNMAN_DEVICE_POLICY_AUTO    = 3,
+       CONNMAN_DEVICE_POLICY_MANUAL  = 4,
+};
+
+struct connman_device;
+
+extern struct connman_device *connman_device_create(const char *node,
+                                               enum connman_device_type type);
+extern struct connman_device *connman_device_ref(struct connman_device *device);
+extern void connman_device_unref(struct connman_device *device);
+
+extern enum connman_device_type connman_device_get_type(struct connman_device *device);
+extern const char *connman_device_get_name(struct connman_device *device);
+extern const char *connman_device_get_path(struct connman_device *device);
+extern void connman_device_set_index(struct connman_device *device,
+                                                               int index);
+extern int connman_device_get_index(struct connman_device *device);
+extern void connman_device_set_interface(struct connman_device *device,
+                                                       const char *interface);
+extern const char *connman_device_get_interface(struct connman_device *device);
+
+extern void connman_device_set_ident(struct connman_device *device,
+                                                       const char *ident);
+
+extern void connman_device_set_policy(struct connman_device *device,
+                                       enum connman_device_policy policy);
+extern void connman_device_set_mode(struct connman_device *device,
+                                               enum connman_device_mode mode);
+extern enum connman_device_mode connman_device_get_mode(struct connman_device *device);
+extern void connman_device_set_secondary(struct connman_device *device,
+                                               connman_bool_t secondary);
+extern connman_bool_t connman_device_get_secondary(struct connman_device *device);
+
+extern int connman_device_set_powered(struct connman_device *device,
+                                               connman_bool_t powered);
+extern int connman_device_set_carrier(struct connman_device *device,
+                                               connman_bool_t carrier);
+extern int connman_device_set_scanning(struct connman_device *device,
+                                               connman_bool_t scanning);
+extern int connman_device_set_disconnected(struct connman_device *device,
+                                               connman_bool_t disconnected);
+
+extern int connman_device_set_string(struct connman_device *device,
+                                       const char *key, const char *value);
+extern const char *connman_device_get_string(struct connman_device *device,
+                                                       const char *key);
+
+extern int connman_device_add_network(struct connman_device *device,
+                                       struct connman_network *network);
+extern struct connman_network *connman_device_get_network(struct connman_device *device,
+                                                       const char *identifier);
+extern int connman_device_remove_network(struct connman_device *device,
+                                                       const char *identifier);
+
+extern int connman_device_register(struct connman_device *device);
+extern void connman_device_unregister(struct connman_device *device);
+
+extern void *connman_device_get_data(struct connman_device *device);
+extern void connman_device_set_data(struct connman_device *device, void *data);
+
+struct connman_device_driver {
+       const char *name;
+       enum connman_device_type type;
+       int priority;
+       int (*probe) (struct connman_device *device);
+       void (*remove) (struct connman_device *device);
+       int (*enable) (struct connman_device *device);
+       int (*disable) (struct connman_device *device);
+       int (*scan) (struct connman_device *device);
+       int (*join) (struct connman_device *device,
+                                       struct connman_network *network);
+};
+
+extern int connman_device_driver_register(struct connman_device_driver *driver);
+extern void connman_device_driver_unregister(struct connman_device_driver *driver);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __CONNMAN_DEVICE_H */
diff --git a/include/driver.h b/include/driver.h
new file mode 100644 (file)
index 0000000..61ec465
--- /dev/null
@@ -0,0 +1,58 @@
+/*
+ *
+ *  Connection Manager
+ *
+ *  Copyright (C) 2007-2009  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
+ *
+ */
+
+#ifndef __CONNMAN_DRIVER_H
+#define __CONNMAN_DRIVER_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <connman/element.h>
+
+/**
+ * SECTION:driver
+ * @title: Driver premitives
+ * @short_description: Functions for registering drivers
+ */
+
+#define CONNMAN_DRIVER_PRIORITY_LOW      -100
+#define CONNMAN_DRIVER_PRIORITY_DEFAULT     0
+#define CONNMAN_DRIVER_PRIORITY_HIGH      100
+
+struct connman_driver {
+       const char *name;
+       enum connman_element_type type;
+       int priority;
+       int (*probe) (struct connman_element *element);
+       void (*remove) (struct connman_element *element);
+       void (*update) (struct connman_element *element);
+       void (*change) (struct connman_element *element);
+};
+
+extern int connman_driver_register(struct connman_driver *driver);
+extern void connman_driver_unregister(struct connman_driver *driver);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __CONNMAN_DRIVER_H */
diff --git a/include/element.h b/include/element.h
new file mode 100644 (file)
index 0000000..bc6a2a9
--- /dev/null
@@ -0,0 +1,158 @@
+/*
+ *
+ *  Connection Manager
+ *
+ *  Copyright (C) 2007-2009  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
+ *
+ */
+
+#ifndef __CONNMAN_ELEMENT_H
+#define __CONNMAN_ELEMENT_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <errno.h>
+#include <glib.h>
+
+#include <connman/property.h>
+#include <connman/types.h>
+#include <connman/ipv4.h>
+
+/**
+ * SECTION:element
+ * @title: Element premitives
+ * @short_description: Functions for handling elements
+ */
+
+enum connman_element_type {
+       CONNMAN_ELEMENT_TYPE_UNKNOWN    = 0,
+       CONNMAN_ELEMENT_TYPE_ROOT       = 1,
+       CONNMAN_ELEMENT_TYPE_PROFILE    = 2,
+       CONNMAN_ELEMENT_TYPE_DEVICE     = 3,
+       CONNMAN_ELEMENT_TYPE_NETWORK    = 4,
+       CONNMAN_ELEMENT_TYPE_SERVICE    = 5,
+       CONNMAN_ELEMENT_TYPE_PPP        = 6,
+       CONNMAN_ELEMENT_TYPE_IPV4       = 7,
+       CONNMAN_ELEMENT_TYPE_IPV6       = 8,
+       CONNMAN_ELEMENT_TYPE_DHCP       = 9,
+       CONNMAN_ELEMENT_TYPE_BOOTP      = 10,
+       CONNMAN_ELEMENT_TYPE_ZEROCONF   = 11,
+       CONNMAN_ELEMENT_TYPE_CONNECTION = 42,
+       CONNMAN_ELEMENT_TYPE_VENDOR     = 10000,
+};
+
+enum connman_element_state {
+       CONNMAN_ELEMENT_STATE_UNKNOWN = 0,
+       CONNMAN_ELEMENT_STATE_ERROR   = 1,
+       CONNMAN_ELEMENT_STATE_IDLE    = 2,
+       CONNMAN_ELEMENT_STATE_DONE    = 3,
+};
+
+enum connman_element_error {
+       CONNMAN_ELEMENT_ERROR_UNKNOWN     = 0,
+       CONNMAN_ELEMENT_ERROR_FAILED      = 1,
+       CONNMAN_ELEMENT_ERROR_DHCP_FAILED = 2,
+};
+
+struct connman_driver;
+
+struct connman_element {
+       gint refcount;
+       gint index;
+       gchar *name;
+       gchar *path;
+       enum connman_element_type type;
+       enum connman_element_state state;
+       enum connman_element_error error;
+       gboolean enabled;
+       gboolean configuring;
+       gchar *devname;
+
+       struct connman_element *parent;
+
+       struct connman_driver *driver;
+       void *driver_data;
+
+       void (*destruct) (struct connman_element *element);
+
+       union {
+               void *private;
+               struct connman_device *device;
+               struct connman_network *network;
+       };
+
+       GHashTable *properties;
+
+       struct {
+               enum connman_ipv4_method method;
+               gchar *address;
+               gchar *netmask;
+               gchar *gateway;
+               gchar *network;
+               gchar *broadcast;
+               gchar *nameserver;
+       } ipv4;
+};
+
+extern struct connman_element *connman_element_create(const char *name);
+extern struct connman_element *connman_element_ref(struct connman_element *element);
+extern void connman_element_unref(struct connman_element *element);
+
+extern int connman_element_get_value(struct connman_element *element,
+                               enum connman_property_id id, void *value);
+
+extern int connman_element_set_string(struct connman_element *element,
+                                       const char *key, const char *value);
+extern const char *connman_element_get_string(struct connman_element *element,
+                                                       const char *key);
+extern int connman_element_set_uint8(struct connman_element *element,
+                               const char *key, connman_uint8_t value);
+extern connman_uint8_t connman_element_get_uint8(struct connman_element *element,
+                                                       const char *key);
+extern int connman_element_set_blob(struct connman_element *element,
+                       const char *key, const void *data, unsigned int size);
+extern const void *connman_element_get_blob(struct connman_element *element,
+                                       const char *key, unsigned int *size);
+
+extern int connman_element_register(struct connman_element *element,
+                                       struct connman_element *parent);
+extern void connman_element_unregister(struct connman_element *element);
+extern void connman_element_unregister_children(struct connman_element *element);
+extern void connman_element_update(struct connman_element *element);
+
+extern int connman_element_set_enabled(struct connman_element *element,
+                                                       gboolean enabled);
+extern void connman_element_set_error(struct connman_element *element,
+                                       enum connman_element_error error);
+
+static inline void *connman_element_get_data(struct connman_element *element)
+{
+       return element->driver_data;
+}
+
+static inline void connman_element_set_data(struct connman_element *element,
+                                                               void *data)
+{
+       element->driver_data = data;
+}
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __CONNMAN_ELEMENT_H */
diff --git a/include/inet.h b/include/inet.h
new file mode 100644 (file)
index 0000000..3aacbdc
--- /dev/null
@@ -0,0 +1,43 @@
+/*
+ *
+ *  Connection Manager
+ *
+ *  Copyright (C) 2007-2009  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
+ *
+ */
+
+#ifndef __CONNMAN_INET_H
+#define __CONNMAN_INET_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <connman/device.h>
+
+extern int connman_inet_ifindex(const char *name);
+extern char *connman_inet_ifname(int index);
+
+extern int connman_inet_ifup(int index);
+extern int connman_inet_ifdown(int index);
+
+extern struct connman_device *connman_inet_create_device(int index);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __CONNMAN_INET_H */
diff --git a/include/ipconfig.h b/include/ipconfig.h
new file mode 100644 (file)
index 0000000..9e92c69
--- /dev/null
@@ -0,0 +1,54 @@
+/*
+ *
+ *  Connection Manager
+ *
+ *  Copyright (C) 2007-2009  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
+ *
+ */
+
+#ifndef __CONNMAN_IPCONFIG_H
+#define __CONNMAN_IPCONFIG_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * SECTION:ipconfig
+ * @title: IP configuration premitives
+ * @short_description: Functions for registering IP configuration modules
+ */
+
+#define CONNMAN_IPCONFIG_PRIORITY_LOW      -100
+#define CONNMAN_IPCONFIG_PRIORITY_DEFAULT     0
+#define CONNMAN_IPCONFIG_PRIORITY_HIGH      100
+
+struct connman_ipconfig {
+       const char *name;
+       int priority;
+       int (*request) (const char *interface);
+       int (*release) (const char *interface);
+       int (*renew) (const char *interface);
+};
+
+extern int connman_ipconfig_register(struct connman_ipconfig *ipconfig);
+extern void connman_ipconfig_unregister(struct connman_ipconfig *ipconfig);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __CONNMAN_IPCONFIG_H */
diff --git a/include/ipv4.h b/include/ipv4.h
new file mode 100644 (file)
index 0000000..2f57d0c
--- /dev/null
@@ -0,0 +1,46 @@
+/*
+ *
+ *  Connection Manager
+ *
+ *  Copyright (C) 2007-2009  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
+ *
+ */
+
+#ifndef __CONNMAN_IPV4_H
+#define __CONNMAN_IPV4_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * SECTION:ipv4
+ * @title: IPv4 premitives
+ * @short_description: Functions for handling IPv4
+ */
+
+enum connman_ipv4_method {
+       CONNMAN_IPV4_METHOD_UNKNOWN = 0,
+       CONNMAN_IPV4_METHOD_OFF     = 1,
+       CONNMAN_IPV4_METHOD_STATIC  = 2,
+       CONNMAN_IPV4_METHOD_DHCP    = 3,
+};
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __CONNMAN_IPV4_H */
diff --git a/include/log.h b/include/log.h
new file mode 100644 (file)
index 0000000..d154ba7
--- /dev/null
@@ -0,0 +1,56 @@
+/*
+ *
+ *  Connection Manager
+ *
+ *  Copyright (C) 2007-2009  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
+ *
+ */
+
+#ifndef __CONNMAN_LOG_H
+#define __CONNMAN_LOG_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * SECTION:log
+ * @title: Logging premitives
+ * @short_description: Functions for logging error and debug information
+ */
+
+extern void connman_info(const char *format, ...)
+                               __attribute__((format(printf, 1, 2)));
+extern void connman_error(const char *format, ...)
+                               __attribute__((format(printf, 1, 2)));
+extern void connman_debug(const char *format, ...)
+                               __attribute__((format(printf, 1, 2)));
+
+/**
+ * DBG:
+ * @fmt: format string
+ * @arg...: list of arguments
+ *
+ * Simple macro around connman_debug() which also include the function
+ * name it is called in.
+ */
+#define DBG(fmt, arg...) connman_debug("%s:%s() " fmt, __FILE__, __FUNCTION__ , ## arg)
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __CONNMAN_LOG_H */
diff --git a/include/network.h b/include/network.h
new file mode 100644 (file)
index 0000000..7710913
--- /dev/null
@@ -0,0 +1,129 @@
+/*
+ *
+ *  Connection Manager
+ *
+ *  Copyright (C) 2007-2009  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
+ *
+ */
+
+#ifndef __CONNMAN_NETWORK_H
+#define __CONNMAN_NETWORK_H
+
+#include <connman/types.h>
+#include <connman/device.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * SECTION:network
+ * @title: Network premitives
+ * @short_description: Functions for handling networks
+ */
+
+enum connman_network_type {
+       CONNMAN_NETWORK_TYPE_UNKNOWN       = 0,
+       CONNMAN_NETWORK_TYPE_WIFI          = 1,
+       CONNMAN_NETWORK_TYPE_WIMAX         = 2,
+       CONNMAN_NETWORK_TYPE_BLUETOOTH_PAN = 8,
+       CONNMAN_NETWORK_TYPE_BLUETOOTH_DUN = 9,
+       CONNMAN_NETWORK_TYPE_HSO           = 23,
+       CONNMAN_NETWORK_TYPE_VENDOR        = 10000,
+};
+
+enum connman_network_protocol {
+       CONNMAN_NETWORK_PROTOCOL_UNKNOWN = 0,
+       CONNMAN_NETWORK_PROTOCOL_IP      = 1,
+       CONNMAN_NETWORK_PROTOCOL_PPP     = 2,
+};
+
+struct connman_network;
+
+extern struct connman_network *connman_network_create(const char *identifier,
+                                               enum connman_network_type type);
+extern struct connman_network *connman_network_ref(struct connman_network *network);
+extern void connman_network_unref(struct connman_network *network);
+
+extern enum connman_network_type connman_network_get_type(struct connman_network *network);
+extern const char *connman_network_get_identifier(struct connman_network *network);
+
+extern const char *connman_network_get_path(struct connman_network *network);
+extern void connman_network_set_index(struct connman_network *network,
+                                                               int index);
+extern int connman_network_get_index(struct connman_network *network);
+
+extern void connman_network_set_protocol(struct connman_network *network,
+                                       enum connman_network_protocol protocol);
+extern void connman_network_set_group(struct connman_network *network,
+                                                       const char *group);
+
+extern int connman_network_set_available(struct connman_network *network,
+                                               connman_bool_t available);
+extern connman_bool_t connman_network_get_available(struct connman_network *network);
+extern int connman_network_set_associating(struct connman_network *network,
+                                               connman_bool_t associating);
+extern int connman_network_set_connected(struct connman_network *network,
+                                               connman_bool_t connected);
+extern connman_bool_t connman_network_get_connected(struct connman_network *network);
+
+extern int connman_network_set_address(struct connman_network *network,
+                               const void *address, unsigned int size);
+extern int connman_network_set_name(struct connman_network *network,
+                                                       const char *name);
+extern int connman_network_set_strength(struct connman_network *network,
+                                               connman_uint8_t strength);
+
+extern int connman_network_set_string(struct connman_network *network,
+                                       const char *key, const char *value);
+extern const char *connman_network_get_string(struct connman_network *network,
+                                                       const char *key);
+extern int connman_network_set_uint8(struct connman_network *network,
+                                       const char *key, connman_uint8_t value);
+extern connman_uint8_t connman_network_get_uint8(struct connman_network *network,
+                                                       const char *key);
+extern int connman_network_set_uint16(struct connman_network *network,
+                                       const char *key, connman_uint16_t value);
+extern connman_uint16_t connman_network_get_uint16(struct connman_network *network,
+                                                       const char *key);
+extern int connman_network_set_blob(struct connman_network *network,
+                       const char *key, const void *data, unsigned int size);
+extern const void *connman_network_get_blob(struct connman_network *network,
+                                       const char *key, unsigned int *size);
+
+extern struct connman_device *connman_network_get_device(struct connman_network *network);
+
+extern void *connman_network_get_data(struct connman_network *network);
+extern void connman_network_set_data(struct connman_network *network, void *data);
+
+struct connman_network_driver {
+       const char *name;
+       enum connman_network_type type;
+       int priority;
+       int (*probe) (struct connman_network *network);
+       void (*remove) (struct connman_network *network);
+       int (*connect) (struct connman_network *network);
+       int (*disconnect) (struct connman_network *network);
+};
+
+extern int connman_network_driver_register(struct connman_network_driver *driver);
+extern void connman_network_driver_unregister(struct connman_network_driver *driver);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __CONNMAN_NETWORK_H */
diff --git a/include/notifier.h b/include/notifier.h
new file mode 100644 (file)
index 0000000..046b8bf
--- /dev/null
@@ -0,0 +1,56 @@
+/*
+ *
+ *  Connection Manager
+ *
+ *  Copyright (C) 2007-2009  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
+ *
+ */
+
+#ifndef __CONNMAN_NOTIFIER_H
+#define __CONNMAN_NOTIFIER_H
+
+#include <connman/device.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * SECTION:notifier
+ * @title: Notifier premitives
+ * @short_description: Functions for registering notifier modules
+ */
+
+#define CONNMAN_NOTIFIER_PRIORITY_LOW      -100
+#define CONNMAN_NOTIFIER_PRIORITY_DEFAULT     0
+#define CONNMAN_NOTIFIER_PRIORITY_HIGH      100
+
+struct connman_notifier {
+       const char *name;
+       int priority;
+       void (*device_enabled) (enum connman_device_type type,
+                                               connman_bool_t enabled);
+       void (*offline_mode) (connman_bool_t enabled);
+};
+
+extern int connman_notifier_register(struct connman_notifier *notifier);
+extern void connman_notifier_unregister(struct connman_notifier *notifier);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __CONNMAN_NOTIFIER_H */
diff --git a/include/option.h b/include/option.h
new file mode 100644 (file)
index 0000000..b80c8c9
--- /dev/null
@@ -0,0 +1,35 @@
+/*
+ *
+ *  Connection Manager
+ *
+ *  Copyright (C) 2007-2009  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
+ *
+ */
+
+#ifndef __CONNMAN_OPTION_H
+#define __CONNMAN_OPTION_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+extern const char *connman_option_get_string(const char *key);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __CONNMAN_OPTION_H */
diff --git a/include/plugin.h b/include/plugin.h
new file mode 100644 (file)
index 0000000..db76ad9
--- /dev/null
@@ -0,0 +1,99 @@
+/*
+ *
+ *  Connection Manager
+ *
+ *  Copyright (C) 2007-2009  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
+ *
+ */
+
+#ifndef __CONNMAN_PLUGIN_H
+#define __CONNMAN_PLUGIN_H
+
+#include <connman/version.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#ifndef CONNMAN_API_SUBJECT_TO_CHANGE
+#error "Please define CONNMAN_API_SUBJECT_TO_CHANGE to acknowledge your \
+understanding that ConnMan hasn't reached a stable API."
+#endif
+
+#define CONNMAN_PLUGIN_PRIORITY_LOW      -100
+#define CONNMAN_PLUGIN_PRIORITY_DEFAULT     0
+#define CONNMAN_PLUGIN_PRIORITY_HIGH      100
+
+/**
+ * SECTION:plugin
+ * @title: Plugin premitives
+ * @short_description: Functions for declaring plugins
+ */
+
+struct connman_plugin_desc {
+       const char *name;
+       const char *description;
+       const char *version;
+       int priority;
+       int (*init) (void);
+       void (*exit) (void);
+};
+
+/**
+ * CONNMAN_PLUGIN_DEFINE:
+ * @name: plugin name
+ * @description: plugin description
+ * @version: plugin version string
+ * @init: init function called on plugin loading
+ * @exit: exit function called on plugin removal
+ *
+ * Macro for defining a plugin descriptor
+ *
+ * |[
+ * #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)
+ * ]|
+ */
+#ifdef CONNMAN_PLUGIN_BUILTIN
+#define CONNMAN_PLUGIN_DEFINE(name, description, version, priority, init, exit) \
+               struct connman_plugin_desc __connman_builtin_ ## name = { \
+                       #name, description, version, priority, init, exit \
+               };
+#else
+#define CONNMAN_PLUGIN_DEFINE(name, description, version, priority, init, exit) \
+               extern struct connman_plugin_desc connman_plugin_desc \
+                               __attribute__ ((visibility("default"))); \
+               struct connman_plugin_desc connman_plugin_desc = { \
+                       #name, description, version, priority, init, exit \
+               };
+#endif
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __CONNMAN_PLUGIN_H */
diff --git a/include/property.h b/include/property.h
new file mode 100644 (file)
index 0000000..a6ea83c
--- /dev/null
@@ -0,0 +1,70 @@
+/*
+ *
+ *  Connection Manager
+ *
+ *  Copyright (C) 2007-2009  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
+ *
+ */
+
+#ifndef __CONNMAN_PROPERTY_H
+#define __CONNMAN_PROPERTY_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * SECTION:property
+ * @title: Property premitives
+ * @short_description: Functions for handling properties
+ */
+
+#define CONNMAN_PROPERTY_ID_NAME       "Name"
+#define CONNMAN_PROPERTY_ID_TYPE       "Type"
+#define CONNMAN_PROPERTY_ID_PRIORITY   "Priority"
+#define CONNMAN_PROPERTY_ID_STRENGTH   "Strength"
+
+enum connman_property_id {
+       CONNMAN_PROPERTY_ID_INVALID = 0,
+
+       CONNMAN_PROPERTY_ID_IPV4_METHOD,
+       CONNMAN_PROPERTY_ID_IPV4_ADDRESS,
+       CONNMAN_PROPERTY_ID_IPV4_NETMASK,
+       CONNMAN_PROPERTY_ID_IPV4_GATEWAY,
+       CONNMAN_PROPERTY_ID_IPV4_BROADCAST,
+       CONNMAN_PROPERTY_ID_IPV4_NAMESERVER,
+};
+
+enum connman_property_type {
+       CONNMAN_PROPERTY_TYPE_INVALID = 0,
+       CONNMAN_PROPERTY_TYPE_STRING,
+       CONNMAN_PROPERTY_TYPE_UINT8,
+       CONNMAN_PROPERTY_TYPE_BLOB,
+};
+
+struct connman_property {
+       enum connman_property_id id;
+       int type;
+       int subtype;
+       void *value;
+       unsigned int size;
+};
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __CONNMAN_PROPERTY_H */
diff --git a/include/resolver.h b/include/resolver.h
new file mode 100644 (file)
index 0000000..d5fefe9
--- /dev/null
@@ -0,0 +1,59 @@
+/*
+ *
+ *  Connection Manager
+ *
+ *  Copyright (C) 2007-2009  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
+ *
+ */
+
+#ifndef __CONNMAN_RESOLVER_H
+#define __CONNMAN_RESOLVER_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * SECTION:resolver
+ * @title: Resolver premitives
+ * @short_description: Functions for registering resolver modules
+ */
+
+#define CONNMAN_RESOLVER_PRIORITY_LOW      -100
+#define CONNMAN_RESOLVER_PRIORITY_DEFAULT     0
+#define CONNMAN_RESOLVER_PRIORITY_HIGH      100
+
+struct connman_resolver {
+       const char *name;
+       int priority;
+       int (*append) (const char *interface, const char *domain,
+                                                       const char *server);
+       int (*remove) (const char *interface, const char *domain,
+                                                       const char *server);
+};
+
+extern int connman_resolver_register(struct connman_resolver *resolver);
+extern void connman_resolver_unregister(struct connman_resolver *resolver);
+
+extern int connman_resolver_append(const char *interface, const char *domain,
+                                                       const char *server);
+extern int connman_resolver_remove_all(const char *interface);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __CONNMAN_RESOLVER_H */
diff --git a/include/rfkill.h b/include/rfkill.h
new file mode 100644 (file)
index 0000000..fc8848c
--- /dev/null
@@ -0,0 +1,33 @@
+/*
+ *
+ *  Connection Manager
+ *
+ *  Copyright (C) 2007-2009  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
+ *
+ */
+
+#ifndef __CONNMAN_RFKILL_H
+#define __CONNMAN_RFKILL_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __CONNMAN_RFKILL_H */
diff --git a/include/rtnl.h b/include/rtnl.h
new file mode 100644 (file)
index 0000000..c78556b
--- /dev/null
@@ -0,0 +1,67 @@
+/*
+ *
+ *  Connection Manager
+ *
+ *  Copyright (C) 2007-2009  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
+ *
+ */
+
+#ifndef __CONNMAN_RTNL_H
+#define __CONNMAN_RTNL_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * SECTION:rtnl
+ * @title: RTNL premitives
+ * @short_description: Functions for registering RTNL modules
+ */
+
+typedef void (* connman_rtnl_link_cb_t) (unsigned flags, unsigned change,
+                                                       void *user_data);
+
+extern unsigned int connman_rtnl_add_newlink_watch(int index,
+                       connman_rtnl_link_cb_t callback, void *user_data);
+extern void connman_rtnl_remove_watch(unsigned int id);
+
+#define CONNMAN_RTNL_PRIORITY_LOW      -100
+#define CONNMAN_RTNL_PRIORITY_DEFAULT     0
+#define CONNMAN_RTNL_PRIORITY_HIGH      100
+
+struct connman_rtnl {
+       const char *name;
+       int priority;
+       void (*newlink) (unsigned short type, int index,
+                                       unsigned flags, unsigned change);
+       void (*dellink) (unsigned short type, int index,
+                                       unsigned flags, unsigned change);
+       void (*newgateway) (int index, const char *gateway);
+       void (*delgateway) (int index, const char *gateway);
+};
+
+extern int connman_rtnl_register(struct connman_rtnl *rtnl);
+extern void connman_rtnl_unregister(struct connman_rtnl *rtnl);
+
+extern int connman_rtnl_send_getlink(void);
+extern int connman_rtnl_send_getroute(void);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __CONNMAN_RTNL_H */
diff --git a/include/security.h b/include/security.h
new file mode 100644 (file)
index 0000000..9e27a5f
--- /dev/null
@@ -0,0 +1,59 @@
+/*
+ *
+ *  Connection Manager
+ *
+ *  Copyright (C) 2007-2009  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
+ *
+ */
+
+#ifndef __CONNMAN_SECURITY_H
+#define __CONNMAN_SECURITY_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * SECTION:security
+ * @title: Security premitives
+ * @short_description: Functions for registering security modules
+ */
+
+enum connman_security_privilege {
+       CONNMAN_SECURITY_PRIVILEGE_PUBLIC  = 0,
+       CONNMAN_SECURITY_PRIVILEGE_MODIFY  = 1,
+       CONNMAN_SECURITY_PRIVILEGE_SECRET  = 2,
+};
+
+#define CONNMAN_SECURITY_PRIORITY_LOW      -100
+#define CONNMAN_SECURITY_PRIORITY_DEFAULT     0
+#define CONNMAN_SECURITY_PRIORITY_HIGH      100
+
+struct connman_security {
+       const char *name;
+       int priority;
+       int (*authorize_sender) (const char *sender,
+                               enum connman_security_privilege privilege);
+};
+
+extern int connman_security_register(struct connman_security *security);
+extern void connman_security_unregister(struct connman_security *security);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __CONNMAN_SECURITY_H */
diff --git a/include/service.h b/include/service.h
new file mode 100644 (file)
index 0000000..eddd546
--- /dev/null
@@ -0,0 +1,80 @@
+/*
+ *
+ *  Connection Manager
+ *
+ *  Copyright (C) 2007-2009  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
+ *
+ */
+
+#ifndef __CONNMAN_SERVICE_H
+#define __CONNMAN_SERVICE_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * SECTION:service
+ * @title: Service premitives
+ * @short_description: Functions for handling services
+ */
+
+enum connman_service_type {
+       CONNMAN_SERVICE_TYPE_UNKNOWN  = 0,
+       CONNMAN_SERVICE_TYPE_ETHERNET = 1,
+       CONNMAN_SERVICE_TYPE_WIFI     = 2,
+       CONNMAN_SERVICE_TYPE_WIMAX    = 3,
+};
+
+enum connman_service_mode {
+       CONNMAN_SERVICE_MODE_UNKNOWN = 0,
+       CONNMAN_SERVICE_MODE_MANAGED = 1,
+       CONNMAN_SERVICE_MODE_ADHOC   = 2,
+};
+
+enum connman_service_security {
+       CONNMAN_SERVICE_SECURITY_UNKNOWN = 0,
+       CONNMAN_SERVICE_SECURITY_NONE    = 1,
+       CONNMAN_SERVICE_SECURITY_WEP     = 2,
+       CONNMAN_SERVICE_SECURITY_WPA     = 3,
+       CONNMAN_SERVICE_SECURITY_RSN     = 4,
+};
+
+enum connman_service_state {
+       CONNMAN_SERVICE_STATE_UNKNOWN       = 0,
+       CONNMAN_SERVICE_STATE_IDLE          = 1,
+       CONNMAN_SERVICE_STATE_CARRIER       = 2,
+       CONNMAN_SERVICE_STATE_ASSOCIATION   = 3,
+       CONNMAN_SERVICE_STATE_CONFIGURATION = 4,
+       CONNMAN_SERVICE_STATE_READY         = 5,
+       CONNMAN_SERVICE_STATE_DISCONNECT    = 6,
+       CONNMAN_SERVICE_STATE_FAILURE       = 7,
+};
+
+struct connman_service;
+
+extern struct connman_service *connman_service_create(void);
+extern struct connman_service *connman_service_ref(struct connman_service *service);
+extern void connman_service_unref(struct connman_service *service);
+
+extern int connman_service_set_favorite(struct connman_service *service,
+                                               connman_bool_t favorite);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __CONNMAN_SERVICE_H */
diff --git a/include/storage.h b/include/storage.h
new file mode 100644 (file)
index 0000000..61cbf9a
--- /dev/null
@@ -0,0 +1,66 @@
+/*
+ *
+ *  Connection Manager
+ *
+ *  Copyright (C) 2007-2009  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
+ *
+ */
+
+#ifndef __CONNMAN_STORAGE_H
+#define __CONNMAN_STORAGE_H
+
+#include <connman/device.h>
+#include <connman/network.h>
+#include <connman/service.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * SECTION:storage
+ * @title: Storage premitives
+ * @short_description: Functions for registering storage modules
+ */
+
+#define CONNMAN_STORAGE_PRIORITY_LOW      -100
+#define CONNMAN_STORAGE_PRIORITY_DEFAULT     0
+#define CONNMAN_STORAGE_PRIORITY_HIGH      100
+
+struct connman_storage {
+       const char *name;
+       int priority;
+       enum connman_device_type device_type;
+       int (*device_init) (void);
+       int (*device_load) (struct connman_device *device);
+       int (*device_save) (struct connman_device *device);
+       enum connman_network_type network_type;
+       int (*network_init) (struct connman_device *device);
+       int (*network_load) (struct connman_network *network);
+       int (*network_save) (struct connman_network *network);
+       enum connman_service_type service_type;
+       int (*service_load) (struct connman_service *service);
+       int (*service_save) (struct connman_service *service);
+};
+
+extern int connman_storage_register(struct connman_storage *storage);
+extern void connman_storage_unregister(struct connman_storage *storage);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __CONNMAN_STORAGE_H */
diff --git a/include/types.h b/include/types.h
new file mode 100644 (file)
index 0000000..ab58f1e
--- /dev/null
@@ -0,0 +1,45 @@
+/*
+ *
+ *  Connection Manager
+ *
+ *  Copyright (C) 2007-2009  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
+ *
+ */
+
+#ifndef __CONNMAN_TYPES_H
+#define __CONNMAN_TYPES_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#ifndef        FALSE
+#define        FALSE   (0)
+#endif
+
+#ifndef        TRUE
+#define        TRUE    (!FALSE)
+#endif
+
+typedef int            connman_bool_t;
+typedef unsigned char  connman_uint8_t;
+typedef unsigned short connman_uint16_t;
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __CONNMAN_TYPES_H */
diff --git a/include/version.h.in b/include/version.h.in
new file mode 100644 (file)
index 0000000..5d44396
--- /dev/null
@@ -0,0 +1,35 @@
+/*
+ *
+ *  Connection Manager
+ *
+ *  Copyright (C) 2007-2009  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
+ *
+ */
+
+#ifndef __CONNMAN_VERSION_H
+#define __CONNMAN_VERSION_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#define CONNMAN_VERSION        "@VERSION@"
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __CONNMAN_VERSION_H */
diff --git a/plugins/Makefile.am b/plugins/Makefile.am
new file mode 100644 (file)
index 0000000..8c315e5
--- /dev/null
@@ -0,0 +1,224 @@
+
+plugindir = $(libdir)/connman/plugins
+
+plugin_LTLIBRARIES =
+
+builtin_modules =
+builtin_sources =
+builtin_libadd =
+builtin_cflags =
+
+if LOOPBACK
+if LOOPBACK_BUILTIN
+builtin_modules += loopback
+builtin_sources += loopback.c
+else
+plugin_LTLIBRARIES += loopback.la
+endif
+endif
+
+if ETHERNET
+if ETHERNET_BUILTIN
+builtin_modules += ethernet
+builtin_sources += ethernet.c
+else
+plugin_LTLIBRARIES += ethernet.la
+ethernet_la_SOURCES = ethernet.c
+endif
+endif
+
+if WIFI
+if WIFI_BUILTIN
+builtin_modules += wifi
+builtin_sources += wifi.c supplicant.h supplicant.c
+else
+plugin_LTLIBRARIES += wifi.la
+wifi_la_SOURCES = wifi.c supplicant.h supplicant.c
+wifi_la_LIBADD = @GDBUS_LIBS@
+endif
+endif
+
+if BLUETOOTH
+if BLUETOOTH_BUILTIN
+builtin_modules += bluetooth
+builtin_sources += bluetooth.c
+else
+plugin_LTLIBRARIES += bluetooth.la
+bluetooth_la_SOURCES = bluetooth.c
+bluetooth_la_LIBADD = @GDBUS_LIBS@
+endif
+endif
+
+if OFONO
+if OFONO_BUILTIN
+builtin_modules += ofono
+builtin_sources += ofono.c
+else
+plugin_LTLIBRARIES += ofono.la
+bluetooth_la_LIBADD = @GDBUS_LIBS@
+endif
+endif
+
+if MODEMMGR
+if MODEMMGR_BUILTIN
+builtin_modules += modemmgr
+builtin_sources += modemmgr.c
+else
+plugin_LTLIBRARIES += modemmgr.la
+modemmgr_la_LIBADD = @GDBUS_LIBS@
+endif
+endif
+
+if NOVATEL
+plugin_LTLIBRARIES += novatel.la
+novatel_la_SOURCES = novatel.c modem.h modem.c
+endif
+
+if HUAWEI
+plugin_LTLIBRARIES += huawei.la
+huawei_la_SOURCES = huawei.c modem.h modem.c
+endif
+
+if HSO
+if HSO_BUILTIN
+builtin_modules += hso
+builtin_sources += hso.c modem.h modem.c
+builtin_libadd += @GATCHAT_LIBS@
+builtin_cflags += @GATCHAT_CFLAGS@
+else
+plugin_LTLIBRARIES += hso.la
+hso_la_SOURCES = hso.c modem.h modem.c
+hso_la_LIBADD = @GATCHAT_LIBS@
+hso_la_CFLAGS = $(AM_CFLAGS) @GATCHAT_CFLAGS@
+endif
+endif
+
+if UDHCP
+if UDHCP_BUILTIN
+builtin_modules += udhcp
+builtin_sources += udhcp.c task.h task.c
+builtin_cflags += -DUDHCPC=\"@UDHCPC@\"
+else
+plugin_LTLIBRARIES += udhcp.la
+udhcp_la_SOURCES = udhcp.c task.h task.c
+udhcp_la_CFLAGS = $(AM_CFLAGS) -DUDHCPC=\"@UDHCPC@\" \
+               -DSTATEDIR=\""$(statedir)"\" -DSCRIPTDIR=\""$(scriptdir)"\"
+endif
+endif
+
+if DHCLIENT
+if DHCLIENT_BUILTIN
+builtin_modules += dhclient
+builtin_sources += dhclient.c
+builtin_cflags += -DDHCLIENT=\"@DHCLIENT@\"
+else
+plugin_LTLIBRARIES += dhclient.la
+dhclient_la_SOURCES = dhclient.c
+dhclient_la_CFLAGS = $(AM_CFLAGS) -DDHCLIENT=\"@DHCLIENT@\" \
+               -DSTATEDIR=\""$(statedir)"\" -DSCRIPTDIR=\""$(scriptdir)"\"
+endif
+endif
+
+if PPPD
+plugin_LTLIBRARIES += pppd.la
+pppd_la_CFLAGS = $(AM_CFLAGS) -DPPPD=\"@PPPD@\"
+endif
+
+if RESOLVCONF
+if RESOLVCONF_BUILTIN
+builtin_modules += resolvconf
+builtin_sources += resolvconf.c
+builtin_cflags += -DRESOLVCONF=\"@RESOLVCONF@\"
+else
+plugin_LTLIBRARIES += resolvconf.la
+resolvconf_la_CFLAGS = $(AM_CFLAGS) -DRESOLVCONF=\"@RESOLVCONF@\"
+endif
+endif
+
+if DNSPROXY
+if DNSPROXY_BUILTIN
+builtin_modules += dnsproxy
+builtin_sources += dnsproxy.c
+else
+plugin_LTLIBRARIES += dnsproxy.la
+endif
+endif
+
+if POLKIT
+if POLKIT_BUILTIN
+builtin_modules += polkit
+builtin_sources += polkit.c
+builtin_libadd += @POLKIT_LIBS@
+builtin_cflags += @POLKIT_CFLAGS@
+else
+plugin_LTLIBRARIES += polkit.la
+polkit_la_LIBADD = @POLKIT_LIBS@ @GLIB_LIBS@
+polkit_la_CFLAGS = $(AM_CFLAGS) @POLKIT_CFLAGS@
+endif
+
+if DATAFILES
+policydir = @POLKIT_DATADIR@
+
+policy_DATA = connman.policy
+endif
+endif
+
+if IWMX
+plugin_LTLIBRARIES += iwmxsdk.la
+iwmxsdk_la_SOURCES = iwmx.h iwmx.c iwmxsdk.c
+iwmxsdk_la_LIBADD = @IWMXSDK_LIBS@ @GLIB_LIBS@
+iwmxsdk_la_CFLAGS = $(AM_CFLAGS) @IWMXSDK_CFLAGS@
+endif
+
+if IOSPM
+plugin_LTLIBRARIES += iospm.la
+endif
+
+if FAKE
+plugin_LTLIBRARIES += fake.la
+endif
+
+noinst_LTLIBRARIES = libbuiltin.la
+
+libbuiltin_la_SOURCES = $(builtin_sources)
+libbuiltin_la_LIBADD = $(builtin_libadd)
+libbuiltin_la_CFLAGS = $(AM_CFLAGS) \
+               $(builtin_cflags) -DCONNMAN_PLUGIN_BUILTIN \
+               -DSTATEDIR=\""$(statedir)"\" -DSCRIPTDIR=\""$(scriptdir)"\"
+
+BUILT_SOURCES = builtin.h
+
+nodist_libbuiltin_la_SOURCES = $(BUILT_SOURCES)
+
+AM_LDFLAGS = -no-undefined -module -avoid-version
+
+statedir = $(localstatedir)/run/connman
+
+if MAINTAINER_MODE
+scriptdir = $(abs_top_srcdir)/scripts
+else
+scriptdir = $(libdir)/connman/scripts
+endif
+
+AM_CFLAGS = -fvisibility=hidden @GLIB_CFLAGS@ @GDBUS_CFLAGS@
+
+INCLUDES = -I$(top_builddir)/include
+
+CLEANFILES = $(BUILT_SOURCES) connman.policy
+
+EXTRA_DIST = polkit.policy
+
+MAINTAINERCLEANFILES = Makefile.in
+
+builtin.h:
+       echo "" > $@
+       list='$(builtin_modules)'; for i in $$list; \
+         do echo "extern struct connman_plugin_desc __connman_builtin_$$i;" >> $@; done
+       echo "" >> $@
+       echo "static struct connman_plugin_desc *__connman_builtin[] = {" >> $@
+       list='$(builtin_modules)'; for i in $$list; \
+         do echo "&__connman_builtin_$$i," >> $@; done
+       echo "NULL };" >> $@
+
+connman.policy: polkit.policy
+       cp $< $@
diff --git a/plugins/bluetooth.c b/plugins/bluetooth.c
new file mode 100644 (file)
index 0000000..8279f61
--- /dev/null
@@ -0,0 +1,881 @@
+/*
+ *
+ *  Connection Manager
+ *
+ *  Copyright (C) 2007-2009  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 <errno.h>
+#include <stdlib.h>
+
+#include <gdbus.h>
+
+#define CONNMAN_API_SUBJECT_TO_CHANGE
+#include <connman/plugin.h>
+#include <connman/device.h>
+#include <connman/inet.h>
+#include <connman/dbus.h>
+#include <connman/log.h>
+
+#define BLUEZ_SERVICE                  "org.bluez"
+#define BLUEZ_MANAGER_INTERFACE                BLUEZ_SERVICE ".Manager"
+#define BLUEZ_ADAPTER_INTERFACE                BLUEZ_SERVICE ".Adapter"
+#define BLUEZ_DEVICE_INTERFACE         BLUEZ_SERVICE ".Device"
+#define BLUEZ_NETWORK_INTERFACE                BLUEZ_SERVICE ".Network"
+
+#define LIST_ADAPTERS                  "ListAdapters"
+#define ADAPTER_ADDED                  "AdapterAdded"
+#define ADAPTER_REMOVED                        "AdapterRemoved"
+
+#define PROPERTY_CHANGED               "PropertyChanged"
+#define GET_PROPERTIES                 "GetProperties"
+#define SET_PROPERTY                   "SetProperty"
+
+#define CONNECT                                "Connect"
+#define DISCONNECT                     "Disconnect"
+
+#define TIMEOUT 5000
+
+typedef void (* properties_callback_t) (DBusConnection *connection,
+                                                       const char *path,
+                                                       DBusMessage *message,
+                                                       void *user_data);
+
+struct properties_data {
+       DBusConnection *connection;
+       DBusMessage *message;
+       properties_callback_t callback;
+       void *user_data;
+};
+
+static void get_properties_reply(DBusPendingCall *call, void *user_data)
+{
+       struct properties_data *data = user_data;
+       DBusMessage *reply;
+       const char *path;
+
+       reply = dbus_pending_call_steal_reply(call);
+       if (reply == NULL)
+               goto done;
+
+       path = dbus_message_get_path(data->message);
+
+       data->callback(data->connection, path, reply, data->user_data);
+
+       dbus_message_unref(reply);
+
+done:
+       dbus_message_unref(data->message);
+       g_free(data);
+}
+
+static void get_properties(DBusConnection *connection,
+                               const char *path, const char *interface,
+                               properties_callback_t callback, void *user_data)
+{
+       struct properties_data *data;
+       DBusMessage *message;
+       DBusPendingCall *call;
+
+       DBG("path %s interface %s", path, interface);
+
+       data = g_try_new0(struct properties_data, 1);
+       if (data == NULL)
+               return;
+
+       message = dbus_message_new_method_call(BLUEZ_SERVICE, path,
+                                               interface, GET_PROPERTIES);
+       if (message == NULL) {
+               g_free(data);
+               return;
+       }
+
+       if (dbus_connection_send_with_reply(connection, message,
+                                               &call, TIMEOUT) == FALSE) {
+               connman_error("Failed to get properties for %s", interface);
+               dbus_message_unref(message);
+               g_free(data);
+               return;
+       }
+
+       if (call == NULL) {
+               connman_error("D-Bus connection not available");
+               dbus_message_unref(message);
+               g_free(data);
+               return;
+       }
+
+       data->connection = connection;
+       data->message    = message;
+       data->callback   = callback;
+       data->user_data  = user_data;
+
+       dbus_pending_call_set_notify(call, get_properties_reply, data, NULL);
+}
+
+struct adapter_data {
+       DBusConnection *connection;
+};
+
+struct network_data {
+       DBusConnection *connection;
+       char *interface;
+};
+
+static int pan_probe(struct connman_network *network)
+{
+       struct connman_device *device = connman_network_get_device(network);
+       struct adapter_data *adapter;
+       struct network_data *data;
+
+       DBG("network %p", network);
+
+       if (device == NULL)
+               return -EINVAL;
+
+       adapter = connman_device_get_data(device);
+       if (adapter == NULL)
+               return -EINVAL;
+
+       data = g_try_new0(struct network_data, 1);
+       if (data == NULL)
+               return -ENOMEM;
+
+       data->connection = adapter->connection;
+
+       connman_network_set_data(network, data);
+
+       return 0;
+}
+
+static void pan_remove(struct connman_network *network)
+{
+       struct network_data *data = connman_network_get_data(network);
+
+       DBG("network %p", network);
+
+       connman_network_set_data(network, NULL);
+
+       g_free(data);
+}
+
+static void connect_reply(DBusPendingCall *call, void *user_data)
+{
+       struct connman_network *network = user_data;
+       struct network_data *data = connman_network_get_data(network);
+       DBusMessage *reply;
+       DBusError error;
+       const char *interface = NULL;
+       int index;
+
+       DBG("network %p", network);
+
+       reply = dbus_pending_call_steal_reply(call);
+       if (reply == NULL)
+               return;
+
+       dbus_error_init(&error);
+
+       if (dbus_message_get_args(reply, &error,
+                                       DBUS_TYPE_STRING, &interface,
+                                               DBUS_TYPE_INVALID) == FALSE) {
+               if (dbus_error_is_set(&error) == TRUE) {
+                       connman_error("%s", error.message);
+                       dbus_error_free(&error);
+               } else
+                       connman_error("Wrong arguments for connect");
+               goto done;
+       }
+
+       if (interface == NULL)
+               goto done;
+
+       DBG("interface %s", interface);
+
+       data->interface = g_strdup(interface);
+
+       index = connman_inet_ifindex(interface);
+
+       connman_network_set_index(network, index);
+       connman_network_set_connected(network, TRUE);
+
+done:
+       dbus_message_unref(reply);
+}
+
+static int pan_connect(struct connman_network *network)
+{
+       struct network_data *data = connman_network_get_data(network);
+       const char *path = connman_network_get_string(network, "Node");
+       const char *uuid = "nap";
+       DBusMessage *message;
+       DBusPendingCall *call;
+
+       DBG("network %p", network);
+
+       message = dbus_message_new_method_call(BLUEZ_SERVICE, path,
+                                       BLUEZ_NETWORK_INTERFACE, CONNECT);
+       if (message == NULL)
+               return -ENOMEM;
+
+       dbus_message_append_args(message, DBUS_TYPE_STRING, &uuid,
+                                                       DBUS_TYPE_INVALID);
+
+       if (dbus_connection_send_with_reply(data->connection, message,
+                                       &call, TIMEOUT * 10) == FALSE) {
+               connman_error("Failed to connect service");
+               dbus_message_unref(message);
+               return -EINVAL;
+       }
+
+       if (call == NULL) {
+               connman_error("D-Bus connection not available");
+               dbus_message_unref(message);
+               return -EINVAL;
+       }
+
+       dbus_pending_call_set_notify(call, connect_reply, network, NULL);
+
+       dbus_message_unref(message);
+
+       return -EINPROGRESS;
+}
+
+static void disconnect_reply(DBusPendingCall *call, void *user_data)
+{
+       struct connman_network *network = user_data;
+       struct network_data *data = connman_network_get_data(network);
+       DBusMessage *reply;
+       DBusError error;
+
+       DBG("network %p", network);
+
+       reply = dbus_pending_call_steal_reply(call);
+       if (reply == NULL)
+               return;
+
+       dbus_error_init(&error);
+
+       if (dbus_message_get_args(reply, &error, DBUS_TYPE_INVALID) == FALSE) {
+               if (dbus_error_is_set(&error) == TRUE) {
+                       connman_error("%s", error.message);
+                       dbus_error_free(&error);
+               } else
+                       connman_error("Wrong arguments for disconnect");
+               goto done;
+       }
+
+       g_free(data->interface);
+       data->interface = NULL;
+
+       connman_network_set_connected(network, FALSE);
+       connman_network_set_index(network, -1);
+
+done:
+       dbus_message_unref(reply);
+}
+
+static int pan_disconnect(struct connman_network *network)
+{
+       struct network_data *data = connman_network_get_data(network);
+       const char *path = connman_network_get_string(network, "Node");
+       DBusMessage *message;
+       DBusPendingCall *call;
+
+       DBG("network %p", network);
+
+       if (data->interface == NULL)
+               return -EINVAL;
+
+       message = dbus_message_new_method_call(BLUEZ_SERVICE, path,
+                                       BLUEZ_NETWORK_INTERFACE, DISCONNECT);
+       if (message == NULL)
+               return -ENOMEM;
+
+       dbus_message_append_args(message, DBUS_TYPE_INVALID);
+
+       if (dbus_connection_send_with_reply(data->connection, message,
+                                               &call, TIMEOUT) == FALSE) {
+               connman_error("Failed to disconnect service");
+               dbus_message_unref(message);
+               return -EINVAL;
+       }
+
+       if (call == NULL) {
+               connman_error("D-Bus connection not available");
+               dbus_message_unref(message);
+               return -EINVAL;
+       }
+
+       dbus_pending_call_set_notify(call, disconnect_reply, network, NULL);
+
+       dbus_message_unref(message);
+
+       return -EINPROGRESS;
+}
+
+static struct connman_network_driver pan_driver = {
+       .name           = "bluetooth-pan",
+       .type           = CONNMAN_NETWORK_TYPE_BLUETOOTH_PAN,
+       .probe          = pan_probe,
+       .remove         = pan_remove,
+       .connect        = pan_connect,
+       .disconnect     = pan_disconnect,
+};
+
+static int bluetooth_probe(struct connman_device *adapter)
+{
+       struct adapter_data *data;
+
+       DBG("adapter %p", adapter);
+
+       data = g_try_new0(struct adapter_data, 1);
+       if (data == NULL)
+               return -ENOMEM;
+
+       data->connection = connman_dbus_get_connection();
+       if (data->connection == NULL) {
+               g_free(data);
+               return -EIO;
+       }
+
+       connman_device_set_data(adapter, data);
+
+       return 0;
+}
+
+static void bluetooth_remove(struct connman_device *adapter)
+{
+       struct adapter_data *data = connman_device_get_data(adapter);
+
+       DBG("adapter %p", adapter);
+
+       connman_device_set_data(adapter, NULL);
+
+       dbus_connection_unref(data->connection);
+
+       g_free(data);
+}
+
+static void powered_reply(DBusPendingCall *call, void *user_data)
+{
+       DBusMessage *reply;
+
+       DBG("");
+
+       reply = dbus_pending_call_steal_reply(call);
+
+       dbus_message_unref(reply);
+}
+
+static int change_powered(DBusConnection *connection, const char *path,
+                                                       dbus_bool_t powered)
+{
+       DBusMessage *message;
+       DBusMessageIter iter;
+       DBusPendingCall *call;
+
+       DBG("");
+
+       message = dbus_message_new_method_call(BLUEZ_SERVICE, path,
+                                       BLUEZ_ADAPTER_INTERFACE, SET_PROPERTY);
+       if (message == NULL)
+               return -ENOMEM;
+
+       dbus_message_iter_init_append(message, &iter);
+       connman_dbus_property_append_variant(&iter, "Powered",
+                                               DBUS_TYPE_BOOLEAN, &powered);
+
+       if (dbus_connection_send_with_reply(connection, message,
+                                               &call, TIMEOUT) == FALSE) {
+               connman_error("Failed to change Powered property");
+               dbus_message_unref(message);
+               return -EINVAL;
+       }
+
+       if (call == NULL) {
+               connman_error("D-Bus connection not available");
+               dbus_message_unref(message);
+               return -EINVAL;
+       }
+
+       dbus_pending_call_set_notify(call, powered_reply, NULL, NULL);
+
+       dbus_message_unref(message);
+
+       return -EINPROGRESS;
+}
+
+static int bluetooth_enable(struct connman_device *adapter)
+{
+       struct adapter_data *data = connman_device_get_data(adapter);
+       const char *path = connman_device_get_string(adapter, "Node");
+
+       DBG("adapter %p", adapter);
+
+       return change_powered(data->connection, path, TRUE);
+}
+
+static int bluetooth_disable(struct connman_device *adapter)
+{
+       struct adapter_data *data = connman_device_get_data(adapter);
+       const char *path = connman_device_get_string(adapter, "Node");
+
+       DBG("adapter %p", adapter);
+
+       return change_powered(data->connection, path, FALSE);
+}
+
+static int bluetooth_scan(struct connman_device *adapter)
+{
+       DBG("adapter %p", adapter);
+
+       return -EIO;
+}
+
+static struct connman_device_driver bluetooth_driver = {
+       .name           = "bluetooth",
+       .type           = CONNMAN_DEVICE_TYPE_BLUETOOTH,
+       .probe          = bluetooth_probe,
+       .remove         = bluetooth_remove,
+       .enable         = bluetooth_enable,
+       .disable        = bluetooth_disable,
+       .scan           = bluetooth_scan,
+};
+
+static GSList *adapter_list = NULL;
+
+static void free_adapters(void)
+{
+       GSList *list;
+
+       DBG("");
+
+       for (list = adapter_list; list; list = list->next) {
+               struct connman_device *adapter = list->data;
+
+               connman_device_unregister(adapter);
+               connman_device_unref(adapter);
+       }
+
+       g_slist_free(adapter_list);
+       adapter_list = NULL;
+}
+
+static struct connman_device *find_adapter(const char *path)
+{
+       GSList *list;
+
+       DBG("path %s", path);
+
+       for (list = adapter_list; list; list = list->next) {
+               struct connman_device *adapter = list->data;
+               const char *adapter_path = connman_device_get_string(adapter,
+                                                                       "Node");
+
+               if (adapter_path == NULL)
+                       continue;
+
+               if (g_str_equal(adapter_path, path) == TRUE)
+                       return adapter;
+       }
+
+       return NULL;
+}
+
+static void device_properties(DBusConnection *connection, const char *path,
+                               DBusMessage *message, void *user_data)
+{
+       struct connman_device *device = user_data;
+       const char *node = g_basename(path);
+       struct connman_network *network;
+
+       DBG("path %s", path);
+
+       network = connman_device_get_network(device, node);
+       if (network != NULL)
+               return;
+
+       network = connman_network_create(node,
+                                       CONNMAN_NETWORK_TYPE_BLUETOOTH_PAN);
+       if (network == NULL)
+               return;
+
+       connman_network_set_protocol(network, CONNMAN_NETWORK_PROTOCOL_IP);
+
+       connman_network_set_string(network, "Node", path);
+
+       connman_device_add_network(device, network);
+}
+
+static void check_devices(struct connman_device *adapter,
+                       DBusConnection *connection, DBusMessageIter *array)
+{
+       DBusMessageIter value;
+
+       if (dbus_message_iter_get_arg_type(array) != DBUS_TYPE_ARRAY)
+               return;
+
+       dbus_message_iter_recurse(array, &value);
+
+       while (dbus_message_iter_get_arg_type(&value) == DBUS_TYPE_OBJECT_PATH) {
+               const char *path;
+
+               dbus_message_iter_get_basic(&value, &path);
+
+               get_properties(connection, path, BLUEZ_DEVICE_INTERFACE,
+                                               device_properties, adapter);
+
+               dbus_message_iter_next(&value);
+       }
+}
+
+static void property_changed(DBusConnection *connection, DBusMessage *message)
+{
+       const char *path = dbus_message_get_path(message);
+       struct connman_device *adapter;
+       DBusMessageIter iter, value;
+       const char *key;
+
+       DBG("path %s", path);
+
+       adapter = find_adapter(path);
+       if (adapter == NULL)
+               return;
+
+       if (dbus_message_iter_init(message, &iter) == FALSE)
+               return;
+
+       dbus_message_iter_get_basic(&iter, &key);
+
+       dbus_message_iter_next(&iter);
+       dbus_message_iter_recurse(&iter, &value);
+
+       if (g_str_equal(key, "Powered") == TRUE) {
+               gboolean val;
+
+               dbus_message_iter_get_basic(&value, &val);
+               connman_device_set_powered(adapter, val);
+       } else if (g_str_equal(key, "Discovering") == TRUE) {
+               gboolean val;
+
+               dbus_message_iter_get_basic(&value, &val);
+               connman_device_set_scanning(adapter, val);
+       }
+}
+
+static void parse_adapter_properties(struct connman_device *adapter,
+                                               DBusConnection *connection,
+                                                       DBusMessage *reply)
+{
+       DBusMessageIter array, dict;
+
+       if (dbus_message_iter_init(reply, &array) == FALSE)
+               return;
+
+       if (dbus_message_iter_get_arg_type(&array) != DBUS_TYPE_ARRAY)
+               return;
+
+       dbus_message_iter_recurse(&array, &dict);
+
+       while (dbus_message_iter_get_arg_type(&dict) == DBUS_TYPE_DICT_ENTRY) {
+               DBusMessageIter entry, value;
+               const char *key;
+
+               dbus_message_iter_recurse(&dict, &entry);
+               dbus_message_iter_get_basic(&entry, &key);
+
+               dbus_message_iter_next(&entry);
+               dbus_message_iter_recurse(&entry, &value);
+
+               if (g_str_equal(key, "Powered") == TRUE) {
+                       gboolean val;
+
+                       dbus_message_iter_get_basic(&value, &val);
+                       connman_device_set_powered(adapter, val);
+               } else if (g_str_equal(key, "Discovering") == TRUE) {
+                       gboolean val;
+
+                       dbus_message_iter_get_basic(&value, &val);
+                       connman_device_set_scanning(adapter, val);
+               } else if (g_str_equal(key, "Devices") == TRUE) {
+                       check_devices(adapter, connection, &value);
+               }
+
+               dbus_message_iter_next(&dict);
+       }
+}
+
+static void adapter_properties(DBusConnection *connection, const char *path,
+                               DBusMessage *message, void *user_data)
+{
+       const char *node = g_basename(path);
+       struct connman_device *adapter;
+
+       DBG("path %s", path);
+
+       adapter = find_adapter(path);
+       if (adapter != NULL)
+               goto done;
+
+       adapter = connman_device_create(node, CONNMAN_DEVICE_TYPE_BLUETOOTH);
+       if (adapter == NULL)
+               return;
+
+       connman_device_set_string(adapter, "Node", path);
+
+       if (node != NULL && g_str_has_prefix(node, "hci") == TRUE) {
+               int index;
+               errno = 0;
+               index = atoi(node + 3);
+               if (errno == 0)
+                       connman_device_set_index(adapter, index);
+       }
+
+       connman_device_set_interface(adapter, node);
+
+       connman_device_set_policy(adapter, CONNMAN_DEVICE_POLICY_MANUAL);
+       connman_device_set_mode(adapter, CONNMAN_DEVICE_MODE_NETWORK_MULTIPLE);
+
+       if (connman_device_register(adapter) < 0) {
+               connman_device_unref(adapter);
+               return;
+       }
+
+       adapter_list = g_slist_append(adapter_list, adapter);
+
+done:
+       parse_adapter_properties(adapter, connection, message);
+}
+
+static void add_adapter(DBusConnection *connection, const char *path)
+{
+       DBG("path %s", path);
+
+       get_properties(connection, path, BLUEZ_ADAPTER_INTERFACE,
+                                               adapter_properties, NULL);
+}
+
+static void remove_adapter(DBusConnection *connection, const char *path)
+{
+       struct connman_device *adapter;
+
+       DBG("path %s", path);
+
+       adapter = find_adapter(path);
+       if (adapter == NULL)
+               return;
+
+       adapter_list = g_slist_remove(adapter_list, adapter);
+
+       connman_device_unregister(adapter);
+       connman_device_unref(adapter);
+}
+
+static void list_adapters_reply(DBusPendingCall *call, void *user_data)
+{
+       DBusConnection *connection = user_data;
+       DBusMessage *reply;
+       DBusError error;
+       char **adapters;
+       int i, num_adapters;
+
+       DBG("");
+
+       reply = dbus_pending_call_steal_reply(call);
+
+       dbus_error_init(&error);
+
+       if (dbus_message_get_args(reply, &error,
+                               DBUS_TYPE_ARRAY, DBUS_TYPE_OBJECT_PATH,
+                                               &adapters, &num_adapters,
+                                               DBUS_TYPE_INVALID) == FALSE) {
+               if (dbus_error_is_set(&error) == TRUE) {
+                       connman_error("%s", error.message);
+                       dbus_error_free(&error);
+               } else
+                       connman_error("Wrong arguments for adapter list");
+               goto done;
+       }
+
+       for (i = 0; i < num_adapters; i++)
+               get_properties(connection, adapters[i],
+                                       BLUEZ_ADAPTER_INTERFACE,
+                                               adapter_properties, NULL);
+
+       g_strfreev(adapters);
+
+done:
+       dbus_message_unref(reply);
+}
+
+static void bluetooth_connect(DBusConnection *connection, void *user_data)
+{
+       DBusMessage *message;
+       DBusPendingCall *call;
+
+       DBG("connection %p", connection);
+
+       message = dbus_message_new_method_call(BLUEZ_SERVICE, "/",
+                               BLUEZ_MANAGER_INTERFACE, LIST_ADAPTERS);
+       if (message == NULL)
+               return;
+
+       if (dbus_connection_send_with_reply(connection, message,
+                                               &call, TIMEOUT) == FALSE) {
+               connman_error("Failed to get Bluetooth adapters");
+               goto done;
+       }
+
+       if (call == NULL) {
+               connman_error("D-Bus connection not available");
+               goto done;
+       }
+
+       dbus_pending_call_set_notify(call, list_adapters_reply,
+                                                       connection, NULL);
+
+done:
+       dbus_message_unref(message);
+}
+
+static void bluetooth_disconnect(DBusConnection *connection, void *user_data)
+{
+       DBG("connection %p", connection);
+
+       free_adapters();
+}
+
+static DBusHandlerResult bluetooth_signal(DBusConnection *connection,
+                                       DBusMessage *message, void *user_data)
+{
+       if (dbus_message_has_interface(message,
+                       BLUEZ_MANAGER_INTERFACE) == FALSE &&
+                               dbus_message_has_interface(message,
+                                       BLUEZ_ADAPTER_INTERFACE) == FALSE)
+               return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
+
+       DBG("connection %p", connection);
+
+       if (dbus_message_is_signal(message, BLUEZ_ADAPTER_INTERFACE,
+                                               PROPERTY_CHANGED) == TRUE) {
+               property_changed(connection, message);
+       } else if (dbus_message_is_signal(message, BLUEZ_MANAGER_INTERFACE,
+                                               ADAPTER_ADDED) == TRUE) {
+               const char *path;
+               dbus_message_get_args(message, NULL,
+                                       DBUS_TYPE_OBJECT_PATH, &path,
+                                                       DBUS_TYPE_INVALID);
+               add_adapter(connection, path);
+       } else if (dbus_message_is_signal(message, BLUEZ_MANAGER_INTERFACE,
+                                               ADAPTER_REMOVED) == TRUE) {
+               const char *path;
+               dbus_message_get_args(message, NULL,
+                                       DBUS_TYPE_OBJECT_PATH, &path,
+                                                       DBUS_TYPE_INVALID);
+               remove_adapter(connection, path);
+       }
+
+       return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
+}
+
+static DBusConnection *connection;
+static guint watch;
+
+static const char *added_rule = "type=signal,member=" ADAPTER_ADDED
+                                       ",interface=" BLUEZ_MANAGER_INTERFACE;
+static const char *removed_rule = "type=signal,member=" ADAPTER_REMOVED
+                                       ",interface=" BLUEZ_MANAGER_INTERFACE;
+
+static const char *adapter_rule = "type=signal,member=" PROPERTY_CHANGED
+                                       ",interface=" BLUEZ_ADAPTER_INTERFACE;
+
+static int bluetooth_init(void)
+{
+       int err = -EIO;
+
+       connection = connman_dbus_get_connection();
+       if (connection == NULL)
+               return -EIO;
+
+       if (dbus_connection_add_filter(connection, bluetooth_signal,
+                                                       NULL, NULL) == FALSE)
+               goto unref;
+
+       err = connman_network_driver_register(&pan_driver);
+       if (err < 0)
+               goto remove;
+
+       err = connman_device_driver_register(&bluetooth_driver);
+       if (err < 0) {
+               connman_network_driver_unregister(&pan_driver);
+               goto remove;
+       }
+
+       watch = g_dbus_add_service_watch(connection, BLUEZ_SERVICE,
+                       bluetooth_connect, bluetooth_disconnect, NULL, NULL);
+       if (watch == 0) {
+               connman_device_driver_unregister(&bluetooth_driver);
+               connman_network_driver_unregister(&pan_driver);
+               err = -EIO;
+               goto remove;
+       }
+
+       if (g_dbus_check_service(connection, BLUEZ_SERVICE) == TRUE)
+               bluetooth_connect(connection, NULL);
+
+       dbus_bus_add_match(connection, added_rule, NULL);
+       dbus_bus_add_match(connection, removed_rule, NULL);
+       dbus_bus_add_match(connection, adapter_rule, NULL);
+       dbus_connection_flush(connection);
+
+       return 0;
+
+remove:
+       dbus_connection_remove_filter(connection, bluetooth_signal, NULL);
+
+unref:
+       dbus_connection_unref(connection);
+
+       return err;
+}
+
+static void bluetooth_exit(void)
+{
+       dbus_bus_remove_match(connection, adapter_rule, NULL);
+       dbus_bus_remove_match(connection, removed_rule, NULL);
+       dbus_bus_remove_match(connection, added_rule, NULL);
+       dbus_connection_flush(connection);
+
+       g_dbus_remove_watch(connection, watch);
+
+       free_adapters();
+
+       connman_device_driver_unregister(&bluetooth_driver);
+       connman_network_driver_unregister(&pan_driver);
+
+       dbus_connection_remove_filter(connection, bluetooth_signal, NULL);
+
+       dbus_connection_unref(connection);
+}
+
+CONNMAN_PLUGIN_DEFINE(bluetooth, "Bluetooth technology plugin", VERSION,
+               CONNMAN_PLUGIN_PRIORITY_DEFAULT, bluetooth_init, bluetooth_exit)
diff --git a/plugins/dhclient.c b/plugins/dhclient.c
new file mode 100644 (file)
index 0000000..10f32ff
--- /dev/null
@@ -0,0 +1,388 @@
+/*
+ *
+ *  Connection Manager
+ *
+ *  Copyright (C) 2007-2009  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 <unistd.h>
+#include <sys/wait.h>
+#include <glib/gstdio.h>
+
+#define CONNMAN_API_SUBJECT_TO_CHANGE
+#include <connman/plugin.h>
+#include <connman/driver.h>
+#include <connman/inet.h>
+#include <connman/dbus.h>
+#include <connman/log.h>
+
+#define DHCLIENT_INTF "org.isc.dhclient"
+#define DHCLIENT_PATH "/org/isc/dhclient"
+
+static const char *busname;
+
+struct dhclient_task {
+       GPid pid;
+       int ifindex;
+       gchar *ifname;
+       struct connman_element *element;
+};
+
+static GSList *task_list = NULL;
+
+static struct dhclient_task *find_task_by_pid(GPid pid)
+{
+       GSList *list;
+
+       for (list = task_list; list; list = list->next) {
+               struct dhclient_task *task = list->data;
+
+               if (task->pid == pid)
+                       return task;
+       }
+
+       return NULL;
+}
+
+static struct dhclient_task *find_task_by_index(int index)
+{
+       GSList *list;
+
+       for (list = task_list; list; list = list->next) {
+               struct dhclient_task *task = list->data;
+
+               if (task->ifindex == index)
+                       return task;
+       }
+
+       return NULL;
+}
+
+static void kill_task(struct dhclient_task *task)
+{
+       DBG("task %p name %s pid %d", task, task->ifname, task->pid);
+
+       if (task->pid > 0)
+               kill(task->pid, SIGTERM);
+}
+
+static void unlink_task(struct dhclient_task *task)
+{
+       gchar *pathname;
+
+       DBG("task %p name %s pid %d", task, task->ifname, task->pid);
+
+       pathname = g_strdup_printf("%s/dhclient.%s.pid",
+                                               STATEDIR, task->ifname);
+       g_unlink(pathname);
+       g_free(pathname);
+
+       pathname = g_strdup_printf("%s/dhclient.%s.leases",
+                                               STATEDIR, task->ifname);
+       g_unlink(pathname);
+       g_free(pathname);
+}
+
+static void task_died(GPid pid, gint status, gpointer data)
+{
+       struct dhclient_task *task = data;
+
+       if (WIFEXITED(status))
+               DBG("exit status %d for %s", WEXITSTATUS(status), task->ifname);
+       else
+               DBG("signal %d killed %s", WTERMSIG(status), task->ifname);
+
+       g_spawn_close_pid(pid);
+       task->pid = 0;
+
+       task_list = g_slist_remove(task_list, task);
+
+       unlink_task(task);
+
+       g_free(task->ifname);
+       g_free(task);
+}
+
+static void task_setup(gpointer data)
+{
+       struct dhclient_task *task = data;
+
+       DBG("task %p name %s", task, task->ifname);
+}
+
+static int dhclient_probe(struct connman_element *element)
+{
+       struct dhclient_task *task;
+       char *argv[16], *envp[1], address[128], pidfile[PATH_MAX];
+       char leases[PATH_MAX], config[PATH_MAX], script[PATH_MAX];
+
+       DBG("element %p name %s", element, element->name);
+
+       if (access(DHCLIENT, X_OK) < 0)
+               return -errno;
+
+       task = g_try_new0(struct dhclient_task, 1);
+       if (task == NULL)
+               return -ENOMEM;
+
+       task->ifindex = element->index;
+       task->ifname = connman_inet_ifname(element->index);
+       task->element = element;
+
+       if (task->ifname == NULL) {
+               g_free(task);
+               return -ENOMEM;
+       }
+
+       DBG("request %s", task->ifname);
+
+       snprintf(address, sizeof(address) - 1, "BUSNAME=%s", busname);
+       snprintf(pidfile, sizeof(pidfile) - 1,
+                       "%s/dhclient.%s.pid", STATEDIR, task->ifname);
+       snprintf(leases, sizeof(leases) - 1,
+                       "%s/dhclient.%s.leases", STATEDIR, task->ifname);
+       snprintf(config, sizeof(config) - 1, "%s/dhclient.conf", SCRIPTDIR);
+       snprintf(script, sizeof(script) - 1, "%s/dhclient-script", SCRIPTDIR);
+
+       argv[0] = DHCLIENT;
+       argv[1] = "-d";
+       argv[2] = "-q";
+       argv[3] = "-e";
+       argv[4] = address;
+       argv[5] = "-pf";
+       argv[6] = pidfile;
+       argv[7] = "-lf";
+       argv[8] = leases;
+       argv[9] = "-cf";
+       argv[10] = config;
+       argv[11] = "-sf";
+       argv[12] = script;
+       argv[13] = task->ifname;
+       argv[14] = "-n";
+       argv[15] = NULL;
+
+       envp[0] = NULL;
+
+       if (g_spawn_async(NULL, argv, envp, G_SPAWN_DO_NOT_REAP_CHILD,
+                               task_setup, task, &task->pid, NULL) == FALSE) {
+               connman_error("Failed to spawn dhclient");
+               return -1;
+       }
+
+       task_list = g_slist_append(task_list, task);
+
+       g_child_watch_add(task->pid, task_died, task);
+
+       DBG("executed %s with pid %d", DHCLIENT, task->pid);
+
+       return 0;
+}
+
+static void dhclient_remove(struct connman_element *element)
+{
+       struct dhclient_task *task;
+
+       DBG("element %p name %s", element, element->name);
+
+       task = find_task_by_index(element->index);
+       if (task != NULL)
+               task_list = g_slist_remove(task_list, task);
+
+       if (task == NULL)
+               return;
+
+       DBG("release %s", task->ifname);
+
+       kill_task(task);
+}
+
+static void dhclient_change(struct connman_element *element)
+{
+       DBG("element %p name %s", element, element->name);
+
+       if (element->state == CONNMAN_ELEMENT_STATE_ERROR)
+               connman_element_set_error(element->parent,
+                                       CONNMAN_ELEMENT_ERROR_DHCP_FAILED);
+}
+
+static struct connman_driver dhclient_driver = {
+       .name           = "dhclient",
+       .type           = CONNMAN_ELEMENT_TYPE_DHCP,
+       .probe          = dhclient_probe,
+       .remove         = dhclient_remove,
+       .change         = dhclient_change,
+};
+
+static DBusHandlerResult dhclient_filter(DBusConnection *conn,
+                                               DBusMessage *msg, void *data)
+{
+       DBusMessageIter iter, dict;
+       dbus_uint32_t pid;
+       struct dhclient_task *task;
+       const char *text, *key, *value;
+
+       if (dbus_message_is_method_call(msg, DHCLIENT_INTF, "notify") == FALSE)
+               return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
+
+       dbus_message_iter_init(msg, &iter);
+
+       dbus_message_iter_get_basic(&iter, &pid);
+       dbus_message_iter_next(&iter);
+
+       dbus_message_iter_get_basic(&iter, &text);
+       dbus_message_iter_next(&iter);
+
+       DBG("change %d to %s", pid, text);
+
+       task = find_task_by_pid(pid);
+
+       if (task == NULL)
+               return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
+
+       dbus_message_iter_recurse(&iter, &dict);
+
+       while (dbus_message_iter_get_arg_type(&dict) == DBUS_TYPE_DICT_ENTRY) {
+               DBusMessageIter entry;
+
+               dbus_message_iter_recurse(&dict, &entry);
+               dbus_message_iter_get_basic(&entry, &key);
+               dbus_message_iter_next(&entry);
+               dbus_message_iter_get_basic(&entry, &value);
+
+               DBG("%s = %s", key, value);
+
+               if (g_ascii_strcasecmp(key, "new_ip_address") == 0) {
+                       g_free(task->element->ipv4.address);
+                       task->element->ipv4.address = g_strdup(value);
+               }
+
+               if (g_ascii_strcasecmp(key, "new_subnet_mask") == 0) {
+                       g_free(task->element->ipv4.netmask);
+                       task->element->ipv4.netmask = g_strdup(value);
+               }
+
+               if (g_ascii_strcasecmp(key, "new_routers") == 0) {
+                       g_free(task->element->ipv4.gateway);
+                       task->element->ipv4.gateway = g_strdup(value);
+               }
+
+               if (g_ascii_strcasecmp(key, "new_network_number") == 0) {
+                       g_free(task->element->ipv4.network);
+                       task->element->ipv4.network = g_strdup(value);
+               }
+
+               if (g_ascii_strcasecmp(key, "new_broadcast_address") == 0) {
+                       g_free(task->element->ipv4.broadcast);
+                       task->element->ipv4.broadcast = g_strdup(value);
+               }
+
+               if (g_ascii_strcasecmp(key, "new_domain_name_servers") == 0) {
+                       g_free(task->element->ipv4.nameserver);
+                       task->element->ipv4.nameserver = g_strdup(value);
+               }
+
+               if (g_ascii_strcasecmp(key, "new_domain_name") == 0) {
+               }
+
+               if (g_ascii_strcasecmp(key, "new_domain_search") == 0) {
+               }
+
+               if (g_ascii_strcasecmp(key, "new_host_name") == 0) {
+               }
+
+               dbus_message_iter_next(&dict);
+       }
+
+       if (g_ascii_strcasecmp(text, "PREINIT") == 0) {
+       } else if (g_ascii_strcasecmp(text, "BOUND") == 0 ||
+                               g_ascii_strcasecmp(text, "REBOOT") == 0) {
+               struct connman_element *element;
+               element = connman_element_create(NULL);
+               element->type = CONNMAN_ELEMENT_TYPE_IPV4;
+               element->index = task->ifindex;
+               connman_element_update(task->element);
+               if (connman_element_register(element, task->element) < 0)
+                       connman_element_unref(element);
+       } else if (g_ascii_strcasecmp(text, "RENEW") == 0 ||
+                               g_ascii_strcasecmp(text, "REBIND") == 0) {
+               connman_element_update(task->element);
+       } else if (g_ascii_strcasecmp(text, "FAIL") == 0) {
+               connman_element_set_error(task->element,
+                                               CONNMAN_ELEMENT_ERROR_FAILED);
+       } else {
+       }
+
+       return DBUS_HANDLER_RESULT_HANDLED;
+}
+
+static DBusConnection *connection;
+
+static const char *dhclient_rule = "path=" DHCLIENT_PATH
+                                               ",interface=" DHCLIENT_INTF;
+
+static int dhclient_init(void)
+{
+       int err;
+
+       connection = connman_dbus_get_connection();
+
+       busname = dbus_bus_get_unique_name(connection);
+       busname = CONNMAN_SERVICE;
+
+       dbus_connection_add_filter(connection, dhclient_filter, NULL, NULL);
+
+       dbus_bus_add_match(connection, dhclient_rule, NULL);
+
+       err = connman_driver_register(&dhclient_driver);
+       if (err < 0) {
+               dbus_connection_unref(connection);
+               return err;
+       }
+
+       return 0;
+}
+
+static void dhclient_exit(void)
+{
+       GSList *list;
+
+       for (list = task_list; list; list = list->next) {
+               struct dhclient_task *task = list->data;
+
+               DBG("killing process %d", task->pid);
+
+               kill_task(task);
+               unlink_task(task);
+       }
+
+       g_slist_free(task_list);
+
+       connman_driver_unregister(&dhclient_driver);
+
+       dbus_bus_remove_match(connection, dhclient_rule, NULL);
+
+       dbus_connection_remove_filter(connection, dhclient_filter, NULL);
+
+       dbus_connection_unref(connection);
+}
+
+CONNMAN_PLUGIN_DEFINE(dhclient, "ISC DHCP client plugin", VERSION,
+               CONNMAN_PLUGIN_PRIORITY_DEFAULT, dhclient_init, dhclient_exit)
diff --git a/plugins/dnsproxy.c b/plugins/dnsproxy.c
new file mode 100644 (file)
index 0000000..ff7ce8a
--- /dev/null
@@ -0,0 +1,687 @@
+/*
+ *
+ *  Connection Manager
+ *
+ *  Copyright (C) 2007-2009  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 <errno.h>
+#include <unistd.h>
+#include <string.h>
+#include <stdint.h>
+#include <arpa/inet.h>
+#include <netinet/in.h>
+
+#define CONNMAN_API_SUBJECT_TO_CHANGE
+#include <connman/plugin.h>
+#include <connman/resolver.h>
+#include <connman/log.h>
+
+#include <glib.h>
+
+#if __BYTE_ORDER == __LITTLE_ENDIAN
+struct domain_hdr {
+       uint16_t id;
+       uint8_t rd:1;
+       uint8_t tc:1;
+       uint8_t aa:1;
+       uint8_t opcode:4;
+       uint8_t qr:1;
+       uint8_t rcode:4;
+       uint8_t z:3;
+       uint8_t ra:1;
+       uint16_t qdcount;
+       uint16_t ancount;
+       uint16_t nscount;
+       uint16_t arcount;
+} __attribute__ ((packed));
+#elif __BYTE_ORDER == __BIG_ENDIAN
+struct domain_hdr {
+       uint16_t id;
+       uint8_t qr:1;
+       uint8_t opcode:4;
+       uint8_t aa:1;
+       uint8_t tc:1;
+       uint8_t rd:1;
+       uint8_t ra:1;
+       uint8_t z:3;
+       uint8_t rcode:4;
+       uint16_t qdcount;
+       uint16_t ancount;
+       uint16_t nscount;
+       uint16_t arcount;
+} __attribute__ ((packed));
+#else
+#error "Unknown byte order"
+#endif
+
+struct server_data {
+       char *interface;
+       char *domain;
+       char *server;
+       GIOChannel *channel;
+       guint watch;
+};
+
+struct request_data {
+       struct sockaddr_in sin;
+       socklen_t len;
+       guint16 srcid;
+       guint16 dstid;
+       guint16 altid;
+       guint timeout;
+       guint numserv;
+       guint numresp;
+       gpointer resp;
+       gsize resplen;
+};
+
+static GSList *server_list = NULL;
+static GSList *request_list = NULL;
+static guint16 request_id = 0x0000;
+
+static GIOChannel *listener_channel = NULL;
+static guint listener_watch = 0;
+
+static struct request_data *find_request(guint16 id)
+{
+       GSList *list;
+
+       for (list = request_list; list; list = list->next) {
+               struct request_data *req = list->data;
+
+               if (req->dstid == id || req->altid == id)
+                       return req;
+       }
+
+       return NULL;
+}
+
+static struct server_data *find_server(const char *interface,
+                                       const char *domain, const char *server)
+{
+       GSList *list;
+
+       DBG("interface %s server %s", interface, server);
+
+       for (list = server_list; list; list = list->next) {
+               struct server_data *data = list->data;
+
+               if (data->interface == NULL || data->server == NULL)
+                       continue;
+
+               if (g_str_equal(data->interface, interface) == TRUE &&
+                               g_str_equal(data->server, server) == TRUE) {
+                       if (domain == NULL) {
+                               if (data->domain == NULL)
+                                       return data;
+                               continue;
+                       }
+
+                       if (g_str_equal(data->domain, domain) == TRUE)
+                               return data;
+               }
+       }
+
+       return NULL;
+}
+
+static gboolean server_event(GIOChannel *channel, GIOCondition condition,
+                                                       gpointer user_data)
+{
+       struct server_data *data = user_data;
+       struct request_data *req;
+       unsigned char buf[768];
+       struct domain_hdr *hdr = (void *) &buf;
+       int sk, err, len;
+
+       if (condition & (G_IO_NVAL | G_IO_ERR | G_IO_HUP)) {
+               connman_error("Error with server channel");
+               data->watch = 0;
+               return FALSE;
+       }
+
+       sk = g_io_channel_unix_get_fd(channel);
+
+       len = recv(sk, buf, sizeof(buf), 0);
+       if (len < 12)
+               return TRUE;
+
+       DBG("Received %d bytes (id 0x%04x)", len, buf[0] | buf[1] << 8);
+
+       req = find_request(buf[0] | buf[1] << 8);
+       if (req == NULL)
+               return TRUE;
+
+       DBG("id 0x%04x rcode %d", hdr->id, hdr->rcode);
+
+       buf[0] = req->srcid & 0xff;
+       buf[1] = req->srcid >> 8;
+
+       req->numresp++;
+
+       if (hdr->rcode == 0 || req->resp == NULL) {
+               g_free(req->resp);
+               req->resplen = 0;
+
+               req->resp = g_try_malloc(len);
+               if (req->resp == NULL)
+                       return TRUE;
+
+               memcpy(req->resp, buf, len);
+               req->resplen = len;
+       }
+
+       if (hdr->rcode > 0 && req->numresp < req->numserv)
+               return TRUE;
+
+       if (req->timeout > 0)
+               g_source_remove(req->timeout);
+
+       request_list = g_slist_remove(request_list, req);
+
+       sk = g_io_channel_unix_get_fd(listener_channel);
+
+       err = sendto(sk, req->resp, req->resplen, 0,
+                               (struct sockaddr *) &req->sin, req->len);
+
+       g_free(req->resp);
+       g_free(req);
+
+       return TRUE;
+}
+
+static struct server_data *create_server(const char *interface,
+                                       const char *domain, const char *server)
+{
+       struct server_data *data;
+       struct sockaddr_in sin;
+       int sk;
+
+       DBG("interface %s server %s", interface, server);
+
+       sk = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
+       if (sk < 0) {
+               connman_error("Failed to create server %s socket", server);
+               return NULL;
+       }
+
+       if (setsockopt(sk, SOL_SOCKET, SO_BINDTODEVICE,
+                               interface, strlen(interface) + 1) < 0) {
+               connman_error("Failed to bind server %s to interface %s",
+                                                       server, interface);
+               close(sk);
+               return NULL;
+       }
+
+       memset(&sin, 0, sizeof(sin));
+       sin.sin_family = AF_INET;
+       sin.sin_port = htons(53);
+       sin.sin_addr.s_addr = inet_addr(server);
+
+       if (connect(sk, (struct sockaddr *) &sin, sizeof(sin)) < 0) {
+               connman_error("Failed to connect server %s", server);
+               close(sk);
+               return NULL;
+       }
+
+       data = g_try_new0(struct server_data, 1);
+       if (data == NULL) {
+               connman_error("Failed to allocate server %s data", server);
+               close(sk);
+               return NULL;
+       }
+
+       data->channel = g_io_channel_unix_new(sk);
+       if (data->channel == NULL) {
+               connman_error("Failed to create server %s channel", server);
+               close(sk);
+               g_free(data);
+               return NULL;
+       }
+
+       g_io_channel_set_close_on_unref(data->channel, TRUE);
+
+       data->watch = g_io_add_watch(data->channel, G_IO_IN,
+                                                       server_event, data);
+
+       data->interface = g_strdup(interface);
+       data->domain = g_strdup(domain);
+       data->server = g_strdup(server);
+
+       return data;
+}
+
+static void destroy_server(struct server_data *data)
+{
+       DBG("interface %s server %s", data->interface, data->server);
+
+       if (data->watch > 0)
+               g_source_remove(data->watch);
+
+       g_io_channel_unref(data->channel);
+
+       g_free(data->server);
+       g_free(data->domain);
+       g_free(data->interface);
+       g_free(data);
+}
+
+static int dnsproxy_append(const char *interface, const char *domain,
+                                                       const char *server)
+{
+       struct server_data *data;
+
+       DBG("interface %s server %s", interface, server);
+
+       if (g_str_equal(server, "127.0.0.1") == TRUE)
+               return -ENODEV;
+
+       data = create_server(interface, domain, server);
+       if (data == NULL)
+               return -EIO;
+
+       server_list = g_slist_append(server_list, data);
+
+       return 0;
+}
+
+static int dnsproxy_remove(const char *interface, const char *domain,
+                                                       const char *server)
+{
+       struct server_data *data;
+
+       DBG("interface %s server %s", interface, server);
+
+       if (g_str_equal(server, "127.0.0.1") == TRUE)
+               return -ENODEV;
+
+       data = find_server(interface, domain, server);
+       if (data == NULL)
+               return 0;
+
+       server_list = g_slist_remove(server_list, data);
+
+       destroy_server(data);
+
+       return 0;
+}
+
+static struct connman_resolver dnsproxy_resolver = {
+       .name           = "dnsproxy",
+       .priority       = CONNMAN_RESOLVER_PRIORITY_HIGH,
+       .append         = dnsproxy_append,
+       .remove         = dnsproxy_remove,
+};
+
+static int parse_request(unsigned char *buf, int len,
+                                       char *name, unsigned int size)
+{
+       struct domain_hdr *hdr = (void *) buf;
+       uint16_t qdcount = ntohs(hdr->qdcount);
+       unsigned char *ptr;
+       char *last_label = NULL;
+       int label_count = 0;
+       unsigned int remain, used = 0;
+
+       if (len < 12)
+               return -EINVAL;
+
+       DBG("id 0x%04x qr %d opcode %d qdcount %d",
+                               hdr->id, hdr->qr, hdr->opcode, qdcount);
+
+       if (hdr->qr != 0 || qdcount != 1)
+               return -EINVAL;
+
+       memset(name, 0, size);
+
+       ptr = buf + 12;
+       remain = len - 12;
+
+       while (remain > 0) {
+               uint8_t len = *ptr;
+
+               if (len == 0x00) {
+                       if (label_count > 0)
+                               last_label = (char *) (ptr + 1);
+                       break;
+               }
+
+               label_count++;
+
+               if (used + len + 1 > size)
+                       return -ENOBUFS;
+
+               strncat(name, (char *) (ptr + 1), len);
+               strcat(name, ".");
+
+               used += len + 1;
+
+               ptr += len + 1;
+               remain -= len + 1;
+       }
+
+       DBG("query %s (%d labels)", name, label_count);
+
+       return 0;
+}
+
+static void send_response(int sk, unsigned char *buf, int len,
+                               const struct sockaddr *to, socklen_t tolen)
+{
+       struct domain_hdr *hdr = (void *) buf;
+       int err;
+
+       if (len < 12)
+               return;
+
+       DBG("id 0x%04x qr %d opcode %d", hdr->id, hdr->qr, hdr->opcode);
+
+       hdr->qr = 1;
+       hdr->rcode = 2;
+
+       hdr->ancount = 0;
+       hdr->nscount = 0;
+       hdr->arcount = 0;
+
+       err = sendto(sk, buf, len, 0, to, tolen);
+}
+
+static int append_query(unsigned char *buf, unsigned int size,
+                               const char *query, const char *domain)
+{
+       unsigned char *ptr = buf;
+       char *offset;
+
+       DBG("query %s domain %s", query, domain);
+
+       offset = (char *) query;
+       while (offset != NULL) {
+               char *tmp;
+
+               tmp = strchr(offset, '.');
+               if (tmp == NULL) {
+                       if (strlen(offset) == 0)
+                               break;
+                       *ptr = strlen(offset);
+                       memcpy(ptr + 1, offset, strlen(offset));
+                       ptr += strlen(offset) + 1;
+                       break;
+               }
+
+               *ptr = tmp - offset;
+               memcpy(ptr + 1, offset, tmp - offset);
+               ptr += tmp - offset + 1;
+
+               offset = tmp + 1;
+       }
+
+       offset = (char *) domain;
+       while (offset != NULL) {
+               char *tmp;
+
+               tmp = strchr(offset, '.');
+               if (tmp == NULL) {
+                       if (strlen(offset) == 0)
+                               break;
+                       *ptr = strlen(offset);
+                       memcpy(ptr + 1, offset, strlen(offset));
+                       ptr += strlen(offset) + 1;
+                       break;
+               }
+
+               *ptr = tmp - offset;
+               memcpy(ptr + 1, offset, tmp - offset);
+               ptr += tmp - offset + 1;
+
+               offset = tmp + 1;
+       }
+
+       *ptr++ = 0x00;
+
+       return ptr - buf;
+}
+
+static gboolean request_timeout(gpointer user_data)
+{
+       struct request_data *req = user_data;
+
+       DBG("id 0x%04x", req->srcid);
+
+       request_list = g_slist_remove(request_list, req);
+
+       if (req->resplen > 0 && req->resp != NULL) {
+               int sk, err;
+
+               sk = g_io_channel_unix_get_fd(listener_channel);
+
+               err = sendto(sk, req->resp, req->resplen, 0,
+                               (struct sockaddr *) &req->sin, req->len);
+       }
+
+       g_free(req->resp);
+       g_free(req);
+
+       return FALSE;
+}
+
+static gboolean listener_event(GIOChannel *channel, GIOCondition condition,
+                                                       gpointer user_data)
+{
+       GSList *list;
+       unsigned char buf[768];
+       char query[512];
+       struct request_data *req;
+       struct sockaddr_in sin;
+       socklen_t size = sizeof(sin);
+       int sk, err, len;
+
+       if (condition & (G_IO_NVAL | G_IO_ERR | G_IO_HUP)) {
+               connman_error("Error with listener channel");
+               listener_watch = 0;
+               return FALSE;
+       }
+
+       sk = g_io_channel_unix_get_fd(channel);
+
+       memset(&sin, 0, sizeof(sin));
+       len = recvfrom(sk, buf, sizeof(buf), 0,
+                                       (struct sockaddr *) &sin, &size);
+       if (len < 2)
+               return TRUE;
+
+       DBG("Received %d bytes (id 0x%04x)", len, buf[0] | buf[1] << 8);
+
+       err = parse_request(buf, len, query, sizeof(query));
+       if (err < 0 || g_slist_length(server_list) == 0) {
+               send_response(sk, buf, len, (struct sockaddr *) &sin, size);
+               return TRUE;
+       }
+
+       req = g_try_new0(struct request_data, 1);
+       if (req == NULL)
+               return TRUE;
+
+       memcpy(&req->sin, &sin, sizeof(sin));
+       req->len = size;
+
+       request_id += 2;
+       if (request_id == 0x0000 || request_id == 0xffff)
+               request_id += 2;
+
+       req->srcid = buf[0] | (buf[1] << 8);
+       req->dstid = request_id;
+       req->altid = request_id + 1;
+
+       buf[0] = req->dstid & 0xff;
+       buf[1] = req->dstid >> 8;
+
+       request_list = g_slist_append(request_list, req);
+
+       req->numserv = 0;
+       req->timeout = g_timeout_add_seconds(5, request_timeout, req);
+
+       for (list = server_list; list; list = list->next) {
+               struct server_data *data = list->data;
+
+               DBG("server %s domain %s", data->server, data->domain);
+
+               sk = g_io_channel_unix_get_fd(data->channel);
+
+               err = send(sk, buf, len, 0);
+
+               req->numserv++;
+
+               if (data->domain != NULL) {
+                       unsigned char alt[1024];
+                       struct domain_hdr *hdr = (void *) &alt;
+                       int altlen;
+
+                       alt[0] = req->altid & 0xff;
+                       alt[1] = req->altid >> 8;
+
+                       memcpy(alt + 2, buf + 2, 10);
+                       hdr->qdcount = htons(1);
+
+                       altlen = append_query(alt + 12, sizeof(alt) - 12,
+                                                       query, data->domain);
+                       if (altlen < 0)
+                               continue;
+
+                       alt[altlen + 12] = 0x00;
+                       alt[altlen + 13] = 0x01;
+                       alt[altlen + 14] = 0x00;
+                       alt[altlen + 15] = 0x01;
+
+                       err = send(sk, alt, altlen + 12 + 4, 0);
+
+                       req->numserv++;
+               }
+       }
+
+       return TRUE;
+}
+
+static int create_listener(void)
+{
+       const char *ifname = "lo";
+       struct sockaddr_in sin;
+       int sk;
+
+       DBG("");
+
+       sk = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
+       if (sk < 0) {
+               connman_error("Failed to create listener socket");
+               return -EIO;
+       }
+
+       //setsockopt(sk, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt));
+       //setsockopt(sk, SOL_IP, IP_PKTINFO, &opt, sizeof(opt));
+
+       if (setsockopt(sk, SOL_SOCKET, SO_BINDTODEVICE,
+                                       ifname, strlen(ifname) + 1) < 0) {
+               connman_error("Failed to bind listener interface");
+               close(sk);
+               return -EIO;
+       }
+
+       memset(&sin, 0, sizeof(sin));
+       sin.sin_family = AF_INET;
+       sin.sin_port = htons(53);
+       sin.sin_addr.s_addr = inet_addr("127.0.0.1");
+       //sin.sin_addr.s_addr = INADDR_ANY;
+
+       if (bind(sk, (struct sockaddr *) &sin, sizeof(sin)) < 0) {
+               connman_error("Failed to bind listener socket");
+               close(sk);
+               return -EIO;
+       }
+
+       listener_channel = g_io_channel_unix_new(sk);
+       if (listener_channel == NULL) {
+               connman_error("Failed to create listener channel");
+               close(sk);
+               return -EIO;
+       }
+
+       g_io_channel_set_close_on_unref(listener_channel, TRUE);
+
+       listener_watch = g_io_add_watch(listener_channel, G_IO_IN,
+                                                       listener_event, NULL);
+
+       connman_resolver_append("lo", NULL, "127.0.0.1");
+
+       return 0;
+}
+
+static void destroy_listener(void)
+{
+       GSList *list;
+
+       DBG("");
+
+       connman_resolver_remove_all("lo");
+
+       if (listener_watch > 0)
+               g_source_remove(listener_watch);
+
+       for (list = request_list; list; list = list->next) {
+               struct request_data *req = list->data;
+
+               DBG("Dropping request (id 0x%04x -> 0x%04x)",
+                                               req->srcid, req->dstid);
+
+               g_free(req->resp);
+               g_free(req);
+               list->data = NULL;
+       }
+
+       g_slist_free(request_list);
+       request_list = NULL;
+
+       g_io_channel_unref(listener_channel);
+}
+
+static int dnsproxy_init(void)
+{
+       int err;
+
+       err = create_listener();
+       if (err < 0)
+               return err;
+
+       err = connman_resolver_register(&dnsproxy_resolver);
+       if (err < 0)
+               destroy_listener();
+
+       return err;
+}
+
+static void dnsproxy_exit(void)
+{
+       destroy_listener();
+
+       connman_resolver_unregister(&dnsproxy_resolver);
+}
+
+CONNMAN_PLUGIN_DEFINE(dnsproxy, "DNS proxy resolver plugin", VERSION,
+                CONNMAN_PLUGIN_PRIORITY_DEFAULT, dnsproxy_init, dnsproxy_exit)
diff --git a/plugins/ethernet.c b/plugins/ethernet.c
new file mode 100644 (file)
index 0000000..3013271
--- /dev/null
@@ -0,0 +1,152 @@
+/*
+ *
+ *  Connection Manager
+ *
+ *  Copyright (C) 2007-2009  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 <errno.h>
+#include <net/if.h>
+
+#ifndef IFF_LOWER_UP
+#define IFF_LOWER_UP   0x10000
+#endif
+
+#include <glib.h>
+
+#define CONNMAN_API_SUBJECT_TO_CHANGE
+#include <connman/plugin.h>
+#include <connman/device.h>
+#include <connman/inet.h>
+#include <connman/rtnl.h>
+#include <connman/log.h>
+
+struct ethernet_data {
+       int index;
+       unsigned flags;
+       unsigned int watch;
+};
+
+static void ethernet_newlink(unsigned flags, unsigned change, void *user_data)
+{
+       struct connman_device *device = user_data;
+       struct ethernet_data *ethernet = connman_device_get_data(device);
+
+       DBG("index %d flags %d change %d", ethernet->index, flags, change);
+
+       if ((ethernet->flags & IFF_UP) != (flags & IFF_UP)) {
+               if (flags & IFF_UP) {
+                       DBG("power on");
+                       connman_device_set_powered(device, TRUE);
+               } else {
+                       DBG("power off");
+                       connman_device_set_powered(device, FALSE);
+               }
+       }
+
+       if ((ethernet->flags & IFF_LOWER_UP) != (flags & IFF_LOWER_UP)) {
+               if (flags & IFF_LOWER_UP) {
+                       DBG("carrier on");
+                       connman_device_set_carrier(device, TRUE);
+               } else {
+                       DBG("carrier off");
+                       connman_device_set_carrier(device, FALSE);
+               }
+       }
+
+       ethernet->flags = flags;
+}
+
+static int ethernet_probe(struct connman_device *device)
+{
+       struct ethernet_data *ethernet;
+
+       DBG("device %p", device);
+
+       ethernet = g_try_new0(struct ethernet_data, 1);
+       if (ethernet == NULL)
+               return -ENOMEM;
+
+       connman_device_set_data(device, ethernet);
+
+       ethernet->index = connman_device_get_index(device);
+       ethernet->flags = 0;
+
+       ethernet->watch = connman_rtnl_add_newlink_watch(ethernet->index,
+                                               ethernet_newlink, device);
+
+       connman_rtnl_send_getlink();
+
+       return 0;
+}
+
+static void ethernet_remove(struct connman_device *device)
+{
+       struct ethernet_data *ethernet = connman_device_get_data(device);
+
+       DBG("device %p", device);
+
+       connman_device_set_data(device, NULL);
+
+       connman_rtnl_remove_watch(ethernet->watch);
+
+       g_free(ethernet);
+}
+
+static int ethernet_enable(struct connman_device *device)
+{
+       struct ethernet_data *ethernet = connman_device_get_data(device);
+
+       DBG("device %p", device);
+
+       return connman_inet_ifup(ethernet->index);
+}
+
+static int ethernet_disable(struct connman_device *device)
+{
+       struct ethernet_data *ethernet = connman_device_get_data(device);
+
+       DBG("device %p", device);
+
+       return connman_inet_ifdown(ethernet->index);
+}
+
+static struct connman_device_driver ethernet_driver = {
+       .name           = "ethernet",
+       .type           = CONNMAN_DEVICE_TYPE_ETHERNET,
+       .probe          = ethernet_probe,
+       .remove         = ethernet_remove,
+       .enable         = ethernet_enable,
+       .disable        = ethernet_disable,
+};
+
+static int ethernet_init(void)
+{
+       return connman_device_driver_register(&ethernet_driver);
+}
+
+static void ethernet_exit(void)
+{
+       connman_device_driver_unregister(&ethernet_driver);
+}
+
+CONNMAN_PLUGIN_DEFINE(ethernet, "Ethernet interface plugin", VERSION,
+               CONNMAN_PLUGIN_PRIORITY_DEFAULT, ethernet_init, ethernet_exit)
diff --git a/plugins/fake.c b/plugins/fake.c
new file mode 100644 (file)
index 0000000..8a271be
--- /dev/null
@@ -0,0 +1,110 @@
+/*
+ *
+ *  Connection Manager
+ *
+ *  Copyright (C) 2007-2009  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>
+
+#define CONNMAN_API_SUBJECT_TO_CHANGE
+#include <connman/plugin.h>
+#include <connman/device.h>
+#include <connman/log.h>
+
+static void create_network(struct connman_device *device, const char *name)
+{
+       struct connman_network *network;
+
+       network = connman_network_create(name, CONNMAN_NETWORK_TYPE_VENDOR);
+       if (network == NULL)
+               return;
+
+       connman_device_add_network(device, network);
+       connman_network_unref(network);
+}
+
+static int device_probe(struct connman_device *device)
+{
+       DBG("");
+
+       return 0;
+}
+
+static void device_remove(struct connman_device *device)
+{
+       DBG("");
+}
+
+static int device_enable(struct connman_device *device)
+{
+       DBG("");
+
+       create_network(device, "network_one");
+       create_network(device, "network_two");
+
+       return 0;
+}
+
+static int device_disable(struct connman_device *device)
+{
+       DBG("");
+
+       return 0;
+}
+
+static struct connman_device_driver device_driver = {
+       .name           = "fake",
+       .type           = CONNMAN_DEVICE_TYPE_VENDOR,
+       .probe          = device_probe,
+       .remove         = device_remove,
+       .enable         = device_enable,
+       .disable        = device_disable,
+};
+
+static void create_device(const char *name)
+{
+       struct connman_device *device;
+
+       device = connman_device_create(name, CONNMAN_DEVICE_TYPE_VENDOR);
+       if (device == NULL)
+               return;
+
+       connman_device_set_mode(device, CONNMAN_DEVICE_MODE_NETWORK_SINGLE);
+
+       connman_device_register(device);
+       connman_device_unref(device);
+}
+
+static int fake_init(void)
+{
+       create_device("fake");
+
+       return connman_device_driver_register(&device_driver);
+}
+
+static void fake_exit(void)
+{
+       connman_device_driver_unregister(&device_driver);
+}
+
+CONNMAN_PLUGIN_DEFINE(fake, "Tesing plugin", VERSION,
+               CONNMAN_PLUGIN_PRIORITY_DEFAULT, fake_init, fake_exit)
diff --git a/plugins/hso.c b/plugins/hso.c
new file mode 100644 (file)
index 0000000..21df00d
--- /dev/null
@@ -0,0 +1,246 @@
+/*
+ *
+ *  Connection Manager
+ *
+ *  Copyright (C) 2007-2009  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 <errno.h>
+#include <string.h>
+
+#include <glib.h>
+
+#define CONNMAN_API_SUBJECT_TO_CHANGE
+#include <connman/plugin.h>
+#include <connman/device.h>
+#include <connman/resolver.h>
+#include <connman/log.h>
+
+#include "modem.h"
+
+struct hso_data {
+       int index;
+       struct modem_data *modem;
+};
+
+static void owandata_callback(const char *buf, void *user_data)
+{
+       //struct hso_data *data = user_data;
+       char *str, ip[16], nm[16], ns1[16], ns2[16], ns3[16], ns4[16], val[20];
+       int err, num;
+
+       str = g_strrstr(buf, "_OWANDATA");
+       if (str == NULL || strstr(buf, "ERROR") != NULL)
+               return;
+
+       err = sscanf(str, "_OWANDATA: %d, %[^,], %[^,], "
+                                       "%[^,], %[^,], %[^,], %[^,], %s",
+                               &num, ip, nm, ns1, ns2, ns3, ns4, val);
+
+       if (err != 8) {
+               DBG("parsed %d arguments", err);
+               return;
+       }
+
+       DBG("ip %s dns %s %s val %s", ip, ns1, ns2, val);
+
+       //connman_resolver_append(data->iface, NULL, ns1);
+       //connman_resolver_append(data->iface, NULL, ns2);
+}
+
+static void owancall_callback(const char *buf, void *user_data)
+{
+       struct hso_data *data = user_data;
+
+       DBG("");
+
+       if (g_strrstr(buf, "_OWANCALL: 1, 3") != NULL) {
+               DBG("%s", buf);
+               //modem_command(modem, owancall_callback, data,
+               //                      "_OWANCALL", "%d,%d,%d", 1, 1, 1);
+       }
+
+       if (g_strrstr(buf, "_OWANCALL: 1, 1") != NULL) {
+               DBG("%s", buf);
+               modem_command(data->modem, owandata_callback, data,
+                                               "_OWANDATA", "%d", 1);
+       }
+
+       if (g_strrstr(buf, "\r\nOK\r\n") != NULL) {
+               modem_command(data->modem, owandata_callback, data,
+                                               "_OWANDATA", "%d", 1);
+       }
+}
+
+static int network_probe(struct connman_network *network)
+{
+       DBG("network %p", network);
+
+       return 0;
+}
+
+static void network_remove(struct connman_network *network)
+{
+       DBG("network %p", network);
+}
+
+static int network_connect(struct connman_network *network)
+{
+       DBG("network %p", network);
+
+       return 0;
+}
+
+static int network_disconnect(struct connman_network *network)
+{
+       DBG("network %p", network);
+
+       return 0;
+}
+
+static struct connman_network_driver network_driver = {
+       .name           = "hso-network",
+       .type           = CONNMAN_NETWORK_TYPE_HSO,
+       .probe          = network_probe,
+       .remove         = network_remove,
+       .connect        = network_connect,
+       .disconnect     = network_disconnect,
+};
+
+static int hso_probe(struct connman_device *device)
+{
+       struct hso_data *data;
+
+       DBG("device %p", device);
+
+       data = g_try_new0(struct hso_data, 1);
+       if (data == NULL)
+               return -ENOMEM;
+
+       data->index = connman_device_get_index(device);
+
+       data->modem = modem_create("/dev/ttyHS0");
+       if (data->modem == NULL) {
+               g_free(data);
+               return -EIO;
+       }
+
+       connman_device_set_data(device, data);
+
+       modem_add_callback(data->modem, "_OWANCALL",
+                                               owancall_callback, data);
+
+       return 0;
+}
+
+static void hso_remove(struct connman_device *device)
+{
+       struct hso_data *data = connman_device_get_data(device);
+
+       DBG("device %p", device);
+
+       connman_device_set_data(device, NULL);
+
+       modem_destroy(data->modem);
+
+       g_free(data);
+}
+
+static int hso_enable(struct connman_device *device)
+{
+       struct hso_data *data = connman_device_get_data(device);
+       struct connman_network *network;
+       int err;
+
+       DBG("device %p", device);
+
+       err = modem_open(data->modem);
+       if (err < 0)
+               return err;
+
+       connman_device_set_powered(device, TRUE);
+
+       modem_command(data->modem, NULL, NULL, "Z", NULL);
+       modem_command(data->modem, NULL, NULL, "I", NULL);
+
+       modem_command(data->modem, owancall_callback, data,
+                                       "_OWANCALL", "%d,%d,%d", 1, 1, 1);
+
+       network = connman_network_create("internet", CONNMAN_NETWORK_TYPE_HSO);
+       connman_device_add_network(device, network);
+
+       return 0;
+}
+
+static int hso_disable(struct connman_device *device)
+{
+       struct hso_data *data = connman_device_get_data(device);
+       //const char *iface = connman_device_get_interface(device);
+
+       DBG("device %p", device);
+
+       //connman_resolver_remove_all(iface);
+
+       modem_command(data->modem, owancall_callback, data,
+                                       "_OWANCALL", "%d,%d,%d", 1, 0, 0);
+
+       connman_device_set_powered(device, FALSE);
+
+       modem_close(data->modem);
+
+       return 0;
+}
+
+static struct connman_device_driver hso_driver = {
+       .name           = "hso",
+       .type           = CONNMAN_DEVICE_TYPE_HSO,
+       .probe          = hso_probe,
+       .remove         = hso_remove,
+       .enable         = hso_enable,
+       .disable        = hso_disable,
+};
+
+static int hso_init(void)
+{
+       int err;
+
+       err = connman_network_driver_register(&network_driver);
+       if (err < 0)
+               return err;
+
+       err = connman_device_driver_register(&hso_driver);
+       if (err < 0) {
+               connman_network_driver_unregister(&network_driver);
+               return err;
+       }
+
+       return 0;
+}
+
+static void hso_exit(void)
+{
+       connman_device_driver_unregister(&hso_driver);
+       connman_network_driver_register(&network_driver);
+}
+
+CONNMAN_PLUGIN_DEFINE(hso, "Option HSO device plugin", VERSION,
+               CONNMAN_PLUGIN_PRIORITY_DEFAULT, hso_init, hso_exit)
diff --git a/plugins/huawei.c b/plugins/huawei.c
new file mode 100644 (file)
index 0000000..c1a2c0d
--- /dev/null
@@ -0,0 +1,83 @@
+/*
+ *
+ *  Connection Manager
+ *
+ *  Copyright (C) 2007-2009  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 CONNMAN_API_SUBJECT_TO_CHANGE
+#include <connman/plugin.h>
+#include <connman/device.h>
+#include <connman/log.h>
+
+#include "modem.h"
+
+static int huawei_probe(struct connman_device *device)
+{
+       DBG("device %p", device);
+
+       return 0;
+}
+
+static void huawei_remove(struct connman_device *device)
+{
+       DBG("device %p", device);
+}
+
+static int huawei_enable(struct connman_device *device)
+{
+       DBG("device %p", device);
+
+       connman_device_set_powered(device, TRUE);
+
+       return 0;
+}
+
+static int huawei_disable(struct connman_device *device)
+{
+       DBG("device %p", device);
+
+       connman_device_set_powered(device, FALSE);
+
+       return 0;
+}
+
+static struct connman_device_driver huawei_driver = {
+       .name           = "huawei",
+       .type           = CONNMAN_DEVICE_TYPE_HUAWEI,
+       .probe          = huawei_probe,
+       .remove         = huawei_remove,
+       .enable         = huawei_enable,
+       .disable        = huawei_disable,
+};
+
+static int huawei_init(void)
+{
+       return connman_device_driver_register(&huawei_driver);
+}
+
+static void huawei_exit(void)
+{
+       connman_device_driver_unregister(&huawei_driver);
+}
+
+CONNMAN_PLUGIN_DEFINE(huawei, "HUAWEI Mobile device plugin", VERSION,
+               CONNMAN_PLUGIN_PRIORITY_DEFAULT, huawei_init, huawei_exit)
diff --git a/plugins/iospm.c b/plugins/iospm.c
new file mode 100644 (file)
index 0000000..af6250b
--- /dev/null
@@ -0,0 +1,112 @@
+/*
+ *
+ *  Connection Manager
+ *
+ *  Copyright (C) 2007-2009  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 CONNMAN_API_SUBJECT_TO_CHANGE
+#include <connman/plugin.h>
+#include <connman/notifier.h>
+#include <connman/dbus.h>
+#include <connman/log.h>
+
+#define IOSPM_SERVICE          "com.intel.mid.ospm"
+#define IOSPM_INTERFACE                IOSPM_SERVICE ".Comms"
+
+#define IOSPM_BLUETOOTH                "/com/intel/mid/ospm/bluetooth"
+#define IOSPM_FLIGHT_MODE      "/com/intel/mid/ospm/flight_mode"
+
+static DBusConnection *connection;
+
+static void send_indication(const char *path, connman_bool_t enabled)
+{
+       DBusMessage *message;
+       const char *method;
+
+       DBG("path %s enabled %d", path, enabled);
+
+       if (enabled == TRUE)
+               method = "IndicateStart";
+       else
+               method = "IndicateStop";
+
+       message = dbus_message_new_method_call(IOSPM_SERVICE, path,
+                                               IOSPM_INTERFACE, method);
+       if (message == NULL)
+               return;
+
+       dbus_message_set_no_reply(message, TRUE);
+
+       dbus_connection_send(connection, message, NULL);
+
+       dbus_message_unref(message);
+}
+
+static void iospm_device_enabled(enum connman_device_type type,
+                                               connman_bool_t enabled)
+{
+       switch (type) {
+       case CONNMAN_DEVICE_TYPE_UNKNOWN:
+       case CONNMAN_DEVICE_TYPE_VENDOR:
+       case CONNMAN_DEVICE_TYPE_ETHERNET:
+       case CONNMAN_DEVICE_TYPE_WIFI:
+       case CONNMAN_DEVICE_TYPE_WIMAX:
+       case CONNMAN_DEVICE_TYPE_GPS:
+       case CONNMAN_DEVICE_TYPE_HSO:
+       case CONNMAN_DEVICE_TYPE_NOZOMI:
+       case CONNMAN_DEVICE_TYPE_HUAWEI:
+       case CONNMAN_DEVICE_TYPE_NOVATEL:
+               break;
+       case CONNMAN_DEVICE_TYPE_BLUETOOTH:
+               send_indication(IOSPM_BLUETOOTH, enabled);
+               break;
+       }
+}
+
+static void iospm_offline_mode(connman_bool_t enabled)
+{
+       send_indication(IOSPM_FLIGHT_MODE, enabled);
+}
+
+static struct connman_notifier iospm_notifier = {
+       .name           = "iospm",
+       .priority       = CONNMAN_NOTIFIER_PRIORITY_DEFAULT,
+       .device_enabled = iospm_device_enabled,
+       .offline_mode   = iospm_offline_mode,
+};
+
+static int iospm_init(void)
+{
+       connection = connman_dbus_get_connection();
+
+       return connman_notifier_register(&iospm_notifier);
+}
+
+static void iospm_exit(void)
+{
+       connman_notifier_unregister(&iospm_notifier);
+
+       dbus_connection_unref(connection);
+}
+
+CONNMAN_PLUGIN_DEFINE(ospm, "Intel OSPM notification plugin", VERSION,
+               CONNMAN_PLUGIN_PRIORITY_DEFAULT, iospm_init, iospm_exit)
diff --git a/plugins/iwmx.c b/plugins/iwmx.c
new file mode 100644 (file)
index 0000000..ccbd1d2
--- /dev/null
@@ -0,0 +1,587 @@
+/*
+ *
+ *  Connection Manager
+ *
+ *  Copyright (C) 2007-2009  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 <errno.h>
+#include <string.h>
+
+#include <glib.h>
+
+#define CONNMAN_API_SUBJECT_TO_CHANGE
+#include <connman/plugin.h>
+#include <connman/device.h>
+#include <connman/inet.h>
+#include <connman/log.h>
+
+#include <WiMaxAPI.h>
+#include <WiMaxAPIEx.h>
+
+#include "iwmx.h"
+
+/*
+ * Connman plugin interface
+ *
+ * This part deals with the connman internals
+ */
+
+/* WiMAX network driver probe/remove, nops */
+static int iwmx_cm_network_probe(struct connman_network *nw)
+{
+       return 0;
+}
+
+static void iwmx_cm_network_remove(struct connman_network *nw)
+{
+}
+
+/*
+ * Called by connman when it wants us to tell the device to connect to
+ * the network @network_el; the device is @network_el->parent.
+ *
+ * We do a synchronous call to start the connection; the logic
+ * attached to the status change callback will update the connman
+ * internals once the change happens.
+ */
+static int iwmx_cm_network_connect(struct connman_network *nw)
+{
+       int result;
+       struct wmxsdk *wmxsdk;
+       const char *station_name = connman_network_get_identifier(nw);
+
+       wmxsdk = connman_device_get_data(connman_network_get_device(nw));
+       result = iwmx_sdk_connect(wmxsdk, nw);
+       DBG("(nw %p [%s] wmxsdk %p) = %d\n", nw, station_name, wmxsdk, result);
+       return result;
+}
+
+/*
+ * Called by connman to have the device @nw->parent
+ * disconnected from @nw.
+ *
+ * We do a synchronous call to start the disconnection; the logic
+ * attached to the status change callback will update the connman
+ * internals once the change happens.
+ */
+static int iwmx_cm_network_disconnect(struct connman_network *nw)
+{
+       int result;
+       struct wmxsdk *wmxsdk;
+       const char *station_name = connman_network_get_identifier(nw);
+
+       wmxsdk = connman_device_get_data(connman_network_get_device(nw));
+       result = iwmx_sdk_disconnect(wmxsdk);
+       DBG("(nw %p [%s] wmxsdk %p) = %d\n", nw, station_name, wmxsdk, result);
+       return 0;
+}
+
+/*
+ * "Driver" for the networks detected by a device.
+ */
+static struct connman_network_driver iwmx_cm_network_driver = {
+       .name           = "iwmx",
+       .type           = CONNMAN_NETWORK_TYPE_WIMAX,
+       .probe          = iwmx_cm_network_probe,
+       .remove         = iwmx_cm_network_remove,
+       .connect        = iwmx_cm_network_connect,
+       .disconnect     = iwmx_cm_network_disconnect,
+};
+
+/*
+ * A (maybe) new network is available, create/update its data
+ *
+ * If the network is new, we create and register a new element; if it
+ * is not, we reuse the one in the list.
+ *
+ * NOTE:
+ *   wmxsdk->network_mutex has to be locked
+ */
+struct connman_network *__iwmx_cm_network_available(
+                       struct wmxsdk *wmxsdk, const char *station_name,
+                       const char *station_type,
+                       const void *sdk_nspname, size_t sdk_nspname_size,
+                                                               int strength)
+{
+       struct connman_network *nw = NULL;
+       struct connman_device *dev = wmxsdk->dev;
+       char group[3 * strlen(station_name) + 1];
+       unsigned cnt;
+
+       nw = connman_device_get_network(dev, station_name);
+       if (nw == NULL) {
+               DBG("new network %s", station_name);
+               nw = connman_network_create(station_name,
+                                           CONNMAN_NETWORK_TYPE_WIMAX);
+               connman_network_set_index(nw, connman_device_get_index(dev));
+               connman_network_set_protocol(nw, CONNMAN_NETWORK_PROTOCOL_IP);
+               connman_network_set_name(nw, station_name);
+               connman_network_set_blob(nw, "WiMAX.NSP.name",
+                                        sdk_nspname, sdk_nspname_size);
+               /* FIXME: add roaming info? */
+               /* Set the group name -- this has to be a unique
+                * [a-zA-Z0-9_] string common to all the networks that
+                * are actually the same provider. In WiMAX each
+                * network from the CAPI is a single provider, so we
+                * just set this as the network name, encoded in
+                * hex. */
+               for (cnt = 0; station_name[cnt] != 0; cnt++)
+                       sprintf(group + 3 * cnt, "%02x", station_name[cnt]);
+               group[3 * cnt + 1] = 0;
+               connman_network_set_group(nw, station_name);
+               if (connman_device_add_network(dev, nw) < 0) {
+                       connman_network_unref(nw);
+                       goto error_add;
+               }
+       } else
+               DBG("updating network %s nw %p\n", station_name, nw);
+       connman_network_set_available(nw, TRUE);
+       connman_network_set_strength(nw, strength);
+       connman_network_set_string(nw, "WiMAX Network Type", station_type);
+error_add:
+       return nw;
+}
+
+/*
+ * A new network is available [locking version]
+ *
+ * See __iwmx_cm_network_available() for docs
+ */
+struct connman_network *iwmx_cm_network_available(
+                       struct wmxsdk *wmxsdk, const char *station_name,
+                       const char *station_type,
+                       const void *sdk_nspname, size_t sdk_nspname_size,
+                                                               int strength)
+{
+       struct connman_network *nw;
+
+       g_static_mutex_lock(&wmxsdk->network_mutex);
+       nw = __iwmx_cm_network_available(wmxsdk, station_name, station_type,
+                                       sdk_nspname, sdk_nspname_size,
+                                       strength);
+       g_static_mutex_unlock(&wmxsdk->network_mutex);
+       return nw;
+}
+
+/*
+ * The device has been enabled, make sure connman knows
+ */
+static void iwmx_cm_dev_enabled(struct wmxsdk *wmxsdk)
+{
+       struct connman_device *dev = wmxsdk->dev;
+       connman_inet_ifup(connman_device_get_index(dev));
+       connman_device_set_powered(dev, TRUE);
+}
+
+/*
+ * The device has been disabled, make sure connman is aware of it.
+ */
+static void iwmx_cm_dev_disabled(struct wmxsdk *wmxsdk)
+{
+       struct connman_device *dev = wmxsdk->dev;
+       connman_inet_ifdown(connman_device_get_index(dev));
+       connman_device_set_powered(dev, FALSE);
+}
+
+/*
+ * The device has been (externally to connman) connnected to a
+ * network, make sure connman knows.
+ *
+ * When the device is connected to a network, this function is called
+ * to change connman's internal state to reflect the fact.
+ *
+ * If the change came from an external entity, that means that our
+ * connect code wasn't called. Our connect code sets
+ * @wmxsdk->connecting_nw to the network we were connecting
+ * to. If it is unset, it means an external entity forced the device
+ * to connect. In that case, we need to find out which network it was
+ * connected to, and create/lookup a @nw for it.
+ *
+ * Once the nw is set, then we are done.
+ */
+static void iwmx_cm_dev_connected(struct wmxsdk *wmxsdk)
+{
+       struct connman_network *nw;
+
+       g_mutex_lock(wmxsdk->connect_mutex);
+       nw = wmxsdk->connecting_nw;
+       if (nw == NULL) {
+               nw = __iwmx_sdk_get_connected_network(wmxsdk);
+               if (nw == NULL) {
+                       connman_error("wmxsdk: can't find connected network\n");
+                       goto error_nw_find;
+               }
+       }
+       wmxsdk->nw = connman_network_ref(nw);
+       wmxsdk->connecting_nw = NULL;
+       connman_network_set_connected(nw, TRUE);
+       DBG("connected to network %s\n",
+           connman_network_get_identifier(nw));
+error_nw_find:
+       g_mutex_unlock(wmxsdk->connect_mutex);
+}
+
+/*
+ * The device has been (externally to connman) disconnnected, make
+ * sure connman knows
+ *
+ * We need to reverse the steps done in iwmx_cm_dev_connected().
+ * If the event was caused by an external entity and we had no record
+ * of being connected to a network...well, bad luck. We'll just
+ * pretend it happened ok.
+ */
+static void __iwmx_cm_dev_disconnected(struct wmxsdk *wmxsdk)
+{
+       struct connman_network *nw = wmxsdk->nw;
+
+       if (nw != NULL) {
+               DBG("disconnected from network %s\n",
+                                       connman_network_get_identifier(nw));
+               connman_network_set_connected(nw, FALSE);
+               connman_network_unref(nw);
+               wmxsdk->nw = NULL;
+       } else
+               DBG("disconnected from unknown network\n");
+}
+
+/*
+ * The device has been disconnnected, make sure connman knows
+ *
+ * See __iwmx_cm_dev_disconnect() for more information.
+ */
+static void iwmx_cm_dev_disconnected(struct wmxsdk *wmxsdk)
+{
+       g_mutex_lock(wmxsdk->connect_mutex);
+       __iwmx_cm_dev_disconnected(wmxsdk);
+       g_mutex_unlock(wmxsdk->connect_mutex);
+}
+
+/*
+ * Handle a change in state
+ *
+ * This is were most of the action happens. When the device changes
+ * state, this will catch it (through the state change callback or an
+ * explicit call) and call iwmx_cm_dev_*ed() to indicate to connman what
+ * happened.
+ *
+ * Finally, cache the new device status.
+ */
+void __iwmx_cm_state_change(struct wmxsdk *wmxsdk,
+                                       WIMAX_API_DEVICE_STATUS __new_status)
+{
+       WIMAX_API_DEVICE_STATUS __old_status = wmxsdk->status;
+       WIMAX_API_DEVICE_STATUS old_status;
+       WIMAX_API_DEVICE_STATUS new_status;
+
+       /*
+        * Simplify state transition computations.
+        *
+        * For practical effects, some states are the same
+        */
+
+       /* Conection_Idle is the same as Data_Connected */
+       if (__old_status == WIMAX_API_DEVICE_STATUS_Connection_Idle)
+               old_status = WIMAX_API_DEVICE_STATUS_Data_Connected;
+       else
+               old_status = __old_status;
+       if (__new_status == WIMAX_API_DEVICE_STATUS_Connection_Idle)
+               new_status = WIMAX_API_DEVICE_STATUS_Data_Connected;
+       else
+               new_status = __new_status;
+
+       /* Radio off: all are just RF_OFF_SW (the highest) */
+       switch (__old_status) {
+       case WIMAX_API_DEVICE_STATUS_RF_OFF_HW_SW:
+       case WIMAX_API_DEVICE_STATUS_RF_OFF_HW:
+       case WIMAX_API_DEVICE_STATUS_RF_OFF_SW:
+               old_status = WIMAX_API_DEVICE_STATUS_RF_OFF_SW;
+               break;
+       default:
+               old_status = __old_status;
+               break;
+       }
+
+       switch (__new_status) {
+       case WIMAX_API_DEVICE_STATUS_RF_OFF_HW_SW:
+       case WIMAX_API_DEVICE_STATUS_RF_OFF_HW:
+       case WIMAX_API_DEVICE_STATUS_RF_OFF_SW:
+               new_status = WIMAX_API_DEVICE_STATUS_RF_OFF_SW;
+               break;
+       default:
+               new_status = __new_status;
+               break;
+       }
+
+       /* If no real state change, do nothing */
+       if (old_status == new_status) {
+               DBG("no state changed\n");
+               return;
+       } else
+               DBG("state change from %d (%d: %s) to %d (%d: %s)\n",
+                   old_status, __old_status,
+                   iwmx_sdk_dev_status_to_str(__old_status),
+                   new_status, __new_status,
+                   iwmx_sdk_dev_status_to_str(__new_status));
+
+       /* Cleanup old state */
+       switch (old_status) {
+       case WIMAX_API_DEVICE_STATUS_UnInitialized:
+               /* This means the plugin is starting but the device is
+                * in some state already, so we need to update our
+                * internal knowledge of it. */
+               if (new_status > WIMAX_API_DEVICE_STATUS_RF_OFF_SW)
+                       iwmx_cm_dev_enabled(wmxsdk);
+               break;
+       case WIMAX_API_DEVICE_STATUS_RF_OFF_SW:
+               /* This means the radio is being turned on, so enable
+                * the device ( unless going to uninitialized). */
+               if (new_status != WIMAX_API_DEVICE_STATUS_RF_OFF_SW)
+                       iwmx_cm_dev_enabled(wmxsdk);
+               break;
+       case WIMAX_API_DEVICE_STATUS_Ready:
+               break;
+       case WIMAX_API_DEVICE_STATUS_Scanning:
+               break;
+       case WIMAX_API_DEVICE_STATUS_Connecting:
+               break;
+       case WIMAX_API_DEVICE_STATUS_Data_Connected:
+               iwmx_cm_dev_disconnected(wmxsdk);
+               break;
+       default:
+               connman_error("wmxsdk: unknown old status %d\n", old_status);
+               return;
+       };
+
+       /* Implement new state */
+       switch (new_status) {
+       case WIMAX_API_DEVICE_STATUS_UnInitialized:
+               break;
+       case WIMAX_API_DEVICE_STATUS_RF_OFF_SW:
+               /* This means the radio is being turned off, so
+                * disable the device unless coming from uninitialized. */
+               if (old_status != WIMAX_API_DEVICE_STATUS_UnInitialized)
+                       iwmx_cm_dev_disabled(wmxsdk);
+               break;
+       case WIMAX_API_DEVICE_STATUS_Ready:
+               break;
+       case WIMAX_API_DEVICE_STATUS_Scanning:
+               break;
+       case WIMAX_API_DEVICE_STATUS_Connecting:
+               break;
+       case WIMAX_API_DEVICE_STATUS_Data_Connected:
+               iwmx_cm_dev_connected(wmxsdk);
+               break;
+       default:
+               connman_error("wmxsdk: unknown new status %d\n", old_status);
+               return;
+       };
+       wmxsdk->status = __new_status;
+}
+
+/*
+ * Implement a device state transition [locking version]
+ *
+ * See __iwmx_cm_state_change()
+ */
+void iwmx_cm_state_change(struct wmxsdk *wmxsdk,
+                                WIMAX_API_DEVICE_STATUS __new_status)
+{
+       g_mutex_lock(wmxsdk->status_mutex);
+       __iwmx_cm_state_change(wmxsdk, __new_status);
+       g_mutex_unlock(wmxsdk->status_mutex);
+}
+
+/*
+ * Read the cached device status
+ */
+WIMAX_API_DEVICE_STATUS iwmx_cm_status_get(struct wmxsdk *wmxsdk)
+{
+       WIMAX_API_DEVICE_STATUS status;
+
+       g_mutex_lock(wmxsdk->status_mutex);
+       status = wmxsdk->status;
+       g_mutex_unlock(wmxsdk->status_mutex);
+       return status;
+}
+
+/*
+ * Called by connman when a device is enabled by the user
+ *
+ * We need to turn the radio on; the state change function will poke
+ * the internals.
+ */
+static int iwmx_cm_enable(struct connman_device *dev)
+{
+       int result;
+       struct wmxsdk *wmxsdk = connman_device_get_data(dev);
+
+       connman_inet_ifup(connman_device_get_index(dev));
+       result = iwmx_sdk_rf_state_set(wmxsdk, WIMAX_API_RF_ON);
+       return result;
+}
+
+/*
+ * Called by connman when a device is disabled by the user
+ *
+ * Simple: just make sure the radio is off; the state change function
+ * will poke the internals.
+ */
+static int iwmx_cm_disable(struct connman_device *dev)
+{
+       int result;
+       struct wmxsdk *wmxsdk = connman_device_get_data(dev);
+
+       result = iwmx_sdk_rf_state_set(wmxsdk, WIMAX_API_RF_OFF);
+       connman_inet_ifdown(connman_device_get_index(dev));
+       return 0;
+}
+
+/*
+ * Probe deferred call from when the mainloop is idle
+ *
+ * probe() schedules this to be called from the mainloop when idle to
+ * do a device status evaluation. Needed because of an internal race
+ * condition in connman. FIXME: deploy into _probe() when fixed.
+ */
+static gboolean __iwmx_cm_probe_dpc(gpointer _wmxsdk)
+{
+       int result;
+       struct wmxsdk *wmxsdk = _wmxsdk;
+       result = iwmx_sdk_get_device_status(wmxsdk);
+       if (result < 0)
+               connman_error("wmxsdk: can't get status: %d\n", result);
+       else
+               iwmx_cm_state_change(wmxsdk, result);
+       return FALSE;
+}
+
+/*
+ * Called by connman when a new device pops in
+ *
+ * We allocate our private structure, register with the WiMAX API,
+ * open their device, subscribe to all the callbacks.
+ *
+ * At the end, we launch a deferred call (to work around current
+ * connman issues that need to be fixed in the future) and update the
+ * device's status. This allows us to pick up the current status and
+ * adapt connman's idea of the device to it.
+ */
+static int iwmx_cm_probe(struct connman_device *dev)
+{
+       int result;
+       struct wmxsdk *wmxsdk = NULL;
+
+       wmxsdk = connman_device_get_data(dev);
+       if (wmxsdk == NULL)
+               /* not called from a discovery done by the WiMAX
+                * Network Service, ignore */
+               return -ENODEV;
+
+       result = iwmx_sdk_setup(wmxsdk);
+       if (result < 0)
+               goto error_setup;
+
+       /* There is a race condition in the connman core that doesn't
+        * allow us to call this directly and things to work properly
+        * FIXME FIXME FIXME: merge _dpc call in here when connman is fixed */
+       g_idle_add(__iwmx_cm_probe_dpc, wmxsdk);
+       return 0;
+
+       iwmx_sdk_remove(wmxsdk);
+error_setup:
+       return result;
+}
+
+/*
+ * Called when a device is removed from connman
+ *
+ * Cleanup all that is done in _probe. Remove callbacks, unregister
+ * from the WiMAX API.
+ */
+static void iwmx_cm_remove(struct connman_device *dev)
+{
+       struct wmxsdk *wmxsdk = connman_device_get_data(dev);
+       iwmx_sdk_remove(wmxsdk);
+}
+
+/*
+ * Called by connman to ask the device to scan for networks
+ *
+ * We have set in the WiMAX API the scan result callbacks, so we just
+ * start a simple scan (not a wide one).
+ *
+ * First we obtain the current list of networks and pass it to the
+ * callback processor. Then we start an scan cycle.
+ */
+static int iwmx_cm_scan(struct connman_device *dev)
+{
+       struct wmxsdk *wmxsdk = connman_device_get_data(dev);
+       return iwmx_sdk_scan(wmxsdk);
+}
+
+/*
+ * Driver for a WiMAX API based device.
+ */
+static struct connman_device_driver iwmx_cm_device_driver = {
+       .name           = "iwmx",
+       .type           = CONNMAN_DEVICE_TYPE_WIMAX,
+       .probe          = iwmx_cm_probe,
+       .remove         = iwmx_cm_remove,
+       .enable         = iwmx_cm_enable,
+       .disable        = iwmx_cm_disable,
+       .scan           = iwmx_cm_scan,
+};
+
+static int iwmx_cm_init(void)
+{
+       int result;
+
+       result = connman_device_driver_register(&iwmx_cm_device_driver);
+       if (result < 0)
+               goto error_driver_register;
+       result = connman_network_driver_register(&iwmx_cm_network_driver);
+       if (result < 0)
+               goto error_network_driver_register;
+       result = iwmx_sdk_api_init();
+       if (result < 0)
+               goto error_iwmx_sdk_init;
+       return 0;
+
+error_iwmx_sdk_init:
+       connman_network_driver_unregister(&iwmx_cm_network_driver);
+error_network_driver_register:
+       connman_device_driver_unregister(&iwmx_cm_device_driver);
+error_driver_register:
+       return result;
+}
+
+static void iwmx_cm_exit(void)
+{
+       iwmx_sdk_api_exit();
+       connman_network_driver_unregister(&iwmx_cm_network_driver);
+       connman_device_driver_unregister(&iwmx_cm_device_driver);
+}
+
+CONNMAN_PLUGIN_DEFINE(iwmx, "Intel WiMAX SDK / Common API plugin",
+                       CONNMAN_VERSION, CONNMAN_PLUGIN_PRIORITY_LOW,
+                                               iwmx_cm_init, iwmx_cm_exit);
diff --git a/plugins/iwmx.h b/plugins/iwmx.h
new file mode 100644 (file)
index 0000000..265c00c
--- /dev/null
@@ -0,0 +1,171 @@
+/*
+ *
+ *  Connection Manager
+ *
+ *  Copyright (C) 2007-2009  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
+ *
+ */
+
+/*
+ *
+ * The plugin is broken in two main parts: the glue to connman
+ * (iwmx_cm_*() functions) and the glue to the libiWmxSdk (iwmx_sdk_*()
+ * functions). They connect using a well defined interface.
+ *
+ * The plugin is state based and operates reactively to state
+ * transtitions on the WiMAX device or to user requests, even from
+ * external control tools that are not aware of connman.
+ *
+ * When the user requests connman to do something, it goes into a call
+ * implemented by the 'struct connman_driver iwmx_cm_driver' (or
+ * iwmx_cm_network_driver) that will instruct libiWmxSDK to change the
+ * device's state.
+ *
+ * When the device changes state, a state change callback is sent back
+ * by libiWmxSDK, which gets fed to iwmx_cm_state_change(), which
+ * evaluates the state change and updates connman's internal state in
+ * response.
+ *
+ * This allows the device to be also controlled by external tools
+ * without driving connman out of state.
+ *
+ * Device's state changes can be caused through:
+ *
+ *  - connman (by user request)
+ *
+ *  - any other external utility (eg: WmxSDK's wimaxcu)
+ *
+ *  - external stimuli: network connection broken when going out of
+ *    range
+ *
+ * Functions named __*() normally indicate that require locking. See
+ * their doc header.
+ *
+ * ENUMERATION
+ *
+ * When we receive a normal probe request [iwmx_cm_probe()] from
+ * connman, we ignore it (we can tell based on the connman device
+ * having NULL data).
+ *
+ * The plugin has registered with the WiMAX Network Service and it
+ * will listen to its device add/rm messages [iwmx_sdk_addremove_cb()]
+ * and use that to create a  device [iwmx_sdk_dev_add()] which will be
+ * registered with connman. [iwmx_cm_dev_add()]. Then connman will
+ * enumerate the device, call again iwmx_cm_probe() and at this time,
+ * we'll recognize it, pass through iwmx_sdk_setup() and complete the
+ * probe process.
+ *
+ * If the daemon dies, in theory the plugin will realize and remove
+ * the WiMAX device.
+ */
+
+struct wmxsdk {
+       struct WIMAX_API_DEVICE_ID device_id;
+       struct connman_device *dev;
+
+       GStaticMutex network_mutex;
+
+       WIMAX_API_DEVICE_STATUS status;
+       GMutex *status_mutex;
+
+       /*
+        * nw points to the network we are connected to. connecting_nw
+        * points to the network we have requested to connect.
+        */
+       GMutex *connect_mutex;
+       struct connman_network *connecting_nw, *nw;
+
+       char name[100];
+       char ifname[16];
+};
+
+/* Initialize a [zeroed] struct wmxsdk */
+static inline void wmxsdk_init(struct wmxsdk *wmxsdk)
+{
+       g_static_mutex_init(&wmxsdk->network_mutex);
+
+       wmxsdk->status = WIMAX_API_DEVICE_STATUS_UnInitialized;
+       wmxsdk->status_mutex = g_mutex_new();
+       g_assert(wmxsdk->status_mutex);
+
+       wmxsdk->connect_mutex = g_mutex_new();
+       g_assert(wmxsdk->connect_mutex);
+}
+
+/* Misc utilities */
+#define ARRAY_SIZE(a) (sizeof(a)/sizeof(a[0]))
+#define container_of(pointer, type, member)                            \
+({                                                                     \
+       type *object = NULL;                                            \
+       size_t offset = (void *) &object->member - (void *) object;     \
+       (type *) ((void *) pointer - offset);                           \
+})
+
+/* Misc values */
+enum {
+       /**
+        * Time we wait for callbacks: 5s
+        *
+        * I know, it is huge, but L4 and the device sometimes take
+        * some time, especially when there is crypto involved.
+        */
+       IWMX_SDK_L4_TIMEOUT_US = 5 * 1000 * 1000,
+
+       /*
+        * WARNING!!!!!
+        *
+        * ONLY ONE DEVICE SUPPORTED
+        *
+        * - on removal, there is no way to know which device was
+        *   removed (the removed device is removed from the list and
+        *   the callback doesn't have any more information than the
+        *   index in the list that getlistdevice would return -- racy
+        *   as hell).
+        *
+        * - on insertion, there is not enough information provided.
+        */
+       IWMX_SDK_DEV_MAX = 1,
+};
+
+struct connman_network *__iwmx_cm_network_available(
+                       struct wmxsdk *wmxsdk, const char *station_name,
+                       const char *station_type,
+                       const void *sdk_nspname, size_t sdk_nspname_size,
+                                                               int strength);
+
+struct connman_network *iwmx_cm_network_available(
+                       struct wmxsdk *wmxsdk, const char *station_name,
+                       const char *station_type,
+                       const void *sdk_nspname, size_t sdk_nspname_size,
+                                                               int strength);
+
+WIMAX_API_DEVICE_STATUS iwmx_cm_status_get(struct wmxsdk *wmxsdk);
+void __iwmx_cm_state_change(struct wmxsdk *wmxsdk,
+                                       WIMAX_API_DEVICE_STATUS __new_status);
+void iwmx_cm_state_change(struct wmxsdk *wmxsdk,
+                                       WIMAX_API_DEVICE_STATUS __new_status);
+
+int iwmx_sdk_connect(struct wmxsdk *wmxsdk, struct connman_network *nw);
+int iwmx_sdk_disconnect(struct wmxsdk *wmxsdk);
+struct connman_network *__iwmx_sdk_get_connected_network(struct wmxsdk *wmxsdk);
+const char *iwmx_sdk_dev_status_to_str(WIMAX_API_DEVICE_STATUS status);
+int iwmx_sdk_rf_state_set(struct wmxsdk *wmxsdk, WIMAX_API_RF_STATE rf_state);
+WIMAX_API_DEVICE_STATUS iwmx_sdk_get_device_status(struct wmxsdk *wmxsdk);
+int iwmx_sdk_setup(struct wmxsdk *wmxsdk);
+void iwmx_sdk_remove(struct wmxsdk *wmxsdk);
+int iwmx_sdk_scan(struct wmxsdk *wmxsdk);
+int iwmx_sdk_api_init(void);
+void iwmx_sdk_api_exit(void);
diff --git a/plugins/iwmxsdk.c b/plugins/iwmxsdk.c
new file mode 100644 (file)
index 0000000..60466e5
--- /dev/null
@@ -0,0 +1,1015 @@
+/*
+ *
+ *  Connection Manager
+ *
+ *  Copyright (C) 2007-2009  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 <errno.h>
+#include <string.h>
+#include <net/if.h>
+
+#include <glib.h>
+
+#define CONNMAN_API_SUBJECT_TO_CHANGE
+#include <connman/device.h>
+#include <connman/inet.h>
+#include <connman/log.h>
+
+#include <WiMaxAPI.h>
+#include <WiMaxAPIEx.h>
+
+#include "iwmx.h"
+
+/* Yes, this is dirty; see above on IWMX_SDK_DEV_MAX*/
+static struct wmxsdk g_iwmx_sdk_devs[IWMX_SDK_DEV_MAX];
+
+static struct wmxsdk *deviceid_to_wmxsdk(struct WIMAX_API_DEVICE_ID *device_id)
+{
+       return container_of(device_id, struct wmxsdk, device_id);
+}
+
+static struct WIMAX_API_DEVICE_ID g_api;
+
+
+/*
+ * FIXME: pulled it it out of some hole
+ *
+ * the cinr to percentage computation comes from the L3/L4 doc
+ *
+ * But some other places (L4 code) have a more complex, seemingly
+ * logarithmical computation.
+ *
+ * Oh well...
+ *
+ */
+static int cinr_to_percentage(int cinr)
+{
+       int strength;
+       if (cinr <= -5)
+               strength = 0;
+       else if (cinr >= 25)
+               strength = 100;
+       else    /* Calc percentage on the value from -5 to 25 */
+               strength = ((100UL * (cinr - -5)) / (25 - -5));
+       return strength;
+}
+
+/*
+ * Convert a WiMAX API status to an string.
+ */
+const char *iwmx_sdk_dev_status_to_str(WIMAX_API_DEVICE_STATUS status)
+{
+       switch (status) {
+       case WIMAX_API_DEVICE_STATUS_UnInitialized:
+               return "Uninitialized";
+               break;
+       case WIMAX_API_DEVICE_STATUS_RF_OFF_HW_SW:
+               return "Device RF Off(both H/W and S/W)";
+               break;
+       case WIMAX_API_DEVICE_STATUS_RF_OFF_HW:
+               return "Device RF Off(via H/W switch)";
+       case WIMAX_API_DEVICE_STATUS_RF_OFF_SW:
+               return "Device RF Off(via S/W switch)";
+       case WIMAX_API_DEVICE_STATUS_Ready:
+               return "Device is ready";
+       case WIMAX_API_DEVICE_STATUS_Scanning:
+               return "Device is scanning";
+       case WIMAX_API_DEVICE_STATUS_Connecting:
+               return "Connection in progress";
+       case WIMAX_API_DEVICE_STATUS_Data_Connected:
+               return "Layer 2 connected";
+       case WIMAX_API_DEVICE_STATUS_Connection_Idle:
+               return "Idle connection";
+       default:
+               return "unknown state";
+       }
+}
+
+/*
+ * Get the device's status from the device
+ *
+ * Does NOT cache the result
+ * Does NOT trigger a state change in connman
+ *
+ * Returns < 0 errno code on error, status code if ok.
+ */
+WIMAX_API_DEVICE_STATUS iwmx_sdk_get_device_status(struct wmxsdk *wmxsdk)
+{
+       WIMAX_API_RET r;
+       char errstr[512];
+       UINT32 errstr_size = sizeof(errstr);
+
+       WIMAX_API_DEVICE_STATUS dev_status;
+       WIMAX_API_CONNECTION_PROGRESS_INFO pi;
+
+       r = GetDeviceStatus(&wmxsdk->device_id, &dev_status, &pi);
+       if (r != WIMAX_API_RET_SUCCESS) {
+               GetErrorString(&wmxsdk->device_id, r, errstr, &errstr_size);
+               connman_error("wmxsdk: Cannot read device state: %d (%s)\n",
+                       r, errstr);
+               dev_status = -EIO;
+       }
+       return dev_status;
+}
+
+/*
+ * Get the device's status from the device but return a string describing it
+ *
+ * Same conditions as iwmx_sdk_get_device_status().
+ */
+static const char *iwmx_sdk_get_device_status_str(struct wmxsdk *wmxsdk)
+{
+       const char *result;
+       WIMAX_API_DEVICE_STATUS dev_status;
+
+       dev_status = iwmx_sdk_get_device_status(wmxsdk);
+       if ((int) dev_status < 0)
+               result = "cannot read device state";
+       else
+               result = iwmx_sdk_dev_status_to_str(dev_status);
+       return result;
+}
+
+/*
+ * Translate a WiMAX network type to a readable name.
+ */
+static const char *iwmx_sdk_network_type_name(enum _WIMAX_API_NETWORK_TYPE network_type)
+{
+       static char *network_type_name[] = {
+               [WIMAX_API_HOME] = "",
+               [WIMAX_API_PARTNER] = " (partner network)",
+               [WIMAX_API_ROAMING_PARTNER] = " (roaming partner network)",
+               [WIMAX_API_UNKNOWN] = " (unknown network)",
+       };
+       if (network_type > WIMAX_API_UNKNOWN)
+               return "(BUG! UNKNOWN NETWORK_TYPE MODE)";
+       else
+               return network_type_name[network_type];
+}
+
+/*
+ * If the device is connected but we don't know about the network,
+ * create the knowledge of it.
+ *
+ * Asks the WiMAX API to report which NSP we are connected to and we
+ * create/update a network_el in the device's network list. Then
+ * return it.
+ *
+ * Returns NULL on error.
+ *
+ * NOTE: wmxsdk->network_mutex has to be taken
+ */
+struct connman_network *__iwmx_sdk_get_connected_network(struct wmxsdk *wmxsdk)
+{
+       struct connman_network *nw;
+
+       struct WIMAX_API_CONNECTED_NSP_INFO nsp_info;
+       WIMAX_API_RET r;
+       char errstr[512];
+       UINT32 errstr_size = sizeof(errstr);
+
+       /* The device is getting connected due to an external (to
+        * connman) event; find which is the nw we are getting
+        * connected to. if we don't have it, add it */
+       r = GetConnectedNSP(&wmxsdk->device_id, &nsp_info);
+       if (r != WIMAX_API_RET_SUCCESS) {
+               GetErrorString(&wmxsdk->device_id, r, errstr, &errstr_size);
+               connman_error(
+                       "wmxsdk: Cannot get connected NSP info: %d (%s)\n",
+                       r, errstr);
+               strcpy((char *) nsp_info.NSPName, "unknown");
+               nw = iwmx_cm_network_available(
+                       wmxsdk, "unknown",
+                       iwmx_sdk_network_type_name(WIMAX_API_UNKNOWN),
+                       nsp_info.NSPName, strlen((char *) nsp_info.NSPName) + 1,
+                       cinr_to_percentage(nsp_info.CINR - 10));
+       } else {
+               nw = iwmx_cm_network_available(
+                       wmxsdk, (char *) nsp_info.NSPName,
+                       iwmx_sdk_network_type_name(nsp_info.networkType),
+                       nsp_info.NSPName, strlen((char *) nsp_info.NSPName) + 1,
+                       cinr_to_percentage(nsp_info.CINR - 10));
+       }
+       return nw;
+}
+
+/*
+ * Callback for a RF State command
+ *
+ * Called by the WiMAX API when a command sent to change the RF state
+ * is completed. This is just a confirmation of what happened with the
+ * command.
+ *
+ * We don't do anything, as when the device changes state, the state
+ * change callback is called and that will fiddle with the connman
+ * internals.
+ */
+static void __iwmx_sdk_rf_state_cb(struct WIMAX_API_DEVICE_ID *device_id,
+                                               WIMAX_API_RF_STATE rf_state)
+{
+       DBG("rf_state changed to %d\n", rf_state);
+}
+
+/*
+ * Turn the radio on or off
+ *
+ * First it checks that we are in the right state before doing
+ * anything; there might be no need to do anything.
+ *
+ * Issue a command to the WiMAX API, wait for a callback confirming it
+ * is done. Sometimes the callback is missed -- in that case, do force
+ * a state change evaluation.
+ *
+ * Frustration note:
+ *
+ *      Geezoos efing Xist, they make difficult even the most simple
+ *      of the operations
+ *
+ *      This thing is definitely a pain. If the radio is ON already
+ *      and you switch it on again...well, there is no way to tell
+ *      because you don't get a callback saying it basically
+ *      suceeded. But on the other hand, if the thing was in a
+ *      different state and action needs to be taken, you have to wait
+ *      for a callback to confirm it's done. However, there is also an
+ *      state change callback, which is almost the same, so now you
+ *      have to handle things in two "unrelated" threads of execution.
+ *
+ *      How the shpx are you expected to tell the difference? Check
+ *      status first? On timeout? Nice gap (eighteen wheeler size) for
+ *      race conditions.
+ */
+int iwmx_sdk_rf_state_set(struct wmxsdk *wmxsdk, WIMAX_API_RF_STATE rf_state)
+{
+       int result;
+
+       WIMAX_API_RET r;
+       char errstr[512];
+       UINT32 errstr_size = sizeof(errstr);
+       WIMAX_API_DEVICE_STATUS dev_status;
+
+       g_assert(rf_state == WIMAX_API_RF_ON || rf_state == WIMAX_API_RF_OFF);
+
+       /* Guess what the current radio state is; if it is ON
+        * already, don't redo it. */
+       dev_status = iwmx_sdk_get_device_status(wmxsdk);
+       if ((int) dev_status < 0) {
+               result = dev_status;
+               goto error_get_status;
+       }
+       switch (dev_status) {
+       case WIMAX_API_DEVICE_STATUS_UnInitialized:
+               result = -EINVAL;
+               goto error_cant_do;
+       case WIMAX_API_DEVICE_STATUS_RF_OFF_HW_SW:
+       case WIMAX_API_DEVICE_STATUS_RF_OFF_HW:
+               connman_error(
+                       "wmxsdk: cannot turn on radio: hw switch is off\n");
+               result = -EPERM;
+               goto error_cant_do;
+               break;
+       case WIMAX_API_DEVICE_STATUS_RF_OFF_SW:
+               if (rf_state == WIMAX_API_RF_OFF) {
+                       result = 0;
+                       DBG("radio is already off\n");
+                       goto out_done;
+               }
+               break;
+       case WIMAX_API_DEVICE_STATUS_Ready:
+       case WIMAX_API_DEVICE_STATUS_Scanning:
+       case WIMAX_API_DEVICE_STATUS_Connecting:
+       case WIMAX_API_DEVICE_STATUS_Data_Connected:
+       case WIMAX_API_DEVICE_STATUS_Connection_Idle:
+               if (rf_state == WIMAX_API_RF_ON) {
+                       result = 0;
+                       DBG("radio is already on\n");
+                       goto out_done;
+               }
+               break;
+       default:
+               g_assert(1);
+       }
+       /* Ok, flip the radio */
+       r = CmdControlPowerManagement(&wmxsdk->device_id, rf_state);
+       if (r != WIMAX_API_RET_SUCCESS) {
+               GetErrorString(&wmxsdk->device_id, r, errstr, &errstr_size);
+               connman_error("wmxsdk: Cannot flip radio to %d: %d (%s) "
+                             "[device is in state %s]\n",
+                             rf_state, r, errstr,
+                             iwmx_sdk_get_device_status_str(wmxsdk));
+               result = -EIO;
+       } else
+               result = -EINPROGRESS;
+out_done:
+error_cant_do:
+error_get_status:
+       return result;
+}
+
+/*
+ * Callback for a Connect command
+ *
+ * Called by the WiMAX API when a command sent to connect is
+ * completed. This is just a confirmation of what happened with the
+ * command.
+ *
+ * WE DON'T DO MUCH HERE -- the real meat happens when a state change
+ * callback is sent, where we detect we move to connected state (or
+ * from disconnecting to something else); the state change callback is
+ * called and that will fiddle with the connman internals.
+ */
+static void __iwmx_sdk_connect_cb(struct WIMAX_API_DEVICE_ID *device_id,
+                                       WIMAX_API_NETWORK_CONNECTION_RESP resp)
+{
+       WIMAX_API_DEVICE_STATUS status;
+       struct wmxsdk *wmxsdk = deviceid_to_wmxsdk(device_id);
+
+       status = iwmx_cm_status_get(wmxsdk);
+       if (resp == WIMAX_API_CONNECTION_SUCCESS) {
+               if (status != WIMAX_API_DEVICE_STATUS_Data_Connected
+                   && status != WIMAX_API_DEVICE_STATUS_Connection_Idle)
+                       connman_error("wmxsdk: error: connect worked, but state"
+                                     " didn't change (now it is %d [%s])\n",
+                                     status,
+                                     iwmx_sdk_dev_status_to_str(status));
+       } else
+               connman_error("wmxsdk: failed to connect (status %d: %s)\n",
+                             status, iwmx_sdk_dev_status_to_str(status));
+}
+
+/*
+ * Connect to a network
+ *
+ * This function starts the connection process to a given network;
+ * when the device changes status, the status change callback will
+ * tell connman if the network is finally connected or not.
+ *
+ * One of the reasons it is done like that is to allow external tools
+ * to control the device and the plugin just passing the status so
+ * connman displays the right info.
+ */
+int iwmx_sdk_connect(struct wmxsdk *wmxsdk, struct connman_network *nw)
+{
+       int result;
+
+       WIMAX_API_RET r;
+       char errstr[512];
+       UINT32 errstr_size = sizeof(errstr);
+       WIMAX_API_DEVICE_STATUS dev_status;
+       const char *station_name = connman_network_get_identifier(nw);
+       const void *sdk_nspname;
+       unsigned int sdk_nspname_size;
+
+       g_mutex_lock(wmxsdk->connect_mutex);
+       /* Guess what the current radio state is; if it is ON
+        * already, don't redo it. */
+       dev_status = iwmx_cm_status_get(wmxsdk);
+       if ((int) dev_status < 0) {
+               result = dev_status;
+               goto error_get_status;
+       }
+       switch (dev_status) {
+       case WIMAX_API_DEVICE_STATUS_UnInitialized:
+               connman_error("wmxsdk: SW BUG? HW is uninitialized\n");
+               result = -EINVAL;
+               goto error_cant_do;
+       case WIMAX_API_DEVICE_STATUS_RF_OFF_HW_SW:
+       case WIMAX_API_DEVICE_STATUS_RF_OFF_HW:
+       case WIMAX_API_DEVICE_STATUS_RF_OFF_SW:
+               connman_error("wmxsdk: Cannot connect: radio is off\n");
+               result = -EPERM;
+               goto error_cant_do;
+       case WIMAX_API_DEVICE_STATUS_Ready:
+       case WIMAX_API_DEVICE_STATUS_Scanning:
+               break;
+       case WIMAX_API_DEVICE_STATUS_Connecting:
+               DBG("Connect already pending, waiting for it\n");
+               result = -EINPROGRESS;
+               goto error_cant_do;
+       case WIMAX_API_DEVICE_STATUS_Data_Connected:
+       case WIMAX_API_DEVICE_STATUS_Connection_Idle:
+               connman_error("wmxsdk: BUG? need to disconnect?\n");
+               result = -EINVAL;
+               goto error_cant_do;
+       default:
+               g_assert(1);
+       }
+
+       /* Ok, do the connection, wait for a callback */
+       wmxsdk->connecting_nw = connman_network_ref(nw);
+       sdk_nspname = connman_network_get_blob(nw, "WiMAX.NSP.name",
+                                                       &sdk_nspname_size);
+       g_assert(sdk_nspname != NULL);
+       r = CmdConnectToNetwork(&wmxsdk->device_id, (void *) sdk_nspname, 0, 0);
+       if (r != WIMAX_API_RET_SUCCESS) {
+               GetErrorString(&wmxsdk->device_id, r, errstr, &errstr_size);
+               connman_error("wmxsdk: Cannot connect to network %s: %d (%s)"
+                             " - device is in state '%s'\n",
+                             station_name, r, errstr,
+                             iwmx_sdk_get_device_status_str(wmxsdk));
+               result = -EIO;
+               connman_network_unref(nw);
+               wmxsdk->connecting_nw = NULL;
+       } else
+               result = -EINPROGRESS;
+error_cant_do:
+error_get_status:
+       g_mutex_unlock(wmxsdk->connect_mutex);
+       return result;
+}
+
+/*
+ * Callback for a Disconnect command
+ *
+ * Called by the WiMAX API when a command sent to connect is
+ * completed. This is just a confirmation of what happened with the
+ * command.
+ *
+ * When the device changes state, the state change callback is called
+ * and that will fiddle with the connman internals.
+ *
+ * We just update the result of the command and wake up anybody who is
+ * waiting for this conditional variable.
+ */
+static void __iwmx_sdk_disconnect_cb(struct WIMAX_API_DEVICE_ID *device_id,
+                                       WIMAX_API_NETWORK_CONNECTION_RESP resp)
+{
+       struct wmxsdk *wmxsdk = deviceid_to_wmxsdk(device_id);
+       WIMAX_API_DEVICE_STATUS status;
+
+       status = iwmx_cm_status_get(wmxsdk);
+       if (resp == WIMAX_API_CONNECTION_SUCCESS) {
+               if (status == WIMAX_API_DEVICE_STATUS_Data_Connected
+                   || status == WIMAX_API_DEVICE_STATUS_Connection_Idle)
+                       connman_error("wmxsdk: error: disconnect worked, "
+                                     "but state didn't change (now it is "
+                                     "%d [%s])\n", status,
+                                     iwmx_sdk_dev_status_to_str(status));
+       } else
+               connman_error("wmxsdk: failed to disconnect (status %d: %s)\n",
+                             status, iwmx_sdk_dev_status_to_str(status));
+}
+
+/*
+ * Disconnect from a network
+ *
+ * This function tells the device to disconnect; the state change
+ * callback will take care of inform connman's internals.
+ */
+int iwmx_sdk_disconnect(struct wmxsdk *wmxsdk)
+{
+       int result;
+
+       WIMAX_API_RET r;
+       char errstr[512];
+       UINT32 errstr_size = sizeof(errstr);
+       WIMAX_API_DEVICE_STATUS dev_status;
+
+       g_mutex_lock(wmxsdk->connect_mutex);
+       /* Guess what the current radio state is; if it is ON
+        * already, don't redo it. */
+       dev_status = iwmx_sdk_get_device_status(wmxsdk);
+       if ((int) dev_status < 0) {
+               result = dev_status;
+               goto error_get_status;
+       }
+       switch (dev_status) {
+       case WIMAX_API_DEVICE_STATUS_UnInitialized:
+               connman_error("wmxsdk: SW BUG? HW is uninitialized\n");
+               result = -EINVAL;
+               goto error_cant_do;
+       case WIMAX_API_DEVICE_STATUS_RF_OFF_HW_SW:
+       case WIMAX_API_DEVICE_STATUS_RF_OFF_HW:
+       case WIMAX_API_DEVICE_STATUS_RF_OFF_SW:
+               DBG("Cannot disconnect, radio is off; ignoring\n");
+               result = 0;
+               goto error_cant_do;
+       case WIMAX_API_DEVICE_STATUS_Ready:
+       case WIMAX_API_DEVICE_STATUS_Scanning:
+               DBG("Cannot disconnect, already disconnected; ignoring\n");
+               result = 0;
+               goto error_cant_do;
+       case WIMAX_API_DEVICE_STATUS_Connecting:
+       case WIMAX_API_DEVICE_STATUS_Data_Connected:
+       case WIMAX_API_DEVICE_STATUS_Connection_Idle:
+               break;
+       default:
+               g_assert(1);
+       }
+       /* Ok, flip the radio */
+       r = CmdDisconnectFromNetwork(&wmxsdk->device_id);
+       if (r != WIMAX_API_RET_SUCCESS) {
+               GetErrorString(&wmxsdk->device_id, r, errstr, &errstr_size);
+               connman_error("wmxsdk: Cannot disconnect from network: "
+                             "%d (%s)\n", r, errstr);
+               result = -EIO;
+       } else
+               result = -EINPROGRESS;
+error_cant_do:
+error_get_status:
+       g_mutex_unlock(wmxsdk->connect_mutex);
+       return result;
+}
+
+/*
+ * Callback for state change messages
+ *
+ * Just pass them to the state transition handler
+ */
+static void __iwmx_sdk_state_change_cb(struct WIMAX_API_DEVICE_ID *device_id,
+                                       WIMAX_API_DEVICE_STATUS status,
+                                       WIMAX_API_STATUS_REASON reason,
+                                       WIMAX_API_CONNECTION_PROGRESS_INFO pi)
+{
+       struct wmxsdk *wmxsdk = deviceid_to_wmxsdk(device_id);
+       iwmx_cm_state_change(wmxsdk, status);
+}
+
+/*
+ * Called by _iwmx_sdk_*scan_cb() when [wide or preferred] scan results
+ * are available.
+ *
+ * From here we update the connman core idea of which networks are
+ * available.
+ */
+static void __iwmx_sdk_scan_common_cb(struct WIMAX_API_DEVICE_ID *device_id,
+                                       struct WIMAX_API_NSP_INFO_EX *nsp_list,
+                                                       UINT32 nsp_list_size)
+{
+       struct wmxsdk *wmxsdk = deviceid_to_wmxsdk(device_id);
+       unsigned itr;
+       char station_name[256];
+
+       g_static_mutex_lock(&wmxsdk->network_mutex);
+       for (itr = 0; itr < nsp_list_size; itr++) {
+               int strength;
+               struct WIMAX_API_NSP_INFO_EX *nsp_info = &nsp_list[itr];
+               snprintf(station_name, sizeof(station_name),
+                        "%s", (char *)nsp_info->NSPName);
+               /* CAPI is reporing link quality as zero -- if it is
+                * zero, check if it is a bug by computing it based on
+                * CINR. If it is different, use the computed one. */
+               strength = nsp_info->linkQuality;
+               if (strength == 0) {    /* huh */
+                       int linkq_expected =
+                               cinr_to_percentage(nsp_info->CINR - 10);
+                       if (linkq_expected != strength)
+                               strength = linkq_expected;
+               }
+
+               __iwmx_cm_network_available(
+                       wmxsdk, station_name,
+                       iwmx_sdk_network_type_name(nsp_info->networkType),
+                       nsp_info->NSPName,
+                       strlen((char *) nsp_info->NSPName) + 1,
+                       strength);
+       }
+       g_static_mutex_unlock(&wmxsdk->network_mutex);
+}
+
+/*
+ * Called by the WiMAX API when we get a wide scan result
+ *
+ * We treat them same as wide, so we just call that.
+ */
+static void __iwmx_sdk_wide_scan_cb(struct WIMAX_API_DEVICE_ID *device_id,
+                               struct WIMAX_API_NSP_INFO_EX *nsp_list,
+                                                       UINT32 nsp_list_size)
+{
+       __iwmx_sdk_scan_common_cb(device_id, nsp_list, nsp_list_size);
+}
+
+/*
+ * Called by the WiMAX API when we get a normal (non wide) scan result
+ *
+ * We treat them same as wide, so we just call that.
+ */
+static void __iwmx_sdk_scan_cb(struct WIMAX_API_DEVICE_ID *device_id,
+                               struct WIMAX_API_NSP_INFO_EX *nsp_list,
+                               UINT32 nsp_list_size, UINT32 searchProgress)
+{
+       __iwmx_sdk_scan_common_cb(device_id, nsp_list, nsp_list_size);
+}
+
+/*
+ * Called to ask the device to scan for networks
+ *
+ * We don't really scan as the WiMAX SDK daemon scans in the
+ * background for us. We just get the results. See iwmx_sdk_setup().
+ */
+int iwmx_sdk_scan(struct wmxsdk *wmxsdk)
+{
+       int result;
+
+       UINT32 nsp_list_length = 10;
+       struct WIMAX_API_NSP_INFO_EX nsp_list[10];      /* FIXME: up to 32? */
+
+       WIMAX_API_RET r;
+       char errstr[512];
+       UINT32 errstr_size = sizeof(errstr);
+
+       r = GetNetworkListEx(&wmxsdk->device_id, nsp_list, &nsp_list_length);
+       if (r != WIMAX_API_RET_SUCCESS) {
+               GetErrorString(&wmxsdk->device_id, r, errstr, &errstr_size);
+               connman_error("wmxsdk: Cannot get network list: %d (%s)\n",
+                             r, errstr);
+               result = -EIO;
+               goto error_scan;
+       }
+
+       if (nsp_list_length == 0)
+               DBG("no networks\n");
+       else
+               __iwmx_sdk_scan_common_cb(&wmxsdk->device_id, nsp_list,
+                                       nsp_list_length);
+       result = 0;
+error_scan:
+       return result;
+}
+
+/*
+ * Initialize the WiMAX API, register with it, setup callbacks
+ *
+ * Called through
+ *
+ * iwmx_sdk_dev_add
+ *   connman_inet_create_device
+ *      connman_register
+ *         iwmx_cm_probe()
+ */
+int iwmx_sdk_setup(struct wmxsdk *wmxsdk)
+{
+       int result;
+
+       WIMAX_API_RET r;
+
+       char errstr[512];
+       UINT32 errstr_size = sizeof(errstr);
+
+       result = -ENFILE;
+
+       /* device_id initialized by iwmx_sdk_dev_add */
+
+       r = WiMaxDeviceOpen(&wmxsdk->device_id);
+       if (r != WIMAX_API_RET_SUCCESS) {
+               GetErrorString(&wmxsdk->device_id, r, errstr, &errstr_size);
+               connman_error("wmxsdk: Cannot open device: %d (%s)\n",
+                             r, errstr);
+               goto error_wimaxdeviceopen;
+       }
+
+       /*
+        * We scan in auto mode (in the background)
+        *
+        * Otherwise is messy -- if we have connman triggering a scan
+        * when we call iwmx_cm_scan() -> iwmx_sdk_scan(), most of the
+        * times that causes a race condition when the UI asks for a
+        * scan right before displaying the network menu. As there is
+        * no way to cancel an ongoing scan before connecting, we are
+        * stuck. So we do auto bg and have iwmx_sdk_scan() just return
+        * the current network list.
+        */
+       r = SetConnectionMode(&wmxsdk->device_id,
+                             WIMAX_API_CONNECTION_AUTO_SCAN_MANUAL_CONNECT);
+       if (r != WIMAX_API_RET_SUCCESS) {
+               GetErrorString(&wmxsdk->device_id, r, errstr, &errstr_size);
+               connman_error("wmxsdk: Cannot set connectin mode to manual: "
+                             "%d (%s)\n", r, errstr);
+               goto error_connection_mode;
+       }
+
+       r = SubscribeControlPowerManagement(&wmxsdk->device_id,
+                                           __iwmx_sdk_rf_state_cb);
+       if (r != WIMAX_API_RET_SUCCESS) {
+               GetErrorString(&wmxsdk->device_id, r, errstr, &errstr_size);
+               connman_error("wmxsdk: Cannot subscribe to radio change "
+                             "events: %u (%s)\n", r, errstr);
+               result = -EIO;
+               goto error_subscribe_rf_state;
+       }
+
+       r = SubscribeDeviceStatusChange(&wmxsdk->device_id,
+                                       __iwmx_sdk_state_change_cb);
+       if (r != WIMAX_API_RET_SUCCESS) {
+               GetErrorString(&wmxsdk->device_id, r, errstr, &errstr_size);
+               connman_error("wmxsdk: Cannot subscribe to state chaneg events:"
+                             "%d (%s)\n", r, errstr);
+               goto error_subscribe_state_change;
+       }
+
+       r = SubscribeNetworkSearchWideScanEx(&wmxsdk->device_id,
+                                            __iwmx_sdk_wide_scan_cb);
+       if (r != WIMAX_API_RET_SUCCESS) {
+               GetErrorString(&wmxsdk->device_id, r, errstr, &errstr_size);
+               connman_error("wmxsdk: Cannot subscribe to wide scan events: "
+                             "%d (%s)\n", r, errstr);
+               goto error_subscribe_wide_scan;
+       }
+       r = SubscribeNetworkSearchEx(&wmxsdk->device_id, __iwmx_sdk_scan_cb);
+       if (r != WIMAX_API_RET_SUCCESS) {
+               GetErrorString(&wmxsdk->device_id, r, errstr, &errstr_size);
+               connman_error("wmxsdk: Cannot subscribe to scan events: "
+                             "%d (%s)\n", r, errstr);
+               goto error_subscribe_scan;
+       }
+
+       r = SubscribeConnectToNetwork(&wmxsdk->device_id,
+                                     __iwmx_sdk_connect_cb);
+       if (r != WIMAX_API_RET_SUCCESS) {
+               GetErrorString(&wmxsdk->device_id, r, errstr, &errstr_size);
+               connman_error("wmxsdk: Cannot subscribe to connect events: "
+                             "%d (%s)\n", r, errstr);
+               goto error_subscribe_connect;
+       }
+
+       r = SubscribeDisconnectToNetwork(&wmxsdk->device_id,
+                                        __iwmx_sdk_disconnect_cb);
+       if (r != WIMAX_API_RET_SUCCESS) {
+               GetErrorString(&wmxsdk->device_id, r, errstr, &errstr_size);
+               connman_error("wmxsdk: Cannot subscribe to disconnect events: "
+                             "%d (%s)\n", r, errstr);
+               goto error_subscribe_disconnect;
+       }
+       result = 0;
+out:
+       return result;
+
+       UnsubscribeDisconnectToNetwork(&wmxsdk->device_id);
+error_subscribe_disconnect:
+       UnsubscribeConnectToNetwork(&wmxsdk->device_id);
+error_subscribe_connect:
+       UnsubscribeNetworkSearchEx(&wmxsdk->device_id);
+error_subscribe_scan:
+       UnsubscribeNetworkSearchWideScanEx(&wmxsdk->device_id);
+error_subscribe_wide_scan:
+       UnsubscribeDeviceStatusChange(&wmxsdk->device_id);
+error_subscribe_state_change:
+       UnsubscribeControlPowerManagement(&wmxsdk->device_id);
+error_subscribe_rf_state:
+error_connection_mode:
+       WiMaxDeviceClose(&wmxsdk->device_id);
+error_wimaxdeviceopen:
+       goto out;
+}
+
+/*
+ * Called when a device is removed from connman
+ *
+ * Cleanup all that is done in iwmx_sdk_setup(). Remove callbacks,
+ * unregister from the WiMAX API.
+ */
+void iwmx_sdk_remove(struct wmxsdk *wmxsdk)
+{
+       UnsubscribeDisconnectToNetwork(&wmxsdk->device_id);
+       UnsubscribeConnectToNetwork(&wmxsdk->device_id);
+       UnsubscribeNetworkSearchEx(&wmxsdk->device_id);
+       UnsubscribeNetworkSearchWideScanEx(&wmxsdk->device_id);
+       UnsubscribeDeviceStatusChange(&wmxsdk->device_id);
+       UnsubscribeControlPowerManagement(&wmxsdk->device_id);
+       WiMaxDeviceClose(&wmxsdk->device_id);
+}
+
+static void iwmx_sdk_dev_add(unsigned idx, unsigned api_idx, const char *name)
+{
+       int result, ifindex;
+       struct wmxsdk *wmxsdk;
+       const char *s;
+
+       if (idx >= IWMX_SDK_DEV_MAX) {
+               connman_error("BUG! idx (%u) >= IWMX_SDK_DEV_MAX (%u)\n",
+                             idx, IWMX_SDK_DEV_MAX);
+               goto error_bug;
+       }
+       wmxsdk = &g_iwmx_sdk_devs[idx];
+       if (wmxsdk->dev != NULL) {
+               connman_error("BUG! device index %u already enumerated?\n",
+                             idx);
+               goto error_bug;
+       }
+
+       memset(wmxsdk, 0, sizeof(*wmxsdk));
+       wmxsdk_init(wmxsdk);
+       /*
+        * This depends on a hack in the WiMAX Network Service; it has
+        * to return, as part of the device name, a string "if:IFNAME"
+        * where the OS's device name is stored.
+        */
+       s = strstr(name, "if:");
+       if (s == NULL
+           || sscanf(s, "if:%15[^ \f\n\r\t\v]", wmxsdk->ifname) != 1) {
+               connman_error("Cannot extract network interface name off '%s'",
+                             name);
+               goto error_noifname;
+       }
+       DBG("network interface name: '%s'", wmxsdk->ifname);
+
+       ifindex = if_nametoindex(wmxsdk->ifname);
+       if (ifindex <= 0) {
+               result = -ENFILE;
+               connman_error("wxmsdk: %s: cannot find interface index\n",
+                             wmxsdk->ifname);
+               goto error_noifname;
+       }
+
+       wmxsdk->dev = connman_inet_create_device(ifindex);
+       if (wmxsdk->dev == NULL) {
+               connman_error("wmxsdk: %s: failed to create connman_device\n",
+                             name);
+               goto error_create;
+       }
+       strncpy(wmxsdk->name, name, sizeof(wmxsdk->name));
+       connman_device_set_data(wmxsdk->dev, wmxsdk);
+
+       wmxsdk->device_id.privilege = WIMAX_API_PRIVILEGE_READ_WRITE;
+       wmxsdk->device_id.deviceIndex = api_idx;
+
+       result = connman_device_register(wmxsdk->dev);
+       if (result < 0) {
+               connman_error("wmxsdk: %s: failed to register: %d\n",
+                             wmxsdk->ifname, result);
+               goto error_dev_add;
+       }
+       return;
+
+error_dev_add:
+       wmxsdk->name[0] = 0;
+       connman_device_unref(wmxsdk->dev);
+       wmxsdk->dev = NULL;
+error_noifname:
+error_create:
+error_bug:
+       return;
+}
+
+static void iwmx_sdk_dev_rm(unsigned idx)
+{
+       struct wmxsdk *wmxsdk;
+
+       if (idx >= IWMX_SDK_DEV_MAX) {
+               connman_error("BUG! idx (%u) >= IWMX_SDK_DEV_MAX (%u)\n",
+                             idx, IWMX_SDK_DEV_MAX);
+               goto error_bug;
+       }
+       wmxsdk = &g_iwmx_sdk_devs[idx];
+       if (wmxsdk->dev == NULL) {
+               DBG("device index %u not enumerated? ignoring\n", idx);
+               goto error_bug;
+       }
+
+       connman_device_unregister(wmxsdk->dev);
+       wmxsdk->name[0] = 0;
+       connman_device_unref(wmxsdk->dev);
+       memset(wmxsdk, 0, sizeof(*wmxsdk));
+error_bug:
+       return;
+}
+
+static void iwmx_sdk_addremove_cb(struct WIMAX_API_DEVICE_ID *devid,
+                                                               BOOL presence)
+{
+       unsigned int cnt;
+       WIMAX_API_RET r;
+       struct WIMAX_API_HW_DEVICE_ID device_id_list[5];
+       UINT32 device_id_list_size = ARRAY_SIZE(device_id_list);
+
+       char errstr[512];
+       UINT32 errstr_size = sizeof(errstr);
+
+       DBG("cb: handle %u index #%u is %d\n", devid->sdkHandle,
+           devid->deviceIndex, presence);
+
+       r = GetListDevice(devid, device_id_list, &device_id_list_size);
+       if (r != WIMAX_API_RET_SUCCESS) {
+               GetErrorString(devid, r, errstr, &errstr_size);
+               connman_error("wmxsdk: Cannot obtain list "
+                             "of devices: %d (%s)\n", r, errstr);
+               return;
+       }
+
+       if (device_id_list_size == 0)
+               DBG("No WiMAX devices reported\n");
+       else
+               for (cnt = 0; cnt < device_id_list_size; cnt++) {
+                       struct WIMAX_API_HW_DEVICE_ID *dev =
+                               device_id_list + cnt;
+                       DBG("#%u index #%u device %s\n",
+                           cnt, dev->deviceIndex, dev->deviceName);
+               }
+       if (device_id_list_size < devid->deviceIndex) {
+               connman_error("wmxsdk: changed device (%u) not in the list? "
+                             "(%u items)\n",
+                             devid->deviceIndex, device_id_list_size);
+               return;
+       }
+
+       if (presence) {
+               struct WIMAX_API_HW_DEVICE_ID *dev =
+                       device_id_list + devid->deviceIndex;
+               iwmx_sdk_dev_add(devid->deviceIndex, dev->deviceIndex,
+                              dev->deviceName);
+       } else {
+               iwmx_sdk_dev_rm(devid->deviceIndex);
+       }
+}
+
+/*
+ * Initialize the WiMAX API, register with it, setup callbacks for
+ * device coming up / dissapearing
+ */
+int iwmx_sdk_api_init(void)
+{
+       int result;
+       unsigned int cnt;
+       WIMAX_API_RET r;
+       char errstr[512];
+       UINT32 errstr_size = sizeof(errstr);
+
+       struct WIMAX_API_HW_DEVICE_ID device_id_list[5];
+       UINT32 device_id_list_size = ARRAY_SIZE(device_id_list);
+
+       memset(&g_api, 0, sizeof(g_api));
+       g_api.privilege = WIMAX_API_PRIVILEGE_READ_WRITE;
+
+       result = -EIO;
+       r = WiMaxAPIOpen(&g_api);
+       if (r != WIMAX_API_RET_SUCCESS) {
+               GetErrorString(&g_api, r, errstr, &errstr_size);
+               connman_error("wmxsdk: WiMaxAPIOpen failed with %d (%s)\n",
+                             r, errstr);
+               goto error_wimaxapiopen;
+       }
+
+       r = SubscribeDeviceInsertRemove(&g_api, iwmx_sdk_addremove_cb);
+       if (r != WIMAX_API_RET_SUCCESS) {
+               GetErrorString(&g_api, r, errstr, &errstr_size);
+               connman_error("wmxsdk: insert/remove subscribe failed with "
+                             "%d (%s)\n", r, errstr);
+               goto error_close;
+       }
+
+       r = GetListDevice(&g_api, device_id_list, &device_id_list_size);
+       if (r != WIMAX_API_RET_SUCCESS) {
+               GetErrorString(&g_api, r, errstr, &errstr_size);
+               connman_error("wmxsdk: Cannot obtain list "
+                             "of devices: %d (%s)\n", r, errstr);
+               goto error_close;
+       }
+       if (device_id_list_size < g_api.deviceIndex) {
+               connman_error("wmxsdk: changed device (%u) not in the list? "
+                             "(%u items)\n",
+                             g_api.deviceIndex, device_id_list_size);
+       }
+
+       if (device_id_list_size == 0)
+               DBG("No WiMAX devices reported\n");
+       else
+               for (cnt = 0; cnt < device_id_list_size; cnt++) {
+                       struct WIMAX_API_HW_DEVICE_ID *dev =
+                               device_id_list + cnt;
+                       DBG("#%u index #%u device %s\n",
+                           cnt, dev->deviceIndex, dev->deviceName);
+                       iwmx_sdk_dev_add(cnt, dev->deviceIndex,
+                                        dev->deviceName);
+               }
+       return 0;
+
+error_close:
+       WiMaxAPIClose(&g_api);
+error_wimaxapiopen:
+       return result;
+}
+
+void iwmx_sdk_api_exit(void)
+{
+       WIMAX_API_RET r;
+
+       char errstr[512];
+       UINT32 errstr_size = sizeof(errstr);
+
+       r = WiMaxAPIClose(&g_api);
+       if (r != WIMAX_API_RET_SUCCESS) {
+               GetErrorString(&g_api, r, errstr, &errstr_size);
+               connman_error("wmxsdk: WiMaxAPIClose failed with %d (%s)\n",
+                             r, errstr);
+       }
+       return;
+}
diff --git a/plugins/loopback.c b/plugins/loopback.c
new file mode 100644 (file)
index 0000000..782853e
--- /dev/null
@@ -0,0 +1,259 @@
+/*
+ *
+ *  Connection Manager
+ *
+ *  Copyright (C) 2007-2009  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 <errno.h>
+#include <unistd.h>
+#include <limits.h>
+#include <string.h>
+#include <sys/ioctl.h>
+#include <sys/inotify.h>
+#include <sys/socket.h>
+#include <arpa/inet.h>
+#include <net/if.h>
+
+#include <glib.h>
+
+#define CONNMAN_API_SUBJECT_TO_CHANGE
+#include <connman/plugin.h>
+#include <connman/log.h>
+
+#if 0
+static GIOChannel *inotify_channel = NULL;
+
+static int hostname_descriptor = -1;
+
+static gboolean inotify_event(GIOChannel *channel,
+                                       GIOCondition condition, gpointer data)
+{
+       unsigned char buf[129], *ptr = buf;
+       gsize len;
+       GIOError err;
+
+       if (condition & (G_IO_HUP | G_IO_ERR))
+               return FALSE;
+
+       memset(buf, 0, sizeof(buf));
+
+       err = g_io_channel_read(channel, (gchar *) buf, sizeof(buf) - 1, &len);
+       if (err != G_IO_ERROR_NONE) {
+               if (err == G_IO_ERROR_AGAIN)
+                       return TRUE;
+               connman_error("Reading from inotify channel failed");
+               return FALSE;
+       }
+
+       while (len >= sizeof(struct inotify_event)) {
+               struct inotify_event *evt = (struct inotify_event *) ptr;
+
+               if (evt->wd == hostname_descriptor) {
+                       if (evt->mask & (IN_CREATE | IN_MOVED_TO))
+                               connman_info("create hostname file");
+
+                       if (evt->mask & (IN_DELETE | IN_MOVED_FROM))
+                               connman_info("delete hostname file");
+
+                       if (evt->mask & (IN_MODIFY | IN_MOVE_SELF))
+                               connman_info("modify hostname file");
+               }
+
+               len -= sizeof(struct inotify_event) + evt->len;
+               ptr += sizeof(struct inotify_event) + evt->len;
+       }
+
+       return TRUE;
+}
+
+static int create_watch(void)
+{
+       int fd;
+
+       fd = inotify_init();
+       if (fd < 0) {
+               connman_error("Creation of inotify context failed");
+               return -EIO;
+       }
+
+       inotify_channel = g_io_channel_unix_new(fd);
+       if (inotify_channel == NULL) {
+               connman_error("Creation of inotify channel failed");
+               close(fd);
+               return -EIO;
+       }
+
+       hostname_descriptor = inotify_add_watch(fd, "/etc/hostname",
+                               IN_MODIFY | IN_DELETE_SELF | IN_MOVE_SELF);
+       if (hostname_descriptor < 0) {
+               connman_error("Creation of hostname watch failed");
+               g_io_channel_unref(inotify_channel);
+               inotify_channel = NULL;
+               close(fd);
+               return -EIO;
+       }
+
+       g_io_add_watch(inotify_channel, G_IO_IN | G_IO_ERR | G_IO_HUP,
+                                                       inotify_event, NULL);
+
+       return 0;
+}
+
+static void remove_watch(void)
+{
+       int fd;
+
+       if (inotify_channel == NULL)
+               return;
+
+       fd = g_io_channel_unix_get_fd(inotify_channel);
+
+       if (hostname_descriptor >= 0)
+               inotify_rm_watch(fd, hostname_descriptor);
+
+       g_io_channel_unref(inotify_channel);
+
+       close(fd);
+}
+#endif
+
+static void create_hostname(void)
+{
+       const char *name = "localhost";
+
+       if (sethostname(name, strlen(name)) < 0)
+               connman_error("Failed to set hostname to %s", name);
+}
+
+static int setup_hostname(void)
+{
+       char name[HOST_NAME_MAX + 1];
+
+       memset(name, 0, sizeof(name));
+
+       if (gethostname(name, HOST_NAME_MAX) < 0) {
+               connman_error("Failed to get current hostname");
+               return -EIO;
+       }
+
+       if (strlen(name) > 0 && strcmp(name, "(none)") != 0)
+               connman_info("System hostname is %s", name);
+       else
+               create_hostname();
+
+       memset(name, 0, sizeof(name));
+
+       if (getdomainname(name, HOST_NAME_MAX) < 0) {
+               connman_error("Failed to get current domainname");
+               return -EIO;
+       }
+
+       if (strlen(name) > 0 && strcmp(name, "(none)") != 0)
+               connman_info("System domainname is %s", name);
+
+       return 0;
+}
+
+static int setup_loopback(void)
+{
+       struct ifreq ifr;
+       struct sockaddr_in *addr;
+       int sk, err;
+
+       sk = socket(PF_INET, SOCK_DGRAM, 0);
+       if (sk < 0)
+               return -1;
+
+       memset(&ifr, 0, sizeof(ifr));
+       strcpy(ifr.ifr_name, "lo");
+
+       if (ioctl(sk, SIOCGIFFLAGS, &ifr) < 0) {
+               err = -errno;
+               goto done;
+       }
+
+       if (ifr.ifr_flags & IFF_UP) {
+               err = -EALREADY;
+               connman_info("The loopback interface is already up");
+               goto done;
+       }
+
+       addr = (struct sockaddr_in *) &ifr.ifr_addr;
+       addr->sin_family = AF_INET;
+       addr->sin_addr.s_addr = inet_addr("127.0.0.1");
+
+       err = ioctl(sk, SIOCSIFADDR, &ifr);
+       if (err < 0) {
+               err = -errno;
+               connman_error("Setting address failed (%s)", strerror(-err));
+               goto done;
+       }
+
+       addr = (struct sockaddr_in *) &ifr.ifr_netmask;
+       addr->sin_family = AF_INET;
+       addr->sin_addr.s_addr = inet_addr("255.0.0.0");
+
+       err = ioctl(sk, SIOCSIFNETMASK, &ifr);
+       if (err < 0) {
+               err = -errno;
+               connman_error("Setting netmask failed (%s)", strerror(-err));
+               goto done;
+       }
+
+       if (ioctl(sk, SIOCGIFFLAGS, &ifr) < 0) {
+               err = -errno;
+               goto done;
+       }
+
+       ifr.ifr_flags |= IFF_UP;
+
+       if (ioctl(sk, SIOCSIFFLAGS, &ifr) < 0) {
+               err = -errno;
+               connman_error("Activating loopback interface failed (%s)",
+                                                       strerror(-err));
+               goto done;
+       }
+
+done:
+       close(sk);
+
+       return err;
+}
+
+static int loopback_init(void)
+{
+       setup_loopback();
+
+       setup_hostname();
+
+       //create_watch();
+
+       return 0;
+}
+
+static void loopback_exit(void)
+{
+       //remove_watch();
+}
+
+CONNMAN_PLUGIN_DEFINE(loopback, "Loopback device plugin", VERSION,
+               CONNMAN_PLUGIN_PRIORITY_HIGH, loopback_init, loopback_exit)
diff --git a/plugins/modem.c b/plugins/modem.c
new file mode 100644 (file)
index 0000000..4c8a3e3
--- /dev/null
@@ -0,0 +1,317 @@
+/*
+ *
+ *  Connection Manager
+ *
+ *  Copyright (C) 2007-2009  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 <errno.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <stdarg.h>
+#include <string.h>
+#include <termios.h>
+
+#include <glib.h>
+
+#include <connman/log.h>
+
+#include "modem.h"
+
+struct modem_data {
+       char *device;
+       GIOChannel *channel;
+       guint watch;
+       GSList *callbacks;
+       GSList *commands;
+       char buf[1024];
+       int offset;
+};
+
+struct modem_callback {
+       char *command;
+       modem_cb_t function;
+       void *user_data;
+};
+
+struct modem_cmd {
+       char *cmd;
+       char *arg;
+       modem_cb_t callback;
+       void *user_data;
+};
+
+static int send_command(struct modem_data *modem, struct modem_cmd *cmd)
+{
+       char *buf;
+       int fd, err;
+
+       if (cmd->arg == NULL) {
+               DBG("AT%s", cmd->cmd);
+               buf = g_strdup_printf("AT%s\r\n", cmd->cmd);
+       } else {
+               DBG("AT%s=%s", cmd->cmd, cmd->arg);
+               buf = g_strdup_printf("AT%s=%s\r\n", cmd->cmd, cmd->arg);
+       }
+
+       fd = g_io_channel_unix_get_fd(modem->channel);
+       err = write(fd, buf, strlen(buf));
+
+       fsync(fd);
+
+       g_free(buf);
+
+       return err;
+}
+
+static int queue_command(struct modem_data *modem, struct modem_cmd *cmd)
+{
+       modem->commands = g_slist_append(modem->commands, cmd);
+
+       if (g_slist_length(modem->commands) > 1)
+               return 0;
+
+       return send_command(modem, cmd);
+}
+
+struct modem_data *modem_create(const char *device)
+{
+       struct modem_data *modem;
+
+       DBG("device %s", device);
+
+       modem = g_try_new0(struct modem_data, 1);
+       if (modem == NULL)
+               return NULL;
+
+       modem->device = g_strdup(device);
+
+       DBG("modem %p", modem);
+
+       return modem;
+}
+
+void modem_destroy(struct modem_data *modem)
+{
+       DBG("modem %p", modem);
+
+       if (modem == NULL)
+               return;
+
+       g_free(modem->device);
+       g_free(modem);
+}
+
+static gboolean modem_event(GIOChannel *channel,
+                               GIOCondition condition, gpointer user_data)
+{
+       struct modem_data *modem = user_data;
+       struct modem_cmd *cmd;
+       GSList *list;
+       gsize len;
+       GIOError err;
+
+       if (condition & (G_IO_NVAL | G_IO_HUP | G_IO_ERR))
+               return FALSE;
+
+       err = g_io_channel_read(channel, modem->buf + modem->offset,
+                               sizeof(modem->buf) - modem->offset, &len);
+       if (err) {
+               if (err == G_IO_ERROR_AGAIN)
+                       return TRUE;
+               return FALSE;
+       }
+
+       DBG("Read %zu bytes (offset %d)", len, modem->offset);
+
+       if (g_str_has_suffix(modem->buf, "\r\n") == TRUE) {
+               for (list = modem->callbacks; list; list = list->next) {
+                       struct modem_callback *callback = list->data;
+
+                       if (callback->function == NULL)
+                               continue;
+
+                       if (g_strrstr(modem->buf, callback->command) != NULL)
+                               callback->function(modem->buf,
+                                                       callback->user_data);
+               }
+       }
+
+       if (g_strrstr(modem->buf, "\r\nERROR\r\n") == NULL &&
+                               g_strrstr(modem->buf, "\r\nOK\r\n") == NULL) {
+               modem->offset += len;
+               return TRUE;
+       }
+
+       memset(modem->buf, 0, sizeof(modem->buf));
+       modem->offset = 0;
+
+       cmd = g_slist_nth_data(modem->commands, 0);
+       if (cmd == NULL)
+               return TRUE;
+
+       modem->commands = g_slist_remove(modem->commands, cmd);
+
+       DBG("AT%s", cmd->cmd);
+
+       if (cmd->callback)
+               cmd->callback(modem->buf, cmd->user_data);
+
+       g_free(cmd->arg);
+       g_free(cmd->cmd);
+       g_free(cmd);
+
+       cmd = g_slist_nth_data(modem->commands, 0);
+       if (cmd == NULL)
+               return TRUE;
+
+       send_command(modem, cmd);
+
+       return TRUE;
+}
+
+static int open_device(const char *device)
+{
+       struct termios ti;
+       int fd;
+
+       fd = open(device, O_RDWR | O_NOCTTY);
+       if (fd < 0)
+               return -1;
+
+       tcflush(fd, TCIOFLUSH);
+
+       /* Switch TTY to raw mode */
+       memset(&ti, 0, sizeof(ti));
+       cfmakeraw(&ti);
+
+       tcsetattr(fd, TCSANOW, &ti);
+
+       return fd;
+}
+
+int modem_open(struct modem_data *modem)
+{
+       int fd, try = 5;
+
+       DBG("modem %p", modem);
+
+       if (modem == NULL)
+               return -ENOENT;
+
+       while (try-- > 0) {
+               fd = open_device(modem->device);
+               if (fd < 0) {
+                       sleep(1);
+                       continue;
+               }
+               try = 0;
+       }
+
+       if (fd < 0) {
+               connman_error("Can't open %s device", modem->device);
+               return -EIO;
+       }
+
+       modem->channel = g_io_channel_unix_new(fd);
+       g_io_channel_set_close_on_unref(modem->channel, TRUE);
+
+       modem->watch = g_io_add_watch(modem->channel,
+                               G_IO_IN | G_IO_NVAL | G_IO_HUP | G_IO_ERR,
+                                                       modem_event, modem);
+
+       return 0;
+}
+
+int modem_close(struct modem_data *modem)
+{
+       DBG("modem %p", modem);
+
+       if (modem == NULL)
+               return -ENOENT;
+
+       g_source_remove(modem->watch);
+       modem->watch = 0;
+
+       g_io_channel_unref(modem->channel);
+       modem->channel = NULL;
+
+       return 0;
+}
+
+int modem_add_callback(struct modem_data *modem, const char *command,
+                                       modem_cb_t function, void *user_data)
+{
+       struct modem_callback *callback;
+
+       callback = g_try_new0(struct modem_callback, 1);
+       if (callback == NULL)
+               return -ENOMEM;
+
+       callback->command   = g_strdup(command);
+       callback->function  = function;
+       callback->user_data = user_data;
+
+       modem->callbacks = g_slist_append(modem->callbacks, callback);
+
+       return 0;
+}
+
+static int modem_command_valist(struct modem_data *modem, modem_cb_t callback,
+                                       void *user_data, const char *command,
+                                       const char *format, va_list var_args)
+{
+       struct modem_cmd *cmd;
+
+       cmd = g_try_new0(struct modem_cmd, 1);
+       if (cmd == NULL)
+               return -ENOMEM;
+
+       cmd->cmd = g_strdup(command);
+       if (format != NULL)
+               cmd->arg = g_strdup_vprintf(format, var_args);
+
+       cmd->callback  = callback;
+       cmd->user_data = user_data;
+
+       return queue_command(modem, cmd);
+}
+
+int modem_command(struct modem_data *modem,
+                               modem_cb_t callback, void *user_data,
+                               const char *command, const char *format, ...)
+{
+       va_list args;
+       int err;
+
+       DBG("modem %p", modem);
+
+       if (modem == NULL)
+               return -ENOENT;
+
+       va_start(args, format);
+       err = modem_command_valist(modem, callback, user_data,
+                                               command, format, args);
+       va_end(args);
+
+       return err;
+}
diff --git a/plugins/modem.h b/plugins/modem.h
new file mode 100644 (file)
index 0000000..f26425c
--- /dev/null
@@ -0,0 +1,39 @@
+/*
+ *
+ *  Connection Manager
+ *
+ *  Copyright (C) 2007-2009  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
+ *
+ */
+
+typedef struct modem_data modem_t;
+
+struct modem_data;
+
+struct modem_data *modem_create(const char *device);
+void modem_destroy(struct modem_data *modem);
+
+int modem_open(struct modem_data *modem);
+int modem_close(struct modem_data *modem);
+
+typedef void (* modem_cb_t) (const char *buf, void *user_data);
+
+int modem_add_callback(struct modem_data *modem, const char *command,
+                                       modem_cb_t function, void *user_data);
+
+int modem_command(struct modem_data *modem,
+                               modem_cb_t callback, void *user_data,
+                               const char *command, const char *format, ...);
diff --git a/plugins/modemmgr.c b/plugins/modemmgr.c
new file mode 100644 (file)
index 0000000..0950848
--- /dev/null
@@ -0,0 +1,115 @@
+/*
+ *
+ *  Connection Manager
+ *
+ *  Copyright (C) 2007-2009  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 <errno.h>
+
+#include <gdbus.h>
+
+#define CONNMAN_API_SUBJECT_TO_CHANGE
+#include <connman/plugin.h>
+#include <connman/dbus.h>
+#include <connman/log.h>
+
+#define MODEMMGR_SERVICE       "org.freedesktop.ModemManager"
+#define MODEMMGR_INTERFACE     MODEMMGR_SERVICE
+
+#define ENUMERATE_DEVICES      "EnumerateDevices"
+
+#define TIMEOUT 5000
+
+static void enumerate_devices_reply(DBusPendingCall *call, void *user_data)
+{
+       DBusMessage *reply;
+
+       DBG("");
+
+       reply = dbus_pending_call_steal_reply(call);
+
+       dbus_message_unref(reply);
+}
+
+static void modemmgr_connect(DBusConnection *connection, void *user_data)
+{
+       DBusMessage *message;
+       DBusPendingCall *call;
+
+       DBG("connection %p", connection);
+
+       message = dbus_message_new_method_call(MODEMMGR_SERVICE, "/",
+                               MODEMMGR_INTERFACE, ENUMERATE_DEVICES);
+       if (message == NULL)
+               return;
+
+       if (dbus_connection_send_with_reply(connection, message,
+                                               &call, TIMEOUT) == FALSE) {
+               connman_error("Failed to get modem devices");
+               goto done;
+       }
+
+       if (call == NULL) {
+               connman_error("D-Bus connection not available");
+               goto done;
+       }
+
+       dbus_pending_call_set_notify(call, enumerate_devices_reply,
+                                                       NULL, NULL);
+
+done:
+       dbus_message_unref(message);
+}
+
+static void modemmgr_disconnect(DBusConnection *connection, void *user_data)
+{
+       DBG("connection %p", connection);
+}
+
+static DBusConnection *connection;
+static guint watch;
+
+static int modemmgr_init(void)
+{
+       connection = connman_dbus_get_connection();
+       if (connection == NULL)
+               return -EIO;
+
+       watch = g_dbus_add_service_watch(connection, MODEMMGR_SERVICE,
+                       modemmgr_connect, modemmgr_disconnect, NULL, NULL);
+       if (watch == 0) {
+               dbus_connection_unref(connection);
+               return -EIO;
+       }
+
+       return 0;
+}
+
+static void modemmgr_exit(void)
+{
+       g_dbus_remove_watch(connection, watch);
+
+       dbus_connection_unref(connection);
+}
+
+CONNMAN_PLUGIN_DEFINE(modemmgr, "Modem Manager plugin", VERSION,
+               CONNMAN_PLUGIN_PRIORITY_DEFAULT, modemmgr_init, modemmgr_exit)
diff --git a/plugins/novatel.c b/plugins/novatel.c
new file mode 100644 (file)
index 0000000..4c0c9aa
--- /dev/null
@@ -0,0 +1,83 @@
+/*
+ *
+ *  Connection Manager
+ *
+ *  Copyright (C) 2007-2009  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 CONNMAN_API_SUBJECT_TO_CHANGE
+#include <connman/plugin.h>
+#include <connman/device.h>
+#include <connman/log.h>
+
+#include "modem.h"
+
+static int novatel_probe(struct connman_device *device)
+{
+       DBG("device %p", device);
+
+       return 0;
+}
+
+static void novatel_remove(struct connman_device *device)
+{
+       DBG("device %p", device);
+}
+
+static int novatel_enable(struct connman_device *device)
+{
+       DBG("device %p", device);
+
+       connman_device_set_powered(device, TRUE);
+
+       return 0;
+}
+
+static int novatel_disable(struct connman_device *device)
+{
+       DBG("device %p", device);
+
+       connman_device_set_powered(device, FALSE);
+
+       return 0;
+}
+
+static struct connman_device_driver novatel_driver = {
+       .name           = "novatel",
+       .type           = CONNMAN_DEVICE_TYPE_NOVATEL,
+       .probe          = novatel_probe,
+       .remove         = novatel_remove,
+       .enable         = novatel_enable,
+       .disable        = novatel_disable,
+};
+
+static int novatel_init(void)
+{
+       return connman_device_driver_register(&novatel_driver);
+}
+
+static void novatel_exit(void)
+{
+       connman_device_driver_unregister(&novatel_driver);
+}
+
+CONNMAN_PLUGIN_DEFINE(novatel, "Novatel Wireless device plugin", VERSION,
+               CONNMAN_PLUGIN_PRIORITY_DEFAULT, novatel_init, novatel_exit)
diff --git a/plugins/ofono.c b/plugins/ofono.c
new file mode 100644 (file)
index 0000000..2055c05
--- /dev/null
@@ -0,0 +1,39 @@
+/*
+ *
+ *  Connection Manager
+ *
+ *  Copyright (C) 2007-2009  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 CONNMAN_API_SUBJECT_TO_CHANGE
+#include <connman/plugin.h>
+
+static int ofono_init(void)
+{
+       return 0;
+}
+
+static void ofono_exit(void)
+{
+}
+
+CONNMAN_PLUGIN_DEFINE(ofono, "oFono telephony plugin", VERSION,
+               CONNMAN_PLUGIN_PRIORITY_DEFAULT, ofono_init, ofono_exit)
diff --git a/plugins/polkit.c b/plugins/polkit.c
new file mode 100644 (file)
index 0000000..16dd9d5
--- /dev/null
@@ -0,0 +1,179 @@
+/*
+ *
+ *  Connection Manager
+ *
+ *  Copyright (C) 2007-2009  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 <errno.h>
+
+#include <glib.h>
+#include <polkit-dbus/polkit-dbus.h>
+
+#define CONNMAN_API_SUBJECT_TO_CHANGE
+#include <connman/plugin.h>
+#include <connman/security.h>
+#include <connman/dbus.h>
+#include <connman/log.h>
+
+#define ACTION_MODIFY "org.moblin.connman.modify"
+#define ACTION_SECRET "org.moblin.connman.secret"
+
+static DBusConnection *connection;
+static PolKitContext *polkit_context;
+
+static int polkit_authorize(const char *sender,
+                               enum connman_security_privilege privilege)
+{
+       DBusError error;
+       PolKitCaller *caller;
+       PolKitAction *action;
+       PolKitResult result;
+       const char *id = NULL;
+
+       DBG("sender %s", sender);
+
+       switch (privilege) {
+       case CONNMAN_SECURITY_PRIVILEGE_PUBLIC:
+               return 0;
+       case CONNMAN_SECURITY_PRIVILEGE_MODIFY:
+               id = ACTION_MODIFY;
+               break;
+       case CONNMAN_SECURITY_PRIVILEGE_SECRET:
+               id = ACTION_SECRET;
+               break;
+       }
+
+       dbus_error_init(&error);
+
+       caller = polkit_caller_new_from_dbus_name(connection, sender, &error);
+       if (caller == NULL) {
+               if (dbus_error_is_set(&error) == TRUE) {
+                       connman_error("%s", error.message);
+                       dbus_error_free(&error);
+               } else
+                       connman_error("Failed to get caller information");
+               return -EIO;
+       }
+
+       action = polkit_action_new();
+       polkit_action_set_action_id(action, id);
+
+       result = polkit_context_is_caller_authorized(polkit_context,
+                                               action, caller, TRUE, NULL);
+
+       polkit_action_unref(action);
+       polkit_caller_unref(caller);
+
+       DBG("result %s", polkit_result_to_string_representation(result));
+
+       if (result == POLKIT_RESULT_NO)
+               return -EPERM;
+
+       return 0;
+}
+
+static struct connman_security polkit_security = {
+       .name                   = "polkit",
+       .authorize_sender       = polkit_authorize,
+};
+
+static gboolean watch_event(GIOChannel *channel, GIOCondition condition,
+                                                       gpointer user_data)
+{
+       PolKitContext *context = user_data;
+       int fd;
+
+       DBG("context %p", context);
+
+       fd = g_io_channel_unix_get_fd(channel);
+
+       polkit_context_io_func(context, fd);
+
+       return TRUE;
+}
+
+static int add_watch(PolKitContext *context, int fd)
+{
+       GIOChannel *channel;
+       guint id = 0;
+
+       DBG("context %p", context);
+
+       channel = g_io_channel_unix_new(fd);
+       if (channel == NULL)
+               return 0;
+
+       id = g_io_add_watch(channel, G_IO_IN, watch_event, context);
+
+       g_io_channel_unref(channel);
+
+       return id;
+}
+
+static void remove_watch(PolKitContext *context, int id)
+{
+       DBG("context %p", context);
+
+       g_source_remove(id);
+}
+
+static int polkit_init(void)
+{
+       int err;
+
+       connection = connman_dbus_get_connection();
+       if (connection == NULL)
+               return -EIO;
+
+       polkit_context = polkit_context_new();
+
+       polkit_context_set_io_watch_functions(polkit_context,
+                                               add_watch, remove_watch);
+
+       if (polkit_context_init(polkit_context, NULL) == FALSE) {
+               connman_error("Can't initialize PolicyKit");
+               polkit_context_unref(polkit_context);
+               dbus_connection_unref(connection);
+               return -EIO;
+       }
+
+       err = connman_security_register(&polkit_security);
+       if (err < 0) {
+               polkit_context_unref(polkit_context);
+               dbus_connection_unref(connection);
+               return err;
+       }
+
+       return 0;
+}
+
+static void polkit_exit(void)
+{
+       connman_security_unregister(&polkit_security);
+
+       polkit_context_unref(polkit_context);
+
+       dbus_connection_unref(connection);
+}
+
+CONNMAN_PLUGIN_DEFINE(polkit, "PolicyKit authorization plugin", VERSION,
+               CONNMAN_PLUGIN_PRIORITY_DEFAULT, polkit_init, polkit_exit)
diff --git a/plugins/polkit.policy b/plugins/polkit.policy
new file mode 100644 (file)
index 0000000..79d5c73
--- /dev/null
@@ -0,0 +1,29 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE policyconfig PUBLIC
+ "-//freedesktop//DTD PolicyKit Policy Configuration 1.0//EN"
+ "http://www.freedesktop.org/standards/PolicyKit/1.0/policyconfig.dtd">
+
+<policyconfig>
+
+  <vendor>Connection Manager</vendor>
+  <icon_name>network-wireless</icon_name>
+
+  <action id="org.moblin.connman.modify">
+    <description>Settings configuration</description>
+    <message>Policy prevents modification of settings</message>
+    <defaults>
+      <allow_inactive>no</allow_inactive>
+      <allow_active>auth_self_keep_session</allow_active>
+    </defaults>
+  </action>
+
+  <action id="org.moblin.connman.secret">
+    <description>Secrets configuration</description>
+    <message>Policy prevents modification of secrets</message>
+    <defaults>
+      <allow_inactive>no</allow_inactive>
+      <allow_active>auth_admin_keep_session</allow_active>
+    </defaults>
+  </action>
+
+</policyconfig>
diff --git a/plugins/pppd.c b/plugins/pppd.c
new file mode 100644 (file)
index 0000000..bd026b9
--- /dev/null
@@ -0,0 +1,61 @@
+/*
+ *
+ *  Connection Manager
+ *
+ *  Copyright (C) 2007-2009  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 CONNMAN_API_SUBJECT_TO_CHANGE
+#include <connman/plugin.h>
+#include <connman/driver.h>
+#include <connman/log.h>
+
+static int pppd_probe(struct connman_element *element)
+{
+       DBG("element %p name %s", element, element->name);
+
+       return -ENODEV;
+}
+
+static void pppd_remove(struct connman_element *element)
+{
+       DBG("element %p name %s", element, element->name);
+}
+
+static struct connman_driver pppd_driver = {
+       .name           = "pppd",
+       .type           = CONNMAN_ELEMENT_TYPE_PPP,
+       .probe          = pppd_probe,
+       .remove         = pppd_remove,
+};
+
+static int pppd_init(void)
+{
+       return connman_driver_register(&pppd_driver);
+}
+
+static void pppd_exit(void)
+{
+       connman_driver_unregister(&pppd_driver);
+}
+
+CONNMAN_PLUGIN_DEFINE(pppd, "Point-to-point protocol plugin", VERSION,
+               CONNMAN_PLUGIN_PRIORITY_DEFAULT, pppd_init, pppd_exit)
diff --git a/plugins/resolvconf.c b/plugins/resolvconf.c
new file mode 100644 (file)
index 0000000..df3a834
--- /dev/null
@@ -0,0 +1,97 @@
+/*
+ *
+ *  Connection Manager
+ *
+ *  Copyright (C) 2007-2009  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 <errno.h>
+#include <unistd.h>
+#include <stdlib.h>
+
+#define CONNMAN_API_SUBJECT_TO_CHANGE
+#include <connman/plugin.h>
+#include <connman/resolver.h>
+#include <connman/log.h>
+
+#include <glib.h>
+
+static int resolvconf_append(const char *interface, const char *domain,
+                                                       const char *server)
+{
+       char *cmd;
+       int err;
+
+       DBG("interface %s server %s", interface, server);
+
+       if (access(RESOLVCONF, X_OK) < 0)
+               return -errno;
+
+       cmd = g_strdup_printf("echo \"nameserver %s\" | %s -a %s",
+                                               server, RESOLVCONF, interface);
+
+       DBG("%s", cmd);
+
+       err = system(cmd);
+
+       g_free(cmd);
+
+       return err;
+}
+
+static int resolvconf_remove(const char *interface, const char *domain,
+                                                       const char *server)
+{
+       char *cmd;
+       int err;
+
+       DBG("interface %s server %s", interface, server);
+
+       cmd = g_strdup_printf("%s -d %s", RESOLVCONF, interface);
+
+       DBG("%s", cmd);
+
+       err = system(cmd);
+
+       g_free(cmd);
+
+       return err;
+}
+
+static struct connman_resolver resolvconf_resolver = {
+       .name           = "resolvconf",
+       .priority       = CONNMAN_RESOLVER_PRIORITY_DEFAULT,
+       .append         = resolvconf_append,
+       .remove         = resolvconf_remove,
+};
+
+static int resolvconf_init(void)
+{
+       return connman_resolver_register(&resolvconf_resolver);
+}
+
+static void resolvconf_exit(void)
+{
+       connman_resolver_unregister(&resolvconf_resolver);
+}
+
+CONNMAN_PLUGIN_DEFINE(resolvconf, "Name resolver plugin", VERSION,
+               CONNMAN_PLUGIN_PRIORITY_DEFAULT, resolvconf_init, resolvconf_exit)
diff --git a/plugins/supplicant.c b/plugins/supplicant.c
new file mode 100644 (file)
index 0000000..e9612f3
--- /dev/null
@@ -0,0 +1,1759 @@
+/*
+ *
+ *  Connection Manager
+ *
+ *  Copyright (C) 2007-2009  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 <errno.h>
+#include <stdlib.h>
+#include <string.h>
+#include <net/ethernet.h>
+
+#include <gdbus.h>
+
+#define CONNMAN_API_SUBJECT_TO_CHANGE
+#include <connman/device.h>
+#include <connman/option.h>
+#include <connman/inet.h>
+#include <connman/dbus.h>
+#include <connman/log.h>
+
+#include "supplicant.h"
+
+#define TIMEOUT 5000
+
+#define IEEE80211_CAP_ESS       0x0001
+#define IEEE80211_CAP_IBSS      0x0002
+#define IEEE80211_CAP_PRIVACY   0x0010
+
+#define SUPPLICANT_NAME  "fi.epitest.hostap.WPASupplicant"
+#define SUPPLICANT_INTF  "fi.epitest.hostap.WPASupplicant"
+#define SUPPLICANT_PATH  "/fi/epitest/hostap/WPASupplicant"
+
+/* Taken from "WPA Supplicant - Common definitions" */
+enum supplicant_state {
+       /**
+        * WPA_DISCONNECTED - Disconnected state
+        *
+        * This state indicates that client is not associated, but is likely to
+        * start looking for an access point. This state is entered when a
+        * connection is lost.
+        */
+       WPA_DISCONNECTED,
+
+       /**
+        * WPA_INACTIVE - Inactive state (wpa_supplicant disabled)
+        *
+        * This state is entered if there are no enabled networks in the
+        * configuration. wpa_supplicant is not trying to associate with a new
+        * network and external interaction (e.g., ctrl_iface call to add or
+        * enable a network) is needed to start association.
+        */
+       WPA_INACTIVE,
+
+       /**
+        * WPA_SCANNING - Scanning for a network
+        *
+        * This state is entered when wpa_supplicant starts scanning for a
+        * network.
+        */
+       WPA_SCANNING,
+
+       /**
+        * WPA_ASSOCIATING - Trying to associate with a BSS/SSID
+        *
+        * This state is entered when wpa_supplicant has found a suitable BSS
+        * to associate with and the driver is configured to try to associate
+        * with this BSS in ap_scan=1 mode. When using ap_scan=2 mode, this
+        * state is entered when the driver is configured to try to associate
+        * with a network using the configured SSID and security policy.
+        */
+       WPA_ASSOCIATING,
+
+       /**
+        * WPA_ASSOCIATED - Association completed
+        *
+        * This state is entered when the driver reports that association has
+        * been successfully completed with an AP. If IEEE 802.1X is used
+        * (with or without WPA/WPA2), wpa_supplicant remains in this state
+        * until the IEEE 802.1X/EAPOL authentication has been completed.
+        */
+       WPA_ASSOCIATED,
+
+       /**
+        * WPA_4WAY_HANDSHAKE - WPA 4-Way Key Handshake in progress
+        *
+        * This state is entered when WPA/WPA2 4-Way Handshake is started. In
+        * case of WPA-PSK, this happens when receiving the first EAPOL-Key
+        * frame after association. In case of WPA-EAP, this state is entered
+        * when the IEEE 802.1X/EAPOL authentication has been completed.
+        */
+       WPA_4WAY_HANDSHAKE,
+
+       /**
+        * WPA_GROUP_HANDSHAKE - WPA Group Key Handshake in progress
+        *
+        * This state is entered when 4-Way Key Handshake has been completed
+        * (i.e., when the supplicant sends out message 4/4) and when Group
+        * Key rekeying is started by the AP (i.e., when supplicant receives
+        * message 1/2).
+        */
+       WPA_GROUP_HANDSHAKE,
+
+       /**
+        * WPA_COMPLETED - All authentication completed
+        *
+        * This state is entered when the full authentication process is
+        * completed. In case of WPA2, this happens when the 4-Way Handshake is
+        * successfully completed. With WPA, this state is entered after the
+        * Group Key Handshake; with IEEE 802.1X (non-WPA) connection is
+        * completed after dynamic keys are received (or if not used, after
+        * the EAP authentication has been completed). With static WEP keys and
+        * plaintext connections, this state is entered when an association
+        * has been completed.
+        *
+        * This state indicates that the supplicant has completed its
+        * processing for the association phase and that data connection is
+        * fully configured.
+        */
+       WPA_COMPLETED,
+
+       /**
+        * WPA_INVALID - Invalid state (parsing error)
+        *
+        * This state is returned if the string input is invalid. It is not
+        * an official wpa_supplicant state.
+        */
+       WPA_INVALID,
+};
+
+struct supplicant_result {
+       char *path;
+       char *name;
+       unsigned char *addr;
+       unsigned int addr_len;
+       unsigned char *ssid;
+       unsigned int ssid_len;
+       dbus_uint16_t capabilities;
+       gboolean adhoc;
+       gboolean has_wep;
+       gboolean has_wpa;
+       gboolean has_rsn;
+       gboolean has_wps;
+       dbus_int32_t frequency;
+       dbus_int32_t quality;
+       dbus_int32_t noise;
+       dbus_int32_t level;
+       dbus_int32_t maxrate;
+};
+
+struct supplicant_task {
+       int ifindex;
+       char *ifname;
+       struct connman_device *device;
+       struct connman_network *network;
+       char *path;
+       char *netpath;
+       gboolean created;
+       enum supplicant_state state;
+       gboolean noscan;
+       GSList *scan_results;
+};
+
+static GSList *task_list = NULL;
+
+static DBusConnection *connection;
+
+static void free_task(struct supplicant_task *task)
+{
+       DBG("task %p", task);
+
+       g_free(task->ifname);
+       g_free(task->path);
+       g_free(task);
+}
+
+static struct supplicant_task *find_task_by_index(int index)
+{
+       GSList *list;
+
+       for (list = task_list; list; list = list->next) {
+               struct supplicant_task *task = list->data;
+
+               if (task->ifindex == index)
+                       return task;
+       }
+
+       return NULL;
+}
+
+static struct supplicant_task *find_task_by_path(const char *path)
+{
+       GSList *list;
+
+       for (list = task_list; list; list = list->next) {
+               struct supplicant_task *task = list->data;
+
+               if (g_strcmp0(task->path, path) == 0)
+                       return task;
+       }
+
+       return NULL;
+}
+
+static void add_interface_reply(DBusPendingCall *call, void *user_data)
+{
+       struct supplicant_task *task = user_data;
+       DBusMessage *reply;
+       DBusError error;
+       const char *path;
+
+       DBG("task %p", task);
+
+       reply = dbus_pending_call_steal_reply(call);
+       if (reply == NULL)
+               return;
+
+       if (dbus_message_get_type(reply) == DBUS_MESSAGE_TYPE_ERROR)
+               goto failed;
+
+       dbus_error_init(&error);
+
+       if (dbus_message_get_args(reply, &error, DBUS_TYPE_OBJECT_PATH, &path,
+                                               DBUS_TYPE_INVALID) == FALSE) {
+               if (dbus_error_is_set(&error) == TRUE) {
+                       connman_error("%s", error.message);
+                       dbus_error_free(&error);
+               } else
+                       connman_error("Wrong arguments for add interface");
+               goto failed;
+       }
+
+       DBG("path %s", path);
+
+       task->path = g_strdup(path);
+       task->created = TRUE;
+
+       connman_device_set_powered(task->device, TRUE);
+
+       dbus_message_unref(reply);
+
+       return;
+
+failed:
+       task_list = g_slist_remove(task_list, task);
+
+       connman_device_unref(task->device);
+
+       free_task(task);
+}
+
+static int add_interface(struct supplicant_task *task)
+{
+       const char *driver = connman_option_get_string("wifi");
+       DBusMessage *message;
+       DBusMessageIter array, dict;
+       DBusPendingCall *call;
+
+       DBG("task %p", task);
+
+       message = dbus_message_new_method_call(SUPPLICANT_NAME, SUPPLICANT_PATH,
+                                       SUPPLICANT_INTF, "addInterface");
+       if (message == NULL)
+               return -ENOMEM;
+
+       dbus_message_iter_init_append(message, &array);
+
+       dbus_message_iter_append_basic(&array,
+                                       DBUS_TYPE_STRING, &task->ifname);
+
+       dbus_message_iter_open_container(&array, 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);
+
+       connman_dbus_dict_append_variant(&dict, "driver",
+                                               DBUS_TYPE_STRING, &driver);
+
+       dbus_message_iter_close_container(&array, &dict);
+
+       if (dbus_connection_send_with_reply(connection, message,
+                                               &call, TIMEOUT) == FALSE) {
+               connman_error("Failed to add interface");
+               dbus_message_unref(message);
+               return -EIO;
+       }
+
+       if (call == NULL) {
+               connman_error("D-Bus connection not available");
+               dbus_message_unref(message);
+               return -EIO;
+       }
+
+       dbus_pending_call_set_notify(call, add_interface_reply, task, NULL);
+
+       dbus_message_unref(message);
+
+       return -EINPROGRESS;
+}
+
+static void get_interface_reply(DBusPendingCall *call, void *user_data)
+{
+       struct supplicant_task *task = user_data;
+       DBusMessage *reply;
+       DBusError error;
+       const char *path;
+
+       DBG("task %p", task);
+
+       reply = dbus_pending_call_steal_reply(call);
+       if (reply == NULL)
+               return;
+
+       if (dbus_message_get_type(reply) == DBUS_MESSAGE_TYPE_ERROR) {
+               add_interface(task);
+               goto done;
+       }
+
+       dbus_error_init(&error);
+
+       if (dbus_message_get_args(reply, &error, DBUS_TYPE_OBJECT_PATH, &path,
+                                               DBUS_TYPE_INVALID) == FALSE) {
+               if (dbus_error_is_set(&error) == TRUE) {
+                       connman_error("%s", error.message);
+                       dbus_error_free(&error);
+               } else
+                       connman_error("Wrong arguments for get interface");
+               goto done;
+       }
+
+       DBG("path %s", path);
+
+       task->path = g_strdup(path);
+       task->created = FALSE;
+
+       connman_device_set_powered(task->device, TRUE);
+
+done:
+       dbus_message_unref(reply);
+}
+
+static int create_interface(struct supplicant_task *task)
+{
+       DBusMessage *message;
+       DBusPendingCall *call;
+
+       DBG("task %p", task);
+
+       message = dbus_message_new_method_call(SUPPLICANT_NAME, SUPPLICANT_PATH,
+                                       SUPPLICANT_INTF, "getInterface");
+       if (message == NULL)
+               return -ENOMEM;
+
+       dbus_message_append_args(message, DBUS_TYPE_STRING, &task->ifname,
+                                                       DBUS_TYPE_INVALID);
+
+       if (dbus_connection_send_with_reply(connection, message,
+                                               &call, TIMEOUT) == FALSE) {
+               connman_error("Failed to get interface");
+               dbus_message_unref(message);
+               return -EIO;
+       }
+
+       if (call == NULL) {
+               connman_error("D-Bus connection not available");
+               dbus_message_unref(message);
+               return -EIO;
+       }
+
+       dbus_pending_call_set_notify(call, get_interface_reply, task, NULL);
+
+       dbus_message_unref(message);
+
+       return -EINPROGRESS;
+}
+
+static void remove_interface_reply(DBusPendingCall *call, void *user_data)
+{
+       struct supplicant_task *task = user_data;
+       DBusMessage *reply;
+
+       DBG("task %p", task);
+
+       reply = dbus_pending_call_steal_reply(call);
+
+       connman_device_set_powered(task->device, FALSE);
+
+       connman_device_unref(task->device);
+
+       connman_inet_ifdown(task->ifindex);
+
+       free_task(task);
+
+       dbus_message_unref(reply);
+}
+
+static int remove_interface(struct supplicant_task *task)
+{
+       DBusMessage *message;
+       DBusPendingCall *call;
+
+       DBG("task %p", task);
+
+       if (task->created == FALSE) {
+               connman_device_set_powered(task->device, FALSE);
+               return 0;
+       }
+
+       message = dbus_message_new_method_call(SUPPLICANT_NAME, SUPPLICANT_PATH,
+                                       SUPPLICANT_INTF, "removeInterface");
+       if (message == NULL)
+               return -ENOMEM;
+
+       dbus_message_append_args(message, DBUS_TYPE_OBJECT_PATH, &task->path,
+                                                       DBUS_TYPE_INVALID);
+
+       if (dbus_connection_send_with_reply(connection, message,
+                                               &call, TIMEOUT) == FALSE) {
+               connman_error("Failed to remove interface");
+               dbus_message_unref(message);
+               return -EIO;
+       }
+
+       if (call == NULL) {
+               connman_error("D-Bus connection not available");
+               dbus_message_unref(message);
+               return -EIO;
+       }
+
+       dbus_pending_call_set_notify(call, remove_interface_reply, task, NULL);
+
+       dbus_message_unref(message);
+
+       return -EINPROGRESS;
+}
+
+#if 0
+static int set_ap_scan(struct supplicant_task *task)
+{
+       DBusMessage *message, *reply;
+       DBusError error;
+       guint32 ap_scan = 1;
+
+       DBG("task %p", task);
+
+       message = dbus_message_new_method_call(SUPPLICANT_NAME, task->path,
+                               SUPPLICANT_INTF ".Interface", "setAPScan");
+       if (message == NULL)
+               return -ENOMEM;
+
+       dbus_message_append_args(message, DBUS_TYPE_UINT32, &ap_scan,
+                                                       DBUS_TYPE_INVALID);
+
+       dbus_error_init(&error);
+
+       reply = dbus_connection_send_with_reply_and_block(connection,
+                                                       message, -1, &error);
+       if (reply == NULL) {
+               if (dbus_error_is_set(&error) == TRUE) {
+                       connman_error("%s", error.message);
+                       dbus_error_free(&error);
+               } else
+                       connman_error("Failed to set AP scan");
+               dbus_message_unref(message);
+               return -EIO;
+       }
+
+       dbus_message_unref(message);
+
+       dbus_message_unref(reply);
+
+       return 0;
+}
+#endif
+
+static int add_network(struct supplicant_task *task)
+{
+       DBusMessage *message, *reply;
+       DBusError error;
+       const char *path;
+
+       DBG("task %p", task);
+
+       if (task->netpath != NULL)
+               return -EALREADY;
+
+       message = dbus_message_new_method_call(SUPPLICANT_NAME, task->path,
+                               SUPPLICANT_INTF ".Interface", "addNetwork");
+       if (message == NULL)
+               return -ENOMEM;
+
+       dbus_error_init(&error);
+
+       reply = dbus_connection_send_with_reply_and_block(connection,
+                                                       message, -1, &error);
+       if (reply == NULL) {
+               if (dbus_error_is_set(&error) == TRUE) {
+                       connman_error("%s", error.message);
+                       dbus_error_free(&error);
+               } else
+                       connman_error("Failed to add network");
+               dbus_message_unref(message);
+               return -EIO;
+       }
+
+       dbus_message_unref(message);
+
+       dbus_error_init(&error);
+
+       if (dbus_message_get_args(reply, &error, DBUS_TYPE_OBJECT_PATH, &path,
+                                               DBUS_TYPE_INVALID) == FALSE) {
+               if (dbus_error_is_set(&error) == TRUE) {
+                       connman_error("%s", error.message);
+                       dbus_error_free(&error);
+               } else
+                       connman_error("Wrong arguments for network");
+               dbus_message_unref(reply);
+               return -EIO;
+       }
+
+       DBG("path %s", path);
+
+       task->netpath = g_strdup(path);
+
+       dbus_message_unref(reply);
+
+       return 0;
+}
+
+static int remove_network(struct supplicant_task *task)
+{
+       DBusMessage *message, *reply;
+       DBusError error;
+
+       DBG("task %p", task);
+
+       if (task->netpath == NULL)
+               return -EINVAL;
+
+       message = dbus_message_new_method_call(SUPPLICANT_NAME, task->path,
+                               SUPPLICANT_INTF ".Interface", "removeNetwork");
+       if (message == NULL)
+               return -ENOMEM;
+
+       dbus_message_append_args(message, DBUS_TYPE_OBJECT_PATH, &task->netpath,
+                                                       DBUS_TYPE_INVALID);
+
+       dbus_error_init(&error);
+
+       reply = dbus_connection_send_with_reply_and_block(connection,
+                                                       message, -1, &error);
+       if (reply == NULL) {
+               if (dbus_error_is_set(&error) == TRUE) {
+                       connman_error("%s", error.message);
+                       dbus_error_free(&error);
+               } else
+                       connman_error("Failed to remove network");
+               dbus_message_unref(message);
+               return -EIO;
+       }
+
+       dbus_message_unref(message);
+
+       dbus_message_unref(reply);
+
+       g_free(task->netpath);
+       task->netpath = NULL;
+
+       return 0;
+}
+
+static int select_network(struct supplicant_task *task)
+{
+       DBusMessage *message, *reply;
+       DBusError error;
+
+       DBG("task %p", task);
+
+       if (task->netpath == NULL)
+               return -EINVAL;
+
+       message = dbus_message_new_method_call(SUPPLICANT_NAME, task->path,
+                               SUPPLICANT_INTF ".Interface", "selectNetwork");
+       if (message == NULL)
+               return -ENOMEM;
+
+       dbus_message_append_args(message, DBUS_TYPE_OBJECT_PATH, &task->netpath,
+                                                       DBUS_TYPE_INVALID);
+
+       dbus_error_init(&error);
+
+       reply = dbus_connection_send_with_reply_and_block(connection,
+                                                       message, -1, &error);
+       if (reply == NULL) {
+               if (dbus_error_is_set(&error) == TRUE) {
+                       connman_error("%s", error.message);
+                       dbus_error_free(&error);
+               } else
+                       connman_error("Failed to select network");
+               dbus_message_unref(message);
+               return -EIO;
+       }
+
+       dbus_message_unref(message);
+
+       dbus_message_unref(reply);
+
+       return 0;
+}
+
+static int enable_network(struct supplicant_task *task)
+{
+       DBusMessage *message, *reply;
+       DBusError error;
+
+       DBG("task %p", task);
+
+       if (task->netpath == NULL)
+               return -EINVAL;
+
+       message = dbus_message_new_method_call(SUPPLICANT_NAME, task->netpath,
+                                       SUPPLICANT_INTF ".Network", "enable");
+       if (message == NULL)
+               return -ENOMEM;
+
+       dbus_error_init(&error);
+
+       reply = dbus_connection_send_with_reply_and_block(connection,
+                                                       message, -1, &error);
+       if (reply == NULL) {
+               if (dbus_error_is_set(&error) == TRUE) {
+                       connman_error("%s", error.message);
+                       dbus_error_free(&error);
+               } else
+                       connman_error("Failed to enable network");
+               dbus_message_unref(message);
+               return -EIO;
+       }
+
+       dbus_message_unref(message);
+
+       dbus_message_unref(reply);
+
+       return 0;
+}
+
+static int disable_network(struct supplicant_task *task)
+{
+       DBusMessage *message, *reply;
+       DBusError error;
+
+       DBG("task %p", task);
+
+       if (task->netpath == NULL)
+               return -EINVAL;
+
+       message = dbus_message_new_method_call(SUPPLICANT_NAME, task->netpath,
+                                       SUPPLICANT_INTF ".Network", "disable");
+       if (message == NULL)
+               return -ENOMEM;
+
+       dbus_error_init(&error);
+
+       reply = dbus_connection_send_with_reply_and_block(connection,
+                                                       message, -1, &error);
+       if (reply == NULL) {
+               if (dbus_error_is_set(&error) == TRUE) {
+                       connman_error("%s", error.message);
+                       dbus_error_free(&error);
+               } else
+                       connman_error("Failed to disable network");
+               dbus_message_unref(message);
+               return -EIO;
+       }
+
+       dbus_message_unref(message);
+
+       dbus_message_unref(reply);
+
+       return 0;
+}
+
+static int set_network(struct supplicant_task *task,
+                               const unsigned char *network, int len,
+                               const char *address, const char *security,
+                                                       const char *passphrase)
+{
+       DBusMessage *message, *reply;
+       DBusMessageIter array, dict;
+       DBusError error;
+
+       DBG("task %p", task);
+
+       if (task->netpath == NULL)
+               return -EINVAL;
+
+       message = dbus_message_new_method_call(SUPPLICANT_NAME, task->netpath,
+                                       SUPPLICANT_INTF ".Network", "set");
+       if (message == NULL)
+               return -ENOMEM;
+
+       dbus_message_iter_init_append(message, &array);
+
+       dbus_message_iter_open_container(&array, 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);
+
+       if (address == NULL) {
+               dbus_uint32_t scan_ssid = 1;
+               connman_dbus_dict_append_variant(&dict, "scan_ssid",
+                                               DBUS_TYPE_UINT32, &scan_ssid);
+       } else
+               connman_dbus_dict_append_variant(&dict, "bssid",
+                                               DBUS_TYPE_STRING, &address);
+
+       connman_dbus_dict_append_array(&dict, "ssid",
+                                       DBUS_TYPE_BYTE, &network, len);
+
+       if (g_ascii_strcasecmp(security, "wpa") == 0 ||
+                               g_ascii_strcasecmp(security, "rsn") == 0) {
+               const char *key_mgmt = "WPA-PSK";
+               connman_dbus_dict_append_variant(&dict, "key_mgmt",
+                                               DBUS_TYPE_STRING, &key_mgmt);
+
+               if (passphrase && strlen(passphrase) > 0)
+                       connman_dbus_dict_append_variant(&dict, "psk",
+                                               DBUS_TYPE_STRING, &passphrase);
+       } else if (g_ascii_strcasecmp(security, "wep") == 0) {
+               const char *key_mgmt = "NONE", *index = "0";
+               connman_dbus_dict_append_variant(&dict, "key_mgmt",
+                                               DBUS_TYPE_STRING, &key_mgmt);
+
+               if (passphrase) {
+                       int size = strlen(passphrase);
+                       if (size == 10 || size == 26) {
+                               unsigned char *key = malloc(13);
+                               char tmp[3];
+                               int i;
+                               memset(tmp, 0, sizeof(tmp));
+                               if (key == NULL)
+                                       size = 0;
+                               for (i = 0; i < size / 2; i++) {
+                                       memcpy(tmp, passphrase + (i * 2), 2);
+                                       key[i] = (unsigned char) strtol(tmp,
+                                                               NULL, 16);
+                               }
+                               connman_dbus_dict_append_array(&dict,
+                                               "wep_key0", DBUS_TYPE_BYTE,
+                                                       &key, size / 2);
+                               free(key);
+                       } else
+                               connman_dbus_dict_append_variant(&dict,
+                                               "wep_key0", DBUS_TYPE_STRING,
+                                                               &passphrase);
+                       connman_dbus_dict_append_variant(&dict, "wep_tx_keyidx",
+                                               DBUS_TYPE_STRING, &index);
+               }
+       } else {
+               const char *key_mgmt = "NONE";
+               connman_dbus_dict_append_variant(&dict, "key_mgmt",
+                                               DBUS_TYPE_STRING, &key_mgmt);
+       }
+
+       dbus_message_iter_close_container(&array, &dict);
+
+       dbus_error_init(&error);
+
+       reply = dbus_connection_send_with_reply_and_block(connection,
+                                                       message, -1, &error);
+       if (reply == NULL) {
+               if (dbus_error_is_set(&error) == TRUE) {
+                       connman_error("%s", error.message);
+                       dbus_error_free(&error);
+               } else
+                       connman_error("Failed to set network options");
+               dbus_message_unref(message);
+               return -EIO;
+       }
+
+       dbus_message_unref(message);
+
+       dbus_message_unref(reply);
+
+       return 0;
+}
+
+static int initiate_scan(struct supplicant_task *task)
+{
+       DBusMessage *message;
+       DBusPendingCall *call;
+
+       DBG("task %p", task);
+
+       message = dbus_message_new_method_call(SUPPLICANT_NAME, task->path,
+                                       SUPPLICANT_INTF ".Interface", "scan");
+       if (message == NULL)
+               return -ENOMEM;
+
+       if (dbus_connection_send_with_reply(connection, message,
+                                               &call, TIMEOUT) == FALSE) {
+               connman_error("Failed to initiate scan");
+               dbus_message_unref(message);
+               return -EIO;
+       }
+
+       dbus_message_unref(message);
+
+       return 0;
+}
+
+static struct {
+       char *name;
+       char *value;
+} special_ssid[] = {
+       { "<hidden>", "hidden"  },
+       { "default",  "linksys" },
+       { "wireless"  },
+       { "linksys"   },
+       { "netgear"   },
+       { "dlink"     },
+       { "2wire"     },
+       { "compaq"    },
+       { "tsunami"   },
+       { "comcomcom", "3com"     },
+       { "3Com",      "3com"     },
+       { "Symbol",    "symbol"   },
+       { "Motorola",  "motorola" },
+       { "Wireless" , "wireless" },
+       { "WLAN",      "wlan"     },
+       { }
+};
+
+static char *build_group(const char *addr, const char *name,
+                       const unsigned char *ssid, unsigned int ssid_len,
+                                       const char *mode, const char *security)
+{
+       GString *str;
+       unsigned int i;
+
+       if (addr == NULL)
+               return NULL;
+
+       str = g_string_sized_new((ssid_len * 2) + 24);
+       if (str == NULL)
+               return NULL;
+
+       for (i = 0; special_ssid[i].name; i++) {
+               if (g_strcmp0(special_ssid[i].name, name) == 0) {
+                       if (special_ssid[i].value == NULL)
+                               g_string_append_printf(str, "%s_%s",
+                                                               name, addr);
+                       else
+                               g_string_append_printf(str, "%s_%s",
+                                               special_ssid[i].value, addr);
+                       goto done;
+               }
+       }
+
+       if (ssid_len > 0 && ssid[0] != '\0') {
+               for (i = 0; i < ssid_len; i++)
+                       g_string_append_printf(str, "%02x", ssid[i]);
+       } else
+               g_string_append_printf(str, "hidden_%s", addr);
+
+done:
+       g_string_append_printf(str, "_%s_%s", mode, security);
+
+       return g_string_free(str, FALSE);
+}
+
+static void extract_addr(DBusMessageIter *value,
+                                       struct supplicant_result *result)
+{
+       DBusMessageIter array;
+       struct ether_addr *eth;
+       unsigned char *addr;
+       int addr_len;
+
+       dbus_message_iter_recurse(value, &array);
+       dbus_message_iter_get_fixed_array(&array, &addr, &addr_len);
+
+       if (addr_len != 6)
+               return;
+
+       result->addr = g_try_malloc(addr_len);
+       if (result->addr == NULL)
+               return;
+
+       memcpy(result->addr, addr, addr_len);
+       result->addr_len = addr_len;
+
+       result->path = g_try_malloc0(13);
+       if (result->path == NULL)
+               return;
+
+       eth = (void *) addr;
+
+       snprintf(result->path, 13, "%02x%02x%02x%02x%02x%02x",
+                                               eth->ether_addr_octet[0],
+                                               eth->ether_addr_octet[1],
+                                               eth->ether_addr_octet[2],
+                                               eth->ether_addr_octet[3],
+                                               eth->ether_addr_octet[4],
+                                               eth->ether_addr_octet[5]);
+}
+
+static void extract_ssid(DBusMessageIter *value,
+                                       struct supplicant_result *result)
+{
+       DBusMessageIter array;
+       unsigned char *ssid;
+       int ssid_len;
+
+       dbus_message_iter_recurse(value, &array);
+       dbus_message_iter_get_fixed_array(&array, &ssid, &ssid_len);
+
+       if (ssid_len < 1)
+               return;
+
+       result->ssid = g_try_malloc(ssid_len);
+       if (result->ssid == NULL)
+               return;
+
+       memcpy(result->ssid, ssid, ssid_len);
+       result->ssid_len = ssid_len;
+
+       result->name = g_try_malloc0(ssid_len + 1);
+       if (result->name == NULL)
+               return;
+
+       memcpy(result->name, ssid, ssid_len);
+}
+
+static void extract_wpaie(DBusMessageIter *value,
+                                       struct supplicant_result *result)
+{
+       DBusMessageIter array;
+       unsigned char *ie;
+       int ie_len;
+
+       dbus_message_iter_recurse(value, &array);
+       dbus_message_iter_get_fixed_array(&array, &ie, &ie_len);
+
+       if (ie_len > 0)
+               result->has_wpa = TRUE;
+}
+
+static void extract_rsnie(DBusMessageIter *value,
+                                       struct supplicant_result *result)
+{
+       DBusMessageIter array;
+       unsigned char *ie;
+       int ie_len;
+
+       dbus_message_iter_recurse(value, &array);
+       dbus_message_iter_get_fixed_array(&array, &ie, &ie_len);
+
+       if (ie_len > 0)
+               result->has_rsn = TRUE;
+}
+
+static void extract_wpsie(DBusMessageIter *value,
+                                       struct supplicant_result *result)
+{
+       DBusMessageIter array;
+       unsigned char *ie;
+       int ie_len;
+
+       dbus_message_iter_recurse(value, &array);
+       dbus_message_iter_get_fixed_array(&array, &ie, &ie_len);
+
+       if (ie_len > 0)
+               result->has_wps = TRUE;
+}
+
+static void extract_capabilites(DBusMessageIter *value,
+                                       struct supplicant_result *result)
+{
+       dbus_message_iter_get_basic(value, &result->capabilities);
+
+       if (result->capabilities & IEEE80211_CAP_ESS)
+               result->adhoc = FALSE;
+       else if (result->capabilities & IEEE80211_CAP_IBSS)
+               result->adhoc = TRUE;
+
+       if (result->capabilities & IEEE80211_CAP_PRIVACY)
+               result->has_wep = TRUE;
+}
+
+static unsigned char calculate_strength(struct supplicant_result *result)
+{
+       if (result->quality < 0) {
+               unsigned char strength;
+
+               if (result->level > 0)
+                       strength = 100 - result->level;
+               else
+                       strength = 120 + result->level;
+
+               if (strength > 100)
+                       strength = 100;
+
+               return strength;
+       }
+
+       return result->quality;
+}
+
+static unsigned short calculate_channel(struct supplicant_result *result)
+{
+       if (result->frequency < 0)
+               return 0;
+
+       return (result->frequency - 2407) / 5;
+}
+
+static void get_properties(struct supplicant_task *task);
+
+static void properties_reply(DBusPendingCall *call, void *user_data)
+{
+       struct supplicant_task *task = user_data;
+       struct supplicant_result result;
+       struct connman_network *network;
+       DBusMessage *reply;
+       DBusMessageIter array, dict;
+       unsigned char strength;
+       unsigned short channel, frequency;
+       const char *mode, *security;
+       char *group;
+
+       DBG("task %p", task);
+
+       reply = dbus_pending_call_steal_reply(call);
+       if (reply == NULL) {
+               get_properties(task);
+               return;
+       }
+
+       if (dbus_message_get_type(reply) == DBUS_MESSAGE_TYPE_ERROR) {
+               dbus_message_unref(reply);
+               get_properties(task);
+               return;
+       }
+
+       memset(&result, 0, sizeof(result));
+       result.frequency = -1;
+       result.quality = -1;
+       result.level = 0;
+       result.noise = 0;
+
+       dbus_message_iter_init(reply, &array);
+
+       dbus_message_iter_recurse(&array, &dict);
+
+       while (dbus_message_iter_get_arg_type(&dict) == DBUS_TYPE_DICT_ENTRY) {
+               DBusMessageIter entry, value;
+               const char *key;
+
+               dbus_message_iter_recurse(&dict, &entry);
+               dbus_message_iter_get_basic(&entry, &key);
+
+               dbus_message_iter_next(&entry);
+
+               dbus_message_iter_recurse(&entry, &value);
+
+               //type = dbus_message_iter_get_arg_type(&value);
+               //dbus_message_iter_get_basic(&value, &val);
+
+               /* 
+                * bssid        : a (97)
+                * ssid         : a (97)
+                * wpaie        : a (97)
+                * rsnie        : a (97)
+                * wpsie        : a (97)
+                * frequency    : i (105)
+                * capabilities : q (113)
+                * quality      : i (105)
+                * noise        : i (105)
+                * level        : i (105)
+                * maxrate      : i (105)
+                */
+
+               if (g_str_equal(key, "bssid") == TRUE)
+                       extract_addr(&value, &result);
+               else if (g_str_equal(key, "ssid") == TRUE)
+                       extract_ssid(&value, &result);
+               else if (g_str_equal(key, "wpaie") == TRUE)
+                       extract_wpaie(&value, &result);
+               else if (g_str_equal(key, "rsnie") == TRUE)
+                       extract_rsnie(&value, &result);
+               else if (g_str_equal(key, "wpsie") == TRUE)
+                       extract_wpsie(&value, &result);
+               else if (g_str_equal(key, "capabilities") == TRUE)
+                       extract_capabilites(&value, &result);
+               else if (g_str_equal(key, "frequency") == TRUE)
+                       dbus_message_iter_get_basic(&value, &result.frequency);
+               else if (g_str_equal(key, "quality") == TRUE)
+                       dbus_message_iter_get_basic(&value, &result.quality);
+               else if (g_str_equal(key, "noise") == TRUE)
+                       dbus_message_iter_get_basic(&value, &result.noise);
+               else if (g_str_equal(key, "level") == TRUE)
+                       dbus_message_iter_get_basic(&value, &result.level);
+               else if (g_str_equal(key, "maxrate") == TRUE)
+                       dbus_message_iter_get_basic(&value, &result.maxrate);
+
+               dbus_message_iter_next(&dict);
+       }
+
+       if (result.path == NULL)
+               goto done;
+
+       if (result.path[0] == '\0')
+               goto done;
+
+       if (result.frequency > 0 && result.frequency < 14)
+               result.frequency = 2407 + (5 * result.frequency);
+       else if (result.frequency == 14)
+               result.frequency = 2484;
+
+       strength = calculate_strength(&result);
+       channel  = calculate_channel(&result);
+
+       frequency = (result.frequency < 0) ? 0 : result.frequency;
+
+       if (result.has_rsn == TRUE)
+               security = "rsn";
+       else if (result.has_wpa == TRUE)
+               security = "wpa";
+       else if (result.has_wep == TRUE)
+               security = "wep";
+       else
+               security = "none";
+
+       mode = (result.adhoc == TRUE) ? "adhoc" : "managed";
+
+       group = build_group(result.path, result.name,
+                                       result.ssid, result.ssid_len,
+                                                       mode, security);
+
+       network = connman_device_get_network(task->device, result.path);
+       if (network == NULL) {
+               int index;
+
+               network = connman_network_create(result.path,
+                                               CONNMAN_NETWORK_TYPE_WIFI);
+               if (network == NULL)
+                       goto done;
+
+               index = connman_device_get_index(task->device);
+               connman_network_set_index(network, index);
+
+               connman_network_set_protocol(network,
+                                               CONNMAN_NETWORK_PROTOCOL_IP);
+
+               connman_network_set_address(network, result.addr,
+                                                       result.addr_len);
+
+               if (connman_device_add_network(task->device, network) < 0) {
+                       connman_network_unref(network);
+                       goto done;
+               }
+       }
+
+       if (result.name != NULL && result.name[0] != '\0')
+               connman_network_set_name(network, result.name);
+
+       connman_network_set_blob(network, "WiFi.SSID",
+                                               result.ssid, result.ssid_len);
+
+       connman_network_set_string(network, "WiFi.Mode", mode);
+
+       DBG("%s (%s %s) strength %d (%s)",
+                               result.name, mode, security, strength,
+                               (result.has_wps == TRUE) ? "WPS" : "no WPS");
+
+       connman_network_set_available(network, TRUE);
+       connman_network_set_strength(network, strength);
+
+       connman_network_set_uint16(network, "Frequency", frequency);
+       connman_network_set_uint16(network, "WiFi.Channel", channel);
+       connman_network_set_string(network, "WiFi.Security", security);
+
+       connman_network_set_group(network, group);
+
+       g_free(group);
+
+done:
+       g_free(result.path);
+       g_free(result.addr);
+       g_free(result.name);
+       g_free(result.ssid);
+
+       dbus_message_unref(reply);
+
+       get_properties(task);
+}
+
+static void get_properties(struct supplicant_task *task)
+{
+       DBusMessage *message;
+       DBusPendingCall *call;
+       char *path;
+
+       path = g_slist_nth_data(task->scan_results, 0);
+       if (path == NULL)
+               goto noscan;
+
+       message = dbus_message_new_method_call(SUPPLICANT_NAME, path,
+                                               SUPPLICANT_INTF ".BSSID",
+                                                               "properties");
+
+       task->scan_results = g_slist_remove(task->scan_results, path);
+       g_free(path);
+
+       if (message == NULL)
+               goto noscan;
+
+       if (dbus_connection_send_with_reply(connection, message,
+                                               &call, TIMEOUT) == FALSE) {
+               connman_error("Failed to get network properties");
+               dbus_message_unref(message);
+               goto noscan;
+       }
+
+       if (call == NULL) {
+               connman_error("D-Bus connection not available");
+               dbus_message_unref(message);
+               goto noscan;
+       }
+
+       dbus_pending_call_set_notify(call, properties_reply, task, NULL);
+
+       dbus_message_unref(message);
+
+       return;
+
+noscan:
+       if (task->noscan == FALSE)
+               connman_device_set_scanning(task->device, FALSE);
+}
+
+static void scan_results_reply(DBusPendingCall *call, void *user_data)
+{
+       struct supplicant_task *task = user_data;
+       DBusMessage *reply;
+       DBusError error;
+       char **results;
+       int i, num_results;
+
+       DBG("task %p", task);
+
+       reply = dbus_pending_call_steal_reply(call);
+       if (reply == NULL)
+               goto noscan;
+
+       if (dbus_message_get_type(reply) == DBUS_MESSAGE_TYPE_ERROR)
+               goto done;
+
+       dbus_error_init(&error);
+
+       if (dbus_message_get_args(reply, &error,
+                               DBUS_TYPE_ARRAY, DBUS_TYPE_OBJECT_PATH,
+                                               &results, &num_results,
+                                               DBUS_TYPE_INVALID) == FALSE) {
+               if (dbus_error_is_set(&error) == TRUE) {
+                       connman_error("%s", error.message);
+                       dbus_error_free(&error);
+               } else
+                       connman_error("Wrong arguments for scan result");
+               goto done;
+       }
+
+       if (num_results == 0)
+               goto done;
+
+       for (i = 0; i < num_results; i++) {
+               char *path = g_strdup(results[i]);
+               if (path == NULL)
+                       continue;
+
+               task->scan_results = g_slist_append(task->scan_results, path);
+       }
+
+       g_strfreev(results);
+
+       dbus_message_unref(reply);
+
+       get_properties(task);
+
+       return;
+
+done:
+       dbus_message_unref(reply);
+
+noscan:
+       if (task->noscan == FALSE)
+               connman_device_set_scanning(task->device, FALSE);
+}
+
+static void scan_results_available(struct supplicant_task *task)
+{
+       DBusMessage *message;
+       DBusPendingCall *call;
+
+       DBG("task %p", task);
+
+       message = dbus_message_new_method_call(SUPPLICANT_NAME, task->path,
+                                               SUPPLICANT_INTF ".Interface",
+                                                       "scanResults");
+       if (message == NULL)
+               return;
+
+       if (dbus_connection_send_with_reply(connection, message,
+                                               &call, TIMEOUT) == FALSE) {
+               connman_error("Failed to request scan result");
+               goto done;
+       }
+
+       if (task->noscan == FALSE)
+               connman_device_set_scanning(task->device, TRUE);
+
+       if (call == NULL) {
+               connman_error("D-Bus connection not available");
+               goto done;
+       }
+
+       dbus_pending_call_set_notify(call, scan_results_reply, task, NULL);
+
+done:
+       dbus_message_unref(message);
+}
+
+static enum supplicant_state string2state(const char *state)
+{
+       if (g_str_equal(state, "INACTIVE") == TRUE)
+               return WPA_INACTIVE;
+       else if (g_str_equal(state, "SCANNING") == TRUE)
+               return WPA_SCANNING;
+       else if (g_str_equal(state, "ASSOCIATING") == TRUE)
+               return WPA_ASSOCIATING;
+       else if (g_str_equal(state, "ASSOCIATED") == TRUE)
+               return WPA_ASSOCIATED;
+       else if (g_str_equal(state, "GROUP_HANDSHAKE") == TRUE)
+               return WPA_GROUP_HANDSHAKE;
+       else if (g_str_equal(state, "4WAY_HANDSHAKE") == TRUE)
+               return WPA_4WAY_HANDSHAKE;
+       else if (g_str_equal(state, "COMPLETED") == TRUE)
+               return WPA_COMPLETED;
+       else if (g_str_equal(state, "DISCONNECTED") == TRUE)
+               return WPA_DISCONNECTED;
+       else
+               return WPA_INVALID;
+}
+
+static void state_change(struct supplicant_task *task, DBusMessage *msg)
+{
+       DBusError error;
+       const char *newstate, *oldstate;
+       enum supplicant_state state;
+
+       dbus_error_init(&error);
+
+       if (dbus_message_get_args(msg, &error, DBUS_TYPE_STRING, &newstate,
+                                               DBUS_TYPE_STRING, &oldstate,
+                                               DBUS_TYPE_INVALID) == FALSE) {
+               if (dbus_error_is_set(&error) == TRUE) {
+                       connman_error("%s", error.message);
+                       dbus_error_free(&error);
+               } else
+                       connman_error("Wrong arguments for state change");
+               return;
+       }
+
+       DBG("state %s ==> %s", oldstate, newstate);
+
+       state = string2state(newstate);
+       if (state == WPA_INVALID)
+               return;
+
+       task->state = state;
+
+       switch (task->state) {
+       case WPA_SCANNING:
+               task->noscan = TRUE;
+               connman_device_set_scanning(task->device, TRUE);
+               break;
+       case WPA_ASSOCIATING:
+       case WPA_ASSOCIATED:
+       case WPA_4WAY_HANDSHAKE:
+       case WPA_GROUP_HANDSHAKE:
+               task->noscan = TRUE;
+               break;
+       case WPA_COMPLETED:
+       case WPA_DISCONNECTED:
+               task->noscan = FALSE;
+               break;
+       case WPA_INACTIVE:
+               task->noscan = FALSE;
+               connman_device_set_scanning(task->device, FALSE);
+               break;
+       case WPA_INVALID:
+               break;
+       }
+
+       if (task->network == NULL)
+               return;
+
+       switch (task->state) {
+       case WPA_COMPLETED:
+               /* carrier on */
+               connman_network_set_connected(task->network, TRUE);
+               connman_device_set_scanning(task->device, FALSE);
+               break;
+       case WPA_DISCONNECTED:
+               /* carrier off */
+               connman_network_set_connected(task->network, FALSE);
+               connman_device_set_scanning(task->device, FALSE);
+               break;
+       case WPA_ASSOCIATING:
+               connman_network_set_associating(task->network, TRUE);
+               break;
+       default:
+               connman_network_set_associating(task->network, FALSE);
+               break;
+       }
+}
+
+static DBusHandlerResult supplicant_filter(DBusConnection *conn,
+                                               DBusMessage *msg, void *data)
+{
+       struct supplicant_task *task;
+       const char *member, *path;
+
+       if (dbus_message_has_interface(msg,
+                               SUPPLICANT_INTF ".Interface") == FALSE)
+               return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
+
+       member = dbus_message_get_member(msg);
+       if (member == NULL)
+               return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
+
+       path = dbus_message_get_path(msg);
+       if (path == NULL)
+               return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
+
+       task = find_task_by_path(path);
+       if (task == NULL)
+               return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
+
+       DBG("task %p member %s", task, member);
+
+       if (g_str_equal(member, "ScanResultsAvailable") == TRUE)
+               scan_results_available(task);
+       else if (g_str_equal(member, "StateChange") == TRUE)
+               state_change(task, msg);
+
+       return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
+}
+
+int supplicant_start(struct connman_device *device)
+{
+       struct supplicant_task *task;
+
+       DBG("device %p", device);
+
+       task = g_try_new0(struct supplicant_task, 1);
+       if (task == NULL)
+               return -ENOMEM;
+
+       task->ifindex = connman_device_get_index(device);
+       task->ifname = connman_inet_ifname(task->ifindex);
+
+       if (task->ifname == NULL) {
+               g_free(task);
+               return -ENOMEM;
+       }
+
+       task->device = connman_device_ref(device);
+
+       task->created = FALSE;
+       task->noscan = FALSE;
+       task->state = WPA_INVALID;
+
+       task_list = g_slist_append(task_list, task);
+
+       return create_interface(task);
+}
+
+int supplicant_stop(struct connman_device *device)
+{
+       int index = connman_device_get_index(device);
+       struct supplicant_task *task;
+
+       DBG("device %p", device);
+
+       task = find_task_by_index(index);
+       if (task == NULL)
+               return -ENODEV;
+
+       task_list = g_slist_remove(task_list, task);
+
+       disable_network(task);
+
+       remove_network(task);
+
+       return remove_interface(task);
+}
+
+int supplicant_scan(struct connman_device *device)
+{
+       int index = connman_device_get_index(device);
+       struct supplicant_task *task;
+       int err;
+
+       DBG("device %p", device);
+
+       task = find_task_by_index(index);
+       if (task == NULL)
+               return -ENODEV;
+
+       switch (task->state) {
+       case WPA_SCANNING:
+               return -EALREADY;
+       case WPA_ASSOCIATING:
+       case WPA_ASSOCIATED:
+       case WPA_4WAY_HANDSHAKE:
+       case WPA_GROUP_HANDSHAKE:
+               return -EBUSY;
+       default:
+               break;
+       }
+
+       err = initiate_scan(task);
+
+       return 0;
+}
+
+int supplicant_connect(struct connman_network *network)
+{
+       struct supplicant_task *task;
+       const char *address, *security, *passphrase;
+       const void *ssid;
+       unsigned int ssid_len;
+       int index;
+
+       DBG("network %p", network);
+
+       address = connman_network_get_string(network, "Address");
+       security = connman_network_get_string(network, "WiFi.Security");
+       passphrase = connman_network_get_string(network, "WiFi.Passphrase");
+
+       ssid = connman_network_get_blob(network, "WiFi.SSID", &ssid_len);
+
+       DBG("address %s security %s passphrase %s",
+                                       address, security, passphrase);
+
+       if (security == NULL && passphrase == NULL)
+               return -EINVAL;
+
+       if (g_str_equal(security, "none") == FALSE && passphrase == NULL)
+               return -EINVAL;
+
+       index = connman_network_get_index(network);
+
+       task = find_task_by_index(index);
+       if (task == NULL)
+               return -ENODEV;
+
+       task->network = connman_network_ref(network);
+
+       add_network(task);
+
+       select_network(task);
+       disable_network(task);
+
+       set_network(task, ssid, ssid_len, address, security, passphrase);
+
+       enable_network(task);
+
+       connman_network_set_associating(task->network, TRUE);
+
+       return 0;
+}
+
+int supplicant_disconnect(struct connman_network *network)
+{
+       struct supplicant_task *task;
+       int index;
+
+       DBG("network %p", network);
+
+       index = connman_network_get_index(network);
+
+       task = find_task_by_index(index);
+       if (task == NULL)
+               return -ENODEV;
+
+       disable_network(task);
+
+       remove_network(task);
+
+       connman_network_set_connected(task->network, FALSE);
+
+       connman_network_unref(task->network);
+
+       return 0;
+}
+
+static void supplicant_activate(DBusConnection *conn)
+{
+       DBusMessage *message;
+
+       DBG("conn %p", conn);
+
+       message = dbus_message_new_method_call(SUPPLICANT_NAME, "/",
+                               DBUS_INTERFACE_INTROSPECTABLE, "Introspect");
+       if (message == NULL)
+               return;
+
+       dbus_message_set_no_reply(message, TRUE);
+
+       dbus_connection_send(conn, message, NULL);
+
+       dbus_message_unref(message);
+}
+
+static GSList *driver_list = NULL;
+
+static void supplicant_probe(DBusConnection *conn, void *user_data)
+{
+       GSList *list;
+
+       DBG("conn %p", conn);
+
+       for (list = driver_list; list; list = list->next) {
+               struct supplicant_driver *driver = list->data;
+
+               DBG("driver %p name %s", driver, driver->name);
+
+               if (driver->probe)
+                       driver->probe();
+       }
+}
+
+static void supplicant_remove(DBusConnection *conn, void *user_data)
+{
+       GSList *list;
+
+       DBG("conn %p", conn);
+
+       for (list = driver_list; list; list = list->next) {
+               struct supplicant_driver *driver = list->data;
+
+               DBG("driver %p name %s", driver, driver->name);
+
+               if (driver->remove)
+                       driver->remove();
+       }
+}
+
+static const char *supplicant_rule = "type=signal,"
+                               "interface=" SUPPLICANT_INTF ".Interface";
+static guint watch;
+
+static int supplicant_create(void)
+{
+       if (g_slist_length(driver_list) > 0)
+               return 0;
+
+       connection = connman_dbus_get_connection();
+       if (connection == NULL)
+               return -EIO;
+
+       DBG("connection %p", connection);
+
+       if (dbus_connection_add_filter(connection,
+                               supplicant_filter, NULL, NULL) == FALSE) {
+               connection = connman_dbus_get_connection();
+               return -EIO;
+       }
+
+       dbus_bus_add_match(connection, supplicant_rule, NULL);
+       dbus_connection_flush(connection);
+
+       watch = g_dbus_add_service_watch(connection, SUPPLICANT_NAME,
+                       supplicant_probe, supplicant_remove, NULL, NULL);
+
+       return 0;
+}
+
+static void supplicant_destroy(void)
+{
+       if (g_slist_length(driver_list) > 0)
+               return;
+
+       DBG("connection %p", connection);
+
+       if (watch > 0)
+               g_dbus_remove_watch(connection, watch);
+
+       dbus_bus_remove_match(connection, supplicant_rule, NULL);
+       dbus_connection_flush(connection);
+
+       dbus_connection_remove_filter(connection, supplicant_filter, NULL);
+
+       dbus_connection_unref(connection);
+       connection = NULL;
+}
+
+int supplicant_register(struct supplicant_driver *driver)
+{
+       int err;
+
+       DBG("driver %p name %s", driver, driver->name);
+
+       err = supplicant_create();
+       if (err < 0)
+               return err;
+
+       driver_list = g_slist_append(driver_list, driver);
+
+       if (g_dbus_check_service(connection, SUPPLICANT_NAME) == TRUE)
+               supplicant_probe(connection, NULL);
+       else
+               supplicant_activate(connection);
+
+       return 0;
+}
+
+void supplicant_unregister(struct supplicant_driver *driver)
+{
+       DBG("driver %p name %s", driver, driver->name);
+
+       supplicant_remove(connection, NULL);
+
+       driver_list = g_slist_remove(driver_list, driver);
+
+       supplicant_destroy();
+}
diff --git a/plugins/supplicant.h b/plugins/supplicant.h
new file mode 100644 (file)
index 0000000..fb49722
--- /dev/null
@@ -0,0 +1,39 @@
+/*
+ *
+ *  Connection Manager
+ *
+ *  Copyright (C) 2007-2009  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
+ *
+ */
+
+#include <connman/device.h>
+#include <connman/network.h>
+
+struct supplicant_driver {
+       const char *name;
+       void (*probe) (void);
+       void (*remove) (void);
+};
+
+int supplicant_register(struct supplicant_driver *driver);
+void supplicant_unregister(struct supplicant_driver *driver);
+
+int supplicant_start(struct connman_device *device);
+int supplicant_stop(struct connman_device *device);
+int supplicant_scan(struct connman_device *device);
+
+int supplicant_connect(struct connman_network *network);
+int supplicant_disconnect(struct connman_network *network);
diff --git a/plugins/task.c b/plugins/task.c
new file mode 100644 (file)
index 0000000..f0ef658
--- /dev/null
@@ -0,0 +1,145 @@
+/*
+ *
+ *  Connection Manager
+ *
+ *  Copyright (C) 2007-2009  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 <unistd.h>
+#include <sys/wait.h>
+
+#include <glib.h>
+
+#include <connman/log.h>
+
+#include "task.h"
+
+struct task_data {
+       pid_t pid;
+       int index;
+       task_cb_t callback;
+       void *user_data;
+};
+
+static GSList *task_list = NULL;
+
+struct task_data *task_find_by_pid(pid_t pid)
+{
+       GSList *list;
+
+       for (list = task_list; list; list = list->next) {
+               struct task_data *task = list->data;
+
+               if (task->pid == pid)
+                       return task;
+       }
+
+       return NULL;
+}
+
+struct task_data *task_find_by_index(int index)
+{
+       GSList *list;
+
+       for (list = task_list; list; list = list->next) {
+               struct task_data *task = list->data;
+
+               if (task->index == index)
+                       return task;
+       }
+
+       return NULL;
+}
+
+static void task_died(GPid pid, gint status, gpointer user_data)
+{
+       struct task_data *task = user_data;
+
+       if (WIFEXITED(status))
+               DBG("task %p exit status %d", task, WEXITSTATUS(status));
+       else
+               DBG("task %p signal %d", task, WTERMSIG(status));
+
+       g_spawn_close_pid(pid);
+       task->pid = 0;
+
+       task_list = g_slist_remove(task_list, task);
+
+       if (task->callback)
+               task->callback(task->index, task->user_data);
+
+       g_free(task);
+}
+
+static void task_setup(gpointer user_data)
+{
+       struct task_data *task = user_data;
+
+       DBG("task %p", task);
+}
+
+struct task_data *task_spawn(int index, char **argv, char **envp,
+                                       task_cb_t callback, void *user_data)
+{
+       GSpawnFlags flags = G_SPAWN_DO_NOT_REAP_CHILD |
+                                               G_SPAWN_STDOUT_TO_DEV_NULL;
+       struct task_data *task;
+
+       DBG("index %d", index);
+
+       task = g_try_new0(struct task_data, 1);
+       if (task == NULL)
+               return NULL;
+
+       task->index = index;
+
+       task->callback  = callback;
+       task->user_data = user_data;
+
+       if (g_spawn_async(NULL, argv, envp, flags,
+                               task_setup, task, &task->pid, NULL) == FALSE) {
+               connman_error("Failed to spawn task");
+               return NULL;
+       }
+
+       task_list = g_slist_append(task_list, task);
+
+       g_child_watch_add(task->pid, task_died, task);
+
+       DBG("task %p pid %d", task, task->pid);
+
+       return task;
+}
+
+int task_kill(struct task_data *task)
+{
+       DBG("task %p", task);
+
+       if (task->pid > 0)
+               kill(task->pid, SIGTERM);
+
+       return 0;
+}
+
+void *task_get_data(struct task_data *task)
+{
+       return task->user_data;
+}
diff --git a/plugins/task.h b/plugins/task.h
new file mode 100644 (file)
index 0000000..6472576
--- /dev/null
@@ -0,0 +1,35 @@
+/*
+ *
+ *  Connection Manager
+ *
+ *  Copyright (C) 2007-2009  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
+ *
+ */
+
+#include <sys/types.h>
+
+struct task_data;
+
+typedef void (* task_cb_t) (int index, void *user_data);
+
+struct task_data *task_find_by_pid(pid_t pid);
+struct task_data *task_find_by_index(int index);
+
+struct task_data *task_spawn(int index, char **argv, char **envp,
+                                       task_cb_t callback, void *user_data);
+int task_kill(struct task_data *task);
+
+void *task_get_data(struct task_data *task);
diff --git a/plugins/udhcp.c b/plugins/udhcp.c
new file mode 100644 (file)
index 0000000..6ee0ac8
--- /dev/null
@@ -0,0 +1,215 @@
+/*
+ *
+ *  Connection Manager
+ *
+ *  Copyright (C) 2007-2009  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 <unistd.h>
+
+#define CONNMAN_API_SUBJECT_TO_CHANGE
+#include <connman/plugin.h>
+#include <connman/driver.h>
+#include <connman/inet.h>
+#include <connman/dbus.h>
+#include <connman/log.h>
+
+#include "task.h"
+
+#define UDHCPC_INTF  "net.busybox.udhcpc"
+#define UDHCPC_PATH  "/net/busybox/udhcpc"
+
+static int udhcp_probe(struct connman_element *element)
+{
+       struct task_data *task;
+       char *argv[9], *envp[2], *ifname;
+       char pidfile[PATH_MAX], script[PATH_MAX];
+
+       DBG("element %p name %s", element, element->name);
+
+       if (access(UDHCPC, X_OK) < 0)
+               return -errno;
+
+       ifname = connman_inet_ifname(element->index);
+       if (ifname == NULL)
+               return -ENOMEM;
+
+       snprintf(pidfile, sizeof(pidfile) - 1,
+                               "%s/udhcpc.%s.pid", STATEDIR, ifname);
+       snprintf(script, sizeof(script) - 1, "%s/udhcpc-script", SCRIPTDIR);
+
+       argv[0] = UDHCPC;
+       argv[1] = "-f";
+       argv[2] = "-i";
+       argv[3] = ifname;
+       argv[4] = "-p";
+       argv[5] = pidfile;
+       argv[6] = "-s";
+       argv[7] = script;
+       argv[8] = NULL;
+
+       envp[0] = NULL;
+
+       task = task_spawn(element->index, argv, envp, NULL, element);
+       if (task == NULL) {
+               g_free(ifname);
+               return -EIO;
+       }
+
+       g_free(ifname);
+
+       return 0;
+}
+
+static void udhcp_remove(struct connman_element *element)
+{
+       struct task_data *task;
+
+       DBG("element %p name %s", element, element->name);
+
+       task = task_find_by_index(element->index);
+       if (task == NULL)
+               return;
+
+       task_kill(task);
+}
+
+static struct connman_driver udhcp_driver = {
+       .name           = "udhcp",
+       .type           = CONNMAN_ELEMENT_TYPE_DHCP,
+       .priority       = CONNMAN_DRIVER_PRIORITY_HIGH,
+       .probe          = udhcp_probe,
+       .remove         = udhcp_remove,
+};
+
+static void udhcp_bound(DBusMessage *msg, gboolean renew)
+{
+       struct task_data *task;
+       struct connman_element *element, *parent;
+       const char *interface, *address, *netmask, *broadcast, *gateway, *dns;
+       int index;
+
+       dbus_message_get_args(msg, NULL, DBUS_TYPE_STRING, &interface,
+                                       DBUS_TYPE_STRING, &address,
+                                       DBUS_TYPE_STRING, &netmask,
+                                       DBUS_TYPE_STRING, &broadcast,
+                                       DBUS_TYPE_STRING, &gateway,
+                                       DBUS_TYPE_STRING, &dns,
+                                                       DBUS_TYPE_INVALID);
+
+       DBG("%s ==> address %s gateway %s", interface, address, gateway);
+
+       index = connman_inet_ifindex(interface);
+       if (index < 0)
+               return;
+
+       task = task_find_by_index(index);
+       if (task == NULL)
+               return;
+
+       parent = task_get_data(task);
+       if (parent == NULL)
+               return;
+
+       g_free(parent->ipv4.address);
+       parent->ipv4.address = g_strdup(address);
+
+       g_free(parent->ipv4.netmask);
+       parent->ipv4.netmask = g_strdup(netmask);
+
+       g_free(parent->ipv4.broadcast);
+       parent->ipv4.broadcast = g_strdup(broadcast);
+
+       g_free(parent->ipv4.gateway);
+       parent->ipv4.gateway = g_strdup(gateway);
+
+       g_free(parent->ipv4.nameserver);
+       parent->ipv4.nameserver = g_strdup(dns);
+
+       connman_element_update(parent);
+
+       if (renew == TRUE)
+               return;
+
+       element = connman_element_create(NULL);
+       if (element == NULL)
+               return;
+
+       element->type = CONNMAN_ELEMENT_TYPE_IPV4;
+       element->index = index;
+
+       if (connman_element_register(element, parent) < 0)
+               connman_element_unref(element);
+}
+
+static DBusHandlerResult udhcp_filter(DBusConnection *conn,
+                                               DBusMessage *msg, void *data)
+{
+       if (dbus_message_is_method_call(msg, UDHCPC_INTF, "bound") == TRUE) {
+               udhcp_bound(msg, FALSE);
+               return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
+       }
+
+       if (dbus_message_is_method_call(msg, UDHCPC_INTF, "renew") == TRUE) {
+               udhcp_bound(msg, TRUE);
+               return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
+       }
+
+       return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
+}
+
+static DBusConnection *connection;
+
+static const char *udhcp_rule = "path=" UDHCPC_PATH ",interface=" UDHCPC_INTF;
+
+static int udhcp_init(void)
+{
+       int err;
+
+       connection = connman_dbus_get_connection();
+
+       dbus_connection_add_filter(connection, udhcp_filter, NULL, NULL);
+
+       dbus_bus_add_match(connection, udhcp_rule, NULL);
+
+       err = connman_driver_register(&udhcp_driver);
+       if (err < 0) {
+               dbus_connection_unref(connection);
+               return err;
+       }
+
+       return 0;
+}
+
+static void udhcp_exit(void)
+{
+       connman_driver_unregister(&udhcp_driver);
+
+       dbus_bus_remove_match(connection, udhcp_rule, NULL);
+
+       dbus_connection_remove_filter(connection, udhcp_filter, NULL);
+
+       dbus_connection_unref(connection);
+}
+
+CONNMAN_PLUGIN_DEFINE(udhcp, "uDHCP client plugin", VERSION,
+               CONNMAN_PLUGIN_PRIORITY_DEFAULT, udhcp_init, udhcp_exit)
diff --git a/plugins/wifi.c b/plugins/wifi.c
new file mode 100644 (file)
index 0000000..08b3d48
--- /dev/null
@@ -0,0 +1,212 @@
+/*
+ *
+ *  Connection Manager
+ *
+ *  Copyright (C) 2007-2009  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 <errno.h>
+
+#include <dbus/dbus.h>
+#include <glib.h>
+
+#define CONNMAN_API_SUBJECT_TO_CHANGE
+#include <connman/plugin.h>
+#include <connman/device.h>
+#include <connman/log.h>
+
+#include "supplicant.h"
+
+#define CLEANUP_TIMEOUT   8    /* in seconds */
+#define INACTIVE_TIMEOUT  12   /* in seconds */
+
+struct wifi_data {
+       char *identifier;
+       connman_bool_t connected;
+};
+
+static int network_probe(struct connman_network *network)
+{
+       DBG("network %p", network);
+
+       return 0;
+}
+
+static void network_remove(struct connman_network *network)
+{
+       DBG("network %p", network);
+}
+
+static int network_connect(struct connman_network *network)
+{
+       DBG("network %p", network);
+
+       return supplicant_connect(network);
+}
+
+static int network_disconnect(struct connman_network *network)
+{
+       DBG("network %p", network);
+
+       return supplicant_disconnect(network);
+}
+
+static struct connman_network_driver network_driver = {
+       .name           = "wifi",
+       .type           = CONNMAN_NETWORK_TYPE_WIFI,
+       .probe          = network_probe,
+       .remove         = network_remove,
+       .connect        = network_connect,
+       .disconnect     = network_disconnect,
+};
+
+static int wifi_probe(struct connman_device *device)
+{
+       struct wifi_data *data;
+
+       DBG("device %p", device);
+
+       data = g_try_new0(struct wifi_data, 1);
+       if (data == NULL)
+               return -ENOMEM;
+
+       data->connected = FALSE;
+
+       connman_device_set_data(device, data);
+
+       return 0;
+}
+
+static void wifi_remove(struct connman_device *device)
+{
+       struct wifi_data *data = connman_device_get_data(device);
+
+       DBG("device %p", device);
+
+       connman_device_set_data(device, NULL);
+
+       g_free(data->identifier);
+       g_free(data);
+}
+
+static int wifi_enable(struct connman_device *device)
+{
+       DBG("device %p", device);
+
+       return supplicant_start(device);
+}
+
+static int wifi_disable(struct connman_device *device)
+{
+       struct wifi_data *data = connman_device_get_data(device);
+
+       DBG("device %p", device);
+
+       data->connected = FALSE;
+
+       return supplicant_stop(device);
+}
+
+static int wifi_scan(struct connman_device *device)
+{
+       DBG("device %p", device);
+
+       return supplicant_scan(device);
+}
+
+static int wifi_join(struct connman_device *device,
+                                       struct connman_network *network)
+{
+       int err;
+
+       DBG("device %p", device);
+
+       err = supplicant_connect(network);
+       if (err < 0)
+               return err;
+
+       connman_network_ref(network);
+
+       connman_device_add_network(device, network);
+
+       connman_network_set_available(network, TRUE);
+
+       return 0;
+}
+
+static struct connman_device_driver wifi_driver = {
+       .name           = "wifi",
+       .type           = CONNMAN_DEVICE_TYPE_WIFI,
+       .probe          = wifi_probe,
+       .remove         = wifi_remove,
+       .enable         = wifi_enable,
+       .disable        = wifi_disable,
+       .scan           = wifi_scan,
+       .join           = wifi_join,
+};
+
+static void wifi_register(void)
+{
+       DBG("");
+
+       if (connman_device_driver_register(&wifi_driver) < 0)
+               connman_error("Failed to register WiFi driver");
+}
+
+static void wifi_unregister(void)
+{
+       DBG("");
+
+       connman_device_driver_unregister(&wifi_driver);
+}
+
+static struct supplicant_driver supplicant = {
+       .name           = "wifi",
+       .probe          = wifi_register,
+       .remove         = wifi_unregister,
+};
+
+static int wifi_init(void)
+{
+       int err;
+
+       err = connman_network_driver_register(&network_driver);
+       if (err < 0)
+               return err;
+
+       err = supplicant_register(&supplicant);
+       if (err < 0) {
+               connman_network_driver_unregister(&network_driver);
+               return err;
+       }
+
+       return 0;
+}
+
+static void wifi_exit(void)
+{
+       supplicant_unregister(&supplicant);
+
+       connman_network_driver_unregister(&network_driver);
+}
+
+CONNMAN_PLUGIN_DEFINE(wifi, "WiFi interface plugin", VERSION,
+               CONNMAN_PLUGIN_PRIORITY_DEFAULT, wifi_init, wifi_exit)
diff --git a/scripts/Makefile.am b/scripts/Makefile.am
new file mode 100644 (file)
index 0000000..a0c6363
--- /dev/null
@@ -0,0 +1,37 @@
+
+initdir = $(sysconfdir)/init.d
+
+init_SCRIPTS = connman
+
+DISTCLEANFILES = $(init_SCRIPTS)
+
+scriptdir = $(libdir)/connman/scripts
+
+script_DATA =
+script_PROGRAMS =
+script_LTLIBRARIES =
+
+if UDHCP
+script_PROGRAMS += udhcpc-script
+
+udhcpc_script_LDADD = @DBUS_LIBS@
+endif
+
+if DHCLIENT
+script_DATA += dhclient.conf
+script_PROGRAMS += dhclient-script
+
+dhclient_script_LDADD = @DBUS_LIBS@
+endif
+
+if PPPD
+script_LTLIBRARIES += pppd-plugin.la
+
+pppd_plugin_la_LDFLAGS = -module -avoid-version
+endif
+
+AM_CFLAGS = @DBUS_CFLAGS@
+
+EXTRA_DIST = dhclient.conf
+
+MAINTAINERCLEANFILES = Makefile.in
diff --git a/scripts/connman.in b/scripts/connman.in
new file mode 100644 (file)
index 0000000..ec98f39
--- /dev/null
@@ -0,0 +1,46 @@
+#!/bin/sh
+
+DAEMON=@prefix@/sbin/connmand
+DESC="Connection Manager"
+
+. /lib/lsb/init-functions
+
+if [ -f @sysconfdir@/default/connman ] ; then
+       . @sysconfdir@/default/connman
+fi
+
+set -e
+
+do_start() {
+       start-stop-daemon --start --oknodo --exec $DAEMON -- $DAEMON_OPTS
+}
+
+do_stop() {
+       start-stop-daemon --stop --oknodo --quiet --exec $DAEMON
+}
+
+case "$1" in
+  start)
+       log_daemon_msg "Starting $DESC"
+       do_start
+       log_end_msg $?
+       ;;
+  stop)
+       log_daemon_msg "Stopping $DESC"
+       do_stop
+       log_end_msg $?
+       ;;
+  restart|force-reload)
+       log_daemon_msg "Restarting $DESC"
+       do_stop
+       sleep 1
+       do_start
+       log_end_msg $?
+       ;;
+  *)
+       log_success_msg "Usage: $0 {start|stop|restart|force-reload}" >&2
+       exit 1
+       ;;
+esac
+
+exit 0
diff --git a/scripts/dhclient-script.c b/scripts/dhclient-script.c
new file mode 100644 (file)
index 0000000..94543a0
--- /dev/null
@@ -0,0 +1,128 @@
+/*
+ *
+ *  Connection Manager
+ *
+ *  Copyright (C) 2007-2009  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 <string.h>
+
+#include <dbus/dbus.h>
+
+#define DHCLIENT_INTF "org.isc.dhclient"
+#define DHCLIENT_PATH "/org/isc/dhclient"
+
+extern char **environ;
+
+static void append(DBusMessageIter *dict, const char *pattern)
+{
+       DBusMessageIter entry;
+       const char *key, *value;
+       char *delim;
+
+       delim = strchr(pattern, '=');
+       *delim = '\0';
+
+       key = pattern;
+       value = delim + 1;
+
+       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_append_basic(&entry, DBUS_TYPE_STRING, &value);
+
+       dbus_message_iter_close_container(dict, &entry);
+}
+
+int main(int argc, char *argv[])
+{
+       DBusConnection *conn;
+       DBusError error;
+       DBusMessage *msg;
+       DBusMessageIter iter, dict;
+       dbus_uint32_t pid;
+       char **envp, *busname, *reason, *interface;
+
+       busname = getenv("BUSNAME");
+
+       pid = atoi(getenv("pid"));
+       reason = getenv("reason");
+       interface = getenv("interface");
+
+       dbus_error_init(&error);
+
+       conn = dbus_bus_get(DBUS_BUS_SYSTEM, &error);
+       if (conn == NULL) {
+               if (dbus_error_is_set(&error) == TRUE) {
+                       fprintf(stderr, "%s\n", error.message);
+                       dbus_error_free(&error);
+               } else
+                       fprintf(stderr, "Failed to get on system bus\n");
+               return 0;
+       }
+
+       msg = dbus_message_new_method_call(busname, DHCLIENT_PATH,
+                                               DHCLIENT_INTF, "notify");
+       if (msg == NULL) {
+               dbus_connection_unref(conn);
+               fprintf(stderr, "Failed to allocate method call\n");
+               return 0;
+       }
+
+       dbus_message_set_no_reply(msg, TRUE);
+
+       dbus_message_append_args(msg, DBUS_TYPE_UINT32, &pid,
+                               DBUS_TYPE_STRING, &reason, DBUS_TYPE_INVALID);
+
+       dbus_message_iter_init_append(msg, &iter);
+
+       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);
+
+       for (envp = environ; envp && *envp; envp++) {
+               if (strlen(*envp) < 5)
+                       continue;
+
+               if (strncmp(*envp, "new_", 4) == 0 ||
+                               strncmp(*envp, "old_", 4) == 0 ||
+                                       strncmp(*envp, "alia", 4) == 0)
+                       append(&dict, *envp);
+       }
+
+       dbus_message_iter_close_container(&iter, &dict);
+
+       if (dbus_connection_send(conn, msg, NULL) == FALSE)
+               fprintf(stderr, "Failed to send message\n");
+
+       dbus_connection_flush(conn);
+
+       dbus_message_unref(msg);
+
+       dbus_connection_unref(conn);
+
+       return 0;
+}
diff --git a/scripts/dhclient.conf b/scripts/dhclient.conf
new file mode 100644 (file)
index 0000000..4b206a3
--- /dev/null
@@ -0,0 +1,3 @@
+send host-name "<hostname>";
+request subnet-mask, broadcast-address, time-offset, routers,
+       domain-name, domain-name-servers, host-name;
diff --git a/scripts/pppd-plugin.c b/scripts/pppd-plugin.c
new file mode 100644 (file)
index 0000000..e8bae0e
--- /dev/null
@@ -0,0 +1,82 @@
+/*
+ *
+ *  Connection Manager
+ *
+ *  Copyright (C) 2007-2009  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 <netinet/in.h>
+#include <arpa/inet.h>
+
+#include <pppd/pppd.h>
+#include <pppd/fsm.h>
+#include <pppd/ipcp.h>
+
+static void notifier_phasechange(void *data, int arg)
+{
+       printf("phasechange: data %p arg %d\n", data, arg);
+}
+
+static void notifier_exit(void *data, int arg)
+{
+       printf("exitnotify: data %p arg %d\n", data, arg);
+}
+
+static void notifier_ipup(void *data, int arg)
+{
+       ipcp_options opts = ipcp_gotoptions[0];
+       ipcp_options peer = ipcp_hisoptions[0];
+
+       printf("ipup: data %p arg %d\n", data, arg);
+
+       printf("%s: %s -> %s\n", ifname,
+                               inet_ntoa(*((struct in_addr *) &opts.ouraddr)),
+                               inet_ntoa(*((struct in_addr *) &peer.hisaddr)));
+
+       script_unsetenv("USEPEERDNS");
+       script_unsetenv("DNS1");
+       script_unsetenv("DNS2");
+}
+
+static void notifier_ipdown(void *data, int arg)
+{
+       printf("ipdown: data %p arg %d\n", data, arg);
+}
+
+char pppd_version[] = VERSION;
+
+int plugin_init(void);
+
+int plugin_init(void)
+{
+#if 0
+       path_ipup[0] = '\0';
+       path_ipdown[0] = '\0';
+#endif
+
+       add_notifier(&phasechange, notifier_phasechange, NULL);
+       add_notifier(&exitnotify, notifier_exit, NULL);
+
+       add_notifier(&ip_up_notifier, notifier_ipup, NULL);
+       add_notifier(&ip_down_notifier, notifier_ipdown, NULL);
+
+       return 0;
+}
diff --git a/scripts/udhcpc-script.c b/scripts/udhcpc-script.c
new file mode 100644 (file)
index 0000000..b5f9c54
--- /dev/null
@@ -0,0 +1,113 @@
+/*
+ *
+ *  Connection Manager
+ *
+ *  Copyright (C) 2007-2009  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 <string.h>
+
+#include <dbus/dbus.h>
+
+#define UDHCPC_INTF  "net.busybox.udhcpc"
+#define UDHCPC_PATH  "/net/busybox/udhcpc"
+
+int main(int argc, char *argv[])
+{
+       DBusConnection *conn;
+       DBusError error;
+       DBusMessage *msg;
+       char *busname, *interface, *address, *netmask, *broadcast;
+       char *gateway, *dns;
+
+       if (argc < 2)
+               return 0;
+
+       if (strcmp(argv[1], "bound") != 0 && strcmp(argv[1], "renew") != 0)
+               return 0;
+
+       busname = "org.moblin.connman";
+
+       interface = getenv("interface");
+
+       address = getenv("ip");
+       if (address == NULL)
+               address = "";
+
+       netmask = getenv("subnet");
+       if (netmask == NULL)
+               netmask = "";
+
+       broadcast = getenv("broadcast");
+       if (broadcast == NULL)
+               broadcast = "";
+
+       gateway = getenv("router");
+       if (gateway == NULL)
+               gateway = "";
+
+       dns = getenv("dns");
+       if (dns == NULL)
+               dns = "";
+
+       dbus_error_init(&error);
+
+       conn = dbus_bus_get(DBUS_BUS_SYSTEM, &error);
+       if (conn == NULL) {
+               if (dbus_error_is_set(&error) == TRUE) {
+                       fprintf(stderr, "%s\n", error.message);
+                       dbus_error_free(&error);
+               } else
+                       fprintf(stderr, "Failed to get on system bus\n");
+               return 0;
+       }
+
+       msg = dbus_message_new_method_call(busname, UDHCPC_PATH,
+                                               UDHCPC_INTF, argv[1]);
+       if (msg == NULL) {
+               dbus_connection_unref(conn);
+               fprintf(stderr, "Failed to allocate method call\n");
+               return 0;
+       }
+
+       dbus_message_set_no_reply(msg, TRUE);
+
+       dbus_message_append_args(msg, DBUS_TYPE_STRING, &interface,
+                                       DBUS_TYPE_STRING, &address,
+                                       DBUS_TYPE_STRING, &netmask,
+                                       DBUS_TYPE_STRING, &broadcast,
+                                       DBUS_TYPE_STRING, &gateway,
+                                       DBUS_TYPE_STRING, &dns,
+                                                       DBUS_TYPE_INVALID);
+
+       if (dbus_connection_send(conn, msg, NULL) == FALSE)
+               fprintf(stderr, "Failed to send message\n");
+
+       dbus_connection_flush(conn);
+
+       dbus_message_unref(msg);
+
+       dbus_connection_unref(conn);
+
+       return 0;
+}
diff --git a/shave-libtool.in b/shave-libtool.in
new file mode 100644 (file)
index 0000000..1f3a720
--- /dev/null
@@ -0,0 +1,69 @@
+#!/bin/sh
+
+# we need sed
+SED=@SED@
+if test -z "$SED" ; then
+SED=sed
+fi
+
+lt_unmangle ()
+{
+   last_result=`echo $1 | $SED -e 's#.libs/##' -e 's#[0-9a-zA-Z_\-\.]*_la-##'`
+}
+
+# the real libtool to use
+LIBTOOL="$1"
+shift
+
+# if 1, don't print anything, the underlaying wrapper will do it
+pass_though=0
+
+# scan the arguments, keep the right ones for libtool, and discover the mode
+preserved_args=
+while test "$#" -gt 0; do
+    opt="$1"
+    shift
+
+    case $opt in
+    --mode=*)
+        mode=`echo $opt | $SED -e 's/[-_a-zA-Z0-9]*=//'`
+        preserved_args="$preserved_args $opt"
+        ;;
+    -o)
+        lt_output="$1"
+        preserved_args="$preserved_args $opt"
+       ;;
+    *)
+        preserved_args="$preserved_args $opt"
+        ;;
+      esac
+done
+
+case "$mode" in
+compile)
+    # shave will be called and print the actual CC/CXX/LINK line
+    preserved_args="$preserved_args --shave-mode=$mode"
+    pass_though=1
+    ;;
+link)
+    preserved_args="$preserved_args --shave-mode=$mode"
+    Q="  LINK  "
+    ;;
+*)
+    # let's u
+    # echo "*** libtool: Unimplemented mode: $mode, fill a bug report"
+    ;;
+esac
+
+lt_unmangle "$lt_output"
+output=$last_result
+
+if test -z $V; then
+    if test $pass_though -eq 0; then
+        echo "$Q$output"
+    fi
+    $LIBTOOL --silent $preserved_args
+else
+    echo $LIBTOOL $preserved_args
+    $LIBTOOL $preserved_args
+fi
diff --git a/shave.in b/shave.in
new file mode 100644 (file)
index 0000000..174641e
--- /dev/null
+++ b/shave.in
@@ -0,0 +1,76 @@
+#!/bin/sh
+
+# we need sed
+SED=@SED@
+if test -z "$SED" ; then
+SED=sed
+fi
+
+lt_unmangle ()
+{
+   last_result=`echo $1 | $SED -e 's#.libs/##' -e 's#[0-9a-zA-Z_\-\.]*_la-##'`
+}
+
+# the tool to wrap (cc, cxx, ar, ranlib, ..)
+tool="$1"
+shift
+
+# the reel tool (to call)
+REEL_TOOL="$1"
+shift
+
+pass_through=0
+preserved_args=
+while test "$#" -gt 0; do
+    opt="$1"
+    shift
+
+    case $opt in
+    --shave-mode=*)
+        mode=`echo $opt | $SED -e 's/[-_a-zA-Z0-9]*=//'`
+       ;;
+    -o)
+        lt_output="$1"
+        preserved_args="$preserved_args $opt"
+       ;;
+    *)
+        preserved_args="$preserved_args $opt"
+        ;;
+      esac
+done
+
+# mode=link is handled in the libtool wrapper
+case "$mode,$tool" in
+link,*)
+    pass_through=1
+    ;;
+*,cxx)
+    Q="  CXX   "
+    ;;
+*,cc)
+    Q="  CC    "
+    ;;
+*,fc)
+    Q="  FC    "
+    ;;
+*,f77)
+    Q="  F77   "
+    ;;
+*,*)
+    # should not happen
+    Q="  CC    "
+    ;;
+esac
+
+lt_unmangle "$lt_output"
+output=$last_result
+
+if test -z $V; then
+    if test $pass_through -eq 0; then
+        echo "$Q$output"
+    fi
+    $REEL_TOOL $preserved_args
+else
+    echo $REEL_TOOL $preserved_args
+    $REEL_TOOL $preserved_args
+fi
diff --git a/src/Makefile.am b/src/Makefile.am
new file mode 100644 (file)
index 0000000..4414ec2
--- /dev/null
@@ -0,0 +1,72 @@
+
+if DATAFILES
+dbusdir = @DBUS_DATADIR@
+
+dbus_DATA = connman.conf
+endif
+
+sbin_PROGRAMS = connmand
+
+connmand_SOURCES = main.c connman.h log.c selftest.c error.c plugin.c \
+                       element.c device.c network.c connection.c \
+                       manager.c profile.c service.c agent.c notifier.c \
+                       security.c resolver.c ipconfig.c rfkill.c \
+                       storage.c ipv4.c detect.c rtnl.c inet.c dbus.c
+
+if UDEV
+connmand_SOURCES += udev.c
+
+if DATAFILES
+rulesdir = @UDEV_DATADIR@
+
+rules_DATA = 92-connman.rules
+endif
+endif
+
+connmand_LDADD = $(top_builddir)/plugins/libbuiltin.la \
+               @GDBUS_LIBS@ @GLIB_LIBS@ @GTHREAD_LIBS@ @UDEV_LIBS@ -ldl
+
+connmand_LDFLAGS = -Wl,--export-dynamic -Wl,--version-script=connman.ver
+
+connmand_DEPENDENCIES = connman.ver $(top_builddir)/plugins/libbuiltin.la
+
+CLEANFILES = connman.ver connman.exp connman.conf 92-connman.rules
+
+statedir = $(localstatedir)/run/connman
+
+storagedir = $(localstatedir)/lib/connman
+
+if MAINTAINER_MODE
+plugindir = $(abs_top_srcdir)/plugins/.libs
+else
+plugindir = $(libdir)/connman/plugins
+endif
+
+AM_CFLAGS = @UDEV_CFLAGS@ @GTHREAD_CFLAGS@ @GLIB_CFLAGS@ @GDBUS_CFLAGS@ \
+                                       -DSTATEDIR=\""$(statedir)"\" \
+                                       -DPLUGINDIR=\""$(plugindir)"\" \
+                                       -DSTORAGEDIR=\""$(storagedir)\""
+
+INCLUDES = -I$(top_builddir)/include -I$(top_builddir)/plugins
+
+EXTRA_DIST = connman-dbus.conf connman-polkit.conf connman.rules
+
+MAINTAINERCLEANFILES = Makefile.in
+
+connman.exp: $(connmand_OBJECTS)
+       nm -B *.o | awk '{ print $$3 }' | sort -u | grep -E -e '^connman_' > $@
+
+connman.ver: connman.exp
+       echo "{ global:" > $@
+       cat $< | sed -e "s/\(.*\)/\1;/" >> $@
+       echo "local: *; };" >> $@
+
+connman.conf: connman-dbus.conf connman-polkit.conf
+if POLKIT
+       cp $(top_srcdir)/src/connman-polkit.conf $@
+else
+       cp $(top_srcdir)/src/connman-dbus.conf $@
+endif
+
+92-connman.rules: connman.rules
+       cp $< $@
diff --git a/src/agent.c b/src/agent.c
new file mode 100644 (file)
index 0000000..5ae62b2
--- /dev/null
@@ -0,0 +1,125 @@
+/*
+ *
+ *  Connection Manager
+ *
+ *  Copyright (C) 2007-2009  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 <errno.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <gdbus.h>
+
+#include "connman.h"
+
+static DBusConnection *connection = NULL;
+static guint agent_watch = 0;
+static gchar *agent_path = NULL;
+static gchar *agent_sender = NULL;
+
+static void agent_free(void)
+{
+       agent_watch = 0;
+
+       g_free(agent_sender);
+       agent_sender = NULL;
+
+       g_free(agent_path);
+       agent_path = NULL;
+}
+
+static void agent_disconnect(DBusConnection *connection, void *data)
+{
+       DBG("data %p", data);
+
+       agent_free();
+}
+
+int __connman_agent_register(const char *sender, const char *path)
+{
+       DBG("sender %s path %s", sender, path);
+
+       if (agent_path != NULL)
+               return -EEXIST;
+
+       agent_sender = g_strdup(sender);
+       agent_path = g_strdup(path);
+
+       agent_watch = g_dbus_add_disconnect_watch(connection, sender,
+                                               agent_disconnect, NULL, NULL);
+
+       return 0;
+}
+
+int __connman_agent_unregister(const char *sender, const char *path)
+{
+       DBG("sender %s path %s", sender, path);
+
+       if (agent_path == NULL)
+               return -ENOENT;
+
+       if (agent_watch > 0)
+               g_dbus_remove_watch(connection, agent_watch);
+
+       agent_free();
+
+       return 0;
+}
+
+int __connman_agent_init(DBusConnection *conn)
+{
+       DBG("conn %p", conn);
+
+       connection = dbus_connection_ref(conn);
+       if (connection == NULL)
+               return -1;
+
+       return 0;
+}
+
+void __connman_agent_cleanup(void)
+{
+       DBusMessage *msg;
+
+       DBG("conn %p", connection);
+
+       if (agent_watch > 0)
+               g_dbus_remove_watch(connection, agent_watch);
+
+       if (agent_path == NULL)
+               return;
+
+       msg = dbus_message_new_method_call(agent_sender, agent_path,
+                                       CONNMAN_AGENT_INTERFACE, "Release");
+       if (msg == NULL)
+               return;
+
+       dbus_message_set_no_reply(msg, TRUE);
+
+       dbus_connection_send(connection, msg, NULL);
+
+       dbus_message_unref(msg);
+
+       agent_free();
+
+       dbus_connection_unref(connection);
+}
diff --git a/src/connection.c b/src/connection.c
new file mode 100644 (file)
index 0000000..293b911
--- /dev/null
@@ -0,0 +1,623 @@
+/*
+ *
+ *  Connection Manager
+ *
+ *  Copyright (C) 2007-2009  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 <errno.h>
+#include <unistd.h>
+#include <string.h>
+#include <sys/ioctl.h>
+#include <arpa/inet.h>
+#include <net/if.h>
+#include <net/route.h>
+
+#include <gdbus.h>
+
+#include "connman.h"
+
+struct gateway_data {
+       int index;
+       char *gateway;
+};
+
+static GSList *gateway_list = NULL;
+
+static struct gateway_data *find_gateway(int index, const char *gateway)
+{
+       GSList *list;
+
+       if (gateway == NULL)
+               return NULL;
+
+       for (list = gateway_list; list; list = list->next) {
+               struct gateway_data *data = list->data;
+
+               if (data->gateway == NULL)
+                       continue;
+
+               if (data->index == index &&
+                               g_str_equal(data->gateway, gateway) == TRUE)
+                       return data;
+       }
+
+       return NULL;
+}
+
+static void remove_gateway(int index, const char *gateway)
+{
+       struct gateway_data *data;
+
+       data = find_gateway(index, gateway);
+       if (data == NULL)
+               return;
+
+       gateway_list = g_slist_remove(gateway_list, data);
+}
+
+static int set_route(struct connman_element *element, const char *gateway)
+{
+       struct ifreq ifr;
+       struct rtentry rt;
+       struct sockaddr_in *addr;
+       int sk, err;
+
+       DBG("element %p", element);
+
+       sk = socket(PF_INET, SOCK_DGRAM, 0);
+       if (sk < 0)
+               return -1;
+
+       memset(&ifr, 0, sizeof(ifr));
+       ifr.ifr_ifindex = element->index;
+
+       if (ioctl(sk, SIOCGIFNAME, &ifr) < 0) {
+               close(sk);
+               return -1;
+       }
+
+       DBG("ifname %s", ifr.ifr_name);
+
+       memset(&rt, 0, sizeof(rt));
+       rt.rt_flags = RTF_UP | RTF_HOST;
+
+       addr = (struct sockaddr_in *) &rt.rt_dst;
+       addr->sin_family = AF_INET;
+       addr->sin_addr.s_addr = inet_addr(gateway);
+
+       addr = (struct sockaddr_in *) &rt.rt_gateway;
+       addr->sin_family = AF_INET;
+       addr->sin_addr.s_addr = INADDR_ANY;
+
+       addr = (struct sockaddr_in *) &rt.rt_genmask;
+       addr->sin_family = AF_INET;
+       addr->sin_addr.s_addr = INADDR_ANY;
+
+       rt.rt_dev = ifr.ifr_name;
+
+       err = ioctl(sk, SIOCADDRT, &rt);
+       if (err < 0)
+               connman_error("Setting host gateway route failed (%s)",
+                                                       strerror(errno));
+
+       memset(&rt, 0, sizeof(rt));
+       rt.rt_flags = RTF_UP | RTF_GATEWAY;
+
+       addr = (struct sockaddr_in *) &rt.rt_dst;
+       addr->sin_family = AF_INET;
+       addr->sin_addr.s_addr = INADDR_ANY;
+
+       addr = (struct sockaddr_in *) &rt.rt_gateway;
+       addr->sin_family = AF_INET;
+       addr->sin_addr.s_addr = inet_addr(gateway);
+
+       addr = (struct sockaddr_in *) &rt.rt_genmask;
+       addr->sin_family = AF_INET;
+       addr->sin_addr.s_addr = INADDR_ANY;
+
+       err = ioctl(sk, SIOCADDRT, &rt);
+       if (err < 0)
+               connman_error("Setting default route failed (%s)",
+                                                       strerror(errno));
+
+       close(sk);
+
+       return err;
+}
+
+static int del_route(struct connman_element *element, const char *gateway)
+{
+       struct ifreq ifr;
+       struct rtentry rt;
+       struct sockaddr_in *addr;
+       int sk, err;
+
+       DBG("element %p", element);
+
+       sk = socket(PF_INET, SOCK_DGRAM, 0);
+       if (sk < 0)
+               return -1;
+
+       memset(&ifr, 0, sizeof(ifr));
+       ifr.ifr_ifindex = element->index;
+
+       if (ioctl(sk, SIOCGIFNAME, &ifr) < 0) {
+               close(sk);
+               return -1;
+       }
+
+       DBG("ifname %s", ifr.ifr_name);
+
+       memset(&rt, 0, sizeof(rt));
+       rt.rt_flags = RTF_UP | RTF_GATEWAY;
+
+       addr = (struct sockaddr_in *) &rt.rt_dst;
+       addr->sin_family = AF_INET;
+       addr->sin_addr.s_addr = INADDR_ANY;
+
+       addr = (struct sockaddr_in *) &rt.rt_gateway;
+       addr->sin_family = AF_INET;
+       addr->sin_addr.s_addr = inet_addr(gateway);
+
+       addr = (struct sockaddr_in *) &rt.rt_genmask;
+       addr->sin_family = AF_INET;
+       addr->sin_addr.s_addr = INADDR_ANY;
+
+       err = ioctl(sk, SIOCDELRT, &rt);
+       if (err < 0)
+               connman_error("Removing default route failed (%s)",
+                                                       strerror(errno));
+
+       close(sk);
+
+       return err;
+}
+
+static DBusConnection *connection;
+
+static void emit_default_signal(struct connman_element *element)
+{
+       DBusMessage *signal;
+       DBusMessageIter entry, value;
+       const char *key = "Default";
+
+       signal = dbus_message_new_signal(element->path,
+                       CONNMAN_CONNECTION_INTERFACE, "PropertyChanged");
+       if (signal == NULL)
+               return;
+
+       dbus_message_iter_init_append(signal, &entry);
+
+       dbus_message_iter_append_basic(&entry, DBUS_TYPE_STRING, &key);
+
+       dbus_message_iter_open_container(&entry, DBUS_TYPE_VARIANT,
+                                       DBUS_TYPE_BOOLEAN_AS_STRING, &value);
+       dbus_message_iter_append_basic(&value, DBUS_TYPE_BOOLEAN,
+                                                       &element->enabled);
+       dbus_message_iter_close_container(&entry, &value);
+
+       g_dbus_send_message(connection, signal);
+}
+
+static void set_default(struct connman_element *element, gpointer user_data)
+{
+       struct gateway_data *data = user_data;
+
+       DBG("element %p name %s", element, element->name);
+
+       if (element->index != data->index)
+               return;
+
+       if (element->enabled == TRUE)
+               return;
+
+       connman_element_set_enabled(element, TRUE);
+       emit_default_signal(element);
+}
+
+static void del_default(struct connman_element *element, gpointer user_data)
+{
+       struct gateway_data *data = user_data;
+
+       DBG("element %p name %s", element, element->name);
+
+       if (element->index != data->index)
+               return;
+
+       if (element->enabled == FALSE)
+               return;
+
+       connman_element_set_enabled(element, FALSE);
+       emit_default_signal(element);
+}
+
+static void new_default(struct connman_element *element, gpointer user_data)
+{
+       struct connman_service *service;
+       const char *gateway;
+
+       DBG("element %p name %s", element, element->name);
+
+       if (g_slist_length(gateway_list) > 0)
+               return;
+
+       connman_element_get_value(element,
+                               CONNMAN_PROPERTY_ID_IPV4_GATEWAY, &gateway);
+
+       DBG("gateway %s", gateway);
+
+       if (gateway == NULL)
+               return;
+
+       if (set_route(element, gateway) < 0)
+               return;
+
+       service = __connman_element_get_service(element);
+       __connman_service_indicate_default(service);
+
+       connman_element_set_enabled(element, TRUE);
+       emit_default_signal(element);
+}
+
+static void connection_newgateway(int index, const char *gateway)
+{
+       struct gateway_data *data;
+
+       DBG("index %d gateway %s", index, gateway);
+
+       data = find_gateway(index, gateway);
+       if (data != NULL)
+               return;
+
+       data = g_try_new0(struct gateway_data, 1);
+       if (data == NULL)
+               return;
+
+       data->index = index;
+       data->gateway = g_strdup(gateway);
+
+       gateway_list = g_slist_append(gateway_list, data);
+
+       __connman_element_foreach(NULL, CONNMAN_ELEMENT_TYPE_CONNECTION,
+                                                       set_default, data);
+}
+
+static void connection_delgateway(int index, const char *gateway)
+{
+       struct gateway_data *data;
+
+       DBG("index %d gateway %s", index, gateway);
+
+       data = find_gateway(index, gateway);
+       if (data == NULL)
+               return;
+
+       gateway_list = g_slist_remove(gateway_list, data);
+
+       __connman_element_foreach(NULL, CONNMAN_ELEMENT_TYPE_CONNECTION,
+                                                       del_default, data);
+
+       g_free(data->gateway);
+       g_free(data);
+
+       if (g_slist_length(gateway_list) > 0)
+               return;
+
+       DBG("selecting new default gateway");
+
+       __connman_element_foreach(NULL, CONNMAN_ELEMENT_TYPE_CONNECTION,
+                                                       new_default, NULL);
+}
+
+static struct connman_rtnl connection_rtnl = {
+       .name           = "connection",
+       .newgateway     = connection_newgateway,
+       .delgateway     = connection_delgateway,
+};
+
+static DBusMessage *get_properties(DBusConnection *conn,
+                                       DBusMessage *msg, void *data)
+{
+       struct connman_element *element = data;
+       DBusMessage *reply;
+       DBusMessageIter array, dict;
+       connman_uint8_t strength;
+       const char *device, *network;
+       const char *type;
+
+       DBG("conn %p", conn);
+
+       if (__connman_security_check_privilege(msg,
+                                       CONNMAN_SECURITY_PRIVILEGE_PUBLIC) < 0)
+               return __connman_error_permission_denied(msg);
+
+       reply = dbus_message_new_method_return(msg);
+       if (reply == NULL)
+               return NULL;
+
+       dbus_message_iter_init_append(reply, &array);
+
+       dbus_message_iter_open_container(&array, 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);
+
+       type = connman_element_get_string(element, "Type");
+       if (type != NULL)
+               connman_dbus_dict_append_variant(&dict, "Type",
+                                               DBUS_TYPE_STRING, &type);
+
+       strength = connman_element_get_uint8(element, "Strength");
+       if (strength > 0)
+               connman_dbus_dict_append_variant(&dict, "Strength",
+                                               DBUS_TYPE_BYTE, &strength);
+
+       if (element->devname != NULL)
+               connman_dbus_dict_append_variant(&dict, "Interface",
+                                       DBUS_TYPE_STRING, &element->devname);
+
+       connman_dbus_dict_append_variant(&dict, "Default",
+                                       DBUS_TYPE_BOOLEAN, &element->enabled);
+
+       device = __connman_element_get_device_path(element);
+       if (device != NULL)
+               connman_dbus_dict_append_variant(&dict, "Device",
+                                       DBUS_TYPE_OBJECT_PATH, &device);
+
+       network = __connman_element_get_network_path(element);
+       if (network != NULL)
+               connman_dbus_dict_append_variant(&dict, "Network",
+                                       DBUS_TYPE_OBJECT_PATH, &network);
+
+       __connman_element_append_ipv4(element, &dict);
+
+       dbus_message_iter_close_container(&array, &dict);
+
+       return reply;
+}
+
+static DBusMessage *set_property(DBusConnection *conn,
+                                       DBusMessage *msg, void *data)
+{
+       DBusMessageIter iter, value;
+       const char *name;
+       int type;
+
+       DBG("conn %p", conn);
+
+       if (dbus_message_iter_init(msg, &iter) == FALSE)
+               return __connman_error_invalid_arguments(msg);
+
+       dbus_message_iter_get_basic(&iter, &name);
+       dbus_message_iter_next(&iter);
+       dbus_message_iter_recurse(&iter, &value);
+
+       if (__connman_security_check_privilege(msg,
+                                       CONNMAN_SECURITY_PRIVILEGE_MODIFY) < 0)
+               return __connman_error_permission_denied(msg);
+
+       type = dbus_message_iter_get_arg_type(&value);
+
+       return g_dbus_create_reply(msg, DBUS_TYPE_INVALID);
+}
+
+static GDBusMethodTable connection_methods[] = {
+       { "GetProperties", "",   "a{sv}", get_properties },
+       { "SetProperty",   "sv", "",      set_property   },
+       { },
+};
+
+static GDBusSignalTable connection_signals[] = {
+       { "PropertyChanged", "sv" },
+       { },
+};
+
+static void append_connections(DBusMessageIter *entry)
+{
+       DBusMessageIter value, iter;
+       const char *key = "Connections";
+
+       dbus_message_iter_append_basic(entry, DBUS_TYPE_STRING, &key);
+
+       dbus_message_iter_open_container(entry, DBUS_TYPE_VARIANT,
+               DBUS_TYPE_ARRAY_AS_STRING DBUS_TYPE_OBJECT_PATH_AS_STRING,
+                                                               &value);
+
+       dbus_message_iter_open_container(&value, DBUS_TYPE_ARRAY,
+                               DBUS_TYPE_OBJECT_PATH_AS_STRING, &iter);
+       __connman_element_list(NULL, CONNMAN_ELEMENT_TYPE_CONNECTION, &iter);
+       dbus_message_iter_close_container(&value, &iter);
+
+       dbus_message_iter_close_container(entry, &value);
+}
+
+static void emit_connections_signal(void)
+{
+       DBusMessage *signal;
+       DBusMessageIter entry;
+
+       signal = dbus_message_new_signal(CONNMAN_MANAGER_PATH,
+                               CONNMAN_MANAGER_INTERFACE, "PropertyChanged");
+       if (signal == NULL)
+               return;
+
+       dbus_message_iter_init_append(signal, &entry);
+
+       append_connections(&entry);
+
+       g_dbus_send_message(connection, signal);
+}
+
+static int register_interface(struct connman_element *element)
+{
+       DBG("element %p name %s", element, element->name);
+
+       if (g_dbus_register_interface(connection, element->path,
+                                       CONNMAN_CONNECTION_INTERFACE,
+                                       connection_methods, connection_signals,
+                                       NULL, element, NULL) == FALSE) {
+               connman_error("Failed to register %s connection", element->path);
+               return -EIO;
+       }
+
+       emit_connections_signal();
+
+       return 0;
+}
+
+static void unregister_interface(struct connman_element *element)
+{
+       DBG("element %p name %s", element, element->name);
+
+       emit_connections_signal();
+
+       g_dbus_unregister_interface(connection, element->path,
+                                               CONNMAN_CONNECTION_INTERFACE);
+}
+
+static int connection_probe(struct connman_element *element)
+{
+       struct connman_service *service;
+       const char *gateway = NULL;
+
+       DBG("element %p name %s", element, element->name);
+
+       if (element->parent == NULL)
+               return -ENODEV;
+
+       if (element->parent->type != CONNMAN_ELEMENT_TYPE_IPV4)
+               return -ENODEV;
+
+       connman_element_get_value(element,
+                               CONNMAN_PROPERTY_ID_IPV4_GATEWAY, &gateway);
+
+       DBG("gateway %s", gateway);
+
+       if (register_interface(element) < 0)
+               return -ENODEV;
+
+       service = __connman_element_get_service(element);
+       __connman_service_indicate_state(service,
+                                       CONNMAN_SERVICE_STATE_READY);
+
+       if (gateway == NULL)
+               return 0;
+
+       if (find_gateway(element->index, gateway) != NULL) {
+               DBG("previous gateway still present");
+               goto done;
+       }
+
+       if (g_slist_length(gateway_list) > 0) {
+               DBG("default gateway already present");
+               return 0;
+       }
+
+       if (set_route(element, gateway) < 0)
+               return 0;
+
+done:
+       service = __connman_element_get_service(element);
+       __connman_service_indicate_default(service);
+
+       connman_element_set_enabled(element, TRUE);
+       emit_default_signal(element);
+
+       return 0;
+}
+
+static void connection_remove(struct connman_element *element)
+{
+       struct connman_service *service;
+       const char *gateway = NULL;
+
+       DBG("element %p name %s", element, element->name);
+
+       service = __connman_element_get_service(element);
+       __connman_service_indicate_state(service,
+                                       CONNMAN_SERVICE_STATE_DISCONNECT);
+
+       unregister_interface(element);
+
+       connman_element_get_value(element,
+                               CONNMAN_PROPERTY_ID_IPV4_GATEWAY, &gateway);
+
+       DBG("gateway %s", gateway);
+
+       if (gateway == NULL)
+               return;
+
+       remove_gateway(element->index, gateway);
+
+       connman_element_set_enabled(element, FALSE);
+       emit_default_signal(element);
+
+       del_route(element, gateway);
+}
+
+static struct connman_driver connection_driver = {
+       .name           = "connection",
+       .type           = CONNMAN_ELEMENT_TYPE_CONNECTION,
+       .priority       = CONNMAN_DRIVER_PRIORITY_LOW,
+       .probe          = connection_probe,
+       .remove         = connection_remove,
+};
+
+int __connman_connection_init(void)
+{
+       DBG("");
+
+       connection = connman_dbus_get_connection();
+
+       if (connman_rtnl_register(&connection_rtnl) < 0)
+               connman_error("Failed to setup RTNL gateway driver");
+
+       connman_rtnl_send_getroute();
+
+       return connman_driver_register(&connection_driver);
+}
+
+void __connman_connection_cleanup(void)
+{
+       GSList *list;
+
+       DBG("");
+
+       connman_driver_unregister(&connection_driver);
+
+       connman_rtnl_unregister(&connection_rtnl);
+
+       for (list = gateway_list; list; list = list->next) {
+               struct gateway_data *data = list->data;
+
+               DBG("index %d gateway %s", data->index, data->gateway);
+
+               g_free(data->gateway);
+               g_free(data);
+               list->data = NULL;
+       }
+
+       g_slist_free(gateway_list);
+       gateway_list = NULL;
+
+       dbus_connection_unref(connection);
+}
diff --git a/src/connman-dbus.conf b/src/connman-dbus.conf
new file mode 100644 (file)
index 0000000..b4d4acd
--- /dev/null
@@ -0,0 +1,15 @@
+<!DOCTYPE busconfig PUBLIC "-//freedesktop//DTD D-BUS Bus Configuration 1.0//EN"
+ "http://www.freedesktop.org/standards/dbus/1.0/busconfig.dtd">
+<busconfig>
+    <policy user="root">
+        <allow own="org.moblin.connman"/>
+        <allow send_destination="org.moblin.connman"/>
+        <allow send_interface="org.moblin.connman.Agent"/>
+    </policy>
+    <policy at_console="true">
+        <allow send_destination="org.moblin.connman"/>
+    </policy>
+    <policy context="default">
+        <deny send_destination="org.moblin.connman"/>
+    </policy>
+</busconfig>
diff --git a/src/connman-polkit.conf b/src/connman-polkit.conf
new file mode 100644 (file)
index 0000000..9e44b8b
--- /dev/null
@@ -0,0 +1,11 @@
+<!DOCTYPE busconfig PUBLIC "-//freedesktop//DTD D-BUS Bus Configuration 1.0//EN"
+ "http://www.freedesktop.org/standards/dbus/1.0/busconfig.dtd">
+<busconfig>
+    <policy user="root">
+        <allow own="org.moblin.connman"/>
+        <allow send_interface="org.moblin.connman.Agent"/>
+    </policy>
+    <policy context="default">
+        <allow send_destination="org.moblin.connman"/>
+    </policy>
+</busconfig>
diff --git a/src/connman.h b/src/connman.h
new file mode 100644 (file)
index 0000000..86f1793
--- /dev/null
@@ -0,0 +1,271 @@
+/*
+ *
+ *  Connection Manager
+ *
+ *  Copyright (C) 2007-2009  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
+ *
+ */
+
+#include <glib.h>
+
+#define CONNMAN_API_SUBJECT_TO_CHANGE
+
+#include <connman/dbus.h>
+
+#define NM_SERVICE    "org.freedesktop.NetworkManager"
+#define NM_PATH       "/org/freedesktop/NetworkManager"
+#define NM_INTERFACE  NM_SERVICE
+
+int __connman_dbus_init(DBusConnection *conn);
+void __connman_dbus_cleanup(void);
+
+DBusMessage *__connman_error_failed(DBusMessage *msg, int errnum);
+DBusMessage *__connman_error_invalid_arguments(DBusMessage *msg);
+DBusMessage *__connman_error_permission_denied(DBusMessage *msg);
+DBusMessage *__connman_error_not_supported(DBusMessage *msg);
+DBusMessage *__connman_error_not_implemented(DBusMessage *msg);
+DBusMessage *__connman_error_no_carrier(DBusMessage *msg);
+DBusMessage *__connman_error_in_progress(DBusMessage *msg);
+DBusMessage *__connman_error_already_connected(DBusMessage *msg);
+DBusMessage *__connman_error_operation_aborted(DBusMessage *msg);
+DBusMessage *__connman_error_operation_timeout(DBusMessage *msg);
+DBusMessage *__connman_error_invalid_service(DBusMessage *msg);
+
+int __connman_selftest(void);
+
+int __connman_manager_init(DBusConnection *conn, gboolean compat);
+void __connman_manager_cleanup(void);
+
+int __connman_agent_init(DBusConnection *conn);
+void __connman_agent_cleanup(void);
+
+int __connman_agent_register(const char *sender, const char *path);
+int __connman_agent_unregister(const char *sender, const char *path);
+
+int __connman_profile_init(DBusConnection *conn);
+void __connman_profile_cleanup(void);
+
+void __connman_profile_list(DBusMessageIter *iter);
+const char *__connman_profile_active_ident(void);
+const char *__connman_profile_active_path(void);
+
+void __connman_profile_changed(void);
+
+#include <connman/log.h>
+
+int __connman_log_init(gboolean detach, gboolean debug);
+void __connman_log_cleanup(void);
+
+void __connman_toggle_debug(void);
+gboolean __connman_debug_enabled(void);
+
+#include <connman/option.h>
+
+#include <connman/plugin.h>
+
+int __connman_plugin_init(const char *pattern, const char *exclude);
+void __connman_plugin_cleanup(void);
+
+#include <connman/security.h>
+
+int __connman_security_check_privilege(DBusMessage *message,
+                               enum connman_security_privilege privilege);
+
+#include <connman/ipv4.h>
+
+const char *__connman_ipv4_method2string(enum connman_ipv4_method method);
+enum connman_ipv4_method __connman_ipv4_string2method(const char *method);
+
+#include <connman/inet.h>
+
+#include <connman/rfkill.h>
+
+int __connman_rfkill_init(void);
+void __connman_rfkill_cleanup(void);
+
+#include <connman/ipconfig.h>
+
+#include <connman/resolver.h>
+
+int __connman_resolver_init(void);
+void __connman_resolver_cleanup(void);
+
+int __connman_resolver_selftest(void);
+
+#include <connman/storage.h>
+
+int __connman_storage_init(void);
+void __connman_storage_cleanup(void);
+
+int __connman_storage_init_device();
+int __connman_storage_load_device(struct connman_device *device);
+int __connman_storage_save_device(struct connman_device *device);
+int __connman_storage_init_network(struct connman_device *device);
+int __connman_storage_load_network(struct connman_network *network);
+int __connman_storage_save_network(struct connman_network *network);
+int __connman_storage_init_service();
+int __connman_storage_load_service(struct connman_service *service);
+int __connman_storage_save_service(struct connman_service *service);
+
+#include <connman/driver.h>
+
+void __connman_driver_rescan(struct connman_driver *driver);
+
+#include <connman/element.h>
+
+int __connman_element_init(DBusConnection *conn, const char *device,
+                                                       const char *nodevice);
+void __connman_element_start(void);
+void __connman_element_stop(void);
+void __connman_element_cleanup(void);
+
+void __connman_element_initialize(struct connman_element *element);
+
+typedef void (* element_cb_t) (struct connman_element *element,
+                                                       gpointer user_data);
+
+void __connman_element_foreach(struct connman_element *element,
+                               enum connman_element_type type,
+                               element_cb_t callback, gpointer user_data);
+void __connman_element_list(struct connman_element *element,
+                                       enum connman_element_type type,
+                                                       DBusMessageIter *iter);
+int __connman_element_count(struct connman_element *element,
+                                       enum connman_element_type type);
+
+struct connman_service *__connman_element_get_service(struct connman_element *element);
+struct connman_device *__connman_element_get_device(struct connman_element *element);
+const char *__connman_element_get_device_path(struct connman_element *element);
+const char *__connman_element_get_network_path(struct connman_element *element);
+
+const char *__connman_element_type2string(enum connman_element_type type);
+
+static inline void __connman_element_lock(struct connman_element *element)
+{
+}
+
+static inline void __connman_element_unlock(struct connman_element *element)
+{
+}
+
+int __connman_element_append_ipv4(struct connman_element *element,
+                                               DBusMessageIter *dict);
+int __connman_element_set_ipv4(struct connman_element *element,
+                               const char *name, DBusMessageIter *value);
+
+int __connman_detect_init(void);
+void __connman_detect_cleanup(void);
+
+int __connman_ipv4_init(void);
+void __connman_ipv4_cleanup(void);
+
+int __connman_connection_init(void);
+void __connman_connection_cleanup(void);
+
+#ifdef HAVE_UDEV
+int __connman_udev_init(void);
+void __connman_udev_cleanup(void);
+#else
+static inline int __connman_udev_init(void)
+{
+       return 0;
+}
+
+static inline void __connman_udev_cleanup(void)
+{
+}
+#endif
+
+#include <connman/device.h>
+
+int __connman_device_init(void);
+void __connman_device_cleanup(void);
+
+void __connman_device_increase_connections(struct connman_device *device);
+void __connman_device_decrease_connections(struct connman_device *device);
+
+void __connman_device_set_network(struct connman_device *device,
+                                       struct connman_network *network);
+
+int __connman_device_connect(struct connman_device *device);
+int __connman_device_disconnect(struct connman_device *device);
+
+connman_bool_t __connman_device_has_driver(struct connman_device *device);
+
+const char *__connman_device_get_type(struct connman_device *device);
+const char *__connman_device_get_ident(struct connman_device *device);
+
+int __connman_device_set_offlinemode(connman_bool_t offlinemode);
+
+int __connman_profile_add_device(struct connman_device *device);
+int __connman_profile_remove_device(struct connman_device *device);
+
+#include <connman/network.h>
+
+int __connman_network_init(void);
+void __connman_network_cleanup(void);
+
+void __connman_network_set_device(struct connman_network *network,
+                                       struct connman_device *device);
+
+int __connman_network_connect(struct connman_network *network);
+int __connman_network_disconnect(struct connman_network *network);
+
+connman_bool_t __connman_network_has_driver(struct connman_network *network);
+
+const char *__connman_network_get_type(struct connman_network *network);
+const char *__connman_network_get_group(struct connman_network *network);
+const char *__connman_network_get_ident(struct connman_network *network);
+
+int __connman_profile_add_network(struct connman_network *network);
+int __connman_profile_remove_network(struct connman_network *network);
+
+#include <connman/service.h>
+
+int __connman_service_init(void);
+void __connman_service_cleanup(void);
+
+void __connman_service_list(DBusMessageIter *iter);
+
+void __connman_service_put(struct connman_service *service);
+
+struct connman_service *__connman_service_lookup_from_device(struct connman_device *device);
+struct connman_service *__connman_service_create_from_device(struct connman_device *device);
+
+struct connman_service *__connman_service_lookup_from_network(struct connman_network *network);
+struct connman_service *__connman_service_create_from_network(struct connman_network *network);
+
+int __connman_service_set_carrier(struct connman_service *service,
+                                               connman_bool_t carrier);
+int __connman_service_indicate_state(struct connman_service *service,
+                                       enum connman_service_state state);
+int __connman_service_indicate_default(struct connman_service *service);
+
+#include <connman/notifier.h>
+
+int __connman_notifier_init(void);
+void __connman_notifier_cleanup(void);
+
+void __connman_notifier_device_type_increase(enum connman_device_type type);
+void __connman_notifier_device_type_decrease(enum connman_device_type type);
+void __connman_notifier_offline_mode(connman_bool_t enabled);
+
+#include <connman/rtnl.h>
+
+int __connman_rtnl_init(void);
+void __connman_rtnl_cleanup(void);
+
+int __connman_rtnl_send(const void *buf, size_t len);
diff --git a/src/connman.rules b/src/connman.rules
new file mode 100644 (file)
index 0000000..05ec054
--- /dev/null
@@ -0,0 +1,18 @@
+
+SUBSYSTEM=="rfkill", ENV{CONNMAN_TYPE}="rfkill"
+
+SUBSYSTEM=="net", KERNEL=="eth*", ENV{CONNMAN_TYPE}="ethernet"
+SUBSYSTEM=="net", KERNEL=="wlan*", ENV{CONNMAN_TYPE}="wifi"
+
+SUBSYSTEM=="net", DRIVERS=="hso", ENV{CONNMAN_TYPE}="hso"
+
+SUBSYSTEM=="tty", KERNEL=="noz[0-9]*", ENV{CONNMAN_TYPE}="nozomi"
+
+SUBSYSTEM=="tty", ATTRS{idVendor}=="12d1", ATTRS{idProduct}=="1003", \
+                                               ENV{CONNMAN_TYPE}="huawei"
+
+SUBSYSTEM=="tty", ATTRS{idVendor}=="1410", ATTRS{idProduct}=="4400", \
+                                               ENV{CONNMAN_TYPE}="novatel"
+
+ENV{CONNMAN_TYPE}=="?*", ENV{CONNMAN_INTERFACE}="$kernel", \
+                               RUN+="socket:@/org/moblin/connman/udev"
diff --git a/src/connman.service.in b/src/connman.service.in
new file mode 100644 (file)
index 0000000..e514c77
--- /dev/null
@@ -0,0 +1,4 @@
+[D-BUS Service]
+Name=org.moblin.connman
+Exec=@prefix@/sbin/connmand
+User=root
diff --git a/src/dbus.c b/src/dbus.c
new file mode 100644 (file)
index 0000000..daa2239
--- /dev/null
@@ -0,0 +1,166 @@
+/*
+ *
+ *  Connection Manager
+ *
+ *  Copyright (C) 2007-2009  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 <string.h>
+
+#include "connman.h"
+
+char *connman_dbus_encode_string(const char *value)
+{
+       GString *str;
+       unsigned int i, size;
+
+       if (value == NULL)
+               return NULL;
+
+       size = strlen(value);
+
+       str = g_string_new(NULL);
+       if (str == NULL)
+               return NULL;
+
+       for (i = 0; i < size; i++) {
+               const char tmp = value[i];
+               if ((tmp < '0' || tmp > '9') && (tmp < 'A' || tmp > 'Z') &&
+                                               (tmp < 'a' || tmp > 'z'))
+                       g_string_append_printf(str, "_%02x", tmp);
+               else
+                       str = g_string_append_c(str, tmp);
+       }
+
+       return g_string_free(str, FALSE);
+}
+
+void connman_dbus_property_append_variant(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_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);
+}
+
+void connman_dbus_dict_append_array(DBusMessageIter *dict,
+                               const char *key, int type, void *val, int len)
+{
+       DBusMessageIter entry, value, array;
+       const char *variant_sig, *array_sig;
+
+       switch (type) {
+       case DBUS_TYPE_BYTE:
+               variant_sig = DBUS_TYPE_ARRAY_AS_STRING DBUS_TYPE_BYTE_AS_STRING;
+               array_sig = DBUS_TYPE_BYTE_AS_STRING;
+               break;
+       default:
+               return;
+       }
+
+       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,
+                                                       variant_sig, &value);
+
+       dbus_message_iter_open_container(&value, DBUS_TYPE_ARRAY,
+                                                       array_sig, &array);
+       dbus_message_iter_append_fixed_array(&array, type, val, len);
+       dbus_message_iter_close_container(&value, &array);
+
+       dbus_message_iter_close_container(&entry, &value);
+
+       dbus_message_iter_close_container(dict, &entry);
+}
+
+void connman_dbus_dict_append_variant(DBusMessageIter *dict,
+                                       const char *key, int type, void *val)
+{
+       DBusMessageIter entry;
+
+       dbus_message_iter_open_container(dict, DBUS_TYPE_DICT_ENTRY,
+                                                               NULL, &entry);
+
+       connman_dbus_property_append_variant(&entry, key, type, val);
+
+       dbus_message_iter_close_container(dict, &entry);
+}
+
+static DBusConnection *connection = NULL;
+
+DBusConnection *connman_dbus_get_connection(void)
+{
+       if (connection == NULL)
+               return NULL;
+
+       return dbus_connection_ref(connection);
+}
+
+int __connman_dbus_init(DBusConnection *conn)
+{
+       connection = conn;
+
+       return 0;
+}
+
+void __connman_dbus_cleanup(void)
+{
+       connection = NULL;
+}
diff --git a/src/detect.c b/src/detect.c
new file mode 100644 (file)
index 0000000..f239d85
--- /dev/null
@@ -0,0 +1,125 @@
+/*
+ *
+ *  Connection Manager
+ *
+ *  Copyright (C) 2007-2009  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 <glib.h>
+
+#include "connman.h"
+
+static GSList *device_list = NULL;
+
+static struct connman_device *find_device(int index)
+{
+       GSList *list;
+
+       for (list = device_list; list; list = list->next) {
+               struct connman_device *device = list->data;
+
+               if (connman_device_get_index(device) == index)
+                       return device;
+       }
+
+       return NULL;
+}
+
+static void detect_newlink(unsigned short type, int index,
+                                       unsigned flags, unsigned change)
+{
+       struct connman_device *device;
+
+       DBG("type %d index %d", type, index);
+
+       device = find_device(index);
+       if (device != NULL)
+               return;
+
+       device = connman_inet_create_device(index);
+       if (device == NULL)
+               return;
+
+       if (connman_device_register(device) < 0) {
+               connman_device_unref(device);
+               return;
+       }
+
+       device_list = g_slist_append(device_list, device);
+}
+
+static void detect_dellink(unsigned short type, int index,
+                                       unsigned flags, unsigned change)
+{
+       struct connman_device *device;
+
+       DBG("type %d index %d", type, index);
+
+       device = find_device(index);
+       if (device == NULL)
+               return;
+
+       device_list = g_slist_remove(device_list, device);
+
+       connman_device_unregister(device);
+       connman_device_unref(device);
+}
+
+static struct connman_rtnl detect_rtnl = {
+       .name           = "detect",
+       .priority       = CONNMAN_RTNL_PRIORITY_LOW,
+       .newlink        = detect_newlink,
+       .dellink        = detect_dellink,
+};
+
+int __connman_detect_init(void)
+{
+       int err;
+
+       DBG("");
+
+       err = connman_rtnl_register(&detect_rtnl);
+       if (err < 0)
+               return err;
+
+       connman_rtnl_send_getlink();
+
+       return 0;
+}
+
+void __connman_detect_cleanup(void)
+{
+       GSList *list;
+
+       DBG("");
+
+       connman_rtnl_unregister(&detect_rtnl);
+
+       for (list = device_list; list; list = list->next) {
+               struct connman_device *device = list->data;
+
+               connman_device_unregister(device);
+               connman_device_unref(device);
+       }
+
+       g_slist_free(device_list);
+       device_list = NULL;
+}
diff --git a/src/device.c b/src/device.c
new file mode 100644 (file)
index 0000000..cd9105d
--- /dev/null
@@ -0,0 +1,2018 @@
+/*
+ *
+ *  Connection Manager
+ *
+ *  Copyright (C) 2007-2009  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 <errno.h>
+#include <string.h>
+
+#include <gdbus.h>
+
+#include "connman.h"
+
+struct connman_device {
+       struct connman_element element;
+       enum connman_device_type type;
+       enum connman_device_mode mode;
+       enum connman_device_policy policy;
+       connman_bool_t secondary;
+       connman_bool_t powered;
+       connman_bool_t carrier;
+       connman_bool_t scanning;
+       connman_bool_t disconnected;
+       connman_uint8_t priority;
+       connman_uint16_t scan_interval;
+       char *name;
+       char *node;
+       char *address;
+       char *interface;
+       char *ident;
+       unsigned int connections;
+       guint scan_timeout;
+
+       struct connman_device_driver *driver;
+       void *driver_data;
+
+       connman_bool_t registered;
+
+       char *last_network;
+       struct connman_network *network;
+       GHashTable *networks;
+};
+
+static gboolean device_scan_trigger(gpointer user_data)
+{
+       struct connman_device *device = user_data;
+
+       DBG("device %p", device);
+
+       if (device->driver == NULL) {
+               device->scan_timeout = 0;
+               return FALSE;
+       }
+
+       if (device->driver->scan)
+               device->driver->scan(device);
+
+       return TRUE;
+}
+
+static const char *type2description(enum connman_device_type type)
+{
+       switch (type) {
+       case CONNMAN_DEVICE_TYPE_UNKNOWN:
+       case CONNMAN_DEVICE_TYPE_VENDOR:
+               break;
+       case CONNMAN_DEVICE_TYPE_ETHERNET:
+               return "Ethernet";
+       case CONNMAN_DEVICE_TYPE_WIFI:
+               return "Wireless";
+       case CONNMAN_DEVICE_TYPE_WIMAX:
+               return "WiMAX";
+       case CONNMAN_DEVICE_TYPE_BLUETOOTH:
+               return "Bluetooth";
+       case CONNMAN_DEVICE_TYPE_GPS:
+               return "GPS";
+       case CONNMAN_DEVICE_TYPE_HSO:
+       case CONNMAN_DEVICE_TYPE_NOZOMI:
+       case CONNMAN_DEVICE_TYPE_HUAWEI:
+       case CONNMAN_DEVICE_TYPE_NOVATEL:
+               return "Cellular";
+       }
+
+       return NULL;
+}
+
+static const char *type2string(enum connman_device_type type)
+{
+       switch (type) {
+       case CONNMAN_DEVICE_TYPE_UNKNOWN:
+       case CONNMAN_DEVICE_TYPE_VENDOR:
+               break;
+       case CONNMAN_DEVICE_TYPE_ETHERNET:
+               return "ethernet";
+       case CONNMAN_DEVICE_TYPE_WIFI:
+               return "wifi";
+       case CONNMAN_DEVICE_TYPE_WIMAX:
+               return "wimax";
+       case CONNMAN_DEVICE_TYPE_BLUETOOTH:
+               return "bluetooth";
+       case CONNMAN_DEVICE_TYPE_GPS:
+               return "gps";
+       case CONNMAN_DEVICE_TYPE_HSO:
+       case CONNMAN_DEVICE_TYPE_HUAWEI:
+       case CONNMAN_DEVICE_TYPE_NOZOMI:
+       case CONNMAN_DEVICE_TYPE_NOVATEL:
+               return "cellular";
+       }
+
+       return NULL;
+}
+
+static const char *policy2string(enum connman_device_policy policy)
+{
+       switch (policy) {
+       case CONNMAN_DEVICE_POLICY_UNKNOWN:
+               break;
+       case CONNMAN_DEVICE_POLICY_IGNORE:
+               return "ignore";
+       case CONNMAN_DEVICE_POLICY_OFF:
+               return "off";
+       case CONNMAN_DEVICE_POLICY_AUTO:
+               return "auto";
+       case CONNMAN_DEVICE_POLICY_MANUAL:
+               return "manual";
+       }
+
+       return NULL;
+}
+
+static enum connman_device_policy string2policy(const char *policy)
+{
+       if (g_str_equal(policy, "ignore") == TRUE)
+               return CONNMAN_DEVICE_POLICY_IGNORE;
+       else if (g_str_equal(policy, "off") == TRUE)
+               return CONNMAN_DEVICE_POLICY_OFF;
+       else if (g_str_equal(policy, "auto") == TRUE)
+               return CONNMAN_DEVICE_POLICY_AUTO;
+       else if (g_str_equal(policy, "manual") == TRUE)
+               return CONNMAN_DEVICE_POLICY_MANUAL;
+       else
+               return CONNMAN_DEVICE_POLICY_UNKNOWN;
+}
+
+static int set_carrier(struct connman_device *device, connman_bool_t carrier)
+{
+       struct connman_service *service;
+
+       service = __connman_service_lookup_from_device(device);
+       __connman_service_set_carrier(service, carrier);
+
+       if (carrier == TRUE) {
+               enum connman_element_type type = CONNMAN_ELEMENT_TYPE_UNKNOWN;
+               struct connman_element *element;
+
+               device->disconnected = TRUE;
+
+               switch (device->policy) {
+               case CONNMAN_DEVICE_POLICY_UNKNOWN:
+               case CONNMAN_DEVICE_POLICY_IGNORE:
+               case CONNMAN_DEVICE_POLICY_OFF:
+               case CONNMAN_DEVICE_POLICY_MANUAL:
+                       return 0;
+               case CONNMAN_DEVICE_POLICY_AUTO:
+                       break;
+               }
+
+               switch (device->element.ipv4.method) {
+               case CONNMAN_IPV4_METHOD_UNKNOWN:
+               case CONNMAN_IPV4_METHOD_OFF:
+                       return 0;
+               case CONNMAN_IPV4_METHOD_STATIC:
+                       type = CONNMAN_ELEMENT_TYPE_IPV4;
+                       break;
+               case CONNMAN_IPV4_METHOD_DHCP:
+                       type = CONNMAN_ELEMENT_TYPE_DHCP;
+                       break;
+               }
+
+               element = connman_element_create(NULL);
+               if (element != NULL) {
+                       element->type  = type;
+                       element->index = device->element.index;
+
+                       if (connman_element_register(element,
+                                                       &device->element) < 0)
+                               connman_element_unref(element);
+
+                       device->disconnected = FALSE;
+
+                       __connman_service_indicate_state(service,
+                                       CONNMAN_SERVICE_STATE_CONFIGURATION);
+               }
+       } else
+               connman_element_unregister_children(&device->element);
+
+       return 0;
+}
+
+static int set_powered(struct connman_device *device, connman_bool_t powered)
+{
+       struct connman_device_driver *driver = device->driver;
+       int err;
+
+       DBG("device %p powered %d", device, powered);
+
+       if (!driver)
+               return -EINVAL;
+
+       if (powered == TRUE) {
+               if (driver->enable) {
+                       err = driver->enable(device);
+                       __connman_notifier_device_type_increase(device->type);
+               } else
+                       err = -EINVAL;
+       } else {
+               g_hash_table_remove_all(device->networks);
+
+               set_carrier(device, FALSE);
+
+               if (driver->disable) {
+                       err = driver->disable(device);
+                       __connman_notifier_device_type_decrease(device->type);
+               } else
+                       err = -EINVAL;
+       }
+
+       return err;
+}
+
+static int set_policy(DBusConnection *connection,
+                               struct connman_device *device,
+                                       enum connman_device_policy policy)
+{
+       DBusMessage *signal;
+       DBusMessageIter entry, value;
+       const char *str, *key = "Policy";
+       int err = 0;
+
+       DBG("device %p policy %d", device, policy);
+
+       if (device->policy == policy)
+               return 0;
+
+       switch (policy) {
+       case CONNMAN_DEVICE_POLICY_UNKNOWN:
+               return -EINVAL;
+       case CONNMAN_DEVICE_POLICY_IGNORE:
+               break;
+       case CONNMAN_DEVICE_POLICY_OFF:
+               if (device->powered == TRUE)
+                       err = set_powered(device, FALSE);
+               break;
+       case CONNMAN_DEVICE_POLICY_AUTO:
+       case CONNMAN_DEVICE_POLICY_MANUAL:
+               if (device->powered == FALSE)
+                       err = set_powered(device, TRUE);
+               else
+                       err = set_carrier(device, device->carrier);
+               break;
+       }
+
+       if (err < 0)
+               return err;
+
+       device->policy = policy;
+
+       signal = dbus_message_new_signal(device->element.path,
+                               CONNMAN_DEVICE_INTERFACE, "PropertyChanged");
+       if (signal == NULL)
+               return 0;
+
+       dbus_message_iter_init_append(signal, &entry);
+
+       dbus_message_iter_append_basic(&entry, DBUS_TYPE_STRING, &key);
+
+       str = policy2string(policy);
+
+       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);
+
+       g_dbus_send_message(connection, signal);
+
+       return 0;
+}
+
+static void append_path(gpointer key, gpointer value, gpointer user_data)
+{
+       struct connman_element *element = value;
+       DBusMessageIter *iter = user_data;
+
+       dbus_message_iter_append_basic(iter, DBUS_TYPE_OBJECT_PATH,
+                                                       &element->path);
+}
+
+static void append_networks(struct connman_device *device,
+                                               DBusMessageIter *entry)
+{
+       DBusMessageIter value, iter;
+       const char *key = "Networks";
+
+       dbus_message_iter_append_basic(entry, DBUS_TYPE_STRING, &key);
+
+       dbus_message_iter_open_container(entry, DBUS_TYPE_VARIANT,
+               DBUS_TYPE_ARRAY_AS_STRING DBUS_TYPE_OBJECT_PATH_AS_STRING,
+                                                               &value);
+
+       dbus_message_iter_open_container(&value, DBUS_TYPE_ARRAY,
+                               DBUS_TYPE_OBJECT_PATH_AS_STRING, &iter);
+       g_hash_table_foreach(device->networks, append_path, &iter);
+       dbus_message_iter_close_container(&value, &iter);
+
+       dbus_message_iter_close_container(entry, &value);
+}
+
+static DBusMessage *get_properties(DBusConnection *conn,
+                                       DBusMessage *msg, void *data)
+{
+       struct connman_device *device = data;
+       DBusMessage *reply;
+       DBusMessageIter array, dict, entry;
+       const char *str;
+
+       DBG("conn %p", conn);
+
+       if (__connman_security_check_privilege(msg,
+                                       CONNMAN_SECURITY_PRIVILEGE_PUBLIC) < 0)
+               return __connman_error_permission_denied(msg);
+
+       reply = dbus_message_new_method_return(msg);
+       if (reply == NULL)
+               return NULL;
+
+       dbus_message_iter_init_append(reply, &array);
+
+       dbus_message_iter_open_container(&array, 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);
+
+       if (device->name != NULL)
+               connman_dbus_dict_append_variant(&dict, "Name",
+                                       DBUS_TYPE_STRING, &device->name);
+
+       str = type2string(device->type);
+       if (str != NULL)
+               connman_dbus_dict_append_variant(&dict, "Type",
+                                               DBUS_TYPE_STRING, &str);
+
+       if (device->address != NULL)
+               connman_dbus_dict_append_variant(&dict, "Address",
+                                       DBUS_TYPE_STRING, &device->address);
+
+       if (device->interface != NULL)
+               connman_dbus_dict_append_variant(&dict, "Interface",
+                                       DBUS_TYPE_STRING, &device->interface);
+
+       str = policy2string(device->policy);
+       if (str != NULL)
+               connman_dbus_dict_append_variant(&dict, "Policy",
+                                               DBUS_TYPE_STRING, &str);
+
+       if (device->priority > 0)
+               connman_dbus_dict_append_variant(&dict, "Priority",
+                                       DBUS_TYPE_BYTE, &device->priority);
+
+       connman_dbus_dict_append_variant(&dict, "Powered",
+                                       DBUS_TYPE_BOOLEAN, &device->powered);
+
+       if (device->driver && device->driver->scan)
+               connman_dbus_dict_append_variant(&dict, "Scanning",
+                                       DBUS_TYPE_BOOLEAN, &device->scanning);
+
+       switch (device->mode) {
+       case CONNMAN_DEVICE_MODE_UNKNOWN:
+               break;
+       case CONNMAN_DEVICE_MODE_TRANSPORT_IP:
+               __connman_element_append_ipv4(&device->element, &dict);
+               break;
+       case CONNMAN_DEVICE_MODE_NETWORK_SINGLE:
+       case CONNMAN_DEVICE_MODE_NETWORK_MULTIPLE:
+               if (device->scan_interval > 0)
+                       connman_dbus_dict_append_variant(&dict, "ScanInterval",
+                               DBUS_TYPE_UINT16, &device->scan_interval);
+
+               dbus_message_iter_open_container(&dict, DBUS_TYPE_DICT_ENTRY,
+                                                               NULL, &entry);
+               append_networks(device, &entry);
+               dbus_message_iter_close_container(&dict, &entry);
+               break;
+       }
+
+       dbus_message_iter_close_container(&array, &dict);
+
+       return reply;
+}
+
+static DBusMessage *set_property(DBusConnection *conn,
+                                       DBusMessage *msg, void *data)
+{
+       struct connman_device *device = data;
+       DBusMessageIter iter, value;
+       const char *name;
+       int type;
+
+       DBG("conn %p", conn);
+
+       if (dbus_message_iter_init(msg, &iter) == FALSE)
+               return __connman_error_invalid_arguments(msg);
+
+       dbus_message_iter_get_basic(&iter, &name);
+       dbus_message_iter_next(&iter);
+       dbus_message_iter_recurse(&iter, &value);
+
+       if (__connman_security_check_privilege(msg,
+                                       CONNMAN_SECURITY_PRIVILEGE_MODIFY) < 0)
+               return __connman_error_permission_denied(msg);
+
+       type = dbus_message_iter_get_arg_type(&value);
+
+       if (g_str_equal(name, "Powered") == TRUE) {
+               connman_bool_t powered;
+               int err;
+
+               if (type != DBUS_TYPE_BOOLEAN)
+                       return __connman_error_invalid_arguments(msg);
+
+               dbus_message_iter_get_basic(&value, &powered);
+
+               if (device->powered == powered)
+                       return __connman_error_invalid_arguments(msg);
+
+               err = set_powered(device, powered);
+               if (err < 0 && err != -EINPROGRESS)
+                       return __connman_error_failed(msg, -err);
+       } else if (g_str_equal(name, "Policy") == TRUE) {
+               enum connman_device_policy policy;
+               const char *str;
+               int err;
+
+               if (type != DBUS_TYPE_STRING)
+                       return __connman_error_invalid_arguments(msg);
+
+               dbus_message_iter_get_basic(&value, &str);
+               policy = string2policy(str);
+               if (policy == CONNMAN_DEVICE_POLICY_UNKNOWN)
+                       return __connman_error_invalid_arguments(msg);
+
+               err = set_policy(conn, device, policy);
+               if (err < 0)
+                       return __connman_error_failed(msg, -err);
+       } else if (g_str_equal(name, "Priority") == TRUE) {
+               connman_uint8_t priority;
+
+               if (type != DBUS_TYPE_BYTE)
+                       return __connman_error_invalid_arguments(msg);
+
+               dbus_message_iter_get_basic(&value, &priority);
+
+               device->priority = priority;
+       } else if (g_str_equal(name, "ScanInterval") == TRUE) {
+               connman_uint16_t interval;
+
+               switch (device->mode) {
+               case CONNMAN_DEVICE_MODE_UNKNOWN:
+               case CONNMAN_DEVICE_MODE_TRANSPORT_IP:
+                       return __connman_error_invalid_arguments(msg);
+               case CONNMAN_DEVICE_MODE_NETWORK_SINGLE:
+               case CONNMAN_DEVICE_MODE_NETWORK_MULTIPLE:
+                       break;
+               }
+
+               if (type != DBUS_TYPE_UINT16)
+                       return __connman_error_invalid_arguments(msg);
+
+               dbus_message_iter_get_basic(&value, &interval);
+
+               device->scan_interval = interval;
+
+               if (device->scan_timeout > 0) {
+                       g_source_remove(device->scan_timeout);
+                       device->scan_timeout = 0;
+               }
+
+               if (device->scan_interval > 0) {
+                       guint interval = device->scan_interval;
+                       device->scan_timeout = g_timeout_add_seconds(interval,
+                                               device_scan_trigger, device);
+               }
+       } else if (g_str_has_prefix(name, "IPv4") == TRUE) {
+               switch (device->mode) {
+               case CONNMAN_DEVICE_MODE_UNKNOWN:
+               case CONNMAN_DEVICE_MODE_NETWORK_SINGLE:
+               case CONNMAN_DEVICE_MODE_NETWORK_MULTIPLE:
+                       return __connman_error_invalid_arguments(msg);
+               case CONNMAN_DEVICE_MODE_TRANSPORT_IP:
+                       __connman_element_set_ipv4(&device->element,
+                                                               name, &value);
+                       break;
+               }
+       }
+
+       __connman_storage_save_device(device);
+
+       return g_dbus_create_reply(msg, DBUS_TYPE_INVALID);
+}
+
+static DBusMessage *join_network(DBusConnection *conn,
+                                       DBusMessage *msg, void *data)
+{
+       struct connman_device *device = data;
+       struct connman_network *network;
+       enum connman_network_type type;
+       DBusMessageIter iter, array;
+       int err, index;
+
+       DBG("conn %p", conn);
+
+       if (__connman_security_check_privilege(msg,
+                                       CONNMAN_SECURITY_PRIVILEGE_MODIFY) < 0)
+               return __connman_error_permission_denied(msg);
+
+       if (!device->driver || !device->driver->join)
+               return __connman_error_not_supported(msg);
+
+       dbus_message_iter_init(msg, &iter);
+       dbus_message_iter_recurse(&iter, &array);
+
+       switch (device->type) {
+       case CONNMAN_DEVICE_TYPE_WIFI:
+               type = CONNMAN_NETWORK_TYPE_WIFI;
+               break;
+       default:
+               return __connman_error_not_supported(msg);
+       }
+
+       network = connman_network_create("00_00_00_00_00_00", type);
+       if (network == NULL)
+               return __connman_error_failed(msg, ENOMEM);
+
+       while (dbus_message_iter_get_arg_type(&array) == DBUS_TYPE_DICT_ENTRY) {
+               DBusMessageIter entry, value;
+               const char *key, *str;
+
+               dbus_message_iter_recurse(&array, &entry);
+               dbus_message_iter_get_basic(&entry, &key);
+
+               dbus_message_iter_next(&entry);
+               dbus_message_iter_recurse(&entry, &value);
+
+               switch (dbus_message_iter_get_arg_type(&value)) {
+               case DBUS_TYPE_STRING:
+                       dbus_message_iter_get_basic(&value, &str);
+                       if (g_str_equal(key, "WiFi.SSID") == TRUE)
+                               connman_network_set_blob(network, key,
+                                                       str, strlen(str));
+                       else
+                               connman_network_set_string(network, key, str);
+                       break;
+               }
+
+               dbus_message_iter_next(&array);
+       }
+
+       index = connman_device_get_index(device);
+       connman_network_set_index(network, index);
+
+       connman_network_set_protocol(network, CONNMAN_NETWORK_PROTOCOL_IP);
+
+       err = device->driver->join(device, network);
+
+       connman_network_unref(network);
+
+       if (err < 0)
+               return __connman_error_failed(msg, -err);
+
+       return g_dbus_create_reply(msg, DBUS_TYPE_INVALID);
+}
+
+static DBusMessage *create_network(DBusConnection *conn,
+                                       DBusMessage *msg, void *data)
+{
+       DBG("conn %p", conn);
+
+       if (__connman_security_check_privilege(msg,
+                                       CONNMAN_SECURITY_PRIVILEGE_MODIFY) < 0)
+               return __connman_error_permission_denied(msg);
+
+       return __connman_error_invalid_arguments(msg);
+}
+
+static DBusMessage *remove_network(DBusConnection *conn,
+                                       DBusMessage *msg, void *data)
+{
+       DBG("conn %p", conn);
+
+       if (__connman_security_check_privilege(msg,
+                                       CONNMAN_SECURITY_PRIVILEGE_MODIFY) < 0)
+               return __connman_error_permission_denied(msg);
+
+       return __connman_error_invalid_arguments(msg);
+}
+
+static DBusMessage *propose_scan(DBusConnection *conn,
+                                       DBusMessage *msg, void *data)
+{
+       struct connman_device *device = data;
+       int err;
+
+       DBG("conn %p", conn);
+
+       switch (device->mode) {
+       case CONNMAN_DEVICE_MODE_UNKNOWN:
+       case CONNMAN_DEVICE_MODE_TRANSPORT_IP:
+               return __connman_error_not_supported(msg);
+       case CONNMAN_DEVICE_MODE_NETWORK_SINGLE:
+       case CONNMAN_DEVICE_MODE_NETWORK_MULTIPLE:
+               break;
+       }
+
+       if (!device->driver || !device->driver->scan)
+               return __connman_error_not_supported(msg);
+
+       if (device->powered == FALSE)
+               return __connman_error_failed(msg, EINVAL);
+
+       err = device->driver->scan(device);
+       if (err < 0)
+               return __connman_error_failed(msg, -err);
+
+       return g_dbus_create_reply(msg, DBUS_TYPE_INVALID);
+}
+
+static GDBusMethodTable device_methods[] = {
+       { "GetProperties", "",      "a{sv}", get_properties },
+       { "SetProperty",   "sv",    "",      set_property   },
+       { "JoinNetwork",   "a{sv}", "",      join_network   },
+       { "CreateNetwork", "a{sv}", "o",     create_network },
+       { "RemoveNetwork", "o",     "",      remove_network },
+       { "ProposeScan",   "",      "",      propose_scan   },
+       { },
+};
+
+static GDBusSignalTable device_signals[] = {
+       { "PropertyChanged", "sv" },
+       { },
+};
+
+static DBusConnection *connection;
+
+static void append_devices(DBusMessageIter *entry)
+{
+       DBusMessageIter value, iter;
+       const char *key = "Devices";
+
+       dbus_message_iter_append_basic(entry, DBUS_TYPE_STRING, &key);
+
+       dbus_message_iter_open_container(entry, DBUS_TYPE_VARIANT,
+               DBUS_TYPE_ARRAY_AS_STRING DBUS_TYPE_OBJECT_PATH_AS_STRING,
+                                                               &value);
+
+       dbus_message_iter_open_container(&value, DBUS_TYPE_ARRAY,
+                               DBUS_TYPE_OBJECT_PATH_AS_STRING, &iter);
+       __connman_element_list(NULL, CONNMAN_ELEMENT_TYPE_DEVICE, &iter);
+       dbus_message_iter_close_container(&value, &iter);
+
+       dbus_message_iter_close_container(entry, &value);
+}
+
+static void emit_devices_signal(void)
+{
+       DBusMessage *signal;
+       DBusMessageIter entry;
+
+       signal = dbus_message_new_signal(CONNMAN_MANAGER_PATH,
+                               CONNMAN_MANAGER_INTERFACE, "PropertyChanged");
+       if (signal == NULL)
+               return;
+
+       dbus_message_iter_init_append(signal, &entry);
+
+       append_devices(&entry);
+
+       g_dbus_send_message(connection, signal);
+}
+
+static int register_interface(struct connman_element *element)
+{
+       struct connman_device *device = element->device;
+
+       DBG("element %p name %s", element, element->name);
+
+       if (g_dbus_register_interface(connection, element->path,
+                                       CONNMAN_DEVICE_INTERFACE,
+                                       device_methods, device_signals,
+                                       NULL, device, NULL) == FALSE) {
+               connman_error("Failed to register %s device", element->path);
+               return -EIO;
+       }
+
+       device->registered = TRUE;
+
+       emit_devices_signal();
+
+       return 0;
+}
+
+static void unregister_interface(struct connman_element *element)
+{
+       struct connman_device *device = element->device;
+
+       DBG("element %p name %s", element, element->name);
+
+       device->registered = FALSE;
+
+       emit_devices_signal();
+
+       g_dbus_unregister_interface(connection, element->path,
+                                               CONNMAN_DEVICE_INTERFACE);
+}
+
+static void device_enable(struct connman_device *device)
+{
+       DBG("device %p", device);
+
+       if (device->policy == CONNMAN_DEVICE_POLICY_IGNORE ||
+                               device->policy == CONNMAN_DEVICE_POLICY_OFF)
+               return;
+
+       if (device->powered == TRUE)
+               return;
+
+       if (device->driver->enable) {
+               device->driver->enable(device);
+               __connman_notifier_device_type_increase(device->type);
+       }
+}
+
+static void device_disable(struct connman_device *device)
+{
+       DBG("device %p", device);
+
+       if (device->policy == CONNMAN_DEVICE_POLICY_IGNORE)
+               return;
+
+       if (device->powered == FALSE)
+               return;
+
+       g_hash_table_remove_all(device->networks);
+
+       if (device->driver->disable) {
+               device->driver->disable(device);
+               __connman_notifier_device_type_decrease(device->type);
+       }
+}
+
+static int setup_device(struct connman_device *device)
+{
+       int err;
+
+       DBG("device %p", device);
+
+       err = register_interface(&device->element);
+       if (err < 0) {
+               if (device->driver->remove)
+                       device->driver->remove(device);
+               device->driver = NULL;
+               return err;
+       }
+
+       switch (device->mode) {
+       case CONNMAN_DEVICE_MODE_UNKNOWN:
+       case CONNMAN_DEVICE_MODE_NETWORK_SINGLE:
+       case CONNMAN_DEVICE_MODE_NETWORK_MULTIPLE:
+               break;
+       case CONNMAN_DEVICE_MODE_TRANSPORT_IP:
+               if (device->secondary == FALSE)
+                       __connman_profile_add_device(device);
+               break;
+       }
+
+       device_enable(device);
+
+       return 0;
+}
+
+static void probe_driver(struct connman_element *element, gpointer user_data)
+{
+       struct connman_device_driver *driver = user_data;
+
+       DBG("element %p name %s", element, element->name);
+
+       if (element->device == NULL)
+               return;
+
+       if (element->device->driver != NULL)
+               return;
+
+       if (driver->type != element->device->type)
+               return;
+
+       if (driver->probe(element->device) < 0)
+               return;
+
+       element->device->driver = driver;
+
+       setup_device(element->device);
+}
+
+static void remove_device(struct connman_device *device)
+{
+       DBG("device %p", device);
+
+       device_disable(device);
+
+       switch (device->mode) {
+       case CONNMAN_DEVICE_MODE_UNKNOWN:
+       case CONNMAN_DEVICE_MODE_NETWORK_SINGLE:
+       case CONNMAN_DEVICE_MODE_NETWORK_MULTIPLE:
+               break;
+       case CONNMAN_DEVICE_MODE_TRANSPORT_IP:
+               if (device->secondary == FALSE)
+                       __connman_profile_remove_device(device);
+               break;
+       }
+
+       unregister_interface(&device->element);
+
+       if (device->driver->remove)
+               device->driver->remove(device);
+
+       device->driver = NULL;
+}
+
+static void remove_driver(struct connman_element *element, gpointer user_data)
+{
+       struct connman_device_driver *driver = user_data;
+
+       DBG("element %p name %s", element, element->name);
+
+       if (element->device == NULL)
+               return;
+
+       if (element->device->driver == driver)
+               remove_device(element->device);
+}
+
+connman_bool_t __connman_device_has_driver(struct connman_device *device)
+{
+       if (device == NULL || device->driver == NULL)
+               return FALSE;
+
+       return device->registered;
+}
+
+static GSList *driver_list = NULL;
+
+static gint compare_priority(gconstpointer a, gconstpointer b)
+{
+       const struct connman_device_driver *driver1 = a;
+       const struct connman_device_driver *driver2 = b;
+
+       return driver2->priority - driver1->priority;
+}
+
+/**
+ * connman_device_driver_register:
+ * @driver: device driver definition
+ *
+ * Register a new device driver
+ *
+ * Returns: %0 on success
+ */
+int connman_device_driver_register(struct connman_device_driver *driver)
+{
+       DBG("driver %p name %s", driver, driver->name);
+
+       driver_list = g_slist_insert_sorted(driver_list, driver,
+                                                       compare_priority);
+
+       __connman_element_foreach(NULL, CONNMAN_ELEMENT_TYPE_DEVICE,
+                                               probe_driver, driver);
+
+       return 0;
+}
+
+/**
+ * connman_device_driver_unregister:
+ * @driver: device driver definition
+ *
+ * Remove a previously registered device driver
+ */
+void connman_device_driver_unregister(struct connman_device_driver *driver)
+{
+       DBG("driver %p name %s", driver, driver->name);
+
+       driver_list = g_slist_remove(driver_list, driver);
+
+       __connman_element_foreach(NULL, CONNMAN_ELEMENT_TYPE_DEVICE,
+                                               remove_driver, driver);
+}
+
+static void unregister_network(gpointer data)
+{
+       struct connman_network *network = data;
+
+       DBG("network %p", network);
+
+       connman_element_unregister((struct connman_element *) network);
+
+       connman_network_unref(network);
+}
+
+static void device_destruct(struct connman_element *element)
+{
+       struct connman_device *device = element->device;
+
+       DBG("element %p name %s", element, element->name);
+
+       g_free(device->ident);
+       g_free(device->node);
+       g_free(device->name);
+       g_free(device->address);
+       g_free(device->interface);
+
+       g_free(device->last_network);
+
+       g_hash_table_destroy(device->networks);
+       device->networks = NULL;
+}
+
+/**
+ * connman_device_create:
+ * @node: device node name (for example an address)
+ * @type: device type
+ *
+ * Allocate a new device of given #type and assign the #node name to it.
+ *
+ * Returns: a newly-allocated #connman_device structure
+ */
+struct connman_device *connman_device_create(const char *node,
+                                               enum connman_device_type type)
+{
+       struct connman_device *device;
+       const char *str;
+
+       DBG("node %s type %d", node, type);
+
+       device = g_try_new0(struct connman_device, 1);
+       if (device == NULL)
+               return NULL;
+
+       DBG("device %p", device);
+
+       __connman_element_initialize(&device->element);
+
+       device->element.name = g_strdup(node);
+       device->element.type = CONNMAN_ELEMENT_TYPE_DEVICE;
+
+       device->element.device = device;
+       device->element.destruct = device_destruct;
+
+       str = type2string(type);
+       if (str != NULL)
+               connman_element_set_string(&device->element,
+                                       CONNMAN_PROPERTY_ID_TYPE, str);
+
+       device->element.ipv4.method = CONNMAN_IPV4_METHOD_DHCP;
+
+       device->type      = type;
+       device->name      = g_strdup(type2description(device->type));
+       device->mode      = CONNMAN_DEVICE_MODE_UNKNOWN;
+       device->policy    = CONNMAN_DEVICE_POLICY_AUTO;
+       device->secondary = FALSE;
+
+       switch (type) {
+       case CONNMAN_DEVICE_TYPE_UNKNOWN:
+       case CONNMAN_DEVICE_TYPE_VENDOR:
+               device->priority = 0;
+               device->scan_interval = 0;
+               break;
+       case CONNMAN_DEVICE_TYPE_ETHERNET:
+       case CONNMAN_DEVICE_TYPE_WIFI:
+               device->priority = 100;
+               device->scan_interval = 300;
+               break;
+       case CONNMAN_DEVICE_TYPE_WIMAX:
+               device->priority = 20;
+               device->scan_interval = 0;
+               break;
+       case CONNMAN_DEVICE_TYPE_BLUETOOTH:
+               device->priority = 50;
+               device->scan_interval = 0;
+               break;
+       case CONNMAN_DEVICE_TYPE_GPS:
+               device->priority = 0;
+               device->scan_interval = 0;
+               break;
+       case CONNMAN_DEVICE_TYPE_HSO:
+       case CONNMAN_DEVICE_TYPE_NOZOMI:
+       case CONNMAN_DEVICE_TYPE_HUAWEI:
+       case CONNMAN_DEVICE_TYPE_NOVATEL:
+               device->priority = 60;
+               device->scan_interval = 0;
+               break;
+       }
+
+       device->networks = g_hash_table_new_full(g_str_hash, g_str_equal,
+                                               g_free, unregister_network);
+
+       return device;
+}
+
+/**
+ * connman_device_ref:
+ * @device: device structure
+ *
+ * Increase reference counter of device
+ */
+struct connman_device *connman_device_ref(struct connman_device *device)
+{
+       if (connman_element_ref(&device->element) == NULL)
+               return NULL;
+
+       return device;
+}
+
+/**
+ * connman_device_unref:
+ * @device: device structure
+ *
+ * Decrease reference counter of device
+ */
+void connman_device_unref(struct connman_device *device)
+{
+       connman_element_unref(&device->element);
+}
+
+const char *__connman_device_get_type(struct connman_device *device)
+{
+       return type2string(device->type);
+}
+
+/**
+ * connman_device_get_type:
+ * @device: device structure
+ *
+ * Get type of device
+ */
+enum connman_device_type connman_device_get_type(struct connman_device *device)
+{
+       return device->type;
+}
+
+/**
+ * connman_device_get_name:
+ * @device: device structure
+ *
+ * Get unique name of device
+ */
+const char *connman_device_get_name(struct connman_device *device)
+{
+       return device->element.name;
+}
+
+/**
+ * connman_device_get_path:
+ * @device: device structure
+ *
+ * Get path name of device
+ */
+const char *connman_device_get_path(struct connman_device *device)
+{
+       return device->element.path;
+}
+
+/**
+ * connman_device_set_index:
+ * @device: device structure
+ * @index: index number
+ *
+ * Set index number of device
+ */
+void connman_device_set_index(struct connman_device *device, int index)
+{
+       device->element.index = index;
+}
+
+/**
+ * connman_device_get_index:
+ * @device: device structure
+ *
+ * Get index number of device
+ */
+int connman_device_get_index(struct connman_device *device)
+{
+       return device->element.index;
+}
+
+/**
+ * connman_device_set_interface:
+ * @device: device structure
+ * @interface: interface name
+ *
+ * Set interface name of device
+ */
+void connman_device_set_interface(struct connman_device *device,
+                                                       const char *interface)
+{
+       g_free(device->element.devname);
+       device->element.devname = g_strdup(interface);
+
+       g_free(device->interface);
+       device->interface = g_strdup(interface);
+
+       if (device->name == NULL) {
+               const char *str = type2description(device->type);
+               if (str != NULL && device->interface != NULL)
+                       device->name = g_strdup_printf("%s (%s)", str,
+                                                       device->interface);
+       }
+}
+
+/**
+ * connman_device_get_interface:
+ * @device: device structure
+ *
+ * Get interface name of device
+ */
+const char *connman_device_get_interface(struct connman_device *device)
+{
+       return device->interface;
+}
+
+/**
+ * connman_device_set_ident:
+ * @device: device structure
+ * @ident: unique identifier
+ *
+ * Set unique identifier of device
+ */
+void connman_device_set_ident(struct connman_device *device,
+                                                       const char *ident)
+{
+       g_free(device->ident);
+       device->ident = g_strdup(ident);
+}
+
+const char *__connman_device_get_ident(struct connman_device *device)
+{
+       return device->ident;
+}
+
+/**
+ * connman_device_set_policy:
+ * @device: device structure
+ * @policy: power and connection policy
+ *
+ * Change power and connection policy of device
+ */
+void connman_device_set_policy(struct connman_device *device,
+                                       enum connman_device_policy policy)
+{
+       device->policy = policy;
+}
+
+/**
+ * connman_device_set_mode:
+ * @device: device structure
+ * @mode: network mode
+ *
+ * Change network mode of device
+ */
+void connman_device_set_mode(struct connman_device *device,
+                                               enum connman_device_mode mode)
+{
+       device->mode = mode;
+}
+
+/**
+ * connman_device_get_mode:
+ * @device: device structure
+ *
+ * Get network mode of device
+ */
+enum connman_device_mode connman_device_get_mode(struct connman_device *device)
+{
+       return device->mode;
+}
+
+/**
+ * connman_device_set_secondary:
+ * @device: device structure
+ * @secondary: secondary value
+ *
+ * Change secondary value of device
+ */
+void connman_device_set_secondary(struct connman_device *device,
+                                                connman_bool_t secondary)
+{
+       device->secondary = secondary;
+}
+
+/**
+ * connman_device_get_secondary:
+ * @device: device structure
+ *
+ * Get secondary value of device
+ */
+connman_bool_t connman_device_get_secondary(struct connman_device *device)
+{
+       return device->secondary;
+}
+
+/**
+ * connman_device_set_powered:
+ * @device: device structure
+ * @powered: powered state
+ *
+ * Change power state of device
+ */
+int connman_device_set_powered(struct connman_device *device,
+                                               connman_bool_t powered)
+{
+       DBusMessage *signal;
+       DBusMessageIter entry, value;
+       const char *key = "Powered";
+
+       DBG("driver %p powered %d", device, powered);
+
+       if (device->powered == powered)
+               return -EALREADY;
+
+       device->powered = powered;
+
+       if (device->registered == FALSE)
+               return 0;
+
+       signal = dbus_message_new_signal(device->element.path,
+                               CONNMAN_DEVICE_INTERFACE, "PropertyChanged");
+       if (signal == NULL)
+               return 0;
+
+       dbus_message_iter_init_append(signal, &entry);
+
+       dbus_message_iter_append_basic(&entry, DBUS_TYPE_STRING, &key);
+
+       dbus_message_iter_open_container(&entry, DBUS_TYPE_VARIANT,
+                                       DBUS_TYPE_BOOLEAN_AS_STRING, &value);
+       dbus_message_iter_append_basic(&value, DBUS_TYPE_BOOLEAN, &powered);
+       dbus_message_iter_close_container(&entry, &value);
+
+       g_dbus_send_message(connection, signal);
+
+       if (powered == FALSE)
+               return 0;
+
+       if (device->policy != CONNMAN_DEVICE_POLICY_AUTO)
+               return 0;
+
+       if (device->scan_timeout > 0) {
+               g_source_remove(device->scan_timeout);
+               device->scan_timeout = 0;
+       }
+
+       if (device->scan_interval > 0) {
+               guint interval = device->scan_interval;
+               device->scan_timeout = g_timeout_add_seconds(interval,
+                                       device_scan_trigger, device);
+       }
+
+       if (device->driver->scan)
+               device->driver->scan(device);
+
+       return 0;
+}
+
+/**
+ * connman_device_set_carrier:
+ * @device: device structure
+ * @carrier: carrier state
+ *
+ * Change carrier state of device (only for device without scanning)
+ */
+int connman_device_set_carrier(struct connman_device *device,
+                                               connman_bool_t carrier)
+{
+       DBG("driver %p carrier %d", device, carrier);
+
+       switch (device->mode) {
+       case CONNMAN_DEVICE_MODE_UNKNOWN:
+       case CONNMAN_DEVICE_MODE_NETWORK_SINGLE:
+       case CONNMAN_DEVICE_MODE_NETWORK_MULTIPLE:
+               return -EINVAL;
+       case CONNMAN_DEVICE_MODE_TRANSPORT_IP:
+               break;
+       }
+
+       if (device->carrier == carrier)
+               return -EALREADY;
+
+       device->carrier = carrier;
+
+       return set_carrier(device, device->carrier);
+}
+
+int __connman_device_connect(struct connman_device *device)
+{
+       DBG("device %p", device);
+
+       if (device->disconnected == FALSE)
+               return -EINVAL;
+
+       return 0;
+}
+
+int __connman_device_disconnect(struct connman_device *device)
+{
+       GHashTableIter iter;
+       gpointer key, value;
+
+       DBG("device %p", device);
+
+       connman_device_set_disconnected(device, TRUE);
+
+       g_hash_table_iter_init(&iter, device->networks);
+
+       while (g_hash_table_iter_next(&iter, &key, &value) == TRUE) {
+               struct connman_network *network = value;
+
+               if (connman_network_get_connected(network) == TRUE)
+                       __connman_network_disconnect(network);
+       }
+
+       return 0;
+}
+
+static void connect_known_network(struct connman_device *device)
+{
+       struct connman_network *network = NULL;
+       GHashTableIter iter;
+       gpointer key, value;
+       const char *name;
+       unsigned int count = 0;
+
+       DBG("device %p", device);
+
+       g_hash_table_iter_init(&iter, device->networks);
+
+       while (g_hash_table_iter_next(&iter, &key, &value) == TRUE) {
+               connman_uint8_t old_strength, new_strength;
+
+               count++;
+
+               if (connman_network_get_available(value) == FALSE)
+                       continue;
+
+               name = connman_network_get_string(value,
+                                               CONNMAN_PROPERTY_ID_NAME);
+               if (name != NULL && device->last_network != NULL) {
+                       if (g_str_equal(name, device->last_network) == TRUE) {
+                               network = value;
+                               break;
+                       }
+               }
+
+               if (network == NULL) {
+                       network = value;
+                       continue;
+               }
+
+               old_strength = connman_network_get_uint8(network,
+                                               CONNMAN_PROPERTY_ID_STRENGTH);
+               new_strength = connman_network_get_uint8(value,
+                                               CONNMAN_PROPERTY_ID_STRENGTH);
+
+               if (new_strength > old_strength)
+                       network = value;
+       }
+
+       if (network != NULL) {
+               int err;
+
+               name = connman_network_get_string(value,
+                                               CONNMAN_PROPERTY_ID_NAME);
+               if (name != NULL) {
+                       err = __connman_network_connect(network);
+                       if (err == 0 || err == -EINPROGRESS)
+                               return;
+               }
+       }
+
+       if (count > 0)
+               return;
+
+       if (device->driver && device->driver->scan)
+               device->driver->scan(device);
+}
+
+static void mark_network_unavailable(gpointer key, gpointer value,
+                                                       gpointer user_data)
+{
+       struct connman_network *network = value;
+
+       if (connman_network_get_connected(network) == TRUE)
+               return;
+
+       connman_network_set_available(network, FALSE);
+}
+
+static gboolean remove_unavailable_network(gpointer key, gpointer value,
+                                                       gpointer user_data)
+{
+       struct connman_network *network = value;
+
+       if (connman_network_get_connected(network) == TRUE)
+               return FALSE;
+
+       if (connman_network_get_available(network) == TRUE)
+               return FALSE;
+
+       return TRUE;
+}
+
+/**
+ * connman_device_set_scanning:
+ * @device: device structure
+ * @scanning: scanning state
+ *
+ * Change scanning state of device
+ */
+int connman_device_set_scanning(struct connman_device *device,
+                                               connman_bool_t scanning)
+{
+       DBusMessage *signal;
+       DBusMessageIter entry, value;
+       const char *key = "Scanning";
+
+       DBG("driver %p scanning %d", device, scanning);
+
+       if (!device->driver || !device->driver->scan)
+               return -EINVAL;
+
+       if (device->scanning == scanning)
+               return -EALREADY;
+
+       device->scanning = scanning;
+
+       signal = dbus_message_new_signal(device->element.path,
+                               CONNMAN_DEVICE_INTERFACE, "PropertyChanged");
+       if (signal == NULL)
+               return 0;
+
+       dbus_message_iter_init_append(signal, &entry);
+
+       dbus_message_iter_append_basic(&entry, DBUS_TYPE_STRING, &key);
+
+       dbus_message_iter_open_container(&entry, DBUS_TYPE_VARIANT,
+                                       DBUS_TYPE_BOOLEAN_AS_STRING, &value);
+       dbus_message_iter_append_basic(&value, DBUS_TYPE_BOOLEAN, &scanning);
+       dbus_message_iter_close_container(&entry, &value);
+
+       g_dbus_send_message(connection, signal);
+
+       if (scanning == TRUE) {
+               if (device->scan_timeout > 0) {
+                       g_source_remove(device->scan_timeout);
+                       device->scan_timeout = 0;
+               }
+
+               if (device->scan_interval > 0) {
+                       guint interval = device->scan_interval;
+                       device->scan_timeout = g_timeout_add_seconds(interval,
+                                               device_scan_trigger, device);
+               }
+
+               g_hash_table_foreach(device->networks,
+                                       mark_network_unavailable, NULL);
+               return 0;
+       }
+
+       g_hash_table_foreach_remove(device->networks,
+                                       remove_unavailable_network, NULL);
+
+       if (device->connections > 0)
+               return 0;
+
+       if (device->disconnected == TRUE)
+               return 0;
+
+       if (device->policy != CONNMAN_DEVICE_POLICY_AUTO)
+               return 0;
+
+       connect_known_network(device);
+
+       return 0;
+}
+
+/**
+ * connman_device_set_disconnected:
+ * @device: device structure
+ * @disconnected: disconnected state
+ *
+ * Change disconnected state of device (only for device with networks)
+ */
+int connman_device_set_disconnected(struct connman_device *device,
+                                               connman_bool_t disconnected)
+{
+       DBG("driver %p disconnected %d", device, disconnected);
+
+       switch (device->mode) {
+       case CONNMAN_DEVICE_MODE_UNKNOWN:
+       case CONNMAN_DEVICE_MODE_TRANSPORT_IP:
+               return -EINVAL;
+       case CONNMAN_DEVICE_MODE_NETWORK_SINGLE:
+       case CONNMAN_DEVICE_MODE_NETWORK_MULTIPLE:
+               break;
+       }
+
+       if (device->disconnected == disconnected)
+               return -EALREADY;
+
+       device->disconnected = disconnected;
+
+       return 0;
+}
+
+/**
+ * connman_device_set_string:
+ * @device: device structure
+ * @key: unique identifier
+ * @value: string value
+ *
+ * Set string value for specific key
+ */
+int connman_device_set_string(struct connman_device *device,
+                                       const char *key, const char *value)
+{
+       DBG("device %p key %s value %s", device, key, value);
+
+       if (g_str_equal(key, "Address") == TRUE) {
+               g_free(device->address);
+               device->address = g_strdup(value);
+       } else if (g_str_equal(key, "Name") == TRUE) {
+               g_free(device->name);
+               device->name = g_strdup(value);
+       } else if (g_str_equal(key, "Node") == TRUE) {
+               g_free(device->node);
+               device->node = g_strdup(value);
+       }
+
+       return connman_element_set_string(&device->element, key, value);
+}
+
+/**
+ * connman_device_get_string:
+ * @device: device structure
+ * @key: unique identifier
+ *
+ * Get string value for specific key
+ */
+const char *connman_device_get_string(struct connman_device *device,
+                                                       const char *key)
+{
+       DBG("device %p key %s", device, key);
+
+       if (g_str_equal(key, "Address") == TRUE)
+               return device->address;
+       else if (g_str_equal(key, "Name") == TRUE)
+               return device->name;
+       else if (g_str_equal(key, "Node") == TRUE)
+               return device->node;
+
+       return connman_element_get_string(&device->element, key);
+}
+
+static void set_offlinemode(struct connman_element *element, gpointer user_data)
+{
+       struct connman_device *device = element->device;
+       connman_bool_t offlinemode = GPOINTER_TO_UINT(user_data);
+       connman_bool_t powered;
+
+       DBG("element %p name %s", element, element->name);
+
+       if (device == NULL)
+               return;
+
+       powered = (offlinemode == TRUE) ? FALSE : TRUE;
+
+       if (device->powered == powered)
+               return;
+
+       set_powered(device, powered);
+}
+
+int __connman_device_set_offlinemode(connman_bool_t offlinemode)
+{
+       DBG("offlinmode %d", offlinemode);
+
+       __connman_element_foreach(NULL, CONNMAN_ELEMENT_TYPE_DEVICE,
+                       set_offlinemode, GUINT_TO_POINTER(offlinemode));
+
+       __connman_notifier_offline_mode(offlinemode);
+
+       return 0;
+}
+
+void __connman_device_increase_connections(struct connman_device *device)
+{
+       device->connections++;
+}
+
+void __connman_device_decrease_connections(struct connman_device *device)
+{
+       device->connections--;
+}
+
+/**
+ * connman_device_add_network:
+ * @device: device structure
+ * @network: network structure
+ *
+ * Add new network to the device
+ */
+int connman_device_add_network(struct connman_device *device,
+                                       struct connman_network *network)
+{
+       const char *identifier = connman_network_get_identifier(network);
+       int err;
+
+       DBG("device %p network %p", device, network);
+
+       switch (device->mode) {
+       case CONNMAN_DEVICE_MODE_UNKNOWN:
+       case CONNMAN_DEVICE_MODE_TRANSPORT_IP:
+               return -EINVAL;
+       case CONNMAN_DEVICE_MODE_NETWORK_SINGLE:
+       case CONNMAN_DEVICE_MODE_NETWORK_MULTIPLE:
+               break;
+       }
+
+       __connman_network_set_device(network, device);
+
+       __connman_storage_load_network(network);
+
+       err = connman_element_register((struct connman_element *) network,
+                                                       &device->element);
+       if (err < 0) {
+               __connman_network_set_device(network, NULL);
+               return err;
+       }
+
+       g_hash_table_insert(device->networks, g_strdup(identifier),
+                                                               network);
+
+       return 0;
+}
+
+/**
+ * connman_device_get_network:
+ * @device: device structure
+ * @identifier: network identifier
+ *
+ * Get network for given identifier
+ */
+struct connman_network *connman_device_get_network(struct connman_device *device,
+                                                       const char *identifier)
+{
+       DBG("device %p identifier %s", device, identifier);
+
+       return g_hash_table_lookup(device->networks, identifier);
+}
+
+/**
+ * connman_device_remove_network:
+ * @device: device structure
+ * @identifier: network identifier
+ *
+ * Remove network for given identifier
+ */
+int connman_device_remove_network(struct connman_device *device,
+                                                       const char *identifier)
+{
+       DBG("device %p identifier %s", device, identifier);
+
+       g_hash_table_remove(device->networks, identifier);
+
+       return 0;
+}
+
+void __connman_device_set_network(struct connman_device *device,
+                                       struct connman_network *network)
+{
+       const char *name;
+
+       if (device->network == network)
+               return;
+
+       if (device->network != NULL)
+               connman_network_unref(device->network);
+
+       if (network != NULL) {
+               name = connman_network_get_string(network,
+                                               CONNMAN_PROPERTY_ID_NAME);
+               g_free(device->last_network);
+               device->last_network = g_strdup(name);
+
+               device->network = connman_network_ref(network);
+       } else {
+               g_free(device->last_network);
+               device->last_network = NULL;
+
+               device->network = NULL;
+       }
+}
+
+/**
+ * connman_device_register:
+ * @device: device structure
+ *
+ * Register device with the system
+ */
+int connman_device_register(struct connman_device *device)
+{
+       __connman_storage_load_device(device);
+
+       switch (device->mode) {
+       case CONNMAN_DEVICE_MODE_UNKNOWN:
+       case CONNMAN_DEVICE_MODE_TRANSPORT_IP:
+               break;
+       case CONNMAN_DEVICE_MODE_NETWORK_SINGLE:
+       case CONNMAN_DEVICE_MODE_NETWORK_MULTIPLE:
+               __connman_storage_init_network(device);
+               break;
+       }
+
+       return connman_element_register(&device->element, NULL);
+}
+
+/**
+ * connman_device_unregister:
+ * @device: device structure
+ *
+ * Unregister device with the system
+ */
+void connman_device_unregister(struct connman_device *device)
+{
+       __connman_storage_save_device(device);
+
+       connman_element_unregister(&device->element);
+}
+
+/**
+ * connman_device_get_data:
+ * @device: device structure
+ *
+ * Get private device data pointer
+ */
+void *connman_device_get_data(struct connman_device *device)
+{
+       return device->driver_data;
+}
+
+/**
+ * connman_device_set_data:
+ * @device: device structure
+ * @data: data pointer
+ *
+ * Set private device data pointer
+ */
+void connman_device_set_data(struct connman_device *device, void *data)
+{
+       device->driver_data = data;
+}
+
+static gboolean match_driver(struct connman_device *device,
+                                       struct connman_device_driver *driver)
+{
+       if (device->type == driver->type ||
+                       driver->type == CONNMAN_DEVICE_TYPE_UNKNOWN)
+               return TRUE;
+
+       return FALSE;
+}
+
+static int device_probe(struct connman_element *element)
+{
+       struct connman_device *device = element->device;
+       GSList *list;
+
+       DBG("element %p name %s", element, element->name);
+
+       if (device == NULL)
+               return -ENODEV;
+
+       if (device->driver != NULL)
+               return -EALREADY;
+
+       for (list = driver_list; list; list = list->next) {
+               struct connman_device_driver *driver = list->data;
+
+               if (match_driver(device, driver) == FALSE)
+                       continue;
+
+               DBG("driver %p name %s", driver, driver->name);
+
+               if (driver->probe(device) == 0) {
+                       device->driver = driver;
+                       break;
+               }
+       }
+
+       if (device->driver == NULL)
+               return -ENODEV;
+
+       return setup_device(device);
+}
+
+static void device_remove(struct connman_element *element)
+{
+       struct connman_device *device = element->device;
+
+       DBG("element %p name %s", element, element->name);
+
+       if (device == NULL)
+               return;
+
+       if (device->driver == NULL)
+               return;
+
+       remove_device(device);
+}
+
+static struct connman_driver device_driver = {
+       .name           = "device",
+       .type           = CONNMAN_ELEMENT_TYPE_DEVICE,
+       .priority       = CONNMAN_DRIVER_PRIORITY_LOW,
+       .probe          = device_probe,
+       .remove         = device_remove,
+};
+
+static int device_load(struct connman_device *device)
+{
+       GKeyFile *keyfile;
+       gchar *pathname, *identifier, *data = NULL;
+       gsize length;
+       char *str;
+       int val;
+
+       DBG("device %p", device);
+
+       pathname = g_strdup_printf("%s/%s.conf", STORAGEDIR,
+                                       __connman_profile_active_ident());
+       if (pathname == NULL)
+               return -ENOMEM;
+
+       keyfile = g_key_file_new();
+
+       if (g_file_get_contents(pathname, &data, &length, NULL) == FALSE) {
+               g_free(pathname);
+               return -ENOENT;
+       }
+
+       g_free(pathname);
+
+       if (g_key_file_load_from_data(keyfile, data, length,
+                                                       0, NULL) == FALSE) {
+               g_free(data);
+               return -EILSEQ;
+       }
+
+       g_free(data);
+
+       identifier = g_strdup_printf("device_%s", device->element.name);
+       if (identifier == NULL)
+               goto done;
+
+       str = g_key_file_get_string(keyfile, identifier, "Policy", NULL);
+       if (str != NULL) {
+               device->policy = string2policy(str);
+               g_free(str);
+       }
+
+       switch (device->mode) {
+       case CONNMAN_DEVICE_MODE_UNKNOWN:
+       case CONNMAN_DEVICE_MODE_TRANSPORT_IP:
+               break;
+       case CONNMAN_DEVICE_MODE_NETWORK_SINGLE:
+       case CONNMAN_DEVICE_MODE_NETWORK_MULTIPLE:
+               val = g_key_file_get_integer(keyfile, identifier,
+                                                       "ScanInterval", NULL);
+               if (val > 0)
+                       device->scan_interval = val;
+               break;
+       }
+
+done:
+       g_key_file_free(keyfile);
+
+       g_free(identifier);
+
+       return 0;
+}
+
+static int device_save(struct connman_device *device)
+{
+       GKeyFile *keyfile;
+       gchar *pathname, *identifier = NULL, *data = NULL;
+       gsize length;
+       const char *str;
+
+       DBG("device %p", device);
+
+       pathname = g_strdup_printf("%s/%s.conf", STORAGEDIR,
+                                       __connman_profile_active_ident());
+       if (pathname == NULL)
+               return -ENOMEM;
+
+       keyfile = g_key_file_new();
+
+       if (g_file_get_contents(pathname, &data, &length, NULL) == FALSE)
+               goto update;
+
+       if (length > 0) {
+               if (g_key_file_load_from_data(keyfile, data, length,
+                                                       0, NULL) == FALSE)
+                       goto done;
+       }
+
+       g_free(data);
+
+update:
+       identifier = g_strdup_printf("device_%s", device->element.name);
+       if (identifier == NULL)
+               goto done;
+
+       str = policy2string(device->policy);
+       if (str != NULL)
+               g_key_file_set_string(keyfile, identifier, "Policy", str);
+
+       switch (device->mode) {
+       case CONNMAN_DEVICE_MODE_UNKNOWN:
+       case CONNMAN_DEVICE_MODE_TRANSPORT_IP:
+               break;
+       case CONNMAN_DEVICE_MODE_NETWORK_SINGLE:
+       case CONNMAN_DEVICE_MODE_NETWORK_MULTIPLE:
+               if (device->scan_interval > 0)
+                       g_key_file_set_integer(keyfile, identifier,
+                                       "ScanInterval", device->scan_interval);
+               break;
+       }
+
+       data = g_key_file_to_data(keyfile, &length, NULL);
+
+       if (g_file_set_contents(pathname, data, length, NULL) == FALSE)
+               connman_error("Failed to store device information");
+
+done:
+       g_free(data);
+
+       g_key_file_free(keyfile);
+
+       g_free(identifier);
+       g_free(pathname);
+
+       return 0;
+}
+
+static struct connman_storage device_storage = {
+       .name           = "device",
+       .priority       = CONNMAN_STORAGE_PRIORITY_LOW,
+       .device_load    = device_load,
+       .device_save    = device_save,
+};
+
+int __connman_device_init(void)
+{
+       DBG("");
+
+       connection = connman_dbus_get_connection();
+
+       if (connman_storage_register(&device_storage) < 0)
+               connman_error("Failed to register device storage");
+
+       return connman_driver_register(&device_driver);
+}
+
+void __connman_device_cleanup(void)
+{
+       DBG("");
+
+       connman_driver_unregister(&device_driver);
+
+       connman_storage_unregister(&device_storage);
+
+       dbus_connection_unref(connection);
+}
diff --git a/src/element.c b/src/element.c
new file mode 100644 (file)
index 0000000..e670864
--- /dev/null
@@ -0,0 +1,1472 @@
+/*
+ *
+ *  Connection Manager
+ *
+ *  Copyright (C) 2007-2009  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 <errno.h>
+#include <stdarg.h>
+#include <string.h>
+
+#include <glib.h>
+#include <gdbus.h>
+
+#include "connman.h"
+
+static DBusConnection *connection;
+
+static GNode *element_root = NULL;
+static GSList *driver_list = NULL;
+static gchar *device_filter = NULL;
+
+static gboolean started = FALSE;
+
+static const char *type2string(enum connman_element_type type)
+{
+       switch (type) {
+       case CONNMAN_ELEMENT_TYPE_UNKNOWN:
+               return "unknown";
+       case CONNMAN_ELEMENT_TYPE_ROOT:
+               return "root";
+       case CONNMAN_ELEMENT_TYPE_PROFILE:
+               return "profile";
+       case CONNMAN_ELEMENT_TYPE_DEVICE:
+               return "device";
+       case CONNMAN_ELEMENT_TYPE_NETWORK:
+               return "network";
+       case CONNMAN_ELEMENT_TYPE_SERVICE:
+               return "service";
+       case CONNMAN_ELEMENT_TYPE_PPP:
+               return "ppp";
+       case CONNMAN_ELEMENT_TYPE_IPV4:
+               return "ipv4";
+       case CONNMAN_ELEMENT_TYPE_IPV6:
+               return "ipv6";
+       case CONNMAN_ELEMENT_TYPE_DHCP:
+               return "dhcp";
+       case CONNMAN_ELEMENT_TYPE_BOOTP:
+               return "bootp";
+       case CONNMAN_ELEMENT_TYPE_ZEROCONF:
+               return "zeroconf";
+       case CONNMAN_ELEMENT_TYPE_CONNECTION:
+               return "connection";
+       case CONNMAN_ELEMENT_TYPE_VENDOR:
+               return "vendor";
+       }
+
+       return NULL;
+}
+
+const char *__connman_ipv4_method2string(enum connman_ipv4_method method)
+{
+       switch (method) {
+       case CONNMAN_IPV4_METHOD_UNKNOWN:
+               return "unknown";
+       case CONNMAN_IPV4_METHOD_OFF:
+               return "off";
+       case CONNMAN_IPV4_METHOD_STATIC:
+               return "static";
+       case CONNMAN_IPV4_METHOD_DHCP:
+               return "dhcp";
+       }
+
+       return "unknown";
+}
+
+enum connman_ipv4_method __connman_ipv4_string2method(const char *method)
+{
+       if (strcasecmp(method, "off") == 0)
+               return CONNMAN_IPV4_METHOD_OFF;
+       else if (strcasecmp(method, "static") == 0)
+               return CONNMAN_IPV4_METHOD_STATIC;
+       else if (strcasecmp(method, "dhcp") == 0)
+               return CONNMAN_IPV4_METHOD_DHCP;
+       else
+               return CONNMAN_IPV4_METHOD_UNKNOWN;
+}
+
+static void emit_element_signal(DBusConnection *conn, const char *member,
+                                       struct connman_element *element)
+{
+       DBusMessage *signal;
+
+       if (__connman_debug_enabled() == FALSE)
+               return;
+
+       DBG("conn %p member %s", conn, member);
+
+       if (element == NULL)
+               return;
+
+       signal = dbus_message_new_signal(element->path,
+                                       CONNMAN_DEBUG_INTERFACE, member);
+       if (signal == NULL)
+               return;
+
+       g_dbus_send_message(conn, signal);
+}
+
+struct foreach_data {
+       enum connman_element_type type;
+       element_cb_t callback;
+       gpointer user_data;
+};
+
+static gboolean foreach_callback(GNode *node, gpointer user_data)
+{
+       struct connman_element *element = node->data;
+       struct foreach_data *data = user_data;
+
+       DBG("element %p name %s", element, element->name);
+
+       if (element->type == CONNMAN_ELEMENT_TYPE_ROOT)
+               return FALSE;
+
+       if (data->type != CONNMAN_ELEMENT_TYPE_UNKNOWN &&
+                                       data->type != element->type)
+               return FALSE;
+
+       if (data->callback)
+               data->callback(element, data->user_data);
+
+       return FALSE;
+}
+
+void __connman_element_foreach(struct connman_element *element,
+                               enum connman_element_type type,
+                               element_cb_t callback, gpointer user_data)
+{
+       struct foreach_data data = { type, callback, user_data };
+       GNode *node;
+
+       DBG("");
+
+       if (element != NULL) {
+               node = g_node_find(element_root, G_PRE_ORDER,
+                                               G_TRAVERSE_ALL, element);
+               if (node == NULL)
+                       return;
+       } else
+               node = element_root;
+
+       g_node_traverse(node, G_PRE_ORDER, G_TRAVERSE_ALL, -1,
+                                               foreach_callback, &data);
+}
+
+struct append_filter {
+       enum connman_element_type type;
+       DBusMessageIter *iter;
+};
+
+static gboolean append_path(GNode *node, gpointer user_data)
+{
+       struct connman_element *element = node->data;
+       struct append_filter *filter = user_data;
+
+       DBG("element %p name %s", element, element->name);
+
+       if (element->type == CONNMAN_ELEMENT_TYPE_ROOT)
+               return FALSE;
+
+       if (filter->type != CONNMAN_ELEMENT_TYPE_UNKNOWN &&
+                                       filter->type != element->type)
+               return FALSE;
+
+       if (filter->type == CONNMAN_ELEMENT_TYPE_DEVICE &&
+                       __connman_device_has_driver(element->device) == FALSE)
+               return FALSE;
+
+       if (filter->type == CONNMAN_ELEMENT_TYPE_NETWORK &&
+                       __connman_network_has_driver(element->network) == FALSE)
+               return FALSE;
+
+       dbus_message_iter_append_basic(filter->iter,
+                               DBUS_TYPE_OBJECT_PATH, &element->path);
+
+       return FALSE;
+}
+
+void __connman_element_list(struct connman_element *element,
+                                       enum connman_element_type type,
+                                                       DBusMessageIter *iter)
+{
+       struct append_filter filter = { type, iter };
+       GNode *node;
+
+       DBG("");
+
+       if (element != NULL) {
+               node = g_node_find(element_root, G_PRE_ORDER,
+                                               G_TRAVERSE_ALL, element);
+               if (node == NULL)
+                       return;
+       } else
+               node = element_root;
+
+       g_node_traverse(node, G_PRE_ORDER, G_TRAVERSE_ALL, -1,
+                                               append_path, &filter);
+}
+
+struct count_data {
+       enum connman_element_type type;
+       int count;
+};
+
+static gboolean count_element(GNode *node, gpointer user_data)
+{
+       struct connman_element *element = node->data;
+       struct count_data *data = user_data;
+
+       DBG("element %p name %s", element, element->name);
+
+       if (element->type == CONNMAN_ELEMENT_TYPE_ROOT)
+               return FALSE;
+
+       if (data->type != CONNMAN_ELEMENT_TYPE_UNKNOWN &&
+                                       data->type != element->type)
+               return FALSE;
+
+       data->count++;
+
+       return FALSE;
+}
+
+int __connman_element_count(struct connman_element *element,
+                                       enum connman_element_type type)
+{
+       struct count_data data = { type, 0 };
+       GNode *node;
+
+       DBG("");
+
+       if (element != NULL) {
+               node = g_node_find(element_root, G_PRE_ORDER,
+                                               G_TRAVERSE_ALL, element);
+               if (node == NULL)
+                       return 0;
+       } else
+               node = element_root;
+
+       g_node_traverse(node, G_PRE_ORDER, G_TRAVERSE_ALL, -1,
+                                               count_element, &data);
+
+       return data.count;
+}
+
+static struct connman_network *__connman_element_get_network(struct connman_element *element)
+{
+       if (element->type == CONNMAN_ELEMENT_TYPE_NETWORK &&
+                                               element->network != NULL)
+               return element->network;
+
+       if (element->parent == NULL)
+               return NULL;
+
+       return __connman_element_get_network(element->parent);
+}
+
+struct connman_service *__connman_element_get_service(struct connman_element *element)
+{
+       struct connman_service *service = NULL;
+       struct connman_network *network;
+       struct connman_device *device;
+       enum connman_device_type type;
+
+       device = __connman_element_get_device(element);
+       if (device == NULL)
+               return NULL;
+
+       type = connman_device_get_type(device);
+
+       switch (type) {
+       case CONNMAN_DEVICE_TYPE_UNKNOWN:
+       case CONNMAN_DEVICE_TYPE_VENDOR:
+       case CONNMAN_DEVICE_TYPE_BLUETOOTH:
+       case CONNMAN_DEVICE_TYPE_GPS:
+       case CONNMAN_DEVICE_TYPE_HSO:
+       case CONNMAN_DEVICE_TYPE_NOZOMI:
+       case CONNMAN_DEVICE_TYPE_HUAWEI:
+       case CONNMAN_DEVICE_TYPE_NOVATEL:
+               break;
+       case CONNMAN_DEVICE_TYPE_ETHERNET:
+               service = __connman_service_lookup_from_device(device);
+               break;
+       case CONNMAN_DEVICE_TYPE_WIFI:
+       case CONNMAN_DEVICE_TYPE_WIMAX:
+               network = __connman_element_get_network(element);
+               if (network == NULL)
+                       return NULL;
+               service = __connman_service_lookup_from_network(network);
+               break;
+       }
+
+       return service;
+}
+
+struct connman_device *__connman_element_get_device(struct connman_element *element)
+{
+       if (element->type == CONNMAN_ELEMENT_TYPE_DEVICE &&
+                                               element->device != NULL)
+               return element->device;
+
+       if (element->parent == NULL)
+               return NULL;
+
+       return __connman_element_get_device(element->parent);
+}
+
+const char *__connman_element_get_device_path(struct connman_element *element)
+{
+       struct connman_device *device;
+
+       device = __connman_element_get_device(element);
+       if (device == NULL)
+               return NULL;
+
+       return connman_device_get_path(device);
+}
+
+const char *__connman_element_get_network_path(struct connman_element *element)
+{
+       if (element->type == CONNMAN_ELEMENT_TYPE_NETWORK &&
+                                               element->network != NULL)
+               return element->path;
+
+       if (element->parent == NULL)
+               return NULL;
+
+       return __connman_element_get_network_path(element->parent);
+}
+
+static gint compare_priority(gconstpointer a, gconstpointer b)
+{
+       const struct connman_driver *driver1 = a;
+       const struct connman_driver *driver2 = b;
+
+       return driver2->priority - driver1->priority;
+}
+
+static gboolean match_driver(struct connman_element *element,
+                                       struct connman_driver *driver)
+{
+       if (element->type == CONNMAN_ELEMENT_TYPE_ROOT)
+               return FALSE;
+
+       if (element->type == driver->type ||
+                       driver->type == CONNMAN_ELEMENT_TYPE_UNKNOWN)
+               return TRUE;
+
+       return FALSE;
+}
+
+static gboolean probe_driver(GNode *node, gpointer data)
+{
+       struct connman_element *element = node->data;
+       struct connman_driver *driver = data;
+
+       DBG("element %p name %s", element, element->name);
+
+       if (!element->driver && match_driver(element, driver) == TRUE) {
+               if (driver->probe(element) < 0)
+                       return FALSE;
+
+               __connman_element_lock(element);
+               element->driver = driver;
+               __connman_element_unlock(element);
+       }
+
+       return FALSE;
+}
+
+void __connman_driver_rescan(struct connman_driver *driver)
+{
+       DBG("driver %p name %s", driver, driver->name);
+
+       if (!driver->probe)
+               return;
+
+       if (element_root != NULL)
+               g_node_traverse(element_root, G_PRE_ORDER,
+                               G_TRAVERSE_ALL, -1, probe_driver, driver);
+}
+
+/**
+ * connman_driver_register:
+ * @driver: driver definition
+ *
+ * Register a new driver
+ *
+ * Returns: %0 on success
+ */
+int connman_driver_register(struct connman_driver *driver)
+{
+       DBG("driver %p name %s", driver, driver->name);
+
+       if (driver->type == CONNMAN_ELEMENT_TYPE_ROOT)
+               return -EINVAL;
+
+       if (!driver->probe)
+               return -EINVAL;
+
+       driver_list = g_slist_insert_sorted(driver_list, driver,
+                                                       compare_priority);
+
+       if (started == FALSE)
+               return 0;
+
+       if (element_root != NULL)
+               g_node_traverse(element_root, G_PRE_ORDER,
+                               G_TRAVERSE_ALL, -1, probe_driver, driver);
+
+       return 0;
+}
+
+static gboolean remove_driver(GNode *node, gpointer data)
+{
+       struct connman_element *element = node->data;
+       struct connman_driver *driver = data;
+
+       DBG("element %p name %s", element, element->name);
+
+       if (element->driver == driver) {
+               if (driver->remove)
+                       driver->remove(element);
+
+               __connman_element_lock(element);
+               element->driver = NULL;
+               __connman_element_unlock(element);
+       }
+
+       return FALSE;
+}
+
+/**
+ * connman_driver_unregister:
+ * @driver: driver definition
+ *
+ * Remove a previously registered driver
+ */
+void connman_driver_unregister(struct connman_driver *driver)
+{
+       DBG("driver %p name %s", driver, driver->name);
+
+       driver_list = g_slist_remove(driver_list, driver);
+
+       if (element_root != NULL)
+               g_node_traverse(element_root, G_POST_ORDER,
+                               G_TRAVERSE_ALL, -1, remove_driver, driver);
+}
+
+static void unregister_property(gpointer data)
+{
+       struct connman_property *property = data;
+
+       DBG("property %p", property);
+
+       g_free(property->value);
+       g_free(property);
+}
+
+void __connman_element_initialize(struct connman_element *element)
+{
+       DBG("element %p", element);
+
+       element->refcount = 1;
+
+       element->name    = NULL;
+       element->type    = CONNMAN_ELEMENT_TYPE_UNKNOWN;
+       element->state   = CONNMAN_ELEMENT_STATE_UNKNOWN;
+       element->error   = CONNMAN_ELEMENT_ERROR_UNKNOWN;
+       element->index   = -1;
+       element->enabled = FALSE;
+
+       element->configuring = FALSE;
+
+       element->properties = g_hash_table_new_full(g_str_hash, g_str_equal,
+                                               g_free, unregister_property);
+}
+
+/**
+ * connman_element_create:
+ * @name: element name
+ *
+ * Allocate a new element and assign the given #name to it. If the name
+ * is #NULL, it will be later on created based on the element type.
+ *
+ * Returns: a newly-allocated #connman_element structure
+ */
+struct connman_element *connman_element_create(const char *name)
+{
+       struct connman_element *element;
+
+       element = g_try_new0(struct connman_element, 1);
+       if (element == NULL)
+               return NULL;
+
+       DBG("element %p", element);
+
+       __connman_element_initialize(element);
+
+       return element;
+}
+
+struct connman_element *connman_element_ref(struct connman_element *element)
+{
+       DBG("element %p name %s refcount %d", element, element->name,
+                               g_atomic_int_get(&element->refcount) + 1);
+
+       g_atomic_int_inc(&element->refcount);
+
+       return element;
+}
+
+static void free_properties(struct connman_element *element)
+{
+       DBG("element %p name %s", element, element->name);
+
+       __connman_element_lock(element);
+
+       g_hash_table_destroy(element->properties);
+       element->properties = NULL;
+
+       __connman_element_unlock(element);
+}
+
+void connman_element_unref(struct connman_element *element)
+{
+       DBG("element %p name %s refcount %d", element, element->name,
+                               g_atomic_int_get(&element->refcount) - 1);
+
+       if (g_atomic_int_dec_and_test(&element->refcount) == TRUE) {
+               if (element->destruct)
+                       element->destruct(element);
+               free_properties(element);
+               g_free(element->ipv4.address);
+               g_free(element->ipv4.netmask);
+               g_free(element->ipv4.gateway);
+               g_free(element->ipv4.network);
+               g_free(element->ipv4.broadcast);
+               g_free(element->ipv4.nameserver);
+               g_free(element->devname);
+               g_free(element->path);
+               g_free(element->name);
+               g_free(element);
+       }
+}
+
+static int set_static_property(struct connman_element *element,
+                               const char *name, int type, const void *value)
+{
+       struct connman_property *property;
+
+       DBG("element %p name %s", element, element->name);
+
+       if (type != DBUS_TYPE_STRING && type != DBUS_TYPE_BYTE)
+               return -EINVAL;
+
+       property = g_try_new0(struct connman_property, 1);
+       if (property == NULL)
+               return -ENOMEM;
+
+       property->id   = CONNMAN_PROPERTY_ID_INVALID;
+       property->type = type;
+
+       DBG("name %s type %d value %p", name, type, value);
+
+       switch (type) {
+       case DBUS_TYPE_STRING:
+               property->value = g_strdup(*((const char **) value));
+               break;
+       case DBUS_TYPE_BYTE:
+               property->value = g_try_malloc(1);
+               if (property->value != NULL)
+                       memcpy(property->value, value, 1);
+               break;
+       }
+
+       __connman_element_lock(element);
+
+       g_hash_table_replace(element->properties, g_strdup(name), property);
+
+       __connman_element_unlock(element);
+
+       return 0;
+}
+
+static int set_static_array_property(struct connman_element *element,
+                       const char *name, int type, const void *value, int len)
+{
+       struct connman_property *property;
+
+       DBG("element %p name %s", element, element->name);
+
+       if (type != DBUS_TYPE_BYTE)
+               return -EINVAL;
+
+       property = g_try_new0(struct connman_property, 1);
+       if (property == NULL)
+               return -ENOMEM;
+
+       property->id      = CONNMAN_PROPERTY_ID_INVALID;
+       property->type    = DBUS_TYPE_ARRAY;
+       property->subtype = type;
+
+       DBG("name %s type %d value %p", name, type, value);
+
+       switch (type) {
+       case DBUS_TYPE_BYTE:
+               property->value = g_try_malloc(len);
+               if (property->value != NULL) {
+                       memcpy(property->value,
+                               *((const unsigned char **) value), len);
+                       property->size = len;
+               }
+               break;
+       }
+
+       __connman_element_lock(element);
+
+       g_hash_table_replace(element->properties, g_strdup(name), property);
+
+       __connman_element_unlock(element);
+
+       return 0;
+}
+
+#if 0
+static int set_property(struct connman_element *element,
+                               enum connman_property_id id, const void *value)
+{
+       switch (id) {
+       case CONNMAN_PROPERTY_ID_IPV4_ADDRESS:
+               __connman_element_lock(element);
+               g_free(element->ipv4.address);
+               element->ipv4.address = g_strdup(*((const char **) value));
+               __connman_element_unlock(element);
+               break;
+       case CONNMAN_PROPERTY_ID_IPV4_NETMASK:
+               __connman_element_lock(element);
+               g_free(element->ipv4.netmask);
+               element->ipv4.netmask = g_strdup(*((const char **) value));
+               __connman_element_unlock(element);
+               break;
+       case CONNMAN_PROPERTY_ID_IPV4_GATEWAY:
+               __connman_element_lock(element);
+               g_free(element->ipv4.gateway);
+               element->ipv4.gateway = g_strdup(*((const char **) value));
+               __connman_element_unlock(element);
+               break;
+       case CONNMAN_PROPERTY_ID_IPV4_BROADCAST:
+               __connman_element_lock(element);
+               g_free(element->ipv4.broadcast);
+               element->ipv4.broadcast = g_strdup(*((const char **) value));
+               __connman_element_unlock(element);
+               break;
+       case CONNMAN_PROPERTY_ID_IPV4_NAMESERVER:
+               __connman_element_lock(element);
+               g_free(element->ipv4.nameserver);
+               element->ipv4.nameserver = g_strdup(*((const char **) value));
+               __connman_element_unlock(element);
+               break;
+       default:
+               return -EINVAL;
+       }
+
+       return 0;
+}
+#endif
+
+int connman_element_get_value(struct connman_element *element,
+                               enum connman_property_id id, void *value)
+{
+       if (element->type == CONNMAN_ELEMENT_TYPE_ROOT)
+               return -EINVAL;
+
+       switch (id) {
+       case CONNMAN_PROPERTY_ID_IPV4_METHOD:
+               if (element->ipv4.method == CONNMAN_IPV4_METHOD_UNKNOWN)
+                       return connman_element_get_value(element->parent,
+                                                               id, value);
+               __connman_element_lock(element);
+               *((const char **) value) = __connman_ipv4_method2string(element->ipv4.method);
+               __connman_element_unlock(element);
+               break;
+       case CONNMAN_PROPERTY_ID_IPV4_ADDRESS:
+               if (element->ipv4.address == NULL)
+                       return connman_element_get_value(element->parent,
+                                                               id, value);
+               __connman_element_lock(element);
+               *((char **) value) = element->ipv4.address;
+               __connman_element_unlock(element);
+               break;
+       case CONNMAN_PROPERTY_ID_IPV4_NETMASK:
+               if (element->ipv4.netmask == NULL)
+                       return connman_element_get_value(element->parent,
+                                                               id, value);
+               __connman_element_lock(element);
+               *((char **) value) = element->ipv4.netmask;
+               __connman_element_unlock(element);
+               break;
+       case CONNMAN_PROPERTY_ID_IPV4_GATEWAY:
+               if (element->ipv4.gateway == NULL)
+                       return connman_element_get_value(element->parent,
+                                                               id, value);
+               __connman_element_lock(element);
+               *((char **) value) = element->ipv4.gateway;
+               __connman_element_unlock(element);
+               break;
+       case CONNMAN_PROPERTY_ID_IPV4_BROADCAST:
+               if (element->ipv4.broadcast == NULL)
+                       return connman_element_get_value(element->parent,
+                                                               id, value);
+               __connman_element_lock(element);
+               *((char **) value) = element->ipv4.broadcast;
+               __connman_element_unlock(element);
+               break;
+       case CONNMAN_PROPERTY_ID_IPV4_NAMESERVER:
+               if (element->ipv4.nameserver == NULL)
+                       return connman_element_get_value(element->parent,
+                                                               id, value);
+               __connman_element_lock(element);
+               *((char **) value) = element->ipv4.nameserver;
+               __connman_element_unlock(element);
+               break;
+       default:
+               return -EINVAL;
+       }
+
+       return 0;
+}
+
+static gboolean get_static_property(struct connman_element *element,
+                                               const char *name, void *value)
+{
+       struct connman_property *property;
+       gboolean found = FALSE;
+
+       DBG("element %p name %s", element, element->name);
+
+       __connman_element_lock(element);
+
+       property = g_hash_table_lookup(element->properties, name);
+       if (property != NULL) {
+               switch (property->type) {
+               case DBUS_TYPE_STRING:
+                       *((char **) value) = property->value;
+                       found = TRUE;
+                       break;
+               case DBUS_TYPE_BYTE:
+                       memcpy(value, property->value, 1);
+                       found = TRUE;
+                       break;
+               }
+       }
+
+       __connman_element_unlock(element);
+
+       if (found == FALSE && element->parent != NULL)
+               return get_static_property(element->parent, name, value);
+
+       return found;
+}
+
+static gboolean get_static_array_property(struct connman_element *element,
+                       const char *name, void *value, unsigned int *len)
+{
+       struct connman_property *property;
+       gboolean found = FALSE;
+
+       DBG("element %p name %s", element, element->name);
+
+       __connman_element_lock(element);
+
+       property = g_hash_table_lookup(element->properties, name);
+       if (property != NULL) {
+               *((char **) value) = property->value;
+               *len = property->size;
+               found = TRUE;
+       }
+
+       __connman_element_unlock(element);
+
+       return found;
+}
+
+#if 0
+static gboolean match_static_property(struct connman_element *element,
+                                       const char *name, const void *value)
+{
+       struct connman_property *property;
+       gboolean result = FALSE;
+
+       DBG("element %p name %s", element, element->name);
+
+       __connman_element_lock(element);
+
+       property = g_hash_table_lookup(element->properties, name);
+       if (property != NULL) {
+               if (property->type == DBUS_TYPE_STRING)
+                       result = g_str_equal(property->value,
+                                               *((const char **) value));
+       }
+
+       __connman_element_unlock(element);
+
+       return result;
+}
+#endif
+
+/**
+ * connman_element_set_string:
+ * @element: element structure
+ * @key: unique identifier
+ * @value: string value
+ *
+ * Set string value for specific key
+ */
+int connman_element_set_string(struct connman_element *element,
+                                       const char *key, const char *value)
+{
+       return set_static_property(element, key, DBUS_TYPE_STRING, &value);
+}
+
+/**
+ * connman_element_get_string:
+ * @element: element structure
+ * @key: unique identifier
+ *
+ * Get string value for specific key
+ */
+const char *connman_element_get_string(struct connman_element *element,
+                                                       const char *key)
+{
+       const char *value;
+
+       if (get_static_property(element, key, &value) == FALSE)
+               return NULL;
+
+       return value;
+}
+
+/**
+ * connman_element_set_uint8:
+ * @element: element structure
+ * @key: unique identifier
+ * @value: integer value
+ *
+ * Set integer value for specific key
+ */
+int connman_element_set_uint8(struct connman_element *element,
+                                       const char *key, connman_uint8_t value)
+{
+        return set_static_property(element, key, DBUS_TYPE_BYTE, &value);
+}
+
+/**
+ * connman_element_get_uint8:
+ * @element: element structure
+ * @key: unique identifier
+ *
+ * Get integer value for specific key
+ */
+connman_uint8_t connman_element_get_uint8(struct connman_element *element,
+                                                       const char *key)
+{
+       connman_uint8_t value;
+
+       if (get_static_property(element, key, &value) == FALSE)
+                return 0;
+
+       return value;
+}
+
+/**
+ * connman_element_set_blob:
+ * @element: element structure
+ * @key: unique identifier
+ * @data: blob data
+ * @size: blob size
+ *
+ * Set binary blob value for specific key
+ */
+int connman_element_set_blob(struct connman_element *element,
+                       const char *key, const void *data, unsigned int size)
+{
+       return set_static_array_property(element, key,
+                                               DBUS_TYPE_BYTE, &data, size);
+}
+
+/**
+ * connman_element_get_blob:
+ * @element: element structure
+ * @key: unique identifier
+ * @size: pointer to blob size
+ *
+ * Get binary blob value for specific key
+ */
+const void *connman_element_get_blob(struct connman_element *element,
+                                       const char *key, unsigned int *size)
+{
+       void *value;
+
+       if (get_static_array_property(element, key, &value, size) == FALSE)
+               return NULL;
+
+       return value;
+}
+
+int __connman_element_append_ipv4(struct connman_element *element,
+                                               DBusMessageIter *dict)
+{
+       const char *method = NULL;
+       const char *address = NULL, *netmask = NULL, *gateway = NULL;
+
+       connman_element_get_value(element,
+                               CONNMAN_PROPERTY_ID_IPV4_METHOD, &method);
+
+       connman_element_get_value(element,
+                               CONNMAN_PROPERTY_ID_IPV4_ADDRESS, &address);
+       connman_element_get_value(element,
+                               CONNMAN_PROPERTY_ID_IPV4_NETMASK, &netmask);
+       connman_element_get_value(element,
+                               CONNMAN_PROPERTY_ID_IPV4_GATEWAY, &gateway);
+
+       if (method != NULL)
+               connman_dbus_dict_append_variant(dict, "IPv4.Method",
+                                               DBUS_TYPE_STRING, &method);
+
+       if (address != NULL)
+               connman_dbus_dict_append_variant(dict, "IPv4.Address",
+                                               DBUS_TYPE_STRING, &address);
+
+       if (netmask != NULL)
+               connman_dbus_dict_append_variant(dict, "IPv4.Netmask",
+                                               DBUS_TYPE_STRING, &netmask);
+
+       if (gateway != NULL)
+               connman_dbus_dict_append_variant(dict, "IPv4.Gateway",
+                                               DBUS_TYPE_STRING, &gateway);
+
+       return 0;
+}
+
+int __connman_element_set_ipv4(struct connman_element *element,
+                               const char *name, DBusMessageIter *value)
+{
+       int type;
+
+       type = dbus_message_iter_get_arg_type(value);
+
+       if (g_str_equal(name, "IPv4.Method") == TRUE) {
+               enum connman_ipv4_method method;
+               const char *str;
+
+               if (type != DBUS_TYPE_STRING)
+                       return -EINVAL;
+
+               dbus_message_iter_get_basic(value, &str);
+               method = __connman_ipv4_string2method(str);
+               if (method == CONNMAN_IPV4_METHOD_UNKNOWN)
+                       return -EINVAL;
+
+               if (method == element->ipv4.method)
+                       return -EALREADY;
+
+               element->ipv4.method = method;
+
+               connman_element_update(element);
+       } else if (g_str_equal(name, "IPv4.Address") == TRUE) {
+               const char *address;
+
+               if (type != DBUS_TYPE_STRING)
+                       return -EINVAL;
+
+               dbus_message_iter_get_basic(value, &address);
+
+               g_free(element->ipv4.address);
+               element->ipv4.address = g_strdup(address);
+
+               connman_element_update(element);
+       } else if (g_str_equal(name, "IPv4.Netmask") == TRUE) {
+               const char *netmask;
+
+               if (type != DBUS_TYPE_STRING)
+                       return -EINVAL;
+
+               dbus_message_iter_get_basic(value, &netmask);
+
+               g_free(element->ipv4.netmask);
+               element->ipv4.netmask = g_strdup(netmask);
+
+               connman_element_update(element);
+       } else if (g_str_equal(name, "IPv4.Gateway") == TRUE) {
+               const char *gateway;
+
+               if (type != DBUS_TYPE_STRING)
+                       return -EINVAL;
+
+               dbus_message_iter_get_basic(value, &gateway);
+
+               g_free(element->ipv4.gateway);
+               element->ipv4.gateway = g_strdup(gateway);
+
+               connman_element_update(element);
+       }
+
+       return 0;
+}
+
+static void append_state(DBusMessageIter *entry, const char *state)
+{
+       DBusMessageIter value;
+       const char *key = "State";
+
+       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, &state);
+       dbus_message_iter_close_container(entry, &value);
+}
+
+static void emit_state_change(DBusConnection *conn, const char *state)
+{
+       DBusMessage *signal;
+       DBusMessageIter entry;
+
+       DBG("conn %p", conn);
+
+       signal = dbus_message_new_signal(CONNMAN_MANAGER_PATH,
+                               CONNMAN_MANAGER_INTERFACE, "PropertyChanged");
+       if (signal == NULL)
+               return;
+
+       dbus_message_iter_init_append(signal, &entry);
+
+       append_state(&entry, state);
+
+       g_dbus_send_message(conn, signal);
+
+       signal = dbus_message_new_signal(CONNMAN_MANAGER_PATH,
+                               CONNMAN_MANAGER_INTERFACE, "StateChanged");
+       if (signal == NULL)
+               return;
+
+       dbus_message_iter_init_append(signal, &entry);
+       dbus_message_iter_append_basic(&entry, DBUS_TYPE_STRING, &state);
+
+       g_dbus_send_message(conn, signal);
+}
+
+static void probe_element(struct connman_element *element)
+{
+       GSList *list;
+
+       DBG("element %p name %s", element, element->name);
+
+       for (list = driver_list; list; list = list->next) {
+               struct connman_driver *driver = list->data;
+
+               if (match_driver(element, driver) == FALSE)
+                       continue;
+
+               DBG("driver %p name %s", driver, driver->name);
+
+               if (driver->probe(element) == 0) {
+                       __connman_element_lock(element);
+                       element->driver = driver;
+                       __connman_element_unlock(element);
+                       break;
+               }
+       }
+}
+
+static void register_element(gpointer data, gpointer user_data)
+{
+       struct connman_element *element = data;
+       const gchar *basepath;
+       GNode *node;
+
+       __connman_element_lock(element);
+
+       if (element->parent) {
+               node = g_node_find(element_root, G_PRE_ORDER,
+                                       G_TRAVERSE_ALL, element->parent);
+               basepath = element->parent->path;
+       } else {
+               element->parent = element_root->data;
+
+               node = element_root;
+               basepath = "/device";
+       }
+
+       element->path = g_strdup_printf("%s/%s", basepath, element->name);
+
+       __connman_element_unlock(element);
+
+       if (node == NULL) {
+               connman_error("Element registration for %s failed",
+                                                       element->path);
+               return;
+       }
+
+       DBG("element %p path %s", element, element->path);
+
+       g_node_append_data(node, element);
+
+       if (element->type == CONNMAN_ELEMENT_TYPE_DHCP) {
+               element->parent->configuring = TRUE;
+
+               if (__connman_element_count(NULL,
+                                       CONNMAN_ELEMENT_TYPE_CONNECTION) == 0)
+                       emit_state_change(connection, "connecting");
+       }
+
+       if (element->type == CONNMAN_ELEMENT_TYPE_CONNECTION) {
+               struct connman_element *parent = element->parent;
+
+               while (parent) {
+                       parent->configuring = FALSE;
+                       parent = parent->parent;
+               }
+
+               if (__connman_element_count(NULL,
+                                       CONNMAN_ELEMENT_TYPE_CONNECTION) == 1)
+                       emit_state_change(connection, "online");
+       }
+
+       emit_element_signal(connection, "ElementAdded", element);
+
+       if (started == FALSE)
+               return;
+
+       probe_element(element);
+}
+
+/**
+ * connman_element_register:
+ * @element: the element to register
+ * @parent: the parent to register the element with
+ *
+ * Register an element with the core. It will be register under the given
+ * parent of if %NULL is provided under the root element.
+ *
+ * Returns: %0 on success
+ */
+int connman_element_register(struct connman_element *element,
+                                       struct connman_element *parent)
+{
+       DBG("element %p name %s parent %p", element, element->name, parent);
+
+       if (element->devname == NULL)
+               element->devname = g_strdup(element->name);
+
+       if (device_filter && element->type == CONNMAN_ELEMENT_TYPE_DEVICE) {
+               if (g_pattern_match_simple(device_filter,
+                                               element->devname) == FALSE) {
+                       DBG("ignoring %s [%s] device", element->name,
+                                                       element->devname);
+                       return -EPERM;
+               }
+       }
+
+       if (connman_element_ref(element) == NULL)
+               return -EINVAL;
+
+       __connman_element_lock(element);
+
+       if (element->name == NULL) {
+               element->name = g_strdup(type2string(element->type));
+               if (element->name == NULL) {
+                       __connman_element_unlock(element);
+                       return -EINVAL;
+               }
+       }
+
+       if (element->type == CONNMAN_ELEMENT_TYPE_DHCP)
+               element->ipv4.method = CONNMAN_IPV4_METHOD_DHCP;
+
+       element->parent = parent;
+
+       __connman_element_unlock(element);
+
+       register_element(element, NULL);
+
+       return 0;
+}
+
+static gboolean remove_element(GNode *node, gpointer user_data)
+{
+       struct connman_element *element = node->data;
+       struct connman_element *root = user_data;
+
+       DBG("element %p name %s", element, element->name);
+
+       if (element == root)
+               return FALSE;
+
+       if (node != NULL)
+               g_node_unlink(node);
+
+       if (element->driver) {
+               if (element->driver->remove)
+                       element->driver->remove(element);
+
+               __connman_element_lock(element);
+               element->driver = NULL;
+               __connman_element_unlock(element);
+       }
+
+       if (node != NULL)
+               g_node_destroy(node);
+
+       if (element->type == CONNMAN_ELEMENT_TYPE_CONNECTION) {
+               if (__connman_element_count(NULL,
+                                       CONNMAN_ELEMENT_TYPE_CONNECTION) == 0)
+                       emit_state_change(connection, "offline");
+       }
+
+       emit_element_signal(connection, "ElementRemoved", element);
+
+       connman_element_unref(element);
+
+       return FALSE;
+}
+
+void connman_element_unregister(struct connman_element *element)
+{
+       GNode *node;
+
+       DBG("element %p name %s", element, element->name);
+
+       node = g_node_find(element_root, G_PRE_ORDER, G_TRAVERSE_ALL, element);
+
+       if (node != NULL)
+               g_node_traverse(node, G_POST_ORDER,
+                               G_TRAVERSE_ALL, -1, remove_element, NULL);
+}
+
+void connman_element_unregister_children(struct connman_element *element)
+{
+       GNode *node;
+
+       DBG("element %p name %s", element, element->name);
+
+       node = g_node_find(element_root, G_PRE_ORDER, G_TRAVERSE_ALL, element);
+
+       if (node != NULL)
+               g_node_traverse(node, G_POST_ORDER,
+                               G_TRAVERSE_ALL, -1, remove_element, element);
+}
+
+static gboolean update_element(GNode *node, gpointer user_data)
+{
+       struct connman_element *element = node->data;
+
+       DBG("element %p name %s", element, element->name);
+
+       if (element->driver && element->driver->update)
+               element->driver->update(element);
+
+       emit_element_signal(connection, "ElementUpdated", element);
+
+       return FALSE;
+}
+
+void connman_element_update(struct connman_element *element)
+{
+       GNode *node;
+
+       DBG("element %p name %s", element, element->name);
+
+       node = g_node_find(element_root, G_PRE_ORDER, G_TRAVERSE_ALL, element);
+
+       if (node != NULL)
+               g_node_traverse(node, G_PRE_ORDER,
+                               G_TRAVERSE_ALL, -1, update_element, element);
+}
+
+int connman_element_set_enabled(struct connman_element *element,
+                                                       gboolean enabled)
+{
+       if (element->enabled == enabled)
+               return 0;
+
+       element->enabled = enabled;
+
+       connman_element_update(element);
+
+       return 0;
+}
+
+/**
+ * connman_element_set_error:
+ * @element: element structure
+ * @error: error identifier
+ *
+ * Set error state and specific error identifier
+ */
+void connman_element_set_error(struct connman_element *element,
+                                       enum connman_element_error error)
+{
+       struct connman_service *service;
+
+       DBG("element %p error %d", element, error);
+
+       if (element->type == CONNMAN_ELEMENT_TYPE_ROOT)
+               return;
+
+       element->state = CONNMAN_ELEMENT_STATE_ERROR;
+       element->error = error;
+
+       if (element->driver && element->driver->change)
+               element->driver->change(element);
+
+       service = __connman_element_get_service(element);
+       __connman_service_indicate_state(service,
+                                       CONNMAN_SERVICE_STATE_FAILURE);
+}
+
+int __connman_element_init(DBusConnection *conn, const char *device,
+                                                       const char *nodevice)
+{
+       struct connman_element *element;
+
+       DBG("conn %p", conn);
+
+       connection = dbus_connection_ref(conn);
+       if (connection == NULL)
+               return -EIO;
+
+       device_filter = g_strdup(device);
+
+       element = connman_element_create("root");
+
+       element->path = g_strdup("/");
+       element->type = CONNMAN_ELEMENT_TYPE_ROOT;
+
+       element_root = g_node_new(element);
+
+       __connman_notifier_init();
+       __connman_service_init();
+       __connman_network_init();
+       __connman_device_init();
+
+       return 0;
+}
+
+static gboolean probe_node(GNode *node, gpointer data)
+{
+       struct connman_element *element = node->data;
+
+       DBG("element %p name %s", element, element->name);
+
+       if (element->type == CONNMAN_ELEMENT_TYPE_ROOT)
+               return FALSE;
+
+       if (element->driver)
+               return FALSE;
+
+       probe_element(element);
+
+       return FALSE;
+}
+
+void __connman_element_start(void)
+{
+       DBG("");
+
+       g_node_traverse(element_root, G_PRE_ORDER, G_TRAVERSE_ALL, -1,
+                                                       probe_node, NULL);
+
+       started = TRUE;
+
+       __connman_storage_init_device();
+
+       __connman_connection_init();
+       __connman_ipv4_init();
+       __connman_detect_init();
+       __connman_rfkill_init();
+}
+
+void __connman_element_stop(void)
+{
+       DBG("");
+
+       __connman_rfkill_cleanup();
+       __connman_detect_cleanup();
+       __connman_ipv4_cleanup();
+       __connman_connection_cleanup();
+}
+
+static gboolean free_driver(GNode *node, gpointer data)
+{
+       struct connman_element *element = node->data;
+
+       DBG("element %p name %s", element, element->name);
+
+       if (element->driver) {
+               if (element->driver->remove)
+                       element->driver->remove(element);
+
+               __connman_element_lock(element);
+               element->driver = NULL;
+               __connman_element_unlock(element);
+       }
+
+       return FALSE;
+}
+
+static gboolean free_node(GNode *node, gpointer data)
+{
+       struct connman_element *element = node->data;
+
+       DBG("element %p name %s", element, element->name);
+
+       if (g_node_depth(node) > 1)
+               connman_element_unregister(element);
+
+       return FALSE;
+}
+
+void __connman_element_cleanup(void)
+{
+       DBG("");
+
+       __connman_device_cleanup();
+       __connman_network_cleanup();
+       __connman_service_cleanup();
+       __connman_notifier_cleanup();
+
+       g_node_traverse(element_root, G_POST_ORDER, G_TRAVERSE_ALL, -1,
+                                                       free_driver, NULL);
+
+       g_node_traverse(element_root, G_POST_ORDER, G_TRAVERSE_ALL, -1,
+                                                       free_node, NULL);
+
+       g_node_destroy(element_root);
+       element_root = NULL;
+
+       g_free(device_filter);
+
+       dbus_connection_unref(connection);
+}
diff --git a/src/error.c b/src/error.c
new file mode 100644 (file)
index 0000000..af839a2
--- /dev/null
@@ -0,0 +1,100 @@
+/*
+ *
+ *  Connection Manager
+ *
+ *  Copyright (C) 2007-2009  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 <errno.h>
+#include <string.h>
+
+#include <gdbus.h>
+
+#include "connman.h"
+
+DBusMessage *__connman_error_failed(DBusMessage *msg, int errnum)
+{
+       const char *str = strerror(errnum);
+
+       return g_dbus_create_error(msg, CONNMAN_ERROR_INTERFACE
+                                                       ".Failed", str);
+}
+
+DBusMessage *__connman_error_invalid_arguments(DBusMessage *msg)
+{
+       return g_dbus_create_error(msg, CONNMAN_ERROR_INTERFACE
+                               ".InvalidArguments", "Invalid arguments");
+}
+
+DBusMessage *__connman_error_permission_denied(DBusMessage *msg)
+{
+       return g_dbus_create_error(msg, CONNMAN_ERROR_INTERFACE
+                               ".PermissionDenied", "Permission denied");
+}
+
+DBusMessage *__connman_error_not_supported(DBusMessage *msg)
+{
+       return g_dbus_create_error(msg, CONNMAN_ERROR_INTERFACE
+                                       ".NotSupported", "Not supported");
+}
+
+DBusMessage *__connman_error_not_implemented(DBusMessage *msg)
+{
+       return g_dbus_create_error(msg, CONNMAN_ERROR_INTERFACE
+                                       ".NotImplemented", "Not implemented");
+}
+
+DBusMessage *__connman_error_no_carrier(DBusMessage *msg)
+{
+       return g_dbus_create_error(msg, CONNMAN_ERROR_INTERFACE
+                                               ".NoCarrier", "No carrier");
+}
+
+DBusMessage *__connman_error_in_progress(DBusMessage *msg)
+{
+       return g_dbus_create_error(msg, CONNMAN_ERROR_INTERFACE
+                                               ".InProgress", "In progress");
+}
+
+DBusMessage *__connman_error_already_connected(DBusMessage *msg)
+{
+       return g_dbus_create_error(msg, CONNMAN_ERROR_INTERFACE
+                               ".AlreadyConnected", "Already connected");
+
+}
+
+DBusMessage *__connman_error_operation_aborted(DBusMessage *msg)
+{
+       return g_dbus_create_error(msg, CONNMAN_ERROR_INTERFACE
+                               "OperationAborted", "Operation aborted");
+}
+
+DBusMessage *__connman_error_operation_timeout(DBusMessage *msg)
+{
+       return g_dbus_create_error(msg, CONNMAN_ERROR_INTERFACE
+                               "OperationTimeout", "Operation timeout");
+}
+
+DBusMessage *__connman_error_invalid_service(DBusMessage *msg)
+{
+       return g_dbus_create_error(msg, CONNMAN_ERROR_INTERFACE
+                                       "InvalidService", "Invalid service");
+}
diff --git a/src/inet.c b/src/inet.c
new file mode 100644 (file)
index 0000000..56ccc59
--- /dev/null
@@ -0,0 +1,417 @@
+/*
+ *
+ *  Connection Manager
+ *
+ *  Copyright (C) 2007-2009  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 <errno.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/stat.h>
+#include <sys/ioctl.h>
+#include <sys/socket.h>
+#include <net/ethernet.h>
+#include <linux/if_arp.h>
+#include <linux/wireless.h>
+
+#include "connman.h"
+
+int connman_inet_ifindex(const char *name)
+{
+       struct ifreq ifr;
+       int sk, err;
+
+       if (name == NULL)
+               return -1;
+
+       sk = socket(PF_INET, SOCK_DGRAM, 0);
+       if (sk < 0)
+               return -1;
+
+       memset(&ifr, 0, sizeof(ifr));
+       strncpy(ifr.ifr_name, name, sizeof(ifr.ifr_name));
+
+       err = ioctl(sk, SIOCGIFINDEX, &ifr);
+
+       close(sk);
+
+       if (err < 0)
+               return -1;
+
+       return ifr.ifr_ifindex;
+}
+
+char *connman_inet_ifname(int index)
+{
+       struct ifreq ifr;
+       int sk, err;
+
+       if (index < 0)
+               return NULL;
+
+       sk = socket(PF_INET, SOCK_DGRAM, 0);
+       if (sk < 0)
+               return NULL;
+
+       memset(&ifr, 0, sizeof(ifr));
+       ifr.ifr_ifindex = index;
+
+       err = ioctl(sk, SIOCGIFNAME, &ifr);
+
+       close(sk);
+
+       if (err < 0)
+               return NULL;
+
+       return strdup(ifr.ifr_name);
+}
+
+int connman_inet_ifup(int index)
+{
+       struct ifreq ifr;
+       int sk, err;
+
+       sk = socket(PF_INET, SOCK_DGRAM, 0);
+       if (sk < 0)
+               return -errno;
+
+       memset(&ifr, 0, sizeof(ifr));
+       ifr.ifr_ifindex = index;
+
+       if (ioctl(sk, SIOCGIFNAME, &ifr) < 0) {
+               err = -errno;
+               goto done;
+       }
+
+       if (ioctl(sk, SIOCGIFFLAGS, &ifr) < 0) {
+               err = -errno;
+               goto done;
+       }
+
+       if (ifr.ifr_flags & IFF_UP) {
+               err = -EALREADY;
+               goto done;
+       }
+
+       ifr.ifr_flags |= IFF_UP;
+
+       if (ioctl(sk, SIOCSIFFLAGS, &ifr) < 0) {
+               err = -errno;
+               goto done;
+       }
+
+       err = 0;
+
+done:
+       close(sk);
+
+       return err;
+}
+
+int connman_inet_ifdown(int index)
+{
+       struct ifreq ifr;
+       int sk, err;
+
+       sk = socket(PF_INET, SOCK_DGRAM, 0);
+       if (sk < 0)
+               return -errno;
+
+       memset(&ifr, 0, sizeof(ifr));
+       ifr.ifr_ifindex = index;
+
+       if (ioctl(sk, SIOCGIFNAME, &ifr) < 0) {
+               err = -errno;
+               goto done;
+       }
+
+       if (ioctl(sk, SIOCGIFFLAGS, &ifr) < 0) {
+               err = -errno;
+               goto done;
+       }
+
+       if (!(ifr.ifr_flags & IFF_UP)) {
+               err = -EALREADY;
+               goto done;
+       }
+
+       ifr.ifr_flags &= ~IFF_UP;
+
+       if (ioctl(sk, SIOCSIFFLAGS, &ifr) < 0)
+               err = -errno;
+       else
+               err = 0;
+
+done:
+       close(sk);
+
+       return err;
+}
+
+static unsigned short index2type(int index)
+{
+       struct ifreq ifr;
+       int sk, err;
+
+       if (index < 0)
+               return ARPHRD_VOID;
+
+       sk = socket(PF_INET, SOCK_DGRAM, 0);
+       if (sk < 0)
+               return ARPHRD_VOID;
+
+       memset(&ifr, 0, sizeof(ifr));
+       ifr.ifr_ifindex = index;
+
+       err = ioctl(sk, SIOCGIFNAME, &ifr);
+
+       if (err == 0)
+               err = ioctl(sk, SIOCGIFHWADDR, &ifr);
+
+       close(sk);
+
+       if (err < 0)
+               return ARPHRD_VOID;
+
+       return ifr.ifr_hwaddr.sa_family;
+}
+
+static char *index2addr(int index)
+{
+       struct ifreq ifr;
+       struct ether_addr *eth;
+       char *str;
+       int sk, err;
+
+       if (index < 0)
+               return NULL;
+
+       sk = socket(PF_INET, SOCK_DGRAM, 0);
+       if (sk < 0)
+               return NULL;
+
+       memset(&ifr, 0, sizeof(ifr));
+       ifr.ifr_ifindex = index;
+
+       err = ioctl(sk, SIOCGIFNAME, &ifr);
+
+       if (err == 0)
+               err = ioctl(sk, SIOCGIFHWADDR, &ifr);
+
+       close(sk);
+
+       if (err < 0)
+               return NULL;
+
+       str = malloc(18);
+       if (!str)
+               return NULL;
+
+       eth = (void *) &ifr.ifr_hwaddr.sa_data;
+       snprintf(str, 18, "%02X:%02X:%02X:%02X:%02X:%02X",
+                                               eth->ether_addr_octet[0],
+                                               eth->ether_addr_octet[1],
+                                               eth->ether_addr_octet[2],
+                                               eth->ether_addr_octet[3],
+                                               eth->ether_addr_octet[4],
+                                               eth->ether_addr_octet[5]);
+
+       return str;
+}
+
+static char *index2ident(int index, const char *prefix)
+{
+       struct ifreq ifr;
+       struct ether_addr *eth;
+       char *str;
+       int sk, err, len;
+
+       if (index < 0)
+               return NULL;
+
+       sk = socket(PF_INET, SOCK_DGRAM, 0);
+       if (sk < 0)
+               return NULL;
+
+       memset(&ifr, 0, sizeof(ifr));
+       ifr.ifr_ifindex = index;
+
+       err = ioctl(sk, SIOCGIFNAME, &ifr);
+
+       if (err == 0)
+               err = ioctl(sk, SIOCGIFHWADDR, &ifr);
+
+       close(sk);
+
+       if (err < 0)
+               return NULL;
+
+       len = prefix ? strlen(prefix) + 18 : 18;
+
+       str = malloc(len);
+       if (!str)
+               return NULL;
+
+       eth = (void *) &ifr.ifr_hwaddr.sa_data;
+       snprintf(str, len, "%s%02x%02x%02x%02x%02x%02x",
+                                               prefix ? prefix : "",
+                                               eth->ether_addr_octet[0],
+                                               eth->ether_addr_octet[1],
+                                               eth->ether_addr_octet[2],
+                                               eth->ether_addr_octet[3],
+                                               eth->ether_addr_octet[4],
+                                               eth->ether_addr_octet[5]);
+
+       return str;
+}
+
+struct connman_device *connman_inet_create_device(int index)
+{
+       enum connman_device_type devtype = CONNMAN_DEVICE_TYPE_UNKNOWN;
+       enum connman_device_mode mode = CONNMAN_DEVICE_MODE_UNKNOWN;
+       struct connman_device *device;
+       unsigned short type = index2type(index);
+       char *addr, *name, *devname, *ident = NULL;
+
+       if (index < 0)
+               return NULL;
+
+       devname = connman_inet_ifname(index);
+       if (devname == NULL)
+               return NULL;
+
+       if (type == ARPHRD_ETHER) {
+               char bridge_path[PATH_MAX], wimax_path[PATH_MAX];
+               struct stat st;
+               struct iwreq iwr;
+               int sk;
+
+               snprintf(bridge_path, PATH_MAX,
+                                       "/sys/class/net/%s/bridge", devname);
+               snprintf(wimax_path, PATH_MAX,
+                                       "/sys/class/net/%s/wimax", devname);
+
+               memset(&iwr, 0, sizeof(iwr));
+               strncpy(iwr.ifr_ifrn.ifrn_name, devname, IFNAMSIZ);
+
+               sk = socket(PF_INET, SOCK_DGRAM, 0);
+
+               if (g_str_has_prefix(devname, "vmnet") == TRUE ||
+                               g_str_has_prefix(devname, "vboxnet") == TRUE) {
+                       connman_info("Ignoring network interface %s", devname);
+                       devtype = CONNMAN_DEVICE_TYPE_UNKNOWN;
+               } else if (g_str_has_prefix(devname, "bnep") == TRUE)
+                       devtype = CONNMAN_DEVICE_TYPE_UNKNOWN;
+               else if (g_str_has_prefix(devname, "wmx") == TRUE)
+                       devtype = CONNMAN_DEVICE_TYPE_UNKNOWN;
+               else if (stat(wimax_path, &st) == 0 && (st.st_mode & S_IFDIR))
+                       devtype = CONNMAN_DEVICE_TYPE_UNKNOWN;
+               else if (stat(bridge_path, &st) == 0 && (st.st_mode & S_IFDIR))
+                       devtype = CONNMAN_DEVICE_TYPE_UNKNOWN;
+               else if (ioctl(sk, SIOCGIWNAME, &iwr) == 0)
+                       devtype = CONNMAN_DEVICE_TYPE_WIFI;
+               else
+                       devtype = CONNMAN_DEVICE_TYPE_ETHERNET;
+
+               close(sk);
+       } else if (type == ARPHRD_NONE) {
+               if (g_str_has_prefix(devname, "hso") == TRUE)
+                       devtype = CONNMAN_DEVICE_TYPE_HSO;
+       }
+
+       switch (devtype) {
+       case CONNMAN_DEVICE_TYPE_UNKNOWN:
+               g_free(devname);
+               return NULL;
+       case CONNMAN_DEVICE_TYPE_ETHERNET:
+       case CONNMAN_DEVICE_TYPE_WIFI:
+       case CONNMAN_DEVICE_TYPE_WIMAX:
+               name = index2ident(index, "");
+               addr = index2addr(index);
+               break;
+       case CONNMAN_DEVICE_TYPE_BLUETOOTH:
+       case CONNMAN_DEVICE_TYPE_GPS:
+       case CONNMAN_DEVICE_TYPE_HSO:
+       case CONNMAN_DEVICE_TYPE_NOZOMI:
+       case CONNMAN_DEVICE_TYPE_HUAWEI:
+       case CONNMAN_DEVICE_TYPE_NOVATEL:
+       case CONNMAN_DEVICE_TYPE_VENDOR:
+               name = strdup(devname);
+               addr = NULL;
+               break;
+       }
+
+       device = connman_device_create(name, devtype);
+       if (device == NULL) {
+               g_free(devname);
+               g_free(name);
+               g_free(addr);
+               return NULL;
+       }
+
+       switch (devtype) {
+       case CONNMAN_DEVICE_TYPE_UNKNOWN:
+       case CONNMAN_DEVICE_TYPE_VENDOR:
+       case CONNMAN_DEVICE_TYPE_NOZOMI:
+       case CONNMAN_DEVICE_TYPE_HUAWEI:
+       case CONNMAN_DEVICE_TYPE_NOVATEL:
+       case CONNMAN_DEVICE_TYPE_GPS:
+               mode = CONNMAN_DEVICE_MODE_UNKNOWN;
+               break;
+       case CONNMAN_DEVICE_TYPE_ETHERNET:
+               mode = CONNMAN_DEVICE_MODE_TRANSPORT_IP;
+               ident = index2ident(index, NULL);
+               break;
+       case CONNMAN_DEVICE_TYPE_WIFI:
+       case CONNMAN_DEVICE_TYPE_WIMAX:
+               mode = CONNMAN_DEVICE_MODE_NETWORK_SINGLE;
+               ident = index2ident(index, NULL);
+               break;
+       case CONNMAN_DEVICE_TYPE_BLUETOOTH:
+               mode = CONNMAN_DEVICE_MODE_NETWORK_MULTIPLE;
+               break;
+       case CONNMAN_DEVICE_TYPE_HSO:
+               mode = CONNMAN_DEVICE_MODE_NETWORK_SINGLE;
+               connman_device_set_policy(device, CONNMAN_DEVICE_POLICY_MANUAL);
+               break;
+       }
+
+       connman_device_set_mode(device, mode);
+
+       connman_device_set_index(device, index);
+       connman_device_set_interface(device, devname);
+
+       if (ident != NULL) {
+               connman_device_set_ident(device, ident);
+               g_free(ident);
+       }
+
+       connman_device_set_string(device, "Address", addr);
+
+       g_free(devname);
+       g_free(name);
+       g_free(addr);
+
+       return device;
+}
diff --git a/src/ipconfig.c b/src/ipconfig.c
new file mode 100644 (file)
index 0000000..d369296
--- /dev/null
@@ -0,0 +1,67 @@
+/*
+ *
+ *  Connection Manager
+ *
+ *  Copyright (C) 2007-2009  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 "connman.h"
+
+static GSList *ipconfig_list = NULL;
+
+static gint compare_priority(gconstpointer a, gconstpointer b)
+{
+       const struct connman_ipconfig *ipconfig1 = a;
+       const struct connman_ipconfig *ipconfig2 = b;
+
+       return ipconfig2->priority - ipconfig1->priority;
+}
+
+/**
+ * connman_ipconfig_register:
+ * @ipconfig: IP configuration module
+ *
+ * Register a new IP configuration module
+ *
+ * Returns: %0 on success
+ */
+int connman_ipconfig_register(struct connman_ipconfig *ipconfig)
+{
+       DBG("ipconfig %p name %s", ipconfig, ipconfig->name);
+
+       ipconfig_list = g_slist_insert_sorted(ipconfig_list, ipconfig,
+                                                       compare_priority);
+
+       return 0;
+}
+
+/**
+ * connman_ipconfig_unregister:
+ * @ipconfig: IP configuration module
+ *
+ * Remove a previously registered IP configuration module.
+ */
+void connman_ipconfig_unregister(struct connman_ipconfig *ipconfig)
+{
+       DBG("ipconfig %p name %s", ipconfig, ipconfig->name);
+
+       ipconfig_list = g_slist_remove(ipconfig_list, ipconfig);
+}
diff --git a/src/ipv4.c b/src/ipv4.c
new file mode 100644 (file)
index 0000000..b670cfd
--- /dev/null
@@ -0,0 +1,237 @@
+/*
+ *
+ *  Connection Manager
+ *
+ *  Copyright (C) 2007-2009  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 <unistd.h>
+#include <string.h>
+#include <sys/ioctl.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <net/if.h>
+
+#include "connman.h"
+
+struct connman_ipv4 {
+       enum connman_ipv4_method method;
+       struct in_addr address;
+       struct in_addr netmask;
+       struct in_addr broadcast;
+};
+
+static int set_ipv4(struct connman_element *element,
+                       struct connman_ipv4 *ipv4, const char *nameserver)
+{
+       struct ifreq ifr;
+       struct sockaddr_in *addr;
+       int sk, err;
+
+       DBG("element %p ipv4 %p", element, ipv4);
+
+       sk = socket(PF_INET, SOCK_DGRAM, 0);
+       if (sk < 0)
+               return -1;
+
+       memset(&ifr, 0, sizeof(ifr));
+       ifr.ifr_ifindex = element->index;
+
+       if (ioctl(sk, SIOCGIFNAME, &ifr) < 0) {
+               close(sk);
+               return -1;
+       }
+
+       DBG("ifname %s", ifr.ifr_name);
+
+       addr = (struct sockaddr_in *) &ifr.ifr_addr;
+       addr->sin_family = AF_INET;
+       addr->sin_addr = ipv4->address;
+
+       err = ioctl(sk, SIOCSIFADDR, &ifr);
+
+       if (err < 0)
+               DBG("address setting failed (%s)", strerror(errno));
+
+       addr = (struct sockaddr_in *) &ifr.ifr_netmask;
+       addr->sin_family = AF_INET;
+       addr->sin_addr = ipv4->netmask;
+
+       err = ioctl(sk, SIOCSIFNETMASK, &ifr);
+
+       if (err < 0)
+               DBG("netmask setting failed (%s)", strerror(errno));
+
+       addr = (struct sockaddr_in *) &ifr.ifr_broadaddr;
+       addr->sin_family = AF_INET;
+       addr->sin_addr = ipv4->broadcast;
+
+       err = ioctl(sk, SIOCSIFBRDADDR, &ifr);
+
+       if (err < 0)
+               DBG("broadcast setting failed (%s)", strerror(errno));
+
+       close(sk);
+
+       if (nameserver == NULL)
+               connman_error("No nameserver for %s defined", ifr.ifr_name);
+
+       connman_resolver_append(ifr.ifr_name, NULL, nameserver);
+
+       return 0;
+}
+
+static int clear_ipv4(struct connman_element *element)
+{
+       struct ifreq ifr;
+       struct sockaddr_in *addr;
+       int sk, err;
+
+       DBG("element %p", element);
+
+       sk = socket(PF_INET, SOCK_DGRAM, 0);
+       if (sk < 0)
+               return -1;
+
+       memset(&ifr, 0, sizeof(ifr));
+       ifr.ifr_ifindex = element->index;
+
+       if (ioctl(sk, SIOCGIFNAME, &ifr) < 0) {
+               close(sk);
+               return -1;
+       }
+
+       DBG("ifname %s", ifr.ifr_name);
+
+       connman_resolver_remove_all(ifr.ifr_name);
+
+       addr = (struct sockaddr_in *) &ifr.ifr_addr;
+       addr->sin_family = AF_INET;
+       addr->sin_addr.s_addr = INADDR_ANY;
+
+       //err = ioctl(sk, SIOCDIFADDR, &ifr);
+       err = ioctl(sk, SIOCSIFADDR, &ifr);
+
+       close(sk);
+
+       if (err < 0 && errno != EADDRNOTAVAIL) {
+               DBG("address removal failed (%s)", strerror(errno));
+               return -1;
+       }
+
+       return 0;
+}
+
+static char *index2name(int index)
+{
+       struct ifreq ifr;
+       int sk, err;
+
+       if (index < 0)
+               return NULL;
+
+       sk = socket(PF_INET, SOCK_DGRAM, 0);
+       if (sk < 0)
+               return NULL;
+
+       memset(&ifr, 0, sizeof(ifr));
+       ifr.ifr_ifindex = index;
+
+       err = ioctl(sk, SIOCGIFNAME, &ifr);
+
+       close(sk);
+
+       if (err < 0)
+               return NULL;
+
+       return strdup(ifr.ifr_name);
+}
+
+static int ipv4_probe(struct connman_element *element)
+{
+       struct connman_element *connection;
+       struct connman_ipv4 ipv4;
+       const char *address = NULL, *netmask = NULL, *broadcast = NULL;
+       const char *nameserver = NULL;
+
+       DBG("element %p name %s", element, element->name);
+
+       connman_element_get_value(element,
+                               CONNMAN_PROPERTY_ID_IPV4_ADDRESS, &address);
+       connman_element_get_value(element,
+                               CONNMAN_PROPERTY_ID_IPV4_NETMASK, &netmask);
+       connman_element_get_value(element,
+                               CONNMAN_PROPERTY_ID_IPV4_BROADCAST, &broadcast);
+
+       connman_element_get_value(element,
+                       CONNMAN_PROPERTY_ID_IPV4_NAMESERVER, &nameserver);
+
+       DBG("address %s", address);
+       DBG("netmask %s", netmask);
+       DBG("broadcast %s", broadcast);
+
+       if (address == NULL || netmask == NULL)
+               return -EINVAL;
+
+       memset(&ipv4, 0, sizeof(ipv4));
+       ipv4.address.s_addr = inet_addr(address);
+       ipv4.netmask.s_addr = inet_addr(netmask);
+       ipv4.broadcast.s_addr = inet_addr(broadcast);
+
+       set_ipv4(element, &ipv4, nameserver);
+
+       connection = connman_element_create(NULL);
+
+       connection->type    = CONNMAN_ELEMENT_TYPE_CONNECTION;
+       connection->index   = element->index;
+       connection->devname = index2name(element->index);
+
+       if (connman_element_register(connection, element) < 0)
+               connman_element_unref(connection);
+
+       return 0;
+}
+
+static void ipv4_remove(struct connman_element *element)
+{
+       DBG("element %p name %s", element, element->name);
+
+       clear_ipv4(element);
+}
+
+static struct connman_driver ipv4_driver = {
+       .name           = "ipv4",
+       .type           = CONNMAN_ELEMENT_TYPE_IPV4,
+       .priority       = CONNMAN_DRIVER_PRIORITY_LOW,
+       .probe          = ipv4_probe,
+       .remove         = ipv4_remove,
+};
+
+int __connman_ipv4_init(void)
+{
+       return connman_driver_register(&ipv4_driver);
+}
+
+void __connman_ipv4_cleanup(void)
+{
+       connman_driver_unregister(&ipv4_driver);
+}
diff --git a/src/log.c b/src/log.c
new file mode 100644 (file)
index 0000000..3a87b93
--- /dev/null
+++ b/src/log.c
@@ -0,0 +1,127 @@
+/*
+ *
+ *  Connection Manager
+ *
+ *  Copyright (C) 2007-2009  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 <stdarg.h>
+#include <syslog.h>
+
+#include "connman.h"
+
+static volatile gboolean debug_enabled = FALSE;
+
+/**
+ * connman_info:
+ * @format: format string
+ * @Varargs: list of arguments
+ *
+ * Output general information
+ */
+void connman_info(const char *format, ...)
+{
+       va_list ap;
+
+       va_start(ap, format);
+
+       vsyslog(LOG_INFO, format, ap);
+
+       va_end(ap);
+}
+
+/**
+ * connman_error:
+ * @format: format string
+ * @varargs: list of arguments
+ *
+ * Output error messages
+ */
+void connman_error(const char *format, ...)
+{
+       va_list ap;
+
+       va_start(ap, format);
+
+       vsyslog(LOG_ERR, format, ap);
+
+       va_end(ap);
+}
+
+/**
+ * connman_debug:
+ * @format: format string
+ * @varargs: list of arguments
+ *
+ * Output debug message
+ *
+ * The actual output of the debug message is controlled via a command line
+ * switch. If not enabled, these messages will be ignored.
+ */
+void connman_debug(const char *format, ...)
+{
+       va_list ap;
+
+       if (debug_enabled == FALSE)
+               return;
+
+       va_start(ap, format);
+
+       vsyslog(LOG_DEBUG, format, ap);
+
+       va_end(ap);
+}
+
+void __connman_toggle_debug(void)
+{
+       if (debug_enabled == TRUE)
+               debug_enabled = FALSE;
+       else
+               debug_enabled = TRUE;
+}
+
+int __connman_log_init(gboolean detach, gboolean debug)
+{
+       int option = LOG_NDELAY | LOG_PID;
+
+       if (detach == FALSE)
+               option |= LOG_PERROR;
+
+       openlog("connmand", option, LOG_DAEMON);
+
+       syslog(LOG_INFO, "Connection Manager version %s", VERSION);
+
+       debug_enabled = debug;
+
+       return 0;
+}
+
+void __connman_log_cleanup(void)
+{
+       syslog(LOG_INFO, "Exit");
+
+       closelog();
+}
+
+gboolean __connman_debug_enabled(void)
+{
+       return debug_enabled;
+}
diff --git a/src/main.c b/src/main.c
new file mode 100644 (file)
index 0000000..ac7e7f8
--- /dev/null
@@ -0,0 +1,257 @@
+/*
+ *
+ *  Connection Manager
+ *
+ *  Copyright (C) 2007-2009  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 <string.h>
+#include <signal.h>
+#include <getopt.h>
+#include <sys/stat.h>
+#include <net/if.h>
+
+#include <gdbus.h>
+
+#include "connman.h"
+
+static GMainLoop *main_loop = NULL;
+
+static void sig_term(int sig)
+{
+       g_main_loop_quit(main_loop);
+}
+
+static void sig_debug(int sig)
+{
+       __connman_toggle_debug();
+}
+
+static void disconnect_callback(DBusConnection *conn, void *user_data)
+{
+       DBG("D-Bus disconnect");
+
+       g_main_loop_quit(main_loop);
+}
+
+static gchar *option_device = NULL;
+static gchar *option_plugin = NULL;
+static gchar *option_nodevice = NULL;
+static gchar *option_noplugin = NULL;
+static gchar *option_wifi = NULL;
+static gboolean option_detach = TRUE;
+static gboolean option_compat = FALSE;
+static gboolean option_debug = FALSE;
+static gboolean option_selftest = FALSE;
+static gboolean option_version = FALSE;
+
+static GOptionEntry options[] = {
+       { "device", 'i', 0, G_OPTION_ARG_STRING, &option_device,
+                       "Specify networking device or interface", "DEV" },
+       { "nodevice", 'I', 0, G_OPTION_ARG_STRING, &option_nodevice,
+                       "Specify networking interface to ignore", "DEV" },
+       { "plugin", 'p', 0, G_OPTION_ARG_STRING, &option_plugin,
+                               "Specify plugins to load", "NAME" },
+       { "noplugin", 'P', 0, G_OPTION_ARG_STRING, &option_noplugin,
+                               "Specify plugins not to load", "NAME" },
+       { "wifi", 'W', 0, G_OPTION_ARG_STRING, &option_wifi,
+                               "Specify driver for WiFi/Supplicant", "NAME" },
+       { "nodaemon", 'n', G_OPTION_FLAG_REVERSE,
+                               G_OPTION_ARG_NONE, &option_detach,
+                               "Don't fork daemon to background" },
+       { "compat", 'c', 0, G_OPTION_ARG_NONE, &option_compat,
+                               "Enable Network Manager compatibility" },
+       { "debug", 'd', 0, G_OPTION_ARG_NONE, &option_debug,
+                               "Enable debug information output" },
+       { "selftest", 't', 0, G_OPTION_ARG_NONE, &option_selftest,
+                               "Run self testing routines" },
+       { "version", 'v', 0, G_OPTION_ARG_NONE, &option_version,
+                               "Show version information and exit" },
+       { NULL },
+};
+
+const char *connman_option_get_string(const char *key)
+{
+       if (g_strcmp0(key, "wifi") == 0) {
+               if (option_wifi == NULL)
+                       return "wext,nl80211";
+               else
+                       return option_wifi;
+       }
+
+       return NULL;
+}
+
+int main(int argc, char *argv[])
+{
+       GOptionContext *context;
+       GError *error = NULL;
+       DBusConnection *conn;
+       DBusError err;
+       struct sigaction sa;
+
+#ifdef NEED_THREADS
+       if (g_thread_supported() == FALSE)
+               g_thread_init(NULL);
+#endif
+
+       context = g_option_context_new(NULL);
+       g_option_context_add_main_entries(context, options, NULL);
+
+       if (g_option_context_parse(context, &argc, &argv, &error) == FALSE) {
+               if (error != NULL) {
+                       g_printerr("%s\n", error->message);
+                       g_error_free(error);
+               } else
+                       g_printerr("An unknown error occurred\n");
+               exit(1);
+       }
+
+       g_option_context_free(context);
+
+       if (option_version == TRUE) {
+               printf("%s\n", VERSION);
+               exit(0);
+       }
+
+       if (option_detach == TRUE) {
+               if (daemon(0, 0)) {
+                       perror("Can't start daemon");
+                       exit(1);
+               }
+       }
+
+       if (mkdir(STATEDIR, S_IRUSR | S_IWUSR | S_IXUSR |
+                               S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH) < 0) {
+               if (errno != EEXIST)
+                       perror("Failed to create state directory");
+       }
+
+       if (mkdir(STORAGEDIR, S_IRUSR | S_IWUSR | S_IXUSR |
+                               S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH) < 0) {
+               if (errno != EEXIST)
+                       perror("Failed to create storage directory");
+       }
+
+       main_loop = g_main_loop_new(NULL, FALSE);
+
+#ifdef NEED_THREADS
+       if (dbus_threads_init_default() == FALSE) {
+               fprintf(stderr, "Can't init usage of threads\n");
+               exit(1);
+       }
+#endif
+
+       dbus_error_init(&err);
+
+       conn = g_dbus_setup_bus(DBUS_BUS_SYSTEM, CONNMAN_SERVICE, &err);
+       if (conn == NULL) {
+               if (dbus_error_is_set(&err) == TRUE) {
+                       fprintf(stderr, "%s\n", err.message);
+                       dbus_error_free(&err);
+               } else
+                       fprintf(stderr, "Can't register with system bus\n");
+               exit(1);
+       }
+
+       g_dbus_set_disconnect_function(conn, disconnect_callback, NULL, NULL);
+
+       if (option_compat == TRUE) {
+               if (g_dbus_request_name(conn, NM_SERVICE, NULL) == FALSE) {
+                       fprintf(stderr, "Can't register compat service\n");
+                       option_compat = FALSE;
+               }
+       }
+
+       __connman_log_init(option_detach, option_debug);
+
+       if (option_selftest == TRUE) {
+               if (__connman_selftest() < 0) {
+                       connman_error("Self testing routines failed");
+                       goto selftest;
+               }
+       }
+
+       __connman_dbus_init(conn);
+
+       __connman_storage_init();
+       __connman_element_init(conn, option_device, option_nodevice);
+
+       __connman_agent_init(conn);
+       __connman_manager_init(conn, option_compat);
+       __connman_profile_init(conn);
+
+       __connman_resolver_init();
+       __connman_rtnl_init();
+       __connman_udev_init();
+
+       __connman_plugin_init(option_plugin, option_noplugin);
+
+       __connman_element_start();
+
+       g_free(option_device);
+       g_free(option_plugin);
+       g_free(option_nodevice);
+       g_free(option_noplugin);
+
+       memset(&sa, 0, sizeof(sa));
+       sa.sa_handler = sig_term;
+       sigaction(SIGINT, &sa, NULL);
+       sigaction(SIGTERM, &sa, NULL);
+
+       sa.sa_handler = sig_debug;
+       sigaction(SIGUSR2, &sa, NULL);
+
+       g_main_loop_run(main_loop);
+
+       __connman_element_stop();
+
+       __connman_plugin_cleanup();
+
+       __connman_udev_cleanup();
+       __connman_rtnl_cleanup();
+       __connman_resolver_cleanup();
+
+       __connman_profile_cleanup();
+       __connman_manager_cleanup();
+       __connman_agent_cleanup();
+
+       __connman_element_cleanup();
+       __connman_storage_cleanup();
+
+       __connman_dbus_cleanup();
+
+selftest:
+       __connman_log_cleanup();
+
+       dbus_connection_unref(conn);
+
+       g_main_loop_unref(main_loop);
+
+       rmdir(STORAGEDIR);
+
+       rmdir(STATEDIR);
+
+       return 0;
+}
diff --git a/src/manager.c b/src/manager.c
new file mode 100644 (file)
index 0000000..14ea6ca
--- /dev/null
@@ -0,0 +1,500 @@
+/*
+ *
+ *  Connection Manager
+ *
+ *  Copyright (C) 2007-2009  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 <gdbus.h>
+
+#include "connman.h"
+
+enum connman_policy {
+       CONNMAN_POLICY_UNKNOWN  = 0,
+       CONNMAN_POLICY_SINGLE   = 1,
+       CONNMAN_POLICY_MULTIPLE = 2,
+       CONNMAN_POLICY_ASK      = 3,
+};
+
+static enum connman_policy global_policy = CONNMAN_POLICY_SINGLE;
+static connman_bool_t global_offlinemode = FALSE;
+
+static const char *policy2string(enum connman_policy policy)
+{
+       switch (policy) {
+       case CONNMAN_POLICY_UNKNOWN:
+               break;
+       case CONNMAN_POLICY_SINGLE:
+               return "single";
+       case CONNMAN_POLICY_MULTIPLE:
+               return "multiple";
+       case CONNMAN_POLICY_ASK:
+               return "ask";
+       }
+
+       return NULL;
+}
+
+static enum connman_policy string2policy(const char *policy)
+{
+       if (g_str_equal(policy, "single") == TRUE)
+               return CONNMAN_POLICY_SINGLE;
+       else if (g_str_equal(policy, "multiple") == TRUE)
+               return CONNMAN_POLICY_MULTIPLE;
+       else if (g_str_equal(policy, "ask") == TRUE)
+               return CONNMAN_POLICY_ASK;
+       else
+               return CONNMAN_POLICY_UNKNOWN;
+}
+
+static void append_profiles(DBusMessageIter *dict)
+{
+       DBusMessageIter entry, value, iter;
+       const char *key = "Profiles";
+
+       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_ARRAY_AS_STRING DBUS_TYPE_OBJECT_PATH_AS_STRING,
+                                                               &value);
+
+       dbus_message_iter_open_container(&value, DBUS_TYPE_ARRAY,
+                               DBUS_TYPE_OBJECT_PATH_AS_STRING, &iter);
+       __connman_profile_list(&iter);
+       dbus_message_iter_close_container(&value, &iter);
+
+       dbus_message_iter_close_container(&entry, &value);
+
+       dbus_message_iter_close_container(dict, &entry);
+}
+
+static void append_services(DBusMessageIter *dict)
+{
+       DBusMessageIter entry, value, iter;
+       const char *key = "Services";
+
+       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_ARRAY_AS_STRING DBUS_TYPE_OBJECT_PATH_AS_STRING,
+                                                               &value);
+
+       dbus_message_iter_open_container(&value, DBUS_TYPE_ARRAY,
+                               DBUS_TYPE_OBJECT_PATH_AS_STRING, &iter);
+       __connman_service_list(&iter);
+       dbus_message_iter_close_container(&value, &iter);
+
+       dbus_message_iter_close_container(&entry, &value);
+
+       dbus_message_iter_close_container(dict, &entry);
+}
+
+static void append_devices(DBusMessageIter *dict)
+{
+       DBusMessageIter entry, value, iter;
+       const char *key = "Devices";
+
+       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_ARRAY_AS_STRING DBUS_TYPE_OBJECT_PATH_AS_STRING,
+                                                               &value);
+
+       dbus_message_iter_open_container(&value, DBUS_TYPE_ARRAY,
+                               DBUS_TYPE_OBJECT_PATH_AS_STRING, &iter);
+       __connman_element_list(NULL, CONNMAN_ELEMENT_TYPE_DEVICE, &iter);
+       dbus_message_iter_close_container(&value, &iter);
+
+       dbus_message_iter_close_container(&entry, &value);
+
+       dbus_message_iter_close_container(dict, &entry);
+}
+
+static void append_connections(DBusMessageIter *dict)
+{
+       DBusMessageIter entry, value, iter;
+       const char *key = "Connections";
+
+       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_ARRAY_AS_STRING DBUS_TYPE_OBJECT_PATH_AS_STRING,
+                                                               &value);
+
+       dbus_message_iter_open_container(&value, DBUS_TYPE_ARRAY,
+                               DBUS_TYPE_OBJECT_PATH_AS_STRING, &iter);
+       __connman_element_list(NULL, CONNMAN_ELEMENT_TYPE_CONNECTION, &iter);
+       dbus_message_iter_close_container(&value, &iter);
+
+       dbus_message_iter_close_container(&entry, &value);
+
+       dbus_message_iter_close_container(dict, &entry);
+}
+
+static DBusMessage *get_properties(DBusConnection *conn,
+                                       DBusMessage *msg, void *data)
+{
+       DBusMessage *reply;
+       DBusMessageIter array, dict;
+       const char *str;
+
+       DBG("conn %p", conn);
+
+       if (__connman_security_check_privilege(msg,
+                                       CONNMAN_SECURITY_PRIVILEGE_PUBLIC) < 0)
+               return __connman_error_permission_denied(msg);
+
+       reply = dbus_message_new_method_return(msg);
+       if (reply == NULL)
+               return NULL;
+
+       dbus_message_iter_init_append(reply, &array);
+
+       dbus_message_iter_open_container(&array, 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);
+
+       str = __connman_profile_active_path();
+       if (str != NULL)
+               connman_dbus_dict_append_variant(&dict, "ActiveProfile",
+                                               DBUS_TYPE_OBJECT_PATH, &str);
+
+       append_profiles(&dict);
+       append_services(&dict);
+
+       append_devices(&dict);
+       append_connections(&dict);
+
+       if (__connman_element_count(NULL, CONNMAN_ELEMENT_TYPE_CONNECTION) > 0)
+               str = "online";
+       else
+               str = "offline";
+
+       connman_dbus_dict_append_variant(&dict, "State",
+                                               DBUS_TYPE_STRING, &str);
+
+       str = policy2string(global_policy);
+       if (str != NULL)
+               connman_dbus_dict_append_variant(&dict, "Policy",
+                                               DBUS_TYPE_STRING, &str);
+
+       connman_dbus_dict_append_variant(&dict, "OfflineMode",
+                               DBUS_TYPE_BOOLEAN, &global_offlinemode);
+
+       dbus_message_iter_close_container(&array, &dict);
+
+       return reply;
+}
+
+static DBusMessage *set_property(DBusConnection *conn,
+                                       DBusMessage *msg, void *data)
+{
+       DBusMessageIter iter, value;
+       const char *name;
+
+       DBG("conn %p", conn);
+
+       if (dbus_message_iter_init(msg, &iter) == FALSE)
+               return __connman_error_invalid_arguments(msg);
+
+       dbus_message_iter_get_basic(&iter, &name);
+       dbus_message_iter_next(&iter);
+       dbus_message_iter_recurse(&iter, &value);
+
+       if (__connman_security_check_privilege(msg,
+                                       CONNMAN_SECURITY_PRIVILEGE_MODIFY) < 0)
+               return __connman_error_permission_denied(msg);
+
+       if (g_str_equal(name, "Policy") == TRUE) {
+               enum connman_policy policy;
+               const char *str;
+
+               dbus_message_iter_get_basic(&value, &str);
+               policy = string2policy(str);
+               if (policy == CONNMAN_POLICY_UNKNOWN)
+                       return __connman_error_invalid_arguments(msg);
+
+               global_policy = policy;
+       } else if (g_str_equal(name, "OfflineMode") == TRUE) {
+               connman_bool_t offlinemode;
+
+               dbus_message_iter_get_basic(&value, &offlinemode);
+
+               if (global_offlinemode == offlinemode)
+                       return __connman_error_invalid_arguments(msg);
+
+               global_offlinemode = offlinemode;
+
+               __connman_device_set_offlinemode(offlinemode);
+       } else if (g_str_equal(name, "ActiveProfile") == TRUE) {
+               const char *str;
+
+               dbus_message_iter_get_basic(&value, &str);
+
+               return __connman_error_not_supported(msg);
+       }
+
+       return g_dbus_create_reply(msg, DBUS_TYPE_INVALID);
+}
+
+static DBusMessage *add_profile(DBusConnection *conn,
+                                       DBusMessage *msg, void *data)
+{
+       const char *name;
+
+       DBG("conn %p", conn);
+
+       dbus_message_get_args(msg, NULL, DBUS_TYPE_STRING, &name,
+                                                       DBUS_TYPE_INVALID);
+
+       return __connman_error_not_supported(msg);
+}
+
+static DBusMessage *remove_profile(DBusConnection *conn,
+                                       DBusMessage *msg, void *data)
+{
+       const char *path;
+
+       DBG("conn %p", conn);
+
+       dbus_message_get_args(msg, NULL, DBUS_TYPE_OBJECT_PATH, &path,
+                                                       DBUS_TYPE_INVALID);
+
+       return __connman_error_not_supported(msg);
+}
+
+static DBusMessage *connect_service(DBusConnection *conn,
+                                       DBusMessage *msg, void *data)
+{
+       DBusMessageIter iter, array;
+
+       DBG("conn %p", conn);
+
+       if (__connman_security_check_privilege(msg,
+                                       CONNMAN_SECURITY_PRIVILEGE_MODIFY) < 0)
+               return __connman_error_permission_denied(msg);
+
+       dbus_message_iter_init(msg, &iter);
+       dbus_message_iter_recurse(&iter, &array);
+
+       while (dbus_message_iter_get_arg_type(&array) == DBUS_TYPE_DICT_ENTRY) {
+               DBusMessageIter entry, value;
+               const char *key;
+
+               dbus_message_iter_recurse(&array, &entry);
+               dbus_message_iter_get_basic(&entry, &key);
+
+               dbus_message_iter_next(&entry);
+               dbus_message_iter_recurse(&entry, &value);
+
+               switch (dbus_message_iter_get_arg_type(&value)) {
+               }
+
+               dbus_message_iter_next(&array);
+       }
+
+       return __connman_error_not_implemented(msg);
+}
+
+static DBusMessage *register_agent(DBusConnection *conn,
+                                       DBusMessage *msg, void *data)
+{
+       DBusMessage *reply;
+       const char *sender, *path;
+
+       DBG("conn %p", conn);
+
+       sender = dbus_message_get_sender(msg);
+
+       dbus_message_get_args(msg, NULL, DBUS_TYPE_OBJECT_PATH, &path,
+                                                       DBUS_TYPE_INVALID);
+
+       reply = dbus_message_new_method_return(msg);
+       if (reply == NULL)
+               return NULL;
+
+       dbus_message_append_args(reply, DBUS_TYPE_INVALID);
+
+       __connman_agent_register(sender, path);
+
+       return reply;
+}
+
+static DBusMessage *unregister_agent(DBusConnection *conn,
+                                       DBusMessage *msg, void *data)
+{
+       DBusMessage *reply;
+       const char *sender, *path;
+
+       DBG("conn %p", conn);
+
+       sender = dbus_message_get_sender(msg);
+
+       dbus_message_get_args(msg, NULL, DBUS_TYPE_OBJECT_PATH, &path,
+                                                       DBUS_TYPE_INVALID);
+
+       reply = dbus_message_new_method_return(msg);
+       if (reply == NULL)
+               return NULL;
+
+       dbus_message_append_args(reply, DBUS_TYPE_INVALID);
+
+       __connman_agent_unregister(sender, path);
+
+       return reply;
+}
+
+static GDBusMethodTable manager_methods[] = {
+       { "GetProperties",   "",      "a{sv}", get_properties   },
+       { "SetProperty",     "sv",    "",      set_property     },
+       { "AddProfile",      "s",     "o",     add_profile      },
+       { "RemoveProfile",   "o",     "",      remove_profile   },
+       { "ConnectService",  "a{sv}", "o",     connect_service  },
+       { "RegisterAgent",   "o",     "",      register_agent   },
+       { "UnregisterAgent", "o",     "",      unregister_agent },
+       { },
+};
+
+static GDBusSignalTable manager_signals[] = {
+       { "PropertyChanged", "sv" },
+       { "StateChanged",    "s"  },
+       { },
+};
+
+static DBusMessage *nm_sleep(DBusConnection *conn,
+                                       DBusMessage *msg, void *data)
+{
+       DBusMessage *reply;
+
+       DBG("conn %p", conn);
+
+       reply = dbus_message_new_method_return(msg);
+       if (reply == NULL)
+               return NULL;
+
+       dbus_message_append_args(reply, DBUS_TYPE_INVALID);
+
+       return reply;
+}
+
+static DBusMessage *nm_wake(DBusConnection *conn,
+                                       DBusMessage *msg, void *data)
+{
+       DBusMessage *reply;
+
+       DBG("conn %p", conn);
+
+       reply = dbus_message_new_method_return(msg);
+       if (reply == NULL)
+               return NULL;
+
+       dbus_message_append_args(reply, DBUS_TYPE_INVALID);
+
+       return reply;
+}
+
+enum {
+       NM_STATE_UNKNOWN = 0,
+       NM_STATE_ASLEEP,
+       NM_STATE_CONNECTING,
+       NM_STATE_CONNECTED,
+       NM_STATE_DISCONNECTED
+};
+
+static DBusMessage *nm_state(DBusConnection *conn,
+                                       DBusMessage *msg, void *data)
+{
+       DBusMessage *reply;
+       dbus_uint32_t state;
+
+       DBG("conn %p", conn);
+
+       reply = dbus_message_new_method_return(msg);
+       if (reply == NULL)
+               return NULL;
+
+       state = NM_STATE_DISCONNECTED;
+
+       dbus_message_append_args(reply, DBUS_TYPE_UINT32, &state,
+                                                       DBUS_TYPE_INVALID);
+
+       return reply;
+}
+
+static GDBusMethodTable nm_methods[] = {
+       { "sleep", "",  "",   nm_sleep        },
+       { "wake",  "",  "",   nm_wake         },
+       { "state", "",  "u",  nm_state        },
+       { },
+};
+
+static DBusConnection *connection = NULL;
+static gboolean nm_compat = FALSE;
+
+int __connman_manager_init(DBusConnection *conn, gboolean compat)
+{
+       DBG("conn %p", conn);
+
+       connection = dbus_connection_ref(conn);
+       if (connection == NULL)
+               return -1;
+
+       g_dbus_register_interface(connection, CONNMAN_MANAGER_PATH,
+                                       CONNMAN_MANAGER_INTERFACE,
+                                       manager_methods,
+                                       manager_signals, NULL, NULL, NULL);
+
+       if (compat == TRUE) {
+               g_dbus_register_interface(connection, NM_PATH, NM_INTERFACE,
+                                       nm_methods, NULL, NULL, NULL, NULL);
+
+               nm_compat = TRUE;
+       }
+
+       return 0;
+}
+
+void __connman_manager_cleanup(void)
+{
+       DBG("conn %p", connection);
+
+       if (nm_compat == TRUE) {
+               g_dbus_unregister_interface(connection, NM_PATH, NM_INTERFACE);
+       }
+
+       g_dbus_unregister_interface(connection, CONNMAN_MANAGER_PATH,
+                                               CONNMAN_MANAGER_INTERFACE);
+
+       dbus_connection_unref(connection);
+}
diff --git a/src/network.c b/src/network.c
new file mode 100644 (file)
index 0000000..cf0586b
--- /dev/null
@@ -0,0 +1,1318 @@
+/*
+ *
+ *  Connection Manager
+ *
+ *  Copyright (C) 2007-2009  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 <errno.h>
+#include <string.h>
+
+#include <gdbus.h>
+
+#include "connman.h"
+
+struct connman_network {
+       struct connman_element element;
+       enum connman_network_type type;
+       enum connman_network_protocol protocol;
+       connman_bool_t associating;
+       connman_bool_t secondary;
+       connman_bool_t available;
+       connman_bool_t connected;
+       connman_uint8_t strength;
+       connman_uint16_t frequency;
+       char *identifier;
+       char *address;
+       char *name;
+       char *node;
+       char *group;
+
+       struct connman_network_driver *driver;
+       void *driver_data;
+
+       connman_bool_t registered;
+
+       struct connman_device *device;
+
+       struct {
+               void *ssid;
+               int ssid_len;
+               char *mode;
+               unsigned short channel;
+               char *security;
+               char *passphrase;
+       } wifi;
+};
+
+static const char *type2string(enum connman_network_type type)
+{
+       switch (type) {
+       case CONNMAN_NETWORK_TYPE_UNKNOWN:
+       case CONNMAN_NETWORK_TYPE_VENDOR:
+               break;
+       case CONNMAN_NETWORK_TYPE_WIFI:
+               return "wifi";
+       case CONNMAN_NETWORK_TYPE_WIMAX:
+               return "wimax";
+       case CONNMAN_NETWORK_TYPE_BLUETOOTH_PAN:
+       case CONNMAN_NETWORK_TYPE_BLUETOOTH_DUN:
+               return "bluetooth";
+       case CONNMAN_NETWORK_TYPE_HSO:
+               return "cellular";
+       }
+
+       return NULL;
+}
+
+static DBusMessage *get_properties(DBusConnection *conn,
+                                       DBusMessage *msg, void *data)
+{
+       struct connman_network *network = data;
+       DBusMessage *reply;
+       DBusMessageIter array, dict;
+
+       DBG("conn %p", conn);
+
+       if (__connman_security_check_privilege(msg,
+                                       CONNMAN_SECURITY_PRIVILEGE_PUBLIC) < 0)
+               return __connman_error_permission_denied(msg);
+
+       reply = dbus_message_new_method_return(msg);
+       if (reply == NULL)
+               return NULL;
+
+       dbus_message_iter_init_append(reply, &array);
+
+       dbus_message_iter_open_container(&array, 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);
+
+       if (network->device) {
+               const char *path = connman_device_get_path(network->device);
+               if (path != NULL)
+                       connman_dbus_dict_append_variant(&dict, "Device",
+                                               DBUS_TYPE_OBJECT_PATH, &path);
+       }
+
+       if (network->address != NULL)
+               connman_dbus_dict_append_variant(&dict, "Address",
+                                       DBUS_TYPE_STRING, &network->address);
+
+       if (network->name != NULL)
+               connman_dbus_dict_append_variant(&dict, "Name",
+                                       DBUS_TYPE_STRING, &network->name);
+
+       connman_dbus_dict_append_variant(&dict, "Connected",
+                               DBUS_TYPE_BOOLEAN, &network->connected);
+
+       if (network->strength > 0)
+               connman_dbus_dict_append_variant(&dict, "Strength",
+                                       DBUS_TYPE_BYTE, &network->strength);
+
+       if (network->frequency > 0)
+               connman_dbus_dict_append_variant(&dict, "Frequency",
+                                       DBUS_TYPE_UINT16, &network->frequency);
+
+       if (network->wifi.ssid != NULL && network->wifi.ssid_len > 0)
+               connman_dbus_dict_append_array(&dict, "WiFi.SSID",
+                               DBUS_TYPE_BYTE, &network->wifi.ssid,
+                                               network->wifi.ssid_len);
+
+       if (network->wifi.mode != NULL)
+               connman_dbus_dict_append_variant(&dict, "WiFi.Mode",
+                               DBUS_TYPE_STRING, &network->wifi.mode);
+
+       if (network->wifi.channel > 0)
+               connman_dbus_dict_append_variant(&dict, "WiFi.Channel",
+                               DBUS_TYPE_UINT16, &network->wifi.channel);
+
+       if (network->wifi.security != NULL)
+               connman_dbus_dict_append_variant(&dict, "WiFi.Security",
+                               DBUS_TYPE_STRING, &network->wifi.security);
+
+       if (network->wifi.passphrase != NULL &&
+                       __connman_security_check_privilege(msg,
+                               CONNMAN_SECURITY_PRIVILEGE_SECRET) == 0)
+               connman_dbus_dict_append_variant(&dict, "WiFi.Passphrase",
+                               DBUS_TYPE_STRING, &network->wifi.passphrase);
+
+       dbus_message_iter_close_container(&array, &dict);
+
+       return reply;
+}
+
+static DBusMessage *set_property(DBusConnection *conn,
+                                       DBusMessage *msg, void *data)
+{
+       struct connman_network *network = data;
+       DBusMessageIter iter, value;
+       const char *name;
+       int type;
+
+       DBG("conn %p", conn);
+
+       if (dbus_message_iter_init(msg, &iter) == FALSE)
+               return __connman_error_invalid_arguments(msg);
+
+       dbus_message_iter_get_basic(&iter, &name);
+       dbus_message_iter_next(&iter);
+       dbus_message_iter_recurse(&iter, &value);
+
+       if (__connman_security_check_privilege(msg,
+                                       CONNMAN_SECURITY_PRIVILEGE_MODIFY) < 0)
+               return __connman_error_permission_denied(msg);
+
+       type = dbus_message_iter_get_arg_type(&value);
+
+       if (g_str_equal(name, "WiFi.Passphrase") == TRUE) {
+               const char *passphrase;
+
+               if (type != DBUS_TYPE_STRING)
+                       return __connman_error_invalid_arguments(msg);
+
+               if (__connman_security_check_privilege(msg,
+                                       CONNMAN_SECURITY_PRIVILEGE_SECRET) < 0)
+                       return __connman_error_permission_denied(msg);
+
+               dbus_message_iter_get_basic(&value, &passphrase);
+
+               g_free(network->wifi.passphrase);
+               network->wifi.passphrase = g_strdup(passphrase);
+       }
+
+       __connman_storage_save_network(network);
+
+       return g_dbus_create_reply(msg, DBUS_TYPE_INVALID);
+}
+
+static DBusMessage *do_connect(DBusConnection *conn,
+                                       DBusMessage *msg, void *data)
+{
+       struct connman_network *network = data;
+       int err;
+
+       DBG("conn %p", conn);
+
+       if (__connman_security_check_privilege(msg,
+                                       CONNMAN_SECURITY_PRIVILEGE_MODIFY) < 0)
+               return __connman_error_permission_denied(msg);
+
+       if (network->connected == TRUE)
+               return __connman_error_failed(msg, EALREADY);
+
+       if (network->driver && network->driver->connect) {
+               enum connman_device_mode mode;
+
+               mode = connman_device_get_mode(network->device);
+               if (mode == CONNMAN_DEVICE_MODE_NETWORK_SINGLE)
+                       __connman_device_disconnect(network->device);
+
+               err = network->driver->connect(network);
+               if (err < 0 && err != -EINPROGRESS)
+                       return __connman_error_failed(msg, -err);
+       } else
+               network->connected = TRUE;
+
+       return g_dbus_create_reply(msg, DBUS_TYPE_INVALID);
+}
+
+static DBusMessage *do_disconnect(DBusConnection *conn,
+                                       DBusMessage *msg, void *data)
+{
+       struct connman_network *network = data;
+       int err;
+
+       DBG("conn %p", conn);
+
+       if (__connman_security_check_privilege(msg,
+                                       CONNMAN_SECURITY_PRIVILEGE_MODIFY) < 0)
+               return __connman_error_permission_denied(msg);
+
+       if (network->connected == FALSE)
+               return __connman_error_failed(msg, EINVAL);
+
+       connman_element_unregister_children(&network->element);
+
+       connman_device_set_disconnected(network->device, TRUE);
+
+       if (network->driver && network->driver->disconnect) {
+               err = network->driver->disconnect(network);
+               if (err < 0 && err != -EINPROGRESS)
+                       return __connman_error_failed(msg, -err);
+       } else
+               network->connected = FALSE;
+
+       return g_dbus_create_reply(msg, DBUS_TYPE_INVALID);
+}
+
+static GDBusMethodTable network_methods[] = {
+       { "GetProperties", "",   "a{sv}", get_properties },
+       { "SetProperty",   "sv", "",      set_property   },
+       { "Connect",       "",   "",      do_connect     },
+       { "Disconnect",    "",   "",      do_disconnect  },
+       { },
+};
+
+static GDBusSignalTable network_signals[] = {
+       { "PropertyChanged", "sv" },
+       { },
+};
+
+static DBusConnection *connection;
+
+static void append_networks(struct connman_device *device,
+                                               DBusMessageIter *entry)
+{
+       DBusMessageIter value, iter;
+       const char *key = "Networks";
+
+       dbus_message_iter_append_basic(entry, DBUS_TYPE_STRING, &key);
+
+       dbus_message_iter_open_container(entry, DBUS_TYPE_VARIANT,
+               DBUS_TYPE_ARRAY_AS_STRING DBUS_TYPE_OBJECT_PATH_AS_STRING,
+                                                               &value);
+
+       dbus_message_iter_open_container(&value, DBUS_TYPE_ARRAY,
+                               DBUS_TYPE_OBJECT_PATH_AS_STRING, &iter);
+       __connman_element_list((struct connman_element *) device,
+                                       CONNMAN_ELEMENT_TYPE_NETWORK, &iter);
+       dbus_message_iter_close_container(&value, &iter);
+
+       dbus_message_iter_close_container(entry, &value);
+}
+
+static void emit_networks_signal(struct connman_device *device)
+{
+       const char *path = connman_device_get_path(device);
+       DBusMessage *signal;
+       DBusMessageIter entry;
+
+       signal = dbus_message_new_signal(path,
+                               CONNMAN_DEVICE_INTERFACE, "PropertyChanged");
+       if (signal == NULL)
+               return;
+
+       dbus_message_iter_init_append(signal, &entry);
+
+       append_networks(device, &entry);
+
+       g_dbus_send_message(connection, signal);
+}
+
+static int register_interface(struct connman_element *element)
+{
+       struct connman_network *network = element->network;
+
+       DBG("element %p name %s", element, element->name);
+
+       if (g_dbus_register_interface(connection, element->path,
+                                       CONNMAN_NETWORK_INTERFACE,
+                                       network_methods, network_signals,
+                                       NULL, network, NULL) == FALSE) {
+               connman_error("Failed to register %s network", element->path);
+               return -EIO;
+       }
+
+       network->registered = TRUE;
+
+       emit_networks_signal(network->device);
+
+       return 0;
+}
+
+static void unregister_interface(struct connman_element *element)
+{
+       struct connman_network * network = element->network;
+
+       DBG("element %p name %s", element, element->name);
+
+       network->registered = FALSE;
+
+       emit_networks_signal(network->device);
+
+       g_dbus_unregister_interface(connection, element->path,
+                                               CONNMAN_NETWORK_INTERFACE);
+}
+
+connman_bool_t __connman_network_has_driver(struct connman_network *network)
+{
+       if (network == NULL || network->driver == NULL)
+               return FALSE;
+
+       return network->registered;
+}
+
+static GSList *driver_list = NULL;
+
+static gint compare_priority(gconstpointer a, gconstpointer b)
+{
+       const struct connman_network_driver *driver1 = a;
+       const struct connman_network_driver *driver2 = b;
+
+       return driver2->priority - driver1->priority;
+}
+
+/**
+ * connman_network_driver_register:
+ * @driver: network driver definition
+ *
+ * Register a new network driver
+ *
+ * Returns: %0 on success
+ */
+int connman_network_driver_register(struct connman_network_driver *driver)
+{
+       DBG("driver %p name %s", driver, driver->name);
+
+       driver_list = g_slist_insert_sorted(driver_list, driver,
+                                                       compare_priority);
+
+       return 0;
+}
+
+/**
+ * connman_network_driver_unregister:
+ * @driver: network driver definition
+ *
+ * Remove a previously registered network driver
+ */
+void connman_network_driver_unregister(struct connman_network_driver *driver)
+{
+       DBG("driver %p name %s", driver, driver->name);
+
+       driver_list = g_slist_remove(driver_list, driver);
+}
+
+static void network_destruct(struct connman_element *element)
+{
+       struct connman_network *network = element->network;
+
+       DBG("element %p name %s", element, element->name);
+
+       g_free(network->wifi.ssid);
+       g_free(network->wifi.mode);
+       g_free(network->wifi.security);
+       g_free(network->wifi.passphrase);
+
+       g_free(network->group);
+       g_free(network->node);
+       g_free(network->name);
+       g_free(network->address);
+       g_free(network->identifier);
+}
+
+/**
+ * connman_network_create:
+ * @identifier: network identifier (for example an unqiue name)
+ *
+ * Allocate a new network and assign the #identifier to it.
+ *
+ * Returns: a newly-allocated #connman_network structure
+ */
+struct connman_network *connman_network_create(const char *identifier,
+                                               enum connman_network_type type)
+{
+       struct connman_network *network;
+       connman_uint8_t strength = 0;
+       const char *str;
+       char *temp;
+
+       DBG("identifier %s type %d", identifier, type);
+
+       network = g_try_new0(struct connman_network, 1);
+       if (network == NULL)
+               return NULL;
+
+       DBG("network %p", network);
+
+       __connman_element_initialize(&network->element);
+
+       //temp = connman_dbus_encode_string(identifier);
+       temp = g_strdup(identifier);
+       if (temp == NULL) {
+               g_free(network);
+               return NULL;
+       }
+
+       network->element.name = temp;
+       network->element.type = CONNMAN_ELEMENT_TYPE_NETWORK;
+
+       network->element.network = network;
+       network->element.destruct = network_destruct;
+
+       str = type2string(type);
+       if (str != NULL)
+               connman_element_set_string(&network->element, "Type", str);
+
+       connman_element_set_uint8(&network->element, "Strength", strength);
+
+       network->type       = type;
+       network->secondary  = FALSE;
+       network->identifier = g_strdup(identifier);
+
+       return network;
+}
+
+/**
+ * connman_network_ref:
+ * @network: network structure
+ *
+ * Increase reference counter of  network
+ */
+struct connman_network *connman_network_ref(struct connman_network *network)
+{
+       if (connman_element_ref(&network->element) == NULL)
+               return NULL;
+
+       return network;
+}
+
+/**
+ * connman_network_unref:
+ * @network: network structure
+ *
+ * Decrease reference counter of network
+ */
+void connman_network_unref(struct connman_network *network)
+{
+       connman_element_unref(&network->element);
+}
+
+const char *__connman_network_get_type(struct connman_network *network)
+{
+       return type2string(network->type);
+}
+
+/**
+ * connman_network_get_type:
+ * @network: network structure
+ *
+ * Get type of network
+ */
+enum connman_network_type connman_network_get_type(struct connman_network *network)
+{
+       return network->type;
+}
+
+/**
+ * connman_network_get_identifier:
+ * @network: network structure
+ *
+ * Get identifier of network
+ */
+const char *connman_network_get_identifier(struct connman_network *network)
+{
+       return network->identifier;
+}
+
+/**
+ * connman_network_get_path:
+ * @network: network structure
+ *
+ * Get path name of network
+ */
+const char *connman_network_get_path(struct connman_network *network)
+{
+       return network->element.path;
+}
+
+/**
+ * connman_network_set_index:
+ * @network: network structure
+ * @index: index number
+ *
+ * Set index number of network
+ */
+void connman_network_set_index(struct connman_network *network, int index)
+{
+       network->element.index = index;
+}
+
+/**
+ * connman_network_get_index:
+ * @network: network structure
+ *
+ * Get index number of network
+ */
+int connman_network_get_index(struct connman_network *network)
+{
+       return network->element.index;
+}
+
+/**
+ * connman_network_set_protocol:
+ * @network: network structure
+ * @protocol: network protocol
+ *
+ * Change protocol of network
+ */
+void connman_network_set_protocol(struct connman_network *network,
+                                       enum connman_network_protocol protocol)
+{
+       network->protocol = protocol;
+}
+
+/**
+ * connman_network_set_group:
+ * @network: network structure
+ * @group: group name
+ *
+ * Set group name for automatic clustering
+ */
+void connman_network_set_group(struct connman_network *network,
+                                                       const char *group)
+{
+       if (network->secondary == TRUE)
+               return;
+
+       if (g_strcmp0(network->group, group) == 0)
+               return;
+
+       switch (network->type) {
+       case CONNMAN_NETWORK_TYPE_UNKNOWN:
+       case CONNMAN_NETWORK_TYPE_VENDOR:
+       case CONNMAN_NETWORK_TYPE_BLUETOOTH_PAN:
+       case CONNMAN_NETWORK_TYPE_BLUETOOTH_DUN:
+       case CONNMAN_NETWORK_TYPE_HSO:
+               return;
+       case CONNMAN_NETWORK_TYPE_WIFI:
+       case CONNMAN_NETWORK_TYPE_WIMAX:
+               break;
+       }
+
+       if (network->group != NULL) {
+               __connman_profile_remove_network(network);
+
+               g_free(network->group);
+       }
+
+       network->group = g_strdup(group);
+
+       if (network->group != NULL)
+               __connman_profile_add_network(network);
+}
+
+const char *__connman_network_get_group(struct connman_network *network)
+{
+       return network->group;
+}
+
+const char *__connman_network_get_ident(struct connman_network *network)
+{
+       if (network->device == NULL)
+               return NULL;
+
+       return __connman_device_get_ident(network->device);
+}
+
+/**
+ * connman_network_set_available:
+ * @network: network structure
+ * @available: availability state
+ *
+ * Change availability state of network (in range)
+ */
+int connman_network_set_available(struct connman_network *network,
+                                               connman_bool_t available)
+{
+       DBG("network %p available %d", network, available);
+
+       if (network->available == available)
+               return -EALREADY;
+
+       network->available = available;
+
+       return 0;
+}
+
+/**
+ * connman_network_get_available:
+ * @network: network structure
+ *
+ * Get network available setting
+ */
+connman_bool_t connman_network_get_available(struct connman_network *network)
+{
+       return network->available;
+}
+
+/**
+ * connman_network_set_associating:
+ * @network: network structure
+ * @associating: associating state
+ *
+ * Change associating state of network
+ */
+int connman_network_set_associating(struct connman_network *network,
+                                               connman_bool_t associating)
+{
+       DBG("network %p associating %d", network, associating);
+
+       if (network->associating == associating)
+               return -EALREADY;
+
+       network->associating = associating;
+
+       if (associating == TRUE) {
+               struct connman_service *service;
+
+               service = __connman_service_lookup_from_network(network);
+               __connman_service_indicate_state(service,
+                                       CONNMAN_SERVICE_STATE_ASSOCIATION);
+       }
+
+       return 0;
+}
+
+static gboolean set_connected(gpointer user_data)
+{
+       struct connman_network *network = user_data;
+       struct connman_service *service;
+
+       service = __connman_service_lookup_from_network(network);
+
+       if (network->connected == TRUE) {
+               struct connman_element *element;
+               enum connman_element_type type = CONNMAN_ELEMENT_TYPE_UNKNOWN;
+
+               switch (network->protocol) {
+               case CONNMAN_NETWORK_PROTOCOL_UNKNOWN:
+                       return 0;
+               case CONNMAN_NETWORK_PROTOCOL_IP:
+                       type = CONNMAN_ELEMENT_TYPE_DHCP;
+                       break;
+               case CONNMAN_NETWORK_PROTOCOL_PPP:
+                       type = CONNMAN_ELEMENT_TYPE_PPP;
+                       break;
+               }
+
+               __connman_device_increase_connections(network->device);
+
+               __connman_device_set_network(network->device, network);
+
+               connman_device_set_disconnected(network->device, FALSE);
+
+               element = connman_element_create(NULL);
+               if (element != NULL) {
+                       element->type  = type;
+                       element->index = network->element.index;
+
+                       if (connman_element_register(element,
+                                               &network->element) < 0)
+                               connman_element_unref(element);
+
+                       __connman_service_indicate_state(service,
+                                       CONNMAN_SERVICE_STATE_CONFIGURATION);
+               }
+       } else {
+               __connman_service_indicate_state(service,
+                                               CONNMAN_SERVICE_STATE_IDLE);
+
+               connman_element_unregister_children(&network->element);
+
+               __connman_device_set_network(network->device, NULL);
+
+               __connman_device_decrease_connections(network->device);
+       }
+
+       return FALSE;
+}
+
+/**
+ * connman_network_set_connected:
+ * @network: network structure
+ * @connected: connected state
+ *
+ * Change connected state of network
+ */
+int connman_network_set_connected(struct connman_network *network,
+                                               connman_bool_t connected)
+{
+       DBusMessage *signal;
+       DBusMessageIter entry, value;
+       const char *key = "Connected";
+
+       DBG("network %p connected %d", network, connected);
+
+       if (network->connected == connected)
+               return -EALREADY;
+
+       network->connected = connected;
+
+       if (connected == TRUE)
+               network->associating = FALSE;
+
+       if (network->registered == FALSE) {
+               g_idle_add(set_connected, network);
+               return 0;
+       }
+
+       signal = dbus_message_new_signal(network->element.path,
+                               CONNMAN_NETWORK_INTERFACE, "PropertyChanged");
+       if (signal == NULL)
+               return 0;
+
+       dbus_message_iter_init_append(signal, &entry);
+
+       dbus_message_iter_append_basic(&entry, DBUS_TYPE_STRING, &key);
+
+       dbus_message_iter_open_container(&entry, DBUS_TYPE_VARIANT,
+                                       DBUS_TYPE_BOOLEAN_AS_STRING, &value);
+       dbus_message_iter_append_basic(&value, DBUS_TYPE_BOOLEAN, &connected);
+       dbus_message_iter_close_container(&entry, &value);
+
+       g_dbus_send_message(connection, signal);
+
+       set_connected(network);
+
+       return 0;
+}
+
+/**
+ * connman_network_get_connected:
+ * @network: network structure
+ *
+ * Get network connection status
+ */
+connman_bool_t connman_network_get_connected(struct connman_network *network)
+{
+       return network->connected;
+}
+
+/**
+ * __connman_network_connect:
+ * @network: network structure
+ *
+ * Connect network
+ */
+int __connman_network_connect(struct connman_network *network)
+{
+       int err;
+
+       DBG("network %p", network);
+
+       if (network->connected == TRUE)
+               return -EALREADY;
+
+       if (network->driver == NULL)
+               return -EUNATCH;
+
+       if (network->driver->connect == NULL)
+               return -ENOSYS;
+
+       __connman_device_disconnect(network->device);
+
+       err = network->driver->connect(network);
+       if (err == 0) {
+               network->connected = TRUE;
+               set_connected(network);
+       }
+
+       return err;
+}
+
+/**
+ * __connman_network_disconnect:
+ * @network: network structure
+ *
+ * Disconnect network
+ */
+int __connman_network_disconnect(struct connman_network *network)
+{
+       int err;
+
+       DBG("network %p", network);
+
+       if (network->connected == FALSE)
+               return -ENOTCONN;
+
+       if (network->driver == NULL)
+               return -EUNATCH;
+
+       if (network->driver->disconnect == NULL)
+               return -ENOSYS;
+
+       err = network->driver->disconnect(network);
+       if (err == 0) {
+               network->connected = FALSE;
+               set_connected(network);
+       }
+
+       return err;
+}
+
+/**
+ * connman_network_set_address:
+ * @network: network structure
+ * @address: binary address value
+ * @size: binary address length
+ *
+ * Set unique address value for network
+ */
+int connman_network_set_address(struct connman_network *network,
+                               const void *address, unsigned int size)
+{
+       const unsigned char *addr_octet = address;
+       char *str;
+
+       DBG("network %p size %d", network, size);
+
+       if (size != 6)
+               return -EINVAL;
+
+       str = g_strdup_printf("%02X:%02X:%02X:%02X:%02X:%02X",
+                               addr_octet[0], addr_octet[1], addr_octet[2],
+                               addr_octet[3], addr_octet[4], addr_octet[5]);
+       if (str == NULL)
+               return -ENOMEM;
+
+       g_free(network->address);
+       network->address = str;
+
+       return connman_element_set_string(&network->element,
+                                               "Address", network->address);
+}
+
+/**
+ * connman_network_set_name:
+ * @network: network structure
+ * @name: name value
+ *
+ * Set display name value for network
+ */
+int connman_network_set_name(struct connman_network *network,
+                                                       const char *name)
+{
+       DBG("network %p name %s", network, name);
+
+       g_free(network->name);
+       network->name = g_strdup(name);
+
+       return connman_element_set_string(&network->element, "Name", name);
+}
+
+/**
+ * connman_network_set_strength:
+ * @network: network structure
+ * @strength: strength value
+ *
+ * Set signal strength value for network
+ */
+int connman_network_set_strength(struct connman_network *network,
+                                               connman_uint8_t strength)
+{
+       DBG("network %p strengh %d", network, strength);
+
+       network->strength = strength;
+
+       return connman_element_set_uint8(&network->element,
+                                               "Strength", strength);
+}
+
+/**
+ * connman_network_set_string:
+ * @network: network structure
+ * @key: unique identifier
+ * @value: string value
+ *
+ * Set string value for specific key
+ */
+int connman_network_set_string(struct connman_network *network,
+                                       const char *key, const char *value)
+{
+       DBG("network %p key %s value %s", network, key, value);
+
+       if (g_strcmp0(key, "Name") == 0)
+               return connman_network_set_name(network, value);
+
+       if (g_str_equal(key, "Address") == TRUE) {
+               g_free(network->address);
+               network->address = g_strdup(value);
+       } else if (g_str_equal(key, "Node") == TRUE) {
+               g_free(network->node);
+               network->node = g_strdup(value);
+       } else if (g_str_equal(key, "WiFi.Mode") == TRUE) {
+               g_free(network->wifi.mode);
+               network->wifi.mode = g_strdup(value);
+       } else if (g_str_equal(key, "WiFi.Security") == TRUE) {
+               g_free(network->wifi.security);
+               network->wifi.security = g_strdup(value);
+       } else if (g_str_equal(key, "WiFi.Passphrase") == TRUE) {
+               g_free(network->wifi.passphrase);
+               network->wifi.passphrase = g_strdup(value);
+       }
+
+       return connman_element_set_string(&network->element, key, value);
+}
+
+/**
+ * connman_network_get_string:
+ * @network: network structure
+ * @key: unique identifier
+ *
+ * Get string value for specific key
+ */
+const char *connman_network_get_string(struct connman_network *network,
+                                                       const char *key)
+{
+       DBG("network %p key %s", network, key);
+
+       if (g_str_equal(key, "Address") == TRUE)
+               return network->address;
+       else if (g_str_equal(key, "Name") == TRUE)
+               return network->name;
+       else if (g_str_equal(key, "Node") == TRUE)
+               return network->node;
+       else if (g_str_equal(key, "WiFi.Mode") == TRUE)
+               return network->wifi.mode;
+       else if (g_str_equal(key, "WiFi.Security") == TRUE)
+               return network->wifi.security;
+       else if (g_str_equal(key, "WiFi.Passphrase") == TRUE)
+               return network->wifi.passphrase;
+
+       return connman_element_get_string(&network->element, key);
+}
+
+/**
+ * connman_network_set_uint8:
+ * @network: network structure
+ * @key: unique identifier
+ * @value: integer value
+ *
+ * Set integer value for specific key
+ */
+int connman_network_set_uint8(struct connman_network *network,
+                                       const char *key, connman_uint8_t value)
+{
+       DBG("network %p key %s value %d", network, key, value);
+
+       if (g_strcmp0(key, "Strength") == 0)
+               return connman_network_set_strength(network, value);
+
+       return connman_element_set_uint8(&network->element, key, value);
+}
+
+/**
+ * connman_network_get_uint8:
+ * @network: network structure
+ * @key: unique identifier
+ *
+ * Get integer value for specific key
+ */
+connman_uint8_t connman_network_get_uint8(struct connman_network *network,
+                                                       const char *key)
+{
+       DBG("network %p key %s", network, key);
+
+       if (g_str_equal(key, "Strength") == TRUE)
+               return network->strength;
+
+       return connman_element_get_uint8(&network->element, key);
+}
+
+/**
+ * connman_network_set_uint16:
+ * @network: network structure
+ * @key: unique identifier
+ * @value: integer value
+ *
+ * Set integer value for specific key
+ */
+int connman_network_set_uint16(struct connman_network *network,
+                               const char *key, connman_uint16_t value)
+{
+       DBG("network %p key %s value %d", network, key, value);
+
+       if (g_str_equal(key, "Frequency") == TRUE)
+               network->frequency = value;
+       else if (g_str_equal(key, "WiFi.Channel") == TRUE)
+               network->wifi.channel = value;
+
+       return -EINVAL;
+}
+
+/**
+ * connman_network_get_uint16:
+ * @network: network structure
+ * @key: unique identifier
+ *
+ * Get integer value for specific key
+ */
+connman_uint16_t connman_network_get_uint16(struct connman_network *network,
+                                                       const char *key)
+{
+       DBG("network %p key %s", network, key);
+
+       if (g_str_equal(key, "Frequency") == TRUE)
+               return network->frequency;
+       else if (g_str_equal(key, "WiFi.Channel") == TRUE)
+               return network->wifi.channel;
+
+       return 0;
+}
+
+/**
+ * connman_network_set_blob:
+ * @network: network structure
+ * @key: unique identifier
+ * @data: blob data
+ * @size: blob size
+ *
+ * Set binary blob value for specific key
+ */
+int connman_network_set_blob(struct connman_network *network,
+                       const char *key, const void *data, unsigned int size)
+{
+       DBG("network %p key %s size %d", network, key, size);
+
+       if (g_strcmp0(key, "Address") == 0)
+               return connman_network_set_address(network, data, size);
+
+       if (g_str_equal(key, "WiFi.SSID") == TRUE) {
+               g_free(network->wifi.ssid);
+               network->wifi.ssid = g_try_malloc(size);
+               if (network->wifi.ssid != NULL) {
+                       memcpy(network->wifi.ssid, data, size);
+                       network->wifi.ssid_len = size;
+               } else
+                       network->wifi.ssid_len = 0;
+       }
+
+       return connman_element_set_blob(&network->element, key, data, size);
+}
+
+/**
+ * connman_network_get_blob:
+ * @network: network structure
+ * @key: unique identifier
+ * @size: pointer to blob size
+ *
+ * Get binary blob value for specific key
+ */
+const void *connman_network_get_blob(struct connman_network *network,
+                                       const char *key, unsigned int *size)
+{
+       DBG("network %p key %s", network, key);
+
+       if (g_str_equal(key, "WiFi.SSID") == TRUE) {
+               if (size != NULL)
+                       *size = network->wifi.ssid_len;
+               return network->wifi.ssid;
+       }
+
+       return connman_element_get_blob(&network->element, key, size);
+}
+
+void __connman_network_set_device(struct connman_network *network,
+                                       struct connman_device *device)
+{
+       network->device = device;
+}
+
+/**
+ * connman_network_get_device:
+ * @network: network structure
+ *
+ * Get parent device of network
+ */
+struct connman_device *connman_network_get_device(struct connman_network *network)
+{
+       return network->device;
+}
+
+/**
+ * connman_network_get_data:
+ * @network: network structure
+ *
+ * Get private network data pointer
+ */
+void *connman_network_get_data(struct connman_network *network)
+{
+       return network->driver_data;
+}
+
+/**
+ * connman_network_set_data:
+ * @network: network structure
+ * @data: data pointer
+ *
+ * Set private network data pointer
+ */
+void connman_network_set_data(struct connman_network *network, void *data)
+{
+       network->driver_data = data;
+}
+
+static gboolean match_driver(struct connman_network *network,
+                                       struct connman_network_driver *driver)
+{
+       if (network->type == driver->type ||
+                       driver->type == CONNMAN_NETWORK_TYPE_UNKNOWN)
+               return TRUE;
+
+       return FALSE;
+}
+
+static int network_probe(struct connman_element *element)
+{
+       struct connman_network *network = element->network;
+       GSList *list;
+       int err;
+
+       DBG("element %p name %s", element, element->name);
+
+       if (network == NULL)
+               return -ENODEV;
+
+       for (list = driver_list; list; list = list->next) {
+               struct connman_network_driver *driver = list->data;
+
+               if (match_driver(network, driver) == FALSE)
+                       continue;
+
+               DBG("driver %p name %s", driver, driver->name);
+
+               if (driver->probe(network) == 0) {
+                       network->driver = driver;
+                       break;
+               }
+       }
+
+       if (network->driver == NULL)
+               return -ENODEV;
+
+       err = register_interface(element);
+       if (err < 0) {
+               if (network->driver->remove)
+                       network->driver->remove(network);
+               return err;
+       }
+
+       network->secondary = connman_device_get_secondary(network->device);
+
+       switch (network->type) {
+       case CONNMAN_NETWORK_TYPE_UNKNOWN:
+       case CONNMAN_NETWORK_TYPE_VENDOR:
+       case CONNMAN_NETWORK_TYPE_BLUETOOTH_PAN:
+       case CONNMAN_NETWORK_TYPE_BLUETOOTH_DUN:
+       case CONNMAN_NETWORK_TYPE_HSO:
+               break;
+       case CONNMAN_NETWORK_TYPE_WIFI:
+       case CONNMAN_NETWORK_TYPE_WIMAX:
+               if (network->group != NULL && network->secondary == FALSE)
+                       __connman_profile_add_network(network);
+               break;
+       }
+
+       return 0;
+}
+
+static void network_remove(struct connman_element *element)
+{
+       struct connman_network *network = element->network;
+
+       DBG("element %p name %s", element, element->name);
+
+       if (network == NULL)
+               return;
+
+       if (network->driver == NULL)
+               return;
+
+       switch (network->type) {
+       case CONNMAN_NETWORK_TYPE_UNKNOWN:
+       case CONNMAN_NETWORK_TYPE_VENDOR:
+       case CONNMAN_NETWORK_TYPE_BLUETOOTH_PAN:
+       case CONNMAN_NETWORK_TYPE_BLUETOOTH_DUN:
+       case CONNMAN_NETWORK_TYPE_HSO:
+               break;
+       case CONNMAN_NETWORK_TYPE_WIFI:
+       case CONNMAN_NETWORK_TYPE_WIMAX:
+               if (network->group != NULL && network->secondary == FALSE) {
+                       __connman_profile_remove_network(network);
+
+                       g_free(network->group);
+                       network->group = NULL;
+               }
+               break;
+       }
+
+       unregister_interface(element);
+
+       if (network->driver->remove)
+               network->driver->remove(network);
+}
+
+static void network_change(struct connman_element *element)
+{
+       struct connman_network *network = element->network;
+
+       DBG("element %p name %s", element, element->name);
+
+       if (element->state != CONNMAN_ELEMENT_STATE_ERROR)
+               return;
+
+       if (element->error != CONNMAN_ELEMENT_ERROR_DHCP_FAILED)
+               return;
+
+       if (network->connected == FALSE)
+               return;
+
+       connman_element_unregister_children(element);
+
+       connman_device_set_disconnected(network->device, TRUE);
+
+       if (network->driver && network->driver->disconnect) {
+               network->driver->disconnect(network);
+               return;
+       }
+
+       network->connected = FALSE;
+}
+
+static struct connman_driver network_driver = {
+       .name           = "network",
+       .type           = CONNMAN_ELEMENT_TYPE_NETWORK,
+       .priority       = CONNMAN_DRIVER_PRIORITY_LOW,
+       .probe          = network_probe,
+       .remove         = network_remove,
+       .change         = network_change,
+};
+
+int __connman_network_init(void)
+{
+       DBG("");
+
+       connection = connman_dbus_get_connection();
+
+       return connman_driver_register(&network_driver);
+}
+
+void __connman_network_cleanup(void)
+{
+       DBG("");
+
+       connman_driver_unregister(&network_driver);
+
+       dbus_connection_unref(connection);
+}
diff --git a/src/notifier.c b/src/notifier.c
new file mode 100644 (file)
index 0000000..9092b24
--- /dev/null
@@ -0,0 +1,155 @@
+/*
+ *
+ *  Connection Manager
+ *
+ *  Copyright (C) 2007-2009  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 "connman.h"
+
+static GSList *notifier_list = NULL;
+
+static gint compare_priority(gconstpointer a, gconstpointer b)
+{
+       const struct connman_notifier *notifier1 = a;
+       const struct connman_notifier *notifier2 = b;
+
+       return notifier2->priority - notifier1->priority;
+}
+
+/**
+ * connman_notifier_register:
+ * @notifier: notifier module
+ *
+ * Register a new notifier module
+ *
+ * Returns: %0 on success
+ */
+int connman_notifier_register(struct connman_notifier *notifier)
+{
+       DBG("notifier %p name %s", notifier, notifier->name);
+
+       notifier_list = g_slist_insert_sorted(notifier_list, notifier,
+                                                       compare_priority);
+
+       return 0;
+}
+
+/**
+ * connman_notifier_unregister:
+ * @notifier: notifier module
+ *
+ * Remove a previously registered notifier module
+ */
+void connman_notifier_unregister(struct connman_notifier *notifier)
+{
+       DBG("notifier %p name %s", notifier, notifier->name);
+
+       notifier_list = g_slist_remove(notifier_list, notifier);
+}
+
+static void device_enabled(enum connman_device_type type,
+                                               connman_bool_t enabled)
+{
+       GSList *list;
+
+       for (list = notifier_list; list; list = list->next) {
+               struct connman_notifier *notifier = list->data;
+
+               if (notifier->device_enabled)
+                       notifier->device_enabled(type, enabled);
+       }
+
+}
+
+static volatile gint enabled[10];
+
+void __connman_notifier_device_type_increase(enum connman_device_type type)
+{
+       DBG("type %d", type);
+
+       switch (type) {
+       case CONNMAN_DEVICE_TYPE_UNKNOWN:
+       case CONNMAN_DEVICE_TYPE_HSO:
+       case CONNMAN_DEVICE_TYPE_NOZOMI:
+       case CONNMAN_DEVICE_TYPE_HUAWEI:
+       case CONNMAN_DEVICE_TYPE_NOVATEL:
+       case CONNMAN_DEVICE_TYPE_VENDOR:
+               return;
+       case CONNMAN_DEVICE_TYPE_ETHERNET:
+       case CONNMAN_DEVICE_TYPE_WIFI:
+       case CONNMAN_DEVICE_TYPE_WIMAX:
+       case CONNMAN_DEVICE_TYPE_BLUETOOTH:
+       case CONNMAN_DEVICE_TYPE_GPS:
+               if (g_atomic_int_exchange_and_add(&enabled[type], 1) == 0)
+                       device_enabled(type, TRUE);
+               break;
+       }
+}
+
+void __connman_notifier_device_type_decrease(enum connman_device_type type)
+{
+       DBG("type %d", type);
+
+       switch (type) {
+       case CONNMAN_DEVICE_TYPE_UNKNOWN:
+       case CONNMAN_DEVICE_TYPE_HSO:
+       case CONNMAN_DEVICE_TYPE_NOZOMI:
+       case CONNMAN_DEVICE_TYPE_HUAWEI:
+       case CONNMAN_DEVICE_TYPE_NOVATEL:
+       case CONNMAN_DEVICE_TYPE_VENDOR:
+               return;
+       case CONNMAN_DEVICE_TYPE_ETHERNET:
+       case CONNMAN_DEVICE_TYPE_WIFI:
+       case CONNMAN_DEVICE_TYPE_WIMAX:
+       case CONNMAN_DEVICE_TYPE_BLUETOOTH:
+       case CONNMAN_DEVICE_TYPE_GPS:
+               if (g_atomic_int_dec_and_test(&enabled[type]) == TRUE)
+                       device_enabled(type, FALSE);
+               break;
+       }
+}
+
+void __connman_notifier_offline_mode(connman_bool_t enabled)
+{
+       GSList *list;
+
+       DBG("enabled %d", enabled);
+
+       for (list = notifier_list; list; list = list->next) {
+               struct connman_notifier *notifier = list->data;
+
+               if (notifier->offline_mode)
+                       notifier->offline_mode(enabled);
+       }
+}
+
+int __connman_notifier_init(void)
+{
+       DBG("");
+
+       return 0;
+}
+
+void __connman_notifier_cleanup(void)
+{
+       DBG("");
+}
diff --git a/src/plugin.c b/src/plugin.c
new file mode 100644 (file)
index 0000000..6268652
--- /dev/null
@@ -0,0 +1,199 @@
+/*
+ *
+ *  Connection Manager
+ *
+ *  Copyright (C) 2007-2009  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 <dlfcn.h>
+
+#include <glib.h>
+
+#ifdef CONNMAN_PLUGIN_BUILTIN
+#undef CONNMAN_PLUGIN_BUILTIN
+#endif
+
+#include "connman.h"
+
+/*
+ * Plugins that are using libraries with threads and their own mainloop
+ * will crash on exit. This is a bug inside these libraries, but there is
+ * nothing much that can be done about it.
+ */
+#ifdef NEED_THREADS
+#define PLUGINFLAG (RTLD_NOW | RTLD_NODELETE)
+#else
+#define PLUGINFLAG (RTLD_NOW)
+#endif
+
+static GSList *plugins = NULL;
+
+struct connman_plugin {
+       void *handle;
+       gboolean active;
+       struct connman_plugin_desc *desc;
+};
+
+static gint compare_priority(gconstpointer a, gconstpointer b)
+{
+       const struct connman_plugin *plugin1 = a;
+       const struct connman_plugin *plugin2 = b;
+
+       return plugin2->desc->priority - plugin1->desc->priority;
+}
+
+static gboolean add_plugin(void *handle, struct connman_plugin_desc *desc)
+{
+       struct connman_plugin *plugin;
+
+       if (desc->init == NULL)
+               return FALSE;
+
+       if (g_str_equal(desc->version, CONNMAN_VERSION) == FALSE) {
+               connman_error("Version mismatch for %s", desc->description);
+               return FALSE;
+       }
+
+       plugin = g_try_new0(struct connman_plugin, 1);
+       if (plugin == NULL)
+               return FALSE;
+
+       plugin->handle = handle;
+       plugin->active = FALSE;
+       plugin->desc = desc;
+
+       plugins = g_slist_insert_sorted(plugins, plugin, compare_priority);
+
+       return TRUE;
+}
+
+static gboolean check_plugin(struct connman_plugin_desc *desc,
+                               const char *pattern, const char *exclude)
+{
+       if (exclude != NULL &&
+                       g_pattern_match_simple(exclude, desc->name) == TRUE) {
+               connman_info("Excluding %s", desc->description);
+               return FALSE;
+       }
+
+       if (pattern != NULL &&
+                       g_pattern_match_simple(pattern, desc->name) == FALSE) {
+               connman_info("Ignoring %s", desc->description);
+               return FALSE;
+       }
+
+       return TRUE;
+}
+
+#include "builtin.h"
+
+int __connman_plugin_init(const char *pattern, const char *exclude)
+{
+       GSList *list;
+       GDir *dir;
+       const gchar *file;
+       gchar *filename;
+       unsigned int i;
+
+       DBG("");
+
+       for (i = 0; __connman_builtin[i]; i++) {
+               if (check_plugin(__connman_builtin[i],
+                                               pattern, exclude) == FALSE)
+                       continue;
+
+               add_plugin(NULL, __connman_builtin[i]);
+       }
+
+       dir = g_dir_open(PLUGINDIR, 0, NULL);
+       if (dir != NULL) {
+               while ((file = g_dir_read_name(dir)) != NULL) {
+                       void *handle;
+                       struct connman_plugin_desc *desc;
+
+                       if (g_str_has_prefix(file, "lib") == TRUE ||
+                                       g_str_has_suffix(file, ".so") == FALSE)
+                               continue;
+
+                       filename = g_build_filename(PLUGINDIR, file, NULL);
+
+                       handle = dlopen(filename, PLUGINFLAG);
+                       if (handle == NULL) {
+                               connman_error("Can't load %s: %s",
+                                                       filename, dlerror());
+                               g_free(filename);
+                               continue;
+                       }
+
+                       g_free(filename);
+
+                       desc = dlsym(handle, "connman_plugin_desc");
+                       if (desc == NULL) {
+                               connman_error("Can't load symbol: %s",
+                                                               dlerror());
+                               dlclose(handle);
+                               continue;
+                       }
+
+                       if (check_plugin(desc, pattern, exclude) == FALSE) {
+                               dlclose(handle);
+                               continue;
+                       }
+
+                       if (add_plugin(handle, desc) == FALSE)
+                               dlclose(handle);
+               }
+
+               g_dir_close(dir);
+       }
+
+       for (list = plugins; list; list = list->next) {
+               struct connman_plugin *plugin = list->data;
+
+               if (plugin->desc->init() < 0)
+                       continue;
+
+               plugin->active = TRUE;
+       }
+
+       return 0;
+}
+
+void __connman_plugin_cleanup(void)
+{
+       GSList *list;
+
+       DBG("");
+
+       for (list = plugins; list; list = list->next) {
+               struct connman_plugin *plugin = list->data;
+
+               if (plugin->active == TRUE && plugin->desc->exit)
+                       plugin->desc->exit();
+
+               if (plugin->handle != NULL)
+                       dlclose(plugin->handle);
+
+               g_free(plugin);
+       }
+
+       g_slist_free(plugins);
+}
diff --git a/src/profile.c b/src/profile.c
new file mode 100644 (file)
index 0000000..8984872
--- /dev/null
@@ -0,0 +1,232 @@
+/*
+ *
+ *  Connection Manager
+ *
+ *  Copyright (C) 2007-2009  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 <glib.h>
+#include <gdbus.h>
+
+#include "connman.h"
+
+#define PROFILE_DEFAULT_IDENT  "default"
+
+static DBusConnection *connection = NULL;
+
+const char *__connman_profile_active_ident(void)
+{
+       DBG("");
+
+       return PROFILE_DEFAULT_IDENT;
+}
+
+const char *__connman_profile_active_path(void)
+{
+       DBG("");
+
+       return "/profile/" PROFILE_DEFAULT_IDENT;
+}
+
+static void append_services(DBusMessageIter *entry)
+{
+       DBusMessageIter value, iter;
+       const char *key = "Services";
+
+       dbus_message_iter_append_basic(entry, DBUS_TYPE_STRING, &key);
+
+       dbus_message_iter_open_container(entry, DBUS_TYPE_VARIANT,
+               DBUS_TYPE_ARRAY_AS_STRING DBUS_TYPE_OBJECT_PATH_AS_STRING,
+                                                               &value);
+
+       dbus_message_iter_open_container(&value, DBUS_TYPE_ARRAY,
+                               DBUS_TYPE_OBJECT_PATH_AS_STRING, &iter);
+       __connman_service_list(&iter);
+       dbus_message_iter_close_container(&value, &iter);
+
+       dbus_message_iter_close_container(entry, &value);
+}
+
+void __connman_profile_changed(void)
+{
+       const char *path = __connman_profile_active_path();
+       DBusMessage *signal;
+       DBusMessageIter entry;
+
+       signal = dbus_message_new_signal(path,
+                               CONNMAN_PROFILE_INTERFACE, "PropertyChanged");
+       if (signal == NULL)
+               return;
+
+       dbus_message_iter_init_append(signal, &entry);
+       append_services(&entry);
+       g_dbus_send_message(connection, signal);
+
+       signal = dbus_message_new_signal(CONNMAN_MANAGER_PATH,
+                               CONNMAN_MANAGER_INTERFACE, "PropertyChanged");
+       if (signal == NULL)
+               return;
+
+       dbus_message_iter_init_append(signal, &entry);
+       append_services(&entry);
+       g_dbus_send_message(connection, signal);
+}
+
+int __connman_profile_add_device(struct connman_device *device)
+{
+       struct connman_service *service;
+
+       DBG("device %p", device);
+
+       service = __connman_service_create_from_device(device);
+       if (service == NULL)
+               return -EINVAL;
+
+       return 0;
+}
+
+int __connman_profile_remove_device(struct connman_device *device)
+{
+       struct connman_service *service;
+
+       DBG("device %p", device);
+
+       service = __connman_service_lookup_from_device(device);
+       if (service == NULL)
+               return -EINVAL;
+
+       __connman_service_put(service);
+
+       return 0;
+}
+
+int __connman_profile_add_network(struct connman_network *network)
+{
+       struct connman_service *service;
+
+       DBG("network %p", network);
+
+       service = __connman_service_create_from_network(network);
+       if (service == NULL)
+               return -EINVAL;
+
+       return 0;
+}
+
+int __connman_profile_remove_network(struct connman_network *network)
+{
+       struct connman_service *service;
+
+       DBG("network %p", network);
+
+       service = __connman_service_lookup_from_network(network);
+       if (service == NULL)
+               return -EINVAL;
+
+       __connman_service_put(service);
+
+       return 0;
+}
+
+void __connman_profile_list(DBusMessageIter *iter)
+{
+       const char *path = __connman_profile_active_path();
+
+       DBG("");
+
+       dbus_message_iter_append_basic(iter, DBUS_TYPE_OBJECT_PATH, &path);
+}
+
+static DBusMessage *get_properties(DBusConnection *conn,
+                                       DBusMessage *msg, void *data)
+{
+       const char *name = "Default";
+       DBusMessage *reply;
+       DBusMessageIter array, dict, entry;
+
+       DBG("conn %p", conn);
+
+       reply = dbus_message_new_method_return(msg);
+       if (reply == NULL)
+               return NULL;
+
+       dbus_message_iter_init_append(reply, &array);
+
+       dbus_message_iter_open_container(&array, 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);
+
+       connman_dbus_dict_append_variant(&dict, "Name",
+                                               DBUS_TYPE_STRING, &name);
+
+       dbus_message_iter_open_container(&dict, DBUS_TYPE_DICT_ENTRY,
+                                                               NULL, &entry);
+       append_services(&entry);
+       dbus_message_iter_close_container(&dict, &entry);
+
+       dbus_message_iter_close_container(&array, &dict);
+
+       return reply;
+}
+
+static GDBusMethodTable profile_methods[] = {
+       { "GetProperties", "", "a{sv}", get_properties },
+       { },
+};
+
+static GDBusSignalTable profile_signals[] = {
+       { "PropertyChanged", "sv" },
+       { },
+};
+
+int __connman_profile_init(DBusConnection *conn)
+{
+       const char *path = __connman_profile_active_path();
+
+       DBG("conn %p", conn);
+
+       connection = dbus_connection_ref(conn);
+       if (connection == NULL)
+               return -1;
+
+       g_dbus_register_interface(connection, path,
+                                       CONNMAN_PROFILE_INTERFACE,
+                                       profile_methods, profile_signals,
+                                                       NULL, NULL, NULL);
+
+       return 0;
+}
+
+void __connman_profile_cleanup(void)
+{
+       const char *path = __connman_profile_active_path();
+
+       DBG("conn %p", connection);
+
+       g_dbus_unregister_interface(connection, path,
+                                               CONNMAN_PROFILE_INTERFACE);
+
+       if (connection == NULL)
+               return;
+
+       dbus_connection_unref(connection);
+}
diff --git a/src/resolver.c b/src/resolver.c
new file mode 100644 (file)
index 0000000..24caf73
--- /dev/null
@@ -0,0 +1,299 @@
+/*
+ *
+ *  Connection Manager
+ *
+ *  Copyright (C) 2007-2009  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 <errno.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <string.h>
+#include <sys/stat.h>
+
+#include "connman.h"
+
+struct entry_data {
+       struct connman_resolver *resolver;
+       char *interface;
+       char *domain;
+       char *server;
+};
+
+static GSList *entry_list = NULL;
+static GSList *resolver_list = NULL;
+
+static void remove_entries(GSList *entries)
+{
+       GSList *list;
+
+       for (list = entries; list; list = list->next) {
+               struct entry_data *entry = list->data;
+               struct connman_resolver *resolver = entry->resolver;
+
+               entry_list = g_slist_remove(entry_list, entry);
+
+               if (resolver->remove)
+                       resolver->remove(entry->interface, entry->domain,
+                                                               entry->server);
+
+               g_free(entry->server);
+               g_free(entry->domain);
+               g_free(entry->interface);
+               g_free(entry);
+       }
+
+       g_slist_free(entries);
+}
+
+static gint compare_priority(gconstpointer a, gconstpointer b)
+{
+       const struct connman_resolver *resolver1 = a;
+       const struct connman_resolver *resolver2 = b;
+
+       return resolver2->priority - resolver1->priority;
+}
+
+/**
+ * connman_resolver_register:
+ * @resolver: resolver module
+ *
+ * Register a new resolver module
+ *
+ * Returns: %0 on success
+ */
+int connman_resolver_register(struct connman_resolver *resolver)
+{
+       GSList *list;
+
+       DBG("resolver %p name %s", resolver, resolver->name);
+
+       resolver_list = g_slist_insert_sorted(resolver_list, resolver,
+                                                       compare_priority);
+
+       if (resolver->append == NULL)
+               return 0;
+
+       for (list = entry_list; list; list = list->next) {
+               struct entry_data *entry = list->data;
+
+               if (entry->resolver)
+                       continue;
+
+               if (resolver->append(entry->interface, entry->domain,
+                                                       entry->server) == 0)
+                       entry->resolver = resolver;
+       }
+
+       return 0;
+}
+
+/**
+ * connman_resolver_unregister:
+ * @resolver: resolver module
+ *
+ * Remove a previously registered resolver module
+ */
+void connman_resolver_unregister(struct connman_resolver *resolver)
+{
+       GSList *list, *matches = NULL;
+
+       DBG("resolver %p name %s", resolver, resolver->name);
+
+       resolver_list = g_slist_remove(resolver_list, resolver);
+
+       for (list = entry_list; list; list = list->next) {
+               struct entry_data *entry = list->data;
+
+               if (entry->resolver != resolver)
+                       continue;
+
+               matches = g_slist_append(matches, entry);
+       }
+
+       remove_entries(matches);
+}
+
+/**
+ * connman_resolver_append:
+ * @interface: network interface
+ * @domain: domain limitation
+ * @server: server address
+ *
+ * Append resolver server address to current list
+ */
+int connman_resolver_append(const char *interface, const char *domain,
+                                                       const char *server)
+{
+       struct entry_data *entry;
+       GSList *list;
+
+       DBG("interface %s domain %s server %s", interface, domain, server);
+
+       if (server == NULL)
+               return -EINVAL;
+
+       entry = g_try_new0(struct entry_data, 1);
+       if (entry == NULL)
+               return -ENOMEM;
+
+       entry->interface = g_strdup(interface);
+       entry->domain = g_strdup(domain);
+       entry->server = g_strdup(server);
+
+       entry_list = g_slist_append(entry_list, entry);
+
+       for (list = resolver_list; list; list = list->next) {
+               struct connman_resolver *resolver = list->data;
+
+               if (resolver->append == NULL)
+                       continue;
+
+               if (resolver->append(interface, domain, server) == 0) {
+                       entry->resolver = resolver;
+                       break;
+               }
+       }
+
+       return 0;
+}
+
+/**
+ * connman_resolver_remove_all:
+ * @interface: network interface
+ *
+ * Remove all resolver server address for the specified interface
+ */
+int connman_resolver_remove_all(const char *interface)
+{
+       GSList *list, *matches = NULL;
+
+       DBG("interface %s", interface);
+
+       for (list = entry_list; list; list = list->next) {
+               struct entry_data *entry = list->data;
+
+               if (g_str_equal(entry->interface, interface) == FALSE)
+                       continue;
+
+               matches = g_slist_append(matches, entry);
+       }
+
+       remove_entries(matches);
+
+       return 0;
+}
+
+static int selftest_append(const char *interface, const char *domain,
+                                                       const char *server)
+{
+       DBG("server %s", server);
+
+       return 0;
+}
+
+static int selftest_remove(const char *interface, const char *domain,
+                                                       const char *server)
+{
+       DBG("server %s", server);
+
+       return 0;
+}
+
+static struct connman_resolver selftest_resolver = {
+       .name     = "selftest",
+       .priority = CONNMAN_RESOLVER_PRIORITY_HIGH + 42,
+       .append   = selftest_append,
+       .remove   = selftest_remove,
+};
+
+int __connman_resolver_selftest(void)
+{
+       connman_resolver_append("wlan0", "lwn.net", "192.168.0.1");
+
+       connman_resolver_register(&selftest_resolver);
+
+       connman_resolver_append("eth0", "moblin.org", "192.168.42.1");
+       connman_resolver_append("wlan0", "lwn.net", "192.168.0.2");
+
+       connman_resolver_remove_all("wlan0");
+
+       connman_resolver_unregister(&selftest_resolver);
+
+       return 0;
+}
+
+static int resolvfile_append(const char *interface, const char *domain,
+                                                       const char *server)
+{
+       char *cmd;
+       int fd, len, err;
+
+       DBG("interface %s server %s", interface, server);
+
+       fd = open("/etc/resolv.conf", O_RDWR | O_CREAT,
+                                       S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH);
+       if (fd < 0)
+               return -errno;
+
+       err = ftruncate(fd, 0);
+
+       cmd = g_strdup_printf("# Generated by Connection Manager\n"
+                                               "nameserver %s\n", server);
+
+       len = write(fd, cmd, strlen(cmd));
+
+       g_free(cmd);
+
+       close(fd);
+
+       return 0;
+}
+
+static int resolvfile_remove(const char *interface, const char *domain,
+                                                       const char *server)
+{
+       DBG("interface %s server %s", interface, server);
+
+       return 0;
+}
+
+static struct connman_resolver resolvfile_resolver = {
+       .name           = "resolvfile",
+       .priority       = CONNMAN_RESOLVER_PRIORITY_LOW,
+       .append         = resolvfile_append,
+       .remove         = resolvfile_remove,
+};
+
+int __connman_resolver_init(void)
+{
+       DBG("");
+
+       return connman_resolver_register(&resolvfile_resolver);
+}
+
+void __connman_resolver_cleanup(void)
+{
+       DBG("");
+
+       connman_resolver_unregister(&resolvfile_resolver);
+}
diff --git a/src/rfkill.c b/src/rfkill.c
new file mode 100644 (file)
index 0000000..84693b4
--- /dev/null
@@ -0,0 +1,123 @@
+/*
+ *
+ *  Connection Manager
+ *
+ *  Copyright (C) 2007-2009  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 <errno.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <string.h>
+#include <stdint.h>
+
+#include "connman.h"
+
+enum rfkill_type {
+       RFKILL_TYPE_ALL = 0,
+       RFKILL_TYPE_WLAN,
+       RFKILL_TYPE_BLUETOOTH,
+       RFKILL_TYPE_UWB,
+       RFKILL_TYPE_WIMAX,
+       RFKILL_TYPE_WWAN,
+};
+
+enum rfkill_operation {
+       RFKILL_OP_ADD = 0,
+       RFKILL_OP_DEL,
+       RFKILL_OP_CHANGE,
+       RFKILL_OP_CHANGE_ALL,
+};
+
+struct rfkill_event {
+       uint32_t idx;
+       uint8_t  type;
+       uint8_t  op;
+       uint8_t  soft;
+       uint8_t  hard;
+};
+
+static gboolean rfkill_event(GIOChannel *chan,
+                               GIOCondition cond, gpointer data)
+{
+       unsigned char buf[32];
+       struct rfkill_event *event = (void *) buf;
+       gsize len;
+       GIOError err;
+
+       if (cond & (G_IO_NVAL | G_IO_HUP | G_IO_ERR))
+               return FALSE;
+
+       memset(buf, 0, sizeof(buf));
+
+       err = g_io_channel_read(chan, (gchar *) buf, sizeof(buf), &len);
+       if (err) {
+               if (err == G_IO_ERROR_AGAIN)
+                       return TRUE;
+               return FALSE;
+       }
+
+       if (len != sizeof(struct rfkill_event))
+               return TRUE;
+
+       connman_info("RFKILL event: idx %u type %u op %u soft %u hard %u",
+                                       event->idx, event->type, event->op,
+                                               event->soft, event->hard);
+
+       return TRUE;
+}
+
+static GIOChannel *channel = NULL;
+
+int __connman_rfkill_init(void)
+{
+       int fd;
+
+       DBG("");
+
+       fd = open("/dev/rfkill", O_RDWR);
+       if (fd < 0) {
+               connman_error("Failed to open RFKILL control device");
+               return -EIO;
+       }
+
+       channel = g_io_channel_unix_new(fd);
+       g_io_channel_set_close_on_unref(channel, TRUE);
+
+       g_io_add_watch(channel, G_IO_IN | G_IO_NVAL | G_IO_HUP | G_IO_ERR,
+                                                       rfkill_event, NULL);
+
+       return 0;
+}
+
+void __connman_rfkill_cleanup(void)
+{
+       DBG("");
+
+       if (channel == NULL)
+               return;
+
+       g_io_channel_shutdown(channel, TRUE, NULL);
+       g_io_channel_unref(channel);
+
+       channel = NULL;
+}
diff --git a/src/rtnl.c b/src/rtnl.c
new file mode 100644 (file)
index 0000000..70e8c3c
--- /dev/null
@@ -0,0 +1,799 @@
+/*
+ *
+ *  Connection Manager
+ *
+ *  Copyright (C) 2007-2009  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 <unistd.h>
+#include <string.h>
+#include <sys/socket.h>
+#include <arpa/inet.h>
+
+#include <linux/if.h>
+#include <linux/netlink.h>
+#include <linux/rtnetlink.h>
+
+#include <glib.h>
+
+#include "connman.h"
+
+struct watch_data {
+       unsigned int id;
+       int index;
+       connman_rtnl_link_cb_t newlink;
+       void *user_data;
+};
+
+static GSList *watch_list = NULL;
+static unsigned int watch_id = 0;
+
+/**
+ * connman_rtnl_add_newlink_watch:
+ * @index: network device index
+ * @callback: callback function
+ * @user_data: callback data;
+ *
+ * Add a new RTNL watch for newlink events
+ *
+ * Returns: %0 on failure and a unique id on success
+ */
+unsigned int connman_rtnl_add_newlink_watch(int index,
+                       connman_rtnl_link_cb_t callback, void *user_data)
+{
+       struct watch_data *watch;
+
+       watch = g_try_new0(struct watch_data, 1);
+       if (watch == NULL)
+               return 0;
+
+       watch->id = ++watch_id;
+       watch->index = index;
+
+       watch->newlink = callback;
+       watch->user_data = user_data;
+
+       watch_list = g_slist_prepend(watch_list, watch);
+
+       DBG("id %d", watch->id);
+
+       return watch->id;
+}
+
+/**
+ * connman_rtnl_remove_watch:
+ * @id: watch identifier
+ *
+ * Remove the RTNL watch for the identifier
+ */
+void connman_rtnl_remove_watch(unsigned int id)
+{
+       GSList *list;
+
+       DBG("id %d", id);
+
+       if (id == 0)
+               return;
+
+       for (list = watch_list; list; list = list->next) {
+               struct watch_data *watch = list->data;
+
+               if (watch->id  == id) {
+                       watch_list = g_slist_remove(watch_list, watch);
+                       g_free(watch);
+                       break;
+               }
+       }
+}
+
+static GSList *rtnl_list = NULL;
+
+static gint compare_priority(gconstpointer a, gconstpointer b)
+{
+       const struct connman_rtnl *rtnl1 = a;
+       const struct connman_rtnl *rtnl2 = b;
+
+       return rtnl2->priority - rtnl1->priority;
+}
+
+/**
+ * connman_rtnl_register:
+ * @rtnl: RTNL module
+ *
+ * Register a new RTNL module
+ *
+ * Returns: %0 on success
+ */
+int connman_rtnl_register(struct connman_rtnl *rtnl)
+{
+       DBG("rtnl %p name %s", rtnl, rtnl->name);
+
+       rtnl_list = g_slist_insert_sorted(rtnl_list, rtnl,
+                                                       compare_priority);
+
+       return 0;
+}
+
+/**
+ * connman_rtnl_unregister:
+ * @rtnl: RTNL module
+ *
+ * Remove a previously registered RTNL module
+ */
+void connman_rtnl_unregister(struct connman_rtnl *rtnl)
+{
+       DBG("rtnl %p name %s", rtnl, rtnl->name);
+
+       rtnl_list = g_slist_remove(rtnl_list, rtnl);
+}
+
+static void process_newlink(unsigned short type, int index,
+                                       unsigned flags, unsigned change)
+{
+       GSList *list;
+
+       for (list = rtnl_list; list; list = list->next) {
+               struct connman_rtnl *rtnl = list->data;
+
+               if (rtnl->newlink)
+                       rtnl->newlink(type, index, flags, change);
+       }
+
+       for (list = watch_list; list; list = list->next) {
+               struct watch_data *watch = list->data;
+
+               if (watch->index != index)
+                       continue;
+
+               if (watch->newlink)
+                       watch->newlink(flags, change, watch->user_data);
+       }
+}
+
+static void process_dellink(unsigned short type, int index,
+                                       unsigned flags, unsigned change)
+{
+       GSList *list;
+
+       for (list = rtnl_list; list; list = list->next) {
+               struct connman_rtnl *rtnl = list->data;
+
+               if (rtnl->dellink)
+                       rtnl->dellink(type, index, flags, change);
+       }
+}
+
+static char *extract_gateway(struct rtmsg *msg, int bytes, int *index)
+{
+       char *gateway = NULL;
+       struct in_addr addr;
+       struct rtattr *attr;
+
+       for (attr = RTM_RTA(msg); RTA_OK(attr, bytes);
+                                       attr = RTA_NEXT(attr, bytes)) {
+               switch (attr->rta_type) {
+               case RTA_GATEWAY:
+                       addr = *((struct in_addr *) RTA_DATA(attr));
+                       g_free(gateway);
+                       gateway = g_strdup(inet_ntoa(addr));
+                       break;
+               case RTA_OIF:
+                       *index = *((int *) RTA_DATA(attr));
+                       break;
+               }
+       }
+
+       return gateway;
+}
+
+static void process_newgateway(struct rtmsg *msg, int bytes)
+{
+       int index = -1;
+       char *gateway;
+       GSList *list;
+
+       gateway = extract_gateway(msg, bytes, &index);
+       if (gateway == NULL || index < 0)
+               return;
+
+       for (list = rtnl_list; list; list = list->next) {
+               struct connman_rtnl *rtnl = list->data;
+
+               if (rtnl->newgateway)
+                       rtnl->newgateway(index, gateway);
+       }
+
+       g_free(gateway);
+}
+
+static void process_delgateway(struct rtmsg *msg, int bytes)
+{
+       int index = -1;
+       char *gateway;
+       GSList *list;
+
+       gateway = extract_gateway(msg, bytes, &index);
+       if (gateway == NULL || index < 0)
+               return;
+
+       for (list = rtnl_list; list; list = list->next) {
+               struct connman_rtnl *rtnl = list->data;
+
+               if (rtnl->delgateway)
+                       rtnl->delgateway(index, gateway);
+       }
+
+       g_free(gateway);
+}
+
+static inline void print_inet(struct rtattr *attr, const char *name, int family)
+{
+       if (family == AF_INET) {
+               struct in_addr addr;
+               addr = *((struct in_addr *) RTA_DATA(attr));
+               DBG("  attr %s (len %d) %s\n", name,
+                               (int) RTA_PAYLOAD(attr), inet_ntoa(addr));
+       } else
+               DBG("  attr %s (len %d)\n", name, (int) RTA_PAYLOAD(attr));
+}
+
+static inline void print_char(struct rtattr *attr, const char *name)
+{
+       DBG("  attr %s (len %d) %s\n", name, (int) RTA_PAYLOAD(attr),
+                                               (char *) RTA_DATA(attr));
+}
+
+static inline void print_byte(struct rtattr *attr, const char *name)
+{
+       DBG("  attr %s (len %d) 0x%02x\n", name, (int) RTA_PAYLOAD(attr),
+                                       *((unsigned char *) RTA_DATA(attr)));
+}
+
+static inline void print_attr(struct rtattr *attr, const char *name)
+{
+       if (name)
+               DBG("  attr %s (len %d)\n", name, (int) RTA_PAYLOAD(attr));
+       else
+               DBG("  attr %d (len %d)\n",
+                               attr->rta_type, (int) RTA_PAYLOAD(attr));
+}
+
+static void rtnl_link(struct nlmsghdr *hdr)
+{
+#if 0
+       struct ifinfomsg *msg;
+       struct rtattr *attr;
+       int bytes;
+
+       msg = (struct ifinfomsg *) NLMSG_DATA(hdr);
+       bytes = IFLA_PAYLOAD(hdr);
+
+       DBG("ifi_index %d ifi_flags 0x%04x", msg->ifi_index, msg->ifi_flags);
+
+       for (attr = IFLA_RTA(msg); RTA_OK(attr, bytes);
+                                       attr = RTA_NEXT(attr, bytes)) {
+               switch (attr->rta_type) {
+               case IFLA_ADDRESS:
+                       print_attr(attr, "address");
+                       break;
+               case IFLA_BROADCAST:
+                       print_attr(attr, "broadcast");
+                       break;
+               case IFLA_IFNAME:
+                       print_char(attr, "ifname");
+                       break;
+               case IFLA_MTU:
+                       print_attr(attr, "mtu");
+                       break;
+               case IFLA_LINK:
+                       print_attr(attr, "link");
+                       break;
+               case IFLA_QDISC:
+                       print_attr(attr, "qdisc");
+                       break;
+               case IFLA_STATS:
+                       print_attr(attr, "stats");
+                       break;
+               case IFLA_COST:
+                       print_attr(attr, "cost");
+                       break;
+               case IFLA_PRIORITY:
+                       print_attr(attr, "priority");
+                       break;
+               case IFLA_MASTER:
+                       print_attr(attr, "master");
+                       break;
+               case IFLA_WIRELESS:
+                       print_attr(attr, "wireless");
+                       break;
+               case IFLA_PROTINFO:
+                       print_attr(attr, "protinfo");
+                       break;
+               case IFLA_TXQLEN:
+                       print_attr(attr, "txqlen");
+                       break;
+               case IFLA_MAP:
+                       print_attr(attr, "map");
+                       break;
+               case IFLA_WEIGHT:
+                       print_attr(attr, "weight");
+                       break;
+               case IFLA_OPERSTATE:
+                       print_byte(attr, "operstate");
+                       break;
+               case IFLA_LINKMODE:
+                       print_byte(attr, "linkmode");
+                       break;
+               default:
+                       print_attr(attr, NULL);
+                       break;
+               }
+       }
+#endif
+}
+
+static void rtnl_newlink(struct nlmsghdr *hdr)
+{
+       struct ifinfomsg *msg;
+
+       msg = (struct ifinfomsg *) NLMSG_DATA(hdr);
+
+       DBG("ifi_type %d ifi_index %d ifi_flags 0x%04x ifi_change 0x%04x",
+                                       msg->ifi_type, msg->ifi_index,
+                                       msg->ifi_flags, msg->ifi_change);
+
+       process_newlink(msg->ifi_type, msg->ifi_index,
+                                       msg->ifi_flags, msg->ifi_change);
+
+       rtnl_link(hdr);
+}
+
+static void rtnl_dellink(struct nlmsghdr *hdr)
+{
+       struct ifinfomsg *msg;
+
+       msg = (struct ifinfomsg *) NLMSG_DATA(hdr);
+
+       DBG("ifi_type %d ifi_index %d ifi_flags 0x%04x ifi_change 0x%04x",
+                                       msg->ifi_type, msg->ifi_index,
+                                       msg->ifi_flags, msg->ifi_change);
+
+       process_dellink(msg->ifi_type, msg->ifi_index,
+                                       msg->ifi_flags, msg->ifi_change);
+
+       rtnl_link(hdr);
+}
+
+static void rtnl_addr(struct nlmsghdr *hdr)
+{
+       struct ifaddrmsg *msg;
+       struct rtattr *attr;
+       int bytes;
+
+       msg = (struct ifaddrmsg *) NLMSG_DATA(hdr);
+       bytes = IFA_PAYLOAD(hdr);
+
+       DBG("ifa_family %d ifa_index %d", msg->ifa_family, msg->ifa_index);
+
+       for (attr = IFA_RTA(msg); RTA_OK(attr, bytes);
+                                       attr = RTA_NEXT(attr, bytes)) {
+               switch (attr->rta_type) {
+               case IFA_ADDRESS:
+                       print_inet(attr, "address", msg->ifa_family);
+                       break;
+               case IFA_LOCAL:
+                       print_inet(attr, "local", msg->ifa_family);
+                       break;
+               case IFA_LABEL:
+                       print_char(attr, "label");
+                       break;
+               case IFA_BROADCAST:
+                       print_inet(attr, "broadcast", msg->ifa_family);
+                       break;
+               case IFA_ANYCAST:
+                       print_attr(attr, "anycast");
+                       break;
+               case IFA_CACHEINFO:
+                       print_attr(attr, "cacheinfo");
+                       break;
+               case IFA_MULTICAST:
+                       print_attr(attr, "multicast");
+                       break;
+               default:
+                       print_attr(attr, NULL);
+                       break;
+               }
+       }
+}
+
+static void rtnl_route(struct nlmsghdr *hdr)
+{
+#if 0
+       struct rtmsg *msg;
+       struct rtattr *attr;
+       int bytes;
+
+       msg = (struct rtmsg *) NLMSG_DATA(hdr);
+       bytes = RTM_PAYLOAD(hdr);
+
+       DBG("rtm_family %d rtm_flags 0x%04x", msg->rtm_family, msg->rtm_flags);
+
+       for (attr = RTM_RTA(msg); RTA_OK(attr, bytes);
+                                       attr = RTA_NEXT(attr, bytes)) {
+               switch (attr->rta_type) {
+               case RTA_DST:
+                       print_inet(attr, "dst", msg->rtm_family);
+                       break;
+               case RTA_SRC:
+                       print_inet(attr, "src", msg->rtm_family);
+                       break;
+               case RTA_IIF:
+                       print_char(attr, "iif");
+                       break;
+               case RTA_OIF:
+                       print_attr(attr, "oif");
+                       break;
+               case RTA_GATEWAY:
+                       print_inet(attr, "gateway", msg->rtm_family);
+                       break;
+               case RTA_PRIORITY:
+                       print_attr(attr, "priority");
+                       break;
+               case RTA_PREFSRC:
+                       print_inet(attr, "prefsrc", msg->rtm_family);
+                       break;
+               case RTA_METRICS:
+                       print_attr(attr, "metrics");
+                       break;
+               case RTA_TABLE:
+                       print_attr(attr, "table");
+                       break;
+               default:
+                       print_attr(attr, NULL);
+                       break;
+               }
+       }
+#endif
+}
+
+static void rtnl_newroute(struct nlmsghdr *hdr)
+{
+       struct rtmsg *msg;
+
+       msg = (struct rtmsg *) NLMSG_DATA(hdr);
+
+       if (msg->rtm_type == RTN_UNICAST && msg->rtm_table == RT_TABLE_MAIN &&
+                                       msg->rtm_scope == RT_SCOPE_UNIVERSE) {
+               DBG("rtm_table %d rtm_scope %d rtm_type %d rtm_flags 0x%04x",
+                                       msg->rtm_table, msg->rtm_scope,
+                                       msg->rtm_type, msg->rtm_flags);
+               process_newgateway(msg, RTM_PAYLOAD(hdr));
+       }
+
+       rtnl_route(hdr);
+}
+
+static void rtnl_delroute(struct nlmsghdr *hdr)
+{
+       struct rtmsg *msg;
+
+       msg = (struct rtmsg *) NLMSG_DATA(hdr);
+
+       if (msg->rtm_type == RTN_UNICAST && msg->rtm_table == RT_TABLE_MAIN &&
+                                       msg->rtm_scope == RT_SCOPE_UNIVERSE) {
+               DBG("rtm_table %d rtm_scope %d rtm_type %d rtm_flags 0x%04x",
+                                       msg->rtm_table, msg->rtm_scope,
+                                       msg->rtm_type, msg->rtm_flags);
+               process_delgateway(msg, RTM_PAYLOAD(hdr));
+       }
+
+       rtnl_route(hdr);
+}
+
+static const char *type2string(uint16_t type)
+{
+       switch (type) {
+       case NLMSG_NOOP:
+               return "NOOP";
+       case NLMSG_ERROR:
+               return "ERROR";
+       case NLMSG_DONE:
+               return "DONE";
+       case NLMSG_OVERRUN:
+               return "OVERRUN";
+       case RTM_GETLINK:
+               return "GETLINK";
+       case RTM_NEWLINK:
+               return "NEWLINK";
+       case RTM_DELLINK:
+               return "DELLINK";
+       case RTM_NEWADDR:
+               return "NEWADDR";
+       case RTM_DELADDR:
+               return "DELADDR";
+       case RTM_GETROUTE:
+               return "GETROUTE";
+       case RTM_NEWROUTE:
+               return "NEWROUTE";
+       case RTM_DELROUTE:
+               return "DELROUTE";
+       default:
+               return "UNKNOWN";
+       }
+}
+
+static GIOChannel *channel = NULL;
+
+struct rtnl_request {
+       struct nlmsghdr hdr;
+       struct rtgenmsg msg;
+};
+#define RTNL_REQUEST_SIZE  (sizeof(struct nlmsghdr) + sizeof(struct rtgenmsg))
+
+static GSList *request_list = NULL;
+static guint32 request_seq = 0;
+
+static struct rtnl_request *find_request(guint32 seq)
+{
+       GSList *list;
+
+       for (list = request_list; list; list = list->next) {
+               struct rtnl_request *req = list->data;
+
+               if (req->hdr.nlmsg_seq == seq)
+                       return req;
+       }
+
+       return NULL;
+}
+
+static int send_request(struct rtnl_request *req)
+{
+       struct sockaddr_nl addr;
+       int sk;
+
+       DBG("%s len %d type %d flags 0x%04x seq %d",
+                               type2string(req->hdr.nlmsg_type),
+                               req->hdr.nlmsg_len, req->hdr.nlmsg_type,
+                               req->hdr.nlmsg_flags, req->hdr.nlmsg_seq);
+
+       sk = g_io_channel_unix_get_fd(channel);
+
+       memset(&addr, 0, sizeof(addr));
+       addr.nl_family = AF_NETLINK;
+
+       return sendto(sk, req, req->hdr.nlmsg_len, 0,
+                               (struct sockaddr *) &addr, sizeof(addr));
+}
+
+static int queue_request(struct rtnl_request *req)
+{
+       request_list = g_slist_append(request_list, req);
+
+       if (g_slist_length(request_list) > 1)
+               return 0;
+
+       return send_request(req);
+}
+
+static int process_response(guint32 seq)
+{
+       struct rtnl_request *req;
+
+       DBG("seq %d", seq);
+
+       req = find_request(seq);
+       if (req != NULL) {
+               request_list = g_slist_remove(request_list, req);
+               g_free(req);
+       }
+
+       req = g_slist_nth_data(request_list, 0);
+       if (req == NULL)
+               return 0;
+
+       return send_request(req);
+}
+
+static void rtnl_message(void *buf, size_t len)
+{
+       DBG("buf %p len %zd", buf, len);
+
+       while (len > 0) {
+               struct nlmsghdr *hdr = buf;
+               struct nlmsgerr *err;
+
+               if (!NLMSG_OK(hdr, len))
+                       break;
+
+               DBG("%s len %d type %d flags 0x%04x seq %d",
+                                       type2string(hdr->nlmsg_type),
+                                       hdr->nlmsg_len, hdr->nlmsg_type,
+                                       hdr->nlmsg_flags, hdr->nlmsg_seq);
+
+               switch (hdr->nlmsg_type) {
+               case NLMSG_NOOP:
+               case NLMSG_OVERRUN:
+                       return;
+               case NLMSG_DONE:
+                       process_response(hdr->nlmsg_seq);
+                       return;
+               case NLMSG_ERROR:
+                       err = NLMSG_DATA(hdr);
+                       DBG("error %d (%s)", -err->error,
+                                               strerror(-err->error));
+                       return;
+               case RTM_NEWLINK:
+                       rtnl_newlink(hdr);
+                       break;
+               case RTM_DELLINK:
+                       rtnl_dellink(hdr);
+                       break;
+               case RTM_NEWADDR:
+               case RTM_DELADDR:
+                       rtnl_addr(hdr);
+                       break;
+               case RTM_NEWROUTE:
+                       rtnl_newroute(hdr);
+                       break;
+               case RTM_DELROUTE:
+                       rtnl_delroute(hdr);
+                       break;
+               }
+
+               len -= hdr->nlmsg_len;
+               buf += hdr->nlmsg_len;
+       }
+}
+
+static gboolean netlink_event(GIOChannel *chan,
+                               GIOCondition cond, gpointer data)
+{
+       unsigned char buf[4096];
+       gsize len;
+       GIOError err;
+
+       if (cond & (G_IO_NVAL | G_IO_HUP | G_IO_ERR))
+               return FALSE;
+
+       memset(buf, 0, sizeof(buf));
+
+       err = g_io_channel_read(chan, (gchar *) buf, sizeof(buf), &len);
+       if (err) {
+               if (err == G_IO_ERROR_AGAIN)
+                       return TRUE;
+               return FALSE;
+       }
+
+       rtnl_message(buf, len);
+
+       return TRUE;
+}
+
+int connman_rtnl_send_getlink(void)
+{
+       struct rtnl_request *req;
+
+       DBG("");
+
+       req = g_try_malloc0(RTNL_REQUEST_SIZE);
+       if (req == NULL)
+               return -ENOMEM;
+
+       req->hdr.nlmsg_len = RTNL_REQUEST_SIZE;
+       req->hdr.nlmsg_type = RTM_GETLINK;
+       req->hdr.nlmsg_flags = NLM_F_REQUEST | NLM_F_DUMP;
+       req->hdr.nlmsg_pid = 0;
+       req->hdr.nlmsg_seq = request_seq++;
+       req->msg.rtgen_family = AF_INET;
+
+       return queue_request(req);
+}
+
+int connman_rtnl_send_getroute(void)
+{
+       struct rtnl_request *req;
+
+       DBG("");
+
+       req = g_try_malloc0(RTNL_REQUEST_SIZE);
+       if (req == NULL)
+               return -ENOMEM;
+
+       req->hdr.nlmsg_len = RTNL_REQUEST_SIZE;
+       req->hdr.nlmsg_type = RTM_GETROUTE;
+       req->hdr.nlmsg_flags = NLM_F_REQUEST | NLM_F_DUMP;
+       req->hdr.nlmsg_pid = 0;
+       req->hdr.nlmsg_seq = request_seq++;
+       req->msg.rtgen_family = AF_INET;
+
+       return queue_request(req);
+}
+
+int __connman_rtnl_init(void)
+{
+       struct sockaddr_nl addr;
+       int sk;
+
+       DBG("");
+
+       sk = socket(PF_NETLINK, SOCK_DGRAM, NETLINK_ROUTE);
+       if (sk < 0)
+               return -1;
+
+       memset(&addr, 0, sizeof(addr));
+       addr.nl_family = AF_NETLINK;
+       addr.nl_groups = RTMGRP_LINK | RTMGRP_IPV4_ROUTE;
+       //addr.nl_groups = RTMGRP_LINK | RTMGRP_IPV4_IFADDR;
+       //addr.nl_groups = RTMGRP_LINK | RTMGRP_IPV4_IFADDR | RTMGRP_IPV4_ROUTE;
+
+       if (bind(sk, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
+               close(sk);
+               return -1;
+       }
+
+       channel = g_io_channel_unix_new(sk);
+       g_io_channel_set_close_on_unref(channel, TRUE);
+
+       g_io_add_watch(channel, G_IO_IN | G_IO_NVAL | G_IO_HUP | G_IO_ERR,
+                                                       netlink_event, NULL);
+
+       return 0;
+}
+
+void __connman_rtnl_cleanup(void)
+{
+       GSList *list;
+
+       DBG("");
+
+       for (list = watch_list; list; list = list->next) {
+               struct watch_data *watch = list->data;
+
+               DBG("removing watch %d", watch->id);
+
+               g_free(watch);
+               list->data = NULL;
+       }
+
+       g_slist_free(watch_list);
+       watch_list = NULL;
+
+       for (list = request_list; list; list = list->next) {
+               struct rtnl_request *req = list->data;
+
+               DBG("%s len %d type %d flags 0x%04x seq %d",
+                               type2string(req->hdr.nlmsg_type),
+                               req->hdr.nlmsg_len, req->hdr.nlmsg_type,
+                               req->hdr.nlmsg_flags, req->hdr.nlmsg_seq);
+
+               g_free(req);
+               list->data = NULL;
+       }
+
+       g_slist_free(request_list);
+       request_list = NULL;
+
+       g_io_channel_shutdown(channel, TRUE, NULL);
+       g_io_channel_unref(channel);
+
+       channel = NULL;
+}
diff --git a/src/security.c b/src/security.c
new file mode 100644 (file)
index 0000000..9b27423
--- /dev/null
@@ -0,0 +1,92 @@
+/*
+ *
+ *  Connection Manager
+ *
+ *  Copyright (C) 2007-2009  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 "connman.h"
+
+static GSList *security_list = NULL;
+
+static gint compare_priority(gconstpointer a, gconstpointer b)
+{
+       const struct connman_security *security1 = a;
+       const struct connman_security *security2 = b;
+
+       return security2->priority - security1->priority;
+}
+
+/**
+ * connman_security_register:
+ * @security: security module
+ *
+ * Register a new security module
+ *
+ * Returns: %0 on success
+ */
+int connman_security_register(struct connman_security *security)
+{
+       DBG("security %p name %s", security, security->name);
+
+       security_list = g_slist_insert_sorted(security_list, security,
+                                                       compare_priority);
+
+       return 0;
+}
+
+/**
+ * connman_security_unregister:
+ * @security: security module
+ *
+ * Remove a previously registered security module
+ */
+void connman_security_unregister(struct connman_security *security)
+{
+       DBG("security %p name %s", security, security->name);
+
+       security_list = g_slist_remove(security_list, security);
+}
+
+int __connman_security_check_privilege(DBusMessage *message,
+                               enum connman_security_privilege privilege)
+{
+       GSList *list;
+       const char *sender;
+       int err = 0;
+
+       DBG("message %p", message);
+
+       sender = dbus_message_get_sender(message);
+
+       for (list = security_list; list; list = list->next) {
+               struct connman_security *security = list->data;
+
+               DBG("%s", security->name);
+
+               if (security->authorize_sender) {
+                       err = security->authorize_sender(sender, privilege);
+                       break;
+               }
+       }
+
+       return err;
+}
diff --git a/src/selftest.c b/src/selftest.c
new file mode 100644 (file)
index 0000000..727181d
--- /dev/null
@@ -0,0 +1,43 @@
+/*
+ *
+ *  Connection Manager
+ *
+ *  Copyright (C) 2007-2009  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 "connman.h"
+
+int __connman_selftest(void)
+{
+       int err;
+
+       connman_info("Start self testing");
+
+       connman_info("Testing resolver unit");
+       err = __connman_resolver_selftest();
+       if (err < 0)
+               goto done;
+
+done:
+       connman_info("Finished self testing");
+
+       return err;
+}
diff --git a/src/service.c b/src/service.c
new file mode 100644 (file)
index 0000000..aa89d5d
--- /dev/null
@@ -0,0 +1,1389 @@
+/*
+ *
+ *  Connection Manager
+ *
+ *  Copyright (C) 2007-2009  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 <gdbus.h>
+
+#include "connman.h"
+
+static DBusConnection *connection = NULL;
+
+static GSequence *service_list = NULL;
+static GHashTable *service_hash = NULL;
+
+struct connman_service {
+       gint refcount;
+       char *identifier;
+       char *path;
+       enum connman_service_type type;
+       enum connman_service_mode mode;
+       enum connman_service_security security;
+       enum connman_service_state state;
+       connman_uint8_t strength;
+       connman_bool_t favorite;
+       connman_bool_t hidden;
+       GTimeVal modified;
+       unsigned int order;
+       char *name;
+       char *passphrase;
+       char *profile;
+       struct connman_device *device;
+       struct connman_network *network;
+       DBusMessage *pending;
+       guint timeout;
+};
+
+static void append_path(gpointer value, gpointer user_data)
+{
+       struct connman_service *service = value;
+       DBusMessageIter *iter = user_data;
+
+       if (service->path == NULL)
+               return;
+
+       dbus_message_iter_append_basic(iter, DBUS_TYPE_OBJECT_PATH,
+                                                       &service->path);
+}
+
+void __connman_service_list(DBusMessageIter *iter)
+{
+       DBG("");
+
+       g_sequence_foreach(service_list, append_path, iter);
+}
+
+struct find_data {
+       const char *path;
+       struct connman_service *service;
+};
+
+static void compare_path(gpointer value, gpointer user_data)
+{
+       struct connman_service *service = value;
+       struct find_data *data = user_data;
+
+       if (data->service != NULL)
+               return;
+
+       if (g_strcmp0(service->path, data->path) == 0)
+               data->service = service;
+}
+
+static struct connman_service *find_service(const char *path)
+{
+       struct find_data data = { .path = path, .service = NULL };
+
+       DBG("path %s", path);
+
+       g_sequence_foreach(service_list, compare_path, &data);
+
+       return data.service;
+}
+
+static const char *type2string(enum connman_service_type type)
+{
+       switch (type) {
+       case CONNMAN_SERVICE_TYPE_UNKNOWN:
+               break;
+       case CONNMAN_SERVICE_TYPE_ETHERNET:
+               return "ethernet";
+       case CONNMAN_SERVICE_TYPE_WIFI:
+               return "wifi";
+       case CONNMAN_SERVICE_TYPE_WIMAX:
+               return "wimax";
+       }
+
+       return NULL;
+}
+
+static const char *mode2string(enum connman_service_mode mode)
+{
+       switch (mode) {
+       case CONNMAN_SERVICE_MODE_UNKNOWN:
+               break;
+       case CONNMAN_SERVICE_MODE_MANAGED:
+               return "managed";
+       case CONNMAN_SERVICE_MODE_ADHOC:
+               return "adhoc";
+       }
+
+       return NULL;
+}
+
+static const char *security2string(enum connman_service_security security)
+{
+       switch (security) {
+       case CONNMAN_SERVICE_SECURITY_UNKNOWN:
+               break;
+       case CONNMAN_SERVICE_SECURITY_NONE:
+               return "none";
+       case CONNMAN_SERVICE_SECURITY_WEP:
+               return "wep";
+       case CONNMAN_SERVICE_SECURITY_WPA:
+               return "wpa";
+       case CONNMAN_SERVICE_SECURITY_RSN:
+               return "rsn";
+       }
+
+       return NULL;
+}
+
+static const char *state2string(enum connman_service_state state)
+{
+       switch (state) {
+       case CONNMAN_SERVICE_STATE_UNKNOWN:
+               break;
+       case CONNMAN_SERVICE_STATE_IDLE:
+               return "idle";
+       case CONNMAN_SERVICE_STATE_CARRIER:
+               return "carrier";
+       case CONNMAN_SERVICE_STATE_ASSOCIATION:
+               return "association";
+       case CONNMAN_SERVICE_STATE_CONFIGURATION:
+               return "configuration";
+       case CONNMAN_SERVICE_STATE_READY:
+               return "ready";
+       case CONNMAN_SERVICE_STATE_DISCONNECT:
+               return "disconnect";
+       case CONNMAN_SERVICE_STATE_FAILURE:
+               return "failure";
+       }
+
+       return NULL;
+}
+
+static void state_changed(struct connman_service *service)
+{
+       DBusMessage *signal;
+       DBusMessageIter entry, value;
+       const char *str, *key = "State";
+
+       if (service->path == NULL)
+               return;
+
+       str = state2string(service->state);
+       if (str == NULL)
+               return;
+
+       signal = dbus_message_new_signal(service->path,
+                               CONNMAN_SERVICE_INTERFACE, "PropertyChanged");
+       if (signal == NULL)
+               return;
+
+       dbus_message_iter_init_append(signal, &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);
+
+       g_dbus_send_message(connection, signal);
+}
+
+static void strength_changed(struct connman_service *service)
+{
+       DBusMessage *signal;
+       DBusMessageIter entry, value;
+       const char *key = "Strength";
+
+       if (service->path == NULL)
+               return;
+
+       if (service->strength == 0)
+               return;
+
+       signal = dbus_message_new_signal(service->path,
+                               CONNMAN_SERVICE_INTERFACE, "PropertyChanged");
+       if (signal == NULL)
+               return;
+
+       dbus_message_iter_init_append(signal, &entry);
+
+       dbus_message_iter_append_basic(&entry, DBUS_TYPE_STRING, &key);
+
+       dbus_message_iter_open_container(&entry, DBUS_TYPE_VARIANT,
+                                       DBUS_TYPE_BYTE_AS_STRING, &value);
+       dbus_message_iter_append_basic(&value, DBUS_TYPE_BYTE,
+                                                       &service->strength);
+       dbus_message_iter_close_container(&entry, &value);
+
+       g_dbus_send_message(connection, signal);
+}
+
+static DBusMessage *get_properties(DBusConnection *conn,
+                                       DBusMessage *msg, void *user_data)
+{
+       struct connman_service *service = user_data;
+       DBusMessage *reply;
+       DBusMessageIter array, dict;
+       const char *str;
+
+       DBG("service %p", service);
+
+       reply = dbus_message_new_method_return(msg);
+       if (reply == NULL)
+               return NULL;
+
+       dbus_message_iter_init_append(reply, &array);
+
+       dbus_message_iter_open_container(&array, 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);
+
+       str = type2string(service->type);
+       if (str != NULL)
+               connman_dbus_dict_append_variant(&dict, "Type",
+                                               DBUS_TYPE_STRING, &str);
+
+       str = mode2string(service->mode);
+       if (str != NULL)
+               connman_dbus_dict_append_variant(&dict, "Mode",
+                                               DBUS_TYPE_STRING, &str);
+
+       str = security2string(service->security);
+       if (str != NULL)
+               connman_dbus_dict_append_variant(&dict, "Security",
+                                               DBUS_TYPE_STRING, &str);
+
+       str = state2string(service->state);
+       if (str != NULL)
+               connman_dbus_dict_append_variant(&dict, "State",
+                                               DBUS_TYPE_STRING, &str);
+
+       if (service->strength > 0)
+               connman_dbus_dict_append_variant(&dict, "Strength",
+                                       DBUS_TYPE_BYTE, &service->strength);
+
+       connman_dbus_dict_append_variant(&dict, "Favorite",
+                                       DBUS_TYPE_BOOLEAN, &service->favorite);
+
+       if (service->name != NULL)
+               connman_dbus_dict_append_variant(&dict, "Name",
+                                       DBUS_TYPE_STRING, &service->name);
+
+       if (service->passphrase != NULL &&
+                       __connman_security_check_privilege(msg,
+                               CONNMAN_SECURITY_PRIVILEGE_SECRET) == 0)
+               connman_dbus_dict_append_variant(&dict, "Passphrase",
+                               DBUS_TYPE_STRING, &service->passphrase);
+
+       dbus_message_iter_close_container(&array, &dict);
+
+       return reply;
+}
+
+static DBusMessage *set_property(DBusConnection *conn,
+                                       DBusMessage *msg, void *user_data)
+{
+       struct connman_service *service = user_data;
+       DBusMessageIter iter, value;
+       const char *name;
+       int type;
+
+       DBG("service %p", service);
+
+       if (dbus_message_iter_init(msg, &iter) == FALSE)
+               return __connman_error_invalid_arguments(msg);
+
+       dbus_message_iter_get_basic(&iter, &name);
+       dbus_message_iter_next(&iter);
+       dbus_message_iter_recurse(&iter, &value);
+
+       if (__connman_security_check_privilege(msg,
+                                       CONNMAN_SECURITY_PRIVILEGE_MODIFY) < 0)
+               return __connman_error_permission_denied(msg);
+
+       type = dbus_message_iter_get_arg_type(&value);
+
+       if (g_str_equal(name, "Passphrase") == TRUE) {
+               const char *passphrase;
+
+               if (type != DBUS_TYPE_STRING)
+                       return __connman_error_invalid_arguments(msg);
+
+               if (__connman_security_check_privilege(msg,
+                                       CONNMAN_SECURITY_PRIVILEGE_SECRET) < 0)
+                       return __connman_error_permission_denied(msg);
+
+               dbus_message_iter_get_basic(&value, &passphrase);
+
+               g_free(service->passphrase);
+               service->passphrase = g_strdup(passphrase);
+
+               if (service->network != NULL)
+                       connman_network_set_string(service->network,
+                               "WiFi.Passphrase", service->passphrase);
+
+               __connman_storage_save_service(service);
+       }
+
+       return g_dbus_create_reply(msg, DBUS_TYPE_INVALID);
+}
+
+static gboolean connect_timeout(gpointer user_data)
+{
+       struct connman_service *service = user_data;
+
+       DBG("service %p", service);
+
+       service->timeout = 0;
+
+       if (service->network != NULL)
+               __connman_network_disconnect(service->network);
+
+       if (service->pending != NULL) {
+               DBusMessage *reply;
+
+               reply = __connman_error_operation_timeout(service->pending);
+               if (reply != NULL)
+                       g_dbus_send_message(connection, reply);
+
+               dbus_message_unref(service->pending);
+               service->pending = NULL;
+
+               __connman_service_indicate_state(service,
+                                       CONNMAN_SERVICE_STATE_FAILURE);
+       }
+
+       return FALSE;
+}
+
+static DBusMessage *connect_service(DBusConnection *conn,
+                                       DBusMessage *msg, void *user_data)
+{
+       struct connman_service *service = user_data;
+
+       DBG("service %p", service);
+
+       if (service->pending != NULL)
+               return __connman_error_in_progress(msg);
+
+       if (service->state == CONNMAN_SERVICE_STATE_READY)
+               return __connman_error_already_connected(msg);
+
+       if (service->network != NULL) {
+               int err;
+
+               if (service->hidden == TRUE)
+                       return __connman_error_invalid_service(msg);
+
+               connman_network_set_string(service->network,
+                               "WiFi.Passphrase", service->passphrase);
+
+               err = __connman_network_connect(service->network);
+               if (err < 0 && err != -EINPROGRESS)
+                       return __connman_error_failed(msg, -err);
+
+               service->pending = dbus_message_ref(msg);
+
+               service->timeout = g_timeout_add_seconds(45,
+                                               connect_timeout, service);
+
+               return NULL;
+       } else if (service->device != NULL) {
+               if (service->favorite == FALSE)
+                       return __connman_error_no_carrier(msg);
+
+               if (__connman_device_connect(service->device) < 0)
+                       return __connman_error_failed(msg, EINVAL);
+
+               service->pending = dbus_message_ref(msg);
+               service->timeout = g_timeout_add_seconds(15,
+                                               connect_timeout, service);
+
+               return NULL;
+       }
+
+       return __connman_error_not_supported(msg);
+}
+
+static DBusMessage *disconnect_service(DBusConnection *conn,
+                                       DBusMessage *msg, void *user_data)
+{
+       struct connman_service *service = user_data;
+
+       DBG("service %p", service);
+
+       if (service->pending != NULL) {
+               DBusMessage *reply;
+
+               reply = __connman_error_operation_aborted(service->pending);
+               if (reply != NULL)
+                       g_dbus_send_message(conn, reply);
+
+               dbus_message_unref(service->pending);
+               service->pending = NULL;
+
+               return g_dbus_create_reply(msg, DBUS_TYPE_INVALID);
+       }
+
+       if (service->network != NULL) {
+               int err;
+
+               err = __connman_network_disconnect(service->network);
+               if (err < 0 && err != -EINPROGRESS)
+                       return __connman_error_failed(msg, -err);
+
+               return g_dbus_create_reply(msg, DBUS_TYPE_INVALID);
+       } else if (service->device != NULL) {
+               int err;
+
+               if (service->favorite == FALSE)
+                       return __connman_error_no_carrier(msg);
+
+               err = __connman_device_disconnect(service->device);
+               if (err < 0)
+                       return __connman_error_failed(msg, -err);
+
+               return g_dbus_create_reply(msg, DBUS_TYPE_INVALID);
+       }
+
+       return __connman_error_not_supported(msg);
+}
+
+static DBusMessage *remove_service(DBusConnection *conn,
+                                       DBusMessage *msg, void *user_data)
+{
+       struct connman_service *service = user_data;
+
+       DBG("service %p", service);
+
+       if (service->type == CONNMAN_SERVICE_TYPE_ETHERNET)
+               return __connman_error_not_supported(msg);
+
+       if (service->favorite == FALSE)
+               return __connman_error_not_supported(msg);
+
+       if (service->network != NULL)
+               __connman_network_disconnect(service->network);
+
+       connman_service_set_favorite(service, FALSE);
+       __connman_storage_save_service(service);
+
+       return g_dbus_create_reply(msg, DBUS_TYPE_INVALID);
+}
+
+static DBusMessage *move_before(DBusConnection *conn,
+                                       DBusMessage *msg, void *user_data)
+{
+       struct connman_service *service = user_data;
+       struct connman_service *target;
+       const char *path;
+       GSequenceIter *src, *dst;
+
+       DBG("service %p", service);
+
+       dbus_message_get_args(msg, NULL, DBUS_TYPE_OBJECT_PATH, &path,
+                                                       DBUS_TYPE_INVALID);
+
+       if (service->favorite == FALSE)
+               return __connman_error_not_supported(msg);
+
+       target = find_service(path);
+       if (target == NULL || target->favorite == FALSE || target == service)
+               return __connman_error_invalid_service(msg);
+
+       DBG("target %s", target->identifier);
+
+       g_get_current_time(&service->modified);
+       __connman_storage_save_service(service);
+
+       src = g_hash_table_lookup(service_hash, service->identifier);
+       dst = g_hash_table_lookup(service_hash, target->identifier);
+
+#if 0
+       g_sequence_move(src, dst);
+
+       __connman_profile_changed();
+
+       return g_dbus_create_reply(msg, DBUS_TYPE_INVALID);
+#endif
+       return __connman_error_not_implemented(msg);
+}
+
+static DBusMessage *move_after(DBusConnection *conn,
+                                       DBusMessage *msg, void *user_data)
+{
+       struct connman_service *service = user_data;
+       struct connman_service *target;
+       const char *path;
+
+       DBG("service %p", service);
+
+       dbus_message_get_args(msg, NULL, DBUS_TYPE_OBJECT_PATH, &path,
+                                                       DBUS_TYPE_INVALID);
+
+       if (service->favorite == FALSE)
+               return __connman_error_not_supported(msg);
+
+       target = find_service(path);
+       if (target == NULL || target->favorite == FALSE || target == service)
+               return __connman_error_invalid_service(msg);
+
+       DBG("target %s", target->identifier);
+
+       g_get_current_time(&service->modified);
+       __connman_storage_save_service(service);
+
+       return __connman_error_not_implemented(msg);
+}
+
+static GDBusMethodTable service_methods[] = {
+       { "GetProperties", "",   "a{sv}", get_properties     },
+       { "SetProperty",   "sv", "",      set_property       },
+       { "Connect",       "",   "",      connect_service,
+                                               G_DBUS_METHOD_FLAG_ASYNC },
+       { "Disconnect",    "",   "",      disconnect_service },
+       { "Remove",        "",   "",      remove_service     },
+       { "MoveBefore",    "o",  "",      move_before        },
+       { "MoveAfter",     "o",  "",      move_after         },
+       { },
+};
+
+static GDBusSignalTable service_signals[] = {
+       { "PropertyChanged", "sv" },
+       { },
+};
+
+static void service_free(gpointer user_data)
+{
+       struct connman_service *service = user_data;
+       char *path = service->path;
+
+       DBG("service %p", service);
+
+       g_hash_table_remove(service_hash, service->identifier);
+
+       if (service->timeout > 0)
+               g_source_remove(service->timeout);
+
+       if (service->pending != NULL) {
+               dbus_message_unref(service->pending);
+               service->pending = NULL;
+       }
+
+       service->path = NULL;
+
+       if (path != NULL) {
+               __connman_profile_changed();
+
+               g_dbus_unregister_interface(connection, path,
+                                               CONNMAN_SERVICE_INTERFACE);
+               g_free(path);
+       }
+
+       if (service->network != NULL)
+               connman_network_unref(service->network);
+
+       g_free(service->profile);
+       g_free(service->name);
+       g_free(service->passphrase);
+       g_free(service->identifier);
+       g_free(service);
+}
+
+/**
+ * __connman_service_put:
+ * @service: service structure
+ *
+ * Release service if no longer needed
+ */
+void __connman_service_put(struct connman_service *service)
+{
+       DBG("service %p", service);
+
+       if (g_atomic_int_dec_and_test(&service->refcount) == TRUE) {
+               GSequenceIter *iter;
+
+               iter = g_hash_table_lookup(service_hash, service->identifier);
+               if (iter != NULL)
+                       g_sequence_remove(iter);
+               else
+                       service_free(service);
+       }
+}
+
+static void __connman_service_initialize(struct connman_service *service)
+{
+       DBG("service %p", service);
+
+       service->refcount = 1;
+
+       service->type     = CONNMAN_SERVICE_TYPE_UNKNOWN;
+       service->mode     = CONNMAN_SERVICE_MODE_UNKNOWN;
+       service->security = CONNMAN_SERVICE_SECURITY_UNKNOWN;
+       service->state    = CONNMAN_SERVICE_STATE_UNKNOWN;
+
+       service->favorite = FALSE;
+       service->hidden = FALSE;
+
+       service->order = 0;
+}
+
+/**
+ * connman_service_create:
+ *
+ * Allocate a new service.
+ *
+ * Returns: a newly-allocated #connman_service structure
+ */
+struct connman_service *connman_service_create(void)
+{
+       struct connman_service *service;
+
+       service = g_try_new0(struct connman_service, 1);
+       if (service == NULL)
+               return NULL;
+
+       DBG("service %p", service);
+
+       __connman_service_initialize(service);
+
+       return service;
+}
+
+/**
+ * connman_service_ref:
+ * @service: service structure
+ *
+ * Increase reference counter of service
+ */
+struct connman_service *connman_service_ref(struct connman_service *service)
+{
+       g_atomic_int_inc(&service->refcount);
+
+       return service;
+}
+
+/**
+ * connman_service_unref:
+ * @service: service structure
+ *
+ * Decrease reference counter of service
+ */
+void connman_service_unref(struct connman_service *service)
+{
+       __connman_service_put(service);
+}
+
+static gint service_compare(gconstpointer a, gconstpointer b,
+                                                       gpointer user_data)
+{
+       struct connman_service *service_a = (void *) a;
+       struct connman_service *service_b = (void *) b;
+
+       if (service_a->state != service_b->state) {
+               if (service_a->state == CONNMAN_SERVICE_STATE_READY)
+                       return -1;
+               if (service_b->state == CONNMAN_SERVICE_STATE_READY)
+                       return 1;
+       }
+
+       if (service_a->order > service_b->order)
+               return -1;
+
+       if (service_a->order < service_b->order)
+               return 1;
+
+       if (service_a->favorite == TRUE && service_b->favorite == FALSE)
+               return -1;
+
+       if (service_a->favorite == FALSE && service_b->favorite == TRUE)
+               return 1;
+
+       return (gint) service_b->strength - (gint) service_a->strength;
+}
+
+/**
+ * connman_service_set_favorite:
+ * @service: service structure
+ * @favorite: favorite value
+ *
+ * Change the favorite setting of service
+ */
+int connman_service_set_favorite(struct connman_service *service,
+                                               connman_bool_t favorite)
+{
+       GSequenceIter *iter;
+
+       iter = g_hash_table_lookup(service_hash, service->identifier);
+       if (iter == NULL)
+               return -ENOENT;
+
+       if (service->favorite == favorite)
+               return -EALREADY;
+
+       service->favorite = favorite;
+
+       g_sequence_sort_changed(iter, service_compare, NULL);
+
+       __connman_profile_changed();
+
+       return 0;
+}
+
+int __connman_service_set_carrier(struct connman_service *service,
+                                               connman_bool_t carrier)
+{
+       DBG("service %p carrier %d", service, carrier);
+
+       if (service == NULL)
+               return -EINVAL;
+
+       switch (service->type) {
+       case CONNMAN_SERVICE_TYPE_UNKNOWN:
+       case CONNMAN_SERVICE_TYPE_WIFI:
+       case CONNMAN_SERVICE_TYPE_WIMAX:
+               return -EINVAL;
+       case CONNMAN_SERVICE_TYPE_ETHERNET:
+               break;
+       }
+
+       if (carrier == FALSE) {
+               service->state = CONNMAN_SERVICE_STATE_DISCONNECT;
+               state_changed(service);
+
+               service->state = CONNMAN_SERVICE_STATE_IDLE;
+               state_changed(service);
+       } else {
+               service->state = CONNMAN_SERVICE_STATE_CARRIER;
+               state_changed(service);
+       }
+
+       return connman_service_set_favorite(service, carrier);
+}
+
+int __connman_service_indicate_state(struct connman_service *service,
+                                       enum connman_service_state state)
+{
+       GSequenceIter *iter;
+
+       DBG("service %p state %d", service, state);
+
+       if (service == NULL)
+               return -EINVAL;
+
+       if (state == CONNMAN_SERVICE_STATE_CARRIER)
+               return __connman_service_set_carrier(service, TRUE);
+
+       if (service->state == state)
+               return -EALREADY;
+
+       if (service->state == CONNMAN_SERVICE_STATE_IDLE &&
+                               state == CONNMAN_SERVICE_STATE_DISCONNECT)
+               return -EINVAL;
+
+       if (state == CONNMAN_SERVICE_STATE_IDLE &&
+                       service->state != CONNMAN_SERVICE_STATE_DISCONNECT) {
+               service->state = CONNMAN_SERVICE_STATE_DISCONNECT;
+               state_changed(service);
+       }
+
+       service->state = state;
+       state_changed(service);
+
+       if (state == CONNMAN_SERVICE_STATE_READY) {
+               connman_service_set_favorite(service, TRUE);
+               __connman_storage_save_service(service);
+
+               if (service->timeout > 0)
+                       g_source_remove(service->timeout);
+
+               if (service->pending != NULL) {
+                       g_dbus_send_reply(connection, service->pending,
+                                                       DBUS_TYPE_INVALID);
+
+                       dbus_message_unref(service->pending);
+                       service->pending = NULL;
+               }
+
+               g_get_current_time(&service->modified);
+               __connman_storage_save_service(service);
+       }
+
+       if (state == CONNMAN_SERVICE_STATE_FAILURE) {
+               if (service->timeout > 0)
+                       g_source_remove(service->timeout);
+
+               if (service->pending != NULL) {
+                       DBusMessage *reply;
+
+                       reply = __connman_error_failed(service->pending, EIO);
+                       if (reply != NULL)
+                               g_dbus_send_message(connection, reply);
+
+                       dbus_message_unref(service->pending);
+                       service->pending = NULL;
+               }
+
+               service->state = CONNMAN_SERVICE_STATE_IDLE;
+               state_changed(service);
+       }
+
+       iter = g_hash_table_lookup(service_hash, service->identifier);
+       if (iter != NULL)
+               g_sequence_sort_changed(iter, service_compare, NULL);
+
+       __connman_profile_changed();
+
+       return 0;
+}
+
+int __connman_service_indicate_default(struct connman_service *service)
+{
+       DBG("service %p", service);
+
+       return 0;
+}
+
+/**
+ * __connman_service_lookup:
+ * @identifier: service identifier
+ *
+ * Look up a service by identifier (reference count will not be increased)
+ */
+static struct connman_service *__connman_service_lookup(const char *identifier)
+{
+       GSequenceIter *iter;
+
+       iter = g_hash_table_lookup(service_hash, identifier);
+       if (iter != NULL)
+               return g_sequence_get(iter);
+
+       return NULL;
+}
+
+/**
+ * __connman_service_get:
+ * @identifier: service identifier
+ *
+ * Look up a service by identifier or create a new one if not found
+ */
+static struct connman_service *__connman_service_get(const char *identifier)
+{
+       struct connman_service *service;
+       GSequenceIter *iter;
+
+       iter = g_hash_table_lookup(service_hash, identifier);
+       if (iter != NULL) {
+               service = g_sequence_get(iter);
+               if (service != NULL)
+                       g_atomic_int_inc(&service->refcount);
+               return service;
+       }
+
+       service = g_try_new0(struct connman_service, 1);
+       if (service == NULL)
+               return NULL;
+
+       DBG("service %p", service);
+
+       __connman_service_initialize(service);
+
+       service->identifier = g_strdup(identifier);
+
+       service->profile = g_strdup(__connman_profile_active_ident());
+
+       __connman_storage_load_service(service);
+
+       iter = g_sequence_insert_sorted(service_list, service,
+                                               service_compare, NULL);
+
+       g_hash_table_insert(service_hash, service->identifier, iter);
+
+       return service;
+}
+
+static int service_register(struct connman_service *service)
+{
+       const char *path = __connman_profile_active_path();
+       GSequenceIter *iter;
+
+       DBG("service %p", service);
+
+       if (service->path != NULL)
+               return -EALREADY;
+
+       service->path = g_strdup_printf("%s/%s", path, service->identifier);
+
+       DBG("path %s", service->path);
+
+       g_dbus_register_interface(connection, service->path,
+                                       CONNMAN_SERVICE_INTERFACE,
+                                       service_methods, service_signals,
+                                                       NULL, service, NULL);
+
+       __connman_storage_load_service(service);
+
+       iter = g_hash_table_lookup(service_hash, service->identifier);
+       if (iter != NULL)
+               g_sequence_sort_changed(iter, service_compare, NULL);
+
+       __connman_profile_changed();
+
+       return 0;
+}
+
+/**
+ * connman_service_lookup_from_device:
+ * @device: device structure
+ *
+ * Look up a service by device (reference count will not be increased)
+ */
+struct connman_service *__connman_service_lookup_from_device(struct connman_device *device)
+{
+       struct connman_service *service;
+       const char *ident;
+       char *name;
+
+       ident = __connman_device_get_ident(device);
+       if (ident == NULL)
+               return NULL;
+
+       name = g_strdup_printf("%s_%s",
+                               __connman_device_get_type(device), ident);
+
+       service = __connman_service_lookup(name);
+
+       g_free(name);
+
+       return service;
+}
+
+static enum connman_service_type convert_device_type(struct connman_device *device)
+{
+       enum connman_device_type type = connman_device_get_type(device);
+
+       switch (type) {
+       case CONNMAN_DEVICE_TYPE_UNKNOWN:
+       case CONNMAN_DEVICE_TYPE_VENDOR:
+       case CONNMAN_DEVICE_TYPE_WIFI:
+       case CONNMAN_DEVICE_TYPE_WIMAX:
+       case CONNMAN_DEVICE_TYPE_BLUETOOTH:
+       case CONNMAN_DEVICE_TYPE_GPS:
+       case CONNMAN_DEVICE_TYPE_HSO:
+       case CONNMAN_DEVICE_TYPE_NOZOMI:
+       case CONNMAN_DEVICE_TYPE_HUAWEI:
+       case CONNMAN_DEVICE_TYPE_NOVATEL:
+               break;
+       case CONNMAN_DEVICE_TYPE_ETHERNET:
+               return CONNMAN_SERVICE_TYPE_ETHERNET;
+       }
+
+       return CONNMAN_SERVICE_TYPE_UNKNOWN;
+}
+
+/**
+ * connman_service_create_from_device:
+ * @device: device structure
+ *
+ * Look up service by device and if not found, create one
+ */
+struct connman_service *__connman_service_create_from_device(struct connman_device *device)
+{
+       struct connman_service *service;
+       const char *ident;
+       char *name;
+
+       ident = __connman_device_get_ident(device);
+       if (ident == NULL)
+               return NULL;
+
+       name = g_strdup_printf("%s_%s",
+                               __connman_device_get_type(device), ident);
+
+       service = __connman_service_get(name);
+       if (service == NULL)
+               goto done;
+
+       if (service->path != NULL) {
+               __connman_service_put(service);
+               service = NULL;
+               goto done;
+       }
+
+       service->type = convert_device_type(device);
+
+       service->device = device;
+
+       service_register(service);
+
+done:
+       g_free(name);
+
+       return service;
+}
+
+/**
+ * connman_service_lookup_from_network:
+ * @network: network structure
+ *
+ * Look up a service by network (reference count will not be increased)
+ */
+struct connman_service *__connman_service_lookup_from_network(struct connman_network *network)
+{
+       struct connman_service *service;
+       const char *ident, *group;
+       char *name;
+
+       ident = __connman_network_get_ident(network);
+       if (ident == NULL)
+               return NULL;
+
+       group = __connman_network_get_group(network);
+       if (group == NULL)
+               return NULL;
+
+       name = g_strdup_printf("%s_%s_%s",
+                       __connman_network_get_type(network), ident, group);
+
+       service = __connman_service_lookup(name);
+
+       g_free(name);
+
+       return service;
+}
+
+static enum connman_service_type convert_network_type(struct connman_network *network)
+{
+       enum connman_network_type type = connman_network_get_type(network);
+
+       switch (type) {
+       case CONNMAN_NETWORK_TYPE_UNKNOWN:
+       case CONNMAN_NETWORK_TYPE_VENDOR:
+       case CONNMAN_NETWORK_TYPE_BLUETOOTH_PAN:
+       case CONNMAN_NETWORK_TYPE_BLUETOOTH_DUN:
+       case CONNMAN_NETWORK_TYPE_HSO:
+               break;
+       case CONNMAN_NETWORK_TYPE_WIFI:
+               return CONNMAN_SERVICE_TYPE_WIFI;
+       case CONNMAN_NETWORK_TYPE_WIMAX:
+               return CONNMAN_SERVICE_TYPE_WIMAX;
+       }
+
+       return CONNMAN_SERVICE_TYPE_UNKNOWN;
+}
+
+static enum connman_service_mode convert_wifi_mode(const char *mode)
+{
+       if (mode == NULL)
+               return CONNMAN_SERVICE_MODE_UNKNOWN;
+       else if (g_str_equal(mode, "managed") == TRUE)
+               return CONNMAN_SERVICE_MODE_MANAGED;
+       else if (g_str_equal(mode, "adhoc") == TRUE)
+               return CONNMAN_SERVICE_MODE_ADHOC;
+       else
+               return CONNMAN_SERVICE_MODE_UNKNOWN;
+}
+
+static enum connman_service_mode convert_wifi_security(const char *security)
+{
+       if (security == NULL)
+               return CONNMAN_SERVICE_SECURITY_UNKNOWN;
+       else if (g_str_equal(security, "none") == TRUE)
+               return CONNMAN_SERVICE_SECURITY_NONE;
+       else if (g_str_equal(security, "wep") == TRUE)
+               return CONNMAN_SERVICE_SECURITY_WEP;
+       else if (g_str_equal(security, "wpa") == TRUE)
+               return CONNMAN_SERVICE_SECURITY_WPA;
+       else if (g_str_equal(security, "rsn") == TRUE)
+               return CONNMAN_SERVICE_SECURITY_RSN;
+       else
+               return CONNMAN_SERVICE_SECURITY_UNKNOWN;
+}
+
+static void update_from_network(struct connman_service *service,
+                                       struct connman_network *network)
+{
+       connman_uint8_t strength = service->strength;
+       GSequenceIter *iter;
+       const char *str;
+
+       str = connman_network_get_string(network, "Name");
+       if (str != NULL) {
+               g_free(service->name);
+               service->name = g_strdup(str);
+               service->hidden = FALSE;
+       } else {
+               g_free(service->name);
+               service->name = NULL;
+               service->hidden = TRUE;
+       }
+
+       service->strength = connman_network_get_uint8(network, "Strength");
+
+       str = connman_network_get_string(network, "WiFi.Mode");
+       service->mode = convert_wifi_mode(str);
+
+       str = connman_network_get_string(network, "WiFi.Security");
+       service->security = convert_wifi_security(str);
+
+       if (service->strength > strength && service->network != NULL) {
+               connman_network_unref(service->network);
+               service->network = NULL;
+
+               strength_changed(service);
+       }
+
+       if (service->network == NULL) {
+               service->network = connman_network_ref(network);
+
+               str = connman_network_get_string(network, "WiFi.Passphrase");
+               if (str != NULL) {
+                       g_free(service->passphrase);
+                       service->passphrase = g_strdup(str);
+               }
+       }
+
+       iter = g_hash_table_lookup(service_hash, service->identifier);
+       if (iter != NULL)
+               g_sequence_sort_changed(iter, service_compare, NULL);
+}
+
+/**
+ * connman_service_create_from_network:
+ * @network: network structure
+ *
+ * Look up service by network and if not found, create one
+ */
+struct connman_service *__connman_service_create_from_network(struct connman_network *network)
+{
+       struct connman_service *service;
+       const char *ident, *group;
+       char *name;
+
+       ident = __connman_network_get_ident(network);
+       if (ident == NULL)
+               return NULL;
+
+       group = __connman_network_get_group(network);
+       if (group == NULL)
+               return NULL;
+
+       name = g_strdup_printf("%s_%s_%s",
+                       __connman_network_get_type(network), ident, group);
+
+       service = __connman_service_get(name);
+       if (service == NULL)
+               goto done;
+
+       if (service->path != NULL) {
+               update_from_network(service, network);
+
+               __connman_profile_changed();
+
+               __connman_service_put(service);
+               service = NULL;
+               goto done;
+       }
+
+       service->type = convert_network_type(network);
+
+       service->state = CONNMAN_SERVICE_STATE_IDLE;
+
+       update_from_network(service, network);
+
+       service_register(service);
+
+done:
+       g_free(name);
+
+       return service;
+}
+
+static int service_load(struct connman_service *service)
+{
+       GKeyFile *keyfile;
+       gchar *pathname, *data = NULL;
+       gsize length;
+       gchar *str;
+
+       DBG("service %p", service);
+
+       if (service->profile == NULL)
+               return -EINVAL;
+
+       pathname = g_strdup_printf("%s/%s.conf", STORAGEDIR, service->profile);
+       if (pathname == NULL)
+               return -ENOMEM;
+
+       keyfile = g_key_file_new();
+
+       if (g_file_get_contents(pathname, &data, &length, NULL) == FALSE) {
+               g_free(pathname);
+               return -ENOENT;
+       }
+
+       g_free(pathname);
+
+       if (g_key_file_load_from_data(keyfile, data, length,
+                                                       0, NULL) == FALSE) {
+               g_free(data);
+               return -EILSEQ;
+       }
+
+       g_free(data);
+
+       switch (service->type) {
+       case CONNMAN_SERVICE_TYPE_UNKNOWN:
+       case CONNMAN_SERVICE_TYPE_ETHERNET:
+               break;
+       case CONNMAN_SERVICE_TYPE_WIFI:
+       case CONNMAN_SERVICE_TYPE_WIMAX:
+               service->favorite = g_key_file_get_boolean(keyfile,
+                               service->identifier, "Favorite", NULL);
+               break;
+       }
+
+       str = g_key_file_get_string(keyfile,
+                               service->identifier, "Modified", NULL);
+       if (str != NULL) {
+               g_time_val_from_iso8601(str, &service->modified);
+               g_free(str);
+       }
+
+       str = g_key_file_get_string(keyfile,
+                               service->identifier, "Passphrase", NULL);
+       if (str != NULL) {
+               g_free(service->passphrase);
+               service->passphrase = str;
+       }
+
+       g_key_file_free(keyfile);
+
+       return 0;
+}
+
+static int service_save(struct connman_service *service)
+{
+       GKeyFile *keyfile;
+       gchar *pathname, *data = NULL;
+       gsize length;
+       gchar *str;
+
+       DBG("service %p", service);
+
+       if (service->profile == NULL)
+               return -EINVAL;
+
+       pathname = g_strdup_printf("%s/%s.conf", STORAGEDIR, service->profile);
+       if (pathname == NULL)
+               return -ENOMEM;
+
+       keyfile = g_key_file_new();
+
+       if (g_file_get_contents(pathname, &data, &length, NULL) == FALSE)
+               goto update;
+
+       if (length > 0) {
+               if (g_key_file_load_from_data(keyfile, data, length,
+                                                       0, NULL) == FALSE)
+                       goto done;
+       }
+
+       g_free(data);
+
+update:
+       if (service->name != NULL)
+               g_key_file_set_string(keyfile, service->identifier,
+                                               "Name", service->name);
+
+       switch (service->type) {
+       case CONNMAN_SERVICE_TYPE_UNKNOWN:
+       case CONNMAN_SERVICE_TYPE_ETHERNET:
+               break;
+       case CONNMAN_SERVICE_TYPE_WIFI:
+       case CONNMAN_SERVICE_TYPE_WIMAX:
+               g_key_file_set_boolean(keyfile, service->identifier,
+                                       "Favorite", service->favorite);
+               break;
+       }
+
+       str = g_time_val_to_iso8601(&service->modified);
+       if (str != NULL) {
+               g_key_file_set_string(keyfile, service->identifier,
+                                                       "Modified", str);
+               g_free(str);
+       }
+
+       if (service->passphrase != NULL)
+               g_key_file_set_string(keyfile, service->identifier,
+                                       "Passphrase", service->passphrase);
+
+       data = g_key_file_to_data(keyfile, &length, NULL);
+
+       if (g_file_set_contents(pathname, data, length, NULL) == FALSE)
+               connman_error("Failed to store service information");
+
+done:
+       g_free(data);
+
+       g_key_file_free(keyfile);
+
+       g_free(pathname);
+
+       return 0;
+}
+
+static struct connman_storage service_storage = {
+       .name           = "service",
+       .priority       = CONNMAN_STORAGE_PRIORITY_LOW,
+       .service_load   = service_load,
+       .service_save   = service_save,
+};
+
+int __connman_service_init(void)
+{
+       DBG("");
+
+       connection = connman_dbus_get_connection();
+
+       if (connman_storage_register(&service_storage) < 0)
+               connman_error("Failed to register service storage");
+
+       service_hash = g_hash_table_new_full(g_str_hash, g_str_equal,
+                                                               NULL, NULL);
+
+       service_list = g_sequence_new(service_free);
+
+       return 0;
+}
+
+void __connman_service_cleanup(void)
+{
+       DBG("");
+
+       g_sequence_free(service_list);
+       service_list = NULL;
+
+       g_hash_table_destroy(service_hash);
+       service_hash = NULL;
+
+       connman_storage_unregister(&service_storage);
+
+       dbus_connection_unref(connection);
+}
diff --git a/src/storage.c b/src/storage.c
new file mode 100644 (file)
index 0000000..08b7249
--- /dev/null
@@ -0,0 +1,230 @@
+/*
+ *
+ *  Connection Manager
+ *
+ *  Copyright (C) 2007-2009  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 "connman.h"
+
+static GSList *storage_list = NULL;
+
+static gint compare_priority(gconstpointer a, gconstpointer b)
+{
+       const struct connman_storage *storage1 = a;
+       const struct connman_storage *storage2 = b;
+
+       return storage2->priority - storage1->priority;
+}
+
+/**
+ * connman_storage_register:
+ * @storage: storage module
+ *
+ * Register a new storage module
+ *
+ * Returns: %0 on success
+ */
+int connman_storage_register(struct connman_storage *storage)
+{
+       DBG("storage %p name %s", storage, storage->name);
+
+       storage_list = g_slist_insert_sorted(storage_list, storage,
+                                                       compare_priority);
+
+       return 0;
+}
+
+/**
+ * connman_storage_unregister:
+ * @storage: storage module
+ *
+ * Remove a previously registered storage module
+ */
+void connman_storage_unregister(struct connman_storage *storage)
+{
+       DBG("storage %p name %s", storage, storage->name);
+
+       storage_list = g_slist_remove(storage_list, storage);
+}
+
+int __connman_storage_init_device(void)
+{
+       GSList *list;
+
+       DBG("");
+
+       for (list = storage_list; list; list = list->next) {
+               struct connman_storage *storage = list->data;
+
+               if (storage->device_init) {
+                       if (storage->device_init() == 0)
+                               return 0;
+               }
+       }
+
+       return -ENOENT;
+}
+
+int __connman_storage_load_device(struct connman_device *device)
+{
+       GSList *list;
+
+       DBG("device %p", device);
+
+       for (list = storage_list; list; list = list->next) {
+               struct connman_storage *storage = list->data;
+
+               if (storage->device_load) {
+                       if (storage->device_load(device) == 0)
+                               return 0;
+               }
+       }
+
+       return -ENOENT;
+}
+
+int __connman_storage_save_device(struct connman_device *device)
+{
+       GSList *list;
+
+       DBG("device %p", device);
+
+       for (list = storage_list; list; list = list->next) {
+               struct connman_storage *storage = list->data;
+
+               if (storage->device_save) {
+                       if (storage->device_save(device) == 0)
+                               return 0;
+               }
+       }
+
+       return -ENOENT;
+}
+
+int __connman_storage_init_network(struct connman_device *device)
+{
+       GSList *list;
+
+       DBG("device %p", device);
+
+       for (list = storage_list; list; list = list->next) {
+               struct connman_storage *storage = list->data;
+
+               if (storage->network_init) {
+                       if (storage->network_init(device) == 0)
+                               return 0;
+               }
+       }
+
+       return -ENOENT;
+}
+
+int __connman_storage_load_network(struct connman_network *network)
+{
+       GSList *list;
+
+       DBG("network %p", network);
+
+       for (list = storage_list; list; list = list->next) {
+               struct connman_storage *storage = list->data;
+
+               if (storage->network_load) {
+                       if (storage->network_load(network) == 0)
+                               return 0;
+               }
+       }
+
+       return -ENOENT;
+}
+
+int __connman_storage_save_network(struct connman_network *network)
+{
+       GSList *list;
+
+       DBG("network %p", network);
+
+       for (list = storage_list; list; list = list->next) {
+               struct connman_storage *storage = list->data;
+
+               if (storage->network_save) {
+                       if (storage->network_save(network) == 0)
+                               return 0;
+               }
+       }
+
+       return -ENOENT;
+}
+
+int __connman_storage_init_service(void)
+{
+       DBG("");
+
+       return -ENOENT;
+}
+
+int __connman_storage_load_service(struct connman_service *service)
+{
+       GSList *list;
+
+       DBG("service %p", service);
+
+       for (list = storage_list; list; list = list->next) {
+               struct connman_storage *storage = list->data;
+
+               if (storage->service_load) {
+                       if (storage->service_load(service) == 0)
+                               return 0;
+               }
+       }
+
+       return -ENOENT;
+}
+
+int __connman_storage_save_service(struct connman_service *service)
+{
+       GSList *list;
+
+       DBG("service %p", service);
+
+       for (list = storage_list; list; list = list->next) {
+               struct connman_storage *storage = list->data;
+
+               if (storage->service_save) {
+                       if (storage->service_save(service) == 0)
+                               return 0;
+               }
+       }
+
+       return -ENOENT;
+}
+
+int __connman_storage_init(void)
+{
+       DBG("");
+
+       return 0;
+}
+
+void __connman_storage_cleanup(void)
+{
+       DBG("");
+}
diff --git a/src/udev.c b/src/udev.c
new file mode 100644 (file)
index 0000000..d38abe6
--- /dev/null
@@ -0,0 +1,343 @@
+/*
+ *
+ *  Connection Manager
+ *
+ *  Copyright (C) 2007-2009  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 <sys/types.h>
+
+#define LIBUDEV_I_KNOW_THE_API_IS_SUBJECT_TO_CHANGE
+#include <libudev.h>
+
+#include <glib.h>
+
+#include "connman.h"
+
+#ifdef NEED_UDEV_ENUMERATE_ADD_MATCH_PROPERTY
+static int udev_enumerate_add_match_property(struct udev_enumerate *enumerate,
+                                       const char *property, const char *value)
+{
+       return -EINVAL;
+}
+#endif
+
+#ifdef NEED_UDEV_DEVICE_GET_PARENT_WITH_SUBSYSTEM_DEVTYPE
+static struct udev_device *udev_device_get_parent_with_subsystem_devtype(struct udev_device *device,
+                                               const char *subsystem, const char *devtype)
+{
+       return NULL;
+}
+#endif
+
+static GSList *device_list = NULL;
+
+static struct connman_device *find_device(const char *interface)
+{
+       GSList *list;
+
+       if (interface == NULL)
+               return NULL;
+
+       for (list = device_list; list; list = list->next) {
+               struct connman_device *device = list->data;
+               const char *device_interface;
+
+               device_interface = connman_device_get_interface(device);
+               if (device_interface == NULL)
+                       continue;
+
+               if (g_str_equal(device_interface, interface) == TRUE)
+                       return device;
+       }
+
+       return NULL;
+}
+
+static void add_device(struct udev_device *udev_device)
+{
+       enum connman_device_type devtype = CONNMAN_DEVICE_TYPE_UNKNOWN;
+       struct connman_device *device;
+       struct udev_list_entry *entry;
+       const char *type = NULL, *interface = NULL;
+
+       DBG("");
+
+       entry = udev_device_get_properties_list_entry(udev_device);
+       while (entry) {
+               const char *name = udev_list_entry_get_name(entry);
+
+               if (g_str_has_prefix(name, "CONNMAN_TYPE") == TRUE)
+                       type = udev_list_entry_get_value(entry);
+               else if (g_str_has_prefix(name, "CONNMAN_INTERFACE") == TRUE)
+                       interface = udev_list_entry_get_value(entry);
+
+               entry = udev_list_entry_get_next(entry);
+       }
+
+       device = find_device(interface);
+       if (device != NULL)
+               return;
+
+       if (type == NULL || interface == NULL)
+               return;
+
+       if (g_str_equal(interface, "ttyUSB0") == FALSE &&
+                               g_str_equal(interface, "noz0") == FALSE)
+               return;
+
+       if (g_str_equal(type, "nozomi") == TRUE)
+               devtype = CONNMAN_DEVICE_TYPE_NOZOMI;
+       else if (g_str_equal(type, "huawei") == TRUE)
+               devtype = CONNMAN_DEVICE_TYPE_HUAWEI;
+       else if (g_str_equal(type, "novatel") == TRUE)
+               devtype = CONNMAN_DEVICE_TYPE_NOVATEL;
+       else
+               return;
+
+       device = connman_device_create(interface, devtype);
+       if (device == NULL)
+               return;
+
+       connman_device_set_mode(device, CONNMAN_DEVICE_MODE_NETWORK_SINGLE);
+       connman_device_set_policy(device, CONNMAN_DEVICE_POLICY_MANUAL);
+
+       connman_device_set_interface(device, interface);
+
+       if (connman_device_register(device) < 0) {
+               connman_device_unref(device);
+               return;
+       }
+
+       device_list = g_slist_append(device_list, device);
+}
+
+static void remove_device(struct udev_device *udev_device)
+{
+       struct connman_device *device;
+       struct udev_list_entry *entry;
+       const char *interface = NULL;
+
+       DBG("");
+
+       entry = udev_device_get_properties_list_entry(udev_device);
+       while (entry) {
+               const char *name = udev_list_entry_get_name(entry);
+
+               if (g_str_has_prefix(name, "CONNMAN_INTERFACE") == TRUE)
+                       interface = udev_list_entry_get_value(entry);
+
+               entry = udev_list_entry_get_next(entry);
+       }
+
+       device = find_device(interface);
+       if (device == NULL)
+               return;
+
+       device_list = g_slist_remove(device_list, device);
+
+       connman_device_unregister(device);
+       connman_device_unref(device);
+}
+
+static void print_properties(struct udev_device *device, const char *prefix)
+{
+       struct udev_list_entry *entry;
+
+       entry = udev_device_get_properties_list_entry(device);
+       while (entry) {
+               const char *name = udev_list_entry_get_name(entry);
+               const char *value = udev_list_entry_get_value(entry);
+
+               if (g_str_has_prefix(name, "CONNMAN") == TRUE ||
+                               g_str_has_prefix(name, "RFKILL") == TRUE ||
+                               g_str_has_prefix(name, "ID_MODEM") == TRUE ||
+                               g_str_equal(name, "ID_VENDOR") == TRUE ||
+                               g_str_equal(name, "ID_MODEL") == TRUE ||
+                               g_str_equal(name, "INTERFACE") == TRUE ||
+                               g_str_equal(name, "IFINDEX") == TRUE ||
+                               g_str_equal(name, "DEVNAME") == TRUE ||
+                               g_str_equal(name, "DEVPATH") == TRUE)
+                       connman_debug("%s%s = %s", prefix, name, value);
+
+               entry = udev_list_entry_get_next(entry);
+       }
+}
+
+static void print_device(struct udev_device *device, const char *action)
+{
+       const char *subsystem, *devtype = NULL;
+       struct udev_device *parent;
+
+       connman_debug("=== %s ===", action);
+       print_properties(device, "");
+
+       parent = udev_device_get_parent(device);
+       if (parent == NULL)
+               return;
+
+       subsystem = udev_device_get_subsystem(parent);
+
+       if (subsystem != NULL &&
+                       g_str_equal(subsystem, "usb-serial") == TRUE) {
+               subsystem = "usb";
+               devtype = "usb_device";
+       }
+
+       parent = udev_device_get_parent_with_subsystem_devtype(device,
+                                                       subsystem, devtype);
+       print_properties(parent, "    ");
+}
+
+static void enumerate_devices(struct udev *context)
+{
+       struct udev_enumerate *enumerate;
+       struct udev_list_entry *entry;
+
+       enumerate = udev_enumerate_new(context);
+       if (enumerate == NULL)
+               return;
+
+       udev_enumerate_add_match_property(enumerate, "CONNMAN_TYPE", "?*");
+
+       udev_enumerate_scan_devices(enumerate);
+
+       entry = udev_enumerate_get_list_entry(enumerate);
+       while (entry) {
+               const char *syspath = udev_list_entry_get_name(entry);
+               struct udev_device *device;
+
+               device = udev_device_new_from_syspath(context, syspath);
+
+               print_device(device, "coldplug");
+
+               add_device(device);
+
+               udev_device_unref(device);
+
+               entry = udev_list_entry_get_next(entry);
+       }
+
+       udev_enumerate_unref(enumerate);
+}
+
+static gboolean udev_event(GIOChannel *channel,
+                               GIOCondition condition, gpointer user_data)
+{
+       struct udev_monitor *monitor = user_data;
+       struct udev_device *device;
+       const char *action;
+
+       device = udev_monitor_receive_device(monitor);
+       if (device == NULL)
+               return TRUE;
+
+       action = udev_device_get_action(device);
+       if (action == NULL)
+               goto done;
+
+       print_device(device, action);
+
+       if (g_str_equal(action, "add") == TRUE)
+               add_device(device);
+       else if (g_str_equal(action, "remove") == TRUE)
+               remove_device(device);
+
+done:
+       udev_device_unref(device);
+
+       return TRUE;
+}
+
+static struct udev *udev_ctx;
+static struct udev_monitor *udev_mon;
+static guint udev_watch = 0;
+
+int __connman_udev_init(void)
+{
+       GIOChannel *channel;
+       int fd;
+
+       DBG("");
+
+       udev_ctx = udev_new();
+       if (udev_ctx == NULL) {
+               connman_error("Failed to create udev context");
+               return -1;
+       }
+
+       udev_mon = udev_monitor_new_from_socket(udev_ctx,
+                                               "@/org/moblin/connman/udev");
+       if (udev_mon == NULL) {
+               connman_error("Failed to create udev monitor");
+               udev_unref(udev_ctx);
+               udev_ctx = NULL;
+               return -1;
+       }
+
+       if (udev_monitor_enable_receiving(udev_mon) < 0) {
+               connman_error("Failed to enable udev monitor");
+               udev_unref(udev_ctx);
+               udev_ctx = NULL;
+               udev_monitor_unref(udev_mon);
+               return -1;
+       }
+
+       enumerate_devices(udev_ctx);
+
+       fd = udev_monitor_get_fd(udev_mon);
+
+       channel = g_io_channel_unix_new(fd);
+       if (channel == NULL)
+               return 0;
+
+       udev_watch = g_io_add_watch(channel, G_IO_IN, udev_event, udev_mon);
+
+       g_io_channel_unref(channel);
+
+       return 0;
+}
+
+void __connman_udev_cleanup(void)
+{
+       GSList *list;
+
+       DBG("");
+
+       if (udev_watch > 0)
+               g_source_remove(udev_watch);
+
+       for (list = device_list; list; list = list->next) {
+               struct connman_device *device = list->data;
+
+               connman_device_unregister(device);
+               connman_device_unref(device);
+       }
+
+       g_slist_free(device_list);
+       device_list = NULL;
+
+       if (udev_ctx == NULL)
+               return;
+
+       udev_monitor_unref(udev_mon);
+       udev_unref(udev_ctx);
+}
diff --git a/test/Makefile.am b/test/Makefile.am
new file mode 100644 (file)
index 0000000..a4ec186
--- /dev/null
@@ -0,0 +1,11 @@
+
+EXTRA_DIST = get-state list-profiles list-services \
+               list-connections select-connection \
+               list-devices enable-device disable-device start-scanning \
+               list-networks select-network disable-network create-network \
+               set-passphrase set-address set-policy set-priority \
+               connect-network disconnect-network join-network \
+               simple-agent show-introspection test-compat test-manager \
+               test-connman monitor-connman monitor-services debug-connman
+
+MAINTAINERCLEANFILES = Makefile.in
diff --git a/test/connect-network b/test/connect-network
new file mode 100755 (executable)
index 0000000..37cf60b
--- /dev/null
@@ -0,0 +1,40 @@
+#!/usr/bin/python
+
+import sys
+import dbus
+
+if (len(sys.argv) < 2):
+       print "Usage: %s <network>" % (sys.argv[0])
+       sys.exit(1)
+
+bus = dbus.SystemBus()
+
+manager = dbus.Interface(bus.get_object("org.moblin.connman", "/"),
+                                       "org.moblin.connman.Manager")
+
+properties = manager.GetProperties()
+
+for path in properties["Devices"]:
+       device = dbus.Interface(bus.get_object("org.moblin.connman", path),
+                                               "org.moblin.connman.Device")
+
+       properties = device.GetProperties()
+
+       if properties["Type"] not in ["wifi", "wimax",
+                                       "bluetooth", "cellular"]:
+               continue
+
+       for path in properties["Networks"]:
+               network = dbus.Interface(bus.get_object("org.moblin.connman", path),
+                                               "org.moblin.connman.Network")
+
+               properties = network.GetProperties()
+
+               dev = path[path.rfind("/") + 1:]
+
+               if "Name" not in properties.keys():
+                       continue
+
+               if dev == sys.argv[1] or properties["Name"] == sys.argv[1]:
+                       print "Connecting %s" % (path)
+                       network.Connect()
diff --git a/test/create-network b/test/create-network
new file mode 100755 (executable)
index 0000000..32c8ab3
--- /dev/null
@@ -0,0 +1,26 @@
+#!/usr/bin/python
+
+import sys
+import dbus
+
+if (len(sys.argv) < 2):
+       print "Usage: %s <network>" % (sys.argv[0])
+       sys.exit(1)
+
+bus = dbus.SystemBus()
+
+manager = dbus.Interface(bus.get_object("org.moblin.connman", "/"),
+                                       "org.moblin.connman.Manager")
+
+properties = manager.GetProperties()
+
+for path in properties["Devices"]:
+       device = dbus.Interface(bus.get_object("org.moblin.connman", path),
+                                               "org.moblin.connman.Device")
+
+       properties = device.GetProperties()
+
+       if (properties["Type"] != "wifi" and properties["Type"] != "wimax"):
+               continue;
+
+       path = device.CreateNetwork({ "WiFi.SSID": sys.argv[1] });
diff --git a/test/debug-connman b/test/debug-connman
new file mode 100755 (executable)
index 0000000..8d7cbb7
--- /dev/null
@@ -0,0 +1,41 @@
+#!/usr/bin/python
+
+import gobject
+
+import dbus
+import dbus.mainloop.glib
+
+def element_signal(path, member):
+       if member == "ElementAdded":
+               action = "Add   "
+       elif member == "ElementRemoved":
+               action = "Remove"
+       elif member == "ElementUpdated":
+               action = "Update"
+       else:
+               return
+       print "%s  [ %s ]" % (action, path)
+
+if __name__ == '__main__':
+       dbus.mainloop.glib.DBusGMainLoop(set_as_default=True)
+
+       bus = dbus.SystemBus()
+
+       bus.add_signal_receiver(element_signal,
+                                       bus_name="org.moblin.connman",
+                                       signal_name = "ElementAdded",
+                                               path_keyword="path",
+                                               member_keyword="member")
+       bus.add_signal_receiver(element_signal,
+                                       bus_name="org.moblin.connman",
+                                       signal_name = "ElementRemoved",
+                                               path_keyword="path",
+                                               member_keyword="member")
+       bus.add_signal_receiver(element_signal,
+                                       bus_name="org.moblin.connman",
+                                       signal_name = "ElementUpdated",
+                                               path_keyword="path",
+                                               member_keyword="member")
+
+       mainloop = gobject.MainLoop()
+       mainloop.run()
diff --git a/test/disable-device b/test/disable-device
new file mode 100755 (executable)
index 0000000..7aadab0
--- /dev/null
@@ -0,0 +1,28 @@
+#!/usr/bin/python
+
+import sys
+import dbus
+
+if (len(sys.argv) < 2):
+       print "Usage: %s <device>" % (sys.argv[0])
+       sys.exit(1)
+
+bus = dbus.SystemBus()
+
+manager = dbus.Interface(bus.get_object('org.moblin.connman', "/"),
+                                       'org.moblin.connman.Manager')
+
+properties = manager.GetProperties()
+
+for path in properties["Devices"]:
+       device = dbus.Interface(bus.get_object('org.moblin.connman', path),
+                                               'org.moblin.connman.Device')
+
+       properties = device.GetProperties()
+
+       if properties["Interface"] != sys.argv[1]:
+               continue;
+
+       print "Disabling device %s" % (path)
+
+       device.SetProperty("Powered", dbus.Boolean(0));
diff --git a/test/disable-network b/test/disable-network
new file mode 100755 (executable)
index 0000000..0f26154
--- /dev/null
@@ -0,0 +1,31 @@
+#!/usr/bin/python
+
+import dbus
+
+bus = dbus.SystemBus()
+
+manager = dbus.Interface(bus.get_object("org.moblin.connman", "/"),
+                                       "org.moblin.connman.Manager")
+
+properties = manager.GetProperties()
+
+for path in properties["Devices"]:
+       device = dbus.Interface(bus.get_object("org.moblin.connman", path),
+                                               "org.moblin.connman.Device")
+
+       properties = device.GetProperties()
+
+       if (properties["Type"] != "wifi" and properties["Type"] != "wimax"):
+               continue;
+
+       print "[ %s ]" % (path)
+
+       for path in properties["Networks"]:
+               network = dbus.Interface(bus.get_object("org.moblin.connman", path),
+                                               "org.moblin.connman.Network")
+
+               properties = network.GetProperties()
+
+               if (properties["Connected"] == dbus.Boolean(1)):
+                       print "Disconnecting %s" % (path)
+                       network.Disconnect()
diff --git a/test/disconnect-network b/test/disconnect-network
new file mode 100755 (executable)
index 0000000..3bb235a
--- /dev/null
@@ -0,0 +1,40 @@
+#!/usr/bin/python
+
+import sys
+import dbus
+
+if (len(sys.argv) < 2):
+       print "Usage: %s <network>" % (sys.argv[0])
+       sys.exit(1)
+
+bus = dbus.SystemBus()
+
+manager = dbus.Interface(bus.get_object("org.moblin.connman", "/"),
+                                       "org.moblin.connman.Manager")
+
+properties = manager.GetProperties()
+
+for path in properties["Devices"]:
+       device = dbus.Interface(bus.get_object("org.moblin.connman", path),
+                                               "org.moblin.connman.Device")
+
+       properties = device.GetProperties()
+
+       if properties["Type"] not in ["wifi", "wimax",
+                                       "bluetooth", "cellular"]:
+               continue
+
+       for path in properties["Networks"]:
+               network = dbus.Interface(bus.get_object("org.moblin.connman", path),
+                                               "org.moblin.connman.Network")
+
+               properties = network.GetProperties()
+
+               dev = path[path.rfind("/") + 1:]
+
+               if "Name" not in properties.keys():
+                       continue
+
+               if dev == sys.argv[1] or properties["Name"] == sys.argv[1]:
+                       print "Disconnecting %s" % (path)
+                       network.Disconnect()
diff --git a/test/enable-device b/test/enable-device
new file mode 100755 (executable)
index 0000000..b5b1a9c
--- /dev/null
@@ -0,0 +1,28 @@
+#!/usr/bin/python
+
+import sys
+import dbus
+
+if (len(sys.argv) < 2):
+       print "Usage: %s <device>" % (sys.argv[0])
+       sys.exit(1)
+
+bus = dbus.SystemBus()
+
+manager = dbus.Interface(bus.get_object('org.moblin.connman', "/"),
+                                       'org.moblin.connman.Manager')
+
+properties = manager.GetProperties()
+
+for path in properties["Devices"]:
+       device = dbus.Interface(bus.get_object('org.moblin.connman', path),
+                                               'org.moblin.connman.Device')
+
+       properties = device.GetProperties()
+
+       if properties["Interface"] != sys.argv[1]:
+               continue;
+
+       print "Enabling device %s" % (path)
+
+       device.SetProperty("Powered", dbus.Boolean(1));
diff --git a/test/get-state b/test/get-state
new file mode 100755 (executable)
index 0000000..38c1dc9
--- /dev/null
@@ -0,0 +1,12 @@
+#!/usr/bin/python
+
+import dbus
+
+bus = dbus.SystemBus()
+
+manager = dbus.Interface(bus.get_object('org.moblin.connman', "/"),
+                                       'org.moblin.connman.Manager')
+
+properties = manager.GetProperties()
+
+print "System is %s" % (properties["State"])
diff --git a/test/join-network b/test/join-network
new file mode 100755 (executable)
index 0000000..b5f63c9
--- /dev/null
@@ -0,0 +1,42 @@
+#!/usr/bin/python
+
+import sys
+import dbus
+
+if (len(sys.argv) < 2):
+       print "Usage: %s <ssid> [passphrase] [security]" % (sys.argv[0])
+       sys.exit(1)
+
+bus = dbus.SystemBus()
+
+manager = dbus.Interface(bus.get_object("org.moblin.connman", "/"),
+                                       "org.moblin.connman.Manager")
+
+properties = manager.GetProperties()
+
+for path in properties["Devices"]:
+       device = dbus.Interface(bus.get_object("org.moblin.connman", path),
+                                               "org.moblin.connman.Device")
+
+       properties = device.GetProperties()
+
+       if (properties["Type"] != "wifi"):
+               continue;
+
+       print "[ %s ]" % (path)
+       print "Attempting to join %s" % (sys.argv[1])
+
+       if len(sys.argv) > 2:
+               if len(sys.argv) > 3:
+                       security = sys.argv[3]
+               else:
+                       security = "rsn"
+               passphrase = sys.argv[2]
+       else:
+               security = "none"
+               passphrase = ""
+
+       device.JoinNetwork({ "WiFi.Mode": "managed",
+                                       "WiFi.SSID": sys.argv[1],
+                                       "WiFi.Security": security,
+                                       "WiFi.Passphrase": passphrase })
diff --git a/test/list-connections b/test/list-connections
new file mode 100755 (executable)
index 0000000..655e50a
--- /dev/null
@@ -0,0 +1,27 @@
+#!/usr/bin/python
+
+import dbus
+
+bus = dbus.SystemBus()
+
+manager = dbus.Interface(bus.get_object("org.moblin.connman", "/"),
+                                       "org.moblin.connman.Manager")
+
+properties = manager.GetProperties()
+
+for path in properties["Connections"]:
+       connection = dbus.Interface(bus.get_object("org.moblin.connman", path),
+                                               "org.moblin.connman.Connection")
+
+       properties = connection.GetProperties()
+
+       print "[ %s ]" % (path)
+
+       for key in properties.keys():
+               if key in ["Strength", "Priority"]:
+                       val = int(properties[key])
+               else:
+                       val = str(properties[key])
+               print "    %s = %s" % (key, val)
+
+       print
diff --git a/test/list-devices b/test/list-devices
new file mode 100755 (executable)
index 0000000..691a6fb
--- /dev/null
@@ -0,0 +1,23 @@
+#!/usr/bin/python
+
+import dbus
+
+bus = dbus.SystemBus()
+
+manager = dbus.Interface(bus.get_object("org.moblin.connman", "/"),
+                                       "org.moblin.connman.Manager")
+
+properties = manager.GetProperties()
+
+for path in properties["Devices"]:
+       device = dbus.Interface(bus.get_object("org.moblin.connman", path),
+                                               "org.moblin.connman.Device")
+
+       properties = device.GetProperties()
+
+       print "[ %s ]" % (path)
+
+       for key in properties.keys():
+               print "    %s = %s" % (key, properties[key])
+
+       print
diff --git a/test/list-networks b/test/list-networks
new file mode 100755 (executable)
index 0000000..e8fb00a
--- /dev/null
@@ -0,0 +1,54 @@
+#!/usr/bin/python
+
+import dbus
+import string
+
+bus = dbus.SystemBus()
+
+manager = dbus.Interface(bus.get_object("org.moblin.connman", "/"),
+                                       "org.moblin.connman.Manager")
+
+properties = manager.GetProperties()
+
+def convert_ssid(ssid_list):
+       ssid = ""
+       for byte in ssid_list:
+               if (str(byte) in string.printable):
+                       ssid = ssid + str(byte)
+               else:
+                       ssid = ssid + "."
+       return ssid
+
+for path in properties["Devices"]:
+       device = dbus.Interface(bus.get_object("org.moblin.connman", path),
+                                               "org.moblin.connman.Device")
+
+       properties = device.GetProperties()
+
+       try:
+               if properties["Type"] not in ["wifi", "wimax",
+                                       "bluetooth", "cellular"]:
+                       continue
+       except:
+               continue
+
+       print "[ %s ]" % (path)
+
+       for path in properties["Networks"]:
+               network = dbus.Interface(bus.get_object("org.moblin.connman", path),
+                                               "org.moblin.connman.Network")
+
+               properties = network.GetProperties()
+
+               print "    [ %s ]" % (path)
+
+               for key in properties.keys():
+                       if key == "WiFi.SSID":
+                               ssid = convert_ssid(properties[key])
+                               print "        %s = [ %s ]" % (key, ssid)
+                       elif key in ["Strength", "Priority"]:
+                               print "        %s = %d" % (key, properties[key])
+                       else:
+                               print "        %s = %s" % (key, properties[key])
+
+       print
diff --git a/test/list-profiles b/test/list-profiles
new file mode 100755 (executable)
index 0000000..710a36c
--- /dev/null
@@ -0,0 +1,34 @@
+#!/usr/bin/python
+
+import dbus
+
+bus = dbus.SystemBus()
+
+manager = dbus.Interface(bus.get_object("org.moblin.connman", "/"),
+                                       "org.moblin.connman.Manager")
+
+properties = manager.GetProperties()
+
+active = properties["ActiveProfile"]
+
+for path in properties["Profiles"]:
+       if (active == path):
+               print "[ %s ]  <== active" % (path)
+       else:
+               print "[ %s ]" % (path)
+
+       profile = dbus.Interface(bus.get_object("org.moblin.connman", path),
+                                               "org.moblin.connman.Profile")
+
+       properties = profile.GetProperties()
+       for key in properties.keys():
+               if key in ["Services"]:
+                       list = ""
+                       for path in properties["Services"]:
+                               val = str(path)
+                               list = list + val[val.rfind("/") + 1:] + " "
+                       print "    Services = [ %s]" % (list)
+               else:
+                       print "    %s = %s" % (key, properties[key])
+
+       print
diff --git a/test/list-services b/test/list-services
new file mode 100755 (executable)
index 0000000..522e122
--- /dev/null
@@ -0,0 +1,27 @@
+#!/usr/bin/python
+
+import dbus
+
+bus = dbus.SystemBus()
+
+manager = dbus.Interface(bus.get_object("org.moblin.connman", "/"),
+                                       "org.moblin.connman.Manager")
+
+properties = manager.GetProperties()
+
+for path in properties["Services"]:
+       service = dbus.Interface(bus.get_object("org.moblin.connman", path),
+                                               "org.moblin.connman.Service")
+
+       properties = service.GetProperties()
+
+       print "[ %s ]" % (path)
+
+       for key in properties.keys():
+               if key in ["Strength"]:
+                       val = int(properties[key])
+               else:
+                       val = str(properties[key])
+               print "    %s = %s" % (key, val)
+
+       print
diff --git a/test/monitor-connman b/test/monitor-connman
new file mode 100755 (executable)
index 0000000..886373a
--- /dev/null
@@ -0,0 +1,28 @@
+#!/usr/bin/python
+
+import gobject
+
+import dbus
+import dbus.mainloop.glib
+
+def property_changed(name, value, path, interface):
+       iface = interface[interface.rfind(".") + 1:]
+       if name in ["Strength", "Priority"]:
+               val = int(value)
+       else:
+               val = str(value)
+       print "{%s} [%s] %s = %s" % (iface, path, name, val)
+
+if __name__ == '__main__':
+       dbus.mainloop.glib.DBusGMainLoop(set_as_default=True)
+
+       bus = dbus.SystemBus()
+
+       bus.add_signal_receiver(property_changed,
+                                       bus_name="org.moblin.connman",
+                                       signal_name = "PropertyChanged",
+                                               path_keyword="path",
+                                               interface_keyword="interface")
+
+       mainloop = gobject.MainLoop()
+       mainloop.run()
diff --git a/test/monitor-services b/test/monitor-services
new file mode 100755 (executable)
index 0000000..1d2b44e
--- /dev/null
@@ -0,0 +1,37 @@
+#!/usr/bin/python
+
+import gobject
+
+import dbus
+import dbus.mainloop.glib
+
+def property_changed(name, value, path, interface):
+       iface = interface[interface.rfind(".") + 1:]
+       ipath = path[path.rfind("/") + 1:]
+       if iface not in ["Service"]:
+               return
+       if name in ["Profiles", "Services",
+                               "Devices", "Networks", "Connections"]:
+               val = "["
+               for i in value:
+                       val = val + " " + i[i.rfind("/") + 1:]
+               val = val + " ]"
+       elif name in ["Strength", "Priority"]:
+               val = int(value)
+       else:
+               val = str(value)
+       print "{%s} [%s] %s = %s" % (iface, ipath, name, val)
+
+if __name__ == '__main__':
+       dbus.mainloop.glib.DBusGMainLoop(set_as_default=True)
+
+       bus = dbus.SystemBus()
+
+       bus.add_signal_receiver(property_changed,
+                                       bus_name="org.moblin.connman",
+                                       signal_name = "PropertyChanged",
+                                               path_keyword="path",
+                                               interface_keyword="interface")
+
+       mainloop = gobject.MainLoop()
+       mainloop.run()
diff --git a/test/select-connection b/test/select-connection
new file mode 100755 (executable)
index 0000000..34d1f93
--- /dev/null
@@ -0,0 +1,23 @@
+#!/usr/bin/python
+
+import dbus
+
+bus = dbus.SystemBus()
+
+manager = dbus.Interface(bus.get_object("org.moblin.connman", "/"),
+                                       "org.moblin.connman.Manager")
+
+properties = manager.GetProperties()
+
+for path in properties["Connections"]:
+       connection = dbus.Interface(bus.get_object("org.moblin.connman", path),
+                                               "org.moblin.connman.Connection")
+
+       properties = connection.GetProperties()
+
+       if (properties["Default"] == dbus.Boolean(1)):
+               continue
+
+       print "[ %s ]" % (path)
+
+       connection.SetProperty("Default", dbus.Boolean(1))
diff --git a/test/select-network b/test/select-network
new file mode 100755 (executable)
index 0000000..376d9d2
--- /dev/null
@@ -0,0 +1,42 @@
+#!/usr/bin/python
+
+import sys
+import dbus
+
+if (len(sys.argv) < 2):
+       print "Usage: %s <network>" % (sys.argv[0])
+       sys.exit(1)
+
+bus = dbus.SystemBus()
+
+manager = dbus.Interface(bus.get_object("org.moblin.connman", "/"),
+                                       "org.moblin.connman.Manager")
+
+properties = manager.GetProperties()
+
+for path in properties["Devices"]:
+       device = dbus.Interface(bus.get_object("org.moblin.connman", path),
+                                               "org.moblin.connman.Device")
+
+       properties = device.GetProperties()
+
+       if (properties["Type"] != "wifi" and properties["Type"] != "wimax"):
+               continue
+
+       print "[ %s ]" % (path)
+
+       for path in properties["Networks"]:
+               network = dbus.Interface(bus.get_object("org.moblin.connman", path),
+                                               "org.moblin.connman.Network")
+
+               properties = network.GetProperties()
+
+               if "Name" not in properties.keys():
+                       continue
+
+               if (properties["Connected"] == dbus.Boolean(1)):
+                       continue
+
+               if (properties["Name"] == sys.argv[1]):
+                       print "Connecting %s" % (path)
+                       network.Connect()
diff --git a/test/set-address b/test/set-address
new file mode 100755 (executable)
index 0000000..ebff30a
--- /dev/null
@@ -0,0 +1,26 @@
+#!/usr/bin/python
+
+import sys
+import dbus
+
+if (len(sys.argv) < 2):
+       print "Usage: %s <address>" % (sys.argv[0])
+       sys.exit(1)
+
+bus = dbus.SystemBus()
+
+manager = dbus.Interface(bus.get_object('org.moblin.connman', "/"),
+                                       'org.moblin.connman.Manager')
+
+properties = manager.GetProperties()
+
+for path in properties["Devices"]:
+       device = dbus.Interface(bus.get_object('org.moblin.connman', path),
+                                               'org.moblin.connman.Device')
+
+       print "Setting static address %s for %s" % (sys.argv[1], path)
+
+       device.SetProperty("IPv4.Method", "static")
+       device.SetProperty("IPv4.Address", sys.argv[1])
+
+       print
diff --git a/test/set-passphrase b/test/set-passphrase
new file mode 100755 (executable)
index 0000000..f80d754
--- /dev/null
@@ -0,0 +1,34 @@
+#!/usr/bin/python
+
+import sys
+import dbus
+
+if (len(sys.argv) < 3):
+       print "Usage: %s <network> <passphrase>" % (sys.argv[0])
+       sys.exit(1)
+
+bus = dbus.SystemBus()
+
+manager = dbus.Interface(bus.get_object("org.moblin.connman", "/"),
+                                       "org.moblin.connman.Manager")
+
+properties = manager.GetProperties()
+
+for path in properties["Devices"]:
+       device = dbus.Interface(bus.get_object("org.moblin.connman", path),
+                                               "org.moblin.connman.Device")
+
+       properties = device.GetProperties()
+
+       if (properties["Type"] != "wifi" and properties["Type"] != "wimax"):
+               continue;
+
+       for path in properties["Networks"]:
+               network = dbus.Interface(bus.get_object("org.moblin.connman", path),
+                                               "org.moblin.connman.Network")
+
+               properties = network.GetProperties()
+
+               if (properties["Name"] == sys.argv[1]):
+                       print "Setting passphrase for %s" % (path)
+                       network.SetProperty("WiFi.Passphrase", sys.argv[2])
diff --git a/test/set-policy b/test/set-policy
new file mode 100755 (executable)
index 0000000..5f26690
--- /dev/null
@@ -0,0 +1,28 @@
+#!/usr/bin/python
+
+import sys
+import dbus
+
+if (len(sys.argv) < 3):
+       print "Usage: %s <device> <policy>" % (sys.argv[0])
+       sys.exit(1)
+
+bus = dbus.SystemBus()
+
+manager = dbus.Interface(bus.get_object("org.moblin.connman", "/"),
+                                       "org.moblin.connman.Manager")
+
+properties = manager.GetProperties()
+
+for path in properties["Devices"]:
+       device = dbus.Interface(bus.get_object("org.moblin.connman", path),
+                                               "org.moblin.connman.Device")
+
+       properties = device.GetProperties()
+
+       if properties["Interface"] != sys.argv[1]:
+               continue;
+
+       print "Setting policy \"%s\" for %s" % (sys.argv[2], path)
+
+       device.SetProperty("Policy", sys.argv[2])
diff --git a/test/set-priority b/test/set-priority
new file mode 100755 (executable)
index 0000000..37a0e72
--- /dev/null
@@ -0,0 +1,28 @@
+#!/usr/bin/python
+
+import sys
+import dbus
+
+if (len(sys.argv) < 3):
+       print "Usage: %s <device> <priority>" % (sys.argv[0])
+       sys.exit(1)
+
+bus = dbus.SystemBus()
+
+manager = dbus.Interface(bus.get_object('org.moblin.connman', "/"),
+                                       'org.moblin.connman.Manager')
+
+properties = manager.GetProperties()
+
+for path in properties["Devices"]:
+       device = dbus.Interface(bus.get_object('org.moblin.connman', path),
+                                               'org.moblin.connman.Device')
+
+       properties = device.GetProperties()
+
+       if properties["Interface"] != sys.argv[1]:
+               continue;
+
+       print "Setting priority %d for device %s" % (int(sys.argv[2]), path)
+
+       device.SetProperty("Priority", dbus.Byte(int(sys.argv[2])));
diff --git a/test/show-introspection b/test/show-introspection
new file mode 100755 (executable)
index 0000000..bbf93c8
--- /dev/null
@@ -0,0 +1,21 @@
+#!/usr/bin/python
+
+import dbus
+
+bus = dbus.SystemBus()
+
+object = dbus.Interface(bus.get_object("org.moblin.connman", '/'),
+                               "org.freedesktop.DBus.Introspectable")
+
+print object.Introspect()
+
+manager = dbus.Interface(bus.get_object("org.moblin.connman", "/"),
+                                       "org.moblin.connman.Manager")
+
+properties = manager.GetProperties()
+
+for path in properties["Devices"]:
+       object = dbus.Interface(bus.get_object("org.moblin.connman", path),
+                                       "org.freedesktop.DBus.Introspectable")
+
+       print object.Introspect()
diff --git a/test/simple-agent b/test/simple-agent
new file mode 100755 (executable)
index 0000000..cb3a675
--- /dev/null
@@ -0,0 +1,31 @@
+#!/usr/bin/python
+
+import gobject
+
+import dbus
+import dbus.service
+import dbus.mainloop.glib
+
+class Agent(dbus.service.Object):
+       @dbus.service.method("org.moblin.connman.Agent",
+                                       in_signature='', out_signature='')
+       def Release(self):
+               print("Release")
+               mainloop.quit()
+
+if __name__ == '__main__':
+       dbus.mainloop.glib.DBusGMainLoop(set_as_default=True)
+
+       bus = dbus.SystemBus()
+       manager = dbus.Interface(bus.get_object('org.moblin.connman', "/"),
+                                       'org.moblin.connman.Manager')
+
+       path = "/test/agent"
+       object = Agent(bus, path)
+
+       manager.RegisterAgent(path)
+
+       mainloop = gobject.MainLoop()
+       mainloop.run()
+
+       #manager.UnregisterAgent(path)
diff --git a/test/start-scanning b/test/start-scanning
new file mode 100755 (executable)
index 0000000..cf36477
--- /dev/null
@@ -0,0 +1,26 @@
+#!/usr/bin/python
+
+import dbus
+
+bus = dbus.SystemBus()
+
+manager = dbus.Interface(bus.get_object('org.moblin.connman', "/"),
+                                       'org.moblin.connman.Manager')
+
+properties = manager.GetProperties()
+
+for path in properties["Devices"]:
+       device = dbus.Interface(bus.get_object('org.moblin.connman', path),
+                                               'org.moblin.connman.Device')
+
+       properties = device.GetProperties()
+
+       print "[ %s ]" % (path)
+
+       if properties["Type"] in ["wifi", "wimax"]:
+               print "   Started scanning"
+               device.ProposeScan()
+       else:
+               print "   No scanning"
+
+       print
diff --git a/test/test-compat b/test/test-compat
new file mode 100755 (executable)
index 0000000..cd1ca7a
--- /dev/null
@@ -0,0 +1,15 @@
+#!/usr/bin/python
+
+import dbus
+
+bus = dbus.SystemBus()
+
+manager = dbus.Interface(bus.get_object('org.freedesktop.NetworkManager',
+                               '/org/freedesktop/NetworkManager'),
+                                       'org.freedesktop.NetworkManager')
+
+states = [ "unknown", "asleep", "connecting", "connected", "disconnected" ]
+
+state = manager.state()
+
+print "System is %s" % (states[state])
diff --git a/test/test-connman b/test/test-connman
new file mode 100755 (executable)
index 0000000..81dca91
--- /dev/null
@@ -0,0 +1,383 @@
+#!/usr/bin/python
+
+import sys
+import dbus
+
+bus = dbus.SystemBus()
+
+manager = dbus.Interface(bus.get_object("org.moblin.connman", "/"),
+                                       "org.moblin.connman.Manager")
+
+if len(sys.argv) < 2:
+       print "Usage: %s <command>" % (sys.argv[0])
+       print ""
+       print "  state"
+       print "  services"
+       print "  passphrase <service> [passphrase]"
+       print "  connect <service>"
+       print "  disconnect <service>"
+       print "  remove <service>"
+       print ""
+       print "  scan [ <interface> ]"
+       print "  dev <interface>"
+       print "  dev <interface> scan"
+       print "  dev <interface> networks"
+       print "  dev <interface> connect <network>"
+       print "  dev <interface> remember <network>"
+       print "  dev <interface> disconnect [network]"
+       print "  dev <interface> policy [ignore|off|auto|manual]"
+       print "  dev <interface> powered [on|off]"
+       print "  dev <interface> priority [0-255]"
+       sys.exit(1)
+
+def print_properties(path, properties):
+       print "[ %s ]" % (path)
+       for key in properties.keys():
+               if key == "Networks":
+                       continue
+
+               if key in ["Powered", "Scanning", "Connected",
+                                       "Available", "Remember", "Default"]:
+                       if properties[key] == dbus.Boolean(1):
+                               val = "true"
+                       else:
+                               val = "false"
+               elif key in ["Strength", "Priority"]:
+                       val = int(properties[key])
+               else:
+                       val = str(properties[key])
+
+               print "     %s = %s" % (key, val)
+
+       if "Networks" in properties.keys():
+               list = ""
+               for path in properties["Networks"]:
+                       val = str(path)
+                       list = list + val[val.rfind("/") + 1:] + " "
+               print "     Networks = [ %s]" % (list)
+
+def print_networks(networks):
+       for path in networks:
+               network = dbus.Interface(bus.get_object("org.moblin.connman", path),
+                                               "org.moblin.connman.Network")
+
+               properties = network.GetProperties()
+
+               if properties["Connected"] == dbus.Boolean(1):
+                       connected = "*"
+               else:
+                       connected = " "
+
+               if "Name" in properties.keys():
+                       name = properties["Name"]
+               else:
+                       name = ""
+
+               strength = int(properties["Strength"])
+
+               details = ""
+               try:
+                       details += "{" + properties["WiFi.Mode"] + "} "
+               except:
+                       pass
+               try:
+                       details += "{" + properties["WiFi.Security"] + "} "
+               except:
+                       pass
+               if "WiFi.Passphrase" in properties.keys():
+                       if properties["WiFi.Passphrase"] != "":
+                               details += "{passphrase present}"
+
+               print "%s %-26s %3d%%  %s" % (connected,
+                                               name, strength, details)
+
+def select_network(networks, name):
+       for path in networks:
+               network = dbus.Interface(bus.get_object("org.moblin.connman", path),
+                                               "org.moblin.connman.Network")
+
+               properties = network.GetProperties()
+
+               if properties["Name"] != name:
+                       continue
+
+               if properties["Connected"] == dbus.Boolean(1):
+                       print "Already connected to network %s" % (name)
+                       break
+
+               print "Selecting network %s" % (name)
+
+               network.Connect()
+
+def remember_network(networks, name):
+       for path in networks:
+               network = dbus.Interface(bus.get_object("org.moblin.connman", path),
+                                               "org.moblin.connman.Network")
+
+               properties = network.GetProperties()
+
+               if properties["Name"] != name:
+                       continue
+
+               if properties["Remember"] == dbus.Boolean(1):
+                       print "Already a known network %s" % (name)
+                       break
+
+               print "Remembering network %s" % (name)
+
+               network.SetProperty("Remember", dbus.Boolean(1))
+
+def disconnect_network(networks, name):
+       for path in networks:
+               network = dbus.Interface(bus.get_object("org.moblin.connman", path),
+                                               "org.moblin.connman.Network")
+
+               properties = network.GetProperties()
+
+               if name != "" and properties["Name"] != name:
+                       continue
+
+               if properties["Connected"] == dbus.Boolean(1):
+                       name = properties["Name"]
+                       print "Disconnecting from network %s" % (name)
+                       network.Disconnect()
+
+def print_services(services):
+       for path in services:
+               service = dbus.Interface(bus.get_object("org.moblin.connman", path),
+                                               "org.moblin.connman.Service")
+
+               properties = service.GetProperties()
+
+               identifier = path[path.rfind("/") + 1:]
+
+               if properties["Favorite"] == dbus.Boolean(1):
+                       favorite = "*"
+               else:
+                       favorite = " "
+
+               if "Name" in properties.keys():
+                       name = properties["Name"]
+               else:
+                       name = "{" + properties["Type"] + "}"
+
+               print "%s %-26s { %s }" % (favorite, name, identifier)
+
+if sys.argv[1] == "state":
+       properties = manager.GetProperties()
+
+       print "System is %s" % (properties["State"])
+
+elif sys.argv[1] in ["services", "list", "show"]:
+       properties = manager.GetProperties()
+
+       print_services(properties["Services"])
+
+elif sys.argv[1] in ["passphrase", "pass"]:
+       if (len(sys.argv) < 3):
+               print "Need at least service parameter"
+               sys.exit(1)
+
+       path = "/profile/default/" + sys.argv[2]
+
+       service = dbus.Interface(bus.get_object("org.moblin.connman", path),
+                                               "org.moblin.connman.Service")
+
+       if (len(sys.argv) > 3):
+               passphrase = sys.argv[3]
+
+               service.SetProperty("Passphrase", passphrase);
+
+               print "Passphrase %s set for %s" % (passphrase, sys.argv[2])
+       else:
+               properties = service.GetProperties()
+
+               if "Name" in properties.keys():
+                       name = properties["Name"]
+               else:
+                       name = "{" + properties["Type"] + "}"
+
+               if "Passphrase" in properties.keys():
+                       passphrase = properties["Passphrase"]
+               else:
+                       passphrase = "not set"
+
+               print "Passphrase for %s is %s" % (name, passphrase)
+
+elif sys.argv[1] in ["connect", "conn"]:
+       if (len(sys.argv) < 3):
+               print "Need at least service parameter"
+               sys.exit(1)
+
+       path = "/profile/default/" + sys.argv[2]
+
+       service = dbus.Interface(bus.get_object("org.moblin.connman", path),
+                                               "org.moblin.connman.Service")
+
+       try:
+               service.Connect(timeout=60000)
+       except dbus.DBusException, error:
+               print "%s: %s" % (error._dbus_error_name, error.message)
+
+elif sys.argv[1] in ["disconnect", "disc"]:
+       if (len(sys.argv) < 3):
+               print "Need at least service parameter"
+               sys.exit(1)
+
+       path = "/profile/default/" + sys.argv[2]
+
+       service = dbus.Interface(bus.get_object("org.moblin.connman", path),
+                                               "org.moblin.connman.Service")
+
+       try:
+               service.Disconnect()
+       except dbus.DBusException, error:
+               print "%s: %s" % (error._dbus_error_name, error.message)
+
+elif sys.argv[1] in ["remove"]:
+       if (len(sys.argv) < 3):
+               print "Need at least service parameter"
+               sys.exit(1)
+
+       path = "/profile/default/" + sys.argv[2]
+
+       service = dbus.Interface(bus.get_object("org.moblin.connman", path),
+                                               "org.moblin.connman.Service")
+
+       properties = service.GetProperties()
+
+       if properties["Favorite"] == dbus.Boolean(0):
+               print "Only favorite services can be removed"
+               sys.exit(1)
+
+       try:
+               service.Remove()
+       except dbus.DBusException, error:
+               print "%s: %s" % (error._dbus_error_name, error.message)
+
+elif sys.argv[1] == "scan":
+       properties = manager.GetProperties()
+
+       interface = ""
+       found = 0
+
+       if len(sys.argv) > 2:
+               interface = sys.argv[2]
+
+       for path in properties["Devices"]:
+               device = dbus.Interface(bus.get_object("org.moblin.connman", path),
+                                               "org.moblin.connman.Device")
+
+               properties = device.GetProperties()
+
+               if interface != "" and properties["Interface"] != interface:
+                       continue
+
+               if properties["Type"] in ["wifi", "wimax"]:
+                       interface = properties["Interface"]
+                       print "Propose scanning for device %s" % (interface)
+                       device.ProposeScan()
+                       found = 1
+               elif interface != "":
+                       print "No scanning support for device %s" % (interface)
+                       found = 1
+
+       if found == 0:
+               print "No such device"
+
+elif sys.argv[1] == "dev":
+       properties = manager.GetProperties()
+
+       interface = ""
+       command = ""
+       value = ""
+       found = 0
+
+       if len(sys.argv) > 2:
+               interface = sys.argv[2]
+               if len(sys.argv) > 3:
+                       command = sys.argv[3]
+                       if len(sys.argv) > 4:
+                               value = sys.argv[4]
+
+       for path in properties["Devices"]:
+               device = dbus.Interface(bus.get_object("org.moblin.connman", path),
+                                               "org.moblin.connman.Device")
+
+               properties = device.GetProperties()
+
+               if interface != "" and properties["Interface"] != interface:
+                       continue
+
+               if command == "scan":
+                       if properties["Type"] in ["wifi", "wimax"]:
+                               interface = properties["Interface"]
+                               print "Scan for device %s" % (interface)
+                               device.ProposeScan()
+                       else:
+                               print "No scanning for device %s" % (interface)
+               elif command in ["networks", "net"]:
+                       if "Networks" in properties.keys():
+                               print_networks(properties["Networks"])
+                       else:
+                               print "Device has no networks"
+               elif command in ["connect", "conn"] and value != "":
+                       if "Networks" in properties.keys():
+                               select_network(properties["Networks"], value)
+                       else:
+                               print "Device can't connect networks"
+               elif command in ["connect", "conn"]:
+                       print "Need to specify network"
+               elif command in ["remember", "known"] and value != "":
+                       if "Networks" in properties.keys():
+                               remember_network(properties["Networks"], value)
+                       else:
+                               print "Device has no networks"
+               elif command in ["remember", "known"]:
+                       print "Need to specify network"
+               elif command in ["disconnect", "disc"] and value != "":
+                       if "Networks" in properties.keys():
+                               disconnect_network(properties["Networks"], value)
+                       else:
+                               print "Device has no networks"
+               elif command in ["discconnect", "disc"]:
+                       if "Networks" in properties.keys():
+                               disconnect_network(properties["Networks"], "")
+                       else:
+                               print "Device has no networks"
+               elif command == "policy" and value != "":
+                       policy = value
+                       device.SetProperty("Policy", policy)
+               elif command == "policy":
+                       interface = properties["Interface"]
+                       policy = properties["Policy"]
+                       print "Policy of device %s is %s" % (interface, policy)
+               elif command == "powered" and value != "":
+                       if value == "on":
+                               powered = dbus.Boolean(1)
+                       elif value == "off":
+                               powered = dbus.Boolean(0)
+                       else:
+                               powered = dbus.Boolean(value)
+                       device.SetProperty("Powered", powered)
+               elif command == "powered":
+                       interface = properties["Interface"]
+                       if properties["Powered"] == dbus.Boolean(1):
+                               powered = "on"
+                       else:
+                               powered = "off"
+                       print "Device %s is powered %s" % (interface, powered)
+               elif command == "priority" and value != "":
+                       priority = int(value)
+                       device.SetProperty("Priority", priority)
+               elif command == "priority":
+                       interface = properties["Interface"]
+                       priority = properties["Priority"]
+                       print "Device %s has priority of %d" % (interface, priority)
+               elif command == "list" or command == "":
+                       print_properties(path, properties)
+               else:
+                       print "Unknown command"
+
+else:
+       print "Unknown command"
diff --git a/test/test-manager b/test/test-manager
new file mode 100755 (executable)
index 0000000..28a20de
--- /dev/null
@@ -0,0 +1,73 @@
+#!/usr/bin/python
+
+import dbus
+
+bus = dbus.SystemBus()
+
+manager = dbus.Interface(bus.get_object("org.moblin.connman", "/"),
+                                       "org.moblin.connman.Manager")
+
+properties = manager.GetProperties()
+
+def print_properties(key, value):
+       if key == "Profiles":
+               interface = "org.moblin.connman.Profile"
+       elif key == "Devices":
+               interface = "org.moblin.connman.Device"
+       elif key == "Connections":
+               interface = "org.moblin.connman.Connection"
+       elif key == "Services":
+               interface = "org.moblin.connman.Service"
+       else:
+               return
+
+       print "%s" % (key)
+       for path in value:
+               print "    %s" % (path)
+               obj = dbus.Interface(bus.get_object("org.moblin.connman", path),
+                                                               interface)
+
+               properties = obj.GetProperties()
+
+               for key in properties.keys():
+                       if key in ["Networks", "Services"]:
+                               continue
+
+                       if key in ["Powered", "Scanning", "Connected",
+                                       "Available", "Remember", "Default"]:
+                               if properties[key] == dbus.Boolean(1):
+                                       val = "true"
+                               else:
+                                       val = "false"
+                       elif key in ["Strength", "Priority"]:
+                               val = int(properties[key])
+                       else:
+                               val = str(properties[key])
+
+                       print "        %s = %s" % (key, val)
+
+               if "Networks" in properties.keys():
+                       list = ""
+                       for path in properties["Networks"]:
+                               val = str(path)
+                               list = list + val[val.rfind("/") + 1:] + " "
+                       print "        Networks = [ %s]" % (list)
+               if "Services" in properties.keys():
+                       list = ""
+                       for path in properties["Services"]:
+                               val = str(path)
+                               list = list + val[val.rfind("/") + 1:] + " "
+                       print "        Services = [ %s]" % (list)
+
+for key in properties.keys():
+       if key in ["Profiles", "Devices", "Connections", "Services"]:
+               print_properties(key, properties[key])
+       elif key in ["OfflineMode"]:
+               print "%s" % (key)
+               if properties[key] == dbus.Boolean(1):
+                       print "    true"
+               else:
+                       print "    false"
+       else:
+               print "%s" % (key)
+               print "    %s" % (properties[key])
diff --git a/test/test-supplicant b/test/test-supplicant
new file mode 100755 (executable)
index 0000000..e79228d
--- /dev/null
@@ -0,0 +1,59 @@
+#!/usr/bin/python
+
+import dbus
+import time
+
+WPA_NAME='fi.epitest.hostap.WPASupplicant'
+WPA_INTF='fi.epitest.hostap.WPASupplicant'
+WPA_PATH='/fi/epitest/hostap/WPASupplicant'
+
+bus = dbus.SystemBus()
+
+dummy = dbus.Interface(bus.get_object(WPA_NAME, WPA_PATH),
+                               'org.freedesktop.DBus.Introspectable')
+
+#print dummy.Introspect()
+
+manager = dbus.Interface(bus.get_object(WPA_NAME, WPA_PATH), WPA_INTF)
+
+try:
+       path = manager.getInterface("wlan0")
+except:
+       path = manager.addInterface("wlan0")
+
+interface = dbus.Interface(bus.get_object(WPA_NAME, path),
+                                       WPA_INTF + ".Interface")
+
+print interface.state()
+
+print interface.scan()
+
+print "[ %s ]" % (path)
+
+capabilities = interface.capabilities()
+
+for key in capabilities.keys():
+       list = ""
+       for value in capabilities[key]:
+               list += " " + value
+       print "    %s =%s" % (key, list)
+
+time.sleep(2)
+
+print interface.state()
+
+results = interface.scanResults()
+
+print results
+
+path = results[0]
+
+print "[ %s ]" % (path)
+
+bssid = dbus.Interface(bus.get_object(WPA_NAME, path),
+                                       WPA_INTF + ".BSSID")
+
+properties = bssid.properties()
+
+for key in properties.keys():
+       print "    %s = %s" % (key, properties[key])
diff --git a/tools/Makefile.am b/tools/Makefile.am
new file mode 100644 (file)
index 0000000..af219ac
--- /dev/null
@@ -0,0 +1,10 @@
+
+if TOOLS
+noinst_PROGRAMS = wifi-scan
+
+wifi_scan_LDADD = @GLIB_LIBS@ @NETLINK_LIBS@
+endif
+
+AM_CFLAGS = @NETLINK_CFLAGS@ @GLIB_CFLAGS@
+
+MAINTAINERCLEANFILES = Makefile.in
diff --git a/tools/wifi-scan.c b/tools/wifi-scan.c
new file mode 100644 (file)
index 0000000..6f110c1
--- /dev/null
@@ -0,0 +1,29 @@
+/*
+ *
+ *  Connection Manager
+ *
+ *  Copyright (C) 2007-2009  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
+
+int main(int argc, char *argv[])
+{
+       return 0;
+}