mirror of
https://github.com/haiwen/ccnet-server.git
synced 2025-04-27 10:20:49 +00:00
Initial commit of Ccnet server.
This commit is contained in:
commit
ed8404c061
90
.gitignore
vendored
Normal file
90
.gitignore
vendored
Normal file
@ -0,0 +1,90 @@
|
||||
*~
|
||||
*.bak
|
||||
*.o
|
||||
*.exe
|
||||
cscope*
|
||||
*#
|
||||
Makefile.in
|
||||
net/Makefile
|
||||
net/.deps
|
||||
ltmain.sh
|
||||
libtool
|
||||
*.lo
|
||||
*.la
|
||||
lib/libccnet.a
|
||||
lib/libcommon.a
|
||||
lib/ccnetevent.c
|
||||
lib/ccnetobj.c
|
||||
lib/event-object.h
|
||||
lib/ccnet-object.h
|
||||
net/ccnet-test
|
||||
install-sh
|
||||
depcomp
|
||||
config.guess
|
||||
config.h
|
||||
config.log
|
||||
config.status
|
||||
config.sub
|
||||
config.cache
|
||||
configure
|
||||
*/.deps
|
||||
cli/Makefile
|
||||
cli/ccnet-cli
|
||||
cli/ccnet-create-group
|
||||
autom4te*
|
||||
po/POTFILES
|
||||
missing
|
||||
mkinstalldirs
|
||||
stamp-h1
|
||||
*.libs/
|
||||
Makefile
|
||||
aclocal.m4
|
||||
gtk/ccnet-gtk
|
||||
*core
|
||||
tools/ccnet-init
|
||||
gtk/ccnet-board
|
||||
gtk/ccnet-msgview
|
||||
data/new-conf*
|
||||
m4/intltool.m4
|
||||
m4/libtool.m4
|
||||
m4/ltoptions.m4
|
||||
m4/ltsugar.m4
|
||||
m4/ltversion.m4
|
||||
m4/lt~obsolete.m4
|
||||
ccnet-*.tar.gz
|
||||
config.h.in
|
||||
py-compile
|
||||
python/ccnet/ccnet.c
|
||||
*.stamp
|
||||
include/ccnet/buildin-types.h
|
||||
lib/buildin-types.c
|
||||
*.pyc
|
||||
*.tmp.ui
|
||||
*.defs
|
||||
*.log
|
||||
.deps
|
||||
*.db
|
||||
cli/ccnet-servtool
|
||||
cli/ccnet-tool
|
||||
tests/peer-add/conf1/c882e263e9d02c63ca6b61c68508761cbc74c358.peer
|
||||
tests/peer-add/conf1/c882e263e9d02c63ca6b61c68508761cbc74c358.user
|
||||
*.dll
|
||||
*.aps
|
||||
*.so
|
||||
build-stamp
|
||||
debian/files
|
||||
debian/seafile
|
||||
debian/*.substvars
|
||||
net/daemon/ccnet
|
||||
net/server/ccnet-server
|
||||
lib/searpc-marshal.h
|
||||
lib/searpc-signature.h
|
||||
lib/rpc_table.stamp
|
||||
lib/rpc_table.tmp
|
||||
net/cluster/ccnet-cserver
|
||||
tests/common-conf.sh
|
||||
libccnet.pc
|
||||
demo/ccnet-demo-client
|
||||
demo/ccnet-demo-server
|
||||
net/daemon/ccnet-test
|
||||
/compile
|
18
.travis.yml
Normal file
18
.travis.yml
Normal file
@ -0,0 +1,18 @@
|
||||
sudo: false
|
||||
language: c
|
||||
compiler:
|
||||
- gcc
|
||||
- clang
|
||||
addons:
|
||||
apt:
|
||||
packages:
|
||||
- valac
|
||||
- uuid-dev
|
||||
- libevent-dev
|
||||
- re2c
|
||||
- libjansson-dev
|
||||
cache:
|
||||
directories:
|
||||
- $HOME/downloads
|
||||
script:
|
||||
- ./ci/travis.sh
|
126
HACKING
Normal file
126
HACKING
Normal file
@ -0,0 +1,126 @@
|
||||
|
||||
Terminology
|
||||
===========
|
||||
|
||||
* Peer
|
||||
* Ccnet daemon
|
||||
* Client
|
||||
* Service daemon: for example seafile daemon.
|
||||
|
||||
|
||||
Peer Transfer Layer
|
||||
===================
|
||||
|
||||
Communication between peer is packet oriented.
|
||||
|
||||
The packet structure is defined in `lib/packet.h` as
|
||||
|
||||
struct ccnet_header {
|
||||
uint8_t version;
|
||||
uint8_t type;
|
||||
uint16_t length; /* length of payload */
|
||||
uint32_t id; /* for identify a service session id */
|
||||
};
|
||||
|
||||
struct ccnet_packet {
|
||||
struct ccnet_header header;
|
||||
char data[0];
|
||||
};
|
||||
|
||||
So the max length of payload is 65535.
|
||||
|
||||
|
||||
Service Invocation
|
||||
==================
|
||||
|
||||
Ccnet provide a service invocation layer upon transfer layer.
|
||||
|
||||
Local Service Invoking
|
||||
----------------------
|
||||
|
||||
A local service is provided either by ccnet daemon or a service
|
||||
daemon. To invoke a local service, client first sends a REQUEST
|
||||
packet containing the service name and arguments to ccnet
|
||||
daemon. Ccnet daemon will find the daemon who provide the service and
|
||||
start the service. This starts a `service session`. A service session
|
||||
is identified by a unique id. Later communication for this session
|
||||
using REQUEST and UPDATE packets. These packets constains the unique
|
||||
id in their header for the partners to identify the session.
|
||||
|
||||
<service-name> <service-name>
|
||||
Client --------------> Ccnet Daemon --------------> Service Daemon
|
||||
|
||||
UPDATE UPDATE
|
||||
Client --------------> Ccnet Daemon ------------> Service Daemon
|
||||
<------------- <------------
|
||||
RESPONSE RESPONSE
|
||||
|
||||
|
||||
Remote Service Invoking
|
||||
-----------------------
|
||||
|
||||
|
||||
remote <peer-id> <service-name> <service-name>
|
||||
Client -----------------------> Ccnet Daemon --------------> Remote Ccnet
|
||||
|
||||
UPDATE UPDATE
|
||||
Client -----------------------> Ccnet Daemon --------------> Remote Ccnet
|
||||
<----------------------- <--------------
|
||||
RESPONSE RESPONSE
|
||||
|
||||
Note: if <peer-id> is self, remote service invoking will be
|
||||
automatically turned to local service invoking.
|
||||
|
||||
|
||||
Service Registration
|
||||
--------------------
|
||||
|
||||
The daemons implements a service by a subclass of `CcnetProcessor` and
|
||||
registers it to ccnet daemon by calling `ccnet_register_service()`:
|
||||
|
||||
CcnetClient *client;
|
||||
ccnet_register_service (client, "seafile-rpcserver",
|
||||
CCNET_TYPE_RPCSERVER_PROC);
|
||||
|
||||
This is used by the seafile daemon to register service
|
||||
"seafile-rpcserver" to ccnet daemon.
|
||||
|
||||
Inside the ccnet daemon, a service is registered simply by calling
|
||||
|
||||
void
|
||||
ccnet_proc_factory_register_processor (CcnetProcFactory *factory,
|
||||
const char *proc_name,
|
||||
GType type);
|
||||
|
||||
|
||||
Processor Management
|
||||
====================
|
||||
|
||||
Local Service Invoking
|
||||
----------------------
|
||||
|
||||
To support local service invoking, a service proxy processor and a
|
||||
service stub processor will be started when necessary. The runtime
|
||||
configuration is as following:
|
||||
|
||||
Local Client Ccnet Daemon Service Daemon
|
||||
----------------------------------
|
||||
Client Processor ---> | Service Proxy --> Service Stub | --> Daemon Processor
|
||||
----------------------------------
|
||||
|
||||
Remote Service Invoking
|
||||
-----------------------
|
||||
|
||||
The runtime configuration is as following:
|
||||
|
||||
Local Client Ccnet Daemon Remote Ccnet Daemon
|
||||
----------------------------------
|
||||
Client Processor ---> | Service Proxy --> Service Stub | --> Daemon Processor
|
||||
----------------------------------
|
||||
|
||||
|
||||
Notes when using processor
|
||||
--------------------------
|
||||
|
||||
* A class of processor may provide different services, we use 'processor->name'
|
||||
to remember which service an instance of a processor class provided.
|
255
LICENSE.txt
Normal file
255
LICENSE.txt
Normal file
@ -0,0 +1,255 @@
|
||||
This program is released under Affero GPLv3, with the following additional
|
||||
permission to link with OpenSSL library.
|
||||
|
||||
If you modify this program, or any covered work, by linking or
|
||||
combining it with the OpenSSL project's OpenSSL library (or a
|
||||
modified version of that library), containing parts covered by the
|
||||
terms of the OpenSSL or SSLeay licenses, Seafile Ltd.
|
||||
grants you additional permission to convey the resulting work.
|
||||
Corresponding Source for a non-source form of such a combination
|
||||
shall include the source code for the parts of OpenSSL used as well
|
||||
as that of the covered work.
|
||||
|
||||
The source code files under 'python' directory is released under
|
||||
Apache License v2.0. You can find Apache License 2.0 file in that
|
||||
directory.
|
||||
|
||||
Some individual files have their speicific license:
|
||||
|
||||
* lib/buffer.c is from libevent, so it has a BSD-3-clause license.
|
||||
|
||||
Copyright (c) 2002-2007 Niels Provos <provos@citi.umich.edu>
|
||||
Copyright (c) 2007-2010 Niels Provos and Nick Mathewson
|
||||
|
||||
|
||||
GNU AFFERO GENERAL PUBLIC LICENSE
|
||||
|
||||
Version 3, 19 November 2007
|
||||
|
||||
Copyright © 2007 Free Software Foundation, Inc. <http://fsf.org/>
|
||||
Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed.
|
||||
|
||||
Preamble
|
||||
|
||||
The GNU Affero General Public License is a free, copyleft license for software and other kinds of works, specifically designed to ensure cooperation with the community in the case of network server software.
|
||||
|
||||
The licenses for most software and other practical works are designed to take away your freedom to share and change the works. By contrast, our General Public Licenses are intended to guarantee your freedom to share and change all versions of a program--to make sure it remains free software for all its users.
|
||||
|
||||
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 them 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.
|
||||
|
||||
Developers that use our General Public Licenses protect your rights with two steps: (1) assert copyright on the software, and (2) offer you this License which gives you legal permission to copy, distribute and/or modify the software.
|
||||
|
||||
A secondary benefit of defending all users' freedom is that improvements made in alternate versions of the program, if they receive widespread use, become available for other developers to incorporate. Many developers of free software are heartened and encouraged by the resulting cooperation. However, in the case of software used on network servers, this result may fail to come about. The GNU General Public License permits making a modified version and letting the public access it on a server without ever releasing its source code to the public.
|
||||
|
||||
The GNU Affero General Public License is designed specifically to ensure that, in such cases, the modified source code becomes available to the community. It requires the operator of a network server to provide the source code of the modified version running there to the users of that server. Therefore, public use of a modified version, on a publicly accessible server, gives the public access to the source code of the modified version.
|
||||
|
||||
An older license, called the Affero General Public License and published by Affero, was designed to accomplish similar goals. This is a different license, not a version of the Affero GPL, but Affero has released a new version of the Affero GPL which permits relicensing under this license.
|
||||
|
||||
The precise terms and conditions for copying, distribution and modification follow.
|
||||
|
||||
TERMS AND CONDITIONS
|
||||
|
||||
0. Definitions.
|
||||
|
||||
"This License" refers to version 3 of the GNU Affero General Public License.
|
||||
|
||||
"Copyright" also means copyright-like laws that apply to other kinds of works, such as semiconductor masks.
|
||||
|
||||
"The Program" refers to any copyrightable work licensed under this License. Each licensee is addressed as "you". "Licensees" and "recipients" may be individuals or organizations.
|
||||
|
||||
To "modify" a work means to copy from or adapt all or part of the work in a fashion requiring copyright permission, other than the making of an exact copy. The resulting work is called a "modified version" of the earlier work or a work "based on" the earlier work.
|
||||
|
||||
A "covered work" means either the unmodified Program or a work based on the Program.
|
||||
|
||||
To "propagate" a work means to do anything with it that, without permission, would make you directly or secondarily liable for infringement under applicable copyright law, except executing it on a computer or modifying a private copy. Propagation includes copying, distribution (with or without modification), making available to the public, and in some countries other activities as well.
|
||||
|
||||
To "convey" a work means any kind of propagation that enables other parties to make or receive copies. Mere interaction with a user through a computer network, with no transfer of a copy, is not conveying.
|
||||
|
||||
An interactive user interface displays "Appropriate Legal Notices" to the extent that it includes a convenient and prominently visible feature that (1) displays an appropriate copyright notice, and (2) tells the user that there is no warranty for the work (except to the extent that warranties are provided), that licensees may convey the work under this License, and how to view a copy of this License. If the interface presents a list of user commands or options, such as a menu, a prominent item in the list meets this criterion.
|
||||
|
||||
1. Source Code.
|
||||
|
||||
The "source code" for a work means the preferred form of the work for making modifications to it. "Object code" means any non-source form of a work.
|
||||
|
||||
A "Standard Interface" means an interface that either is an official standard defined by a recognized standards body, or, in the case of interfaces specified for a particular programming language, one that is widely used among developers working in that language.
|
||||
|
||||
The "System Libraries" of an executable work include anything, other than the work as a whole, that (a) is included in the normal form of packaging a Major Component, but which is not part of that Major Component, and (b) serves only to enable use of the work with that Major Component, or to implement a Standard Interface for which an implementation is available to the public in source code form. A "Major Component", in this context, means a major essential component (kernel, window system, and so on) of the specific operating system (if any) on which the executable work runs, or a compiler used to produce the work, or an object code interpreter used to run it.
|
||||
|
||||
The "Corresponding Source" for a work in object code form means all the source code needed to generate, install, and (for an executable work) run the object code and to modify the work, including scripts to control those activities. However, it does not include the work's System Libraries, or general-purpose tools or generally available free programs which are used unmodified in performing those activities but which are not part of the work. For example, Corresponding Source includes interface definition files associated with source files for the work, and the source code for shared libraries and dynamically linked subprograms that the work is specifically designed to require, such as by intimate data communication or control flow between those subprograms and other parts of the work.
|
||||
|
||||
The Corresponding Source need not include anything that users can regenerate automatically from other parts of the Corresponding Source.
|
||||
|
||||
The Corresponding Source for a work in source code form is that same work.
|
||||
|
||||
2. Basic Permissions.
|
||||
|
||||
All rights granted under this License are granted for the term of copyright on the Program, and are irrevocable provided the stated conditions are met. This License explicitly affirms your unlimited permission to run the unmodified Program. The output from running a covered work is covered by this License only if the output, given its content, constitutes a covered work. This License acknowledges your rights of fair use or other equivalent, as provided by copyright law.
|
||||
|
||||
You may make, run and propagate covered works that you do not convey, without conditions so long as your license otherwise remains in force. You may convey covered works to others for the sole purpose of having them make modifications exclusively for you, or provide you with facilities for running those works, provided that you comply with the terms of this License in conveying all material for which you do not control copyright. Those thus making or running the covered works for you must do so exclusively on your behalf, under your direction and control, on terms that prohibit them from making any copies of your copyrighted material outside their relationship with you.
|
||||
|
||||
Conveying under any other circumstances is permitted solely under the conditions stated below. Sublicensing is not allowed; section 10 makes it unnecessary.
|
||||
|
||||
3. Protecting Users' Legal Rights From Anti-Circumvention Law.
|
||||
|
||||
No covered work shall be deemed part of an effective technological measure under any applicable law fulfilling obligations under article 11 of the WIPO copyright treaty adopted on 20 December 1996, or similar laws prohibiting or restricting circumvention of such measures.
|
||||
|
||||
When you convey a covered work, you waive any legal power to forbid circumvention of technological measures to the extent such circumvention is effected by exercising rights under this License with respect to the covered work, and you disclaim any intention to limit operation or modification of the work as a means of enforcing, against the work's users, your or third parties' legal rights to forbid circumvention of technological measures.
|
||||
|
||||
4. Conveying Verbatim Copies.
|
||||
|
||||
You may convey 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; keep intact all notices stating that this License and any non-permissive terms added in accord with section 7 apply to the code; keep intact all notices of the absence of any warranty; and give all recipients a copy of this License along with the Program.
|
||||
|
||||
You may charge any price or no price for each copy that you convey, and you may offer support or warranty protection for a fee.
|
||||
|
||||
5. Conveying Modified Source Versions.
|
||||
|
||||
You may convey a work based on the Program, or the modifications to produce it from the Program, in the form of source code under the terms of section 4, provided that you also meet all of these conditions:
|
||||
|
||||
a) The work must carry prominent notices stating that you modified it, and giving a relevant date.
|
||||
b) The work must carry prominent notices stating that it is released under this License and any conditions added under section 7. This requirement modifies the requirement in section 4 to "keep intact all notices".
|
||||
c) You must license the entire work, as a whole, under this License to anyone who comes into possession of a copy. This License will therefore apply, along with any applicable section 7 additional terms, to the whole of the work, and all its parts, regardless of how they are packaged. This License gives no permission to license the work in any other way, but it does not invalidate such permission if you have separately received it.
|
||||
d) If the work has interactive user interfaces, each must display Appropriate Legal Notices; however, if the Program has interactive interfaces that do not display Appropriate Legal Notices, your work need not make them do so.
|
||||
A compilation of a covered work with other separate and independent works, which are not by their nature extensions of the covered work, and which are not combined with it such as to form a larger program, in or on a volume of a storage or distribution medium, is called an "aggregate" if the compilation and its resulting copyright are not used to limit the access or legal rights of the compilation's users beyond what the individual works permit. Inclusion of a covered work in an aggregate does not cause this License to apply to the other parts of the aggregate.
|
||||
|
||||
6. Conveying Non-Source Forms.
|
||||
|
||||
You may convey a covered work in object code form under the terms of sections 4 and 5, provided that you also convey the machine-readable Corresponding Source under the terms of this License, in one of these ways:
|
||||
|
||||
a) Convey the object code in, or embodied in, a physical product (including a physical distribution medium), accompanied by the Corresponding Source fixed on a durable physical medium customarily used for software interchange.
|
||||
b) Convey the object code in, or embodied in, a physical product (including a physical distribution medium), accompanied by a written offer, valid for at least three years and valid for as long as you offer spare parts or customer support for that product model, to give anyone who possesses the object code either (1) a copy of the Corresponding Source for all the software in the product that is covered by this License, on a durable physical medium customarily used for software interchange, for a price no more than your reasonable cost of physically performing this conveying of source, or (2) access to copy the Corresponding Source from a network server at no charge.
|
||||
c) Convey individual copies of the object code with a copy of the written offer to provide the Corresponding Source. This alternative is allowed only occasionally and noncommercially, and only if you received the object code with such an offer, in accord with subsection 6b.
|
||||
d) Convey the object code by offering access from a designated place (gratis or for a charge), and offer equivalent access to the Corresponding Source in the same way through the same place at no further charge. You need not require recipients to copy the Corresponding Source along with the object code. If the place to copy the object code is a network server, the Corresponding Source may be on a different server (operated by you or a third party) that supports equivalent copying facilities, provided you maintain clear directions next to the object code saying where to find the Corresponding Source. Regardless of what server hosts the Corresponding Source, you remain obligated to ensure that it is available for as long as needed to satisfy these requirements.
|
||||
e) Convey the object code using peer-to-peer transmission, provided you inform other peers where the object code and Corresponding Source of the work are being offered to the general public at no charge under subsection 6d.
|
||||
A separable portion of the object code, whose source code is excluded from the Corresponding Source as a System Library, need not be included in conveying the object code work.
|
||||
|
||||
A "User Product" is either (1) a "consumer product", which means any tangible personal property which is normally used for personal, family, or household purposes, or (2) anything designed or sold for incorporation into a dwelling. In determining whether a product is a consumer product, doubtful cases shall be resolved in favor of coverage. For a particular product received by a particular user, "normally used" refers to a typical or common use of that class of product, regardless of the status of the particular user or of the way in which the particular user actually uses, or expects or is expected to use, the product. A product is a consumer product regardless of whether the product has substantial commercial, industrial or non-consumer uses, unless such uses represent the only significant mode of use of the product.
|
||||
|
||||
"Installation Information" for a User Product means any methods, procedures, authorization keys, or other information required to install and execute modified versions of a covered work in that User Product from a modified version of its Corresponding Source. The information must suffice to ensure that the continued functioning of the modified object code is in no case prevented or interfered with solely because modification has been made.
|
||||
|
||||
If you convey an object code work under this section in, or with, or specifically for use in, a User Product, and the conveying occurs as part of a transaction in which the right of possession and use of the User Product is transferred to the recipient in perpetuity or for a fixed term (regardless of how the transaction is characterized), the Corresponding Source conveyed under this section must be accompanied by the Installation Information. But this requirement does not apply if neither you nor any third party retains the ability to install modified object code on the User Product (for example, the work has been installed in ROM).
|
||||
|
||||
The requirement to provide Installation Information does not include a requirement to continue to provide support service, warranty, or updates for a work that has been modified or installed by the recipient, or for the User Product in which it has been modified or installed. Access to a network may be denied when the modification itself materially and adversely affects the operation of the network or violates the rules and protocols for communication across the network.
|
||||
|
||||
Corresponding Source conveyed, and Installation Information provided, in accord with this section must be in a format that is publicly documented (and with an implementation available to the public in source code form), and must require no special password or key for unpacking, reading or copying.
|
||||
|
||||
7. Additional Terms.
|
||||
|
||||
"Additional permissions" are terms that supplement the terms of this License by making exceptions from one or more of its conditions. Additional permissions that are applicable to the entire Program shall be treated as though they were included in this License, to the extent that they are valid under applicable law. If additional permissions apply only to part of the Program, that part may be used separately under those permissions, but the entire Program remains governed by this License without regard to the additional permissions.
|
||||
|
||||
When you convey a copy of a covered work, you may at your option remove any additional permissions from that copy, or from any part of it. (Additional permissions may be written to require their own removal in certain cases when you modify the work.) You may place additional permissions on material, added by you to a covered work, for which you have or can give appropriate copyright permission.
|
||||
|
||||
Notwithstanding any other provision of this License, for material you add to a covered work, you may (if authorized by the copyright holders of that material) supplement the terms of this License with terms:
|
||||
|
||||
a) Disclaiming warranty or limiting liability differently from the terms of sections 15 and 16 of this License; or
|
||||
b) Requiring preservation of specified reasonable legal notices or author attributions in that material or in the Appropriate Legal Notices displayed by works containing it; or
|
||||
c) Prohibiting misrepresentation of the origin of that material, or requiring that modified versions of such material be marked in reasonable ways as different from the original version; or
|
||||
d) Limiting the use for publicity purposes of names of licensors or authors of the material; or
|
||||
e) Declining to grant rights under trademark law for use of some trade names, trademarks, or service marks; or
|
||||
f) Requiring indemnification of licensors and authors of that material by anyone who conveys the material (or modified versions of it) with contractual assumptions of liability to the recipient, for any liability that these contractual assumptions directly impose on those licensors and authors.
|
||||
All other non-permissive additional terms are considered "further restrictions" within the meaning of section 10. If the Program as you received it, or any part of it, contains a notice stating that it is governed by this License along with a term that is a further restriction, you may remove that term. If a license document contains a further restriction but permits relicensing or conveying under this License, you may add to a covered work material governed by the terms of that license document, provided that the further restriction does not survive such relicensing or conveying.
|
||||
|
||||
If you add terms to a covered work in accord with this section, you must place, in the relevant source files, a statement of the additional terms that apply to those files, or a notice indicating where to find the applicable terms.
|
||||
|
||||
Additional terms, permissive or non-permissive, may be stated in the form of a separately written license, or stated as exceptions; the above requirements apply either way.
|
||||
|
||||
8. Termination.
|
||||
|
||||
You may not propagate or modify a covered work except as expressly provided under this License. Any attempt otherwise to propagate or modify it is void, and will automatically terminate your rights under this License (including any patent licenses granted under the third paragraph of section 11).
|
||||
|
||||
However, if you cease all violation of this License, then your license from a particular copyright holder is reinstated (a) provisionally, unless and until the copyright holder explicitly and finally terminates your license, and (b) permanently, if the copyright holder fails to notify you of the violation by some reasonable means prior to 60 days after the cessation.
|
||||
|
||||
Moreover, your license from a particular copyright holder is reinstated permanently if the copyright holder notifies you of the violation by some reasonable means, this is the first time you have received notice of violation of this License (for any work) from that copyright holder, and you cure the violation prior to 30 days after your receipt of the notice.
|
||||
|
||||
Termination of your rights under this section does not terminate the licenses of parties who have received copies or rights from you under this License. If your rights have been terminated and not permanently reinstated, you do not qualify to receive new licenses for the same material under section 10.
|
||||
|
||||
9. Acceptance Not Required for Having Copies.
|
||||
|
||||
You are not required to accept this License in order to receive or run a copy of the Program. Ancillary propagation of a covered work occurring solely as a consequence of using peer-to-peer transmission to receive a copy likewise does not require acceptance. However, nothing other than this License grants you permission to propagate or modify any covered work. These actions infringe copyright if you do not accept this License. Therefore, by modifying or propagating a covered work, you indicate your acceptance of this License to do so.
|
||||
|
||||
10. Automatic Licensing of Downstream Recipients.
|
||||
|
||||
Each time you convey a covered work, the recipient automatically receives a license from the original licensors, to run, modify and propagate that work, subject to this License. You are not responsible for enforcing compliance by third parties with this License.
|
||||
|
||||
An "entity transaction" is a transaction transferring control of an organization, or substantially all assets of one, or subdividing an organization, or merging organizations. If propagation of a covered work results from an entity transaction, each party to that transaction who receives a copy of the work also receives whatever licenses to the work the party's predecessor in interest had or could give under the previous paragraph, plus a right to possession of the Corresponding Source of the work from the predecessor in interest, if the predecessor has it or can get it with reasonable efforts.
|
||||
|
||||
You may not impose any further restrictions on the exercise of the rights granted or affirmed under this License. For example, you may not impose a license fee, royalty, or other charge for exercise of rights granted under this License, and you may not initiate litigation (including a cross-claim or counterclaim in a lawsuit) alleging that any patent claim is infringed by making, using, selling, offering for sale, or importing the Program or any portion of it.
|
||||
|
||||
11. Patents.
|
||||
|
||||
A "contributor" is a copyright holder who authorizes use under this License of the Program or a work on which the Program is based. The work thus licensed is called the contributor's "contributor version".
|
||||
|
||||
A contributor's "essential patent claims" are all patent claims owned or controlled by the contributor, whether already acquired or hereafter acquired, that would be infringed by some manner, permitted by this License, of making, using, or selling its contributor version, but do not include claims that would be infringed only as a consequence of further modification of the contributor version. For purposes of this definition, "control" includes the right to grant patent sublicenses in a manner consistent with the requirements of this License.
|
||||
|
||||
Each contributor grants you a non-exclusive, worldwide, royalty-free patent license under the contributor's essential patent claims, to make, use, sell, offer for sale, import and otherwise run, modify and propagate the contents of its contributor version.
|
||||
|
||||
In the following three paragraphs, a "patent license" is any express agreement or commitment, however denominated, not to enforce a patent (such as an express permission to practice a patent or covenant not to sue for patent infringement). To "grant" such a patent license to a party means to make such an agreement or commitment not to enforce a patent against the party.
|
||||
|
||||
If you convey a covered work, knowingly relying on a patent license, and the Corresponding Source of the work is not available for anyone to copy, free of charge and under the terms of this License, through a publicly available network server or other readily accessible means, then you must either (1) cause the Corresponding Source to be so available, or (2) arrange to deprive yourself of the benefit of the patent license for this particular work, or (3) arrange, in a manner consistent with the requirements of this License, to extend the patent license to downstream recipients. "Knowingly relying" means you have actual knowledge that, but for the patent license, your conveying the covered work in a country, or your recipient's use of the covered work in a country, would infringe one or more identifiable patents in that country that you have reason to believe are valid.
|
||||
|
||||
If, pursuant to or in connection with a single transaction or arrangement, you convey, or propagate by procuring conveyance of, a covered work, and grant a patent license to some of the parties receiving the covered work authorizing them to use, propagate, modify or convey a specific copy of the covered work, then the patent license you grant is automatically extended to all recipients of the covered work and works based on it.
|
||||
|
||||
A patent license is "discriminatory" if it does not include within the scope of its coverage, prohibits the exercise of, or is conditioned on the non-exercise of one or more of the rights that are specifically granted under this License. You may not convey a covered work if you are a party to an arrangement with a third party that is in the business of distributing software, under which you make payment to the third party based on the extent of your activity of conveying the work, and under which the third party grants, to any of the parties who would receive the covered work from you, a discriminatory patent license (a) in connection with copies of the covered work conveyed by you (or copies made from those copies), or (b) primarily for and in connection with specific products or compilations that contain the covered work, unless you entered into that arrangement, or that patent license was granted, prior to 28 March 2007.
|
||||
|
||||
Nothing in this License shall be construed as excluding or limiting any implied license or other defenses to infringement that may otherwise be available to you under applicable patent law.
|
||||
|
||||
12. No Surrender of Others' Freedom.
|
||||
|
||||
If 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 convey a covered work so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not convey it at all. For example, if you agree to terms that obligate you to collect a royalty for further conveying from those to whom you convey the Program, the only way you could satisfy both those terms and this License would be to refrain entirely from conveying the Program.
|
||||
|
||||
13. Remote Network Interaction; Use with the GNU General Public License.
|
||||
|
||||
Notwithstanding any other provision of this License, if you modify the Program, your modified version must prominently offer all users interacting with it remotely through a computer network (if your version supports such interaction) an opportunity to receive the Corresponding Source of your version by providing access to the Corresponding Source from a network server at no charge, through some standard or customary means of facilitating copying of software. This Corresponding Source shall include the Corresponding Source for any work covered by version 3 of the GNU General Public License that is incorporated pursuant to the following paragraph.
|
||||
|
||||
Notwithstanding any other provision of this License, you have permission to link or combine any covered work with a work licensed under version 3 of the GNU General Public License into a single combined work, and to convey the resulting work. The terms of this License will continue to apply to the part which is the covered work, but the work with which it is combined will remain governed by version 3 of the GNU General Public License.
|
||||
|
||||
14. Revised Versions of this License.
|
||||
|
||||
The Free Software Foundation may publish revised and/or new versions of the GNU Affero 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 that a certain numbered version of the GNU Affero General Public License "or any later version" applies to it, you have the option of following the terms and conditions either of that numbered version or of any later version published by the Free Software Foundation. If the Program does not specify a version number of the GNU Affero General Public License, you may choose any version ever published by the Free Software Foundation.
|
||||
|
||||
If the Program specifies that a proxy can decide which future versions of the GNU Affero General Public License can be used, that proxy's public statement of acceptance of a version permanently authorizes you to choose that version for the Program.
|
||||
|
||||
Later license versions may give you additional or different permissions. However, no additional obligations are imposed on any author or copyright holder as a result of your choosing to follow a later version.
|
||||
|
||||
15. Disclaimer of Warranty.
|
||||
|
||||
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.
|
||||
|
||||
16. Limitation of Liability.
|
||||
|
||||
IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS 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.
|
||||
|
||||
17. Interpretation of Sections 15 and 16.
|
||||
|
||||
If the disclaimer of warranty and limitation of liability provided above cannot be given local legal effect according to their terms, reviewing courts shall apply local law that most closely approximates an absolute waiver of all civil liability in connection with the Program, unless a warranty or assumption of liability accompanies a copy of the Program in return for a fee.
|
||||
|
||||
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 state 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 Affero General Public License as
|
||||
published by the Free Software Foundation, either version 3 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 Affero General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Affero General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
Also add information on how to contact you by electronic and paper mail.
|
||||
|
||||
If your software can interact with users remotely through a computer network, you should also make sure that it provides a way for users to get its source. For example, if your program is a web application, its interface could display a "Source" link that leads users to an archive of the code. There are many ways you could offer source, and different solutions will be better for different programs; see section 13 for the specific requirements.
|
||||
|
||||
You should also get your employer (if you work as a programmer) or school, if any, to sign a "copyright disclaimer" for the program, if necessary. For more information on this, and how to apply and follow the GNU AGPL, see <http://www.gnu.org/licenses/>.
|
||||
|
22
Makefile.am
Normal file
22
Makefile.am
Normal file
@ -0,0 +1,22 @@
|
||||
## Process this file with automake to produce Makefile.in
|
||||
|
||||
|
||||
pcfiles = libccnet.pc
|
||||
pkgconfig_DATA = $(pcfiles)
|
||||
pkgconfigdir = $(libdir)/pkgconfig
|
||||
|
||||
SUBDIRS = include lib net tools python
|
||||
|
||||
EXTRA_DIST = install-sh libccnet.pc.in LICENCE.txt
|
||||
|
||||
ACLOCAL_AMFLAGS = -I m4
|
||||
|
||||
install-data-local:
|
||||
if MACOS
|
||||
sed -i '' -e "s|(DESTDIR)|${DESTDIR}|g" $(pcfiles)
|
||||
else
|
||||
${SED} -i "s|(DESTDIR)|${DESTDIR}|g" $(pcfiles)
|
||||
endif
|
||||
|
||||
dist-hook:
|
||||
git log --format='%H' -1 > $(distdir)/latest_commit
|
40
README.markdown
Normal file
40
README.markdown
Normal file
@ -0,0 +1,40 @@
|
||||
Ccnet is a framework for writing networked applications in C. It
|
||||
provides the following basic services:
|
||||
|
||||
1. Peer identification
|
||||
2. Connection Management
|
||||
3. Service invocation
|
||||
4. Message sending
|
||||
|
||||
In ccnet network, there are two types of nodes, i.e., client and server.
|
||||
Server has the following functions:
|
||||
|
||||
1. User management
|
||||
2. Group management
|
||||
|
||||
This repository is the Ccnet server.
|
||||
|
||||
Dependency
|
||||
==========
|
||||
|
||||
The following packages are required to build ccnet:
|
||||
|
||||
valac >= 0.8
|
||||
libsearpc
|
||||
libmysqlclient-dev for compiling ccnet server
|
||||
|
||||
Compile
|
||||
=======
|
||||
|
||||
To compile the client components, just
|
||||
|
||||
./autogen.sh && ./configure && make && make install
|
||||
|
||||
In Mac OS, use
|
||||
|
||||
LDFLAGS="-L/opt/local/lib -L/usr/local/mysql/lib -Xlinker -headerpad_max_install_names" ./configure
|
||||
|
||||
License
|
||||
=======
|
||||
|
||||
Ccnet server is published under AGPLv3. See LICENSE.txt for details.
|
89
autogen.sh
Executable file
89
autogen.sh
Executable file
@ -0,0 +1,89 @@
|
||||
#!/bin/bash
|
||||
# Run this to generate all the initial makefiles, etc.
|
||||
|
||||
: ${AUTOCONF=autoconf}
|
||||
: ${AUTOHEADER=autoheader}
|
||||
: ${AUTOMAKE=automake}
|
||||
: ${ACLOCAL=aclocal}
|
||||
if test "$(uname)" != "Darwin"; then
|
||||
: ${LIBTOOLIZE=libtoolize}
|
||||
else
|
||||
: ${LIBTOOLIZE=glibtoolize}
|
||||
fi
|
||||
: ${LIBTOOL=libtool}
|
||||
|
||||
srcdir=`dirname $0`
|
||||
test -z "$srcdir" && srcdir=.
|
||||
|
||||
ORIGDIR=`pwd`
|
||||
cd $srcdir
|
||||
PROJECT=ccnet
|
||||
TEST_TYPE=-f
|
||||
CONFIGURE=configure.ac
|
||||
|
||||
DIE=0
|
||||
|
||||
($AUTOCONF --version) < /dev/null > /dev/null 2>&1 || {
|
||||
echo
|
||||
echo "You must have autoconf installed to compile $PROJECT."
|
||||
echo "Download the appropriate package for your distribution,"
|
||||
echo "or get the source tarball at ftp://ftp.gnu.org/pub/gnu/"
|
||||
DIE=1
|
||||
}
|
||||
|
||||
|
||||
($AUTOMAKE --version) < /dev/null > /dev/null 2>&1 || {
|
||||
echo
|
||||
echo "You must have automake installed to compile $PROJECT."
|
||||
echo "Get ftp://sourceware.cygnus.com/pub/automake/automake-1.7.tar.gz"
|
||||
echo "(or a newer version if it is available)"
|
||||
DIE=1
|
||||
}
|
||||
|
||||
if test "$(uname)" != "Darwin"; then
|
||||
(grep "^AC_PROG_LIBTOOL" $CONFIGURE >/dev/null) && {
|
||||
($LIBTOOL --version) < /dev/null > /dev/null 2>&1 || {
|
||||
echo
|
||||
echo "**Error**: You must have \`libtool' installed to compile $PROJECT."
|
||||
echo "Get ftp://ftp.gnu.org/pub/gnu/libtool-1.4.tar.gz"
|
||||
echo "(or a newer version if it is available)"
|
||||
DIE=1
|
||||
}
|
||||
}
|
||||
fi
|
||||
|
||||
if test "$DIE" -eq 1; then
|
||||
exit 1
|
||||
fi
|
||||
|
||||
dr=`dirname .`
|
||||
echo processing $dr
|
||||
aclocalinclude="$aclocalinclude -I m4"
|
||||
|
||||
if test x"$MSYSTEM" = x"MINGW32"; then
|
||||
aclocalinclude="$aclocalinclude -I /mingw32/share/aclocal"
|
||||
elif test "$(uname)" = "Darwin"; then
|
||||
aclocalinclude="$aclocalinclude -I /opt/local/share/aclocal"
|
||||
fi
|
||||
|
||||
|
||||
echo "Creating $dr/aclocal.m4 ..."
|
||||
test -r $dr/aclocal.m4 || touch $dr/aclocal.m4
|
||||
echo "Making $dr/aclocal.m4 writable ..."
|
||||
test -r $dr/aclocal.m4 && chmod u+w $dr/aclocal.m4
|
||||
|
||||
|
||||
echo "Running $LIBTOOLIZE..."
|
||||
$LIBTOOLIZE --force --copy
|
||||
|
||||
echo "Running $ACLOCAL $aclocalinclude ..."
|
||||
$ACLOCAL $aclocalinclude
|
||||
|
||||
echo "Running $AUTOHEADER..."
|
||||
$AUTOHEADER
|
||||
|
||||
echo "Running $AUTOMAKE --gnu $am_opt ..."
|
||||
$AUTOMAKE --add-missing --gnu $am_opt
|
||||
|
||||
echo "Running $AUTOCONF ..."
|
||||
$AUTOCONF
|
348
configure.ac
Normal file
348
configure.ac
Normal file
@ -0,0 +1,348 @@
|
||||
dnl Process this file with autoconf to produce a configure script.
|
||||
|
||||
|
||||
AC_PREREQ(2.61)
|
||||
AC_INIT([ccnet], [5.1.4], [daniel.pan@seafile.com])
|
||||
AC_CONFIG_SRCDIR([net/server/ccnet-server.c])
|
||||
AC_CONFIG_HEADER([config.h])
|
||||
|
||||
AC_CONFIG_MACRO_DIR([m4])
|
||||
|
||||
AM_INIT_AUTOMAKE([1.9 foreign])
|
||||
|
||||
#AC_MINGW32
|
||||
AC_CANONICAL_BUILD
|
||||
|
||||
dnl enable the build of share library by default
|
||||
AC_ENABLE_SHARED
|
||||
|
||||
AC_SUBST(LIBTOOL_DEPS)
|
||||
|
||||
# Checks for programs.
|
||||
AC_PROG_CC
|
||||
#AM_C_PROTOTYPES
|
||||
AC_C_CONST
|
||||
AC_PROG_MAKE_SET
|
||||
# AC_PROG_RANLIB
|
||||
LT_INIT
|
||||
|
||||
# Checks for headers.
|
||||
AC_CHECK_HEADERS([sys/ioctl.h sys/time.h stdarg.h])
|
||||
|
||||
# Checks for typedefs, structures, and compiler characteristics.
|
||||
AC_SYS_LARGEFILE
|
||||
|
||||
# Checks for library functions.
|
||||
#AC_CHECK_FUNCS([alarm dup2 ftruncate getcwd gethostbyname gettimeofday memmove memset mkdir rmdir select setlocale socket strcasecmp strchr strdup strrchr strstr strtol uname utime strtok_r sendfile])
|
||||
|
||||
# check platform
|
||||
AC_MSG_CHECKING(for WIN32)
|
||||
if test "$build_os" = "mingw32" -o "$build_os" = "mingw64"; then
|
||||
bwin32=true
|
||||
AC_MSG_RESULT(compile in mingw)
|
||||
else
|
||||
AC_MSG_RESULT(no)
|
||||
fi
|
||||
|
||||
AC_MSG_CHECKING(for Mac)
|
||||
if test "$(uname)" = "Darwin"; then
|
||||
bmac=true
|
||||
AC_MSG_RESULT(compile in mac)
|
||||
else
|
||||
AC_MSG_RESULT(no)
|
||||
fi
|
||||
|
||||
AC_MSG_CHECKING(for Linux)
|
||||
if test "$bmac" != "true" -a "$bwin32" != "true"; then
|
||||
blinux=true
|
||||
AC_MSG_RESULT(compile in linux)
|
||||
else
|
||||
AC_MSG_RESULT(no)
|
||||
fi
|
||||
|
||||
AC_ARG_ENABLE(ldap, AC_HELP_STRING([--enable-ldap], [enable LDAP]),
|
||||
[compile_ldap=$enableval],[compile_ldap="no"])
|
||||
|
||||
AC_ARG_ENABLE(python,
|
||||
AC_HELP_STRING([--enable-python],[build ccnet python binding]),
|
||||
[compile_python=$enableval],
|
||||
[compile_python=yes])
|
||||
|
||||
AM_CONDITIONAL([COMPILE_PYTHON], [test "${compile_python}" = "yes"])
|
||||
|
||||
AM_CONDITIONAL([WIN32], [test "$bwin32" = "true"])
|
||||
AM_CONDITIONAL([MACOS], [test "$bmac" = "true"])
|
||||
AM_CONDITIONAL([LINUX], [test "$blinux" = "true"])
|
||||
|
||||
|
||||
# check libraries
|
||||
if test "$bwin32" != true; then
|
||||
if test "$bmac" = true; then
|
||||
AC_CHECK_LIB(c, uuid_generate, [echo "found library uuid"],
|
||||
AC_MSG_ERROR([*** Unable to find uuid_generate in libc]), )
|
||||
else
|
||||
AC_CHECK_LIB(uuid, uuid_generate, [echo "found library uuid"],
|
||||
AC_MSG_ERROR([*** Unable to find uuid library]), )
|
||||
fi
|
||||
fi
|
||||
|
||||
AC_CHECK_LIB(pthread, pthread_create, [echo "found library pthread"], AC_MSG_ERROR([*** Unable to find pthread library]), )
|
||||
AC_CHECK_LIB(sqlite3, sqlite3_open,[echo "found library sqlite3"] , AC_MSG_ERROR([*** Unable to find sqlite3 library]), )
|
||||
AC_CHECK_LIB(crypto, SHA1_Init, [echo "found library crypto"], AC_MSG_ERROR([*** Unable to find openssl crypto library]), )
|
||||
|
||||
PTHREAD_CFLAGS=-pthread
|
||||
PTHREAD_LIBS=-lpthread
|
||||
has_winpthread=false
|
||||
if test "$bwin32" = "true"; then
|
||||
has_winpthread=false
|
||||
dnl this will tell us if the implementation of pthread is winpthread
|
||||
AC_CHECK_LIB(winpthread, pthread_create, has_winpthread=true,
|
||||
[echo "found library winpthread"], )
|
||||
PTHREAD_CFLAGS=-pthread
|
||||
PTHREAD_LIBS=-lwinpthread
|
||||
fi
|
||||
|
||||
dnl Saddly, the old mingw gcc doesn't support -pthread flag
|
||||
if test "$bwin32" = "true" -a "$has_winpthread" != "true"; then
|
||||
PTHREAD_CFLAGS=
|
||||
PTHREAD_LIBS=-lpthread
|
||||
fi
|
||||
|
||||
AC_SUBST(PTHREAD_CFLAGS)
|
||||
AC_SUBST(PTHREAD_LIBS)
|
||||
|
||||
dnl Do we need to use AX_LIB_SQLITE3 to check sqlite?
|
||||
dnl AX_LIB_SQLITE3
|
||||
|
||||
CONSOLE=
|
||||
if test "$bwin32" = "true"; then
|
||||
AC_ARG_ENABLE(console, AC_HELP_STRING([--enable-console], [enable console]),
|
||||
[console=$enableval],[console="yes"])
|
||||
if test x${console} != xyes ; then
|
||||
CONSOLE="-Wl,--subsystem,windows -Wl,--entry,_mainCRTStartup"
|
||||
fi
|
||||
fi
|
||||
AC_SUBST(CONSOLE)
|
||||
|
||||
if test "$bwin32" = true; then
|
||||
LIB_WS32=-lws2_32
|
||||
LIB_GDI32=-lgdi32
|
||||
LIB_RT=
|
||||
LIB_INTL=-lintl
|
||||
LIBS=
|
||||
LIB_RESOLV=
|
||||
LIB_UUID=-lrpcrt4
|
||||
LIB_IPHLPAPI=-liphlpapi
|
||||
LIB_SHELL32=-lshell32
|
||||
LIB_PSAPI=-lpsapi
|
||||
MSVC_CFLAGS=-D__MSVCRT_VERSION__=0x0601
|
||||
LIB_DIRWATCH=
|
||||
elif test "$bmac" = true ; then
|
||||
LIB_WS32=
|
||||
LIB_GDI32=
|
||||
LIB_RT=
|
||||
LIB_INTL=
|
||||
LIB_RESOLV=-lresolv
|
||||
LIB_UUID=
|
||||
LIB_IPHLPAPI=
|
||||
LIB_SHELL32=
|
||||
LIB_PSAPI=
|
||||
MSVC_CFLAGS=
|
||||
LIB_DIRWATCH="-framework CoreServices"
|
||||
else
|
||||
LIB_WS32=
|
||||
LIB_GDI32=
|
||||
LIB_RT=
|
||||
LIB_INTL=
|
||||
LIB_RESOLV=-lresolv
|
||||
LIB_UUID=-luuid
|
||||
LIB_IPHLPAPI=
|
||||
LIB_SHELL32=
|
||||
LIB_PSAPI=
|
||||
MSVC_CFLAGS=
|
||||
LIB_DIRWATCH=
|
||||
fi
|
||||
|
||||
AC_SUBST(LIB_WS32)
|
||||
AC_SUBST(LIB_GDI32)
|
||||
AC_SUBST(LIB_RT)
|
||||
AC_SUBST(LIB_INTL)
|
||||
AC_SUBST(LIB_RESOLV)
|
||||
AC_SUBST(LIB_UUID)
|
||||
AC_SUBST(LIB_IPHLPAPI)
|
||||
AC_SUBST(LIB_SHELL32)
|
||||
AC_SUBST(LIB_PSAPI)
|
||||
AC_SUBST(MSVC_CFLAGS)
|
||||
AC_SUBST(LIB_DIRWATCH)
|
||||
|
||||
LIBEVENT_REQUIRED=2.0
|
||||
# gtk and glib
|
||||
APPINDICATOR_REQUIRED=0.0.7
|
||||
GLIB_REQUIRED=2.16.0
|
||||
GTK_REQUIRED=2.16.0
|
||||
SEARPC_REQUIRED=1.0
|
||||
ZDB_REQUIRED=2.10
|
||||
LIBNAUTILUS_EXTENSION_REQUIRED=2.30.1
|
||||
SEARPC_REQUIRED=1.0
|
||||
CURL_REQUIRED=7.17
|
||||
|
||||
PKG_CHECK_MODULES(SSL, [openssl])
|
||||
AC_SUBST(SSL_CFLAGS)
|
||||
AC_SUBST(SSL_LIBS)
|
||||
|
||||
PKG_CHECK_MODULES(GLIB2, [glib-2.0 >= $GLIB_REQUIRED])
|
||||
AC_SUBST(GLIB2_CFLAGS)
|
||||
AC_SUBST(GLIB2_LIBS)
|
||||
|
||||
PKG_CHECK_MODULES(GOBJECT, [gobject-2.0 >= $GLIB_REQUIRED])
|
||||
AC_SUBST(GOBJECT_CFLAGS)
|
||||
AC_SUBST(GOBJECT_LIBS)
|
||||
|
||||
PKG_CHECK_MODULES(SEARPC, [libsearpc >= $SEARPC_REQUIRED])
|
||||
AC_SUBST(SEARPC_CFLAGS)
|
||||
AC_SUBST(SEARPC_LIBS)
|
||||
|
||||
PKG_CHECK_MODULES(LIBEVENT, [libevent >= $LIBEVENT_REQUIRED])
|
||||
AC_SUBST(LIBEVENT_CFLAGS)
|
||||
AC_SUBST(LIBEVENT_LIBS)
|
||||
|
||||
if test x${compile_python} = xyes; then
|
||||
AM_PATH_PYTHON([2.6])
|
||||
|
||||
if test "$bwin32" = true; then
|
||||
# set pyexecdir to somewhere like /c/Python26/Lib/site-packages
|
||||
pyexecdir=${PYTHON_DIR}/Lib/site-packages
|
||||
pythondir=${pyexecdir}
|
||||
pkgpyexecdir=${pyexecdir}/${PACKAGE}
|
||||
pkgpythondir=${pythondir}/${PACKAGE}
|
||||
|
||||
fi # end for bwin32
|
||||
|
||||
fi
|
||||
|
||||
mysql="yes"
|
||||
check_mysql_config()
|
||||
{
|
||||
AC_PATH_PROG([MYSQLCONFIG], [mysql_config], [no], [$PATH:/usr/local/bin:/usr/local/mysql/bin])
|
||||
if test "x$MYSQLCONFIG" = "xno"
|
||||
then
|
||||
AC_MSG_WARN([mysql_config is required to build seafile server with mysql])
|
||||
mysql="no"
|
||||
fi
|
||||
}
|
||||
AC_MSG_CHECKING(for mysql)
|
||||
AC_ARG_WITH([mysql],
|
||||
AS_HELP_STRING([--with-mysql(=<path>)],
|
||||
[Path is optional and if given should specify the full path to the MySQL
|
||||
configure script, mysql_config. E.g. --with-mysql=/<path>/mysql_config]),
|
||||
[
|
||||
if test "xno" = "x$with_mysql"; then
|
||||
AC_MSG_RESULT([no])
|
||||
mysql="no"
|
||||
else
|
||||
AC_MSG_RESULT([yes])
|
||||
AC_CHECK_FILE([$with_mysql], [MYSQLCONFIG=$with_mysql], [check_mysql_config])
|
||||
fi
|
||||
],
|
||||
[
|
||||
AC_MSG_RESULT([yes])
|
||||
check_mysql_config
|
||||
])
|
||||
if test "xyes" = "x$mysql"; then
|
||||
tmp_CPPFLAGS=$CPPFLAGS
|
||||
tmp_LDFLAGS=$LDFLAGS
|
||||
CPPFLAGS="`$MYSQLCONFIG --include` $CPPFLAGS"
|
||||
LDFLAGS="`$MYSQLCONFIG --libs` $LDFLAGS"
|
||||
AC_CHECK_HEADERS([mysql.h])
|
||||
if test "xyes" = "x$mysql"; then
|
||||
echo "found mysql client library"
|
||||
MYSQL_CFLAGS=`$MYSQLCONFIG --include`
|
||||
MYSQL_LIBS=`$MYSQLCONFIG --libs`
|
||||
AC_SUBST(MYSQL_CFLAGS)
|
||||
AC_SUBST(MYSQL_LIBS)
|
||||
fi
|
||||
CPPFLAGS=$tmp_CPPFLAGS
|
||||
LDFLAGS=$tmp_LDFLAGS
|
||||
fi
|
||||
|
||||
postgresql="yes"
|
||||
check_postgres_config()
|
||||
{
|
||||
AC_PATH_PROG([PGCONFIG], [pg_config], [no], [$PATH:/usr/local/bin:/usr/local/pgsql/bin])
|
||||
if test "x$PGCONFIG" = "xno"
|
||||
then
|
||||
AC_MSG_WARN([pg_config is required to build seafile server with postgresql])
|
||||
postgresql="no"
|
||||
fi
|
||||
}
|
||||
AC_MSG_CHECKING(for postgresql)
|
||||
AC_ARG_WITH([postgresql],
|
||||
AS_HELP_STRING([--with-postgresql(=<path>)],
|
||||
[Path is optional and if given should specify the full path to the PostgreSQL
|
||||
configure script, pg_config. E.g. --with-postgresql=/<path>/pg_config]),
|
||||
[
|
||||
if test "xno" = "x$with_postgresql"; then
|
||||
AC_MSG_RESULT([no])
|
||||
postgresql="no"
|
||||
else
|
||||
AC_MSG_RESULT([yes])
|
||||
AC_CHECK_FILE([$with_postgresql], [PGCONFIG=$with_postgresql],[check_postgres_config])
|
||||
fi
|
||||
],
|
||||
[
|
||||
AC_MSG_RESULT([yes])
|
||||
check_postgres_config
|
||||
])
|
||||
if test "xyes" = "x$postgresql"; then
|
||||
tmp_CPPFLAGS=$CPPFLAGS
|
||||
tmp_LDFLAGS=$LDFLAGS
|
||||
CPPFLAGS="-I`$PGCONFIG --includedir` $CPPFLAGS"
|
||||
LDFLAGS="-L`$PGCONFIG --libdir` $LDFLAGS"
|
||||
AC_CHECK_HEADERS([libpq-fe.h], [], [postgresql="no"])
|
||||
if test "xyes" = "x$postgresql"; then
|
||||
echo "found postgresql client library"
|
||||
PGSQL_CFLAGS="-I`$PGCONFIG --includedir`"
|
||||
PGSQL_LIBS="-L`$PGCONFIG --libdir` -lpq"
|
||||
AC_SUBST(PGSQL_CFLAGS)
|
||||
AC_SUBST(PGSQL_LIBS)
|
||||
fi
|
||||
CPPFLAGS=$tmp_CPPFLAGS
|
||||
LDFLAGS=$tmp_LDFLAGS
|
||||
fi
|
||||
|
||||
if test "${compile_ldap}" = "yes"; then
|
||||
if test "$bwin32" != true; then
|
||||
AC_CHECK_LIB(ldap, ldap_init, [have_ldap="yes"],
|
||||
AC_MSG_ERROR([*** Unable to find ldap client library]), )
|
||||
|
||||
if test "${have_ldap}" = "yes"; then
|
||||
echo "found ldap client library"
|
||||
AC_DEFINE([HAVE_LDAP], [1], [Define if ldap library exists.])
|
||||
AC_SUBST(LDAP_LIBS, "-lldap -llber")
|
||||
fi
|
||||
else
|
||||
AC_DEFINE([HAVE_LDAP], [1], [Define if ldap library exists.])
|
||||
AC_SUBST(LDAP_LIBS, "-lWldap32")
|
||||
fi
|
||||
|
||||
|
||||
fi
|
||||
|
||||
ac_configure_args="$ac_configure_args -q"
|
||||
|
||||
AC_CONFIG_FILES(
|
||||
Makefile
|
||||
libccnet.pc
|
||||
net/Makefile
|
||||
net/server/Makefile
|
||||
net/common/Makefile
|
||||
net/common/db-wrapper/Makefile
|
||||
lib/Makefile
|
||||
tools/Makefile
|
||||
include/Makefile
|
||||
include/ccnet/Makefile
|
||||
python/Makefile
|
||||
python/ccnet/Makefile
|
||||
)
|
||||
|
||||
AC_OUTPUT
|
6
include/Makefile.am
Normal file
6
include/Makefile.am
Normal file
@ -0,0 +1,6 @@
|
||||
|
||||
SUBDIRS = ccnet
|
||||
|
||||
include_HEADERS = ccnet.h
|
||||
|
||||
ccnetdir = $(includedir)/ccnet
|
128
include/ccnet.h
Normal file
128
include/ccnet.h
Normal file
@ -0,0 +1,128 @@
|
||||
/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
|
||||
|
||||
#ifndef _CCNET_H
|
||||
#define _CCNET_H
|
||||
|
||||
#include <ccnet/option.h>
|
||||
|
||||
#include <glib.h>
|
||||
|
||||
#include <ccnet/valid-check.h>
|
||||
#include <ccnet/peer.h>
|
||||
#include <ccnet/message.h>
|
||||
#include <ccnet/status-code.h>
|
||||
#include <ccnet/processor.h>
|
||||
|
||||
#include <ccnet/ccnet-session-base.h>
|
||||
#include <ccnet/ccnet-client.h>
|
||||
|
||||
#include <ccnet/proc-factory.h>
|
||||
#include <ccnet/sendcmd-proc.h>
|
||||
#include <ccnet/mqclient-proc.h>
|
||||
#include <ccnet/invoke-service-proc.h>
|
||||
|
||||
#include <ccnet/timer.h>
|
||||
|
||||
#include <searpc-client.h>
|
||||
|
||||
/* mainloop */
|
||||
|
||||
void ccnet_main (CcnetClient *client);
|
||||
|
||||
typedef void (*RegisterServiceCB) (gboolean success);
|
||||
void ccnet_register_service (CcnetClient *client,
|
||||
const char *service, const char *group,
|
||||
GType proc_type, RegisterServiceCB cb);
|
||||
gboolean ccnet_register_service_sync (CcnetClient *client,
|
||||
const char *service,
|
||||
const char *group);
|
||||
CcnetClient *ccnet_init (const char *central_config_dir, const char *confdir);
|
||||
|
||||
void ccnet_send_command (CcnetClient *client, const char *command,
|
||||
SendcmdProcRcvrspCallback cmd_cb, void *cbdata);
|
||||
|
||||
void ccnet_add_peer (CcnetClient *client, const char *id, const char *addr);
|
||||
|
||||
void ccnet_connect_peer (CcnetClient *client, const char *id);
|
||||
void ccnet_disconnect_peer (CcnetClient *client, const char *id);
|
||||
|
||||
/* client pool */
|
||||
|
||||
struct CcnetClientPool;
|
||||
typedef struct CcnetClientPool CcnetClientPool;
|
||||
|
||||
struct CcnetClientPool *
|
||||
ccnet_client_pool_new (const char *central_config_dir, const char *conf_dir);
|
||||
|
||||
CcnetClient *
|
||||
ccnet_client_pool_get_client (struct CcnetClientPool *cpool);
|
||||
|
||||
void
|
||||
ccnet_client_pool_return_client (struct CcnetClientPool *cpool,
|
||||
CcnetClient *client);
|
||||
|
||||
/* rpc wrapper */
|
||||
|
||||
/* Create rpc client using a single client for transport. */
|
||||
SearpcClient *
|
||||
ccnet_create_rpc_client (CcnetClient *cclient, const char *peer_id,
|
||||
const char *service_name);
|
||||
|
||||
/* Create rpc client using client pool for transport. */
|
||||
SearpcClient *
|
||||
ccnet_create_pooled_rpc_client (struct CcnetClientPool *cpool,
|
||||
const char *peer_id,
|
||||
const char *service);
|
||||
|
||||
SearpcClient *
|
||||
ccnet_create_async_rpc_client (CcnetClient *cclient, const char *peer_id,
|
||||
const char *service_name);
|
||||
|
||||
void ccnet_rpc_client_free (SearpcClient *client);
|
||||
void ccnet_async_rpc_client_free (SearpcClient *client);
|
||||
|
||||
CcnetPeer *ccnet_get_peer (SearpcClient *client, const char *peer_id);
|
||||
CcnetPeer *ccnet_get_peer_by_idname (SearpcClient *client, const char *idname);
|
||||
int ccnet_get_peer_net_state (SearpcClient *client, const char *peer_id);
|
||||
int ccnet_get_peer_bind_status (SearpcClient *client, const char *peer_id);
|
||||
int ccnet_peer_is_ready (SearpcClient *client, const char *peer_id);
|
||||
CcnetPeer *ccnet_get_default_relay (SearpcClient *client);
|
||||
GList *ccnet_get_peers_by_role (SearpcClient *client, const char *role);
|
||||
|
||||
char *ccnet_get_binding_email (SearpcClient *client, const char *peer_id);
|
||||
GList *ccnet_get_groups_by_user (SearpcClient *client, const char *user);
|
||||
GList *
|
||||
ccnet_get_group_members (SearpcClient *client, int group_id);
|
||||
int
|
||||
ccnet_org_user_exists (SearpcClient *client, int org_id, const char *user);
|
||||
|
||||
int
|
||||
ccnet_get_binding_email_async (SearpcClient *client, const char *peer_id,
|
||||
AsyncCallback callback, void *user_data);
|
||||
|
||||
char *ccnet_sign_message (SearpcClient *client, const char *message);
|
||||
int ccnet_verify_message (SearpcClient *client,
|
||||
const char *message,
|
||||
const char *sig_base64,
|
||||
const char *peer_id);
|
||||
|
||||
char *
|
||||
ccnet_pubkey_encrypt (SearpcClient *client,
|
||||
const char *msg_base64,
|
||||
const char *peer_id);
|
||||
|
||||
char *
|
||||
ccnet_privkey_decrypt (SearpcClient *client, const char *msg_base64);
|
||||
|
||||
char *ccnet_get_config (SearpcClient *client, const char *key);
|
||||
int ccnet_set_config (SearpcClient *client, const char *key, const char *value);
|
||||
|
||||
void
|
||||
ccnet_login_to_relay (SearpcClient *client, const char *relay_id,
|
||||
const char *username, const char *passwd);
|
||||
|
||||
int
|
||||
ccnet_update_peer_address (SearpcClient *client, const char *peer_id,
|
||||
const char *addr, int port);
|
||||
|
||||
#endif
|
11
include/ccnet/Makefile.am
Normal file
11
include/ccnet/Makefile.am
Normal file
@ -0,0 +1,11 @@
|
||||
|
||||
ccnetdir = $(includedir)/ccnet
|
||||
|
||||
ccnet_HEADERS = ccnet-client.h peer.h proc-factory.h \
|
||||
message.h option.h \
|
||||
processor.h sendcmd-proc.h \
|
||||
mqclient-proc.h invoke-service-proc.h \
|
||||
status-code.h cevent.h timer.h ccnet-session-base.h \
|
||||
valid-check.h job-mgr.h packet.h \
|
||||
async-rpc-proc.h ccnetrpc-transport.h \
|
||||
rpcserver-proc.h threaded-rpcserver-proc.h
|
35
include/ccnet/async-rpc-proc.h
Normal file
35
include/ccnet/async-rpc-proc.h
Normal file
@ -0,0 +1,35 @@
|
||||
/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
|
||||
|
||||
#ifndef CCNET_ASYNC_RPC_PROC_H
|
||||
#define CCNET_ASYNC_RPC_PROC_H
|
||||
|
||||
#include <glib-object.h>
|
||||
|
||||
|
||||
#define CCNET_TYPE_ASYNC_RPC_PROC (ccnet_async_rpc_proc_get_type ())
|
||||
#define CCNET_ASYNC_RPC_PROC(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), CCNET_TYPE_ASYNC_RPC_PROC, CcnetAsyncRpcProc))
|
||||
#define CCNET_IS_ASYNC_RPC_PROC(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), CCNET_TYPE_ASYNC_RPC_PROC))
|
||||
#define CCNET_ASYNC_RPC_PROC_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), CCNET_TYPE_ASYNC_RPC_PROC, CcnetAsyncRpcProcClass))
|
||||
#define IS_CCNET_ASYNC_RPC_PROC_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), CCNET_TYPE_ASYNC_RPC_PROC))
|
||||
#define CCNET_ASYNC_RPC_PROC_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), CCNET_TYPE_ASYNC_RPC_PROC, CcnetAsyncRpcProcClass))
|
||||
|
||||
typedef struct _CcnetAsyncRpcProc CcnetAsyncRpcProc;
|
||||
typedef struct _CcnetAsyncRpcProcClass CcnetAsyncRpcProcClass;
|
||||
|
||||
struct _CcnetAsyncRpcProc {
|
||||
CcnetProcessor parent_instance;
|
||||
};
|
||||
|
||||
struct _CcnetAsyncRpcProcClass {
|
||||
CcnetProcessorClass parent_class;
|
||||
};
|
||||
|
||||
GType ccnet_async_rpc_proc_get_type ();
|
||||
|
||||
void ccnet_async_rpc_proc_set_rpc (CcnetAsyncRpcProc *proc,
|
||||
const char *service,
|
||||
char *fcall_str,
|
||||
size_t fcall_len,
|
||||
void *rpc_priv);
|
||||
#endif
|
||||
|
193
include/ccnet/ccnet-client.h
Normal file
193
include/ccnet/ccnet-client.h
Normal file
@ -0,0 +1,193 @@
|
||||
/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
|
||||
|
||||
#ifndef CCNET_CLIENT_SESSION_H
|
||||
#define CCNET_CLIENT_SESSION_H
|
||||
|
||||
|
||||
#define SESSION_ID_LENGTH 40
|
||||
|
||||
|
||||
#include <glib.h>
|
||||
#include <glib-object.h>
|
||||
|
||||
#if defined(__FreeBSD__) || defined(__NetBSD__) || defined(__OpenBSD__)
|
||||
#include <event2/util.h>
|
||||
#else
|
||||
#include <evutil.h>
|
||||
#endif
|
||||
|
||||
#include "ccnet-session-base.h"
|
||||
|
||||
#define CCNET_TYPE_CLIENT (ccnet_client_get_type ())
|
||||
#define CCNET_CLIENT(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), CCNET_TYPE_CLIENT, CcnetClient))
|
||||
#define CCNET_IS_CLIENT(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), CCNET_TYPE_CLIENT))
|
||||
#define CCNET_CLIENT_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), CCNET_TYPE_CLIENT, CcnetClientClass))
|
||||
#define CCNET_IS_CLIENT_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), CCNET_TYPE_CLIENT))
|
||||
#define CCNET_CLIENT_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), CCNET_TYPE_CLIENT, CcnetClientClass))
|
||||
|
||||
typedef struct _CcnetClient CcnetClient;
|
||||
typedef struct _CcnetClientClass CcnetClientClass;
|
||||
|
||||
|
||||
struct _CcnetPeer;
|
||||
struct _CcnetUser;
|
||||
struct _CcnetProcessor;
|
||||
struct CcnetPacketIO;
|
||||
|
||||
#include <ccnet/message.h>
|
||||
#include <ccnet/processor.h>
|
||||
|
||||
#define SESSION_CONFIG_FILENAME "ccnet.conf"
|
||||
#define CCNET_USER_ID_START 1000
|
||||
|
||||
/**
|
||||
* CcnetClientMode:
|
||||
* @CCNET_CLIENT_SYNC: Synchronous mode
|
||||
* @CCNET_CLIENT_ASYNC: Asynchronous mode
|
||||
*
|
||||
* #CcnetClient can run in synchronous or asynchronous mode. In
|
||||
* synchronous mode, every function call to #CcnetClient is blocked
|
||||
* until the ccnet daemon returns. In asynchronous mode, function
|
||||
* calls are not blocked, and the user should use the processor
|
||||
* mechanism to interact with the daemon.
|
||||
*
|
||||
**/
|
||||
typedef enum {
|
||||
CCNET_CLIENT_SYNC,
|
||||
CCNET_CLIENT_ASYNC
|
||||
} CcnetClientMode;
|
||||
|
||||
|
||||
|
||||
struct CcnetResponse {
|
||||
char *code;
|
||||
char *code_msg;
|
||||
char *content;
|
||||
int clen; /* content length */
|
||||
};
|
||||
|
||||
typedef struct CcnetClientPriv CcnetClientPriv;
|
||||
|
||||
|
||||
/**
|
||||
* CcnetClient:
|
||||
* @id: The ccnet ID in plain text, including the ending '\0'
|
||||
* @id_sha1: The ccnet ID in binary.
|
||||
* @id_quark: The ccnet ID in GQuark.
|
||||
* @name: Ccnet name
|
||||
* @mode: See #CcnetClientMode
|
||||
*/
|
||||
struct _CcnetClient
|
||||
{
|
||||
CcnetSessionBase base;
|
||||
|
||||
/*< public >*/
|
||||
|
||||
int mode;
|
||||
|
||||
char *central_config_dir;
|
||||
char *config_dir;
|
||||
char *config_file;
|
||||
|
||||
int daemon_port;
|
||||
char *un_path;
|
||||
|
||||
int connected : 1;
|
||||
|
||||
struct _CcnetProcFactory *proc_factory;
|
||||
struct _CcnetGroupManager *group_mgr;
|
||||
/* For processor threads. */
|
||||
struct _CcnetJobManager *job_mgr;
|
||||
|
||||
struct CcnetResponse response;
|
||||
|
||||
|
||||
/*< private >*/
|
||||
evutil_socket_t connfd;
|
||||
uint32_t req_id; /* the current request id */
|
||||
|
||||
struct CcnetPacketIO *io;
|
||||
|
||||
GHashTable *processors;
|
||||
GList *rpc_pool;
|
||||
|
||||
CcnetClientPriv *priv;
|
||||
};
|
||||
|
||||
struct _CcnetClientClass
|
||||
{
|
||||
CcnetSessionBaseClass parent_class;
|
||||
};
|
||||
|
||||
GType ccnet_client_get_type (void);
|
||||
|
||||
CcnetClient* ccnet_client_new (void);
|
||||
int ccnet_client_load_confdir (CcnetClient *client, const char *central_config_dir, const char *confdir);
|
||||
|
||||
/*
|
||||
void ccnet_client_add_alias (CcnetClient *client, const char *alias_str);
|
||||
void ccnet_client_del_alias (CcnetClient *client, const char *alias_str);
|
||||
gboolean ccnet_client_has_alias (CcnetClient *client, const char *alias_id);
|
||||
*/
|
||||
|
||||
int ccnet_client_connect_daemon (CcnetClient *client, CcnetClientMode mode);
|
||||
int ccnet_client_disconnect_daemon (CcnetClient *client);
|
||||
|
||||
|
||||
uint32_t ccnet_client_get_request_id (CcnetClient *client);
|
||||
|
||||
/* async mode */
|
||||
void ccnet_client_run_synchronizer (CcnetClient *client);
|
||||
|
||||
void ccnet_client_add_processor (CcnetClient *client,
|
||||
CcnetProcessor *processor);
|
||||
|
||||
void ccnet_client_remove_processor (CcnetClient *client,
|
||||
CcnetProcessor *processor);
|
||||
|
||||
CcnetProcessor *
|
||||
ccnet_client_get_processor (CcnetClient *client, int id);
|
||||
|
||||
int ccnet_client_read_input (CcnetClient *client);
|
||||
|
||||
/* sync mode */
|
||||
int ccnet_client_read_response (CcnetClient *client);
|
||||
|
||||
void ccnet_client_send_request (CcnetClient *client,
|
||||
int req_id, const char *req);
|
||||
|
||||
void ccnet_client_send_update (CcnetClient *client, int req_id,
|
||||
const char *code, const char *reason,
|
||||
const char *content, int clen);
|
||||
|
||||
void ccnet_client_send_response (CcnetClient *client, int req_id,
|
||||
const char *code, const char *reason,
|
||||
const char *content, int clen);
|
||||
|
||||
const char *ccnet_client_send_cmd (CcnetClient *client,
|
||||
const char *cmd, GError **error);
|
||||
|
||||
int ccnet_client_send_message (CcnetClient *client,
|
||||
CcnetMessage *message);
|
||||
|
||||
|
||||
/* For a sync client to receive a given type of message,
|
||||
1. call ccnet_client_prepare_recv_message() with the message type you want to receive
|
||||
2. call ccnet_client_receive_message() repeatly
|
||||
*/
|
||||
int
|
||||
ccnet_client_prepare_recv_message (CcnetClient *client,
|
||||
const char *app);
|
||||
|
||||
CcnetMessage *
|
||||
ccnet_client_receive_message (CcnetClient *client);
|
||||
|
||||
uint32_t
|
||||
ccnet_client_get_rpc_request_id (CcnetClient *client, const char *peer_id,
|
||||
const char *service);
|
||||
void
|
||||
ccnet_client_clean_rpc_request (CcnetClient *client, uint32_t req_id);
|
||||
|
||||
/* void ccnet_client_send_event (CcnetClient *client, GObject *event); */
|
||||
|
||||
#endif
|
47
include/ccnet/ccnet-session-base.h
Normal file
47
include/ccnet/ccnet-session-base.h
Normal file
@ -0,0 +1,47 @@
|
||||
/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
|
||||
|
||||
#ifndef CCNET_SESSION_BASE_H
|
||||
#define CCNET_SESSION_BASE_H
|
||||
|
||||
#include <glib-object.h>
|
||||
|
||||
#define CCNET_TYPE_SESSION_BASE (ccnet_session_base_get_type ())
|
||||
#define CCNET_SESSION_BASE(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), CCNET_TYPE_SESSION_BASE, CcnetSessionBase))
|
||||
#define CCNET_IS_SESSION_BASE(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), CCNET_TYPE_SESSION_BASE))
|
||||
#define CCNET_SESSION_BASE_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), CCNET_TYPE_SESSION_BASE, CcnetSessionBaseClass))
|
||||
#define CCNET_IS_SESSION_BASE_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), CCNET_TYPE_SESSION_BASE))
|
||||
#define CCNET_SESSION_BASE_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), CCNET_TYPE_SESSION_BASE, CcnetSessionBaseClass))
|
||||
|
||||
#define CCNET_PIPE_NAME "ccnet.sock"
|
||||
|
||||
typedef struct _CcnetSessionBase CcnetSessionBase;
|
||||
typedef struct _CcnetSessionBaseClass CcnetSessionBaseClass;
|
||||
|
||||
struct _CcnetSessionBase {
|
||||
GObject parent_instance;
|
||||
|
||||
char id[41];
|
||||
unsigned char id_sha1[20];
|
||||
|
||||
char *user_name;
|
||||
|
||||
char *name;
|
||||
|
||||
int public_port;
|
||||
int net_status;
|
||||
|
||||
char *service_url;
|
||||
char *relay_id;
|
||||
};
|
||||
|
||||
|
||||
struct _CcnetSessionBaseClass {
|
||||
GObjectClass parent_class;
|
||||
};
|
||||
|
||||
|
||||
GType ccnet_session_base_get_type (void);
|
||||
|
||||
CcnetSessionBase *ccnet_session_base_new (void);
|
||||
|
||||
#endif
|
30
include/ccnet/ccnetrpc-transport.h
Normal file
30
include/ccnet/ccnetrpc-transport.h
Normal file
@ -0,0 +1,30 @@
|
||||
#ifndef CCNETRPC_TRANPORT_H
|
||||
#define CCNETRPC_TRANPORT_H
|
||||
|
||||
#include <ccnet.h>
|
||||
|
||||
typedef struct {
|
||||
/* either session or pool will be set. */
|
||||
CcnetClient *session;
|
||||
CcnetClientPool *pool;
|
||||
char *peer_id; /* NULL if local */
|
||||
char *service;
|
||||
} CcnetrpcTransportParam; /* this structure will be parsed to
|
||||
* ccnet_transport_send ()
|
||||
*/
|
||||
|
||||
typedef struct {
|
||||
CcnetClient *session;
|
||||
char *peer_id; /* NULL if local */
|
||||
char *service;
|
||||
} CcnetrpcAsyncTransportParam; /* this structure will be parsed to
|
||||
* ccnet_async_transport_send ()
|
||||
*/
|
||||
|
||||
char *ccnetrpc_transport_send (void *arg,
|
||||
const gchar *fcall_str, size_t fcall_len, size_t *ret_len);
|
||||
|
||||
int ccnetrpc_async_transport_send (void *arg, gchar *fcall_str,
|
||||
size_t fcall_len, void *rpc_priv);
|
||||
|
||||
#endif /* SEARPC_TRANPORT_H */
|
61
include/ccnet/cevent.h
Normal file
61
include/ccnet/cevent.h
Normal file
@ -0,0 +1,61 @@
|
||||
/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
|
||||
|
||||
/*
|
||||
* CEvent is used for send message from a work thread to main thread.
|
||||
*/
|
||||
#ifndef CEVENT_H
|
||||
#define CEVENT_H
|
||||
|
||||
#if defined(__FreeBSD__) || defined(__NetBSD__) || defined(__OpenBSD__)
|
||||
#include <event2/event.h>
|
||||
#include <event2/event_compat.h>
|
||||
#include <event2/event_struct.h>
|
||||
#else
|
||||
#include <event.h>
|
||||
#endif
|
||||
|
||||
#include <glib.h>
|
||||
|
||||
#include <pthread.h>
|
||||
|
||||
#ifdef WIN32
|
||||
#define ccnet_pipe_t intptr_t
|
||||
#else
|
||||
#define ccnet_pipe_t int
|
||||
#endif
|
||||
|
||||
typedef struct CEvent CEvent;
|
||||
|
||||
typedef void (*cevent_handler) (CEvent *event, void *handler_data);
|
||||
|
||||
struct CEvent {
|
||||
uint32_t id;
|
||||
void *data;
|
||||
};
|
||||
|
||||
|
||||
typedef struct CEventManager CEventManager;
|
||||
|
||||
struct CEventManager {
|
||||
|
||||
ccnet_pipe_t pipefd[2];
|
||||
struct event event;
|
||||
GHashTable *handler_table;
|
||||
uint32_t next_id;
|
||||
|
||||
pthread_mutex_t mutex;
|
||||
};
|
||||
|
||||
CEventManager* cevent_manager_new ();
|
||||
|
||||
int cevent_manager_start (CEventManager *manager);
|
||||
|
||||
uint32_t cevent_manager_register (CEventManager *manager,
|
||||
cevent_handler handler, void *handler_data);
|
||||
|
||||
void cevent_manager_unregister (CEventManager *manager, uint32_t id);
|
||||
|
||||
void cevent_manager_add_event (CEventManager *manager, uint32_t id,
|
||||
void *event_data);
|
||||
|
||||
#endif
|
39
include/ccnet/invoke-service-proc.h
Normal file
39
include/ccnet/invoke-service-proc.h
Normal file
@ -0,0 +1,39 @@
|
||||
/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
|
||||
|
||||
#ifndef CCNET_SERVICE_PROC_H
|
||||
#define CCNET_SERVICE_PROC_H
|
||||
|
||||
#include <glib-object.h>
|
||||
#include "processor.h"
|
||||
|
||||
#define CCNET_TYPE_SERVICE_PROC (ccnet_service_proc_get_type ())
|
||||
#define CCNET_SERVICE_PROC(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), CCNET_TYPE_SERVICE_PROC, CcnetServiceProc))
|
||||
#define CCNET_IS_SERVICE_PROC(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), CCNET_TYPE_SERVICE_PROC))
|
||||
#define CCNET_SERVICE_PROC_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), CCNET_TYPE_SERVICE_PROC, CcnetServiceProcClass))
|
||||
#define CCNET_IS_SERVICE_PROC_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), CCNET_TYPE_SERVICE_PROC))
|
||||
#define CCNET_SERVICE_PROC_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), CCNET_TYPE_SERVICE_PROC, CcnetServiceProcClass))
|
||||
|
||||
typedef struct _CcnetServiceProc CcnetServiceProc;
|
||||
typedef struct _CcnetServiceProcClass CcnetServiceProcClass;
|
||||
|
||||
typedef void (*ResponseCB) (const char *code, const char *code_msg,
|
||||
char *content, int clen, void *cb_data);
|
||||
|
||||
struct _CcnetServiceProc {
|
||||
CcnetProcessor parent_instance;
|
||||
|
||||
ResponseCB resp_cb;
|
||||
void *cb_data;
|
||||
};
|
||||
|
||||
struct _CcnetServiceProcClass {
|
||||
CcnetProcessorClass parent_class;
|
||||
};
|
||||
|
||||
GType ccnet_service_proc_get_type ();
|
||||
|
||||
void ccnet_service_proc_set_response_cb (CcnetServiceProc *proc,
|
||||
ResponseCB resp_cb, void *data);
|
||||
|
||||
|
||||
#endif
|
57
include/ccnet/job-mgr.h
Normal file
57
include/ccnet/job-mgr.h
Normal file
@ -0,0 +1,57 @@
|
||||
/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
|
||||
|
||||
/**
|
||||
* Job Manager manages long term jobs. These jobs are run in their
|
||||
* own threads.
|
||||
*/
|
||||
|
||||
#ifndef JOB_MGR_H
|
||||
#define JOB_MGR_H
|
||||
|
||||
#include <glib.h>
|
||||
|
||||
struct _CcnetSession;
|
||||
|
||||
typedef struct _CcnetJob CcnetJob;
|
||||
typedef struct _CcnetJobManager CcnetJobManager;
|
||||
|
||||
/*
|
||||
The thread func should return the result back by
|
||||
return (void *)result;
|
||||
The result will be passed to JobDoneCallback.
|
||||
*/
|
||||
typedef void* (*JobThreadFunc)(void *data);
|
||||
typedef void (*JobDoneCallback)(void *result);
|
||||
|
||||
|
||||
struct _CcnetJobManager {
|
||||
GHashTable *jobs;
|
||||
|
||||
GThreadPool *thread_pool;
|
||||
|
||||
int next_job_id;
|
||||
};
|
||||
|
||||
void
|
||||
ccnet_job_cancel (CcnetJob *job);
|
||||
|
||||
CcnetJobManager *
|
||||
ccnet_job_manager_new (int max_threads);
|
||||
|
||||
void
|
||||
ccnet_job_manager_free (CcnetJobManager *mgr);
|
||||
|
||||
int
|
||||
ccnet_job_manager_schedule_job (CcnetJobManager *mgr,
|
||||
JobThreadFunc func,
|
||||
JobDoneCallback done_func,
|
||||
void *data);
|
||||
|
||||
/**
|
||||
* Wait a specific job to be done.
|
||||
*/
|
||||
void
|
||||
ccnet_job_manager_wait_job (CcnetJobManager *mgr, int job_id);
|
||||
|
||||
|
||||
#endif
|
78
include/ccnet/message.h
Normal file
78
include/ccnet/message.h
Normal file
@ -0,0 +1,78 @@
|
||||
/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
|
||||
|
||||
#ifndef CCNET_MESSAGE_H
|
||||
#define CCNET_MESSAGE_H
|
||||
|
||||
|
||||
#include <glib.h>
|
||||
#include <glib-object.h>
|
||||
|
||||
#define CCNET_TYPE_MESSAGE (ccnet_message_get_type ())
|
||||
#define CCNET_MESSAGE(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), CCNET_TYPE_MESSAGE, CcnetMessage))
|
||||
#define CCNET_IS_MESSAGE(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), CCNET_TYPE_MESSAGE))
|
||||
#define CCNET_MESSAGE_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), CCNET_TYPE_MESSAGE, CcnetMessageClass))
|
||||
#define CCNET_IS_MESSAGE_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), CCNET_TYPE_MESSAGE))
|
||||
#define CCNET_MESSAGE_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), CCNET_TYPE_MESSAGE, CcnetMessageClass))
|
||||
|
||||
typedef struct _CcnetMessage CcnetMessage;
|
||||
typedef struct _CcnetMessageClass CcnetMessageClass;
|
||||
|
||||
#define FLAG_TO_GROUP 0x01
|
||||
#define FLAG_IS_ERROR 0x02
|
||||
#define FLAG_WITH_BLOOM 0x04
|
||||
#define FLAG_IS_ACK 0x08
|
||||
#define FLAG_IS_RENDEZVOUS 0x10
|
||||
#define FLAG_TO_USER 0x20
|
||||
|
||||
struct _CcnetMessage
|
||||
{
|
||||
GObject parent_instance;
|
||||
|
||||
char flags;
|
||||
char *id; /* UUID */
|
||||
|
||||
char from[41];
|
||||
char to[41];
|
||||
|
||||
int ctime; /* creation time */
|
||||
int rtime; /* receive time */
|
||||
|
||||
char *app; /* application */
|
||||
char *body;
|
||||
};
|
||||
|
||||
struct _CcnetMessageClass
|
||||
{
|
||||
GObjectClass parent_class;
|
||||
};
|
||||
|
||||
GType ccnet_message_get_type (void);
|
||||
|
||||
CcnetMessage* ccnet_message_new (const char *from_id,
|
||||
const char *to_id,
|
||||
const char *app,
|
||||
const char *body,
|
||||
int flags);
|
||||
|
||||
CcnetMessage* ccnet_message_new_full (const char *from_id,
|
||||
const char *to_id,
|
||||
const char *app,
|
||||
const char *body,
|
||||
time_t ctime,
|
||||
time_t rcv_time,
|
||||
const char *msg_id,
|
||||
int flags);
|
||||
|
||||
void ccnet_message_free (CcnetMessage *msg);
|
||||
|
||||
void ccnet_message_to_string_buf (CcnetMessage *msg, GString *buf);
|
||||
CcnetMessage *ccnet_message_from_string (char *buf, int len);
|
||||
|
||||
gboolean ccnet_message_is_to_group(CcnetMessage *msg);
|
||||
|
||||
/* to avoid string allocation */
|
||||
/* inline void ccnet_message_body_take (CcnetMessage *msg, char *body); */
|
||||
|
||||
/* inline void ccnet_message_body_dup (CcnetMessage *msg, char *body); */
|
||||
|
||||
#endif
|
44
include/ccnet/mqclient-proc.h
Normal file
44
include/ccnet/mqclient-proc.h
Normal file
@ -0,0 +1,44 @@
|
||||
/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
|
||||
|
||||
#ifndef CCNET_MQCLIENT_PROC_H
|
||||
#define CCNET_MQCLIENT_PROC_H
|
||||
|
||||
#include <glib-object.h>
|
||||
|
||||
#include "processor.h"
|
||||
#include "message.h"
|
||||
|
||||
#define CCNET_TYPE_MQCLIENT_PROC (ccnet_mqclient_proc_get_type ())
|
||||
#define CCNET_MQCLIENT_PROC(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), CCNET_TYPE_MQCLIENT_PROC, CcnetMqclientProc))
|
||||
#define CCNET_IS_MQCLIENT_PROC(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), CCNET_TYPE_MQCLIENT_PROC))
|
||||
#define CCNET_MQCLIENT_PROC_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), CCNET_TYPE_MQCLIENT_PROC, CcnetMqclientProcClass))
|
||||
#define CCNET_IS_MQCLIENT_PROC_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), CCNET_TYPE_MQCLIENT_PROC))
|
||||
#define CCNET_MQCLIENT_PROC_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), CCNET_TYPE_MQCLIENT_PROC, CcnetMqclientProcClass))
|
||||
|
||||
typedef struct _CcnetMqclientProc CcnetMqclientProc;
|
||||
typedef struct _CcnetMqclientProcClass CcnetMqclientProcClass;
|
||||
|
||||
typedef void (*MessageGotCB) (CcnetMessage *message, void *data);
|
||||
|
||||
struct _CcnetMqclientProc {
|
||||
CcnetProcessor parent_instance;
|
||||
|
||||
MessageGotCB message_got_cb;
|
||||
void *cb_data;
|
||||
};
|
||||
|
||||
struct _CcnetMqclientProcClass {
|
||||
CcnetProcessorClass parent_class;
|
||||
};
|
||||
|
||||
void ccnet_mqclient_proc_set_message_got_cb (CcnetMqclientProc *,
|
||||
MessageGotCB, void *);
|
||||
|
||||
GType ccnet_mqclient_proc_get_type ();
|
||||
|
||||
void ccnet_mqclient_proc_put_message (CcnetMqclientProc *proc,
|
||||
CcnetMessage *message);
|
||||
|
||||
void ccnet_mqclient_proc_unsubscribe_apps (CcnetMqclientProc *proc);
|
||||
|
||||
#endif
|
50
include/ccnet/option.h
Normal file
50
include/ccnet/option.h
Normal file
@ -0,0 +1,50 @@
|
||||
/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
|
||||
|
||||
#ifndef CCNET_OPTION_H
|
||||
#define CCNET_OPTION_H
|
||||
|
||||
#include <stdio.h>
|
||||
#include <glib.h>
|
||||
|
||||
#ifdef WIN32
|
||||
static inline char *GetDeafaultDir()
|
||||
{
|
||||
static char buf[128];
|
||||
static int inited = 0;
|
||||
|
||||
if (!inited) {
|
||||
const char *home = g_get_home_dir();
|
||||
inited = 1;
|
||||
snprintf(buf, 128, "%s/ccnet", home);
|
||||
}
|
||||
return buf;
|
||||
}
|
||||
|
||||
#define DEFAULT_CONFIG_DIR GetDeafaultDir()
|
||||
#define CONFIG_FILE_NAME "ccnet.conf"
|
||||
#define PREFS_FILE_NAME "prefs.conf"
|
||||
#else
|
||||
#define DEFAULT_CONFIG_DIR "~/.ccnet"
|
||||
#define CONFIG_FILE_NAME "ccnet.conf"
|
||||
#define PREFS_FILE_NAME "prefs.conf"
|
||||
#endif
|
||||
|
||||
#define PEER_KEYFILE "mykey.peer"
|
||||
|
||||
#define MAX_USERNAME_LEN 20
|
||||
#define MIN_USERNAME_LEN 2
|
||||
|
||||
#define DEFAULT_PORT 10001
|
||||
|
||||
#define CHAT_APP "Chat"
|
||||
#define PEERMGR_APP "PeerMgr"
|
||||
#define GROUPMGR_APP "GroupMgr"
|
||||
|
||||
|
||||
enum {
|
||||
NET_STATUS_DOWN,
|
||||
NET_STATUS_INNAT,
|
||||
NET_STATUS_FULL
|
||||
};
|
||||
|
||||
#endif
|
34
include/ccnet/packet.h
Normal file
34
include/ccnet/packet.h
Normal file
@ -0,0 +1,34 @@
|
||||
/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
|
||||
|
||||
#ifndef CCNET_PEER_MSG
|
||||
#define CCNET_PEER_MSG
|
||||
|
||||
#define CCNET_MSG_OK 0
|
||||
#define CCNET_MSG_HANDSHAKE 1
|
||||
#define CCNET_MSG_REQUEST 2
|
||||
#define CCNET_MSG_RESPONSE 3
|
||||
#define CCNET_MSG_UPDATE 4
|
||||
#define CCNET_MSG_RELAY 5 /* NOT USED NOW */
|
||||
#define CCNET_MSG_ENCPACKET 6 /* an encrypt packet */
|
||||
|
||||
typedef struct ccnet_header ccnet_header;
|
||||
|
||||
struct ccnet_header {
|
||||
uint8_t version;
|
||||
uint8_t type;
|
||||
uint16_t length; /* length of payload */
|
||||
uint32_t id; /* used as length in ecrypted packet */
|
||||
};
|
||||
|
||||
typedef struct ccnet_packet ccnet_packet;
|
||||
|
||||
struct ccnet_packet {
|
||||
struct ccnet_header header;
|
||||
char data[0];
|
||||
};
|
||||
|
||||
#define CCNET_PACKET_MAX_PAYLOAD_LEN 65535
|
||||
#define CCNET_PACKET_LENGTH_HEADER 8
|
||||
#define CCNET_USER_ID_START 1000
|
||||
|
||||
#endif
|
88
include/ccnet/peer.h
Normal file
88
include/ccnet/peer.h
Normal file
@ -0,0 +1,88 @@
|
||||
/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
|
||||
|
||||
#ifndef CCNET_PEER_H
|
||||
#define CCNET_PEER_H
|
||||
|
||||
#include <glib.h>
|
||||
#include <glib-object.h>
|
||||
#include <stdint.h>
|
||||
|
||||
|
||||
#define CCNET_TYPE_PEER (ccnet_peer_get_type ())
|
||||
#define CCNET_PEER(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), CCNET_TYPE_PEER, CcnetPeer))
|
||||
#define CCNET_IS_PEER(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), CCNET_TYPE_PEER))
|
||||
#define CCNET_PEER_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), CCNET_TYPE_PEER, CcnetPeerClass))
|
||||
#define CCNET_IS_PEER_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), CCNET_TYPE_PEER))
|
||||
#define CCNET_PEER_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), CCNET_TYPE_PEER, CcnetPeerClass))
|
||||
|
||||
|
||||
enum {
|
||||
PEER_DOWN,
|
||||
PEER_CONNECTED
|
||||
};
|
||||
|
||||
enum {
|
||||
BIND_UNKNOWN,
|
||||
BIND_YES,
|
||||
BIND_NO
|
||||
};
|
||||
|
||||
typedef struct _CcnetPeer CcnetPeer;
|
||||
typedef struct _CcnetPeerClass CcnetPeerClass;
|
||||
|
||||
#define CCNET_PEERID_LEN 40
|
||||
|
||||
|
||||
struct _CcnetPeer
|
||||
{
|
||||
GObject parent_instance;
|
||||
char id[41];
|
||||
char user_id[41];
|
||||
|
||||
gint64 timestamp;
|
||||
|
||||
char *name;
|
||||
|
||||
unsigned int is_self : 1;
|
||||
unsigned int can_connect : 1;
|
||||
unsigned int in_local_network : 1;
|
||||
unsigned int in_connection : 1;
|
||||
unsigned int is_ready : 1;
|
||||
unsigned int encrypt_channel : 1;
|
||||
|
||||
gboolean login_started;
|
||||
char *login_error;
|
||||
gboolean logout_started;
|
||||
|
||||
|
||||
char *public_addr;
|
||||
uint16_t public_port;
|
||||
char *service_url; /* http server for relay in seaflie */
|
||||
|
||||
char *addr_str;
|
||||
uint16_t port;
|
||||
|
||||
int net_state;
|
||||
|
||||
GList *role_list;
|
||||
|
||||
GList *myrole_list; /* my role on this user */
|
||||
|
||||
gint8 bind_status;
|
||||
char *bind_email;
|
||||
char *session_key;
|
||||
};
|
||||
|
||||
struct _CcnetPeerClass
|
||||
{
|
||||
GObjectClass parent_class;
|
||||
};
|
||||
|
||||
|
||||
GType ccnet_peer_get_type (void);
|
||||
|
||||
|
||||
CcnetPeer* ccnet_peer_new (const char *id);
|
||||
|
||||
|
||||
#endif
|
74
include/ccnet/proc-factory.h
Normal file
74
include/ccnet/proc-factory.h
Normal file
@ -0,0 +1,74 @@
|
||||
/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
|
||||
|
||||
#ifndef CCNET_PROC_FACTORY_H
|
||||
#define CCNET_PROC_FACTORY_H
|
||||
|
||||
|
||||
#include <glib-object.h>
|
||||
|
||||
#include "processor.h"
|
||||
|
||||
|
||||
#define CCNET_TYPE_PROC_FACTORY (ccnet_proc_factory_get_type ())
|
||||
#define CCNET_PROC_FACTORY(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), CCNET_TYPE_PROC_FACTORY, CcnetProcFactory))
|
||||
#define CCNET_IS_PROC_FACTORY(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), CCNET_TYPE_PROC_FACTORY))
|
||||
#define CCNET_PROC_FACTORY_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), CCNET_TYPE_PROC_FACTORY, CcnetProcFactoryClass))
|
||||
#define CCNET_IS_PROC_FACTORY_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), CCNET_TYPE_PROC_FACTORY))
|
||||
#define CCNET_PROC_FACTORY_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), CCNET_TYPE_PROC_FACTORY, CcnetProcFactoryClass))
|
||||
|
||||
typedef struct _CcnetProcFactory CcnetProcFactory;
|
||||
typedef struct _CcnetProcFactoryClass CcnetProcFactoryClass;
|
||||
|
||||
struct _CcnetProcFactory
|
||||
{
|
||||
GObject parent_instance;
|
||||
|
||||
/* protected */
|
||||
CcnetClient *session;
|
||||
};
|
||||
|
||||
struct _CcnetProcFactoryClass
|
||||
{
|
||||
GObjectClass parent_class;
|
||||
};
|
||||
|
||||
GType ccnet_proc_factory_get_type (void);
|
||||
|
||||
CcnetProcFactory *ccnet_proc_factory_new (CcnetClient *session);
|
||||
|
||||
void ccnet_proc_factory_register_processor (CcnetProcFactory *facotry,
|
||||
const char *serv_name,
|
||||
GType proc_type);
|
||||
|
||||
|
||||
enum {
|
||||
SLAVE = 0,
|
||||
MASTER = 1,
|
||||
};
|
||||
|
||||
|
||||
CcnetProcessor*
|
||||
ccnet_proc_factory_create_processor (CcnetProcFactory *factory,
|
||||
const char *serv_name,
|
||||
int is_master,
|
||||
int req_id);
|
||||
|
||||
CcnetProcessor*
|
||||
ccnet_proc_factory_create_master_processor (CcnetProcFactory *factory,
|
||||
const char *serv_name);
|
||||
|
||||
CcnetProcessor *
|
||||
ccnet_proc_factory_create_remote_master_processor (CcnetProcFactory *factory,
|
||||
const char *serv_name,
|
||||
const char *peer_id);
|
||||
CcnetProcessor *
|
||||
ccnet_proc_factory_create_slave_processor (CcnetProcFactory *factory,
|
||||
const char *serv_name,
|
||||
const char *peer_id,
|
||||
int req_id);
|
||||
|
||||
void ccnet_proc_factory_recycle(CcnetProcFactory *factory,
|
||||
CcnetProcessor *processor);
|
||||
|
||||
|
||||
#endif
|
168
include/ccnet/processor.h
Normal file
168
include/ccnet/processor.h
Normal file
@ -0,0 +1,168 @@
|
||||
/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
|
||||
|
||||
#ifndef CCNET_PROCESSOR_H
|
||||
#define CCNET_PROCESSOR_H
|
||||
|
||||
#include <glib.h>
|
||||
#include <glib-object.h>
|
||||
#include <stdint.h>
|
||||
#include <ccnet/status-code.h>
|
||||
|
||||
#include <pthread.h>
|
||||
|
||||
#ifdef WIN32
|
||||
#define ccnet_pipe_t intptr_t
|
||||
#else
|
||||
#define ccnet_pipe_t int
|
||||
#endif
|
||||
|
||||
struct _CcnetClient;
|
||||
|
||||
#define CCNET_TYPE_PROCESSOR (ccnet_processor_get_type ())
|
||||
#define CCNET_PROCESSOR(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), CCNET_TYPE_PROCESSOR, CcnetProcessor))
|
||||
#define CCNET_IS_PROCESSOR(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), CCNET_TYPE_PROCESSOR))
|
||||
#define CCNET_PROCESSOR_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), CCNET_TYPE_PROCESSOR, CcnetProcessorClass))
|
||||
#define CCNET_IS_PROCESSOR_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), CCNET_TYPE_PROCESSOR))
|
||||
#define CCNET_PROCESSOR_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), CCNET_TYPE_PROCESSOR, CcnetProcessorClass))
|
||||
|
||||
typedef struct _CcnetProcessor CcnetProcessor;
|
||||
typedef struct _CcnetProcessorClass CcnetProcessorClass;
|
||||
|
||||
|
||||
struct _CcnetProcessor {
|
||||
GObject parent_instance;
|
||||
|
||||
char *peer_id;
|
||||
struct _CcnetClient *session;
|
||||
|
||||
char *name;
|
||||
|
||||
/* highest bit = 0, master; highest bit = 1, slave */
|
||||
uint32_t id;
|
||||
int state;
|
||||
int failure;
|
||||
|
||||
struct CcnetTimer *timer;
|
||||
|
||||
int num_retry;
|
||||
|
||||
/* Set to 1 when handling update or response */
|
||||
gboolean is_active;
|
||||
|
||||
/* Internal flags for handling processor thread. */
|
||||
gboolean thread_running;
|
||||
gboolean delay_shutdown;
|
||||
gboolean was_success;
|
||||
};
|
||||
|
||||
enum {
|
||||
STATE_IN_SHUTDOWN = 1 << 8,
|
||||
STATE_RECYCLED,
|
||||
};
|
||||
|
||||
enum {
|
||||
PROC_NOTSET,
|
||||
PROC_DONE,
|
||||
PROC_REMOTE_DEAD,
|
||||
PROC_NO_SERVICE,
|
||||
PROC_PERM_ERR,
|
||||
PROC_BAD_RESP, /* code[0] =='5' || '4' */
|
||||
};
|
||||
|
||||
#define SLAVE_MASK 0x80000000
|
||||
#define REQUEST_ID_MASK 0x7fffffff
|
||||
#define REQUEST_ID(processor_id) ((processor_id) & REQUEST_ID_MASK)
|
||||
#define UPDATE_ID(processor_id) ((processor_id) & REQUEST_ID_MASK)
|
||||
#define RESPONSE_ID(processor_id) ((processor_id) & REQUEST_ID_MASK)
|
||||
#define SLAVE_ID(request_id) ((request_id) | SLAVE_MASK)
|
||||
#define MASTER_ID(request_id) (request_id)
|
||||
|
||||
#define PRINT_ID(processor_id) ((processor_id) & SLAVE_MASK) ? \
|
||||
- REQUEST_ID(processor_id) : REQUEST_ID(processor_id)
|
||||
#define IS_SLAVE(processor) ((processor)->id & SLAVE_MASK)
|
||||
#define GET_PNAME(processor) CCNET_PROCESSOR_GET_CLASS(processor)->name
|
||||
|
||||
struct _CcnetProcessorClass {
|
||||
GObjectClass parent_class;
|
||||
|
||||
char *name;
|
||||
|
||||
/* pure virtual function */
|
||||
int (*start) (CcnetProcessor *processor,
|
||||
int argc, char **argv);
|
||||
void (*handle_update) (CcnetProcessor *processor,
|
||||
char *code, char *code_msg,
|
||||
char *content, int clen);
|
||||
void (*handle_response) (CcnetProcessor *processor,
|
||||
char *code, char *code_msg,
|
||||
char *content, int clen);
|
||||
|
||||
void (*handle_sigchld) (CcnetProcessor *processor,
|
||||
int status);
|
||||
|
||||
void (*shutdown) (CcnetProcessor *processor);
|
||||
|
||||
void (*release_resource) (CcnetProcessor *processor);
|
||||
};
|
||||
|
||||
GType ccnet_processor_get_type ();
|
||||
|
||||
int ccnet_processor_start (CcnetProcessor *processor,
|
||||
int argc, char **argv);
|
||||
|
||||
int ccnet_processor_startl
|
||||
(CcnetProcessor *processor, ...) G_GNUC_NULL_TERMINATED;
|
||||
|
||||
void ccnet_processor_done (CcnetProcessor *processor, gboolean success);
|
||||
|
||||
void ccnet_processor_handle_update (CcnetProcessor *processor,
|
||||
char *code, char *code_msg,
|
||||
char *content, int clen);
|
||||
|
||||
void ccnet_processor_handle_response (CcnetProcessor *processor,
|
||||
char *code, char *code_msg,
|
||||
char *content, int clen);
|
||||
|
||||
void ccnet_processor_handle_sigchld (CcnetProcessor *processor,
|
||||
int status);
|
||||
|
||||
|
||||
void ccnet_processor_send_request (CcnetProcessor *processor,
|
||||
const char *request);
|
||||
|
||||
void ccnet_processor_send_request_l (CcnetProcessor *processor,
|
||||
...) G_GNUC_NULL_TERMINATED;
|
||||
|
||||
void ccnet_processor_send_update(CcnetProcessor *processor,
|
||||
const char *code,
|
||||
const char *code_msg,
|
||||
const char *content, int clen);
|
||||
|
||||
void ccnet_processor_send_response(CcnetProcessor *processor,
|
||||
const char *code,
|
||||
const char *code_msg,
|
||||
const char *content, int clen);
|
||||
|
||||
/*
|
||||
The thread func should return the result back by
|
||||
return (void *)result;
|
||||
The result will be passed to ProcThreadDoneFunc.
|
||||
In the done func, the caller should check whether processor->delay_shutdown
|
||||
is TRUE. If it is, you should call processor_done().
|
||||
*/
|
||||
typedef void* (*ProcThreadFunc)(void *data);
|
||||
typedef void (*ProcThreadDoneFunc)(void *result);
|
||||
|
||||
struct _CcnetJobManager;
|
||||
|
||||
/*
|
||||
* @job_mgr: the thread pool to create the worker thread.
|
||||
* If it's NULL, processor->session->job_mgr will be used.
|
||||
*/
|
||||
int ccnet_processor_thread_create (CcnetProcessor *processor,
|
||||
struct _CcnetJobManager *job_mgr,
|
||||
ProcThreadFunc func,
|
||||
ProcThreadDoneFunc done_func,
|
||||
void *data);
|
||||
|
||||
#endif
|
30
include/ccnet/rpcserver-proc.h
Normal file
30
include/ccnet/rpcserver-proc.h
Normal file
@ -0,0 +1,30 @@
|
||||
/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
|
||||
|
||||
#ifndef CCNET_RPCSERVER_PROC_H
|
||||
#define CCNET_RPCSERVER_PROC_H
|
||||
|
||||
#include <glib-object.h>
|
||||
|
||||
|
||||
#define CCNET_TYPE_RPCSERVER_PROC (ccnet_rpcserver_proc_get_type ())
|
||||
#define CCNET_RPCSERVER_PROC(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), CCNET_TYPE_RPCSERVER_PROC, CcnetRpcserverProc))
|
||||
#define CCNET_IS_RPCSERVER_PROC(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), CCNET_TYPE_RPCSERVER_PROC))
|
||||
#define CCNET_RPCSERVER_PROC_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), CCNET_TYPE_RPCSERVER_PROC, CcnetRpcserverProcClass))
|
||||
#define IS_CCNET_RPCSERVER_PROC_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), CCNET_TYPE_RPCSERVER_PROC))
|
||||
#define CCNET_RPCSERVER_PROC_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), CCNET_TYPE_RPCSERVER_PROC, CcnetRpcserverProcClass))
|
||||
|
||||
typedef struct _CcnetRpcserverProc CcnetRpcserverProc;
|
||||
typedef struct _CcnetRpcserverProcClass CcnetRpcserverProcClass;
|
||||
|
||||
struct _CcnetRpcserverProc {
|
||||
CcnetProcessor parent_instance;
|
||||
};
|
||||
|
||||
struct _CcnetRpcserverProcClass {
|
||||
CcnetProcessorClass parent_class;
|
||||
};
|
||||
|
||||
GType ccnet_rpcserver_proc_get_type ();
|
||||
|
||||
#endif
|
||||
|
41
include/ccnet/sendcmd-proc.h
Normal file
41
include/ccnet/sendcmd-proc.h
Normal file
@ -0,0 +1,41 @@
|
||||
/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
|
||||
|
||||
#ifndef CCNET_CMD_PROC_H
|
||||
#define CCNET_CMD_PROC_H
|
||||
|
||||
#include <glib-object.h>
|
||||
|
||||
#include "processor.h"
|
||||
|
||||
#define CCNET_TYPE_SENDCMD_PROC (ccnet_sendcmd_proc_get_type ())
|
||||
#define CCNET_SENDCMD_PROC(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), CCNET_TYPE_SENDCMD_PROC, CcnetSendcmdProc))
|
||||
#define CCNET_IS_SENDCMD_PROC(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), CCNET_TYPE_SENDCMD_PROC))
|
||||
#define CCNET_SENDCMD_PROC_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), CCNET_TYPE_SENDCMD_PROC, CcnetSendcmdProcClass))
|
||||
#define CCNET_IS_SENDCMD_PROC_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), CCNET_TYPE_SENDCMD_PROC))
|
||||
#define CCNET_SENDCMD_PROC_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), CCNET_TYPE_SENDCMD_PROC, CcnetSendcmdProcClass))
|
||||
|
||||
typedef struct _CcnetSendcmdProc CcnetSendcmdProc;
|
||||
typedef struct _CcnetSendcmdProcClass CcnetSendcmdProcClass;
|
||||
|
||||
typedef int (*SendcmdProcRcvrspCallback) (const char *code, char *content,
|
||||
int clen, void *data);
|
||||
|
||||
struct _CcnetSendcmdProc {
|
||||
CcnetProcessor parent_instance;
|
||||
|
||||
SendcmdProcRcvrspCallback rcvrsp_cb;
|
||||
void *cb_data;
|
||||
};
|
||||
|
||||
struct _CcnetSendcmdProcClass {
|
||||
CcnetProcessorClass parent_class;
|
||||
};
|
||||
|
||||
GType ccnet_sendcmd_proc_get_type ();
|
||||
|
||||
int ccnet_sendcmd_proc_send_command (CcnetSendcmdProc *proc, const char *cmd);
|
||||
void ccnet_sendcmd_proc_set_rcvrsp_cb (CcnetSendcmdProc *proc,
|
||||
SendcmdProcRcvrspCallback rcvrsp_cb,
|
||||
void *data);
|
||||
|
||||
#endif
|
64
include/ccnet/status-code.h
Normal file
64
include/ccnet/status-code.h
Normal file
@ -0,0 +1,64 @@
|
||||
/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
|
||||
|
||||
#ifndef STATUS_CODE_H
|
||||
#define STATUS_CODE_H
|
||||
|
||||
/* SC: Status Code. SS: Status String */
|
||||
|
||||
/* Error Code for use in GError */
|
||||
#define EC_NETWORK_ERR 1
|
||||
#define ES_NETWORK_ERR "Network Error"
|
||||
|
||||
|
||||
#define SC_PROC_KEEPALIVE "100"
|
||||
#define SS_PROC_KEEPALIVE "processor keep alive"
|
||||
#define SC_PROC_ALIVE "101"
|
||||
#define SS_PROC_ALIVE "processor is alive"
|
||||
#define SC_PROC_DEAD "102"
|
||||
#define SS_PROC_DEAD "processor is dead"
|
||||
#define SC_PROC_DONE "103"
|
||||
#define SS_PROC_DONE "service is done"
|
||||
|
||||
|
||||
#define SC_OK "200"
|
||||
#define SS_OK "OK"
|
||||
#define SC_PERM_CHECKING "250"
|
||||
#define SS_PERM_CHECKING "Permission Checking"
|
||||
|
||||
|
||||
#define SC_SHUTDOWN "500"
|
||||
#define SS_SHUTDOWN "Shutdown"
|
||||
#define SC_CREATE_PROC_ERR "501"
|
||||
#define SS_CREATE_PROC_ERR "Create Processor Error"
|
||||
#define SC_BAD_PEER "502"
|
||||
#define SS_BAD_PEER "Bad peer id"
|
||||
#define SC_BAD_USER "502"
|
||||
#define SS_BAD_USER "Bad user id"
|
||||
#define SC_BAD_ARGS "503"
|
||||
#define SS_BAD_ARGS "Bad arguments"
|
||||
#define SC_PERM_ERR "504"
|
||||
#define SS_PERM_ERR "Permission Error"
|
||||
#define SC_BAD_UPDATE_CODE "506"
|
||||
#define SS_BAD_UPDATE_CODE "Bad update code"
|
||||
#define SC_BAD_RESPONSE_CODE "507"
|
||||
#define SS_BAD_RESPONSE_CODE "Bad response code"
|
||||
#define SC_VERSION_MISMATCH "508"
|
||||
#define SS_VERSION_MISMATCH "Version Mismatch"
|
||||
#define SC_UNKNOWN_PEER "510"
|
||||
#define SS_UNKNOWN_PEER "Unknown peer"
|
||||
#define SC_UNKNOWN_SERVICE "511"
|
||||
#define SS_UNKNOWN_SERVICE "Unknown service"
|
||||
#define SC_UNKNOWN_SERVICE_IN_PERM "511"
|
||||
#define SS_UNKNOWN_SERVICE_IN_PERM "Unknown service in permission check"
|
||||
#define SC_PEER_UNREACHABLE "512"
|
||||
#define SS_PEER_UNREACHABLE "Peer Unreachable"
|
||||
#define SC_CON_TIMEOUT "513"
|
||||
#define SS_CON_TIMEOUT "connection timeout"
|
||||
#define SC_KEEPALIVE_TIMEOUT "514"
|
||||
#define SS_KEEPALIVE_TIMEOUT "keepalive timeout"
|
||||
#define SC_NETDOWN "515"
|
||||
#define SS_NETDOWN "peer down"
|
||||
#define SC_SERV_EXISTED "516"
|
||||
#define SS_SERV_EXISTED "The service existed"
|
||||
|
||||
#endif
|
30
include/ccnet/threaded-rpcserver-proc.h
Normal file
30
include/ccnet/threaded-rpcserver-proc.h
Normal file
@ -0,0 +1,30 @@
|
||||
/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
|
||||
|
||||
#ifndef CCNET_THREADED_RPCSERVER_PROC_H
|
||||
#define CCNET_THREADED_RPCSERVER_PROC_H
|
||||
|
||||
#include <glib-object.h>
|
||||
|
||||
|
||||
#define CCNET_TYPE_THREADED_RPCSERVER_PROC (ccnet_threaded_rpcserver_proc_get_type ())
|
||||
#define CCNET_THREADED_RPCSERVER_PROC(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), CCNET_TYPE_THREADED_RPCSERVER_PROC, CcnetThreadedRpcserverProc))
|
||||
#define CCNET_IS_THREADED_RPCSERVER_PROC(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), CCNET_TYPE_THREADED_RPCSERVER_PROC))
|
||||
#define CCNET_THREADED_RPCSERVER_PROC_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), CCNET_TYPE_THREADED_RPCSERVER_PROC, CcnetThreadedRpcserverProcClass))
|
||||
#define IS_CCNET_THREADED_RPCSERVER_PROC_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), CCNET_TYPE_THREADED_RPCSERVER_PROC))
|
||||
#define CCNET_THREADED_RPCSERVER_PROC_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), CCNET_TYPE_THREADED_RPCSERVER_PROC, CcnetThreadedRpcserverProcClass))
|
||||
|
||||
typedef struct _CcnetThreadedRpcserverProc CcnetThreadedRpcserverProc;
|
||||
typedef struct _CcnetThreadedRpcserverProcClass CcnetThreadedRpcserverProcClass;
|
||||
|
||||
struct _CcnetThreadedRpcserverProc {
|
||||
CcnetProcessor parent_instance;
|
||||
};
|
||||
|
||||
struct _CcnetThreadedRpcserverProcClass {
|
||||
CcnetProcessorClass parent_class;
|
||||
};
|
||||
|
||||
GType ccnet_threaded_rpcserver_proc_get_type ();
|
||||
|
||||
#endif
|
||||
|
28
include/ccnet/timer.h
Normal file
28
include/ccnet/timer.h
Normal file
@ -0,0 +1,28 @@
|
||||
/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
|
||||
|
||||
#ifndef CCNET_TIMER_H
|
||||
#define CCNET_TIMER_H
|
||||
|
||||
/* return TRUE to reschedule the timer, return FALSE to cancle the timer */
|
||||
typedef int (*TimerCB) (void *data);
|
||||
|
||||
struct CcnetTimer;
|
||||
|
||||
typedef struct CcnetTimer CcnetTimer;
|
||||
|
||||
/**
|
||||
* Calls timer_func(user_data) after the specified interval.
|
||||
* The timer is freed if timer_func returns zero.
|
||||
* Otherwise, it's called again after the same interval.
|
||||
*/
|
||||
CcnetTimer* ccnet_timer_new (TimerCB func,
|
||||
void *user_data,
|
||||
uint64_t timeout_milliseconds);
|
||||
|
||||
/**
|
||||
* Frees a timer and sets the timer pointer to NULL.
|
||||
*/
|
||||
void ccnet_timer_free (CcnetTimer **timer);
|
||||
|
||||
|
||||
#endif
|
29
include/ccnet/valid-check.h
Normal file
29
include/ccnet/valid-check.h
Normal file
@ -0,0 +1,29 @@
|
||||
/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
|
||||
|
||||
#ifndef VALID_CHECK_H
|
||||
#define VALID_CHECK_H
|
||||
|
||||
#include <string.h>
|
||||
|
||||
inline static gboolean user_id_valid(const char *id)
|
||||
{
|
||||
if (!id || strlen(id) != 40)
|
||||
return FALSE;
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
inline static gboolean peer_id_valid(const char *id)
|
||||
{
|
||||
if (!id || strlen(id) != 40)
|
||||
return FALSE;
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
gboolean is_uuid_valid (const char *uuid_str);
|
||||
|
||||
inline static gboolean group_id_valid(const char *id)
|
||||
{
|
||||
return is_uuid_valid(id);
|
||||
}
|
||||
|
||||
#endif
|
102
lib/Makefile.am
Normal file
102
lib/Makefile.am
Normal file
@ -0,0 +1,102 @@
|
||||
|
||||
AM_CPPFLAGS = @GLIB2_CFLAGS@ -I$(top_srcdir)/include \
|
||||
-I$(top_srcdir)/include/ccnet \
|
||||
-I$(top_srcdir)/lib \
|
||||
-I$(top_builddir)/include \
|
||||
-DG_LOG_DOMAIN=\"Ccnet\" \
|
||||
@SEARPC_CFLAGS@ \
|
||||
@MSVC_CFLAGS@ \
|
||||
-Wall
|
||||
|
||||
BUILT_SOURCES = gensource
|
||||
|
||||
lib_LTLIBRARIES = libccnet.la
|
||||
|
||||
noinst_HEADERS = buffer.h \
|
||||
packet-io.h \
|
||||
include.h \
|
||||
marshal.h \
|
||||
peer-common.h \
|
||||
string-util.h \
|
||||
libccnet_utils.h \
|
||||
ccnet-object.h \
|
||||
rpc-common.h \
|
||||
net.h \
|
||||
utils.h \
|
||||
bloom-filter.h \
|
||||
db.h \
|
||||
rsa.h
|
||||
|
||||
ccnetincludedir = $(includedir)/ccnet
|
||||
ccnetinclude_DATA = ccnet-object.h
|
||||
|
||||
libccnet_la_CPPFLAGS = $(AM_CPPFLAGS) -DCCNET_LIB @PTHREAD_CFLAGS@
|
||||
|
||||
libccnet_la_SOURCES = ccnet-client.c packet-io.c libccnet_utils.c \
|
||||
message.c proc-factory.c \
|
||||
processor.c buffer.c \
|
||||
peer.c sendcmd-proc.c \
|
||||
mqclient-proc.c invoke-service-proc.c \
|
||||
marshal.c \
|
||||
mainloop.c cevent.c timer.c ccnet-session-base.c job-mgr.c \
|
||||
rpcserver-proc.c ccnetrpc-transport.c threaded-rpcserver-proc.c \
|
||||
ccnetobj.c \
|
||||
async-rpc-proc.c ccnet-rpc-wrapper.c \
|
||||
client-pool.c
|
||||
|
||||
EXTRA_DIST = ccnetobj.vala rpc_table.py
|
||||
|
||||
libccnet_la_LDFLAGS = -no-undefined -version-info 0:0:0
|
||||
libccnet_la_LIBADD = @PTHREAD_LIBS@ @GLIB2_LIBS@ @GOBJECT_LIBS@ @SSL_LIBS@ @LIB_GDI32@ \
|
||||
@LIB_UUID@ @LIB_WS32@ @LIB_PSAPI@ -lsqlite3 \
|
||||
@LIBEVENT_LIBS@ @SEARPC_LIBS@ @LIB_SHELL32@
|
||||
|
||||
|
||||
noinst_LTLIBRARIES = libccnetd.la
|
||||
|
||||
libccnetd_la_SOURCES = utils.c db.c job-mgr.c \
|
||||
rsa.c bloom-filter.c marshal.c net.c timer.c ccnet-session-base.c \
|
||||
ccnetobj.c
|
||||
|
||||
libccnetd_la_LDFLAGS = -no-undefined
|
||||
libccnetd_la_LIBADD = @GLIB2_LIBS@ @GOBJECT_LIBS@ @SSL_LIBS@ @LIB_GDI32@ \
|
||||
-lsqlite3 @LIBEVENT_LIBS@ @LIB_WS32@ @LIB_UUID@ \
|
||||
@LIB_SHELL32@ @LIB_PSAPI@ @SEARPC_LIBS@
|
||||
|
||||
|
||||
ccnet_object_define = ccnetobj.vala
|
||||
|
||||
valac_gen = ccnetobj.c ccnet-object.h
|
||||
|
||||
ccnet-client.c: ccnet-object.h
|
||||
|
||||
ccnet-object.h: ${ccnet_object_define}
|
||||
rm -f $@
|
||||
valac --pkg posix ${ccnet_object_define} -C -H ccnet-object.h
|
||||
|
||||
ccnetobj.c: ${ccnet_object_define}
|
||||
rm -f $@
|
||||
valac -C --pkg posix ${ccnet_object_define}
|
||||
|
||||
searpc_gen = searpc-signature.h searpc-marshal.h
|
||||
|
||||
gensource: ${searpc_gen} ${valac_gen}
|
||||
|
||||
rpc_table.stamp: ${top_srcdir}/lib/rpc_table.py
|
||||
@rm -f rpc_table.tmp
|
||||
@touch rpc_table.tmp
|
||||
@echo "[libsearpc]: generating rpc header files"
|
||||
@PYTHON@ `which searpc-codegen.py` ${top_srcdir}/lib/rpc_table.py
|
||||
@echo "[libsearpc]: done"
|
||||
@mv -f rpc_table.tmp $@
|
||||
|
||||
${searpc_gen}: rpc_table.stamp
|
||||
|
||||
clean-local:
|
||||
rm -f ${searpc_gen}
|
||||
rm -f rpc_table.pyc
|
||||
rm -f rpc_table.stamp
|
||||
rm -f rpc_table.tmp
|
||||
rm -f ${valac_gen}
|
||||
|
||||
CLEANFILES = ${searpc_gen} ${valac_gen}
|
137
lib/async-rpc-proc.c
Normal file
137
lib/async-rpc-proc.c
Normal file
@ -0,0 +1,137 @@
|
||||
/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
|
||||
|
||||
#include <string.h>
|
||||
|
||||
#include <ccnet.h>
|
||||
#include <searpc-client.h>
|
||||
#include "rpc-common.h"
|
||||
#include <ccnet/async-rpc-proc.h>
|
||||
|
||||
#define MAX_RET_LEN 5242880 /* 5M */
|
||||
|
||||
typedef struct {
|
||||
const char *service;
|
||||
char *fcall_str;
|
||||
size_t fcall_len;
|
||||
void *rpc_priv;
|
||||
GString *buf;
|
||||
} CcnetAsyncRpcProcPriv;
|
||||
|
||||
#define GET_PRIV(o) \
|
||||
(G_TYPE_INSTANCE_GET_PRIVATE ((o), CCNET_TYPE_ASYNC_RPC_PROC, CcnetAsyncRpcProcPriv))
|
||||
|
||||
G_DEFINE_TYPE (CcnetAsyncRpcProc, ccnet_async_rpc_proc, CCNET_TYPE_PROCESSOR)
|
||||
|
||||
static int start (CcnetProcessor *processor, int argc, char **argv);
|
||||
static void handle_response (CcnetProcessor *processor,
|
||||
char *code, char *code_msg,
|
||||
char *content, int clen);
|
||||
|
||||
static void
|
||||
release_resource(CcnetProcessor *processor)
|
||||
{
|
||||
CcnetAsyncRpcProcPriv *priv = GET_PRIV (processor);
|
||||
g_free (priv->fcall_str);
|
||||
|
||||
CCNET_PROCESSOR_CLASS (ccnet_async_rpc_proc_parent_class)->release_resource (processor);
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
ccnet_async_rpc_proc_class_init (CcnetAsyncRpcProcClass *klass)
|
||||
{
|
||||
CcnetProcessorClass *proc_class = CCNET_PROCESSOR_CLASS (klass);
|
||||
|
||||
proc_class->start = start;
|
||||
proc_class->handle_response = handle_response;
|
||||
proc_class->release_resource = release_resource;
|
||||
proc_class->name = "async-rpc-proc";
|
||||
|
||||
g_type_class_add_private (klass, sizeof(CcnetAsyncRpcProcPriv));
|
||||
}
|
||||
|
||||
static void
|
||||
ccnet_async_rpc_proc_init (CcnetAsyncRpcProc *processor)
|
||||
{
|
||||
}
|
||||
|
||||
void
|
||||
ccnet_async_rpc_proc_set_rpc (CcnetAsyncRpcProc *proc,
|
||||
const char *service,
|
||||
char *fcall_str,
|
||||
size_t fcall_len,
|
||||
void *rpc_priv)
|
||||
{
|
||||
CcnetAsyncRpcProcPriv *priv = GET_PRIV (proc);
|
||||
|
||||
priv->service = service;
|
||||
priv->fcall_str = fcall_str;
|
||||
priv->fcall_len = fcall_len;
|
||||
priv->rpc_priv = rpc_priv;
|
||||
}
|
||||
|
||||
|
||||
static int
|
||||
start (CcnetProcessor *processor, int argc, char **argv)
|
||||
{
|
||||
CcnetAsyncRpcProcPriv *priv = GET_PRIV (processor);
|
||||
char buf[256];
|
||||
|
||||
if (argc != 0) {
|
||||
ccnet_processor_done (processor, FALSE);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (processor->peer_id) {
|
||||
snprintf (buf, sizeof(buf), "remote %s %s",
|
||||
processor->peer_id, priv->service);
|
||||
} else
|
||||
snprintf (buf, sizeof(buf), "%s", priv->service);
|
||||
ccnet_processor_send_request (processor, buf);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void
|
||||
handle_response (CcnetProcessor *processor,
|
||||
char *code, char *code_msg,
|
||||
char *content, int clen)
|
||||
{
|
||||
CcnetAsyncRpcProcPriv *priv = GET_PRIV (processor);
|
||||
|
||||
if (memcmp (code, SC_OK, 3) == 0) {
|
||||
ccnet_processor_send_update (processor, SC_CLIENT_CALL, SS_CLIENT_CALL,
|
||||
priv->fcall_str,
|
||||
priv->fcall_len);
|
||||
return;
|
||||
}
|
||||
|
||||
if (memcmp (code, SC_SERVER_RET, 3) == 0) {
|
||||
if (priv->buf == NULL)
|
||||
searpc_client_generic_callback (content, clen, priv->rpc_priv, NULL);
|
||||
else {
|
||||
g_string_append_len (priv->buf, content, clen);
|
||||
searpc_client_generic_callback (priv->buf->str, priv->buf->len,
|
||||
priv->rpc_priv, NULL);
|
||||
g_string_free (priv->buf, TRUE);
|
||||
priv->buf = NULL;
|
||||
}
|
||||
ccnet_processor_done (processor, TRUE);
|
||||
} else if (memcmp (code, SC_SERVER_MORE, 3) == 0) {
|
||||
if (priv->buf == NULL)
|
||||
priv->buf = g_string_new (NULL);
|
||||
g_string_append_len (priv->buf, content, clen);
|
||||
|
||||
if (priv->buf->len > MAX_RET_LEN) {
|
||||
g_warning ("[async-rpc] ret is too long\n");
|
||||
g_string_free (priv->buf, TRUE);
|
||||
priv->buf = NULL;
|
||||
ccnet_processor_send_update (processor, "400",
|
||||
"Too many data", NULL, 0);
|
||||
ccnet_processor_done (processor, FALSE);
|
||||
} else
|
||||
ccnet_processor_send_update (
|
||||
processor, SC_CLIENT_MORE, SS_CLIENT_MORE, NULL, 0);
|
||||
}
|
||||
|
||||
}
|
176
lib/bloom-filter.c
Normal file
176
lib/bloom-filter.c
Normal file
@ -0,0 +1,176 @@
|
||||
/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
|
||||
|
||||
#include <limits.h>
|
||||
#include <stdint.h>
|
||||
#include <string.h>
|
||||
#include <openssl/sha.h>
|
||||
#include <assert.h>
|
||||
|
||||
#include "bloom-filter.h"
|
||||
|
||||
#define SETBIT(a, n) (a[n/CHAR_BIT] |= (1<<(n%CHAR_BIT)))
|
||||
#define CLEARBIT(a, n) (a[n/CHAR_BIT] &= ~(1<<(n%CHAR_BIT)))
|
||||
#define GETBIT(a, n) (a[n/CHAR_BIT] & (1<<(n%CHAR_BIT)))
|
||||
|
||||
Bloom* bloom_create(size_t size, int k, int counting)
|
||||
{
|
||||
Bloom *bloom;
|
||||
size_t csize = 0;
|
||||
|
||||
if (k <=0 || k > 10) return NULL;
|
||||
|
||||
if ( !(bloom = malloc(sizeof(Bloom))) ) return NULL;
|
||||
if ( !(bloom->a = calloc((size+CHAR_BIT-1)/CHAR_BIT, sizeof(char))) )
|
||||
{
|
||||
free (bloom);
|
||||
return NULL;
|
||||
}
|
||||
if (counting) {
|
||||
csize = size*4;
|
||||
bloom->counters = calloc((csize+CHAR_BIT-1)/CHAR_BIT, sizeof(char));
|
||||
if (!bloom->counters) {
|
||||
free (bloom);
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
bloom->asize = size;
|
||||
bloom->csize = csize;
|
||||
bloom->k = k;
|
||||
bloom->counting = counting;
|
||||
|
||||
return bloom;
|
||||
}
|
||||
|
||||
int bloom_destroy(Bloom *bloom)
|
||||
{
|
||||
free (bloom->a);
|
||||
if (bloom->counting) free (bloom->counters);
|
||||
free (bloom);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void
|
||||
incr_bit (Bloom *bf, unsigned int bit_idx)
|
||||
{
|
||||
unsigned int char_idx, offset;
|
||||
unsigned char value;
|
||||
unsigned int high;
|
||||
unsigned int low;
|
||||
|
||||
SETBIT (bf->a, bit_idx);
|
||||
|
||||
if (!bf->counting) return;
|
||||
|
||||
char_idx = bit_idx / 2;
|
||||
offset = bit_idx % 2;
|
||||
|
||||
value = bf->counters[char_idx];
|
||||
low = value & 0xF;
|
||||
high = (value & 0xF0) >> 4;
|
||||
|
||||
if (offset == 0) {
|
||||
if (low < 0xF)
|
||||
low++;
|
||||
} else {
|
||||
if (high < 0xF)
|
||||
high++;
|
||||
}
|
||||
value = ((high << 4) | low);
|
||||
|
||||
bf->counters[char_idx] = value;
|
||||
}
|
||||
|
||||
static void
|
||||
decr_bit (Bloom *bf, unsigned int bit_idx)
|
||||
{
|
||||
unsigned int char_idx, offset;
|
||||
unsigned char value;
|
||||
unsigned int high;
|
||||
unsigned int low;
|
||||
|
||||
if (!bf->counting) {
|
||||
CLEARBIT (bf->a, bit_idx);
|
||||
return;
|
||||
}
|
||||
|
||||
char_idx = bit_idx / 2;
|
||||
offset = bit_idx % 2;
|
||||
|
||||
value = bf->counters[char_idx];
|
||||
low = value & 0xF;
|
||||
high = (value & 0xF0) >> 4;
|
||||
|
||||
/* decrement, but once we have reached the max, never go back! */
|
||||
if (offset == 0) {
|
||||
if ((low > 0) && (low < 0xF))
|
||||
low--;
|
||||
if (low == 0) {
|
||||
CLEARBIT (bf->a, bit_idx);
|
||||
}
|
||||
} else {
|
||||
if ((high > 0) && (high < 0xF))
|
||||
high--;
|
||||
if (high == 0) {
|
||||
CLEARBIT (bf->a, bit_idx);
|
||||
}
|
||||
}
|
||||
value = ((high << 4) | low);
|
||||
|
||||
bf->counters[char_idx] = value;
|
||||
}
|
||||
|
||||
int bloom_add(Bloom *bloom, const char *s)
|
||||
{
|
||||
int i;
|
||||
SHA_CTX c;
|
||||
unsigned char sha1[20];
|
||||
int16_t *sha_int = (int16_t *)&sha1;
|
||||
|
||||
SHA1_Init(&c);
|
||||
SHA1_Update(&c, s, strlen(s));
|
||||
SHA1_Final (sha1, &c);
|
||||
|
||||
for (i=0; i < bloom->k; ++i)
|
||||
incr_bit (bloom, sha_int[i] % bloom->asize);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int bloom_remove(Bloom *bloom, const char *s)
|
||||
{
|
||||
int i;
|
||||
SHA_CTX c;
|
||||
unsigned char sha1[20];
|
||||
int16_t *sha_int = (int16_t *)&sha1;
|
||||
|
||||
if (!bloom->counting)
|
||||
return -1;
|
||||
|
||||
SHA1_Init(&c);
|
||||
SHA1_Update(&c, s, strlen(s));
|
||||
SHA1_Final (sha1, &c);
|
||||
|
||||
for (i=0; i < bloom->k; ++i)
|
||||
decr_bit (bloom, sha_int[i] % bloom->asize);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int bloom_test(Bloom *bloom, const char *s)
|
||||
{
|
||||
int i;
|
||||
SHA_CTX c;
|
||||
unsigned char sha1[20];
|
||||
int16_t *sha_int = (int16_t *)&sha1;
|
||||
|
||||
SHA1_Init(&c);
|
||||
SHA1_Update(&c, s, strlen(s));
|
||||
SHA1_Final (sha1, &c);
|
||||
|
||||
for (i=0; i < bloom->k; ++i)
|
||||
if(!(GETBIT(bloom->a, sha_int[i] % bloom->asize))) return 0;
|
||||
|
||||
return 1;
|
||||
}
|
23
lib/bloom-filter.h
Normal file
23
lib/bloom-filter.h
Normal file
@ -0,0 +1,23 @@
|
||||
/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
|
||||
|
||||
#ifndef __BLOOM_H__
|
||||
#define __BLOOM_H__
|
||||
|
||||
#include <stdlib.h>
|
||||
|
||||
typedef struct {
|
||||
size_t asize;
|
||||
unsigned char *a;
|
||||
size_t csize;
|
||||
unsigned char *counters;
|
||||
int k;
|
||||
char counting:1;
|
||||
} Bloom;
|
||||
|
||||
Bloom *bloom_create (size_t size, int k, int counting);
|
||||
int bloom_destroy (Bloom *bloom);
|
||||
int bloom_add (Bloom *bloom, const char *s);
|
||||
int bloom_remove (Bloom *bloom, const char *s);
|
||||
int bloom_test (Bloom *bloom, const char *s);
|
||||
|
||||
#endif
|
396
lib/buffer.c
Normal file
396
lib/buffer.c
Normal file
@ -0,0 +1,396 @@
|
||||
/*
|
||||
* Copyright (c) 2002, 2003 Niels Provos <provos@citi.umich.edu>
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* 3. The name of the author may not be used to endorse or promote products
|
||||
* derived from this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
|
||||
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
|
||||
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
|
||||
* IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
|
||||
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
|
||||
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
|
||||
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include "config.h"
|
||||
#endif
|
||||
|
||||
#ifdef WIN32
|
||||
#include <winsock2.h>
|
||||
#include <windows.h>
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_VASPRINTF
|
||||
/* If we have vasprintf, we need to define this before we include stdio.h. */
|
||||
#define _GNU_SOURCE
|
||||
#endif
|
||||
|
||||
#include <sys/types.h>
|
||||
|
||||
#ifdef HAVE_SYS_TIME_H
|
||||
#include <sys/time.h>
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_SYS_IOCTL_H
|
||||
#include <sys/ioctl.h>
|
||||
#endif
|
||||
|
||||
#include <assert.h>
|
||||
#include <errno.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#ifdef HAVE_STDARG_H
|
||||
#include <stdarg.h>
|
||||
#endif
|
||||
#ifdef HAVE_UNISTD_H
|
||||
#include <unistd.h>
|
||||
#endif
|
||||
|
||||
#include "buffer.h"
|
||||
|
||||
struct buffer *
|
||||
buffer_new(void)
|
||||
{
|
||||
struct buffer *buffer;
|
||||
|
||||
buffer = calloc(1, sizeof(struct buffer));
|
||||
|
||||
return (buffer);
|
||||
}
|
||||
|
||||
void
|
||||
buffer_free(struct buffer *buffer)
|
||||
{
|
||||
if (buffer->orig_buffer != NULL)
|
||||
free(buffer->orig_buffer);
|
||||
free(buffer);
|
||||
}
|
||||
|
||||
/*
|
||||
* This is a destructive add. The data from one buffer moves into
|
||||
* the other buffer.
|
||||
*/
|
||||
|
||||
#define SWAP(x,y) do { \
|
||||
(x)->buffer = (y)->buffer; \
|
||||
(x)->orig_buffer = (y)->orig_buffer; \
|
||||
(x)->misalign = (y)->misalign; \
|
||||
(x)->totallen = (y)->totallen; \
|
||||
(x)->off = (y)->off; \
|
||||
} while (0)
|
||||
|
||||
int
|
||||
buffer_add_buffer(struct buffer *outbuf, struct buffer *inbuf)
|
||||
{
|
||||
int res;
|
||||
|
||||
/* Short cut for better performance */
|
||||
if (outbuf->off == 0) {
|
||||
struct buffer tmp;
|
||||
size_t oldoff = inbuf->off;
|
||||
|
||||
/* Swap them directly */
|
||||
SWAP(&tmp, outbuf);
|
||||
SWAP(outbuf, inbuf);
|
||||
SWAP(inbuf, &tmp);
|
||||
|
||||
/*
|
||||
* Optimization comes with a price; we need to notify the
|
||||
* buffer if necessary of the changes. oldoff is the amount
|
||||
* of data that we transfered from inbuf to outbuf
|
||||
*/
|
||||
if (inbuf->off != oldoff && inbuf->cb != NULL)
|
||||
(*inbuf->cb)(inbuf, oldoff, inbuf->off, inbuf->cbarg);
|
||||
if (oldoff && outbuf->cb != NULL)
|
||||
(*outbuf->cb)(outbuf, 0, oldoff, outbuf->cbarg);
|
||||
|
||||
return (0);
|
||||
}
|
||||
|
||||
res = buffer_add(outbuf, inbuf->buffer, inbuf->off);
|
||||
if (res == 0) {
|
||||
/* We drain the input buffer on success */
|
||||
buffer_drain(inbuf, inbuf->off);
|
||||
}
|
||||
|
||||
return (res);
|
||||
}
|
||||
|
||||
|
||||
/* Reads data from an event buffer and drains the bytes read */
|
||||
|
||||
int
|
||||
buffer_remove(struct buffer *buf, void *data, size_t datlen)
|
||||
{
|
||||
size_t nread = datlen;
|
||||
if (nread >= buf->off)
|
||||
nread = buf->off;
|
||||
|
||||
memcpy(data, buf->buffer, nread);
|
||||
buffer_drain(buf, nread);
|
||||
|
||||
return (nread);
|
||||
}
|
||||
|
||||
/*
|
||||
* Reads a line terminated by either '\r\n', '\n\r' or '\r' or '\n'.
|
||||
* The returned buffer needs to be freed by the called.
|
||||
*/
|
||||
|
||||
char *
|
||||
buffer_readline(struct buffer *buffer)
|
||||
{
|
||||
u_char *data = BUFFER_DATA(buffer);
|
||||
size_t len = BUFFER_LENGTH(buffer);
|
||||
char *line;
|
||||
unsigned int i;
|
||||
|
||||
for (i = 0; i < len; i++) {
|
||||
if (data[i] == '\r' || data[i] == '\n')
|
||||
break;
|
||||
}
|
||||
|
||||
if (i == len)
|
||||
return (NULL);
|
||||
|
||||
if ((line = malloc(i + 1)) == NULL) {
|
||||
fprintf(stderr, "%s: out of memory\n", __func__);
|
||||
buffer_drain(buffer, i);
|
||||
return (NULL);
|
||||
}
|
||||
|
||||
memcpy(line, data, i);
|
||||
line[i] = '\0';
|
||||
|
||||
/*
|
||||
* Some protocols terminate a line with '\r\n', so check for
|
||||
* that, too.
|
||||
*/
|
||||
if ( i < len - 1 ) {
|
||||
char fch = data[i], sch = data[i+1];
|
||||
|
||||
/* Drain one more character if needed */
|
||||
if ( (sch == '\r' || sch == '\n') && sch != fch )
|
||||
i += 1;
|
||||
}
|
||||
|
||||
buffer_drain(buffer, i + 1);
|
||||
|
||||
return (line);
|
||||
}
|
||||
|
||||
/* Adds data to an event buffer */
|
||||
|
||||
static void
|
||||
buffer_align(struct buffer *buf)
|
||||
{
|
||||
memmove(buf->orig_buffer, buf->buffer, buf->off);
|
||||
buf->buffer = buf->orig_buffer;
|
||||
buf->misalign = 0;
|
||||
}
|
||||
|
||||
/* Expands the available space in the event buffer to at least datlen */
|
||||
|
||||
int
|
||||
buffer_expand(struct buffer *buf, size_t datlen)
|
||||
{
|
||||
size_t need = buf->misalign + buf->off + datlen;
|
||||
|
||||
/* If we can fit all the data, then we don't have to do anything */
|
||||
if (buf->totallen >= need)
|
||||
return (0);
|
||||
|
||||
/*
|
||||
* If the misalignment fulfills our data needs, we just force an
|
||||
* alignment to happen. Afterwards, we have enough space.
|
||||
*/
|
||||
if (buf->misalign >= datlen) {
|
||||
buffer_align(buf);
|
||||
} else {
|
||||
void *newbuf;
|
||||
size_t length = buf->totallen;
|
||||
|
||||
if (length < 256)
|
||||
length = 256;
|
||||
while (length < need)
|
||||
length <<= 1;
|
||||
|
||||
if (buf->orig_buffer != buf->buffer)
|
||||
buffer_align(buf);
|
||||
if ((newbuf = realloc(buf->buffer, length)) == NULL)
|
||||
return (-1);
|
||||
|
||||
buf->orig_buffer = buf->buffer = newbuf;
|
||||
buf->totallen = length;
|
||||
}
|
||||
|
||||
return (0);
|
||||
}
|
||||
|
||||
int
|
||||
buffer_add(struct buffer *buf, const void *data, size_t datlen)
|
||||
{
|
||||
size_t need = buf->misalign + buf->off + datlen;
|
||||
size_t oldoff = buf->off;
|
||||
|
||||
if (buf->totallen < need) {
|
||||
if (buffer_expand(buf, datlen) == -1)
|
||||
return (-1);
|
||||
}
|
||||
|
||||
memcpy(buf->buffer + buf->off, data, datlen);
|
||||
buf->off += datlen;
|
||||
|
||||
if (datlen && buf->cb != NULL)
|
||||
(*buf->cb)(buf, oldoff, buf->off, buf->cbarg);
|
||||
|
||||
return (0);
|
||||
}
|
||||
|
||||
void
|
||||
buffer_drain(struct buffer *buf, size_t len)
|
||||
{
|
||||
size_t oldoff = buf->off;
|
||||
|
||||
if (len >= buf->off) {
|
||||
buf->off = 0;
|
||||
buf->buffer = buf->orig_buffer;
|
||||
buf->misalign = 0;
|
||||
goto done;
|
||||
}
|
||||
|
||||
buf->buffer += len;
|
||||
buf->misalign += len;
|
||||
|
||||
buf->off -= len;
|
||||
|
||||
done:
|
||||
/* Tell someone about changes in this buffer */
|
||||
if (buf->off != oldoff && buf->cb != NULL)
|
||||
(*buf->cb)(buf, oldoff, buf->off, buf->cbarg);
|
||||
|
||||
}
|
||||
|
||||
/*
|
||||
* Reads data from a file descriptor into a buffer.
|
||||
*/
|
||||
|
||||
#define BUFFER_MAX_READ 4096
|
||||
|
||||
int
|
||||
buffer_read(struct buffer *buf, int fd, int howmuch)
|
||||
{
|
||||
u_char *p;
|
||||
size_t oldoff = buf->off;
|
||||
int n = BUFFER_MAX_READ;
|
||||
|
||||
#if defined(FIONREAD)
|
||||
#ifdef WIN32
|
||||
unsigned long lng = n;
|
||||
if (ioctlsocket(fd, FIONREAD, &lng) == -1 || (n=lng) == 0) {
|
||||
#else
|
||||
if (ioctl(fd, FIONREAD, &n) == -1 || n == 0) {
|
||||
#endif
|
||||
n = BUFFER_MAX_READ;
|
||||
} else if (n > BUFFER_MAX_READ && n > howmuch) {
|
||||
/*
|
||||
* It's possible that a lot of data is available for
|
||||
* reading. We do not want to exhaust resources
|
||||
* before the reader has a chance to do something
|
||||
* about it. If the reader does not tell us how much
|
||||
* data we should read, we artifically limit it.
|
||||
*/
|
||||
if (n > buf->totallen << 2)
|
||||
n = buf->totallen << 2;
|
||||
if (n < BUFFER_MAX_READ)
|
||||
n = BUFFER_MAX_READ;
|
||||
}
|
||||
#endif
|
||||
if (howmuch < 0 || howmuch > n)
|
||||
howmuch = n;
|
||||
|
||||
/* If we don't have FIONREAD, we might waste some space here */
|
||||
if (buffer_expand(buf, howmuch) == -1)
|
||||
return (-1);
|
||||
|
||||
/* We can append new data at this point */
|
||||
p = buf->buffer + buf->off;
|
||||
|
||||
#ifndef WIN32
|
||||
n = read(fd, p, howmuch);
|
||||
#else
|
||||
n = recv(fd, (char *)p, howmuch, 0);
|
||||
#endif
|
||||
if (n == -1)
|
||||
return (-1);
|
||||
if (n == 0)
|
||||
return (0);
|
||||
|
||||
buf->off += n;
|
||||
|
||||
/* Tell someone about changes in this buffer */
|
||||
if (buf->off != oldoff && buf->cb != NULL)
|
||||
(*buf->cb)(buf, oldoff, buf->off, buf->cbarg);
|
||||
|
||||
return (n);
|
||||
}
|
||||
|
||||
int
|
||||
buffer_write(struct buffer *buffer, int fd)
|
||||
{
|
||||
int n;
|
||||
|
||||
#ifndef WIN32
|
||||
n = write(fd, buffer->buffer, buffer->off);
|
||||
#else
|
||||
n = send(fd, (char *)buffer->buffer, buffer->off, 0);
|
||||
#endif
|
||||
if (n == -1)
|
||||
return (-1);
|
||||
if (n == 0)
|
||||
return (0);
|
||||
buffer_drain(buffer, n);
|
||||
|
||||
return (n);
|
||||
}
|
||||
|
||||
u_char *
|
||||
buffer_find(struct buffer *buffer, const u_char *what, size_t len)
|
||||
{
|
||||
u_char *search = buffer->buffer, *end = search + buffer->off;
|
||||
u_char *p;
|
||||
|
||||
while (search < end &&
|
||||
(p = memchr(search, *what, end - search)) != NULL) {
|
||||
if (p + len > end)
|
||||
break;
|
||||
if (memcmp(p, what, len) == 0)
|
||||
return (p);
|
||||
search = p + 1;
|
||||
}
|
||||
|
||||
return (NULL);
|
||||
}
|
||||
|
||||
void buffer_setcb(struct buffer *buffer,
|
||||
void (*cb)(struct buffer *, size_t, size_t, void *),
|
||||
void *cbarg)
|
||||
{
|
||||
buffer->cb = cb;
|
||||
buffer->cbarg = cbarg;
|
||||
}
|
193
lib/buffer.h
Normal file
193
lib/buffer.h
Normal file
@ -0,0 +1,193 @@
|
||||
#ifndef BUFFER_H
|
||||
#define BUFFER_H
|
||||
|
||||
#include <stdarg.h>
|
||||
|
||||
#ifdef WIN32
|
||||
typedef unsigned char u_char;
|
||||
typedef unsigned short u_short;
|
||||
#endif
|
||||
|
||||
|
||||
struct buffer {
|
||||
u_char *buffer;
|
||||
u_char *orig_buffer;
|
||||
|
||||
size_t misalign;
|
||||
size_t totallen;
|
||||
size_t off;
|
||||
|
||||
void (*cb)(struct buffer *, size_t, size_t, void *);
|
||||
void *cbarg;
|
||||
};
|
||||
|
||||
/* Just for error reporting - use other constants otherwise */
|
||||
#define BUFFER_READ 0x01
|
||||
#define BUFFER_WRITE 0x02
|
||||
#define BUFFER_EOF 0x10
|
||||
#define BUFFER_ERROR 0x20
|
||||
#define BUFFER_TIMEOUT 0x40
|
||||
|
||||
#define BUFFER_LENGTH(x) (x)->off
|
||||
#define BUFFER_DATA(x) (x)->buffer
|
||||
#define BUFFER_INPUT(x) (x)->input
|
||||
#define BUFFER_OUTPUT(x) (x)->output
|
||||
|
||||
/**
|
||||
Allocate storage for a new buffer.
|
||||
|
||||
@return a pointer to a newly allocated buffer struct, or NULL if an error
|
||||
occurred
|
||||
*/
|
||||
struct buffer *buffer_new(void);
|
||||
|
||||
|
||||
/**
|
||||
Deallocate storage for an buffer.
|
||||
|
||||
@param pointer to the buffer to be freed
|
||||
*/
|
||||
void buffer_free(struct buffer *);
|
||||
|
||||
|
||||
/**
|
||||
Expands the available space in an event buffer.
|
||||
|
||||
Expands the available space in the event buffer to at least datlen
|
||||
|
||||
@param buf the event buffer to be expanded
|
||||
@param datlen the new minimum length requirement
|
||||
@return 0 if successful, or -1 if an error occurred
|
||||
*/
|
||||
int buffer_expand(struct buffer *, size_t);
|
||||
|
||||
|
||||
/**
|
||||
Append data to the end of an buffer.
|
||||
|
||||
@param buf the event buffer to be appended to
|
||||
@param data pointer to the beginning of the data buffer
|
||||
@param datlen the number of bytes to be copied from the data buffer
|
||||
*/
|
||||
int buffer_add(struct buffer *, const void *, size_t);
|
||||
|
||||
|
||||
|
||||
/**
|
||||
Read data from an event buffer and drain the bytes read.
|
||||
|
||||
@param buf the event buffer to be read from
|
||||
@param data the destination buffer to store the result
|
||||
@param datlen the maximum size of the destination buffer
|
||||
@return the number of bytes read
|
||||
*/
|
||||
int buffer_remove(struct buffer *, void *, size_t);
|
||||
|
||||
|
||||
/**
|
||||
* Read a single line from an event buffer.
|
||||
*
|
||||
* Reads a line terminated by either '\r\n', '\n\r' or '\r' or '\n'.
|
||||
* The returned buffer needs to be freed by the caller.
|
||||
*
|
||||
* @param buffer the buffer to read from
|
||||
* @return pointer to a single line, or NULL if an error occurred
|
||||
*/
|
||||
char *buffer_readline(struct buffer *);
|
||||
|
||||
|
||||
/**
|
||||
Move data from one buffer into another buffer.
|
||||
|
||||
This is a destructive add. The data from one buffer moves into
|
||||
the other buffer. The destination buffer is expanded as needed.
|
||||
|
||||
@param outbuf the output buffer
|
||||
@param inbuf the input buffer
|
||||
@return 0 if successful, or -1 if an error occurred
|
||||
*/
|
||||
int buffer_add_buffer(struct buffer *, struct buffer *);
|
||||
|
||||
|
||||
/**
|
||||
Append a formatted string to the end of an buffer.
|
||||
|
||||
@param buf the buffer that will be appended to
|
||||
@param fmt a format string
|
||||
@param ... arguments that will be passed to printf(3)
|
||||
@return The number of bytes added if successful, or -1 if an error occurred.
|
||||
*/
|
||||
int buffer_add_printf(struct buffer *, const char *fmt, ...)
|
||||
#ifdef __GNUC__
|
||||
__attribute__((format(printf, 2, 3)))
|
||||
#endif
|
||||
;
|
||||
|
||||
|
||||
/**
|
||||
Append a va_list formatted string to the end of an buffer.
|
||||
|
||||
@param buf the buffer that will be appended to
|
||||
@param fmt a format string
|
||||
@param ap a varargs va_list argument array that will be passed to vprintf(3)
|
||||
@return The number of bytes added if successful, or -1 if an error occurred.
|
||||
*/
|
||||
int buffer_add_vprintf(struct buffer *, const char *fmt, va_list ap);
|
||||
|
||||
|
||||
/**
|
||||
Remove a specified number of bytes data from the beginning of an buffer.
|
||||
|
||||
@param buf the buffer to be drained
|
||||
@param len the number of bytes to drain from the beginning of the buffer
|
||||
@return 0 if successful, or -1 if an error occurred
|
||||
*/
|
||||
void buffer_drain(struct buffer *, size_t);
|
||||
|
||||
|
||||
/**
|
||||
Write the contents of an buffer to a file descriptor.
|
||||
|
||||
The buffer will be drained after the bytes have been successfully written.
|
||||
|
||||
@param buffer the buffer to be written and drained
|
||||
@param fd the file descriptor to be written to
|
||||
@return the number of bytes written, or -1 if an error occurred
|
||||
@see buffer_read()
|
||||
*/
|
||||
int buffer_write(struct buffer *, int);
|
||||
|
||||
|
||||
/**
|
||||
Read from a file descriptor and store the result in an buffer.
|
||||
|
||||
@param buf the buffer to store the result
|
||||
@param fd the file descriptor to read from
|
||||
@param howmuch the number of bytes to be read
|
||||
@return the number of bytes read, or -1 if an error occurred
|
||||
@see buffer_write()
|
||||
*/
|
||||
int buffer_read(struct buffer *, int, int);
|
||||
|
||||
|
||||
/**
|
||||
Find a string within an buffer.
|
||||
|
||||
@param buffer the buffer to be searched
|
||||
@param what the string to be searched for
|
||||
@param len the length of the search string
|
||||
@return a pointer to the beginning of the search string, or NULL if the search failed.
|
||||
*/
|
||||
u_char *buffer_find(struct buffer *, const u_char *, size_t);
|
||||
|
||||
/**
|
||||
Set a callback to invoke when the buffer is modified.
|
||||
|
||||
@param buffer the buffer to be monitored
|
||||
@param cb the callback function to invoke when the buffer is modified
|
||||
@param cbarg an argument to be provided to the callback function
|
||||
*/
|
||||
void buffer_setcb(struct buffer *, void (*)(struct buffer *, size_t, size_t, void *), void *);
|
||||
|
||||
|
||||
#endif
|
36
lib/buildin-types.c.template
Normal file
36
lib/buildin-types.c.template
Normal file
@ -0,0 +1,36 @@
|
||||
/*** BEGIN file-header ***/
|
||||
#include <glib.h>
|
||||
#include <glib-object.h>
|
||||
|
||||
#include <ccnet.h>
|
||||
/*** END file-header ***/
|
||||
|
||||
/*** BEGIN file-production ***/
|
||||
/* enumerations from "@filename@" */
|
||||
/*** END file-production ***/
|
||||
|
||||
/*** BEGIN value-header ***/
|
||||
GType
|
||||
@enum_name@_get_type (void)
|
||||
{
|
||||
static GType etype = 0;
|
||||
if (G_UNLIKELY(etype == 0)) {
|
||||
static const G@Type@Value values[] = {
|
||||
/*** END value-header ***/
|
||||
|
||||
/*** BEGIN value-production ***/
|
||||
{ @VALUENAME@, "@VALUENAME@", "@valuenick@" },
|
||||
/*** END value-production ***/
|
||||
|
||||
/*** BEGIN value-tail ***/
|
||||
{ 0, NULL, NULL }
|
||||
};
|
||||
etype = g_@type@_register_static (g_intern_static_string ("@EnumName@"), values);
|
||||
}
|
||||
return etype;
|
||||
}
|
||||
|
||||
/*** END value-tail ***/
|
||||
|
||||
/*** BEGIN file-tail ***/
|
||||
/*** END file-tail ***/
|
949
lib/ccnet-client.c
Normal file
949
lib/ccnet-client.c
Normal file
@ -0,0 +1,949 @@
|
||||
/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
|
||||
|
||||
#include "option.h"
|
||||
#include "include.h"
|
||||
|
||||
|
||||
#include <signal.h>
|
||||
#include <dirent.h>
|
||||
#include <stdio.h>
|
||||
|
||||
#ifdef WIN32
|
||||
#include <inttypes.h>
|
||||
#include <winsock2.h>
|
||||
#include <ws2tcpip.h>
|
||||
#else
|
||||
#include <sys/types.h>
|
||||
#include <sys/socket.h>
|
||||
#include <netinet/in.h>
|
||||
#include <arpa/inet.h>
|
||||
#include <sys/un.h>
|
||||
#endif
|
||||
|
||||
#include "message.h"
|
||||
|
||||
#include "processor.h"
|
||||
#include "packet-io.h"
|
||||
#include "peer.h"
|
||||
#include "ccnet-client.h"
|
||||
|
||||
#include "proc-factory.h"
|
||||
#include "job-mgr.h"
|
||||
|
||||
#include "ccnet-object.h"
|
||||
|
||||
/**
|
||||
* SECTION:ccnet-client
|
||||
* @short_description: The basic class to interact with the ccnet daemon
|
||||
* @title: CcnetClient
|
||||
* @include: ccnet.h
|
||||
*
|
||||
* CcnetClient is the basic class to interact with the ccnet daemon.
|
||||
* CcnetClient can work in two different mode, i.e., CCNET_CLIENT_SYNC
|
||||
* and CCNET_CLIENT_ASYNC (See #CcnetClientMode). The two modes have
|
||||
* different sets of APIs.
|
||||
*
|
||||
*/
|
||||
|
||||
G_DEFINE_TYPE (CcnetClient, ccnet_client, CCNET_TYPE_SESSION_BASE);
|
||||
|
||||
|
||||
|
||||
static void handle_packet (ccnet_packet *packet, void *vclient);
|
||||
static void ccnet_client_free (GObject *object);
|
||||
static void free_rpc_pool (CcnetClient *client);
|
||||
|
||||
|
||||
static void
|
||||
set_property (GObject *object, guint property_id,
|
||||
const GValue *v, GParamSpec *pspec)
|
||||
{
|
||||
/* CcnetClient *client = CCNET_CLIENT (object); */
|
||||
switch (property_id) {
|
||||
default:
|
||||
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
get_property (GObject *object, guint property_id,
|
||||
GValue *v, GParamSpec *pspec)
|
||||
{
|
||||
/* CcnetClient *client = CCNET_CLIENT (object); */
|
||||
switch (property_id) {
|
||||
default:
|
||||
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
ccnet_client_class_init (CcnetClientClass *klass)
|
||||
{
|
||||
GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
|
||||
|
||||
gobject_class->set_property = set_property;
|
||||
gobject_class->get_property = get_property;
|
||||
gobject_class->finalize = ccnet_client_free;
|
||||
|
||||
/* ccnet_object_init (); */
|
||||
}
|
||||
|
||||
static void
|
||||
ccnet_client_init (CcnetClient *client)
|
||||
{
|
||||
client->connfd = -1;
|
||||
client->req_id = CCNET_USER_ID_START;
|
||||
client->processors = g_hash_table_new_full (g_int_hash, g_int_equal,
|
||||
g_free, NULL);
|
||||
client->proc_factory = ccnet_proc_factory_new (client);
|
||||
/* job_mgr should be created by user, so that max_thread can
|
||||
* be chosen properly. */
|
||||
}
|
||||
|
||||
CcnetClient*
|
||||
ccnet_client_new (void)
|
||||
{
|
||||
return g_object_new (CCNET_TYPE_CLIENT, NULL);
|
||||
}
|
||||
|
||||
static void
|
||||
ccnet_client_free (GObject *object)
|
||||
{
|
||||
CcnetClient *client = CCNET_CLIENT (object);
|
||||
if (client->io)
|
||||
ccnet_client_disconnect_daemon (client);
|
||||
|
||||
if (client->config_dir)
|
||||
free (client->config_dir);
|
||||
g_free (client->config_file);
|
||||
if (client->proc_factory)
|
||||
g_object_unref (client->proc_factory);
|
||||
if (client->job_mgr)
|
||||
ccnet_job_manager_free (client->job_mgr);
|
||||
if (client->processors)
|
||||
g_hash_table_destroy (client->processors);
|
||||
|
||||
free_rpc_pool (client);
|
||||
|
||||
G_OBJECT_CLASS(ccnet_client_parent_class)->finalize (object);
|
||||
}
|
||||
|
||||
int
|
||||
ccnet_client_load_confdir (CcnetClient *client, const char *central_config_dir_r, const char *config_dir_r)
|
||||
{
|
||||
char *config_file = NULL, *config_dir = NULL, *central_config_dir = NULL;
|
||||
char *id = NULL, *name = NULL, *port_str = NULL, *un_path = NULL,
|
||||
*user_name = NULL, *service_url = NULL;
|
||||
unsigned char sha1[20];
|
||||
GKeyFile *key_file;
|
||||
CcnetSessionBase *base = CCNET_SESSION_BASE(client);
|
||||
|
||||
config_dir = ccnet_util_expand_path (config_dir_r);
|
||||
|
||||
if (ccnet_util_checkdir(config_dir) < 0) {
|
||||
g_warning ("Config dir %s does not exist or is not "
|
||||
"a directory.\n", config_dir);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (central_config_dir_r) {
|
||||
central_config_dir = ccnet_util_expand_path (central_config_dir_r);
|
||||
if (ccnet_util_checkdir(config_dir) < 0) {
|
||||
g_warning ("Server config dir %s does not exist or is not "
|
||||
"a directory.\n", central_config_dir);
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
config_file =
|
||||
g_strconcat(central_config_dir ? central_config_dir : config_dir, "/",
|
||||
SESSION_CONFIG_FILENAME, NULL);
|
||||
ccnet_debug ("using config file %s\n", config_file);
|
||||
key_file = g_key_file_new();
|
||||
if (!g_key_file_load_from_file (key_file, config_file,
|
||||
G_KEY_FILE_KEEP_COMMENTS, NULL))
|
||||
{
|
||||
g_warning ("Can't load config file %s.\n", config_file);
|
||||
goto onerror;
|
||||
}
|
||||
|
||||
id = ccnet_util_key_file_get_string (key_file, "General", "ID");
|
||||
user_name = ccnet_util_key_file_get_string (key_file, "General", "USER_NAME");
|
||||
name = ccnet_util_key_file_get_string (key_file, "General", "NAME");
|
||||
service_url = ccnet_util_key_file_get_string (key_file, "General", "SERVICE_URL");
|
||||
port_str = ccnet_util_key_file_get_string (key_file, "Client", "PORT");
|
||||
un_path = ccnet_util_key_file_get_string (key_file, "Client", "UNIX_SOCKET");
|
||||
|
||||
if ( (id == NULL) || (strlen (id) != SESSION_ID_LENGTH)
|
||||
|| (ccnet_util_hex_to_sha1 (id, sha1) < 0) )
|
||||
{
|
||||
ccnet_error ("Wrong ID\n");
|
||||
g_key_file_free (key_file);
|
||||
goto onerror;
|
||||
}
|
||||
|
||||
|
||||
memcpy (base->id, id, 40);
|
||||
base->id[40] = '\0';
|
||||
base->user_name = g_strdup(user_name);
|
||||
base->name = g_strdup(name);
|
||||
memcpy (base->id_sha1, sha1, 20);
|
||||
if (service_url)
|
||||
base->service_url = g_strdup(service_url);
|
||||
|
||||
client->config_file = g_strdup(config_file);
|
||||
client->config_dir = config_dir;
|
||||
client->central_config_dir = central_config_dir;
|
||||
|
||||
if (port_str)
|
||||
client->daemon_port = atoi (port_str);
|
||||
client->un_path = un_path;
|
||||
|
||||
g_free (id);
|
||||
g_free (name);
|
||||
g_free (user_name);
|
||||
g_free (port_str);
|
||||
g_free (config_file);
|
||||
g_free (service_url);
|
||||
g_key_file_free (key_file);
|
||||
return 0;
|
||||
|
||||
onerror:
|
||||
g_free (id);
|
||||
g_free (name);
|
||||
g_free (user_name);
|
||||
g_free (port_str);
|
||||
g_free (config_file);
|
||||
g_free (service_url);
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
||||
int
|
||||
ccnet_client_connect_daemon (CcnetClient *client, CcnetClientMode mode)
|
||||
{
|
||||
evutil_socket_t sockfd;
|
||||
/* CcnetProcessor *processor; */
|
||||
|
||||
#ifdef WIN32
|
||||
static int inited = 0;
|
||||
if (inited == 0) {
|
||||
inited = 1;
|
||||
WSADATA wsadata;
|
||||
WSAStartup(0x0101, &wsadata);
|
||||
}
|
||||
#endif
|
||||
|
||||
g_return_val_if_fail (client->connected == 0, -1);
|
||||
|
||||
client->mode = mode;
|
||||
|
||||
#ifdef WIN32
|
||||
sockfd = socket(AF_INET, SOCK_STREAM, 0);
|
||||
struct sockaddr_in servaddr;
|
||||
memset (&servaddr, 0, sizeof(servaddr));
|
||||
servaddr.sin_family = AF_INET;
|
||||
servaddr.sin_port = htons (client->daemon_port);
|
||||
ccnet_util_inet_pton (AF_INET, "127.0.0.1", &servaddr.sin_addr);
|
||||
if (connect (sockfd, (struct sockaddr *) &servaddr, (socklen_t)sizeof(servaddr)) < 0)
|
||||
return -1;
|
||||
#else
|
||||
char *un_path = NULL;
|
||||
|
||||
sockfd = socket(AF_UNIX, SOCK_STREAM, 0);
|
||||
struct sockaddr_un servaddr;
|
||||
servaddr.sun_family = AF_UNIX;
|
||||
|
||||
if (!client->un_path)
|
||||
un_path = g_build_filename (client->config_dir, CCNET_PIPE_NAME, NULL);
|
||||
else
|
||||
un_path = g_strdup(client->un_path);
|
||||
|
||||
g_strlcpy (servaddr.sun_path, un_path, sizeof(servaddr.sun_path));
|
||||
g_free (un_path);
|
||||
if (connect(sockfd, (struct sockaddr *)&servaddr, (socklen_t)sizeof(servaddr)) < 0) {
|
||||
return -1;
|
||||
}
|
||||
#endif
|
||||
|
||||
client->connfd = sockfd;
|
||||
client->io = ccnet_packet_io_new (client->connfd);
|
||||
|
||||
if (mode == CCNET_CLIENT_ASYNC)
|
||||
ccnet_packet_io_set_callback (client->io, handle_packet, client);
|
||||
|
||||
client->connected = 1;
|
||||
|
||||
g_debug ("connected to daemon\n");
|
||||
|
||||
return client->connfd;
|
||||
}
|
||||
|
||||
void
|
||||
ccnet_client_run_synchronizer (CcnetClient *client)
|
||||
{
|
||||
g_return_if_fail(client->mode == CCNET_CLIENT_ASYNC);
|
||||
}
|
||||
|
||||
|
||||
int
|
||||
ccnet_client_disconnect_daemon (CcnetClient *client)
|
||||
{
|
||||
ccnet_packet_io_free (client->io);
|
||||
client->io = NULL;
|
||||
client->connfd = -1;
|
||||
client->connected = 0;
|
||||
free_rpc_pool (client);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
uint32_t
|
||||
ccnet_client_get_request_id (CcnetClient *client)
|
||||
{
|
||||
return (++client->req_id);
|
||||
}
|
||||
|
||||
typedef struct RpcPoolItem {
|
||||
uint32_t req_id;
|
||||
char *peer_id;
|
||||
char *service;
|
||||
} RpcPoolItem;
|
||||
|
||||
static void
|
||||
free_rpc_pool_item (RpcPoolItem *item)
|
||||
{
|
||||
g_free (item->peer_id);
|
||||
g_free (item->service);
|
||||
g_free (item);
|
||||
}
|
||||
|
||||
static void
|
||||
free_rpc_pool (CcnetClient *client)
|
||||
{
|
||||
GList *ptr;
|
||||
for (ptr = client->rpc_pool; ptr; ptr = ptr->next) {
|
||||
RpcPoolItem *item = ptr->data;
|
||||
free_rpc_pool_item (item);
|
||||
}
|
||||
g_list_free (client->rpc_pool);
|
||||
client->rpc_pool = NULL;
|
||||
}
|
||||
|
||||
static RpcPoolItem *
|
||||
get_pool_item (CcnetClient *client, const char *peer_id,
|
||||
const char *service)
|
||||
{
|
||||
GList *ptr;
|
||||
for (ptr = client->rpc_pool; ptr; ptr = ptr->next) {
|
||||
RpcPoolItem *item = ptr->data;
|
||||
if (g_strcmp0(peer_id, item->peer_id) == 0 &&
|
||||
g_strcmp0(service, item->service) == 0)
|
||||
return item;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static uint32_t
|
||||
start_request (CcnetClient *client, const char *peer_id,
|
||||
const char *service)
|
||||
{
|
||||
uint32_t req_id = ccnet_client_get_request_id (client);
|
||||
char buf[512];
|
||||
|
||||
if (!peer_id)
|
||||
snprintf (buf, 512, "%s", service);
|
||||
else
|
||||
snprintf (buf, 512, "remote %s %s", peer_id, service);
|
||||
ccnet_client_send_request (client, req_id, buf);
|
||||
|
||||
if (ccnet_client_read_response (client) < 0) {
|
||||
g_warning ("[RPC] failed to read response.\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (memcmp (client->response.code, "200", 3) != 0) {
|
||||
g_warning ("[RPC] failed to start rpc server: %s %s.\n",
|
||||
client->response.code, client->response.code_msg);
|
||||
return 0;
|
||||
}
|
||||
|
||||
return req_id;
|
||||
}
|
||||
|
||||
uint32_t
|
||||
ccnet_client_get_rpc_request_id (CcnetClient *client, const char *peer_id,
|
||||
const char *service)
|
||||
{
|
||||
RpcPoolItem *item = get_pool_item (client, peer_id, service);
|
||||
if (item)
|
||||
return item->req_id;
|
||||
|
||||
uint32_t req_id = start_request (client, peer_id, service);
|
||||
if (req_id == 0)
|
||||
return 0;
|
||||
|
||||
item = g_new0 (RpcPoolItem, 1);
|
||||
item->req_id = req_id;
|
||||
item->peer_id = g_strdup (peer_id);
|
||||
item->service = g_strdup (service);
|
||||
client->rpc_pool = g_list_prepend (client->rpc_pool, item);
|
||||
return req_id;
|
||||
}
|
||||
|
||||
void
|
||||
ccnet_client_clean_rpc_request (CcnetClient *client, uint32_t req_id)
|
||||
{
|
||||
GList *ptr;
|
||||
RpcPoolItem *target = NULL;
|
||||
|
||||
for (ptr = client->rpc_pool; ptr; ptr = ptr->next) {
|
||||
RpcPoolItem *item = ptr->data;
|
||||
if (req_id == item->req_id)
|
||||
target = item;
|
||||
}
|
||||
if (!target) return;
|
||||
|
||||
client->rpc_pool = g_list_remove (client->rpc_pool, target);
|
||||
free_rpc_pool_item (target);
|
||||
}
|
||||
|
||||
|
||||
/* functions used in ASYNC mode */
|
||||
void
|
||||
ccnet_client_add_processor (CcnetClient *client, CcnetProcessor *processor)
|
||||
{
|
||||
int *key = g_new0 (int, 1);
|
||||
|
||||
*key = processor->id;
|
||||
g_hash_table_insert (client->processors, key, processor);
|
||||
}
|
||||
|
||||
void
|
||||
ccnet_client_remove_processor (CcnetClient *client, CcnetProcessor *processor)
|
||||
{
|
||||
g_hash_table_remove (client->processors, &processor->id);
|
||||
}
|
||||
|
||||
CcnetProcessor *
|
||||
ccnet_client_get_processor (CcnetClient *client, int id)
|
||||
{
|
||||
return g_hash_table_lookup (client->processors, &id);
|
||||
}
|
||||
|
||||
|
||||
int
|
||||
ccnet_client_read_input (CcnetClient *client)
|
||||
{
|
||||
if (!client->io)
|
||||
return -1;
|
||||
return ccnet_packet_io_read(client->io);
|
||||
}
|
||||
|
||||
static void create_processor (CcnetClient *client, int req_id,
|
||||
int argc, char **argv)
|
||||
{
|
||||
CcnetProcessor *processor;
|
||||
CcnetProcFactory *factory = client->proc_factory;
|
||||
char *peer_id;
|
||||
|
||||
if (strcmp(argv[0], "remote") == 0) {
|
||||
peer_id = argv[1];
|
||||
argc -= 2;
|
||||
argv += 2;
|
||||
} else
|
||||
peer_id = client->base.id;
|
||||
|
||||
processor = ccnet_proc_factory_create_slave_processor (
|
||||
factory, argv[0], peer_id, req_id);
|
||||
|
||||
if (processor) {
|
||||
ccnet_processor_start (processor, argc-1, argv+1);
|
||||
} else
|
||||
ccnet_client_send_response (client, req_id,
|
||||
SC_CREATE_PROC_ERR,
|
||||
SS_CREATE_PROC_ERR,
|
||||
NULL, 0);
|
||||
}
|
||||
|
||||
#if 0
|
||||
void
|
||||
ccnet_client_send_event (CcnetClient *client, GObject *event)
|
||||
{
|
||||
if (!event) return;
|
||||
CcnetProcessor *processor = NULL;
|
||||
processor = ccnet_proc_factory_create_master_processor
|
||||
(client->proc_factory, "send-event");
|
||||
ccnet_sendevent_proc_set_event (CCNET_SENDEVENT_PROC(processor),
|
||||
(CcnetEvent *)event);
|
||||
ccnet_processor_start (processor, 0, NULL);
|
||||
}
|
||||
#endif
|
||||
|
||||
static void
|
||||
handle_request (CcnetClient *client, int req_id, char *data, int len)
|
||||
{
|
||||
char *msg;
|
||||
gchar **commands;
|
||||
gchar **pcmd;
|
||||
int i;
|
||||
|
||||
/* TODO: remove string copy */
|
||||
g_return_if_fail (len >= 1);
|
||||
msg = g_malloc (len+1);
|
||||
memcpy (msg, data, len);
|
||||
msg[len] = '\0';
|
||||
|
||||
commands = g_strsplit_set (msg, " \t", 10);
|
||||
for (i=0, pcmd = commands; *pcmd; pcmd++)
|
||||
i++;
|
||||
g_free (msg);
|
||||
|
||||
create_processor (client, req_id, i, commands);
|
||||
g_strfreev (commands);
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
handle_response (CcnetClient *client, int req_id, char *data, int len)
|
||||
{
|
||||
CcnetProcessor *processor;
|
||||
char *code, *code_msg = 0, *content = 0;
|
||||
int clen;
|
||||
char *ptr, *end;
|
||||
|
||||
g_return_if_fail (len >= 4);
|
||||
|
||||
code = data;
|
||||
|
||||
ptr = data + 3;
|
||||
if (*ptr == '\n') {
|
||||
/* no code_msg */
|
||||
*ptr++ = '\0';
|
||||
content = ptr;
|
||||
clen = len - (ptr - data);
|
||||
goto parsed;
|
||||
}
|
||||
|
||||
if (*ptr != ' ')
|
||||
goto error;
|
||||
|
||||
*ptr++ = '\0';
|
||||
code_msg = ptr;
|
||||
|
||||
end = data + len;
|
||||
for (ptr = data; *ptr != '\n' && ptr != end; ptr++) ;
|
||||
|
||||
if (ptr == end) /* must end with '\n' */
|
||||
goto error;
|
||||
|
||||
/* if (*(ptr-1) == '\r') */
|
||||
/* *(ptr-1) = '\0'; */
|
||||
*ptr++ = '\0';
|
||||
content = ptr;
|
||||
clen = len - (ptr - data);
|
||||
|
||||
parsed:
|
||||
/* ccnet_message ("Receive response %s from %s\n", msg, peer->id); */
|
||||
/* ccnet_message ("code %s, code_msg %s, content %s\n", code, code_msg, */
|
||||
/* content); */
|
||||
processor = ccnet_client_get_processor (client, MASTER_ID (req_id));
|
||||
if (processor == NULL) {
|
||||
if (strcmp (code, SC_PROC_DEAD) != 0) {
|
||||
ccnet_debug ("Delayed response from daemon, id is %d, %s %s\n",
|
||||
MASTER_ID(req_id), code, code_msg);
|
||||
ccnet_client_send_update (client, req_id,
|
||||
SC_PROC_DEAD, SS_PROC_DEAD,
|
||||
NULL, 0);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
/* ccnet_debug ("[client] handle_response %s id is %d, %s %s\n", */
|
||||
/* GET_PNAME(processor), req_id, code, code_msg); */
|
||||
|
||||
ccnet_processor_handle_response (processor, code, code_msg, content, clen);
|
||||
return;
|
||||
|
||||
error:
|
||||
g_warning ("Bad response format from daemon\n");
|
||||
}
|
||||
|
||||
static void
|
||||
handle_update (CcnetClient *client, int req_id, char *data, int len)
|
||||
{
|
||||
CcnetProcessor *processor;
|
||||
char *code, *code_msg = 0, *content = 0;
|
||||
int clen;
|
||||
char *ptr, *end;
|
||||
|
||||
g_return_if_fail (len >= 4);
|
||||
|
||||
code = data;
|
||||
|
||||
ptr = data + 3;
|
||||
if (*ptr == '\n') {
|
||||
/* no code_msg */
|
||||
*ptr++ = '\0';
|
||||
content = ptr;
|
||||
clen = len - (ptr - data);
|
||||
goto parsed;
|
||||
}
|
||||
|
||||
if (*ptr != ' ')
|
||||
goto error;
|
||||
|
||||
*ptr++ = '\0';
|
||||
code_msg = ptr;
|
||||
|
||||
end = data + len;
|
||||
for (ptr = data; *ptr != '\n' && ptr != end; ptr++) ;
|
||||
|
||||
if (ptr == end) /* must end with '\n' */
|
||||
goto error;
|
||||
|
||||
/* if (*(ptr-1) == '\r') */
|
||||
/* *(ptr-1) = '\0'; */
|
||||
*ptr++ = '\0';
|
||||
content = ptr;
|
||||
clen = len - (ptr - data);
|
||||
|
||||
parsed:
|
||||
processor = ccnet_client_get_processor (client, SLAVE_ID(req_id));
|
||||
if (processor == NULL) {
|
||||
if (strcmp (code, SC_PROC_DEAD) != 0) {
|
||||
ccnet_debug ("Delayed update from daemon, id is %d, %s %s\n",
|
||||
req_id, code, code_msg);
|
||||
ccnet_client_send_response (client, req_id,
|
||||
SC_PROC_DEAD, SS_PROC_DEAD,
|
||||
NULL, 0);
|
||||
}
|
||||
return;
|
||||
}
|
||||
/* ccnet_debug ("[client] handle_update %s id is %d, %s %s\n", */
|
||||
/* GET_PNAME(processor), req_id, code, code_msg); */
|
||||
|
||||
ccnet_processor_handle_update (processor, code, code_msg, content, clen);
|
||||
return;
|
||||
|
||||
error:
|
||||
g_warning ("Bad update format\n");
|
||||
}
|
||||
|
||||
|
||||
static void handle_packet (ccnet_packet *packet, void *vclient)
|
||||
{
|
||||
CcnetClient *client = vclient;
|
||||
|
||||
if (packet == NULL) {
|
||||
/* disconnected from daemon */
|
||||
g_warning ("Disconnected from daemon\n");
|
||||
return;
|
||||
}
|
||||
|
||||
switch (packet->header.type) {
|
||||
case CCNET_MSG_REQUEST:
|
||||
handle_request (client, packet->header.id,
|
||||
packet->data, packet->header.length);
|
||||
break;
|
||||
case CCNET_MSG_RESPONSE:
|
||||
handle_response (client, packet->header.id,
|
||||
packet->data, packet->header.length);
|
||||
break;
|
||||
case CCNET_MSG_UPDATE:
|
||||
handle_update (client, packet->header.id,
|
||||
packet->data, packet->header.length);
|
||||
break;
|
||||
default:
|
||||
g_return_if_reached ();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
ccnet_client_send_request (CcnetClient *client, int req_id, const char *req)
|
||||
{
|
||||
ccnet_packet_prepare (client->io, CCNET_MSG_REQUEST, req_id);
|
||||
ccnet_packet_write_string (client->io, req);
|
||||
ccnet_packet_finish_send (client->io);
|
||||
|
||||
g_debug ("Send a request: id %d, cmd %s\n", req_id, req);
|
||||
}
|
||||
|
||||
/**
|
||||
* ccnet_client_send_update:
|
||||
* @client:
|
||||
* @req_id: request id
|
||||
* @code: A string of three numbers. Like "200"
|
||||
* @reason: long description for @code, can't contain '\n', can be %NULL
|
||||
* @content: A char array, can be %NULL.
|
||||
* @clen: length of %content, in bytes.
|
||||
*/
|
||||
void
|
||||
ccnet_client_send_update (CcnetClient *client, int req_id,
|
||||
const char *code, const char *reason,
|
||||
const char *content, int clen)
|
||||
{
|
||||
g_return_if_fail (req_id > 0);
|
||||
g_return_if_fail (clen < CCNET_PACKET_MAX_PAYLOAD_LEN);
|
||||
|
||||
ccnet_packet_prepare (client->io, CCNET_MSG_UPDATE, req_id);
|
||||
/* code line */
|
||||
ccnet_packet_add (client->io, code, 3);
|
||||
if (reason) {
|
||||
ccnet_packet_add (client->io, " ", 1);
|
||||
ccnet_packet_write_string (client->io, reason);
|
||||
}
|
||||
ccnet_packet_add (client->io, "\n", 1);
|
||||
if (content)
|
||||
ccnet_packet_add (client->io, content, clen);
|
||||
|
||||
ccnet_packet_finish_send (client->io);
|
||||
|
||||
/* g_debug ("[client] Send an update: id %d: %s %s len=%d\n", */
|
||||
/* req_id, code, reason, clen); */
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
ccnet_client_send_response (CcnetClient *client, int req_id,
|
||||
const char *code, const char *reason,
|
||||
const char *content, int clen)
|
||||
{
|
||||
g_return_if_fail (clen < CCNET_PACKET_MAX_PAYLOAD_LEN);
|
||||
|
||||
ccnet_packet_prepare (client->io, CCNET_MSG_RESPONSE, req_id);
|
||||
/* code line */
|
||||
ccnet_packet_add (client->io, code, 3);
|
||||
if (reason) {
|
||||
ccnet_packet_add (client->io, " ", 1);
|
||||
ccnet_packet_write_string (client->io, reason);
|
||||
}
|
||||
ccnet_packet_add (client->io, "\n", 1);
|
||||
if (content)
|
||||
ccnet_packet_add (client->io, content, clen);
|
||||
|
||||
ccnet_packet_finish_send (client->io);
|
||||
|
||||
/* g_debug ("[client] Send an response: id %d: %s %s len=%d\n", */
|
||||
/* req_id, code, reason, clen); */
|
||||
}
|
||||
|
||||
|
||||
/* functions used in SYNC mode */
|
||||
|
||||
/**
|
||||
* ccnet_client_read_response:
|
||||
* @client:
|
||||
*
|
||||
* Read response from the daemon. The response can be accessed by
|
||||
* client->response.
|
||||
*
|
||||
* Returns: -1 if io error, -2 if response packet format error
|
||||
*/
|
||||
int
|
||||
ccnet_client_read_response (CcnetClient *client)
|
||||
{
|
||||
ccnet_packet *packet;
|
||||
char *data;
|
||||
int len, clen;
|
||||
char *code, *code_msg = 0, *content = 0;
|
||||
char *ptr, *end;
|
||||
|
||||
restart:
|
||||
if ( (packet = ccnet_packet_io_read_packet (client->io)) == NULL)
|
||||
return -1;
|
||||
|
||||
if (packet->header.type != CCNET_MSG_RESPONSE)
|
||||
goto error;
|
||||
|
||||
data = packet->data;
|
||||
len = packet->header.length;
|
||||
|
||||
g_return_val_if_fail (len >= 4, -1);
|
||||
|
||||
code = data;
|
||||
|
||||
ptr = data + 3;
|
||||
if (*ptr == '\n') {
|
||||
/* no code_msg */
|
||||
*ptr++ = '\0';
|
||||
content = ptr;
|
||||
clen = len - (ptr - data);
|
||||
goto parsed;
|
||||
}
|
||||
|
||||
if (*ptr != ' ')
|
||||
goto error;
|
||||
|
||||
*ptr++ = '\0';
|
||||
code_msg = ptr;
|
||||
|
||||
end = data + len;
|
||||
for (ptr = data; *ptr != '\n' && ptr != end; ptr++) ;
|
||||
|
||||
if (ptr == end) /* must end with '\n' */
|
||||
goto error;
|
||||
|
||||
/* if (*(ptr-1) == '\r') */
|
||||
/* *(ptr-1) = '\0'; */
|
||||
*ptr++ = '\0';
|
||||
content = ptr;
|
||||
clen = len - (ptr - data);
|
||||
|
||||
/* In synchronized mode, we only have one processor at a
|
||||
time. The processor id is client->req_id, other
|
||||
processors are all treat as dead. */
|
||||
|
||||
/*
|
||||
if (packet->header.id != client->req_id) {
|
||||
if (strcmp (code, SC_PROC_DEAD) != 0) {
|
||||
g_debug ("Read response error: want %d, get %d %s %s\n",
|
||||
client->req_id, packet->header.id, code, code_msg);
|
||||
ccnet_client_send_update(client, packet->header.id,
|
||||
SC_PROC_DEAD, SS_PROC_DEAD, NULL, 0);
|
||||
}
|
||||
goto restart;
|
||||
}
|
||||
*/
|
||||
|
||||
/* handle processor keep alive response */
|
||||
if (strncmp(code, SC_PROC_KEEPALIVE, 3) == 0) {
|
||||
ccnet_client_send_update(client, packet->header.id,
|
||||
SC_PROC_ALIVE, SS_PROC_ALIVE, NULL, 0);
|
||||
goto restart;
|
||||
}
|
||||
|
||||
|
||||
parsed:
|
||||
client->response.code = code;
|
||||
client->response.code_msg = code_msg;
|
||||
client->response.content = content;
|
||||
client->response.clen = clen;
|
||||
return 0;
|
||||
|
||||
error:
|
||||
g_warning ("Bad response format from daemon\n");
|
||||
return -2;
|
||||
}
|
||||
|
||||
static int read_response_common(CcnetClient *client)
|
||||
{
|
||||
if (ccnet_client_read_response(client) < 0)
|
||||
return -1;
|
||||
if (client->response.code[0] == '4' ||
|
||||
client->response.code[0] == '5') {
|
||||
g_warning ("Error response from daemon: %s %s\n",
|
||||
client->response.code, client->response.code_msg);
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static gboolean
|
||||
check_response_error (CcnetClient *client, GError **error)
|
||||
{
|
||||
if (client->response.code[0] == '4' ||
|
||||
client->response.code[0] == '5') {
|
||||
g_set_error (error, CCNET_DOMAIN, atoi(client->response.code),
|
||||
"%s", client->response.code_msg);
|
||||
return TRUE;
|
||||
}
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
|
||||
const char *
|
||||
ccnet_client_send_cmd (CcnetClient *client, const char *cmd, GError **error)
|
||||
{
|
||||
int req_id = ccnet_client_get_request_id (client);
|
||||
ccnet_client_send_request (client, req_id, "receive-cmd");
|
||||
|
||||
if (ccnet_client_read_response(client) < 0) {
|
||||
g_set_error (error, CCNET_DOMAIN, EC_NETWORK_ERR, "%s", ES_NETWORK_ERR);
|
||||
goto on_error;
|
||||
}
|
||||
|
||||
ccnet_client_send_update (client, req_id,
|
||||
"200", NULL, cmd, strlen(cmd) + 1);
|
||||
if (ccnet_client_read_response(client) < 0) {
|
||||
g_set_error (error, CCNET_DOMAIN, EC_NETWORK_ERR, "%s", ES_NETWORK_ERR);
|
||||
goto on_error;
|
||||
}
|
||||
|
||||
if (check_response_error(client, error)) {
|
||||
goto on_error;
|
||||
}
|
||||
|
||||
ccnet_client_send_update (client, req_id,
|
||||
SC_PROC_DONE, SS_PROC_DONE,
|
||||
NULL, 0);
|
||||
return client->response.content;
|
||||
|
||||
on_error:
|
||||
ccnet_client_send_update (client, req_id,
|
||||
SC_PROC_DONE, SS_PROC_DONE,
|
||||
NULL, 0);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
#define SC_MSG "300"
|
||||
|
||||
int
|
||||
ccnet_client_send_message (CcnetClient *client,
|
||||
CcnetMessage *message)
|
||||
{
|
||||
GString *buf;
|
||||
int req_id = ccnet_client_get_request_id (client);
|
||||
|
||||
ccnet_client_send_request (client, req_id, "mq-server");
|
||||
|
||||
if (ccnet_client_read_response (client) < 0)
|
||||
return -1; /* TODO: handle response code */
|
||||
|
||||
buf = g_string_new (NULL);
|
||||
ccnet_message_to_string_buf (message, buf);
|
||||
ccnet_client_send_update (client, req_id,
|
||||
SC_MSG, NULL, buf->str, buf->len+1);
|
||||
if (ccnet_client_read_response (client) < 0)
|
||||
return -1;
|
||||
|
||||
g_string_free (buf, TRUE);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
ccnet_client_prepare_recv_message (CcnetClient *client,
|
||||
const char *app)
|
||||
{
|
||||
int req_id = ccnet_client_get_request_id (client);
|
||||
char buf[256];
|
||||
|
||||
snprintf (buf, sizeof(buf), "mq-server %s", app);
|
||||
ccnet_client_send_request (client, req_id, buf);
|
||||
|
||||
if (read_response_common (client) < 0)
|
||||
return -1;
|
||||
|
||||
if (memcmp(client->response.code, "200", 3) != 0)
|
||||
return -1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
CcnetMessage *
|
||||
ccnet_client_receive_message (CcnetClient *client)
|
||||
{
|
||||
CcnetMessage *message;
|
||||
|
||||
if (read_response_common (client) < 0)
|
||||
return NULL;
|
||||
|
||||
message = ccnet_message_from_string (client->response.content,
|
||||
client->response.clen);
|
||||
return message;
|
||||
}
|
325
lib/ccnet-rpc-wrapper.c
Normal file
325
lib/ccnet-rpc-wrapper.c
Normal file
@ -0,0 +1,325 @@
|
||||
/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
|
||||
|
||||
#include "include.h"
|
||||
|
||||
#include <ccnet.h>
|
||||
#include <ccnet-object.h>
|
||||
#include <searpc-client.h>
|
||||
#include <ccnet/ccnetrpc-transport.h>
|
||||
|
||||
|
||||
SearpcClient *
|
||||
ccnet_create_rpc_client (CcnetClient *cclient,
|
||||
const char *peer_id,
|
||||
const char *service_name)
|
||||
{
|
||||
SearpcClient *rpc_client;
|
||||
CcnetrpcTransportParam *priv;
|
||||
|
||||
priv = g_new0(CcnetrpcTransportParam, 1);
|
||||
priv->session = cclient;
|
||||
priv->peer_id = g_strdup(peer_id);
|
||||
priv->service = g_strdup(service_name);
|
||||
|
||||
rpc_client = searpc_client_new ();
|
||||
rpc_client->send = ccnetrpc_transport_send;
|
||||
rpc_client->arg = priv;
|
||||
|
||||
return rpc_client;
|
||||
}
|
||||
|
||||
SearpcClient *
|
||||
ccnet_create_pooled_rpc_client (struct CcnetClientPool *cpool,
|
||||
const char *peer_id,
|
||||
const char *service)
|
||||
{
|
||||
SearpcClient *rpc_client;
|
||||
CcnetrpcTransportParam *priv;
|
||||
|
||||
priv = g_new0(CcnetrpcTransportParam, 1);
|
||||
priv->pool = cpool;
|
||||
priv->peer_id = g_strdup(peer_id);
|
||||
priv->service = g_strdup(service);
|
||||
|
||||
rpc_client = searpc_client_new ();
|
||||
rpc_client->send = ccnetrpc_transport_send;
|
||||
rpc_client->arg = priv;
|
||||
|
||||
return rpc_client;
|
||||
}
|
||||
|
||||
SearpcClient *
|
||||
ccnet_create_async_rpc_client (CcnetClient *cclient, const char *peer_id,
|
||||
const char *service_name)
|
||||
{
|
||||
SearpcClient *rpc_client;
|
||||
CcnetrpcAsyncTransportParam *async_priv;
|
||||
|
||||
async_priv = g_new0 (CcnetrpcAsyncTransportParam, 1);
|
||||
async_priv->session = cclient;
|
||||
async_priv->peer_id = g_strdup(peer_id);
|
||||
async_priv->service = g_strdup (service_name);
|
||||
|
||||
rpc_client = searpc_client_new ();
|
||||
|
||||
rpc_client->async_send = ccnetrpc_async_transport_send;
|
||||
rpc_client->async_arg = async_priv;
|
||||
|
||||
return rpc_client;
|
||||
}
|
||||
|
||||
void
|
||||
ccnet_rpc_client_free (SearpcClient *client)
|
||||
{
|
||||
CcnetrpcTransportParam *priv;
|
||||
|
||||
if (!client) return;
|
||||
|
||||
priv = client->arg;
|
||||
|
||||
g_free (priv->peer_id);
|
||||
g_free (priv->service);
|
||||
g_free (priv);
|
||||
|
||||
searpc_client_free (client);
|
||||
}
|
||||
|
||||
void
|
||||
ccnet_async_rpc_client_free (SearpcClient *client)
|
||||
{
|
||||
CcnetrpcAsyncTransportParam *priv = client->arg;
|
||||
|
||||
g_free (priv->peer_id);
|
||||
g_free (priv->service);
|
||||
g_free (priv);
|
||||
|
||||
searpc_client_free (client);
|
||||
}
|
||||
|
||||
CcnetPeer *
|
||||
ccnet_get_peer (SearpcClient *client, const char *peer_id)
|
||||
{
|
||||
if (!peer_id)
|
||||
return NULL;
|
||||
|
||||
return (CcnetPeer *) searpc_client_call__object(
|
||||
client, "get_peer",CCNET_TYPE_PEER, NULL,
|
||||
1, "string", peer_id);
|
||||
}
|
||||
|
||||
CcnetPeer *
|
||||
ccnet_get_peer_by_idname (SearpcClient *client, const char *idname)
|
||||
{
|
||||
if (!idname)
|
||||
return NULL;
|
||||
return (CcnetPeer *) searpc_client_call__object(
|
||||
client, "get_peer_by_idname", CCNET_TYPE_PEER, NULL,
|
||||
1, "string", idname);
|
||||
}
|
||||
|
||||
int
|
||||
ccnet_get_peer_net_state (SearpcClient *client, const char *peer_id)
|
||||
{
|
||||
CcnetPeer *peer;
|
||||
int ret;
|
||||
peer = ccnet_get_peer (client, peer_id);
|
||||
if (!peer)
|
||||
return PEER_DOWN;
|
||||
ret = peer->net_state;
|
||||
g_object_unref (peer);
|
||||
return ret;
|
||||
}
|
||||
|
||||
int
|
||||
ccnet_get_peer_bind_status (SearpcClient *client, const char *peer_id)
|
||||
{
|
||||
CcnetPeer *peer;
|
||||
int ret;
|
||||
peer = ccnet_get_peer (client, peer_id);
|
||||
if (!peer)
|
||||
return BIND_UNKNOWN;
|
||||
ret = peer->bind_status;
|
||||
g_object_unref (peer);
|
||||
return ret;
|
||||
}
|
||||
|
||||
CcnetPeer *
|
||||
ccnet_get_default_relay (SearpcClient *client)
|
||||
{
|
||||
CcnetSessionBase *base = (CcnetSessionBase *)
|
||||
searpc_client_call__object(
|
||||
client, "get_session_info", CCNET_TYPE_SESSION_BASE, NULL, 0);
|
||||
|
||||
if (!base)
|
||||
return NULL;
|
||||
|
||||
CcnetPeer *relay = ccnet_get_peer (client, base->relay_id);
|
||||
g_object_unref (base);
|
||||
return relay;
|
||||
}
|
||||
|
||||
GList *
|
||||
ccnet_get_peers_by_role (SearpcClient *client, const char *role)
|
||||
{
|
||||
if (!role)
|
||||
return NULL;
|
||||
|
||||
return searpc_client_call__objlist (
|
||||
client, "get_peers_by_role", CCNET_TYPE_PEER, NULL,
|
||||
1, "string", role);
|
||||
}
|
||||
|
||||
char *
|
||||
ccnet_get_binding_email (SearpcClient *client, const char *peer_id)
|
||||
{
|
||||
if (!peer_id)
|
||||
return NULL;
|
||||
|
||||
return searpc_client_call__string (
|
||||
client, "get_binding_email", NULL,
|
||||
1, "string", peer_id);
|
||||
}
|
||||
|
||||
GList *
|
||||
ccnet_get_groups_by_user (SearpcClient *client, const char *user)
|
||||
{
|
||||
if (!user)
|
||||
return NULL;
|
||||
|
||||
return searpc_client_call__objlist (
|
||||
client, "get_groups", CCNET_TYPE_GROUP, NULL,
|
||||
1, "string", user);
|
||||
}
|
||||
|
||||
GList *
|
||||
ccnet_get_group_members (SearpcClient *client, int group_id)
|
||||
{
|
||||
return searpc_client_call__objlist (
|
||||
client, "get_group_members", CCNET_TYPE_GROUP_USER, NULL,
|
||||
1, "int", group_id);
|
||||
}
|
||||
|
||||
int
|
||||
ccnet_org_user_exists (SearpcClient *client, int org_id, const char *user)
|
||||
{
|
||||
return searpc_client_call__int (client, "org_user_exists", NULL,
|
||||
2, "int", org_id, "string", user);
|
||||
}
|
||||
|
||||
#if 0
|
||||
int
|
||||
ccnet_get_peer_async (SearpcClient *client, const char *peer_id,
|
||||
AsyncCallback callback, void *user_data)
|
||||
{
|
||||
return get_peer_async (client, peer_id, callback, user_data);
|
||||
}
|
||||
#endif
|
||||
|
||||
int
|
||||
ccnet_get_binding_email_async (SearpcClient *client, const char *peer_id,
|
||||
AsyncCallback callback, void *user_data)
|
||||
{
|
||||
return searpc_client_async_call__string (
|
||||
client, "get_binding_email", callback, user_data,
|
||||
1, "string", peer_id);
|
||||
}
|
||||
|
||||
char *
|
||||
ccnet_sign_message (SearpcClient *client, const char *message)
|
||||
{
|
||||
if (!message)
|
||||
return NULL;
|
||||
|
||||
return searpc_client_call__string (
|
||||
client, "sign_message", NULL,
|
||||
1, "string", message);
|
||||
}
|
||||
|
||||
int
|
||||
ccnet_verify_message (SearpcClient *client,
|
||||
const char *message,
|
||||
const char *sig_base64,
|
||||
const char *peer_id)
|
||||
{
|
||||
if (!message || !sig_base64 || !peer_id)
|
||||
return -1;
|
||||
|
||||
return searpc_client_call__int (
|
||||
client, "verify_message", NULL,
|
||||
3, "string", message, "string", sig_base64, "string", peer_id);
|
||||
}
|
||||
|
||||
char *
|
||||
ccnet_pubkey_encrypt (SearpcClient *client,
|
||||
const char *msg_base64,
|
||||
const char *peer_id)
|
||||
{
|
||||
if (!msg_base64 || !peer_id)
|
||||
return NULL;
|
||||
|
||||
return searpc_client_call__string (client, "pubkey_encrypt", NULL, 2,
|
||||
"string", msg_base64, "string", peer_id);
|
||||
}
|
||||
|
||||
char *
|
||||
ccnet_privkey_decrypt (SearpcClient *client, const char *msg_base64)
|
||||
{
|
||||
if (!msg_base64)
|
||||
return NULL;
|
||||
|
||||
return searpc_client_call__string (client, "privkey_decrypt", NULL, 1,
|
||||
"string", msg_base64);
|
||||
}
|
||||
|
||||
char *
|
||||
ccnet_get_config (SearpcClient *client, const char *key)
|
||||
{
|
||||
if (!key)
|
||||
return NULL;
|
||||
|
||||
return searpc_client_call__string (
|
||||
client, "get_config", NULL,
|
||||
1, "string", key);
|
||||
}
|
||||
|
||||
int
|
||||
ccnet_set_config (SearpcClient *client, const char *key, const char *value)
|
||||
{
|
||||
if (!key || !value)
|
||||
return -1;
|
||||
|
||||
return searpc_client_call__int (
|
||||
client, "set_config", NULL,
|
||||
2, "string", key, "string", value);
|
||||
}
|
||||
|
||||
void
|
||||
ccnet_login_to_relay (SearpcClient *client, const char *relay_id,
|
||||
const char *username, const char *passwd)
|
||||
{
|
||||
searpc_client_call__int (client, "login_relay", NULL,
|
||||
3, "string", relay_id,
|
||||
"string", username, "string", passwd);
|
||||
}
|
||||
|
||||
gboolean
|
||||
ccnet_peer_is_ready (SearpcClient *client, const char *peer_id)
|
||||
{
|
||||
CcnetPeer *peer;
|
||||
gboolean ret;
|
||||
peer = ccnet_get_peer (client, peer_id);
|
||||
if (!peer)
|
||||
return FALSE;
|
||||
ret = peer->is_ready;
|
||||
g_object_unref (peer);
|
||||
return ret;
|
||||
}
|
||||
|
||||
int
|
||||
ccnet_update_peer_address (SearpcClient *client, const char *peer_id,
|
||||
const char *addr, int port)
|
||||
{
|
||||
return searpc_client_call__int (
|
||||
client, "update_peer_address", NULL,
|
||||
3, "string", peer_id, "string", addr, "int", port);
|
||||
}
|
145
lib/ccnet-session-base.c
Normal file
145
lib/ccnet-session-base.c
Normal file
@ -0,0 +1,145 @@
|
||||
|
||||
#include "option.h"
|
||||
#include "include.h"
|
||||
|
||||
#include "ccnet-session-base.h"
|
||||
|
||||
G_DEFINE_TYPE (CcnetSessionBase, ccnet_session_base, G_TYPE_OBJECT);
|
||||
|
||||
enum {
|
||||
P_ID = 1,
|
||||
P_USER_NAME,
|
||||
P_NAME,
|
||||
P_PUBLIC_PORT,
|
||||
P_NET_STATUS,
|
||||
P_DEFAULT_RELAY,
|
||||
};
|
||||
|
||||
|
||||
static void
|
||||
set_property (GObject *object, guint property_id,
|
||||
const GValue *v, GParamSpec *pspec)
|
||||
{
|
||||
CcnetSessionBase *session = CCNET_SESSION_BASE (object);
|
||||
const char *relay;
|
||||
|
||||
switch (property_id) {
|
||||
case P_ID:
|
||||
strcpy(session->id, g_value_get_string(v));
|
||||
break;
|
||||
case P_NAME:
|
||||
g_free (session->name);
|
||||
session->name = g_strdup (g_value_get_string(v));
|
||||
break;
|
||||
case P_USER_NAME:
|
||||
g_free (session->user_name);
|
||||
session->user_name = g_strdup (g_value_get_string(v));
|
||||
break;
|
||||
case P_PUBLIC_PORT:
|
||||
session->public_port = g_value_get_int (v);
|
||||
break;
|
||||
case P_NET_STATUS:
|
||||
session->net_status = g_value_get_int (v);
|
||||
break;
|
||||
case P_DEFAULT_RELAY:
|
||||
relay = g_value_get_string(v);
|
||||
session->relay_id = g_strdup(relay);
|
||||
break;
|
||||
default:
|
||||
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
|
||||
return;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
static void
|
||||
get_property (GObject *object, guint property_id,
|
||||
GValue *v, GParamSpec *pspec)
|
||||
{
|
||||
CcnetSessionBase *session = CCNET_SESSION_BASE (object);
|
||||
|
||||
switch (property_id) {
|
||||
/* commont properties to session and daemon */
|
||||
case P_ID:
|
||||
g_value_set_string (v, session->id);
|
||||
break;
|
||||
case P_USER_NAME:
|
||||
g_value_set_string (v, session->user_name);
|
||||
break;
|
||||
case P_NAME:
|
||||
g_value_set_string (v, session->name);
|
||||
break;
|
||||
case P_PUBLIC_PORT:
|
||||
g_value_set_int (v, session->public_port);
|
||||
break;
|
||||
case P_NET_STATUS:
|
||||
g_value_set_int (v, session->net_status);
|
||||
break;
|
||||
case P_DEFAULT_RELAY:
|
||||
g_value_set_string (v, session->relay_id);
|
||||
break;
|
||||
default:
|
||||
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
finalize(GObject *gobject)
|
||||
{
|
||||
CcnetSessionBase *s = CCNET_SESSION_BASE(gobject);
|
||||
|
||||
g_free (s->user_name);
|
||||
g_free (s->name);
|
||||
g_free (s->relay_id);
|
||||
|
||||
G_OBJECT_CLASS(ccnet_session_base_parent_class)->finalize (gobject);
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
ccnet_session_base_class_init (CcnetSessionBaseClass *klass)
|
||||
{
|
||||
GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
|
||||
|
||||
gobject_class->set_property = set_property;
|
||||
gobject_class->get_property = get_property;
|
||||
gobject_class->finalize = finalize;
|
||||
|
||||
g_object_class_install_property (gobject_class, P_ID,
|
||||
g_param_spec_string ("id", NULL, "ID",
|
||||
NULL, G_PARAM_READWRITE));
|
||||
|
||||
g_object_class_install_property (gobject_class, P_USER_NAME,
|
||||
g_param_spec_string ("user-name", NULL, "User Name",
|
||||
NULL, G_PARAM_READWRITE));
|
||||
|
||||
g_object_class_install_property (gobject_class, P_NAME,
|
||||
g_param_spec_string ("name", NULL, "Name",
|
||||
NULL, G_PARAM_READWRITE));
|
||||
|
||||
g_object_class_install_property (gobject_class, P_PUBLIC_PORT,
|
||||
g_param_spec_int ("public-port", NULL, "Public Port",
|
||||
0, 65525, 0, G_PARAM_READWRITE));
|
||||
|
||||
g_object_class_install_property (gobject_class, P_NET_STATUS,
|
||||
g_param_spec_int ("net-status", NULL, "Network Status",
|
||||
0, NET_STATUS_FULL, NET_STATUS_DOWN,
|
||||
G_PARAM_READWRITE));
|
||||
|
||||
g_object_class_install_property (gobject_class, P_DEFAULT_RELAY,
|
||||
g_param_spec_string ("default-relay", NULL, "Default Relay",
|
||||
NULL, G_PARAM_READWRITE));
|
||||
}
|
||||
|
||||
static void
|
||||
ccnet_session_base_init (CcnetSessionBase *sbase)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
CcnetSessionBase *
|
||||
ccnet_session_base_new (void)
|
||||
{
|
||||
return g_object_new (CCNET_TYPE_SESSION_BASE, NULL);
|
||||
}
|
79
lib/ccnetobj.vala
Normal file
79
lib/ccnetobj.vala
Normal file
@ -0,0 +1,79 @@
|
||||
|
||||
|
||||
namespace Ccnet {
|
||||
|
||||
|
||||
public class Proc : Object {
|
||||
|
||||
public string peer_name { get; set; }
|
||||
|
||||
public string _name;
|
||||
public string name {
|
||||
get { return _name; }
|
||||
set { _name = value; }
|
||||
}
|
||||
|
||||
public int _ctime;
|
||||
public int ctime {
|
||||
get { return _ctime; }
|
||||
set { _ctime = value; }
|
||||
}
|
||||
|
||||
public int _dtime; //dead time
|
||||
public int dtime {
|
||||
get { return _dtime; }
|
||||
set { _dtime = value; }
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public class EmailUser : Object {
|
||||
|
||||
public int id { get; set; }
|
||||
public string email { get; set; }
|
||||
public bool is_staff { get; set; }
|
||||
public bool is_active { get; set; }
|
||||
public int64 ctime { get; set; }
|
||||
public string source { get; set; }
|
||||
public string role { get; set; }
|
||||
public string password { get; set; }
|
||||
}
|
||||
|
||||
public class Group : Object {
|
||||
|
||||
public int id { get; set; }
|
||||
public string group_name { get; set; }
|
||||
public string creator_name { get; set; }
|
||||
public int64 timestamp { get; set; }
|
||||
public string source { get; set; }
|
||||
}
|
||||
|
||||
public class GroupUser : Object {
|
||||
|
||||
public int group_id { get; set; }
|
||||
public string user_name { get; set; }
|
||||
public int is_staff { get; set; }
|
||||
}
|
||||
|
||||
public class Organization : Object {
|
||||
|
||||
public int org_id { get; set; }
|
||||
public string email { get; set; }
|
||||
public int is_staff { get; set; }
|
||||
public string org_name { get; set; }
|
||||
public string url_prefix { get; set; }
|
||||
public string creator { get; set; }
|
||||
public int64 ctime { get; set; }
|
||||
|
||||
}
|
||||
|
||||
public class PeerStat : Object {
|
||||
public string id { get; set; }
|
||||
public string name { get; set; }
|
||||
public string ip { get; set; }
|
||||
public bool encrypt { get; set; }
|
||||
public int64 last_up { get; set; }
|
||||
public int proc_num { get; set; }
|
||||
}
|
||||
|
||||
} // namespace
|
184
lib/ccnetrpc-transport.c
Normal file
184
lib/ccnetrpc-transport.c
Normal file
@ -0,0 +1,184 @@
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
|
||||
#include <ccnet/ccnetrpc-transport.h>
|
||||
#include "rpc-common.h"
|
||||
#include <ccnet/async-rpc-proc.h>
|
||||
|
||||
static char *
|
||||
invoke_service (CcnetClient *session,
|
||||
const char *peer_id,
|
||||
const char *service,
|
||||
const char *fcall_str,
|
||||
size_t fcall_len,
|
||||
size_t *ret_len)
|
||||
{
|
||||
struct CcnetResponse *rsp;
|
||||
uint32_t req_id;
|
||||
GString *buf;
|
||||
|
||||
req_id = ccnet_client_get_rpc_request_id (session, peer_id, service);
|
||||
if (req_id == 0) {
|
||||
*ret_len = 0;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
ccnet_client_send_update (session, req_id,
|
||||
SC_CLIENT_CALL, SS_CLIENT_CALL,
|
||||
fcall_str, fcall_len);
|
||||
|
||||
if (ccnet_client_read_response (session) < 0) {
|
||||
*ret_len = 0;
|
||||
ccnet_client_clean_rpc_request (session, req_id);
|
||||
return NULL;
|
||||
}
|
||||
rsp = &session->response;
|
||||
|
||||
if (memcmp (rsp->code, SC_SERVER_RET, 3) == 0) {
|
||||
*ret_len = (size_t) rsp->clen;
|
||||
return g_strndup (rsp->content, rsp->clen);
|
||||
} else if (memcmp (rsp->code, SC_SERVER_MORE, 3) != 0) {
|
||||
g_warning ("[Sea RPC] Bad response: %s %s.\n", rsp->code, rsp->code_msg);
|
||||
*ret_len = 0;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
buf = g_string_new_len (rsp->content, rsp->clen);
|
||||
while (1) {
|
||||
ccnet_client_send_update (session, req_id,
|
||||
SC_CLIENT_MORE, SS_CLIENT_MORE,
|
||||
fcall_str, fcall_len);
|
||||
|
||||
if (ccnet_client_read_response (session) < 0) {
|
||||
*ret_len = 0;
|
||||
ccnet_client_clean_rpc_request (session, req_id);
|
||||
g_string_free (buf, TRUE);
|
||||
return NULL;
|
||||
}
|
||||
rsp = &session->response;
|
||||
|
||||
if (memcmp (rsp->code, SC_SERVER_RET, 3) == 0) {
|
||||
g_string_append_len (buf, rsp->content, rsp->clen);
|
||||
*ret_len = buf->len;
|
||||
return g_string_free (buf, FALSE);
|
||||
} else if (memcmp (rsp->code, SC_SERVER_MORE, 3) == 0) {
|
||||
g_string_append_len (buf, rsp->content, rsp->clen);
|
||||
} else {
|
||||
g_warning ("[Sea RPC] Bad response: %s %s.\n",
|
||||
rsp->code, rsp->code_msg);
|
||||
*ret_len = 0;
|
||||
g_string_free (buf, TRUE);
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
/* Never reach here. */
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static CcnetClient *
|
||||
create_new_client (const char *central_config_dir, const char *conf_dir)
|
||||
{
|
||||
CcnetClient *client;
|
||||
|
||||
client = ccnet_client_new ();
|
||||
if (ccnet_client_load_confdir (client, central_config_dir, conf_dir) < 0) {
|
||||
g_warning ("[Sea RPC] Failed to load conf dir.\n");
|
||||
g_object_unref (client);
|
||||
return NULL;
|
||||
}
|
||||
if (ccnet_client_connect_daemon (client, CCNET_CLIENT_SYNC) < 0) {
|
||||
g_warning ("[Sea RPC] Failed to connect ccnet.\n");
|
||||
g_object_unref (client);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return client;
|
||||
}
|
||||
|
||||
char *
|
||||
ccnetrpc_transport_send (void *arg, const gchar *fcall_str,
|
||||
size_t fcall_len, size_t *ret_len)
|
||||
{
|
||||
CcnetrpcTransportParam *priv;
|
||||
CcnetClient *session, *new_session;
|
||||
|
||||
g_warn_if_fail (arg != NULL && fcall_str != NULL);
|
||||
|
||||
priv = (CcnetrpcTransportParam *)arg;
|
||||
|
||||
if (priv->session != NULL) {
|
||||
/* Use single ccnet client as transport. */
|
||||
return invoke_service (priv->session, priv->peer_id, priv->service,
|
||||
fcall_str, fcall_len, ret_len);
|
||||
} else {
|
||||
/* Use client pool as transport. */
|
||||
|
||||
session = ccnet_client_pool_get_client (priv->pool);
|
||||
if (!session) {
|
||||
g_warning ("[Sea RPC] Failed to get client from pool.\n");
|
||||
*ret_len = 0;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
char *ret = invoke_service (session, priv->peer_id, priv->service,
|
||||
fcall_str, fcall_len, ret_len);
|
||||
if (ret != NULL) {
|
||||
ccnet_client_pool_return_client (priv->pool, session);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* If we failed to send data through the ccnet client returned by
|
||||
* client pool, ccnet may have been restarted.
|
||||
* In this case, we create a new ccnet client and put it into
|
||||
* the client pool after use.
|
||||
*/
|
||||
|
||||
g_message ("[Sea RPC] Ccnet disconnected. Connect again.\n");
|
||||
|
||||
new_session = create_new_client (session->central_config_dir, session->config_dir);
|
||||
if (!new_session) {
|
||||
*ret_len = 0;
|
||||
return NULL;
|
||||
}
|
||||
g_object_unref (session);
|
||||
|
||||
ret = invoke_service (new_session, priv->peer_id, priv->service,
|
||||
fcall_str, fcall_len, ret_len);
|
||||
if (ret != NULL)
|
||||
ccnet_client_pool_return_client (priv->pool, new_session);
|
||||
else
|
||||
g_object_unref (new_session);
|
||||
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
int
|
||||
ccnetrpc_async_transport_send (void *arg, gchar *fcall_str,
|
||||
size_t fcall_len, void *rpc_priv)
|
||||
{
|
||||
CcnetrpcAsyncTransportParam *priv;
|
||||
CcnetClient *session;
|
||||
CcnetProcessor *proc;
|
||||
|
||||
g_warn_if_fail (arg != NULL && fcall_str != NULL);
|
||||
|
||||
priv = (CcnetrpcAsyncTransportParam *)arg;
|
||||
session = priv->session;
|
||||
|
||||
if (!priv->peer_id)
|
||||
proc = ccnet_proc_factory_create_master_processor (
|
||||
session->proc_factory, "async-rpc");
|
||||
else
|
||||
proc = ccnet_proc_factory_create_remote_master_processor (
|
||||
session->proc_factory, "async-rpc", priv->peer_id);
|
||||
|
||||
ccnet_async_rpc_proc_set_rpc ((CcnetAsyncRpcProc *)proc, priv->service,
|
||||
fcall_str, fcall_len, rpc_priv);
|
||||
ccnet_processor_start (proc, 0, NULL);
|
||||
return 0;
|
||||
}
|
105
lib/cevent.c
Normal file
105
lib/cevent.c
Normal file
@ -0,0 +1,105 @@
|
||||
/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
|
||||
|
||||
#include "include.h"
|
||||
#include "cevent.h"
|
||||
|
||||
#define CEVENT_SIZE (sizeof(CEvent))
|
||||
|
||||
typedef struct Handler {
|
||||
cevent_handler handler;
|
||||
void *handler_data;
|
||||
} Handler;
|
||||
|
||||
CEventManager* cevent_manager_new ()
|
||||
{
|
||||
CEventManager *manager;
|
||||
|
||||
manager = g_new0 (CEventManager, 1);
|
||||
pthread_mutex_init (&manager->mutex, NULL);
|
||||
manager->handler_table = g_hash_table_new_full (g_direct_hash,
|
||||
g_direct_equal, NULL, g_free);
|
||||
|
||||
return manager;
|
||||
}
|
||||
|
||||
void pipe_callback (evutil_socket_t fd, short event, void *vmgr)
|
||||
{
|
||||
CEventManager *manager = (CEventManager *) vmgr;
|
||||
CEvent *cevent;
|
||||
char buf[CEVENT_SIZE];
|
||||
|
||||
if (ccnet_util_pipereadn(fd, buf, CEVENT_SIZE) != CEVENT_SIZE) {
|
||||
g_warning ("read pipe error\n");
|
||||
return;
|
||||
}
|
||||
|
||||
cevent = (CEvent *)buf;
|
||||
Handler *h = g_hash_table_lookup (manager->handler_table,
|
||||
(gconstpointer)(long)cevent->id);
|
||||
if (h == NULL) {
|
||||
g_warning ("no handler for event type %d\n", cevent->id);
|
||||
return;
|
||||
}
|
||||
|
||||
h->handler(cevent, h->handler_data);
|
||||
}
|
||||
|
||||
int cevent_manager_start (CEventManager *manager)
|
||||
{
|
||||
if (ccnet_util_pipe(manager->pipefd) < 0) {
|
||||
g_warning ("pipe error: %s\n", strerror(errno));
|
||||
return -1;
|
||||
}
|
||||
|
||||
event_set (&manager->event, manager->pipefd[0],
|
||||
EV_READ | EV_PERSIST, pipe_callback, manager);
|
||||
event_add (&manager->event, NULL);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
uint32_t cevent_manager_register (CEventManager *manager,
|
||||
cevent_handler handler, void *handler_data)
|
||||
{
|
||||
uint32_t id;
|
||||
Handler *h;
|
||||
|
||||
h = g_new0(Handler, 1);
|
||||
h->handler = handler;
|
||||
h->handler_data = handler_data;
|
||||
|
||||
/* Since we're using 32-bit int for id, it may wrap around to 0.
|
||||
* If some caller persistently use one id, it's handler may be
|
||||
* overwritten by others.
|
||||
*/
|
||||
do {
|
||||
id = manager->next_id++;
|
||||
} while (g_hash_table_lookup (manager->handler_table, (gpointer)(long)id));
|
||||
|
||||
g_hash_table_insert (manager->handler_table, (gpointer)(long)id, h);
|
||||
|
||||
return id;
|
||||
}
|
||||
|
||||
void cevent_manager_unregister (CEventManager *manager, uint32_t id)
|
||||
{
|
||||
g_hash_table_remove (manager->handler_table, (gpointer)(long)id);
|
||||
}
|
||||
|
||||
void
|
||||
cevent_manager_add_event (CEventManager *manager, uint32_t id,
|
||||
void *data)
|
||||
{
|
||||
pthread_mutex_lock (&manager->mutex);
|
||||
|
||||
struct CEvent cevent;
|
||||
char *buf = (char *) &cevent;
|
||||
|
||||
cevent.id = id;
|
||||
cevent.data = data;
|
||||
if (ccnet_util_pipewriten(manager->pipefd[1], buf, CEVENT_SIZE) != CEVENT_SIZE) {
|
||||
g_warning ("add event error\n");
|
||||
}
|
||||
|
||||
pthread_mutex_unlock (&manager->mutex);
|
||||
}
|
63
lib/client-pool.c
Normal file
63
lib/client-pool.c
Normal file
@ -0,0 +1,63 @@
|
||||
#include "include.h"
|
||||
|
||||
#include <ccnet.h>
|
||||
#include <ccnet/ccnetrpc-transport.h>
|
||||
|
||||
#include <glib.h>
|
||||
#include <pthread.h>
|
||||
|
||||
struct CcnetClientPool {
|
||||
GQueue *clients;
|
||||
pthread_mutex_t lock;
|
||||
const char *central_config_dir;
|
||||
const char *conf_dir;
|
||||
};
|
||||
|
||||
struct CcnetClientPool *
|
||||
ccnet_client_pool_new (const char *central_config_dir, const char *conf_dir)
|
||||
{
|
||||
CcnetClientPool *pool = g_new0 (CcnetClientPool, 1);
|
||||
|
||||
pool->clients = g_queue_new ();
|
||||
pthread_mutex_init (&pool->lock, NULL);
|
||||
pool->conf_dir = g_strdup(conf_dir);
|
||||
pool->central_config_dir = g_strdup(central_config_dir);
|
||||
|
||||
return pool;
|
||||
}
|
||||
|
||||
CcnetClient *
|
||||
ccnet_client_pool_get_client (struct CcnetClientPool *cpool)
|
||||
{
|
||||
CcnetClient *client;
|
||||
|
||||
pthread_mutex_lock (&cpool->lock);
|
||||
client = g_queue_pop_head (cpool->clients);
|
||||
pthread_mutex_unlock (&cpool->lock);
|
||||
|
||||
if (!client) {
|
||||
client = ccnet_client_new ();
|
||||
if (ccnet_client_load_confdir (client, cpool->central_config_dir, cpool->conf_dir) < 0) {
|
||||
g_warning ("[client pool] Failed to load conf dir.\n");
|
||||
g_object_unref (client);
|
||||
return NULL;
|
||||
}
|
||||
if (ccnet_client_connect_daemon (client, CCNET_CLIENT_SYNC) < 0) {
|
||||
g_warning ("[client pool] Failed to connect.\n");
|
||||
g_object_unref (client);
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
return client;
|
||||
}
|
||||
|
||||
void
|
||||
ccnet_client_pool_return_client (struct CcnetClientPool *cpool,
|
||||
CcnetClient *client)
|
||||
{
|
||||
pthread_mutex_lock (&cpool->lock);
|
||||
g_queue_push_tail (cpool->clients, client);
|
||||
pthread_mutex_unlock (&cpool->lock);
|
||||
}
|
||||
|
223
lib/db.c
Normal file
223
lib/db.c
Normal file
@ -0,0 +1,223 @@
|
||||
|
||||
#include <glib.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include "db.h"
|
||||
|
||||
int
|
||||
sqlite_open_db (const char *db_path, sqlite3 **db)
|
||||
{
|
||||
int result;
|
||||
const char *errmsg;
|
||||
|
||||
result = sqlite3_open (db_path, db);
|
||||
if (result) {
|
||||
errmsg = sqlite3_errmsg (*db);
|
||||
|
||||
g_warning ("Couldn't open database:'%s', %s\n",
|
||||
db_path, errmsg ? errmsg : "no error given");
|
||||
|
||||
sqlite3_close (*db);
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int sqlite_close_db (sqlite3 *db)
|
||||
{
|
||||
return sqlite3_close (db);
|
||||
}
|
||||
|
||||
sqlite3_stmt *
|
||||
sqlite_query_prepare (sqlite3 *db, const char *sql)
|
||||
{
|
||||
sqlite3_stmt *stmt;
|
||||
int result;
|
||||
|
||||
result = sqlite3_prepare_v2 (db, sql, -1, &stmt, NULL);
|
||||
|
||||
if (result != SQLITE_OK) {
|
||||
const gchar *str = sqlite3_errmsg (db);
|
||||
|
||||
g_warning ("Couldn't prepare query, error:%d->'%s'\n\t%s\n",
|
||||
result, str ? str : "no error given", sql);
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return stmt;
|
||||
}
|
||||
|
||||
int
|
||||
sqlite_query_exec (sqlite3 *db, const char *sql)
|
||||
{
|
||||
char *errmsg = NULL;
|
||||
int result;
|
||||
|
||||
result = sqlite3_exec (db, sql, NULL, NULL, &errmsg);
|
||||
|
||||
if (result != SQLITE_OK) {
|
||||
if (errmsg != NULL) {
|
||||
g_warning ("SQL error: %d - %s\n:\t%s\n", result, errmsg, sql);
|
||||
sqlite3_free (errmsg);
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
sqlite_begin_transaction (sqlite3 *db)
|
||||
{
|
||||
char *sql = "BEGIN TRANSACTION;";
|
||||
return sqlite_query_exec (db, sql);
|
||||
}
|
||||
|
||||
int
|
||||
sqlite_end_transaction (sqlite3 *db)
|
||||
{
|
||||
char *sql = "END TRANSACTION;";
|
||||
return sqlite_query_exec (db, sql);
|
||||
}
|
||||
|
||||
|
||||
gboolean
|
||||
sqlite_check_for_existence (sqlite3 *db, const char *sql)
|
||||
{
|
||||
sqlite3_stmt *stmt;
|
||||
int result;
|
||||
|
||||
stmt = sqlite_query_prepare (db, sql);
|
||||
if (!stmt)
|
||||
return FALSE;
|
||||
|
||||
result = sqlite3_step (stmt);
|
||||
if (result == SQLITE_ERROR) {
|
||||
const gchar *str = sqlite3_errmsg (db);
|
||||
|
||||
g_warning ("Couldn't execute query, error: %d->'%s'\n",
|
||||
result, str ? str : "no error given");
|
||||
sqlite3_finalize (stmt);
|
||||
return FALSE;
|
||||
}
|
||||
sqlite3_finalize (stmt);
|
||||
|
||||
if (result == SQLITE_ROW)
|
||||
return TRUE;
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
int
|
||||
sqlite_foreach_selected_row (sqlite3 *db, const char *sql,
|
||||
SqliteRowFunc callback, void *data)
|
||||
{
|
||||
sqlite3_stmt *stmt;
|
||||
int result;
|
||||
int n_rows = 0;
|
||||
|
||||
stmt = sqlite_query_prepare (db, sql);
|
||||
if (!stmt) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
while (1) {
|
||||
result = sqlite3_step (stmt);
|
||||
if (result != SQLITE_ROW)
|
||||
break;
|
||||
n_rows++;
|
||||
if (!callback (stmt, data))
|
||||
break;
|
||||
}
|
||||
|
||||
if (result == SQLITE_ERROR) {
|
||||
const gchar *s = sqlite3_errmsg (db);
|
||||
|
||||
g_warning ("Couldn't execute query, error: %d->'%s'\n",
|
||||
result, s ? s : "no error given");
|
||||
sqlite3_finalize (stmt);
|
||||
return -1;
|
||||
}
|
||||
|
||||
sqlite3_finalize (stmt);
|
||||
return n_rows;
|
||||
}
|
||||
|
||||
int sqlite_get_int (sqlite3 *db, const char *sql)
|
||||
{
|
||||
int ret = -1;
|
||||
int result;
|
||||
sqlite3_stmt *stmt;
|
||||
|
||||
if ( !(stmt = sqlite_query_prepare(db, sql)) )
|
||||
return 0;
|
||||
|
||||
result = sqlite3_step (stmt);
|
||||
if (result == SQLITE_ROW) {
|
||||
ret = sqlite3_column_int (stmt, 0);
|
||||
sqlite3_finalize (stmt);
|
||||
return ret;
|
||||
}
|
||||
|
||||
if (result == SQLITE_ERROR) {
|
||||
const gchar *str = sqlite3_errmsg (db);
|
||||
g_warning ("Couldn't prepare query, error: %d->'%s'\n",
|
||||
result, str ? str : "no error given");
|
||||
sqlite3_finalize (stmt);
|
||||
return 0;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
gint64 sqlite_get_int64 (sqlite3 *db, const char *sql)
|
||||
{
|
||||
gint64 ret = -1;
|
||||
int result;
|
||||
sqlite3_stmt *stmt;
|
||||
|
||||
if ( !(stmt = sqlite_query_prepare(db, sql)) )
|
||||
return 0;
|
||||
|
||||
result = sqlite3_step (stmt);
|
||||
if (result == SQLITE_ROW) {
|
||||
ret = sqlite3_column_int64 (stmt, 0);
|
||||
sqlite3_finalize (stmt);
|
||||
return ret;
|
||||
}
|
||||
|
||||
if (result == SQLITE_ERROR) {
|
||||
const gchar *str = sqlite3_errmsg (db);
|
||||
g_warning ("Couldn't prepare query, error: %d->'%s'\n",
|
||||
result, str ? str : "no error given");
|
||||
sqlite3_finalize (stmt);
|
||||
return 0;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
char *sqlite_get_string (sqlite3 *db, const char *sql)
|
||||
{
|
||||
const char *res = NULL;
|
||||
int result;
|
||||
sqlite3_stmt *stmt;
|
||||
|
||||
if ( !(stmt = sqlite_query_prepare(db, sql)) )
|
||||
return NULL;
|
||||
|
||||
result = sqlite3_step (stmt);
|
||||
if (result == SQLITE_ROW) {
|
||||
res = (const char *)sqlite3_column_text (stmt, 0);
|
||||
sqlite3_finalize (stmt);
|
||||
return g_strdup(res);
|
||||
}
|
||||
|
||||
if (result == SQLITE_ERROR) {
|
||||
const gchar *str = sqlite3_errmsg (db);
|
||||
g_warning ("Couldn't prepare query, error: %d->'%s'\n",
|
||||
result, str ? str : "no error given");
|
||||
sqlite3_finalize (stmt);
|
||||
return NULL;
|
||||
}
|
||||
return NULL;
|
||||
}
|
33
lib/db.h
Normal file
33
lib/db.h
Normal file
@ -0,0 +1,33 @@
|
||||
/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
|
||||
|
||||
#ifndef DB_UTILS_H
|
||||
#define DB_UTILS_H
|
||||
|
||||
#include <sqlite3.h>
|
||||
|
||||
int sqlite_open_db (const char *db_path, sqlite3 **db);
|
||||
|
||||
int sqlite_close_db (sqlite3 *db);
|
||||
|
||||
sqlite3_stmt *sqlite_query_prepare (sqlite3 *db, const char *sql);
|
||||
|
||||
int sqlite_query_exec (sqlite3 *db, const char *sql);
|
||||
int sqlite_begin_transaction (sqlite3 *db);
|
||||
int sqlite_end_transaction (sqlite3 *db);
|
||||
|
||||
gboolean sqlite_check_for_existence (sqlite3 *db, const char *sql);
|
||||
|
||||
typedef gboolean (*SqliteRowFunc) (sqlite3_stmt *stmt, void *data);
|
||||
|
||||
int
|
||||
sqlite_foreach_selected_row (sqlite3 *db, const char *sql,
|
||||
SqliteRowFunc callback, void *data);
|
||||
|
||||
int sqlite_get_int (sqlite3 *db, const char *sql);
|
||||
|
||||
gint64 sqlite_get_int64 (sqlite3 *db, const char *sql);
|
||||
|
||||
char *sqlite_get_string (sqlite3 *db, const char *sql);
|
||||
|
||||
|
||||
#endif
|
295
lib/htree.c
Normal file
295
lib/htree.c
Normal file
@ -0,0 +1,295 @@
|
||||
/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <stdio.h>
|
||||
#include <assert.h>
|
||||
|
||||
#include "htree.h"
|
||||
|
||||
#define MAX_HEIGHT 7
|
||||
#define INDEX(hashid,depth) (depth%2 ? (hashid[depth/2] && 0x0f):(hashid[depth/2] >> 4))
|
||||
#define IS_NODE(n) (n->is_node)
|
||||
|
||||
static const int g_index[] = {0, 1, 17, 273, 4369, 69905, 1118481, 17895697, 286331153};
|
||||
|
||||
static inline void hashxor(char *hashid, char *id, int len)
|
||||
{
|
||||
int i = 0;
|
||||
for (i = 0; i < len; ++i) {
|
||||
hashid[i] = hashid[i] ^ id[i];
|
||||
}
|
||||
}
|
||||
|
||||
static inline int get_right_height (int size)
|
||||
{
|
||||
int height = 0;
|
||||
while (g_index[height+1] < size)
|
||||
++height;
|
||||
if (height < 1)return 1;
|
||||
return height;
|
||||
}
|
||||
|
||||
static inline unsigned int get_pos (HTree *tree, HTNode *node)
|
||||
{
|
||||
return (node - tree->nodes) - g_index[(int)node->depth];
|
||||
}
|
||||
|
||||
static inline void ht_set_data (HTree *tree, HTNode *node, HTData *data)
|
||||
{
|
||||
tree->datas[node - tree->nodes] = data;
|
||||
}
|
||||
|
||||
static inline HTItem *create_item (unsigned char *hashid, void *data)
|
||||
{
|
||||
HTItem *it = malloc (sizeof (HTItem));
|
||||
it->hashid = hashid;
|
||||
it->data = data;
|
||||
it->next = NULL;
|
||||
}
|
||||
|
||||
static inline void delete_item (HTree *tree, HTItem *it)
|
||||
{
|
||||
if (tree->item_free)
|
||||
tree->item_free (it);
|
||||
free (it);
|
||||
}
|
||||
|
||||
|
||||
static void *load_node (HTree *tree, HTNode *node)
|
||||
{
|
||||
HTData *data = ht_get_data (tree, node);
|
||||
if (data) return data;
|
||||
|
||||
data = malloc (sizeof(HTData));
|
||||
data->size = 0;
|
||||
data->hashid = malloc (tree->hashid_len);
|
||||
memset (data->hashid, 0, tree->hashid_len);
|
||||
data->item_list = NULL;
|
||||
ht_set_data (tree, node, data);
|
||||
return data;
|
||||
}
|
||||
|
||||
static void delete_data (HTree *tree, HTData *data)
|
||||
{
|
||||
HTItem *p = NULL;;
|
||||
if (data) {
|
||||
if (data->hashid)
|
||||
free (data->hashid);
|
||||
while (data->item_list) {
|
||||
p = data->item_list;
|
||||
data->item_list = p->next;
|
||||
delete_item (tree, p);
|
||||
}
|
||||
free(data);
|
||||
}
|
||||
}
|
||||
|
||||
static int add_item (HTree *tree, HTNode *node, unsigned char *hashid, void *data)
|
||||
{
|
||||
if (node == NULL)
|
||||
return 0;
|
||||
int r = 0, i = 0;
|
||||
HTData *htdata = NULL;
|
||||
if (IS_NODE(node)) {
|
||||
r = add_item (tree,
|
||||
ht_get_child (tree, node, INDEX(hashid, node->depth)),
|
||||
hashid,
|
||||
data);
|
||||
if (r) {
|
||||
htdata = load_node(tree, node);
|
||||
hashxor (htdata->hashid, hashid, tree->hashid_len);
|
||||
}
|
||||
return r;
|
||||
}
|
||||
|
||||
htdata = load_node(tree, node);
|
||||
HTItem *p = htdata->item_list;
|
||||
for (i = 0; i < htdata->size; i++){
|
||||
if (memcmp (hashid, p->hashid, tree->hashid_len) == 0) {
|
||||
return 0;
|
||||
}
|
||||
p = p->next;
|
||||
}
|
||||
HTItem *it = create_item (hashid, data);
|
||||
it->next = htdata->item_list;
|
||||
htdata->item_list = it;
|
||||
++htdata->size;
|
||||
hashxor (htdata->hashid, it->hashid, tree->hashid_len);
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int remove_item (HTree *tree, HTNode *node, unsigned char *hashid)
|
||||
{
|
||||
if (node == NULL)
|
||||
return 0;
|
||||
int r = 0, i = 0;
|
||||
HTData *data = NULL;
|
||||
if (IS_NODE(node)) {
|
||||
r = remove_item (tree,
|
||||
ht_get_child (tree, node, INDEX(hashid, node->depth)),
|
||||
hashid);
|
||||
if (r) {
|
||||
data = load_node(tree, node);
|
||||
hashxor (data->hashid, hashid, tree->hashid_len);
|
||||
}
|
||||
return r;
|
||||
}
|
||||
|
||||
data = load_node(tree, node);
|
||||
HTItem *p = data->item_list;
|
||||
HTItem *pre = NULL;
|
||||
for (i = 0; i < data->size; i++){
|
||||
if (memcmp (hashid, p->hashid, tree->hashid_len) == 0) {
|
||||
if (pre == NULL) {
|
||||
data->item_list = p->next;
|
||||
} else {
|
||||
pre->next = p->next;
|
||||
}
|
||||
--data->size;
|
||||
hashxor (data->hashid, hashid, tree->hashid_len);
|
||||
delete_item (tree, p);
|
||||
return 1;
|
||||
}
|
||||
pre = p;
|
||||
p = p->next;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
HTree* ht_new (int size, int hashlen)
|
||||
{
|
||||
HTree *ht = (HTree *)malloc (sizeof(HTree));
|
||||
int height = get_right_height (size);
|
||||
int i = 0, j = 0;
|
||||
|
||||
ht->height = height;
|
||||
ht->size = g_index[height];
|
||||
ht->hashid_len = hashlen;
|
||||
ht->item_free = NULL;
|
||||
ht->nodes = malloc (ht->size *sizeof(HTNode));
|
||||
ht->datas = malloc (ht->size *sizeof(HTData *));
|
||||
memset (ht->nodes, 0, ht->size *sizeof(HTNode));
|
||||
memset (ht->datas, 0, ht->size *sizeof(HTData *));
|
||||
|
||||
for (i = 0; i < height; ++i) {
|
||||
for (j = g_index[i]; j < g_index[i+1]; ++j) {
|
||||
(ht->nodes[j]).is_node = 1;
|
||||
(ht->nodes[j]).depth = i;
|
||||
}
|
||||
}
|
||||
for (j = g_index[height-1]; j < g_index[height]; ++j) {
|
||||
(ht->nodes[j]).is_node = 0;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
int ht_add (HTree *tree, unsigned char *hashid, void *data)
|
||||
{
|
||||
return add_item (tree, tree->nodes, hashid, data);
|
||||
}
|
||||
|
||||
int ht_remove (HTree *tree, unsigned char *hashid)
|
||||
{
|
||||
return remove_item (tree, tree->nodes, hashid);
|
||||
}
|
||||
|
||||
void ht_resize (HTree *ht, int size)
|
||||
{
|
||||
int height = get_right_height (size);
|
||||
if (height <= ht->height) {
|
||||
return;
|
||||
}
|
||||
/* TODO resize the hash tree */
|
||||
}
|
||||
|
||||
void ht_clear (HTree *tree)
|
||||
{
|
||||
assert (tree);
|
||||
|
||||
int i;
|
||||
for(i = 0; i < tree->size; i++){
|
||||
delete_data (tree, tree->datas[i]);
|
||||
}
|
||||
memset (tree->datas, 0, sizeof(HTData*) * tree->size);
|
||||
free (tree->nodes);
|
||||
free (tree->datas);
|
||||
free (tree);
|
||||
}
|
||||
|
||||
HTData* ht_get_data (HTree *tree, HTNode *node)
|
||||
{
|
||||
return tree->datas[node - tree->nodes];
|
||||
}
|
||||
|
||||
unsigned char* ht_get_node_hash (HTree *tree, HTNode *node)
|
||||
{
|
||||
if (tree->datas[node - tree->nodes] == NULL)
|
||||
return NULL;
|
||||
return (tree->datas[node - tree->nodes])->hashid;
|
||||
}
|
||||
|
||||
HTNode *ht_get_child (HTree *tree, HTNode *node, int b)
|
||||
{
|
||||
assert(0 <= b && b <= 0x0f);
|
||||
assert(node->depth < tree->height - 1);
|
||||
|
||||
int i = g_index[node->depth + 1] + (get_pos(tree, node) << 4) + b;
|
||||
|
||||
if (i >= tree->size){
|
||||
fprintf(stderr, "get_child out of bound: %dth %d >= %d\n", b, i, tree->size);
|
||||
return NULL;
|
||||
}
|
||||
return tree->nodes + i;
|
||||
}
|
||||
|
||||
HTNode *ht_get_parent (HTree *tree, HTNode *node)
|
||||
{
|
||||
if (node->depth == 0)
|
||||
return NULL;
|
||||
int i = g_index[node->depth - 1] + (get_pos(tree, node) >> 4) ;
|
||||
return tree->nodes + i;
|
||||
}
|
||||
|
||||
HTNode *ht_get_brother (HTree *tree, HTNode *node)
|
||||
{
|
||||
if (get_pos(tree, node) & 0xff < 15)
|
||||
return node + 1;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static void remove_node (HTree *tree, HTNode *node)
|
||||
{
|
||||
HTData* data = ht_get_data (tree, node);
|
||||
if (data == NULL)
|
||||
return;
|
||||
|
||||
if (HTNODE_IS_LEAF(node)) {
|
||||
delete_data (tree, data);
|
||||
} else {
|
||||
int i = 0;
|
||||
HTNode *child = NULL;
|
||||
for (i = 0; i < 16; ++i) {
|
||||
child = ht_get_child (tree, node, i);
|
||||
remove_node (tree, child);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ht_remove_node (HTree *tree, HTNode *node)
|
||||
{
|
||||
HTData *data = ht_get_data (tree, node);
|
||||
if (data == NULL)
|
||||
return;
|
||||
char *hash = data->hashid;
|
||||
HTNode *parent = ht_get_parent (tree, node);
|
||||
|
||||
while (parent) {
|
||||
char *phash = ht_get_node_hash (tree, parent);
|
||||
hashxor (phash, hash, tree->hashid_len);
|
||||
parent = ht_get_parent (tree, parent);
|
||||
}
|
||||
remove_node (tree, node);
|
||||
ht_set_data (tree, node, NULL);
|
||||
}
|
76
lib/htree.h
Normal file
76
lib/htree.h
Normal file
@ -0,0 +1,76 @@
|
||||
/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
|
||||
#ifndef __HTREE_H__
|
||||
#define __HTREE_H__
|
||||
|
||||
typedef struct hash_tree HTree;
|
||||
typedef void (*ItemFreeFunc)(void *);
|
||||
|
||||
struct ht_item {
|
||||
unsigned char *hashid;
|
||||
void *data;
|
||||
struct ht_item *next;
|
||||
};
|
||||
|
||||
struct ht_data {
|
||||
int size;
|
||||
unsigned char *hashid;
|
||||
struct ht_item *item_list;
|
||||
};
|
||||
|
||||
struct ht_node {
|
||||
unsigned char is_node;
|
||||
unsigned char depth; /* from 0 */
|
||||
};
|
||||
|
||||
struct hash_tree {
|
||||
int height;
|
||||
int hashid_len;
|
||||
int item_data_len;
|
||||
struct ht_node *nodes; /* an array of ht_node */
|
||||
struct ht_data **datas; /* an array of the pointer of ht_data*/
|
||||
int size;
|
||||
ItemFreeFunc item_free;
|
||||
};
|
||||
|
||||
typedef struct ht_item HTItem;
|
||||
typedef struct ht_data HTData;
|
||||
typedef struct ht_node HTNode;
|
||||
|
||||
#define HTNODE_IS_LEAF(n) (!(((HTNode *)n)->is_node))
|
||||
#define HTNODE_IS_NULL(tree, n) (ht_get_data(tree, n) == NULL)
|
||||
|
||||
HTree* ht_new (int size, int hashlen);
|
||||
void ht_clear (HTree *tree);
|
||||
int ht_add (HTree *tree, unsigned char *hashid, void *data);
|
||||
int ht_remove (HTree *tree, unsigned char *hashid);
|
||||
void ht_resize (HTree *ht, int size);
|
||||
HTData* ht_get_data (HTree *tree, HTNode *node);
|
||||
unsigned char* ht_get_node_hash (HTree *tree, HTNode *node);
|
||||
HTNode *ht_get_parent (HTree *tree, HTNode *node);
|
||||
HTNode *ht_get_child (HTree *tree, HTNode *node, int b);
|
||||
HTNode *ht_get_brother (HTree *tree, HTNode *node);
|
||||
void ht_remove_node (HTree *tree, HTNode *node);
|
||||
|
||||
static inline void ht_set_free_func (HTree *tree, ItemFreeFunc item_free)
|
||||
{
|
||||
tree->item_free = item_free;
|
||||
}
|
||||
|
||||
static inline HTNode *ht_get_root (HTree *tree)
|
||||
{
|
||||
return tree->nodes;
|
||||
}
|
||||
|
||||
static inline int ht_get_node_seq (HTree *tree, HTNode *node)
|
||||
{
|
||||
return node - tree->nodes;
|
||||
}
|
||||
|
||||
static inline HTNode *ht_get_node_by_seq (HTree *tree, int seq)
|
||||
{
|
||||
return tree->nodes + seq;
|
||||
}
|
||||
|
||||
|
||||
|
||||
#endif /* __HTREE_H__ */
|
35
lib/include.h
Normal file
35
lib/include.h
Normal file
@ -0,0 +1,35 @@
|
||||
|
||||
#include <config.h>
|
||||
#include <stdint.h>
|
||||
#include <unistd.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <errno.h>
|
||||
|
||||
#include <glib.h>
|
||||
|
||||
#include "libccnet_utils.h"
|
||||
#include <ccnet/valid-check.h>
|
||||
|
||||
#ifndef ccnet_warning
|
||||
#define ccnet_warning(fmt, ...) g_warning( "%s: " fmt, __func__ , ##__VA_ARGS__)
|
||||
#endif
|
||||
|
||||
#ifndef ccnet_error
|
||||
#define ccnet_error(fmt, ...) g_error( "%s: " fmt, __func__ , ##__VA_ARGS__)
|
||||
#endif
|
||||
|
||||
#ifndef ccnet_message
|
||||
#define ccnet_message(fmt, ...) g_message(fmt, ##__VA_ARGS__)
|
||||
#endif
|
||||
|
||||
#ifndef ccnet_debug
|
||||
#define ccnet_debug(fmt, ...) g_debug(fmt, ##__VA_ARGS__)
|
||||
#endif
|
||||
|
||||
#define CCNET_DOMAIN g_quark_from_string("ccnet")
|
||||
|
||||
#ifndef ENABLE_DEBUG
|
||||
#undef g_debug
|
||||
#define g_debug(...)
|
||||
#endif
|
76
lib/invoke-service-proc.c
Normal file
76
lib/invoke-service-proc.c
Normal file
@ -0,0 +1,76 @@
|
||||
/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
|
||||
|
||||
#include "include.h"
|
||||
|
||||
#include "ccnet-client.h"
|
||||
|
||||
#include "peer.h"
|
||||
#include "processor.h"
|
||||
#include "invoke-service-proc.h"
|
||||
|
||||
static int invoke_service_start (CcnetProcessor *processor,
|
||||
int argc, char **argv);
|
||||
static void handle_response (CcnetProcessor *processor,
|
||||
char *code, char *code_msg,
|
||||
char *content, int clen);
|
||||
|
||||
|
||||
#define GET_PRIV(o) \
|
||||
(G_TYPE_INSTANCE_GET_PRIVATE ((o), CCNET_TYPE_SERVICE_PROC, CcnetServiceProcPriv))
|
||||
|
||||
G_DEFINE_TYPE (CcnetServiceProc, ccnet_service_proc, CCNET_TYPE_PROCESSOR)
|
||||
|
||||
static void
|
||||
ccnet_service_proc_class_init (CcnetServiceProcClass *klass)
|
||||
{
|
||||
CcnetProcessorClass *proc_class = CCNET_PROCESSOR_CLASS (klass);
|
||||
|
||||
proc_class->start = invoke_service_start;
|
||||
proc_class->handle_response = handle_response;
|
||||
}
|
||||
|
||||
static void
|
||||
ccnet_service_proc_init (CcnetServiceProc *processor)
|
||||
{
|
||||
/* CCNET_PROCESSOR (processor)->state = INIT; */
|
||||
}
|
||||
|
||||
static int invoke_service_start (CcnetProcessor *processor,
|
||||
int argc, char **argv)
|
||||
{
|
||||
char *cmd;
|
||||
|
||||
if (argc == 0) {
|
||||
g_warning ("Invalid argument for service-proc\n");
|
||||
ccnet_processor_done (processor, FALSE);
|
||||
}
|
||||
|
||||
cmd = ccnet_util_strjoin_n (" ", argc, argv);
|
||||
|
||||
ccnet_client_send_request (processor->session,
|
||||
REQUEST_ID (processor->id), cmd);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void
|
||||
ccnet_service_proc_set_response_cb (CcnetServiceProc *proc,
|
||||
ResponseCB resp_cb, void *data)
|
||||
{
|
||||
proc->resp_cb = resp_cb;
|
||||
proc->cb_data = data;
|
||||
}
|
||||
|
||||
|
||||
static void handle_response (CcnetProcessor *processor,
|
||||
char *code, char *code_msg,
|
||||
char *content, int clen)
|
||||
{
|
||||
CcnetServiceProc *proc = (CcnetServiceProc *) processor;
|
||||
|
||||
if (proc->resp_cb)
|
||||
proc->resp_cb (code, code_msg, content, clen, proc->cb_data);
|
||||
|
||||
if (code[0] == '4' || code[0] == '5')
|
||||
ccnet_processor_done (processor, FALSE);
|
||||
}
|
175
lib/job-mgr.c
Normal file
175
lib/job-mgr.c
Normal file
@ -0,0 +1,175 @@
|
||||
/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
|
||||
|
||||
#if defined(__FreeBSD__) || defined(__NetBSD__) || defined(__OpenBSD__)
|
||||
#include <event2/event.h>
|
||||
#include <event2/event_compat.h>
|
||||
#else
|
||||
#include <event.h>
|
||||
#endif
|
||||
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <errno.h>
|
||||
|
||||
#define MAX_THREADS 50
|
||||
#define MAX_IDLE_THREADS 10
|
||||
|
||||
#ifdef CCNET_LIB
|
||||
#include "libccnet_utils.h"
|
||||
#define pipereadn ccnet_util_pipereadn
|
||||
#define pipewriten ccnet_util_pipewriten
|
||||
#define pipeclose ccnet_util_pipeclose
|
||||
#define ccnet_pipe ccnet_util_pipe
|
||||
#else
|
||||
#include "utils.h"
|
||||
#endif
|
||||
|
||||
#include "job-mgr.h"
|
||||
|
||||
struct _CcnetJob {
|
||||
CcnetJobManager *manager;
|
||||
|
||||
int id;
|
||||
ccnet_pipe_t pipefd[2];
|
||||
|
||||
JobThreadFunc thread_func;
|
||||
JobDoneCallback done_func; /* called when the thread is done */
|
||||
void *data;
|
||||
|
||||
/* the done callback should only access this field */
|
||||
void *result;
|
||||
};
|
||||
|
||||
|
||||
void
|
||||
ccnet_job_manager_remove_job (CcnetJobManager *mgr, int job_id);
|
||||
|
||||
static void
|
||||
job_thread_wrapper (void *vdata, void *unused)
|
||||
{
|
||||
CcnetJob *job = vdata;
|
||||
|
||||
|
||||
job->result = job->thread_func (job->data);
|
||||
if (pipewriten (job->pipefd[1], "a", 1) != 1) {
|
||||
g_warning ("[Job Manager] write to pipe error: %s\n", strerror(errno));
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
job_done_cb (evutil_socket_t fd, short event, void *vdata)
|
||||
{
|
||||
CcnetJob *job = vdata;
|
||||
char buf[1];
|
||||
|
||||
if (pipereadn (job->pipefd[0], buf, 1) != 1) {
|
||||
g_warning ("[Job Manager] read pipe error: %s\n", strerror(errno));
|
||||
}
|
||||
pipeclose (job->pipefd[0]);
|
||||
pipeclose (job->pipefd[1]);
|
||||
if (job->done_func) {
|
||||
job->done_func (job->result);
|
||||
}
|
||||
|
||||
ccnet_job_manager_remove_job (job->manager, job->id);
|
||||
}
|
||||
|
||||
int
|
||||
job_thread_create (CcnetJob *job)
|
||||
{
|
||||
if (ccnet_pipe (job->pipefd) < 0) {
|
||||
g_warning ("pipe error: %s\n", strerror(errno));
|
||||
return -1;
|
||||
}
|
||||
|
||||
g_thread_pool_push (job->manager->thread_pool, job, NULL);
|
||||
|
||||
#ifndef UNIT_TEST
|
||||
event_once (job->pipefd[0], EV_READ, job_done_cb, job, NULL);
|
||||
#endif
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
CcnetJob *
|
||||
ccnet_job_new ()
|
||||
{
|
||||
CcnetJob *job;
|
||||
|
||||
job = g_new0 (CcnetJob, 1);
|
||||
return job;
|
||||
}
|
||||
|
||||
void
|
||||
ccnet_job_free (CcnetJob *job)
|
||||
{
|
||||
g_free (job);
|
||||
}
|
||||
|
||||
CcnetJobManager *
|
||||
ccnet_job_manager_new (int max_threads)
|
||||
{
|
||||
CcnetJobManager *mgr;
|
||||
|
||||
mgr = g_new0 (CcnetJobManager, 1);
|
||||
mgr->jobs = g_hash_table_new_full (g_direct_hash, g_direct_equal,
|
||||
NULL, (GDestroyNotify)ccnet_job_free);
|
||||
mgr->thread_pool = g_thread_pool_new (job_thread_wrapper,
|
||||
NULL,
|
||||
max_threads,
|
||||
FALSE,
|
||||
NULL);
|
||||
/* g_thread_pool_set_max_unused_threads (MAX_IDLE_THREADS); */
|
||||
|
||||
return mgr;
|
||||
}
|
||||
|
||||
void
|
||||
ccnet_job_manager_free (CcnetJobManager *mgr)
|
||||
{
|
||||
g_hash_table_destroy (mgr->jobs);
|
||||
g_thread_pool_free (mgr->thread_pool, TRUE, FALSE);
|
||||
g_free (mgr);
|
||||
}
|
||||
|
||||
int
|
||||
ccnet_job_manager_schedule_job (CcnetJobManager *mgr,
|
||||
JobThreadFunc func,
|
||||
JobDoneCallback done_func,
|
||||
void *data)
|
||||
{
|
||||
CcnetJob *job = ccnet_job_new ();
|
||||
job->id = mgr->next_job_id++;
|
||||
job->manager = mgr;
|
||||
job->thread_func = func;
|
||||
job->done_func = done_func;
|
||||
job->data = data;
|
||||
|
||||
g_hash_table_insert (mgr->jobs, (gpointer)(long)job->id, job);
|
||||
|
||||
if (job_thread_create (job) < 0) {
|
||||
g_hash_table_remove (mgr->jobs, (gpointer)(long)job->id);
|
||||
return -1;
|
||||
}
|
||||
|
||||
return job->id;
|
||||
}
|
||||
|
||||
void
|
||||
ccnet_job_manager_remove_job (CcnetJobManager *mgr, int job_id)
|
||||
{
|
||||
g_hash_table_remove (mgr->jobs, (gpointer)(long)job_id);
|
||||
}
|
||||
|
||||
#ifdef UNIT_TEST
|
||||
void
|
||||
ccnet_job_manager_wait_job (CcnetJobManager *mgr, int job_id)
|
||||
{
|
||||
CcnetJob *job;
|
||||
|
||||
job = g_hash_table_lookup (mgr->jobs, (gpointer)(long)job_id);
|
||||
/* manually call job_done_cb */
|
||||
job_done_cb (0, 0, (void *)job);
|
||||
}
|
||||
#endif
|
882
lib/libccnet_utils.c
Normal file
882
lib/libccnet_utils.c
Normal file
@ -0,0 +1,882 @@
|
||||
/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
|
||||
|
||||
#include <config.h>
|
||||
|
||||
#include "libccnet_utils.h"
|
||||
|
||||
#ifdef WIN32
|
||||
#include <winsock2.h>
|
||||
#include <ws2tcpip.h>
|
||||
#include <Rpc.h>
|
||||
#include <shlobj.h>
|
||||
#include <psapi.h>
|
||||
#else
|
||||
#include <arpa/inet.h>
|
||||
#endif
|
||||
|
||||
#ifndef WIN32
|
||||
#include <pwd.h>
|
||||
#include <uuid/uuid.h>
|
||||
#endif
|
||||
|
||||
#include <unistd.h>
|
||||
#include <sys/time.h>
|
||||
#include <sys/types.h>
|
||||
#include <fcntl.h>
|
||||
#include <sys/stat.h>
|
||||
#include <dirent.h>
|
||||
#include <errno.h>
|
||||
#include <limits.h>
|
||||
#include <stdarg.h>
|
||||
#include <ctype.h>
|
||||
|
||||
#include <string.h>
|
||||
#include <openssl/sha.h>
|
||||
#include <openssl/hmac.h>
|
||||
#include <openssl/evp.h>
|
||||
#include <openssl/bio.h>
|
||||
#include <openssl/buffer.h>
|
||||
|
||||
|
||||
#include <glib.h>
|
||||
#include <glib/gstdio.h>
|
||||
#include <searpc-utils.h>
|
||||
|
||||
#ifdef WIN32
|
||||
int
|
||||
ccnet_util_pgpipe (ccnet_pipe_t handles[2])
|
||||
{
|
||||
SOCKET s;
|
||||
struct sockaddr_in serv_addr;
|
||||
int len = sizeof( serv_addr );
|
||||
|
||||
handles[0] = handles[1] = INVALID_SOCKET;
|
||||
|
||||
if ( ( s = socket( AF_INET, SOCK_STREAM, 0 ) ) == INVALID_SOCKET )
|
||||
{
|
||||
g_warning("pgpipe failed to create socket: %d\n", WSAGetLastError());
|
||||
return -1;
|
||||
}
|
||||
|
||||
memset( &serv_addr, 0, sizeof( serv_addr ) );
|
||||
serv_addr.sin_family = AF_INET;
|
||||
serv_addr.sin_port = htons(0);
|
||||
serv_addr.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
|
||||
if (bind(s, (SOCKADDR *) & serv_addr, len) == SOCKET_ERROR)
|
||||
{
|
||||
g_warning("pgpipe failed to bind: %d\n", WSAGetLastError());
|
||||
closesocket(s);
|
||||
return -1;
|
||||
}
|
||||
if (listen(s, 1) == SOCKET_ERROR)
|
||||
{
|
||||
g_warning("pgpipe failed to listen: %d\n", WSAGetLastError());
|
||||
closesocket(s);
|
||||
return -1;
|
||||
}
|
||||
if (getsockname(s, (SOCKADDR *) & serv_addr, &len) == SOCKET_ERROR)
|
||||
{
|
||||
g_warning("pgpipe failed to getsockname: %d\n", WSAGetLastError());
|
||||
closesocket(s);
|
||||
return -1;
|
||||
}
|
||||
if ((handles[1] = socket(PF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET)
|
||||
{
|
||||
g_warning("pgpipe failed to create socket 2: %d\n", WSAGetLastError());
|
||||
closesocket(s);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (connect(handles[1], (SOCKADDR *) & serv_addr, len) == SOCKET_ERROR)
|
||||
{
|
||||
g_warning("pgpipe failed to connect socket: %d\n", WSAGetLastError());
|
||||
closesocket(s);
|
||||
return -1;
|
||||
}
|
||||
if ((handles[0] = accept(s, (SOCKADDR *) & serv_addr, &len)) == INVALID_SOCKET)
|
||||
{
|
||||
g_warning("pgpipe failed to accept socket: %d\n", WSAGetLastError());
|
||||
closesocket(handles[1]);
|
||||
handles[1] = INVALID_SOCKET;
|
||||
closesocket(s);
|
||||
return -1;
|
||||
}
|
||||
closesocket(s);
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
struct timeval
|
||||
ccnet_util_timeval_from_msec (uint64_t milliseconds)
|
||||
{
|
||||
struct timeval ret;
|
||||
const uint64_t microseconds = milliseconds * 1000;
|
||||
ret.tv_sec = microseconds / 1000000;
|
||||
ret.tv_usec = microseconds % 1000000;
|
||||
return ret;
|
||||
}
|
||||
|
||||
int
|
||||
ccnet_util_checkdir (const char *dir)
|
||||
{
|
||||
struct stat st;
|
||||
|
||||
#ifdef WIN32
|
||||
/* remove trailing '\\' */
|
||||
char *path = g_strdup(dir);
|
||||
char *p = (char *)path + strlen(path) - 1;
|
||||
while (*p == '\\' || *p == '/') *p-- = '\0';
|
||||
if ((g_stat(dir, &st) < 0) || !S_ISDIR(st.st_mode)) {
|
||||
g_free (path);
|
||||
return -1;
|
||||
}
|
||||
g_free (path);
|
||||
return 0;
|
||||
#else
|
||||
if ((g_stat(dir, &st) < 0) || !S_ISDIR(st.st_mode))
|
||||
return -1;
|
||||
return 0;
|
||||
#endif
|
||||
}
|
||||
|
||||
int
|
||||
ccnet_util_checkdir_with_mkdir (const char *dir)
|
||||
{
|
||||
#ifdef WIN32
|
||||
int ret;
|
||||
char *path = g_strdup(dir);
|
||||
char *p = (char *)path + strlen(path) - 1;
|
||||
while (*p == '\\' || *p == '/') *p-- = '\0';
|
||||
ret = g_mkdir_with_parents(path, 0755);
|
||||
g_free (path);
|
||||
return ret;
|
||||
#else
|
||||
return g_mkdir_with_parents(dir, 0755);
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
ssize_t /* Read "n" bytes from a descriptor. */
|
||||
ccnet_util_recvn(evutil_socket_t fd, void *vptr, size_t n)
|
||||
{
|
||||
size_t nleft;
|
||||
ssize_t nread;
|
||||
char *ptr;
|
||||
|
||||
ptr = vptr;
|
||||
nleft = n;
|
||||
while (nleft > 0) {
|
||||
#ifndef WIN32
|
||||
if ( (nread = read(fd, ptr, nleft)) < 0)
|
||||
#else
|
||||
if ( (nread = recv(fd, ptr, nleft, 0)) < 0)
|
||||
#endif
|
||||
{
|
||||
if (errno == EINTR)
|
||||
nread = 0; /* and call read() again */
|
||||
else
|
||||
return(-1);
|
||||
} else if (nread == 0)
|
||||
break; /* EOF */
|
||||
|
||||
nleft -= nread;
|
||||
ptr += nread;
|
||||
}
|
||||
return(n - nleft); /* return >= 0 */
|
||||
}
|
||||
|
||||
ssize_t /* Write "n" bytes to a descriptor. */
|
||||
ccnet_util_sendn (evutil_socket_t fd, const void *vptr, size_t n)
|
||||
{
|
||||
size_t nleft;
|
||||
ssize_t nwritten;
|
||||
const char *ptr;
|
||||
|
||||
ptr = vptr;
|
||||
nleft = n;
|
||||
while (nleft > 0) {
|
||||
#ifndef WIN32
|
||||
if ( (nwritten = write(fd, ptr, nleft)) <= 0)
|
||||
#else
|
||||
if ( (nwritten = send(fd, ptr, nleft, 0)) <= 0)
|
||||
#endif
|
||||
{
|
||||
if (nwritten < 0 && errno == EINTR)
|
||||
nwritten = 0; /* and call write() again */
|
||||
else
|
||||
return(-1); /* error */
|
||||
}
|
||||
|
||||
nleft -= nwritten;
|
||||
ptr += nwritten;
|
||||
}
|
||||
return(n);
|
||||
}
|
||||
|
||||
char*
|
||||
ccnet_util_expand_path (const char *src)
|
||||
{
|
||||
#ifdef WIN32
|
||||
char new_path[PATH_MAX + 1];
|
||||
char *p = new_path;
|
||||
const char *q = src;
|
||||
|
||||
memset(new_path, 0, sizeof(new_path));
|
||||
if (*src == '~') {
|
||||
const char *home = g_get_home_dir();
|
||||
memcpy(new_path, home, strlen(home));
|
||||
p += strlen(new_path);
|
||||
q++;
|
||||
}
|
||||
memcpy(p, q, strlen(q));
|
||||
|
||||
/* delete the charactor '\' or '/' at the end of the path
|
||||
* because the function stat faied to deal with directory names
|
||||
* with '\' or '/' in the end */
|
||||
p = new_path + strlen(new_path) - 1;
|
||||
while(*p == '\\' || *p == '/') *p-- = '\0';
|
||||
|
||||
return strdup (new_path);
|
||||
#else
|
||||
const char *next_in, *ntoken;
|
||||
char new_path[PATH_MAX + 1];
|
||||
char *next_out;
|
||||
int len;
|
||||
|
||||
/* special cases */
|
||||
if (!src || *src == '\0')
|
||||
return NULL;
|
||||
if (strlen(src) > PATH_MAX)
|
||||
return NULL;
|
||||
|
||||
next_in = src;
|
||||
next_out = new_path;
|
||||
*next_out = '\0';
|
||||
|
||||
if (*src == '~') {
|
||||
/* handle src start with '~' or '~<user>' like '~plt' */
|
||||
struct passwd *pw = NULL;
|
||||
|
||||
for ( ; *next_in != '/' && *next_in != '\0'; next_in++) ;
|
||||
|
||||
len = next_in - src;
|
||||
if (len == 1) {
|
||||
pw = getpwuid (geteuid());
|
||||
} else {
|
||||
/* copy '~<user>' to new_path */
|
||||
memcpy (new_path, src, len);
|
||||
new_path[len] = '\0';
|
||||
pw = getpwnam (new_path + 1);
|
||||
}
|
||||
if (pw == NULL)
|
||||
return NULL;
|
||||
|
||||
len = strlen (pw->pw_dir);
|
||||
memcpy (new_path, pw->pw_dir, len);
|
||||
next_out = new_path + len;
|
||||
*next_out = '\0';
|
||||
|
||||
if (*next_in == '\0')
|
||||
return strdup (new_path);
|
||||
} else if (*src != '/') {
|
||||
getcwd (new_path, PATH_MAX);
|
||||
for ( ; *next_out; next_out++) ; /* to '\0' */
|
||||
}
|
||||
|
||||
while (*next_in != '\0') {
|
||||
/* move ntoken to the next not '/' char */
|
||||
for (ntoken = next_in; *ntoken == '/'; ntoken++) ;
|
||||
|
||||
for (next_in = ntoken; *next_in != '/'
|
||||
&& *next_in != '\0'; next_in++) ;
|
||||
|
||||
len = next_in - ntoken;
|
||||
|
||||
if (len == 0) {
|
||||
/* the path ends with '/', keep it */
|
||||
*next_out++ = '/';
|
||||
*next_out = '\0';
|
||||
break;
|
||||
}
|
||||
|
||||
if (len == 2 && ntoken[0] == '.' && ntoken[1] == '.')
|
||||
{
|
||||
/* '..' */
|
||||
for (; next_out > new_path && *next_out != '/'; next_out--)
|
||||
;
|
||||
*next_out = '\0';
|
||||
} else if (ntoken[0] != '.' || len != 1) {
|
||||
/* not '.' */
|
||||
*next_out++ = '/';
|
||||
memcpy (next_out, ntoken, len);
|
||||
next_out += len;
|
||||
*next_out = '\0';
|
||||
}
|
||||
}
|
||||
|
||||
/* the final special case */
|
||||
if (new_path[0] == '\0') {
|
||||
new_path[0] = '/';
|
||||
new_path[1] = '\0';
|
||||
}
|
||||
return strdup (new_path);
|
||||
#endif
|
||||
}
|
||||
|
||||
#ifndef WIN32
|
||||
char* ccnet_util_gen_uuid ()
|
||||
{
|
||||
char *uuid_str = g_malloc (37);
|
||||
uuid_t uuid;
|
||||
|
||||
uuid_generate (uuid);
|
||||
uuid_unparse_lower (uuid, uuid_str);
|
||||
|
||||
return uuid_str;
|
||||
}
|
||||
|
||||
#else
|
||||
char* ccnet_util_gen_uuid ()
|
||||
{
|
||||
char *uuid_str = g_malloc (37);
|
||||
unsigned char *str = NULL;
|
||||
UUID uuid;
|
||||
|
||||
UuidCreate(&uuid);
|
||||
UuidToString(&uuid, &str);
|
||||
memcpy(uuid_str, str, 37);
|
||||
RpcStringFree(&str);
|
||||
return uuid_str;
|
||||
}
|
||||
#endif
|
||||
|
||||
char* ccnet_util_strjoin_n (const char *seperator, int argc, char **argv)
|
||||
{
|
||||
GString *buf;
|
||||
int i;
|
||||
char *str;
|
||||
|
||||
if (argc == 0)
|
||||
return NULL;
|
||||
|
||||
buf = g_string_new (argv[0]);
|
||||
for (i = 1; i < argc; ++i) {
|
||||
g_string_append (buf, seperator);
|
||||
g_string_append (buf, argv[i]);
|
||||
}
|
||||
|
||||
str = buf->str;
|
||||
g_string_free (buf, FALSE);
|
||||
return str;
|
||||
}
|
||||
|
||||
/**
|
||||
* handle the empty string problem.
|
||||
*/
|
||||
gchar*
|
||||
ccnet_util_key_file_get_string (GKeyFile *keyf,
|
||||
const char *category,
|
||||
const char *key)
|
||||
{
|
||||
gchar *v;
|
||||
|
||||
if (!g_key_file_has_key (keyf, category, key, NULL))
|
||||
return NULL;
|
||||
|
||||
v = g_key_file_get_string (keyf, category, key, NULL);
|
||||
if (v != NULL && v[0] == '\0') {
|
||||
g_free(v);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return v;
|
||||
}
|
||||
|
||||
void
|
||||
ccnet_util_string_list_free (GList *str_list)
|
||||
{
|
||||
GList *ptr = str_list;
|
||||
|
||||
while (ptr) {
|
||||
g_free (ptr->data);
|
||||
ptr = ptr->next;
|
||||
}
|
||||
|
||||
g_list_free (str_list);
|
||||
}
|
||||
|
||||
void
|
||||
ccnet_util_string_list_join (GList *str_list, GString *str, const char *seperator)
|
||||
{
|
||||
GList *ptr;
|
||||
if (!str_list)
|
||||
return;
|
||||
|
||||
ptr = str_list;
|
||||
g_string_append (str, ptr->data);
|
||||
|
||||
for (ptr = ptr->next; ptr; ptr = ptr->next) {
|
||||
g_string_append (str, seperator);
|
||||
g_string_append (str, (char *)ptr->data);
|
||||
}
|
||||
}
|
||||
|
||||
static GList *
|
||||
string_list_parse (const char *list_in_str, const char *seperator)
|
||||
{
|
||||
if (!list_in_str)
|
||||
return NULL;
|
||||
|
||||
GList *list = NULL;
|
||||
char **array = g_strsplit (list_in_str, seperator, 0);
|
||||
char **ptr;
|
||||
|
||||
for (ptr = array; *ptr; ptr++) {
|
||||
list = g_list_prepend (list, g_strdup(*ptr));
|
||||
}
|
||||
list = g_list_reverse (list);
|
||||
|
||||
g_strfreev (array);
|
||||
return list;
|
||||
}
|
||||
|
||||
GList *
|
||||
ccnet_util_string_list_parse_sorted (const char *list_in_str, const char *seperator)
|
||||
{
|
||||
GList *list = string_list_parse (list_in_str, seperator);
|
||||
|
||||
return g_list_sort (list, (GCompareFunc)g_strcmp0);
|
||||
}
|
||||
|
||||
static unsigned hexval(char c)
|
||||
{
|
||||
if (c >= '0' && c <= '9')
|
||||
return c - '0';
|
||||
if (c >= 'a' && c <= 'f')
|
||||
return c - 'a' + 10;
|
||||
if (c >= 'A' && c <= 'F')
|
||||
return c - 'A' + 10;
|
||||
return ~0;
|
||||
}
|
||||
|
||||
int
|
||||
ccnet_util_hex_to_rawdata (const char *hex_str,
|
||||
unsigned char *rawdata,
|
||||
int n_bytes)
|
||||
{
|
||||
int i;
|
||||
for (i = 0; i < n_bytes; i++) {
|
||||
unsigned int val = (hexval(hex_str[0]) << 4) | hexval(hex_str[1]);
|
||||
if (val & ~0xff)
|
||||
return -1;
|
||||
*rawdata++ = val;
|
||||
hex_str += 2;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
#ifdef WIN32
|
||||
|
||||
#ifndef EAFNOSUPPORT
|
||||
#define EAFNOSUPPORT WSAEAFNOSUPPORT
|
||||
#endif
|
||||
|
||||
#ifndef IN6ADDRSZ
|
||||
#define IN6ADDRSZ 16
|
||||
#endif
|
||||
|
||||
#ifndef INT16SZ
|
||||
#define INT16SZ 2
|
||||
#endif
|
||||
|
||||
#ifndef INADDRSZ
|
||||
#define INADDRSZ 4
|
||||
#endif
|
||||
|
||||
#ifndef inet_ntop
|
||||
static const char *
|
||||
inet_ntop4 (const u_char *src, char *dst, size_t size)
|
||||
{
|
||||
static const char fmt[] = "%u.%u.%u.%u";
|
||||
char tmp[sizeof("255.255.255.255")];
|
||||
int l;
|
||||
l = _snprintf(tmp, size, fmt, src[0], src[1], src[2], src[3]);
|
||||
if (l <= 0 || l >= size) {
|
||||
return (NULL);
|
||||
}
|
||||
strncpy(dst, tmp, size);
|
||||
return (dst);
|
||||
}
|
||||
|
||||
static const char *
|
||||
inet_ntop6 (const u_char *src, char *dst, size_t size)
|
||||
{
|
||||
char tmp[sizeof "ffff:ffff:ffff:ffff:ffff:ffff:255.255.255.255"];
|
||||
char *tp, *ep;
|
||||
struct
|
||||
{
|
||||
int base, len;
|
||||
} best, cur;
|
||||
u_int words[IN6ADDRSZ / INT16SZ];
|
||||
int i;
|
||||
int advance;
|
||||
memset(words, '\0', sizeof(words));
|
||||
for (i = 0; i < IN6ADDRSZ; i++)
|
||||
words[i / 2] |= (src[i] << ((1 - (i % 2)) << 3));
|
||||
best.base = -1;
|
||||
cur.base = -1;
|
||||
best.len = -1;
|
||||
cur.len = -1;
|
||||
for (i = 0; i < (IN6ADDRSZ / INT16SZ); i++) {
|
||||
if (words[i] == 0) {
|
||||
if (cur.base == -1)
|
||||
cur.base = i, cur.len = 1;
|
||||
else
|
||||
cur.len++;
|
||||
}
|
||||
else {
|
||||
if (cur.base != -1) {
|
||||
if (best.base == -1 || cur.len > best.len)
|
||||
best = cur;
|
||||
cur.base = -1;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (cur.base != -1) {
|
||||
if (best.base == -1 || cur.len > best.len)
|
||||
best = cur;
|
||||
}
|
||||
if (best.base != -1 && best.len < 2)
|
||||
best.base = -1;
|
||||
tp = tmp;
|
||||
ep = tmp + sizeof(tmp);
|
||||
for (i = 0; i < (IN6ADDRSZ / INT16SZ) && tp < ep; i++) {
|
||||
/** Are we inside the best run of 0x00's? */
|
||||
if (best.base != -1 && i >= best.base &&
|
||||
i < (best.base + best.len)) {
|
||||
if (i == best.base) {
|
||||
if (tp + 1 >= ep)
|
||||
return (NULL);
|
||||
*tp++ = ':';
|
||||
}
|
||||
continue;
|
||||
}
|
||||
/** Are we following an initial run of 0x00s or any real hex? */
|
||||
if (i != 0) {
|
||||
if (tp + 1 >= ep)
|
||||
return (NULL);
|
||||
*tp++ = ':';
|
||||
}
|
||||
/** Is this address an encapsulated IPv4? */
|
||||
if (i == 6 && best.base == 0 &&
|
||||
(best.len == 6 || (best.len == 5 && words[5] == 0xffff))) {
|
||||
if (!inet_ntop4(src+12, tp, (size_t)(ep - tp)))
|
||||
return (NULL);
|
||||
tp += strlen(tp);
|
||||
break;
|
||||
}
|
||||
advance = snprintf(tp, ep - tp, "%x", words[i]);
|
||||
if (advance <= 0 || advance >= ep - tp)
|
||||
return (NULL);
|
||||
tp += advance;
|
||||
}
|
||||
/** Was it a trailing run of 0x00's? */
|
||||
if (best.base != -1 && (best.base + best.len) == (IN6ADDRSZ / INT16SZ)) {
|
||||
if (tp + 1 >= ep)
|
||||
return (NULL);
|
||||
*tp++ = ':';
|
||||
}
|
||||
if (tp + 1 >= ep)
|
||||
return (NULL);
|
||||
*tp++ = '\0';
|
||||
|
||||
/**
|
||||
* Check for overflow, copy, and we're done.
|
||||
*/
|
||||
if ((size_t)(tp - tmp) > size) {
|
||||
errno = ENOSPC;
|
||||
return (NULL);
|
||||
}
|
||||
strncpy(dst, tmp, size);
|
||||
dst[size] = '\0';
|
||||
return (dst);
|
||||
}
|
||||
|
||||
const char *
|
||||
ccnet_util_inet_ntop(int af, const void *src, char *dst, size_t size)
|
||||
{
|
||||
switch (af) {
|
||||
case AF_INET:
|
||||
return (inet_ntop4(src, dst, size));
|
||||
case AF_INET6:
|
||||
return (inet_ntop6(src, dst, size));
|
||||
default:
|
||||
return (NULL);
|
||||
}
|
||||
/** NOTREACHED */
|
||||
}
|
||||
#endif //inet_ntop
|
||||
|
||||
#ifndef inet_aton
|
||||
int
|
||||
ccnet_util_inet_aton (const char *string, struct in_addr *addr)
|
||||
{
|
||||
addr->s_addr = inet_addr(string);
|
||||
if (addr->s_addr != -1 || strcmp("255.255.255.255", string) == 0)
|
||||
return 1;
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifndef inet_pton
|
||||
/*
|
||||
* Don't even consider trying to compile this on a system where
|
||||
* sizeof(int) < 4. sizeof(int) > 4 is fine; all the world's not a VAX.
|
||||
*/
|
||||
|
||||
/* int
|
||||
* inet_pton4(src, dst, pton)
|
||||
* when last arg is 0: inet_aton(). with hexadecimal, octal and shorthand.
|
||||
* when last arg is 1: inet_pton(). decimal dotted-quad only.
|
||||
* return:
|
||||
* 1 if `src' is a valid input, else 0.
|
||||
* notice:
|
||||
* does not touch `dst' unless it's returning 1.
|
||||
* author:
|
||||
* Paul Vixie, 1996.
|
||||
*/
|
||||
static int
|
||||
inet_pton4 (const char *src, u_char *dst, int pton)
|
||||
{
|
||||
u_int val;
|
||||
u_int digit;
|
||||
int base, n;
|
||||
unsigned char c;
|
||||
u_int parts[4];
|
||||
register u_int *pp = parts;
|
||||
|
||||
c = *src;
|
||||
for (;;) {
|
||||
/*
|
||||
* Collect number up to ``.''.
|
||||
* Values are specified as for C:
|
||||
* 0x=hex, 0=octal, isdigit=decimal.
|
||||
*/
|
||||
if (!isdigit(c))
|
||||
return (0);
|
||||
val = 0; base = 10;
|
||||
if (c == '0') {
|
||||
c = *++src;
|
||||
if (c == 'x' || c == 'X')
|
||||
base = 16, c = *++src;
|
||||
else if (isdigit(c) && c != '9')
|
||||
base = 8;
|
||||
}
|
||||
/* inet_pton() takes decimal only */
|
||||
if (pton && base != 10)
|
||||
return (0);
|
||||
for (;;) {
|
||||
if (isdigit(c)) {
|
||||
digit = c - '0';
|
||||
if (digit >= base)
|
||||
break;
|
||||
val = (val * base) + digit;
|
||||
c = *++src;
|
||||
} else if (base == 16 && isxdigit(c)) {
|
||||
digit = c + 10 - (islower(c) ? 'a' : 'A');
|
||||
if (digit >= 16)
|
||||
break;
|
||||
val = (val << 4) | digit;
|
||||
c = *++src;
|
||||
} else
|
||||
break;
|
||||
}
|
||||
if (c == '.') {
|
||||
/*
|
||||
* Internet format:
|
||||
* a.b.c.d
|
||||
* a.b.c (with c treated as 16 bits)
|
||||
* a.b (with b treated as 24 bits)
|
||||
* a (with a treated as 32 bits)
|
||||
*/
|
||||
if (pp >= parts + 3)
|
||||
return (0);
|
||||
*pp++ = val;
|
||||
c = *++src;
|
||||
} else
|
||||
break;
|
||||
}
|
||||
/*
|
||||
* Check for trailing characters.
|
||||
*/
|
||||
if (c != '\0' && !isspace(c))
|
||||
return (0);
|
||||
/*
|
||||
* Concoct the address according to
|
||||
* the number of parts specified.
|
||||
*/
|
||||
n = pp - parts + 1;
|
||||
/* inet_pton() takes dotted-quad only. it does not take shorthand. */
|
||||
if (pton && n != 4)
|
||||
return (0);
|
||||
switch (n) {
|
||||
|
||||
case 0:
|
||||
return (0); /* initial nondigit */
|
||||
|
||||
case 1: /* a -- 32 bits */
|
||||
break;
|
||||
|
||||
case 2: /* a.b -- 8.24 bits */
|
||||
if (parts[0] > 0xff || val > 0xffffff)
|
||||
return (0);
|
||||
val |= parts[0] << 24;
|
||||
break;
|
||||
|
||||
case 3: /* a.b.c -- 8.8.16 bits */
|
||||
if ((parts[0] | parts[1]) > 0xff || val > 0xffff)
|
||||
return (0);
|
||||
val |= (parts[0] << 24) | (parts[1] << 16);
|
||||
break;
|
||||
|
||||
case 4: /* a.b.c.d -- 8.8.8.8 bits */
|
||||
if ((parts[0] | parts[1] | parts[2] | val) > 0xff)
|
||||
return (0);
|
||||
val |= (parts[0] << 24) | (parts[1] << 16) | (parts[2] << 8);
|
||||
break;
|
||||
}
|
||||
if (dst) {
|
||||
val = htonl(val);
|
||||
memcpy(dst, &val, INADDRSZ);
|
||||
}
|
||||
return (1);
|
||||
}
|
||||
|
||||
/* int
|
||||
* inet_pton6(src, dst)
|
||||
* convert presentation level address to network order binary form.
|
||||
* return:
|
||||
* 1 if `src' is a valid [RFC1884 2.2] address, else 0.
|
||||
* notice:
|
||||
* (1) does not touch `dst' unless it's returning 1.
|
||||
* (2) :: in a full address is silently ignored.
|
||||
* credit:
|
||||
* inspired by Mark Andrews.
|
||||
* author:
|
||||
* Paul Vixie, 1996.
|
||||
*/
|
||||
static int
|
||||
inet_pton6 (const char *src, u_char *dst)
|
||||
{
|
||||
static const char xdigits_l[] = "0123456789abcdef",
|
||||
xdigits_u[] = "0123456789ABCDEF";
|
||||
u_char tmp[IN6ADDRSZ], *tp, *endp, *colonp;
|
||||
const char *xdigits, *curtok;
|
||||
int ch, saw_xdigit;
|
||||
u_int val;
|
||||
|
||||
memset((tp = tmp), '\0', IN6ADDRSZ);
|
||||
endp = tp + IN6ADDRSZ;
|
||||
colonp = NULL;
|
||||
/* Leading :: requires some special handling. */
|
||||
if (*src == ':')
|
||||
if (*++src != ':')
|
||||
return (0);
|
||||
curtok = src;
|
||||
saw_xdigit = 0;
|
||||
val = 0;
|
||||
while ((ch = *src++) != '\0') {
|
||||
const char *pch;
|
||||
|
||||
if ((pch = strchr((xdigits = xdigits_l), ch)) == NULL)
|
||||
pch = strchr((xdigits = xdigits_u), ch);
|
||||
if (pch != NULL) {
|
||||
val <<= 4;
|
||||
val |= (pch - xdigits);
|
||||
if (val > 0xffff)
|
||||
return (0);
|
||||
saw_xdigit = 1;
|
||||
continue;
|
||||
}
|
||||
if (ch == ':') {
|
||||
curtok = src;
|
||||
if (!saw_xdigit) {
|
||||
if (colonp)
|
||||
return (0);
|
||||
colonp = tp;
|
||||
continue;
|
||||
} else if (*src == '\0')
|
||||
return (0);
|
||||
if (tp + INT16SZ > endp)
|
||||
return (0);
|
||||
*tp++ = (u_char) (val >> 8) & 0xff;
|
||||
*tp++ = (u_char) val & 0xff;
|
||||
saw_xdigit = 0;
|
||||
val = 0;
|
||||
continue;
|
||||
}
|
||||
if (ch == '.' && ((tp + INADDRSZ) <= endp) &&
|
||||
inet_pton4(curtok, tp, 1) > 0) {
|
||||
tp += INADDRSZ;
|
||||
saw_xdigit = 0;
|
||||
break; /* '\0' was seen by inet_pton4(). */
|
||||
}
|
||||
return (0);
|
||||
}
|
||||
if (saw_xdigit) {
|
||||
if (tp + INT16SZ > endp)
|
||||
return (0);
|
||||
*tp++ = (u_char) (val >> 8) & 0xff;
|
||||
*tp++ = (u_char) val & 0xff;
|
||||
}
|
||||
if (colonp != NULL) {
|
||||
/*
|
||||
* Since some memmove()'s erroneously fail to handle
|
||||
* overlapping regions, we'll do the shift by hand.
|
||||
*/
|
||||
const int n = tp - colonp;
|
||||
int i;
|
||||
|
||||
if (tp == endp)
|
||||
return (0);
|
||||
for (i = 1; i <= n; i++) {
|
||||
endp[- i] = colonp[n - i];
|
||||
colonp[n - i] = 0;
|
||||
}
|
||||
tp = endp;
|
||||
}
|
||||
if (tp != endp)
|
||||
return (0);
|
||||
memcpy(dst, tmp, IN6ADDRSZ);
|
||||
return (1);
|
||||
}
|
||||
|
||||
/* int
|
||||
* inet_pton(af, src, dst)
|
||||
* convert from presentation format (which usually means ASCII printable)
|
||||
* to network format (which is usually some kind of binary format).
|
||||
* return:
|
||||
* 1 if the address was valid for the specified address family
|
||||
* 0 if the address wasn't valid (`dst' is untouched in this case)
|
||||
* -1 if some other error occurred (`dst' is untouched in this case, too)
|
||||
* author:
|
||||
* Paul Vixie, 1996.
|
||||
*/
|
||||
int
|
||||
ccnet_util_inet_pton (int af, const char *src, void *dst)
|
||||
{
|
||||
switch (af) {
|
||||
case AF_INET:
|
||||
return (inet_pton4(src, dst, 1));
|
||||
case AF_INET6:
|
||||
return (inet_pton6(src, dst));
|
||||
default:
|
||||
errno = EAFNOSUPPORT;
|
||||
return (-1);
|
||||
}
|
||||
/* NOTREACHED */
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif //WIN32
|
89
lib/libccnet_utils.h
Normal file
89
lib/libccnet_utils.h
Normal file
@ -0,0 +1,89 @@
|
||||
/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
|
||||
#ifndef LIBCCNET_UTILS_H
|
||||
#define LIBCCNET_UTILS_H
|
||||
|
||||
/**
|
||||
* All the helper function names are prefixed with ccnet_util_XXX to avoid
|
||||
* name conflict.
|
||||
*/
|
||||
|
||||
#include <sys/time.h>
|
||||
#include <stdint.h>
|
||||
#include <unistd.h>
|
||||
#include <stdarg.h>
|
||||
#include <glib.h>
|
||||
#include <glib-object.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#if defined(__FreeBSD__) || defined(__NetBSD__) || defined(__OpenBSD__)
|
||||
#include <event2/util.h>
|
||||
#else
|
||||
#include <evutil.h>
|
||||
#endif
|
||||
|
||||
#ifdef WIN32
|
||||
#include <errno.h>
|
||||
#include <glib/gstdio.h>
|
||||
|
||||
#ifndef WEXITSTATUS
|
||||
#define WEXITSTATUS(status) (((status) & 0xff00) >> 8)
|
||||
#endif
|
||||
|
||||
/* Borrowed from libevent */
|
||||
#define ccnet_pipe_t intptr_t
|
||||
|
||||
int ccnet_util_pgpipe (ccnet_pipe_t handles[2]);
|
||||
#define ccnet_util_pipe(a) ccnet_util_pgpipe((a))
|
||||
#define ccnet_util_pipeclose(a) closesocket((a))
|
||||
#else
|
||||
#define ccnet_pipe_t int
|
||||
#define ccnet_util_pipe(a) pipe((a))
|
||||
#define ccnet_util_pipeclose(a) close((a))
|
||||
#endif
|
||||
|
||||
#define ccnet_util_pipereadn(a,b,c) ccnet_util_recvn((a),(b),(c))
|
||||
#define ccnet_util_pipewriten(a,b,c) ccnet_util_sendn((a),(b),(c))
|
||||
|
||||
#ifndef O_BINARY
|
||||
#define O_BINARY 0
|
||||
#endif
|
||||
|
||||
struct timeval ccnet_util_timeval_from_msec (uint64_t milliseconds);
|
||||
|
||||
char* ccnet_util_gen_uuid ();
|
||||
|
||||
/* dir operations */
|
||||
int ccnet_util_checkdir (const char *dir);
|
||||
char* ccnet_util_expand_path (const char *src);
|
||||
|
||||
/* Read "n" bytes from a socket. */
|
||||
ssize_t ccnet_util_recvn(evutil_socket_t fd, void *vptr, size_t n);
|
||||
ssize_t ccnet_util_sendn(evutil_socket_t fd, const void *vptr, size_t n);
|
||||
|
||||
/* string utilities */
|
||||
|
||||
char * ccnet_util_strjoin_n (const char *seperator, int argc, char **argv);
|
||||
|
||||
void ccnet_util_string_list_free (GList *str_list);
|
||||
void ccnet_util_string_list_join (GList *str_list, GString *strbuf, const char *seperator);
|
||||
GList *ccnet_util_string_list_parse_sorted (const char *list_in_str, const char *seperator);
|
||||
|
||||
gchar* ccnet_util_key_file_get_string (GKeyFile *keyf,
|
||||
const char *category,
|
||||
const char *key);
|
||||
|
||||
#define ccnet_util_hex_to_sha1(hex, sha1) \
|
||||
ccnet_util_hex_to_rawdata((hex), (sha1), 20)
|
||||
|
||||
int
|
||||
ccnet_util_hex_to_rawdata (const char *hex_str,
|
||||
unsigned char *rawdata,
|
||||
int n_bytes);
|
||||
|
||||
#ifdef WIN32
|
||||
int ccnet_util_inet_pton(int af, const char *src, void *dst);
|
||||
#else
|
||||
#define ccnet_util_inet_pton inet_pton
|
||||
#endif
|
||||
|
||||
#endif
|
160
lib/mainloop.c
Normal file
160
lib/mainloop.c
Normal file
@ -0,0 +1,160 @@
|
||||
/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
|
||||
|
||||
#include "include.h"
|
||||
#include <ccnet.h>
|
||||
|
||||
#if defined(__FreeBSD__) || defined(__NetBSD__) || defined(__OpenBSD__)
|
||||
#include <event2/event.h>
|
||||
#include <event2/event_compat.h>
|
||||
#include <event2/event_struct.h>
|
||||
#else
|
||||
#include <event.h>
|
||||
#endif
|
||||
|
||||
static int
|
||||
cmdrsp_cb (const char *code, char *content, int clen, void *data)
|
||||
{
|
||||
RegisterServiceCB cb = data;
|
||||
|
||||
if (clen != 0)
|
||||
ccnet_debug ("Receive cmd response {\n%s}\n", content);
|
||||
else
|
||||
ccnet_debug ("Receive cmd response null\n");
|
||||
|
||||
if (cb) {
|
||||
if (memcmp (SC_SERV_EXISTED, code, 3) == 0)
|
||||
cb (FALSE);
|
||||
else
|
||||
cb (TRUE);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
ccnet_register_service (CcnetClient *client,
|
||||
const char *service,
|
||||
const char *group,
|
||||
GType proc_type,
|
||||
RegisterServiceCB cb)
|
||||
{
|
||||
char buf[512];
|
||||
g_return_if_fail (group);
|
||||
|
||||
ccnet_proc_factory_register_processor (client->proc_factory,
|
||||
service,
|
||||
proc_type);
|
||||
snprintf (buf, 512, "register-service %s %s", service, group);
|
||||
ccnet_send_command (client, buf, cmdrsp_cb, cb);
|
||||
}
|
||||
|
||||
gboolean
|
||||
ccnet_register_service_sync (CcnetClient *client,
|
||||
const char *service,
|
||||
const char *group)
|
||||
{
|
||||
char buf[512];
|
||||
GError *error = NULL;
|
||||
|
||||
snprintf (buf, 512, "register-service %s %s", service, group);
|
||||
ccnet_client_send_cmd (client, buf, &error);
|
||||
if (error) {
|
||||
ccnet_warning ("Bad response for register service %s: %d %s",
|
||||
service, error->code, error->message);
|
||||
return FALSE;
|
||||
}
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static void read_cb (evutil_socket_t fd, short event, void *vclient)
|
||||
{
|
||||
CcnetClient *client = vclient;
|
||||
|
||||
if (ccnet_client_read_input (client) <= 0) {
|
||||
ccnet_client_disconnect_daemon (client);
|
||||
exit (1);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Inititialize ccnet client structure, connect daemon and initialize
|
||||
* event loop.
|
||||
*/
|
||||
CcnetClient *
|
||||
ccnet_init (const char *central_config_dir, const char *confdir)
|
||||
{
|
||||
CcnetClient *client;
|
||||
|
||||
client = ccnet_client_new ();
|
||||
if ( (ccnet_client_load_confdir(client, central_config_dir, confdir)) < 0 ) {
|
||||
ccnet_warning ("Read config dir error\n");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
if (ccnet_client_connect_daemon (client, CCNET_CLIENT_ASYNC) < 0) {
|
||||
ccnet_warning ("Connect to ccnet daemon error\n");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
ccnet_client_run_synchronizer (client);
|
||||
|
||||
event_init ();
|
||||
|
||||
return client;
|
||||
}
|
||||
|
||||
void
|
||||
ccnet_main (CcnetClient *client)
|
||||
{
|
||||
struct event ev;
|
||||
|
||||
event_set (&ev, client->connfd, EV_READ | EV_PERSIST, read_cb, client);
|
||||
event_add (&ev, NULL);
|
||||
|
||||
event_dispatch ();
|
||||
}
|
||||
|
||||
void ccnet_send_command (CcnetClient *client, const char *command,
|
||||
SendcmdProcRcvrspCallback cmd_cb, void *cbdata)
|
||||
{
|
||||
CcnetSendcmdProc *sendcmd_proc = (CcnetSendcmdProc *)
|
||||
ccnet_proc_factory_create_master_processor (client->proc_factory,
|
||||
"send-cmd");
|
||||
ccnet_sendcmd_proc_set_rcvrsp_cb (sendcmd_proc, cmd_cb, cbdata);
|
||||
ccnet_processor_start (CCNET_PROCESSOR(sendcmd_proc), 0, NULL);
|
||||
ccnet_sendcmd_proc_send_command (sendcmd_proc, command);
|
||||
}
|
||||
|
||||
/* add-peer [--id <peer-id>] [--addr <peer-addr:port>]
|
||||
*/
|
||||
void ccnet_add_peer (CcnetClient *client, const char *id, const char *addr)
|
||||
{
|
||||
char buf[256];
|
||||
if (id == NULL || strlen(id) != 40 || addr == NULL)
|
||||
return;
|
||||
|
||||
snprintf (buf, 256, "add-peer --id %s --addr %s", id, addr);
|
||||
ccnet_send_command (client, buf, NULL, NULL);
|
||||
}
|
||||
|
||||
void ccnet_connect_peer (CcnetClient *client, const char *id)
|
||||
{
|
||||
char buf[256];
|
||||
if (id == NULL || strlen(id) != 40)
|
||||
return;
|
||||
|
||||
snprintf (buf, 256, "connect %s", id);
|
||||
ccnet_send_command (client, buf, NULL, NULL);
|
||||
}
|
||||
|
||||
void ccnet_disconnect_peer (CcnetClient *client, const char *id)
|
||||
{
|
||||
char buf[256];
|
||||
if (id == NULL || strlen(id) != 40)
|
||||
return;
|
||||
|
||||
snprintf (buf, 256, "disconnect %s", id);
|
||||
ccnet_send_command (client, buf, NULL, NULL);
|
||||
}
|
86
lib/marshal.c
Normal file
86
lib/marshal.c
Normal file
@ -0,0 +1,86 @@
|
||||
|
||||
#include <glib-object.h>
|
||||
|
||||
|
||||
#ifdef G_ENABLE_DEBUG
|
||||
#define g_marshal_value_peek_boolean(v) g_value_get_boolean (v)
|
||||
#define g_marshal_value_peek_char(v) g_value_get_char (v)
|
||||
#define g_marshal_value_peek_uchar(v) g_value_get_uchar (v)
|
||||
#define g_marshal_value_peek_int(v) g_value_get_int (v)
|
||||
#define g_marshal_value_peek_uint(v) g_value_get_uint (v)
|
||||
#define g_marshal_value_peek_long(v) g_value_get_long (v)
|
||||
#define g_marshal_value_peek_ulong(v) g_value_get_ulong (v)
|
||||
#define g_marshal_value_peek_int64(v) g_value_get_int64 (v)
|
||||
#define g_marshal_value_peek_uint64(v) g_value_get_uint64 (v)
|
||||
#define g_marshal_value_peek_enum(v) g_value_get_enum (v)
|
||||
#define g_marshal_value_peek_flags(v) g_value_get_flags (v)
|
||||
#define g_marshal_value_peek_float(v) g_value_get_float (v)
|
||||
#define g_marshal_value_peek_double(v) g_value_get_double (v)
|
||||
#define g_marshal_value_peek_string(v) (char*) g_value_get_string (v)
|
||||
#define g_marshal_value_peek_param(v) g_value_get_param (v)
|
||||
#define g_marshal_value_peek_boxed(v) g_value_get_boxed (v)
|
||||
#define g_marshal_value_peek_pointer(v) g_value_get_pointer (v)
|
||||
#define g_marshal_value_peek_object(v) g_value_get_object (v)
|
||||
#else /* !G_ENABLE_DEBUG */
|
||||
/* WARNING: This code accesses GValues directly, which is UNSUPPORTED API.
|
||||
* Do not access GValues directly in your code. Instead, use the
|
||||
* g_value_get_*() functions
|
||||
*/
|
||||
#define g_marshal_value_peek_boolean(v) (v)->data[0].v_int
|
||||
#define g_marshal_value_peek_char(v) (v)->data[0].v_int
|
||||
#define g_marshal_value_peek_uchar(v) (v)->data[0].v_uint
|
||||
#define g_marshal_value_peek_int(v) (v)->data[0].v_int
|
||||
#define g_marshal_value_peek_uint(v) (v)->data[0].v_uint
|
||||
#define g_marshal_value_peek_long(v) (v)->data[0].v_long
|
||||
#define g_marshal_value_peek_ulong(v) (v)->data[0].v_ulong
|
||||
#define g_marshal_value_peek_int64(v) (v)->data[0].v_int64
|
||||
#define g_marshal_value_peek_uint64(v) (v)->data[0].v_uint64
|
||||
#define g_marshal_value_peek_enum(v) (v)->data[0].v_long
|
||||
#define g_marshal_value_peek_flags(v) (v)->data[0].v_ulong
|
||||
#define g_marshal_value_peek_float(v) (v)->data[0].v_float
|
||||
#define g_marshal_value_peek_double(v) (v)->data[0].v_double
|
||||
#define g_marshal_value_peek_string(v) (v)->data[0].v_pointer
|
||||
#define g_marshal_value_peek_param(v) (v)->data[0].v_pointer
|
||||
#define g_marshal_value_peek_boxed(v) (v)->data[0].v_pointer
|
||||
#define g_marshal_value_peek_pointer(v) (v)->data[0].v_pointer
|
||||
#define g_marshal_value_peek_object(v) (v)->data[0].v_pointer
|
||||
#endif /* !G_ENABLE_DEBUG */
|
||||
|
||||
|
||||
/* VOID:POINTER,STRING (marshal.list:1) */
|
||||
void
|
||||
g_cclosure_user_marshal_VOID__POINTER_STRING (GClosure *closure,
|
||||
GValue *return_value G_GNUC_UNUSED,
|
||||
guint n_param_values,
|
||||
const GValue *param_values,
|
||||
gpointer invocation_hint G_GNUC_UNUSED,
|
||||
gpointer marshal_data)
|
||||
{
|
||||
typedef void (*GMarshalFunc_VOID__POINTER_STRING) (gpointer data1,
|
||||
gpointer arg_1,
|
||||
gpointer arg_2,
|
||||
gpointer data2);
|
||||
register GMarshalFunc_VOID__POINTER_STRING callback;
|
||||
register GCClosure *cc = (GCClosure*) closure;
|
||||
register gpointer data1, data2;
|
||||
|
||||
g_return_if_fail (n_param_values == 3);
|
||||
|
||||
if (G_CCLOSURE_SWAP_DATA (closure))
|
||||
{
|
||||
data1 = closure->data;
|
||||
data2 = g_value_peek_pointer (param_values + 0);
|
||||
}
|
||||
else
|
||||
{
|
||||
data1 = g_value_peek_pointer (param_values + 0);
|
||||
data2 = closure->data;
|
||||
}
|
||||
callback = (GMarshalFunc_VOID__POINTER_STRING) (marshal_data ? marshal_data : cc->callback);
|
||||
|
||||
callback (data1,
|
||||
g_marshal_value_peek_pointer (param_values + 1),
|
||||
g_marshal_value_peek_string (param_values + 2),
|
||||
data2);
|
||||
}
|
||||
|
20
lib/marshal.h
Normal file
20
lib/marshal.h
Normal file
@ -0,0 +1,20 @@
|
||||
|
||||
#ifndef __g_cclosure_user_marshal_MARSHAL_H__
|
||||
#define __g_cclosure_user_marshal_MARSHAL_H__
|
||||
|
||||
#include <glib-object.h>
|
||||
|
||||
G_BEGIN_DECLS
|
||||
|
||||
/* VOID:POINTER,STRING (marshal.list:1) */
|
||||
extern void g_cclosure_user_marshal_VOID__POINTER_STRING (GClosure *closure,
|
||||
GValue *return_value,
|
||||
guint n_param_values,
|
||||
const GValue *param_values,
|
||||
gpointer invocation_hint,
|
||||
gpointer marshal_data);
|
||||
|
||||
G_END_DECLS
|
||||
|
||||
#endif /* __g_cclosure_user_marshal_MARSHAL_H__ */
|
||||
|
1
lib/marshal.list
Normal file
1
lib/marshal.list
Normal file
@ -0,0 +1 @@
|
||||
VOID:POINTER,STRING
|
269
lib/message.c
Normal file
269
lib/message.c
Normal file
@ -0,0 +1,269 @@
|
||||
/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
|
||||
|
||||
#include "include.h"
|
||||
|
||||
#include "message.h"
|
||||
|
||||
enum {
|
||||
P_ID = 1,
|
||||
P_FLAGS,
|
||||
P_FROM,
|
||||
P_TO,
|
||||
P_CTIME,
|
||||
P_RTIME,
|
||||
P_APP,
|
||||
P_BODY,
|
||||
};
|
||||
|
||||
G_DEFINE_TYPE (CcnetMessage, ccnet_message, G_TYPE_OBJECT);
|
||||
|
||||
|
||||
static void
|
||||
set_property (GObject *object, guint property_id,
|
||||
const GValue *v, GParamSpec *pspec)
|
||||
{
|
||||
/* CcnetMessage *message = CCNET_MESSAGE (object); */
|
||||
|
||||
switch (property_id) {
|
||||
default:
|
||||
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
get_property (GObject *object, guint property_id,
|
||||
GValue *v, GParamSpec *pspec)
|
||||
{
|
||||
CcnetMessage *message = CCNET_MESSAGE (object);
|
||||
|
||||
switch (property_id) {
|
||||
case P_ID:
|
||||
g_value_set_string (v, message->id);
|
||||
break;
|
||||
case P_FLAGS:
|
||||
g_value_set_uint (v, message->flags);
|
||||
break;
|
||||
case P_FROM:
|
||||
g_value_set_string (v, message->from);
|
||||
break;
|
||||
case P_TO:
|
||||
g_value_set_string (v, message->to);
|
||||
break;
|
||||
case P_CTIME:
|
||||
g_value_set_uint (v, message->ctime);
|
||||
break;
|
||||
case P_RTIME:
|
||||
g_value_set_uint (v, message->rtime);
|
||||
break;
|
||||
case P_APP:
|
||||
g_value_set_string (v, message->app);
|
||||
break;
|
||||
case P_BODY:
|
||||
g_value_set_string (v, message->body);
|
||||
break;
|
||||
default:
|
||||
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void finalize (GObject *object)
|
||||
{
|
||||
CcnetMessage *message = (CcnetMessage *)object;
|
||||
g_free (message->app);
|
||||
g_free (message->id);
|
||||
g_free (message->body);
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
ccnet_message_class_init (CcnetMessageClass *klass)
|
||||
{
|
||||
GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
|
||||
|
||||
gobject_class->set_property = set_property;
|
||||
gobject_class->get_property = get_property;
|
||||
gobject_class->finalize = finalize;
|
||||
|
||||
|
||||
g_object_class_install_property (gobject_class, P_ID,
|
||||
g_param_spec_string ("id", NULL, "ID",
|
||||
NULL, G_PARAM_READWRITE));
|
||||
|
||||
g_object_class_install_property (gobject_class, P_FLAGS,
|
||||
g_param_spec_uint ("flags", NULL, "Flags",
|
||||
0, UINT_MAX, 0, G_PARAM_READWRITE));
|
||||
|
||||
g_object_class_install_property (gobject_class, P_FROM,
|
||||
g_param_spec_string ("from_id", NULL, "From ID",
|
||||
NULL, G_PARAM_READWRITE));
|
||||
|
||||
g_object_class_install_property (gobject_class, P_TO,
|
||||
g_param_spec_string ("to_id", NULL, "To ID",
|
||||
NULL, G_PARAM_READWRITE));
|
||||
|
||||
g_object_class_install_property (gobject_class, P_CTIME,
|
||||
g_param_spec_uint ("ctime", NULL, "Creation Time",
|
||||
0, UINT_MAX, 0, G_PARAM_READWRITE));
|
||||
|
||||
g_object_class_install_property (gobject_class, P_RTIME,
|
||||
g_param_spec_uint ("rtime", NULL, "Receiving Time",
|
||||
0, UINT_MAX, 0, G_PARAM_READWRITE));
|
||||
|
||||
g_object_class_install_property (gobject_class, P_APP,
|
||||
g_param_spec_string ("app", NULL, "application",
|
||||
NULL, G_PARAM_READWRITE));
|
||||
|
||||
g_object_class_install_property (gobject_class, P_BODY,
|
||||
g_param_spec_string ("body", NULL, "message body",
|
||||
NULL, G_PARAM_READWRITE));
|
||||
|
||||
}
|
||||
|
||||
static void
|
||||
ccnet_message_init (CcnetMessage *message)
|
||||
{
|
||||
}
|
||||
|
||||
CcnetMessage *
|
||||
ccnet_message_new_full (const char *from,
|
||||
const char *to,
|
||||
const char *app,
|
||||
const char *body,
|
||||
time_t ctime,
|
||||
time_t rcv_time,
|
||||
const char *msg_id,
|
||||
int flags)
|
||||
{
|
||||
CcnetMessage *message;
|
||||
|
||||
if (!from || !to || !app)
|
||||
return NULL;
|
||||
|
||||
message = g_object_new (CCNET_TYPE_MESSAGE, NULL);
|
||||
|
||||
message->flags = flags;
|
||||
memcpy (message->from, from, 40);
|
||||
message->from[40] = '\0';
|
||||
memcpy (message->to, to, 40);
|
||||
message->to[40] = '\0';
|
||||
message->app = g_strdup(app);
|
||||
message->body = g_strdup(body);
|
||||
message->ctime = (ctime ? ctime : time(NULL));
|
||||
message->rtime = rcv_time;
|
||||
message->id = (msg_id ? g_strdup (msg_id) : ccnet_util_gen_uuid());
|
||||
|
||||
return message;
|
||||
}
|
||||
|
||||
CcnetMessage *
|
||||
ccnet_message_new (const char *from,
|
||||
const char *to,
|
||||
const char *app,
|
||||
const char *body,
|
||||
int flags)
|
||||
{
|
||||
return ccnet_message_new_full (from, to, app, body, 0, 0, NULL, flags);
|
||||
}
|
||||
|
||||
void
|
||||
ccnet_message_free (CcnetMessage *message)
|
||||
{
|
||||
g_object_unref (message);
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
ccnet_message_to_string_buf (CcnetMessage *msg, GString *buf)
|
||||
{
|
||||
g_string_printf (buf, "%d %s %s %s %d %d %s %s", msg->flags,
|
||||
msg->from,
|
||||
msg->to,
|
||||
msg->id,
|
||||
(int)msg->ctime,
|
||||
(int)msg->rtime,
|
||||
msg->app,
|
||||
msg->body);
|
||||
}
|
||||
|
||||
CcnetMessage *
|
||||
ccnet_message_from_string (char *buf, int len)
|
||||
{
|
||||
char flags;
|
||||
int is_group_msg;
|
||||
char *from_id, *to_id, *msg_id, *body, *p, *end, *app;
|
||||
int ctime, rcv_time = 0;
|
||||
CcnetMessage *message;
|
||||
|
||||
g_return_val_if_fail (buf[len-1] == '\0', NULL);
|
||||
|
||||
p = buf + 1;
|
||||
while (*p != ' ' && *p) ++p;
|
||||
if (*p != ' ')
|
||||
goto error;
|
||||
*p = '\0';
|
||||
flags = atoi (buf);
|
||||
is_group_msg = flags & FLAG_TO_GROUP;
|
||||
|
||||
from_id = ++p;
|
||||
p += 40;
|
||||
g_return_val_if_fail (*p == ' ', NULL);
|
||||
*p = '\0';
|
||||
|
||||
to_id = ++p;
|
||||
if (!is_group_msg)
|
||||
p += 40; /* SHA-1 */
|
||||
else
|
||||
p += 36; /* UUID */
|
||||
g_return_val_if_fail (*p == ' ', NULL);
|
||||
*p = '\0';
|
||||
|
||||
msg_id = ++p;
|
||||
p += 36;
|
||||
g_return_val_if_fail (*p == ' ', NULL);
|
||||
*p++ = '\0';
|
||||
|
||||
end = strchr (p, ' ');
|
||||
*end = '\0';
|
||||
ctime = atoi (p);
|
||||
|
||||
p = end + 1;
|
||||
end = strchr (p, ' ');
|
||||
*end = '\0';
|
||||
rcv_time = atoi (p);
|
||||
|
||||
p = app = end + 1;
|
||||
while (*p != ' ' && *p) ++p;
|
||||
if (*p != ' ')
|
||||
goto error;
|
||||
*p = '\0';
|
||||
body = p + 1;
|
||||
|
||||
message = ccnet_message_new_full (from_id, to_id,
|
||||
app, body,
|
||||
ctime, rcv_time,
|
||||
msg_id, flags);
|
||||
return message;
|
||||
|
||||
error:
|
||||
return NULL;
|
||||
}
|
||||
|
||||
gboolean
|
||||
ccnet_message_is_to_group(CcnetMessage *msg)
|
||||
{
|
||||
return msg->flags & FLAG_TO_GROUP;
|
||||
}
|
||||
|
||||
void
|
||||
ccnet_message_body_take (CcnetMessage *msg, char *body)
|
||||
{
|
||||
msg->body = body;
|
||||
}
|
||||
|
||||
void
|
||||
ccnet_message_body_dup (CcnetMessage *msg, char *body)
|
||||
{
|
||||
msg->body = g_strdup (body);
|
||||
}
|
151
lib/mqclient-proc.c
Normal file
151
lib/mqclient-proc.c
Normal file
@ -0,0 +1,151 @@
|
||||
/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
|
||||
|
||||
#include <config.h>
|
||||
|
||||
#include <stdio.h>
|
||||
|
||||
#include "ccnet-client.h"
|
||||
#include "mqclient-proc.h"
|
||||
|
||||
#define SC_MSG "300"
|
||||
#define SC_UNSUBSCRIBE "301"
|
||||
|
||||
enum {
|
||||
INIT,
|
||||
REQUEST_SENT,
|
||||
READY
|
||||
};
|
||||
|
||||
enum {
|
||||
RECV_MSG_SIG,
|
||||
LAST_SIGNAL
|
||||
};
|
||||
|
||||
static guint signals[LAST_SIGNAL] = { 0 };
|
||||
|
||||
static int mq_client_start (CcnetProcessor *processor, int argc, char **argv);
|
||||
|
||||
static void handle_response (CcnetProcessor *processor,
|
||||
char *code, char *code_msg,
|
||||
char *content, int clen);
|
||||
|
||||
|
||||
G_DEFINE_TYPE (CcnetMqclientProc, ccnet_mqclient_proc, CCNET_TYPE_PROCESSOR)
|
||||
|
||||
static void
|
||||
ccnet_mqclient_proc_class_init (CcnetMqclientProcClass *klass)
|
||||
{
|
||||
CcnetProcessorClass *proc_class = CCNET_PROCESSOR_CLASS (klass);
|
||||
|
||||
proc_class->start = mq_client_start;
|
||||
proc_class->handle_response = handle_response;
|
||||
proc_class->name = "mqclient-proc";
|
||||
|
||||
signals[RECV_MSG_SIG] =
|
||||
g_signal_new ("recv-msg", CCNET_TYPE_MQCLIENT_PROC ,
|
||||
G_SIGNAL_RUN_LAST,
|
||||
0,
|
||||
NULL, NULL, /* no accumulator */
|
||||
g_cclosure_marshal_VOID__OBJECT,
|
||||
G_TYPE_NONE, 1, CCNET_TYPE_MESSAGE);
|
||||
}
|
||||
|
||||
static void
|
||||
ccnet_mqclient_proc_init (CcnetMqclientProc *processor)
|
||||
{
|
||||
CCNET_PROCESSOR (processor)->state = INIT;
|
||||
}
|
||||
|
||||
static int
|
||||
mq_client_start (CcnetProcessor *processor, int argc, char **argv)
|
||||
{
|
||||
GString *buf;
|
||||
int i;
|
||||
|
||||
buf = g_string_new ("mq-server");
|
||||
for (i = 0; i < argc; ++i) {
|
||||
g_string_append (buf, " ");
|
||||
g_string_append (buf, argv[i]);
|
||||
}
|
||||
|
||||
ccnet_client_send_request (processor->session, REQUEST_ID (processor->id),
|
||||
buf->str);
|
||||
processor->state = REQUEST_SENT;
|
||||
|
||||
g_string_free (buf, TRUE);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void handle_response (CcnetProcessor *processor,
|
||||
char *code, char *code_msg,
|
||||
char *content, int clen)
|
||||
{
|
||||
CcnetMqclientProc *proc = (CcnetMqclientProc *) processor;
|
||||
CcnetMessage *msg;
|
||||
|
||||
switch (processor->state) {
|
||||
case REQUEST_SENT:
|
||||
if (code[0] != '2') {
|
||||
g_warning ("receive bad response: %s %s\n", code, code_msg);
|
||||
ccnet_processor_done (processor, FALSE);
|
||||
return;
|
||||
}
|
||||
|
||||
processor->state = READY;
|
||||
break;
|
||||
case READY:
|
||||
if (code[0] != '2' && code[0] != '3') {
|
||||
g_warning ("receive bad response: %s %s\n", code, code_msg);
|
||||
return;
|
||||
}
|
||||
|
||||
/* message notification. */
|
||||
if (code[0] == '3' && code[2] == '0') {
|
||||
msg = ccnet_message_from_string (content, clen);
|
||||
if (!msg) {
|
||||
g_warning ("Wrong message format.\n");
|
||||
ccnet_processor_done (processor, FALSE);
|
||||
break;
|
||||
}
|
||||
if (proc->message_got_cb)
|
||||
proc->message_got_cb (msg, proc->cb_data);
|
||||
g_signal_emit (proc, signals[RECV_MSG_SIG], 0, msg);
|
||||
ccnet_message_free (msg);
|
||||
}
|
||||
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void ccnet_mqclient_proc_set_message_got_cb (CcnetMqclientProc *processor,
|
||||
MessageGotCB callback,
|
||||
void *cb_data)
|
||||
{
|
||||
processor->message_got_cb = callback;
|
||||
processor->cb_data = cb_data;
|
||||
}
|
||||
|
||||
void ccnet_mqclient_proc_put_message (CcnetMqclientProc *proc,
|
||||
CcnetMessage *message)
|
||||
{
|
||||
CcnetProcessor *processor = (CcnetProcessor *) proc;
|
||||
GString *msg_buf;
|
||||
|
||||
msg_buf = g_string_new (NULL);
|
||||
|
||||
ccnet_message_to_string_buf (message, msg_buf);
|
||||
ccnet_client_send_update (processor->session, UPDATE_ID(processor->id),
|
||||
SC_MSG, NULL, msg_buf->str, msg_buf->len+1);
|
||||
g_string_free (msg_buf, TRUE);
|
||||
}
|
||||
|
||||
void ccnet_mqclient_proc_unsubscribe_apps (CcnetMqclientProc *proc)
|
||||
{
|
||||
CcnetProcessor *processor = (CcnetProcessor *) proc;
|
||||
|
||||
ccnet_client_send_update (processor->session, UPDATE_ID(processor->id),
|
||||
SC_UNSUBSCRIBE, NULL, NULL, 0);
|
||||
ccnet_processor_done (processor, TRUE);
|
||||
}
|
468
lib/net.c
Normal file
468
lib/net.c
Normal file
@ -0,0 +1,468 @@
|
||||
/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
|
||||
|
||||
#ifdef WIN32
|
||||
#define WINVER 0x0501
|
||||
#endif
|
||||
#include "include.h"
|
||||
|
||||
#include <unistd.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
|
||||
#ifdef WIN32
|
||||
#include <inttypes.h>
|
||||
#include <winsock2.h>
|
||||
#include <ctype.h>
|
||||
#include <ws2tcpip.h>
|
||||
#define UNUSED
|
||||
#else
|
||||
#include <sys/types.h>
|
||||
#include <sys/socket.h>
|
||||
#include <sys/ioctl.h>
|
||||
#include <netinet/in.h>
|
||||
#include <arpa/inet.h>
|
||||
#include <netdb.h>
|
||||
#include <sys/un.h>
|
||||
#include <net/if.h>
|
||||
#include <netinet/tcp.h>
|
||||
#endif
|
||||
|
||||
#include <fcntl.h>
|
||||
|
||||
#include "net.h"
|
||||
|
||||
|
||||
#ifdef WIN32
|
||||
|
||||
#ifndef inet_aton
|
||||
int inet_aton(const char *string, struct in_addr *addr)
|
||||
{
|
||||
addr->s_addr = inet_addr(string);
|
||||
if (addr->s_addr != -1 || strcmp("255.255.255.255", string) == 0)
|
||||
return 1;
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif //WIN32
|
||||
|
||||
int
|
||||
ccnet_netSetTOS (evutil_socket_t s, int tos)
|
||||
{
|
||||
#ifdef IP_TOS
|
||||
return setsockopt( s, IPPROTO_IP, IP_TOS, (char*)&tos, sizeof( tos ) );
|
||||
#else
|
||||
return 0;
|
||||
#endif
|
||||
}
|
||||
|
||||
static evutil_socket_t
|
||||
makeSocketNonBlocking (evutil_socket_t fd)
|
||||
{
|
||||
if (fd >= 0)
|
||||
{
|
||||
if (evutil_make_socket_nonblocking(fd))
|
||||
{
|
||||
ccnet_warning ("Couldn't make socket nonblock: %s",
|
||||
evutil_socket_error_to_string(EVUTIL_SOCKET_ERROR()));
|
||||
evutil_closesocket(fd);
|
||||
fd = -1;
|
||||
}
|
||||
}
|
||||
return fd;
|
||||
}
|
||||
|
||||
static evutil_socket_t
|
||||
createSocket (int family, int nonblock)
|
||||
{
|
||||
evutil_socket_t fd;
|
||||
int ret;
|
||||
|
||||
fd = socket (family, SOCK_STREAM, 0);
|
||||
|
||||
if (fd < 0) {
|
||||
ccnet_warning("create Socket failed %d\n", fd);
|
||||
} else if (nonblock) {
|
||||
int nodelay = 1;
|
||||
|
||||
fd = makeSocketNonBlocking( fd );
|
||||
|
||||
ret = setsockopt (fd, IPPROTO_TCP, TCP_NODELAY,
|
||||
(char *)&nodelay, sizeof(nodelay));
|
||||
if (ret < 0) {
|
||||
ccnet_warning("setsockopt failed\n");
|
||||
evutil_closesocket(fd);
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
return fd;
|
||||
}
|
||||
|
||||
evutil_socket_t
|
||||
ccnet_net_open_tcp (const struct sockaddr *sa, int nonblock)
|
||||
{
|
||||
evutil_socket_t s;
|
||||
int sa_len;
|
||||
|
||||
if( (s = createSocket(sa->sa_family, nonblock)) < 0 )
|
||||
return -1;
|
||||
|
||||
#ifndef WIN32
|
||||
if (sa->sa_family == AF_INET)
|
||||
sa_len = sizeof (struct sockaddr_in);
|
||||
else
|
||||
sa_len = sizeof (struct sockaddr_in6);
|
||||
#else
|
||||
if (sa->sa_family == AF_INET)
|
||||
sa_len = sizeof (struct sockaddr_in);
|
||||
else
|
||||
return -1;
|
||||
#endif
|
||||
|
||||
|
||||
if( (connect(s, sa, sa_len) < 0)
|
||||
#ifdef WIN32
|
||||
&& (sockerrno != WSAEWOULDBLOCK)
|
||||
#endif
|
||||
&& (sockerrno != EINPROGRESS) )
|
||||
{
|
||||
evutil_closesocket(s);
|
||||
s = -1;
|
||||
}
|
||||
|
||||
return s;
|
||||
}
|
||||
|
||||
evutil_socket_t
|
||||
ccnet_net_bind_tcp (int port, int nonblock)
|
||||
{
|
||||
#ifndef WIN32
|
||||
int sockfd, n;
|
||||
struct addrinfo hints, *res, *ressave;
|
||||
char buf[10];
|
||||
|
||||
memset (&hints, 0,sizeof (struct addrinfo));
|
||||
hints.ai_flags = AI_PASSIVE;
|
||||
hints.ai_family = AF_UNSPEC;
|
||||
hints.ai_socktype = SOCK_STREAM;
|
||||
|
||||
snprintf (buf, sizeof(buf), "%d", port);
|
||||
|
||||
if ( (n = getaddrinfo(NULL, buf, &hints, &res) ) != 0) {
|
||||
ccnet_warning ("getaddrinfo fails: %s\n", gai_strerror(n));
|
||||
return -1;
|
||||
}
|
||||
|
||||
ressave = res;
|
||||
|
||||
do {
|
||||
int on = 1;
|
||||
|
||||
sockfd = socket(res->ai_family, res->ai_socktype, res->ai_protocol);
|
||||
if (sockfd < 0)
|
||||
continue; /* error - try next one */
|
||||
|
||||
if (setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)) < 0) {
|
||||
ccnet_warning ("setsockopt of SO_REUSEADDR error\n");
|
||||
continue;
|
||||
}
|
||||
|
||||
if (nonblock)
|
||||
sockfd = makeSocketNonBlocking (sockfd);
|
||||
if (sockfd < 0)
|
||||
continue; /* error - try next one */
|
||||
|
||||
if (bind(sockfd, res->ai_addr, res->ai_addrlen) == 0)
|
||||
break; /* success */
|
||||
|
||||
close(sockfd); /* bind error - close and try next one */
|
||||
} while ( (res = res->ai_next) != NULL);
|
||||
|
||||
freeaddrinfo (ressave);
|
||||
|
||||
if (res == NULL) {
|
||||
ccnet_warning ("bind fails: %s\n", strerror(errno));
|
||||
return -1;
|
||||
}
|
||||
|
||||
return sockfd;
|
||||
#else
|
||||
|
||||
evutil_socket_t s;
|
||||
struct sockaddr_in sock;
|
||||
const int type = AF_INET;
|
||||
#if defined( SO_REUSEADDR ) || defined( SO_REUSEPORT )
|
||||
int optval;
|
||||
#endif
|
||||
|
||||
if ((s = createSocket(type, nonblock)) < 0)
|
||||
return -1;
|
||||
|
||||
optval = 1;
|
||||
setsockopt (s, SOL_SOCKET, SO_REUSEADDR, (char*)&optval, sizeof(optval));
|
||||
|
||||
memset(&sock, 0, sizeof(sock));
|
||||
sock.sin_family = AF_INET;
|
||||
sock.sin_addr.s_addr = INADDR_ANY;
|
||||
sock.sin_port = htons(port);
|
||||
|
||||
if ( bind(s, (struct sockaddr *)&sock, sizeof(struct sockaddr_in)) < 0)
|
||||
{
|
||||
ccnet_warning ("bind fails: %s\n", strerror(errno));
|
||||
evutil_closesocket (s);
|
||||
return -1;
|
||||
}
|
||||
if (nonblock)
|
||||
s = makeSocketNonBlocking (s);
|
||||
|
||||
return s;
|
||||
#endif
|
||||
}
|
||||
|
||||
evutil_socket_t
|
||||
ccnet_net_accept (evutil_socket_t b, struct sockaddr_storage *cliaddr,
|
||||
socklen_t *len, int nonblock)
|
||||
{
|
||||
evutil_socket_t s;
|
||||
/* int nodelay = 1; */
|
||||
|
||||
s = accept (b, (struct sockaddr *)cliaddr, len);
|
||||
|
||||
/* setsockopt (s, IPPROTO_TCP, TCP_NODELAY, &nodelay, sizeof(nodelay)); */
|
||||
if (nonblock)
|
||||
makeSocketNonBlocking(s);
|
||||
|
||||
return s;
|
||||
}
|
||||
|
||||
|
||||
evutil_socket_t
|
||||
ccnet_net_bind_v4 (const char *ipaddr, int *port)
|
||||
{
|
||||
evutil_socket_t sockfd;
|
||||
struct sockaddr_in addr;
|
||||
int on = 1;
|
||||
|
||||
sockfd = socket (AF_INET, SOCK_STREAM, 0);
|
||||
if (sockfd < 0) {
|
||||
ccnet_warning("create socket failed: %s\n", strerror(errno));
|
||||
exit(-1);
|
||||
}
|
||||
|
||||
memset (&addr, 0, sizeof (struct sockaddr_in));
|
||||
addr.sin_family = AF_INET;
|
||||
if (inet_aton(ipaddr, &addr.sin_addr) == 0) {
|
||||
ccnet_warning ("Bad ip address %s\n", ipaddr);
|
||||
return -1;
|
||||
}
|
||||
addr.sin_port = htons (*port);
|
||||
|
||||
if (setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, (char *)&on, sizeof(on)) < 0)
|
||||
{
|
||||
ccnet_warning ("setsockopt of SO_REUSEADDR error: %s\n",
|
||||
strerror(errno));
|
||||
return -1;
|
||||
}
|
||||
|
||||
if ( bind(sockfd, (struct sockaddr *)&addr, sizeof(addr)) < 0) {
|
||||
ccnet_warning ("Bind error: %s\n", strerror (errno));
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
||||
if (*port == 0) {
|
||||
struct sockaddr_storage ss;
|
||||
socklen_t len;
|
||||
|
||||
len = sizeof(ss);
|
||||
if (getsockname(sockfd, (struct sockaddr *)&ss, &len) < 0) {
|
||||
ccnet_warning ("getsockname error: %s\n", strerror(errno));
|
||||
return -1;
|
||||
}
|
||||
*port = sock_port ((struct sockaddr *)&ss);
|
||||
}
|
||||
|
||||
return sockfd;
|
||||
}
|
||||
|
||||
|
||||
|
||||
char *
|
||||
sock_ntop(const struct sockaddr *sa, socklen_t salen)
|
||||
{
|
||||
static char str[128]; /* Unix domain is largest */
|
||||
|
||||
switch (sa->sa_family) {
|
||||
case AF_INET: {
|
||||
struct sockaddr_in *sin = (struct sockaddr_in *) sa;
|
||||
|
||||
if (evutil_inet_ntop(AF_INET, &sin->sin_addr, str, sizeof(str)) == NULL)
|
||||
return(NULL);
|
||||
return(str);
|
||||
}
|
||||
|
||||
#ifdef IPv6
|
||||
case AF_INET6: {
|
||||
struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *) sa;
|
||||
|
||||
if (evutil_inet_ntop(AF_INET6, &sin6->sin6_addr, str, sizeof(str) - 1) == NULL)
|
||||
return(NULL);
|
||||
return (str);
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifndef WIN32
|
||||
#ifdef AF_UNIX
|
||||
case AF_UNIX: {
|
||||
struct sockaddr_un *unp = (struct sockaddr_un *) sa;
|
||||
|
||||
/* OK to have no pathname bound to the socket: happens on
|
||||
every connect() unless client calls bind() first. */
|
||||
if (unp->sun_path[0] == 0)
|
||||
strcpy(str, "(no pathname bound)");
|
||||
else
|
||||
snprintf(str, sizeof(str), "%s", unp->sun_path);
|
||||
return(str);
|
||||
}
|
||||
#endif
|
||||
#endif
|
||||
|
||||
default:
|
||||
snprintf(str, sizeof(str), "sock_ntop: unknown AF_xxx: %d, len %d",
|
||||
sa->sa_family, salen);
|
||||
return(str);
|
||||
}
|
||||
return (NULL);
|
||||
}
|
||||
|
||||
int
|
||||
sock_pton (const char *addr_str, uint16_t port, struct sockaddr_storage *sa)
|
||||
{
|
||||
struct sockaddr_in *saddr = (struct sockaddr_in *) sa;
|
||||
|
||||
#ifndef WIN32
|
||||
struct sockaddr_in6 *saddr6 = (struct sockaddr_in6 *) sa;
|
||||
#endif
|
||||
|
||||
if (evutil_inet_pton (AF_INET, addr_str, &saddr->sin_addr) == 1 ) {
|
||||
saddr->sin_family = AF_INET;
|
||||
saddr->sin_port = htons (port);
|
||||
return 0;
|
||||
}
|
||||
#ifndef WIN32
|
||||
else if (evutil_inet_pton (AF_INET6, addr_str, &saddr6->sin6_addr) == 1)
|
||||
{
|
||||
saddr6->sin6_family = AF_INET6;
|
||||
saddr6->sin6_port = htons (port);
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* return 1 if addr_str is a valid ipv4 or ipv6 address */
|
||||
int
|
||||
is_valid_ipaddr (const char *addr_str)
|
||||
{
|
||||
struct sockaddr_storage addr;
|
||||
if (!addr_str)
|
||||
return 0;
|
||||
if (sock_pton(addr_str, 0, &addr) < 0)
|
||||
return 0;
|
||||
return 1;
|
||||
}
|
||||
|
||||
uint16_t
|
||||
sock_port (const struct sockaddr *sa)
|
||||
{
|
||||
switch (sa->sa_family) {
|
||||
case AF_INET: {
|
||||
struct sockaddr_in *sin = (struct sockaddr_in *) sa;
|
||||
return ntohs(sin->sin_port);
|
||||
}
|
||||
#ifdef IPv6
|
||||
case AF_INET6: {
|
||||
struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *) sa;
|
||||
|
||||
return ntohs(sin6->sin6_port);
|
||||
}
|
||||
#endif
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
evutil_socket_t
|
||||
udp_client (const char *host, const char *serv,
|
||||
struct sockaddr **saptr, socklen_t *lenp)
|
||||
{
|
||||
evutil_socket_t sockfd;
|
||||
int n;
|
||||
struct addrinfo hints, *res, *ressave;
|
||||
|
||||
memset (&hints, 0, sizeof(struct addrinfo));
|
||||
hints.ai_family = AF_UNSPEC;
|
||||
hints.ai_socktype = SOCK_DGRAM;
|
||||
|
||||
if ((n = getaddrinfo(host, serv, &hints, &res)) != 0) {
|
||||
ccnet_warning ("udp_client error for %s, %s: %s",
|
||||
host, serv, gai_strerror(n));
|
||||
return -1;
|
||||
}
|
||||
ressave = res;
|
||||
|
||||
do {
|
||||
sockfd = socket(res->ai_family, res->ai_socktype, res->ai_protocol);
|
||||
if (sockfd >= 0)
|
||||
break; /* success */
|
||||
} while ( (res = res->ai_next) != NULL);
|
||||
|
||||
if (res == NULL) { /* errno set from final socket() */
|
||||
ccnet_warning ("udp_client error for %s, %s", host, serv);
|
||||
freeaddrinfo (ressave);
|
||||
return -1;
|
||||
}
|
||||
|
||||
*saptr = malloc(res->ai_addrlen);
|
||||
memcpy(*saptr, res->ai_addr, res->ai_addrlen);
|
||||
*lenp = res->ai_addrlen;
|
||||
|
||||
freeaddrinfo(ressave);
|
||||
|
||||
return (sockfd);
|
||||
}
|
||||
|
||||
|
||||
int
|
||||
family_to_level(int family)
|
||||
{
|
||||
switch (family) {
|
||||
case AF_INET:
|
||||
return IPPROTO_IP;
|
||||
#ifdef IPV6
|
||||
case AF_INET6:
|
||||
return IPPROTO_IPV6;
|
||||
#endif
|
||||
default:
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
int
|
||||
sockfd_to_family(evutil_socket_t sockfd)
|
||||
{
|
||||
struct sockaddr_storage ss;
|
||||
socklen_t len;
|
||||
|
||||
len = sizeof(ss);
|
||||
if (getsockname(sockfd, (struct sockaddr *) &ss, &len) < 0)
|
||||
return(-1);
|
||||
return(ss.ss_family);
|
||||
}
|
72
lib/net.h
Normal file
72
lib/net.h
Normal file
@ -0,0 +1,72 @@
|
||||
|
||||
#ifndef CCNET_NET_H
|
||||
#define CCNET_NET_H
|
||||
|
||||
#ifdef WIN32
|
||||
#include <inttypes.h>
|
||||
#include <winsock2.h>
|
||||
#include <ws2tcpip.h>
|
||||
typedef int socklen_t;
|
||||
#define UNUSED
|
||||
#else
|
||||
#include <sys/types.h>
|
||||
#include <sys/socket.h>
|
||||
#include <netinet/in.h>
|
||||
#include <arpa/inet.h>
|
||||
#include <netdb.h>
|
||||
#include <sys/un.h>
|
||||
#include <net/if.h>
|
||||
#include <netinet/tcp.h>
|
||||
#endif
|
||||
|
||||
#if defined(__FreeBSD__) || defined(__NetBSD__) || defined(__OpenBSD__)
|
||||
#include <event2/util.h>
|
||||
#else
|
||||
#include <evutil.h>
|
||||
#endif
|
||||
|
||||
#ifdef WIN32
|
||||
/* #define ECONNREFUSED WSAECONNREFUSED */
|
||||
/* #define ECONNRESET WSAECONNRESET */
|
||||
/* #define EHOSTUNREACH WSAEHOSTUNREACH */
|
||||
/* #define EINPROGRESS WSAEINPROGRESS */
|
||||
/* #define ENOTCONN WSAENOTCONN */
|
||||
/* #define EWOULDBLOCK WSAEWOULDBLOCK */
|
||||
#define sockerrno WSAGetLastError( )
|
||||
#else
|
||||
#include <errno.h>
|
||||
#define sockerrno errno
|
||||
#endif
|
||||
|
||||
#ifdef WIN32
|
||||
extern int inet_aton(const char *string, struct in_addr *addr);
|
||||
extern const char *inet_ntop(int af, const void *src, char *dst, size_t size);
|
||||
extern int inet_pton(int af, const char *src, void *dst);
|
||||
#endif
|
||||
|
||||
evutil_socket_t ccnet_net_open_tcp (const struct sockaddr *sa, int nonblock);
|
||||
evutil_socket_t ccnet_net_bind_tcp (int port, int nonblock);
|
||||
evutil_socket_t ccnet_net_accept (evutil_socket_t b,
|
||||
struct sockaddr_storage *cliaddr,
|
||||
socklen_t *len, int nonblock);
|
||||
|
||||
/* bind to an IPv4 address, if (*port == 0) the port number will be returned */
|
||||
evutil_socket_t ccnet_net_bind_v4 (const char *ipaddr, int *port);
|
||||
|
||||
int ccnet_netSetTOS ( evutil_socket_t s, int tos );
|
||||
|
||||
char *sock_ntop(const struct sockaddr *sa, socklen_t salen);
|
||||
uint16_t sock_port (const struct sockaddr *sa);
|
||||
|
||||
/* return 1 if addr_str is a valid ipv4 or ipv6 address */
|
||||
int is_valid_ipaddr (const char *addr_str);
|
||||
|
||||
|
||||
/* return 0 if success, -1 if error */
|
||||
int sock_pton (const char *addr_str, uint16_t port,
|
||||
struct sockaddr_storage *sa);
|
||||
|
||||
evutil_socket_t udp_client (const char *host, const char *serv,
|
||||
struct sockaddr **saptr, socklen_t *lenp);
|
||||
|
||||
#endif
|
239
lib/packet-io.c
Normal file
239
lib/packet-io.c
Normal file
@ -0,0 +1,239 @@
|
||||
/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
|
||||
|
||||
#include <config.h>
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#ifdef WIN32
|
||||
#include <winsock2.h>
|
||||
#else
|
||||
#include <netinet/in.h>
|
||||
#endif
|
||||
|
||||
#include <unistd.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <errno.h>
|
||||
|
||||
|
||||
#include <assert.h>
|
||||
#include <string.h>
|
||||
|
||||
|
||||
#include <glib.h>
|
||||
|
||||
#include "packet.h"
|
||||
#include "packet-io.h"
|
||||
#include "buffer.h"
|
||||
|
||||
|
||||
static ssize_t /* Write "n" bytes to a descriptor. */
|
||||
writen(evutil_socket_t fd, const void *vptr, size_t n)
|
||||
{
|
||||
size_t nleft;
|
||||
ssize_t nwritten;
|
||||
const char *ptr;
|
||||
|
||||
ptr = vptr;
|
||||
nleft = n;
|
||||
while (nleft > 0) {
|
||||
#ifndef WIN32
|
||||
if ( (nwritten = write(fd, ptr, nleft)) <= 0) {
|
||||
#else
|
||||
if ( (nwritten = send(fd, (char *)ptr, nleft, 0)) <= 0) {
|
||||
#endif
|
||||
if (nwritten < 0 && errno == EINTR)
|
||||
nwritten = 0; /* and call write() again */
|
||||
else
|
||||
return(-1); /* error */
|
||||
}
|
||||
|
||||
nleft -= nwritten;
|
||||
ptr += nwritten;
|
||||
}
|
||||
return(n);
|
||||
}
|
||||
|
||||
static ssize_t /* Read "n" bytes from a descriptor. */
|
||||
readn(evutil_socket_t fd, struct buffer *buf, size_t n)
|
||||
{
|
||||
size_t nleft;
|
||||
ssize_t nread;
|
||||
|
||||
nleft = n;
|
||||
while (nleft > 0) {
|
||||
if ( (nread = buffer_read(buf, fd, nleft)) < 0) {
|
||||
if (errno == EINTR)
|
||||
nread = 0; /* and call read() again */
|
||||
else
|
||||
return(-1);
|
||||
} else if (nread == 0)
|
||||
break; /* EOF */
|
||||
|
||||
nleft -= nread;
|
||||
}
|
||||
return(n - nleft); /* return >= 0 */
|
||||
}
|
||||
|
||||
|
||||
CcnetPacketIO*
|
||||
ccnet_packet_io_new (evutil_socket_t fd)
|
||||
{
|
||||
CcnetPacketIO *io;
|
||||
|
||||
io = g_malloc (sizeof(CcnetPacketIO));
|
||||
io->fd = fd;
|
||||
io->buffer = buffer_new ();
|
||||
io->in_buf = buffer_new ();
|
||||
|
||||
return io;
|
||||
}
|
||||
|
||||
void
|
||||
ccnet_packet_io_free (CcnetPacketIO *io)
|
||||
{
|
||||
evutil_closesocket(io->fd);
|
||||
buffer_free (io->buffer);
|
||||
buffer_free (io->in_buf);
|
||||
g_free (io);
|
||||
}
|
||||
|
||||
void
|
||||
ccnet_packet_prepare (CcnetPacketIO *io, int type, int id)
|
||||
{
|
||||
ccnet_header header;
|
||||
|
||||
header.version = 1;
|
||||
header.type = type;
|
||||
header.length = 0;
|
||||
header.id = htonl (id);
|
||||
buffer_add (io->buffer, &header, sizeof (header));
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
ccnet_packet_write_string (CcnetPacketIO *io, const char *str)
|
||||
{
|
||||
int len;
|
||||
|
||||
len = strlen(str);
|
||||
buffer_add (io->buffer, str, len);
|
||||
}
|
||||
|
||||
void
|
||||
ccnet_packet_add (CcnetPacketIO *io, const char *buf, int len)
|
||||
{
|
||||
buffer_add (io->buffer, buf, len);
|
||||
}
|
||||
|
||||
void
|
||||
ccnet_packet_finish (CcnetPacketIO *io)
|
||||
{
|
||||
ccnet_header *header;
|
||||
header = (ccnet_header *) BUFFER_DATA(io->buffer);
|
||||
header->length = htons (BUFFER_LENGTH(io->buffer)
|
||||
- CCNET_PACKET_LENGTH_HEADER);
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
ccnet_packet_send (CcnetPacketIO *io)
|
||||
{
|
||||
writen (io->fd, BUFFER_DATA (io->buffer), io->buffer->off);
|
||||
buffer_drain (io->buffer, io->buffer->off);
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
ccnet_packet_finish_send (CcnetPacketIO *io)
|
||||
{
|
||||
ccnet_packet_finish (io);
|
||||
ccnet_packet_send (io);
|
||||
}
|
||||
|
||||
ccnet_packet *
|
||||
ccnet_packet_io_read_packet (CcnetPacketIO* io)
|
||||
{
|
||||
ccnet_packet *packet;
|
||||
int len;
|
||||
|
||||
buffer_drain (io->in_buf, io->in_buf->off);
|
||||
|
||||
if (readn (io->fd, io->in_buf, CCNET_PACKET_LENGTH_HEADER) <= 0)
|
||||
return NULL;
|
||||
|
||||
packet = (ccnet_packet *) BUFFER_DATA(io->in_buf);
|
||||
len = ntohs (packet->header.length);
|
||||
if (len > 0) {
|
||||
if (readn (io->fd, io->in_buf, len) <= 0)
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* Note: must reset packet since readn() may cause realloc of buffer */
|
||||
packet = (ccnet_packet *) BUFFER_DATA(io->in_buf);
|
||||
packet->header.length = len;
|
||||
packet->header.id = ntohl (packet->header.id);
|
||||
|
||||
return packet;
|
||||
}
|
||||
|
||||
void
|
||||
ccnet_packet_io_set_callback (CcnetPacketIO *io,
|
||||
got_packet_callback func,
|
||||
void *user_data)
|
||||
{
|
||||
io->func = func;
|
||||
io->user_data = user_data;
|
||||
}
|
||||
|
||||
/* return 0 on EOF, -1 on error, 1 otherwise */
|
||||
int
|
||||
ccnet_packet_io_read (CcnetPacketIO *io)
|
||||
{
|
||||
int n;
|
||||
ccnet_packet *packet;
|
||||
int len;
|
||||
|
||||
again:
|
||||
if ( (n = buffer_read(io->in_buf, io->fd, 1024)) < 0) {
|
||||
if (errno == EINTR)
|
||||
goto again;
|
||||
|
||||
g_warning ("read from connfd error: %s.\n", strerror(errno));
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (n == 0) {
|
||||
if (io->func)
|
||||
io->func (NULL, io->user_data);
|
||||
return 0;
|
||||
}
|
||||
|
||||
while (BUFFER_LENGTH(io->in_buf) >= CCNET_PACKET_LENGTH_HEADER)
|
||||
{
|
||||
packet = (ccnet_packet *) BUFFER_DATA(io->in_buf);
|
||||
len = ntohs (packet->header.length);
|
||||
|
||||
if (BUFFER_LENGTH (io->in_buf) - CCNET_PACKET_LENGTH_HEADER < len)
|
||||
break;
|
||||
|
||||
packet->header.length = len;
|
||||
packet->header.id = ntohl (packet->header.id);
|
||||
|
||||
io->func (packet, io->user_data);
|
||||
buffer_drain (io->in_buf, len + CCNET_PACKET_LENGTH_HEADER);
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
/* void */
|
||||
/* ccnet_send_request (int req_id, const char *req) */
|
||||
/* { */
|
||||
/* ccnet_packet_prepear (CCNET_MSG_REQUEST, req_id); */
|
||||
/* ccnet_packet_write_string (req); */
|
||||
/* ccnet_packet_finish_send (); */
|
||||
|
||||
/* fprintf (stderr, "Send a request: id %d, cmd %s\n", req_id, req); */
|
||||
/* } */
|
50
lib/packet-io.h
Normal file
50
lib/packet-io.h
Normal file
@ -0,0 +1,50 @@
|
||||
/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
|
||||
|
||||
#ifndef CCNET_CLI_IO_H
|
||||
#define CCNET_CLI_IO_H
|
||||
|
||||
#include <packet.h>
|
||||
|
||||
#if defined(__FreeBSD__) || defined(__NetBSD__) || defined(__OpenBSD__)
|
||||
#include <event2/util.h>
|
||||
#else
|
||||
#include <evutil.h>
|
||||
#endif
|
||||
|
||||
struct buffer;
|
||||
|
||||
typedef struct CcnetPacketIO CcnetPacketIO;
|
||||
|
||||
typedef void (*got_packet_callback) (ccnet_packet *packet, void *user_data);
|
||||
|
||||
struct CcnetPacketIO {
|
||||
evutil_socket_t fd;
|
||||
|
||||
struct buffer *buffer;
|
||||
|
||||
struct buffer *in_buf;
|
||||
|
||||
got_packet_callback func;
|
||||
void *user_data;
|
||||
};
|
||||
|
||||
CcnetPacketIO* ccnet_packet_io_new (evutil_socket_t fd);
|
||||
|
||||
void ccnet_packet_io_free (CcnetPacketIO *io);
|
||||
|
||||
void ccnet_packet_prepare (CcnetPacketIO *io, int type, int id);
|
||||
void ccnet_packet_write_string (CcnetPacketIO *io, const char *str);
|
||||
void ccnet_packet_add (CcnetPacketIO *io, const char *buf, int len);
|
||||
void ccnet_packet_finish (CcnetPacketIO *io);
|
||||
void ccnet_packet_send (CcnetPacketIO *io);
|
||||
void ccnet_packet_finish_send (CcnetPacketIO *io);
|
||||
|
||||
void ccnet_packet_io_set_callback (CcnetPacketIO *io,
|
||||
got_packet_callback func,
|
||||
void *user_data);
|
||||
|
||||
int ccnet_packet_io_read (CcnetPacketIO *io);
|
||||
|
||||
ccnet_packet* ccnet_packet_io_read_packet (CcnetPacketIO* io);
|
||||
|
||||
#endif
|
304
lib/peer-common.h
Normal file
304
lib/peer-common.h
Normal file
@ -0,0 +1,304 @@
|
||||
#ifdef CCNET_LIB
|
||||
#define string_list_parse_sorted ccnet_util_string_list_parse_sorted
|
||||
#define string_list_free ccnet_util_string_list_free
|
||||
#define string_list_join ccnet_util_string_list_join
|
||||
#endif
|
||||
|
||||
G_DEFINE_TYPE (CcnetPeer, ccnet_peer, G_TYPE_OBJECT);
|
||||
|
||||
enum {
|
||||
P_ID = 1,
|
||||
P_IS_SELF,
|
||||
P_NAME,
|
||||
P_PUBLIC_ADDR,
|
||||
P_PUBLIC_PORT,
|
||||
P_SERVICE_URL,
|
||||
P_IP,
|
||||
P_PORT,
|
||||
P_AUTH_STATE,
|
||||
P_NET_STATE,
|
||||
P_PUBKEY,
|
||||
P_CAN_CONNECT, /* can be connected */
|
||||
P_IN_LOCAL_NET,
|
||||
P_IN_CONNECTION,
|
||||
P_IS_READY,
|
||||
P_ROLE_LIST,
|
||||
P_MY_ROLE_LIST,
|
||||
P_SESSION_KEY,
|
||||
P_ENCRYPT_CHANNEL,
|
||||
};
|
||||
|
||||
static void
|
||||
get_property (GObject *object, guint property_id,
|
||||
GValue *v, GParamSpec *pspec)
|
||||
{
|
||||
CcnetPeer *peer = (CcnetPeer *)object;
|
||||
GString *buf;
|
||||
|
||||
switch (property_id) {
|
||||
case P_ID:
|
||||
g_value_set_string (v, peer->id);
|
||||
break;
|
||||
case P_IS_SELF:
|
||||
g_value_set_boolean (v, peer->is_self);
|
||||
break;
|
||||
case P_NAME:
|
||||
g_value_set_string (v, peer->name);
|
||||
break;
|
||||
case P_PUBLIC_ADDR:
|
||||
g_value_set_string (v, peer->public_addr);
|
||||
break;
|
||||
case P_PUBLIC_PORT:
|
||||
g_value_set_int (v, peer->public_port);
|
||||
break;
|
||||
case P_IP:
|
||||
g_value_set_string (v, peer->addr_str);
|
||||
break;
|
||||
case P_PORT:
|
||||
g_value_set_int (v, peer->port);
|
||||
break;
|
||||
case P_SERVICE_URL:
|
||||
g_value_set_string (v, peer->service_url);
|
||||
break;
|
||||
case P_NET_STATE:
|
||||
g_value_set_int (v, peer->net_state);
|
||||
break;
|
||||
case P_PUBKEY:
|
||||
#ifndef CCNET_LIB
|
||||
if (peer->pubkey) {
|
||||
GString *str = public_key_to_gstring(peer->pubkey);
|
||||
g_value_set_string (v, str->str);
|
||||
g_string_free(str, TRUE);
|
||||
} else
|
||||
g_value_set_string (v, NULL);
|
||||
#else
|
||||
g_value_set_string (v, NULL);
|
||||
#endif
|
||||
break;
|
||||
case P_CAN_CONNECT:
|
||||
g_value_set_boolean (v, peer->can_connect);
|
||||
break;
|
||||
case P_IN_LOCAL_NET:
|
||||
g_value_set_boolean (v, peer->in_local_network);
|
||||
break;
|
||||
case P_IN_CONNECTION:
|
||||
g_value_set_boolean (v, peer->in_connection);
|
||||
break;
|
||||
case P_IS_READY:
|
||||
g_value_set_boolean (v, peer->is_ready);
|
||||
break;
|
||||
case P_ROLE_LIST:
|
||||
buf = g_string_new (NULL);
|
||||
string_list_join (peer->role_list, buf, ",");
|
||||
g_value_take_string (v, buf->str);
|
||||
g_string_free (buf, FALSE);
|
||||
break;
|
||||
case P_MY_ROLE_LIST:
|
||||
buf = g_string_new (NULL);
|
||||
string_list_join (peer->myrole_list, buf, ",");
|
||||
g_value_take_string (v, buf->str);
|
||||
g_string_free (buf, FALSE);
|
||||
break;
|
||||
case P_SESSION_KEY:
|
||||
g_value_set_string (v, peer->session_key);
|
||||
break;
|
||||
case P_ENCRYPT_CHANNEL:
|
||||
g_value_set_boolean (v, peer->encrypt_channel);
|
||||
break;
|
||||
default:
|
||||
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
set_roles (CcnetPeer *peer, const char *roles)
|
||||
{
|
||||
if (!roles)
|
||||
return;
|
||||
GList *role_list = string_list_parse_sorted (roles, ",");
|
||||
|
||||
string_list_free (peer->role_list);
|
||||
peer->role_list = role_list;
|
||||
}
|
||||
|
||||
static void
|
||||
set_my_roles (CcnetPeer *peer, const char *roles)
|
||||
{
|
||||
if (!roles)
|
||||
return;
|
||||
GList *role_list = string_list_parse_sorted (roles, ",");
|
||||
|
||||
string_list_free (peer->myrole_list);
|
||||
peer->myrole_list = role_list;
|
||||
}
|
||||
|
||||
static void
|
||||
set_property_common (GObject *object, guint property_id,
|
||||
const GValue *v, GParamSpec *pspec)
|
||||
{
|
||||
CcnetPeer *peer = (CcnetPeer *)object;
|
||||
|
||||
switch (property_id) {
|
||||
case P_ID:
|
||||
memcpy(peer->id, g_value_get_string(v), 41);
|
||||
break;
|
||||
case P_NAME:
|
||||
g_free (peer->name);
|
||||
peer->name = g_value_dup_string(v);
|
||||
break;
|
||||
case P_IS_SELF:
|
||||
peer->is_self = g_value_get_boolean(v);
|
||||
break;
|
||||
case P_PUBLIC_ADDR:
|
||||
g_free (peer->public_addr);
|
||||
peer->public_addr = g_value_dup_string(v);
|
||||
break;
|
||||
case P_PUBLIC_PORT:
|
||||
peer->public_port = g_value_get_int (v);
|
||||
break;
|
||||
case P_SERVICE_URL:
|
||||
g_free (peer->service_url);
|
||||
peer->service_url = g_value_dup_string(v);
|
||||
break;
|
||||
case P_IP:
|
||||
g_free (peer->addr_str);
|
||||
peer->addr_str = g_value_dup_string(v);
|
||||
break;
|
||||
case P_PORT:
|
||||
peer->port = g_value_get_int (v);
|
||||
break;
|
||||
case P_NET_STATE:
|
||||
peer->net_state = g_value_get_int (v);
|
||||
break;
|
||||
case P_PUBKEY:
|
||||
#ifndef CCNET_LIB
|
||||
if (peer->pubkey)
|
||||
RSA_free(peer->pubkey);
|
||||
peer->pubkey = public_key_from_string ((char *)g_value_get_string(v));
|
||||
#endif
|
||||
break;
|
||||
case P_CAN_CONNECT:
|
||||
peer->can_connect = g_value_get_boolean (v);
|
||||
break;
|
||||
case P_IN_LOCAL_NET:
|
||||
peer->in_local_network = g_value_get_boolean (v);
|
||||
break;
|
||||
case P_IN_CONNECTION:
|
||||
peer->in_connection = g_value_get_boolean (v);
|
||||
break;
|
||||
case P_IS_READY:
|
||||
peer->is_ready = g_value_get_boolean (v);
|
||||
break;
|
||||
case P_ROLE_LIST:
|
||||
set_roles (peer, g_value_get_string(v));
|
||||
break;
|
||||
case P_MY_ROLE_LIST:
|
||||
set_my_roles (peer, g_value_get_string(v));
|
||||
break;
|
||||
case P_SESSION_KEY:
|
||||
g_free (peer->session_key);
|
||||
peer->session_key = g_value_dup_string (v);
|
||||
break;
|
||||
case P_ENCRYPT_CHANNEL:
|
||||
peer->encrypt_channel = g_value_get_boolean (v);
|
||||
break;
|
||||
default:
|
||||
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
define_properties (GObjectClass *gobject_class)
|
||||
{
|
||||
g_object_class_install_property (gobject_class, P_ID,
|
||||
g_param_spec_string ("id", NULL, "Node ID",
|
||||
NULL, G_PARAM_READWRITE));
|
||||
|
||||
g_object_class_install_property (gobject_class, P_NAME,
|
||||
g_param_spec_string ("name", NULL, "Hostname",
|
||||
NULL, G_PARAM_READWRITE));
|
||||
|
||||
g_object_class_install_property (gobject_class, P_PUBLIC_ADDR,
|
||||
g_param_spec_string ("public-addr", NULL, "Public Addrress",
|
||||
NULL, G_PARAM_READWRITE));
|
||||
|
||||
g_object_class_install_property (gobject_class, P_PUBLIC_PORT,
|
||||
g_param_spec_int ("public-port", NULL, "Public Port",
|
||||
0, 65535, 0, G_PARAM_READWRITE));
|
||||
|
||||
g_object_class_install_property (gobject_class, P_SERVICE_URL,
|
||||
g_param_spec_string ("service-url", NULL, "Service Url",
|
||||
NULL, G_PARAM_READWRITE));
|
||||
|
||||
g_object_class_install_property (gobject_class, P_IP,
|
||||
g_param_spec_string ("ip", NULL, "Dynamic IP",
|
||||
NULL, G_PARAM_READWRITE));
|
||||
|
||||
g_object_class_install_property (gobject_class, P_PORT,
|
||||
g_param_spec_int ("port", NULL, "Dynamic Port",
|
||||
0, 65535, 0, G_PARAM_READWRITE));
|
||||
|
||||
g_object_class_install_property (gobject_class, P_NET_STATE,
|
||||
g_param_spec_int ("net-state", NULL, "Network State",
|
||||
-1, 3, 0,
|
||||
G_PARAM_READWRITE));
|
||||
|
||||
g_object_class_install_property (gobject_class, P_IS_SELF,
|
||||
g_param_spec_boolean ("is-self", NULL, "Is self",
|
||||
0, G_PARAM_READWRITE));
|
||||
|
||||
g_object_class_install_property (gobject_class, P_PUBKEY,
|
||||
g_param_spec_string ("pubkey", NULL, "Public Key",
|
||||
NULL, G_PARAM_READWRITE));
|
||||
|
||||
g_object_class_install_property (gobject_class, P_CAN_CONNECT,
|
||||
g_param_spec_boolean ("can-connect", NULL, "Can be connect via TCP",
|
||||
0, G_PARAM_READWRITE));
|
||||
|
||||
g_object_class_install_property (gobject_class, P_IN_LOCAL_NET,
|
||||
g_param_spec_boolean ("in-local-network", NULL, "In local network",
|
||||
0, G_PARAM_READWRITE));
|
||||
|
||||
|
||||
g_object_class_install_property (gobject_class, P_IN_CONNECTION,
|
||||
g_param_spec_boolean ("in-connection", NULL, "in connection",
|
||||
0, G_PARAM_READWRITE));
|
||||
|
||||
g_object_class_install_property (gobject_class, P_IS_READY,
|
||||
g_param_spec_boolean ("is_ready", NULL, "service ready",
|
||||
0, G_PARAM_READWRITE));
|
||||
|
||||
g_object_class_install_property (gobject_class, P_ROLE_LIST,
|
||||
g_param_spec_string ("role-list", NULL, "Role list",
|
||||
NULL, G_PARAM_READWRITE));
|
||||
|
||||
g_object_class_install_property (gobject_class, P_MY_ROLE_LIST,
|
||||
g_param_spec_string ("myrole-list", NULL, "My role list",
|
||||
NULL, G_PARAM_READWRITE));
|
||||
|
||||
|
||||
g_object_class_install_property (gobject_class, P_SESSION_KEY,
|
||||
g_param_spec_string ("session-key", NULL, "session key",
|
||||
NULL, G_PARAM_READWRITE));
|
||||
|
||||
g_object_class_install_property (gobject_class, P_ENCRYPT_CHANNEL,
|
||||
g_param_spec_boolean ("encrypt-channel", NULL, "encrypt channel",
|
||||
0, G_PARAM_READWRITE));
|
||||
}
|
||||
|
||||
|
||||
const char*
|
||||
ccnet_peer_get_net_state_string (int net_state)
|
||||
{
|
||||
switch (net_state) {
|
||||
case PEER_DOWN:
|
||||
return "down";
|
||||
case PEER_CONNECTED:
|
||||
return "connected";
|
||||
default:
|
||||
return "unknown";
|
||||
}
|
||||
}
|
74
lib/peer.c
Normal file
74
lib/peer.c
Normal file
@ -0,0 +1,74 @@
|
||||
/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
|
||||
|
||||
#include "include.h"
|
||||
#include "peer.h"
|
||||
#include "ccnet-client.h"
|
||||
#include "peer-common.h"
|
||||
|
||||
static void ccnet_peer_free (GObject *object);
|
||||
|
||||
|
||||
static void
|
||||
set_property (GObject *object, guint property_id,
|
||||
const GValue *v, GParamSpec *pspec)
|
||||
{
|
||||
set_property_common (object, property_id, v, pspec);
|
||||
}
|
||||
|
||||
static void
|
||||
ccnet_peer_class_init (CcnetPeerClass *klass)
|
||||
{
|
||||
GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
|
||||
|
||||
gobject_class->set_property = set_property;
|
||||
gobject_class->get_property = get_property;
|
||||
gobject_class->finalize = ccnet_peer_free;
|
||||
|
||||
define_properties (gobject_class);
|
||||
}
|
||||
|
||||
static void
|
||||
ccnet_peer_init (CcnetPeer *peer)
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
CcnetPeer*
|
||||
ccnet_peer_new (const char *id)
|
||||
{
|
||||
CcnetPeer *peer;
|
||||
|
||||
peer = g_object_new (CCNET_TYPE_PEER, NULL);
|
||||
memcpy (peer->id, id, 40);
|
||||
peer->id[40] = '\0';
|
||||
|
||||
peer->net_state = PEER_DOWN;
|
||||
peer->public_port = 0;
|
||||
peer->port = 0;
|
||||
return peer;
|
||||
}
|
||||
|
||||
void
|
||||
ccnet_peer_free (GObject *object)
|
||||
{
|
||||
CcnetPeer *peer = CCNET_PEER (object);
|
||||
GList *ptr;
|
||||
|
||||
g_free (peer->name);
|
||||
g_free (peer->addr_str);
|
||||
g_free (peer->service_url);
|
||||
g_free (peer->public_addr);
|
||||
g_free (peer->login_error);
|
||||
g_free (peer->bind_email);
|
||||
g_free (peer->session_key);
|
||||
for (ptr = peer->role_list; ptr; ptr = ptr->next)
|
||||
g_free (ptr->data);
|
||||
g_list_free (peer->role_list);
|
||||
|
||||
for (ptr = peer->myrole_list; ptr; ptr = ptr->next)
|
||||
g_free (ptr->data);
|
||||
g_list_free (peer->myrole_list);
|
||||
|
||||
G_OBJECT_CLASS(ccnet_peer_parent_class)->finalize (object);
|
||||
}
|
||||
|
215
lib/proc-factory.c
Normal file
215
lib/proc-factory.c
Normal file
@ -0,0 +1,215 @@
|
||||
/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
|
||||
|
||||
#include "include.h"
|
||||
#include "processor.h"
|
||||
#include "ccnet-client.h"
|
||||
|
||||
#include "peer.h"
|
||||
#include "proc-factory.h"
|
||||
|
||||
typedef struct {
|
||||
GHashTable *proc_type_table;
|
||||
} CcnetProcFactoryPriv;
|
||||
|
||||
#define GET_PRIV(o) \
|
||||
(G_TYPE_INSTANCE_GET_PRIVATE ((o), CCNET_TYPE_PROC_FACTORY, CcnetProcFactoryPriv))
|
||||
|
||||
G_DEFINE_TYPE (CcnetProcFactory, ccnet_proc_factory, G_TYPE_OBJECT)
|
||||
|
||||
static void
|
||||
ccnet_proc_factory_free (GObject *factory);
|
||||
|
||||
static void
|
||||
ccnet_proc_factory_class_init (CcnetProcFactoryClass *klass)
|
||||
{
|
||||
GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
|
||||
|
||||
gobject_class->finalize = ccnet_proc_factory_free;
|
||||
|
||||
g_type_class_add_private (klass, sizeof (CcnetProcFactoryPriv));
|
||||
}
|
||||
|
||||
static void
|
||||
ccnet_proc_factory_init (CcnetProcFactory *factory)
|
||||
{
|
||||
CcnetProcFactoryPriv *priv = GET_PRIV (factory);
|
||||
|
||||
priv->proc_type_table = g_hash_table_new_full (g_str_hash, g_str_equal,
|
||||
g_free, NULL);
|
||||
}
|
||||
|
||||
static void
|
||||
ccnet_proc_factory_free (GObject *factory)
|
||||
{
|
||||
CcnetProcFactoryPriv *priv = GET_PRIV (factory);
|
||||
|
||||
g_hash_table_destroy (priv->proc_type_table);
|
||||
}
|
||||
|
||||
void
|
||||
ccnet_proc_factory_register_processor (CcnetProcFactory *factory,
|
||||
const char *serv_name,
|
||||
GType proc_type)
|
||||
{
|
||||
CcnetProcFactoryPriv *priv = GET_PRIV (factory);
|
||||
|
||||
CcnetProcessorClass *proc_class =
|
||||
(CcnetProcessorClass *)g_type_class_ref(proc_type);
|
||||
g_type_class_unref (proc_class);
|
||||
|
||||
/* check dumplication */
|
||||
if (g_hash_table_lookup(priv->proc_type_table, serv_name))
|
||||
return;
|
||||
|
||||
g_hash_table_insert (priv->proc_type_table, g_strdup (serv_name),
|
||||
(gpointer) proc_type);
|
||||
}
|
||||
|
||||
GType ccnet_sendcmd_proc_get_type ();
|
||||
GType ccnet_mqclient_proc_get_type ();
|
||||
GType ccnet_async_rpc_proc_get_type ();
|
||||
|
||||
CcnetProcFactory *ccnet_proc_factory_new (CcnetClient *session)
|
||||
{
|
||||
CcnetProcFactory *factory;
|
||||
|
||||
factory = g_object_new (CCNET_TYPE_PROC_FACTORY, NULL);
|
||||
factory->session = session;
|
||||
|
||||
/* register fundamental processors */
|
||||
|
||||
ccnet_proc_factory_register_processor (factory, "send-cmd",
|
||||
ccnet_sendcmd_proc_get_type ());
|
||||
/* ccnet_proc_factory_register_processor (factory, "send-event", */
|
||||
/* ccnet_sendevent_proc_get_type ()); */
|
||||
ccnet_proc_factory_register_processor (factory, "mq-client",
|
||||
ccnet_mqclient_proc_get_type ());
|
||||
|
||||
ccnet_proc_factory_register_processor (factory, "async-rpc",
|
||||
ccnet_async_rpc_proc_get_type ());
|
||||
|
||||
return factory;
|
||||
}
|
||||
|
||||
static GType ccnet_proc_factory_get_proc_type (CcnetProcFactory *factory,
|
||||
const char *serv_name)
|
||||
{
|
||||
CcnetProcFactoryPriv *priv = GET_PRIV (factory);
|
||||
|
||||
return (GType) g_hash_table_lookup (priv->proc_type_table, serv_name);
|
||||
}
|
||||
|
||||
CcnetProcessor *
|
||||
ccnet_proc_factory_create_processor (CcnetProcFactory *factory,
|
||||
const char *serv_name,
|
||||
int is_master,
|
||||
int req_id)
|
||||
{
|
||||
GType type;
|
||||
CcnetProcessor *processor;
|
||||
|
||||
type = ccnet_proc_factory_get_proc_type (factory, serv_name);
|
||||
if (type == 0) {
|
||||
g_warning ("No processor for service: %s.\n", serv_name);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
processor = g_object_new (type, NULL);
|
||||
processor->session = factory->session;
|
||||
if (is_master) {
|
||||
if (req_id == 0)
|
||||
processor->id = MASTER_ID (
|
||||
ccnet_client_get_request_id (factory->session));
|
||||
else
|
||||
processor->id = MASTER_ID (req_id);
|
||||
} else
|
||||
processor->id = SLAVE_ID (req_id);
|
||||
|
||||
/* Set the service this processor provide.
|
||||
* This may be different from the processor class name.
|
||||
*/
|
||||
processor->name = g_strdup(serv_name);
|
||||
|
||||
ccnet_client_add_processor (factory->session, processor);
|
||||
|
||||
return processor;
|
||||
}
|
||||
|
||||
|
||||
CcnetProcessor *
|
||||
ccnet_proc_factory_create_master_processor (CcnetProcFactory *factory,
|
||||
const char *serv_name)
|
||||
{
|
||||
return ccnet_proc_factory_create_processor (factory, serv_name,
|
||||
MASTER, 0);
|
||||
}
|
||||
|
||||
CcnetProcessor *
|
||||
ccnet_proc_factory_create_remote_master_processor (CcnetProcFactory *factory,
|
||||
const char *serv_name,
|
||||
const char *peer_id)
|
||||
{
|
||||
GType type;
|
||||
CcnetProcessor *processor;
|
||||
|
||||
type = ccnet_proc_factory_get_proc_type (factory, serv_name);
|
||||
if (type == 0) {
|
||||
ccnet_warning ("No such processor type: %s.\n", serv_name);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
processor = g_object_new (type, NULL);
|
||||
processor->peer_id = g_strdup(peer_id);
|
||||
processor->session = factory->session;
|
||||
processor->id = MASTER_ID (ccnet_client_get_request_id (factory->session));
|
||||
|
||||
/* Set the real processor name.
|
||||
* This may be different from the processor class name.
|
||||
*/
|
||||
processor->name = g_strdup(serv_name);
|
||||
|
||||
ccnet_client_add_processor (factory->session, processor);
|
||||
|
||||
return processor;
|
||||
}
|
||||
|
||||
|
||||
CcnetProcessor *
|
||||
ccnet_proc_factory_create_slave_processor (CcnetProcFactory *factory,
|
||||
const char *serv_name,
|
||||
const char *peer_id,
|
||||
int req_id)
|
||||
{
|
||||
GType type;
|
||||
CcnetProcessor *processor;
|
||||
|
||||
type = ccnet_proc_factory_get_proc_type (factory, serv_name);
|
||||
if (type == 0) {
|
||||
g_warning ("No such processor type: %s.\n", serv_name);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
processor = g_object_new (type, NULL);
|
||||
processor->peer_id = g_strdup(peer_id);
|
||||
processor->session = factory->session;
|
||||
processor->id = SLAVE_ID (req_id);
|
||||
|
||||
/* Set the real processor name.
|
||||
* This may be different from the processor class name.
|
||||
*/
|
||||
processor->name = g_strdup(serv_name);
|
||||
|
||||
ccnet_client_add_processor (factory->session, processor);
|
||||
|
||||
return processor;
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
ccnet_proc_factory_recycle (CcnetProcFactory *factory,
|
||||
CcnetProcessor *processor)
|
||||
{
|
||||
ccnet_client_remove_processor (factory->session, processor);
|
||||
g_object_unref (processor);
|
||||
}
|
353
lib/processor.c
Normal file
353
lib/processor.c
Normal file
@ -0,0 +1,353 @@
|
||||
/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
|
||||
|
||||
#include "include.h"
|
||||
|
||||
#include <pthread.h>
|
||||
|
||||
#if defined(__FreeBSD__) || defined(__NetBSD__) || defined(__OpenBSD__)
|
||||
#include <event2/event.h>
|
||||
#else
|
||||
#include <event.h>
|
||||
#endif
|
||||
|
||||
#include "ccnet-client.h"
|
||||
#include "processor.h"
|
||||
#include "proc-factory.h"
|
||||
#include "timer.h"
|
||||
#include "peer.h"
|
||||
#include "job-mgr.h"
|
||||
|
||||
G_DEFINE_TYPE (CcnetProcessor, ccnet_processor, G_TYPE_OBJECT);
|
||||
|
||||
static void default_shutdown (CcnetProcessor *processor);
|
||||
static void default_release_resource (CcnetProcessor *processor);
|
||||
|
||||
enum {
|
||||
DONE_SIG, /* connection down */
|
||||
LAST_SIGNAL
|
||||
};
|
||||
|
||||
static guint signals[LAST_SIGNAL] = { 0 };
|
||||
|
||||
static void
|
||||
ccnet_processor_class_init (CcnetProcessorClass *klass)
|
||||
{
|
||||
/* GObjectClass *gobject_class = G_OBJECT_CLASS (klass); */
|
||||
|
||||
klass->start = NULL;
|
||||
klass->handle_update = NULL;
|
||||
klass->handle_response = NULL;
|
||||
klass->shutdown = default_shutdown;
|
||||
klass->release_resource = default_release_resource;
|
||||
|
||||
|
||||
signals[DONE_SIG] =
|
||||
g_signal_new ("done", CCNET_TYPE_PROCESSOR,
|
||||
G_SIGNAL_RUN_LAST,
|
||||
0, /* no class singal handler */
|
||||
NULL, NULL, /* no accumulator */
|
||||
g_cclosure_marshal_VOID__BOOLEAN,
|
||||
G_TYPE_NONE, 1, G_TYPE_BOOLEAN);
|
||||
}
|
||||
|
||||
static void
|
||||
ccnet_processor_init (CcnetProcessor *processor)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
int ccnet_processor_start (CcnetProcessor *processor, int argc, char **argv)
|
||||
{
|
||||
if (!processor->session->connected) {
|
||||
g_warning ("[proc] Not connected to daemon.\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
processor->failure = PROC_NOTSET;
|
||||
|
||||
return CCNET_PROCESSOR_GET_CLASS (processor)->start (processor, argc, argv);
|
||||
}
|
||||
|
||||
int ccnet_processor_startl (CcnetProcessor *processor, ...)
|
||||
{
|
||||
va_list ap;
|
||||
int argc = 0;
|
||||
char **argv = g_malloc (sizeof(char *) * 10);
|
||||
char *arg;
|
||||
int max = 10;
|
||||
int ret;
|
||||
|
||||
va_start (ap, processor);
|
||||
arg = va_arg (ap, char *);
|
||||
while (arg) {
|
||||
if (argc >= max) {
|
||||
max *= 2;
|
||||
argv = realloc (argv, sizeof(char *) * max);
|
||||
}
|
||||
argv[argc++] = arg;
|
||||
|
||||
arg = va_arg (ap, char *);
|
||||
}
|
||||
va_end (ap);
|
||||
|
||||
ret = ccnet_processor_start (processor, argc, argv);
|
||||
g_free (argv);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void default_shutdown (CcnetProcessor *processor)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
static void default_release_resource(CcnetProcessor *processor)
|
||||
{
|
||||
g_free (processor->name);
|
||||
g_free (processor->peer_id);
|
||||
if (processor->timer)
|
||||
ccnet_timer_free (&processor->timer);
|
||||
}
|
||||
|
||||
/* should be called before recycle */
|
||||
void
|
||||
ccnet_processor_release_resource(CcnetProcessor *processor)
|
||||
{
|
||||
CCNET_PROCESSOR_GET_CLASS (processor)->release_resource(processor);
|
||||
}
|
||||
|
||||
void
|
||||
ccnet_processor_done (CcnetProcessor *processor,
|
||||
gboolean success)
|
||||
{
|
||||
if (processor->thread_running) {
|
||||
processor->delay_shutdown = TRUE;
|
||||
processor->was_success = success;
|
||||
return;
|
||||
}
|
||||
|
||||
if (processor->state == STATE_IN_SHUTDOWN) {
|
||||
return;
|
||||
}
|
||||
processor->state = STATE_IN_SHUTDOWN;
|
||||
if (processor->failure == PROC_NOTSET && success)
|
||||
processor->failure = PROC_DONE;
|
||||
|
||||
g_debug ("[proc] Processor %s(%d) done %d\n", GET_PNAME(processor),
|
||||
PRINT_ID(processor->id), success);
|
||||
|
||||
/* Notify */
|
||||
if (!IS_SLAVE (processor) && success) {
|
||||
ccnet_processor_send_update (processor, SC_PROC_DONE, SS_PROC_DONE,
|
||||
NULL, 0);
|
||||
}
|
||||
|
||||
g_signal_emit (processor, signals[DONE_SIG], 0, success);
|
||||
|
||||
ccnet_client_remove_processor (processor->session, processor);
|
||||
ccnet_processor_release_resource (processor);
|
||||
ccnet_proc_factory_recycle (processor->session->proc_factory, processor);
|
||||
}
|
||||
|
||||
void ccnet_processor_handle_update (CcnetProcessor *processor,
|
||||
char *code, char *code_msg,
|
||||
char *content, int clen)
|
||||
{
|
||||
g_object_ref (processor);
|
||||
processor->is_active = TRUE;
|
||||
|
||||
if (code[0] == '5') {
|
||||
ccnet_debug ("[Proc] Shutdown processor %s(%d) for bad update: %s %s\n",
|
||||
GET_PNAME(processor), PRINT_ID(processor->id),
|
||||
code, code_msg);
|
||||
|
||||
if (memcmp(code, SC_UNKNOWN_SERVICE, 3) == 0)
|
||||
processor->failure = PROC_NO_SERVICE;
|
||||
else if (memcmp(code, SC_PERM_ERR, 3) == 0)
|
||||
processor->failure = PROC_PERM_ERR;
|
||||
else if (memcmp(code, SC_NETDOWN, 3) == 0)
|
||||
processor->failure = PROC_REMOTE_DEAD;
|
||||
else
|
||||
processor->failure = PROC_BAD_RESP;
|
||||
|
||||
ccnet_processor_done (processor, FALSE);
|
||||
return;
|
||||
}
|
||||
|
||||
if (strncmp (code, SC_PROC_KEEPALIVE, 3) == 0) {
|
||||
ccnet_processor_send_response (processor, SC_PROC_ALIVE,
|
||||
SS_PROC_ALIVE, NULL, 0);
|
||||
} else if (strncmp (code, SC_PROC_DEAD, 3) == 0) {
|
||||
ccnet_debug ("[proc] Shutdown processor %s(%d) when peer(%.8s) processor is dead\n",
|
||||
GET_PNAME(processor), PRINT_ID(processor->id),
|
||||
processor->peer_id);
|
||||
processor->failure = PROC_REMOTE_DEAD;
|
||||
ccnet_processor_done (processor, FALSE);
|
||||
} else if (strncmp (code, SC_PROC_DONE, 3) == 0) {
|
||||
ccnet_debug ("[proc] Shutdown processor when receive 103: service done\n");
|
||||
ccnet_processor_done (processor, TRUE);
|
||||
} else {
|
||||
CCNET_PROCESSOR_GET_CLASS (processor)->handle_update (processor,
|
||||
code, code_msg,
|
||||
content, clen);
|
||||
}
|
||||
processor->is_active = FALSE;
|
||||
g_object_unref (processor);
|
||||
}
|
||||
|
||||
void ccnet_processor_handle_response (CcnetProcessor *processor,
|
||||
char *code, char *code_msg,
|
||||
char *content, int clen)
|
||||
{
|
||||
g_return_if_fail (CCNET_PROCESSOR_GET_CLASS(processor)->handle_response != NULL);
|
||||
|
||||
g_object_ref (processor);
|
||||
processor->is_active = TRUE;
|
||||
|
||||
if (code[0] == '5') {
|
||||
ccnet_debug ("[Proc] Shutdown processor %s(%d) for bad response: %s %s from %s\n",
|
||||
GET_PNAME(processor), PRINT_ID(processor->id),
|
||||
code, code_msg, processor->peer_id);
|
||||
if (memcmp(code, SC_UNKNOWN_SERVICE, 3) == 0)
|
||||
processor->failure = PROC_NO_SERVICE;
|
||||
else if (memcmp(code, SC_PERM_ERR, 3) == 0)
|
||||
processor->failure = PROC_PERM_ERR;
|
||||
else if (memcmp(code, SC_NETDOWN, 3) == 0)
|
||||
processor->failure = PROC_REMOTE_DEAD;
|
||||
else
|
||||
processor->failure = PROC_BAD_RESP;
|
||||
|
||||
ccnet_processor_done (processor, FALSE);
|
||||
return;
|
||||
}
|
||||
|
||||
if (strncmp (code, SC_PROC_KEEPALIVE, 3) == 0) {
|
||||
ccnet_processor_send_update (processor, SC_PROC_ALIVE,
|
||||
SS_PROC_ALIVE, NULL, 0);
|
||||
} else if (strncmp (code, SC_PROC_DEAD, 3) == 0) {
|
||||
ccnet_debug ("[proc] Shutdown processor %s(%d) when peer(%.8s) processor is dead\n",
|
||||
GET_PNAME(processor), PRINT_ID(processor->id),
|
||||
processor->peer_id);
|
||||
processor->failure = PROC_REMOTE_DEAD;
|
||||
ccnet_processor_done (processor, FALSE);
|
||||
} else {
|
||||
CCNET_PROCESSOR_GET_CLASS (processor)->handle_response (processor,
|
||||
code, code_msg,
|
||||
content, clen);
|
||||
}
|
||||
processor->is_active = FALSE;
|
||||
g_object_unref (processor);
|
||||
}
|
||||
|
||||
|
||||
void ccnet_processor_handle_sigchld (CcnetProcessor *processor, int status)
|
||||
{
|
||||
CCNET_PROCESSOR_GET_CLASS (processor)->handle_sigchld (processor,
|
||||
status);
|
||||
}
|
||||
|
||||
void
|
||||
ccnet_processor_send_request (CcnetProcessor *processor,
|
||||
const char *request)
|
||||
{
|
||||
ccnet_client_send_request (processor->session,
|
||||
REQUEST_ID (processor->id),
|
||||
request);
|
||||
}
|
||||
|
||||
void
|
||||
ccnet_processor_send_request_l (CcnetProcessor *processor, ...)
|
||||
{
|
||||
va_list ap;
|
||||
GString *buf = g_string_new(NULL);
|
||||
char *arg;
|
||||
|
||||
va_start (ap, processor);
|
||||
arg = va_arg (ap, char *);
|
||||
while (arg) {
|
||||
g_string_append (buf, arg);
|
||||
arg = va_arg (ap, char *);
|
||||
}
|
||||
va_end (ap);
|
||||
|
||||
ccnet_client_send_request (processor->session,
|
||||
REQUEST_ID (processor->id),
|
||||
buf->str);
|
||||
|
||||
g_string_free (buf, TRUE);
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
ccnet_processor_send_update(CcnetProcessor *processor,
|
||||
const char *code,
|
||||
const char *code_msg,
|
||||
const char *content, int clen)
|
||||
{
|
||||
ccnet_client_send_update (processor->session, UPDATE_ID(processor->id),
|
||||
code, code_msg, content, clen);
|
||||
}
|
||||
|
||||
void
|
||||
ccnet_processor_send_response(CcnetProcessor *processor,
|
||||
const char *code,
|
||||
const char *code_msg,
|
||||
const char *content, int clen)
|
||||
{
|
||||
ccnet_client_send_response (processor->session, RESPONSE_ID(processor->id),
|
||||
code, code_msg, content, clen);
|
||||
}
|
||||
|
||||
typedef struct ProcThreadData {
|
||||
CcnetProcessor *proc;
|
||||
ProcThreadFunc func;
|
||||
void *data;
|
||||
ProcThreadDoneFunc done_func;
|
||||
void *result;
|
||||
} ProcThreadData;
|
||||
|
||||
static void
|
||||
processor_thread_done (void *vdata)
|
||||
{
|
||||
ProcThreadData *tdata = vdata;
|
||||
|
||||
tdata->proc->thread_running = FALSE;
|
||||
|
||||
if (tdata->proc->delay_shutdown)
|
||||
ccnet_processor_done (tdata->proc, tdata->proc->was_success);
|
||||
else
|
||||
tdata->done_func (tdata->result);
|
||||
|
||||
g_free (tdata);
|
||||
}
|
||||
|
||||
static void *
|
||||
processor_thread_func_wrapper (void *vdata)
|
||||
{
|
||||
ProcThreadData *tdata = vdata;
|
||||
tdata->result = tdata->func (tdata->data);
|
||||
return vdata;
|
||||
}
|
||||
|
||||
int
|
||||
ccnet_processor_thread_create (CcnetProcessor *processor,
|
||||
CcnetJobManager *job_mgr,
|
||||
ProcThreadFunc func,
|
||||
ProcThreadDoneFunc done_func,
|
||||
void *data)
|
||||
{
|
||||
ProcThreadData *tdata;
|
||||
|
||||
tdata = g_new(ProcThreadData, 1);
|
||||
tdata->proc = processor;
|
||||
tdata->func = func;
|
||||
tdata->done_func = done_func;
|
||||
tdata->data = data;
|
||||
|
||||
ccnet_job_manager_schedule_job (job_mgr ? job_mgr : processor->session->job_mgr,
|
||||
processor_thread_func_wrapper,
|
||||
processor_thread_done,
|
||||
tdata);
|
||||
processor->thread_running = TRUE;
|
||||
return 0;
|
||||
}
|
41
lib/rpc-common.h
Normal file
41
lib/rpc-common.h
Normal file
@ -0,0 +1,41 @@
|
||||
/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
|
||||
|
||||
#ifndef RPC_COMMON_H
|
||||
#define RPC_COMMON_H
|
||||
|
||||
#include "packet.h"
|
||||
|
||||
#define SC_CLIENT_CALL "301"
|
||||
#define SS_CLIENT_CALL "CLIENT CALL"
|
||||
#define SC_CLIENT_MORE "302"
|
||||
#define SS_CLIENT_MORE "MORE"
|
||||
#define SC_SERVER_RET "311"
|
||||
#define SS_SERVER_RET "SERVER RET"
|
||||
#define SC_SERVER_MORE "312"
|
||||
#define SS_SERVER_MORE "HAS MORE"
|
||||
#define SC_SERVER_ERR "411"
|
||||
#define SS_SERVER_ERR "Fail to invoke the function, check the function"
|
||||
|
||||
/* MESSAGE_HEADER = SC_SERVER_RET(3) + " " + SS_SERVER_RET(10) + "\n"(1) + "\n"(1) */
|
||||
#define MESSAGE_HEADER 64 /* leave enough space */
|
||||
#define MAX_TRANSFER_LENGTH (CCNET_PACKET_MAX_PAYLOAD_LEN - MESSAGE_HEADER)
|
||||
|
||||
/*
|
||||
Client Server
|
||||
<xxx>-rpcserver
|
||||
---------------------->
|
||||
|
||||
200 OK
|
||||
<----------------------
|
||||
301 Func String
|
||||
---------------------->
|
||||
|
||||
312 HAS MORE
|
||||
<-----------------------
|
||||
302 MORE
|
||||
---------------------->
|
||||
311 SERVER RET
|
||||
<-----------------------
|
||||
*/
|
||||
|
||||
#endif
|
63
lib/rpc_table.py
Normal file
63
lib/rpc_table.py
Normal file
@ -0,0 +1,63 @@
|
||||
"""
|
||||
Define RPC functions needed to generate
|
||||
"""
|
||||
|
||||
# [ <ret-type>, [<arg_types>] ]
|
||||
func_table = [
|
||||
[ "int", [] ],
|
||||
[ "int", ["int"] ],
|
||||
[ "int", ["int", "int"] ],
|
||||
[ "int", ["int", "string"] ],
|
||||
[ "int", ["int", "string", "int"] ],
|
||||
[ "int", ["int", "string", "string"] ],
|
||||
[ "int", ["int", "string", "int", "int"] ],
|
||||
[ "int", ["string"] ],
|
||||
[ "int", ["string", "int"] ],
|
||||
[ "int", ["string", "int", "string"] ],
|
||||
[ "int", ["string", "int", "string", "string"] ],
|
||||
[ "int", ["string", "int", "string", "int", "int"] ],
|
||||
[ "int", ["string", "string"] ],
|
||||
[ "int", ["string", "string", "string"] ],
|
||||
[ "int", ["string", "string", "int"] ],
|
||||
[ "int", ["string", "string", "int", "int"] ],
|
||||
[ "int", ["string", "string", "string", "string"] ],
|
||||
[ "int", ["string", "string", "string", "string", "string"] ],
|
||||
[ "int", ["string", "string", "string", "string", "string", "string"] ],
|
||||
[ "int", ["string", "string", "string", "string", "string", "string", "string"] ],
|
||||
[ "int", ["string", "int64"]],
|
||||
[ "int", ["int", "int64"]],
|
||||
[ "int", ["int", "string", "int64"]],
|
||||
[ "int64", [] ],
|
||||
[ "int64", ["string"] ],
|
||||
[ "int64", ["int"]],
|
||||
[ "int64", ["int", "string"]],
|
||||
[ "string", [] ],
|
||||
[ "string", ["int"] ],
|
||||
[ "string", ["string"] ],
|
||||
[ "string", ["string", "int"] ],
|
||||
[ "string", ["string", "string"] ],
|
||||
[ "string", ["string", "string", "string"] ],
|
||||
[ "string", ["string", "string", "string", "string"] ],
|
||||
[ "string", ["string", "string", "string", "string", "int"] ],
|
||||
[ "string", ["string", "string", "string", "string", "string"] ],
|
||||
[ "string", ["string", "string", "string", "string", "string", "string"] ],
|
||||
[ "string", ["string", "string", "string", "string", "string", "int"] ],
|
||||
[ "string", ["string", "string", "string", "string", "string", "string", "string", "string", "string"] ],
|
||||
[ "objlist", [] ],
|
||||
[ "objlist", ["int"] ],
|
||||
[ "objlist", ["int", "int"] ],
|
||||
[ "objlist", ["int", "int", "int"] ],
|
||||
[ "objlist", ["string"] ],
|
||||
[ "objlist", ["string", "int"] ],
|
||||
[ "objlist", ["string", "int", "int"] ],
|
||||
[ "objlist", ["string", "int", "int", "string"] ],
|
||||
[ "objlist", ["string", "string"] ],
|
||||
[ "objlist", ["string", "string", "string"] ],
|
||||
[ "objlist", ["string", "string", "int"] ],
|
||||
[ "objlist", ["string", "string", "int", "int"] ],
|
||||
[ "objlist", ["int", "int", "string"] ],
|
||||
[ "object", [] ],
|
||||
[ "object", ["int"] ],
|
||||
[ "object", ["string"] ],
|
||||
[ "object", ["string", "string"] ],
|
||||
]
|
116
lib/rpcserver-proc.c
Normal file
116
lib/rpcserver-proc.c
Normal file
@ -0,0 +1,116 @@
|
||||
/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
|
||||
|
||||
#include "include.h"
|
||||
|
||||
#include <ccnet/processor.h>
|
||||
#include <ccnet/rpcserver-proc.h>
|
||||
#include "searpc-server.h"
|
||||
#include "rpc-common.h"
|
||||
|
||||
typedef struct {
|
||||
char *buf;
|
||||
int len;
|
||||
int off;
|
||||
} CcnetRpcserverProcPriv;
|
||||
|
||||
#define GET_PRIV(o) \
|
||||
(G_TYPE_INSTANCE_GET_PRIVATE ((o), CCNET_TYPE_RPCSERVER_PROC, CcnetRpcserverProcPriv))
|
||||
|
||||
G_DEFINE_TYPE (CcnetRpcserverProc, ccnet_rpcserver_proc, CCNET_TYPE_PROCESSOR)
|
||||
|
||||
static int start (CcnetProcessor *processor, int argc, char **argv);
|
||||
static void handle_update (CcnetProcessor *processor,
|
||||
char *code, char *code_msg,
|
||||
char *content, int clen);
|
||||
|
||||
static void
|
||||
release_resource(CcnetProcessor *processor)
|
||||
{
|
||||
CCNET_PROCESSOR_CLASS (ccnet_rpcserver_proc_parent_class)->release_resource (processor);
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
ccnet_rpcserver_proc_class_init (CcnetRpcserverProcClass *klass)
|
||||
{
|
||||
CcnetProcessorClass *proc_class = CCNET_PROCESSOR_CLASS (klass);
|
||||
|
||||
proc_class->start = start;
|
||||
proc_class->handle_update = handle_update;
|
||||
proc_class->release_resource = release_resource;
|
||||
proc_class->name = "rpcserver-proc";
|
||||
|
||||
g_type_class_add_private (klass, sizeof(CcnetRpcserverProcPriv));
|
||||
}
|
||||
|
||||
static void
|
||||
ccnet_rpcserver_proc_init (CcnetRpcserverProc *processor)
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
static int
|
||||
start (CcnetProcessor *processor, int argc, char **argv)
|
||||
{
|
||||
ccnet_processor_send_response (processor, SC_OK, SS_OK, NULL, 0);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
handle_update (CcnetProcessor *processor,
|
||||
char *code, char *code_msg,
|
||||
char *content, int clen)
|
||||
{
|
||||
CcnetRpcserverProcPriv *priv = GET_PRIV (processor);
|
||||
|
||||
if (memcmp (code, SC_CLIENT_CALL, 3) == 0) {
|
||||
gsize ret_len;
|
||||
char *svc_name = processor->name;
|
||||
char *ret = searpc_server_call_function (svc_name, content, clen, &ret_len);
|
||||
|
||||
if (ret_len < MAX_TRANSFER_LENGTH) {
|
||||
ccnet_processor_send_response (
|
||||
processor, SC_SERVER_RET, SS_SERVER_RET, ret, ret_len);
|
||||
g_free (ret);
|
||||
/* ccnet_processor_done (processor, TRUE); */
|
||||
return;
|
||||
}
|
||||
|
||||
/* we need to split data into multiple segments */
|
||||
priv->buf = ret;
|
||||
priv->len = ret_len;
|
||||
priv->off = 0;
|
||||
|
||||
ccnet_processor_send_response (processor, SC_SERVER_MORE,
|
||||
SS_SERVER_MORE, priv->buf,
|
||||
MAX_TRANSFER_LENGTH);
|
||||
priv->off = MAX_TRANSFER_LENGTH;
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (memcmp (code, SC_CLIENT_MORE, 3) == 0) {
|
||||
if (priv->off + MAX_TRANSFER_LENGTH < priv->len) {
|
||||
ccnet_processor_send_response (
|
||||
processor, SC_SERVER_MORE, SS_SERVER_MORE,
|
||||
priv->buf + priv->off, MAX_TRANSFER_LENGTH);
|
||||
priv->off += MAX_TRANSFER_LENGTH;
|
||||
} else {
|
||||
ccnet_processor_send_response (
|
||||
processor, SC_SERVER_RET, SS_SERVER_RET,
|
||||
priv->buf + priv->off, priv->len - priv->off);
|
||||
g_free (priv->buf);
|
||||
/* ccnet_processor_done (processor, TRUE); */
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
ccnet_processor_send_response (processor, SC_BAD_UPDATE_CODE,
|
||||
SS_BAD_UPDATE_CODE, NULL, 0);
|
||||
g_warning ("[rpc-server] Bad update: %s %s.\n", code, code_msg);
|
||||
if (priv->buf)
|
||||
g_free (priv->buf);
|
||||
ccnet_processor_done (processor, FALSE);
|
||||
}
|
161
lib/rsa.c
Normal file
161
lib/rsa.c
Normal file
@ -0,0 +1,161 @@
|
||||
/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
|
||||
|
||||
#include <openssl/pem.h>
|
||||
#include <openssl/rand.h>
|
||||
#include <openssl/rsa.h>
|
||||
#include <openssl/err.h>
|
||||
|
||||
#include <string.h>
|
||||
#include <glib.h>
|
||||
|
||||
#include "rsa.h"
|
||||
#include "utils.h"
|
||||
|
||||
RSA*
|
||||
private_key_to_pub(RSA *priv)
|
||||
{
|
||||
RSA *pub = RSA_new();
|
||||
|
||||
pub->n = BN_dup(priv->n);
|
||||
pub->e = BN_dup(priv->e);
|
||||
|
||||
return pub;
|
||||
}
|
||||
|
||||
|
||||
GString* public_key_to_gstring(const RSA *rsa)
|
||||
{
|
||||
GString *buf = g_string_new(NULL);
|
||||
unsigned char *temp;
|
||||
char *coded;
|
||||
|
||||
gsize len = BN_num_bytes(rsa->n);
|
||||
temp = malloc(len);
|
||||
BN_bn2bin(rsa->n, temp);
|
||||
coded = g_base64_encode(temp, len);
|
||||
g_string_append (buf, coded);
|
||||
g_string_append_c (buf, ' ');
|
||||
g_free(coded);
|
||||
|
||||
len = BN_num_bytes(rsa->e);
|
||||
temp = realloc(temp, len);
|
||||
BN_bn2bin(rsa->e, temp);
|
||||
coded = g_base64_encode(temp, len);
|
||||
g_string_append (buf, coded);
|
||||
g_free(coded);
|
||||
|
||||
free(temp);
|
||||
|
||||
return buf;
|
||||
}
|
||||
|
||||
void
|
||||
public_key_append_to_gstring(const RSA *rsa, GString *buf)
|
||||
{
|
||||
unsigned char *temp;
|
||||
char *coded;
|
||||
|
||||
gsize len = BN_num_bytes(rsa->n);
|
||||
temp = malloc(len);
|
||||
BN_bn2bin(rsa->n, temp);
|
||||
coded = g_base64_encode(temp, len);
|
||||
g_string_append (buf, coded);
|
||||
g_string_append_c (buf, ' ');
|
||||
g_free(coded);
|
||||
|
||||
len = BN_num_bytes(rsa->e);
|
||||
temp = realloc(temp, len);
|
||||
BN_bn2bin(rsa->e, temp);
|
||||
coded = g_base64_encode(temp, len);
|
||||
g_string_append (buf, coded);
|
||||
g_free(coded);
|
||||
|
||||
free(temp);
|
||||
}
|
||||
|
||||
RSA* public_key_from_string(char *str)
|
||||
{
|
||||
char *p;
|
||||
unsigned char *num;
|
||||
gsize len;
|
||||
if (!str)
|
||||
return NULL;
|
||||
|
||||
if ( !(p = strchr(str, ' ')) )
|
||||
return NULL;
|
||||
*p = '\0';
|
||||
|
||||
RSA *key = RSA_new();
|
||||
|
||||
num = g_base64_decode(str, &len);
|
||||
key->n = BN_bin2bn(num, len, NULL);
|
||||
if (!key->n)
|
||||
goto err;
|
||||
g_free(num);
|
||||
|
||||
num = g_base64_decode(p+1, &len);
|
||||
key->e = BN_bin2bn(num, len, NULL);
|
||||
if (!key->e)
|
||||
goto err;
|
||||
g_free(num);
|
||||
|
||||
*p = ' ';
|
||||
return key;
|
||||
err:
|
||||
*p = ' ';
|
||||
RSA_free (key);
|
||||
g_free(num);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
unsigned char *
|
||||
private_key_decrypt(RSA *key, unsigned char *data, int len, int *decrypt_len)
|
||||
{
|
||||
int size;
|
||||
unsigned char *buf;
|
||||
|
||||
size = RSA_size(key);
|
||||
buf = g_malloc(size);
|
||||
*decrypt_len = RSA_private_decrypt(len, data, buf, key, RSA_PKCS1_PADDING);
|
||||
|
||||
return buf;
|
||||
}
|
||||
|
||||
unsigned char *
|
||||
public_key_encrypt(RSA *key, unsigned char *data, int len, int *encrypt_len)
|
||||
{
|
||||
int size;
|
||||
unsigned char *buf;
|
||||
|
||||
size = RSA_size(key);
|
||||
buf = g_malloc(size);
|
||||
*encrypt_len = RSA_public_encrypt(len, data, buf, key, RSA_PKCS1_PADDING);
|
||||
|
||||
return buf;
|
||||
}
|
||||
|
||||
char *
|
||||
id_from_pubkey (RSA *pubkey)
|
||||
{
|
||||
GString *buf;
|
||||
unsigned char sha1[20];
|
||||
char *id = g_malloc(41);
|
||||
|
||||
buf = public_key_to_gstring (pubkey);
|
||||
calculate_sha1 (sha1, buf->str);
|
||||
sha1_to_hex (sha1, id);
|
||||
g_string_free (buf, TRUE);
|
||||
|
||||
return id;
|
||||
}
|
||||
|
||||
RSA *
|
||||
generate_private_key(u_int bits)
|
||||
{
|
||||
RSA *private = NULL;
|
||||
|
||||
private = RSA_generate_key(bits, 35, NULL, NULL);
|
||||
if (private == NULL)
|
||||
g_error ("rsa_generate_private_key: key generation failed.");
|
||||
return private;
|
||||
}
|
31
lib/rsa.h
Normal file
31
lib/rsa.h
Normal file
@ -0,0 +1,31 @@
|
||||
/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
|
||||
|
||||
#ifndef CCNET_RSA_H
|
||||
#define CCNET_RSA_H
|
||||
|
||||
#include <openssl/pem.h>
|
||||
#include <openssl/rand.h>
|
||||
#include <openssl/rsa.h>
|
||||
#include <openssl/err.h>
|
||||
|
||||
|
||||
RSA* private_key_to_pub(RSA *priv);
|
||||
|
||||
GString* public_key_to_gstring(const RSA *rsa);
|
||||
void public_key_append_to_gstring(const RSA *rsa, GString *buf);
|
||||
|
||||
RSA* public_key_from_string(char *str);
|
||||
|
||||
unsigned char* private_key_decrypt(RSA *key, unsigned char *data,
|
||||
int len, int *decrypt_len);
|
||||
|
||||
unsigned char* public_key_encrypt(RSA *key, unsigned char *data,
|
||||
int len, int *encrypt_len);
|
||||
|
||||
|
||||
char *id_from_pubkey (RSA *pubkey);
|
||||
|
||||
RSA* generate_private_key(u_int bits);
|
||||
|
||||
|
||||
#endif
|
136
lib/sendcmd-proc.c
Normal file
136
lib/sendcmd-proc.c
Normal file
@ -0,0 +1,136 @@
|
||||
/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
|
||||
|
||||
#include <config.h>
|
||||
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "ccnet-client.h"
|
||||
#include "sendcmd-proc.h"
|
||||
|
||||
enum {
|
||||
INIT,
|
||||
REQUEST_SENT,
|
||||
CONNECTED
|
||||
};
|
||||
|
||||
typedef struct {
|
||||
int persist : 1;
|
||||
} CcnetSendcmdProcPriv;
|
||||
|
||||
#define GET_PRIV(o) \
|
||||
(G_TYPE_INSTANCE_GET_PRIVATE ((o), CCNET_TYPE_SENDCMD_PROC, CcnetSendcmdProcPriv))
|
||||
|
||||
static int send_cmd_start (CcnetProcessor *processor, int argc, char **argv);
|
||||
|
||||
static void handle_response (CcnetProcessor *processor,
|
||||
char *code, char *code_msg,
|
||||
char *content, int clen);
|
||||
|
||||
|
||||
G_DEFINE_TYPE (CcnetSendcmdProc, ccnet_sendcmd_proc, CCNET_TYPE_PROCESSOR);
|
||||
|
||||
static void
|
||||
ccnet_sendcmd_proc_class_init (CcnetSendcmdProcClass *klass)
|
||||
{
|
||||
CcnetProcessorClass *proc_class = CCNET_PROCESSOR_CLASS (klass);
|
||||
|
||||
proc_class->start = send_cmd_start;
|
||||
proc_class->name = "sendcmd-proc";
|
||||
proc_class->handle_response = handle_response;
|
||||
|
||||
g_type_class_add_private (klass, sizeof (CcnetSendcmdProcPriv));
|
||||
}
|
||||
|
||||
static void
|
||||
ccnet_sendcmd_proc_init (CcnetSendcmdProc *processor)
|
||||
{
|
||||
CCNET_PROCESSOR (processor)->state = INIT;
|
||||
}
|
||||
|
||||
|
||||
static int
|
||||
send_cmd_start (CcnetProcessor *processor, int argc, char **argv)
|
||||
{
|
||||
char buf[256];
|
||||
CcnetSendcmdProcPriv *priv = GET_PRIV (processor);
|
||||
|
||||
priv->persist = 0;
|
||||
|
||||
while (0 < argc && argv[0][0] == '-') {
|
||||
switch (argv[0][1]) {
|
||||
case 'p':
|
||||
priv->persist = 1;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
argc--; argv++;
|
||||
}
|
||||
if (priv->persist) {
|
||||
snprintf (buf, 256, "receive-cmd -p");
|
||||
} else {
|
||||
snprintf (buf, 256, "receive-cmd");
|
||||
}
|
||||
ccnet_client_send_request (processor->session,
|
||||
REQUEST_ID (processor->id), buf);
|
||||
processor->state = REQUEST_SENT;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
|
||||
static void handle_response (CcnetProcessor *processor,
|
||||
char *code, char *code_msg,
|
||||
char *content, int clen)
|
||||
{
|
||||
CcnetSendcmdProcPriv *priv = GET_PRIV (processor);
|
||||
CcnetSendcmdProc *proc = (CcnetSendcmdProc *) processor;
|
||||
|
||||
if (code[0] != '2') {
|
||||
g_warning ("Receive bad response %s %s\n", code, code_msg);
|
||||
}
|
||||
|
||||
switch (processor->state) {
|
||||
case REQUEST_SENT:
|
||||
processor->state = CONNECTED;
|
||||
return;
|
||||
|
||||
case CONNECTED:
|
||||
if (proc->rcvrsp_cb) {
|
||||
proc->rcvrsp_cb (code, content, clen, proc->cb_data);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
if (!priv->persist)
|
||||
ccnet_processor_done (processor, TRUE);
|
||||
}
|
||||
|
||||
int
|
||||
ccnet_sendcmd_proc_send_command (CcnetSendcmdProc *proc, const char *cmd)
|
||||
{
|
||||
CcnetProcessor *processor = CCNET_PROCESSOR (proc);
|
||||
|
||||
/*
|
||||
if (processor->state != CONNECTED)
|
||||
return -1;
|
||||
*/
|
||||
ccnet_client_send_update (processor->session, UPDATE_ID(processor->id),
|
||||
"200", NULL,
|
||||
cmd, strlen(cmd)+1); /* including '\0' */
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void
|
||||
ccnet_sendcmd_proc_set_rcvrsp_cb (CcnetSendcmdProc *proc,
|
||||
SendcmdProcRcvrspCallback rcvrsp_cb,
|
||||
void *data)
|
||||
{
|
||||
proc->rcvrsp_cb = rcvrsp_cb;
|
||||
proc->cb_data = data;
|
||||
}
|
59
lib/string-util.h
Normal file
59
lib/string-util.h
Normal file
@ -0,0 +1,59 @@
|
||||
/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
|
||||
|
||||
#ifndef CCNET_STRING_H
|
||||
#define CCNET_STRING_H
|
||||
|
||||
#include <stdio.h>
|
||||
|
||||
#define END_0(buf,len) (buf[(len)-1] == '\0')
|
||||
|
||||
|
||||
static inline int get_version(char *str)
|
||||
{
|
||||
int v;
|
||||
|
||||
if (str[0] != 'v')
|
||||
return 0;
|
||||
if (sscanf(str+1, "%d", &v) != 1)
|
||||
return 0;
|
||||
return v;
|
||||
}
|
||||
|
||||
|
||||
|
||||
#define sgoto_next(p) do { \
|
||||
while (*p != ' ' && *p) ++p; \
|
||||
if (*p != ' ') \
|
||||
goto error; \
|
||||
*p = '\0'; \
|
||||
++p; \
|
||||
} while (0)
|
||||
|
||||
#define sget_len(val, p) do { \
|
||||
char *tmp = p; \
|
||||
sgoto_next(p); \
|
||||
val = atoi(tmp); \
|
||||
if (val == 0) \
|
||||
goto error; \
|
||||
} while (0)
|
||||
|
||||
|
||||
/* get a string with format "%s " */
|
||||
#define sget_str(str, p) do { \
|
||||
str = p; \
|
||||
sgoto_next(p); \
|
||||
} while (0)
|
||||
|
||||
/* get a string with format "%d %s " */
|
||||
#define sget_str_with_len(str, p) do { \
|
||||
int len; \
|
||||
sget_len(len, p); \
|
||||
str = p; \
|
||||
p += len; \
|
||||
*p = '\0'; \
|
||||
++p; \
|
||||
} while (0)
|
||||
|
||||
|
||||
|
||||
#endif
|
155
lib/threaded-rpcserver-proc.c
Normal file
155
lib/threaded-rpcserver-proc.c
Normal file
@ -0,0 +1,155 @@
|
||||
/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
|
||||
|
||||
#include "include.h"
|
||||
|
||||
#include <ccnet/ccnet-client.h>
|
||||
#include <ccnet/processor.h>
|
||||
#include <ccnet/threaded-rpcserver-proc.h>
|
||||
#include "searpc-server.h"
|
||||
#include "rpc-common.h"
|
||||
#include "job-mgr.h"
|
||||
|
||||
typedef struct {
|
||||
char *call_buf;
|
||||
gsize call_len;
|
||||
char *buf;
|
||||
gsize len;
|
||||
int off;
|
||||
char *error_message;
|
||||
} CcnetThreadedRpcserverProcPriv;
|
||||
|
||||
#define GET_PRIV(o) \
|
||||
(G_TYPE_INSTANCE_GET_PRIVATE ((o), CCNET_TYPE_THREADED_RPCSERVER_PROC, CcnetThreadedRpcserverProcPriv))
|
||||
|
||||
G_DEFINE_TYPE (CcnetThreadedRpcserverProc, ccnet_threaded_rpcserver_proc, CCNET_TYPE_PROCESSOR)
|
||||
|
||||
static int start (CcnetProcessor *processor, int argc, char **argv);
|
||||
static void handle_update (CcnetProcessor *processor,
|
||||
char *code, char *code_msg,
|
||||
char *content, int clen);
|
||||
|
||||
static void
|
||||
release_resource(CcnetProcessor *processor)
|
||||
{
|
||||
CcnetThreadedRpcserverProcPriv *priv = GET_PRIV(processor);
|
||||
|
||||
g_free (priv->buf);
|
||||
|
||||
CCNET_PROCESSOR_CLASS (ccnet_threaded_rpcserver_proc_parent_class)->release_resource (processor);
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
ccnet_threaded_rpcserver_proc_class_init (CcnetThreadedRpcserverProcClass *klass)
|
||||
{
|
||||
CcnetProcessorClass *proc_class = CCNET_PROCESSOR_CLASS (klass);
|
||||
|
||||
proc_class->start = start;
|
||||
proc_class->handle_update = handle_update;
|
||||
proc_class->release_resource = release_resource;
|
||||
proc_class->name = "threaded-rpcserver-proc";
|
||||
|
||||
g_type_class_add_private (klass, sizeof(CcnetThreadedRpcserverProcPriv));
|
||||
}
|
||||
|
||||
static void
|
||||
ccnet_threaded_rpcserver_proc_init (CcnetThreadedRpcserverProc *processor)
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
static int
|
||||
start (CcnetProcessor *processor, int argc, char **argv)
|
||||
{
|
||||
ccnet_processor_send_response (processor, SC_OK, SS_OK, NULL, 0);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void *
|
||||
call_function_job (void *vprocessor)
|
||||
{
|
||||
CcnetProcessor *processor = vprocessor;
|
||||
CcnetThreadedRpcserverProcPriv *priv = GET_PRIV(processor);
|
||||
char *svc_name = processor->name;
|
||||
|
||||
priv->buf = searpc_server_call_function (svc_name, priv->call_buf, priv->call_len,
|
||||
&priv->len);
|
||||
g_free (priv->call_buf);
|
||||
|
||||
return vprocessor;
|
||||
}
|
||||
|
||||
static void
|
||||
call_function_done (void *vprocessor)
|
||||
{
|
||||
CcnetProcessor *processor = vprocessor;
|
||||
CcnetThreadedRpcserverProcPriv *priv;
|
||||
|
||||
priv = GET_PRIV(processor);
|
||||
|
||||
if (priv->buf) {
|
||||
if (priv->len < MAX_TRANSFER_LENGTH) {
|
||||
ccnet_processor_send_response (processor, SC_SERVER_RET, SS_SERVER_RET,
|
||||
priv->buf, priv->len);
|
||||
g_free (priv->buf);
|
||||
priv->buf = NULL;
|
||||
/* ccnet_processor_done (processor, TRUE); */
|
||||
return;
|
||||
}
|
||||
|
||||
/* we need to split data into multiple segments */
|
||||
ccnet_processor_send_response (processor, SC_SERVER_MORE,
|
||||
SS_SERVER_MORE, priv->buf,
|
||||
MAX_TRANSFER_LENGTH);
|
||||
priv->off = MAX_TRANSFER_LENGTH;
|
||||
} else {
|
||||
char *message = priv->error_message ? priv->error_message : "";
|
||||
ccnet_processor_send_response (processor, SC_SERVER_ERR,
|
||||
message,
|
||||
NULL, 0);
|
||||
g_free (priv->error_message);
|
||||
ccnet_processor_done (processor, FALSE);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
handle_update (CcnetProcessor *processor,
|
||||
char *code, char *code_msg,
|
||||
char *content, int clen)
|
||||
{
|
||||
CcnetThreadedRpcserverProcPriv *priv = GET_PRIV (processor);
|
||||
|
||||
if (memcmp (code, SC_CLIENT_CALL, 3) == 0) {
|
||||
priv->call_buf = g_memdup (content, clen);
|
||||
priv->call_len = (gsize)clen;
|
||||
ccnet_processor_thread_create (processor,
|
||||
NULL,
|
||||
call_function_job,
|
||||
call_function_done,
|
||||
processor);
|
||||
return;
|
||||
}
|
||||
|
||||
if (memcmp (code, SC_CLIENT_MORE, 3) == 0) {
|
||||
if (priv->off + MAX_TRANSFER_LENGTH < priv->len) {
|
||||
ccnet_processor_send_response (
|
||||
processor, SC_SERVER_MORE, SS_SERVER_MORE,
|
||||
priv->buf + priv->off, MAX_TRANSFER_LENGTH);
|
||||
priv->off += MAX_TRANSFER_LENGTH;
|
||||
} else {
|
||||
ccnet_processor_send_response (
|
||||
processor, SC_SERVER_RET, SS_SERVER_RET,
|
||||
priv->buf + priv->off, priv->len - priv->off);
|
||||
g_free (priv->buf);
|
||||
priv->buf = NULL;
|
||||
/* ccnet_processor_done (processor, TRUE); */
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
ccnet_processor_send_response (processor, SC_BAD_UPDATE_CODE,
|
||||
SS_BAD_UPDATE_CODE, NULL, 0);
|
||||
g_warning ("[rpc-server] Bad update: %s %s.\n", code, code_msg);
|
||||
ccnet_processor_done (processor, FALSE);
|
||||
}
|
81
lib/timer.c
Normal file
81
lib/timer.c
Normal file
@ -0,0 +1,81 @@
|
||||
/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
|
||||
|
||||
#if defined(__FreeBSD__) || defined(__NetBSD__) || defined(__OpenBSD__)
|
||||
#include <event2/event.h>
|
||||
#include <event2/event_compat.h>
|
||||
#include <event2/event_struct.h>
|
||||
#else
|
||||
#include <event.h>
|
||||
#endif
|
||||
|
||||
#include <sys/time.h>
|
||||
|
||||
#ifdef CCNET_LIB
|
||||
#include "libccnet_utils.h"
|
||||
#define timeval_from_msec ccnet_util_timeval_from_msec
|
||||
#else
|
||||
#include "utils.h"
|
||||
#endif
|
||||
|
||||
#include "timer.h"
|
||||
|
||||
struct CcnetTimer
|
||||
{
|
||||
struct event event;
|
||||
struct timeval tv;
|
||||
TimerCB func;
|
||||
void *user_data;
|
||||
uint8_t inCallback;
|
||||
};
|
||||
|
||||
static void
|
||||
timer_callback (evutil_socket_t fd, short event, void *vtimer)
|
||||
{
|
||||
int more;
|
||||
struct CcnetTimer *timer = vtimer;
|
||||
|
||||
timer->inCallback = 1;
|
||||
more = (*timer->func) (timer->user_data);
|
||||
timer->inCallback = 0;
|
||||
|
||||
if (more)
|
||||
evtimer_add (&timer->event, &timer->tv);
|
||||
else
|
||||
ccnet_timer_free (&timer);
|
||||
}
|
||||
|
||||
void
|
||||
ccnet_timer_free (CcnetTimer **ptimer)
|
||||
{
|
||||
CcnetTimer *timer;
|
||||
|
||||
/* zero out the argument passed in */
|
||||
g_return_if_fail (ptimer);
|
||||
|
||||
timer = *ptimer;
|
||||
*ptimer = NULL;
|
||||
|
||||
/* destroy the timer directly or via the command queue */
|
||||
if (timer && !timer->inCallback)
|
||||
{
|
||||
event_del (&timer->event);
|
||||
g_free (timer);
|
||||
}
|
||||
}
|
||||
|
||||
CcnetTimer*
|
||||
ccnet_timer_new (TimerCB func,
|
||||
void *user_data,
|
||||
uint64_t interval_milliseconds)
|
||||
{
|
||||
CcnetTimer *timer = g_new0 (CcnetTimer, 1);
|
||||
|
||||
timer->tv = timeval_from_msec (interval_milliseconds);
|
||||
timer->func = func;
|
||||
timer->user_data = user_data;
|
||||
|
||||
evtimer_set (&timer->event, timer_callback, timer);
|
||||
evtimer_add (&timer->event, &timer->tv);
|
||||
|
||||
return timer;
|
||||
}
|
1657
lib/utils.c
Normal file
1657
lib/utils.c
Normal file
File diff suppressed because it is too large
Load Diff
332
lib/utils.h
Normal file
332
lib/utils.h
Normal file
@ -0,0 +1,332 @@
|
||||
/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
|
||||
|
||||
#ifndef CCNET_UTILS_H
|
||||
#define CCNET_UTILS_H
|
||||
|
||||
#include <sys/time.h>
|
||||
#include <stdint.h>
|
||||
#include <unistd.h>
|
||||
#include <stdarg.h>
|
||||
#include <glib.h>
|
||||
#include <glib-object.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#if defined(__FreeBSD__) || defined(__NetBSD__) || defined(__OpenBSD__)
|
||||
#include <event2/util.h>
|
||||
#else
|
||||
#include <evutil.h>
|
||||
#endif
|
||||
|
||||
#ifdef WIN32
|
||||
#include <errno.h>
|
||||
#include <glib/gstdio.h>
|
||||
|
||||
#ifndef WEXITSTATUS
|
||||
#define WEXITSTATUS(status) (((status) & 0xff00) >> 8)
|
||||
#endif
|
||||
|
||||
/* Borrowed from libevent */
|
||||
#define ccnet_pipe_t intptr_t
|
||||
|
||||
int pgpipe (ccnet_pipe_t handles[2]);
|
||||
#define ccnet_mkdir(a,b) g_mkdir((a),(b))
|
||||
#define ccnet_pipe(a) pgpipe((a))
|
||||
#define piperead(a,b,c) recv((a),(b),(c),0)
|
||||
#define pipewrite(a,b,c) send((a),(b),(c),0)
|
||||
#define pipeclose(a) closesocket((a))
|
||||
|
||||
static inline int ccnet_rename(const char *oldfile, const char *newfile)
|
||||
{
|
||||
int ret = g_rename (oldfile, newfile);
|
||||
if (ret < 0) {
|
||||
if (errno != EEXIST)
|
||||
return -1;
|
||||
|
||||
ret = g_unlink(oldfile);
|
||||
|
||||
if (ret < 0) {
|
||||
g_warning("ccnet_rename failed because g_unlink failed\n");
|
||||
return -1;
|
||||
}
|
||||
return g_rename(oldfile, newfile);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
#else
|
||||
|
||||
#define ccnet_pipe_t int
|
||||
|
||||
#define ccnet_mkdir(a,b) g_mkdir((a),(b))
|
||||
#define ccnet_pipe(a) pipe((a))
|
||||
#define piperead(a,b,c) read((a),(b),(c))
|
||||
#define pipewrite(a,b,c) write((a),(b),(c))
|
||||
#define pipeclose(a) close((a))
|
||||
#define ccnet_rename g_rename
|
||||
#endif
|
||||
|
||||
#define pipereadn(a,b,c) recvn((a),(b),(c))
|
||||
#define pipewriten(a,b,c) sendn((a),(b),(c))
|
||||
|
||||
#ifndef O_BINARY
|
||||
#define O_BINARY 0
|
||||
#endif
|
||||
|
||||
/* for debug */
|
||||
#ifndef ccnet_warning
|
||||
#define ccnet_warning(fmt, ...) g_warning("%s(%d): " fmt, __FILE__, __LINE__, ##__VA_ARGS__)
|
||||
#endif
|
||||
|
||||
#ifndef ccnet_error
|
||||
#define ccnet_error(fmt, ...) g_error("%s(%d): " fmt, __FILE__, __LINE__, ##__VA_ARGS__)
|
||||
#endif
|
||||
|
||||
#ifndef ccnet_message
|
||||
#define ccnet_message(fmt, ...) g_message("%s(%d): " fmt, __FILE__, __LINE__, ##__VA_ARGS__)
|
||||
#endif
|
||||
|
||||
#define CCNET_DOMAIN g_quark_from_string("ccnet")
|
||||
|
||||
|
||||
struct timeval timeval_from_msec (uint64_t milliseconds);
|
||||
|
||||
|
||||
size_t ccnet_strlcpy (char *dst, const char *src, size_t size);
|
||||
|
||||
void rawdata_to_hex (const unsigned char *rawdata, char *hex_str, int n_bytes);
|
||||
int hex_to_rawdata (const char *hex_str, unsigned char *rawdata, int n_bytes);
|
||||
|
||||
#define sha1_to_hex(sha1, hex) rawdata_to_hex((sha1), (hex), 20)
|
||||
#define hex_to_sha1(hex, sha1) hex_to_rawdata((hex), (sha1), 20)
|
||||
|
||||
int calculate_sha1 (unsigned char *sha1, const char *msg);
|
||||
int ccnet_sha1_equal (const void *v1, const void *v2);
|
||||
unsigned int ccnet_sha1_hash (const void *v);
|
||||
|
||||
char* gen_uuid ();
|
||||
void gen_uuid_inplace (char *buf);
|
||||
gboolean is_uuid_valid (const char *uuid_str);
|
||||
|
||||
|
||||
/* dir operations */
|
||||
int checkdir (const char *dir);
|
||||
int checkdir_with_mkdir (const char *path);
|
||||
char* ccnet_expand_path (const char *src);
|
||||
|
||||
/**
|
||||
* Make directory with 256 sub-directories from '00' to 'ff'.
|
||||
* `base` and subdir will be created if they are not existing.
|
||||
*/
|
||||
int objstore_mkdir (const char *base);
|
||||
void objstore_get_path (char *path, const char *base, const char *obj_id);
|
||||
|
||||
|
||||
char** strsplit_by_space (char *string, int *length);
|
||||
|
||||
/* Read "n" bytes from a descriptor. */
|
||||
ssize_t readn(int fd, void *vptr, size_t n);
|
||||
ssize_t writen(int fd, const void *vptr, size_t n);
|
||||
|
||||
/* Read "n" bytes from a socket. */
|
||||
ssize_t recvn(evutil_socket_t fd, void *vptr, size_t n);
|
||||
ssize_t sendn(evutil_socket_t fd, const void *vptr, size_t n);
|
||||
|
||||
int copy_fd (int ifd, int ofd);
|
||||
int copy_file (const char *dst, const char *src, int mode);
|
||||
|
||||
|
||||
/* string utilities */
|
||||
|
||||
char** strsplit_by_char (char *string, int *length, char c);
|
||||
|
||||
char * strjoin_n (const char *seperator, int argc, char **argv);
|
||||
|
||||
int is_ipaddr_valid (const char *ip);
|
||||
|
||||
typedef void (*KeyValueFunc) (void *data, const char *key, char *value);
|
||||
void parse_key_value_pairs (char *string, KeyValueFunc func, void *data);
|
||||
|
||||
typedef gboolean (*KeyValueFunc2) (void *data, const char *key,
|
||||
const char *value);
|
||||
void parse_key_value_pairs2 (char *string, KeyValueFunc2 func, void *data);
|
||||
|
||||
gchar* ccnet_key_file_get_string (GKeyFile *keyf,
|
||||
const char *category,
|
||||
const char *key);
|
||||
|
||||
|
||||
GList *string_list_append (GList *str_list, const char *string);
|
||||
GList *string_list_append_sorted (GList *str_list, const char *string);
|
||||
GList *string_list_remove (GList *str_list, const char *string);
|
||||
void string_list_free (GList *str_list);
|
||||
gboolean string_list_is_exists (GList *str_list, const char *string);
|
||||
void string_list_join (GList *str_list, GString *strbuf, const char *seperator);
|
||||
GList *string_list_parse (const char *list_in_str, const char *seperator);
|
||||
GList *string_list_parse_sorted (const char *list_in_str, const char *seperator);
|
||||
gboolean string_list_sorted_is_equal (GList *list1, GList *list2);
|
||||
|
||||
char** ncopy_string_array (char **orig, int n);
|
||||
void nfree_string_array (char **array, int n);
|
||||
|
||||
/* 64bit time */
|
||||
gint64 get_current_time();
|
||||
|
||||
int
|
||||
ccnet_generate_cipher (const char *passwd, int plen,
|
||||
unsigned char *key, unsigned char *iv);
|
||||
|
||||
int
|
||||
ccnet_encrypt_with_key (char **data_out,
|
||||
int *out_len,
|
||||
const char *data_in,
|
||||
const int in_len,
|
||||
const unsigned char *key,
|
||||
const unsigned char *iv);
|
||||
|
||||
int
|
||||
ccnet_decrypt_with_key (char **data_out,
|
||||
int *out_len,
|
||||
const char *data_in,
|
||||
const int in_len,
|
||||
const unsigned char *key,
|
||||
const unsigned char *iv);
|
||||
|
||||
int
|
||||
ccnet_encrypt (char **data_out,
|
||||
int *out_len,
|
||||
const char *data_in,
|
||||
const int in_len,
|
||||
const char *code,
|
||||
const int code_len);
|
||||
|
||||
|
||||
int
|
||||
ccnet_decrypt (char **data_out,
|
||||
int *out_len,
|
||||
const char *data_in,
|
||||
const int in_len,
|
||||
const char *code,
|
||||
const int code_len);
|
||||
|
||||
|
||||
/*
|
||||
* Utility functions for converting data to/from network byte order.
|
||||
*/
|
||||
|
||||
static inline uint64_t
|
||||
hton64(uint64_t val)
|
||||
{
|
||||
uint64_t ret;
|
||||
uint8_t *ptr = (uint8_t *)&ret;
|
||||
|
||||
ptr[0]=((val)>>56)&0xFF;
|
||||
ptr[1]=((val)>>48)&0xFF;
|
||||
ptr[2]=((val)>>40)&0xFF;
|
||||
ptr[3]=((val)>>32)&0xFF;
|
||||
ptr[4]=((val)>>24)&0xFF;
|
||||
ptr[5]=((val)>>16)&0xFF;
|
||||
ptr[6]=((val)>>8)&0xFF;
|
||||
ptr[7]=(val)&0xFF;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static inline uint64_t
|
||||
ntoh64(uint64_t val)
|
||||
{
|
||||
uint64_t t64;
|
||||
uint8_t *ptr = (uint8_t *)&val;
|
||||
|
||||
t64=(ptr[3]+256*(ptr[2]+256*(ptr[1]+256*ptr[0])));
|
||||
t64<<=32;
|
||||
t64|=((ptr[7]+256*(ptr[6]+256*(ptr[5]+256*ptr[4]))))&0xffffffffU;
|
||||
|
||||
return t64;
|
||||
}
|
||||
|
||||
static inline void put64bit(uint8_t **ptr,uint64_t val) {
|
||||
(*ptr)[0]=((val)>>56)&0xFF;
|
||||
(*ptr)[1]=((val)>>48)&0xFF;
|
||||
(*ptr)[2]=((val)>>40)&0xFF;
|
||||
(*ptr)[3]=((val)>>32)&0xFF;
|
||||
(*ptr)[4]=((val)>>24)&0xFF;
|
||||
(*ptr)[5]=((val)>>16)&0xFF;
|
||||
(*ptr)[6]=((val)>>8)&0xFF;
|
||||
(*ptr)[7]=(val)&0xFF;
|
||||
(*ptr)+=8;
|
||||
}
|
||||
|
||||
static inline void put32bit(uint8_t **ptr,uint32_t val) {
|
||||
(*ptr)[0]=((val)>>24)&0xFF;
|
||||
(*ptr)[1]=((val)>>16)&0xFF;
|
||||
(*ptr)[2]=((val)>>8)&0xFF;
|
||||
(*ptr)[3]=(val)&0xFF;
|
||||
(*ptr)+=4;
|
||||
}
|
||||
|
||||
static inline void put16bit(uint8_t **ptr,uint16_t val) {
|
||||
(*ptr)[0]=((val)>>8)&0xFF;
|
||||
(*ptr)[1]=(val)&0xFF;
|
||||
(*ptr)+=2;
|
||||
}
|
||||
|
||||
static inline void put8bit(uint8_t **ptr,uint8_t val) {
|
||||
(*ptr)[0]=(val)&0xFF;
|
||||
(*ptr)++;
|
||||
}
|
||||
|
||||
static inline uint64_t get64bit(const uint8_t **ptr) {
|
||||
uint64_t t64;
|
||||
t64=((*ptr)[3]+256*((*ptr)[2]+256*((*ptr)[1]+256*(*ptr)[0])));
|
||||
t64<<=32;
|
||||
t64|=(((*ptr)[7]+256*((*ptr)[6]+256*((*ptr)[5]+256*(*ptr)[4]))))&0xffffffffU;
|
||||
(*ptr)+=8;
|
||||
return t64;
|
||||
}
|
||||
|
||||
static inline uint32_t get32bit(const uint8_t **ptr) {
|
||||
uint32_t t32;
|
||||
t32=((*ptr)[3]+256*((*ptr)[2]+256*((*ptr)[1]+256*(*ptr)[0])));
|
||||
(*ptr)+=4;
|
||||
return t32;
|
||||
}
|
||||
|
||||
static inline uint16_t get16bit(const uint8_t **ptr) {
|
||||
uint32_t t16;
|
||||
t16=(*ptr)[1]+256*(*ptr)[0];
|
||||
(*ptr)+=2;
|
||||
return t16;
|
||||
}
|
||||
|
||||
static inline uint8_t get8bit(const uint8_t **ptr) {
|
||||
uint32_t t8;
|
||||
t8=(*ptr)[0];
|
||||
(*ptr)++;
|
||||
return t8;
|
||||
}
|
||||
|
||||
/* Convert between local encoding and utf8. Returns the converted
|
||||
* string if success, otherwise return NULL
|
||||
*/
|
||||
char *ccnet_locale_from_utf8 (const gchar *src);
|
||||
char *ccnet_locale_to_utf8 (const gchar *src);
|
||||
|
||||
/* Detect whether a process with the given name is running right now. */
|
||||
gboolean process_is_running(const char *name);
|
||||
|
||||
#ifdef WIN32
|
||||
int win32_kill_process (const char *process_name_in);
|
||||
int win32_spawn_process (char *cmd, char *wd);
|
||||
char *wchar_to_utf8 (const wchar_t *src);
|
||||
wchar_t *wchar_from_utf8 (const char *src);
|
||||
char **get_argv_utf8 (int *argc);
|
||||
#endif
|
||||
|
||||
char* ccnet_object_type_from_id (const char *object_id);
|
||||
|
||||
gint64 ccnet_calc_directory_size (const char *path, GError **error);
|
||||
|
||||
#ifdef WIN32
|
||||
char * strtok_r(char *s, const char *delim, char **save_ptr);
|
||||
#endif
|
||||
|
||||
#endif
|
11
libccnet.pc.in
Normal file
11
libccnet.pc.in
Normal file
@ -0,0 +1,11 @@
|
||||
prefix=(DESTDIR)@prefix@
|
||||
exec_prefix=@exec_prefix@
|
||||
libdir=@libdir@
|
||||
includedir=@includedir@
|
||||
|
||||
Name: libccnet
|
||||
Description: Client library for accessing ccnet service.
|
||||
Version: @VERSION@
|
||||
Libs: -L${libdir} -lccnet @SEARPC_LIBS@ @LIBEVENT_LIBS@ @LIB_UUID@
|
||||
Cflags: -I${includedir} @SEARPC_CFLAGS@ @LIBEVENT_CFLAGS@
|
||||
Requires: gobject-2.0 glib-2.0
|
66
m4/python.m4
Normal file
66
m4/python.m4
Normal file
@ -0,0 +1,66 @@
|
||||
## this one is commonly used with AM_PATH_PYTHONDIR ...
|
||||
dnl AM_CHECK_PYMOD(MODNAME [,SYMBOL [,ACTION-IF-FOUND [,ACTION-IF-NOT-FOUND]]])
|
||||
dnl Check if a module containing a given symbol is visible to python.
|
||||
AC_DEFUN([AM_CHECK_PYMOD],
|
||||
[AC_REQUIRE([AM_PATH_PYTHON])
|
||||
py_mod_var=`echo $1['_']$2 | sed 'y%./+-%__p_%'`
|
||||
AC_MSG_CHECKING(for ifelse([$2],[],,[$2 in ])python module $1)
|
||||
AC_CACHE_VAL(py_cv_mod_$py_mod_var, [
|
||||
ifelse([$2],[], [prog="
|
||||
import sys
|
||||
try:
|
||||
import $1
|
||||
except ImportError:
|
||||
sys.exit(1)
|
||||
except:
|
||||
sys.exit(0)
|
||||
sys.exit(0)"], [prog="
|
||||
import $1
|
||||
$1.$2"])
|
||||
if $PYTHON -c "$prog" 1>&AC_FD_CC 2>&AC_FD_CC
|
||||
then
|
||||
eval "py_cv_mod_$py_mod_var=yes"
|
||||
else
|
||||
eval "py_cv_mod_$py_mod_var=no"
|
||||
fi
|
||||
])
|
||||
py_val=`eval "echo \`echo '$py_cv_mod_'$py_mod_var\`"`
|
||||
if test "x$py_val" != xno; then
|
||||
AC_MSG_RESULT(yes)
|
||||
ifelse([$3], [],, [$3
|
||||
])dnl
|
||||
else
|
||||
AC_MSG_RESULT(no)
|
||||
ifelse([$4], [],, [$4
|
||||
])dnl
|
||||
fi
|
||||
])
|
||||
|
||||
dnl a macro to check for ability to create python extensions
|
||||
dnl AM_CHECK_PYTHON_HEADERS([ACTION-IF-POSSIBLE], [ACTION-IF-NOT-POSSIBLE])
|
||||
dnl function also defines PYTHON_INCLUDES
|
||||
AC_DEFUN([AM_CHECK_PYTHON_HEADERS],
|
||||
[AC_REQUIRE([AM_PATH_PYTHON])
|
||||
AC_MSG_CHECKING(for headers required to compile python extensions)
|
||||
dnl deduce PYTHON_INCLUDES
|
||||
py_prefix=`$PYTHON -c "import sys; print sys.prefix"`
|
||||
py_exec_prefix=`$PYTHON -c "import sys; print sys.exec_prefix"`
|
||||
if test -x "$PYTHON-config"; then
|
||||
PYTHON_INCLUDES=`$PYTHON-config --includes 2>/dev/null`
|
||||
else
|
||||
PYTHON_INCLUDES="-I${py_prefix}/include/python${PYTHON_VERSION}"
|
||||
if test "$py_prefix" != "$py_exec_prefix"; then
|
||||
PYTHON_INCLUDES="$PYTHON_INCLUDES -I${py_exec_prefix}/include/python${PYTHON_VERSION}"
|
||||
fi
|
||||
fi
|
||||
AC_SUBST(PYTHON_INCLUDES)
|
||||
dnl check if the headers exist:
|
||||
save_CPPFLAGS="$CPPFLAGS"
|
||||
CPPFLAGS="$CPPFLAGS $PYTHON_INCLUDES"
|
||||
AC_TRY_CPP([#include <Python.h>],dnl
|
||||
[AC_MSG_RESULT(found)
|
||||
$1],dnl
|
||||
[AC_MSG_RESULT(not found)
|
||||
$2])
|
||||
CPPFLAGS="$save_CPPFLAGS"
|
||||
])
|
1
net/Makefile.am
Normal file
1
net/Makefile.am
Normal file
@ -0,0 +1 @@
|
||||
SUBDIRS = common server
|
1
net/common/Makefile.am
Normal file
1
net/common/Makefile.am
Normal file
@ -0,0 +1 @@
|
||||
SUBDIRS = db-wrapper
|
124
net/common/algorithms.c
Normal file
124
net/common/algorithms.c
Normal file
@ -0,0 +1,124 @@
|
||||
/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
|
||||
|
||||
#include "common.h"
|
||||
|
||||
#include "peer.h"
|
||||
#include "session.h"
|
||||
#include "peer-mgr.h"
|
||||
|
||||
#include "message.h"
|
||||
#include "message-manager.h"
|
||||
|
||||
#include "proc-factory.h"
|
||||
#include "processors/sendmsg-proc.h"
|
||||
|
||||
#include "algorithms.h"
|
||||
|
||||
#define DEBUG_FLAG CCNET_DEBUG_MESSAGE
|
||||
#include "log.h"
|
||||
|
||||
|
||||
#if 0
|
||||
static void notify_sendmsg_success (CcnetSession *session, CcnetMessage *ack_msg,
|
||||
char *destid)
|
||||
{
|
||||
CcnetMessageManager *msg_mgr = session->msg_mgr;
|
||||
CcnetMessage *msg;
|
||||
char buf[128] = {0};
|
||||
|
||||
g_assert (ack_msg != NULL);
|
||||
snprintf (buf, sizeof(buf), "%s %s", ack_msg->id, destid);
|
||||
msg = ccnet_message_new (session->base.id, ack_msg->from, ack_msg->app,
|
||||
buf, FLAG_IS_ACK);
|
||||
if (memcmp(session->base.id, ack_msg->from, 40) == 0)
|
||||
ccnet_message_manager_add_msg (msg_mgr, msg, MSG_TYPE_SYS);
|
||||
else
|
||||
ccnet_send_message (session, msg);
|
||||
}
|
||||
|
||||
static void msg_cb (CcnetProcessor *proc, gboolean success, void *data)
|
||||
{
|
||||
CcnetMessage *msg = data;
|
||||
if (success)
|
||||
notify_sendmsg_success (proc->session, msg, proc->peer->id);
|
||||
}
|
||||
#endif /* 0 */
|
||||
|
||||
static void
|
||||
ccnet_send_msg_to_peer (CcnetSession *session, CcnetPeer *peer,
|
||||
CcnetMessage *msg)
|
||||
{
|
||||
CcnetProcessor *processor;
|
||||
CcnetProcFactory *factory = session->proc_factory;
|
||||
|
||||
if (peer->net_state == PEER_CONNECTED) {
|
||||
processor = ccnet_proc_factory_create_master_processor
|
||||
(factory, "send-msg", peer);
|
||||
|
||||
ccnet_sendmsg_proc_set_msg (CCNET_SENDMSG_PROC(processor), msg);
|
||||
/* g_signal_connect (processor, "done", */
|
||||
/* G_CALLBACK(msg_cb), msg); */
|
||||
ccnet_processor_start (processor, 0, NULL);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
ccnet_send_message (CcnetSession *session, CcnetMessage *msg)
|
||||
{
|
||||
CcnetPeer *peer;
|
||||
|
||||
peer = ccnet_peer_manager_get_peer (session->peer_mgr, msg->to);
|
||||
if (peer) {
|
||||
if (peer->is_self)
|
||||
ccnet_message_manager_add_msg (session->msg_mgr,
|
||||
msg, MSG_TYPE_RECV);
|
||||
else
|
||||
ccnet_send_msg_to_peer (session, peer, msg);
|
||||
|
||||
g_object_unref (peer);
|
||||
} else
|
||||
g_warning ("Invalid peer id %s\n", msg->to);
|
||||
}
|
||||
|
||||
|
||||
static gboolean
|
||||
check_message_permission (CcnetSession *session, CcnetMessage *msg)
|
||||
{
|
||||
CcnetPeerManager *peer_mgr = session->peer_mgr;
|
||||
CcnetPeer *from;
|
||||
|
||||
if (strcmp(msg->app, IPEERMGR_APP) == 0)
|
||||
return TRUE;
|
||||
|
||||
if (msg->flags & FLAG_IS_ACK)
|
||||
return TRUE;
|
||||
|
||||
from = ccnet_peer_manager_get_peer (peer_mgr, msg->from);
|
||||
if (!from)
|
||||
return FALSE;
|
||||
/* peer to peer message */
|
||||
/* TODO: improve performance */
|
||||
if (ccnet_peer_has_role(from, "MyPeer") ||
|
||||
ccnet_peer_has_role(from, "MyRelay") ||
|
||||
ccnet_peer_has_role(from, "MyClient")) {
|
||||
g_object_unref (from);
|
||||
return TRUE;
|
||||
}
|
||||
g_object_unref (from);
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
|
||||
int
|
||||
ccnet_recv_message (CcnetSession *session, CcnetMessage *msg)
|
||||
{
|
||||
CcnetMessageManager *msg_mgr = session->msg_mgr;
|
||||
|
||||
if (check_message_permission(session, msg) == FALSE)
|
||||
return -1;
|
||||
|
||||
ccnet_message_manager_add_msg (msg_mgr, msg, MSG_TYPE_RECV);
|
||||
|
||||
return 0;
|
||||
}
|
10
net/common/algorithms.h
Normal file
10
net/common/algorithms.h
Normal file
@ -0,0 +1,10 @@
|
||||
/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
|
||||
|
||||
#ifndef CCNET_ALGORITHMS_H
|
||||
#define CCNET_ALGORITHMS_H
|
||||
|
||||
void ccnet_send_message (CcnetSession *session, CcnetMessage *msg);
|
||||
int ccnet_recv_message (CcnetSession *session, CcnetMessage *msg);
|
||||
|
||||
|
||||
#endif
|
114
net/common/ccnet-config.c
Normal file
114
net/common/ccnet-config.c
Normal file
@ -0,0 +1,114 @@
|
||||
/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
|
||||
|
||||
#include "common.h"
|
||||
|
||||
#include "ccnet-config.h"
|
||||
|
||||
static gboolean
|
||||
get_value (sqlite3_stmt *stmt, void *data)
|
||||
{
|
||||
char **p_value = data;
|
||||
|
||||
*p_value = g_strdup((char *) sqlite3_column_text (stmt, 0));
|
||||
/* Only one result. */
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
static char *
|
||||
config_get_string (sqlite3 *config_db, const char *key)
|
||||
{
|
||||
char sql[256];
|
||||
char *value = NULL;
|
||||
|
||||
snprintf (sql, sizeof(sql),
|
||||
"SELECT value FROM Config WHERE key='%s';",
|
||||
key);
|
||||
if (sqlite_foreach_selected_row (config_db, sql,
|
||||
get_value, &value) < 0)
|
||||
return NULL;
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
char *
|
||||
ccnet_session_config_get_string (CcnetSession *session,
|
||||
const char *key)
|
||||
{
|
||||
return (config_get_string (session->config_db, key));
|
||||
}
|
||||
|
||||
int
|
||||
ccnet_session_config_get_int (CcnetSession *session,
|
||||
const char *key,
|
||||
gboolean *exists)
|
||||
{
|
||||
char *value;
|
||||
int ret;
|
||||
|
||||
value = config_get_string (session->config_db, key);
|
||||
if (!value) {
|
||||
*exists = FALSE;
|
||||
return -1;
|
||||
}
|
||||
|
||||
*exists = TRUE;
|
||||
ret = atoi (value);
|
||||
g_free (value);
|
||||
return ret;
|
||||
}
|
||||
|
||||
int
|
||||
ccnet_session_config_set_string (CcnetSession *session,
|
||||
const char *key,
|
||||
const char *value)
|
||||
{
|
||||
char sql[256];
|
||||
|
||||
snprintf (sql, sizeof(sql),
|
||||
"REPLACE INTO Config VALUES ('%s', '%s');",
|
||||
key, value);
|
||||
if (sqlite_query_exec (session->config_db, sql) < 0)
|
||||
return -1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
ccnet_session_config_set_int (CcnetSession *session,
|
||||
const char *key,
|
||||
int value)
|
||||
{
|
||||
char sql[256];
|
||||
|
||||
snprintf (sql, sizeof(sql),
|
||||
"REPLACE INTO Config VALUES ('%s', %d);",
|
||||
key, value);
|
||||
if (sqlite_query_exec (session->config_db, sql) < 0)
|
||||
return -1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
sqlite3 *
|
||||
ccnet_session_config_open_db (const char *ccnet_dir)
|
||||
{
|
||||
sqlite3 *db;
|
||||
char *db_path;
|
||||
|
||||
db_path = g_build_filename (ccnet_dir, "config.db", NULL);
|
||||
if (sqlite_open_db (db_path, &db) < 0)
|
||||
return NULL;
|
||||
g_free (db_path);
|
||||
|
||||
/*
|
||||
* Values are stored in text. You should convert it
|
||||
* back to integer if needed when you read it from
|
||||
* db.
|
||||
*/
|
||||
char *sql = "CREATE TABLE IF NOT EXISTS Config ("
|
||||
"key TEXT PRIMARY KEY, "
|
||||
"value TEXT);";
|
||||
sqlite_query_exec (db, sql);
|
||||
|
||||
return db;
|
||||
}
|
39
net/common/ccnet-config.h
Normal file
39
net/common/ccnet-config.h
Normal file
@ -0,0 +1,39 @@
|
||||
/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
|
||||
|
||||
#ifndef SEAFILE_CONFIG_H
|
||||
#define SEAFILE_CONFIG_H
|
||||
|
||||
#include "session.h"
|
||||
#include "db.h"
|
||||
|
||||
/*
|
||||
* Returns: config value in string. The string should be freed by caller.
|
||||
*/
|
||||
char *
|
||||
ccnet_session_config_get_string (CcnetSession *session,
|
||||
const char *key);
|
||||
|
||||
/*
|
||||
* Returns:
|
||||
* If key exists, @exists will be set to TRUE and returns the value;
|
||||
* otherwise, @exists will be set to FALSE and returns -1.
|
||||
*/
|
||||
int
|
||||
ccnet_session_config_get_int (CcnetSession *session,
|
||||
const char *key,
|
||||
gboolean *exists);
|
||||
|
||||
int
|
||||
ccnet_session_config_set_string (CcnetSession *session,
|
||||
const char *key,
|
||||
const char *value);
|
||||
|
||||
int
|
||||
ccnet_session_config_set_int (CcnetSession *session,
|
||||
const char *key,
|
||||
int value);
|
||||
|
||||
sqlite3 *
|
||||
ccnet_session_config_open_db (const char *ccnet_dir);
|
||||
|
||||
#endif
|
973
net/common/ccnet-db.c
Normal file
973
net/common/ccnet-db.c
Normal file
@ -0,0 +1,973 @@
|
||||
|
||||
#include "common.h"
|
||||
|
||||
#include "log.h"
|
||||
|
||||
#include "db-wrapper/db-wrapper.h"
|
||||
#include "ccnet-db.h"
|
||||
|
||||
#define MAX_GET_CONNECTION_RETRIES 3
|
||||
|
||||
struct CcnetDB {
|
||||
int type;
|
||||
DBConnPool *pool;
|
||||
};
|
||||
|
||||
struct CcnetDBRow {
|
||||
ResultSet *res;
|
||||
};
|
||||
|
||||
struct CcnetDBTrans {
|
||||
DBConnection *conn;
|
||||
};
|
||||
|
||||
CcnetDB *
|
||||
ccnet_db_new_mysql (const char *host,
|
||||
int port,
|
||||
const char *user,
|
||||
const char *passwd,
|
||||
const char *db_name,
|
||||
const char *unix_socket,
|
||||
gboolean use_ssl,
|
||||
const char *charset,
|
||||
int max_connections)
|
||||
{
|
||||
CcnetDB *db;
|
||||
|
||||
db = g_new0 (CcnetDB, 1);
|
||||
if (!db) {
|
||||
ccnet_warning ("Failed to alloc db structure.\n");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
db->type = CCNET_DB_TYPE_MYSQL;
|
||||
|
||||
db->pool = db_conn_pool_new_mysql (host, user, passwd, port, db_name,
|
||||
unix_socket, use_ssl, charset, max_connections);
|
||||
|
||||
return db;
|
||||
}
|
||||
|
||||
CcnetDB *
|
||||
ccnet_db_new_pgsql (const char *host,
|
||||
const char *user,
|
||||
const char *passwd,
|
||||
const char *db_name,
|
||||
const char *unix_socket)
|
||||
{
|
||||
CcnetDB *db;
|
||||
|
||||
db = g_new0 (CcnetDB, 1);
|
||||
if (!db) {
|
||||
ccnet_warning ("Failed to alloc db structure.\n");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
db->type = CCNET_DB_TYPE_PGSQL;
|
||||
|
||||
db->pool = db_conn_pool_new_pgsql (host, user, passwd, db_name, unix_socket);
|
||||
|
||||
return db;
|
||||
}
|
||||
|
||||
CcnetDB *
|
||||
ccnet_db_new_sqlite (const char *db_path)
|
||||
{
|
||||
CcnetDB *db;
|
||||
|
||||
db = g_new0 (CcnetDB, 1);
|
||||
if (!db) {
|
||||
ccnet_warning ("Failed to alloc db structure.\n");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
db->type = CCNET_DB_TYPE_SQLITE;
|
||||
|
||||
db->pool = db_conn_pool_new_sqlite (db_path, 0);
|
||||
|
||||
return db;
|
||||
}
|
||||
|
||||
void
|
||||
ccnet_db_free (CcnetDB *db)
|
||||
{
|
||||
db_conn_pool_free (db->pool);
|
||||
g_free (db);
|
||||
}
|
||||
|
||||
int
|
||||
ccnet_db_type (CcnetDB *db)
|
||||
{
|
||||
return db->type;
|
||||
}
|
||||
|
||||
static DBConnection *
|
||||
get_db_connection (CcnetDB *db)
|
||||
{
|
||||
DBConnection *conn;
|
||||
GError *error = NULL;
|
||||
|
||||
conn = db_conn_pool_get_connection (db->pool, &error);
|
||||
|
||||
if (!conn) {
|
||||
ccnet_warning ("Failed to get database connection: %s.\n", error->message);
|
||||
g_clear_error (&error);
|
||||
}
|
||||
|
||||
return conn;
|
||||
}
|
||||
|
||||
int
|
||||
ccnet_db_query (CcnetDB *db, const char *sql)
|
||||
{
|
||||
GError *error = NULL;
|
||||
int ret = 0;
|
||||
|
||||
DBConnection *conn = get_db_connection (db);
|
||||
if (!conn)
|
||||
return -1;
|
||||
|
||||
if (!db_connection_execute (conn, sql, &error)) {
|
||||
ccnet_warning ("Error exec query %s: %s.\n", sql, error->message);
|
||||
g_clear_error (&error);
|
||||
ret = -1;
|
||||
}
|
||||
|
||||
db_connection_close (conn);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
gboolean
|
||||
ccnet_db_check_for_existence (CcnetDB *db, const char *sql, gboolean *db_err)
|
||||
{
|
||||
DBConnection *conn;
|
||||
ResultSet *result;
|
||||
gboolean ret = TRUE;
|
||||
GError *error = NULL;
|
||||
|
||||
*db_err = FALSE;
|
||||
|
||||
conn = get_db_connection (db);
|
||||
if (!conn) {
|
||||
*db_err = TRUE;
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
result = db_connection_execute_query (conn, sql, &error);
|
||||
if (error) {
|
||||
ccnet_warning ("Error exec query %s: %s.\n", sql, error->message);
|
||||
g_clear_error (&error);
|
||||
*db_err = TRUE;
|
||||
ret = FALSE;
|
||||
goto out;
|
||||
}
|
||||
|
||||
ret = result_set_next (result, &error);
|
||||
if (error) {
|
||||
ccnet_warning ("Error exec query %s: %s.\n", sql, error->message);
|
||||
g_clear_error (&error);
|
||||
*db_err = TRUE;
|
||||
}
|
||||
|
||||
out:
|
||||
db_connection_close (conn);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
int
|
||||
ccnet_db_foreach_selected_row (CcnetDB *db, const char *sql,
|
||||
CcnetDBRowFunc callback, void *data)
|
||||
{
|
||||
DBConnection *conn;
|
||||
ResultSet *result;
|
||||
CcnetDBRow ccnet_row;
|
||||
int n_rows = 0;
|
||||
GError *error = NULL;
|
||||
|
||||
conn = get_db_connection (db);
|
||||
if (!conn)
|
||||
return -1;
|
||||
|
||||
result = db_connection_execute_query (conn, sql, &error);
|
||||
if (error) {
|
||||
ccnet_warning ("Error exec query %s: %s.\n", sql, error->message);
|
||||
g_clear_error (&error);
|
||||
n_rows = -1;
|
||||
goto out;
|
||||
}
|
||||
|
||||
ccnet_row.res = result;
|
||||
|
||||
while (result_set_next (result, &error)) {
|
||||
n_rows++;
|
||||
if (!callback (&ccnet_row, data))
|
||||
break;
|
||||
}
|
||||
|
||||
if (error) {
|
||||
ccnet_warning ("Error exec query %s: %s.\n", sql, error->message);
|
||||
g_clear_error (&error);
|
||||
n_rows = -1;
|
||||
}
|
||||
|
||||
out:
|
||||
db_connection_close (conn);
|
||||
return n_rows;
|
||||
}
|
||||
|
||||
const char *
|
||||
ccnet_db_row_get_column_text (CcnetDBRow *row, guint32 idx)
|
||||
{
|
||||
GError *error = NULL;
|
||||
g_return_val_if_fail (idx < result_set_get_column_count(row->res), NULL);
|
||||
|
||||
return result_set_get_string (row->res, idx, &error);
|
||||
}
|
||||
|
||||
int
|
||||
ccnet_db_row_get_column_int (CcnetDBRow *row, guint32 idx)
|
||||
{
|
||||
GError *error = NULL;
|
||||
g_return_val_if_fail (idx < result_set_get_column_count(row->res), -1);
|
||||
|
||||
return result_set_get_int (row->res, idx, &error);
|
||||
}
|
||||
|
||||
gint64
|
||||
ccnet_db_row_get_column_int64 (CcnetDBRow *row, guint32 idx)
|
||||
{
|
||||
GError *error = NULL;
|
||||
g_return_val_if_fail (idx < result_set_get_column_count(row->res), -1);
|
||||
|
||||
return result_set_get_int64 (row->res, idx, &error);
|
||||
}
|
||||
|
||||
int
|
||||
ccnet_db_get_int (CcnetDB *db, const char *sql)
|
||||
{
|
||||
int ret = -1;
|
||||
DBConnection *conn;
|
||||
ResultSet *result;
|
||||
CcnetDBRow ccnet_row;
|
||||
GError *error = NULL;
|
||||
|
||||
conn = get_db_connection (db);
|
||||
if (!conn)
|
||||
return -1;
|
||||
|
||||
result = db_connection_execute_query (conn, sql, &error);
|
||||
if (error) {
|
||||
ccnet_warning ("Error exec query %s: %s.\n", sql, error->message);
|
||||
g_clear_error (&error);
|
||||
goto out;
|
||||
}
|
||||
|
||||
ccnet_row.res = result;
|
||||
if (!result_set_next (result, &error)) {
|
||||
if (error) {
|
||||
ccnet_warning ("Error exec query %s: %s.\n", sql, error->message);
|
||||
g_clear_error (&error);
|
||||
}
|
||||
goto out;
|
||||
}
|
||||
|
||||
ret = ccnet_db_row_get_column_int (&ccnet_row, 0);
|
||||
|
||||
out:
|
||||
db_connection_close (conn);
|
||||
return ret;
|
||||
}
|
||||
|
||||
gint64
|
||||
ccnet_db_get_int64 (CcnetDB *db, const char *sql)
|
||||
{
|
||||
gint64 ret = -1;
|
||||
DBConnection *conn;
|
||||
ResultSet *result;
|
||||
CcnetDBRow ccnet_row;
|
||||
GError *error = NULL;
|
||||
|
||||
conn = get_db_connection (db);
|
||||
if (!conn)
|
||||
return -1;
|
||||
|
||||
result = db_connection_execute_query (conn, sql, &error);
|
||||
if (error) {
|
||||
ccnet_warning ("Error exec query %s: %s.\n", sql, error->message);
|
||||
g_clear_error (&error);
|
||||
goto out;
|
||||
}
|
||||
|
||||
ccnet_row.res = result;
|
||||
if (!result_set_next (result, &error)) {
|
||||
if (error) {
|
||||
ccnet_warning ("Error exec query %s: %s.\n", sql, error->message);
|
||||
g_clear_error (&error);
|
||||
}
|
||||
goto out;
|
||||
}
|
||||
|
||||
ret = ccnet_db_row_get_column_int64 (&ccnet_row, 0);
|
||||
|
||||
out:
|
||||
db_connection_close (conn);
|
||||
return ret;
|
||||
}
|
||||
|
||||
char *
|
||||
ccnet_db_get_string (CcnetDB *db, const char *sql)
|
||||
{
|
||||
char *ret = NULL;
|
||||
const char *s;
|
||||
DBConnection *conn;
|
||||
ResultSet *result;
|
||||
CcnetDBRow ccnet_row;
|
||||
GError *error = NULL;
|
||||
|
||||
conn = get_db_connection (db);
|
||||
if (!conn)
|
||||
return NULL;
|
||||
|
||||
result = db_connection_execute_query (conn, sql, &error);
|
||||
if (error) {
|
||||
ccnet_warning ("Error exec query %s: %s.\n", sql, error->message);
|
||||
g_clear_error (&error);
|
||||
goto out;
|
||||
}
|
||||
|
||||
ccnet_row.res = result;
|
||||
if (!result_set_next (result, &error)) {
|
||||
if (error) {
|
||||
ccnet_warning ("Error exec query %s: %s.\n", sql, error->message);
|
||||
g_clear_error (&error);
|
||||
}
|
||||
goto out;
|
||||
}
|
||||
|
||||
s = ccnet_db_row_get_column_text (&ccnet_row, 0);
|
||||
ret = g_strdup(s);
|
||||
|
||||
out:
|
||||
db_connection_close (conn);
|
||||
return ret;
|
||||
}
|
||||
|
||||
char *
|
||||
ccnet_db_escape_string (CcnetDB *db, const char *from)
|
||||
{
|
||||
const char *p = from;
|
||||
char *to, *q;
|
||||
|
||||
to = g_malloc0 (2*strlen(from)+1);
|
||||
q = to;
|
||||
|
||||
while (*p != '\0') {
|
||||
if (*p == '\'' || *p == '\\' || *p == '"') {
|
||||
*q = *p;
|
||||
*(++q) = *p;
|
||||
} else
|
||||
*q = *p;
|
||||
++p;
|
||||
++q;
|
||||
}
|
||||
|
||||
return to;
|
||||
}
|
||||
|
||||
gboolean
|
||||
pgsql_index_exists (CcnetDB *db, const char *index_name)
|
||||
{
|
||||
char sql[256];
|
||||
gboolean db_err = FALSE;
|
||||
|
||||
snprintf (sql, sizeof(sql),
|
||||
"SELECT 1 FROM pg_class WHERE relname='%s'",
|
||||
index_name);
|
||||
return ccnet_db_check_for_existence (db, sql, &db_err);
|
||||
}
|
||||
|
||||
/* Prepared Statements */
|
||||
|
||||
struct CcnetDBStatement {
|
||||
DBStmt *p;
|
||||
DBConnection *conn;
|
||||
};
|
||||
typedef struct CcnetDBStatement CcnetDBStatement;
|
||||
|
||||
CcnetDBStatement *
|
||||
ccnet_db_prepare_statement (CcnetDB *db, const char *sql)
|
||||
{
|
||||
DBStmt *p;
|
||||
CcnetDBStatement *ret = g_new0 (CcnetDBStatement, 1);
|
||||
GError *error = NULL;
|
||||
|
||||
DBConnection *conn = get_db_connection (db);
|
||||
if (!conn) {
|
||||
g_free (ret);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
p = db_connection_prepare_statement (conn, sql, &error);
|
||||
if (!p) {
|
||||
ccnet_warning ("Error prepare statement %s: %s.\n", sql, error->message);
|
||||
g_clear_error (&error);
|
||||
g_free (ret);
|
||||
db_connection_close (conn);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
ret->p = p;
|
||||
ret->conn = conn;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
void
|
||||
ccnet_db_statement_free (CcnetDBStatement *p)
|
||||
{
|
||||
db_connection_close (p->conn);
|
||||
g_free (p);
|
||||
}
|
||||
|
||||
int
|
||||
ccnet_db_statement_set_int (DBStmt *p, int idx, int x)
|
||||
{
|
||||
GError *error = NULL;
|
||||
|
||||
if (!db_stmt_set_int (p, idx, x, &error)) {
|
||||
ccnet_warning ("Error set int in prep stmt: %s.\n", error->message);
|
||||
g_clear_error (&error);
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
ccnet_db_statement_set_string (DBStmt *p, int idx, const char *s)
|
||||
{
|
||||
GError *error = NULL;
|
||||
|
||||
if (!db_stmt_set_string (p, idx, s, &error)) {
|
||||
ccnet_warning ("Error set string in prep stmt: %s.\n", error->message);
|
||||
g_clear_error (&error);
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
ccnet_db_statement_set_int64 (DBStmt *p, int idx, gint64 x)
|
||||
{
|
||||
GError *error = NULL;
|
||||
|
||||
if (!db_stmt_set_int64 (p, idx, x, &error)) {
|
||||
ccnet_warning ("Error set int64 in prep stmt: %s.\n", error->message);
|
||||
g_clear_error (&error);
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
set_parameters_va (DBStmt *p, int n, va_list args)
|
||||
{
|
||||
int i;
|
||||
const char *type;
|
||||
|
||||
for (i = 0; i < n; ++i) {
|
||||
type = va_arg (args, const char *);
|
||||
if (strcmp(type, "int") == 0) {
|
||||
int x = va_arg (args, int);
|
||||
if (ccnet_db_statement_set_int (p, i, x) < 0)
|
||||
return -1;
|
||||
} else if (strcmp (type, "int64") == 0) {
|
||||
gint64 x = va_arg (args, gint64);
|
||||
if (ccnet_db_statement_set_int64 (p, i, x) < 0)
|
||||
return -1;
|
||||
} else if (strcmp (type, "string") == 0) {
|
||||
const char *s = va_arg (args, const char *);
|
||||
if (ccnet_db_statement_set_string (p, i, s) < 0)
|
||||
return -1;
|
||||
} else {
|
||||
ccnet_warning ("BUG: invalid prep stmt parameter type %s.\n", type);
|
||||
g_return_val_if_reached (-1);
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
ccnet_db_statement_query (CcnetDB *db, const char *sql, int n, ...)
|
||||
{
|
||||
CcnetDBStatement *p;
|
||||
int ret = 0;
|
||||
GError *error = NULL;
|
||||
|
||||
p = ccnet_db_prepare_statement (db, sql);
|
||||
if (!p)
|
||||
return -1;
|
||||
|
||||
va_list args;
|
||||
va_start (args, n);
|
||||
if (set_parameters_va (p->p, n, args) < 0) {
|
||||
ccnet_db_statement_free (p);
|
||||
va_end (args);
|
||||
return -1;
|
||||
}
|
||||
va_end (args);
|
||||
|
||||
if (!db_stmt_execute (p->p, &error)) {
|
||||
ccnet_warning ("Error execute prep stmt: %s.\n", error->message);
|
||||
g_clear_error (&error);
|
||||
ret = -1;
|
||||
}
|
||||
|
||||
ccnet_db_statement_free (p);
|
||||
return ret;
|
||||
}
|
||||
|
||||
gboolean
|
||||
ccnet_db_statement_exists (CcnetDB *db, const char *sql, int n, ...)
|
||||
{
|
||||
CcnetDBStatement *p;
|
||||
ResultSet *result;
|
||||
gboolean ret = TRUE;
|
||||
GError *error = NULL;
|
||||
|
||||
p = ccnet_db_prepare_statement (db, sql);
|
||||
if (!p) {
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
va_list args;
|
||||
va_start (args, n);
|
||||
if (set_parameters_va (p->p, n, args) < 0) {
|
||||
ccnet_db_statement_free (p);
|
||||
va_end (args);
|
||||
return FALSE;
|
||||
}
|
||||
va_end (args);
|
||||
|
||||
result = db_stmt_execute_query (p->p, &error);
|
||||
if (error) {
|
||||
ccnet_warning ("Error exec prep stmt: %s.\n", error->message);
|
||||
g_clear_error (&error);
|
||||
ret = FALSE;
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (!result_set_next (result, &error))
|
||||
ret = FALSE;
|
||||
|
||||
if (error) {
|
||||
ccnet_warning ("Error get next result from prep stmt: %s.\n", error->message);
|
||||
g_clear_error (&error);
|
||||
}
|
||||
|
||||
out:
|
||||
ccnet_db_statement_free (p);
|
||||
return ret;
|
||||
}
|
||||
|
||||
int
|
||||
ccnet_db_statement_foreach_row (CcnetDB *db,
|
||||
const char *sql,
|
||||
CcnetDBRowFunc callback, void *data,
|
||||
int n, ...)
|
||||
{
|
||||
CcnetDBStatement *p;
|
||||
ResultSet *result;
|
||||
CcnetDBRow ccnet_row;
|
||||
int n_rows = 0;
|
||||
GError *error = NULL;
|
||||
|
||||
p = ccnet_db_prepare_statement (db, sql);
|
||||
if (!p)
|
||||
return -1;
|
||||
|
||||
va_list args;
|
||||
va_start (args, n);
|
||||
if (set_parameters_va (p->p, n, args) < 0) {
|
||||
ccnet_db_statement_free (p);
|
||||
va_end (args);
|
||||
return -1;
|
||||
}
|
||||
va_end (args);
|
||||
|
||||
result = db_stmt_execute_query (p->p, &error);
|
||||
if (error) {
|
||||
ccnet_warning ("Error exec prep stmt: %s.\n", error->message);
|
||||
g_clear_error (&error);
|
||||
n_rows = -1;
|
||||
goto out;
|
||||
}
|
||||
|
||||
ccnet_row.res = result;
|
||||
|
||||
while (result_set_next (result, &error)) {
|
||||
n_rows++;
|
||||
if (!callback (&ccnet_row, data))
|
||||
break;
|
||||
}
|
||||
|
||||
if (error) {
|
||||
ccnet_warning ("Error get next result from prep stmt: %s.\n", error->message);
|
||||
g_clear_error (&error);
|
||||
n_rows = -1;
|
||||
}
|
||||
|
||||
out:
|
||||
ccnet_db_statement_free (p);
|
||||
return n_rows;
|
||||
}
|
||||
|
||||
int
|
||||
ccnet_db_statement_get_int (CcnetDB *db, const char *sql, int n, ...)
|
||||
{
|
||||
CcnetDBStatement *p;
|
||||
int ret = -1;
|
||||
ResultSet *result;
|
||||
CcnetDBRow ccnet_row;
|
||||
GError *error = NULL;
|
||||
|
||||
p = ccnet_db_prepare_statement (db, sql);
|
||||
if (!p)
|
||||
return -1;
|
||||
|
||||
va_list args;
|
||||
va_start (args, n);
|
||||
if (set_parameters_va (p->p, n, args) < 0) {
|
||||
ccnet_db_statement_free (p);
|
||||
va_end (args);
|
||||
return -1;
|
||||
}
|
||||
va_end (args);
|
||||
|
||||
result = db_stmt_execute_query (p->p, &error);
|
||||
if (error) {
|
||||
ccnet_warning ("Error exec prep stmt: %s.\n", error->message);
|
||||
g_clear_error (&error);
|
||||
goto out;
|
||||
}
|
||||
|
||||
ccnet_row.res = result;
|
||||
if (!result_set_next (result, &error)) {
|
||||
if (error) {
|
||||
ccnet_warning ("Error get next result from prep stmt: %s.\n", error->message);
|
||||
g_clear_error (&error);
|
||||
}
|
||||
goto out;
|
||||
}
|
||||
|
||||
ret = ccnet_db_row_get_column_int (&ccnet_row, 0);
|
||||
|
||||
out:
|
||||
ccnet_db_statement_free (p);
|
||||
return ret;
|
||||
}
|
||||
|
||||
gint64
|
||||
ccnet_db_statement_get_int64 (CcnetDB *db, const char *sql, int n, ...)
|
||||
{
|
||||
CcnetDBStatement *p;
|
||||
gint64 ret = -1;
|
||||
ResultSet *result;
|
||||
CcnetDBRow ccnet_row;
|
||||
GError *error = NULL;
|
||||
|
||||
p = ccnet_db_prepare_statement (db, sql);
|
||||
if (!p)
|
||||
return -1;
|
||||
|
||||
va_list args;
|
||||
va_start (args, n);
|
||||
if (set_parameters_va (p->p, n, args) < 0) {
|
||||
ccnet_db_statement_free (p);
|
||||
va_end (args);
|
||||
return -1;
|
||||
}
|
||||
va_end (args);
|
||||
|
||||
result = db_stmt_execute_query (p->p, &error);
|
||||
if (error) {
|
||||
ccnet_warning ("Error exec prep stmt: %s.\n", error->message);
|
||||
g_clear_error (&error);
|
||||
goto out;
|
||||
}
|
||||
|
||||
ccnet_row.res = result;
|
||||
if (!result_set_next (result, &error)) {
|
||||
if (error) {
|
||||
ccnet_warning ("Error get next result from prep stmt: %s.\n", error->message);
|
||||
g_clear_error (&error);
|
||||
}
|
||||
goto out;
|
||||
}
|
||||
|
||||
ret = ccnet_db_row_get_column_int64 (&ccnet_row, 0);
|
||||
|
||||
out:
|
||||
ccnet_db_statement_free (p);
|
||||
return ret;
|
||||
}
|
||||
|
||||
char *
|
||||
ccnet_db_statement_get_string (CcnetDB *db, const char *sql, int n, ...)
|
||||
{
|
||||
CcnetDBStatement *p;
|
||||
char *ret = NULL;
|
||||
const char *s;
|
||||
ResultSet *result;
|
||||
CcnetDBRow ccnet_row;
|
||||
GError *error = NULL;
|
||||
|
||||
p = ccnet_db_prepare_statement (db, sql);
|
||||
if (!p)
|
||||
return NULL;
|
||||
|
||||
va_list args;
|
||||
va_start (args, n);
|
||||
if (set_parameters_va (p->p, n, args) < 0) {
|
||||
ccnet_db_statement_free (p);
|
||||
va_end (args);
|
||||
return NULL;
|
||||
}
|
||||
va_end (args);
|
||||
|
||||
result = db_stmt_execute_query (p->p, &error);
|
||||
if (error) {
|
||||
ccnet_warning ("Error exec prep stmt: %s.\n", error->message);
|
||||
g_clear_error (&error);
|
||||
goto out;
|
||||
}
|
||||
|
||||
ccnet_row.res = result;
|
||||
if (!result_set_next (result, &error)) {
|
||||
if (error) {
|
||||
ccnet_warning ("Error get next result from prep stmt: %s.\n", error->message);
|
||||
g_clear_error (&error);
|
||||
}
|
||||
goto out;
|
||||
}
|
||||
|
||||
s = ccnet_db_row_get_column_text (&ccnet_row, 0);
|
||||
ret = g_strdup(s);
|
||||
|
||||
out:
|
||||
ccnet_db_statement_free (p);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Transaction */
|
||||
|
||||
CcnetDBTrans *
|
||||
ccnet_db_begin_transaction (CcnetDB *db)
|
||||
{
|
||||
DBConnection *conn;
|
||||
CcnetDBTrans *trans;
|
||||
GError *error = NULL;
|
||||
|
||||
trans = g_new0 (CcnetDBTrans, 1);
|
||||
if (!trans)
|
||||
return NULL;
|
||||
|
||||
conn = get_db_connection (db);
|
||||
if (!conn) {
|
||||
g_free (trans);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
trans->conn = conn;
|
||||
if (!db_connection_begin_transaction (trans->conn, &error)) {
|
||||
ccnet_warning ("Start transaction failed: %s.\n", error->message);
|
||||
g_clear_error (&error);
|
||||
db_connection_close (trans->conn);
|
||||
g_free (trans);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return trans;
|
||||
}
|
||||
|
||||
void
|
||||
ccnet_db_trans_close (CcnetDBTrans *trans)
|
||||
{
|
||||
db_connection_close (trans->conn);
|
||||
g_free (trans);
|
||||
}
|
||||
|
||||
int
|
||||
ccnet_db_commit (CcnetDBTrans *trans)
|
||||
{
|
||||
DBConnection *conn = trans->conn;
|
||||
GError *error = NULL;
|
||||
|
||||
if (!db_connection_commit (conn, &error)) {
|
||||
ccnet_warning ("Commit failed: %s.\n", error->message);
|
||||
g_clear_error (&error);
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
ccnet_db_rollback (CcnetDBTrans *trans)
|
||||
{
|
||||
DBConnection *conn = trans->conn;
|
||||
GError *error = NULL;
|
||||
|
||||
if (!db_connection_commit (conn, &error)) {
|
||||
ccnet_warning ("Rollback failed: %s.\n", error->message);
|
||||
g_clear_error (&error);
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static DBStmt *
|
||||
trans_prepare_statement (DBConnection *conn, const char *sql)
|
||||
{
|
||||
DBStmt *p;
|
||||
GError *error = NULL;
|
||||
|
||||
p = db_connection_prepare_statement (conn, sql, &error);
|
||||
if (!p) {
|
||||
ccnet_warning ("Error prepare statement %s: %s.\n", sql, error->message);
|
||||
g_clear_error (&error);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return p;
|
||||
}
|
||||
|
||||
int
|
||||
ccnet_db_trans_query (CcnetDBTrans *trans, const char *sql, int n, ...)
|
||||
{
|
||||
DBStmt *p;
|
||||
GError *error = NULL;
|
||||
|
||||
p = trans_prepare_statement (trans->conn, sql);
|
||||
if (!p)
|
||||
return -1;
|
||||
|
||||
va_list args;
|
||||
va_start (args, n);
|
||||
if (set_parameters_va (p, n, args) < 0) {
|
||||
va_end (args);
|
||||
return -1;
|
||||
}
|
||||
va_end (args);
|
||||
|
||||
if (!db_stmt_execute (p, &error)) {
|
||||
ccnet_warning ("Error exec prep stmt: %s.\n", error->message);
|
||||
g_clear_error (&error);
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
gboolean
|
||||
ccnet_db_trans_check_for_existence (CcnetDBTrans *trans,
|
||||
const char *sql,
|
||||
gboolean *db_err,
|
||||
int n, ...)
|
||||
{
|
||||
ResultSet *result;
|
||||
gboolean ret = TRUE;
|
||||
GError *error = NULL;
|
||||
|
||||
*db_err = FALSE;
|
||||
|
||||
DBStmt *p;
|
||||
|
||||
p = trans_prepare_statement (trans->conn, sql);
|
||||
if (!p) {
|
||||
*db_err = TRUE;
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
va_list args;
|
||||
va_start (args, n);
|
||||
if (set_parameters_va (p, n, args) < 0) {
|
||||
*db_err = TRUE;
|
||||
va_end (args);
|
||||
return FALSE;
|
||||
}
|
||||
va_end (args);
|
||||
|
||||
result = db_stmt_execute_query (p, &error);
|
||||
if (error) {
|
||||
ccnet_warning ("Error exec prep stmt: %s.\n", error->message);
|
||||
g_clear_error (&error);
|
||||
*db_err = TRUE;
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
if (!result_set_next (result, &error))
|
||||
ret = FALSE;
|
||||
|
||||
if (error) {
|
||||
ccnet_warning ("Error get next result from prep stmt: %s.\n", error->message);
|
||||
g_clear_error (&error);
|
||||
*db_err = TRUE;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
int
|
||||
ccnet_db_trans_foreach_selected_row (CcnetDBTrans *trans, const char *sql,
|
||||
CcnetDBRowFunc callback, void *data,
|
||||
int n, ...)
|
||||
{
|
||||
ResultSet *result;
|
||||
CcnetDBRow ccnet_row;
|
||||
int n_rows = 0;
|
||||
GError *error = NULL;
|
||||
|
||||
DBStmt *p;
|
||||
|
||||
p = trans_prepare_statement (trans->conn, sql);
|
||||
if (!p)
|
||||
return FALSE;
|
||||
|
||||
va_list args;
|
||||
va_start (args, n);
|
||||
if (set_parameters_va (p, n, args) < 0) {
|
||||
va_end (args);
|
||||
return -1;
|
||||
}
|
||||
va_end (args);
|
||||
|
||||
result = db_stmt_execute_query (p, &error);
|
||||
if (error) {
|
||||
ccnet_warning ("Error exec prep stmt: %s.\n", error->message);
|
||||
g_clear_error (&error);
|
||||
return -1;
|
||||
}
|
||||
|
||||
ccnet_row.res = result;
|
||||
|
||||
while (result_set_next (result, &error)) {
|
||||
n_rows++;
|
||||
if (!callback (&ccnet_row, data))
|
||||
break;
|
||||
}
|
||||
|
||||
if (error) {
|
||||
ccnet_warning ("Error get next result from prep stmt: %s.\n", error->message);
|
||||
g_clear_error (&error);
|
||||
n_rows = -1;
|
||||
}
|
||||
|
||||
return n_rows;
|
||||
}
|
133
net/common/ccnet-db.h
Normal file
133
net/common/ccnet-db.h
Normal file
@ -0,0 +1,133 @@
|
||||
#ifndef CCNET_DB_H
|
||||
#define CCNET_DB_H
|
||||
|
||||
#define DB_SQLITE "sqlite"
|
||||
#define DB_MYSQL "mysql"
|
||||
#define DB_PGSQL "pgsql"
|
||||
|
||||
enum {
|
||||
CCNET_DB_TYPE_SQLITE,
|
||||
CCNET_DB_TYPE_MYSQL,
|
||||
CCNET_DB_TYPE_PGSQL,
|
||||
};
|
||||
|
||||
typedef struct CcnetDB CcnetDB;
|
||||
typedef struct CcnetDBRow CcnetDBRow;
|
||||
typedef struct CcnetDBTrans CcnetDBTrans;
|
||||
|
||||
typedef gboolean (*CcnetDBRowFunc) (CcnetDBRow *, void *);
|
||||
|
||||
CcnetDB *
|
||||
ccnet_db_new_mysql (const char *host,
|
||||
int port,
|
||||
const char *user,
|
||||
const char *passwd,
|
||||
const char *db,
|
||||
const char *unix_socket,
|
||||
gboolean use_ssl,
|
||||
const char *charset,
|
||||
int max_connections);
|
||||
|
||||
CcnetDB *
|
||||
ccnet_db_new_pgsql (const char *host,
|
||||
const char *user,
|
||||
const char *passwd,
|
||||
const char *db_name,
|
||||
const char *unix_socket);
|
||||
|
||||
CcnetDB *
|
||||
ccnet_db_new_sqlite (const char *db_path);
|
||||
|
||||
void
|
||||
ccnet_db_free (CcnetDB *db);
|
||||
|
||||
int
|
||||
ccnet_db_type (CcnetDB *db);
|
||||
|
||||
int
|
||||
ccnet_db_query (CcnetDB *db, const char *sql);
|
||||
|
||||
gboolean
|
||||
ccnet_db_check_for_existence (CcnetDB *db, const char *sql, gboolean *db_err);
|
||||
|
||||
int
|
||||
ccnet_db_foreach_selected_row (CcnetDB *db, const char *sql,
|
||||
CcnetDBRowFunc callback, void *data);
|
||||
|
||||
const char *
|
||||
ccnet_db_row_get_column_text (CcnetDBRow *row, guint32 idx);
|
||||
|
||||
int
|
||||
ccnet_db_row_get_column_int (CcnetDBRow *row, guint32 idx);
|
||||
|
||||
gint64
|
||||
ccnet_db_row_get_column_int64 (CcnetDBRow *row, guint32 idx);
|
||||
|
||||
int
|
||||
ccnet_db_get_int (CcnetDB *db, const char *sql);
|
||||
|
||||
gint64
|
||||
ccnet_db_get_int64 (CcnetDB *db, const char *sql);
|
||||
|
||||
char *
|
||||
ccnet_db_get_string (CcnetDB *db, const char *sql);
|
||||
|
||||
/* Transaction related */
|
||||
|
||||
CcnetDBTrans *
|
||||
ccnet_db_begin_transaction (CcnetDB *db);
|
||||
|
||||
void
|
||||
ccnet_db_trans_close (CcnetDBTrans *trans);
|
||||
|
||||
int
|
||||
ccnet_db_commit (CcnetDBTrans *trans);
|
||||
|
||||
int
|
||||
ccnet_db_rollback (CcnetDBTrans *trans);
|
||||
|
||||
int
|
||||
ccnet_db_trans_query (CcnetDBTrans *trans, const char *sql, int n, ...);
|
||||
|
||||
gboolean
|
||||
ccnet_db_trans_check_for_existence (CcnetDBTrans *trans,
|
||||
const char *sql,
|
||||
gboolean *db_err,
|
||||
int n, ...);
|
||||
|
||||
int
|
||||
ccnet_db_trans_foreach_selected_row (CcnetDBTrans *trans, const char *sql,
|
||||
CcnetDBRowFunc callback, void *data,
|
||||
int n, ...);
|
||||
|
||||
/* Escape a string contant by doubling '\" characters.
|
||||
*/
|
||||
char *
|
||||
ccnet_db_escape_string (CcnetDB *db, const char *from);
|
||||
|
||||
gboolean
|
||||
pgsql_index_exists (CcnetDB *db, const char *index_name);
|
||||
|
||||
/* Prepared Statements */
|
||||
|
||||
int
|
||||
ccnet_db_statement_query (CcnetDB *db, const char *sql, int n, ...);
|
||||
|
||||
gboolean
|
||||
ccnet_db_statement_exists (CcnetDB *db, const char *sql, int n, ...);
|
||||
|
||||
int
|
||||
ccnet_db_statement_foreach_row (CcnetDB *db, const char *sql,
|
||||
CcnetDBRowFunc callback, void *data,
|
||||
int n, ...);
|
||||
|
||||
int
|
||||
ccnet_db_statement_get_int (CcnetDB *db, const char *sql, int n, ...);
|
||||
|
||||
gint64
|
||||
ccnet_db_statement_get_int64 (CcnetDB *db, const char *sql, int n, ...);
|
||||
|
||||
char *
|
||||
ccnet_db_statement_get_string (CcnetDB *db, const char *sql, int n, ...);
|
||||
|
||||
#endif
|
31
net/common/common.h
Normal file
31
net/common/common.h
Normal file
@ -0,0 +1,31 @@
|
||||
/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
|
||||
|
||||
#ifndef COMMON_H
|
||||
#define COMMON_H
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include <config.h>
|
||||
#endif
|
||||
|
||||
#include <unistd.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdint.h> /* uint32_t */
|
||||
#include <sys/types.h> /* size_t */
|
||||
#include <errno.h>
|
||||
#include <string.h>
|
||||
#include <limits.h>
|
||||
|
||||
#include "option.h"
|
||||
|
||||
#ifndef ULLONG_MAX
|
||||
#define ULLONG_MAX 18446744073709551615ULL
|
||||
#endif
|
||||
|
||||
#define IPEERMGR_APP "IPeerMgr"
|
||||
|
||||
#include <glib.h>
|
||||
|
||||
#include "utils.h"
|
||||
#include <ccnet/valid-check.h>
|
||||
|
||||
#endif
|
693
net/common/connect-mgr.c
Normal file
693
net/common/connect-mgr.c
Normal file
@ -0,0 +1,693 @@
|
||||
/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
|
||||
|
||||
#include "common.h"
|
||||
|
||||
#include <stdio.h>
|
||||
#include <event2/util.h>
|
||||
|
||||
#include "net.h"
|
||||
#include "packet.h"
|
||||
#include "packet-io.h"
|
||||
#include "peer.h"
|
||||
#include "session.h"
|
||||
#include "handshake.h"
|
||||
#include "message.h"
|
||||
#include "peer-mgr.h"
|
||||
#include "connect-mgr.h"
|
||||
#include "message-manager.h"
|
||||
#include "proc-factory.h"
|
||||
|
||||
|
||||
#define MAX_RECONNECTIONS_PER_PULSE 5
|
||||
#define LISTEN_INTERVAL 1000 /* 1s */
|
||||
#define RECONNECT_PERIOD_MSEC 10000
|
||||
|
||||
|
||||
#define DEBUG_FLAG CCNET_DEBUG_CONNECTION
|
||||
#include "log.h"
|
||||
|
||||
extern void ccnet_peer_set_net_state (CcnetPeer *peer, int net_state);
|
||||
extern gboolean
|
||||
ccnet_peer_manager_on_peer_resolved (CcnetPeerManager *manager,
|
||||
CcnetPeer *peer);
|
||||
extern void
|
||||
ccnet_peer_manager_on_peer_resolve_failed (CcnetPeerManager *manager,
|
||||
CcnetPeer *peer);
|
||||
|
||||
static void dns_lookup_peer (CcnetPeer* peer);
|
||||
|
||||
CcnetConnManager *
|
||||
ccnet_conn_manager_new (CcnetSession *session)
|
||||
{
|
||||
CcnetConnManager *manager;
|
||||
|
||||
manager = g_new0 (CcnetConnManager, 1);
|
||||
manager->session = session;
|
||||
|
||||
return manager;
|
||||
}
|
||||
|
||||
#if 0
|
||||
/* Note: The handshake timeout is 10s.
|
||||
* So intervals[0] must be larger than 10s to
|
||||
* prevent simultaneously connection tryings.
|
||||
* Actually any value larger than 10 is ok.
|
||||
* Here we just set it to 20s.
|
||||
*/
|
||||
static int intervals[] = {
|
||||
20,
|
||||
30,
|
||||
60,
|
||||
15 * 60,
|
||||
30 * 60,
|
||||
60 * 60,
|
||||
};
|
||||
|
||||
static int
|
||||
get_reconnect_interval_secs (const CcnetPeer *peer)
|
||||
{
|
||||
if (peer->is_relay)
|
||||
return intervals[0];
|
||||
int index = MIN (peer->num_fails, G_N_ELEMENTS(intervals) - 1);
|
||||
return intervals[index];
|
||||
}
|
||||
#endif
|
||||
|
||||
void start_keepalive (CcnetPeer *peer)
|
||||
{
|
||||
CcnetProcessor *processor;
|
||||
CcnetProcFactory *factory = peer->manager->session->proc_factory;
|
||||
|
||||
processor = ccnet_proc_factory_create_master_processor (factory,
|
||||
"keepalive2", peer);
|
||||
if (processor == NULL) {
|
||||
ccnet_warning ("Create keepalive2 processor failed\n");
|
||||
return;
|
||||
}
|
||||
ccnet_processor_startl (processor, NULL);
|
||||
}
|
||||
|
||||
static void on_peer_connected (CcnetPeer *peer, CcnetPacketIO *io)
|
||||
{
|
||||
g_return_if_fail (peer->net_state == PEER_DOWN);
|
||||
|
||||
ccnet_peer_set_io (peer, io);
|
||||
ccnet_peer_set_net_state (peer, PEER_CONNECTED);
|
||||
start_keepalive (peer);
|
||||
}
|
||||
|
||||
static void notify_found_peer (CcnetSession *session, CcnetPeer *peer)
|
||||
{
|
||||
char buf[128];
|
||||
|
||||
sprintf (buf, "found %s\n", peer->id);
|
||||
CcnetMessage *message = ccnet_message_new (session->base.id,
|
||||
session->base.id,
|
||||
"System", buf, 0);
|
||||
ccnet_message_manager_add_msg (session->msg_mgr,
|
||||
message, MSG_TYPE_SYS);
|
||||
|
||||
}
|
||||
|
||||
static void on_get_resolve_peer_pub_info_done (CcnetProcessor *processor,
|
||||
gboolean success, void *data)
|
||||
{
|
||||
if (success) {
|
||||
gboolean should_continue;
|
||||
should_continue = ccnet_peer_manager_on_peer_resolved (
|
||||
processor->peer->manager, processor->peer);
|
||||
if (!should_continue)
|
||||
return;
|
||||
start_keepalive (processor->peer);
|
||||
} else {
|
||||
ccnet_message ("Get resolved peer pub info failed\n");
|
||||
ccnet_peer_manager_on_peer_resolve_failed (
|
||||
processor->peer->manager, processor->peer);
|
||||
}
|
||||
}
|
||||
|
||||
static void on_resolve_peer_connected (CcnetPeer *peer, CcnetPacketIO *io)
|
||||
{
|
||||
CcnetProcessor *processor;
|
||||
CcnetProcFactory *factory = peer->manager->session->proc_factory;
|
||||
|
||||
ccnet_peer_set_io (peer, io);
|
||||
ccnet_peer_set_net_state (peer, PEER_CONNECTED);
|
||||
|
||||
processor = ccnet_proc_factory_create_master_processor (
|
||||
factory, "get-pubinfo", peer);
|
||||
if (processor == NULL) {
|
||||
ccnet_warning ("Create get-pubinfo processor failed\n");
|
||||
ccnet_peer_shutdown (peer);
|
||||
return;
|
||||
}
|
||||
g_signal_connect (processor, "done",
|
||||
G_CALLBACK(on_get_resolve_peer_pub_info_done), NULL);
|
||||
|
||||
ccnet_processor_startl (processor, NULL);
|
||||
}
|
||||
|
||||
static void on_get_unauth_peer_pub_info_done (CcnetProcessor *processor,
|
||||
gboolean success, void *data)
|
||||
{
|
||||
if (success) {
|
||||
notify_found_peer (processor->session, processor->peer);
|
||||
start_keepalive (processor->peer);
|
||||
} else
|
||||
ccnet_peer_shutdown (processor->peer);
|
||||
}
|
||||
|
||||
static void on_unauthed_peer_connected (CcnetPeer *peer, CcnetPacketIO *io)
|
||||
{
|
||||
CcnetProcessor *processor;
|
||||
CcnetProcFactory *factory = peer->manager->session->proc_factory;
|
||||
|
||||
ccnet_peer_set_io (peer, io);
|
||||
ccnet_peer_set_net_state (peer, PEER_CONNECTED);
|
||||
|
||||
processor = ccnet_proc_factory_create_master_processor (
|
||||
factory, "get-pubinfo", peer);
|
||||
if (processor == NULL) {
|
||||
ccnet_warning ("Create get-pubinfo processor failed\n");
|
||||
ccnet_peer_shutdown (peer);
|
||||
return;
|
||||
}
|
||||
g_signal_connect (processor, "done",
|
||||
G_CALLBACK(on_get_unauth_peer_pub_info_done), NULL);
|
||||
|
||||
ccnet_processor_startl (processor, NULL);
|
||||
}
|
||||
|
||||
static void set_peer_address_from_socket(CcnetPeer *peer, CcnetPacketIO *io)
|
||||
{
|
||||
if (peer == NULL || io == NULL)
|
||||
return;
|
||||
|
||||
if (peer->addr_str)
|
||||
g_free (peer->addr_str);
|
||||
|
||||
struct sockaddr_in addr;
|
||||
socklen_t len = sizeof(struct sockaddr_in);
|
||||
int socket = io->socket;
|
||||
getpeername (socket, (struct sockaddr *)&addr, &len);
|
||||
char *p = inet_ntoa (addr.sin_addr);
|
||||
peer->addr_str = strdup(p);
|
||||
}
|
||||
|
||||
static void
|
||||
myHandshakeDoneCB (CcnetHandshake *handshake,
|
||||
CcnetPacketIO *io,
|
||||
int is_connected,
|
||||
const char *peer_id,
|
||||
void *vmanager)
|
||||
{
|
||||
CcnetConnManager *manager = vmanager;
|
||||
CcnetPeerManager *peerMgr = manager->session->peer_mgr;
|
||||
CcnetPeer *peer;
|
||||
|
||||
if (!is_connected) {
|
||||
if (ccnet_packet_io_is_incoming (io)) {
|
||||
ccnet_warning ("[conn] incoming handshake fails.\n");
|
||||
ccnet_packet_io_free (io);
|
||||
return;
|
||||
}
|
||||
|
||||
/* temporally use peer, so don't need to increase the reference */
|
||||
peer = handshake->peer;
|
||||
if (peer->num_fails == 0) {
|
||||
/* print error for the first time */
|
||||
ccnet_message ("[Conn] peer %s(%.10s) connection fails\n",
|
||||
peer->name, peer->id);
|
||||
} else {
|
||||
ccnet_debug ("[Conn] peer %s(%.10s) connection fails\n",
|
||||
peer->name, peer->id);
|
||||
}
|
||||
if (peer->net_state == PEER_CONNECTED) {
|
||||
ccnet_debug ("[Conn] But Peer %s(%.10s) is already connected me.\n",
|
||||
peer->name, peer->id);
|
||||
} else if (peer->net_state == PEER_DOWN){
|
||||
ccnet_peer_shutdown(peer);
|
||||
}
|
||||
|
||||
ccnet_packet_io_free (io);
|
||||
|
||||
peer->num_fails++;
|
||||
peer->in_connection = 0;
|
||||
return;
|
||||
}
|
||||
|
||||
if (!ccnet_packet_io_is_incoming (io)) {
|
||||
peer = handshake->peer;
|
||||
peer->in_connection = 0;
|
||||
|
||||
if (peer->to_resolve) {
|
||||
if (!peer_id_valid(peer_id)) {
|
||||
/* TODO: Remove the peer */
|
||||
ccnet_warning ("[Conn] Resolving: Received invalid peer id\n");
|
||||
return;
|
||||
}
|
||||
ccnet_debug ("[Conn] Resolving: Peer %.8s is resolved\n", peer_id);
|
||||
memcpy (peer->id, peer_id, 40);
|
||||
peer->id[40] = '\0';
|
||||
on_resolve_peer_connected (peer, io);
|
||||
return;
|
||||
}
|
||||
/* ref for using the peer below */
|
||||
g_object_ref (peer);
|
||||
} else {
|
||||
/* incoming */
|
||||
if (!peer_id_valid (peer_id)) {
|
||||
ccnet_warning ("Invalid peer (no-id) connecting\n");
|
||||
ccnet_packet_io_free (io);
|
||||
return;
|
||||
}
|
||||
peer = ccnet_peer_manager_get_peer (peerMgr, peer_id);
|
||||
if (!peer) {
|
||||
ccnet_message ("Unknown peer %s connecting\n", peer_id);
|
||||
peer = ccnet_peer_new (peer_id);
|
||||
ccnet_peer_manager_add_peer (peerMgr, peer);
|
||||
set_peer_address_from_socket(peer, io);
|
||||
peer->last_up = time(NULL);
|
||||
on_unauthed_peer_connected (peer, io);
|
||||
g_object_unref (peer);
|
||||
return;
|
||||
}
|
||||
|
||||
if (peer->net_state == PEER_CONNECTED) {
|
||||
ccnet_message ("[Conn] Peer %s (%.10s) is already connected."
|
||||
"But a new handshake comes in. Reconnect this peer.\n",
|
||||
peer->name, peer->id);
|
||||
ccnet_peer_shutdown_no_delay (peer);
|
||||
}
|
||||
|
||||
set_peer_address_from_socket(peer, io);
|
||||
peer->last_up = time(NULL);
|
||||
}
|
||||
/* hold a reference on the peer */
|
||||
|
||||
ccnet_message ("[Conn] Peer %s (%.10s) connected\n",
|
||||
peer->name, peer->id);
|
||||
peer->num_fails = 0;
|
||||
on_peer_connected (peer, io);
|
||||
g_object_unref (peer);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* return %TRUE if an outgoing connection is started, %FALSE otherwise.
|
||||
*
|
||||
* When peer's ip address is not looked up yet, an dns request will be sent,
|
||||
* and when dns done, this function will be called again.
|
||||
*/
|
||||
gboolean
|
||||
ccnet_conn_manager_connect_peer (CcnetConnManager *manager, CcnetPeer *peer)
|
||||
{
|
||||
CcnetPacketIO *io;
|
||||
/* int interval; */
|
||||
const char *addr = NULL;
|
||||
int port = 0;
|
||||
|
||||
if (peer->in_connection)
|
||||
return FALSE;
|
||||
|
||||
/* time_t now = time(NULL); */
|
||||
|
||||
if (peer->net_state == PEER_CONNECTED)
|
||||
return FALSE;
|
||||
|
||||
if (peer->dns_addr)
|
||||
addr = peer->dns_addr;
|
||||
else if (!peer->redirected) {
|
||||
if (!peer->public_addr)
|
||||
goto err_connect;
|
||||
|
||||
if (is_valid_ipaddr(peer->public_addr))
|
||||
addr = peer->public_addr;
|
||||
else {
|
||||
dns_lookup_peer (peer);
|
||||
return TRUE; /* same as out going is started */
|
||||
}
|
||||
} else {
|
||||
if (!peer->redirect_addr)
|
||||
goto err_connect;
|
||||
|
||||
if (is_valid_ipaddr(peer->redirect_addr))
|
||||
addr = peer->redirect_addr;
|
||||
else {
|
||||
dns_lookup_peer (peer);
|
||||
return TRUE; /* same as out going is started */
|
||||
}
|
||||
}
|
||||
|
||||
if (!addr)
|
||||
goto err_connect;
|
||||
|
||||
if (!peer->redirected)
|
||||
port = peer->public_port;
|
||||
else
|
||||
port = peer->redirect_port;
|
||||
|
||||
ccnet_peer_update_address (peer, addr, port);
|
||||
|
||||
/* interval = get_reconnect_interval_secs (peer); */
|
||||
/* if (now - peer->last_try_time < interval) { */
|
||||
/* ccnet_debug ("[Conn] Less than interval: (%d - %d = %d, %d)\n", */
|
||||
/* now, peer->last_try_time, now - peer->last_try_time, */
|
||||
/* interval); */
|
||||
/* return FALSE; */
|
||||
/* } */
|
||||
/* peer->last_try_time = now; */
|
||||
|
||||
if (peer->num_fails == 0) {
|
||||
/* print log for the first time */
|
||||
ccnet_message ("[Conn] Start outgoing connect to %s(%.10s) %s:%d\n",
|
||||
peer->name, peer->id, addr, port);
|
||||
} else {
|
||||
ccnet_debug ("[Conn] Start outgoing connect to %s(%.10s) %s:%d\n",
|
||||
peer->name, peer->id, addr, port);
|
||||
}
|
||||
io = ccnet_packet_io_new_outgoing (manager->session, addr, port);
|
||||
|
||||
if (io == NULL) {
|
||||
/* ccnet_warning ("Failed to create socket for peer %s (%.10s)\n",
|
||||
peer->name, peer->id); */
|
||||
goto err_connect;
|
||||
} else {
|
||||
peer->in_connection = 1;
|
||||
ccnet_handshake_new (manager->session, peer, io,
|
||||
myHandshakeDoneCB, manager);
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
err_connect:
|
||||
peer->num_fails++;
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
static void
|
||||
reconnect_peer (CcnetConnManager *manager, CcnetPeer *peer)
|
||||
{
|
||||
if (peer->net_state == PEER_CONNECTED || peer->in_connection)
|
||||
return;
|
||||
/*
|
||||
if (peer->public_addr == NULL) {
|
||||
ccnet_warning ("[conn-mgr] A peer without public_ip and "
|
||||
"domain is entered in conn_list\n");
|
||||
return;
|
||||
}
|
||||
*/
|
||||
ccnet_conn_manager_connect_peer (manager, peer);
|
||||
}
|
||||
|
||||
static int reconnect_pulse (void *vmanager)
|
||||
{
|
||||
CcnetConnManager *manager = vmanager;
|
||||
/* int conn = 0; */
|
||||
GList *ptr;
|
||||
|
||||
#ifndef CCNET_SERVER
|
||||
GList *peers = ccnet_peer_manager_get_peers_with_role (
|
||||
manager->session->peer_mgr, "MyRelay");
|
||||
for (ptr = peers; ptr; ptr = ptr->next) {
|
||||
CcnetPeer *peer = ptr->data;
|
||||
if (peer->redirected) {
|
||||
if (peer->num_fails > 2)
|
||||
ccnet_peer_unset_redirect (peer);
|
||||
}
|
||||
reconnect_peer (manager, peer);
|
||||
g_object_unref (peer);
|
||||
}
|
||||
g_list_free (peers);
|
||||
#endif
|
||||
|
||||
for (ptr = manager->conn_list; ptr; ptr = ptr->next) {
|
||||
CcnetPeer *peer = ptr->data;
|
||||
if (peer->redirected) {
|
||||
if (peer->num_fails > 2)
|
||||
ccnet_peer_unset_redirect (peer);
|
||||
}
|
||||
reconnect_peer (manager, peer);
|
||||
}
|
||||
|
||||
/*
|
||||
peers = ccnet_peer_manager_get_peers_with_role (
|
||||
manager->session->peer_mgr, "MyPeer");
|
||||
for (ptr = peers; ptr; ptr = ptr->next) {
|
||||
CcnetPeer *peer = ptr->data;
|
||||
|
||||
if (peer->is_self)
|
||||
continue;
|
||||
|
||||
if (!peer->is_relay && !ccnet_peer_is_mypeer(peer))
|
||||
continue;
|
||||
|
||||
if (ccnet_conn_manager_connect_peer (manager, peer) == FALSE)
|
||||
continue;
|
||||
|
||||
if (++conn >= MAX_RECONNECTIONS_PER_PULSE)
|
||||
break;
|
||||
}
|
||||
g_list_free (peers);
|
||||
*/
|
||||
|
||||
/* TODO: teer down connections */
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
void
|
||||
ccnet_conn_manager_add_incoming (CcnetConnManager *manager,
|
||||
struct sockaddr_storage *cliaddr,
|
||||
size_t addrlen,
|
||||
evutil_socket_t socket)
|
||||
{
|
||||
CcnetPacketIO *io;
|
||||
|
||||
io = ccnet_packet_io_new_incoming (manager->session, cliaddr, socket);
|
||||
ccnet_handshake_new (manager->session, NULL, io,
|
||||
myHandshakeDoneCB, manager);
|
||||
}
|
||||
|
||||
|
||||
static int
|
||||
listen_pulse (void * vmanager)
|
||||
{
|
||||
CcnetConnManager *manager = vmanager;
|
||||
|
||||
for ( ;; ) /* check for new incoming peer connections */
|
||||
{
|
||||
evutil_socket_t socket;
|
||||
struct sockaddr_storage cliaddr;
|
||||
socklen_t len = sizeof (struct sockaddr_storage);
|
||||
|
||||
if (manager->bind_socket < 0)
|
||||
break;
|
||||
|
||||
if ((socket = ccnet_net_accept (manager->bind_socket,
|
||||
&cliaddr, &len, 1)) < 0)
|
||||
break;
|
||||
|
||||
ccnet_conn_manager_add_incoming (manager, &cliaddr, len, socket);
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
|
||||
#ifndef CCNET_SERVER
|
||||
#endif /* ifndef CCNET_SERVER */
|
||||
|
||||
|
||||
void
|
||||
ccnet_conn_listen_init (CcnetConnManager *manager)
|
||||
{
|
||||
evutil_socket_t socket;
|
||||
CcnetSession *session = manager->session;
|
||||
|
||||
if (session->base.public_port == 0) {
|
||||
ccnet_message ("Do not listen for incoming peers\n");
|
||||
return;
|
||||
}
|
||||
|
||||
socket = ccnet_net_bind_tcp (session->base.public_port, 1);
|
||||
if (socket >= 0) {
|
||||
ccnet_message ("Opened port %d to listen for "
|
||||
"incoming peer connections\n", session->base.public_port);
|
||||
manager->bind_socket = socket;
|
||||
listen (manager->bind_socket, 5);
|
||||
} else {
|
||||
ccnet_error ("Couldn't open port %d to listen for "
|
||||
"incoming peer connections (errno %d - %s)",
|
||||
session->base.public_port, errno, strerror(errno) );
|
||||
exit (1);
|
||||
}
|
||||
|
||||
manager->listen_timer = ccnet_timer_new (listen_pulse, manager,
|
||||
LISTEN_INTERVAL);
|
||||
}
|
||||
|
||||
typedef struct DNSLookupData {
|
||||
CcnetPeer *peer;
|
||||
char *addr_str;
|
||||
} DNSLookupData;
|
||||
|
||||
static void *
|
||||
dns_lookup (void *vdata)
|
||||
{
|
||||
DNSLookupData *data = vdata;
|
||||
struct evutil_addrinfo hints;
|
||||
struct evutil_addrinfo *answer = NULL;
|
||||
int err;
|
||||
void *addr;
|
||||
char *addr_str;
|
||||
socklen_t size;
|
||||
|
||||
/* Build the hints to tell getaddrinfo how to act. */
|
||||
memset(&hints, 0, sizeof(hints));
|
||||
hints.ai_family = AF_INET; /* only use IPv4 now. */
|
||||
hints.ai_socktype = SOCK_STREAM;
|
||||
hints.ai_protocol = IPPROTO_TCP; /* We want a TCP socket */
|
||||
/* Only return addresses we can use. */
|
||||
hints.ai_flags = EVUTIL_AI_ADDRCONFIG;
|
||||
|
||||
/* Look up the hostname. */
|
||||
err = evutil_getaddrinfo(data->peer->public_addr, NULL, &hints, &answer);
|
||||
if (err != 0) {
|
||||
ccnet_warning("Error while resolving '%s': %s\n",
|
||||
data->peer->public_addr, evutil_gai_strerror(err));
|
||||
return vdata;
|
||||
}
|
||||
|
||||
/* just use the first answer */
|
||||
if (answer->ai_family == AF_INET) {
|
||||
size = INET_ADDRSTRLEN;
|
||||
addr = &((struct sockaddr_in *)(answer->ai_addr))->sin_addr;
|
||||
} else if (answer->ai_family == AF_INET6) {
|
||||
size = INET6_ADDRSTRLEN;
|
||||
addr = &((struct sockaddr_in6 *)(answer->ai_addr))->sin6_addr;
|
||||
} else
|
||||
goto out;
|
||||
|
||||
addr_str = (char *)calloc(size, sizeof(char));
|
||||
if (addr_str == NULL) {
|
||||
ccnet_error("Out of memory\n");
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (!evutil_inet_ntop(answer->ai_family, addr, addr_str, size)) {
|
||||
ccnet_warning("Peer %s domain name %s lookup fail\n",
|
||||
data->peer->name, data->peer->public_addr);
|
||||
free(addr_str);
|
||||
goto out;
|
||||
}
|
||||
|
||||
data->addr_str = addr_str;
|
||||
|
||||
out:
|
||||
evutil_freeaddrinfo (answer);
|
||||
return vdata;
|
||||
}
|
||||
|
||||
static void
|
||||
dns_lookup_cb (void *result)
|
||||
{
|
||||
DNSLookupData *data = result;
|
||||
CcnetPeer *peer = data->peer;
|
||||
|
||||
if (!data->addr_str) {
|
||||
ccnet_warning ("DNS lookup failed for peer %.10s(%s).\n",
|
||||
data->peer->id, data->peer->public_addr);
|
||||
g_free (data);
|
||||
return;
|
||||
}
|
||||
|
||||
g_free(peer->dns_addr);
|
||||
peer->dns_addr = g_strdup (data->addr_str);
|
||||
peer->dns_done = 1;
|
||||
ccnet_conn_manager_connect_peer (peer->manager->session->connMgr, peer);
|
||||
|
||||
free(data->addr_str);
|
||||
g_free (data);
|
||||
}
|
||||
|
||||
static void
|
||||
dns_lookup_peer (CcnetPeer* peer)
|
||||
{
|
||||
DNSLookupData *data;
|
||||
|
||||
if (peer->dns_done)
|
||||
return;
|
||||
|
||||
data = g_new0 (DNSLookupData, 1);
|
||||
data->peer = peer;
|
||||
ccnet_job_manager_schedule_job (peer->manager->session->job_mgr,
|
||||
dns_lookup,
|
||||
dns_lookup_cb,
|
||||
data);
|
||||
}
|
||||
|
||||
void
|
||||
ccnet_conn_manager_start (CcnetConnManager *manager)
|
||||
{
|
||||
#ifdef CCNET_SERVER
|
||||
ccnet_conn_listen_init (manager);
|
||||
#endif
|
||||
manager->reconnect_timer = ccnet_timer_new (reconnect_pulse, manager,
|
||||
RECONNECT_PERIOD_MSEC);
|
||||
}
|
||||
|
||||
void
|
||||
ccnet_conn_manager_stop (CcnetConnManager *manager)
|
||||
{
|
||||
evutil_closesocket (manager->bind_socket);
|
||||
manager->bind_socket = 0;
|
||||
|
||||
ccnet_timer_free (&manager->reconnect_timer);
|
||||
ccnet_timer_free (&manager->listen_timer);
|
||||
}
|
||||
|
||||
void
|
||||
ccnet_conn_manager_add_to_conn_list (CcnetConnManager *manager,
|
||||
CcnetPeer *peer)
|
||||
{
|
||||
if (g_list_find (manager->conn_list, peer)) {
|
||||
ccnet_warning ("[Conn] peer %s(%.8s) is already in conn_list\n",
|
||||
peer->name, peer->id);
|
||||
return;
|
||||
}
|
||||
manager->conn_list = g_list_prepend (manager->conn_list, peer);
|
||||
g_object_ref (peer);
|
||||
}
|
||||
|
||||
void
|
||||
ccnet_conn_manager_remove_from_conn_list (CcnetConnManager *manager,
|
||||
CcnetPeer *peer)
|
||||
{
|
||||
/* we can't directly use g_list_remove, since we have to call
|
||||
* g_object_unref, if the peer is actually in the list
|
||||
*/
|
||||
if (!g_list_find (manager->conn_list, peer))
|
||||
return;
|
||||
manager->conn_list = g_list_remove (manager->conn_list, peer);
|
||||
g_object_unref (peer);
|
||||
}
|
||||
|
||||
void
|
||||
ccnet_conn_manager_cancel_conn (CcnetConnManager *manager,
|
||||
const char *addr, int port)
|
||||
{
|
||||
GList *ptr;
|
||||
|
||||
for (ptr = manager->conn_list; ptr; ptr = ptr->next) {
|
||||
CcnetPeer *peer = ptr->data;
|
||||
if (g_strcmp0(peer->public_addr, addr) == 0 && peer->public_port == port) {
|
||||
manager->conn_list = g_list_delete_link (manager->conn_list, ptr);
|
||||
if (peer->to_resolve) {
|
||||
ccnet_peer_manager_on_peer_resolve_failed (
|
||||
manager->session->peer_mgr, peer);
|
||||
}
|
||||
g_object_unref (peer);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
40
net/common/connect-mgr.h
Normal file
40
net/common/connect-mgr.h
Normal file
@ -0,0 +1,40 @@
|
||||
/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
|
||||
|
||||
#ifndef CCNET_CONNECTION_MANAGER
|
||||
#define CCNET_CONNECTION_MANAGER
|
||||
|
||||
#if defined(__FreeBSD__) || defined(__NetBSD__) || defined(__OpenBSD__)
|
||||
#include <event2/event.h>
|
||||
#else
|
||||
#include <event.h>
|
||||
#endif
|
||||
|
||||
#include "timer.h"
|
||||
|
||||
typedef struct CcnetConnManager CcnetConnManager;
|
||||
|
||||
struct CcnetConnManager
|
||||
{
|
||||
CcnetSession *session;
|
||||
|
||||
CcnetTimer *reconnect_timer;
|
||||
CcnetTimer *listen_timer;
|
||||
|
||||
evutil_socket_t bind_socket;
|
||||
|
||||
GList *conn_list;
|
||||
};
|
||||
|
||||
CcnetConnManager *ccnet_conn_manager_new (CcnetSession *session);
|
||||
void ccnet_conn_manager_start (CcnetConnManager *manager);
|
||||
void ccnet_conn_manager_stop (CcnetConnManager *manager);
|
||||
gboolean ccnet_conn_manager_connect_peer (CcnetConnManager *manager,
|
||||
CcnetPeer *peer);
|
||||
void ccnet_conn_manager_add_to_conn_list (CcnetConnManager *manager,
|
||||
CcnetPeer *peer);
|
||||
void ccnet_conn_manager_remove_from_conn_list (CcnetConnManager *manager,
|
||||
CcnetPeer *peer);
|
||||
void ccnet_conn_manager_cancel_conn (CcnetConnManager *manager,
|
||||
const char *addr, int port);
|
||||
|
||||
#endif
|
12
net/common/db-wrapper/Makefile.am
Normal file
12
net/common/db-wrapper/Makefile.am
Normal file
@ -0,0 +1,12 @@
|
||||
AM_CFLAGS = -I$(top_srcdir)/net/common -I$(top_srcdir)/lib \
|
||||
-I$(top_srcdir)/include -I$(top_srcdir)/include/ccnet \
|
||||
-Wall @GLIB2_CFLAGS@ @MSVC_CFLAGS@ @MYSQL_CFLAGS@ @PGSQL_CFLAGS@
|
||||
|
||||
noinst_LTLIBRARIES = libdbwrapper.la
|
||||
|
||||
noinst_HEADERS = db-wrapper.h mysql-db-ops.h sqlite-db-ops.h pgsql-db-ops.h
|
||||
|
||||
libdbwrapper_la_SOURCES = db-wrapper.c mysql-db-ops.c sqlite-db-ops.c pgsql-db-ops.c
|
||||
|
||||
libdbwrapper_la_LDFLAGS = -Wl,-z -Wl,defs
|
||||
libdbwrapper_la_LIBADD = @SSL_LIBS@ @GLIB2_LIBS@ @MYSQL_LIBS@ -lsqlite3 @PGSQL_LIBS@
|
359
net/common/db-wrapper/db-wrapper.c
Normal file
359
net/common/db-wrapper/db-wrapper.c
Normal file
@ -0,0 +1,359 @@
|
||||
#include "common.h"
|
||||
|
||||
#include "db-wrapper.h"
|
||||
#include "mysql-db-ops.h"
|
||||
#include "sqlite-db-ops.h"
|
||||
#include "pgsql-db-ops.h"
|
||||
|
||||
typedef struct DBOperations {
|
||||
void (*db_conn_pool_free) (DBConnPool *);
|
||||
DBConnection* (*get_db_connection) (DBConnPool *, GError **);
|
||||
void (*db_connection_close) (DBConnection *);
|
||||
gboolean (*db_connection_execute) (DBConnection *, const char *, GError **);
|
||||
ResultSet* (*db_connection_execute_query) (DBConnection *, const char *, GError **);
|
||||
gboolean (*result_set_next) (ResultSet *, GError **);
|
||||
const char* (*result_set_get_string) (ResultSet *, int, GError **);
|
||||
void (*result_set_free) (ResultSet *);
|
||||
int (*result_set_get_column_count) (ResultSet *);
|
||||
DBStmt* (*db_connection_prepare_statement) (DBConnection *, const char *, GError **);
|
||||
gboolean (*db_stmt_set_int) (DBStmt *, int, int, GError **);
|
||||
gboolean (*db_stmt_set_int64) (DBStmt *, int, gint64, GError **);
|
||||
gboolean (*db_stmt_set_string) (DBStmt *, int, const char *, GError **);
|
||||
gboolean (*db_stmt_execute) (DBStmt *, GError **);
|
||||
ResultSet* (*db_stmt_execute_query) (DBStmt *, GError **);
|
||||
void (*db_stmt_free) (DBStmt *);
|
||||
gboolean (*db_connection_begin_transaction) (DBConnection *, GError **);
|
||||
gboolean (*db_connection_commit) (DBConnection *, GError **);
|
||||
gboolean (*db_connection_rollback) (DBConnection *, GError **);
|
||||
} DBOperations;
|
||||
|
||||
static DBOperations db_ops;
|
||||
|
||||
/* DB Connection Pool. */
|
||||
|
||||
DBConnPool *
|
||||
db_conn_pool_new_mysql (const char *host,
|
||||
const char *user,
|
||||
const char *password,
|
||||
unsigned int port,
|
||||
const char *db_name,
|
||||
const char *unix_socket,
|
||||
gboolean use_ssl,
|
||||
const char *charset,
|
||||
int max_connections)
|
||||
{
|
||||
db_ops.db_conn_pool_free = mysql_db_conn_pool_free;
|
||||
db_ops.get_db_connection = mysql_get_db_connection;
|
||||
db_ops.db_connection_close = mysql_db_connection_close;
|
||||
db_ops.db_connection_execute = mysql_db_connection_execute;
|
||||
db_ops.db_connection_execute_query = mysql_execute_query;
|
||||
db_ops.result_set_next = mysql_result_set_next;
|
||||
db_ops.result_set_get_string = mysql_result_set_get_string;
|
||||
db_ops.result_set_free = mysql_result_set_free;
|
||||
db_ops.result_set_get_column_count = mysql_result_set_get_column_count;
|
||||
db_ops.db_connection_prepare_statement = mysql_prepare_statement;
|
||||
db_ops.db_stmt_set_int = mysql_stmt_set_int;
|
||||
db_ops.db_stmt_set_int64 = mysql_stmt_set_int64;
|
||||
db_ops.db_stmt_set_string = mysql_stmt_set_string;
|
||||
db_ops.db_stmt_execute = mysql_db_stmt_execute;
|
||||
db_ops.db_stmt_execute_query = mysql_db_stmt_execute_query;
|
||||
db_ops.db_stmt_free = mysql_db_stmt_free;
|
||||
db_ops.db_connection_begin_transaction = mysql_db_begin_transaction;
|
||||
db_ops.db_connection_commit = mysql_db_commit;
|
||||
db_ops.db_connection_rollback = mysql_db_rollback;
|
||||
|
||||
return mysql_db_conn_pool_new (host, user, password, port, db_name, unix_socket,
|
||||
use_ssl, charset, max_connections);
|
||||
}
|
||||
|
||||
DBConnPool *
|
||||
db_conn_pool_new_pgsql (const char *host,
|
||||
const char *user,
|
||||
const char *password,
|
||||
const char *db_name,
|
||||
const char *unix_socket)
|
||||
{
|
||||
db_ops.db_conn_pool_free = pgsql_db_conn_pool_free;
|
||||
db_ops.get_db_connection = pgsql_get_db_connection;
|
||||
db_ops.db_connection_close = pgsql_db_connection_close;
|
||||
db_ops.db_connection_execute = pgsql_db_connection_execute;
|
||||
db_ops.db_connection_execute_query = pgsql_execute_query;
|
||||
db_ops.result_set_next = pgsql_result_set_next;
|
||||
db_ops.result_set_get_string = pgsql_result_set_get_string;
|
||||
db_ops.result_set_free = pgsql_result_set_free;
|
||||
db_ops.result_set_get_column_count = pgsql_result_set_get_column_count;
|
||||
db_ops.db_connection_prepare_statement = pgsql_prepare_statement;
|
||||
db_ops.db_stmt_set_int = pgsql_stmt_set_int;
|
||||
db_ops.db_stmt_set_int64 = pgsql_stmt_set_int64;
|
||||
db_ops.db_stmt_set_string = pgsql_stmt_set_string;
|
||||
db_ops.db_stmt_execute = pgsql_db_stmt_execute;
|
||||
db_ops.db_stmt_execute_query = pgsql_db_stmt_execute_query;
|
||||
db_ops.db_stmt_free = pgsql_db_stmt_free;
|
||||
db_ops.db_connection_begin_transaction = pgsql_db_begin_transaction;
|
||||
db_ops.db_connection_commit = pgsql_db_commit;
|
||||
db_ops.db_connection_rollback = pgsql_db_rollback;
|
||||
|
||||
return pgsql_db_conn_pool_new (host, user, password, db_name, unix_socket);
|
||||
}
|
||||
|
||||
DBConnPool *
|
||||
db_conn_pool_new_sqlite (const char *db_path, int max_connections)
|
||||
{
|
||||
db_ops.db_conn_pool_free = sqlite_db_conn_pool_free;
|
||||
db_ops.get_db_connection = sqlite_get_db_connection;
|
||||
db_ops.db_connection_close = sqlite_db_connection_close;
|
||||
db_ops.db_connection_execute = sqlite_db_connection_execute;
|
||||
db_ops.db_connection_execute_query = sqlite_execute_query;
|
||||
db_ops.result_set_next = sqlite_result_set_next;
|
||||
db_ops.result_set_get_string = sqlite_result_set_get_string;
|
||||
db_ops.result_set_free = sqlite_result_set_free;
|
||||
db_ops.result_set_get_column_count = sqlite_result_set_get_column_count;
|
||||
db_ops.db_connection_prepare_statement = sqlite_prepare_statement;
|
||||
db_ops.db_stmt_set_int = sqlite_stmt_set_int;
|
||||
db_ops.db_stmt_set_int64 = sqlite_stmt_set_int64;
|
||||
db_ops.db_stmt_set_string = sqlite_stmt_set_string;
|
||||
db_ops.db_stmt_execute = sqlite_db_stmt_execute;
|
||||
db_ops.db_stmt_execute_query = sqlite_db_stmt_execute_query;
|
||||
db_ops.db_stmt_free = sqlite_db_stmt_free;
|
||||
db_ops.db_connection_begin_transaction = sqlite_db_begin_transaction;
|
||||
db_ops.db_connection_commit = sqlite_db_commit;
|
||||
db_ops.db_connection_rollback = sqlite_db_rollback;
|
||||
|
||||
return sqlite_db_conn_pool_new (db_path, max_connections);
|
||||
}
|
||||
|
||||
void
|
||||
db_conn_pool_free (DBConnPool *pool)
|
||||
{
|
||||
return db_ops.db_conn_pool_free (pool);
|
||||
}
|
||||
|
||||
/* DB Connections. */
|
||||
|
||||
DBConnection *
|
||||
db_conn_pool_get_connection (DBConnPool *pool, GError **error)
|
||||
{
|
||||
return db_ops.get_db_connection (pool, error);
|
||||
}
|
||||
|
||||
static void
|
||||
db_connection_clear (DBConnection *conn)
|
||||
{
|
||||
result_set_free (conn->result_set);
|
||||
|
||||
db_stmt_free (conn->stmt);
|
||||
}
|
||||
|
||||
void
|
||||
db_connection_close (DBConnection *conn)
|
||||
{
|
||||
if (!conn)
|
||||
return;
|
||||
|
||||
db_connection_clear (conn);
|
||||
|
||||
db_ops.db_connection_close (conn);
|
||||
}
|
||||
|
||||
gboolean
|
||||
db_connection_execute (DBConnection *conn, const char *sql, GError **error)
|
||||
{
|
||||
return db_ops.db_connection_execute (conn, sql, error);
|
||||
}
|
||||
|
||||
/* Result Sets. */
|
||||
|
||||
void
|
||||
result_set_free (ResultSet *r)
|
||||
{
|
||||
if (!r)
|
||||
return;
|
||||
|
||||
return db_ops.result_set_free (r);
|
||||
}
|
||||
|
||||
ResultSet *
|
||||
db_connection_execute_query (DBConnection *conn, const char *sql, GError **error)
|
||||
{
|
||||
ResultSet *result_set;
|
||||
|
||||
if (conn->result_set) {
|
||||
result_set_free (conn->result_set);
|
||||
conn->result_set = NULL;
|
||||
}
|
||||
|
||||
result_set = db_ops.db_connection_execute_query (conn, sql, error);
|
||||
|
||||
if (result_set)
|
||||
conn->result_set = result_set;
|
||||
|
||||
return result_set;
|
||||
}
|
||||
|
||||
gboolean
|
||||
result_set_next (ResultSet *r, GError **error)
|
||||
{
|
||||
return db_ops.result_set_next (r, error);
|
||||
}
|
||||
|
||||
const char *
|
||||
result_set_get_string (ResultSet *r, int idx, GError **error)
|
||||
{
|
||||
return db_ops.result_set_get_string (r, idx, error);
|
||||
}
|
||||
|
||||
int
|
||||
result_set_get_int (ResultSet *r, int idx, GError **error)
|
||||
{
|
||||
const char *str;
|
||||
char *e;
|
||||
int ret;
|
||||
|
||||
str = db_ops.result_set_get_string (r, idx, error);
|
||||
if (*error) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (!str) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
errno = 0;
|
||||
ret = strtol (str, &e, 10);
|
||||
if (errno || (e == str)) {
|
||||
g_set_error (error, SEAF_DB_ERROR_DOMAIN, SEAF_DB_ERROR_CODE,
|
||||
"Number conversion failed.");
|
||||
return -1;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
gint64
|
||||
result_set_get_int64 (ResultSet *r, int idx, GError **error)
|
||||
{
|
||||
const char *str;
|
||||
char *e;
|
||||
gint64 ret;
|
||||
|
||||
str = db_ops.result_set_get_string (r, idx, error);
|
||||
if (*error) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (!str) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
errno = 0;
|
||||
ret = strtoll (str, &e, 10);
|
||||
if (errno || (e == str)) {
|
||||
g_set_error (error, SEAF_DB_ERROR_DOMAIN, SEAF_DB_ERROR_CODE,
|
||||
"Number conversion failed.");
|
||||
return -1;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
int
|
||||
result_set_get_column_count (ResultSet *r)
|
||||
{
|
||||
return db_ops.result_set_get_column_count (r);
|
||||
}
|
||||
|
||||
/* Prepared Statements. */
|
||||
|
||||
DBStmt *
|
||||
db_connection_prepare_statement (DBConnection *conn, const char *sql, GError **error)
|
||||
{
|
||||
DBStmt *stmt;
|
||||
|
||||
if (conn->stmt) {
|
||||
db_stmt_free (conn->stmt);
|
||||
conn->stmt = NULL;
|
||||
}
|
||||
|
||||
stmt = db_ops.db_connection_prepare_statement (conn, sql, error);
|
||||
|
||||
if (stmt)
|
||||
conn->stmt = stmt;
|
||||
|
||||
return stmt;
|
||||
}
|
||||
|
||||
int
|
||||
db_stmt_set_int (DBStmt *stmt, int idx, int x, GError **error)
|
||||
{
|
||||
return db_ops.db_stmt_set_int (stmt, idx, x, error);
|
||||
}
|
||||
|
||||
int
|
||||
db_stmt_set_int64 (DBStmt *stmt, int idx, gint64 x, GError **error)
|
||||
{
|
||||
return db_ops.db_stmt_set_int64 (stmt, idx, x, error);
|
||||
}
|
||||
|
||||
int
|
||||
db_stmt_set_string (DBStmt *stmt, int idx, const char *s, GError **error)
|
||||
{
|
||||
return db_ops.db_stmt_set_string (stmt, idx, s, error);
|
||||
}
|
||||
|
||||
gboolean
|
||||
db_stmt_execute (DBStmt *stmt, GError **error)
|
||||
{
|
||||
return db_ops.db_stmt_execute (stmt, error);
|
||||
}
|
||||
|
||||
ResultSet *
|
||||
db_stmt_execute_query (DBStmt *stmt, GError **error)
|
||||
{
|
||||
ResultSet *result_set;
|
||||
|
||||
if (stmt->result_set) {
|
||||
result_set_free (stmt->result_set);
|
||||
stmt->result_set = NULL;
|
||||
}
|
||||
|
||||
result_set = db_ops.db_stmt_execute_query (stmt, error);
|
||||
|
||||
if (result_set)
|
||||
stmt->result_set = result_set;
|
||||
|
||||
return result_set;
|
||||
}
|
||||
|
||||
void
|
||||
db_stmt_free (DBStmt *stmt)
|
||||
{
|
||||
if (!stmt)
|
||||
return;
|
||||
|
||||
if (stmt->result_set)
|
||||
result_set_free (stmt->result_set);
|
||||
|
||||
return db_ops.db_stmt_free (stmt);
|
||||
}
|
||||
|
||||
/* Transactions. */
|
||||
|
||||
gboolean
|
||||
db_connection_begin_transaction (DBConnection *conn, GError **error)
|
||||
{
|
||||
return db_ops.db_connection_begin_transaction (conn, error);
|
||||
}
|
||||
|
||||
gboolean
|
||||
db_connection_commit (DBConnection *conn, GError **error)
|
||||
{
|
||||
return db_ops.db_connection_commit (conn, error);
|
||||
}
|
||||
|
||||
gboolean
|
||||
db_connection_rollback (DBConnection *conn, GError **error)
|
||||
{
|
||||
db_connection_clear (conn);
|
||||
|
||||
return db_ops.db_connection_rollback (conn, error);
|
||||
}
|
129
net/common/db-wrapper/db-wrapper.h
Normal file
129
net/common/db-wrapper/db-wrapper.h
Normal file
@ -0,0 +1,129 @@
|
||||
#ifndef DB_WRAPPER_H
|
||||
#define DB_WARPPER_H
|
||||
|
||||
#include <glib.h>
|
||||
|
||||
#define SEAF_DB_ERROR_DOMAIN g_quark_from_string("SEAF_DB")
|
||||
#define SEAF_DB_ERROR_CODE 0
|
||||
|
||||
/* DB Connection Pool. */
|
||||
|
||||
struct DBConnPool {
|
||||
int max_connections;
|
||||
};
|
||||
typedef struct DBConnPool DBConnPool;
|
||||
|
||||
DBConnPool *
|
||||
db_conn_pool_new_mysql (const char *host,
|
||||
const char *user,
|
||||
const char *password,
|
||||
unsigned int port,
|
||||
const char *db_name,
|
||||
const char *unix_socket,
|
||||
gboolean use_ssl,
|
||||
const char *charset,
|
||||
int max_connections);
|
||||
|
||||
DBConnPool *
|
||||
db_conn_pool_new_pgsql (const char *host,
|
||||
const char *user,
|
||||
const char *password,
|
||||
const char *db_name,
|
||||
const char *unix_socket);
|
||||
|
||||
DBConnPool *
|
||||
db_conn_pool_new_sqlite (const char *db_path, int max_connections);
|
||||
|
||||
void
|
||||
db_conn_pool_free (DBConnPool *pool);
|
||||
|
||||
/* DB Connections. */
|
||||
|
||||
struct ResultSet;
|
||||
typedef struct ResultSet ResultSet;
|
||||
|
||||
struct DBStmt;
|
||||
typedef struct DBStmt DBStmt;
|
||||
|
||||
struct DBConnection {
|
||||
DBConnPool *pool;
|
||||
ResultSet *result_set;
|
||||
DBStmt *stmt;
|
||||
};
|
||||
typedef struct DBConnection DBConnection;
|
||||
|
||||
DBConnection *
|
||||
db_conn_pool_get_connection (DBConnPool *pool, GError **error);
|
||||
|
||||
void
|
||||
db_connection_close (DBConnection *conn);
|
||||
|
||||
gboolean
|
||||
db_connection_execute (DBConnection *conn, const char *sql, GError **error);
|
||||
|
||||
/* Result Sets. */
|
||||
|
||||
struct ResultSet {
|
||||
/* Empty */
|
||||
};
|
||||
|
||||
ResultSet *
|
||||
db_connection_execute_query (DBConnection *conn, const char *sql, GError **error);
|
||||
|
||||
gboolean
|
||||
result_set_next (ResultSet *r, GError **error);
|
||||
|
||||
const char *
|
||||
result_set_get_string (ResultSet *r, int idx, GError **error);
|
||||
|
||||
int
|
||||
result_set_get_int (ResultSet *r, int idx, GError **error);
|
||||
|
||||
gint64
|
||||
result_set_get_int64 (ResultSet *r, int idx, GError **error);
|
||||
|
||||
int
|
||||
result_set_get_column_count (ResultSet *r);
|
||||
|
||||
void
|
||||
result_set_free (ResultSet *r);
|
||||
|
||||
/* Prepared Statements. */
|
||||
|
||||
struct DBStmt {
|
||||
ResultSet *result_set;
|
||||
};
|
||||
|
||||
DBStmt *
|
||||
db_connection_prepare_statement (DBConnection *conn, const char *sql, GError **error);
|
||||
|
||||
gboolean
|
||||
db_stmt_set_int (DBStmt *stmt, int idx, int x, GError **error);
|
||||
|
||||
gboolean
|
||||
db_stmt_set_int64 (DBStmt *stmt, int idx, gint64 x, GError **error);
|
||||
|
||||
gboolean
|
||||
db_stmt_set_string (DBStmt *stmt, int idx, const char *s, GError **error);
|
||||
|
||||
gboolean
|
||||
db_stmt_execute (DBStmt *stmt, GError **error);
|
||||
|
||||
ResultSet *
|
||||
db_stmt_execute_query (DBStmt *stmt, GError **error);
|
||||
|
||||
void
|
||||
db_stmt_free (DBStmt *stmt);
|
||||
|
||||
/* Transactions. */
|
||||
|
||||
gboolean
|
||||
db_connection_begin_transaction (DBConnection *conn, GError **error);
|
||||
|
||||
gboolean
|
||||
db_connection_commit (DBConnection *conn, GError **error);
|
||||
|
||||
gboolean
|
||||
db_connection_rollback (DBConnection *conn, GError **error);
|
||||
|
||||
#endif
|
580
net/common/db-wrapper/mysql-db-ops.c
Normal file
580
net/common/db-wrapper/mysql-db-ops.c
Normal file
@ -0,0 +1,580 @@
|
||||
#include "common.h"
|
||||
|
||||
#include "db-wrapper.h"
|
||||
#include "mysql-db-ops.h"
|
||||
|
||||
#include <mysql.h>
|
||||
|
||||
/* Connection Pool. */
|
||||
|
||||
typedef struct MySQLDBConnPool {
|
||||
DBConnPool parent;
|
||||
char *host;
|
||||
char *user;
|
||||
char *password;
|
||||
unsigned int port;
|
||||
char *db_name;
|
||||
char *unix_socket;
|
||||
gboolean use_ssl;
|
||||
char *charset;
|
||||
} MySQLDBConnPool;
|
||||
|
||||
DBConnPool *
|
||||
mysql_db_conn_pool_new (const char *host,
|
||||
const char *user,
|
||||
const char *password,
|
||||
unsigned int port,
|
||||
const char *db_name,
|
||||
const char *unix_socket,
|
||||
gboolean use_ssl,
|
||||
const char *charset,
|
||||
int max_connections)
|
||||
{
|
||||
MySQLDBConnPool *pool = g_new0 (MySQLDBConnPool, 1);
|
||||
|
||||
pool->parent.max_connections = max_connections;
|
||||
pool->host = g_strdup (host);
|
||||
pool->user = g_strdup (user);
|
||||
pool->password = g_strdup (password);
|
||||
pool->port = port;
|
||||
pool->db_name = g_strdup(db_name);
|
||||
pool->unix_socket = g_strdup(unix_socket);
|
||||
pool->use_ssl = use_ssl;
|
||||
pool->charset = g_strdup(charset);
|
||||
|
||||
mysql_library_init (0, NULL, NULL);
|
||||
|
||||
return (DBConnPool *)pool;
|
||||
}
|
||||
|
||||
void
|
||||
mysql_db_conn_pool_free (DBConnPool *vpool)
|
||||
{
|
||||
MySQLDBConnPool *pool = (MySQLDBConnPool *)vpool;
|
||||
|
||||
g_free (pool->host);
|
||||
g_free (pool->user);
|
||||
g_free (pool->password);
|
||||
g_free (pool->db_name);
|
||||
g_free (pool->unix_socket);
|
||||
g_free (pool->charset);
|
||||
|
||||
g_free (pool);
|
||||
}
|
||||
|
||||
/* Connection. */
|
||||
|
||||
typedef struct MySQLDBConnection {
|
||||
DBConnection parent;
|
||||
MYSQL *db;
|
||||
} MySQLDBConnection;
|
||||
|
||||
#define SQL_DEFAULT_TCP_TIMEOUT 3
|
||||
|
||||
static MYSQL *
|
||||
connect_mysql (MySQLDBConnPool *pool, GError **error)
|
||||
{
|
||||
my_bool yes = 1;
|
||||
volatile int connect_timeout = SQL_DEFAULT_TCP_TIMEOUT;
|
||||
unsigned long client_flags = CLIENT_MULTI_STATEMENTS;
|
||||
MYSQL *db;
|
||||
|
||||
db = mysql_init (NULL);
|
||||
if (!db) {
|
||||
g_set_error (error, SEAF_DB_ERROR_DOMAIN, SEAF_DB_ERROR_CODE,
|
||||
"Failed to allocate mysql handle.");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (pool->use_ssl)
|
||||
mysql_ssl_set(db, 0,0,0,0,0);
|
||||
|
||||
if (pool->charset)
|
||||
mysql_options(db, MYSQL_SET_CHARSET_NAME, pool->charset);
|
||||
|
||||
mysql_options(db, MYSQL_OPT_CONNECT_TIMEOUT, (const char*)&connect_timeout);
|
||||
mysql_options(db, MYSQL_OPT_RECONNECT, (const char*)&yes);
|
||||
|
||||
if (mysql_real_connect(db, pool->host, pool->user, pool->password,
|
||||
pool->db_name, pool->port,
|
||||
pool->unix_socket, client_flags)) {
|
||||
return db;
|
||||
} else {
|
||||
g_set_error (error, SEAF_DB_ERROR_DOMAIN, SEAF_DB_ERROR_CODE,
|
||||
"Failed to connect to MySQL: %s", mysql_error(db));
|
||||
mysql_close (db);
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
DBConnection *
|
||||
mysql_get_db_connection (DBConnPool *vpool, GError **error)
|
||||
{
|
||||
MySQLDBConnPool *pool = (MySQLDBConnPool *)vpool;
|
||||
MySQLDBConnection *conn;
|
||||
MYSQL *db = connect_mysql (pool, error);
|
||||
if (!db)
|
||||
return NULL;
|
||||
conn = g_new0 (MySQLDBConnection, 1);
|
||||
conn->db = db;
|
||||
conn->parent.pool = vpool;
|
||||
return (DBConnection *)conn;
|
||||
}
|
||||
|
||||
void
|
||||
mysql_db_connection_close (DBConnection *vconn)
|
||||
{
|
||||
if (!vconn)
|
||||
return;
|
||||
|
||||
MySQLDBConnection *conn = (MySQLDBConnection *)vconn;
|
||||
|
||||
mysql_close (conn->db);
|
||||
|
||||
g_free (conn);
|
||||
}
|
||||
|
||||
gboolean
|
||||
mysql_db_connection_execute (DBConnection *vconn, const char *sql, GError **error)
|
||||
{
|
||||
MySQLDBConnection *conn = (MySQLDBConnection *)vconn;
|
||||
|
||||
int rc = mysql_real_query (conn->db, sql, strlen(sql));
|
||||
if (rc != 0) {
|
||||
g_set_error (error, SEAF_DB_ERROR_DOMAIN, SEAF_DB_ERROR_CODE,
|
||||
"MySQL failed to execute: %s", mysql_error(conn->db));
|
||||
return FALSE;
|
||||
}
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
/* Result Set. */
|
||||
|
||||
#define DEFAULT_COLUMN_SIZE 256
|
||||
|
||||
typedef struct MySQLResultSet {
|
||||
ResultSet parent;
|
||||
MYSQL_STMT *stmt;
|
||||
int column_count;
|
||||
MYSQL_BIND *bind;
|
||||
int need_rebind;
|
||||
} MySQLResultSet;
|
||||
|
||||
void
|
||||
mysql_result_set_free (ResultSet *vr)
|
||||
{
|
||||
if (!vr)
|
||||
return;
|
||||
|
||||
MySQLResultSet *r = (MySQLResultSet *)vr;
|
||||
|
||||
mysql_stmt_free_result (r->stmt);
|
||||
mysql_stmt_close (r->stmt);
|
||||
|
||||
int i;
|
||||
for (i = 0; i < r->column_count; ++i) {
|
||||
g_free (r->bind[i].buffer);
|
||||
g_free (r->bind[i].length);
|
||||
g_free (r->bind[i].is_null);
|
||||
}
|
||||
g_free (r->bind);
|
||||
g_free (r);
|
||||
}
|
||||
|
||||
static MySQLResultSet *
|
||||
mysql_result_set_new (MYSQL_STMT *stmt, GError **error)
|
||||
{
|
||||
MySQLResultSet *r = g_new0 (MySQLResultSet, 1);
|
||||
int i;
|
||||
|
||||
r->stmt = stmt;
|
||||
r->column_count = mysql_stmt_field_count (stmt);
|
||||
r->bind = g_new0 (MYSQL_BIND, r->column_count);
|
||||
for (i = 0; i < r->column_count; ++i) {
|
||||
r->bind[i].buffer = g_malloc (DEFAULT_COLUMN_SIZE + 1);
|
||||
r->bind[i].buffer_type = MYSQL_TYPE_STRING;
|
||||
r->bind[i].buffer_length = DEFAULT_COLUMN_SIZE;
|
||||
r->bind[i].length = g_new0 (unsigned long, 1);
|
||||
r->bind[i].is_null = g_new0 (my_bool, 1);
|
||||
}
|
||||
|
||||
if (mysql_stmt_bind_result (stmt, r->bind) != 0) {
|
||||
g_set_error (error, SEAF_DB_ERROR_DOMAIN, SEAF_DB_ERROR_CODE,
|
||||
"mysql_stmt_bind_result failed: %s\n", mysql_stmt_error(stmt));
|
||||
mysql_result_set_free ((ResultSet*)r);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
static MYSQL_STMT *
|
||||
prepare (MYSQL *db, const char *sql, GError **error)
|
||||
{
|
||||
MYSQL_STMT *stmt;
|
||||
|
||||
stmt = mysql_stmt_init (db);
|
||||
if (!stmt) {
|
||||
g_set_error (error, SEAF_DB_ERROR_DOMAIN, SEAF_DB_ERROR_CODE,
|
||||
"mysql_stmt_init out of memory");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (mysql_stmt_prepare (stmt, sql, strlen(sql)) != 0) {
|
||||
g_set_error (error, SEAF_DB_ERROR_DOMAIN, SEAF_DB_ERROR_CODE,
|
||||
"mysql_stmt_prepare failed: %s",
|
||||
mysql_stmt_error(stmt));
|
||||
mysql_stmt_close (stmt);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return stmt;
|
||||
}
|
||||
|
||||
ResultSet *
|
||||
mysql_execute_query (DBConnection *vconn, const char *sql, GError **error)
|
||||
{
|
||||
MySQLDBConnection *conn = (MySQLDBConnection *)vconn;
|
||||
MYSQL_STMT *stmt;
|
||||
MySQLResultSet *r;
|
||||
|
||||
stmt = prepare (conn->db, sql, error);
|
||||
if (!stmt) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
unsigned long cursor = CURSOR_TYPE_READ_ONLY;
|
||||
mysql_stmt_attr_set (stmt, STMT_ATTR_CURSOR_TYPE, &cursor);
|
||||
|
||||
if (mysql_stmt_execute (stmt) != 0) {
|
||||
g_set_error (error, SEAF_DB_ERROR_DOMAIN, SEAF_DB_ERROR_CODE,
|
||||
"mysql_stmt_execute failed: %s",
|
||||
mysql_stmt_error(stmt));
|
||||
mysql_stmt_close (stmt);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
r = mysql_result_set_new (stmt, error);
|
||||
if (!r) {
|
||||
mysql_stmt_close (stmt);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return (ResultSet *)r;
|
||||
}
|
||||
|
||||
static gboolean
|
||||
check_mysql_column_size (MySQLResultSet *r, int i, GError **error)
|
||||
{
|
||||
unsigned long real_length = *(r->bind[i].length);
|
||||
|
||||
if ((real_length > r->bind[i].buffer_length)) {
|
||||
/* Column was truncated, resize and fetch column directly. */
|
||||
g_free (r->bind[i].buffer);
|
||||
r->bind[i].buffer = g_malloc (real_length + 1);
|
||||
r->bind[i].buffer_length = real_length;
|
||||
if (mysql_stmt_fetch_column (r->stmt, &r->bind[i], i, 0) != 0) {
|
||||
g_set_error (error, SEAF_DB_ERROR_DOMAIN, SEAF_DB_ERROR_CODE,
|
||||
"mysql_stmt_fetch_column failed: %s",
|
||||
mysql_stmt_error(r->stmt));
|
||||
return FALSE;
|
||||
}
|
||||
r->need_rebind = TRUE;
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
gboolean
|
||||
mysql_result_set_next (ResultSet *vr, GError **error)
|
||||
{
|
||||
MySQLResultSet *r = (MySQLResultSet *)vr;
|
||||
|
||||
if (r->need_rebind) {
|
||||
if (mysql_stmt_bind_result (r->stmt, r->bind) != 0) {
|
||||
g_set_error (error, SEAF_DB_ERROR_DOMAIN, SEAF_DB_ERROR_CODE,
|
||||
"mysql_stmt_bind_result failed: %s",
|
||||
mysql_stmt_error(r->stmt));
|
||||
return FALSE;
|
||||
}
|
||||
r->need_rebind = FALSE;
|
||||
}
|
||||
|
||||
int rc = mysql_stmt_fetch (r->stmt);
|
||||
if (rc == 1) {
|
||||
g_set_error (error, SEAF_DB_ERROR_DOMAIN, SEAF_DB_ERROR_CODE,
|
||||
"mysql_stmt_fetch failed: %s", mysql_stmt_error(r->stmt));
|
||||
return FALSE;
|
||||
}
|
||||
return ((rc == 0) || (rc == MYSQL_DATA_TRUNCATED));
|
||||
}
|
||||
|
||||
const char *
|
||||
mysql_result_set_get_string (ResultSet *vr, int i, GError **error)
|
||||
{
|
||||
MySQLResultSet *r = (MySQLResultSet *)vr;
|
||||
char *ret;
|
||||
|
||||
if (i < 0 || i >= r->column_count) {
|
||||
g_set_error (error, SEAF_DB_ERROR_DOMAIN, SEAF_DB_ERROR_CODE,
|
||||
"Column index is out of range.");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (*(r->bind[i].is_null)) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (!check_mysql_column_size (r, i, error)) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
ret = r->bind[i].buffer;
|
||||
ret[r->bind[i].buffer_length] = 0;
|
||||
return ret;
|
||||
}
|
||||
|
||||
int
|
||||
mysql_result_set_get_column_count (ResultSet *vr)
|
||||
{
|
||||
MySQLResultSet *r = (MySQLResultSet *)vr;
|
||||
|
||||
return r->column_count;
|
||||
}
|
||||
|
||||
typedef struct MySQLDBStmt {
|
||||
DBStmt parent;
|
||||
int param_count;
|
||||
MYSQL_STMT *stmt;
|
||||
MYSQL_BIND *bind;
|
||||
} MySQLDBStmt;
|
||||
|
||||
static MySQLDBStmt *
|
||||
mysql_stmt_new (MYSQL_STMT *stmt)
|
||||
{
|
||||
MySQLDBStmt *p = g_new0 (MySQLDBStmt, 1);
|
||||
|
||||
p->stmt = stmt;
|
||||
p->param_count = (int)mysql_stmt_param_count(stmt);
|
||||
if (p->param_count>0) {
|
||||
p->bind = g_new0 (MYSQL_BIND, p->param_count);
|
||||
}
|
||||
|
||||
return p;
|
||||
}
|
||||
|
||||
DBStmt *
|
||||
mysql_prepare_statement (DBConnection *vconn, const char *sql, GError **error)
|
||||
{
|
||||
MySQLDBConnection *conn = (MySQLDBConnection *)vconn;
|
||||
MYSQL_STMT *stmt;
|
||||
MySQLDBStmt *ret;
|
||||
|
||||
stmt = prepare (conn->db, sql, error);
|
||||
if (!stmt) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
ret = mysql_stmt_new (stmt);
|
||||
|
||||
return (DBStmt*)ret;
|
||||
}
|
||||
|
||||
gboolean
|
||||
mysql_stmt_set_int (DBStmt *vstmt, int i, int x, GError **error)
|
||||
{
|
||||
MySQLDBStmt *stmt = (MySQLDBStmt *)vstmt;
|
||||
int *pval;
|
||||
|
||||
if (i < 0 || i >= stmt->param_count) {
|
||||
g_set_error (error, SEAF_DB_ERROR_DOMAIN, SEAF_DB_ERROR_CODE,
|
||||
"Column index is out of range.");
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
pval = g_new (int, 1);
|
||||
*pval = x;
|
||||
|
||||
stmt->bind[i].buffer_type = MYSQL_TYPE_LONG;
|
||||
stmt->bind[i].buffer = (char *)pval;
|
||||
stmt->bind[i].is_null = 0;
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
gboolean
|
||||
mysql_stmt_set_int64 (DBStmt *vstmt, int i, gint64 x, GError **error)
|
||||
{
|
||||
MySQLDBStmt *stmt = (MySQLDBStmt *)vstmt;
|
||||
gint64 *pval;
|
||||
|
||||
if (i < 0 || i >= stmt->param_count) {
|
||||
g_set_error (error, SEAF_DB_ERROR_DOMAIN, SEAF_DB_ERROR_CODE,
|
||||
"Column index is out of range.");
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
pval = g_new (gint64, 1);
|
||||
*pval = x;
|
||||
|
||||
stmt->bind[i].buffer_type = MYSQL_TYPE_LONGLONG;
|
||||
stmt->bind[i].buffer = (char *)pval;
|
||||
stmt->bind[i].is_null = 0;
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
gboolean
|
||||
mysql_stmt_set_string (DBStmt *vstmt, int i, const char *s, GError **error)
|
||||
{
|
||||
MySQLDBStmt *stmt = (MySQLDBStmt *)vstmt;
|
||||
static my_bool yes = TRUE;
|
||||
unsigned long *plen;
|
||||
|
||||
if (i < 0 || i >= stmt->param_count) {
|
||||
g_set_error (error, SEAF_DB_ERROR_DOMAIN, SEAF_DB_ERROR_CODE,
|
||||
"Column index is out of range.");
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
stmt->bind[i].buffer_type = MYSQL_TYPE_STRING;
|
||||
stmt->bind[i].buffer = g_strdup(s);
|
||||
plen = g_new (unsigned long, 1);
|
||||
stmt->bind[i].length = plen;
|
||||
if (!s) {
|
||||
*plen = 0;
|
||||
stmt->bind[i].is_null = &yes;
|
||||
} else {
|
||||
*plen = strlen(s);
|
||||
stmt->bind[i].is_null = 0;
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
gboolean
|
||||
mysql_db_stmt_execute (DBStmt *vstmt, GError **error)
|
||||
{
|
||||
MySQLDBStmt *stmt = (MySQLDBStmt *)vstmt;
|
||||
|
||||
if (stmt->param_count > 0 &&
|
||||
mysql_stmt_bind_param (stmt->stmt, stmt->bind) != 0) {
|
||||
g_set_error (error, SEAF_DB_ERROR_DOMAIN, SEAF_DB_ERROR_CODE,
|
||||
"mysql_stmt_bind_param failed: %s",
|
||||
mysql_stmt_error(stmt->stmt));
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
unsigned long cursor = CURSOR_TYPE_NO_CURSOR;
|
||||
mysql_stmt_attr_set (stmt->stmt, STMT_ATTR_CURSOR_TYPE, &cursor);
|
||||
|
||||
if (mysql_stmt_execute (stmt->stmt) != 0) {
|
||||
g_set_error (error, SEAF_DB_ERROR_DOMAIN, SEAF_DB_ERROR_CODE,
|
||||
"mysql_stmt_execute failed: %s", mysql_stmt_error(stmt->stmt));
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
mysql_stmt_reset (stmt->stmt);
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
ResultSet *
|
||||
mysql_db_stmt_execute_query (DBStmt *vstmt, GError **error)
|
||||
{
|
||||
MySQLDBStmt *stmt = (MySQLDBStmt *)vstmt;
|
||||
|
||||
if (stmt->param_count > 0 &&
|
||||
mysql_stmt_bind_param (stmt->stmt, stmt->bind) != 0) {
|
||||
g_set_error (error, SEAF_DB_ERROR_DOMAIN, SEAF_DB_ERROR_CODE,
|
||||
"mysql_stmt_bind_param failed: %s",
|
||||
mysql_stmt_error(stmt->stmt));
|
||||
return NULL;
|
||||
}
|
||||
|
||||
unsigned long cursor = CURSOR_TYPE_READ_ONLY;
|
||||
mysql_stmt_attr_set (stmt->stmt, STMT_ATTR_CURSOR_TYPE, &cursor);
|
||||
|
||||
if (mysql_stmt_execute (stmt->stmt) != 0) {
|
||||
g_set_error (error, SEAF_DB_ERROR_DOMAIN, SEAF_DB_ERROR_CODE,
|
||||
"mysql_stmt_execute failed: %s", mysql_stmt_error(stmt->stmt));
|
||||
return NULL;
|
||||
}
|
||||
|
||||
MySQLResultSet *r = mysql_result_set_new (stmt->stmt, error);
|
||||
if (*error) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return (ResultSet *)r;
|
||||
}
|
||||
|
||||
void
|
||||
mysql_db_stmt_free (DBStmt *vstmt)
|
||||
{
|
||||
if (!vstmt)
|
||||
return;
|
||||
|
||||
MySQLDBStmt *stmt = (MySQLDBStmt *)vstmt;
|
||||
|
||||
/* If there is a result set associated with this stmt, the mysql stmt
|
||||
* will be freed when freeing the result set.
|
||||
*/
|
||||
if (!stmt->parent.result_set) {
|
||||
mysql_stmt_free_result (stmt->stmt);
|
||||
mysql_stmt_close (stmt->stmt);
|
||||
}
|
||||
|
||||
int i;
|
||||
for (i = 0; i < stmt->param_count; ++i) {
|
||||
g_free (stmt->bind[i].buffer);
|
||||
g_free (stmt->bind[i].length);
|
||||
}
|
||||
g_free (stmt->bind);
|
||||
|
||||
g_free (stmt);
|
||||
}
|
||||
|
||||
/* Transaction. */
|
||||
|
||||
gboolean
|
||||
mysql_db_begin_transaction (DBConnection *vconn, GError **error)
|
||||
{
|
||||
MySQLDBConnection *conn = (MySQLDBConnection *)vconn;
|
||||
|
||||
int rc = mysql_query (conn->db, "START TRANSACTION;");
|
||||
if (rc != 0) {
|
||||
g_set_error (error, SEAF_DB_ERROR_DOMAIN, SEAF_DB_ERROR_CODE,
|
||||
"Failed to begin transaction: %s", mysql_error(conn->db));
|
||||
}
|
||||
|
||||
return (rc == 0);
|
||||
}
|
||||
|
||||
gboolean
|
||||
mysql_db_commit (DBConnection *vconn, GError **error)
|
||||
{
|
||||
MySQLDBConnection *conn = (MySQLDBConnection *)vconn;
|
||||
|
||||
int rc = mysql_query (conn->db, "COMMIT;");
|
||||
if (rc != 0) {
|
||||
g_set_error (error, SEAF_DB_ERROR_DOMAIN, SEAF_DB_ERROR_CODE,
|
||||
"Failed to commit transaction: %s", mysql_error(conn->db));
|
||||
}
|
||||
|
||||
return (rc == 0);
|
||||
}
|
||||
|
||||
gboolean
|
||||
mysql_db_rollback (DBConnection *vconn, GError **error)
|
||||
{
|
||||
MySQLDBConnection *conn = (MySQLDBConnection *)vconn;
|
||||
|
||||
int rc = mysql_query (conn->db, "ROLLBACK;");
|
||||
if (rc != 0) {
|
||||
g_set_error (error, SEAF_DB_ERROR_DOMAIN, SEAF_DB_ERROR_CODE,
|
||||
"Failed to rollback transaction: %s", mysql_error(conn->db));
|
||||
}
|
||||
|
||||
return (rc == 0);
|
||||
}
|
72
net/common/db-wrapper/mysql-db-ops.h
Normal file
72
net/common/db-wrapper/mysql-db-ops.h
Normal file
@ -0,0 +1,72 @@
|
||||
#ifndef MYSQL_DB_OPS_H
|
||||
#define MYSQL_DB_OPS_H
|
||||
|
||||
DBConnPool *
|
||||
mysql_db_conn_pool_new (const char *host,
|
||||
const char *user,
|
||||
const char *password,
|
||||
unsigned int port,
|
||||
const char *db_name,
|
||||
const char *unix_socket,
|
||||
gboolean use_ssl,
|
||||
const char *charset,
|
||||
int max_connections);
|
||||
|
||||
void
|
||||
mysql_db_conn_pool_free (DBConnPool *vpool);
|
||||
|
||||
DBConnection *
|
||||
mysql_get_db_connection (DBConnPool *vpool, GError **error);
|
||||
|
||||
void
|
||||
mysql_db_connection_close (DBConnection *vconn);
|
||||
|
||||
gboolean
|
||||
mysql_db_connection_execute (DBConnection *vconn, const char *sql, GError **error);
|
||||
|
||||
void
|
||||
mysql_result_set_free (ResultSet *vr);
|
||||
|
||||
ResultSet *
|
||||
mysql_execute_query (DBConnection *vconn, const char *sql, GError **error);
|
||||
|
||||
gboolean
|
||||
mysql_result_set_next (ResultSet *vr, GError **error);
|
||||
|
||||
const char *
|
||||
mysql_result_set_get_string (ResultSet *vr, int i, GError **error);
|
||||
|
||||
int
|
||||
mysql_result_set_get_column_count (ResultSet *vr);
|
||||
|
||||
DBStmt *
|
||||
mysql_prepare_statement (DBConnection *vconn, const char *sql, GError **error);
|
||||
|
||||
gboolean
|
||||
mysql_stmt_set_int (DBStmt *vstmt, int i, int x, GError **error);
|
||||
|
||||
gboolean
|
||||
mysql_stmt_set_int64 (DBStmt *vstmt, int i, gint64 x, GError **error);
|
||||
|
||||
gboolean
|
||||
mysql_stmt_set_string (DBStmt *vstmt, int i, const char *s, GError **error);
|
||||
|
||||
gboolean
|
||||
mysql_db_stmt_execute (DBStmt *vstmt, GError **error);
|
||||
|
||||
ResultSet *
|
||||
mysql_db_stmt_execute_query (DBStmt *vstmt, GError **error);
|
||||
|
||||
void
|
||||
mysql_db_stmt_free (DBStmt *vstmt);
|
||||
|
||||
gboolean
|
||||
mysql_db_begin_transaction (DBConnection *vconn, GError **error);
|
||||
|
||||
gboolean
|
||||
mysql_db_commit (DBConnection *vconn, GError **error);
|
||||
|
||||
gboolean
|
||||
mysql_db_rollback (DBConnection *vconn, GError **error);
|
||||
|
||||
#endif
|
475
net/common/db-wrapper/pgsql-db-ops.c
Normal file
475
net/common/db-wrapper/pgsql-db-ops.c
Normal file
@ -0,0 +1,475 @@
|
||||
#include "common.h"
|
||||
|
||||
#include "db-wrapper.h"
|
||||
#include "pgsql-db-ops.h"
|
||||
|
||||
#include <libpq-fe.h>
|
||||
|
||||
typedef struct PGDBConnPool {
|
||||
DBConnPool parent;
|
||||
char *host;
|
||||
char *user;
|
||||
char *password;
|
||||
char *db_name;
|
||||
char *unix_socket;
|
||||
} PGDBConnPool;
|
||||
|
||||
DBConnPool *
|
||||
pgsql_db_conn_pool_new (const char *host,
|
||||
const char *user,
|
||||
const char *password,
|
||||
const char *db_name,
|
||||
const char *unix_socket)
|
||||
{
|
||||
PGDBConnPool *pool = g_new0 (PGDBConnPool, 1);
|
||||
|
||||
pool->host = g_strdup (host);
|
||||
pool->user = g_strdup (user);
|
||||
pool->password = g_strdup (password);
|
||||
pool->db_name = g_strdup(db_name);
|
||||
pool->unix_socket = g_strdup(unix_socket);
|
||||
|
||||
return (DBConnPool *)pool;
|
||||
}
|
||||
|
||||
void
|
||||
pgsql_db_conn_pool_free (DBConnPool *vpool)
|
||||
{
|
||||
PGDBConnPool *pool = (PGDBConnPool *)vpool;
|
||||
|
||||
g_free (pool->host);
|
||||
g_free (pool->user);
|
||||
g_free (pool->password);
|
||||
g_free (pool->db_name);
|
||||
g_free (pool->unix_socket);
|
||||
|
||||
g_free (pool);
|
||||
}
|
||||
|
||||
typedef struct PGDBConnection {
|
||||
DBConnection parent;
|
||||
PGconn *db;
|
||||
} PGDBConnection;
|
||||
|
||||
static char *
|
||||
escape_string_pgsql_connect (const char *str)
|
||||
{
|
||||
GString *buf = g_string_new (NULL);
|
||||
const char *p;
|
||||
|
||||
for (p = str; *p != '\0'; ++p) {
|
||||
if (*p == '\'' || *p == '\\') {
|
||||
g_string_append_c (buf, '\\');
|
||||
g_string_append_c (buf, *p);
|
||||
} else {
|
||||
g_string_append_c (buf, *p);
|
||||
}
|
||||
}
|
||||
|
||||
return g_string_free (buf, FALSE);
|
||||
}
|
||||
|
||||
static PGconn *
|
||||
connect_pgsql (PGDBConnPool *pool, GError **error)
|
||||
{
|
||||
GString *buf = g_string_new("");
|
||||
char *esc_password = NULL;
|
||||
PGconn *db;
|
||||
|
||||
g_string_append_printf (buf, "user='%s' ", pool->user);
|
||||
|
||||
esc_password = escape_string_pgsql_connect (pool->password);
|
||||
g_string_append_printf (buf, "password='%s' ", esc_password);
|
||||
g_free (esc_password);
|
||||
|
||||
if (pool->unix_socket) {
|
||||
g_string_append_printf (buf, "host='%s' ", pool->unix_socket);
|
||||
} else {
|
||||
g_string_append_printf (buf, "host='%s' ", pool->host);
|
||||
}
|
||||
|
||||
g_string_append_printf (buf, "dbname='%s' ", pool->db_name);
|
||||
|
||||
db = PQconnectdb (buf->str);
|
||||
if (PQstatus (db) != CONNECTION_OK) {
|
||||
g_set_error (error, SEAF_DB_ERROR_DOMAIN, SEAF_DB_ERROR_CODE,
|
||||
"PQconnectdb failed: %s", PQerrorMessage (db));
|
||||
PQfinish (db);
|
||||
db = NULL;
|
||||
}
|
||||
|
||||
g_string_free (buf, TRUE);
|
||||
return db;
|
||||
}
|
||||
|
||||
DBConnection *
|
||||
pgsql_get_db_connection (DBConnPool *vpool, GError **error)
|
||||
{
|
||||
PGDBConnPool *pool = (PGDBConnPool *)vpool;
|
||||
PGDBConnection *conn;
|
||||
|
||||
PGconn *db = connect_pgsql (pool, error);
|
||||
if (!db)
|
||||
return NULL;
|
||||
|
||||
conn = g_new0 (PGDBConnection, 1);
|
||||
conn->db = db;
|
||||
conn->parent.pool = vpool;
|
||||
|
||||
return (DBConnection *)conn;
|
||||
}
|
||||
|
||||
void
|
||||
pgsql_db_connection_close (DBConnection *vconn)
|
||||
{
|
||||
if (!vconn)
|
||||
return;
|
||||
|
||||
PGDBConnection *conn = (PGDBConnection *)vconn;
|
||||
|
||||
PQfinish (conn->db);
|
||||
|
||||
g_free (conn);
|
||||
}
|
||||
|
||||
gboolean
|
||||
pgsql_db_connection_execute (DBConnection *vconn, const char *sql, GError **error)
|
||||
{
|
||||
PGDBConnection *conn = (PGDBConnection *)vconn;
|
||||
PGresult *res;
|
||||
gboolean ret = TRUE;
|
||||
|
||||
res = PQexec (conn->db, sql);
|
||||
if (PQresultStatus(res) != PGRES_COMMAND_OK) {
|
||||
g_set_error (error, SEAF_DB_ERROR_DOMAIN, SEAF_DB_ERROR_CODE,
|
||||
"PQexec failed: %s", PQresultErrorMessage(res));
|
||||
ret = FALSE;
|
||||
}
|
||||
PQclear (res);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
typedef struct PGResultSet {
|
||||
ResultSet parent;
|
||||
PGresult *res;
|
||||
int curr_row;
|
||||
int column_count;
|
||||
int row_count;
|
||||
} PGResultSet;
|
||||
|
||||
void
|
||||
pgsql_result_set_free (ResultSet *vr)
|
||||
{
|
||||
if (!vr)
|
||||
return;
|
||||
|
||||
PGResultSet *r = (PGResultSet *)vr;
|
||||
|
||||
PQclear (r->res);
|
||||
g_free (r);
|
||||
}
|
||||
|
||||
static PGResultSet *
|
||||
pgsql_result_set_new (PGresult *res)
|
||||
{
|
||||
PGResultSet *r;
|
||||
|
||||
r = g_new0 (PGResultSet, 1);
|
||||
r->curr_row = -1;
|
||||
r->column_count = PQnfields(res);
|
||||
r->row_count = PQntuples(res);
|
||||
r->res = res;
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
ResultSet *
|
||||
pgsql_execute_query (DBConnection *vconn, const char *sql, GError **error)
|
||||
{
|
||||
PGDBConnection *conn = (PGDBConnection *)vconn;
|
||||
PGresult *res;
|
||||
PGResultSet *r;
|
||||
|
||||
res = PQexec (conn->db, sql);
|
||||
if (PQresultStatus(res) != PGRES_TUPLES_OK) {
|
||||
g_set_error (error, SEAF_DB_ERROR_DOMAIN, SEAF_DB_ERROR_CODE,
|
||||
"PQexec failed: %s", PQresultErrorMessage(res));
|
||||
return NULL;
|
||||
}
|
||||
|
||||
r = pgsql_result_set_new (res);
|
||||
|
||||
return (ResultSet *)r;
|
||||
}
|
||||
|
||||
gboolean
|
||||
pgsql_result_set_next (ResultSet *vr, GError **error)
|
||||
{
|
||||
PGResultSet *r = (PGResultSet *)vr;
|
||||
|
||||
return ((r->curr_row)++ < (r->row_count - 1));
|
||||
}
|
||||
|
||||
const char *
|
||||
pgsql_result_set_get_string (ResultSet *vr, int i, GError **error)
|
||||
{
|
||||
PGResultSet *r = (PGResultSet *)vr;
|
||||
|
||||
if (PQgetisnull(r->res, r->curr_row, i))
|
||||
return NULL;
|
||||
return PQgetvalue(r->res, r->curr_row, i);
|
||||
}
|
||||
|
||||
int
|
||||
pgsql_result_set_get_column_count (ResultSet *vr)
|
||||
{
|
||||
PGResultSet *r = (PGResultSet *)vr;
|
||||
return r->column_count;
|
||||
}
|
||||
|
||||
typedef struct PGDBStmt {
|
||||
DBStmt parent;
|
||||
char *name;
|
||||
PGconn *db;
|
||||
int param_count;
|
||||
char **values;
|
||||
int *lengths;
|
||||
int *formats;
|
||||
} PGDBStmt;
|
||||
|
||||
static PGDBStmt *
|
||||
pgsql_stmt_new (PGconn *db, char *name, int param_count)
|
||||
{
|
||||
PGDBStmt *stmt = g_new0 (PGDBStmt, 1);
|
||||
|
||||
stmt->name = g_strdup(name);
|
||||
stmt->db = db;
|
||||
stmt->param_count = param_count;
|
||||
|
||||
if (stmt->param_count) {
|
||||
stmt->values = g_new0 (char *, param_count);
|
||||
stmt->lengths = g_new0 (int, param_count);
|
||||
stmt->formats = g_new0 (int, param_count);
|
||||
}
|
||||
|
||||
return stmt;
|
||||
}
|
||||
|
||||
/* Convert '?' in the query string to $1, $2, etc. */
|
||||
static char *
|
||||
pgsql_format_query_string (const char *sql, int *param_count)
|
||||
{
|
||||
GString *buf = g_string_new (NULL);
|
||||
const char *p;
|
||||
int i = 0;
|
||||
|
||||
for (p = sql; *p != '\0'; ++p) {
|
||||
if (*p == '?') {
|
||||
++i;
|
||||
g_string_append_c (buf, '$');
|
||||
g_string_append_printf (buf, "%d", i);
|
||||
} else {
|
||||
g_string_append_c (buf, *p);
|
||||
}
|
||||
}
|
||||
|
||||
*param_count = i;
|
||||
|
||||
return g_string_free (buf, FALSE);
|
||||
}
|
||||
|
||||
static gint stmt_id = 0;
|
||||
|
||||
DBStmt *
|
||||
pgsql_prepare_statement (DBConnection *vconn, const char *sql, GError **error)
|
||||
{
|
||||
PGDBConnection *conn = (PGDBConnection *)vconn;
|
||||
char *query;
|
||||
int param_count;
|
||||
char *name;
|
||||
PGresult *res;
|
||||
PGDBStmt *stmt = NULL;
|
||||
|
||||
query = pgsql_format_query_string (sql, ¶m_count);
|
||||
|
||||
g_atomic_int_inc (&stmt_id);
|
||||
name = g_strdup_printf ("%d", stmt_id);
|
||||
|
||||
res = PQprepare (conn->db, name, query, 0, NULL);
|
||||
ExecStatusType status = PQresultStatus(res);
|
||||
if (res && (status == PGRES_EMPTY_QUERY || status == PGRES_COMMAND_OK || status == PGRES_TUPLES_OK)) {
|
||||
stmt = pgsql_stmt_new (conn->db, name, param_count);
|
||||
} else {
|
||||
g_set_error (error, SEAF_DB_ERROR_DOMAIN, SEAF_DB_ERROR_CODE,
|
||||
"PQprepare failed: %s", PQresultErrorMessage(res));
|
||||
}
|
||||
|
||||
PQclear (res);
|
||||
g_free (name);
|
||||
g_free (query);
|
||||
return (DBStmt *)stmt;
|
||||
}
|
||||
|
||||
gboolean
|
||||
pgsql_stmt_set_int (DBStmt *vstmt, int i, int x, GError **error)
|
||||
{
|
||||
PGDBStmt *stmt = (PGDBStmt *)vstmt;
|
||||
|
||||
if (i < 0 || i >= stmt->param_count) {
|
||||
g_set_error (error, SEAF_DB_ERROR_DOMAIN, SEAF_DB_ERROR_CODE,
|
||||
"Column index is out of range.");
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
stmt->values[i] = g_strdup_printf("%d", x);
|
||||
stmt->lengths[i] = 0;
|
||||
stmt->formats[i] = 0;
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
gboolean
|
||||
pgsql_stmt_set_int64 (DBStmt *vstmt, int i, gint64 x, GError **error)
|
||||
{
|
||||
PGDBStmt *stmt = (PGDBStmt *)vstmt;
|
||||
|
||||
if (i < 0 || i >= stmt->param_count) {
|
||||
g_set_error (error, SEAF_DB_ERROR_DOMAIN, SEAF_DB_ERROR_CODE,
|
||||
"Column index is out of range.");
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
stmt->values[i] = g_strdup_printf("%"G_GINT64_FORMAT, x);
|
||||
stmt->lengths[i] = 0;
|
||||
stmt->formats[i] = 0;
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
gboolean
|
||||
pgsql_stmt_set_string (DBStmt *vstmt, int i, const char *s, GError **error)
|
||||
{
|
||||
PGDBStmt *stmt = (PGDBStmt *)vstmt;
|
||||
|
||||
if (i < 0 || i >= stmt->param_count) {
|
||||
g_set_error (error, SEAF_DB_ERROR_DOMAIN, SEAF_DB_ERROR_CODE,
|
||||
"Column index is out of range.");
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
stmt->values[i] = g_strdup(s);
|
||||
stmt->lengths[i] = 0;
|
||||
stmt->formats[i] = 0;
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
gboolean
|
||||
pgsql_db_stmt_execute (DBStmt *vstmt, GError **error)
|
||||
{
|
||||
PGDBStmt *stmt = (PGDBStmt *)vstmt;
|
||||
PGresult *res;
|
||||
gboolean ret;
|
||||
|
||||
res = PQexecPrepared (stmt->db, stmt->name, stmt->param_count,
|
||||
(const char **)stmt->values, stmt->lengths, stmt->formats, 0);
|
||||
if (PQresultStatus(res) != PGRES_COMMAND_OK) {
|
||||
g_set_error (error, SEAF_DB_ERROR_DOMAIN, SEAF_DB_ERROR_CODE,
|
||||
"PGexecPrepared failed: %s", PQresultErrorMessage(res));
|
||||
ret = FALSE;
|
||||
}
|
||||
|
||||
ret = TRUE;
|
||||
|
||||
PQclear(res);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
ResultSet *
|
||||
pgsql_db_stmt_execute_query (DBStmt *vstmt, GError **error)
|
||||
{
|
||||
PGDBStmt *stmt = (PGDBStmt *)vstmt;
|
||||
PGresult *res;
|
||||
PGResultSet *ret = NULL;
|
||||
|
||||
res = PQexecPrepared (stmt->db, stmt->name, stmt->param_count,
|
||||
(const char **)stmt->values, stmt->lengths, stmt->formats, 0);
|
||||
if (PQresultStatus(res) != PGRES_TUPLES_OK) {
|
||||
g_set_error (error, SEAF_DB_ERROR_DOMAIN, SEAF_DB_ERROR_CODE,
|
||||
"PGexecPrepared failed: %s", PQresultErrorMessage(res));
|
||||
}
|
||||
|
||||
ret = pgsql_result_set_new (res);
|
||||
|
||||
return (ResultSet *)ret;
|
||||
}
|
||||
|
||||
void
|
||||
pgsql_db_stmt_free (DBStmt *vstmt)
|
||||
{
|
||||
if (!vstmt)
|
||||
return;
|
||||
|
||||
PGDBStmt *stmt = (PGDBStmt *)vstmt;
|
||||
|
||||
g_free (stmt->name);
|
||||
|
||||
int i;
|
||||
for (i = 0; i < stmt->param_count; ++i)
|
||||
g_free (stmt->values[i]);
|
||||
g_free (stmt->values);
|
||||
|
||||
g_free (stmt->lengths);
|
||||
g_free (stmt->formats);
|
||||
g_free (stmt);
|
||||
}
|
||||
|
||||
gboolean
|
||||
pgsql_db_begin_transaction (DBConnection *vconn, GError **error)
|
||||
{
|
||||
PGDBConnection *conn = (PGDBConnection *)vconn;
|
||||
gboolean ret = TRUE;
|
||||
|
||||
PGresult *res = PQexec(conn->db, "BEGIN TRANSACTION;");
|
||||
if (PQresultStatus(res) != PGRES_COMMAND_OK) {
|
||||
g_set_error (error, SEAF_DB_ERROR_DOMAIN, SEAF_DB_ERROR_CODE,
|
||||
"Begin transaction failed: %s", PQresultErrorMessage(res));
|
||||
ret = FALSE;
|
||||
}
|
||||
PQclear(res);
|
||||
return ret;
|
||||
}
|
||||
|
||||
gboolean
|
||||
pgsql_db_commit (DBConnection *vconn, GError **error)
|
||||
{
|
||||
PGDBConnection *conn = (PGDBConnection *)vconn;
|
||||
gboolean ret = TRUE;
|
||||
|
||||
PGresult *res = PQexec(conn->db, "COMMIT TRANSACTION;");
|
||||
if (PQresultStatus(res) != PGRES_COMMAND_OK) {
|
||||
g_set_error (error, SEAF_DB_ERROR_DOMAIN, SEAF_DB_ERROR_CODE,
|
||||
"Commit transaction failed: %s", PQresultErrorMessage(res));
|
||||
ret = FALSE;
|
||||
}
|
||||
PQclear(res);
|
||||
return ret;
|
||||
}
|
||||
|
||||
gboolean
|
||||
pgsql_db_rollback (DBConnection *vconn, GError **error)
|
||||
{
|
||||
PGDBConnection *conn = (PGDBConnection *)vconn;
|
||||
gboolean ret = TRUE;
|
||||
|
||||
PGresult *res = PQexec(conn->db, "ROLLBACK TRANSACTION;");
|
||||
if (PQresultStatus(res) != PGRES_COMMAND_OK) {
|
||||
g_set_error (error, SEAF_DB_ERROR_DOMAIN, SEAF_DB_ERROR_CODE,
|
||||
"Rollback transaction failed: %s", PQresultErrorMessage(res));
|
||||
ret = FALSE;
|
||||
}
|
||||
PQclear(res);
|
||||
return ret;
|
||||
}
|
68
net/common/db-wrapper/pgsql-db-ops.h
Normal file
68
net/common/db-wrapper/pgsql-db-ops.h
Normal file
@ -0,0 +1,68 @@
|
||||
#ifndef PGSQL_DB_OPS_H
|
||||
#define PGSQL_DB_OPS_H
|
||||
|
||||
DBConnPool *
|
||||
pgsql_db_conn_pool_new (const char *host,
|
||||
const char *user,
|
||||
const char *password,
|
||||
const char *db_name,
|
||||
const char *unix_socket);
|
||||
|
||||
void
|
||||
pgsql_db_conn_pool_free (DBConnPool *vpool);
|
||||
|
||||
DBConnection *
|
||||
pgsql_get_db_connection (DBConnPool *vpool, GError **error);
|
||||
|
||||
void
|
||||
pgsql_db_connection_close (DBConnection *vconn);
|
||||
|
||||
gboolean
|
||||
pgsql_db_connection_execute (DBConnection *vconn, const char *sql, GError **error);
|
||||
|
||||
void
|
||||
pgsql_result_set_free (ResultSet *vr);
|
||||
|
||||
ResultSet *
|
||||
pgsql_execute_query (DBConnection *vconn, const char *sql, GError **error);
|
||||
|
||||
gboolean
|
||||
pgsql_result_set_next (ResultSet *vr, GError **error);
|
||||
|
||||
const char *
|
||||
pgsql_result_set_get_string (ResultSet *vr, int i, GError **error);
|
||||
|
||||
int
|
||||
pgsql_result_set_get_column_count (ResultSet *vr);
|
||||
|
||||
DBStmt *
|
||||
pgsql_prepare_statement (DBConnection *vconn, const char *sql, GError **error);
|
||||
|
||||
gboolean
|
||||
pgsql_stmt_set_int (DBStmt *vstmt, int i, int x, GError **error);
|
||||
|
||||
gboolean
|
||||
pgsql_stmt_set_int64 (DBStmt *vstmt, int i, gint64 x, GError **error);
|
||||
|
||||
gboolean
|
||||
pgsql_stmt_set_string (DBStmt *vstmt, int i, const char *s, GError **error);
|
||||
|
||||
gboolean
|
||||
pgsql_db_stmt_execute (DBStmt *vstmt, GError **error);
|
||||
|
||||
ResultSet *
|
||||
pgsql_db_stmt_execute_query (DBStmt *vstmt, GError **error);
|
||||
|
||||
void
|
||||
pgsql_db_stmt_free (DBStmt *vstmt);
|
||||
|
||||
gboolean
|
||||
pgsql_db_begin_transaction (DBConnection *vconn, GError **error);
|
||||
|
||||
gboolean
|
||||
pgsql_db_commit (DBConnection *vconn, GError **error);
|
||||
|
||||
gboolean
|
||||
pgsql_db_rollback (DBConnection *vconn, GError **error);
|
||||
|
||||
#endif
|
454
net/common/db-wrapper/sqlite-db-ops.c
Normal file
454
net/common/db-wrapper/sqlite-db-ops.c
Normal file
@ -0,0 +1,454 @@
|
||||
#include "common.h"
|
||||
|
||||
#include "db-wrapper.h"
|
||||
#include "sqlite-db-ops.h"
|
||||
|
||||
#include <sqlite3.h>
|
||||
#include <pthread.h>
|
||||
|
||||
/* SQLite thread synchronization rountines. */
|
||||
|
||||
typedef struct UnlockNotification {
|
||||
int fired;
|
||||
pthread_cond_t cond;
|
||||
pthread_mutex_t mutex;
|
||||
} UnlockNotification;
|
||||
|
||||
static void
|
||||
unlock_notify_cb(void **ap_arg, int n_arg)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < n_arg; i++) {
|
||||
UnlockNotification *p = (UnlockNotification *)ap_arg[i];
|
||||
pthread_mutex_lock (&p->mutex);
|
||||
p->fired = 1;
|
||||
pthread_cond_signal (&p->cond);
|
||||
pthread_mutex_unlock (&p->mutex);
|
||||
}
|
||||
}
|
||||
|
||||
static int
|
||||
wait_for_unlock_notify(sqlite3 *db)
|
||||
{
|
||||
UnlockNotification un;
|
||||
un.fired = 0;
|
||||
pthread_mutex_init (&un.mutex, NULL);
|
||||
pthread_cond_init (&un.cond, NULL);
|
||||
|
||||
int rc = sqlite3_unlock_notify(db, unlock_notify_cb, (void *)&un);
|
||||
|
||||
if (rc == SQLITE_OK) {
|
||||
pthread_mutex_lock(&un.mutex);
|
||||
if (!un.fired)
|
||||
pthread_cond_wait (&un.cond, &un.mutex);
|
||||
pthread_mutex_unlock(&un.mutex);
|
||||
}
|
||||
|
||||
pthread_cond_destroy (&un.cond);
|
||||
pthread_mutex_destroy (&un.mutex);
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
static int
|
||||
sqlite3_blocking_step(sqlite3_stmt *stmt)
|
||||
{
|
||||
int rc;
|
||||
while (SQLITE_LOCKED == (rc = sqlite3_step(stmt))) {
|
||||
rc = wait_for_unlock_notify(sqlite3_db_handle(stmt));
|
||||
if (rc != SQLITE_OK)
|
||||
break;
|
||||
sqlite3_reset(stmt);
|
||||
}
|
||||
return rc;
|
||||
}
|
||||
|
||||
static int
|
||||
sqlite3_blocking_prepare_v2(sqlite3 *db, const char *sql, int sql_len, sqlite3_stmt **pstmt, const char **pz)
|
||||
{
|
||||
int rc;
|
||||
while (SQLITE_LOCKED == (rc = sqlite3_prepare_v2(db, sql, sql_len, pstmt, pz))) {
|
||||
rc = wait_for_unlock_notify(db);
|
||||
if (rc != SQLITE_OK)
|
||||
break;
|
||||
}
|
||||
return rc;
|
||||
}
|
||||
|
||||
static int
|
||||
sqlite3_blocking_exec(sqlite3 *db, const char *sql, int (*callback)(void *, int, char **, char **), void *arg, char **errmsg)
|
||||
{
|
||||
int rc;
|
||||
while (SQLITE_LOCKED == (rc = sqlite3_exec(db, sql, callback, arg, errmsg))) {
|
||||
rc = wait_for_unlock_notify(db);
|
||||
if (rc != SQLITE_OK)
|
||||
break;
|
||||
}
|
||||
return rc;
|
||||
}
|
||||
|
||||
typedef struct SQLiteDBConnPool {
|
||||
DBConnPool parent;
|
||||
char *db_path;
|
||||
int max_connections;
|
||||
} SQLiteDBConnPool;
|
||||
|
||||
DBConnPool *
|
||||
sqlite_db_conn_pool_new (const char *db_path, int max_connections)
|
||||
{
|
||||
SQLiteDBConnPool *pool = g_new0 (SQLiteDBConnPool, 1);
|
||||
pool->db_path = g_strdup(db_path);
|
||||
pool->max_connections = max_connections;
|
||||
|
||||
return (DBConnPool *)pool;
|
||||
}
|
||||
|
||||
void
|
||||
sqlite_db_conn_pool_free (DBConnPool *vpool)
|
||||
{
|
||||
if (!vpool)
|
||||
return;
|
||||
|
||||
SQLiteDBConnPool *pool = (SQLiteDBConnPool *)vpool;
|
||||
|
||||
g_free (pool->db_path);
|
||||
g_free (pool);
|
||||
}
|
||||
|
||||
typedef struct SQLiteDBConnection {
|
||||
DBConnection parent;
|
||||
sqlite3 *db;
|
||||
} SQLiteDBConnection;
|
||||
|
||||
DBConnection *
|
||||
sqlite_get_db_connection (DBConnPool *vpool, GError **error)
|
||||
{
|
||||
SQLiteDBConnPool *pool = (SQLiteDBConnPool *)vpool;
|
||||
sqlite3 *db;
|
||||
int result;
|
||||
const char *errmsg;
|
||||
SQLiteDBConnection *conn;
|
||||
|
||||
result = sqlite3_open (pool->db_path, &db);
|
||||
if (result != SQLITE_OK) {
|
||||
errmsg = sqlite3_errmsg(db);
|
||||
g_set_error (error, SEAF_DB_ERROR_DOMAIN, SEAF_DB_ERROR_CODE,
|
||||
"Failed to open sqlite db: %s",
|
||||
errmsg ? errmsg : "no error given");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
conn = g_new0 (SQLiteDBConnection, 1);
|
||||
conn->db = db;
|
||||
|
||||
return (DBConnection *)conn;
|
||||
}
|
||||
|
||||
void
|
||||
sqlite_db_connection_close (DBConnection *vconn)
|
||||
{
|
||||
if (!vconn)
|
||||
return;
|
||||
|
||||
SQLiteDBConnection *conn = (SQLiteDBConnection *)vconn;
|
||||
|
||||
sqlite3_close (conn->db);
|
||||
|
||||
g_free (conn);
|
||||
}
|
||||
|
||||
gboolean
|
||||
sqlite_db_connection_execute (DBConnection *vconn, const char *sql, GError **error)
|
||||
{
|
||||
SQLiteDBConnection *conn = (SQLiteDBConnection *)vconn;
|
||||
char *errmsg = NULL;
|
||||
int rc;
|
||||
|
||||
rc = sqlite3_blocking_exec (conn->db, sql, NULL, NULL, &errmsg);
|
||||
if (rc != SQLITE_OK) {
|
||||
g_set_error (error, SEAF_DB_ERROR_DOMAIN, SEAF_DB_ERROR_CODE,
|
||||
"sqlite3_exec failed: %s",
|
||||
errmsg ? errmsg : "no error given");
|
||||
if (errmsg)
|
||||
sqlite3_free (errmsg);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
typedef struct SQLiteResultSet {
|
||||
ResultSet parent;
|
||||
sqlite3 *db;
|
||||
sqlite3_stmt *stmt;
|
||||
int column_count;
|
||||
} SQLiteResultSet;
|
||||
|
||||
void
|
||||
sqlite_result_set_free (ResultSet *vr)
|
||||
{
|
||||
if (!vr)
|
||||
return;
|
||||
|
||||
SQLiteResultSet *r = (SQLiteResultSet *)vr;
|
||||
|
||||
sqlite3_finalize (r->stmt);
|
||||
|
||||
g_free (r);
|
||||
}
|
||||
|
||||
ResultSet *
|
||||
sqlite_execute_query (DBConnection *vconn, const char *sql, GError **error)
|
||||
{
|
||||
SQLiteDBConnection *conn = (SQLiteDBConnection *)vconn;
|
||||
sqlite3_stmt *stmt;
|
||||
int rc;
|
||||
SQLiteResultSet *r;
|
||||
|
||||
rc = sqlite3_blocking_prepare_v2 (conn->db, sql, -1, &stmt, NULL);
|
||||
if (rc != SQLITE_OK) {
|
||||
g_set_error (error, SEAF_DB_ERROR_DOMAIN, SEAF_DB_ERROR_CODE,
|
||||
"sqlite3_prepare_v2 failed: %s", sqlite3_errmsg(conn->db));
|
||||
return NULL;
|
||||
}
|
||||
|
||||
r = g_new0 (SQLiteResultSet, 1);
|
||||
r->db = conn->db;
|
||||
r->stmt = stmt;
|
||||
r->column_count = sqlite3_column_count (stmt);
|
||||
|
||||
return (ResultSet *)r;
|
||||
}
|
||||
|
||||
gboolean
|
||||
sqlite_result_set_next (ResultSet *vr, GError **error)
|
||||
{
|
||||
SQLiteResultSet *r = (SQLiteResultSet *)vr;
|
||||
int rc;
|
||||
|
||||
rc = sqlite3_blocking_step (r->stmt);
|
||||
if (rc != SQLITE_ROW && rc != SQLITE_DONE) {
|
||||
g_set_error (error, SEAF_DB_ERROR_DOMAIN, SEAF_DB_ERROR_CODE,
|
||||
"sqlite3_step failed: %s", sqlite3_errmsg(r->db));
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
return (rc == SQLITE_ROW);
|
||||
}
|
||||
|
||||
const char *
|
||||
sqlite_result_set_get_string (ResultSet *vr, int i, GError **error)
|
||||
{
|
||||
SQLiteResultSet *r = (SQLiteResultSet *)vr;
|
||||
|
||||
if (i < 0 || i >= r->column_count) {
|
||||
g_set_error (error, SEAF_DB_ERROR_DOMAIN, SEAF_DB_ERROR_CODE,
|
||||
"Column index is out of range.");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return (const char *)sqlite3_column_text (r->stmt, i);
|
||||
}
|
||||
|
||||
int
|
||||
sqlite_result_set_get_column_count (ResultSet *vr)
|
||||
{
|
||||
return ((SQLiteResultSet *)vr)->column_count;
|
||||
}
|
||||
|
||||
typedef struct SQLiteDBStmt {
|
||||
DBStmt parent;
|
||||
int param_count;
|
||||
sqlite3 *db;
|
||||
sqlite3_stmt *stmt;
|
||||
} SQLiteDBStmt;
|
||||
|
||||
DBStmt *
|
||||
sqlite_prepare_statement (DBConnection *vconn, const char *sql, GError **error)
|
||||
{
|
||||
SQLiteDBConnection *conn = (SQLiteDBConnection *)vconn;
|
||||
sqlite3_stmt *stmt;
|
||||
int rc;
|
||||
SQLiteDBStmt *ret;
|
||||
|
||||
rc = sqlite3_blocking_prepare_v2 (conn->db, sql, -1, &stmt, NULL);
|
||||
if (rc != SQLITE_OK) {
|
||||
g_set_error (error, SEAF_DB_ERROR_DOMAIN, SEAF_DB_ERROR_CODE,
|
||||
"sqlite3_prepare_v2 failed: %s", sqlite3_errmsg(conn->db));
|
||||
return NULL;
|
||||
}
|
||||
|
||||
ret = g_new0 (SQLiteDBStmt, 1);
|
||||
ret->stmt = stmt;
|
||||
ret->db = conn->db;
|
||||
ret->param_count = sqlite3_bind_parameter_count (stmt);
|
||||
|
||||
return (DBStmt *)ret;
|
||||
}
|
||||
|
||||
gboolean
|
||||
sqlite_stmt_set_int (DBStmt *vstmt, int i, int x, GError **error)
|
||||
{
|
||||
SQLiteDBStmt *stmt = (SQLiteDBStmt *)vstmt;
|
||||
int rc;
|
||||
|
||||
if (i < 0 || i >= stmt->param_count) {
|
||||
g_set_error (error, SEAF_DB_ERROR_DOMAIN, SEAF_DB_ERROR_CODE,
|
||||
"Column index is out of range.");
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
rc = sqlite3_bind_int (stmt->stmt, i+1, x);
|
||||
if (rc != SQLITE_OK) {
|
||||
g_set_error (error, SEAF_DB_ERROR_DOMAIN, SEAF_DB_ERROR_CODE,
|
||||
"sqlite3_bind_int failed: %s", sqlite3_errstr(rc));
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
gboolean
|
||||
sqlite_stmt_set_int64 (DBStmt *vstmt, int i, gint64 x, GError **error)
|
||||
{
|
||||
SQLiteDBStmt *stmt = (SQLiteDBStmt *)vstmt;
|
||||
int rc;
|
||||
|
||||
if (i < 0 || i >= stmt->param_count) {
|
||||
g_set_error (error, SEAF_DB_ERROR_DOMAIN, SEAF_DB_ERROR_CODE,
|
||||
"Column index is out of range.");
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
rc = sqlite3_bind_int64 (stmt->stmt, i+1, x);
|
||||
if (rc != SQLITE_OK) {
|
||||
g_set_error (error, SEAF_DB_ERROR_DOMAIN, SEAF_DB_ERROR_CODE,
|
||||
"sqlite3_bind_int failed: %s", sqlite3_errstr(rc));
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
gboolean
|
||||
sqlite_stmt_set_string (DBStmt *vstmt, int i, const char *s, GError **error)
|
||||
{
|
||||
SQLiteDBStmt *stmt = (SQLiteDBStmt *)vstmt;
|
||||
int rc;
|
||||
|
||||
if (i < 0 || i >= stmt->param_count) {
|
||||
g_set_error (error, SEAF_DB_ERROR_DOMAIN, SEAF_DB_ERROR_CODE,
|
||||
"Column index is out of range.");
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
rc = sqlite3_bind_text (stmt->stmt, i+1, s, -1, SQLITE_TRANSIENT);
|
||||
if (rc != SQLITE_OK) {
|
||||
g_set_error (error, SEAF_DB_ERROR_DOMAIN, SEAF_DB_ERROR_CODE,
|
||||
"sqlite3_bind_int failed: %s", sqlite3_errstr(rc));
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
gboolean
|
||||
sqlite_db_stmt_execute (DBStmt *vstmt, GError **error)
|
||||
{
|
||||
SQLiteDBStmt *stmt = (SQLiteDBStmt *)vstmt;
|
||||
int rc;
|
||||
|
||||
rc = sqlite3_blocking_step (stmt->stmt);
|
||||
if (rc == SQLITE_DONE) {
|
||||
sqlite3_reset (stmt->stmt);
|
||||
return TRUE;
|
||||
} else if (rc == SQLITE_ROW) {
|
||||
sqlite3_reset (stmt->stmt);
|
||||
g_set_error (error, SEAF_DB_ERROR_DOMAIN, SEAF_DB_ERROR_CODE,
|
||||
"Select statement not allowed in db_stmt_execute.");
|
||||
return FALSE;
|
||||
} else {
|
||||
sqlite3_reset (stmt->stmt);
|
||||
g_set_error (error, SEAF_DB_ERROR_DOMAIN, SEAF_DB_ERROR_CODE,
|
||||
"sqlite3_step failed: %s", sqlite3_errmsg(stmt->db));
|
||||
return FALSE;
|
||||
}
|
||||
}
|
||||
|
||||
ResultSet *
|
||||
sqlite_db_stmt_execute_query (DBStmt *vstmt, GError **error)
|
||||
{
|
||||
SQLiteDBStmt *stmt = (SQLiteDBStmt *)vstmt;
|
||||
SQLiteResultSet *r;
|
||||
|
||||
r = g_new0 (SQLiteResultSet, 1);
|
||||
r->db = stmt->db;
|
||||
r->stmt = stmt->stmt;
|
||||
r->column_count = sqlite3_column_count (r->stmt);
|
||||
|
||||
return (ResultSet *)r;
|
||||
}
|
||||
|
||||
void
|
||||
sqlite_db_stmt_free (DBStmt *vstmt)
|
||||
{
|
||||
if (!vstmt)
|
||||
return;
|
||||
|
||||
SQLiteDBStmt *stmt = (SQLiteDBStmt *)vstmt;
|
||||
|
||||
if (!stmt->parent.result_set) {
|
||||
sqlite3_finalize (stmt->stmt);
|
||||
}
|
||||
|
||||
g_free (stmt);
|
||||
}
|
||||
|
||||
gboolean
|
||||
sqlite_db_begin_transaction (DBConnection *vconn, GError **error)
|
||||
{
|
||||
SQLiteDBConnection *conn = (SQLiteDBConnection *)vconn;
|
||||
int rc;
|
||||
|
||||
rc = sqlite3_blocking_exec (conn->db, "BEGIN TRANSACTION;", NULL, NULL, NULL);
|
||||
if (rc != SQLITE_OK) {
|
||||
g_set_error (error, SEAF_DB_ERROR_DOMAIN, SEAF_DB_ERROR_CODE,
|
||||
"begin transaction failed: %s", sqlite3_errmsg(conn->db));
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
gboolean
|
||||
sqlite_db_commit (DBConnection *vconn, GError **error)
|
||||
{
|
||||
SQLiteDBConnection *conn = (SQLiteDBConnection *)vconn;
|
||||
int rc;
|
||||
|
||||
rc = sqlite3_blocking_exec (conn->db, "COMMIT TRANSACTION;", NULL, NULL, NULL);
|
||||
if (rc != SQLITE_OK) {
|
||||
g_set_error (error, SEAF_DB_ERROR_DOMAIN, SEAF_DB_ERROR_CODE,
|
||||
"commit transaction failed: %s", sqlite3_errmsg(conn->db));
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
gboolean
|
||||
sqlite_db_rollback (DBConnection *vconn, GError **error)
|
||||
{
|
||||
SQLiteDBConnection *conn = (SQLiteDBConnection *)vconn;
|
||||
int rc;
|
||||
|
||||
rc = sqlite3_blocking_exec (conn->db, "ROLLBACK TRANSACTION;", NULL, NULL, NULL);
|
||||
if (rc != SQLITE_OK) {
|
||||
g_set_error (error, SEAF_DB_ERROR_DOMAIN, SEAF_DB_ERROR_CODE,
|
||||
"rollback transaction failed: %s", sqlite3_errmsg(conn->db));
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
}
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user