diff options
author | Marcel Holtmann <marcel@holtmann.org> | 2011-04-07 17:35:42 +0200 |
---|---|---|
committer | Marcel Holtmann <marcel@holtmann.org> | 2011-10-20 23:12:22 -0700 |
commit | 691a38d5a9ff5529c7b30aba878a60598e68b9c9 (patch) | |
tree | e42009b3ccd2a6a0fa766eaa0e9f1b1412b47fa6 | |
download | neard-691a38d5a9ff5529c7b30aba878a60598e68b9c9.tar.gz neard-691a38d5a9ff5529c7b30aba878a60598e68b9c9.tar.bz2 neard-691a38d5a9ff5529c7b30aba878a60598e68b9c9.zip |
Initial revision
-rw-r--r-- | AUTHORS | 2 | ||||
-rw-r--r-- | COPYING | 340 | ||||
-rw-r--r-- | ChangeLog | 0 | ||||
-rw-r--r-- | HACKING | 87 | ||||
-rw-r--r-- | INSTALL | 236 | ||||
-rw-r--r-- | Makefile.am | 58 | ||||
-rw-r--r-- | NEWS | 0 | ||||
-rw-r--r-- | README | 21 | ||||
-rw-r--r-- | TODO | 10 | ||||
-rw-r--r-- | acinclude.m4 | 27 | ||||
-rwxr-xr-x | bootstrap | 7 | ||||
-rwxr-xr-x | bootstrap-configure | 10 | ||||
-rw-r--r-- | configure.ac | 76 | ||||
-rw-r--r-- | doc/adapter-api.txt | 64 | ||||
-rw-r--r-- | doc/manager-api.txt | 40 | ||||
-rw-r--r-- | doc/target-api.txt | 38 | ||||
-rw-r--r-- | gdbus/gdbus.h | 177 | ||||
-rw-r--r-- | gdbus/mainloop.c | 383 | ||||
-rw-r--r-- | gdbus/object.c | 868 | ||||
-rw-r--r-- | gdbus/polkit.c | 202 | ||||
-rw-r--r-- | gdbus/watch.c | 748 | ||||
-rw-r--r-- | include/dbus.h | 132 | ||||
-rw-r--r-- | src/adapter.c | 62 | ||||
-rw-r--r-- | src/dbus.c | 294 | ||||
-rw-r--r-- | src/error.c | 185 | ||||
-rwxr-xr-x | src/genbuiltin | 17 | ||||
-rw-r--r-- | src/log.c | 138 | ||||
-rw-r--r-- | src/log.h | 47 | ||||
-rw-r--r-- | src/main.c | 160 | ||||
-rw-r--r-- | src/manager.c | 101 | ||||
-rw-r--r-- | src/near.h | 60 | ||||
-rw-r--r-- | src/types.h | 31 |
32 files changed, 4621 insertions, 0 deletions
@@ -0,0 +1,2 @@ +Samuel Ortiz <sameo@linux.intel.com> +Marcel Holtmann <marcel@holtmann.org> @@ -0,0 +1,340 @@ + GNU GENERAL PUBLIC LICENSE + Version 2, June 1991 + + Copyright (C) 1989, 1991 Free Software Foundation, Inc. + 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +License is intended to guarantee your freedom to share and change free +software--to make sure the software is free for all its users. This +General Public License applies to most of the Free Software +Foundation's software and to any other program whose authors commit to +using it. (Some other Free Software Foundation software is covered by +the GNU Library General Public License instead.) You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +this service if you wish), that you receive source code or can get it +if you want it, that you can change the software or use pieces of it +in new free programs; and that you know you can do these things. + + To protect your rights, we need to make restrictions that forbid +anyone to deny you these rights or to ask you to surrender the rights. +These restrictions translate to certain responsibilities for you if you +distribute copies of the software, or if you modify it. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must give the recipients all the rights that +you have. You must make sure that they, too, receive or can get the +source code. And you must show them these terms so they know their +rights. + + We protect your rights with two steps: (1) copyright the software, and +(2) offer you this license which gives you legal permission to copy, +distribute and/or modify the software. + + Also, for each author's protection and ours, we want to make certain +that everyone understands that there is no warranty for this free +software. If the software is modified by someone else and passed on, we +want its recipients to know that what they have is not the original, so +that any problems introduced by others will not reflect on the original +authors' reputations. + + Finally, any free program is threatened constantly by software +patents. We wish to avoid the danger that redistributors of a free +program will individually obtain patent licenses, in effect making the +program proprietary. To prevent this, we have made it clear that any +patent must be licensed for everyone's free use or not licensed at all. + + The precise terms and conditions for copying, distribution and +modification follow. + + GNU GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License applies to any program or other work which contains +a notice placed by the copyright holder saying it may be distributed +under the terms of this General Public License. The "Program", below, +refers to any such program or work, and a "work based on the Program" +means either the Program or any derivative work under copyright law: +that is to say, a work containing the Program or a portion of it, +either verbatim or with modifications and/or translated into another +language. (Hereinafter, translation is included without limitation in +the term "modification".) Each licensee is addressed as "you". + +Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running the Program is not restricted, and the output from the Program +is covered only if its contents constitute a work based on the +Program (independent of having been made by running the Program). +Whether that is true depends on what the Program does. + + 1. You may copy and distribute verbatim copies of the Program's +source code as you receive it, in any medium, provided that you +conspicuously and appropriately publish on each copy an appropriate +copyright notice and disclaimer of warranty; keep intact all the +notices that refer to this License and to the absence of any warranty; +and give any other recipients of the Program a copy of this License +along with the Program. + +You may charge a fee for the physical act of transferring a copy, and +you may at your option offer warranty protection in exchange for a fee. + + 2. You may modify your copy or copies of the Program or any portion +of it, thus forming a work based on the Program, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) You must cause the modified files to carry prominent notices + stating that you changed the files and the date of any change. + + b) You must cause any work that you distribute or publish, that in + whole or in part contains or is derived from the Program or any + part thereof, to be licensed as a whole at no charge to all third + parties under the terms of this License. + + c) If the modified program normally reads commands interactively + when run, you must cause it, when started running for such + interactive use in the most ordinary way, to print or display an + announcement including an appropriate copyright notice and a + notice that there is no warranty (or else, saying that you provide + a warranty) and that users may redistribute the program under + these conditions, and telling the user how to view a copy of this + License. (Exception: if the Program itself is interactive but + does not normally print such an announcement, your work based on + the Program is not required to print an announcement.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Program, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Program, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Program. + +In addition, mere aggregation of another work not based on the Program +with the Program (or with a work based on the Program) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may copy and distribute the Program (or a work based on it, +under Section 2) in object code or executable form under the terms of +Sections 1 and 2 above provided that you also do one of the following: + + a) Accompany it with the complete corresponding machine-readable + source code, which must be distributed under the terms of Sections + 1 and 2 above on a medium customarily used for software interchange; or, + + b) Accompany it with a written offer, valid for at least three + years, to give any third party, for a charge no more than your + cost of physically performing source distribution, a complete + machine-readable copy of the corresponding source code, to be + distributed under the terms of Sections 1 and 2 above on a medium + customarily used for software interchange; or, + + c) Accompany it with the information you received as to the offer + to distribute corresponding source code. (This alternative is + allowed only for noncommercial distribution and only if you + received the program in object code or executable form with such + an offer, in accord with Subsection b above.) + +The source code for a work means the preferred form of the work for +making modifications to it. For an executable work, complete source +code means all the source code for all modules it contains, plus any +associated interface definition files, plus the scripts used to +control compilation and installation of the executable. However, as a +special exception, the source code distributed need not include +anything that is normally distributed (in either source or binary +form) with the major components (compiler, kernel, and so on) of the +operating system on which the executable runs, unless that component +itself accompanies the executable. + +If distribution of executable or object code is made by offering +access to copy from a designated place, then offering equivalent +access to copy the source code from the same place counts as +distribution of the source code, even though third parties are not +compelled to copy the source along with the object code. + + 4. You may not copy, modify, sublicense, or distribute the Program +except as expressly provided under this License. Any attempt +otherwise to copy, modify, sublicense or distribute the Program is +void, and will automatically terminate your rights under this License. +However, parties who have received copies, or rights, from you under +this License will not have their licenses terminated so long as such +parties remain in full compliance. + + 5. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Program or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Program (or any work based on the +Program), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Program or works based on it. + + 6. Each time you redistribute the Program (or any work based on the +Program), the recipient automatically receives a license from the +original licensor to copy, distribute or modify the Program subject to +these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties to +this License. + + 7. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Program at all. For example, if a patent +license would not permit royalty-free redistribution of the Program by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Program. + +If any portion of this section is held invalid or unenforceable under +any particular circumstance, the balance of the section is intended to +apply and the section as a whole is intended to apply in other +circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system, which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 8. If the distribution and/or use of the Program is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Program under this License +may add an explicit geographical distribution limitation excluding +those countries, so that distribution is permitted only in or among +countries not thus excluded. In such case, this License incorporates +the limitation as if written in the body of this License. + + 9. The Free Software Foundation may publish revised and/or new versions +of the General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + +Each version is given a distinguishing version number. If the Program +specifies a version number of this License which applies to it and "any +later version", you have the option of following the terms and conditions +either of that version or of any later version published by the Free +Software Foundation. If the Program does not specify a version number of +this License, you may choose any version ever published by the Free Software +Foundation. + + 10. If you wish to incorporate parts of the Program into other free +programs whose distribution conditions are different, write to the author +to ask for permission. For software which is copyrighted by the Free +Software Foundation, write to the Free Software Foundation; we sometimes +make exceptions for this. Our decision will be guided by the two goals +of preserving the free status of all derivatives of our free software and +of promoting the sharing and reuse of software generally. + + NO WARRANTY + + 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY +FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN +OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES +PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED +OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS +TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE +PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, +REPAIR OR CORRECTION. + + 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR +REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, +INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING +OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED +TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY +YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER +PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE +POSSIBILITY OF SUCH DAMAGES. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +convey the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + <one line to give the program's name and a brief idea of what it does.> + Copyright (C) <year> <name of author> + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + + +Also add information on how to contact you by electronic and paper mail. + +If the program is interactive, make it output a short notice like this +when it starts in an interactive mode: + + Gnomovision version 69, Copyright (C) year name of author + Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, the commands you use may +be called something other than `show w' and `show c'; they could even be +mouse-clicks or menu items--whatever suits your program. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the program, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the program + `Gnomovision' (which makes passes at compilers) written by James Hacker. + + <signature of Ty Coon>, 1 April 1989 + Ty Coon, President of Vice + +This General Public License does not permit incorporating your program into +proprietary programs. If your program is a subroutine library, you may +consider it more useful to permit linking proprietary applications with the +library. If this is what you want to do, use the GNU Library General +Public License instead of this License. diff --git a/ChangeLog b/ChangeLog new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/ChangeLog @@ -0,0 +1,87 @@ +Hacking on Near Field Communication 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/.../neard.git + # cd neard + + 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 "neard". The "bootstrap-configure" +automatically includes this option. + + Run daemon in foreground with debugging + # ./src/neard -n -d 'src/*' + +For production installations or distribution packaging it is important that +the "--enable-maintainer-mode" option is NOT used. + +The debugging option -d takes an argument. This argument can be a comma +separated list of file names like 'src/main.c,src/manager.c' to enable debugs +in these files. Simple glob style pattern matching is supported in this list. @@ -0,0 +1,236 @@ +Installation Instructions +************************* + +Copyright (C) 1994, 1995, 1996, 1999, 2000, 2001, 2002, 2004, 2005 Free +Software Foundation, Inc. + +This file is free documentation; the Free Software Foundation gives +unlimited permission to copy, distribute and modify it. + +Basic Installation +================== + +These are generic installation instructions. + + The `configure' shell script attempts to guess correct values for +various system-dependent variables used during compilation. It uses +those values to create a `Makefile' in each directory of the package. +It may also create one or more `.h' files containing system-dependent +definitions. Finally, it creates a shell script `config.status' that +you can run in the future to recreate the current configuration, and a +file `config.log' containing compiler output (useful mainly for +debugging `configure'). + + It can also use an optional file (typically called `config.cache' +and enabled with `--cache-file=config.cache' or simply `-C') that saves +the results of its tests to speed up reconfiguring. (Caching is +disabled by default to prevent problems with accidental use of stale +cache files.) + + If you need to do unusual things to compile the package, please try +to figure out how `configure' could check whether to do them, and mail +diffs or instructions to the address given in the `README' so they can +be considered for the next release. If you are using the cache, and at +some point `config.cache' contains results you don't want to keep, you +may remove or edit it. + + The file `configure.ac' (or `configure.in') is used to create +`configure' by a program called `autoconf'. You only need +`configure.ac' if you want to change it or regenerate `configure' using +a newer version of `autoconf'. + +The simplest way to compile this package is: + + 1. `cd' to the directory containing the package's source code and type + `./configure' to configure the package for your system. If you're + using `csh' on an old version of System V, you might need to type + `sh ./configure' instead to prevent `csh' from trying to execute + `configure' itself. + + Running `configure' takes awhile. While running, it prints some + messages telling which features it is checking for. + + 2. Type `make' to compile the package. + + 3. Optionally, type `make check' to run any self-tests that come with + the package. + + 4. Type `make install' to install the programs and any data files and + documentation. + + 5. You can remove the program binaries and object files from the + source code directory by typing `make clean'. To also remove the + files that `configure' created (so you can compile the package for + a different kind of computer), type `make distclean'. There is + also a `make maintainer-clean' target, but that is intended mainly + for the package's developers. If you use it, you may have to get + all sorts of other programs in order to regenerate files that came + with the distribution. + +Compilers and Options +===================== + +Some systems require unusual options for compilation or linking that the +`configure' script does not know about. Run `./configure --help' for +details on some of the pertinent environment variables. + + You can give `configure' initial values for configuration parameters +by setting variables in the command line or in the environment. Here +is an example: + + ./configure CC=c89 CFLAGS=-O2 LIBS=-lposix + + *Note Defining Variables::, for more details. + +Compiling For Multiple Architectures +==================================== + +You can compile the package for more than one kind of computer at the +same time, by placing the object files for each architecture in their +own directory. To do this, you must use a version of `make' that +supports the `VPATH' variable, such as GNU `make'. `cd' to the +directory where you want the object files and executables to go and run +the `configure' script. `configure' automatically checks for the +source code in the directory that `configure' is in and in `..'. + + If you have to use a `make' that does not support the `VPATH' +variable, you have to compile the package for one architecture at a +time in the source code directory. After you have installed the +package for one architecture, use `make distclean' before reconfiguring +for another architecture. + +Installation Names +================== + +By default, `make install' will install the package's files in +`/usr/local/bin', `/usr/local/man', etc. You can specify an +installation prefix other than `/usr/local' by giving `configure' the +option `--prefix=PREFIX'. + + You can specify separate installation prefixes for +architecture-specific files and architecture-independent files. If you +give `configure' the option `--exec-prefix=PREFIX', the package will +use PREFIX as the prefix for installing programs and libraries. +Documentation and other data files will still use the regular prefix. + + In addition, if you use an unusual directory layout you can give +options like `--bindir=DIR' to specify different values for particular +kinds of files. Run `configure --help' for a list of the directories +you can set and what kinds of files go in them. + + If the package supports it, you can cause programs to be installed +with an extra prefix or suffix on their names by giving `configure' the +option `--program-prefix=PREFIX' or `--program-suffix=SUFFIX'. + +Optional Features +================= + +Some packages pay attention to `--enable-FEATURE' options to +`configure', where FEATURE indicates an optional part of the package. +They may also pay attention to `--with-PACKAGE' options, where PACKAGE +is something like `gnu-as' or `x' (for the X Window System). The +`README' should mention any `--enable-' and `--with-' options that the +package recognizes. + + For packages that use the X Window System, `configure' can usually +find the X include and library files automatically, but if it doesn't, +you can use the `configure' options `--x-includes=DIR' and +`--x-libraries=DIR' to specify their locations. + +Specifying the System Type +========================== + +There may be some features `configure' cannot figure out automatically, +but needs to determine by the type of machine the package will run on. +Usually, assuming the package is built to be run on the _same_ +architectures, `configure' can figure that out, but if it prints a +message saying it cannot guess the machine type, give it the +`--build=TYPE' option. TYPE can either be a short name for the system +type, such as `sun4', or a canonical name which has the form: + + CPU-COMPANY-SYSTEM + +where SYSTEM can have one of these forms: + + OS KERNEL-OS + + See the file `config.sub' for the possible values of each field. If +`config.sub' isn't included in this package, then this package doesn't +need to know the machine type. + + If you are _building_ compiler tools for cross-compiling, you should +use the `--target=TYPE' option to select the type of system they will +produce code for. + + If you want to _use_ a cross compiler, that generates code for a +platform different from the build platform, you should specify the +"host" platform (i.e., that on which the generated programs will +eventually be run) with `--host=TYPE'. + +Sharing Defaults +================ + +If you want to set default values for `configure' scripts to share, you +can create a site shell script called `config.site' that gives default +values for variables like `CC', `cache_file', and `prefix'. +`configure' looks for `PREFIX/share/config.site' if it exists, then +`PREFIX/etc/config.site' if it exists. Or, you can set the +`CONFIG_SITE' environment variable to the location of the site script. +A warning: not all `configure' scripts look for a site script. + +Defining Variables +================== + +Variables not defined in a site shell script can be set in the +environment passed to `configure'. However, some packages may run +configure again during the build, and the customized values of these +variables may be lost. In order to avoid this problem, you should set +them in the `configure' command line, using `VAR=value'. For example: + + ./configure CC=/usr/local2/bin/gcc + +causes the specified `gcc' to be used as the C compiler (unless it is +overridden in the site shell script). Here is a another example: + + /bin/bash ./configure CONFIG_SHELL=/bin/bash + +Here the `CONFIG_SHELL=/bin/bash' operand causes subsequent +configuration-related scripts to be executed by `/bin/bash'. + +`configure' Invocation +====================== + +`configure' recognizes the following options to control how it operates. + +`--help' +`-h' + Print a summary of the options to `configure', and exit. + +`--version' +`-V' + Print the version of Autoconf used to generate the `configure' + script, and exit. + +`--cache-file=FILE' + Enable the cache: use and save the results of the tests in FILE, + traditionally `config.cache'. FILE defaults to `/dev/null' to + disable caching. + +`--config-cache' +`-C' + Alias for `--cache-file=config.cache'. + +`--quiet' +`--silent' +`-q' + Do not print messages saying which checks are being made. To + suppress all normal output, redirect it to `/dev/null' (any error + messages will still be shown). + +`--srcdir=DIR' + Look for the package's source code in directory DIR. Usually + `configure' can determine that directory automatically. + +`configure' also accepts some other, not widely useful, options. Run +`configure --help' for more details. + diff --git a/Makefile.am b/Makefile.am new file mode 100644 index 0000000..a2e0b92 --- /dev/null +++ b/Makefile.am @@ -0,0 +1,58 @@ + +AM_MAKEFLAGS = --no-print-directory + +includedir = @includedir@/near + +noinst_HEADERS = include/dbus.h + +local_headers = $(foreach file,$(include_HEADERS) $(nodist_include_HEADERS) \ + $(noinst_HEADERS), include/near/$(notdir $(file))) + +gdbus_sources = gdbus/gdbus.h gdbus/mainloop.c gdbus/watch.c \ + gdbus/object.c gdbus/polkit.c + +builtin_modules = +builtin_sources = +builtin_cflags = +builtin_libadd = + +libexec_PROGRAMS = src/neard + +src_neard_SOURCES = $(gdbus_sources) $(gweb_sources) $(builtin_sources) \ + src/main.c src/error.c src/near.h src/types.h \ + src/log.h src/log.c src/dbus.c src/manager.c \ + src/adapter.c + +src_neard_LDADD = $(builtin_libadd) @GLIB_LIBS@ @DBUS_LIBS@ -lresolv -ldl + +src_neard_LDFLAGS = -Wl,--export-dynamic + +nodist_src_neard_SOURCES = src/builtin.h + +src/builtin.h: src/genbuiltin $(builtin_sources) + $(AM_V_GEN)$(srcdir)/src/genbuiltin $(builtin_modules) > $@ + +AM_CFLAGS = @GLIB_CFLAGS@ @DBUS_CFLAGS@ $(builtin_cflags) \ + -DMMS_PLUGIN_BUILTIN \ + -DPLUGINDIR=\""$(plugindir)"\" + +INCLUDES = -I$(builddir)/include -I$(builddir)/src -I$(srcdir)/gdbus + +CLEANFILES = src/builtin.h $(local_headers) + +doc_files = doc/manager-api.txt doc/target-api.txt doc/adapter-api.txt + +EXTRA_DIST = src/genbuiltin $(test_scripts) $(doc_files) + +MAINTAINERCLEANFILES = Makefile.in \ + aclocal.m4 configure config.h.in config.sub config.guess \ + ltmain.sh depcomp compile missing install-sh mkinstalldirs + +$(src_neard_OBJECTS) $(plugin_objects): $(local_headers) + +include/near/%.h: include/%.h + $(AM_V_at)$(MKDIR_P) include/near + $(AM_V_GEN)$(LN_S) $(abs_top_srcdir)/$< $@ + +clean-local: + @$(RM) -rf include/near @@ -0,0 +1,21 @@ +Near Field Communication manager +******************************** + +Copyright (C) 2011 Intel Corporation. All rights reserved. + + +Compilation and installation +============================ + +In order to compile proxy daemon you need following software packages: + - GCC compiler + - D-Bus library + - GLib library + +To configure run: + ./configure --prefix=/usr + +Configure automatically searches for all required components and packages. + +To compile and install run: + make && make install @@ -0,0 +1,10 @@ +Background +========== + +- Priority scale: High, Medium and Low + +- Complexity scale: C1, C2, C4 and C8. + The complexity scale is exponential, with complexity 1 being the + lowest complexity. Complexity is a function of both task 'complexity' + and task 'scope'. + diff --git a/acinclude.m4 b/acinclude.m4 new file mode 100644 index 0000000..329c6a9 --- /dev/null +++ b/acinclude.m4 @@ -0,0 +1,27 @@ +AC_DEFUN([AC_PROG_CC_PIE], [ + AC_CACHE_CHECK([whether ${CC-cc} accepts -fPIE], ac_cv_prog_cc_pie, [ + echo 'void f(){}' > conftest.c + if test -z "`${CC-cc} -fPIE -pie -c conftest.c 2>&1`"; then + ac_cv_prog_cc_pie=yes + else + ac_cv_prog_cc_pie=no + fi + rm -rf conftest* + ]) +]) + +AC_DEFUN([COMPILER_FLAGS], [ + if (test "${CFLAGS}" = ""); then + CFLAGS="-Wall -O2 -D_FORTIFY_SOURCE=2" + fi + if (test "$USE_MAINTAINER_MODE" = "yes"); then + CFLAGS+=" -Werror -Wextra" + CFLAGS+=" -Wno-unused-parameter" + CFLAGS+=" -Wno-missing-field-initializers" + CFLAGS+=" -Wdeclaration-after-statement" + CFLAGS+=" -Wmissing-declarations" + CFLAGS+=" -Wredundant-decls" + CFLAGS+=" -Wcast-align" + CFLAGS+=" -DG_DISABLE_DEPRECATED" + fi +]) diff --git a/bootstrap b/bootstrap new file mode 100755 index 0000000..91756f9 --- /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 index 0000000..7d35d71 --- /dev/null +++ b/bootstrap-configure @@ -0,0 +1,10 @@ +#!/bin/sh + +if [ -f config.status ]; then + make maintainer-clean +fi + +./bootstrap && \ + ./configure --enable-maintainer-mode \ + --enable-debug \ + --prefix=/usr $* diff --git a/configure.ac b/configure.ac new file mode 100644 index 0000000..d908a5e --- /dev/null +++ b/configure.ac @@ -0,0 +1,76 @@ +AC_PREREQ(2.60) +AC_INIT(neard, 0.0) + +AM_INIT_AUTOMAKE([foreign subdir-objects color-tests]) +AM_CONFIG_HEADER(config.h) + +m4_ifdef([AM_SILENT_RULES], [AM_SILENT_RULES([yes])]) + +AM_MAINTAINER_MODE + +AC_PREFIX_DEFAULT(/usr/local) + +if (test "${libdir}" = '${exec_prefix}/lib'); then + libdir="${prefix}/lib" +fi + +plugindir="${libdir}/near/plugins" + +PKG_PROG_PKG_CONFIG + +COMPILER_FLAGS + +AC_LANG_C + +AC_PROG_CC +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 + +AC_ARG_ENABLE(optimization, AC_HELP_STRING([--disable-optimization], + [disable code optimization through compiler]), [ + if (test "${enableval}" = "no"); then + CFLAGS="$CFLAGS -O0" + fi +]) + +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" + 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_CHECK_LIB(dl, dlopen, dummy=yes, + AC_MSG_ERROR(dynamic linking loader is required)) + +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) + +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) + +AC_OUTPUT(Makefile) diff --git a/doc/adapter-api.txt b/doc/adapter-api.txt new file mode 100644 index 0000000..39cdca9 --- /dev/null +++ b/doc/adapter-api.txt @@ -0,0 +1,64 @@ +Adapter hierarchy +================= + +Service org.neard +Interface org.neard.Adapter +Object path [variable prefix]/{nfc0,nfc1,...} + +Methods: dict GetProperties() + + Returns all properties for the device. See the + properties section for available properties. + + Possible Errors: org.neard.Error.DoesNotExist + + void SetProperty(string name, variant value) + + Changes the value of the specified property. Only + properties that are listed a read-write are changeable. + On success this will emit a PropertyChanged signal. + + Possible Errors: org.neard.Error.DoesNotExist + org.neard.Error.InvalidArguments + + +Signals PropertyChanged(string name, variant value) + + This signal indicates a changed value of the given + property. + + TargetFound(string address, dict values) + + This signal is sent whenever an NFC target is found, + as a result of a probe response reception. + + The dictionary contains basically the same values + that are returned by the GetProperties method + from the org.neard.Target interface. + + TargetLost(string address) + + This signal is sent whenever the NFC target is no longer + in sight, or when it's been de-activated. + + +Properties: string Mode [readwrite] + + The adapter mode. + + Valid types are "initiator" and "target". + The default value for the adapter mode is "target". + + string CommunicationMode [readwrite] + + The adapter communication mode. + + Valid types are "passive" and "active". In passive mode, the + initiator never turns its RF field off. + The default value for the adapter communication mode + is "passive". + + string Powered [readwrite] + + Switch an adapter on or off. + diff --git a/doc/manager-api.txt b/doc/manager-api.txt new file mode 100644 index 0000000..7d13f01 --- /dev/null +++ b/doc/manager-api.txt @@ -0,0 +1,40 @@ +Manager hierarchy +================= + +Service org.neard +Interface org.neard.Manager +Object path / + +Methods dict GetProperties() + + Returns all properties for the device. See the + properties section for available properties. + + Possible Errors: org.neard.Error.DoesNotExist + + void SetProperty(string name, variant value) + + Changes the value of the specified property. Only + properties that are listed a read-write are changeable. + On success this will emit a PropertyChanged signal. + + Possible Errors: org.neard.Error.DoesNotExist + org.neard.Error.InvalidArguments + + +Signals PropertyChanged(string name, variant value) + + This signal indicates a changed value of the given + property. + + AdapterAdded(object adapter) + + Parameter is the object path of added adapter. + + AdapterRemoved(object adapter) + + Parameter is the object path of removed adapter. + +Properties array{object} Adapters [readonly] + + List of adapter object paths. diff --git a/doc/target-api.txt b/doc/target-api.txt new file mode 100644 index 0000000..3172b78 --- /dev/null +++ b/doc/target-api.txt @@ -0,0 +1,38 @@ +Target hierarchy +================ + +Service org.neard +Interface org.neard.Target +Object path [variable prefix]/{nfc0,nfc1,...}/target_UID + +Method dict GetProperties() + + Returns all properties for the device. See the + properties section for available properties. + + Possible Errors: org.neard.Error.DoesNotExist + + void SetProperty(string name, variant value) + + Changes the value of the specified property. Only + properties that are listed a read-write are changeable. + On success this will emit a PropertyChanged signal. + + Possible Errors: org.neard.Error.DoesNotExist + org.neard.Error.InvalidArguments + +Signals PropertyChanged(string name, variant value) + + This signal indicates a changed value of the given + property. + +Properties string Type [readonly] + + The NFC target type. + + Valid types are "tag" and "device". + + array{dict} Records [readwrite] + + List of NDEF records. + This is only valid for tag type targets. diff --git a/gdbus/gdbus.h b/gdbus/gdbus.h new file mode 100644 index 0000000..a0583e6 --- /dev/null +++ b/gdbus/gdbus.h @@ -0,0 +1,177 @@ +/* + * + * D-Bus helper library + * + * Copyright (C) 2004-2011 Marcel Holtmann <marcel@holtmann.org> + * + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * + */ + +#ifndef __GDBUS_H +#define __GDBUS_H + +#ifdef __cplusplus +extern "C" { +#endif + +#include <dbus/dbus.h> +#include <glib.h> + +typedef 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); + +DBusConnection *g_dbus_setup_private(DBusBusType type, const char *name, + DBusError *error); + +gboolean g_dbus_request_name(DBusConnection *connection, const char *name, + DBusError *error); + +gboolean g_dbus_set_disconnect_function(DBusConnection *connection, + GDBusWatchFunction function, + void *user_data, DBusFreeFunction destroy); + +typedef void (* GDBusDestroyFunction) (void *user_data); + +typedef DBusMessage * (* GDBusMethodFunction) (DBusConnection *connection, + DBusMessage *message, void *user_data); + +typedef guint32 GDBusPendingReply; + +typedef void (* GDBusSecurityFunction) (DBusConnection *connection, + const char *action, + gboolean interaction, + GDBusPendingReply pending); + +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 enum { + G_DBUS_SECURITY_FLAG_DEPRECATED = (1 << 0), + G_DBUS_SECURITY_FLAG_BUILTIN = (1 << 1), + G_DBUS_SECURITY_FLAG_ALLOW_INTERACTION = (1 << 2), +} GDBusSecurityFlags; + +typedef struct { + const char *name; + const char *signature; + const char *reply; + GDBusMethodFunction function; + GDBusMethodFlags flags; + unsigned int privilege; +} GDBusMethodTable; + +typedef struct { + const char *name; + const char *signature; + GDBusSignalFlags flags; +} GDBusSignalTable; + +typedef struct { + const char *name; + const char *type; + GDBusPropertyFlags flags; +} GDBusPropertyTable; + +typedef struct { + unsigned int privilege; + const char *action; + GDBusSecurityFlags flags; + GDBusSecurityFunction function; +} GDBusSecurityTable; + +gboolean g_dbus_register_interface(DBusConnection *connection, + const char *path, const char *name, + const GDBusMethodTable *methods, + const GDBusSignalTable *signals, + const GDBusPropertyTable *properties, + void *user_data, + GDBusDestroyFunction destroy); +gboolean g_dbus_unregister_interface(DBusConnection *connection, + const char *path, const char *name); + +gboolean g_dbus_register_security(const GDBusSecurityTable *security); +gboolean g_dbus_unregister_security(const GDBusSecurityTable *security); + +void g_dbus_pending_success(DBusConnection *connection, + GDBusPendingReply pending); +void g_dbus_pending_error(DBusConnection *connection, + GDBusPendingReply pending, + const char *name, const char *format, ...) + __attribute__((format(printf, 4, 5))); +void g_dbus_pending_error_valist(DBusConnection *connection, + GDBusPendingReply pending, const char *name, + const char *format, va_list args); + +DBusMessage *g_dbus_create_error(DBusMessage *message, const char *name, + const char *format, ...) + __attribute__((format(printf, 3, 4))); +DBusMessage *g_dbus_create_error_valist(DBusMessage *message, const char *name, + const char *format, va_list args); +DBusMessage *g_dbus_create_reply(DBusMessage *message, int type, ...); +DBusMessage *g_dbus_create_reply_valist(DBusMessage *message, + int type, va_list args); + +gboolean g_dbus_send_message(DBusConnection *connection, DBusMessage *message); +gboolean g_dbus_send_reply(DBusConnection *connection, + DBusMessage *message, int type, ...); +gboolean g_dbus_send_reply_valist(DBusConnection *connection, + DBusMessage *message, int type, va_list args); + +gboolean g_dbus_emit_signal(DBusConnection *connection, + const char *path, const char *interface, + const char *name, int type, ...); +gboolean g_dbus_emit_signal_valist(DBusConnection *connection, + const char *path, const char *interface, + const char *name, int type, va_list args); + +guint g_dbus_add_service_watch(DBusConnection *connection, const char *name, + GDBusWatchFunction connect, + GDBusWatchFunction disconnect, + void *user_data, GDBusDestroyFunction destroy); +guint g_dbus_add_disconnect_watch(DBusConnection *connection, const char *name, + GDBusWatchFunction function, + void *user_data, GDBusDestroyFunction destroy); +guint g_dbus_add_signal_watch(DBusConnection *connection, + const char *sender, const char *path, + const char *interface, const char *member, + GDBusSignalFunction function, void *user_data, + GDBusDestroyFunction destroy); +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 index 0000000..8718da0 --- /dev/null +++ b/gdbus/mainloop.c @@ -0,0 +1,383 @@ +/* + * + * D-Bus helper library + * + * Copyright (C) 2004-2011 Marcel Holtmann <marcel@holtmann.org> + * + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * + */ + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#include <glib.h> +#include <dbus/dbus.h> + +#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...) + +struct timeout_handler { + guint id; + DBusTimeout *timeout; +}; + +struct watch_info { + guint id; + DBusWatch *watch; + DBusConnection *conn; +}; + +struct disconnect_data { + GDBusWatchFunction function; + void *user_data; +}; + +static gboolean disconnected_signal(DBusConnection *conn, + DBusMessage *msg, void *data) +{ + struct disconnect_data *dc_data = data; + + error("Got disconnected from the system message bus"); + + dc_data->function(conn, dc_data->user_data); + + dbus_connection_unref(conn); + + return TRUE; +} + +static gboolean message_dispatch(void *data) +{ + DBusConnection *conn = data; + + dbus_connection_ref(conn); + + /* Dispatch messages */ + while (dbus_connection_dispatch(conn) == DBUS_DISPATCH_DATA_REMAINS); + + dbus_connection_unref(conn); + + return FALSE; +} + +static inline void queue_dispatch(DBusConnection *conn, + DBusDispatchStatus status) +{ + if (status == DBUS_DISPATCH_DATA_REMAINS) + g_timeout_add(DISPATCH_TIMEOUT, message_dispatch, conn); +} + +static gboolean watch_func(GIOChannel *chan, GIOCondition cond, gpointer data) +{ + struct watch_info *info = data; + unsigned int flags = 0; + DBusDispatchStatus status; + + dbus_connection_ref(info->conn); + + if (cond & G_IO_IN) flags |= DBUS_WATCH_READABLE; + if (cond & G_IO_OUT) flags |= DBUS_WATCH_WRITABLE; + if (cond & G_IO_HUP) flags |= DBUS_WATCH_HANGUP; + if (cond & G_IO_ERR) flags |= DBUS_WATCH_ERROR; + + dbus_watch_handle(info->watch, flags); + + status = dbus_connection_get_dispatch_status(info->conn); + queue_dispatch(info->conn, status); + + dbus_connection_unref(info->conn); + + return TRUE; +} + +static void watch_info_free(void *data) +{ + struct watch_info *info = data; + + if (info->id > 0) { + g_source_remove(info->id); + info->id = 0; + } + + dbus_connection_unref(info->conn); + + g_free(info); +} + +static dbus_bool_t add_watch(DBusWatch *watch, void *data) +{ + DBusConnection *conn = data; + GIOCondition cond = G_IO_HUP | G_IO_ERR; + GIOChannel *chan; + struct watch_info *info; + unsigned int flags; + int fd; + + if (!dbus_watch_get_enabled(watch)) + return TRUE; + + info = g_new0(struct watch_info, 1); + + fd = dbus_watch_get_unix_fd(watch); + chan = g_io_channel_unix_new(fd); + + info->watch = watch; + info->conn = dbus_connection_ref(conn); + + dbus_watch_set_data(watch, info, watch_info_free); + + flags = dbus_watch_get_flags(watch); + + if (flags & DBUS_WATCH_READABLE) cond |= G_IO_IN; + if (flags & DBUS_WATCH_WRITABLE) cond |= G_IO_OUT; + + info->id = g_io_add_watch(chan, cond, watch_func, info); + + g_io_channel_unref(chan); + + return TRUE; +} + +static void remove_watch(DBusWatch *watch, void *data) +{ + if (dbus_watch_get_enabled(watch)) + return; + + /* will trigger watch_info_free() */ + dbus_watch_set_data(watch, NULL, NULL); +} + +static void watch_toggled(DBusWatch *watch, void *data) +{ + /* Because we just exit on OOM, enable/disable is + * no different from add/remove */ + if (dbus_watch_get_enabled(watch)) + add_watch(watch, data); + else + remove_watch(watch, data); +} + +static gboolean timeout_handler_dispatch(gpointer data) +{ + struct timeout_handler *handler = data; + + handler->id = 0; + + /* if not enabled should not be polled by the main loop */ + if (!dbus_timeout_get_enabled(handler->timeout)) + return FALSE; + + dbus_timeout_handle(handler->timeout); + + return FALSE; +} + +static void timeout_handler_free(void *data) +{ + struct timeout_handler *handler = data; + + if (handler->id > 0) { + g_source_remove(handler->id); + handler->id = 0; + } + + g_free(handler); +} + +static dbus_bool_t add_timeout(DBusTimeout *timeout, void *data) +{ + int interval = dbus_timeout_get_interval(timeout); + struct timeout_handler *handler; + + if (!dbus_timeout_get_enabled(timeout)) + return TRUE; + + handler = g_new0(struct timeout_handler, 1); + + handler->timeout = timeout; + + dbus_timeout_set_data(timeout, handler, timeout_handler_free); + + handler->id = g_timeout_add(interval, timeout_handler_dispatch, + handler); + + return TRUE; +} + +static void remove_timeout(DBusTimeout *timeout, void *data) +{ + /* will trigger timeout_handler_free() */ + dbus_timeout_set_data(timeout, NULL, NULL); +} + +static void timeout_toggled(DBusTimeout *timeout, void *data) +{ + if (dbus_timeout_get_enabled(timeout)) + add_timeout(timeout, data); + else + remove_timeout(timeout, data); +} + +static void dispatch_status(DBusConnection *conn, + DBusDispatchStatus status, void *data) +{ + if (!dbus_connection_get_is_connected(conn)) + return; + + queue_dispatch(conn, status); +} + +static inline void setup_dbus_with_main_loop(DBusConnection *conn) +{ + dbus_connection_set_watch_functions(conn, add_watch, remove_watch, + watch_toggled, conn, NULL); + + dbus_connection_set_timeout_functions(conn, add_timeout, remove_timeout, + timeout_toggled, NULL, NULL); + + dbus_connection_set_dispatch_status_function(conn, dispatch_status, + NULL, NULL); +} + +static gboolean setup_bus(DBusConnection *conn, const char *name, + DBusError *error) +{ + gboolean result; + DBusDispatchStatus status; + + if (name != NULL) { + result = g_dbus_request_name(conn, name, error); + + if (error != NULL) { + if (dbus_error_is_set(error) == TRUE) + return FALSE; + } + + if (result == FALSE) + return FALSE; + } + + setup_dbus_with_main_loop(conn); + + status = dbus_connection_get_dispatch_status(conn); + queue_dispatch(conn, status); + + return TRUE; +} + +DBusConnection *g_dbus_setup_bus(DBusBusType type, const char *name, + DBusError *error) +{ + DBusConnection *conn; + + conn = dbus_bus_get(type, error); + + if (error != NULL) { + if (dbus_error_is_set(error) == TRUE) + return NULL; + } + + if (conn == NULL) + return NULL; + + if (setup_bus(conn, name, error) == FALSE) { + dbus_connection_unref(conn); + return NULL; + } + + return conn; +} + +DBusConnection *g_dbus_setup_private(DBusBusType type, const char *name, + DBusError *error) +{ + DBusConnection *conn; + + conn = dbus_bus_get_private(type, error); + + if (error != NULL) { + if (dbus_error_is_set(error) == TRUE) + return NULL; + } + + if (conn == NULL) + return NULL; + + if (setup_bus(conn, name, error) == FALSE) { + dbus_connection_unref(conn); + return NULL; + } + + return conn; +} + +gboolean g_dbus_request_name(DBusConnection *connection, const char *name, + DBusError *error) +{ + int result; + + result = dbus_bus_request_name(connection, name, + DBUS_NAME_FLAG_DO_NOT_QUEUE, error); + + if (error != NULL) { + if (dbus_error_is_set(error) == TRUE) + return FALSE; + } + + if (result != DBUS_REQUEST_NAME_REPLY_PRIMARY_OWNER) { + if (error != NULL) + dbus_set_error(error, name, "Name already in use"); + + return FALSE; + } + + return TRUE; +} + +gboolean g_dbus_set_disconnect_function(DBusConnection *connection, + GDBusWatchFunction function, + void *user_data, DBusFreeFunction destroy) +{ + struct disconnect_data *dc_data; + + dc_data = g_new0(struct disconnect_data, 1); + + dc_data->function = function; + dc_data->user_data = user_data; + + dbus_connection_set_exit_on_disconnect(connection, FALSE); + + if (g_dbus_add_signal_watch(connection, NULL, NULL, + DBUS_INTERFACE_LOCAL, "Disconnected", + disconnected_signal, dc_data, g_free) == 0) { + error("Failed to add watch for D-Bus Disconnected signal"); + g_free(dc_data); + return FALSE; + } + + return TRUE; +} diff --git a/gdbus/object.c b/gdbus/object.c new file mode 100644 index 0000000..d17a101 --- /dev/null +++ b/gdbus/object.c @@ -0,0 +1,868 @@ +/* + * + * D-Bus helper library + * + * Copyright (C) 2004-2011 Marcel Holtmann <marcel@holtmann.org> + * + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * + */ + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#include <stdio.h> +#include <string.h> + +#include <glib.h> +#include <dbus/dbus.h> + +#include "gdbus.h" + +#define info(fmt...) +#define error(fmt...) +#define debug(fmt...) + +struct generic_data { + unsigned int refcount; + GSList *interfaces; + char *introspect; +}; + +struct interface_data { + char *name; + const GDBusMethodTable *methods; + const GDBusSignalTable *signals; + const GDBusPropertyTable *properties; + void *user_data; + GDBusDestroyFunction destroy; +}; + +struct security_data { + GDBusPendingReply pending; + DBusMessage *message; + const GDBusMethodTable *method; + void *iface_user_data; +}; + +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) +{ + const GDBusMethodTable *method; + const 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>\n"); + + 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 DBusMessage *introspect(DBusConnection *connection, + DBusMessage *message, void *user_data) +{ + struct generic_data *data = user_data; + DBusMessage *reply; + + if (!dbus_message_has_signature(message, DBUS_TYPE_INVALID_AS_STRING)) { + error("Unexpected signature to introspect call"); + return NULL; + } + + if (data->introspect == NULL) + generate_introspection_xml(connection, data, + dbus_message_get_path(message)); + + reply = dbus_message_new_method_return(message); + if (reply == NULL) + return NULL; + + dbus_message_append_args(reply, DBUS_TYPE_STRING, &data->introspect, + DBUS_TYPE_INVALID); + + return reply; +} + +static DBusHandlerResult process_message(DBusConnection *connection, + DBusMessage *message, const GDBusMethodTable *method, + void *iface_user_data) +{ + DBusMessage *reply; + + reply = method->function(connection, message, iface_user_data); + + if (method->flags & G_DBUS_METHOD_FLAG_NOREPLY) { + if (reply != NULL) + dbus_message_unref(reply); + return DBUS_HANDLER_RESULT_HANDLED; + } + + if (method->flags & G_DBUS_METHOD_FLAG_ASYNC) { + if (reply == NULL) + return DBUS_HANDLER_RESULT_HANDLED; + } + + if (reply == NULL) + return DBUS_HANDLER_RESULT_NEED_MEMORY; + + dbus_connection_send(connection, reply, NULL); + dbus_message_unref(reply); + + return DBUS_HANDLER_RESULT_HANDLED; +} + +static GDBusPendingReply next_pending = 1; +static GSList *pending_security = NULL; + +static const GDBusSecurityTable *security_table = NULL; + +void g_dbus_pending_success(DBusConnection *connection, + GDBusPendingReply pending) +{ + GSList *list; + + for (list = pending_security; list; list = list->next) { + struct security_data *secdata = list->data; + + if (secdata->pending != pending) + continue; + + pending_security = g_slist_remove(pending_security, secdata); + + process_message(connection, secdata->message, + secdata->method, secdata->iface_user_data); + + dbus_message_unref(secdata->message); + g_free(secdata); + return; + } +} + +void g_dbus_pending_error_valist(DBusConnection *connection, + GDBusPendingReply pending, const char *name, + const char *format, va_list args) +{ + GSList *list; + + for (list = pending_security; list; list = list->next) { + struct security_data *secdata = list->data; + DBusMessage *reply; + + if (secdata->pending != pending) + continue; + + pending_security = g_slist_remove(pending_security, secdata); + + reply = g_dbus_create_error_valist(secdata->message, + name, format, args); + if (reply != NULL) { + dbus_connection_send(connection, reply, NULL); + dbus_message_unref(reply); + } + + dbus_message_unref(secdata->message); + g_free(secdata); + return; + } +} + +void g_dbus_pending_error(DBusConnection *connection, + GDBusPendingReply pending, + const char *name, const char *format, ...) +{ + va_list args; + + va_start(args, format); + + g_dbus_pending_error_valist(connection, pending, name, format, args); + + va_end(args); +} + +int polkit_check_authorization(DBusConnection *conn, + const char *action, gboolean interaction, + void (*function) (dbus_bool_t authorized, + void *user_data), + void *user_data, int timeout); + +struct builtin_security_data { + DBusConnection *conn; + GDBusPendingReply pending; +}; + +static void builtin_security_result(dbus_bool_t authorized, void *user_data) +{ + struct builtin_security_data *data = user_data; + + if (authorized == TRUE) + g_dbus_pending_success(data->conn, data->pending); + else + g_dbus_pending_error(data->conn, data->pending, + DBUS_ERROR_AUTH_FAILED, NULL); + + g_free(data); +} + +static void builtin_security_function(DBusConnection *conn, + const char *action, + gboolean interaction, + GDBusPendingReply pending) +{ + struct builtin_security_data *data; + + data = g_new0(struct builtin_security_data, 1); + data->conn = conn; + data->pending = pending; + + if (polkit_check_authorization(conn, action, interaction, + builtin_security_result, data, 30000) < 0) + g_dbus_pending_error(conn, pending, NULL, NULL); +} + +static gboolean check_privilege(DBusConnection *conn, DBusMessage *msg, + const GDBusMethodTable *method, void *iface_user_data) +{ + const GDBusSecurityTable *security; + + for (security = security_table; security && security->privilege; + security++) { + struct security_data *secdata; + gboolean interaction; + + if (security->privilege != method->privilege) + continue; + + secdata = g_new(struct security_data, 1); + secdata->pending = next_pending++; + secdata->message = dbus_message_ref(msg); + secdata->method = method; + secdata->iface_user_data = iface_user_data; + + pending_security = g_slist_prepend(pending_security, secdata); + + if (security->flags & G_DBUS_SECURITY_FLAG_ALLOW_INTERACTION) + interaction = TRUE; + else + interaction = FALSE; + + if (!(security->flags & G_DBUS_SECURITY_FLAG_BUILTIN) && + security->function) + security->function(conn, security->action, + interaction, secdata->pending); + else + builtin_security_function(conn, security->action, + interaction, secdata->pending); + + return TRUE; + } + + return FALSE; +} + +static 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 == NULL) + return NULL; + + for (list = interfaces; list; list = list->next) { + struct interface_data *iface = list->data; + if (!strcmp(name, iface->name)) + return iface; + } + + return NULL; +} + +static DBusHandlerResult generic_message(DBusConnection *connection, + DBusMessage *message, void *user_data) +{ + struct generic_data *data = user_data; + struct interface_data *iface; + const GDBusMethodTable *method; + const char *interface; + + interface = dbus_message_get_interface(message); + + iface = find_interface(data->interfaces, interface); + if (iface == NULL) + return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; + + for (method = iface->methods; method && + method->name && method->function; method++) { + if (dbus_message_is_method_call(message, iface->name, + method->name) == FALSE) + continue; + + if (dbus_message_has_signature(message, + method->signature) == FALSE) + continue; + + if (check_privilege(connection, message, method, + iface->user_data) == TRUE) + return DBUS_HANDLER_RESULT_HANDLED; + + return process_message(connection, message, method, + iface->user_data); + } + + return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; +} + +static DBusObjectPathVTable generic_table = { + .unregister_function = generic_unregister, + .message_function = generic_message, +}; + +static 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 == NULL) + goto done; + + if (slash == parent_path && parent_path[1] != '\0') + parent_path[1] = '\0'; + else + *slash = '\0'; + + if (!strlen(parent_path)) + goto done; + + if (dbus_connection_get_object_path_data(conn, parent_path, + (void *) &data) == FALSE) { + goto done; + } + + invalidate_parent_data(conn, parent_path); + + if (data == NULL) + goto done; + + g_free(data->introspect); + data->introspect = NULL; + +done: + g_free(parent_path); +} + +static GDBusMethodTable introspect_methods[] = { + { "Introspect", "", "s", introspect }, + { } +}; + +static void add_interface(struct generic_data *data, const char *name, + const GDBusMethodTable *methods, + const GDBusSignalTable *signals, + const GDBusPropertyTable *properties, + void *user_data, + GDBusDestroyFunction destroy) +{ + struct interface_data *iface; + + 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); +} + +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->refcount = 1; + + data->introspect = g_strdup(DBUS_INTROSPECT_1_0_XML_DOCTYPE_DECL_NODE "<node></node>"); + + if (!dbus_connection_register_object_path(connection, path, + &generic_table, data)) { + g_free(data->introspect); + g_free(data); + return NULL; + } + + invalidate_parent_data(connection, path); + + add_interface(data, DBUS_INTERFACE_INTROSPECTABLE, + introspect_methods, NULL, NULL, data, NULL); + + return data; +} + +static gboolean remove_interface(struct generic_data *data, const char *name) +{ + struct interface_data *iface; + + iface = find_interface(data->interfaces, name); + if (iface == NULL) + return FALSE; + + data->interfaces = g_slist_remove(data->interfaces, iface); + + if (iface->destroy) + iface->destroy(iface->user_data); + + g_free(iface->name); + g_free(iface); + + return TRUE; +} + +static void object_path_unref(DBusConnection *connection, const char *path) +{ + struct generic_data *data = NULL; + + if (dbus_connection_get_object_path_data(connection, path, + (void *) &data) == FALSE) + return; + + if (data == NULL) + return; + + data->refcount--; + + if (data->refcount > 0) + return; + + remove_interface(data, DBUS_INTERFACE_INTROSPECTABLE); + + 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; + const GDBusSignalTable *signal; + + *args = NULL; + if (!dbus_connection_get_object_path_data(conn, path, + (void *) &data) || data == NULL) { + error("dbus_connection_emit_signal: path %s isn't registered", + path); + return FALSE; + } + + iface = find_interface(data->interfaces, interface); + if (iface == NULL) { + error("dbus_connection_emit_signal: %s does not implement %s", + path, interface); + return FALSE; + } + + for (signal = iface->signals; signal && signal->name; signal++) { + if (!strcmp(signal->name, name)) { + *args = signal->signature; + break; + } + } + + if (*args == NULL) { + 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 == NULL) { + error("Unable to allocate new %s.%s signal", interface, name); + return FALSE; + } + + ret = dbus_message_append_args_valist(signal, first, var_args); + if (!ret) + goto fail; + + 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, + const GDBusMethodTable *methods, + const GDBusSignalTable *signals, + const GDBusPropertyTable *properties, + void *user_data, + GDBusDestroyFunction destroy) +{ + struct generic_data *data; + + data = object_path_ref(connection, path); + if (data == NULL) + return FALSE; + + if (find_interface(data->interfaces, name)) { + object_path_unref(connection, path); + return FALSE; + } + + add_interface(data, name, methods, signals, + properties, user_data, destroy); + + g_free(data->introspect); + data->introspect = NULL; + + return TRUE; +} + +gboolean g_dbus_unregister_interface(DBusConnection *connection, + const char *path, const char *name) +{ + struct generic_data *data = NULL; + + if (path == NULL) + return FALSE; + + if (dbus_connection_get_object_path_data(connection, path, + (void *) &data) == FALSE) + return FALSE; + + if (data == NULL) + return FALSE; + + if (remove_interface(data, name) == FALSE) + return FALSE; + + g_free(data->introspect); + data->introspect = NULL; + + object_path_unref(connection, path); + + return TRUE; +} + +gboolean g_dbus_register_security(const GDBusSecurityTable *security) +{ + if (security_table != NULL) + return FALSE; + + security_table = security; + + return TRUE; +} + +gboolean g_dbus_unregister_security(const GDBusSecurityTable *security) +{ + security_table = NULL; + + return TRUE; +} + +DBusMessage *g_dbus_create_error_valist(DBusMessage *message, const char *name, + const char *format, va_list args) +{ + char str[1024]; + + vsnprintf(str, sizeof(str), format, args); + + return dbus_message_new_error(message, name, str); +} + +DBusMessage *g_dbus_create_error(DBusMessage *message, const char *name, + const char *format, ...) +{ + va_list args; + DBusMessage *reply; + + va_start(args, format); + + reply = g_dbus_create_error_valist(message, name, format, args); + + va_end(args); + + return reply; +} + +DBusMessage *g_dbus_create_reply_valist(DBusMessage *message, + int type, va_list args) +{ + DBusMessage *reply; + + reply = dbus_message_new_method_return(message); + if (reply == NULL) + return NULL; + + if (dbus_message_append_args_valist(reply, type, args) == FALSE) { + dbus_message_unref(reply); + return NULL; + } + + return reply; +} + +DBusMessage *g_dbus_create_reply(DBusMessage *message, int type, ...) +{ + va_list args; + DBusMessage *reply; + + va_start(args, type); + + reply = g_dbus_create_reply_valist(message, type, args); + + va_end(args); + + return reply; +} + +gboolean g_dbus_send_message(DBusConnection *connection, DBusMessage *message) +{ + dbus_bool_t result; + + if (dbus_message_get_type(message) == DBUS_MESSAGE_TYPE_METHOD_CALL) + dbus_message_set_no_reply(message, TRUE); + + 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/polkit.c b/gdbus/polkit.c new file mode 100644 index 0000000..9e95fa3 --- /dev/null +++ b/gdbus/polkit.c @@ -0,0 +1,202 @@ +/* + * + * D-Bus helper library + * + * Copyright (C) 2004-2011 Marcel Holtmann <marcel@holtmann.org> + * + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * + */ + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#include <errno.h> + +#include <dbus/dbus.h> + +#include <glib.h> + +int polkit_check_authorization(DBusConnection *conn, + const char *action, gboolean interaction, + void (*function) (dbus_bool_t authorized, + void *user_data), + void *user_data, int timeout); + +static void add_dict_with_string_value(DBusMessageIter *iter, + const char *key, const char *str) +{ + DBusMessageIter dict, entry, value; + + dbus_message_iter_open_container(iter, DBUS_TYPE_ARRAY, + DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING + DBUS_TYPE_STRING_AS_STRING DBUS_TYPE_VARIANT_AS_STRING + DBUS_DICT_ENTRY_END_CHAR_AS_STRING, &dict); + dbus_message_iter_open_container(&dict, DBUS_TYPE_DICT_ENTRY, + NULL, &entry); + + dbus_message_iter_append_basic(&entry, DBUS_TYPE_STRING, &key); + + dbus_message_iter_open_container(&entry, DBUS_TYPE_VARIANT, + DBUS_TYPE_STRING_AS_STRING, &value); + dbus_message_iter_append_basic(&value, DBUS_TYPE_STRING, &str); + dbus_message_iter_close_container(&entry, &value); + + dbus_message_iter_close_container(&dict, &entry); + dbus_message_iter_close_container(iter, &dict); +} + +static void add_empty_string_dict(DBusMessageIter *iter) +{ + DBusMessageIter dict; + + dbus_message_iter_open_container(iter, DBUS_TYPE_ARRAY, + DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING + DBUS_TYPE_STRING_AS_STRING DBUS_TYPE_STRING_AS_STRING + DBUS_DICT_ENTRY_END_CHAR_AS_STRING, &dict); + + dbus_message_iter_close_container(iter, &dict); +} + +static void add_arguments(DBusConnection *conn, DBusMessageIter *iter, + const char *action, dbus_uint32_t flags) +{ + const char *busname = dbus_bus_get_unique_name(conn); + const char *kind = "system-bus-name"; + const char *cancel = ""; + DBusMessageIter subject; + + dbus_message_iter_open_container(iter, DBUS_TYPE_STRUCT, + NULL, &subject); + dbus_message_iter_append_basic(&subject, DBUS_TYPE_STRING, &kind); + add_dict_with_string_value(&subject, "name", busname); + dbus_message_iter_close_container(iter, &subject); + + dbus_message_iter_append_basic(iter, DBUS_TYPE_STRING, &action); + add_empty_string_dict(iter); + dbus_message_iter_append_basic(iter, DBUS_TYPE_UINT32, &flags); + dbus_message_iter_append_basic(iter, DBUS_TYPE_STRING, &cancel); +} + +static dbus_bool_t parse_result(DBusMessageIter *iter) +{ + DBusMessageIter result; + dbus_bool_t authorized, challenge; + + dbus_message_iter_recurse(iter, &result); + + dbus_message_iter_get_basic(&result, &authorized); + dbus_message_iter_get_basic(&result, &challenge); + + return authorized; +} + +struct authorization_data { + void (*function) (dbus_bool_t authorized, void *user_data); + void *user_data; +}; + +static void authorization_reply(DBusPendingCall *call, void *user_data) +{ + struct authorization_data *data = user_data; + DBusMessage *reply; + DBusMessageIter iter; + dbus_bool_t authorized = FALSE; + + reply = dbus_pending_call_steal_reply(call); + + if (dbus_message_get_type(reply) == DBUS_MESSAGE_TYPE_ERROR) + goto done; + + if (dbus_message_has_signature(reply, "(bba{ss})") == FALSE) + goto done; + + dbus_message_iter_init(reply, &iter); + + authorized = parse_result(&iter); + +done: + if (data->function != NULL) + data->function(authorized, data->user_data); + + dbus_message_unref(reply); + + dbus_pending_call_unref(call); +} + +#define AUTHORITY_DBUS "org.freedesktop.PolicyKit1" +#define AUTHORITY_INTF "org.freedesktop.PolicyKit1.Authority" +#define AUTHORITY_PATH "/org/freedesktop/PolicyKit1/Authority" + +int polkit_check_authorization(DBusConnection *conn, + const char *action, gboolean interaction, + void (*function) (dbus_bool_t authorized, + void *user_data), + void *user_data, int timeout) +{ + struct authorization_data *data; + DBusMessage *msg; + DBusMessageIter iter; + DBusPendingCall *call; + dbus_uint32_t flags = 0x00000000; + + if (conn == NULL) + return -EINVAL; + + data = dbus_malloc0(sizeof(*data)); + if (data == NULL) + return -ENOMEM; + + msg = dbus_message_new_method_call(AUTHORITY_DBUS, AUTHORITY_PATH, + AUTHORITY_INTF, "CheckAuthorization"); + if (msg == NULL) { + dbus_free(data); + return -ENOMEM; + } + + if (interaction == TRUE) + flags |= 0x00000001; + + if (action == NULL) + action = "org.freedesktop.policykit.exec"; + + dbus_message_iter_init_append(msg, &iter); + add_arguments(conn, &iter, action, flags); + + if (dbus_connection_send_with_reply(conn, msg, + &call, timeout) == FALSE) { + dbus_message_unref(msg); + dbus_free(data); + return -EIO; + } + + if (call == NULL) { + dbus_message_unref(msg); + dbus_free(data); + return -EIO; + } + + data->function = function; + data->user_data = user_data; + + dbus_pending_call_set_notify(call, authorization_reply, + data, dbus_free); + + dbus_message_unref(msg); + + return 0; +} diff --git a/gdbus/watch.c b/gdbus/watch.c new file mode 100644 index 0000000..fba58c3 --- /dev/null +++ b/gdbus/watch.c @@ -0,0 +1,748 @@ +/* + * + * D-Bus helper library + * + * Copyright (C) 2004-2011 Marcel Holtmann <marcel@holtmann.org> + * + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * + */ + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#include <stdio.h> +#include <string.h> + +#include <glib.h> +#include <dbus/dbus.h> + +#include "gdbus.h" + +#define info(fmt...) +#define error(fmt...) +#define debug(fmt...) + +static DBusHandlerResult message_filter(DBusConnection *connection, + DBusMessage *message, void *user_data); + +static guint listener_id = 0; +static GSList *listeners = NULL; + +struct service_data { + DBusConnection *conn; + DBusPendingCall *call; + char *name; + const char *owner; + guint id; + struct filter_callback *callback; +}; + +struct filter_callback { + GDBusWatchFunction conn_func; + GDBusWatchFunction disc_func; + GDBusSignalFunction signal_func; + GDBusDestroyFunction destroy_func; + struct service_data *data; + void *user_data; + guint id; +}; + +struct filter_data { + DBusConnection *connection; + DBusHandleMessageFunction handle_func; + char *name; + char *owner; + char *path; + char *interface; + char *member; + char *argument; + GSList *callbacks; + GSList *processed; + guint name_watch; + gboolean lock; + gboolean registered; +}; + +static struct filter_data *filter_data_find(DBusConnection *connection, + const char *name, + const char *owner, + const char *path, + const char *interface, + const char *member, + const char *argument) +{ + GSList *current; + + for (current = listeners; + current != NULL; current = current->next) { + struct filter_data *data = current->data; + + if (connection != data->connection) + continue; + + if (name && data->name && + g_str_equal(name, data->name) == FALSE) + continue; + + if (owner && data->owner && + g_str_equal(owner, data->owner) == FALSE) + continue; + + if (path && data->path && + g_str_equal(path, data->path) == FALSE) + continue; + + if (interface && data->interface && + g_str_equal(interface, data->interface) == FALSE) + continue; + + if (member && data->member && + g_str_equal(member, data->member) == FALSE) + continue; + + if (argument && data->argument && + g_str_equal(argument, data->argument) == FALSE) + continue; + + return data; + } + + return NULL; +} + +static void format_rule(struct filter_data *data, char *rule, size_t size) +{ + const char *sender; + int offset; + + offset = snprintf(rule, size, "type='signal'"); + sender = data->name ? : data->owner; + + if (sender) + offset += snprintf(rule + offset, size - offset, + ",sender='%s'", sender); + if (data->path) + offset += snprintf(rule + offset, size - offset, + ",path='%s'", data->path); + if (data->interface) + offset += snprintf(rule + offset, size - offset, + ",interface='%s'", data->interface); + if (data->member) + offset += snprintf(rule + offset, size - offset, + ",member='%s'", data->member); + if (data->argument) + snprintf(rule + offset, size - offset, + ",arg0='%s'", data->argument); +} + +static gboolean add_match(struct filter_data *data, + DBusHandleMessageFunction filter) +{ + DBusError err; + char rule[DBUS_MAXIMUM_MATCH_RULE_LENGTH]; + + format_rule(data, rule, sizeof(rule)); + dbus_error_init(&err); + + dbus_bus_add_match(data->connection, rule, &err); + if (dbus_error_is_set(&err)) { + error("Adding match rule \"%s\" failed: %s", rule, + err.message); + dbus_error_free(&err); + return FALSE; + } + + data->handle_func = filter; + data->registered = TRUE; + + return TRUE; +} + +static gboolean remove_match(struct filter_data *data) +{ + DBusError err; + char rule[DBUS_MAXIMUM_MATCH_RULE_LENGTH]; + + format_rule(data, rule, sizeof(rule)); + + dbus_error_init(&err); + + dbus_bus_remove_match(data->connection, rule, &err); + if (dbus_error_is_set(&err)) { + error("Removing owner match rule for %s failed: %s", + rule, err.message); + dbus_error_free(&err); + return FALSE; + } + + return TRUE; +} + +static struct filter_data *filter_data_get(DBusConnection *connection, + DBusHandleMessageFunction filter, + const char *sender, + const char *path, + const char *interface, + const char *member, + const char *argument) +{ + struct filter_data *data; + const char *name = NULL, *owner = NULL; + + if (filter_data_find(connection, NULL, NULL, NULL, NULL, NULL, NULL) == NULL) { + if (!dbus_connection_add_filter(connection, + message_filter, NULL, NULL)) { + error("dbus_connection_add_filter() failed"); + return NULL; + } + } + + if (sender == NULL) + goto proceed; + + if (sender[0] == ':') + owner = sender; + else + name = sender; + +proceed: + data = filter_data_find(connection, name, owner, path, interface, + member, argument); + if (data) + return data; + + data = g_new0(struct filter_data, 1); + + data->connection = dbus_connection_ref(connection); + data->name = name ? g_strdup(name) : NULL; + data->owner = owner ? g_strdup(owner) : NULL; + data->path = g_strdup(path); + data->interface = g_strdup(interface); + data->member = g_strdup(member); + data->argument = g_strdup(argument); + + if (!add_match(data, filter)) { + g_free(data); + return NULL; + } + + listeners = g_slist_append(listeners, data); + + return data; +} + +static struct filter_callback *filter_data_find_callback( + struct filter_data *data, + guint id) +{ + GSList *l; + + for (l = data->callbacks; l; l = l->next) { + struct filter_callback *cb = l->data; + if (cb->id == id) + return cb; + } + for (l = data->processed; l; l = l->next) { + struct filter_callback *cb = l->data; + if (cb->id == id) + return cb; + } + + return NULL; +} + +static void filter_data_free(struct filter_data *data) +{ + GSList *l; + + for (l = data->callbacks; l != NULL; l = l->next) + g_free(l->data); + + g_slist_free(data->callbacks); + g_dbus_remove_watch(data->connection, data->name_watch); + g_free(data->name); + g_free(data->owner); + g_free(data->path); + g_free(data->interface); + g_free(data->member); + g_free(data->argument); + dbus_connection_unref(data->connection); + g_free(data); +} + +static void filter_data_call_and_free(struct filter_data *data) +{ + GSList *l; + + for (l = data->callbacks; l != NULL; l = l->next) { + struct filter_callback *cb = l->data; + if (cb->disc_func) + cb->disc_func(data->connection, cb->user_data); + if (cb->destroy_func) + cb->destroy_func(cb->user_data); + g_free(cb); + } + + filter_data_free(data); +} + +static struct filter_callback *filter_data_add_callback( + struct filter_data *data, + GDBusWatchFunction connect, + GDBusWatchFunction disconnect, + GDBusSignalFunction signal, + GDBusDestroyFunction destroy, + void *user_data) +{ + struct filter_callback *cb = NULL; + + cb = g_new0(struct filter_callback, 1); + + cb->conn_func = connect; + cb->disc_func = disconnect; + cb->signal_func = signal; + cb->destroy_func = destroy; + cb->user_data = user_data; + cb->id = ++listener_id; + + if (data->lock) + data->processed = g_slist_append(data->processed, cb); + else + data->callbacks = g_slist_append(data->callbacks, cb); + + return cb; +} + +static void service_data_free(struct service_data *data) +{ + struct filter_callback *callback = data->callback; + + dbus_connection_unref(data->conn); + + if (data->call) + dbus_pending_call_unref(data->call); + + if (data->id) + g_source_remove(data->id); + + g_free(data->name); + g_free(data); + + callback->data = NULL; +} + +static gboolean filter_data_remove_callback(struct filter_data *data, + struct filter_callback *cb) +{ + DBusConnection *connection; + + data->callbacks = g_slist_remove(data->callbacks, cb); + data->processed = g_slist_remove(data->processed, cb); + + /* Cancel pending operations */ + if (cb->data) { + if (cb->data->call) + dbus_pending_call_cancel(cb->data->call); + service_data_free(cb->data); + } + + if (cb->destroy_func) + cb->destroy_func(cb->user_data); + + g_free(cb); + + /* Don't remove the filter if other callbacks exist or data is lock + * processing callbacks */ + if (data->callbacks || data->lock) + return TRUE; + + if (data->registered && !remove_match(data)) + return FALSE; + + connection = dbus_connection_ref(data->connection); + listeners = g_slist_remove(listeners, data); + filter_data_free(data); + + /* Remove filter if there are no listeners left for the connection */ + data = filter_data_find(connection, NULL, NULL, NULL, NULL, NULL, + NULL); + if (data == NULL) + dbus_connection_remove_filter(connection, message_filter, + NULL); + + dbus_connection_unref(connection); + + return TRUE; +} + +static DBusHandlerResult signal_filter(DBusConnection *connection, + DBusMessage *message, void *user_data) +{ + struct filter_data *data = user_data; + struct filter_callback *cb; + + while (data->callbacks) { + cb = data->callbacks->data; + + if (cb->signal_func && !cb->signal_func(connection, message, + cb->user_data)) { + filter_data_remove_callback(data, cb); + continue; + } + + /* Check if the watch was removed/freed by the callback + * function */ + if (!g_slist_find(data->callbacks, cb)) + continue; + + data->callbacks = g_slist_remove(data->callbacks, cb); + data->processed = g_slist_append(data->processed, cb); + } + + return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; +} + +static void update_name_cache(const char *name, const char *owner) +{ + GSList *l; + + for (l = listeners; l != NULL; l = l->next) { + struct filter_data *data = l->data; + + if (g_strcmp0(data->name, name) != 0) + continue; + + g_free(data->owner); + data->owner = g_strdup(owner); + } +} + +static const char *check_name_cache(const char *name) +{ + GSList *l; + + for (l = listeners; l != NULL; l = l->next) { + struct filter_data *data = l->data; + + if (g_strcmp0(data->name, name) != 0) + continue; + + return data->owner; + } + + return NULL; +} + +static DBusHandlerResult service_filter(DBusConnection *connection, + DBusMessage *message, void *user_data) +{ + struct filter_data *data = user_data; + struct filter_callback *cb; + char *name, *old, *new; + + if (!dbus_message_get_args(message, NULL, + DBUS_TYPE_STRING, &name, + DBUS_TYPE_STRING, &old, + DBUS_TYPE_STRING, &new, + DBUS_TYPE_INVALID)) { + error("Invalid arguments for NameOwnerChanged signal"); + return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; + } + + update_name_cache(name, new); + + while (data->callbacks) { + cb = data->callbacks->data; + + if (*new == '\0') { + if (cb->disc_func) + cb->disc_func(connection, cb->user_data); + } else { + if (cb->conn_func) + cb->conn_func(connection, cb->user_data); + } + + /* Check if the watch was removed/freed by the callback + * function */ + if (!g_slist_find(data->callbacks, cb)) + continue; + + /* Only auto remove if it is a bus name watch */ + if (data->argument[0] == ':' && + (cb->conn_func == NULL || cb->disc_func == NULL)) { + filter_data_remove_callback(data, cb); + continue; + } + + data->callbacks = g_slist_remove(data->callbacks, cb); + data->processed = g_slist_append(data->processed, cb); + } + + return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; +} + + +static DBusHandlerResult message_filter(DBusConnection *connection, + DBusMessage *message, void *user_data) +{ + struct filter_data *data; + const char *sender, *path, *iface, *member, *arg = NULL; + + /* Only filter signals */ + if (dbus_message_get_type(message) != DBUS_MESSAGE_TYPE_SIGNAL) + return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; + + sender = dbus_message_get_sender(message); + path = dbus_message_get_path(message); + iface = dbus_message_get_interface(message); + member = dbus_message_get_member(message); + dbus_message_get_args(message, NULL, DBUS_TYPE_STRING, &arg, DBUS_TYPE_INVALID); + + /* Sender is always bus name */ + data = filter_data_find(connection, NULL, sender, path, iface, member, + arg); + if (data == NULL) { + error("Got %s.%s signal which has no listeners", iface, member); + return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; + } + + if (data->handle_func) { + data->lock = TRUE; + + data->handle_func(connection, message, data); + + data->callbacks = data->processed; + data->processed = NULL; + data->lock = FALSE; + } + + if (data->callbacks) + return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; + + remove_match(data); + + listeners = g_slist_remove(listeners, data); + filter_data_free(data); + + /* Remove filter if there no listener left for the connection */ + data = filter_data_find(connection, NULL, NULL, NULL, NULL, NULL, + NULL); + if (data == NULL) + dbus_connection_remove_filter(connection, message_filter, + NULL); + + return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; +} + +static gboolean update_service(void *user_data) +{ + struct service_data *data = user_data; + struct filter_callback *cb = data->callback; + + update_name_cache(data->name, data->owner); + if (cb->conn_func) + cb->conn_func(data->conn, cb->user_data); + + service_data_free(data); + + return FALSE; +} + +static void service_reply(DBusPendingCall *call, void *user_data) +{ + struct service_data *data = user_data; + DBusMessage *reply; + DBusError err; + + reply = dbus_pending_call_steal_reply(call); + if (reply == NULL) + return; + + dbus_error_init(&err); + + if (dbus_set_error_from_message(&err, reply)) + goto fail; + + if (dbus_message_get_args(reply, &err, + DBUS_TYPE_STRING, &data->owner, + DBUS_TYPE_INVALID) == FALSE) + goto fail; + + update_service(data); + + goto done; + +fail: + error("%s", err.message); + dbus_error_free(&err); + service_data_free(data); +done: + dbus_message_unref(reply); +} + +static void check_service(DBusConnection *connection, + const char *name, + struct filter_callback *callback) +{ + DBusMessage *message; + struct service_data *data; + + data = g_try_malloc0(sizeof(*data)); + if (data == NULL) { + error("Can't allocate data structure"); + return; + } + + data->conn = dbus_connection_ref(connection); + data->name = g_strdup(name); + data->callback = callback; + callback->data = data; + + data->owner = check_name_cache(name); + if (data->owner != NULL) { + data->id = g_idle_add(update_service, data); + return; + } + + message = dbus_message_new_method_call(DBUS_SERVICE_DBUS, + DBUS_PATH_DBUS, DBUS_INTERFACE_DBUS, "GetNameOwner"); + if (message == NULL) { + error("Can't allocate new message"); + g_free(data); + return; + } + + dbus_message_append_args(message, DBUS_TYPE_STRING, &name, + DBUS_TYPE_INVALID); + + if (dbus_connection_send_with_reply(connection, message, + &data->call, -1) == FALSE) { + error("Failed to execute method call"); + g_free(data); + goto done; + } + + if (data->call == NULL) { + error("D-Bus connection not available"); + g_free(data); + goto done; + } + + dbus_pending_call_set_notify(data->call, service_reply, data, NULL); + +done: + dbus_message_unref(message); +} + +guint g_dbus_add_service_watch(DBusConnection *connection, const char *name, + GDBusWatchFunction connect, + GDBusWatchFunction disconnect, + void *user_data, GDBusDestroyFunction destroy) +{ + struct filter_data *data; + struct filter_callback *cb; + + if (name == NULL) + return 0; + + data = filter_data_get(connection, service_filter, NULL, NULL, + DBUS_INTERFACE_DBUS, "NameOwnerChanged", + name); + if (data == NULL) + return 0; + + cb = filter_data_add_callback(data, connect, disconnect, NULL, NULL, + user_data); + if (cb == NULL) + return 0; + + if (connect) + check_service(connection, name, cb); + + return cb->id; +} + +guint g_dbus_add_disconnect_watch(DBusConnection *connection, const char *name, + GDBusWatchFunction func, + void *user_data, GDBusDestroyFunction destroy) +{ + return g_dbus_add_service_watch(connection, name, NULL, func, + user_data, destroy); +} + +guint g_dbus_add_signal_watch(DBusConnection *connection, + const char *sender, const char *path, + const char *interface, const char *member, + GDBusSignalFunction function, void *user_data, + GDBusDestroyFunction destroy) +{ + struct filter_data *data; + struct filter_callback *cb; + + data = filter_data_get(connection, signal_filter, sender, path, + interface, member, NULL); + if (data == NULL) + return 0; + + cb = filter_data_add_callback(data, NULL, NULL, function, destroy, + user_data); + if (cb == NULL) + return 0; + + if (data->name != NULL && data->name_watch == 0) + data->name_watch = g_dbus_add_service_watch(connection, + data->name, NULL, + NULL, NULL, NULL); + + return cb->id; +} + +gboolean g_dbus_remove_watch(DBusConnection *connection, guint id) +{ + struct filter_data *data; + struct filter_callback *cb; + GSList *ldata; + + if (id == 0) + return FALSE; + + for (ldata = listeners; ldata; ldata = ldata->next) { + data = ldata->data; + + cb = filter_data_find_callback(data, id); + if (cb) { + filter_data_remove_callback(data, cb); + return TRUE; + } + } + + return FALSE; +} + +void g_dbus_remove_all_watches(DBusConnection *connection) +{ + struct filter_data *data; + + while ((data = filter_data_find(connection, NULL, NULL, NULL, NULL, + NULL, NULL))) { + listeners = g_slist_remove(listeners, data); + filter_data_call_and_free(data); + } + + dbus_connection_remove_filter(connection, message_filter, NULL); +} diff --git a/include/dbus.h b/include/dbus.h new file mode 100644 index 0000000..0743e7a --- /dev/null +++ b/include/dbus.h @@ -0,0 +1,132 @@ +/* + * + * neard - Near Field Communication manager + * + * Copyright (C) 2011 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 <dbus/dbus.h> + +#define NFC_SERVICE "net.near" +#define NFC_PATH "/net/near" + +#define NFC_ERROR_INTERFACE NFC_SERVICE ".Error" + +#define NFC_MANAGER_INTERFACE NFC_SERVICE ".Manager" +#define NFC_MANAGER_PATH "/" + +typedef void (* near_dbus_append_cb_t) (DBusMessageIter *iter, + void *user_data); + +DBusConnection *near_dbus_get_connection(void); + +void near_dbus_property_append_basic(DBusMessageIter *iter, + const char *key, int type, void *val); +void near_dbus_property_append_dict(DBusMessageIter *iter, const char *key, + near_dbus_append_cb_t function, void *user_data); +void near_dbus_property_append_array(DBusMessageIter *iter, + const char *key, int type, + near_dbus_append_cb_t function, void *user_data); +void near_dbus_property_append_fixed_array(DBusMessageIter *iter, + const char *key, int type, void *val, int len); + +dbus_bool_t near_dbus_property_changed_basic(const char *path, + const char *interface, const char *key, + int type, void *val); +dbus_bool_t near_dbus_property_changed_dict(const char *path, + const char *interface, const char *key, + near_dbus_append_cb_t function, void *user_data); +dbus_bool_t near_dbus_property_changed_array(const char *path, + const char *interface, const char *key, int type, + near_dbus_append_cb_t function, void *user_data); + +dbus_bool_t near_dbus_setting_changed_basic(const char *owner, + const char *path, const char *key, + int type, void *val); +dbus_bool_t near_dbus_setting_changed_dict(const char *owner, + const char *path, const char *key, + near_dbus_append_cb_t function, + void *user_data); +dbus_bool_t near_dbus_setting_changed_array(const char *owner, + const char *path, const char *key, int type, + near_dbus_append_cb_t function, + void *user_data); + +static inline void near_dbus_dict_open(DBusMessageIter *iter, + DBusMessageIter *dict) +{ + dbus_message_iter_open_container(iter, DBUS_TYPE_ARRAY, + DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING + DBUS_TYPE_STRING_AS_STRING DBUS_TYPE_VARIANT_AS_STRING + DBUS_DICT_ENTRY_END_CHAR_AS_STRING, dict); +} + +static inline void near_dbus_dict_close(DBusMessageIter *iter, + DBusMessageIter *dict) +{ + dbus_message_iter_close_container(iter, dict); +} + +static inline void near_dbus_dict_append_basic(DBusMessageIter *dict, + const char *key, int type, void *val) +{ + DBusMessageIter entry; + + dbus_message_iter_open_container(dict, DBUS_TYPE_DICT_ENTRY, + NULL, &entry); + near_dbus_property_append_basic(&entry, key, type, val); + dbus_message_iter_close_container(dict, &entry); +} + +static inline void near_dbus_dict_append_dict(DBusMessageIter *dict, + const char *key, near_dbus_append_cb_t function, + void *user_data) +{ + DBusMessageIter entry; + + dbus_message_iter_open_container(dict, DBUS_TYPE_DICT_ENTRY, + NULL, &entry); + near_dbus_property_append_dict(&entry, key, function, user_data); + dbus_message_iter_close_container(dict, &entry); +} + +static inline void near_dbus_dict_append_array(DBusMessageIter *dict, + const char *key, int type, near_dbus_append_cb_t function, + void *user_data) +{ + DBusMessageIter entry; + + dbus_message_iter_open_container(dict, DBUS_TYPE_DICT_ENTRY, + NULL, &entry); + near_dbus_property_append_array(&entry, key, + type, function, user_data); + dbus_message_iter_close_container(dict, &entry); +} + +static inline void near_dbus_dict_append_fixed_array(DBusMessageIter *dict, + const char *key, int type, void *val, int len) +{ + DBusMessageIter entry; + + dbus_message_iter_open_container(dict, DBUS_TYPE_DICT_ENTRY, + NULL, &entry); + near_dbus_property_append_fixed_array(&entry, key, type, val, len); + dbus_message_iter_close_container(dict, &entry); +} + +dbus_bool_t near_dbus_validate_ident(const char *ident); +char *near_dbus_encode_string(const char *value); diff --git a/src/adapter.c b/src/adapter.c new file mode 100644 index 0000000..6f10679 --- /dev/null +++ b/src/adapter.c @@ -0,0 +1,62 @@ +/* + * + * neard - Near Field Communication manager + * + * Copyright (C) 2011 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 "near.h" + +static GSList *adapter_list = NULL; + +struct near_adapter { + char *path; +}; + +static void append_path(gpointer value, gpointer user_data) +{ + struct near_adapter *adapter = value; + DBusMessageIter *iter = user_data; + + if (adapter->path == NULL) + return; + + dbus_message_iter_append_basic(iter, DBUS_TYPE_OBJECT_PATH, + &adapter->path); +} + +void __near_adapter_list(DBusMessageIter *iter, void *user_data) +{ + g_slist_foreach(adapter_list, append_path, iter); +} + +int __near_adapter_init(void) +{ + DBG(""); + + return 0; +} + +void __near_adapter_cleanup(void) +{ +} diff --git a/src/dbus.c b/src/dbus.c new file mode 100644 index 0000000..232c844 --- /dev/null +++ b/src/dbus.c @@ -0,0 +1,294 @@ +/* + * + * neard - Near Field Communication manager + * + * Copyright (C) 2011 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 <gdbus.h> + +#include "near.h" + +static DBusConnection *connection; + +dbus_bool_t near_dbus_validate_ident(const char *ident) +{ + unsigned int i; + + if (ident == NULL) + return FALSE; + + for (i = 0; i < strlen(ident); i++) { + if (ident[i] >= '0' && ident[i] <= '9') + continue; + if (ident[i] >= 'a' && ident[i] <= 'z') + continue; + if (ident[i] >= 'A' && ident[i] <= 'Z') + continue; + return FALSE; + } + + return TRUE; +} + +char *near_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 near_dbus_property_append_basic(DBusMessageIter *iter, + const char *key, int type, void *val) +{ + DBusMessageIter value; + const char *signature; + + dbus_message_iter_append_basic(iter, DBUS_TYPE_STRING, &key); + + switch (type) { + case DBUS_TYPE_BOOLEAN: + signature = DBUS_TYPE_BOOLEAN_AS_STRING; + break; + case DBUS_TYPE_STRING: + signature = DBUS_TYPE_STRING_AS_STRING; + break; + case DBUS_TYPE_BYTE: + signature = DBUS_TYPE_BYTE_AS_STRING; + break; + case DBUS_TYPE_UINT16: + signature = DBUS_TYPE_UINT16_AS_STRING; + break; + case DBUS_TYPE_INT16: + signature = DBUS_TYPE_INT16_AS_STRING; + break; + case DBUS_TYPE_UINT32: + signature = DBUS_TYPE_UINT32_AS_STRING; + break; + case DBUS_TYPE_INT32: + signature = DBUS_TYPE_INT32_AS_STRING; + break; + case DBUS_TYPE_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 near_dbus_property_append_dict(DBusMessageIter *iter, const char *key, + near_dbus_append_cb_t function, void *user_data) +{ + DBusMessageIter value, dict; + + dbus_message_iter_append_basic(iter, DBUS_TYPE_STRING, &key); + + dbus_message_iter_open_container(iter, DBUS_TYPE_VARIANT, + DBUS_TYPE_ARRAY_AS_STRING + DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING + DBUS_TYPE_STRING_AS_STRING DBUS_TYPE_VARIANT_AS_STRING + DBUS_DICT_ENTRY_END_CHAR_AS_STRING, &value); + + near_dbus_dict_open(&value, &dict); + if (function) + function(&dict, user_data); + near_dbus_dict_close(&value, &dict); + + dbus_message_iter_close_container(iter, &value); +} + +void near_dbus_property_append_fixed_array(DBusMessageIter *iter, + const char *key, int type, void *val, int len) +{ + DBusMessageIter 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_append_basic(iter, DBUS_TYPE_STRING, &key); + + dbus_message_iter_open_container(iter, 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(iter, &value); +} + +void near_dbus_property_append_array(DBusMessageIter *iter, + const char *key, int type, + near_dbus_append_cb_t function, void *user_data) +{ + DBusMessageIter value, array; + const char *variant_sig, *array_sig; + + switch (type) { + case DBUS_TYPE_STRING: + variant_sig = DBUS_TYPE_ARRAY_AS_STRING DBUS_TYPE_STRING_AS_STRING; + array_sig = DBUS_TYPE_STRING_AS_STRING; + break; + case DBUS_TYPE_OBJECT_PATH: + variant_sig = DBUS_TYPE_ARRAY_AS_STRING DBUS_TYPE_OBJECT_PATH_AS_STRING; + array_sig = DBUS_TYPE_OBJECT_PATH_AS_STRING; + break; + default: + return; + } + + dbus_message_iter_append_basic(iter, DBUS_TYPE_STRING, &key); + + dbus_message_iter_open_container(iter, DBUS_TYPE_VARIANT, + variant_sig, &value); + + dbus_message_iter_open_container(&value, DBUS_TYPE_ARRAY, + array_sig, &array); + if (function) + function(&array, user_data); + dbus_message_iter_close_container(&value, &array); + + dbus_message_iter_close_container(iter, &value); +} + +static DBusConnection *connection = NULL; + +dbus_bool_t near_dbus_property_changed_basic(const char *path, + const char *interface, const char *key, + int type, void *val) +{ + DBusMessage *signal; + DBusMessageIter iter; + + if (path == NULL) + return FALSE; + + signal = dbus_message_new_signal(path, interface, "PropertyChanged"); + if (signal == NULL) + return FALSE; + + dbus_message_iter_init_append(signal, &iter); + near_dbus_property_append_basic(&iter, key, type, val); + + g_dbus_send_message(connection, signal); + + return TRUE; +} + +dbus_bool_t near_dbus_property_changed_dict(const char *path, + const char *interface, const char *key, + near_dbus_append_cb_t function, void *user_data) +{ + DBusMessage *signal; + DBusMessageIter iter; + + if (path == NULL) + return FALSE; + + signal = dbus_message_new_signal(path, interface, "PropertyChanged"); + if (signal == NULL) + return FALSE; + + dbus_message_iter_init_append(signal, &iter); + near_dbus_property_append_dict(&iter, key, function, user_data); + + g_dbus_send_message(connection, signal); + + return TRUE; +} + +dbus_bool_t near_dbus_property_changed_array(const char *path, + const char *interface, const char *key, int type, + near_dbus_append_cb_t function, void *user_data) +{ + DBusMessage *signal; + DBusMessageIter iter; + + if (path == NULL) + return FALSE; + + signal = dbus_message_new_signal(path, interface, "PropertyChanged"); + if (signal == NULL) + return FALSE; + + dbus_message_iter_init_append(signal, &iter); + near_dbus_property_append_array(&iter, key, type, + function, user_data); + + g_dbus_send_message(connection, signal); + + return TRUE; +} + +DBusConnection *near_dbus_get_connection(void) +{ + return connection; +} + +int __near_dbus_init(DBusConnection *conn) +{ + DBG(""); + + connection = conn; + + return 0; +} + +void __near_dbus_cleanup(void) +{ + DBG(""); + + connection = NULL; +} diff --git a/src/error.c b/src/error.c new file mode 100644 index 0000000..f166380 --- /dev/null +++ b/src/error.c @@ -0,0 +1,185 @@ +/* + * + * neard - Near Field Communication manager + * + * Copyright (C) 2011 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 "near.h" + +DBusMessage *__near_error_failed(DBusMessage *msg, int errnum) +{ + const char *str = strerror(errnum); + + switch (errnum) { + case ESRCH: + return __near_error_not_registered(msg); + case ENXIO: + return __near_error_not_found(msg); + case EACCES: + return __near_error_permission_denied(msg); + case EEXIST: + return __near_error_already_exists(msg); + case EINVAL: + return __near_error_invalid_arguments(msg); + case ENOSYS: + return __near_error_not_implemented(msg); + case ENOLINK: + return __near_error_no_carrier(msg); + case ENOTUNIQ: + return __near_error_not_unique(msg); + case EOPNOTSUPP: + return __near_error_not_supported(msg); + case ECONNABORTED: + return __near_error_operation_aborted(msg); + case EISCONN: + return __near_error_already_connected(msg); + case ENOTCONN: + return __near_error_not_connected(msg); + case ETIMEDOUT: + return __near_error_operation_timeout(msg); + case EALREADY: + return __near_error_in_progress(msg); + case ENOKEY: + return __near_error_passphrase_required(msg); + } + + return g_dbus_create_error(msg, NFC_ERROR_INTERFACE + ".Failed", "%s", str); +} + +DBusMessage *__near_error_invalid_arguments(DBusMessage *msg) +{ + return g_dbus_create_error(msg, NFC_ERROR_INTERFACE + ".InvalidArguments", "Invalid arguments"); +} + +DBusMessage *__near_error_permission_denied(DBusMessage *msg) +{ + return g_dbus_create_error(msg, NFC_ERROR_INTERFACE + ".PermissionDenied", "Permission denied"); +} + +DBusMessage *__near_error_passphrase_required(DBusMessage *msg) +{ + return g_dbus_create_error(msg, NFC_ERROR_INTERFACE + ".PassphraseRequired", "Passphrase required"); +} + +DBusMessage *__near_error_not_registered(DBusMessage *msg) +{ + return g_dbus_create_error(msg, NFC_ERROR_INTERFACE + ".NotRegistered", "Not registered"); +} + +DBusMessage *__near_error_not_unique(DBusMessage *msg) +{ + return g_dbus_create_error(msg, NFC_ERROR_INTERFACE + ".NotUnique", "Not unique"); +} + +DBusMessage *__near_error_not_supported(DBusMessage *msg) +{ + return g_dbus_create_error(msg, NFC_ERROR_INTERFACE + ".NotSupported", "Not supported"); +} + +DBusMessage *__near_error_not_implemented(DBusMessage *msg) +{ + return g_dbus_create_error(msg, NFC_ERROR_INTERFACE + ".NotImplemented", "Not implemented"); +} + +DBusMessage *__near_error_not_found(DBusMessage *msg) +{ + return g_dbus_create_error(msg, NFC_ERROR_INTERFACE + ".NotFound", "Not found"); +} + +DBusMessage *__near_error_no_carrier(DBusMessage *msg) +{ + return g_dbus_create_error(msg, NFC_ERROR_INTERFACE + ".NoCarrier", "No carrier"); +} + +DBusMessage *__near_error_in_progress(DBusMessage *msg) +{ + return g_dbus_create_error(msg, NFC_ERROR_INTERFACE + ".InProgress", "In progress"); +} + +DBusMessage *__near_error_already_exists(DBusMessage *msg) +{ + return g_dbus_create_error(msg, NFC_ERROR_INTERFACE + ".AlreadyExists", "Already exists"); +} + +DBusMessage *__near_error_already_enabled(DBusMessage *msg) +{ + return g_dbus_create_error(msg, NFC_ERROR_INTERFACE + ".AlreadyEnabled", "Already enabled"); +} + +DBusMessage *__near_error_already_disabled(DBusMessage *msg) +{ + return g_dbus_create_error(msg, NFC_ERROR_INTERFACE + ".AlreadyDisabled", "Already disabled"); +} + +DBusMessage *__near_error_already_connected(DBusMessage *msg) +{ + return g_dbus_create_error(msg, NFC_ERROR_INTERFACE + ".AlreadyConnected", "Already connected"); +} + +DBusMessage *__near_error_not_connected(DBusMessage *msg) +{ + return g_dbus_create_error(msg, NFC_ERROR_INTERFACE + ".NotConnected", "Not connected"); +} +DBusMessage *__near_error_operation_aborted(DBusMessage *msg) +{ + return g_dbus_create_error(msg, NFC_ERROR_INTERFACE + ".OperationAborted", "Operation aborted"); +} + +DBusMessage *__near_error_operation_timeout(DBusMessage *msg) +{ + return g_dbus_create_error(msg, NFC_ERROR_INTERFACE + ".OperationTimeout", "Operation timeout"); +} + +DBusMessage *__near_error_invalid_service(DBusMessage *msg) +{ + return g_dbus_create_error(msg, NFC_ERROR_INTERFACE + ".InvalidService", "Invalid service"); +} + +DBusMessage *__near_error_invalid_property(DBusMessage *msg) +{ + return g_dbus_create_error(msg, NFC_ERROR_INTERFACE + ".InvalidProperty", "Invalid property"); +} diff --git a/src/genbuiltin b/src/genbuiltin new file mode 100755 index 0000000..9c2ba15 --- /dev/null +++ b/src/genbuiltin @@ -0,0 +1,17 @@ +#!/bin/sh + +for i in $* +do + echo "extern struct near_plugin_desc __near_builtin_$i;" +done + +echo +echo "static struct near_plugin_desc *__near_builtin[] = {" + +for i in $* +do + echo " &__near_builtin_$i," +done + +echo " NULL" +echo "};" diff --git a/src/log.c b/src/log.c new file mode 100644 index 0000000..99fb984 --- /dev/null +++ b/src/log.c @@ -0,0 +1,138 @@ +/* + * + * neard - Near Field Communication manager + * + * Copyright (C) 2011 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 "near.h" + +void near_info(const char *format, ...) +{ + va_list ap; + + va_start(ap, format); + + vsyslog(LOG_INFO, format, ap); + + va_end(ap); +} + +void near_warn(const char *format, ...) +{ + va_list ap; + + va_start(ap, format); + + vsyslog(LOG_WARNING, format, ap); + + va_end(ap); +} + +void near_error(const char *format, ...) +{ + va_list ap; + + va_start(ap, format); + + vsyslog(LOG_ERR, format, ap); + + va_end(ap); +} + +void near_debug(const char *format, ...) +{ + va_list ap; + + va_start(ap, format); + + vsyslog(LOG_DEBUG, format, ap); + + va_end(ap); +} + +extern struct near_debug_desc __start___debug[]; +extern struct near_debug_desc __stop___debug[]; + +static gchar **enabled = NULL; + +static gboolean is_enabled(struct near_debug_desc *desc) +{ + int i; + + if (enabled == NULL) + return FALSE; + + for (i = 0; enabled[i] != NULL; i++) { + if (desc->name != NULL && g_pattern_match_simple(enabled[i], + desc->name) == TRUE) + return TRUE; + if (desc->file != NULL && g_pattern_match_simple(enabled[i], + desc->file) == TRUE) + return TRUE; + } + + return FALSE; +} + +int __near_log_init(const char *debug, gboolean detach) +{ + int option = LOG_NDELAY | LOG_PID; + struct near_debug_desc *desc; + const char *name = NULL, *file = NULL; + + if (debug != NULL) + enabled = g_strsplit_set(debug, ":, ", 0); + + for (desc = __start___debug; desc < __stop___debug; desc++) { + if (file != NULL || name != NULL) { + if (g_strcmp0(desc->file, file) == 0) { + if (desc->name == NULL) + desc->name = name; + } else + file = NULL; + } + + if (is_enabled(desc) == TRUE) + desc->flags |= NEAR_DEBUG_FLAG_PRINT; + } + + if (detach == FALSE) + option |= LOG_PERROR; + + openlog("neard", option, LOG_DAEMON); + + syslog(LOG_INFO, "NEAR daemon version %s", VERSION); + + return 0; +} + +void __near_log_cleanup(void) +{ + syslog(LOG_INFO, "Exit"); + + closelog(); + + g_strfreev(enabled); +} diff --git a/src/log.h b/src/log.h new file mode 100644 index 0000000..6183b88 --- /dev/null +++ b/src/log.h @@ -0,0 +1,47 @@ +/* + * + * neard - Near Field Communication manager + * + * Copyright (C) 2011 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 + * + */ + +void near_info(const char *format, ...) + __attribute__((format(printf, 1, 2))); +void near_warn(const char *format, ...) + __attribute__((format(printf, 1, 2))); +void near_error(const char *format, ...) + __attribute__((format(printf, 1, 2))); +void near_debug(const char *format, ...) + __attribute__((format(printf, 1, 2))); + +struct near_debug_desc { + const char *name; + const char *file; +#define NEAR_DEBUG_FLAG_DEFAULT (0) +#define NEAR_DEBUG_FLAG_PRINT (1 << 0) + unsigned int flags; +} __attribute__((aligned(8))); + +#define DBG(fmt, arg...) do { \ + static struct near_debug_desc __near_debug_desc \ + __attribute__((used, section("__debug"), aligned(8))) = { \ + .file = __FILE__, .flags = NEAR_DEBUG_FLAG_DEFAULT, \ + }; \ + if (__near_debug_desc.flags & NEAR_DEBUG_FLAG_PRINT) \ + near_debug("%s:%s() " fmt, \ + __FILE__, __FUNCTION__ , ## arg); \ +} while (0) diff --git a/src/main.c b/src/main.c new file mode 100644 index 0000000..63e528e --- /dev/null +++ b/src/main.c @@ -0,0 +1,160 @@ +/* + * + * neard - Near Field Communication manager + * + * Copyright (C) 2011 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 <gdbus.h> + +#include "near.h" + +static GMainLoop *main_loop = NULL; + +static volatile sig_atomic_t __terminated = 0; + +static void sig_term(int sig) +{ + if (__terminated > 0) + return; + + __terminated = 1; + + near_info("Terminating"); + + g_main_loop_quit(main_loop); +} + +static void disconnect_callback(DBusConnection *conn, void *user_data) +{ + near_error("D-Bus disconnect"); + + g_main_loop_quit(main_loop); +} + +static gchar *option_debug = NULL; +static gboolean option_detach = TRUE; +static gboolean option_version = FALSE; + +static gboolean parse_debug(const char *key, const char *value, + gpointer user_data, GError **error) +{ + if (value) + option_debug = g_strdup(value); + else + option_debug = g_strdup("*"); + + return TRUE; +} + +static GOptionEntry options[] = { + { "debug", 'd', G_OPTION_FLAG_OPTIONAL_ARG, + G_OPTION_ARG_CALLBACK, parse_debug, + "Specify debug options to enable", "DEBUG" }, + { "nodaemon", 'n', G_OPTION_FLAG_REVERSE, + G_OPTION_ARG_NONE, &option_detach, + "Don't fork daemon to background" }, + { "version", 'v', 0, G_OPTION_ARG_NONE, &option_version, + "Show version information and exit" }, + { NULL }, +}; + +int main(int argc, char *argv[]) +{ + GOptionContext *context; + GError *error = NULL; + DBusConnection *conn; + DBusError err; + struct sigaction sa; + + 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); + } + } + + main_loop = g_main_loop_new(NULL, FALSE); + + dbus_error_init(&err); + + conn = g_dbus_setup_bus(DBUS_BUS_SESSION, NFC_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 session bus\n"); + exit(1); + } + + g_dbus_set_disconnect_function(conn, disconnect_callback, NULL, NULL); + + __near_log_init(option_debug, option_detach); + __near_dbus_init(conn); + + __near_manager_init(conn); + __near_adapter_init(); + + memset(&sa, 0, sizeof(sa)); + sa.sa_handler = sig_term; + sigaction(SIGINT, &sa, NULL); + sigaction(SIGTERM, &sa, NULL); + + g_main_loop_run(main_loop); + + __near_adapter_cleanup(); + __near_manager_cleanup(); + + __near_dbus_cleanup(); + __near_log_cleanup(); + + dbus_connection_unref(conn); + + g_main_loop_unref(main_loop); + + return 0; +} diff --git a/src/manager.c b/src/manager.c new file mode 100644 index 0000000..e071979 --- /dev/null +++ b/src/manager.c @@ -0,0 +1,101 @@ +/* + * + * neard - Near Field Communication manager + * + * Copyright (C) 2011 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 "near.h" + +static DBusMessage *get_properties(DBusConnection *conn, + DBusMessage *msg, void *data) +{ + DBusMessage *reply; + DBusMessageIter array, dict; + + DBG("conn %p", conn); + + reply = dbus_message_new_method_return(msg); + if (reply == NULL) + return NULL; + + dbus_message_iter_init_append(reply, &array); + + near_dbus_dict_open(&array, &dict); + + near_dbus_dict_append_array(&dict, "Adapters", + DBUS_TYPE_OBJECT_PATH, __near_adapter_list, NULL); + + near_dbus_dict_close(&array, &dict); + + return reply; +} + +static DBusMessage *set_property(DBusConnection *conn, + DBusMessage *msg, void *data) +{ + DBG("conn %p", conn); + + return g_dbus_create_reply(msg, DBUS_TYPE_INVALID); +} + +static GDBusMethodTable manager_methods[] = { + { "GetProperties", "", "a{sv}", get_properties }, + { "SetProperty", "sv", "", set_property }, + { }, +}; + +static GDBusSignalTable manager_signals[] = { + { "PropertyChanged", "sv" }, + { "AdapterAdded", "o" }, + { "AdapterRemoved", "o" }, + { } +}; + +static DBusConnection *connection; + +int __near_manager_init(DBusConnection *conn) +{ + DBG(""); + + connection = dbus_connection_ref(conn); + + g_dbus_register_interface(connection, NFC_MANAGER_PATH, + NFC_MANAGER_INTERFACE, + manager_methods, + manager_signals, NULL, NULL, NULL); + + return 0; +} + +void __near_manager_cleanup(void) +{ + DBG(""); + + g_dbus_unregister_interface(connection, NFC_MANAGER_PATH, + NFC_MANAGER_INTERFACE); + + dbus_connection_unref(connection); +} diff --git a/src/near.h b/src/near.h new file mode 100644 index 0000000..25f14c6 --- /dev/null +++ b/src/near.h @@ -0,0 +1,60 @@ +/* + * + * Near Field Communication + * + * Copyright (C) 2011 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> + +#include "log.h" + +int __near_log_init(const char *debug, gboolean detach); +void __near_log_cleanup(void); + +#include <near/dbus.h> + +int __near_dbus_init(DBusConnection *conn); +void __near_dbus_cleanup(void); + +DBusMessage *__near_error_failed(DBusMessage *msg, int errnum); +DBusMessage *__near_error_invalid_arguments(DBusMessage *msg); +DBusMessage *__near_error_permission_denied(DBusMessage *msg); +DBusMessage *__near_error_passphrase_required(DBusMessage *msg); +DBusMessage *__near_error_not_registered(DBusMessage *msg); +DBusMessage *__near_error_not_unique(DBusMessage *msg); +DBusMessage *__near_error_not_supported(DBusMessage *msg); +DBusMessage *__near_error_not_implemented(DBusMessage *msg); +DBusMessage *__near_error_not_found(DBusMessage *msg); +DBusMessage *__near_error_no_carrier(DBusMessage *msg); +DBusMessage *__near_error_in_progress(DBusMessage *msg); +DBusMessage *__near_error_already_exists(DBusMessage *msg); +DBusMessage *__near_error_already_enabled(DBusMessage *msg); +DBusMessage *__near_error_already_disabled(DBusMessage *msg); +DBusMessage *__near_error_already_connected(DBusMessage *msg); +DBusMessage *__near_error_not_connected(DBusMessage *msg); +DBusMessage *__near_error_operation_aborted(DBusMessage *msg); +DBusMessage *__near_error_operation_timeout(DBusMessage *msg); +DBusMessage *__near_error_invalid_service(DBusMessage *msg); +DBusMessage *__near_error_invalid_property(DBusMessage *msg); + +int __near_manager_init(DBusConnection *conn); +void __near_manager_cleanup(void); + +int __near_adapter_init(void); +void __near_adapter_cleanup(void); +void __near_adapter_list(DBusMessageIter *iter, void *user_data); diff --git a/src/types.h b/src/types.h new file mode 100644 index 0000000..ef7fdec --- /dev/null +++ b/src/types.h @@ -0,0 +1,31 @@ +/* + * + * neard - Near Field Communication manager + * + * Copyright (C) 2011 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 FALSE +#define FALSE (0) +#endif + +#ifndef TRUE +#define TRUE (!FALSE) +#endif + +typedef int near_bool_t; + |